My latest ramblings.
[oweals/busybox.git] / coreutils / uniq.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini uniq implementation for busybox
4  *
5  *
6  * Copyright (C) 1999 by Lineo, inc.
7  * Written by John Beppu <beppu@lineo.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  */
24
25 #include "internal.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29
30 static const char uniq_usage[] =
31         "uniq [OPTION]... [INPUT [OUTPUT]]\n"
32         "Discard all but one of successive identical lines from INPUT (or\n"
33         "standard input), writing to OUTPUT (or standard output).\n"
34         "\n"
35         "\t-h\tdisplay this help and exit\n"
36
37         "\n"
38         "A field is a run of whitespace, then non-whitespace characters.\n"
39         "Fields are skipped before chars.\n";
40
41 /* max chars in line */
42 #define UNIQ_MAX 4096
43
44 typedef void (Print) (FILE *, const char *);
45
46 typedef int (Decide) (const char *, const char *);
47
48 /* container for two lines to be compared */
49 typedef struct {
50         char *a;
51         char *b;
52         int recurrence;
53         FILE *in;
54         FILE *out;
55         void *func;
56 } Subject;
57
58 /* set up all the variables of a uniq operation */
59 static Subject *subject_init(Subject * self, FILE * in, FILE * out,
60                                                          void *func)
61 {
62         self->a = NULL;
63         self->b = NULL;
64         self->in = in;
65         self->out = out;
66         self->func = func;
67         self->recurrence = 0;
68         return self;
69 }
70
71 /* point a and b to the appropriate lines;
72  * count the recurrences (if any) of a string;
73  */
74 static Subject *subject_next(Subject * self)
75 {
76         /* tmp line holders */
77         static char line[2][UNIQ_MAX];
78         static int alternator = 0;
79
80         if (fgets(line[alternator], UNIQ_MAX, self->in)) {
81                 self->a = self->b;
82                 self->b = line[alternator];
83                 alternator ^= 1;
84                 return self;
85         }
86
87         return NULL;
88 }
89
90 static Subject *subject_last(Subject * self)
91 {
92         self->a = self->b;
93         self->b = NULL;
94         return self;
95 }
96
97 static Subject *subject_study(Subject * self)
98 {
99         if (self->a == NULL) {
100                 return self;
101         }
102         if (self->b == NULL) {
103                 fprintf(self->out, "%s", self->a);
104                 return self;
105         }
106         if (strcmp(self->a, self->b) == 0) {
107                 self->recurrence++;
108         } else {
109                 fprintf(self->out, "%s", self->a);
110                 self->recurrence = 0;
111         }
112         return self;
113 }
114
115 static int
116 set_file_pointers(int schema, FILE ** in, FILE ** out, char **argv)
117 {
118         switch (schema) {
119         case 0:
120                 *in = stdin;
121                 *out = stdout;
122                 break;
123         case 1:
124                 *in = fopen(argv[0], "r");
125                 *out = stdout;
126                 break;
127         case 2:
128                 *in = fopen(argv[0], "r");
129                 *out = fopen(argv[1], "w");
130                 break;
131         }
132         if (*in == NULL) {
133                 fprintf(stderr, "uniq: %s: %s\n", argv[0], strerror(errno));
134                 return errno;
135         }
136         if (*out == NULL) {
137                 fprintf(stderr, "uniq: %s: %s\n", argv[1], strerror(errno));
138                 return errno;
139         }
140         return 0;
141 }
142
143
144 /* one variable is the decision algo */
145 /* another variable is the printing algo */
146
147 /* I don't think I have to have more than a 1 line memory 
148    this is the one constant */
149
150 /* it seems like GNU/uniq only takes one or two files as an option */
151
152 /* ________________________________________________________________________ */
153 int uniq_main(int argc, char **argv)
154 {
155         int i;
156         char opt;
157         FILE *in, *out;
158         Subject s;
159
160         /* parse argv[] */
161         for (i = 1; i < argc; i++) {
162                 if (argv[i][0] == '-') {
163                         opt = argv[i][1];
164                         switch (opt) {
165                         case '-':
166                         case 'h':
167                                 usage(uniq_usage);
168                         default:
169                                 usage(uniq_usage);
170                         }
171                 } else {
172                         break;
173                 }
174         }
175
176         /* 0 src: stdin; dst: stdout */
177         /* 1 src: file;  dst: stdout */
178         /* 2 src: file;  dst: file   */
179         if (set_file_pointers((argc - 1), &in, &out, &argv[i])) {
180                 exit(1);
181         }
182
183         subject_init(&s, in, out, NULL);
184         while (subject_next(&s)) {
185                 subject_study(&s);
186         }
187         subject_last(&s);
188         subject_study(&s);
189
190         exit(0);
191 }
192
193 /* $Id: uniq.c,v 1.7 2000/02/08 19:58:47 erik Exp $ */