/*
 *****************************************************************************
 *                                                                           *
 *  Copyright (C) 1989-1992 by                                               *
 *  Mark Pizzolato - INFO COMM, Redwood City, California  (415) 369-9366     *
 *                                                                           *
 *  Permission is hereby  granted for the reproduction of this software,     *
 *  on condition that this copyright notice is included in the reproduction, *
 *  and that such reproduction is not for purposes of profit or material     *
 *  gain.                                                                    *
 *                                                                           *
 *****************************************************************************
 */

/* batchnews command

	BATCHNEWS

	Processes lists of files and combines them into standard
	"rnews" format news batches.

	Format

	   BATCHNEWS filespec[,...]

	PARAMETER
			filespec[,...]
			Specifies one or more files to be batched.

	QUALIFIERS
		/BEFORE[=time]
			Specifies that files created before the specified
			time are to be processed.
		/SINCE[=time]
			Specifies that files created after the specified
			time are to be processed.
		/BATCHNAME=filespec
			Specifies the name template of the output batch file.
			If multiple sub batch files are created, the resulting
			files are actually of the form 'batchname'nnn, where nnn
			is the subfile number.  The default batch is written to
			he current SYS$OUTPUT device.  When writing a batch to
			SYS$OUTPUT only a single output file (not limited in
			size) will be produced.
		/SIZE=n
			Specifies that a new version of a batch file should
			be created when the current version exceeds 'n' bytes.
		/MAX_SIZE=n
			Specifies that all processing of input files should
			stop after a total of 'n' bytes have been written to
			batch files.
		/FILELIST=filespec
			Specifies that the names of files to be batched are
			contained in the specified file.  If file are specified
			both on the BATCHNEWS command line and also with a
			/FILELIST qualifier, the command line files are
			processed first, followed by those in the specified 
			file.  If batching is limited by MAX_SIZE while files
			still remain in the specified file list, a new version
			of the /FILELIST specified file is created which
			contains the remaining parts of the original file.
		/NPREFIX
			Causes the letter 'N' be prepended to each line of
			of the batched output file.
		/LOG
		/NOLOG
			Causes the names of the files processed and produced
			to be displayed.

**  MODIFICATION HISTORY:
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
*/

#ifdef __TYPES
#undef __TYPES
#endif

#ifdef __GNUC__
#define variant_union	volatile union
#define variant_struct	volatile struct
#endif

#ifndef NAKED_INCLUDES
#define NAKED_INCLUDES 0
#endif

#if NAKED_INCLUDES

#include starlet
#include clidef
#include climsgdef
#include chfdef
#include stsdef
#include descrip
#include rms

#include types
#include stddef
#include stdio
#include stdlib
#include string
#include ctype
#include errno
#include time
#include stat
#include unixio

#else	/* !NAKED_INCLUDES */

#include <starlet.h>
#include <clidef.h>	/* Comamnd Line Interpreter Codes		*/
#include <climsgdef.h>	/* Command Line Interpreter Message Codes	*/
#include <chfdef.h>
#include <stsdef.h>	/* Status Code Definitions			*/
#include <descrip.h>	/* VMS Descriptor Definitions			*/
#include <rms.h>	/* All RMS Definitions				*/

#if defined(__GNUC__)
  /*  problem is that both multinet and gnu_cc_include contain sys/types.h    *
   *  and the multinet version is missing some declarations (dev_t,off_t,...) *
   *  which are necessary when stat.h gets included, so we force GNU types.h  */
#  include <gnu_cc_include:[sys]types.h>
#elif defined(MULTINET)
#  include <sys/types.h>    /* take the Multinet (or other IP vendor) types.h */
#else
#  include <types.h>        /* take whatever we have */
  /* although TWG has types.h on sys subdirectory, due to a conflict
   * in declarations of off_t, ino_t and dev_t we have to take DEC's types.h */
#endif

#include <stddef.h>
#include <stdio.h>	/* Standard I/O Definitions			*/
#include <stdlib.h>
#include <string.h>	/* String Handling Routine Declarations		*/
#include <ctype.h>	/* Character Classifications			*/
#include <errno.h>	/* Error Codes					*/
#include <time.h>	/* Unix Time Definitions			*/
#include <unixio.h>
#include <unixlib.h>

#ifdef __GNUC__
#include <sys/stat.h>
#else
#include <stat.h>
#endif

#endif

#ifndef L_tmpnam
#define L_tmpnam	256	/* missing in gnu_cc_include  <stdio.h> */
#endif

#ifdef vaxc
/* note: viable for VAX C v2.3 and later; not for v2.2 or earlier */
#define PROTOTYPES	1
#endif

#ifndef	PROTOTYPES
#ifdef	__STDC__
#define	PROTOTYPES	1
#else
#define	PROTOTYPES	0
#endif
#endif

#if PROTOTYPES
#define __ARGS(args) args
#else
#define __ARGS(args) ()
#endif

extern int str$free1_dx();
extern int str$copy_r();
extern int str$copy_dx();

extern int cli$dispatch __ARGS((void));
extern int cli$present __ARGS((struct dsc$descriptor *));
extern int cli$get_value __ARGS((struct dsc$descriptor *,
                                 struct dsc$descriptor *, unsigned short *));
extern int cli$dcl_parse __ARGS((struct dsc$descriptor *, char *, int (*)(),
                                 int (*)(), struct dsc$descriptor *));

extern int lib$get_input __ARGS((void));
extern void lib$signal __ARGS((int, ...));
extern void lib$stop __ARGS((int, ...));
extern int lib$sig_to_ret __ARGS((struct chf$signal_array*, struct chf$mech_array*));
extern void lib$establish __ARGS((int (*exception_handler)(struct chf$signal_array*, struct chf$mech_array*)));

extern int lib$ediv(), lib$subx();
extern int lib$find_file __ARGS((const struct dsc$descriptor *,
				 struct dsc$descriptor *, void *, ...));
extern int lib$find_file_end __ARGS((void *));


#ifdef vaxc
#define c$ac(v)		&(v)
#else
#define c$ac(v)		c$rfi(v)
#endif

#define BUFBLOCKS	126	/* Disk Blocks Per I/O Buffer	*/

#define NULL_DEVICE "_NLA0:"

struct buffer
    {
    struct buffer *next;
    int size;
    char data[512*BUFBLOCKS];
    };

int verbose = 0;		/* Verbose mode - show whats up		*/
int max_size = 0x7fffffff;	/* Maximum Output Batch Size		*/
int tot_size = 0;		/* Accumulated Output written		*/
int max_tot_size = 0x7fffffff;	/* Maximum Accumulated Output written	*/
char *outname = NULL;		/* Output File Name			*/
int sub_num = 0;		/* Sub-Batch Number for Output file	*/
int cur_size = 0;		/* Size of Current Output		*/
int nprefix = 0;		/* Prefix mode - prefix 'N' to each line*/

int read_file(f, buf)
FILE *f;
struct buffer **buf;
    {
    if ((*buf) == NULL)
	*buf = calloc(1, sizeof(**buf));
    if (0 < ((*buf)->size = fread((*buf)->data, 1, sizeof((*buf)->data), f)))
	return((*buf)->size + read_file(f, &(*buf)->next));
    return(0);
    }

void write_file(f, size, buf)
  FILE **f;
  int size;
struct buffer *buf;
    {
    int ls, le, nllast;

    if (size == 0)
	{
	if (verbose)
	    fprintf(stderr, "empty file, skipped\n");
	return;
	}
    cur_size += size;
    if (verbose)
	fprintf(stderr, " -- %d bytes", size);
    fprintf(*f, "%s#! rnews %d\n", (nprefix ? "N" : "" ), size);
    nllast = 1;
    while (size > 0)
	{
	if (nprefix)
	    {
	    le = ls = 0;
	    while (le < buf->size)
		{
		if (buf->data[le] == '\n')
		    {
		    if (nllast)
			fwrite("N", 1, 1, *f);
		    fwrite(&buf->data[ls], 1, le-ls+1, *f);
		    nllast = 1;
		    ls = le + 1;
		    }
		++le;
		}
	    if (ls < buf->size)
		{
		if (nllast)
		    fwrite("N", 1, 1, *f);
		fwrite(&buf->data[ls], 1, buf->size-ls, *f);
		nllast = 0;
		}
	    }
	else
	    fwrite(buf->data, 1, buf->size, *f);
	size -= buf->size;
	buf = buf->next;
	}
    if (verbose)
	fprintf(stderr, " - Done.\n");
    if (cur_size >= max_size)
	{
	char tmpname[L_tmpnam];
	char curname[L_tmpnam];
	char *c;

	fgetname(*f, curname);
	c = strrchr(curname, ';');
	*c = '\0';
	sprintf(tmpname, "%s%03d", curname, ++sub_num);
	*f = freopen(NULL_DEVICE, "r", *f);
	rename(curname, tmpname);
	if (NULL == (*f = freopen(outname, "w", *f,"mbc=127")))
	    {
	    perror(outname);
	    exit(0);
	    }
	if (verbose)
	    fprintf(stderr, "Batch %s complete, %d bytes\n", tmpname, cur_size);
	cur_size = 0;
	}
    }

#define SS_FAILED(status)	(!((status)&STS$M_SUCCESS))
#define SS_OK(status)		(!SS_FAILED(status))

#define DLEN(x)  (x).dsc$w_length
#define DPTR(x)  (x).dsc$a_pointer

#define $DESCRIPTOR_D(x)  struct dsc$descriptor x = { 0, DSC$K_DTYPE_T, \
                          DSC$K_CLASS_D, 0 }

typedef struct {                  /* Quadword time structure */
   unsigned long int l0, l1;
   } uquad;

/* Forward Declarations */
void init_cli(char *, const char *);
void cli_qualifier_action(const char *, void (*action_routine)());
void cli_get_time_value(const char *, uquad *);
void cli_get_bool_value(const char *, int *);
void cli_get_int_value(const char *, int *);
void cli_get_string_value(const char *, char **);
static void usage();
struct dsc$descriptor *c$dsc();
struct dsc$descriptor *c$ldsc();
int *c$alloc_tmp(int);
int *c$rfi(int);

#ifdef	__GNUC__
#include <gnu_hacks.h>
GLOBALVALUEREF(char *,batchnewscmd);
#define batchnewscmd ((char *)batchnewscmd)
#else
globalvalue char * batchnewscmd;
#endif

int main(void)
    {
    struct buffer *buf = NULL;
    int size;
    struct stat statb;
    int cfile, context, status_value;
    $DESCRIPTOR_D(fdesc);
    $DESCRIPTOR_D(rdesc);
    $DESCRIPTOR_D(lastdesc);
    unsigned beforetim[2] = {0xffffffffUL, 0x7fffffffUL};
    time_t beforetime = 0x7fffffff;
    int sincetim[2] = {0, 0};
    time_t sincetime = 0;
    time_t vms_to_unix_time();
    FILE *file_list = NULL;
    char *file_list_name = NULL;
    char item_name[L_tmpnam] = "";

    lib$establish(lib$sig_to_ret);

    init_cli(batchnewscmd, "BATCHNEWS");

    cli_qualifier_action("HELP", usage);
    cli_get_time_value("BEFORE", (uquad *) beforetim);
    if (beforetim[1] != 0x7fffffff)
	beforetime = vms_to_unix_time(beforetim);
    cli_get_time_value("SINCE", (uquad *) sincetim);
    if ((sincetim[0]) || (sincetim[1]))
	sincetime = vms_to_unix_time(sincetim);
    cli_get_bool_value("LOG", &verbose);
    cli_get_bool_value("NPREFIX", &nprefix);
    cli_get_int_value("SIZE", &max_size);
    cli_get_int_value("MAX_SIZE", &max_tot_size);
    cli_get_string_value("FILELIST", &file_list_name);
    if (file_list_name)
	if (NULL == (file_list = fopen(file_list_name, "r", "mbc=64")))
	    {
	    perror(file_list_name);
	    exit(vaxc$errno);
	    }
    /*
     * Open the output batch file if specified.
     */
    cli_get_string_value("BATCHNAME", &outname);
    if (outname != NULL)
	{
	if (NULL == (stdout = freopen(outname, "a", stdout, "mbc=126", "mbf=2")))
	    {
	    perror(outname);
	    return(0);
	    }
	if (0 != fstat(fileno(stdout), &statb))
	    {
	    perror(outname);
	    fprintf(stderr, "Cannot Stat\n");
	    return(0);
	    }
	cur_size = statb.st_size;
	while (1)
	    {
	    char tmpname[L_tmpnam];
	    char curname[L_tmpnam];
	    char *c;

	    fgetname(stdout, curname);
	    c = strrchr(curname, ';');
	    *c = '\0';
	    sprintf(tmpname, "%s%03d", curname, sub_num+1);
	    if (-1 == stat(tmpname, &statb))
		break;
	    else
		++sub_num;
	    }
	}
    else
	max_size = 0x7fffffff;

    /* Assume the rest of the arguments are Logfiles to scan to report on */
    context = 0;
    cfile = 0;
    while (tot_size < max_tot_size)
	{
	if (cfile)
	    {
	    if (SS_OK(lib$find_file(&fdesc, &rdesc, &context, 0, 
					&lastdesc, &status_value, c$ac(0))))
		{
		strncpy(item_name, DPTR(rdesc), DLEN(rdesc));
		item_name[DLEN(rdesc)] = '\0';
		str$copy_dx(&lastdesc, &rdesc);
		}
	    else
		{
		cfile = 0;
		continue;
		}
	    }
	else
	    if (SS_OK(cli$get_value(c$dsc("FILENAME"), &fdesc, NULL)))
		{
		cfile = 1;
		context = 0;
		continue;
		}
	    else
		{
		cfile = 0;
		if (file_list)
		    if (fgets(item_name, sizeof(item_name), file_list))
			strtok(item_name, " \t\n\r\f");
		    else
			{
			fclose(file_list);
			file_list = NULL;
			break;
			}
		else
		    break;
		}
	if (NULL == (stdin = freopen(item_name, "r", stdin, "mbc=126", "mbf=2")))
	    {
	    perror(item_name);
	    stdin = fopen(NULL_DEVICE,"r");
	    continue;
	    }
	if (0 != fstat(fileno(stdin), &statb))
	    {
	    perror(item_name);
	    fprintf(stderr, "Cannot Stat\n");
	    continue;
	    }
	if (statb.st_ctime < sincetime)
	    continue;
	if (statb.st_ctime > beforetime)
	    continue;
	if (0 != (S_IFDIR&statb.st_mode))
	    {
	    if (verbose)
		fprintf(stderr, " - Skipping Directory File\n");
	    continue;
	    }
	if (verbose)
	    fprintf(stderr, "%s", item_name);
	size = read_file(stdin, &buf);
	write_file(&stdout, size, buf);
	tot_size += size;
	}
    if ((tot_size >= max_tot_size) && (sub_num != 0))
	{
	char tmpname[L_tmpnam];

	sprintf(tmpname, "%s%03d", outname, ++sub_num);
	fclose(stdout);
	rename(outname, tmpname);
	}
    if (file_list)
	{
	char new_list_name[L_tmpnam];
	char *cp;
	FILE *list2;
	int lines = 0;

	fgetname(file_list, new_list_name);
	if ( (cp = strrchr(new_list_name, ';')) )
	    *cp = '\0';
	
	if (verbose)
	    fprintf(stderr, " - Rewriting Revised Batch File List: %s\n",
			    new_list_name);
	if (NULL == (list2 = fopen(new_list_name, "w", "mbc=64", "mbf=2")))
	    {
	    perror(new_list_name);
	    return(0);
	    }
	while (fgets(item_name, sizeof(item_name), file_list))
	    {
	    ++lines;
	    fputs(item_name, list2);
	    }
	fclose(list2);
	fclose(file_list);
	if (verbose)
	    fprintf(stderr, " - %d lines written to updated Batch File List\n",
			    lines);
	}
    return(1);
    }

static void usage(void)
    {
    fprintf(stderr, "\n\tBATCHNEWS\n\n");
    fprintf(stderr, "\tProcesses lists of files and combines them into standard\n");
    fprintf(stderr, "\t\"rnews\" format news batches.\n");
    fprintf(stderr, "\n\tFormat\n\n");
    fprintf(stderr, "\t   BATCHNEWS filespec[,...]\n");
    fprintf(stderr, "\n\tPARAMETER\n");
    fprintf(stderr, "\t\t\tfilespec[,...]\n");
    fprintf(stderr, "\t\t\tSpecifies one or more files to be batched.\n");
    fprintf(stderr, "\n\tQUALIFIERS\n");
    fprintf(stderr, "\t\t/BEFORE[=time]\n");
    fprintf(stderr, "\t\t\tSpecifies that files created before the specified\n");
    fprintf(stderr, "\t\t\ttime are to be processed.\n");
    fprintf(stderr, "\t\t/SINCE[=time]\n");
    fprintf(stderr, "\t\t\tSpecifies that files created after the specified\n");
    fprintf(stderr, "\t\t\ttime are to be processed.\n");
    fprintf(stderr, "\t\t/BATCHNAME=filespec\n");
    fprintf(stderr, "\t\t\tSpecifies the name template of the output batch file.\n");
    fprintf(stderr, "\t\t\tIf multiple sub batch files are created, the resulting\n");
    fprintf(stderr, "\t\t\tfiles are actually of the form 'batchname'nnn, where nnn\n");
    fprintf(stderr, "\t\t\tis the subfile number.  The default batch is written to\n");
    fprintf(stderr, "\t\t\the current SYS$OUTPUT device.  When writing a batch to\n");
    fprintf(stderr, "\t\t\tSYS$OUTPUT only a single output file (not limited in\n");
    fprintf(stderr, "\t\t\tsize) will be produced.\n");
    fprintf(stderr, "\t\t/SIZE=n\n");
    fprintf(stderr, "\t\t\tSpecifies that a new version of a batch file should\n");
    fprintf(stderr, "\t\t\tbe created when the current version exceeds 'n' bytes.\n");
    fprintf(stderr, "\t\t/MAX_SIZE=n\n");
    fprintf(stderr, "\t\t\tSpecifies that all processing of input files should\n");
    fprintf(stderr, "\t\t\tstop after a total of 'n' bytes have been written to\n");
    fprintf(stderr, "\t\t\tbatch files.\n");
    fprintf(stderr, "\t\t/FILELIST=filespec\n");
    fprintf(stderr, "\t\t\tSpecifies that the names of files to be batched are\n");
    fprintf(stderr, "\t\t\tcontained in the specified file.  If file are specified\n");
    fprintf(stderr, "\t\t\tboth on the BATCHNEWS command line and also with a\n");
    fprintf(stderr, "\t\t\t/FILELIST qualifier, the command line files are\n");
    fprintf(stderr, "\t\t\tprocessed first, followed by those in the specified\n");
    fprintf(stderr, "\t\t\tfile.  If batching is limited by MAX_SIZE while files\n");
    fprintf(stderr, "\t\t\tstill remain in the specified file list, a new version\n");
    fprintf(stderr, "\t\t\tof the /FILELIST specified file is created which\n");
    fprintf(stderr, "\t\t\tcontains the remaining parts of the original file.\n");
    fprintf(stderr, "\t\t/NPREFIX\n");
    fprintf(stderr, "\t\t\tCauses the letter 'N' be prepended to each line of\n");
    fprintf(stderr, "\t\t\tof the batched output file.\n");
    fprintf(stderr, "\t\t/LOG\n");
    fprintf(stderr, "\t\t/NOLOG\n");
    fprintf(stderr, "\t\t\tCauses the names of the files processed and produced\n"); 
    fprintf(stderr, "\t\t\tto be displayed.\n");
    fprintf(stderr, "\n");
    }

int *c$_tmphead = (int *) 0;

/************************************************************************/

int *c$alloc_tmp(size)
  int size;
{
  int *c$_tmp;

  c$_tmp = (int *) malloc(size + 4);
  *c$_tmp = (int) c$_tmphead;
  c$_tmphead = c$_tmp;
  return(c$_tmphead + 1);
}

/************************************************************************/

struct dsc$descriptor *c$dsc(c$_string)
   char *c$_string;
{
   int *tmp;
   struct dsc$descriptor *c$_tmpdesc;

   while (c$_tmphead) {
      tmp = (int *)(*c$_tmphead);
      free(c$_tmphead);
      c$_tmphead = tmp;
      }                     
   c$_tmpdesc = (struct dsc$descriptor *) c$alloc_tmp(8);
   c$_tmpdesc->dsc$w_length = strlen(c$_string);
   c$_tmpdesc->dsc$b_dtype = DSC$K_DTYPE_T;
   c$_tmpdesc->dsc$b_class = DSC$K_CLASS_S;
   c$_tmpdesc->dsc$a_pointer = c$_string;
   return(c$_tmpdesc);
}

/************************************************************************/

struct dsc$descriptor *c$ldsc(c$_string,c$_length)
   char *c$_string;
   int c$_length;
{
   int *tmp;
   struct dsc$descriptor *c$_tmpdesc;

   while (c$_tmphead) {
      tmp = (int *)(*c$_tmphead);
      free(c$_tmphead); 
      c$_tmphead = tmp;
      }
   c$_tmpdesc = (struct dsc$descriptor *) c$alloc_tmp(8);
   c$_tmpdesc->dsc$w_length = c$_length;
   c$_tmpdesc->dsc$b_dtype = DSC$K_DTYPE_T;
   c$_tmpdesc->dsc$b_class = DSC$K_CLASS_S;
   c$_tmpdesc->dsc$a_pointer = c$_string;
   return(c$_tmpdesc);
}

/*
 *  c$rfi
 *
 *  Generate a reference to a temp integer value
 */

int *c$rfi(value)
  int value;
{
  int *tmp = c$alloc_tmp(4);

  *tmp = value;
  return(tmp);
}

/************************************************************************/

void cksts(status)
    int status;
{
    if (!(status & STS$M_SUCCESS))
	lib$signal(status);
}

void cli_qualifier_action(qualifier,action_routine)
   const char *qualifier;
   void (*action_routine)();
{
    int value;

    cli_get_bool_value(qualifier, &value);
    if (value)
	(*action_routine)();
}

void cli_get_bool_value(qualifier,value)
   const char *qualifier;
   int *value;
{
    int status;

    *value = 0;
    status = cli$present(c$dsc(qualifier));
    if (status == CLI$_PRESENT || status == CLI$_LOCPRES)
      *value = 1;
    else if (status == CLI$_ABSENT || status == CLI$_NEGATED ||
             status == CLI$_LOCNEG)
      *value = 0;
}

void cli_get_int_value(qualifier,value)
   const char *qualifier;
   int *value;
{
    unsigned long int status;
    $DESCRIPTOR_D(rdesc);
    extern int ots$cvt_tz_l();
    extern int ots$cvt_tu_l();
    extern int ots$cvt_to_l();
    int (*rtn)();
    long int num;

    if (cli$present(c$dsc(qualifier)) == CLI$_PRESENT)
	{
	cksts(status = cli$get_value(c$dsc(qualifier),&rdesc,NULL));
	rtn = ots$cvt_tu_l;
	if ((DLEN(rdesc) > 2) && (((char *)(rdesc.dsc$a_pointer))[0] == '%'))
	    {
	    if (((char *)(rdesc.dsc$a_pointer))[1] == 'X')
		rtn = ots$cvt_tz_l;
	    if (((char *)(rdesc.dsc$a_pointer))[1] == 'O')
		rtn = ots$cvt_to_l;
	    }
	cksts(status = (*rtn)(&rdesc, &num, 4, 0));
	*value = num;
	}
}

void cli_get_string_value(qualifier,value)
   const char *qualifier;
   char **value;
{
    unsigned long int status;
    $DESCRIPTOR_D(rdesc);
    $DESCRIPTOR_D(vdesc);
    long int num;

    if (cli$present(c$dsc(qualifier)) == CLI$_PRESENT)
	{
	cksts(status = cli$get_value(c$dsc(qualifier),&rdesc,NULL));
	num = 1 + DLEN(rdesc);
	str$copy_r(&vdesc, &num, DPTR(rdesc));
	*value = DPTR(vdesc);
	(*value)[DLEN(rdesc)] = '\0';
	str$free1_dx(&rdesc);
	}
}

void cli_get_time_value(qualifier,value)
   const char *qualifier;
   uquad *value;
{
    char tmp1[64], tmp2[64];
    unsigned long int status;
    $DESCRIPTOR_D(rdesc);
    uquad restim = { 0, 0 };
   
    if (cli$present(c$dsc(qualifier)) == CLI$_PRESENT)
	{
	status = cli$get_value(c$dsc(qualifier),&rdesc,NULL);
	if (status == CLI$_ABSENT)
	    cksts(sys$gettim(&restim));
	else
	    {
	    cksts(status);
	    strncpy(tmp1,DPTR(rdesc),DLEN(rdesc));
	    tmp1[DLEN(rdesc)] = '\0';
	    str$free1_dx(&rdesc);
	    if (!strcmp(tmp1,"TODAY"))
		{
		cksts(sys$asctim(0,c$ldsc(tmp2,sizeof(tmp2)),0,0));
		*(strchr(tmp2,':')-3) = '\0';
		sprintf(tmp1,"%s 00:00:00.00",tmp2);
		}
	    cksts(sys$bintim(c$dsc(tmp1),&restim));
	    }
	*value = restim;
	}
}

void init_cli(char *table, const char *name)
{
    $DESCRIPTOR_D(cmd);
    int i;
  
    cksts(cli$get_value(c$dsc("$VERB"),&cmd,NULL));

    if (memcmp(DPTR(cmd),name,DLEN(cmd)) == 0)
	return /*1?*/;    /* the command must have been properly defined! */
    /* this code assumes that the verb is shorter than the foreign
	symbol (which includes device:[dir], so, should be reasonable) */
    if ((DLEN(cmd) == 3) && 
	(0 == memcmp("RUN",DPTR(cmd),3)))
	{
	i = strlen(name);
	str$copy_r(&cmd, &i, name);
	}
    else
	{
	cksts(cli$get_value(c$dsc("$LINE"),&cmd,NULL));

	for (i = 0; (i < DLEN(cmd)) &&
		    (((char *)(cmd.dsc$a_pointer))[i] != ' ') &&
		    (((char *)(cmd.dsc$a_pointer))[i] != '/'); ++i)
	    ((char *)(cmd.dsc$a_pointer))[i] = (i < strlen(name))?name[i]:' ';
	}

    cksts(cli$dcl_parse(&cmd,table,lib$get_input,lib$get_input,NULL));
}

time_t vms_to_unix_time(time)
int time[2];
{
static int ubase[2] = {0x4beb4000,0x007c9567}; /* Unix Time Base 1/1/70 */
int     utime[2];
time_t  result;
int     divisor = 10000000;
int     remainder;

    lib$subx(time, ubase, utime);
    lib$ediv(&divisor, utime, &result, &remainder);
    return(result);
}
