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