K&R2 exercise 7-6

2026-10-16

#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