1 /* vi: set sw=4 ts=4: */
3 * uniq implementation for busybox
5 * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 //config: bool "uniq (5.1 kb)"
13 //config: uniq is used to remove duplicate lines from a sorted file.
15 //applet:IF_UNIQ(APPLET(uniq, BB_DIR_USR_BIN, BB_SUID_DROP))
17 //kbuild:lib-$(CONFIG_UNIQ) += uniq.o
19 /* BB_AUDIT SUSv3 compliant */
20 /* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
22 //usage:#define uniq_trivial_usage
23 //usage: "[-cduiz] [-f,s,w N] [FILE [OUTFILE]]"
24 //usage:#define uniq_full_usage "\n\n"
25 //usage: "Discard duplicate lines\n"
26 //usage: "\n -c Prefix lines by the number of occurrences"
27 //usage: "\n -d Only print duplicate lines"
28 //usage: "\n -u Only print unique lines"
29 //usage: "\n -i Ignore case"
30 //usage: "\n -z NUL terminated output"
31 //usage: "\n -f N Skip first N fields"
32 //usage: "\n -s N Skip first N chars (after any skipped fields)"
33 //usage: "\n -w N Compare N characters in line"
35 //usage:#define uniq_example_usage
36 //usage: "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n"
43 int uniq_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
44 int uniq_main(int argc UNUSED_PARAM
, char **argv
)
46 const char *input_filename
;
47 unsigned skip_fields
, skip_chars
, max_chars
;
51 const char *cur_compare
;
55 OPT_d
= 1 << 1, /* print only dups */
56 OPT_u
= 1 << 2, /* print only uniq */
64 skip_fields
= skip_chars
= 0;
67 opt
= getopt32(argv
, "cduf:+s:+w:+iz", &skip_fields
, &skip_chars
, &max_chars
);
70 input_filename
= argv
[0];
74 if (input_filename
[0] != '-' || input_filename
[1]) {
75 close(STDIN_FILENO
); /* == 0 */
76 xopen(input_filename
, O_RDONLY
); /* fd will be 0 */
82 if (output
[0] != '-' || output
[1]) {
83 // Won't work with "uniq - FILE" and closed stdin:
84 //close(STDOUT_FILENO);
85 //xopen(output, O_WRONLY | O_CREAT | O_TRUNC);
86 xmove_fd(xopen(output
, O_WRONLY
| O_CREAT
| O_TRUNC
), STDOUT_FILENO
);
91 cur_compare
= cur_line
= NULL
; /* prime the pump */
92 eol
= (opt
& OPT_z
) ? 0 : '\n';
98 const char *old_compare
;
101 old_compare
= cur_compare
;
104 /* gnu uniq ignores newlines */
105 while ((cur_line
= xmalloc_fgetline(stdin
)) != NULL
) {
106 cur_compare
= cur_line
;
107 for (i
= skip_fields
; i
; i
--) {
108 cur_compare
= skip_whitespace(cur_compare
);
109 cur_compare
= skip_non_whitespace(cur_compare
);
111 for (i
= skip_chars
; *cur_compare
&& i
; i
--) {
118 ? strncasecmp(old_compare
, cur_compare
, max_chars
)
119 : strncmp(old_compare
, cur_compare
, max_chars
)
125 ++dups
; /* testing for overflow seems excessive */
129 if (!(opt
& (OPT_d
<< !!dups
))) { /* (if dups, opt & OPT_u) */
131 /* %7lu matches GNU coreutils 6.9 */
132 printf("%7lu ", dups
+ 1);
134 printf("%s%c", old_line
, eol
);
140 die_if_ferror(stdin
, input_filename
);
142 fflush_stdout_and_exit_SUCCESS();