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
15static const char uniq_opts[] ALIGN1 = "cdu" "f:s:" "cdu\0\1\2\4";
16
17static 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
30int uniq_main(int argc, char **argv);
31int uniq_main(int argc, 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}
104