#include <stdio.h>
#include <descrip.h>
#include <ssdef.h>
#include <ctype.h>
#include <lib$routines.h>
#include <libdtdef.h>
#include <starlet.h>
#include "bboard.h"

/* We've hit a fatal error. Print it, log it, tell OPCOM, then run away */

void critical_error(char * err_line)
{
    printf(err_line+2);
    send_log_file(err_line);
    send_opcom(err_line+2);
    exit(SS$_ABORT);
}

/* Intermediate error. Don't leave unless we're just starting up */

void inter_error(char * log_flag, char * err_line, char * node, char * user)
{
    char temp[256];

    printf(err_line);
    sprintf(temp, "%s%s", log_flag, err_line);
    send_log_file(temp);
    if (node) { 			/* Do we have someone to talk to? */
	send_msg(node, user, err_line); /* Yup, just tell them */
    } else {
	send_opcom(err_line);		/* Nope, must be start up, so tell */
	exit(SS$_ABORT);		/*  OPCOM then run screaming */
    }
}

/* Build a String Descriptor for the passed string */

void str_desc(struct dsc$descriptor * desc, char * str)
{
    desc->dsc$a_pointer = str;
    desc->dsc$w_length = strlen(str);
    desc->dsc$b_dtype = DSC$K_DTYPE_T;
    desc->dsc$b_class = DSC$K_CLASS_S;
}

/* Destroy all non-isgraph() characters */

char * strip(char * s)
{
    register char * so;
    register char * si;

    for (si = so = s; *si; si++) {
	if (isgraph(*si)) {
	    *so++ = *si;
	}
    }
    *so = '\0';
    return s;
}

/* Terminate a string after last non-blank character */

char * trim_end(char * s)
{
    register char * si;

    for (si = s+strlen(s)-1; isspace(*si) && (si >= s); si--);
    *(++si) = '\0';
}

/* Convert "\t" to " " and "  " to " ". Also, lose initial and
    trailing blanks */

char * scrunch(char * s)
{
    register char * si;
    register char * so;

    for (si = s; *si && (*si == ' ' || *si == '\t'); si++);

    for (so = s; *si; si++) {
	if (*si == '\t') {
	    *si = ' ';
	}
	if (*si == ' ' && *(si+1) == ' ') {
	    continue;
	}
	*so++ = *si;
    }
    *so = '\0';

    trim_end(s);

    return (s);
}

/* Upper-case a string */

char * strupr(char * s)
{
    register char *si;

    for (si = s; *si; si++) {
	*si = toupper(*si);
    }
    return s;
}

/* Lower-case a string */

char * strlwr(char * s)
{
    register char * si;

    for (si = s; *si; si++) {
	*si = tolower(*si);
    }
    return s;
}

/* Strip '"' from a string */

char * remove_quotes(char * s)
{
    register char * si;
    register char * so;

    for (so = si = s; *si; si++) {
	if (*si == '"') {
	    continue;
	}
	*so++ = *si;
    }
    *so = '\0';
    return(s);
}

/* Some wierd time handling routines... */

/* Fill a SYSTIM buffer (long[2]) */

long * get_time(long * buf)
{
    sys$gettim(buf);
    return(buf);
}

/* Use SYS$NUMTIM to convert a SYSTIM to the parsed-out NUMTIM format */

struct $NUMTIM * get_numtim(struct $NUMTIM * num, long * buf)
{
    sys$numtim(num, buf);
    return (num);
}

/* Figure out the start and end dates for a particular interval */

long * start_end_time(char delt_type, long * buf_start, long * buf_end)
{
    long delt;				/* Delta time (sort of) */
    long op;				/* Operation to do (passed by ref) */
    SYSTIM temp_time;			/* Temporary time buffer */
    struct $NUMTIM t_numtim;		/* NUMTIM buffer for Months */

    switch (delt_type) {		/* What kind of interval? */

    /* Build a week */
	case 'w':
	case 'W':

	    /* We need to find out the current day of week */

	    op = LIB$K_DAY_OF_WEEK;

	    /* Ask LIB$ to stuff it in delt */

	    lib$cvt_from_internal_time(&op, &delt, buf_start);

	    /* Our weeks start on Sunday */

	    if (delt != 7L) {		/* If it's not Sunday, adjust it */
		op = LIB$K_DELTA_DAYS;
		lib$cvt_to_internal_time(&op, &delt, temp_time);
		lib$sub_times(buf_start, temp_time, buf_start);
	    }

	    /* Now let's find out the beginning of the next period */

	    op = LIB$K_DELTA_WEEKS;
	    delt = 1L;
	    lib$cvt_to_internal_time(&op, &delt, temp_time);
	    lib$add_times(buf_start, temp_time, buf_end);
	    break;

    /* Build a month */
	case 'm':
	case 'M':

	    /* We need to find out the current day of the month */

	    op = LIB$K_DAY_OF_MONTH;

	    /* Ask LIB$ to stuff it in delt */

	    lib$cvt_from_internal_time(&op, &delt, buf_start);

	    /* If we're not on day 1 of the month, find the beginning */

	    if (--delt) {
		op = LIB$K_DELTA_DAYS;
		lib$cvt_to_internal_time(&op, &delt, temp_time);
		lib$sub_times(buf_start, temp_time, buf_start);
	    }

	    /* Convert the beginning of the month to NUMTIM format */

	    sys$numtim(&t_numtim, buf_start);

	    /* Now let's go forward a month */

	    if (++t_numtim.month > 12) {
		t_numtim.month = 1;	/* Crossed a year boundary */
		t_numtim.year++;
	    }

	    /* But we want it back in SYSTIM format */

	    lib$cvt_vectim(&t_numtim, buf_end);
	    break;
    }

    /* Now that we have the current beginning and the beginning of the next */
    /* period, we need to find the current end by subtracting one day from  */
    /* the next beginning. */

    delt = 1L;
    op = LIB$K_DELTA_DAYS;
    lib$cvt_to_internal_time(&op, &delt, &temp_time);
    lib$sub_times(buf_end, temp_time, buf_end);

    /* And we're all done. Whew! */

    return (buf_start);
}

/* Read a \n delimited string and convert the \n to a \0.
   Also, perform tab expansion */

static char * detab(char * s);

int fgets_lcl(char * s, int len, FILE * f)
{
    char * t_status;

    /* First, let's actually read the line */

    t_status = fgets(s, len, f);

    /* If it worked, we got back (s) */

    if (t_status) {
	s[strlen(s)-1] = '\0';          /* Replace \n with \0 */
	detab(s);			/* And expand tabs */
    } else {
	s[0] = '\0';                    /* Error. Zap the string */
    }
    return (int) t_status;		/* And let them know what happened */
}

/* Expand all '\t' characters to the correct number of spaces */

static char * detab(char * s)
{
    char * so;
    char * si;
    int i;

    so = xmalloc(512);			/* Hopefully this will be big enough */

    for (si = s, i = 0; *si; si++) {	/* Scan through the input string */
	if (*si != '\t') {              /* If it's not a tab, just copy it */
	    so[i++] = *si;
	    continue;
	}
	so[i++] = ' ';                  /* That's at least one space */
	for (; i & 7; i++) {		/* Now just keep going until we've */
	    so[i] = ' ';                /*  written to the next multiple of 8 */
	}
    }
    so[i] = '\0';                       /* Terminate the string (finally) */
    strcpy(s, so);			/* Copy it back */
    xfree(so);				/* And release some storage */

    return (s);
}

/* Replacements for malloc() and free() for direct VMS virtual memory
   support
*/


#define LIB$K_VM_FIRST_FIT	1
#define LIB$K_VM_QUICK_FIT	2
#define LIB$K_VM_FREQ_SIZES	3
#define LIB$K_VM_FIXED		4
#define LIB$M_VM_BOUNDARY_TAGS	1
#define LIB$M_VM_GET_FILL0	2
#define LIB$M_VM_GET_FILL1	4
#define LIB$M_VM_FREE_FILL0	8
#define LIB$M_VM_FREE_FILL1	16
#define LIB$M_VM_EXTEND_AREA	32

static unsigned zone_id = 0;

void * xmalloc(unsigned size)
{
    static unsigned zone_init = FALSE;
    static unsigned alloc_type;
    static unsigned alloc_arg;
    static unsigned alloc_flags;

    void * base;
    int status;

    if (!zone_init) {			/* first time through? */
	alloc_type = LIB$K_VM_QUICK_FIT;
	alloc_arg = 8;
	alloc_flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 |
		LIB$M_VM_FREE_FILL0;
	status = lib$create_vm_zone(&zone_id, &alloc_type, &alloc_arg,
		&alloc_flags, 0, 0, 0, 0, 0, 0);
	zone_init = TRUE;
	if (!(status & 1)) {
	    report_error("Create_Vm_Zone", status);
	}
    }

    status = lib$get_vm(&size, &base, &zone_id);
    if (!(status & 1)) {
	report_error("Lib$Get_Vm failed", status);
    }

    return(base);
}

void xfree(void * mem)
{
    int status;

    status = lib$free_vm(0, &mem, &zone_id);
    if (!(status & 1)) {
	report_error("Lib$Free_Vm failed", status);
    }
}
