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