Initial public busybox upstream commit
[busybox4maemo] / coreutils / uniq.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * uniq implementation for busybox
4  *
5  * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8  */
9
10 /* BB_AUDIT SUSv3 compliant */
11 /* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
12
13 #include "libbb.h"
14
15 static const char uniq_opts[] ALIGN1 = "cdu" "f:s:" "cdu\0\1\2\4";
16
17 static FILE *xgetoptfile_uniq_s(char **argv, int read0write2)
18 {
19         const char *n;
20
21         n = *argv;
22         if (n != NULL) {
23                 if ((*n != '-') || n[1]) {
24                         return xfopen(n, "r\0w" + read0write2);
25                 }
26         }
27         return (read0write2) ? stdout : stdin;
28 }
29
30 int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
31 int uniq_main(int argc ATTRIBUTE_UNUSED, char **argv)
32 {
33         FILE *in, *out;
34         unsigned long dups, skip_fields, skip_chars, i;
35         const char *s0, *e0, *s1, *e1, *input_filename;
36         unsigned opt;
37
38         enum {
39                 OPT_c = 0x1,
40                 OPT_d = 0x2,
41                 OPT_u = 0x4,
42                 OPT_f = 0x8,
43                 OPT_s = 0x10,
44         };
45
46         skip_fields = skip_chars = 0;
47
48         opt = getopt32(argv, "cduf:s:", &s0, &s1);
49         if (opt & OPT_f)
50                 skip_fields = xatoul(s0);
51         if (opt & OPT_s)
52                 skip_chars = xatoul(s1);
53         argv += optind;
54
55         input_filename = *argv;
56
57         in = xgetoptfile_uniq_s(argv, 0);
58         if (*argv) {
59                 ++argv;
60         }
61         out = xgetoptfile_uniq_s(argv, 2);
62         if (*argv && argv[1]) {
63                 bb_show_usage();
64         }
65
66         s1 = e1 = NULL;                         /* prime the pump */
67
68         do {
69                 s0 = s1;
70                 e0 = e1;
71                 dups = 0;
72
73                 /* gnu uniq ignores newlines */
74                 while ((s1 = xmalloc_getline(in)) != NULL) {
75                         e1 = s1;
76                         for (i = skip_fields; i; i--) {
77                                 e1 = skip_whitespace(e1);
78                                 e1 = skip_non_whitespace(e1);
79                         }
80                         for (i = skip_chars; *e1 && i; i--) {
81                                 ++e1;
82                         }
83
84                         if (!s0 || strcmp(e0, e1)) {
85                                 break;
86                         }
87
88                         ++dups;          /* Note: Testing for overflow seems excessive. */
89                 }
90
91                 if (s0) {
92                         if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_e) */
93                                 fprintf(out, "\0%d " + (opt & 1), dups + 1);
94                                 fprintf(out, "%s\n", s0);
95                         }
96                         free((void *)s0);
97                 }
98         } while (s1);
99
100         die_if_ferror(in, input_filename);
101
102         fflush_stdout_and_exit(EXIT_SUCCESS);
103 }