uniq.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
uniq.c (2526B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include "text.h"
8 #include "util.h"
9
10 static const char *countfmt = "";
11 static int dflag = 0;
12 static int uflag = 0;
13 static int fskip = 0;
14 static int sskip = 0;
15
16 static struct line prevl;
17 static ssize_t prevoff = -1;
18 static long prevlinecount = 0;
19
20 static size_t
21 uniqskip(struct line *l)
22 {
23 size_t i;
24 int f = fskip, s = sskip;
25
26 for (i = 0; i < l->len && f; --f) {
27 while (isblank(l->data[i]))
28 i++;
29 while (i < l->len && !isblank(l->data[i]))
30 i++;
31 }
32 for (; s && i < l->len && l->data[i] != '\n'; --s, i++)
33 ;
34
35 return i;
36 }
37
38 static void
39 uniqline(FILE *ofp, struct line *l)
40 {
41 size_t loff;
42
43 if (l) {
44 loff = uniqskip(l);
45
46 if (prevoff >= 0 && (l->len - loff) == (prevl.len - prevoff) &&
47 !memcmp(l->data + loff, prevl.data + prevoff, l->len - loff)) {
48 ++prevlinecount;
49 return;
50 }
51 }
52
53 if (prevoff >= 0) {
54 if ((prevlinecount == 1 && !dflag) ||
55 (prevlinecount != 1 && !uflag)) {
56 if (*countfmt)
57 fprintf(ofp, countfmt, prevlinecount);
58 fwrite(prevl.data, 1, prevl.len, ofp);
59 }
60 prevoff = -1;
61 }
62
63 if (l) {
64 if (!prevl.data || l->len >= prevl.len) {
65 prevl.data = erealloc(prevl.data, l->len);
66 }
67 prevl.len = l->len;
68 memcpy(prevl.data, l->data, prevl.len);
69 prevoff = loff;
70 }
71 prevlinecount = 1;
72 }
73
74 static void
75 uniq(FILE *fp, FILE *ofp)
76 {
77 static struct line line;
78 static size_t size;
79 ssize_t len;
80
81 while ((len = getline(&line.data, &size, fp)) > 0) {
82 line.len = len;
83 uniqline(ofp, &line);
84 }
85 }
86
87 static void
88 uniqfinish(FILE *ofp)
89 {
90 uniqline(ofp, NULL);
91 }
92
93 static void
94 usage(void)
95 {
96 eprintf("usage: %s [-c] [-d | -u] [-f fields] [-s chars]"
97 " [input [output]]\n", argv0);
98 }
99
100 int
101 main(int argc, char *argv[])
102 {
103 FILE *fp[2] = { stdin, stdout };
104 int ret = 0, i;
105 char *fname[2] = { "<stdin>", "<stdout>" };
106
107 ARGBEGIN {
108 case 'c':
109 countfmt = "%7ld ";
110 break;
111 case 'd':
112 dflag = 1;
113 break;
114 case 'u':
115 uflag = 1;
116 break;
117 case 'f':
118 fskip = estrtonum(EARGF(usage()), 0, INT_MAX);
119 break;
120 case 's':
121 sskip = estrtonum(EARGF(usage()), 0, INT_MAX);
122 break;
123 default:
124 usage();
125 } ARGEND
126
127 if (argc > 2)
128 usage();
129
130 for (i = 0; i < argc; i++) {
131 if (strcmp(argv[i], "-")) {
132 fname[i] = argv[i];
133 if (!(fp[i] = fopen(argv[i], (i == 0) ? "r" : "w")))
134 eprintf("fopen %s:", argv[i]);
135 }
136 }
137
138 uniq(fp[0], fp[1]);
139 uniqfinish(fp[1]);
140
141 ret |= fshut(fp[0], fname[0]) | fshut(fp[1], fname[1]);
142
143 return ret;
144 }