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