xref: /original-bsd/usr.bin/uniq/uniq.c (revision 0f81f0ee)
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