cmp.c (3754B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include <assert.h>
- #include <errno.h>
- #include <stdbool.h>
- #include <stdio.h> // fopen, fprintf, getline
- #include <stdlib.h> // abort, strtoul
- #include <string.h> // strerror
- #include <unistd.h> // getopt
- static bool opt_s = false, opt_l = false;
- static unsigned long max_bytes = 0;
- const char *argv0 = "cmp";
- #undef MIN
- #define MIN(a, b) (((a) < (b)) ? (a) : (b))
- static int
- do_cmp(FILE *file1, const char *name1, FILE *file2, const char *name2)
- {
- char *line1 = NULL, *line2 = NULL;
- size_t len1 = 0, len2 = 0;
- unsigned long pos = 1, ln = 1;
- while(true)
- {
- ssize_t nread1 = getline(&line1, &len1, file1);
- if(nread1 < 0)
- {
- if(!ferror(file1)) return 0;
- fprintf(stderr,
- "%s: error: Failed to read line %ld from file '%s': %s\n",
- argv0,
- ln,
- name1,
- strerror(errno));
- return 1;
- }
- ssize_t nread2 = getline(&line2, &len2, file2);
- if(nread2 < 0)
- {
- if(!ferror(file2))
- {
- if(!opt_s) fprintf(stderr, "%s: error: EOF on %s line %ld\n", argv0, name2, ln);
- return 1;
- }
- fprintf(stderr,
- "%s: error: Failed to read line %ld from file '%s': %s\n",
- argv0,
- ln,
- name1,
- strerror(errno));
- return 2;
- }
- for(ssize_t i = 0; i < MIN(nread1, nread2); i++)
- {
- if(max_bytes != 0 && pos + i >= max_bytes) return 0;
- if(line1[i] != line2[i])
- {
- if(opt_s) return 1;
- if(opt_l)
- printf("%ld %o %o\n", pos + i, line1[i], line2[i]);
- else
- printf("%s %s differ: char %zd, line %ld\n", name1, name2, i + 1, ln);
- return 1;
- }
- }
- assert(nread1 == nread2);
- pos += nread1;
- ln++;
- }
- return 0;
- }
- static void
- usage(void)
- {
- fprintf(stderr, "Usage: cmp [-l|-s] [-n max_bytes] file1 file2\n");
- }
- int
- main(int argc, char *argv[])
- {
- char *endptr = NULL;
- int c = -1;
- while((c = getopt(argc, argv, ":ln:s")) != -1)
- {
- switch(c)
- {
- case 'l':
- opt_l = true;
- break;
- case 's':
- opt_s = true;
- break;
- case 'n':
- errno = 0;
- max_bytes = strtoul(optarg, &endptr, 0);
- if(errno != 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-n %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && endptr[0] != 0)
- {
- fprintf(stderr,
- "%s: error: Non-numeric characters passed to '-n %s': %s\n",
- argv0,
- optarg,
- endptr);
- return 1;
- }
- break;
- case ':':
- fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
- usage();
- return 1;
- default:
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc != 2)
- {
- fprintf(stderr, "%s: error: Expected 2 arguments, got %d arguments\n", argv0, argc);
- return 1;
- }
- if(strcmp(argv[0], argv[1]) == 0) return 0;
- assert(errno == 0);
- FILE *file1 = NULL;
- if(argv[0][0] == '-' && argv[0][1] == 0)
- file1 = stdin;
- else
- {
- file1 = fopen(argv[0], "r");
- if(file1 == NULL)
- {
- fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[0], strerror(errno));
- return 1;
- }
- }
- FILE *file2 = NULL;
- if(argv[1][0] == '-' && argv[1][1] == 0)
- file2 = stdin;
- else
- {
- file2 = fopen(argv[1], "r");
- if(file2 == NULL)
- {
- fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[1], strerror(errno));
- return 1;
- }
- }
- int ret = do_cmp(file1, argv[0], file2, argv[1]);
- fclose(file1);
- fclose(file2);
- return ret;
- }