5f780008f722392de4ab78bbc9b1065350bb83dc
[enscript.git] / states / prims.c
1 /*
2  * Primitive procedures for states.
3  * Copyright (c) 1997-1999 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7
8 /*
9  * This file is part of GNU enscript.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; see the file COPYING.  If not, write to
23  * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26
27 #include "defs.h"
28
29 /*
30  * Types and definitions.
31  */
32
33 #define DEFUN(prim)                             \
34 static Node *                                   \
35 prim (prim_name, args, env, filename, linenum)  \
36      char *prim_name;                           \
37      List *args;                                \
38      Environment *env;                          \
39      char *filename;                            \
40      unsigned int linenum;
41
42 #define NEED_ARG()                                              \
43 do {                                                            \
44   if (arg == NULL)                                              \
45     {                                                           \
46       fprintf (stderr, _("%s:%d: %s: too few arguments\n"),     \
47                filename, linenum, prim_name);                   \
48       exit (1);                                                 \
49     }                                                           \
50 } while (0)
51
52 #define LAST_ARG()                                              \
53   do {                                                          \
54     if (arg != NULL)                                            \
55       {                                                         \
56         fprintf (stderr, _("%s:%d: %s: too many arguments\n"),  \
57                  filename, linenum, prim_name);                 \
58         exit (1);                                               \
59       }                                                         \
60   } while (0)
61
62 #define MATCH_ARG(type) \
63   match_arg (prim_name, type, &arg, env, filename, linenum)
64
65 #define APPEND(data, len)                               \
66   do {                                                  \
67     if (result_len < result_pos + (len))                \
68       {                                                 \
69         result_len += (len) + 1024;                     \
70         result = xrealloc (result, result_len);         \
71       }                                                 \
72     memcpy (result + result_pos, (data), (len));        \
73     result_pos += (len);                                \
74   } while (0)
75
76 #define FMTSPECIAL(ch) \
77      (('0' <= (ch) && (ch) <= '9') || (ch) == '.' || (ch) == '-')
78
79
80 /*
81  * Some forward protypes.
82  */
83
84 static Node *prim_print ___P ((char *prim_name, List *args,
85                                Environment *env, char *filename,
86                                unsigned int linenum));
87
88
89 /*
90  * Static functions.
91  */
92
93 static Node *
94 match_arg (prim_name, type, argp, env, filename, linenum)
95      char *prim_name;
96      NodeType type;
97      ListItem **argp;
98      Environment *env;
99      char *filename;
100      unsigned int linenum;
101 {
102   ListItem *arg = *argp;
103   Node *n;
104
105   NEED_ARG ();
106   n = eval_expr ((Expr *) arg->data, env);
107   if (type != nVOID && n->type != type)
108     {
109       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
110                filename, linenum, prim_name);
111       exit (1);
112     }
113   *argp = arg->next;
114
115   return n;
116 }
117
118 /* Primitives. */
119
120 DEFUN (prim_call)
121 {
122   ListItem *arg = args->head;
123   Expr *e;
124   char *cp;
125
126   e = (Expr *) arg->data;
127   if (e->type != eSYMBOL)
128     {
129       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
130                filename, linenum, prim_name);
131       exit (1);
132     }
133   cp = e->u.node->u.sym;
134
135   arg = arg->next;
136   LAST_ARG ();
137
138   return execute_state (cp);
139 }
140
141 DEFUN (prim_calln)
142 {
143   ListItem *arg = args->head;
144   Node *n;
145   char *cp;
146
147   n = MATCH_ARG (nSTRING);
148   LAST_ARG ();
149
150   cp = xmalloc (n->u.str.len + 1);
151   memcpy (cp, n->u.str.data, n->u.str.len);
152   cp[n->u.str.len] = '\0';
153
154   node_free (n);
155   n = execute_state (cp);
156   xfree (cp);
157
158   return n;
159 }
160
161
162 DEFUN (prim_check_namerules)
163 {
164   ListItem *arg = args->head;
165   ListItem *i;
166   Cons *c;
167   Node *n;
168
169   LAST_ARG ();
170
171   if (start_state)
172     goto return_false;
173
174   for (i = namerules->head; i; i = i->next)
175     {
176       c = (Cons *) i->data;
177       n = (Node *) c->car;
178
179       if (re_search (REGEXP (n), current_fname, strlen (current_fname),
180                      0, strlen (current_fname), NULL) >= 0)
181         {
182           /* This is it. */
183           n = (Node *) c->cdr;
184
185           start_state = n->u.sym;
186
187           n = node_alloc (nINTEGER);
188           n->u.integer = 1;
189           return n;
190         }
191     }
192
193 return_false:
194
195   n = node_alloc (nINTEGER);
196   n->u.integer = 0;
197
198   return n;
199 }
200
201
202 DEFUN (prim_check_startrules)
203 {
204   ListItem *arg = args->head;
205   ListItem *i;
206   Cons *c;
207   Node *n;
208
209   LAST_ARG ();
210
211   if (start_state)
212     goto return_false;
213
214   for (i = startrules->head; i; i = i->next)
215     {
216       c = (Cons *) i->data;
217       n = (Node *) c->car;
218
219       if (re_search (REGEXP (n), inbuf, data_in_buffer,
220                      0, data_in_buffer, NULL) >= 0)
221         {
222           /* This is it. */
223           n = (Node *) c->cdr;
224
225           start_state = n->u.sym;
226
227           n = node_alloc (nINTEGER);
228           n->u.integer = 1;
229           return n;
230         }
231     }
232
233 return_false:
234
235   n = node_alloc (nINTEGER);
236   n->u.integer = 0;
237
238   return n;
239 }
240
241
242 DEFUN (prim_concat)
243 {
244   ListItem *arg = args->head;
245   Node *n;
246   int len = 0;
247   char *data = NULL;
248
249   NEED_ARG ();
250   for (; arg; arg = arg->next)
251     {
252       n = eval_expr ((Expr *) arg->data, env);
253       if (n->type != nSTRING)
254         {
255           fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
256                    filename, linenum, prim_name);
257           exit (1);
258         }
259
260       if (n->u.str.len > 0)
261         {
262           data = (char *) xrealloc (data, len + n->u.str.len);
263           memcpy (data + len, n->u.str.data, n->u.str.len);
264           len += n->u.str.len;
265         }
266       node_free (n);
267     }
268
269   n = node_alloc (nSTRING);
270   n->u.str.data = data;
271   n->u.str.len = len;
272
273   return n;
274 }
275
276
277 DEFUN (prim_float)
278 {
279   ListItem *arg = args->head;
280   Node *n, *r;
281   char buf[512];
282
283   n = MATCH_ARG (nVOID);
284   LAST_ARG ();
285
286   r = node_alloc (nREAL);
287
288   switch (n->type)
289     {
290     case nVOID:
291     case nREGEXP:
292     case nSYMBOL:
293       r->u.real = 0.0;
294       break;
295
296     case nARRAY:
297       r->u.real = (double) n->u.array.len;
298       break;
299
300     case nSTRING:
301       if (n->u.str.len > sizeof (buf) - 1)
302         r->u.real = 0.0;
303       else
304         {
305           memcpy (buf, n->u.str.data, n->u.str.len);
306           buf[n->u.str.len] = '\0';
307           r->u.real = atof (buf);
308         }
309       break;
310
311     case nINTEGER:
312       r->u.real = (double) n->u.integer;
313       break;
314
315     case nREAL:
316       r->u.real = n->u.real;
317       break;
318     }
319
320   node_free (n);
321   return r;
322 }
323
324
325 DEFUN (prim_getenv)
326 {
327   ListItem *arg = args->head;
328   Node *var, *n;
329   char *key;
330   char *cp;
331
332   var = MATCH_ARG (nSTRING);
333   LAST_ARG ();
334
335   key = (char *) xcalloc (1, var->u.str.len + 1);
336   memcpy (key, var->u.str.data, var->u.str.len);
337
338   cp = getenv (key);
339
340   node_free (var);
341   xfree (key);
342
343   n = node_alloc (nSTRING);
344   if (cp == NULL)
345     {
346       n->u.str.data = (char *) xmalloc (1);
347       n->u.str.len = 0;
348     }
349   else
350     {
351       n->u.str.data = xstrdup (cp);
352       n->u.str.len = strlen (cp);
353     }
354
355   return n;
356 }
357
358
359 DEFUN (prim_int)
360 {
361   ListItem *arg = args->head;
362   Node *n, *r;
363   char buf[512];
364
365   n = MATCH_ARG (nVOID);
366   LAST_ARG ();
367
368   r = node_alloc (nINTEGER);
369
370   switch (n->type)
371     {
372     case nVOID:
373     case nREGEXP:
374     case nSYMBOL:
375       r->u.integer = 0;
376       break;
377
378     case nARRAY:
379       r->u.integer = n->u.array.len;
380       break;
381
382     case nSTRING:
383       if (n->u.str.len > sizeof (buf) - 1)
384         r->u.integer = 0;
385       else
386         {
387           memcpy (buf, n->u.str.data, n->u.str.len);
388           buf[n->u.str.len] = '\0';
389           r->u.integer = atoi (buf);
390         }
391       break;
392
393     case nINTEGER:
394       r->u.integer = n->u.integer;
395       break;
396
397     case nREAL:
398       r->u.integer = (int) n->u.real;
399       break;
400     }
401
402   node_free (n);
403   return r;
404 }
405
406
407 DEFUN (prim_length)
408 {
409   ListItem *arg = args->head;
410   Node *n;
411   int result = 0;
412
413   NEED_ARG ();
414   for (; arg; arg = arg->next)
415     {
416       n = eval_expr ((Expr *) arg->data, env);
417       switch (n->type)
418         {
419         case nSTRING:
420           result += n->u.str.len;
421           break;
422
423         case nARRAY:
424           result += n->u.array.len;
425           break;
426
427         default:
428           fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
429                    filename, linenum, prim_name);
430           exit (1);
431           break;
432         }
433       node_free (n);
434     }
435
436   n = node_alloc (nINTEGER);
437   n->u.integer = result;
438
439   return n;
440 }
441
442
443 DEFUN (prim_list)
444 {
445   ListItem *arg = args->head;
446   unsigned int len;
447   Node *n;
448
449   /* Count list length. */
450   for (len = 0; arg; len++, arg = arg->next)
451     ;
452   arg = args->head;
453
454   /* Create list node. */
455   n = node_alloc (nARRAY);
456   n->u.array.array = (Node **) xcalloc (len + 1, sizeof (Node *));
457   n->u.array.allocated = len + 1;
458   n->u.array.len = len;
459
460   /* Fill it up. */
461   for (len = 0; arg; len++, arg = arg->next)
462     n->u.array.array[len] = eval_expr ((Expr *) arg->data, env);
463
464   return n;
465 }
466
467
468 DEFUN (prim_panic)
469 {
470   fprintf (stderr, _("%s: panic: "), program);
471   ofp = stderr;
472   prim_print (prim_name, args, env, filename, linenum);
473   fprintf (stderr, "\n");
474   exit (1);
475
476   /* NOTREACHED */
477   return nvoid;
478 }
479
480
481 DEFUN (prim_prereq)
482 {
483   ListItem *arg = args->head;
484   Node *s;
485   int over[3];
486   int rver[3];
487   char *cp;
488   int i;
489
490   s = MATCH_ARG (nSTRING);
491   LAST_ARG ();
492
493   /* Our version. */
494   sscanf (VERSION, "%d.%d.%d", &over[0], &over[1], &over[2]);
495
496   /* Required version. */
497
498   cp = (char *) xcalloc (1, s->u.str.len + 1);
499   memcpy (cp, s->u.str.data, s->u.str.len);
500
501   if (sscanf (cp, "%d.%d.%d", &rver[0], &rver[1], &rver[2]) != 3)
502     {
503       fprintf (stderr,
504                _("%s:%d: %s: malformed version string `%s'\n"),
505                filename, linenum, prim_name, cp);
506       exit (1);
507     }
508
509   /* Check version. */
510   for (i = 0; i < 3; i++)
511     {
512       if (over[i] > rver[i])
513         /* Ok, our version is bigger. */
514         break;
515       if (over[i] < rver[i])
516         {
517           /* Fail, our version is too small. */
518           fprintf (stderr,
519                    _("%s: FATAL ERROR: States version %s or higher is required for this script\n"),
520                    program, cp);
521           exit (1);
522         }
523     }
524
525   /* Our version is higher or equal to the required one. */
526   xfree (cp);
527
528   return nvoid;
529 }
530
531
532 static void
533 print_node (n)
534      Node *n;
535 {
536   unsigned int i;
537
538   switch (n->type)
539     {
540     case nVOID:
541       break;
542
543     case nSTRING:
544       fwrite (n->u.str.data, n->u.str.len, 1, ofp);
545       break;
546
547     case nREGEXP:
548       fputc ('/', ofp);
549       fwrite (n->u.re.data, n->u.re.len, 1, ofp);
550       fputc ('/', ofp);
551       break;
552
553     case nINTEGER:
554       fprintf (ofp, "%d", n->u.integer);
555       break;
556
557     case nREAL:
558       fprintf (ofp, "%f", n->u.real);
559       break;
560
561     case nSYMBOL:
562       fprintf (ofp, "%s", n->u.sym);
563       break;
564
565     case nARRAY:
566       for (i = 0; i < n->u.array.len; i++)
567         {
568           print_node (n->u.array.array[i]);
569           if (i + 1 < n->u.array.len)
570             fprintf (ofp, " ");
571         }
572     }
573 }
574
575
576 DEFUN (prim_print)
577 {
578   ListItem *arg = args->head;
579   Node *n;
580
581   NEED_ARG ();
582   for (; arg; arg = arg->next)
583     {
584       n = eval_expr ((Expr *) arg->data, env);
585       print_node (n);
586       node_free (n);
587     }
588
589   return nvoid;
590 }
591
592
593 DEFUN (prim_range)
594 {
595   ListItem *arg = args->head;
596   Node *from, *start, *end, *n;
597   int i;
598
599   NEED_ARG ();
600   from = eval_expr ((Expr *) arg->data, env);
601   arg = arg->next;
602
603   start = MATCH_ARG (nINTEGER);
604   end = MATCH_ARG (nINTEGER);
605   LAST_ARG ();
606
607   if (start->u.integer > end->u.integer)
608     {
609       fprintf (stderr,
610                _("%s:%d: %s: start offset is bigger than end offset\n"),
611                filename, linenum, prim_name);
612       exit (1);
613     }
614
615   if (from->type == nSTRING)
616     {
617       if (end->u.integer > from->u.str.len)
618         {
619           fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
620                    filename, linenum, prim_name);
621           exit (1);
622         }
623
624       n = node_alloc (nSTRING);
625       n->u.str.len = end->u.integer - start->u.integer;
626       /* +1 to avoid zero allocation */
627       n->u.str.data = (char *) xmalloc (n->u.str.len + 1);
628       memcpy (n->u.str.data, from->u.str.data + start->u.integer,
629               n->u.str.len);
630     }
631   else if (from->type == nARRAY)
632     {
633       if (end->u.integer > from->u.array.len)
634         {
635           fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
636                    filename, linenum, prim_name);
637           exit (1);
638         }
639
640       n = node_alloc (nARRAY);
641       n->u.array.len = end->u.integer - start->u.integer;
642       /* +1 to avoid zero allocation */
643       n->u.array.allocated = n->u.array.len + 1;
644       n->u.array.array = (Node **) xcalloc (n->u.array.allocated,
645                                             sizeof (Node *));
646
647       for (i = 0; i < n->u.array.len; i++)
648         n->u.array.array[i]
649           = node_copy (from->u.array.array[i + start->u.integer]);
650     }
651   else
652     {
653       fprintf (stderr, _("%s:%d: %s: illegal argument\n"),
654                filename, linenum, prim_name);
655       exit (1);
656     }
657
658   node_free (from);
659   node_free (start);
660   node_free (end);
661
662   return n;
663 }
664
665
666 DEFUN (prim_regexp)
667 {
668   ListItem *arg = args->head;
669   Node *str, *n;
670
671   str = MATCH_ARG (nSTRING);
672   LAST_ARG ();
673
674   /* Create a new REGEXP node. */
675
676   n = node_alloc (nREGEXP);
677   n->u.re.data = xmalloc (str->u.str.len + 1);
678   n->u.re.len = str->u.str.len;
679   memcpy (n->u.re.data, str->u.str.data, str->u.str.len);
680   n->u.re.data[str->u.str.len] = '\0';
681
682   return n;
683 }
684
685
686 DEFUN (prim_regexp_syntax)
687 {
688   ListItem *arg = args->head;
689   Node *ch, *st;
690   char syntax;
691
692   ch = MATCH_ARG (nINTEGER);
693   st = MATCH_ARG (nINTEGER);
694   LAST_ARG ();
695
696   syntax = (char) st->u.integer;
697   if (syntax != 'w' && syntax != ' ')
698     {
699       fprintf (stderr,
700                _("%s:%d: %s: illegal regexp character syntax: %c\n"),
701                filename, linenum, prim_name, syntax);
702       exit (1);
703     }
704
705   re_set_character_syntax ((unsigned char) ch->u.integer, syntax);
706
707   return nvoid;
708 }
709
710
711 DEFUN (prim_regmatch)
712 {
713   ListItem *arg = args->head;
714   Node *str, *re, *n;
715   static struct re_registers matches = {0, NULL, NULL};
716   static Node *current_match_node = NULL;
717   int i;
718
719   str = MATCH_ARG (nSTRING);
720   re = MATCH_ARG (nREGEXP);
721   LAST_ARG ();
722
723   /* Search for match. */
724   i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
725                  0, str->u.str.len, &matches);
726
727   if (i < 0)
728     {
729       current_match = NULL;
730       node_free (str);
731     }
732   else
733     {
734       node_free (current_match_node);
735       current_match_node = str;
736
737       current_match = &matches;
738       current_match_buf = str->u.str.data;
739     }
740   node_free (re);
741
742   n = node_alloc (nINTEGER);
743   n->u.integer = (i >= 0);
744
745   return n;
746 }
747
748
749 /*
750  * Common regular expression substituter for regsub and regsuball.
751  */
752
753 Node *
754 do_regsubsts (str, re, subst, allp)
755      Node *str;
756      Node *re;
757      Node *subst;
758      int allp;
759 {
760   int i, pos, j;
761   static struct re_registers matches = {0, NULL, NULL};
762   static char *result = NULL;
763   static unsigned int result_len = 0;
764   unsigned int result_pos = 0;
765   int num_matches = 0;
766   int do_expansions_in_substs = 0;
767
768   /* Do we have to do expansions in the substitution string. */
769   for (i = 0; i < subst->u.str.len; i++)
770     if (subst->u.str.data[i] == '$')
771       {
772         do_expansions_in_substs = 1;
773         break;
774       }
775
776   pos = 0;
777   while (1)
778     {
779       /* Search for match. */
780       i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
781                      pos, str->u.str.len - pos, &matches);
782       if (i < 0)
783         goto out;
784
785       num_matches++;
786
787       /* Everything before match. */
788       APPEND (str->u.str.data + pos, matches.start[0] - pos);
789
790       /* Append match. */
791       if (!do_expansions_in_substs)
792         APPEND (subst->u.str.data, subst->u.str.len);
793       else
794         {
795           /* Must process substitution string. */
796           for (i = 0; i < subst->u.str.len; i++)
797             if (subst->u.str.data[i] == '$' && i + 1 < subst->u.str.len)
798               {
799                 i++;
800                 switch (subst->u.str.data[i])
801                   {
802                   case '$':
803                     APPEND ("$", 1);
804                     break;
805
806                   case '0':
807                   case '1':
808                   case '2':
809                   case '3':
810                   case '4':
811                   case '5':
812                   case '6':
813                   case '7':
814                   case '8':
815                   case '9':
816                     j = subst->u.str.data[i] - '0';
817                     if (matches.start[j] >= 0)
818                       APPEND (str->u.str.data + matches.start[j],
819                               matches.end[j] - matches.start[j]);
820                     break;
821
822                   default:
823                     /* Illegal substitution, just pass it through. */
824                     APPEND ("$", 1);
825                     APPEND (subst->u.str.data + i, 1);
826                     break;
827                   }
828               }
829             else
830               APPEND (subst->u.str.data + i, 1);
831         }
832
833       /* Update pos. */
834       pos = matches.end[0];
835
836       if (!allp)
837         break;
838     }
839
840 out:
841   if (num_matches == 0)
842     {
843       /* No matches, just return our original string. */
844       node_free (re);
845       node_free (subst);
846       return str;
847     }
848
849   APPEND (str->u.str.data + pos, str->u.str.len - pos);
850
851   /* Create result node. */
852   node_free (str);
853   node_free (re);
854   node_free (subst);
855
856   str = node_alloc (nSTRING);
857   str->u.str.len = result_pos;
858   str->u.str.data = xmalloc (result_pos);
859   memcpy (str->u.str.data, result, result_pos);
860
861   return str;
862 }
863
864
865 DEFUN (prim_regsub)
866 {
867   ListItem *arg = args->head;
868   Node *str, *re, *subst;
869
870   str = MATCH_ARG (nSTRING);
871   re = MATCH_ARG (nREGEXP);
872   subst = MATCH_ARG (nSTRING);
873   LAST_ARG ();
874
875   return do_regsubsts (str, re, subst, 0);
876 }
877
878
879 DEFUN (prim_regsuball)
880 {
881   ListItem *arg = args->head;
882   Node *str, *re, *subst;
883
884   str = MATCH_ARG (nSTRING);
885   re = MATCH_ARG (nREGEXP);
886   subst = MATCH_ARG (nSTRING);
887   LAST_ARG ();
888
889   return do_regsubsts (str, re, subst, 1);
890 }
891
892
893 DEFUN (prim_require_state)
894 {
895   ListItem *arg = args->head;
896   Expr *e;
897   char *cp;
898   State *state;
899
900   e = (Expr *) arg->data;
901   if (e->type != eSYMBOL)
902     {
903       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
904                filename, linenum, prim_name);
905       exit (1);
906     }
907   cp = e->u.node->u.sym;
908
909   arg = arg->next;
910   LAST_ARG ();
911
912   state = lookup_state (cp);
913   if (state == NULL)
914     {
915       fprintf (stderr, _("%s:%d: %s: couldn't define state `%s'\n"),
916                filename, linenum, prim_name, cp);
917       exit (1);
918     }
919
920   return nvoid;
921 }
922
923
924 DEFUN (prim_split)
925 {
926   ListItem *arg = args->head;
927   Node *re, *str, *n, *n2;
928   int pos, i;
929
930   re = MATCH_ARG (nREGEXP);
931   str = MATCH_ARG (nSTRING);
932   LAST_ARG ();
933
934   /* Create a new array node. */
935   n = node_alloc (nARRAY);
936   n->u.array.allocated = 100;
937   n->u.array.array = (Node **) xcalloc (n->u.array.allocated, sizeof (Node *));
938
939   for (pos = 0; pos < str->u.str.len;)
940     {
941       i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
942                      pos, str->u.str.len - pos, &re->u.re.matches);
943       if (i < 0)
944         /* No more matches. */
945         break;
946
947       /* Append the string before the first match. */
948       n2 = node_alloc (nSTRING);
949       n2->u.str.len = i - pos;
950       n2->u.str.data = (char *) xmalloc (n2->u.str.len + 1);
951       memcpy (n2->u.str.data, str->u.str.data + pos, n2->u.str.len);
952       pos = re->u.re.matches.end[0];
953
954       /*
955        * Check that at least one item fits after us (no need to check
956        * when appending the last item).
957        */
958       if (n->u.array.len + 1 >= n->u.array.allocated)
959         {
960           n->u.array.allocated += 100;
961           n->u.array.array = (Node **) xrealloc (n->u.array.array,
962                                                  n->u.array.allocated
963                                                  * sizeof (Node *));
964         }
965       n->u.array.array[n->u.array.len++] = n2;
966     }
967
968   /* Append all the remaining data. */
969   n2 = node_alloc (nSTRING);
970   n2->u.str.len = str->u.str.len - pos;
971   n2->u.str.data = (char *) xmalloc (n2->u.str.len + 1);
972   memcpy (n2->u.str.data, str->u.str.data + pos, n2->u.str.len);
973
974   n->u.array.array[n->u.array.len++] = n2;
975
976   return n;
977 }
978
979
980 DEFUN (prim_sprintf)
981 {
982   ListItem *arg = args->head;
983   Node *fmt, *n;
984   char buf[512];
985   char ifmt[256];
986   char ifmtopts[256];
987   char *result = NULL;
988   unsigned int result_pos = 0;
989   unsigned int result_len = 0;
990   int i, j;
991   int argument_count = 0;
992   char *cp;
993
994   fmt = MATCH_ARG (nSTRING);
995   cp = fmt->u.str.data;
996
997   /* Process format string and match arguments. */
998   for (i = 0; i < fmt->u.str.len; i++)
999     {
1000       if (cp[i] == '%' && (i + 1 >= fmt->u.str.len || cp[i + 1] == '%'))
1001         {
1002           i++;
1003           APPEND (cp + i, 1);
1004         }
1005       else if (cp[i] == '%')
1006         {
1007           argument_count++;
1008
1009           if (arg == NULL)
1010             {
1011               fprintf (stderr,
1012                        _("%s: primitive `%s': too few arguments for format\n"),
1013                        program, prim_name);
1014               exit (1);
1015             }
1016           n = eval_expr ((Expr *) arg->data, env);
1017           arg = arg->next;
1018
1019           for (i++, j = 0; i < fmt->u.str.len && FMTSPECIAL (cp[i]); i++, j++)
1020             ifmtopts[j] = cp[i];
1021           ifmtopts[j] = '\0';
1022
1023           if (i >= fmt->u.str.len)
1024             {
1025               APPEND ("%", 1);
1026               APPEND (ifmtopts, j);
1027               continue;
1028             }
1029
1030           /* Field type. */
1031           switch (cp[i])
1032             {
1033             case 'x':
1034             case 'X':
1035             case 'd':
1036               if (n->type != nINTEGER)
1037                 {
1038                 no_match:
1039                   fprintf (stderr,
1040                            _("%s:%d: %s: argument %d doesn't match format\n"),
1041                            filename, linenum, prim_name, argument_count);
1042                   exit (1);
1043                 }
1044               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1045               sprintf (buf, ifmt, n->u.integer);
1046
1047               APPEND (buf, strlen (buf));
1048               break;
1049
1050             case 'c':
1051               if (n->type != nINTEGER)
1052                 goto no_match;
1053
1054               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1055               sprintf (buf, ifmt, n->u.integer);
1056
1057               APPEND (buf, strlen (buf));
1058               break;
1059
1060             case 'f':
1061             case 'g':
1062             case 'e':
1063             case 'E':
1064               if (n->type != nREAL)
1065                 goto no_match;
1066
1067               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1068               sprintf (buf, ifmt, n->u.real);
1069
1070               APPEND (buf, strlen (buf));
1071               break;
1072
1073             case 's':
1074               if (n->type != nSTRING)
1075                 goto no_match;
1076
1077               if (ifmtopts[0] != '\0')
1078                 {
1079                   fprintf (stderr,
1080                            _("%s:%d: %s: no extra options can be specified for %%s\n"),
1081                            filename, linenum, prim_name);
1082                   exit (1);
1083                 }
1084               APPEND (n->u.str.data, n->u.str.len);
1085               break;
1086
1087             default:
1088               fprintf (stderr,
1089                        _("%s:%d: %s: illegal type specifier `%c'\n"),
1090                        filename, linenum, prim_name, cp[i]);
1091               exit (1);
1092               break;
1093             }
1094         }
1095       else
1096         APPEND (cp + i, 1);
1097     }
1098
1099   node_free (fmt);
1100
1101   n = node_alloc (nSTRING);
1102   n->u.str.len = result_pos;
1103   n->u.str.data = result;
1104
1105   return n;
1106 }
1107
1108 \f
1109 DEFUN (prim_strcmp)
1110 {
1111   ListItem *arg = args->head;
1112   Node *s1, *s2;
1113   Node *n;
1114   int i, result;
1115   char *cp1, *cp2;
1116
1117   s1 = MATCH_ARG (nSTRING);
1118   s2 = MATCH_ARG (nSTRING);
1119   LAST_ARG ();
1120
1121   cp1 = s1->u.str.data;
1122   cp2 = s2->u.str.data;
1123
1124   for (i = 0; i < s1->u.str.len && i < s2->u.str.len; i++)
1125     {
1126       if (cp1[i] < cp2[i])
1127         {
1128           result = -1;
1129           goto out;
1130         }
1131       if (cp1[i] > cp2[i])
1132         {
1133           result = 1;
1134           goto out;
1135         }
1136     }
1137   /* Strings are so far equal, check lengths. */
1138   if (s1->u.str.len < s2->u.str.len)
1139     result = -1;
1140   else if (s1->u.str.len > s2->u.str.len)
1141     result = 1;
1142   else
1143     result = 0;
1144
1145 out:
1146   node_free (s1);
1147   node_free (s2);
1148   n = node_alloc (nINTEGER);
1149   n->u.integer = result;
1150
1151   return n;
1152 }
1153
1154
1155 DEFUN (prim_string)
1156 {
1157   ListItem *arg = args->head;
1158   Node *n, *r;
1159   char buf[512];
1160
1161   n = MATCH_ARG (nVOID);
1162   LAST_ARG ();
1163
1164   r = node_alloc (nSTRING);
1165
1166   switch (n->type)
1167     {
1168     case nVOID:
1169     case nREGEXP:
1170     case nARRAY:
1171       r->u.str.data = (char *) xcalloc (1, 1);
1172       r->u.str.len = 0;
1173       break;
1174
1175     case nSYMBOL:
1176       r->u.str.len = strlen (n->u.sym);
1177       r->u.str.data = (char *) xmalloc (r->u.str.len);
1178       memcpy (r->u.str.data, n->u.sym, r->u.str.len);
1179       break;
1180
1181     case nSTRING:
1182       r->u.str.len = n->u.str.len;
1183       r->u.str.data = (char *) xmalloc (n->u.str.len);
1184       memcpy (r->u.str.data, n->u.str.data, n->u.str.len);
1185       break;
1186
1187     case nINTEGER:
1188       sprintf (buf, "%d", n->u.integer);
1189       r->u.str.len = strlen (buf);
1190       r->u.str.data = (char *) xmalloc (r->u.str.len);
1191       memcpy (r->u.str.data, buf, r->u.str.len);
1192       break;
1193
1194     case nREAL:
1195       sprintf (buf, "%f", n->u.real);
1196       r->u.str.len = strlen (buf);
1197       r->u.str.data = (char *) xmalloc (r->u.str.len);
1198       memcpy (r->u.str.data, buf, r->u.str.len);
1199       break;
1200     }
1201
1202   node_free (n);
1203   return r;
1204 }
1205
1206
1207 DEFUN (prim_strncmp)
1208 {
1209   ListItem *arg = args->head;
1210   Node *s1, *s2, *len;
1211   Node *n;
1212   int i, result;
1213   char *cp1, *cp2;
1214
1215   s1 = MATCH_ARG (nSTRING);
1216   s2 = MATCH_ARG (nSTRING);
1217   len = MATCH_ARG (nINTEGER);
1218   LAST_ARG ();
1219
1220   cp1 = s1->u.str.data;
1221   cp2 = s2->u.str.data;
1222
1223   for (i = 0; i < s1->u.str.len && i < s2->u.str.len && i < len->u.integer; i++)
1224     {
1225       if (cp1[i] < cp2[i])
1226         {
1227           result = -1;
1228           goto out;
1229         }
1230       if (cp1[i] > cp2[i])
1231         {
1232           result = 1;
1233           goto out;
1234         }
1235     }
1236
1237   /* Check the limit length. */
1238   if (i >= len->u.integer)
1239     {
1240       result = 0;
1241       goto out;
1242     }
1243
1244   /* One or both strings were shorter than limit, check lengths. */
1245   if (s1->u.str.len < s2->u.str.len)
1246     result = -1;
1247   else if (s1->u.str.len > s2->u.str.len)
1248     result = 1;
1249   else
1250     result = 0;
1251
1252 out:
1253   node_free (s1);
1254   node_free (s2);
1255   node_free (len);
1256   n = node_alloc (nINTEGER);
1257   n->u.integer = result;
1258
1259   return n;
1260 }
1261
1262
1263 DEFUN (prim_substring)
1264 {
1265   ListItem *arg = args->head;
1266   Node *str, *start, *end, *n;
1267
1268   str = MATCH_ARG (nSTRING);
1269   start = MATCH_ARG (nINTEGER);
1270   end = MATCH_ARG (nINTEGER);
1271   LAST_ARG ();
1272
1273   if (start->u.integer > end->u.integer)
1274     {
1275       fprintf (stderr,
1276                _("%s:%d: %s: start offset is bigger than end offset\n"),
1277                filename, linenum, prim_name);
1278       exit (1);
1279     }
1280   if (end->u.integer > str->u.str.len)
1281     {
1282       fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
1283                filename, linenum, prim_name);
1284       exit (1);
1285     }
1286
1287   n = node_alloc (nSTRING);
1288   n->u.str.len = end->u.integer - start->u.integer;
1289   /* +1 to avoid zero allocation */
1290   n->u.str.data = (char *) xmalloc (n->u.str.len + 1);
1291
1292   memcpy (n->u.str.data, str->u.str.data + start->u.integer,
1293           n->u.str.len);
1294
1295   node_free (str);
1296   node_free (start);
1297   node_free (end);
1298
1299   return n;
1300 }
1301
1302
1303 DEFUN (prim_system)
1304 {
1305   ListItem *arg = args->head;
1306   Node *str, *n;
1307   char *cmd;
1308   int result;
1309
1310   str = MATCH_ARG (nSTRING);
1311   LAST_ARG ();
1312
1313   cmd = (char *) xcalloc (1, str->u.str.len + 1);
1314   memcpy (cmd, str->u.str.data, str->u.str.len);
1315
1316   result = system (cmd);
1317   xfree (cmd);
1318
1319   n = node_alloc (nINTEGER);
1320   n->u.integer = result;
1321
1322   return n;
1323 }
1324
1325
1326 /*
1327  * Global functions.
1328  */
1329
1330 static struct
1331 {
1332   char *name;
1333   Primitive prim;
1334 } prims[] =
1335   {
1336     {"call",                    prim_call},
1337     {"calln",                   prim_calln},
1338     {"check_namerules",         prim_check_namerules},
1339     {"check_startrules",        prim_check_startrules},
1340     {"concat",                  prim_concat},
1341     {"float",                   prim_float},
1342     {"getenv",                  prim_getenv},
1343     {"int",                     prim_int},
1344     {"length",                  prim_length},
1345     {"list",                    prim_list},
1346     {"panic",                   prim_panic},
1347     {"prereq",                  prim_prereq},
1348     {"print",                   prim_print},
1349     {"range",                   prim_range},
1350     {"regexp",                  prim_regexp},
1351     {"regexp_syntax",           prim_regexp_syntax},
1352     {"regmatch",                prim_regmatch},
1353     {"regsub",                  prim_regsub},
1354     {"regsuball",               prim_regsuball},
1355     {"require_state",           prim_require_state},
1356     {"split",                   prim_split},
1357     {"sprintf",                 prim_sprintf},
1358     {"strcmp",                  prim_strcmp},
1359     {"string",                  prim_string},
1360     {"strncmp",                 prim_strncmp},
1361     {"substring",               prim_substring},
1362     {"system",                  prim_system},
1363
1364     {NULL, NULL},
1365   };
1366
1367 void
1368 init_primitives ()
1369 {
1370   void *old;
1371   int i;
1372
1373   for (i = 0; prims[i].name; i++)
1374     if (!strhash_put (ns_prims, prims[i].name, strlen (prims[i].name),
1375                       (void *) prims[i].prim, &old))
1376       {
1377         fprintf (stderr, _("%s: out of memory\n"), program);
1378         exit (1);
1379       }
1380 }