/* Tool to apply a Nodediff upon a nodelist. Written 1999 by Tobias Ernst and released to the Public Domain. References: FTS-0005 */ #include #include #include #include #include #include "crc16.h" #include "version.h" /* It is no problem if a line grows larger than BUFSZ. This code is intelligent. :-) */ #define BUFSZ 128 unsigned short actualcrc; /* A state machine to analyse the very first line of a nodelist for day number and an optional CRC value. */ enum {SCANDASH, SCANFDIGIT, DAYVAL, SCANCOLON, SCANNDIGIT, CRCVAL, AFTERCRCVAL, FINISH}; int analyze_first_line(FILE *f, unsigned short *crcnum, int *has_crc, unsigned short *daynum) { char c=0; int state = SCANDASH; unsigned short crc, day, hcrc, result = 1; while (state != FINISH) { if (fread(&c, 1, 1, f) != 1) { result = 0; state = FINISH; continue; } if ( c == '\n' ) { hcrc = 1; if (state < CRCVAL) { hcrc = 0; } if (state < DAYVAL) { result = 0; /* analysis failed */ } state = FINISH; } else if (isdigit(c)) { switch (state) { case SCANFDIGIT: day = c - '0'; state = DAYVAL; break; case SCANNDIGIT: crc = c - '0'; state = CRCVAL; break; case DAYVAL: day = day * 10U + (c - '0'); break; case CRCVAL: crc = crc * 10U + (c - '0'); break; default:; } } else if (c == '-') { switch (state) { case SCANDASH: state = SCANFDIGIT; break; default:; } } else if (c == ':') { switch (state) { case SCANCOLON: state = SCANNDIGIT; break; default:; } } else { switch(state) { case CRCVAL: state = AFTERCRCVAL; break; case DAYVAL: state = SCANCOLON; break; default:; } } } *crcnum = crc; *daynum = day; *has_crc = hcrc; return result; } /* Copy a line from in to out, no matter how long it is, and register it with the CRC checker. */ int passline (FILE *from, FILE *to) { char buf1[BUFSZ]; size_t l; do { if (fgets(buf1, BUFSZ, from) == NULL) return -1; if (fputs(buf1, to) == EOF) return -1; l = strlen(buf1); crc16_process(&actualcrc, (unsigned char *)buf1, l); if (!l) return -1; } while (buf1[l - 1] != '\n'); return 0; } /* Skip a line. */ int skipline (FILE *from) { char buf1[BUFSZ]; size_t l; do { if (fgets(buf1, BUFSZ, from) == NULL) return -1; l = strlen(buf1); if (!l) return -1; } while (buf1[l - 1] != '\n'); return 0; } /* Compare two lines. */ int compareline (FILE *from1, FILE *from2) { char buf1[BUFSZ], buf2[BUFSZ]; size_t l; do { if (fgets(buf1, BUFSZ, from1) == NULL) return -1; if (fgets(buf2, BUFSZ, from2) == NULL) return -1; l = strlen(buf1); if (!l) return -1; if (memcmp(buf1, buf2, l)) return -2; } while (buf1[l - 1] != '\n'); return 0; } /* Analyse a Nodediff command */ enum {COPY, ADD, DELETE, END, ILL}; int readcommand(FILE *f, int *argument) { char buffer[64]; int cmd, arg; if (fgets(buffer, 64, f) == NULL) return END; if (*buffer == '\0' || *buffer == 0x1A) return END; switch (*buffer) { case 'A': cmd = ADD; break; case 'C': cmd = COPY; break; case 'D': cmd = DELETE; break; default: return ILL; } arg = atoi(buffer+1); if (arg < 1) return ILL; *argument = arg; return cmd; } /* Construct the name of the new nodelist from the old name plus the extension of the nodediff file */ char *construct_new_filename(char *listname, char *diffname) { int l, m; char *tempname; /* Sanity checks on the given filenames */ if ((l = strlen(listname)) < 5) { fprintf (stderr, "%s is not a valid nodelist filename.\n", listname); return NULL; } if (listname[l-4] != '.' || !isdigit(listname[l-3]) || !isdigit(listname[l-2]) || !isdigit(listname[l-1])) { fprintf (stderr, "%s is not a valid nodelist filename.\n", listname); return NULL; } if ((m = strlen(diffname)) < 5) { fprintf (stderr, "%s is not a valid nodediff filename.\n", listname); return NULL; } if (diffname[m-4] != '.' || !isdigit(diffname[m-3]) || !isdigit(diffname[m-2]) || !isdigit(diffname[m-1])) { fprintf (stderr, "%s is not a valid nodediff filename.\n", listname); return NULL; } /* test for misuse like "nldiff -n nodelist.365 nodediff.365 */ if (!strcmp(diffname+m-4, listname+l-4)) { fprintf (stderr, "%s does not seem to apply to %s.\n", diffname, listname); return NULL; } /* Construct filename of new nodelist */ if ((tempname = malloc(strlen(listname) + 1)) == NULL) { fprintf (stderr, "Out of memory.\n"); return NULL; } strcpy(tempname, listname); strcpy(tempname+l-4, diffname+m-4); return tempname; } /* Parse command line arguments */ enum { REMOVE_NODELIST = 1, REMOVE_NODEDIFF = 2 }; int parse_args (int argc, char **argv, char **listname, char **diffname, int *pflags) { char *args[2]; int i,j = 0; int flags; flags = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'n': flags = flags | REMOVE_NODELIST; break; case 'd': flags = flags | REMOVE_NODEDIFF; break; default: return 0; } if (argv[i][2]) { return 0; } continue; } if (j<2) { args[j++] = argv[i]; } } if (j == 2) { *listname = args[0]; *diffname = args[1]; *pflags = flags; return 1; } return 0; } /* Usage help */ void usage(void) { fprintf (stderr, "nldiff - nodelist differ rev. %s\n", REV); fprintf (stderr, "Usage:\n"\ " nldiff [-n] [-d] LISTNAME.DNR DIFFNAME.DNR\n\n"\ " The -n option causes the original Nodelist to be deleted if the new\n"\ " nodelist could be successfully generated.\n"\ " The -d option causes the Nodediff file to be deleted if the new\n"\ " nodelist could be successfully generated.\n\n"\ "Example:\n"\ " nldiff -n NODELIST.253 NODEDIFF.260\n\n"\ "Remarks:\n"\ " If you want a tool that automatically updates your nodelist\n"\ " without you having to manually specify the day number file\n"\ " extensions, use \"nlupdate\". It will call nldiff internally.\n"); } /* Main program */ int main(int argc, char **argv) { FILE *fn = NULL, *fd = NULL, *fo = NULL; char *listname = NULL, *diffname = NULL, *tempname = NULL; int cmd, arg, i, hascrc, rv; unsigned short crc, newday, expnewday; int crci = 0; int flags = 0; /* parse the command line */ if (!parse_args(argc, argv, &listname, &diffname, &flags)) { usage(); return 8; } /* construct the filename of the new nodelist */ if ((tempname = construct_new_filename(listname, diffname)) == NULL) return 8; expnewday=atoi(tempname + strlen(tempname) - 3); /* open the files */ if ((fn = fopen(listname, "rb")) == NULL) { fprintf (stderr, "Cannot open %s.\n", listname); goto abnormal; } if ((fd = fopen(diffname, "rb")) == NULL) { fprintf (stderr, "Cannot open %s.\n", diffname); goto abnormal; } if ((fo = fopen(tempname, "w+b")) == NULL) { fprintf (stderr, "Cannot create %s.\n", tempname); goto abnormal; } /* Test if the diff's first line matches the list's first line */ switch (compareline(fn, fd)) { case -2: fprintf (stderr, "%s does not seem to apply to %s.\n", diffname, listname); goto abnormal; case -1: fprintf (stderr, "I/O error.\n"); goto abnormal; default:; /* match! */ } if (fseek(fn, 0L, SEEK_SET)) { fprintf (stderr, "File seek error.\n"); goto abnormal; } /* Interpret the commands in the Diff file */ do { cmd = readcommand(fd, &arg); switch(cmd) { case COPY: for (i = 0; i < arg; i++) { if (passline(fn, fo)) { fprintf (stderr, "Copy failed (%d / %d)\n", i, arg); goto abnormal; } if (!crci) { crc16_init(&actualcrc); crci = 1; } } break; case ADD: for (i = 0; i < arg; i++) { if (passline(fd, fo)) { fprintf (stderr, "Add failed (%d / %d)\n", i, arg); goto abnormal; } if (!crci) { crc16_init(&actualcrc); crci = 1; } } break; case DELETE: for (i = 0; i < arg; i++) if (skipline(fn)) { fprintf (stderr, "Delete failed (%d / %d)\n", i, arg); goto abnormal; } break; case END: break; default: fprintf (stderr, "Illegal command encountered.\n"); goto abnormal; } } while (cmd != END); fputc(0x1A, fo); /* Now determine the actual day number and the expected CRC of the newly written nodelist file */ if ((fseek(fo, 0L, SEEK_SET)) || (!analyze_first_line(fo, &crc, &hascrc, &newday))) { fprintf (stderr, "New file is not a valid nodelist.\n"); goto abnormal; } /* Compare the CRC values */ crc16_finalize(&actualcrc); if (hascrc && actualcrc != crc) { fprintf (stderr, "New file does not pass CRC test.\n"); goto abnormal; } if (newday != expnewday) { fprintf (stderr, "New day number and diff file name do not match.\n"); goto abnormal; } fclose(fn); fclose(fd); fclose(fo); rv=0; /* Delete files that are not needed any more, if the user requested it. */ if ((flags & REMOVE_NODELIST) && remove(listname)) { fprintf (stderr, "Cannot remove old nodelist file %s.\n", listname); rv = 8; } if ((flags & REMOVE_NODEDIFF) && remove(diffname)) { fprintf (stderr, "Cannot remove old nodediff file %s.\n", diffname); rv = 8; } return rv; abnormal: fprintf (stderr, "Processing aborted.\n"); if (fn != NULL) fclose(fn); if (fd != NULL) fclose(fd); if (fo != NULL) fclose(fo); if (tempname != NULL) { if (fo != NULL) remove(tempname); free(tempname); } return 8; }