/*	utime.c
 *	V1.0			31-Jul-1995	IfN/Mey
 *
 * SLIGHTLY MODIFIED FOR SAMBA!
 *
 *	(c) Eckart Meyer, 1995			meyer@ifn.ing.tu-bs.de
 *+
 * VMS does not have utime() at all.
 * Here is a VMS implementation. Note that VMS does not have an
 * access time - we set both, the creation date and the modification
 * date to the modtime value.
 *
 * This routine uses local time clock values as input.
 *
 * The code is derived from Joe Medows' FILE utility.
 *-
 */


#if __alpha
#pragma nomember_alignment
#endif

#include <lib$routines.h>
#include <errno.h>
#include <time.h>
#include <ssdef.h>
#include <rmsdef.h>

#include <descrip.h>
#include <starlet.h>
#include <libdef.h>
#include <string.h>
#include <rms.h>
#include <fab.h>
#include <stdio.h>
#include <iodef.h>

/* since the atrdef struct is differently defined on the various
 * compiler versions, we have to define it ourself :-( */
/* #include <atrdef.h> */
#define ATR$C_CREDATE 17                /* 64 BIT CREATION DATE             */
#define ATR$C_REVDATE 18                /* 64 BIT REVISION DATE             */
#define ATR$C_ASCDATES 13               /* REVISION COUNT THRU EXP DATE IN ASCII  */
struct atrdef {
    unsigned short int atr$w_size;      /* SIZE OF ATTRIBUTE IN BYTES       */
    unsigned short int atr$w_type;      /* ATTRIBUTE TYPE CODE              */
    void *atr$l_addr;                   /* ADDRESS OF ATTRIBUTE TEXT        */
};

#include "fibdef.h"

struct utimbuf {
	time_t actime;
	time_t modtime;
};


/*
 *
 * use SHELL$TO_VMS / DECC$TO_VMS here:
 *
 *	decc$to_vms(char *unix_file_spec, int *action_routine,
 *		int wild_flag, int no_directory)
 *
 *	unix_file_spec		- a UNIX style file specification
 *	action routine		- action_routine
 *	wild_flag		- if 1, pass each file to action_routine.
 *				   only existing files are passed.
 *	no_directory		- 0: directory is allowed
 *				- 1: prevent expansion as directory string
 *				- 2: force into dir spec (??)
 *
 *	action_routine(char *vms_file_spec, int type)
 *
 *	vms_file_spec		- one (expanded, if wild_flag is set) vms
 *				  filespec.
 *	type			- 0: some strange "foreign" file...
 *				- 1: normal file
 *				- 2: directory file
 *
 * These Routines have been tested on Alpha with DEC C and on VAX
 * with VAXC. I don't have DEC C for VAX yet.
 */

#ifndef __DECC
#define decc$to_vms shell$to_vms
#endif

char *vms_make_name(char * __name);		/* SAMBA */

int decc$to_vms(const char * __unix_file_spec,
	void (__action_routine)(char * __s, int __type),
	int __wild_flag, int __no_directory);

static char vms_file[255];

static void vms_conv(char *s, int type)
{
	strcpy(vms_file,s);
}


static unsigned long vms_set_file_times(char *filespec, long *date)
{
  static struct FAB Fab;
  static struct NAM Nam;
  static struct fibdef Fib; /* short fib */
 
  static struct dsc$descriptor FibDesc =
    {sizeof(Fib),DSC$K_DTYPE_Z,DSC$K_CLASS_S,(char *)&Fib};
  static struct dsc$descriptor_s DevDesc =
    {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,&Nam.nam$t_dvi[1]};
  static char EName[NAM$C_MAXRSS];
  static char RName[NAM$C_MAXRSS];
  static struct dsc$descriptor_s FileName = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  static struct dsc$descriptor_s string = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  static short int DevChan;
  static short int iosb[4];

  static long int Cdate[2],Rdate[2],Edate[2],Bdate[2];
  static short int revisions;
 
  static long int i,j,status;
  static char *retval;

  static struct atrdef Atr[] = {
    {sizeof(Cdate),ATR$C_CREDATE,&Cdate[0]}, /* Creation date */
    {sizeof(Rdate),ATR$C_REVDATE,&Rdate[0]}, /* Revision date */
    {sizeof(revisions),ATR$C_ASCDATES,&revisions}, /* number of revisions */
    {0,0,0}
  } ;

    /* convert to VMS filespec */
    filespec = vms_make_name(filespec);		/* SAMBA */
    strcpy(vms_file,filespec);		/* preset */
    decc$to_vms(filespec, vms_conv, 0, 1);

/* printf("Filespec: \"%s\"\n",filespec); /**/
/* printf("     vms: \"%s\"\n",vms_file); /**/

    /* initialize RMS structures, we need a NAM to retrieve the FID */
    Fab = cc$rms_fab;
    Fab.fab$l_fna = vms_file ; /* name of file */
    Fab.fab$b_fns = strlen(vms_file);
    Fab.fab$l_nam = &Nam; /* FAB has an associated NAM */
    Nam = cc$rms_nam;
    Nam.nam$l_esa = EName; /* expanded filename */
    Nam.nam$b_ess = sizeof(EName);
    Nam.nam$l_rsa = RName; /* resultant filename */
    Nam.nam$b_rss = sizeof(RName);
 
    /* do $PARSE and $SEARCH here */
    status = sys$parse(&Fab);
    if (!(status & 1)) return(status);

    /* search for the file.. If none signal error */
    status = sys$search(&Fab);
    if (!(status & 1)) return(status);

      /* initialize Device name length, note that this points into the NAM
         to get the device name filled in by the $PARSE, $SEARCH services */
      DevDesc.dsc$w_length = Nam.nam$t_dvi[0];
 
      status = sys$assign(&DevDesc,&DevChan,0,0);
      if (!(status & 1)) return(status);
 
      FileName.dsc$a_pointer = Nam.nam$l_name;
      FileName.dsc$w_length = Nam.nam$b_name+Nam.nam$b_type+Nam.nam$b_ver;
 
      /* Initialize the FIB */
      for (i=0;i<3;i++)
        Fib.fib$r_fid_overlay.fib$w_fid[i]=Nam.nam$w_fid[i];
      for (i=0;i<3;i++)
        Fib.fib$r_did_overlay.fib$w_did[i]=Nam.nam$w_did[i];

      /* Use the IO$_ACCESS function to return info about the file */
      /* Note, used this way, the file is not opened, and the expiration */
      /* and revision dates are not modified */
      status = sys$qiow(0,DevChan,IO$_ACCESS,&iosb,0,0,
                        &FibDesc,&FileName,0,0,&Atr,0);
      if (!(status & 1)) return(status);
      status = iosb[0];
      if (!(status & 1)) return(status);
 
      /* Here is where we set the dates */

      Cdate[0] = date[0];
      Cdate[1] = date[1];
      Rdate[0] = date[0];
      Rdate[1] = date[1];

      /* note, part of the FIB was cleared by earlier QIOW, so reset it */
      Fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
      for (i=0;i<3;i++)
        Fib.fib$r_fid_overlay.fib$w_fid[i]=Nam.nam$w_fid[i];
      for (i=0;i<3;i++)
        Fib.fib$r_did_overlay.fib$w_did[i]=Nam.nam$w_did[i];
 
      /* Use the IO$_MODIFY function to change info about the file */
      /* Note, used this way, the file is not opened, however this would */
      /* normally cause the expiration and revision dates to be modified. */
      /* Using FIB$M_NORECORD prohibits this from happening. */
      status = sys$qiow(0,DevChan,IO$_MODIFY,&iosb,0,0,
                        &FibDesc,&FileName,0,0,&Atr,0);
      if (!(status & 1)) return(status);
 
      status = iosb[0];
      if (!(status & 1)) return(status);
 
      status = sys$dassgn(DevChan);
      if (!(status & 1)) return(status);
 
      return(1);
}


int vms_utime(const char *fname, const struct utimbuf *times)
{
	time_t t;
	unsigned long sts;
	struct tm tm;
	struct vectim_t {
		short int year;
		short int mon;
		short int mday;
		short int hour;
		short int min;
		short int sec;
		short int hundreds;
	} vectim;
	long date[2];

	if (fname == NULL) {
		errno = ENOENT;
		return(-1);
	}
/*
 * Convert UNIX clock value (local time) into a VMS time quadword.
 */
	time(&t);	
	if (times != NULL) t = times->modtime;
	tm = *localtime(&t);
	vectim.year = tm.tm_year;
	vectim.year += (tm.tm_year > 68) ? 1900 : 2000;
	vectim.mon = tm.tm_mon + 1;
	vectim.mday = tm.tm_mday;
	vectim.hour = tm.tm_hour;
	vectim.min = tm.tm_min;
	vectim.sec = tm.tm_sec;
	vectim.hundreds = 0;
	sts = lib$cvt_vectim(&vectim, date);
	if (!(sts&1)) {
		errno = EINVAL;
		return(-1);
	}
/*
 * Do the work...
 */
	sts = vms_set_file_times((char *)fname, date);
/* if (!(sts&1)) lib$signal(sts); /**/
	if (!(sts&1)) {
		errno = EACCES;
		if (sts == RMS$_FNF) errno = ENOENT;
		if (sts == RMS$_SYN) errno = ENOTDIR;
		return(-1);
	}
	return(0);
}



#if 0
main(int argc, char *argv[])
{
	long status;

	status = vms_utime(argv[1],NULL);

	printf("status = %d\n",status);
	if (status < 0) perror("Can't set time");
}
#endif
