uniq.c (5129B)
- // 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 <ctype.h> // isblank
- #include <errno.h>
- #include <stdbool.h>
- #include <stdio.h> // getline
- #include <stdlib.h> // atoi
- #include <string.h> // strncmp
- #include <unistd.h> // getopt
- enum uniq_mode
- {
- UNIQ, // default
- COUNT,
- ONLY_REPEAT,
- NO_REPEAT,
- };
- const char *argv0 = "uniq";
- int
- main(int argc, char *argv[])
- {
- enum uniq_mode mode = UNIQ;
- unsigned long field = 0, shift = 0;
- char *endptr = NULL;
- int c = -1;
- while((c = getopt(argc, argv, ":cdf:s:u")) != -1)
- {
- switch(c)
- {
- case 'c':
- if(mode != UNIQ)
- {
- fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
- return 1;
- }
- mode = COUNT;
- break;
- case 'd':
- if(mode != UNIQ)
- {
- fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
- return 1;
- }
- mode = ONLY_REPEAT;
- break;
- case 'f':
- errno = 0;
- field = strtoul(optarg, &endptr, 0);
- if(errno != 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-f %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && endptr[0] != 0)
- {
- fprintf(stderr,
- "%s: error: Non-numeric characters passed to '-f %s': %s\n",
- argv0,
- optarg,
- endptr);
- return 1;
- }
- break;
- case 's':
- errno = 0;
- shift = strtoul(optarg, &endptr, 0);
- if(errno != 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-f %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && endptr[0] != 0)
- {
- fprintf(stderr,
- "%s: error: Non-numeric characters passed to '-f %s': %s\n",
- argv0,
- optarg,
- endptr);
- return 1;
- }
- break;
- case 'u':
- if(mode != UNIQ)
- {
- fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
- return 1;
- }
- mode = NO_REPEAT;
- break;
- case ':':
- fprintf(stderr, "%s: error: Option '-%c' requires an operand\n", argv0, optopt);
- return 1;
- case '?':
- fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
- return 1;
- default:
- fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- assert(errno == 0);
- FILE *input = stdin;
- FILE *output = stdout;
- switch(argc)
- {
- case 0:
- break;
- case 1:
- input = fopen(argv[0], "r");
- if(input == NULL)
- {
- fprintf(
- stderr, "uniq: error: Failed opening input file '%s': %s\n", argv[0], strerror(errno));
- return 1;
- }
- break;
- case 2:
- input = fopen(argv[0], "r");
- if(input == NULL)
- {
- fprintf(
- stderr, "uniq: error: Failed opening input file '%s': %s\n", argv[0], strerror(errno));
- return 1;
- }
- output = fopen(argv[1], "w");
- if(output == NULL)
- {
- fprintf(
- stderr, "uniq: error: Failed opening output file '%s': %s\n", argv[1], strerror(errno));
- return 1;
- }
- break;
- default:
- fprintf(stderr, "uniq: error: Invalid number of arguments (%d), expected [0..2]\n", argc);
- return 1;
- }
- assert(errno == 0);
- char *first = NULL;
- ssize_t first_len = 0;
- size_t first_shift = 0;
- unsigned counter = 1;
- errno = 0;
- while(true)
- {
- assert(errno == 0);
- char *cur = NULL;
- size_t cur_size = 0;
- ssize_t cur_len = getline(&cur, &cur_size, input);
- size_t cur_shift = shift;
- if(cur_len > 0 && cur[cur_len - 1] == '\n')
- {
- cur[cur_len - 1] = 0;
- cur_len--;
- }
- if(field != 0)
- {
- ssize_t field_shift = 0;
- for(unsigned long i = 0; i < field; i++)
- {
- while(field_shift < cur_len && isblank(cur[field_shift]))
- field_shift++;
- while(field_shift < cur_len && !isblank(cur[field_shift]))
- field_shift++;
- }
- cur_shift += field_shift;
- }
- if(cur_shift > cur_len)
- {
- free(cur);
- cur_size = 0;
- cur = NULL;
- break;
- }
- //fprintf(stderr, "[debug] {cur_shift:%d} <%s>\n", cur_shift, cur+cur_shift);
- if(first != NULL)
- {
- if(cur != NULL && (cur_len - cur_shift == first_len - first_shift) &&
- strncmp(cur + cur_shift, first + first_shift, cur_len - cur_shift) == 0)
- {
- counter += 1;
- }
- else
- {
- switch(mode)
- {
- case UNIQ:
- fwrite(first, first_len, 1, output);
- fprintf(output, "\n");
- break;
- case ONLY_REPEAT:
- if(counter > 1)
- {
- fwrite(first, first_len, 1, output);
- fprintf(output, "\n");
- }
- break;
- case NO_REPEAT:
- if(counter == 1)
- {
- fwrite(first, first_len, 1, output);
- fprintf(output, "\n");
- }
- break;
- case COUNT:
- fprintf(output, "%d %s\n", counter, first);
- break;
- }
- counter = 1;
- free(first);
- }
- }
- if(cur_len < 0)
- {
- if(cur_size > 0) free(cur);
- break;
- }
- if(counter == 1)
- {
- first = cur;
- first_len = cur_len;
- first_shift = cur_shift;
- }
- }
- if(errno != 0)
- {
- fprintf(stderr, "uniq: error: Failed reading: %s\n", strerror(errno));
- return 1;
- }
- return 0;
- }