addrutil: Improve handling of missing select fields.
[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   FIELD f;
1005
1006   while (arg_parse (&pargs, opts))
1007     {
1008       switch (pargs.r_opt)
1009         {
1010         case 'v':
1011           opt.verbose++;
1012           break;
1013         case 'd':
1014           opt.debug++;
1015           break;
1016         case 'c':
1017           opt.checkonly++;
1018           break;
1019         case 's':
1020           opt.sortmode = 1;
1021           add_sortfield (&pargs);
1022           break;
1023         case 'u':
1024           opt.uniqmode = 1;
1025           add_sortfield (&pargs);
1026           break;
1027         case 'f':
1028           opt.format = pargs.r.ret_int;
1029           break;
1030         case 'T':
1031           opt.texfile = 1;
1032         case 't':
1033           opt.template = pargs.r.ret_str;
1034           break;
1035         case 'F':
1036           of = xmalloc (sizeof *of + strlen (pargs.r.ret_str));
1037           of->next = NULL;
1038           strcpy (of->name, pargs.r.ret_str);
1039           if (!(of2 = opt.outfields))
1040             opt.outfields = of;
1041           else
1042             {
1043               for (; of2->next; of2 = of2->next)
1044                 ;
1045               of2->next = of;
1046             }
1047           break;
1048         case 501:
1049           opt.readcsv = 1;
1050           break;
1051         case 'S':
1052           se = parse_selectexpr (pargs.r.ret_str);
1053           if (se)
1054             {
1055               se->next = opt.selectexpr;
1056               opt.selectexpr = se;
1057             }
1058           break;
1059         default:
1060           pargs.err = 2;
1061           break;
1062         }
1063     }
1064
1065   if (opt.selectexpr && opt.debug)
1066     {
1067       FILE *fp = stderr;
1068
1069       fputs ("--- Begin selectors ---\n", fp);
1070       for (se=opt.selectexpr; se; se = se->next)
1071         fprintf (fp, "*(%s) %s '%s'\n",
1072                  se->name,
1073                  se->op == SELECT_SAME?    "= ":
1074                  se->op == SELECT_NOTSAME? "<>":
1075                  se->op == SELECT_SUB?     "=~":
1076                  se->op == SELECT_NOTSUB?  "!~":
1077                  se->op == SELECT_EQ?      "==":
1078                  se->op == SELECT_NE?      "!=":
1079                  se->op == SELECT_LT?      "< ":
1080                  se->op == SELECT_LE?      "<=":
1081                  se->op == SELECT_GT?      "> ":
1082                  se->op == SELECT_GE?      ">=":"[oops]",
1083                  se->value);
1084       fputs ("--- End selectors ---\n", fp);
1085     }
1086
1087   if (opt.readcsv)
1088     {
1089       if (!argc)
1090         read_and_print_csv (NULL);
1091       else
1092         {
1093           for (; argc; argc--, argv++)
1094             read_and_print_csv (*argv);
1095         }
1096       exit (0);
1097     }
1098
1099   if (opt.template)
1100     {
1101       tex.fp = fopen (opt.template, "r");
1102       if (!tex.fp)
1103         {
1104           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1105                    opt.template, strerror (errno));
1106           exit (1);
1107         }
1108     }
1109
1110   if (opt.sortmode && opt.uniqmode)
1111     {
1112       fprintf (stderr,
1113                PGMNAME ": --sort and --uniq may not be used together\n");
1114       exit (1);
1115     }
1116   if ((opt.sortmode||opt.uniqmode) && argc != 1)
1117     {
1118       fprintf (stderr,
1119                PGMNAME ": sorry,"
1120                " sort and uniq is only available for one file\n");
1121       exit (1);
1122     }
1123
1124   /* Print a warning if more than one sort field is given.  FIXME: We
1125      should eventually lift that limit. */
1126   if (opt.sortfields && opt.sortfields->next)
1127     fprintf (stderr,
1128              PGMNAME ": warning: only the first sort field is used\n");
1129
1130   /* For internal purposes we set the sortmode flag with uniqmode.  */
1131   if (opt.uniqmode)
1132     opt.sortmode = 1;
1133
1134   org_argc = argc;
1135   org_argv = argv;
1136
1137  pass_two:
1138   if (!argc)
1139     process (NULL);
1140   else
1141     {
1142       for (; argc; argc--, argv++)
1143         process (*argv);
1144     }
1145
1146   if (opt.template)
1147     {
1148       if (tex.in_record_block && opt.sortmode != 1)
1149         {
1150           print_template (1);
1151         }
1152     }
1153   else if (opt.format == 2 && opt.sortmode != 1)
1154     print_format2 (1);          /* flush */
1155
1156   if (opt.sortmode == 1 && sortlist)
1157     {
1158       if (opt.uniqmode)
1159         do_uniq ();
1160       else
1161         do_sort ();
1162       argc = org_argc;
1163       argv = org_argv;
1164       opt.sortmode = 2;
1165       goto pass_two;
1166     }
1167   else if (opt.sortmode == 2)
1168     {
1169       /* FIXME: cleanup the sort infos */
1170     }
1171
1172
1173   for (se=opt.selectexpr; se; se = se->next)
1174     {
1175       for (f = fieldlist; f; f = f->nextfield)
1176         if (!strcmp (f->name, se->name))
1177           break;
1178       if (!f)
1179         fprintf (stderr, PGMNAME ": warning: "
1180                  "select field '%s' not found in data\n", se->name);
1181     }
1182
1183   if (opt.debug)
1184     {
1185       DATA d;
1186       FILE *fp = stderr;
1187       int n;
1188
1189       fputs ("--- Begin fieldlist ---\n", fp);
1190       for (f = fieldlist; f; f = f->nextfield)
1191         {
1192           n = fprintf (fp, "%.20s:", f->name);
1193           for (d = f->data; d; d = d->next)
1194             fprintf (fp, "%*s idx=%-3d used=%-3u size=%-3u %s\n",
1195                      d == f->data ? 0 : n, "", d->index,
1196                      (unsigned int)d->used, (unsigned int)d->size,
1197                      d->activ ? "activ" : "not-active");
1198           if (!f->data)
1199             putc ('\n', fp);
1200         }
1201       fputs ("--- End fieldlist ---\n", fp);
1202       if (opt.sortmode)
1203         {
1204           fputs ("--- Begin sortlist ---\n", fp);
1205           for (of = opt.sortfields; of; of = of->next)
1206             fprintf (fp, "%.20s%s%s%s\n", of->name,
1207                      of->flags? "/":"",
1208                      (of->flags & SORTFLAG_REVERSE)? "r":"",
1209                      (of->flags & SORTFLAG_NUMERIC)? "n":"");
1210           fputs ("--- End sortlist ---\n", fp);
1211         }
1212       dump_hash_infos ();
1213     }
1214   return 0;
1215 }
1216
1217
1218 static INLINE unsigned long
1219 hash_name (const char *s_arg)
1220 {
1221   const unsigned char *s = (const unsigned char*)s_arg;
1222   unsigned long hashVal = 0;
1223   unsigned long carry;
1224
1225   if (s)
1226     for (; *s; s++)
1227       {
1228         hashVal = (hashVal << 4) + toupper (*s);
1229         if ((carry = (hashVal & 0xf0000000)))
1230           {
1231             hashVal ^= (carry >> 24);
1232             hashVal ^= carry;
1233           }
1234       }
1235
1236   return hashVal % NO_NAMEBUCKETS;
1237 }
1238
1239
1240 static void
1241 dump_hash_infos ()
1242 {
1243   int i, sum, perBucket, n;
1244   NAMEBUCKET r;
1245
1246   perBucket = sum = 0;
1247   for (i = 0; i < NO_NAMEBUCKETS; i++)
1248     {
1249       for (n = 0, r = namebuckets[i]; r; r = r->next)
1250         n++;
1251       sum += n;
1252       if (n > perBucket)
1253         perBucket = n;
1254     }
1255   fprintf (stderr,
1256            "%d entries in %d hash buckets; max. %d entr%s per hash bucket\n",
1257            sum, NO_NAMEBUCKETS, perBucket, perBucket == 1 ? "y" : "ies");
1258 }
1259
1260
1261 static void
1262 log_error (int rc, const char *s, ...)
1263 {
1264   va_list arg_ptr;
1265   FILE *fp = stderr;
1266
1267   va_start (arg_ptr, s);
1268   vfprintf (fp, s, arg_ptr);
1269   putc ('\n', fp);
1270   va_end (arg_ptr);
1271   if (rc)
1272     exit (rc);
1273 }
1274
1275
1276 static void
1277 process (const char *filename)
1278 {
1279   FILE *fp;
1280   int c;
1281   unsigned long lineno = 0;
1282   long lineoff = 0;             /* offset of the current line */
1283   int newline;
1284   int comment = 0;
1285   int linewrn = 0;
1286   char fname[FIELDNAMELEN + 1];
1287   int fnameidx = 0;
1288   int index;                    /* current index */
1289   enum
1290   { sINIT,                      /* no record yet */
1291     sFIELD,                     /* inside a fieldname */
1292     sDATABEG,                   /* waiting for start of value */
1293     sDATA                       /* storing a value */
1294   } state = sINIT;
1295   FIELD f = NULL;               /* current field */
1296   DATA d = NULL;                /* current data slot */
1297   SORT sort = sortlist;
1298   int pending_lf = 0;
1299   int skip_kludge = 0;
1300
1301   if (filename)
1302     {
1303       fp = fopen (filename, "r");
1304       if (!fp)
1305         {
1306           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1307                    filename, strerror (errno));
1308           exit (1);
1309         }
1310     }
1311   else
1312     {
1313       fp = stdin;
1314       filename = "[stdin]";
1315     }
1316
1317   if (opt.sortmode == 2)  /* Sorting/uniqing has been done. */
1318     {
1319       if (!sort)
1320         return;           /* nothing to sort */
1321     next_sortrecord:
1322       if (!sort)
1323         goto ready;
1324       if (sort->offset == -1)
1325         {
1326           /* Skip deleted records.  */
1327           sort = sort->next;
1328           goto next_sortrecord;
1329         }
1330       clearerr (fp);
1331       if (fseek (fp, sort->offset, SEEK_SET))
1332         {
1333           fprintf (stderr, PGMNAME ": error seeking to %ld\n", sort->offset);
1334           exit (2);
1335         }
1336       sort = sort->next;
1337       state = sINIT;
1338       skip_kludge = 1;
1339     }
1340
1341   /* Read the file byte by byte; do not impose a limit on the
1342    * line length. Fieldnames are up to FIELDNAMELEN bytes long.
1343    */
1344   lineno++;
1345   newline = 1;
1346   while ((c = getc (fp)) != EOF)
1347     {
1348       if (c == '\n')
1349         {
1350           switch (state)
1351             {
1352             case sFIELD:
1353               log_error (2, "%s:%ld: fieldname not terminated",
1354                          filename, lineno);
1355               break;
1356             case sDATA:
1357               pending_lf++;
1358               break;
1359             default:
1360               break;
1361             }
1362           lineno++;
1363           lineoff = ftell (fp) - 1;
1364           if (lineoff == -1 && opt.sortmode)
1365             {
1366               log_error (2, "%s:%ld: ftell() failed: %s",
1367                          filename, lineno, strerror (errno));
1368               exit (1);
1369             }
1370           newline = 1;
1371           comment = 0;
1372           linewrn = 0;
1373           continue;
1374         }
1375       else if (comment)
1376         continue;
1377
1378       if (newline)
1379         {                       /* at first column */
1380           if (c == '#')
1381             comment = 1;        /* bybass the entire line */
1382           else if (c == ' ' || c == '\t')
1383             {
1384               switch (state)
1385                 {
1386                 case sINIT:
1387                   break;        /* nothing to do */
1388                 case sFIELD:
1389                   abort ();
1390                 case sDATABEG:
1391                   break;
1392                 case sDATA:
1393                   state = sDATABEG;
1394                   break;
1395                 }
1396             }
1397           else if (c == ':')
1398             log_error (2, "%s:%ld: line starts with a colon", filename, lineno);
1399           else
1400             {
1401               switch (state)
1402                 {
1403                 case sDATABEG:
1404                 case sDATA:
1405                   /*FinishField(); */
1406                   /* fall thru */
1407                 case sINIT:     /* start of a fieldname */
1408                   fnameidx = 0;
1409                   fname[fnameidx++] = c;
1410                   state = sFIELD;
1411                   break;
1412                 case sFIELD:
1413                   abort ();
1414                 }
1415             }
1416           newline = 0;
1417         }
1418       else
1419         {
1420           switch (state)
1421             {
1422             case sINIT:
1423               if (!linewrn)
1424                 {
1425                   log_error (0, "%s:%lu: warning: garbage detected",
1426                              filename, lineno);
1427                   linewrn++;
1428                 }
1429               break;
1430             case sFIELD:
1431               if (c == ':')
1432                 {
1433                   char *p;
1434
1435                   fname[fnameidx] = 0;
1436                   strip_trailing_ws (fname);
1437                   if ((p = strrchr (fname, '.')))
1438                     {
1439                       *p++ = 0;
1440                       strip_trailing_ws (fname);
1441                       index = atoi (p);
1442                       if (index < 0 || index > 255)
1443                         log_error (2, "%s:%lu: invalid index of fieldname",
1444                                    filename, lineno);
1445                     }
1446                   else
1447                     index = 0;  /* must calculate an index */
1448                   if (!*fname)
1449                     log_error (2, "%s:%lu: empty fieldname", filename, lineno);
1450                   new_record_flag = 0;
1451                   f = store_field_name (fname, lineoff);
1452                   if (opt.sortmode == 2 && new_record_flag && !skip_kludge)
1453                     goto next_sortrecord;
1454                   skip_kludge = 0;
1455                   if (!index)
1456                     { /* detect the index */
1457                       /* first a shortcut: */
1458                       if ((d = f->data) && d->index == 1 && !d->activ)
1459                         index = 1;      /* that's it */
1460                       else
1461                         { /* find the highest unused index */
1462                           for (index = 1; d;)
1463                             {
1464                               if (d->index == index)
1465                                 {
1466                                   if (d->activ)
1467                                     {
1468                                       index++;
1469                                       d = f->data;
1470                                     }
1471                                   else
1472                                     break;
1473                                 }
1474                               else
1475                                 d = d->next;
1476                             }
1477                         }
1478                     }
1479                   else
1480                     { /* find a data slot for the given index. */
1481                       for (d = f->data; d; d = d->next)
1482                         if (d->index == index)
1483                           break;
1484                       if (d && d->activ)
1485                         log_error (0, "%s:%lu: warning: %s.%d redefined",
1486                                    filename, lineno, fname, index);
1487                     }
1488                   if (!d)
1489                     { /* create a new slot */
1490                       if ((d = unused_data))
1491                         unused_data = d->next;
1492                       else
1493                         {
1494                           d = xmalloc (sizeof *d + 100);
1495                           d->size = 100 + 1;
1496                         }
1497                       d->index = index;
1498                       d->next = NULL;
1499                       if (!f->data)
1500                         f->data = d;
1501                       else
1502                         {
1503                           DATA d2;
1504                           for (d2 = f->data; d2->next; d2 = d2->next)
1505                             ;
1506                           d2->next = d;
1507                         }
1508                     }
1509                   d->activ = 1;
1510                   d->used = 0;  /* used length */
1511                   pending_lf = 0;
1512                   state = sDATABEG;
1513                 }
1514               else
1515                 {
1516                   if (fnameidx >= FIELDNAMELEN)
1517                     log_error (2, "%s:%ld: fieldname too long",
1518                                filename, lineno);
1519                   fname[fnameidx++] = c;
1520                 }
1521               break;
1522             case sDATABEG:
1523               if (c == ' ' || c == '\t')
1524                 break;
1525               state = sDATA;
1526               /* fall thru */
1527             case sDATA:
1528               if (!d)
1529                 abort ();
1530               for (; pending_lf; pending_lf--)
1531                 {
1532                   if (d->used >= d->size)
1533                     d = expand_data_slot (f, d);
1534                   d->d[d->used++] = '\n';
1535                 }
1536               if (d->used >= d->size)
1537                 d = expand_data_slot (f, d);
1538               d->d[d->used++] = c;
1539               break;
1540             } /* end switch state after first column */
1541         }
1542     }
1543   if (ferror (fp))
1544     {
1545       fprintf (stderr, PGMNAME ":%s:%lu: read error: %s\n",
1546                filename, lineno, strerror (errno));
1547       exit (2);
1548     }
1549   if (!newline)
1550     {
1551       log_error (0, "%s: warning: last line not terminated by a LF", filename);
1552     }
1553   if (opt.sortmode == 2)
1554     goto next_sortrecord;
1555
1556  ready:
1557   finish_record ();
1558   lineno--;
1559   if (opt.verbose)
1560     log_error (0, "%s: %lu line%s processed", filename, lineno,
1561                lineno == 1 ? "" : "s");
1562
1563   if (fp != stdin)
1564     fclose (fp);
1565 }
1566
1567
1568 static void
1569 read_and_print_csv (const char *filename)
1570 {
1571   FILE *fp;
1572   int c;
1573   unsigned long lineno;
1574   int newline, newfield, in_string, any_printed;
1575   int fieldidx = 0;
1576   OUTFIELD of;
1577
1578   if (filename)
1579     {
1580       fp = fopen (filename, "r");
1581       if (!fp)
1582         {
1583           fprintf (stderr, PGMNAME ": failed to open `%s': %s\n",
1584                    filename, strerror (errno));
1585           exit (1);
1586         }
1587     }
1588   else
1589     {
1590       fp = stdin;
1591       filename = "[stdin]";
1592     }
1593
1594   /* Read the file byte by byte; do not impose a limit on the line
1595    * length.  Fieldnames are up to FIELDNAMELEN bytes long.  */
1596   lineno  = 1;
1597   newline = 1;
1598   newfield = 0;
1599   in_string = 0;
1600   any_printed = 0;
1601   of = NULL;
1602   while ((c = getc (fp)) != EOF)
1603     {
1604       if (c == '\r')
1605         continue;
1606
1607       if (c == '\n')  /* End of line.  */
1608         {
1609           putchar ('\n');
1610           putchar ('\n');
1611           lineno++;
1612           newline = 1;
1613           newfield = 0;
1614           in_string = 0;
1615           fieldidx = 0;
1616           continue;
1617         }
1618
1619       if (newline) /* At a new record.  */
1620         {
1621           newline = 0;
1622           newfield = 1;
1623           of = opt.outfields? opt.outfields : NULL;
1624         }
1625
1626       if (newfield)
1627         {
1628           if (of)
1629             printf ("%s:", of->name);
1630           else
1631             printf ("Field_%d:", fieldidx);
1632           newfield = 0;
1633           any_printed = 0;
1634           in_string = 0;
1635         }
1636
1637       if (in_string == 1)
1638         {
1639           if (c == '\"')
1640             in_string = 2; /* Possible end of string.  */
1641           else
1642             {
1643               if (!any_printed)
1644                 {
1645                   any_printed = 1;
1646                   putchar (' ');
1647                 }
1648               putchar (c);
1649             }
1650         }
1651       else if (in_string == 2)
1652         {
1653           if (c == '\"' )
1654             {
1655               putchar ('\"');
1656               in_string = 1;
1657             }
1658           else
1659             in_string = 0;
1660         }
1661
1662       if (in_string)
1663         ;
1664       else if (c == '\"')
1665         {
1666           in_string = 1;
1667         }
1668       else if (c == ',')
1669         {
1670           putchar ('\n');
1671           fieldidx++;
1672           newfield = 1;
1673           if (of)
1674             of = of->next;
1675         }
1676       else
1677         {
1678           if (!any_printed)
1679             {
1680               any_printed = 1;
1681               putchar (' ');
1682             }
1683           putchar (c);
1684         }
1685     }
1686
1687   if (ferror (fp))
1688     {
1689       fprintf (stderr, PGMNAME ":%s:%lu: read error: %s\n",
1690                filename, lineno, strerror (errno));
1691       exit (2);
1692     }
1693   if (!newline)
1694     {
1695       log_error (0, "%s: warning: last line not terminated by a LF", filename);
1696     }
1697
1698   lineno--;
1699   if (opt.verbose)
1700     log_error (0, "%s: %lu line%s processed", filename, lineno,
1701                lineno == 1 ? "" : "s");
1702
1703   if (fp != stdin)
1704     fclose (fp);
1705 }
1706
1707
1708 /*
1709  * Handle the fieldname.
1710  *
1711  * If we already have a field with this name in the current record, we
1712  * append a counter to the field (e.g. "Phone.1", "Phone.2", ... )
1713  * where a counter of 1 is same as the field without a count.  Field
1714  * names are NOT case sensitive.  Index 0 means: Calculate an index if
1715  * this is an unknown field.
1716  *
1717  * Returns: a pointer to the field
1718  */
1719 static FIELD
1720 store_field_name (const char *fname, long offset)
1721 {
1722   unsigned long hash;
1723   NAMEBUCKET buck;
1724   FIELD fdes, f2;
1725
1726   for (buck = namebuckets[hash = hash_name (fname)]; buck; buck = buck->next)
1727     if (!strcasecmp (buck->ptr->name, fname))
1728       {
1729         fdes = buck->ptr;
1730         break;
1731       }
1732
1733   if (buck && fdes == fieldlist)
1734     new_record (offset);
1735   else if (!buck)
1736     { /* A new fieldname.  */
1737       fdes = xcalloc (1, sizeof *fdes + strlen (fname));
1738       strcpy (fdes->name, fname);
1739       /* Create a hash entry to speed up field access.  */
1740       buck = xcalloc (1, sizeof *buck);
1741       buck->ptr = fdes;
1742       buck->next = namebuckets[hash];
1743       namebuckets[hash] = buck;
1744       /* Link the field into the record description.  */
1745       if (!fieldlist)
1746         fieldlist = fdes;
1747       else
1748         {
1749           for (f2 = fieldlist; f2->nextfield; f2 = f2->nextfield)
1750             ;
1751           f2->nextfield = fdes;
1752         }
1753     }
1754   fdes->valid = 1; /* This is in the current record.  */
1755   return fdes;
1756 }
1757
1758
1759
1760 /*
1761  * Replace the data slot DATA by an larger one.
1762  */
1763 static DATA
1764 expand_data_slot (FIELD field, DATA data)
1765 {
1766   DATA d, d2;
1767
1768   for (d = unused_data; d; d = d->next)
1769     if (d->size > data->size)
1770       break;
1771   if (!d)
1772     {
1773       d = xmalloc (sizeof *d + data->size + 200);
1774       d->size = data->size + 200 + 1;
1775     }
1776   memcpy (d->d, data->d, data->used);
1777   d->used = data->used;
1778   d->index = data->index;
1779   d->activ = data->activ;
1780   d->next = data->next;
1781   /* Link it into the field list.   */
1782   if (field->data == data)
1783     field->data = d;
1784   else
1785     {
1786       for (d2 = field->data; d2; d2 = d2->next)
1787         if (d2->next == data)
1788           break;
1789       if (!d2)
1790         abort (); /* Data not linked to field.  */
1791       d2->next = d;
1792     }
1793   data->next = unused_data;
1794   unused_data = data;
1795   return d;
1796 }
1797
1798
1799 /*
1800  * Begin a new record after closing the last one.
1801  */
1802 static void
1803 new_record (long offset)
1804 {
1805   finish_record ();
1806   start_of_record = offset;
1807   new_record_flag = 1;
1808 }
1809
1810
1811 static FIELD
1812 get_first_field ()
1813 {
1814   FIELD f;
1815   OUTFIELD of;
1816
1817   if (opt.outfields)
1818     {
1819       of = opt.outfields;
1820       for (f = fieldlist; f; f = f->nextfield)
1821         if (!strcmp (f->name, of->name))
1822           {
1823             next_outfield = of;
1824             return f;
1825           }
1826       next_outfield = NULL;
1827       return NULL;
1828     }
1829   return (next_field = fieldlist);
1830 }
1831
1832 static FIELD
1833 get_next_field ()
1834 {
1835   FIELD f;
1836   OUTFIELD of;
1837
1838   if (opt.outfields)
1839     {
1840       if (next_outfield && (of = next_outfield->next))
1841         {
1842           for (f = fieldlist; f; f = f->nextfield)
1843             if (!strcmp (f->name, of->name))
1844               {
1845                 next_outfield = of;
1846                 return f;
1847               }
1848         }
1849       next_outfield = NULL;
1850       return NULL;
1851     }
1852   return next_field ? (next_field = next_field->nextfield) : NULL;
1853 }
1854
1855
1856 /* Return true if the record has been selected.  Note that the
1857    selection currently considers only the first active line of a given
1858    field.  */
1859 static int
1860 select_record_p (void)
1861 {
1862   FIELD f;
1863   SELECTEXPR se;
1864   DATA d;
1865   const char *value;
1866   size_t selen, valuelen, n;
1867   long numvalue;
1868   int result = 1;
1869
1870   for (se=opt.selectexpr; se; se = se->next)
1871     {
1872       for (f = fieldlist; f; f = f->nextfield)
1873         if (!strcmp (f->name, se->name))
1874           break;
1875       if (!f || !f->valid)
1876         {
1877           /* No such field.  */
1878           switch (se->op)
1879             {
1880             case SELECT_NOTSAME:
1881             case SELECT_NOTSUB:
1882             case SELECT_NE:
1883               result = 1;
1884               break;
1885             default:
1886               result = 0;
1887               break;
1888             }
1889         }
1890       else
1891         {
1892           for (d = f->data; d; d = d->next)
1893             if (d->activ)
1894               break;
1895           if (!d)
1896             {
1897               value = "";
1898               valuelen = 0;
1899               numvalue = 0;
1900             }
1901           else
1902             {
1903               char tmpbuf[25];
1904               value = d->d;
1905               valuelen = d->used;
1906               n = valuelen;
1907               if (n > sizeof tmpbuf - 1)
1908                 n = sizeof tmpbuf -1;
1909               memcpy (tmpbuf, value, n);
1910               tmpbuf[n] = 0;
1911               numvalue = strtol (tmpbuf, NULL, 10);
1912             }
1913           selen = strlen (se->value);
1914
1915           switch (se->op)
1916             {
1917             case SELECT_SAME:
1918               result = (valuelen == selen && !memcmp (value, se->value, selen));
1919               break;
1920             case SELECT_NOTSAME:
1921               result = !(valuelen == selen && !memcmp (value, se->value,selen));
1922               break;
1923             case SELECT_SUB:
1924               result = !!memstr (value, valuelen, se->value);
1925               break;
1926             case SELECT_NOTSUB:
1927               result = !memstr (value, valuelen, se->value);
1928               break;
1929             case SELECT_EQ:
1930               result = (numvalue == se->numvalue);
1931               break;
1932             case SELECT_NE:
1933               result = (numvalue != se->numvalue);
1934               break;
1935             case SELECT_GT:
1936               result = (numvalue > se->numvalue);
1937               break;
1938             case SELECT_GE:
1939               result = (numvalue >= se->numvalue);
1940               break;
1941             case SELECT_LT:
1942               result = (numvalue < se->numvalue);
1943               break;
1944             case SELECT_LE:
1945               result = (numvalue <= se->numvalue);
1946               break;
1947             }
1948         }
1949       if (!result)
1950         break;
1951     }
1952
1953   return result;
1954 }
1955
1956
1957 /* Return the field to be used for sorting.  Note that we currently
1958    support only one sort field. */
1959 static FIELD
1960 find_sortfield (void)
1961 {
1962   OUTFIELD sf;
1963   FIELD f;
1964
1965   /* If no sortfield has been given use the first one.  */
1966   if (!opt.sortfields)
1967     {
1968       for (f = fieldlist; f; f = f->nextfield)
1969         if (f->valid)
1970           {
1971             sf = xmalloc (sizeof *sf + strlen (f->name));
1972             sf->next = NULL;
1973             sf->flags = 0;
1974             strcpy (sf->name, f->name);
1975             opt.sortfields = sf;
1976             break;
1977           }
1978     }
1979
1980   sf = opt.sortfields;
1981   if (!sf)
1982     return NULL;
1983
1984   for (f = fieldlist; f; f = f->nextfield)
1985     if (!strcmp (f->name, sf->name))
1986       break;
1987   if (f && f->valid)
1988     return f;
1989
1990   return NULL; /* No such field.  */
1991 }
1992
1993
1994 /*
1995  * If we are in a record: close the current record.
1996  */
1997 static void
1998 finish_record ()
1999 {
2000   FIELD f;
2001   DATA d = NULL;
2002   int any = 0;
2003   size_t n;
2004   char *p;
2005   int indent;
2006
2007   if (!opt.checkonly && fieldlist && fieldlist->valid)
2008     {
2009       /* There is a valid record */
2010       if (opt.sortmode == 1)
2011         { /* Store only.  */
2012           SORT sort;
2013           const char *s;
2014
2015           n = 0;
2016           s = ""; /* Default to the empty string.  */
2017           f = find_sortfield ();
2018           for (d = f?f->data:NULL; d; d = d->next)
2019             {
2020               if (d->activ)
2021                 {
2022                   n = d->used;
2023                   s = d->d;
2024                   break;
2025                 }
2026             }
2027
2028           sort = xcalloc (1, sizeof *sort + n + 1);
2029           sort->offset = start_of_record;
2030           memcpy (sort->d, s, n);
2031           sort->d[n] = 0; /* Make sure it is a string.  */
2032           sort->next = sortlist;
2033           sortlist = sort;
2034         }
2035       else if (opt.selectexpr && !select_record_p ())
2036         ;
2037       else if (opt.template)
2038         {
2039           print_template (0);
2040         }
2041       else if (opt.format == 0)
2042         {
2043           for (f = get_first_field (); f; f = get_next_field ())
2044             {
2045               if (any)
2046                 putchar (':');
2047               if (f->valid)
2048                 {
2049                   int need_tab = 0;
2050                   for (d = f->data; d; d = d->next)
2051                     {
2052                       if (d->activ)
2053                         {
2054                           const char *s;
2055                           int i;
2056
2057                           if (need_tab)
2058                             putchar ('\t');
2059                           any = 1;
2060                           need_tab = 1;
2061                           for (i = 0, s = d->d; i < d->used; s++, i++)
2062                             {
2063                               if (*s == '%')
2064                                 fputs ("%25", stdout);
2065                               else if (*s == ':')
2066                                 fputs ("%3A", stdout);
2067                               else if (*s == '\n')
2068                                 fputs ("%0A", stdout);
2069                               else if (*s == '\t')
2070                                 putchar (' ');
2071                               else
2072                                 putchar (*s);
2073                             }
2074                         }
2075                     }
2076                 }
2077             }
2078           if (any)
2079             putchar ('\n');
2080         }
2081       else if (opt.format == 1)
2082         {
2083           for (f = get_first_field (); f; f = get_next_field ())
2084             {
2085               if (f->valid)
2086                 {
2087                   for (d = f->data; d; d = d->next)
2088                     if (d->activ)
2089                       {
2090                         if (d->index != 1)
2091                           printf ("%s%s.%d='%.*s'", any ? ":" : "",
2092                                   f->name, d->index, (int) d->used, d->d);
2093                         else
2094                           printf ("%s%s='%.*s'", any ? ":" : "",
2095                                   f->name, (int) d->used, d->d);
2096                         any = 1;
2097                       }
2098                 }
2099             }
2100           putchar ('\n');
2101         }
2102       else if (opt.format == 2)
2103         {
2104           print_format2 (0);
2105         }
2106       else if (opt.format == 3)
2107         {
2108           for (f = get_first_field (); f; f = get_next_field ())
2109             {
2110               if (f->valid)
2111                 {
2112                   for (d = f->data; d; d = d->next)
2113                     if (d->activ)
2114                       {
2115                         any = 1;
2116                         indent = printf ("%s: ", f->name);
2117                         for (n = 0, p = d->d; n < d->used; n++, p++)
2118                           if (*p == '\n')
2119                             break;
2120                         if (n < d->used) /* Multi-line output.  */
2121                           {
2122                             for (n = 0, p = d->d; n < d->used; n++, p++)
2123                               {
2124                                 putchar (*p);
2125                                 if (*p == '\n')
2126                                   printf ("%*s", indent, "");
2127                               }
2128                           }
2129                         else /* Singe-line output.  */
2130                           {
2131                             printf ("%.*s", (int) d->used, d->d);
2132                           }
2133                         putchar ('\n');
2134                       }
2135                 }
2136             }
2137           if (any)
2138             putchar ('\n');
2139         }
2140       else if (opt.format == 4) /* ';' delimited */
2141         {
2142           for (f = get_first_field (); f; f = get_next_field ())
2143             {
2144               if (any)
2145                 putchar (';');
2146               if (f->valid)
2147                 {
2148                   int any2 = 0;
2149                   for (d = f->data; d; d = d->next)
2150                     {
2151                       if (d->activ)
2152                         {
2153                           if (any2)
2154                             putchar ('|');
2155                           any = 1;
2156                           any2 = 1;
2157                           for (n = 0, p = d->d; n < d->used; n++, p++)
2158                             {
2159                               if (*p == '\n')
2160                                 putchar (' ');
2161                               else if (*p == ';')
2162                                 putchar (',');
2163                               else
2164                                 putchar (*p);
2165                             }
2166                         }
2167                     }
2168                 }
2169             }
2170           if (any)
2171             putchar ('\n');
2172         }
2173     }
2174   output_count++;
2175   for (f = fieldlist; f; f = f->nextfield)
2176     {
2177       f->valid = 0;
2178       /* Set the data blocks inactive.  */
2179       for (d = f->data; d; d = d->next)
2180         d->activ = 0;
2181     }
2182 }
2183
2184
2185
2186 static void
2187 print_format2 (int flushit)
2188 {
2189   static int pending = 0;
2190   static int totlines = 0;
2191   static char *names[] = { "Name", "Street", "City", NULL };
2192   NAMEBUCKET buck;
2193   FIELD f = NULL;
2194   DATA d;
2195   int n, len, lines = 0;
2196   const char *name;
2197   static char buffers[3][40];
2198
2199   if (pending && totlines > 58)
2200     {
2201       putchar ('\f');
2202       totlines = 0;
2203     }
2204   if (flushit && pending)
2205     {
2206       for (n = 0; (name = names[n]); n++)
2207         {
2208           printf ("%-40s\n", buffers[n]);
2209           lines++;
2210           totlines++;
2211         }
2212     }
2213
2214   for (n = 0; !flushit && (name = names[n]); n++)
2215     {
2216       for (buck = namebuckets[hash_name (name)]; buck; buck = buck->next)
2217         if (!strcasecmp (buck->ptr->name, name))
2218           {
2219             f = buck->ptr;
2220             break;
2221           }
2222       if (!f)
2223         continue;
2224
2225       for (d = f->data; d; d = d->next)
2226         if (d->activ && d->index == 1)
2227           break;
2228       if (!d)
2229         continue;
2230       if ((len = (int) d->used) > 38)
2231         len = 38;
2232
2233       if (!pending)
2234         sprintf (buffers[n], "%.*s", len, d->d);
2235       else
2236         {
2237           printf ("%-40s%.*s\n", buffers[n], len, d->d);
2238           lines++;
2239           totlines++;
2240         }
2241     }
2242   if (pending)
2243     {
2244       for (; lines < 5; lines++, totlines++)
2245         putchar ('\n');
2246     }
2247   if (flushit)
2248     {
2249       pending = 0;
2250       totlines = 0;
2251     }
2252   else
2253     pending = !pending;
2254 }
2255
2256
2257 static void
2258 print_template (int flushit)
2259 {
2260   char pseudo_op[200];
2261   int c, pseudo_op_idx = 0;
2262   int state = 0;
2263
2264   if (flushit && tex.end_block)
2265     {
2266       if (fseek (tex.fp, tex.end_block, SEEK_SET))
2267         {
2268           fprintf (stderr, PGMNAME ": error seeking to offset %ld\n",
2269                    tex.end_block);
2270           exit (1);
2271         }
2272     }
2273
2274   while ((c = getc (tex.fp)) != EOF)
2275     {
2276       switch (state)
2277         {
2278         case 0:
2279           if (c == '@')
2280             state = 1;
2281           else
2282             putchar (c);
2283           break;
2284         case 1:
2285           if (c == '@')
2286             {
2287               state = 2;
2288               pseudo_op_idx = 0;
2289             }
2290           else
2291             {
2292               putchar ('@');
2293               ungetc (c, tex.fp);
2294               state = 0;
2295             }
2296           break;
2297         case 2:         /* pseudo-op start */
2298           if (pseudo_op_idx >= sizeof (pseudo_op) - 1)
2299             {
2300               fprintf (stderr, PGMNAME ": pseudo-op too long\n");
2301               exit (1);
2302             }
2303           else if (c == '\n')
2304             {
2305               fprintf (stderr, PGMNAME ": invalid pseudo-op - ignored\n");
2306               pseudo_op[pseudo_op_idx] = 0;
2307               fputs (pseudo_op, stdout);
2308               putchar ('\n');
2309               state = 0;
2310             }
2311           else if (c == '@'
2312                    && pseudo_op_idx && pseudo_op[pseudo_op_idx - 1] == '@')
2313             {
2314               pseudo_op[pseudo_op_idx - 1] = 0;
2315               state = 0;
2316               if (!flushit && process_template_op (pseudo_op))
2317                 return;
2318             }
2319           else
2320             pseudo_op[pseudo_op_idx++] = c;
2321           break;
2322
2323         default:
2324           abort ();
2325         }
2326     }
2327   if (c == EOF)
2328     {
2329       if (ferror (tex.fp))
2330         {
2331           fprintf (stderr, PGMNAME ":%s: read error\n", opt.template);
2332           exit (1);
2333         }
2334       else if (state)
2335         {
2336           fprintf (stderr, PGMNAME ":%s: unclosed pseudo-op\n", opt.template);
2337         }
2338     }
2339
2340 }
2341
2342
2343 static int
2344 process_template_op (const char *op)
2345 {
2346   NAMEBUCKET buck;
2347   FIELD f;
2348   DATA d;
2349
2350   if (!strcasecmp (op, "begin-record-block"))
2351     {
2352       tex.in_record_block = 1;
2353       tex.begin_block = ftell (tex.fp);
2354     }
2355   else if (!strcasecmp (op, "end-record-block"))
2356     {
2357       tex.in_record_block = 0;
2358     }
2359   else if (!strcasecmp (op, "next-record") && tex.in_record_block)
2360     {
2361       tex.end_block = ftell (tex.fp);
2362       if (fseek (tex.fp, tex.begin_block, SEEK_SET))
2363         {
2364           fprintf (stderr, PGMNAME ": error seeking to offset %ld\n",
2365                    tex.begin_block);
2366           exit (1);
2367         }
2368       return 1;
2369     }
2370   else if (!tex.in_record_block)
2371     {
2372       fprintf (stderr,
2373                PGMNAME ": pseudo op '%s' not allowed in this context\n", op);
2374     }
2375   else /* Take it as the key to the record data. */
2376     {
2377       char *p = strchr (op, ':');
2378
2379       if (p)
2380         *p++ = 0; /* Strip modifier. */
2381
2382       f = NULL;
2383       for (buck = namebuckets[hash_name (op)]; buck; buck = buck->next)
2384         if (!strcasecmp (buck->ptr->name, op))
2385           {
2386             f = buck->ptr;
2387             break;
2388           }
2389       if (f) /* We have an entry with this name.  */
2390         {
2391           for (d = f->data; d; d = d->next)
2392             if (d->activ)
2393               {
2394                 printf ("%s", d->index > 1 ? (opt.texfile?"\\par ":"\n\n"):"");
2395                 if (p && !strncmp (p, "N=", 2))
2396                   {
2397                     size_t n;
2398
2399                     for (n = 0; n < d->used; n++)
2400                       if (d->d[n] == '\r')
2401                         ;
2402                       else if (d->d[n] == '\n')
2403                         fputs (p + 2, stdout);
2404                       else
2405                         putchar (((unsigned char *) d->d)[n]);
2406                   }
2407                 else
2408                   printf ("%.*s", (int) d->used, d->d);
2409               }
2410         }
2411     }
2412   return 0;
2413 }
2414
2415
2416 \f
2417 static int
2418 do_sort_fnc_num (const void *arg_a, const void *arg_b)
2419 {
2420   double aval, bval;
2421   SORT a = *(SORT *) arg_a;
2422   SORT b = *(SORT *) arg_b;
2423
2424   aval = strtod (a->d, NULL);
2425   bval = strtod (b->d, NULL);
2426   return aval < bval? -1 : aval > bval? 1 : 0;
2427 }
2428
2429 static int
2430 do_sort_fnc_numrev (const void *arg_a, const void *arg_b)
2431 {
2432   double aval, bval;
2433   SORT a = *(SORT *) arg_a;
2434   SORT b = *(SORT *) arg_b;
2435
2436   aval = strtod (a->d, NULL);
2437   bval = strtod (b->d, NULL);
2438   return aval < bval? 1 : aval > bval? -1 : 0;
2439 }
2440
2441 static int
2442 do_sort_fnc (const void *arg_a, const void *arg_b)
2443 {
2444   SORT a = *(SORT *) arg_a;
2445   SORT b = *(SORT *) arg_b;
2446   return strcmp (a->d, b->d);
2447 }
2448
2449 static int
2450 do_sort_fnc_rev (const void *arg_a, const void *arg_b)
2451 {
2452   SORT a = *(SORT *) arg_a;
2453   SORT b = *(SORT *) arg_b;
2454   return strcmp (b->d, a->d);
2455 }
2456
2457 /*
2458  * Sort the sortlist
2459  */
2460 static void
2461 do_sort ()
2462 {
2463   size_t i, n;
2464   SORT s, *array;
2465   int reverse = opt.sortfields? (opt.sortfields->flags & SORTFLAG_REVERSE) : 0;
2466   int numeric = opt.sortfields? (opt.sortfields->flags & SORTFLAG_NUMERIC) : 0;
2467
2468   for (n = 0, s = sortlist; s; s = s->next)
2469     n++;
2470   if (!n)
2471     return;
2472   array = xmalloc ((n + 1) * sizeof *array);
2473   for (n = 0, s = sortlist; s; s = s->next)
2474     array[n++] = s;
2475   array[n] = NULL;
2476   qsort (array, n, sizeof *array,
2477          (numeric && reverse)? do_sort_fnc_numrev :
2478          (numeric           )? do_sort_fnc_num :
2479          (reverse           )? do_sort_fnc_rev :
2480          /* */                 do_sort_fnc);
2481   sortlist = array[0];
2482   for (i = 0; i < n; i++)
2483     array[i]->next = array[i + 1];
2484 }
2485
2486
2487 /*
2488  * Uniq the sortlist.  Remove all identical records except for the
2489  * last one (or the first if /r is used).
2490  */
2491 static void
2492 do_uniq ()
2493 {
2494   size_t i, n;
2495   SORT s, *array;
2496   int reverse = opt.sortfields? (opt.sortfields->flags & SORTFLAG_REVERSE) : 0;
2497
2498   /* Allocate an array large enough to hold all items.  */
2499   for (n = 0, s = sortlist; s; s = s->next)
2500     n++;
2501   if (!n)
2502     return;
2503   array = xmalloc ((n + 1) * sizeof *array);
2504
2505   /* Put all items into the array and mark the duplicates.  */
2506   for (n = 0, s = sortlist; s; s = s->next)
2507     {
2508       if (reverse)
2509         {
2510           for (i=0; i < n; i++)
2511             if (array[i]->offset != -1 && !strcmp (array[i]->d, s->d))
2512               {
2513                 /* Same item found.  */
2514                 array[i]->offset = -1; /* Mark that as deleted.  */
2515               }
2516         }
2517       else
2518         {
2519           for (i=0; i < n; i++)
2520             if (array[i]->offset != -1 && !strcmp (array[i]->d, s->d))
2521               {
2522                 /* We already have such an item.  */
2523                 s->offset = -1; /* Mark me as deleted.  */
2524                 break;
2525               }
2526         }
2527
2528       array[n++] = s;
2529     }
2530   array[n] = NULL;
2531
2532   /* Rebuild the sortlist.  Reverse it to keep the order of records.  */
2533   if (!n)
2534     sortlist = NULL;
2535   else
2536     {
2537       sortlist = array[n-1];
2538       for (i = n-1; i > 0; i--)
2539         array[i]->next = array[i-1];
2540       array[0]->next = NULL;
2541     }
2542 }
2543
2544 /*
2545 Local Variables:
2546 compile-command: "cc -Wall -O2 -o addrutil addrutil.c"
2547 End:
2548 */