home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / futil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-28  |  24.2 KB  |  598 lines

  1. /*============================================================================*
  2.  * module: FUTIL.C - File utility subroutines: OS/2 2.0 (32-bit) version.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1987, 1988, 1991, 1992, 1993.  Brian E. Yoder
  5.  *
  6.  * Functions contained in this module:
  7.  *   cvtftime()    - Convert file's time/date to FTM structure type.
  8.  *   pathcat()     - Concatenate a path string onto a base path string.
  9.  *   isdir()       - Does a string represent a valid directory name?
  10.  *   hasdrive()    - Does a string have a drive specification?
  11.  *   ishpfs()      - Does a string represent a pathname on an HPFS filesystem?
  12.  *   decompose()   - Decompose '[d:path]name' into 'd:path' and 'name'.
  13.  *   SetFileMode() - Set file's mode (replacement for DosSetFileMode).
  14.  *
  15.  * 04/16/91 - Initial version created from functions in the ffind.c module.
  16.  * 04/17/91 - Added hasdrive().
  17.  * 04/30/91 - Ported to OS/2.  Don't need to include <dos.h>.
  18.  * 05/09/91 - Use #defined labels in case stmt in pathcat().
  19.  * 06/11/91 - Updated isdir() to support long directory names.
  20.  * 04/13/92 - Added ishpfs(). For now: valid on locally-attached drives only.
  21.  * 07/27/92 - Ported to OS/2 2.0 and C Set/2. The SetFileMode() was added
  22.  *            to replace the DosSetFileMode() which is in 1.3 but not 2.0.
  23.  * 04/20/93 - Changed ishpfs() to also return TRUE if the filesystem is
  24.  *            a remote NFS filesystem.
  25.  * 09/28/93 - Changed ishpfs() to also return TRUE if the filesystem is
  26.  *            a remote TVFS filesystem.
  27.  *============================================================================*/
  28. #define LINT_ARGS
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <sys\types.h>
  34. #include <sys\stat.h>
  35.  
  36. #include "util.h"
  37.  
  38. /*============================================================================*
  39.  * Static data
  40.  *============================================================================*/
  41.  
  42. static char  pathsep[] = "\\";   /* Path separator string */
  43.  
  44. #define SEP '\\'                 /* Path separator character definition */
  45.  
  46. /*----------------------------------------------------------------------------*
  47.  * Static structure overwritten by calls to decompose()
  48.  *----------------------------------------------------------------------------*/
  49.  
  50. static PATH_NAME pn;
  51.  
  52. /*============================================================================*
  53.  * cvtftime() - Convert file's time and date stamps to FTM structure type.
  54.  *
  55.  * REMARKS:
  56.  *   This function converts a file's time and date words into month-day-year
  57.  *   and hours-minutes-seconds and stores them in the FTM structure type.
  58.  *
  59.  *   The file's time word (16 bits) is mapped by DOS as follows:
  60.  *
  61.  *       <   high-order byte   >  <   low-order byte   >
  62.  *       15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
  63.  *        h  h  h  h  h  m  m  m  m  m  m  x  x  x  x  x
  64.  *
  65.  *              hh is the number of hours.
  66.  *              mm is the number of minutes.
  67.  *              xx is the number of two-second increments.
  68.  *
  69.  *   The file's date word (16 bits) is mapped by DOS as follows:
  70.  *
  71.  *       <   high-order byte   >  <   low-order byte   >
  72.  *       15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
  73.  *        y  y  y  y  y  y  y  m  m  m  m  d  d  d  d  d
  74.  *
  75.  *              yy is the year:  0-119  (1980-2099).
  76.  *              mm is the month.
  77.  *              dd is the day of the month.
  78.  *
  79.  *   See the DOS technical reference for more information.
  80.  *
  81.  * RETURNS: 0
  82.  *============================================================================*/
  83. int cvtftime(
  84.  
  85. unsigned  ftime ,       /* DOS file time */
  86. unsigned  fdate ,       /* DOS file date */
  87. FTM      *ftm   )       /* Pointer to caller's file time structure */
  88.  
  89. {
  90.   ftm->ftm_sec =  (ftime & 0x001F) << 1;
  91.   ftm->ftm_min =  (ftime >> 5) & 0x003F;
  92.   ftm->ftm_hr =   (ftime >> 11) & 0x001F;
  93.  
  94.   ftm->ftm_day =  fdate & 0x001F;
  95.   ftm->ftm_mon =  (fdate >> 5) & 0x000F;
  96.   ftm->ftm_year = 1980 + ((fdate >> 9) & 0x007F);
  97.  
  98.   return(0);                        /* Return 0: successful */
  99. }
  100.  
  101. /*============================================================================*
  102.  * pathcat() - Concatenate a path string onto a base path string.
  103.  *
  104.  * Description:
  105.  *   This subroutine concatenates the string 'str' to the 'base' pathname.
  106.  *   The calling program must ensure that the array pointed to by 'base'
  107.  *   is large enough to hold 'base', 'str', a possible additional path
  108.  *   separator, and the null string terminating byte.
  109.  *
  110.  *   If 'base' is a drive specification (such as A: or C:), then 'str'
  111.  *   is simply added to 'base':
  112.  *
  113.  *   a) If 'base' ends with a ":" , the 'str' is simply added to 'base'.
  114.  *      In this case, 'base' is assumed to be a drive specification
  115.  *      with no path information (as in A: or C:).
  116.  *
  117.  *   Otherwise, this subroutine ensures that there is one and only one path
  118.  *   separator between 'base' and 'str' as follows:
  119.  *
  120.  *   b) If 'base' doesn't end with a "/" and 'str' doesn't begin with one,
  121.  *      then a "/" is added to 'base' before 'str' is added to 'base'.
  122.  *
  123.  *   c) If 'base' ends with a "/" or 'str' begins with one (but not both),
  124.  *      then 'str' is simply added to 'base'.
  125.  *
  126.  *   d) Otherwise, 'str' is incremented past its beginning "/" before being
  127.  *      added to 'base'.
  128.  *
  129.  * Returns:
  130.  *   Pointer to the 'base' pathname.
  131.  *
  132.  * Notes:
  133.  *   The following table defines the bit pattern, value, and meaning of
  134.  *   the 'action' variable during the operation of this subroutine
  135.  *   (as long as 'base' is not a drive specification with no path):
  136.  *
  137.  *   Last char   First char     Bit
  138.  *   of 'base'    of 'str'    Pattern   Value   Meaning of Value
  139.  *   ---------   ----------   -------   -----   -------------------------
  140.  *     not /       not /       0   0      0     Add path separator first
  141.  *     not /         /         0   1      1     Just concatenate 'str'
  142.  *       /         not /       1   0      2     Just concatenate 'str'
  143.  *       /           /         1   1      3     First increment 'str'
  144.  *============================================================================*/
  145.  
  146. #define ADD_SEP      0       /* Special actions */
  147. #define REMOVE_SEP   3
  148.  
  149. char *pathcat(
  150.  
  151. char *base ,         /* Pointer to base pathname */
  152. char *str  )         /* Pointer to string to add to the base pathname */
  153.  
  154. {
  155.   int   action;      /* Action to be taken during concatenation */
  156.   int   baselen;     /* Length of base string */
  157.  
  158.  /*---------------------------------------------------------------------------*
  159.   * Take care of special cases
  160.   *---------------------------------------------------------------------------*/
  161.  
  162.   baselen = strlen(base);       /* Store length of base string */
  163.  
  164.   if (baselen == 0)             /* If length is zero: */
  165.   {
  166.      strcpy(base, str);               /* Just copy 'str' to 'base' */
  167.      return(base);                    /* Return pointer to 'base' */
  168.   }
  169.  
  170.   if (base[baselen-1] == ':')   /* If last character of base is a colon: */
  171.   {
  172.      strcat(base, str);               /* Append 'str' to end of 'base' */
  173.      return(base);                    /* Return pointer to 'base' */
  174.   }
  175.  
  176.  /*---------------------------------------------------------------------------*
  177.   * Append 'str' to 'base' with exactly one path separator between them
  178.   *---------------------------------------------------------------------------*/
  179.  
  180.   action = 0;                   /* Set 'action' to proper value */
  181.   if (base[baselen-1] == SEP)         /* If 'base' ends with '/' */
  182.      action = action | 2;
  183.   if (*str == SEP)                    /* If 'str' begins with '/' */
  184.      action = action | 1;
  185.  
  186.   switch (action)               /* Depending upon the value of 'action': */
  187.   {
  188.      case ADD_SEP:                    /* Add path separator first */
  189.         strcat(base, pathsep);
  190.         strcat(base, str);
  191.         break;
  192.  
  193.      case REMOVE_SEP:                 /* First increment 'str' past its path */
  194.         str++;                        /* separator before adding to 'base' */
  195.         strcat(base, str);
  196.         break;
  197.  
  198.      default:                         /* Just concatenate 'str' */
  199.         strcat(base, str);
  200.         break;
  201.   }
  202.  
  203.   return(base);                 /* Return pointer to base pathname */
  204. }
  205.  
  206. /*============================================================================*
  207.  * isdir() - Does a string represent a valid directory name?
  208.  *
  209.  * REMARKS:
  210.  *   This subroutine checks to see if a string represents a valid directory
  211.  *   name.
  212.  *
  213.  *   A null string represents the current directory and is a valid directory.
  214.  *
  215.  *   The stat() function call cannot be used for OS/2, since stat() won't
  216.  *   support long filenames.
  217.  *
  218.  * RETURNS:
  219.  *   FALSE, if it 'dirstr' is not a valid directory.
  220.  *   TRUE, if it is.
  221.  *============================================================================*/
  222. int isdir(
  223.  
  224. char *dirstr )                      /* Pointer to a directory name string */
  225.  
  226. {
  227.   int rc;                           /* Return code store area */
  228.   struct stat statb;                /* stat() structure information buffer */
  229.   int dlen;                         /* Length of dirstr string */
  230.   char *enddirstr;                  /* Pointer to end of dirstr specification */
  231.  
  232.   FILESTATUS3 pinfo;                /* Path information structure */
  233.  
  234.  /*---------------------------------------------------------------------------*
  235.   * Get length of dirstr and a pointer to the last character
  236.   *---------------------------------------------------------------------------*/
  237.  
  238.   dlen = strlen(dirstr);            /* Get length */
  239.   enddirstr = dirstr + dlen - 1;    /* Store pointer to the end */
  240.  
  241.   if (dlen == 0)                    /* If string is zero length: */
  242.      return(TRUE);                       /* It's the current directory */
  243.  
  244.  /*---------------------------------------------------------------------------*
  245.   * Take care of checks for special cases
  246.   *---------------------------------------------------------------------------*/
  247.  
  248.   if (strcmp(dirstr, ".") == 0)     /* If "." it's a valid dir spec */
  249.      return(TRUE);
  250.  
  251.   if (strcmp(dirstr, "..") == 0)    /* If ".." it's a valid dir spec */
  252.      return(TRUE);
  253.  
  254.   if ((dlen == 2) &&                /* If "d:" drive spec: it's valid */
  255.       (*enddirstr == ':'))
  256.      return(TRUE);
  257.  
  258.  /*---------------------------------------------------------------------------*
  259.   * Be sure that the dirstr path exists and is a directory
  260.   *---------------------------------------------------------------------------*/
  261.  
  262.  /*--------------------------------------*
  263.   * For OS/2 and long filename support:
  264.   *--------------------------------------*/
  265.  
  266.   rc = DosQueryPathInfo(dirstr,     /* Get path information for dirstr */
  267.                     1,                   /* Level 1 information */
  268.                     &pinfo,
  269.                     sizeof(pinfo));
  270.  
  271.   if (rc != 0)                      /* If error: it's not a directory */
  272.      return(FALSE);
  273.  
  274.   if ((pinfo.attrFile &             /* If its subdirectory attribute bit */
  275.        _A_SUBDIR) == 0)             /* isn't set: it's not a directory */
  276.      return(FALSE);
  277.  
  278.  /*--------------------------------------*
  279.   * DOS and OS/2 8.3 filenames only:
  280.   *--------------------------------------*/
  281.  /*--------------------------------------*
  282.   * rc = stat(dirstr, &statb);
  283.   *
  284.   * if (rc != 0)
  285.   *    return(FALSE);
  286.   *
  287.   * if ((statb.st_mode & S_IFDIR) == 0)
  288.   *    return(FALSE);
  289.   *--------------------------------------*/
  290.  
  291.   return(TRUE);                     /* We passed all of the tests */
  292. }
  293.  
  294. /*============================================================================*
  295.  * hasdrive() - Does a string have a drive specification?
  296.  *
  297.  * REMARKS:
  298.  *   This subroutine checks to see if a string begins with a letter followed
  299.  *   by a colon.
  300.  *
  301.  * RETURNS:
  302.  *   FALSE, if it has no drive specification.
  303.  *   TRUE, if it does.
  304.  *============================================================================*/
  305. int hasdrive(
  306.  
  307. char *dirstr )                      /* Pointer to a directory name string */
  308.  
  309. {
  310.  
  311.   if ( (strlen(dirstr) >= 2) &&     /* If string has 2 or more characters */
  312.        (dirstr[1] == ':') )         /* And the second character is a colon */
  313.             return(TRUE);           /* Then it contains a drive spec */
  314.   else
  315.             return(FALSE);
  316. }
  317.  
  318. /*============================================================================*
  319.  * ishpfs() - Is the drive supported by an HPFS or NFS filesystem?
  320.  *
  321.  * REMARKS:
  322.  *   This subroutine checks to see if the string represents an HPFS filesystem.
  323.  *   If the string begins with a drive letter followed by a colon, then that
  324.  *   drive is checked for having an HPFS filesystem.  Otherwise, the current
  325.  *   drive is checked.
  326.  *
  327.  *   The purpose of this function is to allow programs to determine if the
  328.  *   filesystem supports case-senstive names. HPFS always does, and NFS
  329.  *   probably does (assuming that the NFS filesystem exists on an *ix
  330.  *   machine).
  331.  *
  332.  * RETURNS:
  333.  *   FALSE, if it not HPFS or remote NFS.
  334.  *   TRUE, if it is.
  335.  *
  336.  * NOTES:
  337.  *   At this time, only locally-attached filesystems can be checked for HPFS.
  338.  *   Remotely-attached filesystems have a filesystem name of LAN whether or
  339.  *   not they're HPFS.
  340.  *
  341.  *   The structure returned by the file system query function appears to have
  342.  *   the remote name in a different place for 32-bit code than for 16-bit code.
  343.  *   In addition, for remote-attached filesystems, 32-bit code gets the LAN
  344.  *   or NFS strings stored as the remote name and not the filesystem name.
  345.  *============================================================================*/
  346. int ishpfs(
  347.  
  348. char *pathstr )                     /* Pointer to a pathname string */
  349.  
  350. {
  351.   int     rc;                       /* Return code */
  352.   ULONG   drivenum;                 /* Drive number */
  353.   ULONG   drivemap;                 /* Drive map */
  354.   char    drivestr[3];              /* String: "D:" */
  355.   char   *fsname = "";              /* Pointer to filesystem name */
  356.   char   *rname  = "";              /* Pointer to remote name */
  357.  
  358.   char    sbuff[128];               /* Character array buffer */
  359.   ULONG   bufferlen = sizeof(sbuff);
  360.   FSQBUFFER2 *buffer;               /* For DosQFSAttach() */
  361.   USHORT *ubuff;                    /* Pointer to USHORT array */
  362.  
  363.  /*---------------------------------------------------------------------------*
  364.   * Store the drive spec in drivestr[] as "D:"
  365.   *---------------------------------------------------------------------------*/
  366.  
  367.   drivestr[1] = ':';                /* Store colon and null terminator for */
  368.   drivestr[2] = '\0';               /* the drive spec string */
  369.  
  370.   if (hasdrive(pathstr))            /* If path string has a drive spec: */
  371.   {
  372.      drivestr[0] = pathstr[0];           /* Store its drive letter */
  373.   }
  374.   else                              /* Else: determine current drive's letter: */
  375.   {
  376.      rc = DosQueryCurrentDisk(&drivenum, &drivemap);
  377.      drivestr[0] = 'A' + drivenum - 1;
  378.   }
  379.  
  380.  /*---------------------------------------------------------------------------*
  381.   * Point fsname to the filesystem name for the drive spec in drivestr[]
  382.   *---------------------------------------------------------------------------*/
  383.  
  384.   rc = DosQFSAttach(drivestr,        /* Query FS name for this drive */
  385.                 0L,                  /* Ordinal is ignored */
  386.                 FSAIL_QUERYNAME,     /* Info level: query name */
  387.                 (FSQBUFFER2 *)sbuff, /* Pointer to info buffer */
  388.                 &bufferlen);         /* Size of buffer */
  389.  
  390.   if (rc == 0)                      /* If ok: */
  391.   {
  392.      ubuff = (USHORT *)sbuff;            /* Point ubuff to start of buffer */
  393.      buffer = (FSQBUFFER2 *)sbuff;       /* Point buffer to FS info hdr */
  394.      switch (buffer->iType)              /* Check iType of filesystem: */
  395.      {
  396.         case FSAT_LOCALDRV:                    /* usually FAT or HPFS */
  397.            fsname = &sbuff[ 7 + ubuff[2] ];
  398.            break;
  399.  
  400.         case FSAT_REMOTEDRV:                   /* usually LAN  or NFS */
  401.            fsname = &sbuff[ 7 + ubuff[2] ];
  402.  
  403.            rname = &sbuff[ 7 + ubuff[2] ];
  404.            rname += strlen(rname);
  405.            rname += 1;
  406.  
  407.            break;
  408.      }
  409.   } /* end if (rc == 0)
  410.  
  411.  /*---------------------------------------------------------------------------*
  412.   * Check for various local/remote filesystems that probably preserve case
  413.   *---------------------------------------------------------------------------*/
  414.  
  415.   if (strcmpi(fsname, "HPFS") == 0) /* Check for various local filesystems */
  416.      return(TRUE);
  417.  
  418.   if (strcmpi(fsname, "TVFS") == 0)
  419.      return(TRUE);
  420.  
  421.   if (strcmpi(rname, "NFS") == 0)   /* Test for various remote filesystems */
  422.      return(TRUE);
  423.  
  424.   if (strcmpi(rname, "TVFS") == 0)
  425.      return(TRUE);
  426.  
  427.   return(FALSE);                    /* Not HPFS: FAT, LAN, or other */
  428. }
  429.  
  430. /*============================================================================*
  431.  * decompose() - Decompose '[d:path]name' into 'd:path' and 'name'.
  432.  *
  433.  * REMARKS:
  434.  *   This subroutine scans the specified string.  It determines the pointers
  435.  *   and lengths of the drive/path and the name portions of the string.  Of
  436.  *   course, it assumes that the string represents a valid '[d:path\]name'.
  437.  *
  438.  * RETURNS:
  439.  *   A pointer to a static PATH_NAME structure type.  The information in this
  440.  *   structure is overwritten by each call to this subroutine.  Also, the
  441.  *   pointers in this structure point to locations within the original string.
  442.  *
  443.  *   The structure contains a pointer and a length for each portion of the
  444.  *   string.  The first pointer and length describe the d:path portion.
  445.  *   A pointer to the d:path portion always points to the beginning of the
  446.  *   string.  If the string has no d:path portion, then its length is zero.
  447.  *
  448.  *   The second pointer and length describe the name portion of the string.
  449.  *============================================================================*/
  450. PATH_NAME *decompose(
  451.  
  452. char *str )                         /* Pointer to a [d:path]name string */
  453.  
  454. {
  455.   int rc;                           /* Return code store area */
  456.   char ch;                          /* A character from the string */
  457.   int slen;                         /* Length of string */
  458.   char *endstr;                     /* Pointer to end of string */
  459.   char *pstr;                       /* Pointer to string (generic) */
  460.  
  461.  /*---------------------------------------------------------------------------*
  462.   * Get length of string and a pointer to the last character
  463.   *---------------------------------------------------------------------------*/
  464.  
  465.   slen = strlen(str);               /* Get length */
  466.   endstr = str + slen - 1;          /* Store pointer to the end */
  467.  
  468.  /*---------------------------------------------------------------------------*
  469.   * Initialize PATH_NAME structure (in the static 'pn' variable)
  470.   *---------------------------------------------------------------------------*/
  471.  
  472.   pn.path = str;                    /* Init. pointers to point to string */
  473.   pn.name = str;
  474.  
  475.   pn.pathlen = 0;                   /* Init. path length to zero */
  476.   pn.namelen = slen;                /* Assume whole string is the name */
  477.  
  478.   if (slen == 0)                    /* If string is zero length: */
  479.      return(&pn);                        /* Then just return */
  480.  
  481.  /*---------------------------------------------------------------------------*
  482.   * Starting at end of string, scan backwards:
  483.   *---------------------------------------------------------------------------*/
  484.  
  485.   pstr = endstr;                    /* Point to end of string */
  486.   for (;;)                          /* For each character: */
  487.   {
  488.      ch = *pstr;                         /* Store character */
  489.      if ((ch == SEP) ||                  /* If we found a path separator or */
  490.          (ch == ':'))                    /* a drive colon: */
  491.      break;                                   /* We're done looping */
  492.  
  493.      if (pstr == str)                    /* If we're at the beginning of string: */
  494.         break;                           /*      Then we're done looping */
  495.      pstr--;                             /* Not done: point to previous char */
  496.   }                                 /* End of loop for each character */
  497.  
  498.  /*---------------------------------------------------------------------------*
  499.   * ch contains the character that terminated the loop, and pstr points to it
  500.   *---------------------------------------------------------------------------*/
  501.  
  502.   if (pstr == str)                  /* If we're at the beginning of the string: */
  503.   {
  504.      if (ch != SEP)                      /* If 1st char is not a path separator: */
  505.      {
  506.         pn.name = str;                        /* Then the entire string is the name */
  507.         pn.namelen = slen;                    /* portion (there is no d:path) */
  508.      }
  509.      else                                /* Otherwise: string is '\name': */
  510.      {
  511.         pn.pathlen = 1;                       /* Path portion is the path separator */
  512.         pn.name = pstr+1;                     /* And the name portion is just past it */
  513.         pn.namelen = slen-1;
  514.      }
  515.      return(&pn);                        /* Return */
  516.   }
  517.  
  518.  /*---------------------------------------------------------------------------*
  519.   * We stopped looping before we got to the beginning of the string
  520.   *---------------------------------------------------------------------------*/
  521.  
  522.   switch (ch)                       /* Check the character that stopped the loop: */
  523.   {
  524.      case SEP:                           /* PATH SEPARATOR: */
  525.         pn.pathlen = pstr - str;              /* Path portion is before path sep */
  526.         pn.name = pstr + 1;                   /* Name portion is after path sep */
  527.         pn.namelen = endstr - pstr;
  528.         if (*(pstr-1) == ':')                 /* If drive colon precedes path sep: */
  529.            pn.pathlen++;                           /* Then path sep is a part of d:path */
  530.         break;
  531.  
  532.      case ':':                           /* DRIVE COLON: */
  533.         pn.pathlen = pstr - str + 1;          /* Path portion includes colon */
  534.         pn.name = pstr + 1;                   /* Name portion is after colon */
  535.         pn.namelen = endstr - pstr;
  536.         break;
  537.  
  538.      default:                            /* WE HAD BETTER NEVER GET HERE! */
  539.         break;
  540.   }
  541.  
  542.   return(&pn);                      /* Done */
  543. }
  544.  
  545. /*============================================================================*
  546.  * SetFileMode() - Sets the mode (attributes) for a file.
  547.  *
  548.  * REMARKS:
  549.  *   This subroutine sets the mode bits for an existing file. It performs
  550.  *   the function of the OS/2 1.3 toolkit's DosSetFileMode() subroutine.
  551.  *
  552.  * RETURNS:
  553.  *   0, if successful.  Other, if error.
  554.  *============================================================================*/
  555.  
  556. /* These are the only bits we're allowed to process */
  557.  
  558. #define ModeBits  (FILE_READONLY | \
  559.                    FILE_HIDDEN   | \
  560.                    FILE_SYSTEM   | \
  561.                    FILE_ARCHIVED )
  562.  
  563. int SetFileMode(
  564.  
  565. char *pathname ,                    /* Pointer to file's name */
  566. uint  fmode    )                    /* Mode to set */
  567.  
  568. {
  569.   int         rc;
  570.   ulong       curmode;
  571.   ulong       level = 1;
  572.   FILESTATUS3 fbuff;
  573.   ulong       fbufflen = sizeof(FILESTATUS3);
  574.  
  575.   fmode = fmode & ModeBits;         /* Turn off bits we can't set */
  576.  
  577.   rc = DosQueryPathInfo(pathname,   /* Get current mode for file */
  578.                         level,
  579.                         &fbuff,
  580.                         fbufflen);
  581.  
  582.   if (rc == 0)                      /* If ok: */
  583.   {
  584.      curmode = fbuff.attrFile;           /* Get current mode bits */
  585.      curmode &= ~ModeBits;               /* Turn off the ones we can set */
  586.      curmode |= fmode;                   /* Turn on bits caller wants */
  587.      fbuff.attrFile = curmode;           /* Store new mode in structure */
  588.  
  589.      rc = DosSetPathInfo(pathname,       /* Set new mode for file */
  590.                         level,
  591.                         &fbuff,
  592.                         fbufflen,
  593.                         DSPI_WRTTHRU);
  594.   }                                 /* Endif */
  595.  
  596.   return(rc);                       /* Return to caller */
  597. }
  598.