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