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
main(argc,argv)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
show(ofp,str)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 *
skip(str)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 *
file(name,mode)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
obsolete(argv)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
usage()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__
err(const char * fmt,...)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