/*
 *	  -------------------------------------------------------
 *        Neither  York  University,   Department  of   Computer
 *        Science   nor   the  authors assume any responsibility
 *        for the use or reliability of this software.
 *
 *        Copyright (C) 1986, York University
 *                            Department of Computer Science
 *
 *        General permission to copy  or  modify,  but  not  for
 *        profit,  is  hereby granted, provided  that  the above
 *        copyright notice is included  and  reference  made  to
 *        the fact that reproduction  privileges  were   granted
 *        by the York University, Department of Computer Science.
 *	  -------------------------------------------------------
 *
 *	  Written by: Edward Fung and James P. Lewis
 *		      Department of Computer Science
 *		      York University
 *		      1984, 1985, 1986
 *
 */

/*
 * Facility:  Bulletin
 *
 * Environment:  User mode, non-privileged code.
 *
 * Modified by:
 *
 * 1-000 - EF   ??-???-1984
 * 2-000 - JPL  ??-???-1984
 * 3-000 - JPL  01-JAN-1986
 * 4-000 - JPL  10-JUL-1986
 *
 */

#module bullmisc

#include        ctype.h
#include        descrip.h
#include	file.h
#include        iodef.h
#include	jpidef.h
#include        psldef.h
#include        rms.h
#include        rmsdef.h
#include	smgdef.h
#include        ssdef.h
#include        stdio.h
#include	stsdef.h
#include        "bull.h"
#include        "climsgdef"
#include	"lbrdef"
#include	"libclidef"
#include        "lnmdef"
#include        "syidef"

struct	lst_struct {
	short	buf_len;
	short	item_cd;
	long	*buf_addr;
	long	*retlen_addr;
};

static  short   chan;

init_io()
{
	$DESCRIPTOR(dev, "SYS$INPUT:");
        
	/*
	 * Get a channel for I/O.
	 */

        if (!chan)
                return(sys$assign(&dev, &chan, 0, 0));
}

canc_io()
{
	sys$cancel(chan);	/* Cancel pending I/O. */
 	sys$dassgn(chan);	/* Cancel a channel for I/O. */
}

setup_scr()
{                                         
	long	scr_abort();

	/*
	 * Create a pasteboard, its associated virtual displays,
	 * and keyboard.
	 */

	smg$create_pasteboard(&pstbrd_id, 0, &BRD_LEN, &BRD_WID, 0);

        smg$create_virtual_display(&LEGND_LEN, &BRD_WID, &legnd_id, 0, 0, 0);
        smg$paste_virtual_display(&legnd_id, &pstbrd_id, &LEGND_POS, &1);

        smg$create_virtual_display(&BULL_LEN, &BRD_WID, &bull_id, 0, 0, 0);
        smg$paste_virtual_display(&bull_id, &pstbrd_id, &BULL_POS, &1);

        smg$create_virtual_display(&TXT_LEN, &BRD_WID, &txt_id, 0, 0, 0);
        smg$paste_virtual_display(&txt_id, &pstbrd_id, &TXT_POS, &1);

        smg$create_virtual_display(&BOT1_LEN, &BRD_WID, &bot1_id, 0, 0, 0);
        smg$paste_virtual_display(&bot1_id, &pstbrd_id, &BOT1_POS, &1);

        smg$create_virtual_display(&BOT2_LEN, &BRD_WID, &bot2_id, 0, 0, 0);
        smg$paste_virtual_display(&bot2_id, &pstbrd_id, &BOT2_POS, &1);

        smg$create_virtual_keyboard(&keybrd_id, 0, 0, 0);
	smg$set_out_of_band_asts(&pstbrd_id, &LIB$M_CLI_CTRLY, &scr_abort, 0);
}

setup_lin()
{
	long	lin_abort();

	init_io();	/* Get a channel for I/O. */

	/*
	 * Set CTRL-Y interrupt ast. 
	 */

        lib$disable_ctrl(&LIB$M_CLI_CTRLY, &0); 
	sys$qiow(0, chan, IO$_SETMODE + IO$M_CTRLYAST, 0, 0, 0, lin_abort, 
		 0, PSL$C_SUPER, 0, 0, 0);
}

scr_abort()
{
	smg$cancel_input(&keybrd_id);	/* Cancel pending I/O. */
	sys$canexh(0);	/* Cancel exit handler. */
        smg$delete_pasteboard(&pstbrd_id, &1);	/* Delete a pasteboard. */
	smg$delete_virtual_keyboard(&keybrd_id);	/* reset keyboard */
	sys$exit(SS$_NORMAL);
}                             

lin_abort()
{
	canc_io();	/* Cancel I/O. */
	sys$canexh(0);	/* Cancel exit handler. */
	lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0);	/* Enable CTRL-Y. */
	sys$exit(SS$_NORMAL);
}

exit_handler(actn_rtn, arg)

long    (*actn_rtn)(),
	*arg[];
{
        struct  exit_blk_struct  *exit_blk;

	/*
	 *      Establish an exit handler.
	 */

        exit_blk = (struct exit_blk_struct *) 
		   malloc(sizeof(struct exit_blk_struct));
        
        exit_blk->nxt = 0;
        exit_blk->exit_rtn = actn_rtn;
        exit_blk->num_arg = 2;
        exit_blk->stat_addr = malloc(sizeof(long));
        exit_blk->arg = (long *) arg;

        sys$dclexh(exit_blk);
}

scr_updbull(status, arg)

char    *arg[];
long    *status;
{
        long    one_scr,
		ovrflw = FALSE;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;
        struct  bull_lst_struct   *bull_lst;

        bull_lst = (struct bull_lst_struct *) arg[0]; 
	lnk_lst = (struct lnk_lst_struct *) arg[1];
        one_scr = (long *) arg[2]; 

	/*
	 * Restore screen.
	 */

        if (*status & SS$_NORMAL) {
                if (one_scr) {
                        smg$delete_virtual_display(&legnd_id);
                        smg$delete_pasteboard(&pstbrd_id, &0);
                }
                else    smg$delete_pasteboard(&pstbrd_id, &1);

                smg$delete_virtual_keyboard(&keybrd_id);
        }

	/*
	 * Record read and unread bulletin messages.
	 */

	if (bull_lst->file == -1)	/* Before trying. */
		sys$exit(BULL_CANTUPD);

        lib$disable_ctrl(&LIB$M_CLI_CTRLY, &0); 

        for (cur = lnk_lst->dwn; cur != lnk_lst; cur = cur->dwn) {
                ovrflw |= upd_bull(bull_lst->file,
				   bull_lst->bull_indx[cur->presnt_lib], 
			 	   bull_lst->bull_nam[cur->presnt_lib], 
                         	   &bull_lst->lst_pos[cur->presnt_lib], cur); 
        }

        lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0);

	if (ovrflw)
		sys$exit(BULL_UPDOVRFLW);
	else	sys$exit(*status);
}

lin_updbull(status, arg)

char    *arg[];
long    *status;
{
	long	ovrflw = FALSE;
        struct  lnk_lst_struct	*cur,
				*lnk_lst;
        struct  bull_lst_struct	*bull_lst;

        bull_lst = (struct bull_lst_struct *) arg[0]; 
	lnk_lst = (struct lnk_lst_struct *) arg[1];

        if (*status & SS$_NORMAL) 
		canc_io();

	/*                      
 	 * Record read and unread bulletin messages.
	 */

	if (bull_lst->file == -1)	/* Before trying. */
		sys$exit(BULL_CANTUPD);

        lib$disable_ctrl(&LIB$M_CLI_CTRLY, &0); 

        for (cur = lnk_lst->dwn; cur != lnk_lst; cur = cur->dwn) {
                ovrflw |= upd_bull(bull_lst->file,
				   bull_lst->bull_indx[cur->presnt_lib],
			 	   bull_lst->bull_nam[cur->presnt_lib], 
			 	   &bull_lst->lst_pos[cur->presnt_lib], cur); 
        }

        lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0); 

	if (ovrflw)
		sys$exit(BULL_UPDOVRFLW);
	else	sys$exit(*status);
}

scr_reset(status)

long    *status;
{
	/*
	 * Reset terminal.
	 */

        smg$delete_pasteboard(&pstbrd_id, &1);
        smg$delete_virtual_keyboard(&keybrd_id);
        sys$exit(*status);
}

lin_reset(status)

long    *status;
{
	/*
	 * Reset terminal.
	 */

	lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0);
        sys$exit(*status);
}

get_str(str_inp, str_len, prm_str, getback, getnull, null_char)

char    null_char,
	*prm_str, 
	*str_inp;
long    getback, 
	getnull,
	str_len;
{
        char    *p,
		*t;
        long    prm_len, 
		success;

        init_io();
	prm_len = strlen(prm_str);
        success = FALSE; 

        while (!success) {

		/*
		 * Prompt and get a string.
		 */

                sys$qiow(0, chan, IO$_READPROMPT + IO$M_ESCAPE, 0, 0, 0, 
			 str_inp, str_len, 0, 0, prm_str, prm_len);

		/*
		 * Return RMS$_EOF if CTRL-Z is pressed.
		 */

                if (strchr(str_inp, CTRL_Z) != 0) {
                        *str_inp = '\0';
                        return RMS$_EOF;
                }

                if ((strchr(str_inp, ESCAPE)) != 0)	/* Ignore ESCAPE. */
                        *str_inp = '\0';
                else {
                        t = str_inp;	/* Address of str_inp. */
                        p = strchr(str_inp, RETURN);	/* RETURN location. */

			/*
			 * If there is no RETURN, and it must getback,
			 * then return with empty string; otherwise,
			 * exit with status RMS$_TNS.
			 */

                        if (p == 0) {
                                if (getback) {
					str_inp[str_len] = '\0';
                                        success = TRUE;
                                }
				else	sys$exit(RMS$_TNS);
                        }

			/*
			 * If there is a RETURN at column 1, and getnull
			 * is in effect, it is considered success.
			 * The string has null_char at column 1.
			 */

                        else if ((p - t) == 0) {
                                sprintf(str_inp, "%c", null_char);
                                success = (getnull) ? TRUE : success;
                        }
                        else {	/* RETURN is not at column 1. */

				/*
				 * Get rid of trailing blanks and tabs.
				 */

                                for (--p; 
				     p != t && (*p == ' ' || *p == '\t'); 
				     p--);
                                *++p = '\0';

				/*
				 * Get rid of leading blanks and tabs.
				 */

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

				/*
				 * If the string is not empty, it is
				 * considered success.
				 */

                                if (t == p)
	                                sprintf(str_inp, "%c", null_char);
                                else 	strcpy(str_inp, t);

				success = TRUE;
                        }
                }
        }
}

fix_str(buf1, buf2, siz, getback, getnull, null_char)

char	*buf1,
	*buf2,
	null_char;
long	getback,
	getnull,
	siz;
{
	/*
	 * 	Enable input to be continue to next line.
	 *	Example: $_Bulletin: foo.blb,-
	 *		 $_$
	 */

	strcpy(buf1, buf2);

	if (buf1[strlen(buf1) - 1] == '-')
		buf1[strlen(buf1) - 1] = '\0';

	while (buf2[strlen(buf2) - 1] == '-') {

		if (get_str(buf2, siz, "\r\n_$ ", FALSE, getback, getnull,
		    null_char) == RMS$_EOF)
			sys$exit(CLI$_INSFPRM);

		if (strlen(buf1) + strlen(buf2) > siz) {
			if (getback) {
				strncat(buf1, buf2, siz - strlen(buf1));
				return;
			}
			else	sys$exit(RMS$_TNS);
		}

		strcat(buf1, buf2);

		if (buf1[strlen(buf1) - 1] == '-')
		    	buf1[strlen(buf1) - 1] = '\0';
	}
}

char
*mkstr(len)

long    len;
{
        char    *s;
        long    i;

	/*
	 * Make a dynamic string with a particular length.
	 */

        s = malloc(len + 1);

        for (i = 0;  i < len;  i++)
                s[i] =  ' ';
        s[len] = '\0';

        return s;
}

struct dsc$descriptor
*mkdesc(str)

char    *str;
{
        struct  dsc$descriptor  *p;

	/*
	 * Make a dynamic string descriptor.
	 */

        p = (struct dsc$descriptor *) malloc(sizeof(struct dsc$descriptor));

        p->dsc$w_length = strlen(str);
        p->dsc$b_dtype = DSC$K_DTYPE_T;
        p->dsc$b_class = DSC$K_CLASS_S;
        p->dsc$a_pointer = str;

        return p;
}

char
*lowcase(str)

char    *str;
{
        char    *s;

	/*
	 * Lowercase a string.
	 */

        for (s = str; *s != '\0'; s++)
                *s = _tolower(*s);
}

char
*upcase(str)

char    *str;
{
        char    *s;
                                     
	/*
	 * Uppercase a string.
	 */

        for (s = str; *s != '\0'; s++)
                *s = _toupper(*s);
}

expire(exp_date)

unsigned        exp_date[];
{
        unsigned	cur_tim[2];

	/*
	 * Check if the date has expired.
	 */

        if (exp_date[0] == 0 && exp_date[1] == 0)
                return FALSE;
        else {
                sys$gettim(cur_tim);
                return (exp_date[1] == cur_tim[1] ? 
			exp_date[0] < cur_tim[0] : 
			exp_date[1] < cur_tim[1]);
        }
}

get_txt(id, txt, prmpt, siz)

char	*prmpt,
	*txt;
long	id,
	siz;
{
	char	scratch[2];
        long    func, 
		rend = SMG$M_REVERSE | SMG$M_BOLD,
		ret_len,
		term_cd, 
		txt_len;
        struct  dsc$descriptor  *buf;

	/*
	 * 	Get text with highlight on and off. On upon
	 *	inserting. Off upon deleting.
	 */

        buf = mkdesc(mkstr(20)); 
	func = IO$M_NOECHO | IO$M_ESCAPE | IO$M_NOFILTR; 
	txt_len = strlen(txt);
        ret_len = term_cd = 0;

	smg$put_chars(&id, mkdesc(prmpt), 0, &1, &1, &rend, 0, 0);
	smg$put_chars(&id, mkdesc(txt), 0, 0, 0, &rend, 0, 0);

	do {
	        smg$read_string(&keybrd_id, buf, 0, &1, &func, 0, 0, &ret_len, 
				&term_cd, &id);

	        switch(term_cd) {

		case DEL:
			if (txt_len > 0) {
				txt[--txt_len] = '\0';
				smg$set_cursor_rel(&id, 0, &-1);
				smg$put_chars(&id, mkdesc(" "), 0, 0, 0,
					      0, 0, 0);
				smg$set_cursor_rel(&id, 0, &-1);
			}
			break;

		case CTRL_U:
			*txt = '\0';
			return;
			break;

		case CTRL_W:
			refresh_scr();
			break;

		case RETURN:
			txt[txt_len] = '\0';
			return;
			break;

		default:
			/*
			 * Take any character other than CTRL characters.
			 */

			if (ret_len) {
				if (txt_len > siz) {	/* Oversize. */
					txt[siz] = '\0';
					return;
				}

				if (*buf->dsc$a_pointer > '\037') {
					*buf->dsc$a_pointer = 
						tolower(*buf->dsc$a_pointer);
					txt[txt_len++] = *buf->dsc$a_pointer;
			    		sprintf(scratch, "%c", 
						*buf->dsc$a_pointer);
					smg$put_chars(&id, mkdesc(scratch), 0, 
						0, 0, &rend, 0, 0);
				}
			}
			break;
		}
	} while (term_cd != RETURN);
}

get_resp(id)

long    id;
{                       
        long    func, 
		ret_len,
		term_cd;
        struct  dsc$descriptor  *buf;

	/*
	 * Read a character at a virtual display.
	 */

        buf = mkdesc(mkstr(20)); 
	func = IO$M_NOECHO | IO$M_ESCAPE | IO$M_NOFILTR;
        ret_len = term_cd = 0;

        smg$read_string(&keybrd_id, buf, 0, &1, &func, 0, 0, &ret_len, 
			&term_cd, &id);

        switch(term_cd) {

	case PF2:
        case CTRL_W:
        case CTRL_Z:
        case RETURN:
		return term_cd;
                break;

        default:
		if (!ret_len)
			return 0;
                else   	return (*buf->dsc$a_pointer & 0xffffffff);
		break;
        }
}

sel_msg(id)

long    id;
{
        long    func, 
		ret_len,
		term_cd; 
        struct  dsc$descriptor  *buf;

	/*
	 * Select a bulletin message at marking phase.
	 */

        buf = mkdesc(mkstr(20)); 
	func = IO$M_NOECHO | IO$M_ESCAPE | IO$M_NOFILTR;
        ret_len = term_cd = 0;

        smg$read_string(&keybrd_id, buf, 0, &1, &func, 0, 0, &ret_len, 
			&term_cd, 0);

        switch(term_cd) {

        case UP:
	case DOWN:
	case DEL:
	case PF2:
      	case CTRL_W:
	case CTRL_Z:
	case RETURN:
		return term_cd;
                break;

	default:
		if (!ret_len)
			return 0;
		else	return (*buf->dsc$a_pointer & 0xffffffff);
		break;
        }
}

char
*get_ascdate(bin_tim)

unsigned        bin_tim[];
{
        struct  dsc$descriptor  *asc_tim;

	/*
	 * Return ascii date.
	 */

        asc_tim = mkdesc(mkstr(ASCTIM_LEN));
        sys$asctim(asc_tim, asc_tim, bin_tim, 0);
        asc_tim->dsc$a_pointer[ASCDAT_LEN] = '\0';

        return (lowcase(asc_tim->dsc$a_pointer));
}

get_bull(bull_lst, for_user)

long    for_user;
struct  bull_lst_struct   *bull_lst;
{
        char    *c, 
		buf[QUAL_LEN], 
		scratch[QUAL_LEN],
		*t;
        long    i,
		max_indx,
		status;
        struct  dsc$descriptor  *file,
				*lib;

        lib = mkdesc(mkstr(FILENAM_LEN));
        bull_lst->last_bull = -1;

        if (get_val(BULL_PARAM, 0, 0) == CLI$_PRESENT) {            

		/*
		 * If BULL_PARAM which is a list is present, take it.
		 * This list overrides the search list DEF_BULL.
		 * The list must not be greater than MAX_BULL.
		 */

                while (get_val(BULL_PARAM, lib->dsc$a_pointer, 
		       FILENAM_LEN) != CLI$_ABSENT) {
                        if (++bull_lst->last_bull > MAX_BULL) 
				sys$exit(BULL_TOOMANY);

                        lib->dsc$w_length = strlen(lib->dsc$a_pointer);
                        obtain_spec(lib, bull_lst, for_user);
                }
        }
        else {
		/*
		 * If there is no BULL_PARAM, and if the search list
		 * DEF_BULL is defined, take this search list.
		 * The search list must not be greater than MAX_BULL.
		 */

                if (get_lnmindx("LNM$FILE_DEV", DEF_BULL, 0, 
		    &max_indx) == SS$_NORMAL) {
                        if (max_indx > MAX_BULL - 1) 
				sys$exit(BULL_TOOMANY);

                        for (i = 0; i <= max_indx; i++) {
			   	status = trn_lnm("LNM$FILE_DEV", i, DEF_BULL,
						 0, lib->dsc$a_pointer,
						 FILENAM_LEN);

				STATUS_FAIL(DEF_BULL, status);

                                lib->dsc$w_length = strlen(lib->dsc$a_pointer);
                                ++bull_lst->last_bull;
                                obtain_spec(lib, bull_lst, for_user);
                        }
                }
                else {
			/*
			 * If there is no BULL_PARAM, no search list
			 * DEF_BULL, prompt for the search list then.
			 * This search list must not be greater than MAX_BULL.
			 */

                        if (get_str(scratch, QUAL_LEN - 1, "\r\n_Bulletin: ", 
			    FALSE, FALSE, '\0') == RMS$_EOF)
                                sys$exit(SS$_NORMAL);

			fix_str(buf, scratch, QUAL_LEN - 1, FALSE, FALSE, '\0');

                        c = buf;

                        for (c = buf; *c; c++)
                                *c = (*c == '+') ? ',' : *c;

                        if (buf[strlen(buf) - 1] == ',') 
        			sys$exit(CLI$_INSFPRM);

                        t = c = buf; i = 0;

                        while (*t) {
                                if ((c = strchr(t, ',')) == 0) {
                                        strcpy(lib->dsc$a_pointer, t);
                                        *t = '\0';
                                }
                                else {
                                        *c = '\0';
                                        strcpy(lib->dsc$a_pointer, t);
                                        t = c + 1;
                                }

                                if (++i > MAX_BULL) 
                                        sys$exit(BULL_TOOMANY);

                                lib->dsc$w_length = 
				     	strlen(lib->dsc$a_pointer);
                                ++bull_lst->last_bull;
                                obtain_spec(lib, bull_lst, for_user);
                        }
                }
        }

	/*
	 * 	Access bulletin.dat or DEF_BULLUPD. This is for user only.
	 */

	if (for_user) {	
		status = trn_lnm("LNM$PROCESS_TABLE", 0, DEF_BULLUPD, 0, 
				 buf, FILENAM_LEN);

		if (!((status & STS$M_SUCCESS) >> STS$V_SUCCESS))
	                strcpy(buf, BULLUPD);

		file = mkdesc(mkstr(FILENAM_LEN));
		status = lib$find_file(mkdesc(buf), file, &0, &0, &0, &0, &1);

		if (status == RMS$_NORMAL) {
			bull_lst->file = open(buf, O_RDWR, 0, "shr=nil");

			FILE_FAIL(buf, bull_lst->file);

			i = read(bull_lst->file, (char *) &buf, DAT_LEN);

			if (i != DAT_LEN && i != 0)
				bull_lst->file = creat(buf, 0, "rfm=fix", 
						       "mrs=256", 
						       "dna=bulletin.dat",
					               "shr=get");
		}
		else	bull_lst->file = creat(buf, 0, "rfm=fix", "mrs=256", 
					       "dna=bulletin.dat", "shr=nil");
	}
}

obtain_spec(lib, bull_lst, for_user)

long    for_user;
struct  dsc$descriptor  *lib;
struct  bull_lst_struct   *bull_lst;
{                      
        char    bull_dir[FILENAM_LEN], 
		nam[BULLNAM_LEN], 
		*p,
		*t,
		temp[5];
        extern  unsigned short hash();
        long    func,
		lib_indx,
		status;
        struct  dsc$descriptor  *file;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 *	Find and try to open bulletin library.
	 */

        file = mkdesc(mkstr(QUAL_LEN));

        lib$find_file(lib, file, &0, &def_lib, &0, &0, &1); 
        str$trim(file, file, file); 
        file->dsc$a_pointer[file->dsc$w_length] = '\0';
        strcpy(bull_lst->bull_lib[bull_lst->last_bull], lib->dsc$a_pointer);
        lowcase(bull_lst->bull_lib[bull_lst->last_bull]);

	func = for_user ? LBR$C_READ : LBR$C_UPDATE;
	lbr$ini_control(&lib_indx, &func);

	status = lbr$open(&lib_indx, file, 0, &def_lib);

	STATUS_FAIL(file->dsc$a_pointer, status);

  	lbr$close(&lib_indx);

        if (file->dsc$w_length > FILENAM_LEN - 1) {
		STATUS_FAIL(file->dsc$a_pointer, BULL_TOOLONG);
        }

	/*
	 *	Find and try to open bulletin index.
	 */

        p = strrchr(file->dsc$a_pointer, '.');
        strcpy(p + 1, "IND");

        status = lib$find_file(mkdesc(file->dsc$a_pointer), file, 
			       &0, &0, &0, &0, &0);	

        str$trim(file, file, file); 
        file->dsc$a_pointer[file->dsc$w_length] = '\0';
        strcpy(bull_lst->bull_indx[bull_lst->last_bull], file->dsc$a_pointer);
        lowcase(bull_lst->bull_indx[bull_lst->last_bull]);

        if (status != RMS$_NORMAL && 
	   (for_user || 
	   (!for_user && 
	   get_val(DEL_QUAL, 0, 0) == CLI$_PRESENT ||
	   get_val(EDT_QUAL, 0, 0) == CLI$_PRESENT ||
	   get_val(EXP_QUAL, 0, 0) == CLI$_PRESENT))) {
		STATUS_FAIL(file->dsc$a_pointer, status);
        }

        if (file->dsc$w_length > FILENAM_LEN - 1) {
		STATUS_FAIL(file->dsc$a_pointer, BULL_TOOLONG);
        }

	/*
	 * 	Form bull_nam which is used as the key, an entry in
	 *	the bulletin.dat. This key is unique. It is based on
	 * 	harshing the (dir part of the bulletin library + node)
	 *	and add it to the name part of the bulletin library.
	 */

        p = strrchr(file->dsc$a_pointer, ']');

        for (t = file->dsc$a_pointer; t <= p; t++)
                bull_dir[t - file->dsc$a_pointer] = *t;

        bull_dir[t - file->dsc$a_pointer] = '\0';

        get_node(nam);
        strcat(bull_dir, nam);

        strcpy(bull_lst->bull_nam[bull_lst->last_bull], p + 1);
        p = strchr(bull_lst->bull_nam[bull_lst->last_bull], '.');
        *p = '\0';

        if (strlen(bull_lst->bull_nam[bull_lst->last_bull]) > BULLNAM_LEN - 5) {
		STATUS_FAIL(file->dsc$a_pointer, BULL_TOOLONG);
        }

        sprintf(temp, "%4x", hash(bull_dir));

        strcat(bull_lst->bull_nam[bull_lst->last_bull], temp);
        lowcase(bull_lst->bull_nam[bull_lst->last_bull]);
}

get_val(labl_nam, val, max_siz)

char	*labl_nam,
	*val;
long	max_siz;
{
        long    status;
	struct	dsc$descriptor	*labl_dsc,
				*val_dsc;

	labl_dsc = mkdesc(labl_nam);

	/* 
	 * Check if the keyword, parameter or qualifier is present.
	 * If it is and there is a request to get its value, then 
	 * get it. Otherwise, ignore the last part.
	 */

	if ((status = cli$present(labl_dsc)) == CLI$_PRESENT) {
                if (val) {
			val_dsc = mkdesc(mkstr(QUAL_LEN));

                        status = cli$get_value(labl_dsc, val_dsc, 0);

                        str$trim(val_dsc, val_dsc, val_dsc);
			val_dsc->dsc$a_pointer[val_dsc->dsc$w_length] = '\0';

			if (val_dsc->dsc$w_length > max_siz)
				val_dsc->dsc$a_pointer[max_siz] = '\0';

			strcpy(val, val_dsc->dsc$a_pointer);
                }
        }

        return status;
}

static unsigned short crc16a[] = {
        0000000,        0140301,        0140601,        0000500,
        0141401,        0001700,        0001200,        0141101,
        0143001,        0003300,        0003600,        0143501,
        0002400,        0142701,        0142201,        0002100,        
};
static unsigned short crc16b[] = {
        0000000,        0146001,        0154001,        0012000,
        0170001,        0036000,        0024000,        0162001,
        0120001,        0066000,        0074000,        0132001,
        0050000,        0116001,        0104001,        0043000,
};

unsigned short
hash(buf)
char	*buf;
{
        register char	*tp;
        register unsigned short crc = 0;
        register short	temp;


	/*
	 *      Return the CRC16 hash code for the buffer
	 *      Algorithm from Stu Wecker (Digital memo 130-959-002-00).
	 */

        for (tp = buf; *tp;) {
                temp = *tp++ ^ crc;
                crc = (crc >> 8)
                        ^ crc16a[(temp & 0017)]
                        ^ crc16b[(temp & 0360) >> 4];
        }

        return((crc == 0) ? 1 : crc);
}

get_lnmindx(tbl_nam, lnm, acc_md, max_indx)

char	*tbl_nam,
	*lnm;
long	acc_md,
	*max_indx;
{
	long	attr = LNM$M_CASE_BLIND;
	struct	lst_struct lnm_lst[] = {
                { 4, LNM$_MAX_INDEX, max_indx, 0 },
                { 0, 0, 0, 0 }
        };

	/*
	 * Get the maximum index of a logical name via a logical name table.
	 */

	return sys$trnlnm(&attr, mkdesc(tbl_nam), mkdesc(lnm), acc_md,
	       		  &lnm_lst);
}     

trn_lnm(tbl_nam, indx, lnm, acc_md, eqv_buf, siz)

char	*eqv_buf,
	*lnm,
	*tbl_nam;
long	acc_md,
	indx,
	siz;
{
	long	attr = LNM$M_CASE_BLIND,
		i,
		ret_len = 0,
		status;
	char	scr[LNM$C_NAMLENGTH];
        struct lst_struct lnm_lst[] = {
                { 4, LNM$_INDEX, &indx, 0 },
                { LNM$C_NAMLENGTH, LNM$_STRING, scr, &ret_len }, 
                { 0, 0, 0, 0 }
        };

	/*
	 * Translate a logical name via a logical name table.
	 */
                                          
	if ((status = sys$trnlnm(&attr, mkdesc(tbl_nam), mkdesc(lnm), acc_md,
	    &lnm_lst)) == SS$_NORMAL) {
		i = (ret_len > siz) ? siz : ret_len;
		strncpy(eqv_buf, scr, i);
		eqv_buf[i] = '\0';
	}

	return status;
}     

get_node(nam)

char    *nam;
{
        long    status;
 	struct	lst_struct item_lst = { 16, SYI$_NODENAME, nam, 0 };

	/*
	 * Get current node.
	 */

        return (status = sys$getsyi(0, 0, 0, &item_lst, 0, 0, 0));
}

get_poster(poster)

char	*poster;
{
	char	*c;
        long    status;
 	struct	lst_struct item_lst = { 12, JPI$_USERNAME, poster, 0 };

	status = sys$getjpiw(0, 0, 0, &item_lst, 0, 0, 0);

	for (c = poster; *c && *c != ' '; c++);

	*c = '\0';

	return status;
}
