comparison toys/uniq.c @ 546:a095c02dc431

Implement uniq.
author Georgi Chorbadzhiyski <gf@unixsol.org>
date Wed, 14 Mar 2012 22:04:06 -0500
parents
children f1629eb63806
comparison
equal deleted inserted replaced
545:4a91ede70548 546:a095c02dc431
1 /* vi: set sw=4 ts=4:
2 *
3 * uniq.c - report or filter out repeated lines in a file
4 *
5 * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
6 *
7 * See http://www.opengroup.org/onlinepubs/009695399/utilities/uniq.html
8
9 USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_BIN))
10
11 config UNIQ
12 bool "uniq"
13 default y
14 help
15 usage: uniq [-cduiz] [-w maxchars] [-f fields] [-s char] [input_file [output_file]]
16
17 Report or filter out repeated lines in a file
18
19 -c show counts before each line
20 -d show only lines that are repeated
21 -u show only lines that are unique
22 -i ignore case when comparing lines
23 -z lines end with \0 not \n
24 -w compare maximum X chars per line
25 -f ignore first X fields
26 -s ignore first X chars
27 */
28
29 #include "toys.h"
30
31 DEFINE_GLOBALS(
32 long maxchars;
33 long nchars;
34 long nfields;
35 long repeats;
36 )
37
38 #define TT this.uniq
39
40 #define FLAG_z 16
41 #define FLAG_i 8
42 #define FLAG_c 4
43 #define FLAG_d 2
44 #define FLAG_u 1
45
46 static char *skip(char *str)
47 {
48 int field = 0;
49 long nchars = TT.nchars;
50 long nfields = TT.nfields;
51 // Skip fields first
52 while (nfields && *str) {
53 if (isspace((unsigned char)*str)) {
54 if (field) {
55 field = 0;
56 nfields--;
57 }
58 } else if (!field) {
59 field = 1;
60 }
61 str++;
62 }
63 // Skip chars
64 while (nchars-- && *str)
65 str++;
66 return str;
67 }
68
69 static void print_line(FILE *f, char *line)
70 {
71 if (TT.repeats == 0 && (toys.optflags & FLAG_d))
72 return;
73 if (TT.repeats > 0 && (toys.optflags & FLAG_u))
74 return;
75 if ((toys.optflags & FLAG_c)) {
76 fprintf(f, "%7lu %s", TT.repeats + 1, line);
77 } else {
78 fprintf(f, "%s", line);
79 }
80 if (toys.optflags & FLAG_z)
81 fprintf(f, "%c", '\0');
82 }
83
84 void uniq_main(void)
85 {
86 FILE *infile = stdin;
87 FILE *outfile = stdout;
88 char *thisline = NULL;
89 char *prevline = NULL;
90 size_t thissize, prevsize = 0;
91 char *tmpline;
92 char eol = '\n';
93 size_t tmpsize;
94
95 if (toys.optc >= 1)
96 infile = xfopen(toys.optargs[0], "r");
97
98 if (toys.optc >= 2)
99 outfile = xfopen(toys.optargs[1], "w");
100
101 if (toys.optflags & FLAG_z)
102 eol = '\0';
103
104 // If first line can't be read
105 if (getdelim(&prevline, &prevsize, eol, infile) < 0)
106 return;
107
108 while (getdelim(&thisline, &thissize, eol, infile) > 0) {
109 int diff;
110 char *t1, *t2;
111
112 // If requested get the chosen fields + character offsets.
113 if (TT.nfields || TT.nchars) {
114 t1 = skip(thisline);
115 t2 = skip(prevline);
116 } else {
117 t1 = thisline;
118 t2 = prevline;
119 }
120
121 if (TT.maxchars == 0) {
122 diff = !(toys.optflags & FLAG_i)
123 ? strcmp(t1, t2)
124 : strcasecmp(t1, t2);
125 } else {
126 diff = !(toys.optflags & FLAG_i)
127 ? strncmp(t1, t2, TT.maxchars)
128 : strncasecmp(t1, t2, TT.maxchars);
129 }
130
131 if (diff == 0) { // same
132 TT.repeats++;
133 } else {
134 print_line(outfile, prevline);
135
136 TT.repeats = 0;
137
138 tmpline = prevline;
139 prevline = thisline;
140 thisline = tmpline;
141
142 tmpsize = prevsize;
143 prevsize = thissize;
144 thissize = tmpsize;
145 }
146 }
147
148 print_line(outfile, prevline);
149
150 if (CFG_TOYBOX_FREE) {
151 free(prevline);
152 free(thisline);
153 }
154 }