/* * Tutkitaan RF-viitteen oikeellisuutta * * Muoto: RFttxx * missä RF on vakiomerkkijono, * tt on 2-numeroinen tarkiste ja * xx on 1-21 aakkosnumeerisen merkin (0-9A-Z) jono * * Seassa saa olla yksittäisiä tyhjiä ja pienet kirjaimet samaistetaan isoiksi. * Tarkisteen lasku: * (1) lisätään xx:n loppuun "RF00" * (2) muunnetaan kirjaimet numeroiksi tyyliin A->10, B->11 jne * (3) lasketaan tuloksesta jakojäännös 97:llä jaettaessa * (4) tarkiste on 98 - jakojäännös */ #include #include #include #include #include #define VALIDMAX 25 #define VALIDMIN 5 #define MAXLEN (2*VALIDMAX) /* varataan tilaa myös välityhjille */ /* apumakroja debuggaukseen */ #ifdef DEBUG #define DPRINT1(a,b) fprintf(stderr,(a),(b)) #define DPRINT2(a,b,c) fprintf(stderr,(a),(b),(c)) #else #define DPRINT1(a,b) #define DPRINT2(a,b,c) #endif void tarkista_alku(const char viite[]); void lisaa_normalisoituna(const char *lahde, char kohde[], int maxpituus); char * normalisoi(const char viite[]); int longmod97(char *longnum); void tarkistusmerkkitesti(int laskettu, char viite[]); void virhe(char *fmt, int n); int main(int argc, char **argv) { char viite[MAXLEN + 1] = ""; char *numviite; int jakoj, tarknro, i; if (argc < 2) { printf("Anna viite: "); fgets(viite, MAXLEN, stdin); } else { for (i=1; i 10-35 (ei toimi EBCDIC-koneissa...) */ #define LETTER2NUM(letter) ((letter) - 'A' + 10) void lisaa_normalisoituna(const char *lahde, char kohde[], int maxpituus) { DPRINT2("normalisoidaan '%s' (%lu merkkiä)\n", lahde, strlen(lahde)); for (; *lahde; lahde++) { if (isspace(*lahde)) { continue; } else if (--maxpituus < 0) { virhe("Liian pitka (maks. %d merkkia)", VALIDMAX); } else if (isdigit(*lahde)) { *kohde++ = *lahde; DPRINT2("%c -> %c\n", *lahde, *lahde); } else if (isalpha(*lahde)) { int k = LETTER2NUM(toupper(*lahde)); *kohde++ = '0' + k/10; *kohde++ = '0' + k%10; DPRINT2("%c -> %d\n", *lahde, k); } else { virhe("Laiton merkki '%c'", *lahde); } } } /* lasketaan merkkijonona annetusta kokonaisluvusta modulo 97 */ #ifdef MODLONGLONG /* käytetään long long -lukuja: 64 bittiin mahtuu 13-numeroinen jaettava */ int longmod97(char *longnum) { unsigned long long thenum; errno=0; sscanf(longnum, "%Lu", &thenum); if (errno) virhe("Liian iso, en osaa", 0); DPRINT1("numerona: %Lu\n", thenum); return thenum%97; } #elif MODCHAR /* lasketaan numero kerrallaan ja otetaan joka vaiheessa %97 ylivuodon estämiseksi */ /* käyttäen kaavaa (a+b)%97 == (a%97) +c)%97 */ int longmod97(char *longnum) { int result; result = 0; for (; *longnum; ++longnum) { result = (result*10 + (*longnum - '0')) % 97; } return result; } #else /* paloitellaan 8 numeron pätkiin ja lasketaan jakojäännös paloittain */ /* käyttäen kaavaa (a*b+c)%97 == (a*(b%97)+c)%97 */ /* esim. (a*100000000 + b)%97 == (a*(100000000%97)+b)%97 jne */ /* ei erityisen järkevä ratkaisu... */ int longmod97(char *longnum) { int i, result; long npart; result = 0; for (i = 0; 1 == sscanf(longnum+i, "%8lu", &npart); i += 8) { DPRINT2("'%s' -> %lu\n", longnum+i, npart); switch(strlen(longnum+i)) { default: result *= 100000000%97; break; case 7: result *= 10000000%97; break; case 6: result *= 1000000%97; break; case 5: result *= 100000%97; break; case 4: result *= 10000%97; break; case 3: result *= 1000%97; break; case 2: result *= 100%97; break; case 1: result *= 10%97; break; case 0: virhe("This cannot happen!", 0); } result = (result + npart) % 97; } return result; } #endif