1/* uniq -- remove duplicate lines from a sorted file 2 Copyright (C) 86, 91, 1995-1998, 1999 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 <http://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. */ 26static char *program_name; 27 28static void 29xalloc_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 37void * 38xmalloc (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 52void * 53xrealloc (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 63struct linebuffer 64{ 65 size_t size; /* Allocated. */ 66 size_t length; /* Used. */ 67 char *buffer; 68}; 69 70/* Initialize linebuffer LINEBUFFER for use. */ 71 72static void 73initbuffer (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 85static struct linebuffer * 86readline (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 125static void 126freebuffer (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 140static int 141different (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 157static void 158writeline (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 167static void 168check_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 253int 254main (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} 278