xref: /freebsd/usr.bin/uniq/uniq.c (revision c3f8900e696998c410dc16f9bd9d45c24c413e6b)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
40da30e9aSPeter Wemm  * Copyright (c) 1989, 1993
50da30e9aSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
60da30e9aSPeter Wemm  *
70da30e9aSPeter Wemm  * This code is derived from software contributed to Berkeley by
80da30e9aSPeter Wemm  * Case Larsen.
90da30e9aSPeter Wemm  *
100da30e9aSPeter Wemm  * Redistribution and use in source and binary forms, with or without
110da30e9aSPeter Wemm  * modification, are permitted provided that the following conditions
120da30e9aSPeter Wemm  * are met:
130da30e9aSPeter Wemm  * 1. Redistributions of source code must retain the above copyright
140da30e9aSPeter Wemm  *    notice, this list of conditions and the following disclaimer.
150da30e9aSPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
160da30e9aSPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
170da30e9aSPeter Wemm  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
190da30e9aSPeter Wemm  *    may be used to endorse or promote products derived from this software
200da30e9aSPeter Wemm  *    without specific prior written permission.
210da30e9aSPeter Wemm  *
220da30e9aSPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
230da30e9aSPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
240da30e9aSPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
250da30e9aSPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
260da30e9aSPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
270da30e9aSPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
280da30e9aSPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
290da30e9aSPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
300da30e9aSPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
310da30e9aSPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
320da30e9aSPeter Wemm  * SUCH DAMAGE.
330da30e9aSPeter Wemm  */
340da30e9aSPeter Wemm 
35b881b8beSRobert Watson #include <sys/capsicum.h>
36de89bd6bSPawel Jakub Dawidek 
377672a014SMariusz Zaborski #include <capsicum_helpers.h>
380da30e9aSPeter Wemm #include <ctype.h>
39213915d4SPhilippe Charnier #include <err.h>
40de89bd6bSPawel Jakub Dawidek #include <errno.h>
41a597327bSKyle Evans #include <getopt.h>
42c02e5894SAndrey A. Chernov #include <limits.h>
430c312497SAndrey A. Chernov #include <locale.h>
44de89bd6bSPawel Jakub Dawidek #include <nl_types.h>
4511715600SDag-Erling Smørgrav #include <stdbool.h>
46a8092021SJaakko Heinonen #include <stdint.h>
47213915d4SPhilippe Charnier #include <stdio.h>
480da30e9aSPeter Wemm #include <stdlib.h>
490da30e9aSPeter Wemm #include <string.h>
50de89bd6bSPawel Jakub Dawidek #include <termios.h>
510da30e9aSPeter Wemm #include <unistd.h>
523fead394STim J. Robbins #include <wchar.h>
533fead394STim J. Robbins #include <wctype.h>
540da30e9aSPeter Wemm 
5511715600SDag-Erling Smørgrav static enum { DF_NONE, DF_NOSEP, DF_PRESEP, DF_POSTSEP } Dflag;
5611715600SDag-Erling Smørgrav static bool cflag, dflag, uflag, iflag;
5711715600SDag-Erling Smørgrav static long long numchars, numfields, repeats;
58d9371717SIan Lepore 
59a597327bSKyle Evans static const struct option long_opts[] =
60a597327bSKyle Evans {
61d9371717SIan Lepore 	{"all-repeated",optional_argument,	NULL, 'D'},
62a597327bSKyle Evans 	{"count",	no_argument,		NULL, 'c'},
63a597327bSKyle Evans 	{"repeated",	no_argument,		NULL, 'd'},
64a597327bSKyle Evans 	{"skip-fields",	required_argument,	NULL, 'f'},
65a597327bSKyle Evans 	{"ignore-case",	no_argument,		NULL, 'i'},
66a597327bSKyle Evans 	{"skip-chars",	required_argument,	NULL, 's'},
67a597327bSKyle Evans 	{"unique",	no_argument,		NULL, 'u'},
68a597327bSKyle Evans 	{NULL,		no_argument,		NULL, 0}
69a597327bSKyle Evans };
70a597327bSKyle Evans 
717f3cfdffSEd Schouten static FILE	*file(const char *, const char *);
727f3cfdffSEd Schouten static wchar_t	*convert(const char *);
737f3cfdffSEd Schouten static int	 inlcmp(const char *, const char *);
747f3cfdffSEd Schouten static void	 show(FILE *, const char *);
757f3cfdffSEd Schouten static wchar_t	*skip(wchar_t *);
767f3cfdffSEd Schouten static void	 obsolete(char *[]);
773f330d7dSWarner Losh static void	 usage(void);
78c02e5894SAndrey A. Chernov 
790da30e9aSPeter Wemm int
80f4ac32deSDavid Malone main (int argc, char *argv[])
810da30e9aSPeter Wemm {
825eaad26eSAndrey A. Chernov 	wchar_t *tprev, *tthis;
830da30e9aSPeter Wemm 	FILE *ifp, *ofp;
84d4c2dafaSAndrey A. Chernov 	int ch, comp;
855eaad26eSAndrey A. Chernov 	size_t prevbuflen, thisbuflen, b1;
86d4c2dafaSAndrey A. Chernov 	char *prevline, *thisline, *p;
87899837e8SDag-Erling Smørgrav 	const char *errstr, *ifn, *ofn;
88de89bd6bSPawel Jakub Dawidek 	cap_rights_t rights;
890da30e9aSPeter Wemm 
90b285e268SAndrey A. Chernov 	(void) setlocale(LC_ALL, "");
910c312497SAndrey A. Chernov 
920da30e9aSPeter Wemm 	obsolete(argv);
93d9371717SIan Lepore 	while ((ch = getopt_long(argc, argv, "+D::cdif:s:u", long_opts,
94a597327bSKyle Evans 	    NULL)) != -1)
950da30e9aSPeter Wemm 		switch (ch) {
96d9371717SIan Lepore 		case 'D':
97d9371717SIan Lepore 			if (optarg == NULL || strcasecmp(optarg, "none") == 0)
98d9371717SIan Lepore 				Dflag = DF_NOSEP;
99d9371717SIan Lepore 			else if (strcasecmp(optarg, "prepend") == 0)
100d9371717SIan Lepore 				Dflag = DF_PRESEP;
101d9371717SIan Lepore 			else if (strcasecmp(optarg, "separate") == 0)
102d9371717SIan Lepore 				Dflag = DF_POSTSEP;
103d9371717SIan Lepore 			else
104d9371717SIan Lepore 				usage();
105d9371717SIan Lepore 			break;
1060da30e9aSPeter Wemm 		case 'c':
10711715600SDag-Erling Smørgrav 			cflag = true;
1080da30e9aSPeter Wemm 			break;
1090da30e9aSPeter Wemm 		case 'd':
11011715600SDag-Erling Smørgrav 			dflag = true;
1110da30e9aSPeter Wemm 			break;
1122ca7dc15SJoerg Wunsch 		case 'i':
11311715600SDag-Erling Smørgrav 			iflag = true;
1142ca7dc15SJoerg Wunsch 			break;
1150da30e9aSPeter Wemm 		case 'f':
116e052829eSDaniel Tameling 			numfields = strtonum(optarg, 0, INT_MAX, &errstr);
117e052829eSDaniel Tameling 			if (errstr)
118e052829eSDaniel Tameling 				errx(1, "field skip value is %s: %s", errstr, optarg);
1190da30e9aSPeter Wemm 			break;
1200da30e9aSPeter Wemm 		case 's':
121e052829eSDaniel Tameling 			numchars = strtonum(optarg, 0, INT_MAX, &errstr);
122e052829eSDaniel Tameling 			if (errstr != NULL)
123e052829eSDaniel Tameling 				errx(1, "character skip value is %s: %s", errstr, optarg);
1240da30e9aSPeter Wemm 			break;
1250da30e9aSPeter Wemm 		case 'u':
12611715600SDag-Erling Smørgrav 			uflag = true;
1270da30e9aSPeter Wemm 			break;
1280da30e9aSPeter Wemm 		case '?':
1290da30e9aSPeter Wemm 		default:
1300da30e9aSPeter Wemm 			usage();
1310da30e9aSPeter Wemm 		}
1320da30e9aSPeter Wemm 
133fc630325STim J. Robbins 	argc -= optind;
1340da30e9aSPeter Wemm 	argv += optind;
1350da30e9aSPeter Wemm 
13638b1ff46STim J. Robbins 	if (argc > 2)
13738b1ff46STim J. Robbins 		usage();
13838b1ff46STim J. Robbins 
13911715600SDag-Erling Smørgrav 	if (Dflag && dflag)
14011715600SDag-Erling Smørgrav 		dflag = false;
14111715600SDag-Erling Smørgrav 
1420da30e9aSPeter Wemm 	ifp = stdin;
1433fead394STim J. Robbins 	ifn = "stdin";
1440da30e9aSPeter Wemm 	ofp = stdout;
145899837e8SDag-Erling Smørgrav 	ofn = "stdout";
14638b1ff46STim J. Robbins 	if (argc > 0 && strcmp(argv[0], "-") != 0)
1473fead394STim J. Robbins 		ifp = file(ifn = argv[0], "r");
1487008be5bSPawel Jakub Dawidek 	cap_rights_init(&rights, CAP_FSTAT, CAP_READ);
149377421dfSMariusz Zaborski 	if (caph_rights_limit(fileno(ifp), &rights) < 0)
150de89bd6bSPawel Jakub Dawidek 		err(1, "unable to limit rights for %s", ifn);
1517008be5bSPawel Jakub Dawidek 	cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE);
15238b1ff46STim J. Robbins 	if (argc > 1)
153899837e8SDag-Erling Smørgrav 		ofp = file(ofn = argv[1], "w");
154de89bd6bSPawel Jakub Dawidek 	else
1557008be5bSPawel Jakub Dawidek 		cap_rights_set(&rights, CAP_IOCTL);
156377421dfSMariusz Zaborski 	if (caph_rights_limit(fileno(ofp), &rights) < 0) {
157de89bd6bSPawel Jakub Dawidek 		err(1, "unable to limit rights for %s",
158de89bd6bSPawel Jakub Dawidek 		    argc > 1 ? argv[1] : "stdout");
159de89bd6bSPawel Jakub Dawidek 	}
1607008be5bSPawel Jakub Dawidek 	if (cap_rights_is_set(&rights, CAP_IOCTL)) {
161de89bd6bSPawel Jakub Dawidek 		unsigned long cmd;
162de89bd6bSPawel Jakub Dawidek 
163de89bd6bSPawel Jakub Dawidek 		cmd = TIOCGETA; /* required by isatty(3) in printf(3) */
164de89bd6bSPawel Jakub Dawidek 
165377421dfSMariusz Zaborski 		if (caph_ioctls_limit(fileno(ofp), &cmd, 1) < 0) {
166de89bd6bSPawel Jakub Dawidek 			err(1, "unable to limit ioctls for %s",
167de89bd6bSPawel Jakub Dawidek 			    argc > 1 ? argv[1] : "stdout");
168de89bd6bSPawel Jakub Dawidek 		}
169de89bd6bSPawel Jakub Dawidek 	}
170de89bd6bSPawel Jakub Dawidek 
171a3552326SMariusz Zaborski 	caph_cache_catpages();
1727672a014SMariusz Zaborski 	if (caph_enter() < 0)
173de89bd6bSPawel Jakub Dawidek 		err(1, "unable to enter capability mode");
1740da30e9aSPeter Wemm 
1755eaad26eSAndrey A. Chernov 	prevbuflen = thisbuflen = 0;
1765eaad26eSAndrey A. Chernov 	prevline = thisline = NULL;
1770da30e9aSPeter Wemm 
1785eaad26eSAndrey A. Chernov 	if (getline(&prevline, &prevbuflen, ifp) < 0) {
17926cfaf71SJuli Mallett 		if (ferror(ifp))
18036c5e18dSJuli Mallett 			err(1, "%s", ifn);
1810da30e9aSPeter Wemm 		exit(0);
1823fead394STim J. Robbins 	}
18311715600SDag-Erling Smørgrav 	if (!cflag && !Dflag && !dflag && !uflag)
18411715600SDag-Erling Smørgrav 		show(ofp, prevline);
1855eaad26eSAndrey A. Chernov 	tprev = convert(prevline);
186d4c2dafaSAndrey A. Chernov 
1875eaad26eSAndrey A. Chernov 	tthis = NULL;
1885eaad26eSAndrey A. Chernov 	while (getline(&thisline, &thisbuflen, ifp) >= 0) {
1895eaad26eSAndrey A. Chernov 		if (tthis != NULL)
1905eaad26eSAndrey A. Chernov 			free(tthis);
1915eaad26eSAndrey A. Chernov 		tthis = convert(thisline);
1920da30e9aSPeter Wemm 
193d4c2dafaSAndrey A. Chernov 		if (tthis == NULL && tprev == NULL)
1945eaad26eSAndrey A. Chernov 			comp = inlcmp(thisline, prevline);
195d4c2dafaSAndrey A. Chernov 		else if (tthis == NULL || tprev == NULL)
196d4c2dafaSAndrey A. Chernov 			comp = 1;
1972ca7dc15SJoerg Wunsch 		else
198d4c2dafaSAndrey A. Chernov 			comp = wcscoll(tthis, tprev);
1992ca7dc15SJoerg Wunsch 
2002ca7dc15SJoerg Wunsch 		if (comp) {
201d4c2dafaSAndrey A. Chernov 			/* If different, print; set previous to new value. */
202d9371717SIan Lepore 			if (Dflag == DF_POSTSEP && repeats > 0)
203d9371717SIan Lepore 				fputc('\n', ofp);
20411715600SDag-Erling Smørgrav 			if (!cflag && !Dflag && !dflag && !uflag)
20511715600SDag-Erling Smørgrav 				show(ofp, thisline);
20611715600SDag-Erling Smørgrav 			else if (!Dflag &&
20711715600SDag-Erling Smørgrav 			    (!dflag || (cflag && repeats > 0)) &&
20811715600SDag-Erling Smørgrav 			    (!uflag || repeats == 0))
2090da30e9aSPeter Wemm 				show(ofp, prevline);
210d4c2dafaSAndrey A. Chernov 			p = prevline;
2115eaad26eSAndrey A. Chernov 			b1 = prevbuflen;
2120da30e9aSPeter Wemm 			prevline = thisline;
2135eaad26eSAndrey A. Chernov 			prevbuflen = thisbuflen;
2145eaad26eSAndrey A. Chernov 			if (tprev != NULL)
2155eaad26eSAndrey A. Chernov 				free(tprev);
216d4c2dafaSAndrey A. Chernov 			tprev = tthis;
217d4c2dafaSAndrey A. Chernov 			thisline = p;
2185eaad26eSAndrey A. Chernov 			thisbuflen = b1;
2195eaad26eSAndrey A. Chernov 			tthis = NULL;
2200da30e9aSPeter Wemm 			repeats = 0;
221d9371717SIan Lepore 		} else {
222d9371717SIan Lepore 			if (Dflag) {
223d9371717SIan Lepore 				if (repeats == 0) {
224d9371717SIan Lepore 					if (Dflag == DF_PRESEP)
225d9371717SIan Lepore 						fputc('\n', ofp);
226d9371717SIan Lepore 					show(ofp, prevline);
227d9371717SIan Lepore 				}
22811715600SDag-Erling Smørgrav 			} else if (dflag && !cflag) {
22911715600SDag-Erling Smørgrav 				if (repeats == 0)
23011715600SDag-Erling Smørgrav 					show(ofp, prevline);
231d9371717SIan Lepore 			}
2320da30e9aSPeter Wemm 			++repeats;
233*c3f8900eSDag-Erling Smørgrav 			if (Dflag)
234*c3f8900eSDag-Erling Smørgrav 				show(ofp, thisline);
2350da30e9aSPeter Wemm 		}
236d9371717SIan Lepore 	}
2373fead394STim J. Robbins 	if (ferror(ifp))
23836c5e18dSJuli Mallett 		err(1, "%s", ifn);
23911715600SDag-Erling Smørgrav 	if (!cflag && !Dflag && !dflag && !uflag)
24011715600SDag-Erling Smørgrav 		/* already printed */ ;
24111715600SDag-Erling Smørgrav 	else if (!Dflag &&
24211715600SDag-Erling Smørgrav 	    (!dflag || (cflag && repeats > 0)) &&
24311715600SDag-Erling Smørgrav 	    (!uflag || repeats == 0))
2440da30e9aSPeter Wemm 		show(ofp, prevline);
245899837e8SDag-Erling Smørgrav 	if (fflush(ofp) != 0)
246899837e8SDag-Erling Smørgrav 		err(1, "%s", ofn);
2470da30e9aSPeter Wemm 	exit(0);
2480da30e9aSPeter Wemm }
2490da30e9aSPeter Wemm 
2507f3cfdffSEd Schouten static wchar_t *
2515eaad26eSAndrey A. Chernov convert(const char *str)
252d4c2dafaSAndrey A. Chernov {
253d4c2dafaSAndrey A. Chernov 	size_t n;
2545eaad26eSAndrey A. Chernov 	wchar_t *buf, *ret, *p;
255d4c2dafaSAndrey A. Chernov 
2565eaad26eSAndrey A. Chernov 	if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1)
2575eaad26eSAndrey A. Chernov 		return (NULL);
258d2796d06SAndrey A. Chernov 	if (SIZE_MAX / sizeof(*buf) < n + 1)
259d2796d06SAndrey A. Chernov 		errx(1, "conversion buffer length overflow");
2605eaad26eSAndrey A. Chernov 	if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL)
2615eaad26eSAndrey A. Chernov 		err(1, "malloc");
2625eaad26eSAndrey A. Chernov 	if (mbstowcs(buf, str, n + 1) != n)
2635eaad26eSAndrey A. Chernov 		errx(1, "internal mbstowcs() error");
2645eaad26eSAndrey A. Chernov 	/* The last line may not end with \n. */
2655eaad26eSAndrey A. Chernov 	if (n > 0 && buf[n - 1] == L'\n')
2665eaad26eSAndrey A. Chernov 		buf[n - 1] = L'\0';
2675eaad26eSAndrey A. Chernov 
268d4c2dafaSAndrey A. Chernov 	/* If requested get the chosen fields + character offsets. */
2695eaad26eSAndrey A. Chernov 	if (numfields || numchars) {
2705eaad26eSAndrey A. Chernov 		if ((ret = wcsdup(skip(buf))) == NULL)
2715eaad26eSAndrey A. Chernov 			err(1, "wcsdup");
2725eaad26eSAndrey A. Chernov 		free(buf);
2735eaad26eSAndrey A. Chernov 	} else
274d4c2dafaSAndrey A. Chernov 		ret = buf;
2755eaad26eSAndrey A. Chernov 
276d4c2dafaSAndrey A. Chernov 	if (iflag) {
277d4c2dafaSAndrey A. Chernov 		for (p = ret; *p != L'\0'; p++)
278d4c2dafaSAndrey A. Chernov 			*p = towlower(*p);
279d4c2dafaSAndrey A. Chernov 	}
280d4c2dafaSAndrey A. Chernov 
281d4c2dafaSAndrey A. Chernov 	return (ret);
282d4c2dafaSAndrey A. Chernov }
283d4c2dafaSAndrey A. Chernov 
2847f3cfdffSEd Schouten static int
2855eaad26eSAndrey A. Chernov inlcmp(const char *s1, const char *s2)
2865eaad26eSAndrey A. Chernov {
2875eaad26eSAndrey A. Chernov 	int c1, c2;
2885eaad26eSAndrey A. Chernov 
2895eaad26eSAndrey A. Chernov 	while (*s1 == *s2++)
2905eaad26eSAndrey A. Chernov 		if (*s1++ == '\0')
2915eaad26eSAndrey A. Chernov 			return (0);
2925eaad26eSAndrey A. Chernov 	c1 = (unsigned char)*s1;
2935eaad26eSAndrey A. Chernov 	c2 = (unsigned char)*(s2 - 1);
2945eaad26eSAndrey A. Chernov 	/* The last line may not end with \n. */
2955eaad26eSAndrey A. Chernov 	if (c1 == '\n')
2965eaad26eSAndrey A. Chernov 		c1 = '\0';
2975eaad26eSAndrey A. Chernov 	if (c2 == '\n')
2985eaad26eSAndrey A. Chernov 		c2 = '\0';
2995eaad26eSAndrey A. Chernov 	return (c1 - c2);
3005eaad26eSAndrey A. Chernov }
3015eaad26eSAndrey A. Chernov 
3020da30e9aSPeter Wemm /*
3030da30e9aSPeter Wemm  * show --
3040da30e9aSPeter Wemm  *	Output a line depending on the flags and number of repetitions
3050da30e9aSPeter Wemm  *	of the line.
3060da30e9aSPeter Wemm  */
3077f3cfdffSEd Schouten static void
308d4c2dafaSAndrey A. Chernov show(FILE *ofp, const char *str)
3090da30e9aSPeter Wemm {
3100cd82603STim J. Robbins 	if (cflag)
31111715600SDag-Erling Smørgrav 		(void)fprintf(ofp, "%4lld %s", repeats + 1, str);
312a520574dSEd Maste 	else
3135eaad26eSAndrey A. Chernov 		(void)fprintf(ofp, "%s", str);
3140da30e9aSPeter Wemm }
3150da30e9aSPeter Wemm 
3167f3cfdffSEd Schouten static wchar_t *
3173fead394STim J. Robbins skip(wchar_t *str)
3180da30e9aSPeter Wemm {
31911715600SDag-Erling Smørgrav 	long long nchars, nfields;
3200da30e9aSPeter Wemm 
321d4c2dafaSAndrey A. Chernov 	for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) {
3223fead394STim J. Robbins 		while (iswblank(*str))
323382ac430STim J. Robbins 			str++;
324d4c2dafaSAndrey A. Chernov 		while (*str != L'\0' && !iswblank(*str))
325382ac430STim J. Robbins 			str++;
3260da30e9aSPeter Wemm 	}
327d4c2dafaSAndrey A. Chernov 	for (nchars = numchars; nchars-- && *str != L'\0'; ++str)
328d4c2dafaSAndrey A. Chernov 		;
3290da30e9aSPeter Wemm 	return(str);
3300da30e9aSPeter Wemm }
3310da30e9aSPeter Wemm 
3327f3cfdffSEd Schouten static FILE *
333f4ac32deSDavid Malone file(const char *name, const char *mode)
3340da30e9aSPeter Wemm {
3350da30e9aSPeter Wemm 	FILE *fp;
3360da30e9aSPeter Wemm 
3370da30e9aSPeter Wemm 	if ((fp = fopen(name, mode)) == NULL)
338213915d4SPhilippe Charnier 		err(1, "%s", name);
3390da30e9aSPeter Wemm 	return(fp);
3400da30e9aSPeter Wemm }
3410da30e9aSPeter Wemm 
3427f3cfdffSEd Schouten static void
343f4ac32deSDavid Malone obsolete(char *argv[])
3440da30e9aSPeter Wemm {
345e2ec8ee0SDag-Erling Smørgrav 	char *ap, *p;
3460da30e9aSPeter Wemm 
347213915d4SPhilippe Charnier 	while ((ap = *++argv)) {
3480da30e9aSPeter Wemm 		/* Return if "--" or not an option of any form. */
3490da30e9aSPeter Wemm 		if (ap[0] != '-') {
3500da30e9aSPeter Wemm 			if (ap[0] != '+')
3510da30e9aSPeter Wemm 				return;
352e2ec8ee0SDag-Erling Smørgrav 		} else if (ap[1] == '-') {
3530da30e9aSPeter Wemm 			return;
354e2ec8ee0SDag-Erling Smørgrav 		}
3550c312497SAndrey A. Chernov 		if (!isdigit((unsigned char)ap[1]))
3560da30e9aSPeter Wemm 			continue;
3570da30e9aSPeter Wemm 		/*
3580da30e9aSPeter Wemm 		 * Digit signifies an old-style option.  Malloc space for dash,
3590da30e9aSPeter Wemm 		 * new option and argument.
3600da30e9aSPeter Wemm 		 */
361e2ec8ee0SDag-Erling Smørgrav 		if (asprintf(&p, "-%c%s", ap[0] == '+' ? 's' : 'f', ap + 1) < 0)
3627dd4ac68STim J. Robbins 			err(1, "malloc");
363e2ec8ee0SDag-Erling Smørgrav 		*argv = p;
3640da30e9aSPeter Wemm 	}
3650da30e9aSPeter Wemm }
3660da30e9aSPeter Wemm 
367213915d4SPhilippe Charnier static void
368f4ac32deSDavid Malone usage(void)
3690da30e9aSPeter Wemm {
370b93791f5SDag-Erling Smørgrav 	(void)fprintf(stderr, "usage: uniq [-cdiu] [-D[septype]] "
371b93791f5SDag-Erling Smørgrav 	    "[-f fields] [-s chars] [input [output]]\n");
3720da30e9aSPeter Wemm 	exit(1);
3730da30e9aSPeter Wemm }
374