/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution. 
\*************************************************************************/

/*
 * $Log: not supported by cvs2svn $
 * Revision 1.10  2006/03/16 16:29:43  soliday
 * Xuesong added the setfacl and getfacl commands to the Tcl os package.
 *
 * Revision 1.9  2004/04/30 19:12:06  soliday
 * Fixed issue when compiling on MacOSX
 *
 * Revision 1.8  2003/12/25 18:37:22  borland
 * Increased the size of the temp buffer used for edit_string from SDDS_MAXLINE
 * (1024) to 16384.  Should dynamically allocate this.
 *
 * Revision 1.7  2003/08/08 20:43:11  soliday
 * Updated code so that this is a dynamically loadable extension.
 *
 * Revision 1.6  2002/10/28 15:41:35  soliday
 * Fixed compiler warning.
 *
 * Revision 1.5  2002/08/14 20:44:39  soliday
 * Added Open License
 *
 * Revision 1.4  1999/09/08 15:13:04  soliday
 * Fixed bug with the editstring command. Inserting characters now works fine.
 *
 * Revision 1.3  1999/07/29 19:54:46  soliday
 * Added code to get around unix only command Tcl_GetOpenFile.
 *
 * Revision 1.2  1999/07/23 21:24:15  soliday
 * Added WIN32 support
 *
 * Revision 1.1  1999/06/10 19:37:16  soliday
 * Moved to oag tree.
 *
 * Revision 1.4  1999/06/08 20:47:01  soliday
 * Removed compiler warnings under Linux.
 *
 * Revision 1.3  1999/06/02 14:32:40  soliday
 * Fixed bug in tclEditString.  It now does not alter the input strings.
 *
 * Revision 1.2  1999/04/21 19:51:41  soliday
 * Added tclEditString
 *
 * Revision 1.1  1996/12/16 20:49:57  saunders
 * New extension for implementing os specific functions. Initially,
 * lockf is being implemented.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <tclOS.h>
#if defined(_WIN32)
#include <fcntl.h>
#include <io.h>
#include <sys/locking.h>
#define lockf(handle, mode, nbytes) _locking(handle, mode, nbytes)
#else
#include <sys/types.h>
#include <unistd.h>
#endif
#if defined(ACL_FOUND)
#include <sys/acl.h>
#endif
#include "mdb.h"
#include "SDDS.h"

/*****************************/
/* Local Function Prototypes */
/*****************************/


/******************************************
 *
 *****************************************/
int tclEditString (Tcl_Interp *interp, int argc, char *argv[]) {
  int i;
  Tcl_DString result;
  static char temp[16384];
  /* char *temp;*/

  if (argc < 4) {
    Tcl_SetResult(interp, "usage: os editstring <command sequence> <string> [<string>] ...",NULL);
    return TCL_ERROR;    
  }

  Tcl_DStringInit(&result);
  for (i = 3; i < argc; i++) {
    /* cp_str(&temp, argv[i]);*/
    strcpy(temp, argv[i]);
    if (!edit_string(temp, argv[2])) {
      Tcl_SetResult(interp, "os editstring: problem editing string",NULL);
      Tcl_DStringFree(&result);
      return TCL_ERROR;
    } else {
      Tcl_DStringAppendElement(&result, temp);
    }
  }
  Tcl_DStringResult(interp, &result);

  return TCL_OK;
}

/******************************************
 *
 *****************************************/
int tclOSLockf(Tcl_Interp *interp, int argc, char *argv[]) {

  char *fileId;
  char *function;
  int nfunction;
  char *size;
  int intnsize;
  long nsize;
  int fd;
#if defined(_WIN32)
  Tcl_Channel chan;
  int chanMode;
  long handle;
#else
  FILE *fp;
#endif
  
  if (argc != 5) {
    Tcl_SetResult(interp, "usage: os lockf <tclFileId> <function> <size>, returns TCL_OK, or TCL_ERROR with EAGAIN, EBADF, ECOMM, or EDEADLK",NULL);
    return TCL_ERROR;    
  }

  fileId = argv[2];
  function = argv[3];
  size = argv[4];

#if defined(_WIN32)
  chan = Tcl_GetChannel(interp, fileId, &chanMode);
  if (chan == (Tcl_Channel) NULL) {
    Tcl_SetResult(interp, "EBADF",NULL);
    return TCL_ERROR;
  } 
  if (Tcl_GetChannelHandle(chan, chanMode, 
			   (ClientData *) &handle)) {
    Tcl_SetResult(interp, "EBADF",NULL);
    return TCL_ERROR;
  }
  fd = _open_osfhandle(handle,O_BINARY|_O_RDWR);  
#else
  if (Tcl_GetOpenFile(interp,fileId,1,1,(void**)&fp) != TCL_OK) {
    Tcl_SetResult(interp, "EBADF",NULL);
    return TCL_ERROR;    
  }
  fd = fileno(fp);
#endif
  /* WIN32 defines a non-blocking lock in place of F_TLOCK (They should have the same effect)*/
  /* WIN32 defines a lock for writing in place of F_TEST (They may not have the same effect)*/
  if (!strcmp(function,"F_ULOCK")) {
    nfunction = 0;
  } else if (!strcmp(function,"F_LOCK")) {
    nfunction = 1;
  } else if (!strcmp(function,"F_TLOCK")) {
    nfunction = 2;
  } else if (!strcmp(function,"F_TEST")) {
    nfunction = 3;
  } else {
    Tcl_SetResult(interp, "os lockf: unknown function given",NULL);
    return TCL_ERROR;    
  }

  if (Tcl_GetInt(interp,size,&intnsize) != TCL_OK) {
    Tcl_SetResult(interp, "os lockf: size must be an integer",NULL);
    return TCL_ERROR;
  }
  nsize = intnsize;

  if (lockf(fd,nfunction,nsize) == -1) {
    switch (errno) {
    case EAGAIN:
      Tcl_SetResult(interp, "EAGAIN",NULL);      
      break;
    case EBADF:
      Tcl_SetResult(interp, "EBADF",NULL);      
      break;
#if !defined(_WIN32) && !defined(__APPLE__)
    case ECOMM:
      Tcl_SetResult(interp, "ECOMM",NULL);      
      break;
#endif
    case EDEADLK:
      Tcl_SetResult(interp, "EDEADLK",NULL);      
      break;
#if defined(_WIN32)
    case EACCES: /* WIN32 returns EACCESS in place of EAGAIN */
      Tcl_SetResult(interp, "EAGAIN",NULL);      
      break;
#endif
    default:
      Tcl_SetResult(interp, "os lockf: unknown return from lockf",NULL);
    }
    return TCL_ERROR;
  }

  return TCL_OK;
}

int tclOSSetfacl(Tcl_Interp *interp, int argc, char *argv[]) {
#if !defined(ACL_FOUND)
  Tcl_SetResult(interp,"os setfacl is not implemented on this operating system.",NULL);
  return TCL_ERROR;      
#else
#if defined(SOLARIS)
   aclent_t *aclpp=NULL;
   int cnt=0;
   int aclerr = 0;
   int which,i;
   
   if( argc != 4 ) {
      Tcl_SetResult(interp,"usage: os setfacl acl_list filename, returns TCL_OK, or TCL_ERROR with error messages",NULL);
      return TCL_ERROR;
   }
   aclpp = aclfromtext(argv[2],&cnt);
   if(acl(argv[3],SETACL,cnt,aclpp) == -1) {
      free(aclpp);
      switch(errno ) {
      case ENOENT:
          Tcl_SetResult(interp,"No Such file or directory!",NULL);
	  break;
      case EOPNOTSUPP:
          Tcl_SetResult(interp,"Operation not supported!",NULL);
	  break;
      default:	  
	  Tcl_SetResult(interp,"failed to set acl entries.",NULL);	  
	  break;
      }
      return TCL_ERROR;
   }
   free(aclpp);
   return TCL_OK;
#else
   acl_t acl;
   acl_type_t type;
   char *acl_input =NULL;
   char *fn=NULL;
   if (argc ==4 ) {
       acl_input =  argv[2];
       fn = argv[3];
       type = ACL_TYPE_ACCESS;
   } else if(argc ==5 && !strcmp(argv[2],"-d")) {
       acl_input = argv[3];
       fn = argv[4];
       type = ACL_TYPE_DEFAULT;
   } else {
       Tcl_SetResult(interp,"usage: os setfacl acl_list filename, or os setfacl -d acl_list dirname",NULL);
       return TCL_ERROR;      
   }
   acl = acl_from_text(acl_input);
   if(acl_set_file(fn,type,acl) == -1 ) {
       
       switch(errno ) {
       case ENOENT:
          Tcl_SetResult(interp,"No Such file or directory!",NULL);
	  break;
       case EOPNOTSUPP:
          Tcl_SetResult(interp,"Operation not supported!",NULL);
	  break;
       default:	  
	  Tcl_SetResult(interp,"failed to set acl entries.",NULL);	  
	  break;
      }
      acl_free(&acl);
      return TCL_ERROR;
   }
   acl_free(&acl);    
   return TCL_OK;
#endif
#endif
}



int tclOSGetfacl(Tcl_Interp *interp, int argc, char *argv[]) {
#if !defined(ACL_FOUND)
  Tcl_SetResult(interp,"os getfacl is not implemented on this operating system.",NULL);
  return TCL_ERROR;      
#else
    char *acltext = NULL;
    Tcl_DString result;
    Tcl_DStringInit(&result);
    if( argc !=3 ) {
      Tcl_SetResult(interp,"usage: os getfacl filename",NULL);
      return TCL_ERROR;
    }
#if defined(SOLARIS)
    int cnt=0;      
    aclent_t *aclpp=NULL;
    if( (cnt=acl(argv[2],GETACLCNT,0,NULL)) < 0) {
        switch(errno) {
        case ENOENT :
	  Tcl_SetResult(interp,"No such file or directory!",NULL);
	  break;
	case EOPNOTSUPP :
	  Tcl_SetResult(interp,"Operation not supported!",NULL);
	  break;
	default :
	  Tcl_SetResult(interp,"getfacl failed!",NULL); 
	  break;
	}    
	Tcl_DStringFree(&result);
	return TCL_ERROR;
    }	
    aclpp=(aclent_t*)malloc(sizeof(aclent_t)*cnt);
    acl(argv[2],GETACL,cnt,aclpp);
    acltext = acltotext(aclpp,cnt);
    free(aclpp);
    Tcl_DStringAppendElement(&result,acltext);
#else
    acl_t acl, dacl ;
    char *dacltext = NULL;
    struct stat st;
    acl = acl_get_file(argv[2],(acl_type_t)ACL_TYPE_ACCESS);
    if(!acl) {
        switch(errno) {
        case ENOENT :
	  Tcl_SetResult(interp,"No such file or directory!",NULL);
	  break;
	case EOPNOTSUPP :
	  Tcl_SetResult(interp,"Operation not supported!",NULL);
	  break;
	default :
	  Tcl_SetResult(interp,"getfacl failed!",NULL); 
	  break;
	}    
	Tcl_DStringFree(&result);
	return TCL_ERROR;
    }	
    acltext =  acl_to_text(acl,NULL);
    Tcl_DStringAppendElement(&result,acltext);
    acl_free(&acl);
    free(acltext);
    stat(argv[2],&st);
    if(S_ISDIR(st.st_mode)) {
       dacl =  acl_get_file(argv[2],(acl_type_t)ACL_TYPE_DEFAULT);
       if(!dacl) {
          Tcl_SetResult(interp,"getfacl failed!",NULL);
	  Tcl_DStringFree(&result);
	  return TCL_ERROR;
       }
       dacltext = acl_to_text(dacl,NULL);
       Tcl_DStringAppendElement(&result,dacltext);
       acl_free(&dacl);
       free(dacltext);
    }
#endif
    Tcl_DStringResult(interp,&result);
    return TCL_OK;
#endif
}

/******************************************
 *
 *****************************************/
int tclOSInit(Tcl_Interp *interp) {
  return TCL_OK;
}


