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. */
25 /* The name this program was run with. */
26 static char *program_name
;
31 fprintf (stderr
, "%s: virtual memory exhausted\n", program_name
);
35 /* Allocate N bytes of memory dynamically, with error checking. */
48 /* Change the size of an allocated block of memory P to N bytes,
50 If P is NULL, run xmalloc. */
53 xrealloc (void *p
, size_t n
)
61 /* A 'struct linebuffer' holds a line of text. */
65 size_t size
; /* Allocated. */
66 size_t length
; /* Used. */
70 /* Initialize linebuffer LINEBUFFER for use. */
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
)
89 char *buffer
= linebuffer
->buffer
;
90 char *p
= linebuffer
->buffer
;
91 char *end
= buffer
+ linebuffer
->size
- 1; /* Sentinel. */
93 if (feof (stream
) || ferror (stream
))
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;
119 linebuffer
->length
= p
- buffer
;
123 /* Free linebuffer LINEBUFFER's data. */
126 freebuffer (struct linebuffer
*linebuffer
)
128 free (linebuffer
->buffer
);
131 /* Undefine, to avoid warning about redefinition on some systems. */
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. */
141 different (const char *old
, const char *new, size_t oldlen
, size_t newlen
)
145 order
= memcmp (old
, new, min (oldlen
, newlen
));
148 return oldlen
- newlen
;
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. */
158 writeline (const struct linebuffer
*line
, FILE *stream
, int linecount
)
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. */
168 check_file (const char *infile
, const char *outfile
)
172 struct linebuffer lb1
, lb2
;
173 struct linebuffer
*thisline
, *prevline
, *exch
;
174 char *prevfield
, *thisfield
;
175 size_t prevlen
, thislen
;
178 if (!strcmp (infile
, "-"))
181 istream
= fopen (infile
, "r");
184 fprintf (stderr
, "%s: error opening %s\n", program_name
, infile
);
188 if (!strcmp (outfile
, "-"))
191 ostream
= fopen (outfile
, "w");
194 fprintf (stderr
, "%s: error opening %s\n", program_name
, outfile
);
201 initbuffer (thisline
);
202 initbuffer (prevline
);
204 if (readline (prevline
, istream
) == 0)
206 prevfield
= prevline
->buffer
;
207 prevlen
= prevline
->length
;
209 while (!feof (istream
))
212 if (readline (thisline
, istream
) == 0)
214 thisfield
= thisline
->buffer
;
215 thislen
= thisline
->length
;
216 match
= !different (thisfield
, prevfield
, thislen
, prevlen
);
223 writeline (prevline
, ostream
, match_count
);
227 prevfield
= thisfield
;
234 writeline (prevline
, ostream
, match_count
);
237 if (ferror (istream
) || fclose (istream
) == EOF
)
239 fprintf (stderr
, "%s: error reading %s\n", program_name
, infile
);
243 if (ferror (ostream
) || fclose (ostream
) == EOF
)
245 fprintf (stderr
, "%s: error writing %s\n", program_name
, outfile
);
254 main (int argc
, char **argv
)
256 const char *infile
= "-";
257 const char *outfile
= "-";
260 program_name
= argv
[0];
263 infile
= argv
[optind
++];
266 outfile
= argv
[optind
++];
270 fprintf (stderr
, "%s: too many arguments\n", program_name
);
274 check_file (infile
, outfile
);