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