1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright (c) 2011 Gary Mills
  28  */
  29 
  30 /*      Copyright (c) 1988 AT&T     */
  31 /*        All Rights Reserved   */
  32 
  33 #include        <signal.h>
  34 #include        <unistd.h>
  35 #include        <fcntl.h>
  36 #include        "m4.h"
  37 
  38 #if defined(__lint)
  39 extern int yydebug;
  40 #endif
  41 
  42 #define match(c, s)     (c == *s && (!s[1] || inpmatch(s+1)))
  43 
  44 static char     tmp_name[] = "/tmp/m4aXXXXX";
  45 static wchar_t  prev_char;
  46 static int mb_cur_max;
  47 
  48 static void getflags(int *, char ***, int *);
  49 static void initalloc(void);
  50 static void expand(wchar_t **, int);
  51 static void lnsync(FILE *);
  52 static void fpath(FILE *);
  53 static void puttok(wchar_t *);
  54 static void error3(void);
  55 static wchar_t itochr(int);
  56 /*LINTED: E_STATIC_UNUSED*/
  57 static wchar_t *chkbltin(wchar_t *);
  58 static wchar_t *inpmatch(wchar_t *);
  59 static void chkspace(char **, int *, char ***);
  60 static void catchsig(int);
  61 static FILE *m4open(char ***, char *, int *);
  62 static void showwrap(void);
  63 static void sputchr(wchar_t, FILE *);
  64 static void putchr(wchar_t);
  65 static void *xcalloc(size_t, size_t);
  66 static wint_t myfgetwc(FILE *, int);
  67 static wint_t myfputwc(wchar_t, FILE *);
  68 static int myfeof(int);
  69 
  70 int
  71 main(int argc, char **argv)
  72 {
  73         wchar_t t;
  74         int i, opt_end = 0;
  75         int sigs[] = {SIGHUP, SIGINT, SIGPIPE, 0};
  76 
  77 #if defined(__lint)
  78         yydebug = 0;
  79 #endif
  80 
  81         for (i = 0; sigs[i]; ++i) {
  82                 if (signal(sigs[i], SIG_IGN) != SIG_IGN)
  83                         (void) signal(sigs[i], catchsig);
  84         }
  85         tempfile = mktemp(tmp_name);
  86         (void) close(creat(tempfile, 0));
  87 
  88         (void) setlocale(LC_ALL, "");
  89 
  90 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
  91 #define TEXT_DOMAIN "SYS_TEST"
  92 #endif
  93         (void) textdomain(TEXT_DOMAIN);
  94 
  95         if ((mb_cur_max = MB_CUR_MAX) > 1)
  96                 wide = 1;
  97 
  98         procnam = argv[0];
  99         getflags(&argc, &argv, &opt_end);
 100         initalloc();
 101 
 102         setfname("-");
 103         if (argc > 1) {
 104                 --argc;
 105                 ++argv;
 106                 if (strcmp(argv[0], "-")) {
 107                         ifile[ifx] = m4open(&argv, "r", &argc);
 108                         setfname(argv[0]);
 109                 }
 110         }
 111 
 112         for (;;) {
 113                 token[0] = t = getchr();
 114                 token[1] = EOS;
 115 
 116                 if (t == WEOF) {
 117                         if (ifx > 0) {
 118                                 (void) fclose(ifile[ifx]);
 119                                 ipflr = ipstk[--ifx];
 120                                 continue;
 121                         }
 122 
 123                         getflags(&argc, &argv, &opt_end);
 124 
 125                         if (argc <= 1)
 126                                 /*
 127                                  * If dowrap() has been called, the m4wrap
 128                                  * macro has been processed, and a linked
 129                                  * list of m4wrap strings has been created.
 130                                  * The list starts at wrapstart.
 131                                  */
 132                                 if (wrapstart) {
 133                                         /*
 134                                          * Now that EOF has been processed,
 135                                          * display the m4wrap strings.
 136                                          */
 137                                         showwrap();
 138                                         continue;
 139                                 } else
 140                                         break;
 141                         --argc;
 142                         ++argv;
 143 
 144                         if (ifile[ifx] != stdin)
 145                                 (void) fclose(ifile[ifx]);
 146 
 147                         if (strcmp(argv[0], "-"))
 148                                 ifile[ifx] = m4open(&argv, "r", &argc);
 149                         else
 150                                 ifile[ifx] = stdin;
 151 
 152                         setfname(argv[0]);
 153                         continue;
 154                 }
 155 
 156                 if (is_alpha(t) || t == '_') {
 157                         wchar_t *tp = token+1;
 158                         int tlim = toksize;
 159                         struct nlist    *macadd;  /* temp variable */
 160 
 161                         while ((*tp = getchr()) != WEOF &&
 162                             (is_alnum(*tp) || *tp == '_')) {
 163                                 tp++;
 164                                 if (--tlim <= 0)
 165                                         error2(gettext(
 166                                             "more than %d chars in word"),
 167                                             toksize);
 168                         }
 169                         putbak(*tp);
 170                         *tp = EOS;
 171 
 172                         macadd = lookup(token);
 173                         *Ap = (wchar_t *)macadd;
 174                         if (macadd->def) {
 175                                 if ((wchar_t *)(++Ap) >= astklm) {
 176                                         --Ap;
 177                                         error2(gettext(
 178                                             "more than %d items on "
 179                                             "argument stack"),
 180                                             stksize);
 181                                 }
 182 
 183                                 if (Cp++ == NULL)
 184                                         Cp = callst;
 185 
 186                                 Cp->argp = Ap;
 187                                 *Ap++ = op;
 188                                 puttok(token);
 189                                 stkchr(EOS);
 190                                 t = getchr();
 191                                 putbak(t);
 192 
 193                                 if (t != '(')
 194                                         pbstr(L"()");
 195                                 else    /* try to fix arg count */
 196                                         *Ap++ = op;
 197 
 198                                 Cp->plev = 0;
 199                         } else {
 200                                 puttok(token);
 201                         }
 202                 } else if (match(t, lquote)) {
 203                         int     qlev = 1;
 204 
 205                         for (;;) {
 206                                 token[0] = t = getchr();
 207                                 token[1] = EOS;
 208 
 209                                 if (match(t, rquote)) {
 210                                         if (--qlev > 0)
 211                                                 puttok(token);
 212                                         else
 213                                                 break;
 214                                 } else if (match(t, lquote)) {
 215                                         ++qlev;
 216                                         puttok(token);
 217                                 } else {
 218                                         if (t == WEOF)
 219                                                 error(gettext(
 220                                                 "EOF in quote"));
 221                                         putchr(t);
 222                                 }
 223                         }
 224                 } else if (match(t, lcom) &&
 225                     ((lcom[0] != L'#' || lcom[1] != L'\0') ||
 226                     prev_char != '$')) {
 227 
 228                         /*
 229                          * Don't expand commented macro (between lcom and
 230                          * rcom).
 231                          * What we know so far is that we have found the
 232                          * left comment char (lcom).
 233                          * Make sure we haven't found '#' (lcom) immediately
 234                          * preceded by '$' because we want to expand "$#".
 235                          */
 236 
 237                         puttok(token);
 238                         for (;;) {
 239                                 token[0] = t = getchr();
 240                                 token[1] = EOS;
 241                                 if (match(t, rcom)) {
 242                                         puttok(token);
 243                                         break;
 244                                 } else {
 245                                         if (t == WEOF)
 246                                                 error(gettext(
 247                                                 "EOF in comment"));
 248                                         putchr(t);
 249                                 }
 250                         }
 251                 } else if (Cp == NULL) {
 252                         putchr(t);
 253                 } else if (t == '(') {
 254                         if (Cp->plev)
 255                                 stkchr(t);
 256                         else {
 257                                 /* skip white before arg */
 258                                 while ((t = getchr()) != WEOF && is_space(t))
 259                                         ;
 260 
 261                                 putbak(t);
 262                         }
 263 
 264                         ++Cp->plev;
 265                 } else if (t == ')') {
 266                         --Cp->plev;
 267 
 268                         if (Cp->plev == 0) {
 269                                 stkchr(EOS);
 270                                 expand(Cp->argp, Ap-Cp->argp-1);
 271                                 op = *Cp->argp;
 272                                 Ap = Cp->argp-1;
 273 
 274                                 if (--Cp < callst)
 275                                         Cp = NULL;
 276                         } else
 277                                 stkchr(t);
 278                 } else if (t == ',' && Cp->plev <= 1) {
 279                         stkchr(EOS);
 280                         *Ap = op;
 281 
 282                         if ((wchar_t *)(++Ap) >= astklm) {
 283                                 --Ap;
 284                                 error2(gettext(
 285                                     "more than %d items on argument stack"),
 286                                     stksize);
 287                         }
 288 
 289                         while ((t = getchr()) != WEOF && is_space(t))
 290                                 ;
 291 
 292                         putbak(t);
 293                 } else {
 294                         stkchr(t);
 295                 }
 296         }
 297 
 298         if (Cp != NULL)
 299                 error(gettext(
 300                 "EOF in argument list"));
 301 
 302         delexit(exitstat, 1);
 303         return (0);
 304 }
 305 
 306 static wchar_t *
 307 inpmatch(wchar_t *s)
 308 {
 309         wchar_t *tp = token+1;
 310 
 311         while (*s) {
 312                 *tp = getchr();
 313 
 314                 if (*tp++ != *s++) {
 315                         *tp = EOS;
 316                         pbstr(token+1);
 317                         return (0);
 318                 }
 319         }
 320 
 321         *tp = EOS;
 322         return (token);
 323 }
 324 
 325 static void
 326 getflags(int *xargc, char ***xargv, int *option_end)
 327 {
 328         char    *arg;
 329         char *t;
 330         wchar_t *s[3];
 331 
 332         while (*xargc > 1) {
 333                 arg = (*xargv)[1]; /* point arg to current argument */
 334 
 335                 /*
 336                  * This argument is not an option if it equals "-" or if
 337                  * "--" has already been parsed.
 338                  */
 339                 if (arg[0] != '-' || arg[1] == EOS || *option_end)
 340                         break;
 341                 if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
 342                         *option_end = 1;
 343                 } else {
 344                         switch (arg[1]) {
 345                         case 'B':
 346                                 chkspace(&arg, xargc, xargv);
 347                                 bufsize = atoi(&arg[2]);
 348                                 if (bufsize <= 0) {
 349                                         bufsize = DEF_BUFSIZE;
 350                                 }
 351                                 break;
 352                         case 'D':
 353                                 initalloc();
 354                                 chkspace(&arg, xargc, xargv);
 355                                 for (t = &arg[2]; *t; t++) {
 356                                         if (*t == '=') {
 357                                                 *t++ = EOS;
 358                                                 break;
 359                                         }
 360                                 }
 361                                 s[1] = str2wstr(&arg[2], 1);
 362                                 s[2] = str2wstr(t, 1);
 363                                 dodef(&s[0], 2);
 364                                 free(s[1]);
 365                                 free(s[2]);
 366                                 break;
 367                         case 'H':
 368                                 chkspace(&arg, xargc, xargv);
 369                                 hshsize = atoi(&arg[2]);
 370                                 if (hshsize <= 0) {
 371                                         hshsize = DEF_HSHSIZE;
 372                                 }
 373                                 break;
 374                         case 'S':
 375                                 chkspace(&arg, xargc, xargv);
 376                                 stksize = atoi(&arg[2]);
 377                                 if (stksize <= 0) {
 378                                         stksize = DEF_STKSIZE;
 379                                 }
 380                                 break;
 381                         case 'T':
 382                                 chkspace(&arg, xargc, xargv);
 383                                 toksize = atoi(&arg[2]);
 384                                 if (toksize <= 0) {
 385                                         toksize = DEF_TOKSIZE;
 386                                 }
 387                                 break;
 388                         case 'U':
 389                                 initalloc();
 390                                 chkspace(&arg, xargc, xargv);
 391                                 s[1] = str2wstr(&arg[2], 1);
 392                                 doundef(&s[0], 1);
 393                                 free(s[1]);
 394                                 break;
 395                         case 'e':
 396                                 setbuf(stdout, NULL);
 397                                 (void) signal(SIGINT, SIG_IGN);
 398                                 break;
 399                         case 's':
 400                                 /* turn on line sync */
 401                                 sflag = 1;
 402                                 break;
 403                         default:
 404                                 (void) fprintf(stderr,
 405                                     gettext("%s: bad option: %s\n"),
 406                                     procnam, arg);
 407                                 delexit(NOT_OK, 0);
 408                         }
 409                 } /* end else not "--" */
 410 
 411                 (*xargv)++;
 412                 --(*xargc);
 413         } /* end while options to process */
 414 }
 415 
 416 /*
 417  * Function: chkspace
 418  *
 419  * If there is a space between the option and its argument,
 420  * adjust argptr so that &arg[2] will point to beginning of the option argument.
 421  * This will ensure that processing in getflags() will work, because &arg[2]
 422  * will point to the beginning of the option argument whether or not we have
 423  * a space between the option and its argument.  If there is a space between
 424  * the option and its argument, also adjust xargv and xargc because we are
 425  * processing the next argument.
 426  */
 427 static void
 428 chkspace(char **argptr, int *xargc, char ***xargv)
 429 {
 430         if ((*argptr)[2] == EOS) {
 431                 /* there is a space between the option and its argument */
 432                 (*xargv)++; /* look at the next argument */
 433                 --(*xargc);
 434                 /*
 435                  * Adjust argptr if the option is followed by an
 436                  * option argument.
 437                  */
 438                 if (*xargc > 1) {
 439                         *argptr = (*xargv)[1];
 440                         /* point &arg[2] to beginning of option argument */
 441                         *argptr -= 2;
 442                 }
 443         }
 444 }
 445 
 446 static void
 447 initalloc(void)
 448 {
 449         static int done = 0;
 450         int     t;
 451 
 452         if (done++)
 453                 return;
 454 
 455         hshtab = xcalloc(hshsize, sizeof (struct nlist *));
 456         callst = xcalloc(stksize/3+1, sizeof (struct call));
 457         Ap = argstk = xcalloc(stksize+3, sizeof (wchar_t *));
 458         ipstk[0] = ipflr = ip = ibuf = xcalloc(bufsize+1, sizeof (wchar_t));
 459         op = obuf = xcalloc(bufsize+1, sizeof (wchar_t));
 460         token = xcalloc(toksize+1, sizeof (wchar_t));
 461 
 462         astklm = (wchar_t *)(&argstk[stksize]);
 463         ibuflm = &ibuf[bufsize];
 464         obuflm = &obuf[bufsize];
 465         toklm = &token[toksize];
 466 
 467         for (t = 0; barray[t].bname; ++t) {
 468                 wchar_t p[2] = {0, EOS};
 469 
 470                 p[0] = builtin(t);
 471                 install(barray[t].bname, p, NOPUSH);
 472         }
 473         install(L"unix", nullstr, NOPUSH);
 474 }
 475 
 476 void
 477 install(wchar_t *nam, wchar_t *val, int mode)
 478 {
 479         struct nlist *np;
 480         wchar_t *cp;
 481         int             l;
 482 
 483         if (mode == PUSH)
 484                 (void) lookup(nam);     /* lookup sets hshval */
 485         else
 486                 while (undef(nam))      /* undef calls lookup */
 487                         ;
 488 
 489         np = xcalloc(1, sizeof (*np));
 490         np->name = wstrdup(nam);
 491         np->next = hshtab[hshval];
 492         hshtab[hshval] = np;
 493 
 494         cp = xcalloc((l = wcslen(val))+1, sizeof (*val));
 495         np->def = cp;
 496         cp = &cp[l];
 497 
 498         while (*val)
 499                 *--cp = *val++;
 500 }
 501 
 502 struct nlist *
 503 lookup(wchar_t *str)
 504 {
 505         wchar_t *s1;
 506         struct nlist    *np;
 507         static struct nlist     nodef;
 508 
 509         s1 = str;
 510 
 511         for (hshval = 0; *s1; )
 512                 hshval += *s1++;
 513 
 514         hshval %= hshsize;
 515 
 516         for (np = hshtab[hshval]; np != NULL; np = np->next) {
 517                 if (*str == *np->name && wcscmp(str, np->name) == 0)
 518                         return (np);
 519         }
 520         return (&nodef);
 521 }
 522 
 523 static void
 524 expand(wchar_t **a1, int c)
 525 {
 526         wchar_t *dp;
 527         struct nlist    *sp;
 528 
 529         sp = (struct nlist *)a1[-1];
 530 
 531         if (sp->tflag || trace) {
 532 #if !defined(__lint)    /* lint doesn't grok "%ws" */
 533                 int     i;
 534 
 535                 (void) fprintf(stderr,
 536                     "Trace(%d): %ws", Cp-callst, a1[0]);
 537 #endif
 538 
 539                 if (c > 0) {
 540 #if !defined(__lint)    /* lint doesn't grok "%ws" */
 541                         (void) fprintf(stderr, "(%ws", chkbltin(a1[1]));
 542                         for (i = 2; i <= c; ++i)
 543                                 (void) fprintf(stderr, ",%ws", chkbltin(a1[i]));
 544 #endif
 545                         (void) fprintf(stderr, ")");
 546                 }
 547                 (void) fprintf(stderr, "\n");
 548         }
 549 
 550         dp = sp->def;
 551 
 552         for (; *dp; ++dp) {
 553                 if (is_builtin(*dp)) {
 554                         (*barray[builtin_idx(*dp)].bfunc)(a1, c);
 555                 } else if (dp[1] == '$') {
 556                         if (is_digit(*dp)) {
 557                                 int     n;
 558                                 if ((n = *dp-'0') <= c)
 559                                         pbstr(a1[n]);
 560                                 ++dp;
 561                         } else if (*dp == '#') {
 562                                 pbnum((long)c);
 563                                 ++dp;
 564                         } else if (*dp == '*' || *dp == '@') {
 565                                 int i = c;
 566                                 wchar_t **a = a1;
 567 
 568                                 if (i > 0)
 569                                         for (;;) {
 570                                                 if (*dp == '@')
 571                                                         pbstr(rquote);
 572 
 573                                                 pbstr(a[i--]);
 574 
 575                                                 if (*dp == '@')
 576                                                         pbstr(lquote);
 577 
 578                                                 if (i <= 0)
 579                                                 break;
 580 
 581                                                 pbstr(L",");
 582                                         }
 583                                 ++dp;
 584                         } else
 585                                 putbak(*dp);
 586                 } else
 587                         putbak(*dp);
 588         }
 589 }
 590 
 591 void
 592 setfname(char *s)
 593 {
 594         if (fname[ifx])
 595                 free(fname[ifx]);
 596         if ((fname[ifx] = strdup(s)) == NULL)
 597                 error(gettext("out of storage"));
 598         fline[ifx] = 1;
 599         nflag = 1;
 600         lnsync(stdout);
 601 }
 602 
 603 static void
 604 lnsync(FILE *iop)
 605 {
 606         static int cline = 0;
 607         static int cfile = 0;
 608 
 609         if (!sflag || iop != stdout)
 610                 return;
 611 
 612         if (nflag || ifx != cfile) {
 613                 nflag = 0;
 614                 cfile = ifx;
 615                 (void) fprintf(iop, "#line %d \"", cline = fline[ifx]);
 616                 fpath(iop);
 617                 (void) fprintf(iop, "\"\n");
 618         } else if (++cline != fline[ifx])
 619                 (void) fprintf(iop, "#line %d\n", cline = fline[ifx]);
 620 }
 621 
 622 static void
 623 fpath(FILE *iop)
 624 {
 625         int     i;
 626 
 627         if (fname[0] == NULL)
 628                 return;
 629 
 630         (void) fprintf(iop, "%s", fname[0]);
 631 
 632         for (i = 1; i <= ifx; ++i)
 633                 (void) fprintf(iop, ":%s", fname[i]);
 634 }
 635 
 636 /* ARGSUSED */
 637 static void
 638 catchsig(int i)
 639 {
 640         (void) signal(SIGHUP, SIG_IGN);
 641         (void) signal(SIGINT, SIG_IGN);
 642         delexit(NOT_OK, 0);
 643 }
 644 
 645 void
 646 delexit(int code, int flushio)
 647 {
 648         int i;
 649 
 650         cf = stdout;
 651 
 652 /*
 653  *      if (ofx != 0) {
 654  *              ofx = 0;
 655  *              code = NOT_OK;
 656  *      }
 657  */
 658         ofx = 0;        /* ensure that everything comes out */
 659         for (i = 1; i < 10; i++)
 660                 undiv(i, code);
 661 
 662         tempfile[7] = 'a';
 663         (void) unlink(tempfile);
 664 
 665         /* flush standard I/O buffers, ie: call exit() not _exit() */
 666         if (flushio)
 667                 exit(code);
 668 
 669         _exit(code);
 670 }
 671 
 672 static void
 673 puttok(wchar_t *tp)
 674 {
 675         if (Cp) {
 676                 while (*tp)
 677                         stkchr(*tp++);
 678         } else if (cf) {
 679                 while (*tp) {
 680                         sputchr(*tp++, cf);
 681                 }
 682         }
 683 }
 684 
 685 void
 686 pbstr(wchar_t *str)
 687 {
 688         wchar_t *p;
 689 
 690         for (p = str + wcslen(str); --p >= str; )
 691                 putbak(*p);
 692 }
 693 
 694 void
 695 undiv(int i, int code)
 696 {
 697         FILE *fp;
 698         wint_t c;
 699 
 700         if (i < 1 || i > 9 || i == ofx || !ofile[i])
 701                 return;
 702 
 703         (void) fclose(ofile[i]);
 704         tempfile[7] = 'a'+i;
 705 
 706         if (code == OK && cf) {
 707                 fp = xfopen(tempfile, "r");
 708 
 709                 if (wide) {
 710                         while ((c = myfgetwc(fp, -1)) != WEOF)
 711                                 sputchr((wchar_t)c, cf);
 712                 } else {
 713                         while ((c = (wint_t)getc(fp)) != WEOF)
 714                                 sputchr((wchar_t)c, cf);
 715                 }
 716 
 717                 (void) fclose(fp);
 718         }
 719 
 720         (void) unlink(tempfile);
 721         ofile[i] = NULL;
 722 }
 723 
 724 void
 725 pbnum(long num)
 726 {
 727         pbnbr(num, 10, 1);
 728 }
 729 
 730 void
 731 pbnbr(long nbr, int base, int len)
 732 {
 733         int     neg = 0;
 734 
 735         if (base <= 0)
 736                 return;
 737 
 738         if (nbr < 0)
 739                 neg = 1;
 740         else
 741                 nbr = -nbr;
 742 
 743         while (nbr < 0) {
 744                 int     i;
 745                 if (base > 1) {
 746                         i = nbr%base;
 747                         nbr /= base;
 748 #if (-3 % 2) != -1
 749                         while (i > 0) {
 750                                 i -= base;
 751                                 ++nbr;
 752                         }
 753 #endif
 754                         i = -i;
 755                 } else {
 756                         i = 1;
 757                         ++nbr;
 758                 }
 759                 putbak(itochr(i));
 760                 --len;
 761         }
 762 
 763         while (--len >= 0)
 764                 putbak('0');
 765 
 766         if (neg)
 767                 putbak('-');
 768 }
 769 
 770 static wchar_t
 771 itochr(int i)
 772 {
 773         if (i > 9)
 774                 return ((wchar_t)(i-10+'A'));
 775         else
 776                 return ((wchar_t)(i+'0'));
 777 }
 778 
 779 long
 780 ctol(wchar_t *str)
 781 {
 782         int sign;
 783         long num;
 784 
 785         while (is_space(*str))
 786                 ++str;
 787         num = 0;
 788         if (*str == '-') {
 789                 sign = -1;
 790                 ++str;
 791         } else
 792                 sign = 1;
 793         while (is_digit(*str))
 794                 num = num*10 + *str++ - '0';
 795         return (sign * num);
 796 }
 797 
 798 int
 799 min(int a, int b)
 800 {
 801         if (a > b)
 802                 return (b);
 803         return (a);
 804 }
 805 
 806 FILE *
 807 xfopen(char *name, char *mode)
 808 {
 809         FILE    *fp;
 810 
 811         if ((fp = fopen(name, mode)) == NULL)
 812                 errorf(gettext("cannot open file: %s"),
 813                     strerror(errno));
 814 
 815         return (fp);
 816 }
 817 
 818 /*
 819  * m4open
 820  *
 821  * Continue processing files when unable to open the given file argument.
 822  */
 823 FILE *
 824 m4open(char ***argvec, char *mode, int *argcnt)
 825 {
 826         FILE    *fp;
 827         char *arg;
 828 
 829         while (*argcnt > 0) {
 830                 arg = (*argvec)[0]; /* point arg to current file name */
 831                 if (arg[0] == '-' && arg[1] == EOS)
 832                         return (stdin);
 833                 else {
 834                         if ((fp = fopen(arg, mode)) == NULL) {
 835                                 (void) fprintf(stderr, gettext(
 836                                 "m4: cannot open %s: "), arg);
 837                                 perror("");
 838                                 if (*argcnt == 1) {
 839                                         /* last arg therefore exit */
 840                                         error3();
 841                                 } else {
 842                                         exitstat = 1;
 843                                         (*argvec)++; /* try next arg */
 844                                         (*argcnt)--;
 845                                 }
 846                         } else
 847                                 break;
 848                 }
 849         }
 850         return (fp);
 851 }
 852 
 853 void *
 854 xmalloc(size_t size)
 855 {
 856         void *ptr;
 857 
 858         if ((ptr = malloc(size)) == NULL)
 859                 error(gettext("out of storage"));
 860         return (ptr);
 861 }
 862 
 863 static void *
 864 xcalloc(size_t nbr, size_t size)
 865 {
 866         void    *ptr;
 867 
 868         ptr = xmalloc(nbr * size);
 869         (void) memset(ptr, '\0', nbr * size);
 870         return (ptr);
 871 }
 872 
 873 /* Typical format: "cannot open file: %s" */
 874 /* PRINTFLIKE1 */
 875 void
 876 errorf(char *str, char *serr)
 877 {
 878         char buf[500];
 879 
 880         (void) snprintf(buf, sizeof (buf), str, serr);
 881         error(buf);
 882 }
 883 
 884 /* PRINTFLIKE1 */
 885 void
 886 error2(char *str, int num)
 887 {
 888         char buf[500];
 889 
 890         (void) snprintf(buf, sizeof (buf), str, num);
 891         error(buf);
 892 }
 893 
 894 void
 895 error(char *str)
 896 {
 897         (void) fprintf(stderr, "\n%s:", procnam);
 898         fpath(stderr);
 899         (void) fprintf(stderr, ":%d %s\n", fline[ifx], str);
 900         error3();
 901 }
 902 
 903 static void
 904 error3()
 905 {
 906         if (Cp) {
 907                 struct call     *mptr;
 908 
 909                 /* fix limit */
 910                 *op = EOS;
 911                 (Cp+1)->argp = Ap+1;
 912 
 913                 for (mptr = callst; mptr <= Cp; ++mptr) {
 914                         wchar_t **aptr, **lim;
 915 
 916                         aptr = mptr->argp;
 917                         lim = (mptr+1)->argp-1;
 918                         if (mptr == callst)
 919                                 (void) fputws(*aptr, stderr);
 920                         ++aptr;
 921                         (void) fputs("(", stderr);
 922                         if (aptr < lim)
 923                                 for (;;) {
 924                                         (void) fputws(*aptr++, stderr);
 925                                         if (aptr >= lim)
 926                                                 break;
 927                                         (void) fputs(",", stderr);
 928                                 }
 929                 }
 930                 while (--mptr >= callst)
 931                         (void) fputs(")", stderr);
 932 
 933                 (void) fputs("\n", stderr);
 934         }
 935         delexit(NOT_OK, 1);
 936 }
 937 
 938 static wchar_t *
 939 chkbltin(wchar_t *s)
 940 {
 941         static wchar_t buf[24];
 942 
 943         if (is_builtin(*s)) {
 944                 (void) swprintf(buf, sizeof (buf)/sizeof (wchar_t), L"<%ls>",
 945                     barray[builtin_idx(*s)].bname);
 946                 return (buf);
 947         }
 948         return (s);
 949 }
 950 
 951 wchar_t
 952 getchr()
 953 {
 954         static wchar_t C;
 955 
 956         prev_char = C;
 957         if (ip > ipflr)
 958                 return (*--ip);
 959         if (wide) {
 960                 C = (wchar_t)(myfeof(ifx) ? WEOF : myfgetwc(NULL, ifx));
 961         } else {
 962                 C = (wchar_t)(feof(ifile[ifx]) ?
 963                     WEOF : (wint_t)getc(ifile[ifx]));
 964         }
 965         if (C == '\n')
 966                 fline[ifx]++;
 967         return (C);
 968 }
 969 
 970 /*
 971  * showwrap
 972  *
 973  * Loop through the list of m4wrap strings.  Call pbstr() so that the
 974  * string will be displayed, then delete the list entry and free the memory
 975  * allocated for it.
 976  */
 977 static void
 978 showwrap()
 979 {
 980         struct Wrap *prev;
 981 
 982         while (wrapstart) {
 983                 pbstr(wrapstart->wrapstr);
 984                 free(wrapstart->wrapstr);
 985                 prev = wrapstart;
 986                 wrapstart = wrapstart->nxt;
 987                 free(prev);
 988         }
 989 }
 990 
 991 static void
 992 sputchr(wchar_t c, FILE *f)
 993 {
 994         wint_t ret;
 995 
 996         if (is_builtin(c))
 997                 return;
 998         if (wide)
 999                 ret = myfputwc(c, f);
1000         else
1001                 ret = (wint_t)putc((int)c, f);
1002         if (ret == WEOF)
1003                 error(gettext("output error"));
1004         if (ret == '\n')
1005                 lnsync(f);
1006 }
1007 
1008 static void
1009 putchr(wchar_t c)
1010 {
1011         wint_t ret;
1012 
1013         if (Cp)
1014                 stkchr(c);
1015         else if (cf) {
1016                 if (sflag)
1017                         sputchr(c, cf);
1018                 else {
1019                         if (is_builtin(c))
1020                                 return;
1021                         if (wide)
1022                                 ret = myfputwc(c, cf);
1023                         else
1024                                 ret = (wint_t)putc((int)c, cf);
1025                         if (ret == WEOF) {
1026                                 error(gettext("output error"));
1027                         }
1028                 }
1029         }
1030 }
1031 
1032 wchar_t *
1033 wstrdup(wchar_t *p)
1034 {
1035         size_t len = wcslen(p);
1036         wchar_t *ret;
1037 
1038         ret = xmalloc((len + 1) * sizeof (wchar_t));
1039         (void) wcscpy(ret, p);
1040         return (ret);
1041 }
1042 
1043 int
1044 wstoi(wchar_t *p)
1045 {
1046         return ((int)wcstol(p, NULL, 10));
1047 }
1048 
1049 char *
1050 wstr2str(wchar_t *from, int alloc)
1051 {
1052         static char *retbuf;
1053         static size_t bsiz;
1054         char *p, *ret;
1055 
1056         if (alloc) {
1057                 ret = p = xmalloc(wcslen(from) * mb_cur_max + 1);
1058         } else {
1059                 while (bsiz < (wcslen(from) * mb_cur_max + 1)) {
1060                         if ((p = realloc(retbuf, bsiz + 256)) == NULL)
1061                                 error(gettext("out of storage"));
1062                         bsiz += 256;
1063                         retbuf = p;
1064                 }
1065                 ret = p = retbuf;
1066         }
1067 
1068         if (wide) {
1069                 while (*from) {
1070                         int len;
1071 
1072                         if (*from & INVALID_CHAR) {
1073                                 *p = (char)(*from & ~INVALID_CHAR);
1074                                 len = 1;
1075                         } else {
1076                                 if ((len = wctomb(p, *from)) == -1) {
1077                                         *p = (char)*from;
1078                                         len = 1;
1079                                 }
1080                         }
1081                         p += len;
1082                         from++;
1083                 }
1084         } else {
1085                 while (*from)
1086                         *p++ = (char)*from++;
1087         }
1088         *p = '\0';
1089 
1090         return (ret);
1091 }
1092 
1093 wchar_t *
1094 str2wstr(char *from, int alloc)
1095 {
1096         static wchar_t *retbuf;
1097         static size_t bsiz;
1098         wchar_t *p, *ret;
1099 
1100         if (alloc) {
1101                 ret = p = xmalloc((strlen(from) + 1) * sizeof (wchar_t));
1102         } else {
1103                 while (bsiz < (strlen(from) + 1)) {
1104                         if ((p = realloc(retbuf,
1105                             (bsiz + 256) * sizeof (wchar_t))) == NULL) {
1106                                 error(gettext("out of storage"));
1107                         }
1108                         bsiz += 256;
1109                         retbuf = p;
1110                 }
1111                 ret = p = retbuf;
1112         }
1113 
1114         if (wide) {
1115                 while (*from) {
1116                         int len;
1117                         wchar_t wc;
1118 
1119                         if ((len = mbtowc(&wc, from, mb_cur_max)) <= 0) {
1120                                 wc = *from | INVALID_CHAR;
1121                                 len = 1;
1122                         }
1123                         *p++ = wc;
1124                         from += len;
1125                 }
1126         } else {
1127                 while (*from)
1128                         *p++ = (unsigned char) *from++;
1129         }
1130         *p = 0;
1131 
1132         return (ret);
1133 }
1134 
1135 static wint_t
1136 myfgetwc(FILE *fp, int idx)
1137 {
1138         int i, c, len, nb;
1139         wchar_t wc;
1140         unsigned char *buf;
1141 
1142         if (fp == NULL)
1143                 fp = ifile[idx];
1144         else
1145                 idx = 10; /* extra slot */
1146         buf = ibuffer[idx].buffer;
1147         nb = ibuffer[idx].nbytes;
1148         len = 0;
1149         for (i = 1; i <= mb_cur_max; i++) {
1150                 if (nb < i) {
1151                         c = getc(fp);
1152                         if (c == EOF) {
1153                                 if (nb == 0)
1154                                         return (WEOF);
1155                                 else
1156                                         break;
1157                         }
1158                         buf[nb++] = (unsigned char)c;
1159                 }
1160                 if ((len = mbtowc(&wc, (char *)buf, i)) >= 0)
1161                         break;
1162         }
1163         if (len <= 0) {
1164                 wc = buf[0] | INVALID_CHAR;
1165                 len = 1;
1166         }
1167         nb -= len;
1168         if (nb > 0) {
1169                 for (i = 0; i < nb; i++)
1170                         buf[i] = buf[i + len];
1171         }
1172         ibuffer[idx].nbytes = nb;
1173         return (wc);
1174 }
1175 
1176 static wint_t
1177 myfputwc(wchar_t wc, FILE *fp)
1178 {
1179         if (wc & INVALID_CHAR) {
1180                 wc &= ~INVALID_CHAR;
1181                 return (fputc((int)wc, fp));
1182         }
1183         return (fputwc(wc, fp));
1184 }
1185 
1186 static int
1187 myfeof(int idx)
1188 {
1189         return (ibuffer[idx].nbytes == 0 && feof(ifile[idx]));
1190 }