uniq.c revision 1.1.1.3 1 1.1 cgd /*
2 1.1.1.2 jtc * Copyright (c) 1989, 1993
3 1.1.1.2 jtc * The Regents of the University of California. All rights reserved.
4 1.1 cgd *
5 1.1 cgd * This code is derived from software contributed to Berkeley by
6 1.1 cgd * Case Larsen.
7 1.1 cgd *
8 1.1 cgd * Redistribution and use in source and binary forms, with or without
9 1.1 cgd * modification, are permitted provided that the following conditions
10 1.1 cgd * are met:
11 1.1 cgd * 1. Redistributions of source code must retain the above copyright
12 1.1 cgd * notice, this list of conditions and the following disclaimer.
13 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 cgd * notice, this list of conditions and the following disclaimer in the
15 1.1 cgd * documentation and/or other materials provided with the distribution.
16 1.1 cgd * 3. All advertising materials mentioning features or use of this software
17 1.1 cgd * must display the following acknowledgement:
18 1.1 cgd * This product includes software developed by the University of
19 1.1 cgd * California, Berkeley and its contributors.
20 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
21 1.1 cgd * may be used to endorse or promote products derived from this software
22 1.1 cgd * without specific prior written permission.
23 1.1 cgd *
24 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 cgd * SUCH DAMAGE.
35 1.1 cgd */
36 1.1 cgd
37 1.1 cgd #ifndef lint
38 1.1.1.2 jtc static char copyright[] =
39 1.1.1.2 jtc "@(#) Copyright (c) 1989, 1993\n\
40 1.1.1.2 jtc The Regents of the University of California. All rights reserved.\n";
41 1.1 cgd #endif /* not lint */
42 1.1 cgd
43 1.1 cgd #ifndef lint
44 1.1.1.3 jtc static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95";
45 1.1 cgd #endif /* not lint */
46 1.1 cgd
47 1.1.1.2 jtc #include <errno.h>
48 1.1 cgd #include <stdio.h>
49 1.1 cgd #include <ctype.h>
50 1.1.1.2 jtc #include <stdlib.h>
51 1.1.1.2 jtc #include <string.h>
52 1.1.1.3 jtc #include <unistd.h>
53 1.1.1.2 jtc
54 1.1.1.2 jtc #define MAXLINELEN (8 * 1024)
55 1.1 cgd
56 1.1 cgd int cflag, dflag, uflag;
57 1.1 cgd int numchars, numfields, repeats;
58 1.1 cgd
59 1.1.1.2 jtc void err __P((const char *, ...));
60 1.1.1.2 jtc FILE *file __P((char *, char *));
61 1.1.1.2 jtc void show __P((FILE *, char *));
62 1.1.1.2 jtc char *skip __P((char *));
63 1.1.1.2 jtc void obsolete __P((char *[]));
64 1.1.1.2 jtc void usage __P((void));
65 1.1 cgd
66 1.1.1.2 jtc int
67 1.1.1.2 jtc main (argc, argv)
68 1.1 cgd int argc;
69 1.1.1.2 jtc char *argv[];
70 1.1 cgd {
71 1.1 cgd register char *t1, *t2;
72 1.1.1.2 jtc FILE *ifp, *ofp;
73 1.1.1.2 jtc int ch;
74 1.1.1.2 jtc char *prevline, *thisline, *p;
75 1.1 cgd
76 1.1.1.2 jtc obsolete(argv);
77 1.1.1.2 jtc while ((ch = getopt(argc, argv, "-cdf:s:u")) != EOF)
78 1.1 cgd switch (ch) {
79 1.1 cgd case '-':
80 1.1 cgd --optind;
81 1.1 cgd goto done;
82 1.1 cgd case 'c':
83 1.1 cgd cflag = 1;
84 1.1 cgd break;
85 1.1 cgd case 'd':
86 1.1 cgd dflag = 1;
87 1.1 cgd break;
88 1.1.1.2 jtc case 'f':
89 1.1.1.2 jtc numfields = strtol(optarg, &p, 10);
90 1.1.1.2 jtc if (numfields < 0 || *p)
91 1.1.1.2 jtc err("illegal field skip value: %s", optarg);
92 1.1.1.2 jtc break;
93 1.1.1.2 jtc case 's':
94 1.1.1.2 jtc numchars = strtol(optarg, &p, 10);
95 1.1.1.2 jtc if (numchars < 0 || *p)
96 1.1.1.2 jtc err("illegal character skip value: %s", optarg);
97 1.1.1.2 jtc break;
98 1.1 cgd case 'u':
99 1.1 cgd uflag = 1;
100 1.1 cgd break;
101 1.1 cgd case '?':
102 1.1 cgd default:
103 1.1 cgd usage();
104 1.1 cgd }
105 1.1 cgd
106 1.1 cgd done: argc -= optind;
107 1.1 cgd argv +=optind;
108 1.1 cgd
109 1.1.1.2 jtc /* If no flags are set, default is -d -u. */
110 1.1 cgd if (cflag) {
111 1.1 cgd if (dflag || uflag)
112 1.1 cgd usage();
113 1.1 cgd } else if (!dflag && !uflag)
114 1.1 cgd dflag = uflag = 1;
115 1.1 cgd
116 1.1 cgd switch(argc) {
117 1.1 cgd case 0:
118 1.1 cgd ifp = stdin;
119 1.1 cgd ofp = stdout;
120 1.1 cgd break;
121 1.1 cgd case 1:
122 1.1 cgd ifp = file(argv[0], "r");
123 1.1 cgd ofp = stdout;
124 1.1 cgd break;
125 1.1 cgd case 2:
126 1.1 cgd ifp = file(argv[0], "r");
127 1.1 cgd ofp = file(argv[1], "w");
128 1.1 cgd break;
129 1.1 cgd default:
130 1.1 cgd usage();
131 1.1 cgd }
132 1.1 cgd
133 1.1 cgd prevline = malloc(MAXLINELEN);
134 1.1 cgd thisline = malloc(MAXLINELEN);
135 1.1.1.2 jtc if (prevline == NULL || thisline == NULL)
136 1.1.1.2 jtc err("%s", strerror(errno));
137 1.1.1.2 jtc
138 1.1.1.2 jtc if (fgets(prevline, MAXLINELEN, ifp) == NULL)
139 1.1.1.2 jtc exit(0);
140 1.1 cgd
141 1.1 cgd while (fgets(thisline, MAXLINELEN, ifp)) {
142 1.1.1.2 jtc /* If requested get the chosen fields + character offsets. */
143 1.1 cgd if (numfields || numchars) {
144 1.1 cgd t1 = skip(thisline);
145 1.1 cgd t2 = skip(prevline);
146 1.1 cgd } else {
147 1.1 cgd t1 = thisline;
148 1.1 cgd t2 = prevline;
149 1.1 cgd }
150 1.1 cgd
151 1.1.1.2 jtc /* If different, print; set previous to new value. */
152 1.1 cgd if (strcmp(t1, t2)) {
153 1.1 cgd show(ofp, prevline);
154 1.1 cgd t1 = prevline;
155 1.1 cgd prevline = thisline;
156 1.1 cgd thisline = t1;
157 1.1 cgd repeats = 0;
158 1.1.1.2 jtc } else
159 1.1 cgd ++repeats;
160 1.1 cgd }
161 1.1 cgd show(ofp, prevline);
162 1.1 cgd exit(0);
163 1.1 cgd }
164 1.1 cgd
165 1.1 cgd /*
166 1.1 cgd * show --
167 1.1.1.2 jtc * Output a line depending on the flags and number of repetitions
168 1.1 cgd * of the line.
169 1.1 cgd */
170 1.1.1.2 jtc void
171 1.1 cgd show(ofp, str)
172 1.1 cgd FILE *ofp;
173 1.1 cgd char *str;
174 1.1 cgd {
175 1.1.1.3 jtc
176 1.1.1.3 jtc if (cflag && *str)
177 1.1 cgd (void)fprintf(ofp, "%4d %s", repeats + 1, str);
178 1.1 cgd if (dflag && repeats || uflag && !repeats)
179 1.1 cgd (void)fprintf(ofp, "%s", str);
180 1.1 cgd }
181 1.1 cgd
182 1.1 cgd char *
183 1.1 cgd skip(str)
184 1.1 cgd register char *str;
185 1.1 cgd {
186 1.1 cgd register int infield, nchars, nfields;
187 1.1 cgd
188 1.1 cgd for (nfields = numfields, infield = 0; nfields && *str; ++str)
189 1.1 cgd if (isspace(*str)) {
190 1.1 cgd if (infield) {
191 1.1 cgd infield = 0;
192 1.1 cgd --nfields;
193 1.1 cgd }
194 1.1 cgd } else if (!infield)
195 1.1 cgd infield = 1;
196 1.1 cgd for (nchars = numchars; nchars-- && *str; ++str);
197 1.1 cgd return(str);
198 1.1 cgd }
199 1.1 cgd
200 1.1 cgd FILE *
201 1.1 cgd file(name, mode)
202 1.1 cgd char *name, *mode;
203 1.1 cgd {
204 1.1 cgd FILE *fp;
205 1.1 cgd
206 1.1.1.2 jtc if ((fp = fopen(name, mode)) == NULL)
207 1.1.1.2 jtc err("%s: %s", name, strerror(errno));
208 1.1 cgd return(fp);
209 1.1 cgd }
210 1.1 cgd
211 1.1.1.2 jtc void
212 1.1.1.2 jtc obsolete(argv)
213 1.1.1.2 jtc char *argv[];
214 1.1.1.2 jtc {
215 1.1.1.2 jtc int len;
216 1.1.1.2 jtc char *ap, *p, *start;
217 1.1.1.2 jtc
218 1.1.1.2 jtc while (ap = *++argv) {
219 1.1.1.2 jtc /* Return if "--" or not an option of any form. */
220 1.1.1.2 jtc if (ap[0] != '-') {
221 1.1.1.2 jtc if (ap[0] != '+')
222 1.1.1.2 jtc return;
223 1.1.1.2 jtc } else if (ap[1] == '-')
224 1.1.1.2 jtc return;
225 1.1.1.2 jtc if (!isdigit(ap[1]))
226 1.1.1.2 jtc continue;
227 1.1.1.2 jtc /*
228 1.1.1.2 jtc * Digit signifies an old-style option. Malloc space for dash,
229 1.1.1.2 jtc * new option and argument.
230 1.1.1.2 jtc */
231 1.1.1.2 jtc len = strlen(ap);
232 1.1.1.2 jtc if ((start = p = malloc(len + 3)) == NULL)
233 1.1.1.2 jtc err("%s", strerror(errno));
234 1.1.1.2 jtc *p++ = '-';
235 1.1.1.2 jtc *p++ = ap[0] == '+' ? 's' : 'f';
236 1.1.1.2 jtc (void)strcpy(p, ap + 1);
237 1.1.1.2 jtc *argv = start;
238 1.1.1.2 jtc }
239 1.1.1.2 jtc }
240 1.1.1.2 jtc
241 1.1.1.2 jtc void
242 1.1 cgd usage()
243 1.1 cgd {
244 1.1 cgd (void)fprintf(stderr,
245 1.1.1.2 jtc "usage: uniq [-c | -du] [-f fields] [-s chars] [input [output]]\n");
246 1.1.1.2 jtc exit(1);
247 1.1.1.2 jtc }
248 1.1.1.2 jtc
249 1.1.1.2 jtc #if __STDC__
250 1.1.1.2 jtc #include <stdarg.h>
251 1.1.1.2 jtc #else
252 1.1.1.2 jtc #include <varargs.h>
253 1.1.1.2 jtc #endif
254 1.1.1.2 jtc
255 1.1.1.2 jtc void
256 1.1.1.2 jtc #if __STDC__
257 1.1.1.2 jtc err(const char *fmt, ...)
258 1.1.1.2 jtc #else
259 1.1.1.2 jtc err(fmt, va_alist)
260 1.1.1.2 jtc char *fmt;
261 1.1.1.2 jtc va_dcl
262 1.1.1.2 jtc #endif
263 1.1.1.2 jtc {
264 1.1.1.2 jtc va_list ap;
265 1.1.1.2 jtc #if __STDC__
266 1.1.1.2 jtc va_start(ap, fmt);
267 1.1.1.2 jtc #else
268 1.1.1.2 jtc va_start(ap);
269 1.1.1.2 jtc #endif
270 1.1.1.2 jtc (void)fprintf(stderr, "uniq: ");
271 1.1.1.2 jtc (void)vfprintf(stderr, fmt, ap);
272 1.1.1.2 jtc va_end(ap);
273 1.1.1.2 jtc (void)fprintf(stderr, "\n");
274 1.1 cgd exit(1);
275 1.1.1.2 jtc /* NOTREACHED */
276 1.1 cgd }
277