2 * Primitive procedures for states.
3 * Copyright (c) 1997-1999 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This file is part of GNU enscript.
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)
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.
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.
30 * Types and definitions.
35 prim (prim_name, args, env, filename, linenum) \
46 fprintf (stderr, _("%s:%d: %s: too few arguments\n"), \
47 filename, linenum, prim_name); \
56 fprintf (stderr, _("%s:%d: %s: too many arguments\n"), \
57 filename, linenum, prim_name); \
62 #define MATCH_ARG(type) \
63 match_arg (prim_name, type, &arg, env, filename, linenum)
65 #define APPEND(data, len) \
67 if (result_len < result_pos + (len)) \
69 result_len += (len) + 1024; \
70 result = xrealloc (result, result_len); \
72 memcpy (result + result_pos, (data), (len)); \
73 result_pos += (len); \
76 #define FMTSPECIAL(ch) \
77 (('0' <= (ch) && (ch) <= '9') || (ch) == '.' || (ch) == '-')
81 * Some forward protypes.
84 static Node *prim_print ___P ((char *prim_name, List *args,
85 Environment *env, char *filename,
86 unsigned int linenum));
94 match_arg (prim_name, type, argp, env, filename, linenum)
100 unsigned int linenum;
102 ListItem *arg = *argp;
106 n = eval_expr ((Expr *) arg->data, env);
107 if (type != nVOID && n->type != type)
109 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
110 filename, linenum, prim_name);
122 ListItem *arg = args->head;
126 e = (Expr *) arg->data;
127 if (e->type != eSYMBOL)
129 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
130 filename, linenum, prim_name);
133 cp = e->u.node->u.sym;
138 return execute_state (cp);
143 ListItem *arg = args->head;
147 n = MATCH_ARG (nSTRING);
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';
155 n = execute_state (cp);
162 DEFUN (prim_check_namerules)
164 ListItem *arg = args->head;
174 for (i = namerules->head; i; i = i->next)
176 c = (Cons *) i->data;
179 if (re_search (REGEXP (n), current_fname, strlen (current_fname),
180 0, strlen (current_fname), NULL) >= 0)
185 start_state = n->u.sym;
187 n = node_alloc (nINTEGER);
195 n = node_alloc (nINTEGER);
202 DEFUN (prim_check_startrules)
204 ListItem *arg = args->head;
214 for (i = startrules->head; i; i = i->next)
216 c = (Cons *) i->data;
219 if (re_search (REGEXP (n), inbuf, data_in_buffer,
220 0, data_in_buffer, NULL) >= 0)
225 start_state = n->u.sym;
227 n = node_alloc (nINTEGER);
235 n = node_alloc (nINTEGER);
244 ListItem *arg = args->head;
250 for (; arg; arg = arg->next)
252 n = eval_expr ((Expr *) arg->data, env);
253 if (n->type != nSTRING)
255 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
256 filename, linenum, prim_name);
260 if (n->u.str.len > 0)
262 data = (char *) xrealloc (data, len + n->u.str.len);
263 memcpy (data + len, n->u.str.data, n->u.str.len);
269 n = node_alloc (nSTRING);
270 n->u.str.data = data;
279 ListItem *arg = args->head;
283 n = MATCH_ARG (nVOID);
286 r = node_alloc (nREAL);
297 r->u.real = (double) n->u.array.len;
301 if (n->u.str.len > sizeof (buf) - 1)
305 memcpy (buf, n->u.str.data, n->u.str.len);
306 buf[n->u.str.len] = '\0';
307 r->u.real = atof (buf);
312 r->u.real = (double) n->u.integer;
316 r->u.real = n->u.real;
327 ListItem *arg = args->head;
332 var = MATCH_ARG (nSTRING);
335 key = (char *) xcalloc (1, var->u.str.len + 1);
336 memcpy (key, var->u.str.data, var->u.str.len);
343 n = node_alloc (nSTRING);
346 n->u.str.data = (char *) xmalloc (1);
351 n->u.str.data = xstrdup (cp);
352 n->u.str.len = strlen (cp);
361 ListItem *arg = args->head;
365 n = MATCH_ARG (nVOID);
368 r = node_alloc (nINTEGER);
379 r->u.integer = n->u.array.len;
383 if (n->u.str.len > sizeof (buf) - 1)
387 memcpy (buf, n->u.str.data, n->u.str.len);
388 buf[n->u.str.len] = '\0';
389 r->u.integer = atoi (buf);
394 r->u.integer = n->u.integer;
398 r->u.integer = (int) n->u.real;
409 ListItem *arg = args->head;
414 for (; arg; arg = arg->next)
416 n = eval_expr ((Expr *) arg->data, env);
420 result += n->u.str.len;
424 result += n->u.array.len;
428 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
429 filename, linenum, prim_name);
436 n = node_alloc (nINTEGER);
437 n->u.integer = result;
445 ListItem *arg = args->head;
449 /* Count list length. */
450 for (len = 0; arg; len++, arg = arg->next)
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;
461 for (len = 0; arg; len++, arg = arg->next)
462 n->u.array.array[len] = eval_expr ((Expr *) arg->data, env);
470 fprintf (stderr, _("%s: panic: "), program);
472 prim_print (prim_name, args, env, filename, linenum);
473 fprintf (stderr, "\n");
483 ListItem *arg = args->head;
490 s = MATCH_ARG (nSTRING);
494 sscanf (VERSION, "%d.%d.%d", &over[0], &over[1], &over[2]);
496 /* Required version. */
498 cp = (char *) xcalloc (1, s->u.str.len + 1);
499 memcpy (cp, s->u.str.data, s->u.str.len);
501 if (sscanf (cp, "%d.%d.%d", &rver[0], &rver[1], &rver[2]) != 3)
504 _("%s:%d: %s: malformed version string `%s'\n"),
505 filename, linenum, prim_name, cp);
510 for (i = 0; i < 3; i++)
512 if (over[i] > rver[i])
513 /* Ok, our version is bigger. */
515 if (over[i] < rver[i])
517 /* Fail, our version is too small. */
519 _("%s: FATAL ERROR: States version %s or higher is required for this script\n"),
525 /* Our version is higher or equal to the required one. */
544 fwrite (n->u.str.data, n->u.str.len, 1, ofp);
549 fwrite (n->u.re.data, n->u.re.len, 1, ofp);
554 fprintf (ofp, "%d", n->u.integer);
558 fprintf (ofp, "%f", n->u.real);
562 fprintf (ofp, "%s", n->u.sym);
566 for (i = 0; i < n->u.array.len; i++)
568 print_node (n->u.array.array[i]);
569 if (i + 1 < n->u.array.len)
578 ListItem *arg = args->head;
582 for (; arg; arg = arg->next)
584 n = eval_expr ((Expr *) arg->data, env);
595 ListItem *arg = args->head;
596 Node *from, *start, *end, *n;
600 from = eval_expr ((Expr *) arg->data, env);
603 start = MATCH_ARG (nINTEGER);
604 end = MATCH_ARG (nINTEGER);
607 if (start->u.integer > end->u.integer)
610 _("%s:%d: %s: start offset is bigger than end offset\n"),
611 filename, linenum, prim_name);
615 if (from->type == nSTRING)
617 if (end->u.integer > from->u.str.len)
619 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
620 filename, linenum, prim_name);
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,
631 else if (from->type == nARRAY)
633 if (end->u.integer > from->u.array.len)
635 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
636 filename, linenum, prim_name);
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,
647 for (i = 0; i < n->u.array.len; i++)
649 = node_copy (from->u.array.array[i + start->u.integer]);
653 fprintf (stderr, _("%s:%d: %s: illegal argument\n"),
654 filename, linenum, prim_name);
668 ListItem *arg = args->head;
671 str = MATCH_ARG (nSTRING);
674 /* Create a new REGEXP node. */
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';
686 DEFUN (prim_regexp_syntax)
688 ListItem *arg = args->head;
692 ch = MATCH_ARG (nINTEGER);
693 st = MATCH_ARG (nINTEGER);
696 syntax = (char) st->u.integer;
697 if (syntax != 'w' && syntax != ' ')
700 _("%s:%d: %s: illegal regexp character syntax: %c\n"),
701 filename, linenum, prim_name, syntax);
705 re_set_character_syntax ((unsigned char) ch->u.integer, syntax);
711 DEFUN (prim_regmatch)
713 ListItem *arg = args->head;
715 static struct re_registers matches = {0, NULL, NULL};
716 static Node *current_match_node = NULL;
719 str = MATCH_ARG (nSTRING);
720 re = MATCH_ARG (nREGEXP);
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);
729 current_match = NULL;
734 node_free (current_match_node);
735 current_match_node = str;
737 current_match = &matches;
738 current_match_buf = str->u.str.data;
742 n = node_alloc (nINTEGER);
743 n->u.integer = (i >= 0);
750 * Common regular expression substituter for regsub and regsuball.
754 do_regsubsts (str, re, subst, allp)
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;
766 int do_expansions_in_substs = 0;
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] == '$')
772 do_expansions_in_substs = 1;
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);
787 /* Everything before match. */
788 APPEND (str->u.str.data + pos, matches.start[0] - pos);
791 if (!do_expansions_in_substs)
792 APPEND (subst->u.str.data, subst->u.str.len);
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)
800 switch (subst->u.str.data[i])
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]);
823 /* Illegal substitution, just pass it through. */
825 APPEND (subst->u.str.data + i, 1);
830 APPEND (subst->u.str.data + i, 1);
834 pos = matches.end[0];
841 if (num_matches == 0)
843 /* No matches, just return our original string. */
849 APPEND (str->u.str.data + pos, str->u.str.len - pos);
851 /* Create result node. */
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);
867 ListItem *arg = args->head;
868 Node *str, *re, *subst;
870 str = MATCH_ARG (nSTRING);
871 re = MATCH_ARG (nREGEXP);
872 subst = MATCH_ARG (nSTRING);
875 return do_regsubsts (str, re, subst, 0);
879 DEFUN (prim_regsuball)
881 ListItem *arg = args->head;
882 Node *str, *re, *subst;
884 str = MATCH_ARG (nSTRING);
885 re = MATCH_ARG (nREGEXP);
886 subst = MATCH_ARG (nSTRING);
889 return do_regsubsts (str, re, subst, 1);
893 DEFUN (prim_require_state)
895 ListItem *arg = args->head;
900 e = (Expr *) arg->data;
901 if (e->type != eSYMBOL)
903 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
904 filename, linenum, prim_name);
907 cp = e->u.node->u.sym;
912 state = lookup_state (cp);
915 fprintf (stderr, _("%s:%d: %s: couldn't define state `%s'\n"),
916 filename, linenum, prim_name, cp);
926 ListItem *arg = args->head;
927 Node *re, *str, *n, *n2;
930 re = MATCH_ARG (nREGEXP);
931 str = MATCH_ARG (nSTRING);
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 *));
939 for (pos = 0; pos < str->u.str.len;)
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);
944 /* No more matches. */
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];
955 * Check that at least one item fits after us (no need to check
956 * when appending the last item).
958 if (n->u.array.len + 1 >= n->u.array.allocated)
960 n->u.array.allocated += 100;
961 n->u.array.array = (Node **) xrealloc (n->u.array.array,
965 n->u.array.array[n->u.array.len++] = n2;
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);
974 n->u.array.array[n->u.array.len++] = n2;
982 ListItem *arg = args->head;
988 unsigned int result_pos = 0;
989 unsigned int result_len = 0;
991 int argument_count = 0;
994 fmt = MATCH_ARG (nSTRING);
995 cp = fmt->u.str.data;
997 /* Process format string and match arguments. */
998 for (i = 0; i < fmt->u.str.len; i++)
1000 if (cp[i] == '%' && (i + 1 >= fmt->u.str.len || cp[i + 1] == '%'))
1005 else if (cp[i] == '%')
1012 _("%s: primitive `%s': too few arguments for format\n"),
1013 program, prim_name);
1016 n = eval_expr ((Expr *) arg->data, env);
1019 for (i++, j = 0; i < fmt->u.str.len && FMTSPECIAL (cp[i]); i++, j++)
1020 ifmtopts[j] = cp[i];
1023 if (i >= fmt->u.str.len)
1026 APPEND (ifmtopts, j);
1036 if (n->type != nINTEGER)
1040 _("%s:%d: %s: argument %d doesn't match format\n"),
1041 filename, linenum, prim_name, argument_count);
1044 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1045 sprintf (buf, ifmt, n->u.integer);
1047 APPEND (buf, strlen (buf));
1051 if (n->type != nINTEGER)
1054 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1055 sprintf (buf, ifmt, n->u.integer);
1057 APPEND (buf, strlen (buf));
1064 if (n->type != nREAL)
1067 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1068 sprintf (buf, ifmt, n->u.real);
1070 APPEND (buf, strlen (buf));
1074 if (n->type != nSTRING)
1077 if (ifmtopts[0] != '\0')
1080 _("%s:%d: %s: no extra options can be specified for %%s\n"),
1081 filename, linenum, prim_name);
1084 APPEND (n->u.str.data, n->u.str.len);
1089 _("%s:%d: %s: illegal type specifier `%c'\n"),
1090 filename, linenum, prim_name, cp[i]);
1101 n = node_alloc (nSTRING);
1102 n->u.str.len = result_pos;
1103 n->u.str.data = result;
1111 ListItem *arg = args->head;
1117 s1 = MATCH_ARG (nSTRING);
1118 s2 = MATCH_ARG (nSTRING);
1121 cp1 = s1->u.str.data;
1122 cp2 = s2->u.str.data;
1124 for (i = 0; i < s1->u.str.len && i < s2->u.str.len; i++)
1126 if (cp1[i] < cp2[i])
1131 if (cp1[i] > cp2[i])
1137 /* Strings are so far equal, check lengths. */
1138 if (s1->u.str.len < s2->u.str.len)
1140 else if (s1->u.str.len > s2->u.str.len)
1148 n = node_alloc (nINTEGER);
1149 n->u.integer = result;
1157 ListItem *arg = args->head;
1161 n = MATCH_ARG (nVOID);
1164 r = node_alloc (nSTRING);
1171 r->u.str.data = (char *) xcalloc (1, 1);
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);
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);
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);
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);
1207 DEFUN (prim_strncmp)
1209 ListItem *arg = args->head;
1210 Node *s1, *s2, *len;
1215 s1 = MATCH_ARG (nSTRING);
1216 s2 = MATCH_ARG (nSTRING);
1217 len = MATCH_ARG (nINTEGER);
1220 cp1 = s1->u.str.data;
1221 cp2 = s2->u.str.data;
1223 for (i = 0; i < s1->u.str.len && i < s2->u.str.len && i < len->u.integer; i++)
1225 if (cp1[i] < cp2[i])
1230 if (cp1[i] > cp2[i])
1237 /* Check the limit length. */
1238 if (i >= len->u.integer)
1244 /* One or both strings were shorter than limit, check lengths. */
1245 if (s1->u.str.len < s2->u.str.len)
1247 else if (s1->u.str.len > s2->u.str.len)
1256 n = node_alloc (nINTEGER);
1257 n->u.integer = result;
1263 DEFUN (prim_substring)
1265 ListItem *arg = args->head;
1266 Node *str, *start, *end, *n;
1268 str = MATCH_ARG (nSTRING);
1269 start = MATCH_ARG (nINTEGER);
1270 end = MATCH_ARG (nINTEGER);
1273 if (start->u.integer > end->u.integer)
1276 _("%s:%d: %s: start offset is bigger than end offset\n"),
1277 filename, linenum, prim_name);
1280 if (end->u.integer > str->u.str.len)
1282 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
1283 filename, linenum, prim_name);
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);
1292 memcpy (n->u.str.data, str->u.str.data + start->u.integer,
1305 ListItem *arg = args->head;
1310 str = MATCH_ARG (nSTRING);
1313 cmd = (char *) xcalloc (1, str->u.str.len + 1);
1314 memcpy (cmd, str->u.str.data, str->u.str.len);
1316 result = system (cmd);
1319 n = node_alloc (nINTEGER);
1320 n->u.integer = result;
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},
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},
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))
1377 fprintf (stderr, _("%s: out of memory\n"), program);