1*364bef39Schristos /* $NetBSD: uniq.c,v 1.22 2019/04/23 17:35:10 christos Exp $ */
2972ab0a5Sjtc
361f28255Scgd /*
4972ab0a5Sjtc * Copyright (c) 1989, 1993
5972ab0a5Sjtc * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * This code is derived from software contributed to Berkeley by
861f28255Scgd * Case Larsen.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
1889aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd * may be used to endorse or promote products derived from this software
2061f28255Scgd * without specific prior written permission.
2161f28255Scgd *
2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd * SUCH DAMAGE.
3361f28255Scgd */
3461f28255Scgd
358512105cSlukem #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3798e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
3898e5374cSlukem The Regents of the University of California. All rights reserved.");
3961f28255Scgd #endif /* not lint */
4061f28255Scgd
4161f28255Scgd #ifndef lint
42972ab0a5Sjtc #if 0
43be667101Sjtc static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95";
44972ab0a5Sjtc #endif
45*364bef39Schristos __RCSID("$NetBSD: uniq.c,v 1.22 2019/04/23 17:35:10 christos Exp $");
4661f28255Scgd #endif /* not lint */
4761f28255Scgd
488512105cSlukem #include <err.h>
49dbd4200bSmycroft #include <errno.h>
5061f28255Scgd #include <stdio.h>
5161f28255Scgd #include <ctype.h>
52dbd4200bSmycroft #include <stdlib.h>
53dbd4200bSmycroft #include <string.h>
54be667101Sjtc #include <unistd.h>
55dbd4200bSmycroft
561424501aSchristos static int cflag, dflag, uflag;
571424501aSchristos static int numchars, numfields, repeats;
5861f28255Scgd
591424501aSchristos static FILE *file(const char *, const char *);
601424501aSchristos static void show(FILE *, const char *);
6175088523Sabhinav static const char *skip(const char *, size_t *);
621424501aSchristos static void obsolete(char *[]);
638b0f9554Sperry static void usage(void) __dead;
6461f28255Scgd
65dbd4200bSmycroft int
main(int argc,char * argv[])661424501aSchristos main (int argc, char *argv[])
6761f28255Scgd {
6877df47bdSabhinav const char *prevp, *thisp;
69dbd4200bSmycroft FILE *ifp, *ofp;
70dbd4200bSmycroft int ch;
71dbd4200bSmycroft char *prevline, *thisline, *p;
726acd8da9Schristos size_t prevlinesize, thislinesize, psize;
7377df47bdSabhinav size_t prevlen, thislen;
7461f28255Scgd
751424501aSchristos setprogname(argv[0]);
768512105cSlukem ifp = ofp = NULL;
77dbd4200bSmycroft obsolete(argv);
78f14670a1Suwe while ((ch = getopt(argc, argv, "cdf:s:u")) != -1)
7961f28255Scgd switch (ch) {
8061f28255Scgd case 'c':
8161f28255Scgd cflag = 1;
8261f28255Scgd break;
8361f28255Scgd case 'd':
8461f28255Scgd dflag = 1;
8561f28255Scgd break;
86dbd4200bSmycroft case 'f':
87dbd4200bSmycroft numfields = strtol(optarg, &p, 10);
88dbd4200bSmycroft if (numfields < 0 || *p)
898512105cSlukem errx(1, "illegal field skip value: %s", optarg);
90dbd4200bSmycroft break;
91dbd4200bSmycroft case 's':
92dbd4200bSmycroft numchars = strtol(optarg, &p, 10);
93dbd4200bSmycroft if (numchars < 0 || *p)
948512105cSlukem errx(1, "illegal character skip value: %s",
958512105cSlukem optarg);
96dbd4200bSmycroft break;
9761f28255Scgd case 'u':
9861f28255Scgd uflag = 1;
9961f28255Scgd break;
10061f28255Scgd case '?':
10161f28255Scgd default:
10261f28255Scgd usage();
10361f28255Scgd }
10461f28255Scgd
105*364bef39Schristos argc -= optind;
10661f28255Scgd argv +=optind;
10761f28255Scgd
10861f28255Scgd switch(argc) {
10961f28255Scgd case 0:
11061f28255Scgd ifp = stdin;
11161f28255Scgd ofp = stdout;
11261f28255Scgd break;
11361f28255Scgd case 1:
11461f28255Scgd ifp = file(argv[0], "r");
11561f28255Scgd ofp = stdout;
11661f28255Scgd break;
11761f28255Scgd case 2:
11861f28255Scgd ifp = file(argv[0], "r");
11961f28255Scgd ofp = file(argv[1], "w");
12061f28255Scgd break;
12161f28255Scgd default:
12261f28255Scgd usage();
12361f28255Scgd }
12461f28255Scgd
1256acd8da9Schristos if ((p = fgetln(ifp, &psize)) == NULL)
1266acd8da9Schristos return 0;
12777df47bdSabhinav prevlinesize = prevlen = psize;
1286acd8da9Schristos if ((prevline = malloc(prevlinesize + 1)) == NULL)
1296acd8da9Schristos err(1, "malloc");
1306acd8da9Schristos (void)memcpy(prevline, p, prevlinesize);
1316acd8da9Schristos prevline[prevlinesize] = '\0';
1326acd8da9Schristos
13377df47bdSabhinav if (numfields || numchars)
13477df47bdSabhinav prevp = skip(prevline, &prevlen);
13577df47bdSabhinav else
13677df47bdSabhinav prevp = prevline;
13777df47bdSabhinav
1386acd8da9Schristos thislinesize = psize;
1396acd8da9Schristos if ((thisline = malloc(thislinesize + 1)) == NULL)
1408512105cSlukem err(1, "malloc");
141972ab0a5Sjtc
1426acd8da9Schristos while ((p = fgetln(ifp, &psize)) != NULL) {
1436acd8da9Schristos if (psize > thislinesize) {
1446acd8da9Schristos if ((thisline = realloc(thisline, psize + 1)) == NULL)
1456acd8da9Schristos err(1, "realloc");
1466acd8da9Schristos thislinesize = psize;
1476acd8da9Schristos }
14877df47bdSabhinav thislen = psize;
1496acd8da9Schristos (void)memcpy(thisline, p, psize);
1506acd8da9Schristos thisline[psize] = '\0';
15161f28255Scgd
152dbd4200bSmycroft /* If requested get the chosen fields + character offsets. */
15361f28255Scgd if (numfields || numchars) {
15477df47bdSabhinav thisp = skip(thisline, &thislen);
15561f28255Scgd } else {
15677df47bdSabhinav thisp = thisline;
15761f28255Scgd }
15861f28255Scgd
159dbd4200bSmycroft /* If different, print; set previous to new value. */
16077df47bdSabhinav if (thislen != prevlen || strcmp(thisp, prevp)) {
1611424501aSchristos char *t;
1626acd8da9Schristos size_t ts;
1636acd8da9Schristos
16461f28255Scgd show(ofp, prevline);
1651424501aSchristos t = prevline;
16661f28255Scgd prevline = thisline;
1671424501aSchristos thisline = t;
1686acd8da9Schristos ts = prevlinesize;
1696acd8da9Schristos prevlinesize = thislinesize;
1706acd8da9Schristos thislinesize = ts;
17177df47bdSabhinav prevp = thisp;
17277df47bdSabhinav prevlen = thislen;
17361f28255Scgd repeats = 0;
174dbd4200bSmycroft } else
17561f28255Scgd ++repeats;
17661f28255Scgd }
17761f28255Scgd show(ofp, prevline);
1786acd8da9Schristos free(prevline);
1796acd8da9Schristos free(thisline);
1801424501aSchristos return 0;
18161f28255Scgd }
18261f28255Scgd
18361f28255Scgd /*
18461f28255Scgd * show --
185dbd4200bSmycroft * Output a line depending on the flags and number of repetitions
18661f28255Scgd * of the line.
18761f28255Scgd */
1881424501aSchristos static void
show(FILE * ofp,const char * str)1891424501aSchristos show(FILE *ofp, const char *str)
19061f28255Scgd {
191be667101Sjtc
192d4471f49Sdholland if ((dflag && repeats == 0) || (uflag && repeats > 0))
193d4471f49Sdholland return;
194d4471f49Sdholland if (cflag) {
19561f28255Scgd (void)fprintf(ofp, "%4d %s", repeats + 1, str);
196d4471f49Sdholland } else {
19761f28255Scgd (void)fprintf(ofp, "%s", str);
19861f28255Scgd }
199d4471f49Sdholland }
20061f28255Scgd
2011424501aSchristos static const char *
skip(const char * str,size_t * linesize)20275088523Sabhinav skip(const char *str, size_t *linesize)
20361f28255Scgd {
2048512105cSlukem int infield, nchars, nfields;
20575088523Sabhinav size_t ls = *linesize;
20661f28255Scgd
20775088523Sabhinav for (nfields = numfields, infield = 0; nfields && *str; ++str, --ls)
208ad5d7c5fSchristos if (isspace((unsigned char)*str)) {
20961f28255Scgd if (infield) {
21061f28255Scgd infield = 0;
21161f28255Scgd --nfields;
21261f28255Scgd }
21361f28255Scgd } else if (!infield)
21461f28255Scgd infield = 1;
21575088523Sabhinav for (nchars = numchars; nchars-- && *str; ++str, --ls)
2161424501aSchristos continue;
21775088523Sabhinav *linesize = ls;
2181424501aSchristos return str;
21961f28255Scgd }
22061f28255Scgd
2211424501aSchristos static FILE *
file(const char * name,const char * mode)2221424501aSchristos file(const char *name, const char *mode)
22361f28255Scgd {
22461f28255Scgd FILE *fp;
22561f28255Scgd
226dbd4200bSmycroft if ((fp = fopen(name, mode)) == NULL)
2278512105cSlukem err(1, "%s", name);
22861f28255Scgd return(fp);
22961f28255Scgd }
23061f28255Scgd
2311424501aSchristos static void
obsolete(char * argv[])2321424501aSchristos obsolete(char *argv[])
233dbd4200bSmycroft {
234dbd4200bSmycroft char *ap, *p, *start;
235dbd4200bSmycroft
2368512105cSlukem while ((ap = *++argv) != NULL) {
237dbd4200bSmycroft /* Return if "--" or not an option of any form. */
238dbd4200bSmycroft if (ap[0] != '-') {
239dbd4200bSmycroft if (ap[0] != '+')
240dbd4200bSmycroft return;
241dbd4200bSmycroft } else if (ap[1] == '-')
242dbd4200bSmycroft return;
243ad5d7c5fSchristos if (!isdigit((unsigned char)ap[1]))
244dbd4200bSmycroft continue;
245dbd4200bSmycroft /*
246dbd4200bSmycroft * Digit signifies an old-style option. Malloc space for dash,
247dbd4200bSmycroft * new option and argument.
248dbd4200bSmycroft */
2491424501aSchristos (void)asprintf(&p, "-%c%s", ap[0] == '+' ? 's' : 'f', ap + 1);
250eea9ee07Sitojun if (!p)
2518512105cSlukem err(1, "malloc");
252eea9ee07Sitojun start = p;
253dbd4200bSmycroft *argv = start;
254dbd4200bSmycroft }
255dbd4200bSmycroft }
256dbd4200bSmycroft
2571424501aSchristos static void
usage(void)2581424501aSchristos usage(void)
25961f28255Scgd {
2605559f432Swiz (void)fprintf(stderr, "usage: %s [-cdu] [-f fields] [-s chars] "
2615559f432Swiz "[input_file [output_file]]\n", getprogname());
26261f28255Scgd exit(1);
26361f28255Scgd }
264