The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*********************************************************************
 * MSDOS/Attrib.xs
 *
 * Copyright 1996,1997 Christopher J. Madsen
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the same terms as Perl itself.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either the
 * GNU General Public License or the Artistic License for more details.
 *
 * XSUBs to get and set MS-DOS file attributes under OS/2 or Win32
 *********************************************************************/

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#ifdef __EMX__ /* OS/2 */
  #define INCL_DOSERRORS    /* API error codes */
  #define INCL_DOSFILEMGR   /* File Manager values */
  #include <os2.h>
#else /* WIN32 */
  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
  #define FILE_READONLY   FILE_ATTRIBUTE_READONLY
  #define FILE_HIDDEN     FILE_ATTRIBUTE_HIDDEN
  #define FILE_SYSTEM     FILE_ATTRIBUTE_SYSTEM
  #define FILE_DIRECTORY  FILE_ATTRIBUTE_DIRECTORY
  #define FILE_ARCHIVED   FILE_ATTRIBUTE_ARCHIVE
  typedef DWORD ULONG;
#endif /* WIN32 */

/*********************************************************************
 * Tell Perl about the FILE_ constants:
 *
 * Input:
 *   name:  The constant to return
 *
 * Returns:
 *   The value of the constant
 *   Sets errno and returns 0 if invalid name
 *********************************************************************/

static unsigned int
constant(const char *name)
{
  errno = 0;

  if (strncmp(name,"FILE_",5)) goto not_here;
  name += 5;                    /* Skip over FILE_ */

  if (strEQ(name, "READONLY"))
    return FILE_READONLY;
  if (strEQ(name, "HIDDEN"))
    return FILE_HIDDEN;
  if (strEQ(name, "SYSTEM"))
    return FILE_SYSTEM;
  if (strEQ(name, "DIRECTORY"))
    return FILE_DIRECTORY;
  if (strEQ(name, "ARCHIVED"))
    return FILE_ARCHIVED;
  if (strEQ(name, "CHANGEABLE")) /* All changeable attributes */
    return FILE_READONLY|FILE_HIDDEN|FILE_SYSTEM|FILE_ARCHIVED;

 not_here:
  errno = EINVAL;
  return 0;
} /* end constant */

/*********************************************************************
 * Get the attributes of a file or directory:
 *
 * Input:
 *   attribs:  A six byte buffer to store the attributes in
 *   path:     The pathname to get attributes for
 *
 * Output:
 *   attribs:
 *     A five character string "RHSAD"
 *       Each letter is replaced by an underscore ('_') if the file
 *       does not have the corresponding attribute.
 *     The empty string if an error occured
 *********************************************************************/

static void
get_attribs(char* attribs, const char* path)
{
#ifdef __EMX__
  FILESTATUS3  buf;             /* File info buffer */
  APIRET       rc;              /* Return code */

  rc = DosQueryPathInfo(path, 1 /* get file info */, &buf, sizeof(buf));

  if (rc != 0) {
    if (rc == ERROR_PATH_NOT_FOUND)
      errno = ENOENT;
    else
      errno = EINVAL;
    attribs[0] = '\0';
    return;
  }

  attribs[0] = ((buf.attrFile & FILE_READONLY)  ? 'R' : '_');
  attribs[1] = ((buf.attrFile & FILE_HIDDEN)    ? 'H' : '_');
  attribs[2] = ((buf.attrFile & FILE_SYSTEM)    ? 'S' : '_');
  attribs[3] = ((buf.attrFile & FILE_ARCHIVED)  ? 'A' : '_');
  attribs[4] = ((buf.attrFile & FILE_DIRECTORY) ? 'D' : '_');
  attribs[5] = '\0';

#else /* WIN32 */
  DWORD  rc;                    /* Return code */

  rc = GetFileAttributes(path);

  if (rc == 0xFFFFFFFF) {
    rc = GetLastError();
    if ((rc == ERROR_PATH_NOT_FOUND) || (rc == ERROR_FILE_NOT_FOUND))
      errno = ENOENT;
    else
      errno = EINVAL;
    attribs[0] = '\0';
    return;
  }
  attribs[0] = ((rc & FILE_ATTRIBUTE_READONLY)  ? 'R' : '_');
  attribs[1] = ((rc & FILE_ATTRIBUTE_HIDDEN)    ? 'H' : '_');
  attribs[2] = ((rc & FILE_ATTRIBUTE_SYSTEM)    ? 'S' : '_');
  attribs[3] = ((rc & FILE_ATTRIBUTE_ARCHIVE)   ? 'A' : '_');
  attribs[4] = ((rc & FILE_ATTRIBUTE_DIRECTORY) ? 'D' : '_');
  attribs[5] = '\0';
#endif /* WIN32 */
} /* end get_attribs */

/*********************************************************************
 * Set the attributes of a file or directory:
 *
 * This is intended for internal use only; the .pm file defines a
 * set_attribs function with a more flexible interface.
 *
 * Input:
 *   path:   The pathname to set the attributes for
 *   clear:  Bitmask of attributes to be removed
 *   set:    Bitmask of attributes to be added
 *
 * Note:
 *   CLEAR is applied before SET; therefore, an attribute in both SET
 *   and CLEAR will be set, not cleared.
 *
 * Returns:
 *   True if success
 *   False if error
 *********************************************************************/

static bool
_set_attribs(const char* path, ULONG clear, ULONG set)
{
#ifdef __EMX__
  FILESTATUS3  buf;             /* File info buffer */
  APIRET       rc;              /* Return code */

  rc = DosQueryPathInfo(path, 1 /* get file info */, &buf, sizeof(buf));

  if (rc == 0) {
    buf.attrFile &= ~clear;
    buf.attrFile |= set;
    rc = DosSetPathInfo(path, 1 /* set file info */, &buf, sizeof(buf),
                        0 /* can return immediately */);
  }

  if (rc != 0) {
    if (rc == ERROR_PATH_NOT_FOUND)
      errno = ENOENT;
    else if (rc == ERROR_SHARING_VIOLATION)
      errno = EACCES;
    else
      errno = EINVAL;
    return 0;                   /* Failure */
  } /* end if error */

  return 1;                     /* Success */

#else /* WIN32 */
  DWORD rc;                     /* Return code */

  rc = GetFileAttributes(path);

  if (rc == 0xFFFFFFFF)
    rc = 0;                     /* signal error */
  else {
    rc &= ~clear;
    rc |= set;
    rc = SetFileAttributes(path, rc);
  }

  if (rc == 0) {
    rc = GetLastError();
    if ((rc == ERROR_PATH_NOT_FOUND) || (rc == ERROR_FILE_NOT_FOUND))
      errno = ENOENT;
    else if ((rc == ERROR_ACCESS_DENIED) || (rc == ERROR_SHARING_VIOLATION))
      errno = EACCES;
    else
      errno = EINVAL;
    return 0;                   /* Failure */
  } /* end if error */

  return 1;                     /* Success */
#endif /* WIN32 */
} /* end _set_attribs */

MODULE = MSDOS::Attrib		PACKAGE = MSDOS::Attrib

PROTOTYPES: ENABLE

unsigned int
constant(name)
	char *	name

char *
get_attribs(path)
	char *	path
  CODE:
    char attribs[6];
    get_attribs(attribs, path);
    RETVAL = attribs;
  OUTPUT:
    RETVAL

bool
_set_attribs(path, clear, set)
	char *		path
	unsigned long	clear
	unsigned long	set

# Local Variables:
# tmtrack-file-task: "MSDOS::Attrib.xs"
# End: