Update after gnulib changed.
[libiconv.git] / tests / uniq-u.c
blob6fae8c08912737508ad1128b1dc74bc5560ab7bf
1 /* uniq -- remove duplicate lines from a sorted file
2 Copyright (C) 86, 91, 1995-1998, 1999, 2012 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Richard Stallman and David MacKenzie. */
18 /* 2000-03-22 Trimmed down to the case of "uniq -u" by Bruno Haible. */
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 /* The name this program was run with. */
26 static char *program_name;
28 static void
29 xalloc_fail (void)
31 fprintf (stderr, "%s: virtual memory exhausted\n", program_name);
32 exit (1);
35 /* Allocate N bytes of memory dynamically, with error checking. */
37 void *
38 xmalloc (size_t n)
40 void *p;
42 p = malloc (n);
43 if (p == 0)
44 xalloc_fail ();
45 return p;
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. */
52 void *
53 xrealloc (void *p, size_t n)
55 p = realloc (p, n);
56 if (p == 0)
57 xalloc_fail ();
58 return p;
61 /* A 'struct linebuffer' holds a line of text. */
63 struct linebuffer
65 size_t size; /* Allocated. */
66 size_t length; /* Used. */
67 char *buffer;
70 /* Initialize linebuffer LINEBUFFER for use. */
72 static void
73 initbuffer (struct linebuffer *linebuffer)
75 linebuffer->length = 0;
76 linebuffer->size = 200;
77 linebuffer->buffer = (char *) xmalloc (linebuffer->size);
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. */
85 static struct linebuffer *
86 readline (struct linebuffer *linebuffer, FILE *stream)
88 int c;
89 char *buffer = linebuffer->buffer;
90 char *p = linebuffer->buffer;
91 char *end = buffer + linebuffer->size - 1; /* Sentinel. */
93 if (feof (stream) || ferror (stream))
94 return 0;
98 c = getc (stream);
99 if (c == EOF)
101 if (p == buffer)
102 return 0;
103 if (p[-1] == '\n')
104 break;
105 c = '\n';
107 if (p == end)
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;
115 *p++ = c;
117 while (c != '\n');
119 linebuffer->length = p - buffer;
120 return linebuffer;
123 /* Free linebuffer LINEBUFFER's data. */
125 static void
126 freebuffer (struct linebuffer *linebuffer)
128 free (linebuffer->buffer);
131 /* Undefine, to avoid warning about redefinition on some systems. */
132 #undef min
133 #define min(x, y) ((x) < (y) ? (x) : (y))
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. */
140 static int
141 different (const char *old, const char *new, size_t oldlen, size_t newlen)
143 int order;
145 order = memcmp (old, new, min (oldlen, newlen));
147 if (order == 0)
148 return oldlen - newlen;
149 return order;
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. */
157 static void
158 writeline (const struct linebuffer *line, FILE *stream, int linecount)
160 if (linecount == 0)
161 fwrite (line->buffer, 1, line->length, stream);
164 /* Process input file INFILE with output to OUTFILE.
165 If either is "-", use the standard I/O stream for it instead. */
167 static void
168 check_file (const char *infile, const char *outfile)
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;
178 if (!strcmp (infile, "-"))
179 istream = stdin;
180 else
181 istream = fopen (infile, "r");
182 if (istream == NULL)
184 fprintf (stderr, "%s: error opening %s\n", program_name, infile);
185 exit (1);
188 if (!strcmp (outfile, "-"))
189 ostream = stdout;
190 else
191 ostream = fopen (outfile, "w");
192 if (ostream == NULL)
194 fprintf (stderr, "%s: error opening %s\n", program_name, outfile);
195 exit (1);
198 thisline = &lb1;
199 prevline = &lb2;
201 initbuffer (thisline);
202 initbuffer (prevline);
204 if (readline (prevline, istream) == 0)
205 goto closefiles;
206 prevfield = prevline->buffer;
207 prevlen = prevline->length;
209 while (!feof (istream))
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);
218 if (match)
219 ++match_count;
221 if (!match)
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;
234 writeline (prevline, ostream, match_count);
236 closefiles:
237 if (ferror (istream) || fclose (istream) == EOF)
239 fprintf (stderr, "%s: error reading %s\n", program_name, infile);
240 exit (1);
243 if (ferror (ostream) || fclose (ostream) == EOF)
245 fprintf (stderr, "%s: error writing %s\n", program_name, outfile);
246 exit (1);
249 freebuffer (&lb1);
250 freebuffer (&lb2);
254 main (int argc, char **argv)
256 const char *infile = "-";
257 const char *outfile = "-";
258 int optind = 1;
260 program_name = argv[0];
262 if (optind < argc)
263 infile = argv[optind++];
265 if (optind < argc)
266 outfile = argv[optind++];
268 if (optind < argc)
270 fprintf (stderr, "%s: too many arguments\n", program_name);
271 exit (1);
274 check_file (infile, outfile);
276 exit (0);