Merge branch 'vendor/LIBEDIT'
[dragonfly.git] / usr.bin / sort / init.c
1 /*      $NetBSD: init.c,v 1.27 2010/06/06 00:00:33 wiz Exp $    */
2
3 /*-
4  * Copyright (c) 2000-2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Ben Harris and Jaromir Dolecek.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /*-
33  * Copyright (c) 1993
34  *      The Regents of the University of California.  All rights reserved.
35  *
36  * This code is derived from software contributed to Berkeley by
37  * Peter McIlroy.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63
64 #include "sort.h"
65
66 #include <ctype.h>
67 #include <string.h>
68
69 static void insertcol(struct field *);
70 static const char *setcolumn(const char *, struct field *);
71
72 /*
73  * masks of ignored characters.
74  */
75 static u_char dtable[NBINS], itable[NBINS];
76
77 /*
78  * parsed key options
79  */
80 struct coldesc *clist = NULL;
81 int ncols = 0;
82
83 /*
84  * clist (list of columns which correspond to one or more icol or tcol)
85  * is in increasing order of columns.
86  * Fields are kept in increasing order of fields.
87  */
88
89 /* 
90  * keep clist in order--inserts a column in a sorted array
91  */
92 static void
93 insertcol(struct field *field)
94 {
95         int i;
96         struct coldesc *p;
97
98         /* Make space for new item */
99         p = realloc(clist, (ncols + 2) * sizeof(*clist));
100         if (!p)
101                 err(1, "realloc");
102         clist = p;
103         memset(&clist[ncols], 0, sizeof(clist[ncols]));
104
105         for (i = 0; i < ncols; i++)
106                 if (field->icol.num <= clist[i].num)
107                         break;
108         if (field->icol.num != clist[i].num) {
109                 memmove(clist+i+1, clist+i, sizeof(COLDESC)*(ncols-i));
110                 clist[i].num = field->icol.num;
111                 ncols++;
112         }
113         if (field->tcol.num && field->tcol.num != field->icol.num) {
114                 for (i = 0; i < ncols; i++)
115                         if (field->tcol.num <= clist[i].num)
116                                 break;
117                 if (field->tcol.num != clist[i].num) {
118                         memmove(clist+i+1, clist+i,sizeof(COLDESC)*(ncols-i));
119                         clist[i].num = field->tcol.num;
120                         ncols++;
121                 }
122         }
123 }
124
125 /*
126  * matches fields with the appropriate columns--n^2 but who cares?
127  */
128 void
129 fldreset(struct field *fldtab)
130 {
131         int i;
132
133         fldtab[0].tcol.p = clist + ncols - 1;
134         for (++fldtab; fldtab->icol.num; ++fldtab) {
135                 for (i = 0; fldtab->icol.num != clist[i].num; i++)
136                         ;
137                 fldtab->icol.p = clist + i;
138                 if (!fldtab->tcol.num)
139                         continue;
140                 for (i = 0; fldtab->tcol.num != clist[i].num; i++)
141                         ;
142                 fldtab->tcol.p = clist + i;
143         }
144 }
145
146 /*
147  * interprets a column in a -k field
148  */
149 static const char *
150 setcolumn(const char *pos, struct field *cur_fld)
151 {
152         struct column *col;
153         char *npos;
154         int tmp;
155         col = cur_fld->icol.num ? (&cur_fld->tcol) : (&cur_fld->icol);
156         col->num = (int) strtol(pos, &npos, 10);
157         pos = npos;
158         if (col->num <= 0 && !(col->num == 0 && col == &(cur_fld->tcol)))
159                 errx(2, "field numbers must be positive");
160         if (*pos == '.') {
161                 if (!col->num)
162                         errx(2, "cannot indent end of line");
163                 ++pos;
164                 col->indent = (int) strtol(pos, &npos, 10);
165                 pos = npos;
166                 if (&cur_fld->icol == col)
167                         col->indent--;
168                 if (col->indent < 0)
169                         errx(2, "illegal offset");
170         }
171         for(; (tmp = optval(*pos, cur_fld->tcol.num)); pos++)
172                 cur_fld->flags |= tmp;
173         if (cur_fld->icol.num == 0)
174                 cur_fld->icol.num = 1;
175         return (pos);
176 }
177
178 int
179 setfield(const char *pos, struct field *cur_fld, int gflag)
180 {
181         cur_fld->mask = NULL;
182
183         pos = setcolumn(pos, cur_fld);
184         if (*pos == '\0')                       /* key extends to EOL. */
185                 cur_fld->tcol.num = 0;
186         else {
187                 if (*pos != ',')
188                         errx(2, "illegal field descriptor");
189                 setcolumn((++pos), cur_fld);
190         }
191         if (!cur_fld->flags)
192                 cur_fld->flags = gflag;
193         if (REVERSE)
194                 /* A local 'r' doesn't invert the global one */
195                 cur_fld->flags &= ~R;
196
197         /* Assign appropriate mask table and weight table. */
198         cur_fld->weights = weight_tables[cur_fld->flags & (R | F)];
199         if (cur_fld->flags & I)
200                 cur_fld->mask = itable;
201         else if (cur_fld->flags & D)
202                 cur_fld->mask = dtable;
203
204         cur_fld->flags |= (gflag & (BI | BT));
205         if (!cur_fld->tcol.indent)      /* BT has no meaning at end of field */
206                 cur_fld->flags &= ~BT;
207
208         if (cur_fld->tcol.num
209             && !(!(cur_fld->flags & BI) && cur_fld->flags & BT)
210             && (cur_fld->tcol.num <= cur_fld->icol.num
211                     /* indent if 0 -> end of field, i.e. okay */
212                     && cur_fld->tcol.indent != 0
213                     && cur_fld->tcol.indent < cur_fld->icol.indent))
214                 errx(2, "fields out of order");
215
216         insertcol(cur_fld);
217         return (cur_fld->tcol.num);
218 }
219
220 int
221 optval(int desc, int tcolflag)
222 {
223         switch(desc) {
224         case 'b':
225                 if (!tcolflag)
226                         return BI;
227                 else
228                         return BT;
229         case 'd': return D;
230         case 'f': return F;
231         case 'i': return I;
232         case 'l': return L;
233         case 'n': return N;
234         case 'r': return R;
235         default:  return 0;
236         }
237 }
238
239 /*
240  * Return true if the options found in ARG, according to the getopt
241  * spec in OPTS, require an additional argv word as an option
242  * argument.
243  */
244 static int
245 options_need_argument(const char *arg, const char *opts)
246 {
247         size_t pos;
248         const char *s;
249
250         /*assert(arg[0] == '-');*/
251
252         pos = 1;
253         while (arg[pos]) {
254                 s = strchr(opts, arg[pos]);
255                 if (s == NULL) {
256                         /* invalid option */
257                         return 0;
258                 }
259                 if (s[1] == ':') {
260                         /* option requires argument */
261                         if (arg[pos+1] == '\0') {
262                                 /* no argument in this arg */
263                                 return 1;
264                         }
265                         else {
266                                 /* argument is in this arg; no more options */
267                                 return 0;
268                         }
269                 }
270                 pos++;
271         }
272         return 0;
273 }
274
275 /*
276  * Replace historic +SPEC arguments with appropriate -kSPEC.
277  *
278  * The form can be either a single +SPEC or a pair +SPEC -SPEC.
279  * The following -SPEC is not recognized unless it follows
280  * immediately.
281  */ 
282 void
283 fixit(int *argc, char **argv, const char *opts)
284 {
285         int i, j, sawplus;
286         char *vpos, *tpos, spec[20];
287         int col, indent;
288
289         sawplus = 0;
290         for (i = 1; i < *argc; i++) {
291                 /*
292                  * This loop must stop exactly where getopt will stop.
293                  * Otherwise it turns e.g. "sort x +3" into "sort x
294                  * -k4.1", which will croak if +3 was in fact really a
295                  * file name. In order to do this reliably we need to
296                  * be able to identify argv words that are option
297                  * arguments.
298                  */
299
300                 if (!strcmp(argv[i], "--")) {
301                         /* End of options; stop. */
302                         break;
303                 }
304
305                 if (argv[i][0] == '+') {
306                         /* +POS argument */
307                         sawplus = 1;
308                 } else if (argv[i][0] == '-' && sawplus &&
309                            isdigit((unsigned char)argv[i][1])) {
310                         /* -POS argument */
311                         sawplus = 0;
312                 } else if (argv[i][0] == '-') {
313                         /* other option */
314                         sawplus = 0;
315                         if (options_need_argument(argv[i], opts)) {
316                                 /* skip over the argument */
317                                 i++;
318                         }
319                         continue;
320                 } else {
321                         /* not an option at all; stop */
322                         sawplus = 0;
323                         break;
324                 }
325
326                 /*
327                  * At this point argv[i] is an old-style spec. The
328                  * sawplus flag used by the above loop logic also
329                  * tells us if it's a +SPEC or -SPEC.
330                  */
331                 
332                 /* parse spec */
333                 tpos = argv[i]+1;
334                 col = (int)strtol(tpos, &tpos, 10);
335                 if (*tpos == '.') {
336                         ++tpos;
337                         indent = (int) strtol(tpos, &tpos, 10);
338                 } else
339                         indent = 0;
340                 /* tpos now points to the optional flags */
341
342                 /*
343                  * In the traditional form, x.0 means beginning of line;
344                  * in the new form, x.0 means end of line. Adjust the
345                  * value of INDENT accordingly.
346                  */
347                 if (sawplus) {
348                         /* +POS */
349                         col += 1;
350                         indent += 1;
351                 } else {
352                         /* -POS */
353                         if (indent > 0)
354                                 col += 1;
355                 }
356
357                 /* make the new style spec */
358                 snprintf(spec, sizeof(spec), "%d.%d%s", col, indent, tpos);
359
360                 if (sawplus) {
361                         /* Replace the +POS argument with new-style -kSPEC */
362                         asprintf(&vpos, "-k%s", spec);
363                         argv[i] = vpos;
364                 } else {
365                         /*
366                          * Append the spec to the one from the
367                          * preceding +POS argument, and remove the
368                          * current argv element entirely.
369                          */
370                         asprintf(&vpos, "%s,%s", argv[i-1], spec);
371                         free(argv[i-1]);
372                         argv[i-1] = vpos;
373                         for (j=i; j < *argc; j++)
374                                 argv[j] = argv[j+1];
375                         *argc -= 1;
376                         i--;
377                 }
378         }
379 }
380
381 /*
382  * ascii, Rascii, Ftable, and RFtable map
383  *
384  * Sorting 'weight' tables.
385  * Convert 'ascii' characters into their sort order.
386  * The 'F' variants fold lower case to upper equivalent
387  * The 'R' variants are for reverse sorting.
388  *
389  * The record separator (REC_D) never needs a weight, this frees one
390  * byte value as an 'end of key' marker. This must be 0 for normal
391  * weight tables, and 0xff for reverse weight tables - and is used
392  * to terminate keys so that short keys sort before (after if reverse)
393  * longer keys.
394  *
395  * The field separator has a normal weight - although it cannot occur
396  * within a key unless it is the default (space+tab).
397  *
398  * All other bytes map to the appropriate value for the sort order.
399  * Numeric sorts don't need any tables, they are reversed by negation.
400  *
401  * Global reverse sorts are done by writing the sorted keys in reverse
402  * order - the sort itself is stil forwards.
403  * This means that weights are only ever used when generating keys, any
404  * sort of the original data bytes is always forwards and unweighted.
405  *
406  * Note: this is only good for ASCII sorting.  For different LC 's,
407  * all bets are off.
408  *
409  * itable[] and dtable[] are the masks for -i (ignore non-printables)
410  * and -d (only sort blank and alphanumerics).
411  */
412 void
413 settables(void)
414 {
415         int i;
416         int next_weight = 1;
417         int rev_weight = 254;
418
419         ascii[REC_D] = 0;
420         Rascii[REC_D] = 255;
421         Ftable[REC_D] = 0;
422         RFtable[REC_D] = 255;
423
424         for (i = 0; i < 256; i++) {
425                 if (i == REC_D)
426                         continue;
427                 ascii[i] = next_weight;
428                 Rascii[i] = rev_weight;
429                 if (Ftable[i] == 0) {
430                         Ftable[i] = next_weight;
431                         RFtable[i] = rev_weight;
432                         Ftable[tolower(i)] = next_weight;
433                         RFtable[tolower(i)] = rev_weight;
434                 }
435                 next_weight++;
436                 rev_weight--;
437
438                 if (i == '\n' || isprint(i))
439                         itable[i] = 1;
440
441                 if (i == '\n' || i == '\t' || i == ' ' || isalnum(i))
442                         dtable[i] = 1;
443         }
444 }