#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//compile with: gcc -Wall -Wextra cc-c.c -o cc-c
//run with: ./cc-c zhouyi.txt zhouyi.txt; echo "exit $?" # identical, exit 0
// ./cc-c zhouyi.txt <(sed '272s/\r$/X\r/' zhouyi.txt) # line 272 is >1000 bytes: fgets gives its tail
// ./cc-c zhouyi.txt <(sed '334s/吉/凶/' zhouyi.txt) # differ at line 334
// ./cc-c <(printf 'a\nb\nc\n') <(printf 'a\nb\n'); echo "exit $?" # second ends first, exit 1
// ./cc-c <(printf 'a\nb\n') <(printf 'a\nB\n') # line 2: b vs B
// ./cc-c zhouyi.txt nosuchfile; echo "exit $?" # can't open, exit 2
// ./cc-c zhouyi.txt; echo "exit $?" # wrong arg count, exit 2
//K&R2 exercise 7-6: compare two files, printing the first line where
//they differ. Same exit codes as the Unix cmp and diff:
// 0 same, 1 different, 2 trouble (bad args, can't open a file)
//<(...) in the run lines is bash process substitution: bash runs the
//command and hands the program a file name (/dev/fd/63) to read its
//output from, so no test files need to be made.
#define MAXLINE 1000
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char line1[MAXLINE], line2[MAXLINE];
char *r1, *r2;
long lineno = 1;
char *prog = argv[0];
if (argc != 3) {
fprintf(stderr, "usage: %s file1 file2\n", prog);
exit(2);
}
//IDIOM: assign-in-test -- open and check for NULL in one expression
if ((fp1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n", prog, argv[1]);
exit(2);
}
if ((fp2 = fopen(argv[2], "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n", prog, argv[2]);
exit(2);
}
for (;;) {
r1 = fgets(line1, MAXLINE, fp1);
r2 = fgets(line2, MAXLINE, fp2);
//both ended together: no difference anywhere
if (r1 == NULL && r2 == NULL) {
fclose(fp1);
fclose(fp2);
exit(0);
}
//IDIOM: || guard -- if one side is NULL, strcmp is never called,
//so it never sees a NULL pointer
if (r1 == NULL || r2 == NULL || strcmp(line1, line2) != 0)
break;
//a line longer than MAXLINE comes back from fgets in pieces;
//count a line only when its '\n' has been read
//IDIOM: s[strlen(s)-1] -- the last char of a non-empty string
if (line1[strlen(line1) - 1] == '\n')
lineno++;
}
printf("%s and %s differ at line %ld:\n", argv[1], argv[2], lineno);
printf("< %s", r1 ? line1 : "(end of file)\n");
if (r1 && line1[strlen(line1) - 1] != '\n')
putchar('\n'); //last line had no '\n', or was cut at MAXLINE
printf("> %s", r2 ? line2 : "(end of file)\n");
if (r2 && line2[strlen(line2) - 1] != '\n')
putchar('\n');
fclose(fp1);
fclose(fp2);
exit(1);
}
win@DESKTOP-MEIH88T:~/webdev-projects$ gcc -Wall -Wextra cc-c.c -o cc-c
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt zhouyi.txt; echo "exit $?"
exit 0
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt <(sed '272s/$/X/' zhouyi.txt)
zhouyi.txt and /dev/fd/63 differ at line 272:
<
X
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt <(sed '334s/吉/凶/' zhouyi.txt)
zhouyi.txt and /dev/fd/63 differ at line 334:
< 5. **九五:甘節,吉;往有尚。**
> 5. **九五:甘節,凶;往有尚。**
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c <(printf 'a\nb\nc\n') <(printf 'a\nb\n'); echo "exit $?"
/dev/fd/63 and /dev/fd/62 differ at line 3:
< c
> (end of file)
exit 1
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c <(printf 'a\nb\n') <(printf 'a\nB\n')
/dev/fd/63 and /dev/fd/62 differ at line 2:
< b
> B
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt nosuchfile; echo "exit $?"
./cc-c: can't open nosuchfile
exit 2
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt; echo "exit $?"
usage: ./cc-c file1 file2
exit 2
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c zhouyi.txt <(sed '272s/\r$/X\r/' zhouyi.txt) # > spaces X
zhouyi.txt and /dev/fd/63 differ at line 272:
<
> X