1: /* uniq -- remove duplicate lines from a sorted file
2: Copyright (C) 86, 91, 1995-1998, 1999, 2012 Free Software Foundation, Inc.
3:
4: This program is free software: you can redistribute it and/or modify
5: it under the terms of the GNU General Public License as published by
6: the Free Software Foundation; either version 3 of the License, or
7: (at your option) any later version.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License
15: along with this program. If not, see <https://www.gnu.org/licenses/>. */
16:
17: /* Written by Richard Stallman and David MacKenzie. */
18: /* 2000-03-22 Trimmed down to the case of "uniq -u" by Bruno Haible. */
19:
20: #include <stddef.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: /* The name this program was run with. */
26: static char *program_name;
27:
28: static void
29: xalloc_fail (void)
30: {
31: fprintf (stderr, "%s: virtual memory exhausted\n", program_name);
32: exit (1);
33: }
34:
35: /* Allocate N bytes of memory dynamically, with error checking. */
36:
37: void *
38: xmalloc (size_t n)
39: {
40: void *p;
41:
42: p = malloc (n);
43: if (p == 0)
44: xalloc_fail ();
45: return p;
46: }
47:
48: /* Change the size of an allocated block of memory P to N bytes,
49: with error checking.
50: If P is NULL, run xmalloc. */
51:
52: void *
53: xrealloc (void *p, size_t n)
54: {
55: p = realloc (p, n);
56: if (p == 0)
57: xalloc_fail ();
58: return p;
59: }
60:
61: /* A 'struct linebuffer' holds a line of text. */
62:
63: struct linebuffer
64: {
65: size_t size; /* Allocated. */
66: size_t length; /* Used. */
67: char *buffer;
68: };
69:
70: /* Initialize linebuffer LINEBUFFER for use. */
71:
72: static void
73: initbuffer (struct linebuffer *linebuffer)
74: {
75: linebuffer->length = 0;
76: linebuffer->size = 200;
77: linebuffer->buffer = (char *) xmalloc (linebuffer->size);
78: }
79:
80: /* Read an arbitrarily long line of text from STREAM into LINEBUFFER.
81: Keep the newline; append a newline if it's the last line of a file
82: that ends in a non-newline character. Do not null terminate.
83: Return LINEBUFFER, except at end of file return 0. */
84:
85: static struct linebuffer *
86: readline (struct linebuffer *linebuffer, FILE *stream)
87: {
88: int c;
89: char *buffer = linebuffer->buffer;
90: char *p = linebuffer->buffer;
91: char *end = buffer + linebuffer->size - 1; /* Sentinel. */
92:
93: if (feof (stream) || ferror (stream))
94: return 0;
95:
96: do
97: {
98: c = getc (stream);
99: if (c == EOF)
100: {
101: if (p == buffer)
102: return 0;
103: if (p[-1] == '\n')
104: break;
105: c = '\n';
106: }
107: if (p == end)
108: {
109: linebuffer->size *= 2;
110: buffer = (char *) xrealloc (buffer, linebuffer->size);
111: p = p - linebuffer->buffer + buffer;
112: linebuffer->buffer = buffer;
113: end = buffer + linebuffer->size - 1;
114: }
115: *p++ = c;
116: }
117: while (c != '\n');
118:
119: linebuffer->length = p - buffer;
120: return linebuffer;
121: }
122:
123: /* Free linebuffer LINEBUFFER's data. */
124:
125: static void
126: freebuffer (struct linebuffer *linebuffer)
127: {
128: free (linebuffer->buffer);
129: }
130:
131: /* Undefine, to avoid warning about redefinition on some systems. */
132: #undef min
133: #define min(x, y) ((x) < (y) ? (x) : (y))
134:
135: /* Return zero if two strings OLD and NEW match, nonzero if not.
136: OLD and NEW point not to the beginnings of the lines
137: but rather to the beginnings of the fields to compare.
138: OLDLEN and NEWLEN are their lengths. */
139:
140: static int
141: different (const char *old, const char *new, size_t oldlen, size_t newlen)
142: {
143: int order;
144:
145: order = memcmp (old, new, min (oldlen, newlen));
146:
147: if (order == 0)
148: return oldlen - newlen;
149: return order;
150: }
151:
152: /* Output the line in linebuffer LINE to stream STREAM
153: provided that the switches say it should be output.
154: If requested, print the number of times it occurred, as well;
155: LINECOUNT + 1 is the number of times that the line occurred. */
156:
157: static void
158: writeline (const struct linebuffer *line, FILE *stream, int linecount)
159: {
160: if (linecount == 0)
161: fwrite (line->buffer, 1, line->length, stream);
162: }
163:
164: /* Process input file INFILE with output to OUTFILE.
165: If either is "-", use the standard I/O stream for it instead. */
166:
167: static void
168: check_file (const char *infile, const char *outfile)
169: {
170: FILE *istream;
171: FILE *ostream;
172: struct linebuffer lb1, lb2;
173: struct linebuffer *thisline, *prevline, *exch;
174: char *prevfield, *thisfield;
175: size_t prevlen, thislen;
176: int match_count = 0;
177:
178: if (!strcmp (infile, "-"))
179: istream = stdin;
180: else
181: istream = fopen (infile, "r");
182: if (istream == NULL)
183: {
184: fprintf (stderr, "%s: error opening %s\n", program_name, infile);
185: exit (1);
186: }
187:
188: if (!strcmp (outfile, "-"))
189: ostream = stdout;
190: else
191: ostream = fopen (outfile, "w");
192: if (ostream == NULL)
193: {
194: fprintf (stderr, "%s: error opening %s\n", program_name, outfile);
195: exit (1);
196: }
197:
198: thisline = &lb1;
199: prevline = &lb2;
200:
201: initbuffer (thisline);
202: initbuffer (prevline);
203:
204: if (readline (prevline, istream) == 0)
205: goto closefiles;
206: prevfield = prevline->buffer;
207: prevlen = prevline->length;
208:
209: while (!feof (istream))
210: {
211: int match;
212: if (readline (thisline, istream) == 0)
213: break;
214: thisfield = thisline->buffer;
215: thislen = thisline->length;
216: match = !different (thisfield, prevfield, thislen, prevlen);
217:
218: if (match)
219: ++match_count;
220:
221: if (!match)
222: {
223: writeline (prevline, ostream, match_count);
224: exch = prevline;
225: prevline = thisline;
226: thisline = exch;
227: prevfield = thisfield;
228: prevlen = thislen;
229: if (!match)
230: match_count = 0;
231: }
232: }
233:
234: writeline (prevline, ostream, match_count);
235:
236: closefiles:
237: if (ferror (istream) || fclose (istream) == EOF)
238: {
239: fprintf (stderr, "%s: error reading %s\n", program_name, infile);
240: exit (1);
241: }
242:
243: if (ferror (ostream) || fclose (ostream) == EOF)
244: {
245: fprintf (stderr, "%s: error writing %s\n", program_name, outfile);
246: exit (1);
247: }
248:
249: freebuffer (&lb1);
250: freebuffer (&lb2);
251: }
252:
253: int
254: main (int argc, char **argv)
255: {
256: const char *infile = "-";
257: const char *outfile = "-";
258: int optind = 1;
259:
260: program_name = argv[0];
261:
262: if (optind < argc)
263: infile = argv[optind++];
264:
265: if (optind < argc)
266: outfile = argv[optind++];
267:
268: if (optind < argc)
269: {
270: fprintf (stderr, "%s: too many arguments\n", program_name);
271: exit (1);
272: }
273:
274: check_file (infile, outfile);
275:
276: exit (0);
277: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>