aboutsummaryrefslogtreecommitdiff
path: root/src/uniq.c
diff options
context:
space:
mode:
authorCharles Cabergs <me@cacharle.xyz>2020-08-23 18:10:13 +0200
committerCharles Cabergs <me@cacharle.xyz>2020-08-23 18:10:13 +0200
commit8cf1635366eed1e760b317a401c0a4748ee4befe (patch)
tree1d0b1d1c1268d061c8f3106be2e212dfb34baa6c /src/uniq.c
parent65c411dcb3fd33122cbc314ca5b2a94542bc1e87 (diff)
downloadcoreutils-8cf1635366eed1e760b317a401c0a4748ee4befe.tar.gz
coreutils-8cf1635366eed1e760b317a401c0a4748ee4befe.tar.bz2
coreutils-8cf1635366eed1e760b317a401c0a4748ee4befe.zip
Added uniq (most options not handled)
Diffstat (limited to 'src/uniq.c')
-rw-r--r--src/uniq.c135
1 files changed, 135 insertions, 0 deletions
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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+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;
+}