#include #include #include #include #include #include #include // convert all characters in upper case to lower case void lowercase(char* s) { while (*s) { if (isupper(*s)) *s = tolower(*s); ++s; } } // remove whitespace void ignore_whitespace(char* s) { char* t = s; while (*s) { if (!isspace(*s)) { *t++ = *s; } ++s; } *t = 0; } // remove all non alphanumeric characters void only_alpha(char* s) { char* t = s; while (*s) { if (isalnum(*s)) { *t++ = *s; } ++s; } *t = 0; } // regard arbitrary whitespace as single space void single_whitespace(char *s) { char* t = s; while (*t) { bool incs = false; while (*t && isspace(*t)) { ++t; incs = true; } if (incs) ++s; *s++ = *t++; } //terminate smaller string if (s != t) *s = '\0'; } // create array of anonymous struct // containing function pointers and options struct { char option; void (*filter)(char*); } filters[] = { {'c', &lowercase}, {'w', &ignore_whitespace}, {'s', &single_whitespace}, {'a', &only_alpha} }; #define DIM(v) (sizeof(v)/sizeof(v[0])) /* read a line of arbitrary length from fp and return a pointer to a dynamically allocated array with the line contents (excluding the terminating '\n'); return 0 in case of eof or if no memory was available */ char* readline(FILE* fp) { int len = 32; char* cp = malloc(len); if (!cp) return 0; int i = 0; int ch; while ((ch = getc(fp)) != EOF && ch != '\n') { cp[i++] = ch; if (i == len) { /* double the allocated space */ len *= 2; char* newcp = realloc(cp, len); if (!newcp) { free(cp); return 0; } cp = newcp; } } if (i == 0 && ch == EOF) { free(cp); return 0; } cp[i++] = 0; return realloc(cp, i); /* free unused space */ } int main(int argc, char** argv) { char* cmdname = *argv; char usage[] = "Usage: %s [options] file1 file2 \n"; char options[DIM(filters) + 1]; for (int i = 0; i < DIM(filters); ++i) { options[i] = filters[i].option; } options[DIM(filters)] = 0; struct filter { void (*filter)(char*); struct filter* next; } * selected_filters = 0; // start of list of selected filters int option; while ((option = getopt(argc, argv, options)) != -1) { bool processed = false; for (int i = 0; i < DIM(filters); ++i) { if (filters[i].option == option) { struct filter* filter = malloc(sizeof(struct filter)); assert(filter); filter->filter = filters[i].filter; filter->next = selected_filters; selected_filters = filter; processed = true; break; } } if (!processed) { fprintf(stderr, usage, cmdname); return 1; } } extern int optind; argc -= optind; argv += optind; if (argc != 2) { fprintf(stderr, "Two file names expected: "); fprintf(stderr, usage, cmdname); return 1; } FILE* fp[2]; for (int i = 0; i < 2; ++i) { fp[i] = fopen(argv[i], "r"); if (!fp[i]) { perror(argv[i]); return 1; } } unsigned int line_number = 0; for(;;) { char* line[2]; // read lines for (int i = 0; i < 2; ++i) { line[i] = readline(fp[i]); } if (line[0] == 0 && line[1] == 0) break; if (line[0] == 0 || line[1] == 0) { printf("premature end of %s\n", line[0]? argv[1]: argv[0]); break; } ++line_number; // edit lines according to selected filters for (struct filter* fp = selected_filters; fp; fp = fp->next) { for (int i = 0; i < 2; ++i) { (*(fp->filter))(line[i]); } } // compare lines if (strcmp(line[0], line[1])) { printf("%s:%d: %s\n", argv[0], line_number, line[0]); printf("%s:%d: %s\n", argv[1], line_number, line[1]); } // free lines free(line[0]); free(line[1]); } // free list for (struct filter* fp = selected_filters; fp;) { struct filter* tmp = fp; fp = fp->next; free(tmp); } // close files fclose(fp[0]); fclose(fp[1]); }