home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / src / icont / link.c < prev    next >
C/C++ Source or Header  |  2002-03-20  |  18KB  |  638 lines

  1. /*
  2.  * link.c -- linker main program that controls the linking process.
  3.  */
  4.  
  5. #include "link.h"
  6. #include "tproto.h"
  7. #include "tglobals.h"
  8. #include "../h/header.h"
  9.  
  10. #if SCCX_MX
  11.    #undef ISICONT
  12.    #define ISICONT
  13.    #include "ixhdr.h"
  14. #endif                    /* SCCX_MX */
  15.  
  16. #ifdef Header
  17.    #ifndef ShellHeader
  18.       #include "hdr.h"
  19.    #endif                    /* ShellHeader */
  20.    #ifndef MaxHeader
  21.       #define MaxHeader MaxHdr
  22.    #endif                    /* MaxHeader */
  23. #endif                    /* Header */
  24.  
  25. /*
  26.  * Prototype.
  27.  */
  28.  
  29. static    void    setexe    (char *fname);
  30.  
  31. /*
  32.  * The following code is operating-system dependent [@link.01].  Include
  33.  *  system-dependent files and declarations.
  34.  */
  35.  
  36. #if PORT
  37.    /* nothing to do */
  38.    Deliberate Syntax Error
  39. #endif                    /* PORT */
  40.  
  41. #if AMIGA || MACINTOSH || VMS
  42.    /* nothing to do */
  43. #endif                    /* AMIGA || ... */
  44.  
  45. #if ARM
  46.    #include "kernel.h"
  47.    #include "swis.h"
  48. #endif                    /* ARM */
  49.  
  50. #if MSDOS
  51.    extern char pathToIconDOS[];
  52.    #if MICROSOFT || TURBO || BORLAND_286 || BORLAND_386
  53.       #include <fcntl.h>
  54.    #endif                /* MICROSOFT || TURBO ... */
  55. #endif                    /* MSDOS */
  56.  
  57. #if OS2
  58.    #if MICROSOFT || CSET2
  59.       #include <fcntl.h>
  60.    #endif                /* MICROSOFT || CSET2 */
  61. #endif                    /* OS2 */
  62.  
  63. #if UNIX
  64.    #include <sys/types.h>
  65.    #include <sys/stat.h>
  66. #endif                    /* UNIX */
  67.  
  68. /*
  69.  * End of operating-system specific code.
  70.  */
  71.  
  72. FILE *infile;                /* input file (.u1 or .u2) */
  73. FILE *outfile;                /* interpreter code output file */
  74.  
  75. #ifdef DeBugLinker
  76.    FILE *dbgfile;            /* debug file */
  77.    static char dbgname[MaxFileName];    /* debug file name */
  78. #endif                    /* DeBugLinker */
  79.  
  80. struct lfile *llfiles = NULL;        /* List of files to link */
  81.  
  82. char inname[MaxFileName];        /* input file name */
  83.  
  84. char icnname[MaxFileName];        /* current icon source file name */
  85. int colmno = 0;                /* current source column number */
  86. int lineno = 0;                /* current source line number */
  87. int fatals = 0;                /* number of errors encountered */
  88.  
  89. /*
  90.  *  ilink - link a number of files, returning error count
  91.  */
  92. int ilink(ifiles,outname)
  93. char **ifiles;
  94. char *outname;
  95.    {
  96.    int i;
  97.    struct lfile *lf,*lfls;
  98.    char *filename;            /* name of current input file */
  99.  
  100.    linit();                /* initialize memory structures */
  101.    while (*ifiles)
  102.       alsolink(*ifiles++);        /* make initial list of files */
  103.  
  104.    /*
  105.     * Phase I: load global information contained in .u2 files into
  106.     *  data structures.
  107.     *
  108.     * The list of files to link is maintained as a queue with llfiles
  109.     *  as the base.  lf moves along the list.  Each file is processed
  110.     *  in turn by forming .u2 and .icn names from each file name, each
  111.     *  of which ends in .u1.  The .u2 file is opened and globals is called
  112.     *  to process it.  When the end of the list is reached, lf becomes
  113.     *  NULL and the loop is terminated, completing phase I.  Note that
  114.     *  link instructions in the .u2 file cause files to be added to list
  115.     *  of files to link.
  116.     */
  117.    for (lf = llfiles; lf != NULL; lf = lf->lf_link) {
  118.       filename = lf->lf_name;
  119.       makename(inname, SourceDir, filename, U2Suffix);
  120.       makename(icnname, TargetDir, filename, SourceSuffix);
  121.       infile = fopen(inname, ReadText);
  122.       if (infile == NULL)
  123.          quitf("cannot open %s",inname);
  124.       readglob();
  125.       fclose(infile);
  126.       }
  127.  
  128.    /* Phase II (optional): scan code and suppress unreferenced procs. */
  129.    if (!strinv)
  130.       scanrefs();
  131.  
  132.    /* Phase III: resolve undeclared variables and generate code. */
  133.  
  134.    /*
  135.     * Open the output file.
  136.     */
  137.    #if AMIGA && __SASC
  138.       outfile = fopen(outname, ReadWriteBinary);
  139.    #else                /* AMIGA && __SASC */
  140.       outfile = fopen(outname, WriteBinary);
  141.    #endif                /* AMIGA && __SASC */
  142.  
  143. /*
  144.  * The following code is operating-system dependent [@link.02].  Set
  145.  *  untranslated mode if necessary.
  146.  */
  147.  
  148. #if PORT
  149.    /* probably nothing */
  150.    Deliberate Syntax Error
  151. #endif                    /* PORT */
  152.  
  153. #if AMIGA || ARM || MACINTOSH || UNIX || VMS
  154.    /* nothing to do */
  155. #endif                    /* AMIGA || ARM || ... */
  156.  
  157. #if MSDOS
  158.    #if MICROSOFT || TURBO || BORLAND_286 || BORLAND_386
  159.       setmode(fileno(outfile),O_BINARY);    /* set for untranslated mode */
  160.    #endif                /* MICROSOFT || TURBO ... */
  161. #endif                    /* MSDOS */
  162.  
  163. #if OS2
  164.    #if MICROSOFT || CSET2
  165.       setmode(fileno(outfile),O_BINARY);
  166.    #endif                /* MICROSOFT || CSET2 */
  167. #endif                    /* OS2 */
  168.  
  169. /*
  170.  * End of operating-system specific code.
  171.  */
  172.  
  173.    if (outfile == NULL) {        /* may exist, but can't open for "w" */
  174.       ofile = NULL;            /* so don't delete if it's there */
  175.       quitf("cannot create %s",outname);
  176.       }
  177.  
  178. #if MSDOS && (!NT)
  179.    /*
  180.     * This prepends ixhdr.exe to outfile, so it'll be executable.
  181.     *
  182.     * I don't know what that #if Header stuff was about since my MSDOS
  183.     * distribution didn't include "hdr.h", but it looks very similar to
  184.     * what I'm doing, so I'll put my stuff here, & if somebody who
  185.     * understands all the multi-operating-system porting thinks my code could
  186.     * be folded into it, having it here should make it easy. -- Will Mengarini.
  187.     */
  188.  
  189.    if (makeExe) {
  190.  
  191. #if SCCX_MX
  192.     unsigned long i;
  193.  
  194.     if( makeExe == 1)
  195.     {
  196.         for( i=0; i<IXHDRSIZE; ++i)
  197.             fputc( ixhdrarray[i], outfile);
  198.         fileOffsetOfStuffThatGoesInICX = IXHDRSIZE;
  199.     }
  200.     else
  201.     {
  202.         FILE *fIconDOS = fopen(pathToIconDOS, "rb");
  203.         char oneChar;
  204.  
  205.         if (!fIconDOS)
  206.             quit("unable to find iconx.exe in same dir as icont");
  207.  
  208.         if (setvbuf(fIconDOS, 0, _IOFBF, 4096))
  209.         {
  210.             if (setvbuf(fIconDOS, 0, _IOFBF, 128))
  211.                 quit("setvbuf() failure");
  212.         }
  213.  
  214.         fseek( fIconDOS, 0, 0);
  215.         while( oneChar = fgetc( fIconDOS), !feof( fIconDOS) )
  216.         {
  217.             if( ferror( fIconDOS)  ||  ferror( outfile) )
  218.                 quit("Error copying iconx.exe");
  219.             fputc( oneChar, outfile);
  220.         }
  221.  
  222.         fclose (fIconDOS);
  223.         fileOffsetOfStuffThatGoesInICX = ftell (outfile);
  224.     }
  225. #else                    /* SCCX_MX */
  226.       FILE *fIconDOS = fopen(pathToIconDOS, "rb");
  227.       char bytesThatBeginEveryExe[2] = {0,0}, oneChar;
  228.       unsigned short originalExeBytesMod512, originalExePages;
  229.       unsigned long originalExeBytes, byteCounter;
  230.  
  231.       if (!fIconDOS)
  232.          quit("unable to find ixhdr.exe in same dir as icont");
  233.       if (setvbuf(fIconDOS, 0, _IOFBF, 4096))
  234.          if (setvbuf(fIconDOS, 0, _IOFBF, 128))
  235.             quit("setvbuf() failure");
  236.       fread (&bytesThatBeginEveryExe, 2, 1, fIconDOS);
  237.       if (bytesThatBeginEveryExe[0] != 'M' ||
  238.           bytesThatBeginEveryExe[1] != 'Z')
  239.          quit("ixhdr header is corrupt");
  240.       fread (&originalExeBytesMod512, sizeof originalExeBytesMod512,
  241.             1, fIconDOS);
  242.       fread (&originalExePages,       sizeof originalExePages,
  243.             1, fIconDOS);
  244.       originalExeBytes = (originalExePages - 1)*512 + originalExeBytesMod512;
  245.  
  246.       if (ferror(fIconDOS) || feof(fIconDOS) || !originalExeBytes)
  247.          quit("ixhdr header is corrupt");
  248.       fseek (fIconDOS, 0, 0);
  249.  
  250. #ifdef MSWindows
  251.       for (oneChar=fgetc(fIconDOS); !feof(fIconDOS); oneChar=fgetc(fIconDOS)) {
  252.          if (ferror(fIconDOS) || ferror(outfile)) {
  253.             quit("Error copying ixhdr.exe");
  254.         }
  255.          fputc (oneChar, outfile);
  256.          }
  257. #else                    /* MSWindows */
  258.  
  259.       for (byteCounter = 0; byteCounter < originalExeBytes; byteCounter++) {
  260.          oneChar = fgetc (fIconDOS);
  261.          if (ferror(fIconDOS) || feof(fIconDOS) || ferror(outfile)) {
  262.             quit("Error copying ixhdr.exe");
  263.         }
  264.          fputc (oneChar, outfile);
  265.          }
  266. #endif                    /* MSWindows */
  267.  
  268.       fclose (fIconDOS);
  269.       fileOffsetOfStuffThatGoesInICX = ftell (outfile);
  270. #endif                    /* SCCX_MX */
  271.       }
  272. #endif                                  /* MSDOS && (!NT) */
  273.  
  274. #ifdef Header
  275.    /*
  276.     * Write the bootstrap header to the output file.
  277.     */
  278.  
  279. #ifdef ShellHeader
  280.    /*
  281.     * Write a short shell header terminated by \n\f\n\0.
  282.     * Use magic "#!/bin/sh" to ensure that $0 is set when run via $PATH.
  283.     * Pad header to a multiple of 8 characters.
  284.     */
  285.    {
  286.  
  287. #if AMIGA
  288.    char script[2 * MaxPath + 200];
  289.    sprintf(script,
  290.    "/* ARexx header for direct execution of Icon code */\n\
  291.    PARSE SOURCE type flag filename fullname ext host\n\
  292.    PARSE ARG 1 args 1\n\
  293.    SIGNAL ON ERROR\n\
  294.    ADDRESS command\n\
  295.    \"%s\" fullname args\n\
  296.    ERROR:\n\
  297.    EXIT\n\
  298.    /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*\n[executable Icon binary follows]\n",
  299.    iconxloc);
  300.    strcat(script, "        \n\f\n" + ((int)(strlen(script) + 4) % 8));
  301.    hdrsize = strlen(script) + 1;    /* length includes \0 at end */
  302.    fwrite(script, hdrsize, 1, outfile);    /* write header */
  303. #endif                                  /* AMIGA */
  304.  
  305. #if NT
  306.    /*
  307.     * The NT and Win95 direct execution batch file turns echoing off,
  308.     * launches wiconx, attempts to terminate softly via noop.bat,
  309.     * and terminates the hard way (by exiting the DOS shell) if that
  310.     * fails, rather than fall through and start executing machine code
  311.     * as if it were batch commands.
  312.     */
  313.    char script[2 * MaxPath + 200];
  314.    sprintf(script,
  315.            "@echo off\r\n%s %%0 %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\r\n",
  316.            ((iconxloc!=NULL)?iconxloc:"wiconx"));
  317.    strcat(script,"noop.bat\r\n@echo on\r\n");
  318.    strcat(script,"pause missing noop.bat - press ^c or shell will exit\r\n");
  319.    strcat(script,"exit\r\nrem [executable Icon binary follows]\r\n");
  320.    strcat(script, "        \n\f\n" + ((int)(strlen(script) + 4) % 8));
  321.    hdrsize = strlen(script) + 1;    /* length includes \0 at end */
  322.    fwrite(script, hdrsize, 1, outfile);    /* write header */
  323. #endif                    /* NT */
  324. #if UNIX
  325.    /*
  326.     *  Generate a shell header that searches for iconx in this order:
  327.     *     a.  location specified by ICONX environment variable
  328.     *         (if specified, this MUST work, else the script exits)
  329.     *     b.  iconx in same directory as executing binary
  330.     *     c.  location specified in script
  331.     *         (as generated by icont or as patched later)
  332.     *     d.  iconx in $PATH
  333.     *
  334.     *  The ugly ${1+"$@"} is a workaround for non-POSIX handling
  335.     *  of "$@" by some shells in the absence of any arguments.
  336.     *  Thanks to the Unix-haters handbook for this trick.
  337.     */
  338.    char script[2 * MaxPath + 300];
  339.    sprintf(script, "%s\n%s%-72s\n%s\n\n%s\n%s\n%s\n%s\n\n%s\n",
  340.       "#!/bin/sh",
  341.       "IXBIN=", iconxloc,
  342.       "IXLCL=`echo $0 | sed 's=[^/]*$=iconx='`",
  343.       "[ -n \"$ICONX\" ] && exec \"$ICONX\" $0 ${1+\"$@\"}",
  344.       "[ -x $IXLCL ] && exec $IXLCL $0 ${1+\"$@\"}",
  345.       "[ -x $IXBIN ] && exec $IXBIN $0 ${1+\"$@\"}",
  346.       "exec iconx $0 ${1+\"$@\"}",
  347.       "[executable Icon binary follows]");
  348.    strcat(script, "        \n\f\n" + ((int)(strlen(script) + 4) % 8));
  349.    hdrsize = strlen(script) + 1;    /* length includes \0 at end */
  350.    fwrite(script, hdrsize, 1, outfile);    /* write header */
  351. #endif                    /* UNIX */
  352.    }
  353. #else                    /* ShellHeader */
  354.    /*
  355.     *  Always write MaxHeader bytes.
  356.     */
  357.    fwrite(iconxhdr, sizeof(char), MaxHeader, outfile);
  358.    hdrsize = MaxHeader;
  359. #endif                    /* ShellHeader */
  360. #endif                    /* Header */
  361.  
  362.    for (i = sizeof(struct header); i--;)
  363.       putc(0, outfile);
  364.    fflush(outfile);
  365.    if (ferror(outfile) != 0)
  366.       quit("unable to write to icode file");
  367.  
  368. #ifdef DeBugLinker
  369.    /*
  370.     * Open the .ux file if debugging is on.
  371.     */
  372.    if (Dflag) {
  373.       makename(dbgname, TargetDir, llfiles->lf_name, ".ux");
  374.       dbgfile = fopen(dbgname, WriteText);
  375.       if (dbgfile == NULL)
  376.          quitf("cannot create %s", dbgname);
  377.       }
  378. #endif                    /* DeBugLinker */
  379.  
  380.    /*
  381.     * Loop through input files and generate code for each.
  382.     */
  383.    lfls = llfiles;
  384.    while ((lf = getlfile(&lfls)) != 0) {
  385.       filename = lf->lf_name;
  386.       makename(inname, SourceDir, filename, U1Suffix);
  387.       makename(icnname, TargetDir, filename, SourceSuffix);
  388.       infile = fopen(inname, ReadText);
  389.       if (infile == NULL)
  390.          quitf("cannot open %s", inname);
  391.       gencode();
  392.       fclose(infile);
  393.       }
  394.  
  395.    gentables();        /* Generate record, field, global, global names,
  396.                static, and identifier tables. */
  397.  
  398. #if AMIGA
  399. #ifdef ShellHeader
  400.    /* The icode is enclosed in a deeply nested ARexx comment.
  401.       Here we close up the comment. */
  402.      {
  403.      int c, count = 0, state = 0;
  404.  
  405.      rewind(outfile);
  406.      while ((c = fgetc(outfile)) != EOF)
  407.        switch(state){
  408.          case 0:  /* start state */
  409.            if (c == '/' || c == '*') state = c;
  410.            break;
  411.          case '/':
  412.            if (c == '*') count++;
  413.            state = 0;
  414.            break;
  415.          case '*':
  416.            if (c == '/') count--;
  417.            state = 0;
  418.            break;
  419.          }
  420.      if (count < 0){
  421.        fprintf(stderr, "       Failed to write direct execution header.\n\
  422.        You will have to use iconx to execute %s.\n", outname);
  423.        outname = NULL;
  424.        }
  425.      else {
  426.        fseek(outfile, 0, SEEK_END);
  427.        while (0 < count--){
  428.          fputc('*', outfile);
  429.          fputc('/', outfile);
  430.          }
  431.        }
  432.      }
  433. #endif                    /* ShellHeader */
  434. #endif                    /* AMIGA */
  435.  
  436.    fclose(outfile);
  437.    lmfree();
  438.    if (fatals > 0)
  439.       return fatals;
  440.    setexe(outname);
  441.    return 0;
  442.    }
  443.  
  444. #ifdef ConsoleWindow
  445.    extern FILE *flog;
  446. #endif                    /* ConsoleWindow */
  447. /*
  448.  * lwarn - issue a linker warning message.
  449.  */
  450. void lwarn(s1, s2, s3)
  451. char *s1, *s2, *s3;
  452.    {
  453.  
  454. #ifdef ConsoleWindow
  455.    if (flog != NULL) {
  456.       fprintf(flog, "%s: ", icnname);
  457.       if (lineno)
  458.          fprintf(flog, "Line %d # :", lineno);
  459.       fprintf(flog, "\"%s\": %s%s\n", s1, s2, s3);
  460.       fflush(flog);
  461.       return;
  462.       }
  463. #endif                    /* ConsoleWindow */
  464.    fprintf(stderr, "%s: ", icnname);
  465.    if (lineno)
  466.       fprintf(stderr, "Line %d # :", lineno);
  467.    fprintf(stderr, "\"%s\": %s%s\n", s1, s2, s3);
  468.    fflush(stderr);
  469.    }
  470.  
  471. /*
  472.  * lfatal - issue a fatal linker error message.
  473.  */
  474.  
  475. void lfatal(s1, s2)
  476. char *s1, *s2;
  477.    {
  478.  
  479.    fatals++;
  480. #ifdef ConsoleWindow
  481.    if (flog != NULL) {
  482.       fprintf(flog, "%s: ", icnname);
  483.       if (lineno)
  484.          fprintf(flog, "Line %d # : ", lineno);
  485.       fprintf(flog, "\"%s\": %s\n", s1, s2);
  486.       return;
  487.       }
  488. #endif                    /* ConsoleWindow */
  489.    fprintf(stderr, "%s: ", icnname);
  490.    if (lineno)
  491.       fprintf(stderr, "Line %d # : ", lineno);
  492.    fprintf(stderr, "\"%s\": %s\n", s1, s2);
  493.    }
  494.  
  495. /*
  496.  * setexe - mark the output file as executable
  497.  */
  498.  
  499. static void setexe(fname)
  500. char *fname;
  501.    {
  502.  
  503. /*
  504.  * The following code is operating-system dependent [@link.03].  It changes the
  505.  *  mode of executable file so that it can be executed directly.
  506.  */
  507.  
  508. #if PORT
  509.    /* something is needed */
  510. Deliberate Syntax Error
  511. #endif                    /* PORT */
  512.  
  513. #if AMIGA
  514.    #if __SASC
  515.       if (fname != NULL) {
  516.       struct DiskObject *dob;
  517.  
  518.       chmod(fname,S_ISCRIPT|S_IREAD|S_IWRITE|S_IEXECUTE|S_IDELETE);
  519.       /* Create a WorkBench Icon if necessary. */
  520.       dob = GetDiskObject(fname);
  521.       if (dob == NULL) {
  522.          dob = GetDiskObject(IconIcon);
  523.          if ( dob ) {
  524.             dob->do_CurrentX = NO_ICON_POSITION;
  525.             dob->do_CurrentY = NO_ICON_POSITION;
  526.             PutDiskObject(fname, dob);
  527.             }
  528.          }
  529.       if ( dob ) FreeDiskObject(dob);
  530.       }
  531.    #endif                                  /* __SASC */
  532. #endif                    /* AMIGA */
  533.  
  534. #if ARM
  535.    {
  536.       _kernel_swi_regs regs;
  537.  
  538.       regs.r[0] = 31;
  539.       regs.r[1] = (int)"Icon";
  540.       if (_kernel_swi(OS_FSControl,®s,®s) == NULL)
  541.       {
  542.          regs.r[0] = 18;
  543.          regs.r[1] = (int)fname;
  544.          _kernel_swi(OS_File,®s,®s);
  545.       }
  546.    }
  547. #endif                    /* ARM */
  548.  
  549. #if MSDOS || VMS
  550.    /*
  551.     * can't be made executable
  552.     * note: VMS files can't be made executable, but see "iexe.com" under VMS.
  553.     */
  554. #endif                    /* MSDOS || ... */
  555.  
  556. #if MACINTOSH
  557.    #if MPW
  558.       /* Nothing to do here -- file is set to type TEXT
  559.          (so it can be executed as a script) in tmain.c.
  560.       */
  561.    #endif                /* MPW */
  562. #endif                    /* MACINTOSH */
  563.  
  564. #if MSDOS
  565.    #if MICROSOFT || TURBO || BORLAND_286 || BORLAND_386
  566.       chmod(fname,0755);    /* probably could be smarter... */
  567.    #endif                /* MICROSOFT || TURBO ... */
  568. #endif                    /* MSDOS */
  569.  
  570. #if OS2
  571.     /*
  572.      *    Obtain the EXE stub resource from icont (or xicont)
  573.      *    and write it out as the executable name.  Invoke the resource
  574.      *    compiler to add the icode file as a resource to the executable
  575.      *    This should be portable to Windows NT I believe.  Cheyenne.
  576.      */
  577.     {
  578.     char    *exeres;        /* EXE stub resource pointer */
  579.     unsigned long exereslen;    /* Length of resource */
  580.     char loadmoderr[256];
  581.  
  582.     char exename[256];
  583.     char rcname[256];
  584.     char cmdbuffer[256];
  585.     FILE *exefile;
  586.     FILE *rcfile;
  587.  
  588.     if( noexe ) return;        /* Nothing to do.. */
  589.  
  590.     DosGetResource(0,0x4844,1,&exeres);
  591.     DosQueryResourceSize(0,0x4844,1,&exereslen);
  592.  
  593.     makename(exename,NULL,fname,".exe");
  594.     exefile = fopen(exename,WriteBinary);
  595.     fwrite( exeres, sizeof(char), exereslen, exefile);
  596.     fclose(exefile);
  597.     DosFreeResource(exeres);
  598.  
  599.     makename(rcname,NULL,fname,".rc");
  600.     rcfile = fopen(rcname,WriteText);
  601.  
  602.     fprintf(rcfile,"RESOURCE 0x4843 1 %s\n",fname);
  603.     fclose(rcfile);
  604.  
  605.     sprintf(cmdbuffer,"rc %s %s",rcname,exename);
  606.  
  607.     system(cmdbuffer);
  608.  
  609.     remove(rcname);
  610.     makename(rcname,NULL,fname,".res");
  611.     remove(rcname);
  612.     remove(fname);
  613.     }
  614. #endif                    /* OS2 */
  615.  
  616. #if UNIX
  617.       {
  618.       struct stat stbuf;
  619.       int u, r, m;
  620.       /*
  621.        * Set each of the three execute bits (owner,group,other) if allowed by
  622.        *  the current umask and if the corresponding read bit is set; do not
  623.        *  clear any bits already set.
  624.        */
  625.       umask(u = umask(0));        /* get and restore umask */
  626.       if (stat(fname,&stbuf) == 0)  {    /* must first read existing mode */
  627.          r = (stbuf.st_mode & 0444) >> 2;    /* get & position read bits */
  628.          m = stbuf.st_mode | (r & ~u);        /* set execute bits */
  629.          chmod(fname,m);         /* change file mode */
  630.          }
  631.       }
  632. #endif                    /* UNIX */
  633.  
  634. /*
  635.  * End of operating-system specific code.
  636.  */
  637.    }
  638.