From 8cf1635366eed1e760b317a401c0a4748ee4befe Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sun, 23 Aug 2020 18:10:13 +0200 Subject: Added uniq (most options not handled) --- src/uniq.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/uniq.c (limited to 'src/uniq.c') diff --git a/src/uniq.c b/src/uniq.c new file mode 100644 index 0000000..a769b99 --- /dev/null +++ b/src/uniq.c @@ -0,0 +1,135 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include + +static char *g_name = "uniq"; + +void fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + fputc('\n', stderr); + va_end(ap); + exit(EXIT_FAILURE); +} + +char *xstrdup(const char *s) +{ + char *ret = strdup(s); + if (ret == NULL) + fatal("%s", strerror(errno)); + return ret; +} + +long parse_ulong(char *str, char *error_msg) +{ + char *end; + long ret; + + errno = 0; + ret = strtol(str, &end, 10); + if (errno != 0 || ret < 0 || *end != '\0') + fatal("%s: %s", optarg, error_msg); + return ret; +} + +enum flags +{ + FLAG_COUNT = 1 << 0, + FLAG_REPEATED = 1 << 1, + FLAG_REPEATED_ALL = 1 << 2, + FLAG_IGNORE_CASE = 1 << 3, + FLAG_UNIQUE_ONLY = 1 << 4, +}; + +bool line_equal(char *line1, char *line2, enum flags flags) +{ + if (flags & FLAG_IGNORE_CASE) + return (strcasecmp(line1, line2) == 0); + return (strcmp(line1, line2) == 0); +} + +void line_print(char *line, enum flags flags, FILE *output, int counter) +{ + if (flags & FLAG_COUNT) + fprintf(output, "%7u ", counter); + fputs(line, output); +} + +int main(int argc, char **argv) +{ + int option; + enum flags flags = 0; + long skip_fields = -1; + long skip_chars = -1; + long width = -1; + char delimiter = '\n'; + + while ((option = getopt(argc, argv, "cdDf:is:uzw:")) != -1) + { + switch (option) + { + case 'c': flags |= FLAG_COUNT; break; + case 'd': flags |= FLAG_REPEATED; break; + case 'D': flags |= FLAG_REPEATED_ALL; break; + case 'i': flags |= FLAG_IGNORE_CASE; break; + case 'u': flags |= FLAG_UNIQUE_ONLY; break; + case 'z': delimiter = '\0'; break; + + case 'f': skip_fields = parse_ulong(optarg, "invalid number of fields to skip"); break; + case 's': skip_chars = parse_ulong(optarg, "invalid number of bytes to skip"); break; + case 'w': width = parse_ulong(optarg, "invalid number of bytes to compare"); break; + } + } + + FILE *input = stdin; + FILE *output = stdout; + + if (argc - optind > 2) + fatal("extra operand '%s'", argv[optind + 2]); + if (optind < argc) + { + input = fopen(argv[optind], "r"); + if (input == NULL) + fatal("%s: %s", argv[optind], strerror(errno)); + if (optind + 1 < argc) + { + output = fopen(argv[optind + 1], "w"); + if (output == NULL) + fatal("%s: %s", argv[optind + 1], strerror(errno)); + } + } + + char *line_prev = NULL; + char *line = NULL; + size_t line_size = 0; + size_t counter = 1; + + if (getdelim(&line_prev, &line_size, delimiter, input) == -1) + return EXIT_SUCCESS; + while (getdelim(&line, &line_size, delimiter, input) != -1) + { + if (line_equal(line, line_prev, flags)) + { + counter++; + } + else + { + line_print(line_prev, flags, output, counter); + counter = 1; + free(line_prev); + line_prev = xstrdup(line); + } + } + line_print(line_prev, flags, output, counter); + return EXIT_SUCCESS; +} -- cgit