comm.c (5830B)
- // 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 "../libutils/getopt_nolong.h"
- #include <errno.h>
- #include <locale.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- enum comm_out
- {
- COMM_OUT_NONE = 0,
- COMM_OUT_FILE1 = 1 << 0,
- COMM_OUT_FILE2 = 1 << 1,
- COMM_OUT_COMM = 1 << 2,
- COMM_OUT_ALL = COMM_OUT_FILE1 | COMM_OUT_FILE2 | COMM_OUT_COMM,
- };
- const char *argv0 = "comm";
- enum comm_out c_out = COMM_OUT_ALL;
- static void
- usage(void)
- {
- fprintf(stderr, "Usage: comm [-123] file1 file2\n");
- }
- static int
- drain1(FILE *in, const char *fname, char *buf, size_t *bufsiz)
- {
- errno = 0;
- if(!(c_out & COMM_OUT_FILE1))
- {
- fflush(stdout);
- return 0;
- }
- if(buf) printf("%s", buf);
- for(;;)
- {
- size_t nread = fread(buf, 1, *bufsiz, in);
- if(errno != 0)
- {
- fprintf(
- stderr, "comm: error: Failed reading from file1 \"%s\": %s\n", fname, strerror(errno));
- return 1;
- }
- if(nread == 0) break; // EOF
- fwrite(buf, 1, nread, stdout);
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed writing file1 \"%s\" data to stdout: %s\n",
- fname,
- strerror(errno));
- return 1;
- }
- }
- fflush(stdout);
- return 0;
- }
- static int
- drain2(FILE *in, const char *fname, char *buf, size_t *bufsiz)
- {
- errno = 0;
- if(!(c_out & COMM_OUT_FILE2))
- {
- fflush(stdout);
- return 0;
- }
- if(buf) printf("\t%s", buf);
- for(;;)
- {
- ssize_t nread = getline(&buf, bufsiz, in);
- if(nread < 0)
- {
- if(errno == 0) break; // EOF
- fprintf(stderr,
- "comm: error: Failed reading line from file2 \"%s\": %s\n",
- fname,
- strerror(errno));
- return 1;
- }
- errno = 0;
- fputc('\t', stdout);
- fwrite(buf, 1, nread, stdout);
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed writing file2 \"%s\" data to stdout: %s\n",
- fname,
- strerror(errno));
- return 1;
- }
- }
- fflush(stdout);
- return 0;
- }
- int
- main(int argc, char *argv[])
- {
- if(setlocale(LC_ALL, "") == NULL)
- {
- fprintf(stderr,
- "%s: warning: Failed loading locales. setlocale(LC_ALL, \"\"): %s\n",
- argv0,
- strerror(errno));
- }
- errno = 0;
- for(int c = -1; (c = getopt_nolong(argc, argv, ":123")) != -1;)
- {
- switch(c)
- {
- case '1':
- c_out &= ~COMM_OUT_FILE1;
- break;
- case '2':
- c_out &= ~COMM_OUT_FILE2;
- break;
- case '3':
- c_out &= ~COMM_OUT_COMM;
- break;
- case '?':
- GETOPT_UNKNOWN_OPT
- usage();
- return 1;
- default:
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc != 2)
- {
- fprintf(stderr, "comm: error: Expected 2 arguments, got %d\n", argc);
- usage();
- return 1;
- }
- const char *fname1 = argv[0];
- const char *fname2 = argv[1];
- FILE *file1 = NULL, *file2 = NULL;
- if(fname1[0] == '-' && !fname1[1])
- {
- file1 = stdin;
- }
- else
- {
- file1 = fopen(fname1, "r");
- if(!file1)
- {
- fprintf(stderr, "comm: error: Failed opening file1 \"%s\": %s\n", fname1, strerror(errno));
- return 1;
- }
- }
- if(fname2[0] == '-' && !fname2[1])
- {
- if(fname1[0] == '-' && !fname1[1])
- {
- fprintf(stderr, "comm: error: file1 and file2 cannot be both stdin\n");
- usage();
- return 1;
- }
- file2 = stdin;
- }
- else
- {
- file2 = fopen(fname2, "r");
- if(!file2)
- {
- fprintf(stderr, "comm: error: Failed opening file2 \"%s\": %s\n", fname2, strerror(errno));
- return 1;
- }
- }
- char *f1 = NULL, *f2 = NULL;
- size_t f1n = 0, f2n = 0;
- errno = 0;
- if(getline(&f1, &f1n, file1) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file1 \"%s\": %s\n",
- fname1,
- strerror(errno));
- return 1;
- }
- return drain2(file2, fname2, f2, &f2n);
- }
- errno = 0;
- if(getline(&f2, &f2n, file2) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file2 \"%s\": %s\n",
- fname2,
- strerror(errno));
- return 1;
- }
- return drain1(file1, fname1, f1, &f1n);
- }
- int d = strcoll(f1, f2);
- for(;;)
- {
- // TODO: Compare newly obtained line with it's previous one, to check if the file is sorted
- while(d == 0)
- {
- if(c_out & COMM_OUT_COMM) printf("\t\t%s", f1);
- f1 = NULL;
- f2 = NULL;
- errno = 0;
- if(getline(&f1, &f1n, file1) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file1 \"%s\": %s\n",
- fname1,
- strerror(errno));
- return 1;
- }
- return drain2(file2, fname2, f2, &f2n);
- }
- errno = 0;
- if(getline(&f2, &f2n, file2) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file2 \"%s\": %s\n",
- fname2,
- strerror(errno));
- return 1;
- }
- return drain1(file1, fname1, f1, &f1n);
- }
- d = strcoll(f1, f2);
- }
- while(d > 0)
- {
- if(c_out & COMM_OUT_FILE2) printf("\t%s", f2);
- f2 = NULL;
- errno = 0;
- if(getline(&f2, &f2n, file2) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file2 \"%s\": %s\n",
- fname2,
- strerror(errno));
- return 1;
- }
- return drain1(file1, fname1, f1, &f1n);
- }
- d = strcoll(f1, f2);
- }
- while(d < 0)
- {
- if(c_out & COMM_OUT_FILE1) printf("%s", f1);
- f1 = NULL;
- errno = 0;
- if(getline(&f1, &f1n, file1) < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "comm: error: Failed reading line from file1 \"%s\": %s\n",
- fname1,
- strerror(errno));
- return 1;
- }
- return drain2(file2, fname2, f2, &f2n);
- }
- d = strcoll(f1, f2);
- }
- }
- return 0;
- }