1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Case Larsen. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char copyright[] = 13 "@(#) Copyright (c) 1989, 1993\n\ 14 The Regents of the University of California. All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 05/04/95"; 19 #endif /* not lint */ 20 21 #include <errno.h> 22 #include <stdio.h> 23 #include <ctype.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #define MAXLINELEN (8 * 1024) 29 30 int cflag, dflag, uflag; 31 int numchars, numfields, repeats; 32 33 void err __P((const char *, ...)); 34 FILE *file __P((char *, char *)); 35 void show __P((FILE *, char *)); 36 char *skip __P((char *)); 37 void obsolete __P((char *[])); 38 void usage __P((void)); 39 40 int 41 main (argc, argv) 42 int argc; 43 char *argv[]; 44 { 45 register char *t1, *t2; 46 FILE *ifp, *ofp; 47 int ch; 48 char *prevline, *thisline, *p; 49 50 obsolete(argv); 51 while ((ch = getopt(argc, argv, "-cdf:s:u")) != EOF) 52 switch (ch) { 53 case '-': 54 --optind; 55 goto done; 56 case 'c': 57 cflag = 1; 58 break; 59 case 'd': 60 dflag = 1; 61 break; 62 case 'f': 63 numfields = strtol(optarg, &p, 10); 64 if (numfields < 0 || *p) 65 err("illegal field skip value: %s", optarg); 66 break; 67 case 's': 68 numchars = strtol(optarg, &p, 10); 69 if (numchars < 0 || *p) 70 err("illegal character skip value: %s", optarg); 71 break; 72 case 'u': 73 uflag = 1; 74 break; 75 case '?': 76 default: 77 usage(); 78 } 79 80 done: argc -= optind; 81 argv +=optind; 82 83 /* If no flags are set, default is -d -u. */ 84 if (cflag) { 85 if (dflag || uflag) 86 usage(); 87 } else if (!dflag && !uflag) 88 dflag = uflag = 1; 89 90 switch(argc) { 91 case 0: 92 ifp = stdin; 93 ofp = stdout; 94 break; 95 case 1: 96 ifp = file(argv[0], "r"); 97 ofp = stdout; 98 break; 99 case 2: 100 ifp = file(argv[0], "r"); 101 ofp = file(argv[1], "w"); 102 break; 103 default: 104 usage(); 105 } 106 107 prevline = malloc(MAXLINELEN); 108 thisline = malloc(MAXLINELEN); 109 if (prevline == NULL || thisline == NULL) 110 err("%s", strerror(errno)); 111 112 if (fgets(prevline, MAXLINELEN, ifp) == NULL) 113 exit(0); 114 115 while (fgets(thisline, MAXLINELEN, ifp)) { 116 /* If requested get the chosen fields + character offsets. */ 117 if (numfields || numchars) { 118 t1 = skip(thisline); 119 t2 = skip(prevline); 120 } else { 121 t1 = thisline; 122 t2 = prevline; 123 } 124 125 /* If different, print; set previous to new value. */ 126 if (strcmp(t1, t2)) { 127 show(ofp, prevline); 128 t1 = prevline; 129 prevline = thisline; 130 thisline = t1; 131 repeats = 0; 132 } else 133 ++repeats; 134 } 135 show(ofp, prevline); 136 exit(0); 137 } 138 139 /* 140 * show -- 141 * Output a line depending on the flags and number of repetitions 142 * of the line. 143 */ 144 void 145 show(ofp, str) 146 FILE *ofp; 147 char *str; 148 { 149 150 if (cflag && *str) 151 (void)fprintf(ofp, "%4d %s", repeats + 1, str); 152 if (dflag && repeats || uflag && !repeats) 153 (void)fprintf(ofp, "%s", str); 154 } 155 156 char * 157 skip(str) 158 register char *str; 159 { 160 register int infield, nchars, nfields; 161 162 for (nfields = numfields, infield = 0; nfields && *str; ++str) 163 if (isspace(*str)) { 164 if (infield) { 165 infield = 0; 166 --nfields; 167 } 168 } else if (!infield) 169 infield = 1; 170 for (nchars = numchars; nchars-- && *str; ++str); 171 return(str); 172 } 173 174 FILE * 175 file(name, mode) 176 char *name, *mode; 177 { 178 FILE *fp; 179 180 if ((fp = fopen(name, mode)) == NULL) 181 err("%s: %s", name, strerror(errno)); 182 return(fp); 183 } 184 185 void 186 obsolete(argv) 187 char *argv[]; 188 { 189 int len; 190 char *ap, *p, *start; 191 192 while (ap = *++argv) { 193 /* Return if "--" or not an option of any form. */ 194 if (ap[0] != '-') { 195 if (ap[0] != '+') 196 return; 197 } else if (ap[1] == '-') 198 return; 199 if (!isdigit(ap[1])) 200 continue; 201 /* 202 * Digit signifies an old-style option. Malloc space for dash, 203 * new option and argument. 204 */ 205 len = strlen(ap); 206 if ((start = p = malloc(len + 3)) == NULL) 207 err("%s", strerror(errno)); 208 *p++ = '-'; 209 *p++ = ap[0] == '+' ? 's' : 'f'; 210 (void)strcpy(p, ap + 1); 211 *argv = start; 212 } 213 } 214 215 void 216 usage() 217 { 218 (void)fprintf(stderr, 219 "usage: uniq [-c | -du] [-f fields] [-s chars] [input [output]]\n"); 220 exit(1); 221 } 222 223 #if __STDC__ 224 #include <stdarg.h> 225 #else 226 #include <varargs.h> 227 #endif 228 229 void 230 #if __STDC__ 231 err(const char *fmt, ...) 232 #else 233 err(fmt, va_alist) 234 char *fmt; 235 va_dcl 236 #endif 237 { 238 va_list ap; 239 #if __STDC__ 240 va_start(ap, fmt); 241 #else 242 va_start(ap); 243 #endif 244 (void)fprintf(stderr, "uniq: "); 245 (void)vfprintf(stderr, fmt, ap); 246 va_end(ap); 247 (void)fprintf(stderr, "\n"); 248 exit(1); 249 /* NOTREACHED */ 250 } 251