addrutil: Add select operators -n and -z
[wk-misc.git] / addrutil.c
1 /* [addrutil.c wk 07.03.97] Tool to mess with address lists
2  *      Copyright (c) 1997, 2003, 2014 Werner Koch (dd9jn)
3  *      Copyright (C) 2000 OpenIT GmbH
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  */
19
20 /* $Id: addrutil.c,v 1.5 2005-07-13 13:25:32 werner Exp $ */
21
22 /* How to use:
23
24 This utility works on databases using this format:  Plain text file,
25 a hash mark in the first column denotes a comment line.  Fieldnames must start
26 with a letter in the first column and be terminated by a colon.  The
27 value of a fields starts after the colon, leding white spaces are ignored,
28 the value may be continued on the next line by prepending it with at least
29 one white space.  The first fieldname in a file acts as the record separator.
30 Fieldnames are case insensitive, duplkicated fieldnames are allowed (but not
31 for the first field) and inetrnally index by appendig a number.
32 Here is an example:
33 == addr.db ==============
34 # My address database (this is a comment)
35
36 Name: Alyssa Hacker
37 Email: alyssa@foo.net
38 Street: Cambridge Road 1
39 City: Foovillage
40
41 Name: Ben Bitfiddle
42 Street: Emacs Road 20
43 City: 234567  Gnutown
44 Email: ben@bar.org
45 Phone: 02222-33333
46
47 ===========================================
48
49 This tool may be used to insert the values into a text or TeX file.
50 Here is an example for such an TeX template:
51
52 == letter.tex ========
53 \documentclass[a4paper]{letter}
54 \usepackage[latin1]{inputenc}
55 \usepackage{ifthen}
56 \usepackage{german}
57 \begin{document}
58
59 %% the next line contains a pseudo field which marks the
60 %% start of a block which will be repeated for each record from the
61 %% database.  You may want to view this as a "while (not-database); do"
62 % @@begin-record-block@@
63
64 \begin{letter}{
65 @@Name@@ \\
66 @@Street@@ \\
67 @@City@@
68 }
69
70 \opening{Dear @@Email@@}
71 %% The variable substitution above knows about a special format, e.g.:
72 %% @@Email:N=,@@
73 %% where included linefeeds are replaced by the string after the equal
74 %% sign.
75
76 we are glad to invite you to our annual bit party at the Bit breweries,
77 located in Bitburg/Eifel.
78
79 \closing{Happy hacking}
80
81 \end{letter}
82
83 %% We are ready with this one record, and start over with the next one.
84 %% You may want to view this next-record statement as the closing "done"
85 %% done statement for the above while.
86 % @@next-record@@
87
88 \end{document}
89 ======================
90
91 To send this letter to all the folks from the addr.db you whould use these
92 commands:
93
94   addrutil -T letter.tex addr.db >a.tex && latex a.tex
95
96
97 To convert regular CSV data into our record format you may use this:
98
99   addrutil --readcsv FILENAME
100
101 Add any number of -F options to name the fields.  To select only
102 unique records the -u option may be used.  Example:
103
104   addrutil -f3 -u Name DATA
105
106 will output a new version of DATA where only the latest records with
107 the same value in the Name field are included.  To select only the
108 first record use
109
110   addrutil -f3 -u Name/r DATA
111
112
113 The file may also be sorted on one field:
114
115   addrutil -f3 -s Name DATA
116
117 this sorts the file on field Name.  If no argument to -s is given, the
118 first encountered field is used.  To reverse the sort order use:
119
120   addrutil -f3 -s Name/r DATA
121
122 To use a numeric sort use:
123
124   addrutil -f3 -s Name/n DATA
125
126 Both flags may also be combined.  Currently sorting is only possible
127 on one field; future versions of this tool may allow to add more than
128 one field.  Sorting works only on a regular file and requires that the
129 file does not change during an addrutil run.
130
131  */
132
133 #include <stdio.h>
134 #include <stdlib.h>
135 #include <string.h>
136 #include <stdarg.h>
137 #include <errno.h>
138 #include <ctype.h>
139
140 #define PGMNAME "addrutil"
141 #define VERSION "0.71"
142 #define FIELDNAMELEN 40         /* max. length of a fieldname */
143
144 #ifdef __GNUC__
145 #define INLINE __inline__
146 #else
147 #define INLINE
148 #endif
149
150
151 typedef enum {
152   SELECT_SAME,
153   SELECT_NOTSAME,
154   SELECT_SUB,
155   SELECT_NOTSUB,
156   SELECT_EMPTY,
157   SELECT_NOTEMPTY,
158   SELECT_EQ, /* Numerically equal.  */
159   SELECT_NE, /* Numerically not equal.  */
160   SELECT_LE,
161   SELECT_GE,
162   SELECT_LT,
163   SELECT_GT
164 } select_op_t;
165
166
167 #define SORTFLAG_REVERSE  1
168 #define SORTFLAG_NUMERIC  2
169
170 typedef struct outfield_struct
171 {
172   struct outfield_struct *next;
173   int flags;
174   char name[1];
175 } *OUTFIELD;
176
177
178 typedef struct selectexpr_struct
179 {
180   struct selectexpr_struct *next;
181   select_op_t op;
182   const char *value;  /* Points into NAME.  */
183   long numvalue;
184   char name[1];
185 } *SELECTEXPR;
186
187
188 static struct
189 {
190   int verbose;
191   int debug;
192   int checkonly;
193   int readcsv;
194   int format;
195   int texfile;
196   const char *template;
197   int sortmode;
198   int uniqmode;
199   OUTFIELD outfields;
200   SELECTEXPR selectexpr;
201   OUTFIELD sortfields;
202 } opt;
203
204
205 typedef struct data_struct
206 {
207   struct data_struct *next;
208   int activ;                    /* True if slot is in use. */
209   int index;                    /* Index number of this item.  */
210   size_t size;                  /* Available length of D. */
211   size_t used;                  /* Used length of D. */
212   char d[1];                    /* (this is not a string) */
213 } *DATA;
214
215 static DATA unused_data;        /* LL of unused data blocks. */
216
217 typedef struct field_struct
218 {
219   struct field_struct *nextfield;
220   int valid;                    /* In current record.  */
221   DATA data;                    /* Data storage for this field.  */
222   char name[1];                 /* Extended to the correct length.  */
223 } *FIELD;
224
225
226 typedef struct sort_struct
227 {
228   struct sort_struct *next;
229   long offset;                  /* of the record.  */
230   char d[1];                    /* Concatenated data used for sort.  */
231 } *SORT;
232
233
234 typedef struct namebucket_struct
235 {
236   struct namebucket_struct *next;
237   FIELD ptr;
238 } *NAMEBUCKET;
239
240 #define NO_NAMEBUCKETS 51
241 static NAMEBUCKET namebuckets[NO_NAMEBUCKETS];
242
243
244 static FIELD fieldlist;         /* Description of the record. */
245                                 /* The first field ist the record marker.  */
246 static FIELD next_field;        /* Used by GetFirst/NextField(). */
247 static OUTFIELD next_outfield;
248 static SORT sortlist;           /* Used when sortmode or uniqmode is activ.*/
249 static ulong output_count;
250 static long start_of_record;    /* Fileoffset of the current record.  */
251 static int new_record_flag;
252 static struct
253 {
254   FILE *fp;
255   int in_record_block;
256   long begin_block;
257   long end_block;
258 } tex;
259
260
261 typedef struct
262 {
263   int *argc;                    /* Pointer to argc (value subject to change) */
264   char ***argv;                 /* Pointer to argv (value subject to change) */
265   unsigned flags;               /* Global flags (DO NOT CHANGE) */
266   int err;                      /* Print error about last option */
267                                 /*   1 = warning, 2 = abort */
268   int r_opt;                    /* Return option */
269   int r_type;                   /* Type of return value:   */
270                                 /*   0 = no argument found */
271   union
272   {
273     int ret_int;
274     long ret_long;
275     ulong ret_ulong;
276     char *ret_str;
277   } r;                          /* Return values */
278   struct
279   {
280     int index;
281     int inarg;
282     int stopped;
283     const char *last;
284   } internal;
285 } ARGPARSE_ARGS;
286
287 typedef struct
288 {
289   int short_opt;
290   const char *long_opt;
291   unsigned flags;
292   const char *description;      /* Optional option description.  */
293 } ARGPARSE_OPTS;
294
295
296 static void set_opt_arg (ARGPARSE_ARGS * arg, unsigned flags, char *s);
297 static void show_help (ARGPARSE_OPTS * opts, unsigned flags);
298 static void show_version (void);
299
300 static INLINE unsigned long hash_name (const char *s);
301 static void dump_hash_infos (void);
302 static void log_error (int rc, const char *s, ...);
303 static void process (const char *filename);
304 static void read_and_print_csv (const char *filename);
305 static FIELD store_field_name (const char *fname, long offset);
306 static DATA expand_data_slot (FIELD field, DATA data);
307 static void new_record (long);
308 static FIELD get_first_field (void);
309 static FIELD get_next_field (void);
310 static void finish_record (void);
311 static void print_format2 (int flush);
312 static void print_template (int);
313 static int process_template_op (const char *op);
314 static void do_sort (void);
315 static int do_sort_fnc (const void *arg_a, const void *arg_b);
316 static void do_uniq (void);
317
318 static const char *get_usage_str (int level);
319
320 static void
321 show_usage (int level)
322 {
323   static int sentinel = 0;
324
325   if (sentinel)
326     return;
327
328   sentinel++;
329   if (!level)
330     {
331       fputs (get_usage_str (level), stderr);
332       putc ('\n', stderr);
333       fputs (get_usage_str (31), stderr);
334       fprintf (stderr, "%s (%s)\n", get_usage_str (32), get_usage_str (24));
335       fflush (stderr);
336     }
337   else if (level == 1)
338     {
339       fputs (get_usage_str (level), stderr);
340       putc ('\n', stderr);
341       exit (1);
342     }
343   else if (level == 2)
344     {
345       puts (get_usage_str (level));
346       exit (0);
347     }
348   sentinel--;
349 }
350
351
352 static const char *
353 get_usage_str (int level)
354 {
355   const char *p;
356   switch (level)
357     {
358     case 10:
359     case 0:
360       p = "addrutil - v" VERSION "; " "Copyright (C) 2003 Werner Koch";
361       break;
362     case 13:
363       p = "addrutil";
364       break;
365     case 14:
366       p = VERSION;
367       break;
368     case 1:
369     case 11:
370       p = "Usage: addrutil [options] [files] (-h for help)";
371       break;
372     case 2:
373     case 12:
374       p =
375         "\nSyntax: addrutil [options] [files]\n"
376         "Handle address database files\n";
377       break;
378     case 19:
379       p =                       /* Footer */
380         "Format modes:  0   Colon delimited fields\n"
381         "               1   Colon delimited name=fields pairs\n"
382         "               2   `Name',`Street',`City' formatted for labels\n"
383         "               3   Addrutil format\n"
384         "               4   Semicolon delimited format\n" "";
385       break;
386     default:
387       p = "";
388     }
389   show_usage (level);
390   return p;
391 }
392
393
394 static void *
395 xmalloc (size_t n)
396 {
397   void *p = malloc (n);
398   if (!p)
399     {
400       fprintf (stderr, PGMNAME ": out of memory\n");
401       exit (2);
402     }
403   return p;
404 }
405
406 static void *
407 xcalloc (size_t n, size_t m)
408 {
409   void *p = calloc (n, m);
410   if (!p)
411     {
412       fprintf (stderr, PGMNAME ": out of memory\n");
413       exit (2);
414     }
415   return p;
416 }
417
418
419 static void
420 strip_trailing_ws (char *str)
421 {
422   char *p;
423   char *mark;
424
425   /* find last non space character */
426   for (mark = NULL, p = str; *p; p++)
427     {
428       if (isspace (*(unsigned char *) p))
429         {
430           if (!mark)
431             mark = p;
432         }
433       else
434         mark = NULL;
435     }
436   if (mark)
437     {
438       *mark = '\0';             /* remove trailing spaces */
439     }
440 }
441
442
443 /* Find string SUB in (BUFFER,BUFLEN).  */
444 static const char *
445 memstr (const void *buffer, size_t buflen, const char *sub)
446 {
447   const char *buf = buffer;
448   const char *t = buf;
449   const char *s = sub;
450   size_t n = buflen;
451
452   for (; n; t++, n--)
453     {
454       if (*t == *s)
455         {
456           for (buf = t++, buflen = n--, s++; n && *t == *s; t++, s++, n--)
457             ;
458           if (!*s)
459             return buf;
460           t = buf;
461           s = sub ;
462           n = buflen;
463         }
464     }
465   return NULL;
466 }
467
468
469
470 static int
471 arg_parse (ARGPARSE_ARGS * arg, ARGPARSE_OPTS * opts)
472 {
473   int index;
474   int argc;
475   char **argv;
476   char *s, *s2;
477   int i;
478
479   if (!(arg->flags & (1 << 15)))
480     {                           /* initialize this instance */
481       arg->internal.index = 0;
482       arg->internal.last = NULL;
483       arg->internal.inarg = 0;
484       arg->internal.stopped = 0;
485       arg->err = 0;
486       arg->flags |= 1 << 15;    /* mark initialized */
487       if (*arg->argc < 0)
488         abort ();               /*Invalid argument for ArgParse */
489     }
490   argc = *arg->argc;
491   argv = *arg->argv;
492   index = arg->internal.index;
493
494   if (arg->err)
495     {                           /* last option was erroneous */
496       /* FIXME: We could give more help on the option if opts->desription
497        * is used. Another possibility ist, to autogenerate the help
498        * from these descriptions. */
499       if (arg->r_opt == -3)
500         s = PGMNAME ": missing argument for option \"%.50s\"\n";
501       else
502         s = PGMNAME ": invalid option \"%.50s\"\n";
503       fprintf (stderr, s, arg->internal.last ? arg->internal.last : "[??]");
504       if (arg->err != 1)
505         exit (2);
506       arg->err = 0;
507     }
508
509   if (!index && argc && !(arg->flags & (1 << 4)))
510     {                           /* skip the first entry */
511       argc--;
512       argv++;
513       index++;
514     }
515
516  next_one:
517   if (!argc)
518     {                           /* no more args */
519       arg->r_opt = 0;
520       goto leave;               /* ready */
521     }
522
523   s = *argv;
524   arg->internal.last = s;
525
526   if (arg->internal.stopped && (arg->flags & (1 << 1)))
527     {
528       arg->r_opt = -1;          /* not an option but a argument */
529       arg->r_type = 2;
530       arg->r.ret_str = s;
531       argc--;
532       argv++;
533       index++;                  /* set to next one */
534     }
535   else if (arg->internal.stopped)
536     {                           /* ready */
537       arg->r_opt = 0;
538       goto leave;
539     }
540   else if (*s == '-' && s[1] == '-')
541     {                           /* long option */
542       arg->internal.inarg = 0;
543       if (!s[2] && !(arg->flags & (1 << 3)))
544         {                       /* stop option processing */
545           arg->internal.stopped = 1;
546           argc--;
547           argv++;
548           index++;
549           goto next_one;
550         }
551
552       for (i = 0; opts[i].short_opt; i++)
553         if (opts[i].long_opt && !strcmp (opts[i].long_opt, s + 2))
554           break;
555
556       if (!opts[i].short_opt && !strcmp ("help", s + 2))
557         show_help (opts, arg->flags);
558       else if (!opts[i].short_opt && !strcmp ("version", s + 2))
559         show_version ();
560       else if (!opts[i].short_opt && !strcmp ("warranty", s + 2))
561         {
562           puts (get_usage_str (10));
563           puts (get_usage_str (31));
564           exit (0);
565         }
566
567       arg->r_opt = opts[i].short_opt;
568       if (!opts[i].short_opt)
569         {
570           arg->r_opt = -2;      /* unknown option */
571           arg->r.ret_str = s + 2;
572         }
573       else if ((opts[i].flags & 7))
574         {
575           s2 = argv[1];
576           if (!s2 && (opts[i].flags & 8))
577             {                   /* no argument but it is okay */
578               arg->r_type = 0;  /* because it is optional */
579             }
580           else if (!s2)
581             {
582               arg->r_opt = -3;  /* missing argument */
583             }
584           else if (*s2 == '-' && (opts[i].flags & 8))
585             {
586               /* the argument is optional and the next seems to be
587                * an option. We do not check this possible option
588                * but assume no argument */
589               arg->r_type = 0;
590             }
591           else
592             {
593               set_opt_arg (arg, opts[i].flags, s2);
594               argc--;
595               argv++;
596               index++;          /* skip one */
597             }
598         }
599       else
600         {                       /* does not take an argument */
601           arg->r_type = 0;
602         }
603       argc--;
604       argv++;
605       index++;                  /* set to next one */
606     }
607   else if ((*s == '-' && s[1]) || arg->internal.inarg)
608     {                           /* short option */
609       int dash_kludge = 0;
610       i = 0;
611       if (!arg->internal.inarg)
612         {
613           arg->internal.inarg++;
614           if (arg->flags & (1 << 5))
615             {
616               for (i = 0; opts[i].short_opt; i++)
617                 if (opts[i].long_opt && !strcmp (opts[i].long_opt, s + 1))
618                   {
619                     dash_kludge = 1;
620                     break;
621                   }
622             }
623         }
624       s += arg->internal.inarg;
625
626       if (!dash_kludge)
627         {
628           for (i = 0; opts[i].short_opt; i++)
629             if (opts[i].short_opt == *s)
630               break;
631         }
632
633       if (!opts[i].short_opt && *s == 'h')
634         show_help (opts, arg->flags);
635
636       arg->r_opt = opts[i].short_opt;
637       if (!opts[i].short_opt)
638         {
639           arg->r_opt = -2;              /* unknown option */
640           arg->internal.inarg++;        /* point to the next arg */
641           arg->r.ret_str = s;
642         }
643       else if ((opts[i].flags & 7))
644         {
645           if (s[1] && !dash_kludge)
646             {
647               s2 = s + 1;
648               set_opt_arg (arg, opts[i].flags, s2);
649             }
650           else
651             {
652               s2 = argv[1];
653               if (!s2 && (opts[i].flags & 8))
654                 {                       /* no argument but it is okay */
655                   arg->r_type = 0;      /* because it is optional */
656                 }
657               else if (!s2)
658                 {
659                   arg->r_opt = -3;      /* missing argument */
660                 }
661               else if (*s2 == '-' && s2[1] && (opts[i].flags & 8))
662                 {
663                   /* the argument is optional and the next seems to be
664                    * an option. We do not check this possible option
665                    * but assume no argument */
666                   arg->r_type = 0;
667                 }
668               else
669                 {
670                   set_opt_arg (arg, opts[i].flags, s2);
671                   argc--;
672                   argv++;
673                   index++;      /* skip one */
674                 }
675             }
676           s = "x";              /* so that !s[1] yields false */
677         }
678       else
679         {                       /* does not take an argument */
680           arg->r_type = 0;
681           arg->internal.inarg++;/* point to the next arg */
682         }
683       if (!s[1] || dash_kludge)
684         {                       /* no more concatenated short options */
685           arg->internal.inarg = 0;
686           argc--;
687           argv++;
688           index++;
689         }
690     }
691   else if (arg->flags & (1 << 2))
692     {
693       arg->r_opt = -1;          /* not an option but a argument */
694       arg->r_type = 2;
695       arg->r.ret_str = s;
696       argc--;
697       argv++;
698       index++;                  /* set to next one */
699     }
700   else
701     {
702       arg->internal.stopped = 1;/* stop option processing */
703       goto next_one;
704     }
705
706  leave:
707   *arg->argc = argc;
708   *arg->argv = argv;
709   arg->internal.index = index;
710   return arg->r_opt;
711 }
712
713
714
715 static void
716 set_opt_arg (ARGPARSE_ARGS * arg, unsigned flags, char *s)
717 {
718   int base = (flags & 16) ? 0 : 10;
719
720   switch (arg->r_type = (flags & 7))
721     {
722     case 1:                     /* takes int argument */
723       arg->r.ret_int = (int) strtol (s, NULL, base);
724       break;
725     default:
726     case 2:                     /* takes string argument */
727       arg->r.ret_str = s;
728       break;
729     case 3:                     /* takes long argument   */
730       arg->r.ret_long = strtol (s, NULL, base);
731       break;
732     case 4:                     /* takes ulong argument  */
733       arg->r.ret_ulong = strtoul (s, NULL, base);
734       break;
735     }
736 }
737
738 static void
739 show_help (ARGPARSE_OPTS * opts, unsigned flags)
740 {
741   const char *s;
742
743   puts (get_usage_str (10));
744   s = get_usage_str (12);
745   if (*s == '\n')
746     s++;
747   puts (s);
748   if (opts[0].description)
749     {                           /* auto format the option description */
750       int i, j, indent;
751       /* get max. length of long options */
752       for (i = indent = 0; opts[i].short_opt; i++)
753         {
754           if (opts[i].long_opt)
755             if ((j = strlen (opts[i].long_opt)) > indent && j < 35)
756               indent = j;
757         }
758       /* example: " -v, --verbose   Viele Sachen ausgeben" */
759       indent += 10;
760       puts ("Options:");
761       for (i = 0; opts[i].short_opt; i++)
762         {
763           if (opts[i].short_opt < 256)
764             printf (" -%c", opts[i].short_opt);
765           else
766             fputs ("   ", stdout);
767           j = 3;
768           if (opts[i].long_opt)
769             j += printf ("%c --%s   ", opts[i].short_opt < 256 ? ',' : ' ',
770                          opts[i].long_opt);
771           for (; j < indent; j++)
772             putchar (' ');
773           if ((s = opts[i].description))
774             {
775               for (; *s; s++)
776                 {
777                   if (*s == '\n')
778                     {
779                       if (s[1])
780                         {
781                           putchar ('\n');
782                           for (j = 0; j < indent; j++)
783                             putchar (' ');
784                         }
785                     }
786                   else
787                     putchar (*s);
788                 }
789             }
790           putchar ('\n');
791         }
792       if (flags & 32)
793         puts ("\n(A single dash may be used instead of the double ones)");
794     }
795   if ((s = get_usage_str (19)))
796     {                           /* bug reports to ... */
797       putchar ('\n');
798       fputs (s, stdout);
799     }
800   fflush (stdout);
801   exit (0);
802 }
803
804 static void
805 show_version ()
806 {
807   const char *s;
808   printf ("%s version %s (%s", get_usage_str (13), get_usage_str (14),
809           get_usage_str (45));
810   if ((s = get_usage_str (24)) && *s)
811     {
812       printf (", %s)\n", s);
813     }
814   else
815     {
816       printf (")\n");
817     }
818   fflush (stdout);
819   exit (0);
820 }
821
822 /* Supported expressions are:
823
824    [<ws>]NAME[<ws>]<op>[<ws>]VALUE[<ws>]
825
826    NAME and VALUE may not be the empty string. <ws> indicates white
827    space.  [] indicates optional parts.  If VALUE starts with one of
828    the characters used in any <op> a space after the <op> is required.
829    Valid <op> are:
830
831       =~  Substring must match
832       !~  Substring must not match
833       =   The full string must match
834       <>  The full string must not match
835       ==  The numerical value must match
836       !=  The numerical value must not match
837       <=  The numerical value of the field must be LE than the value.
838       <   The numerical value of the field must be LT than the value.
839       >=  The numerical value of the field must be GT than the value.
840       >=  The numerical value of the field must be GE than the value.
841       -n  True if value is not empty.
842       -z  True if valie is empty.
843
844       Numerical values are computed as long int.  */
845
846
847 static SELECTEXPR
848 parse_selectexpr (const char *expr)
849 {
850   SELECTEXPR se;
851   const char *s0, *s;
852
853   while (*expr == ' ' || *expr == '\t')
854     expr++;
855
856   se = xmalloc (sizeof *se + strlen (expr));
857   se->next = NULL;
858   strcpy (se->name, expr);
859
860   s = strpbrk (expr, "=<>!~-");
861   if (!s || s == expr )
862     log_error (1, "%s: no field name given for select\n", PGMNAME);
863   s0 = s;
864
865   if (!strncmp (s, "=~", 2))
866     {
867       se->op = SELECT_SUB;
868       s += 2;
869     }
870   else if (!strncmp (s, "!~", 2))
871     {
872       se->op = SELECT_NOTSUB;
873       s += 2;
874     }
875   else if (!strncmp (s, "<>", 2))
876     {
877       se->op = SELECT_NOTSAME;
878       s += 2;
879     }
880   else if (!strncmp (s, "==", 2))
881     {
882       se->op = SELECT_EQ;
883       s += 2;
884     }
885   else if (!strncmp (s, "!=", 2))
886     {
887       se->op = SELECT_NE;
888       s += 2;
889     }
890   else if (!strncmp (s, "<=", 2))
891     {
892       se->op = SELECT_LE;
893       s += 2;
894     }
895   else if (!strncmp (s, ">=", 2))
896     {
897       se->op = SELECT_GE;
898       s += 2;
899     }
900   else if (!strncmp (s, "<", 1))
901     {
902       se->op = SELECT_LT;
903       s += 1;
904     }
905   else if (!strncmp (s, ">", 1))
906     {
907       se->op = SELECT_GT;
908       s += 1;
909     }
910   else if (!strncmp (s, "=", 1))
911     {
912       se->op = SELECT_SAME;
913       s += 1;
914     }
915   else if (!strncmp (s, "-z", 2))
916     {
917       se->op = SELECT_EMPTY;
918       s += 2;
919     }
920   else if (!strncmp (s, "-n", 2))
921     {
922       se->op = SELECT_NOTEMPTY;
923       s += 2;
924     }
925   else
926     log_error (1, "%s: invalid select operator\n", PGMNAME);
927
928   /* We require that a space is used if the value starts with any of
929      the operator characters.  */
930   if (se->op == SELECT_EMPTY || se->op == SELECT_NOTEMPTY)
931     ;
932   else if (strchr ("=<>!~", *s))
933     log_error (1, "%s: invalid select operator\n", PGMNAME);
934
935   while (*s == ' ' || *s == '\t')
936     s++;
937
938   if (se->op == SELECT_EMPTY || se->op == SELECT_NOTEMPTY)
939     {
940       if (*s)
941         log_error (1, "%s: value given for -n or -z\n", PGMNAME);
942     }
943   else
944     {
945       if (!*s)
946         log_error (1, "%s: no value given for select\n", PGMNAME);
947     }
948
949   se->name[s0 - expr] = 0;
950   strip_trailing_ws (se->name);
951   if (!se->name[0])
952     log_error (1, "%s: no field name given for select\n", PGMNAME);
953
954   strip_trailing_ws (se->name + (s - expr));
955   se->value = se->name + (s - expr);
956   if (!se->value[0] && !(se->op == SELECT_EMPTY || se->op == SELECT_NOTEMPTY))
957     log_error (1, "%s: no value given for select\n", PGMNAME);
958
959   se->numvalue = strtol (se->value, NULL, 10);
960
961   return se;
962 }
963
964
965 static void
966 add_sortfield (ARGPARSE_ARGS *pargs)
967 {
968   OUTFIELD of, of2;
969   char *p;
970
971   if (pargs->r_type)
972     {
973       of = xmalloc (sizeof *of + strlen (pargs->r.ret_str));
974       of->next = NULL;
975       of->flags = 0;
976       strcpy (of->name, pargs->r.ret_str);
977       p = strchr (of->name, '/');
978       if (p)
979         {
980           *p++ = 0;
981           for (; *p; p++)
982             {
983               switch (*p)
984                 {
985                 case 'r': of->flags |= SORTFLAG_REVERSE; break;
986                 case 'n': of->flags |= SORTFLAG_NUMERIC; break;
987                 default:
988                   fprintf (stderr, PGMNAME
989                            ": warning: unknown sort flag in '%s'\n",
990                            pargs->r.ret_str);
991                   break;
992                 }
993             }
994         }
995
996       if (!(of2 = opt.sortfields))
997         opt.sortfields = of;
998       else
999         {
1000           for (; of2->next; of2 = of2->next)
1001             ;
1002           of2->next = of;
1003         }
1004     }
1005 }
1006
1007
1008 int
1009 main (int argc, char **argv)
1010 {
1011   ARGPARSE_OPTS opts[] = {
1012     {'f', "format", 1, "use output format N"},
1013     {'s', "sort", 2|8, "sort the file"}, /* Takes an optional argument.  */
1014     {'u', "uniq", 2|8, "uniq the file"},
1015     {'S', "select", 2, "output records matching expression" },
1016     {'F', "field", 2, "output this field"},
1017     {'t', "template", 2, "use text file as template"},
1018     {'T', "tex-file", 2, "use TeX file as template"},
1019     {'c', "check-only", 0, "do only a syntax check"},
1020     { 501, "readcsv",   0, "read CSV data" },
1021     {'v', "verbose", 0, "verbose"},
1022     {'d', "debug", 0, "increase the debug level"},
1023     {0}
1024   };
1025   ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
1026   int org_argc;
1027   char **org_argv;
1028   OUTFIELD of, of2;
1029   SELECTEXPR se;
1030   FIELD f;
1031
1032   while (arg_parse (&pargs, opts))
1033     {
1034       switch (pargs.r_opt)
1035         {
1036         case 'v':
1037           opt.verbose++;
1038           break;
1039         case 'd':
1040           opt.debug++;
1041           break;
1042         case 'c':
1043           opt.checkonly++;
1044           break;
1045         case 's':
1046           opt.sortmode = 1;
1047           add_sortfield (&pargs);
1048           break;
1049         case 'u':
1050           opt.uniqmode = 1;
1051           add_sortfield (&pargs);
1052           break;
1053         case 'f':
1054           opt.format = pargs.r.ret_int;
1055           break;
1056         case 'T':
1057           opt.texfile = 1;
1058         case 't':
1059           opt.template = pargs.r.ret_str;
1060           break;
1061         case 'F':
1062           of = xmalloc (sizeof *of + strlen (pargs.r.ret_str));
1063           of->next = NULL;
1064           strcpy (of->name, pargs.r.ret_str);
1065           if (!(of2 = opt.outfields))
1066             opt.outfields = of;
1067           else
1068             {
1069               for (; of2->next; of2 = of2->next)
1070                 ;
1071               of2->next = of;
1072             }
1073           break;
1074         case 501:
1075           opt.readcsv = 1;
1076           break;
1077         case 'S':
1078           se = parse_selectexpr (pargs.r.ret_str);
1079           if (se)
1080             {
1081               se->next = opt.selectexpr;
1082               opt.selectexpr = se;
1083             }
1084           break;
1085         default:
1086           pargs.err = 2;
1087           break;
1088         }
1089     }
1090
1091   if (opt.selectexpr && opt.debug)
1092     {
1093       FILE *fp = stderr;
1094
1095       fputs ("--- Begin selectors ---\n", fp);
1096       for (se=opt.selectexpr; se; se = se->next)
1097         fprintf (fp, "*(%s) %s '%s'\n",
1098                  se->name,
1099                  se->op == SELECT_SAME?    "= ":
1100                  se->op == SELECT_NOTSAME? "<>":
1101                  se->op == SELECT_SUB?     "=~":
1102                  se->op == SELECT_NOTSUB?  "!~":
1103                  se->op == SELECT_EMPTY?   "-z":
1104                  se->op == SELECT_NOTEMPTY?"-n":
1105                  se->op == SELECT_EQ?      "==":
1106                  se->op == SELECT_NE?      "!=":
1107                  se->op == SELECT_LT?      "< ":
1108                  se->op == SELECT_LE?      "<=":
1109                  se->op == SELECT_GT?      "> ":
1110                  se->op == SELECT_GE?      ">=":"[oops]",
1111                  se->value);
1112       fputs ("--- End selectors ---\n", fp);
1113     }
1114
1115   if (opt.readcsv)
1116     {
1117       if (!argc)
1118         read_and_print_csv (NULL);
1119       else
1120         {
1121           for (; argc; argc--, argv++)
1122             read_and_print_csv (*argv);
1123         }
1124       exit (0);
1125     }
1126
1127   if (opt.template)
1128     {
1129       tex.fp = fopen (opt.template, "r");
1130       if (!tex.fp)
1131         {
1132           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1133                    opt.template, strerror (errno));
1134           exit (1);
1135         }
1136     }
1137
1138   if (opt.sortmode && opt.uniqmode)
1139     {
1140       fprintf (stderr,
1141                PGMNAME ": --sort and --uniq may not be used together\n");
1142       exit (1);
1143     }
1144   if ((opt.sortmode||opt.uniqmode) && argc != 1)
1145     {
1146       fprintf (stderr,
1147                PGMNAME ": sorry,"
1148                " sort and uniq is only available for one file\n");
1149       exit (1);
1150     }
1151
1152   /* Print a warning if more than one sort field is given.  FIXME: We
1153      should eventually lift that limit. */
1154   if (opt.sortfields && opt.sortfields->next)
1155     fprintf (stderr,
1156              PGMNAME ": warning: only the first sort field is used\n");
1157
1158   /* For internal purposes we set the sortmode flag with uniqmode.  */
1159   if (opt.uniqmode)
1160     opt.sortmode = 1;
1161
1162   org_argc = argc;
1163   org_argv = argv;
1164
1165  pass_two:
1166   if (!argc)
1167     process (NULL);
1168   else
1169     {
1170       for (; argc; argc--, argv++)
1171         process (*argv);
1172     }
1173
1174   if (opt.template)
1175     {
1176       if (tex.in_record_block && opt.sortmode != 1)
1177         {
1178           print_template (1);
1179         }
1180     }
1181   else if (opt.format == 2 && opt.sortmode != 1)
1182     print_format2 (1);          /* flush */
1183
1184   if (opt.sortmode == 1 && sortlist)
1185     {
1186       if (opt.uniqmode)
1187         do_uniq ();
1188       else
1189         do_sort ();
1190       argc = org_argc;
1191       argv = org_argv;
1192       opt.sortmode = 2;
1193       goto pass_two;
1194     }
1195   else if (opt.sortmode == 2)
1196     {
1197       /* FIXME: cleanup the sort infos */
1198     }
1199
1200
1201   for (se=opt.selectexpr; se; se = se->next)
1202     {
1203       for (f = fieldlist; f; f = f->nextfield)
1204         if (!strcmp (f->name, se->name))
1205           break;
1206       if (!f)
1207         fprintf (stderr, PGMNAME ": warning: "
1208                  "select field '%s' not found in data\n", se->name);
1209     }
1210
1211   if (opt.debug)
1212     {
1213       DATA d;
1214       FILE *fp = stderr;
1215       int n;
1216
1217       fputs ("--- Begin fieldlist ---\n", fp);
1218       for (f = fieldlist; f; f = f->nextfield)
1219         {
1220           n = fprintf (fp, "%.20s:", f->name);
1221           for (d = f->data; d; d = d->next)
1222             fprintf (fp, "%*s idx=%-3d used=%-3u size=%-3u %s\n",
1223                      d == f->data ? 0 : n, "", d->index,
1224                      (unsigned int)d->used, (unsigned int)d->size,
1225                      d->activ ? "activ" : "not-active");
1226           if (!f->data)
1227             putc ('\n', fp);
1228         }
1229       fputs ("--- End fieldlist ---\n", fp);
1230       if (opt.sortmode)
1231         {
1232           fputs ("--- Begin sortlist ---\n", fp);
1233           for (of = opt.sortfields; of; of = of->next)
1234             fprintf (fp, "%.20s%s%s%s\n", of->name,
1235                      of->flags? "/":"",
1236                      (of->flags & SORTFLAG_REVERSE)? "r":"",
1237                      (of->flags & SORTFLAG_NUMERIC)? "n":"");
1238           fputs ("--- End sortlist ---\n", fp);
1239         }
1240       dump_hash_infos ();
1241     }
1242   return 0;
1243 }
1244
1245
1246 static INLINE unsigned long
1247 hash_name (const char *s_arg)
1248 {
1249   const unsigned char *s = (const unsigned char*)s_arg;
1250   unsigned long hashVal = 0;
1251   unsigned long carry;
1252
1253   if (s)
1254     for (; *s; s++)
1255       {
1256         hashVal = (hashVal << 4) + toupper (*s);
1257         if ((carry = (hashVal & 0xf0000000)))
1258           {
1259             hashVal ^= (carry >> 24);
1260             hashVal ^= carry;
1261           }
1262       }
1263
1264   return hashVal % NO_NAMEBUCKETS;
1265 }
1266
1267
1268 static void
1269 dump_hash_infos ()
1270 {
1271   int i, sum, perBucket, n;
1272   NAMEBUCKET r;
1273
1274   perBucket = sum = 0;
1275   for (i = 0; i < NO_NAMEBUCKETS; i++)
1276     {
1277       for (n = 0, r = namebuckets[i]; r; r = r->next)
1278         n++;
1279       sum += n;
1280       if (n > perBucket)
1281         perBucket = n;
1282     }
1283   fprintf (stderr,
1284            "%d entries in %d hash buckets; max. %d entr%s per hash bucket\n",
1285            sum, NO_NAMEBUCKETS, perBucket, perBucket == 1 ? "y" : "ies");
1286 }
1287
1288
1289 static void
1290 log_error (int rc, const char *s, ...)
1291 {
1292   va_list arg_ptr;
1293   FILE *fp = stderr;
1294
1295   va_start (arg_ptr, s);
1296   vfprintf (fp, s, arg_ptr);
1297   putc ('\n', fp);
1298   va_end (arg_ptr);
1299   if (rc)
1300     exit (rc);
1301 }
1302
1303
1304 static void
1305 process (const char *filename)
1306 {
1307   FILE *fp;
1308   int c;
1309   unsigned long lineno = 0;
1310   long lineoff = 0;             /* offset of the current line */
1311   int newline;
1312   int comment = 0;
1313   int linewrn = 0;
1314   char fname[FIELDNAMELEN + 1];
1315   int fnameidx = 0;
1316   int index;                    /* current index */
1317   enum
1318   { sINIT,                      /* no record yet */
1319     sFIELD,                     /* inside a fieldname */
1320     sDATABEG,                   /* waiting for start of value */
1321     sDATA                       /* storing a value */
1322   } state = sINIT;
1323   FIELD f = NULL;               /* current field */
1324   DATA d = NULL;                /* current data slot */
1325   SORT sort = sortlist;
1326   int pending_lf = 0;
1327   int skip_kludge = 0;
1328
1329   if (filename)
1330     {
1331       fp = fopen (filename, "r");
1332       if (!fp)
1333         {
1334           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1335                    filename, strerror (errno));
1336           exit (1);
1337         }
1338     }
1339   else
1340     {
1341       fp = stdin;
1342       filename = "[stdin]";
1343     }
1344
1345   if (opt.sortmode == 2)  /* Sorting/uniqing has been done. */
1346     {
1347       if (!sort)
1348         return;           /* nothing to sort */
1349     next_sortrecord:
1350       if (!sort)
1351         goto ready;
1352       if (sort->offset == -1)
1353         {
1354           /* Skip deleted records.  */
1355           sort = sort->next;
1356           goto next_sortrecord;
1357         }
1358       clearerr (fp);
1359       if (fseek (fp, sort->offset, SEEK_SET))
1360         {
1361           fprintf (stderr, PGMNAME ": error seeking to %ld\n", sort->offset);
1362           exit (2);
1363         }
1364       sort = sort->next;
1365       state = sINIT;
1366       skip_kludge = 1;
1367     }
1368
1369   /* Read the file byte by byte; do not impose a limit on the
1370    * line length. Fieldnames are up to FIELDNAMELEN bytes long.
1371    */
1372   lineno++;
1373   newline = 1;
1374   while ((c = getc (fp)) != EOF)
1375     {
1376       if (c == '\n')
1377         {
1378           switch (state)
1379             {
1380             case sFIELD:
1381               log_error (2, "%s:%ld: fieldname not terminated",
1382                          filename, lineno);
1383               break;
1384             case sDATA:
1385               pending_lf++;
1386               break;
1387             default:
1388               break;
1389             }
1390           lineno++;
1391           lineoff = ftell (fp) - 1;
1392           if (lineoff == -1 && opt.sortmode)
1393             {
1394               log_error (2, "%s:%ld: ftell() failed: %s",
1395                          filename, lineno, strerror (errno));
1396               exit (1);
1397             }
1398           newline = 1;
1399           comment = 0;
1400           linewrn = 0;
1401           continue;
1402         }
1403       else if (comment)
1404         continue;
1405
1406       if (newline)
1407         {                       /* at first column */
1408           if (c == '#')
1409             comment = 1;        /* bybass the entire line */
1410           else if (c == ' ' || c == '\t')
1411             {
1412               switch (state)
1413                 {
1414                 case sINIT:
1415                   break;        /* nothing to do */
1416                 case sFIELD:
1417                   abort ();
1418                 case sDATABEG:
1419                   break;
1420                 case sDATA:
1421                   state = sDATABEG;
1422                   break;
1423                 }
1424             }
1425           else if (c == ':')
1426             log_error (2, "%s:%ld: line starts with a colon", filename, lineno);
1427           else
1428             {
1429               switch (state)
1430                 {
1431                 case sDATABEG:
1432                 case sDATA:
1433                   /*FinishField(); */
1434                   /* fall thru */
1435                 case sINIT:     /* start of a fieldname */
1436                   fnameidx = 0;
1437                   fname[fnameidx++] = c;
1438                   state = sFIELD;
1439                   break;
1440                 case sFIELD:
1441                   abort ();
1442                 }
1443             }
1444           newline = 0;
1445         }
1446       else
1447         {
1448           switch (state)
1449             {
1450             case sINIT:
1451               if (!linewrn)
1452                 {
1453                   log_error (0, "%s:%lu: warning: garbage detected",
1454                              filename, lineno);
1455                   linewrn++;
1456                 }
1457               break;
1458             case sFIELD:
1459               if (c == ':')
1460                 {
1461                   char *p;
1462
1463                   fname[fnameidx] = 0;
1464                   strip_trailing_ws (fname);
1465                   if ((p = strrchr (fname, '.')))
1466                     {
1467                       *p++ = 0;
1468                       strip_trailing_ws (fname);
1469                       index = atoi (p);
1470                       if (index < 0 || index > 255)
1471                         log_error (2, "%s:%lu: invalid index of fieldname",
1472                                    filename, lineno);
1473                     }
1474                   else
1475                     index = 0;  /* must calculate an index */
1476                   if (!*fname)
1477                     log_error (2, "%s:%lu: empty fieldname", filename, lineno);
1478                   new_record_flag = 0;
1479                   f = store_field_name (fname, lineoff);
1480                   if (opt.sortmode == 2 && new_record_flag && !skip_kludge)
1481                     goto next_sortrecord;
1482                   skip_kludge = 0;
1483                   if (!index)
1484                     { /* detect the index */
1485                       /* first a shortcut: */
1486                       if ((d = f->data) && d->index == 1 && !d->activ)
1487                         index = 1;      /* that's it */
1488                       else
1489                         { /* find the highest unused index */
1490                           for (index = 1; d;)
1491                             {
1492                               if (d->index == index)
1493                                 {
1494                                   if (d->activ)
1495                                     {
1496                                       index++;
1497                                       d = f->data;
1498                                     }
1499                                   else
1500                                     break;
1501                                 }
1502                               else
1503                                 d = d->next;
1504                             }
1505                         }
1506                     }
1507                   else
1508                     { /* find a data slot for the given index. */
1509                       for (d = f->data; d; d = d->next)
1510                         if (d->index == index)
1511                           break;
1512                       if (d && d->activ)
1513                         log_error (0, "%s:%lu: warning: %s.%d redefined",
1514                                    filename, lineno, fname, index);
1515                     }
1516                   if (!d)
1517                     { /* create a new slot */
1518                       if ((d = unused_data))
1519                         unused_data = d->next;
1520                       else
1521                         {
1522                           d = xmalloc (sizeof *d + 100);
1523                           d->size = 100 + 1;
1524                         }
1525                       d->index = index;
1526                       d->next = NULL;
1527                       if (!f->data)
1528                         f->data = d;
1529                       else
1530                         {
1531                           DATA d2;
1532                           for (d2 = f->data; d2->next; d2 = d2->next)
1533                             ;
1534                           d2->next = d;
1535                         }
1536                     }
1537                   d->activ = 1;
1538                   d->used = 0;  /* used length */
1539                   pending_lf = 0;
1540                   state = sDATABEG;
1541                 }
1542               else
1543                 {
1544                   if (fnameidx >= FIELDNAMELEN)
1545                     log_error (2, "%s:%ld: fieldname too long",
1546                                filename, lineno);
1547                   fname[fnameidx++] = c;
1548                 }
1549               break;
1550             case sDATABEG:
1551               if (c == ' ' || c == '\t')
1552                 break;
1553               state = sDATA;
1554               /* fall thru */
1555             case sDATA:
1556               if (!d)
1557                 abort ();
1558               for (; pending_lf; pending_lf--)
1559                 {
1560                   if (d->used >= d->size)
1561                     d = expand_data_slot (f, d);
1562                   d->d[d->used++] = '\n';
1563                 }
1564               if (d->used >= d->size)
1565                 d = expand_data_slot (f, d);
1566               d->d[d->used++] = c;
1567               break;
1568             } /* end switch state after first column */
1569         }
1570     }
1571   if (ferror (fp))
1572     {
1573       fprintf (stderr, PGMNAME ":%s:%lu: read error: %s\n",
1574                filename, lineno, strerror (errno));
1575       exit (2);
1576     }
1577   if (!newline)
1578     {
1579       log_error (0, "%s: warning: last line not terminated by a LF", filename);
1580     }
1581   if (opt.sortmode == 2)
1582     goto next_sortrecord;
1583
1584  ready:
1585   finish_record ();
1586   lineno--;
1587   if (opt.verbose)
1588     log_error (0, "%s: %lu line%s processed", filename, lineno,
1589                lineno == 1 ? "" : "s");
1590
1591   if (fp != stdin)
1592     fclose (fp);
1593 }
1594
1595
1596 static void
1597 read_and_print_csv (const char *filename)
1598 {
1599   FILE *fp;
1600   int c;
1601   unsigned long lineno;
1602   int newline, newfield, in_string, any_printed;
1603   int fieldidx = 0;
1604   OUTFIELD of;
1605
1606   if (filename)
1607     {
1608       fp = fopen (filename, "r");
1609       if (!fp)
1610         {
1611           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1612                    filename, strerror (errno));
1613           exit (1);
1614         }
1615     }
1616   else
1617     {
1618       fp = stdin;
1619       filename = "[stdin]";
1620     }
1621
1622   /* Read the file byte by byte; do not impose a limit on the line
1623    * length.  Fieldnames are up to FIELDNAMELEN bytes long.  */
1624   lineno  = 1;
1625   newline = 1;
1626   newfield = 0;
1627   in_string = 0;
1628   any_printed = 0;
1629   of = NULL;
1630   while ((c = getc (fp)) != EOF)
1631     {
1632       if (c == '\r')
1633         continue;
1634
1635       if (c == '\n')  /* End of line.  */
1636         {
1637           putchar ('\n');
1638           putchar ('\n');
1639           lineno++;
1640           newline = 1;
1641           newfield = 0;
1642           in_string = 0;
1643           fieldidx = 0;
1644           continue;
1645         }
1646
1647       if (newline) /* At a new record.  */
1648         {
1649           newline = 0;
1650           newfield = 1;
1651           of = opt.outfields? opt.outfields : NULL;
1652         }
1653
1654       if (newfield)
1655         {
1656           if (of)
1657             printf ("%s:", of->name);
1658           else
1659             printf ("Field_%d:", fieldidx);
1660           newfield = 0;
1661           any_printed = 0;
1662           in_string = 0;
1663         }
1664
1665       if (in_string == 1)
1666         {
1667           if (c == '\"')
1668             in_string = 2; /* Possible end of string.  */
1669           else
1670             {
1671               if (!any_printed)
1672                 {
1673                   any_printed = 1;
1674                   putchar (' ');
1675                 }
1676               putchar (c);
1677             }
1678         }
1679       else if (in_string == 2)
1680         {
1681           if (c == '\"' )
1682             {
1683               putchar ('\"');
1684               in_string = 1;
1685             }
1686           else
1687             in_string = 0;
1688         }
1689
1690       if (in_string)
1691         ;
1692       else if (c == '\"')
1693         {
1694           in_string = 1;
1695         }
1696       else if (c == ',')
1697         {
1698           putchar ('\n');
1699           fieldidx++;
1700           newfield = 1;
1701           if (of)
1702             of = of->next;
1703         }
1704       else
1705         {
1706           if (!any_printed)
1707             {
1708               any_printed = 1;
1709               putchar (' ');
1710             }
1711           putchar (c);
1712         }
1713     }
1714
1715   if (ferror (fp))
1716     {
1717       fprintf (stderr, PGMNAME ":%s:%lu: read error: %s\n",
1718                filename, lineno, strerror (errno));
1719       exit (2);
1720     }
1721   if (!newline)
1722     {
1723       log_error (0, "%s: warning: last line not terminated by a LF", filename);
1724     }
1725
1726   lineno--;
1727   if (opt.verbose)
1728     log_error (0, "%s: %lu line%s processed", filename, lineno,
1729                lineno == 1 ? "" : "s");
1730
1731   if (fp != stdin)
1732     fclose (fp);
1733 }
1734
1735
1736 /*
1737  * Handle the fieldname.
1738  *
1739  * If we already have a field with this name in the current record, we
1740  * append a counter to the field (e.g. "Phone.1", "Phone.2", ... )
1741  * where a counter of 1 is same as the field without a count.  Field
1742  * names are NOT case sensitive.  Index 0 means: Calculate an index if
1743  * this is an unknown field.
1744  *
1745  * Returns: a pointer to the field
1746  */
1747 static FIELD
1748 store_field_name (const char *fname, long offset)
1749 {
1750   unsigned long hash;
1751   NAMEBUCKET buck;
1752   FIELD fdes, f2;
1753
1754   for (buck = namebuckets[hash = hash_name (fname)]; buck; buck = buck->next)
1755     if (!strcasecmp (buck->ptr->name, fname))
1756       {
1757         fdes = buck->ptr;
1758         break;
1759       }
1760
1761   if (buck && fdes == fieldlist)
1762     new_record (offset);
1763   else if (!buck)
1764     { /* A new fieldname.  */
1765       fdes = xcalloc (1, sizeof *fdes + strlen (fname));
1766       strcpy (fdes->name, fname);
1767       /* Create a hash entry to speed up field access.  */
1768       buck = xcalloc (1, sizeof *buck);
1769       buck->ptr = fdes;
1770       buck->next = namebuckets[hash];
1771       namebuckets[hash] = buck;
1772       /* Link the field into the record description.  */
1773       if (!fieldlist)
1774         fieldlist = fdes;
1775       else
1776         {
1777           for (f2 = fieldlist; f2->nextfield; f2 = f2->nextfield)
1778             ;
1779           f2->nextfield = fdes;
1780         }
1781     }
1782   fdes->valid = 1; /* This is in the current record.  */
1783   return fdes;
1784 }
1785
1786
1787
1788 /*
1789  * Replace the data slot DATA by an larger one.
1790  */
1791 static DATA
1792 expand_data_slot (FIELD field, DATA data)
1793 {
1794   DATA d, d2;
1795
1796   for (d = unused_data; d; d = d->next)
1797     if (d->size > data->size)
1798       break;
1799   if (!d)
1800     {
1801       d = xmalloc (sizeof *d + data->size + 200);
1802       d->size = data->size + 200 + 1;
1803     }
1804   memcpy (d->d, data->d, data->used);
1805   d->used = data->used;
1806   d->index = data->index;
1807   d->activ = data->activ;
1808   d->next = data->next;
1809   /* Link it into the field list.   */
1810   if (field->data == data)
1811     field->data = d;
1812   else
1813     {
1814       for (d2 = field->data; d2; d2 = d2->next)
1815         if (d2->next == data)
1816           break;
1817       if (!d2)
1818         abort (); /* Data not linked to field.  */
1819       d2->next = d;
1820     }
1821   data->next = unused_data;
1822   unused_data = data;
1823   return d;
1824 }
1825
1826
1827 /*
1828  * Begin a new record after closing the last one.
1829  */
1830 static void
1831 new_record (long offset)
1832 {
1833   finish_record ();
1834   start_of_record = offset;
1835   new_record_flag = 1;
1836 }
1837
1838
1839 static FIELD
1840 get_first_field ()
1841 {
1842   FIELD f;
1843   OUTFIELD of;
1844
1845   if (opt.outfields)
1846     {
1847       of = opt.outfields;
1848       for (f = fieldlist; f; f = f->nextfield)
1849         if (!strcmp (f->name, of->name))
1850           {
1851             next_outfield = of;
1852             return f;
1853           }
1854       next_outfield = NULL;
1855       return NULL;
1856     }
1857   return (next_field = fieldlist);
1858 }
1859
1860 static FIELD
1861 get_next_field ()
1862 {
1863   FIELD f;
1864   OUTFIELD of;
1865
1866   if (opt.outfields)
1867     {
1868       if (next_outfield && (of = next_outfield->next))
1869         {
1870           for (f = fieldlist; f; f = f->nextfield)
1871             if (!strcmp (f->name, of->name))
1872               {
1873                 next_outfield = of;
1874                 return f;
1875               }
1876         }
1877       next_outfield = NULL;
1878       return NULL;
1879     }
1880   return next_field ? (next_field = next_field->nextfield) : NULL;
1881 }
1882
1883
1884 /* Return true if the record has been selected.  Note that the
1885    selection currently considers only the first active line of a given
1886    field.  */
1887 static int
1888 select_record_p (void)
1889 {
1890   FIELD f;
1891   SELECTEXPR se;
1892   DATA d;
1893   const char *value;
1894   size_t selen, valuelen, n;
1895   long numvalue;
1896   int result = 1;
1897
1898   for (se=opt.selectexpr; se; se = se->next)
1899     {
1900       for (f = fieldlist; f; f = f->nextfield)
1901         if (!strcmp (f->name, se->name))
1902           break;
1903       if (!f || !f->valid)
1904         {
1905           /* No such field.  */
1906           switch (se->op)
1907             {
1908             case SELECT_NOTSAME:
1909             case SELECT_NOTSUB:
1910             case SELECT_NE:
1911             case SELECT_EMPTY:
1912               result = 1;
1913               break;
1914             default:
1915               result = 0;
1916               break;
1917             }
1918         }
1919       else
1920         {
1921           for (d = f->data; d; d = d->next)
1922             if (d->activ)
1923               break;
1924           if (!d)
1925             {
1926               value = "";
1927               valuelen = 0;
1928               numvalue = 0;
1929             }
1930           else
1931             {
1932               char tmpbuf[25];
1933               value = d->d;
1934               valuelen = d->used;
1935               n = valuelen;
1936               if (n > sizeof tmpbuf - 1)
1937                 n = sizeof tmpbuf -1;
1938               memcpy (tmpbuf, value, n);
1939               tmpbuf[n] = 0;
1940               numvalue = strtol (tmpbuf, NULL, 10);
1941             }
1942           selen = strlen (se->value);
1943
1944           switch (se->op)
1945             {
1946             case SELECT_SAME:
1947               result = (valuelen == selen && !memcmp (value, se->value, selen));
1948               break;
1949             case SELECT_NOTSAME:
1950               result = !(valuelen == selen && !memcmp (value, se->value,selen));
1951               break;
1952             case SELECT_SUB:
1953               result = !!memstr (value, valuelen, se->value);
1954               break;
1955             case SELECT_NOTSUB:
1956               result = !memstr (value, valuelen, se->value);
1957               break;
1958             case SELECT_EMPTY:
1959               result = !valuelen;
1960               break;
1961             case SELECT_NOTEMPTY:
1962               result = !!valuelen;
1963               break;
1964             case SELECT_EQ:
1965               result = (numvalue == se->numvalue);
1966               break;
1967             case SELECT_NE:
1968               result = (numvalue != se->numvalue);
1969               break;
1970             case SELECT_GT:
1971               result = (numvalue > se->numvalue);
1972               break;
1973             case SELECT_GE:
1974               result = (numvalue >= se->numvalue);
1975               break;
1976             case SELECT_LT:
1977               result = (numvalue < se->numvalue);
1978               break;
1979             case SELECT_LE:
1980               result = (numvalue <= se->numvalue);
1981               break;
1982             }
1983         }
1984       if (!result)
1985         break;
1986     }
1987
1988   return result;
1989 }
1990
1991
1992 /* Return the field to be used for sorting.  Note that we currently
1993    support only one sort field. */
1994 static FIELD
1995 find_sortfield (void)
1996 {
1997   OUTFIELD sf;
1998   FIELD f;
1999
2000   /* If no sortfield has been given use the first one.  */
2001   if (!opt.sortfields)
2002     {
2003       for (f = fieldlist; f; f = f->nextfield)
2004         if (f->valid)
2005           {
2006             sf = xmalloc (sizeof *sf + strlen (f->name));
2007             sf->next = NULL;
2008             sf->flags = 0;
2009             strcpy (sf->name, f->name);
2010             opt.sortfields = sf;
2011             break;
2012           }
2013     }
2014
2015   sf = opt.sortfields;
2016   if (!sf)
2017     return NULL;
2018
2019   for (f = fieldlist; f; f = f->nextfield)
2020     if (!strcmp (f->name, sf->name))
2021       break;
2022   if (f && f->valid)
2023     return f;
2024
2025   return NULL; /* No such field.  */
2026 }
2027
2028
2029 /*
2030  * If we are in a record: close the current record.
2031  */
2032 static void
2033 finish_record ()
2034 {
2035   FIELD f;
2036   DATA d = NULL;
2037   int any = 0;
2038   size_t n;
2039   char *p;
2040   int indent;
2041
2042   if (!opt.checkonly && fieldlist && fieldlist->valid)
2043     {
2044       /* There is a valid record */
2045       if (opt.sortmode == 1)
2046         { /* Store only.  */
2047           SORT sort;
2048           const char *s;
2049
2050           n = 0;
2051           s = ""; /* Default to the empty string.  */
2052           f = find_sortfield ();
2053           for (d = f?f->data:NULL; d; d = d->next)
2054             {
2055               if (d->activ)
2056                 {
2057                   n = d->used;
2058                   s = d->d;
2059                   break;
2060                 }
2061             }
2062
2063           sort = xcalloc (1, sizeof *sort + n + 1);
2064           sort->offset = start_of_record;
2065           memcpy (sort->d, s, n);
2066           sort->d[n] = 0; /* Make sure it is a string.  */
2067           sort->next = sortlist;
2068           sortlist = sort;
2069         }
2070       else if (opt.selectexpr && !select_record_p ())
2071         ;
2072       else if (opt.template)
2073         {
2074           print_template (0);
2075         }
2076       else if (opt.format == 0)
2077         {
2078           for (f = get_first_field (); f; f = get_next_field ())
2079             {
2080               if (any)
2081                 putchar (':');
2082               if (f->valid)
2083                 {
2084                   int need_tab = 0;
2085                   for (d = f->data; d; d = d->next)
2086                     {
2087                       if (d->activ)
2088                         {
2089                           const char *s;
2090                           int i;
2091
2092                           if (need_tab)
2093                             putchar ('\t');
2094                           any = 1;
2095                           need_tab = 1;
2096                           for (i = 0, s = d->d; i < d->used; s++, i++)
2097                             {
2098                               if (*s == '%')
2099                                 fputs ("%25", stdout);
2100                               else if (*s == ':')
2101                                 fputs ("%3A", stdout);
2102                               else if (*s == '\n')
2103                                 fputs ("%0A", stdout);
2104                               else if (*s == '\t')
2105                                 putchar (' ');
2106                               else
2107                                 putchar (*s);
2108                             }
2109                         }
2110                     }
2111                 }
2112             }
2113           if (any)
2114             putchar ('\n');
2115         }
2116       else if (opt.format == 1)
2117         {
2118           for (f = get_first_field (); f; f = get_next_field ())
2119             {
2120               if (f->valid)
2121                 {
2122                   for (d = f->data; d; d = d->next)
2123                     if (d->activ)
2124                       {
2125                         if (d->index != 1)
2126                           printf ("%s%s.%d='%.*s'", any ? ":" : "",
2127                                   f->name, d->index, (int) d->used, d->d);
2128                         else
2129                           printf ("%s%s='%.*s'", any ? ":" : "",
2130                                   f->name, (int) d->used, d->d);
2131                         any = 1;
2132                       }
2133                 }
2134             }
2135           putchar ('\n');
2136         }
2137       else if (opt.format == 2)
2138         {
2139           print_format2 (0);
2140         }
2141       else if (opt.format == 3)
2142         {
2143           for (f = get_first_field (); f; f = get_next_field ())
2144             {
2145               if (f->valid)
2146                 {
2147                   for (d = f->data; d; d = d->next)
2148                     if (d->activ)
2149                       {
2150                         any = 1;
2151                         indent = printf ("%s: ", f->name);
2152                         for (n = 0, p = d->d; n < d->used; n++, p++)
2153                           if (*p == '\n')
2154                             break;
2155                         if (n < d->used) /* Multi-line output.  */
2156                           {
2157                             for (n = 0, p = d->d; n < d->used; n++, p++)
2158                               {
2159                                 putchar (*p);
2160                                 if (*p == '\n')
2161                                   printf ("%*s", indent, "");
2162                               }
2163                           }
2164                         else /* Singe-line output.  */
2165                           {
2166                             printf ("%.*s", (int) d->used, d->d);
2167                           }
2168                         putchar ('\n');
2169                       }
2170                 }
2171             }
2172           if (any)
2173             putchar ('\n');
2174         }
2175       else if (opt.format == 4) /* ';' delimited */
2176         {
2177           for (f = get_first_field (); f; f = get_next_field ())
2178             {
2179               if (any)
2180                 putchar (';');
2181               if (f->valid)
2182                 {
2183                   int any2 = 0;
2184                   for (d = f->data; d; d = d->next)
2185                     {
2186                       if (d->activ)
2187                         {
2188                           if (any2)
2189                             putchar ('|');
2190                           any = 1;
2191                           any2 = 1;
2192                           for (n = 0, p = d->d; n < d->used; n++, p++)
2193                             {
2194                               if (*p == '\n')
2195                                 putchar (' ');
2196                               else if (*p == ';')
2197                                 putchar (',');
2198                               else
2199                                 putchar (*p);
2200                             }
2201                         }
2202                     }
2203                 }
2204             }
2205           if (any)
2206             putchar ('\n');
2207         }
2208     }
2209   output_count++;
2210   for (f = fieldlist; f; f = f->nextfield)
2211     {
2212       f->valid = 0;
2213       /* Set the data blocks inactive.  */
2214       for (d = f->data; d; d = d->next)
2215         d->activ = 0;
2216     }
2217 }
2218
2219
2220
2221 static void
2222 print_format2 (int flushit)
2223 {
2224   static int pending = 0;
2225   static int totlines = 0;
2226   static char *names[] = { "Name", "Street", "City", NULL };
2227   NAMEBUCKET buck;
2228   FIELD f = NULL;
2229   DATA d;
2230   int n, len, lines = 0;
2231   const char *name;
2232   static char buffers[3][40];
2233
2234   if (pending && totlines > 58)
2235     {
2236       putchar ('\f');
2237       totlines = 0;
2238     }
2239   if (flushit && pending)
2240     {
2241       for (n = 0; (name = names[n]); n++)
2242         {
2243           printf ("%-40s\n", buffers[n]);
2244           lines++;
2245           totlines++;
2246         }
2247     }
2248
2249   for (n = 0; !flushit && (name = names[n]); n++)
2250     {
2251       for (buck = namebuckets[hash_name (name)]; buck; buck = buck->next)
2252         if (!strcasecmp (buck->ptr->name, name))
2253           {
2254             f = buck->ptr;
2255             break;
2256           }
2257       if (!f)
2258         continue;
2259
2260       for (d = f->data; d; d = d->next)
2261         if (d->activ && d->index == 1)
2262           break;
2263       if (!d)
2264         continue;
2265       if ((len = (int) d->used) > 38)
2266         len = 38;
2267
2268       if (!pending)
2269         sprintf (buffers[n], "%.*s", len, d->d);
2270       else
2271         {
2272           printf ("%-40s%.*s\n", buffers[n], len, d->d);
2273           lines++;
2274           totlines++;
2275         }
2276     }
2277   if (pending)
2278     {
2279       for (; lines < 5; lines++, totlines++)
2280         putchar ('\n');
2281     }
2282   if (flushit)
2283     {
2284       pending = 0;
2285       totlines = 0;
2286     }
2287   else
2288     pending = !pending;
2289 }
2290
2291
2292 static void
2293 print_template (int flushit)
2294 {
2295   char pseudo_op[200];
2296   int c, pseudo_op_idx = 0;
2297   int state = 0;
2298
2299   if (flushit && tex.end_block)
2300     {
2301       if (fseek (tex.fp, tex.end_block, SEEK_SET))
2302         {
2303           fprintf (stderr, PGMNAME ": error seeking to offset %ld\n",
2304                    tex.end_block);
2305           exit (1);
2306         }
2307     }
2308
2309   while ((c = getc (tex.fp)) != EOF)
2310     {
2311       switch (state)
2312         {
2313         case 0:
2314           if (c == '@')
2315             state = 1;
2316           else
2317             putchar (c);
2318           break;
2319         case 1:
2320           if (c == '@')
2321             {
2322               state = 2;
2323               pseudo_op_idx = 0;
2324             }
2325           else
2326             {
2327               putchar ('@');
2328               ungetc (c, tex.fp);
2329               state = 0;
2330             }
2331           break;
2332         case 2:         /* pseudo-op start */
2333           if (pseudo_op_idx >= sizeof (pseudo_op) - 1)
2334             {
2335               fprintf (stderr, PGMNAME ": pseudo-op too long\n");
2336               exit (1);
2337             }
2338           else if (c == '\n')
2339             {
2340               fprintf (stderr, PGMNAME ": invalid pseudo-op - ignored\n");
2341               pseudo_op[pseudo_op_idx] = 0;
2342               fputs (pseudo_op, stdout);
2343               putchar ('\n');
2344               state = 0;
2345             }
2346           else if (c == '@'
2347                    && pseudo_op_idx && pseudo_op[pseudo_op_idx - 1] == '@')
2348             {
2349               pseudo_op[pseudo_op_idx - 1] = 0;
2350               state = 0;
2351               if (!flushit && process_template_op (pseudo_op))
2352                 return;
2353             }
2354           else
2355             pseudo_op[pseudo_op_idx++] = c;
2356           break;
2357
2358         default:
2359           abort ();
2360         }
2361     }
2362   if (c == EOF)
2363     {
2364       if (ferror (tex.fp))
2365         {
2366           fprintf (stderr, PGMNAME ":%s: read error\n", opt.template);
2367           exit (1);
2368         }
2369       else if (state)
2370         {
2371           fprintf (stderr, PGMNAME ":%s: unclosed pseudo-op\n", opt.template);
2372         }
2373     }
2374
2375 }
2376
2377
2378 static int
2379 process_template_op (const char *op)
2380 {
2381   NAMEBUCKET buck;
2382   FIELD f;
2383   DATA d;
2384
2385   if (!strcasecmp (op, "begin-record-block"))
2386     {
2387       tex.in_record_block = 1;
2388       tex.begin_block = ftell (tex.fp);
2389     }
2390   else if (!strcasecmp (op, "end-record-block"))
2391     {
2392       tex.in_record_block = 0;
2393     }
2394   else if (!strcasecmp (op, "next-record") && tex.in_record_block)
2395     {
2396       tex.end_block = ftell (tex.fp);
2397       if (fseek (tex.fp, tex.begin_block, SEEK_SET))
2398         {
2399           fprintf (stderr, PGMNAME ": error seeking to offset %ld\n",
2400                    tex.begin_block);
2401           exit (1);
2402         }
2403       return 1;
2404     }
2405   else if (!tex.in_record_block)
2406     {
2407       fprintf (stderr,
2408                PGMNAME ": pseudo op '%s' not allowed in this context\n", op);
2409     }
2410   else /* Take it as the key to the record data. */
2411     {
2412       char *p = strchr (op, ':');
2413
2414       if (p)
2415         *p++ = 0; /* Strip modifier. */
2416
2417       f = NULL;
2418       for (buck = namebuckets[hash_name (op)]; buck; buck = buck->next)
2419         if (!strcasecmp (buck->ptr->name, op))
2420           {
2421             f = buck->ptr;
2422             break;
2423           }
2424       if (f) /* We have an entry with this name.  */
2425         {
2426           for (d = f->data; d; d = d->next)
2427             if (d->activ)
2428               {
2429                 printf ("%s", d->index > 1 ? (opt.texfile?"\\par ":"\n\n"):"");
2430                 if (p && !strncmp (p, "N=", 2))
2431                   {
2432                     size_t n;
2433
2434                     for (n = 0; n < d->used; n++)
2435                       if (d->d[n] == '\r')
2436                         ;
2437                       else if (d->d[n] == '\n')
2438                         fputs (p + 2, stdout);
2439                       else
2440                         putchar (((unsigned char *) d->d)[n]);
2441                   }
2442                 else
2443                   printf ("%.*s", (int) d->used, d->d);
2444               }
2445         }
2446     }
2447   return 0;
2448 }
2449
2450
2451 \f
2452 static int
2453 do_sort_fnc_num (const void *arg_a, const void *arg_b)
2454 {
2455   double aval, bval;
2456   SORT a = *(SORT *) arg_a;
2457   SORT b = *(SORT *) arg_b;
2458
2459   aval = strtod (a->d, NULL);
2460   bval = strtod (b->d, NULL);
2461   return aval < bval? -1 : aval > bval? 1 : 0;
2462 }
2463
2464 static int
2465 do_sort_fnc_numrev (const void *arg_a, const void *arg_b)
2466 {
2467   double aval, bval;
2468   SORT a = *(SORT *) arg_a;
2469   SORT b = *(SORT *) arg_b;
2470
2471   aval = strtod (a->d, NULL);
2472   bval = strtod (b->d, NULL);
2473   return aval < bval? 1 : aval > bval? -1 : 0;
2474 }
2475
2476 static int
2477 do_sort_fnc (const void *arg_a, const void *arg_b)
2478 {
2479   SORT a = *(SORT *) arg_a;
2480   SORT b = *(SORT *) arg_b;
2481   return strcmp (a->d, b->d);
2482 }
2483
2484 static int
2485 do_sort_fnc_rev (const void *arg_a, const void *arg_b)
2486 {
2487   SORT a = *(SORT *) arg_a;
2488   SORT b = *(SORT *) arg_b;
2489   return strcmp (b->d, a->d);
2490 }
2491
2492 /*
2493  * Sort the sortlist
2494  */
2495 static void
2496 do_sort ()
2497 {
2498   size_t i, n;
2499   SORT s, *array;
2500   int reverse = opt.sortfields? (opt.sortfields->flags & SORTFLAG_REVERSE) : 0;
2501   int numeric = opt.sortfields? (opt.sortfields->flags & SORTFLAG_NUMERIC) : 0;
2502
2503   for (n = 0, s = sortlist; s; s = s->next)
2504     n++;
2505   if (!n)
2506     return;
2507   array = xmalloc ((n + 1) * sizeof *array);
2508   for (n = 0, s = sortlist; s; s = s->next)
2509     array[n++] = s;
2510   array[n] = NULL;
2511   qsort (array, n, sizeof *array,
2512          (numeric && reverse)? do_sort_fnc_numrev :
2513          (numeric           )? do_sort_fnc_num :
2514          (reverse           )? do_sort_fnc_rev :
2515          /* */                 do_sort_fnc);
2516   sortlist = array[0];
2517   for (i = 0; i < n; i++)
2518     array[i]->next = array[i + 1];
2519 }
2520
2521
2522 /*
2523  * Uniq the sortlist.  Remove all identical records except for the
2524  * last one (or the first if /r is used).
2525  */
2526 static void
2527 do_uniq ()
2528 {
2529   size_t i, n;
2530   SORT s, *array;
2531   int reverse = opt.sortfields? (opt.sortfields->flags & SORTFLAG_REVERSE) : 0;
2532
2533   /* Allocate an array large enough to hold all items.  */
2534   for (n = 0, s = sortlist; s; s = s->next)
2535     n++;
2536   if (!n)
2537     return;
2538   array = xmalloc ((n + 1) * sizeof *array);
2539
2540   /* Put all items into the array and mark the duplicates.  */
2541   for (n = 0, s = sortlist; s; s = s->next)
2542     {
2543       if (reverse)
2544         {
2545           for (i=0; i < n; i++)
2546             if (array[i]->offset != -1 && !strcmp (array[i]->d, s->d))
2547               {
2548                 /* Same item found.  */
2549                 array[i]->offset = -1; /* Mark that as deleted.  */
2550               }
2551         }
2552       else
2553         {
2554           for (i=0; i < n; i++)
2555             if (array[i]->offset != -1 && !strcmp (array[i]->d, s->d))
2556               {
2557                 /* We already have such an item.  */
2558                 s->offset = -1; /* Mark me as deleted.  */
2559                 break;
2560               }
2561         }
2562
2563       array[n++] = s;
2564     }
2565   array[n] = NULL;
2566
2567   /* Rebuild the sortlist.  Reverse it to keep the order of records.  */
2568   if (!n)
2569     sortlist = NULL;
2570   else
2571     {
2572       sortlist = array[n-1];
2573       for (i = n-1; i > 0; i--)
2574         array[i]->next = array[i-1];
2575       array[0]->next = NULL;
2576     }
2577 }
2578
2579 /*
2580 Local Variables:
2581 compile-command: "cc -Wall -O2 -o addrutil addrutil.c"
2582 End:
2583 */