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