xref: /aosp_15_r20/external/toybox/toys/posix/uniq.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* uniq.c - report or filter out repeated lines in a file
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2012 Georgi Chorbadzhiyski <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker  *
5*cf5a6c84SAndroid Build Coastguard Worker  * See http://opengroup.org/onlinepubs/9699919799/utilities/uniq.html
6*cf5a6c84SAndroid Build Coastguard Worker 
7*cf5a6c84SAndroid Build Coastguard Worker USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
8*cf5a6c84SAndroid Build Coastguard Worker 
9*cf5a6c84SAndroid Build Coastguard Worker config UNIQ
10*cf5a6c84SAndroid Build Coastguard Worker   bool "uniq"
11*cf5a6c84SAndroid Build Coastguard Worker   default y
12*cf5a6c84SAndroid Build Coastguard Worker   help
13*cf5a6c84SAndroid Build Coastguard Worker     usage: uniq [-cduiz] [-w MAXCHARS] [-f FIELDS] [-s CHAR] [INFILE [OUTFILE]]
14*cf5a6c84SAndroid Build Coastguard Worker 
15*cf5a6c84SAndroid Build Coastguard Worker     Report or filter out repeated lines in a file
16*cf5a6c84SAndroid Build Coastguard Worker 
17*cf5a6c84SAndroid Build Coastguard Worker     -c	Show counts before each line
18*cf5a6c84SAndroid Build Coastguard Worker     -d	Show only lines that are repeated
19*cf5a6c84SAndroid Build Coastguard Worker     -u	Show only lines that are unique
20*cf5a6c84SAndroid Build Coastguard Worker     -i	Ignore case when comparing lines
21*cf5a6c84SAndroid Build Coastguard Worker     -z	Lines end with \0 not \n
22*cf5a6c84SAndroid Build Coastguard Worker     -w	Compare maximum X chars per line
23*cf5a6c84SAndroid Build Coastguard Worker     -f	Ignore first X fields
24*cf5a6c84SAndroid Build Coastguard Worker     -s	Ignore first X chars
25*cf5a6c84SAndroid Build Coastguard Worker */
26*cf5a6c84SAndroid Build Coastguard Worker 
27*cf5a6c84SAndroid Build Coastguard Worker #define FOR_uniq
28*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
29*cf5a6c84SAndroid Build Coastguard Worker 
GLOBALS(long w,s,f;long repeats;)30*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
31*cf5a6c84SAndroid Build Coastguard Worker   long w, s, f;
32*cf5a6c84SAndroid Build Coastguard Worker 
33*cf5a6c84SAndroid Build Coastguard Worker   long repeats;
34*cf5a6c84SAndroid Build Coastguard Worker )
35*cf5a6c84SAndroid Build Coastguard Worker 
36*cf5a6c84SAndroid Build Coastguard Worker static char *skip(char *str)
37*cf5a6c84SAndroid Build Coastguard Worker {
38*cf5a6c84SAndroid Build Coastguard Worker   long nchars = TT.s, nfields = TT.f;
39*cf5a6c84SAndroid Build Coastguard Worker 
40*cf5a6c84SAndroid Build Coastguard Worker   // Skip fields first
41*cf5a6c84SAndroid Build Coastguard Worker   while (nfields--) {
42*cf5a6c84SAndroid Build Coastguard Worker     while (*str && isspace(*str)) str++;
43*cf5a6c84SAndroid Build Coastguard Worker     while (*str && !isspace(*str)) str++;
44*cf5a6c84SAndroid Build Coastguard Worker   }
45*cf5a6c84SAndroid Build Coastguard Worker   // Skip chars
46*cf5a6c84SAndroid Build Coastguard Worker   while (*str && nchars--) str++;
47*cf5a6c84SAndroid Build Coastguard Worker 
48*cf5a6c84SAndroid Build Coastguard Worker   return str;
49*cf5a6c84SAndroid Build Coastguard Worker }
50*cf5a6c84SAndroid Build Coastguard Worker 
print_line(FILE * f,char * line)51*cf5a6c84SAndroid Build Coastguard Worker static void print_line(FILE *f, char *line)
52*cf5a6c84SAndroid Build Coastguard Worker {
53*cf5a6c84SAndroid Build Coastguard Worker   if (TT.repeats ? FLAG(u) : FLAG(d)) return;
54*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(c)) fprintf(f, "%7lu ", TT.repeats + 1);
55*cf5a6c84SAndroid Build Coastguard Worker   fputs(line, f);
56*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(z)) fputc(0, f);
57*cf5a6c84SAndroid Build Coastguard Worker }
58*cf5a6c84SAndroid Build Coastguard Worker 
uniq_main(void)59*cf5a6c84SAndroid Build Coastguard Worker void uniq_main(void)
60*cf5a6c84SAndroid Build Coastguard Worker {
61*cf5a6c84SAndroid Build Coastguard Worker   FILE *infile = stdin, *outfile = stdout;
62*cf5a6c84SAndroid Build Coastguard Worker   char *thisline = 0, *prevline = 0, *tmpline, eol = '\n';
63*cf5a6c84SAndroid Build Coastguard Worker   size_t thissize, prevsize = 0, tmpsize;
64*cf5a6c84SAndroid Build Coastguard Worker 
65*cf5a6c84SAndroid Build Coastguard Worker   if (toys.optc >= 1) infile = xfopen(toys.optargs[0], "r");
66*cf5a6c84SAndroid Build Coastguard Worker   if (toys.optc >= 2) outfile = xfopen(toys.optargs[1], "w");
67*cf5a6c84SAndroid Build Coastguard Worker 
68*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(z)) eol = 0;
69*cf5a6c84SAndroid Build Coastguard Worker 
70*cf5a6c84SAndroid Build Coastguard Worker   // If first line can't be read
71*cf5a6c84SAndroid Build Coastguard Worker   if (getdelim(&prevline, &prevsize, eol, infile) < 0) return;
72*cf5a6c84SAndroid Build Coastguard Worker 
73*cf5a6c84SAndroid Build Coastguard Worker   while (getdelim(&thisline, &thissize, eol, infile) > 0) {
74*cf5a6c84SAndroid Build Coastguard Worker     int diff;
75*cf5a6c84SAndroid Build Coastguard Worker     char *t1, *t2;
76*cf5a6c84SAndroid Build Coastguard Worker 
77*cf5a6c84SAndroid Build Coastguard Worker     // If requested get the chosen fields + character offsets.
78*cf5a6c84SAndroid Build Coastguard Worker     if (TT.f || TT.s) {
79*cf5a6c84SAndroid Build Coastguard Worker       t1 = skip(thisline);
80*cf5a6c84SAndroid Build Coastguard Worker       t2 = skip(prevline);
81*cf5a6c84SAndroid Build Coastguard Worker     } else {
82*cf5a6c84SAndroid Build Coastguard Worker       t1 = thisline;
83*cf5a6c84SAndroid Build Coastguard Worker       t2 = prevline;
84*cf5a6c84SAndroid Build Coastguard Worker     }
85*cf5a6c84SAndroid Build Coastguard Worker 
86*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.w)
87*cf5a6c84SAndroid Build Coastguard Worker       diff = !FLAG(i) ? strcmp(t1, t2) : strcasecmp(t1, t2);
88*cf5a6c84SAndroid Build Coastguard Worker     else diff = !FLAG(i) ? strncmp(t1, t2, TT.w) : strncasecmp(t1, t2, TT.w);
89*cf5a6c84SAndroid Build Coastguard Worker 
90*cf5a6c84SAndroid Build Coastguard Worker     if (!diff) TT.repeats++;
91*cf5a6c84SAndroid Build Coastguard Worker     else {
92*cf5a6c84SAndroid Build Coastguard Worker       print_line(outfile, prevline);
93*cf5a6c84SAndroid Build Coastguard Worker 
94*cf5a6c84SAndroid Build Coastguard Worker       TT.repeats = 0;
95*cf5a6c84SAndroid Build Coastguard Worker 
96*cf5a6c84SAndroid Build Coastguard Worker       tmpline = prevline;
97*cf5a6c84SAndroid Build Coastguard Worker       prevline = thisline;
98*cf5a6c84SAndroid Build Coastguard Worker       thisline = tmpline;
99*cf5a6c84SAndroid Build Coastguard Worker 
100*cf5a6c84SAndroid Build Coastguard Worker       tmpsize = prevsize;
101*cf5a6c84SAndroid Build Coastguard Worker       prevsize = thissize;
102*cf5a6c84SAndroid Build Coastguard Worker       thissize = tmpsize;
103*cf5a6c84SAndroid Build Coastguard Worker     }
104*cf5a6c84SAndroid Build Coastguard Worker   }
105*cf5a6c84SAndroid Build Coastguard Worker 
106*cf5a6c84SAndroid Build Coastguard Worker   print_line(outfile, prevline);
107*cf5a6c84SAndroid Build Coastguard Worker 
108*cf5a6c84SAndroid Build Coastguard Worker   if (CFG_TOYBOX_FREE) {
109*cf5a6c84SAndroid Build Coastguard Worker     if (outfile != stdout) fclose(outfile);
110*cf5a6c84SAndroid Build Coastguard Worker     if (infile != stdin) fclose(infile);
111*cf5a6c84SAndroid Build Coastguard Worker     free(prevline);
112*cf5a6c84SAndroid Build Coastguard Worker     free(thisline);
113*cf5a6c84SAndroid Build Coastguard Worker   }
114*cf5a6c84SAndroid Build Coastguard Worker }
115