home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / TOP / USR / SRC / cpp.decus.t.Z / cpp.decus.t / cpp2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-03-06  |  15.3 KB  |  584 lines

  1. /*
  2.  *                C P P 2 . C
  3.  *
  4.  *               Process #control lines
  5.  *
  6.  * Edit history
  7.  * 13-Nov-84    MM    Split from cpp1.c
  8.  */
  9.  
  10. #include    <stdio.h>
  11. #include    <ctype.h>
  12. #include    "cppdef.h"
  13. #include    "cpp.h"
  14. #if HOST == SYS_VMS
  15. /*
  16.  * Include the rms stuff.  (We can't just include rms.h as it uses the
  17.  * VaxC-specific library include syntax that Decus CPP doesn't support.
  18.  * By including things by hand, we can CPP ourself.)
  19.  */
  20. #include    <nam.h>
  21. #include    <fab.h>
  22. #include    <rab.h>
  23. #include    <rmsdef.h>
  24. #endif
  25.  
  26. /*
  27.  * Generate (by hand-inspection) a set of unique values for each control
  28.  * operator.  Note that this is not guaranteed to work for non-Ascii
  29.  * machines.  CPP won't compile if there are hash conflicts.
  30.  */
  31.  
  32. #define    L_assert    ('a' + ('s' << 1))
  33. #define    L_define    ('d' + ('f' << 1))
  34. #define    L_elif        ('e' + ('i' << 1))
  35. #define    L_else        ('e' + ('s' << 1))
  36. #define    L_endif        ('e' + ('d' << 1))
  37. #define    L_if        ('i' + (EOS << 1))
  38. #define    L_ifdef        ('i' + ('d' << 1))
  39. #define    L_ifndef    ('i' + ('n' << 1))
  40. #define    L_include    ('i' + ('c' << 1))
  41. #define    L_line        ('l' + ('n' << 1))
  42. #define    L_nogood    (EOS + (EOS << 1))    /* To catch #i        */
  43. #define    L_pragma    ('p' + ('a' << 1))
  44. #define L_undef        ('u' + ('d' << 1))
  45. #if DEBUG
  46. #define    L_debug        ('d' + ('b' << 1))    /* #debug        */
  47. #define    L_nodebug    ('n' + ('d' << 1))    /* #nodebug        */
  48. #endif
  49.  
  50. int
  51. control(counter)
  52. int        counter;    /* Pending newline counter        */
  53. /*
  54.  * Process #control lines.  Simple commands are processed inline,
  55.  * while complex commands have their own subroutines.
  56.  *
  57.  * The counter is used to force out a newline before #line, and
  58.  * #pragma commands.  This prevents these commands from ending up at
  59.  * the end of the previous line if cpp is invoked with the -C option.
  60.  */
  61. {
  62.     register int        c;
  63.     register char        *tp;
  64.     register int        hash;
  65.     char            *ep;
  66.  
  67.     c = skipws();
  68.     if (c == '\n' || c == EOF_CHAR)
  69.         return (counter + 1);
  70.     if (!isdigit(c))
  71.         scanid(c);            /* Get #word to token[]        */
  72.     else {
  73.         unget();            /* Hack -- allow #123 as a    */
  74.         strcpy(token, "line");    /* synonym for #line 123    */
  75.     }
  76.     hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1));
  77.     switch (hash) {
  78.     case L_assert:    tp = "assert";        break;
  79.     case L_define:    tp = "define";        break;
  80.     case L_elif:    tp = "elif";        break;
  81.     case L_else:    tp = "else";        break;
  82.     case L_endif:    tp = "endif";        break;
  83.     case L_if:    tp = "if";        break;
  84.     case L_ifdef:    tp = "ifdef";        break;
  85.     case L_ifndef:    tp = "ifndef";        break;
  86.     case L_include:    tp = "include";        break;
  87.     case L_line:    tp = "line";        break;
  88.     case L_pragma:    tp = "pragma";        break;
  89.     case L_undef:    tp = "undef";        break;
  90. #if DEBUG
  91.     case L_debug:    tp = "debug";        break;
  92.     case L_nodebug:    tp = "nodebug";        break;
  93. #endif
  94.     default:    hash = L_nogood;
  95.     case L_nogood:    tp = "";        break;
  96.     }
  97.     if (!streq(tp, token))
  98.         hash = L_nogood;
  99.     /*
  100.      * hash is set to a unique value corresponding to the
  101.      * control keyword (or L_nogood if we think it's nonsense).
  102.      */
  103.     if (infile->fp == NULL)
  104.         cwarn("Control line \"%s\" within macro expansion", token);
  105.     if (!compiling) {            /* Not compiling now    */
  106.         switch (hash) {
  107.         case L_if:                /* These can't turn    */
  108.         case L_ifdef:            /*  compilation on, but    */
  109.         case L_ifndef:            /*   we must nest #if's    */
  110.         if (++ifptr >= &ifstack[BLK_NEST])
  111.             goto if_nest_err;
  112.         *ifptr = 0;            /* !WAS_COMPILING    */
  113.         case L_line:            /* Many            */
  114.         /*
  115.          * Are pragma's always processed?
  116.          */
  117.         case L_pragma:            /*  options        */
  118.         case L_include:            /*   are uninteresting    */
  119.         case L_define:            /*    if we        */
  120.         case L_undef:            /*     aren't        */
  121.         case L_assert:            /*      compiling.    */
  122. dump_line:    skipnl();            /* Ignore rest of line    */
  123.         return (counter + 1);
  124.         }
  125.     }
  126.     /*
  127.      * Make sure that #line and #pragma are output on a fresh line.
  128.      */
  129.     if (counter > 0 && (hash == L_line || hash == L_pragma)) {
  130.         putchar('\n');
  131.         counter--;
  132.     }
  133.     switch (hash) {
  134.     case L_line:
  135.         /*
  136.          * Parse the line to update the line number and "progname"
  137.          * field and line number for the next input line.
  138.          * Set wrongline to force it out later.
  139.          */
  140.         c = skipws();
  141.         workp = work;            /* Save name in work    */
  142.         while (c != '\n' && c != EOF_CHAR) {
  143.         save(c);
  144.         c = get();
  145.         }
  146.         unget();
  147.         save(EOS);
  148.         /*
  149.          * Split #line argument into <line-number> and <name>
  150.          * We subtract 1 as we want the number of the next line.
  151.          */
  152.         line = atoi(work) - 1;        /* Reset line number    */
  153.         for (tp = work; isdigit(*tp) || type[*tp] == SPA; tp++)
  154.         ;                /* Skip over digits    */
  155.         if (*tp != EOS) {            /* Got a filename, so:    */
  156.         if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) {
  157.             tp++;            /* Skip over left quote    */
  158.             *ep = EOS;            /* And ignore right one    */
  159.         }
  160.         if (infile->progname != NULL)    /* Give up the old name    */
  161.             free(infile->progname);    /* if it's allocated.    */
  162.             infile->progname = savestring(tp);
  163.         }
  164.         wrongline = TRUE;            /* Force output later    */
  165.         break;
  166.  
  167.     case L_include:
  168.         doinclude();
  169.         break;
  170.  
  171.     case L_define:
  172.         dodefine();
  173.         break;
  174.  
  175.     case L_undef:
  176.         doundef();
  177.         break;
  178.  
  179.     case L_else:
  180.         if (ifptr == &ifstack[0])
  181.         goto nest_err;
  182.         else if ((*ifptr & ELSE_SEEN) != 0)
  183.         goto else_seen_err;
  184.         *ifptr |= ELSE_SEEN;
  185.         if ((*ifptr & WAS_COMPILING) != 0) {
  186.         if (compiling || (*ifptr & TRUE_SEEN) != 0)
  187.             compiling = FALSE;
  188.         else {
  189.             compiling = TRUE;
  190.         }
  191.         }
  192.         break;
  193.  
  194.     case L_elif:
  195.         if (ifptr == &ifstack[0])
  196.         goto nest_err;
  197.         else if ((*ifptr & ELSE_SEEN) != 0) {
  198. else_seen_err:    cerror("#%s may not follow #else", token);
  199.         goto dump_line;
  200.         }
  201.         if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) {
  202.         compiling = FALSE;        /* Done compiling stuff    */
  203.         goto dump_line;            /* Skip this clause    */
  204.         }
  205.         doif(L_if);
  206.         break;
  207.  
  208.     case L_if:
  209.     case L_ifdef:
  210.     case L_ifndef:
  211.         if (++ifptr >= &ifstack[BLK_NEST])
  212. if_nest_err:    cfatal("Too many nested #%s statements", token);
  213.         *ifptr = WAS_COMPILING;
  214.         doif(hash);
  215.         break;
  216.  
  217.     case L_endif:
  218.         if (ifptr == &ifstack[0]) {
  219. nest_err:    cerror("#%s must be in an #if", token);
  220.         goto dump_line;
  221.         }
  222.         if (!compiling && (*ifptr & WAS_COMPILING) != 0)
  223.         wrongline = TRUE;
  224.         compiling = ((*ifptr & WAS_COMPILING) != 0);
  225.         --ifptr;
  226.         break;
  227.  
  228.     case L_assert:
  229.         if (eval() == 0)
  230.         cerror("Preprocessor assertion failure", NULLST);
  231.         break;
  232.  
  233.     case L_pragma:
  234.         /*
  235.          * #pragma is provided to pass "options" to later
  236.          * passes of the compiler.  cpp doesn't have any yet.
  237.          */
  238.         printf("#pragma ");
  239.         while ((c = get()) != '\n' && c != EOF_CHAR)
  240.         cput(c);
  241.         unget();
  242.         break;
  243.  
  244. #if DEBUG
  245.     case L_debug:
  246.         if (debug == 0)
  247.         dumpdef("debug set on");
  248.         debug++;
  249.         break;
  250.  
  251.     case L_nodebug:
  252.         debug--;
  253.         break;
  254. #endif
  255.  
  256.     default:
  257.         /*
  258.          * Undefined #control keyword.
  259.          * Note: the correct behavior may be to warn and
  260.          * pass the line to a subsequent compiler pass.
  261.          * This would allow #asm or similar extensions.
  262.          */
  263.         cerror("Illegal # command \"%s\"", token);
  264.         break;
  265.     }
  266.     if (hash != L_include) {
  267. #if OLD_PREPROCESSOR
  268.         /*
  269.          * Ignore the rest of the #control line so you can write
  270.          *        #if    foo
  271.          *        #endif    foo
  272.          */
  273.         goto dump_line;            /* Take common exit    */
  274. #else
  275.         if (skipws() != '\n') {
  276.         cwarn("Unexpected text in #control line ignored", NULLST);
  277.         skipnl();
  278.         }
  279. #endif
  280.     }
  281.     return (counter + 1);
  282. }
  283.  
  284. FILE_LOCAL
  285. doif(hash)
  286. int        hash;
  287. /*
  288.  * Process an #if, #ifdef, or #ifndef.  The latter two are straightforward,
  289.  * while #if needs a subroutine of its own to evaluate the expression.
  290.  *
  291.  * doif() is called only if compiling is TRUE.  If false, compilation
  292.  * is always supressed, so we don't need to evaluate anything.  This
  293.  * supresses unnecessary warnings.
  294.  */
  295. {
  296.     register int        c;
  297.     register int        found;
  298.  
  299.     if ((c = skipws()) == '\n' || c == EOF_CHAR) {
  300.         unget();
  301.         goto badif;
  302.     }
  303.     if (hash == L_if) {
  304.         unget();
  305.         found = (eval() != 0);    /* Evaluate expr, != 0 is  TRUE    */
  306.         hash = L_ifdef;        /* #if is now like #ifdef    */
  307.     }
  308.     else {
  309.         if (type[c] != LET)        /* Next non-blank isn't letter    */
  310.         goto badif;        /* ... is an error        */
  311.         found = (lookid(c) != NULL); /* Look for it in symbol table    */
  312.     }
  313.     if (found == (hash == L_ifdef)) {
  314.         compiling = TRUE;
  315.         *ifptr |= TRUE_SEEN;
  316.     }
  317.     else {
  318.         compiling = FALSE;
  319.     }
  320.     return;
  321.  
  322. badif:    cerror("#if, #ifdef, or #ifndef without an argument", NULLST);
  323. #if !OLD_PREPROCESSOR
  324.     skipnl();                /* Prevent an extra    */
  325.     unget();                /* Error message    */
  326. #endif
  327.     return;
  328. }
  329.  
  330. FILE_LOCAL
  331. doinclude()
  332. /*
  333.  * Process the #include control line.
  334.  * There are three variations:
  335.  *    #include "file"        search somewhere relative to the
  336.  *                current source file, if not found,
  337.  *                treat as #include <file>.
  338.  *    #include <file>        Search in an implementation-dependent
  339.  *                list of places.
  340.  *    #include token        Expand the token, it must be one of
  341.  *                "file" or <file>, process as such.
  342.  *
  343.  * Note: the November 12 draft forbids '>' in the #include <file> format.
  344.  * This restriction is unnecessary and not implemented.
  345.  */
  346. {
  347.     register int        c;
  348.     register int        delim;
  349. #if HOST == SYS_VMS
  350.     char            def_filename[NAM$C_MAXRSS + 1];
  351. #endif
  352.  
  353.     delim = macroid(skipws());
  354.     if (delim != '<' && delim != '"')
  355.         goto incerr;
  356.     if (delim == '<')
  357.         delim = '>';
  358.     workp = work;
  359.     instring = TRUE;        /* Accept all characters    */
  360.     while ((c = get()) != '\n' && c != EOF_CHAR)
  361.         save(c);            /* Put it away.            */
  362.     unget();            /* Force nl after includee    */
  363.     /*
  364.      * The draft is unclear if the following should be done.
  365.      */
  366.     while (--workp >= work && *workp == ' ')
  367.         ;                /* Trim blanks from filename    */
  368.     if (*workp != delim)
  369.         goto incerr;
  370.     *workp = EOS;            /* Terminate filename        */
  371.     instring = FALSE;
  372. #if HOST == SYS_VMS
  373.     /*
  374.      * Assume the default .h filetype.
  375.      */
  376.     if (!vmsparse(work, ".H", def_filename)) {
  377.         perror(work);        /* Oops.            */
  378.         goto incerr;
  379.     }
  380.     else if (openinclude(def_filename, (delim == '"')))
  381.         return;
  382. #else
  383.     if (openinclude(work, (delim == '"')))
  384.         return;
  385. #endif
  386.     /*
  387.      * No sense continuing if #include file isn't there.
  388.      */
  389.     cfatal("Cannot open include file \"%s\"", work);
  390.  
  391. incerr:    cerror("#include syntax error", NULLST);
  392.     return;
  393. }
  394.  
  395. FILE_LOCAL int
  396. openinclude(filename, searchlocal)
  397. char        *filename;        /* Input file name        */
  398. int        searchlocal;        /* TRUE if #include "file"    */
  399. /*
  400.  * Actually open an include file.  This routine is only called from
  401.  * doinclude() above, but was written as a separate subroutine for
  402.  * programmer convenience.  It searches the list of directories
  403.  * and actually opens the file, linking it into the list of
  404.  * active files.  Returns TRUE if the file was opened, FALSE
  405.  * if openinclude() fails.  No error message is printed.
  406.  */
  407. {
  408.     register char        **incptr;
  409. #if HOST == SYS_VMS
  410. #if NWORK < (NAM$C_MAXRSS + 1)
  411.     << error, NWORK isn't greater than NAM$C_MAXRSS >>
  412. #endif
  413. #endif
  414.     char            tmpname[NWORK];    /* Filename work area    */
  415.  
  416.     if (searchlocal) {
  417.         /*
  418.          * Look in local directory first
  419.          */
  420. #if (HOST == SYS_UNIX) || (HOST == SYS_OSK)
  421.         /*
  422.          * Try to open filename relative to the directory of the current
  423.          * source file (as opposed to the current directory). (ARF, SCK).
  424.          */
  425.         if (filename[0] != '/'
  426.          && hasdirectory(infile->filename, tmpname))
  427.         strcat(tmpname, filename);
  428.         else {
  429.         strcpy(tmpname, filename);
  430.         }
  431. #else
  432.         if (!hasdirectory(filename, tmpname)
  433.          && hasdirectory(infile->filename, tmpname))
  434.         strcat(tmpname, filename);
  435.         else {
  436.         strcpy(tmpname, filename);
  437.         }
  438. #endif
  439.         if (openfile(tmpname))
  440.         return (TRUE);
  441.     }
  442.     /*
  443.      * Look in any directories specified by -I command line
  444.      * arguments, then in the builtin search list.
  445.      */
  446.     for (incptr = incdir; incptr < incend; incptr++) {
  447.         if (strlen(*incptr) + strlen(filename) >= (NWORK - 1))
  448.         cfatal("Filename work buffer overflow", NULLST);
  449.         else {
  450. #if (HOST == SYS_UNIX) || (HOST == SYS_OSK)
  451.         if (filename[0] == '/')
  452.             strcpy(tmpname, filename);
  453.         else {
  454.             sprintf(tmpname, "%s/%s", *incptr, filename);
  455.         }
  456. #else
  457.         if (!hasdirectory(filename, tmpname))
  458.             sprintf(tmpname, "%s%s", *incptr, filename);
  459. #endif
  460.         if (openfile(tmpname))
  461.             return (TRUE);
  462.         }
  463.     }
  464.     return (FALSE);
  465. }
  466.  
  467. FILE_LOCAL int
  468. hasdirectory(source, result)
  469. char        *source;    /* Directory to examine            */
  470. char        *result;    /* Put directory stuff here        */
  471. /*
  472.  * If a device or directory is found in the source filename string, the
  473.  * node/device/directory part of the string is copied to result and
  474.  * hasdirectory returns TRUE.  Else, nothing is copied and it returns FALSE.
  475.  */
  476. {
  477. #if (HOST == SYS_UNIX) || (HOST == SYS_OSK)
  478.     register char        *tp;
  479.  
  480.     if ((tp = strrchr(source, '/')) == NULL)
  481.         return (FALSE);
  482.     else {
  483.         strncpy(result, source, tp - source + 1);
  484.         result[tp - source + 1] = EOS;
  485.         return (TRUE);
  486.     }
  487. #else
  488. #if HOST == SYS_VMS
  489.     if (vmsparse(source, NULLST, result)
  490.      && result[0] != EOS)
  491.         return (TRUE);
  492.     else {
  493.         return (FALSE);
  494.     }
  495. #else
  496.     /*
  497.      * Random DEC operating system (RSX, RT11, RSTS/E)
  498.      */
  499.     register char        *tp;
  500.  
  501.     if ((tp = strrchr(source, ']')) == NULL
  502.      && (tp = strrchr(source, ':')) == NULL)
  503.         return (FALSE);
  504.     else {
  505.         strncpy(result, source, tp - source + 1);
  506.         result[tp - source + 1] = EOS;
  507.         return (TRUE);
  508.     }
  509. #endif
  510. #endif
  511. }
  512.  
  513. #if HOST == SYS_VMS
  514.  
  515. /*
  516.  * EXP_DEV is set if a device was specified, EXP_DIR if a directory
  517.  * is specified.  (Both set indicate a file-logical, but EXP_DEV
  518.  * would be set by itself if you are reading, say, SYS$INPUT:)
  519.  */
  520. #define DEVDIR (NAM$M_EXP_DEV | NAM$M_EXP_DIR)
  521.  
  522. FILE_LOCAL int
  523. vmsparse(source, defstring, result)
  524. char        *source;
  525. char        *defstring;    /* non-NULL -> default string.        */
  526. char        *result;    /* Size is at least NAM$C_MAXRSS + 1    */
  527. /*
  528.  * Parse the source string, applying the default (properly, using
  529.  * the system parse routine), storing it in result.
  530.  * TRUE if it parsed, FALSE on error.
  531.  *
  532.  * If defstring is NULL, there are no defaults and result gets
  533.  * (just) the node::[directory] part of the string (possibly "")
  534.  */
  535. {
  536.     struct FAB    fab = cc$rms_fab;    /* File access block    */
  537.     struct NAM    nam = cc$rms_nam;    /* File name block    */
  538.     char        fullname[NAM$C_MAXRSS + 1];
  539.     register char    *rp;            /* Result pointer    */
  540.  
  541.     fab.fab$l_nam = &nam;            /* fab -> nam        */
  542.     fab.fab$l_fna = source;            /* Source filename    */
  543.     fab.fab$b_fns = strlen(source);        /* Size of source    */
  544.     fab.fab$l_dna = defstring;        /* Default string    */
  545.     if (defstring != NULLST)
  546.         fab.fab$b_dns = strlen(defstring);    /* Size of default    */
  547.     nam.nam$l_esa = fullname;        /* Expanded filename    */
  548.     nam.nam$b_ess = NAM$C_MAXRSS;        /* Expanded name size    */
  549.     if (sys$parse(&fab) == RMS$_NORMAL) {    /* Parse away        */
  550.         fullname[nam.nam$b_esl] = EOS;    /* Terminate string    */
  551.         result[0] = EOS;            /* Just in case        */
  552.         rp = &result[0];
  553.         /*
  554.          * Remove stuff added implicitly, accepting node names and
  555.          * dev:[directory] strings (but not process-permanent files).
  556.          */
  557.         if ((nam.nam$l_fnb & NAM$M_PPF) == 0) {
  558.         if ((nam.nam$l_fnb & NAM$M_NODE) != 0) {
  559.             strncpy(result, nam.nam$l_node, nam.nam$b_node);
  560.             rp += nam.nam$b_node;
  561.             *rp = EOS;
  562.         }
  563.         if ((nam.nam$l_fnb & DEVDIR) == DEVDIR) {
  564.             strncpy(rp, nam.nam$l_dev, nam.nam$b_dev + nam.nam$b_dir);
  565.             rp += nam.nam$b_dev + nam.nam$b_dir;
  566.             *rp = EOS;
  567.         }
  568.         }
  569.         if (defstring != NULLST) {
  570.         strncpy(rp, nam.nam$l_name, nam.nam$b_name + nam.nam$b_type);
  571.         rp += nam.nam$b_name + nam.nam$b_type;
  572.         *rp = EOS;
  573.         if ((nam.nam$l_fnb & NAM$M_EXP_VER) != 0) {
  574.             strncpy(rp, nam.nam$l_ver, nam.nam$b_ver);
  575.             rp[nam.nam$b_ver] = EOS;
  576.         }
  577.         }
  578.         return (TRUE);
  579.     }
  580.     return (FALSE);
  581. }
  582. #endif
  583.  
  584.