/*
 *	  -------------------------------------------------------
 *        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 bullio

#include        descrip.h
#include        file.h
#include        iodef.h
#include        rms.h
#include        rmsdef.h
#include        smgdef.h     
#include        ssdef.h 
#include        stdio.h 
#include	stsdef.h
#include        "bull"  
#include        "lbrdef"

#define		OBSOLETE	\
"\t*** This bulletin message has been deleted just a moment ago ***"

get_msgid(nam)

char	*nam;
{
        long    file; 
	struct	indx_file_struct	buf;

        if ((file = open(nam, O_RDWR, 0, "shr=get,put,upd", "tmo=30")) == -1) {

		/*
		 * Return 1 if file can't be opened.
		 */

                close(file);
                return 1;
        }
        else {
		/*
		 * Otherwise, read the last record, and add one to msg_id.
		 */

                lseek(file, -INDX_LEN-1, 2);	/* Reposition rfa to the
						   beginning of the last
						   record. */
                read(file, (char *) &buf, INDX_LEN);
                close(file);
                return (++buf.msg_id);
        }
}

creat_indx(nam, title, date, exp_date, lin_cnt, poster)

char	*nam, 
	*poster,
	*title;
unsigned	date[], 
		exp_date[];
long	lin_cnt; 
{
	long	file;
        struct  indx_file_struct	buf;

	/*
	 *      If file does not exist, create one.
	 */

        if ((file = open(nam, O_RDWR, 0, "shr=get,put,upd", "tmo=30")) == -1) {
                file = creat(nam, 0, "rfm=fix", "mrs=101", 
			     "shr=get,put,upd");
                buf.msg_id = 1;
        }
        else {
                lseek(file, -INDX_LEN-1, 2);
                read(file, (char *) &buf, INDX_LEN);
                buf.msg_id++;
        }

	/*
	 *      Initialize the buffer and append it to the end of file.
	 */

        buf.status = ' ';
        strcpy(buf.title, title);
        buf.date[0] = date[0];
        buf.date[1] = date[1];
        buf.exp_date[0] = exp_date[0];
        buf.exp_date[1] = exp_date[1];
        buf.lin_cnt = lin_cnt;
	strcpy(buf.poster, poster);
        write(file, (char *) &buf, INDX_LEN);
        close(file);

	return buf.msg_id;
}

insert_lib(lib_indx, file, title, msg_id, lin_cnt)

char	*title;
long	*lib_indx,
	*lin_cnt,
	msg_id;
FILE	*file;
{
	long	mod_rfa[2];
	struct	dsc$descriptor	*buf,
				*key;

	/*
	 *      Make a unique key for the message and insert it into 
	 *	the library. The first record of the message text is 
	 *	the title.
	 */

        key = mkdesc(mkstr(KEY_LEN));
        key->dsc$w_length = sprintf(key->dsc$a_pointer, "%s_%d", KEY_PREFX, 
				    msg_id);

        lbr$put_record(lib_indx, mkdesc(title), mod_rfa);
        lbr$insert_key(lib_indx, key, mod_rfa);

	/*
	 *      Insert bulletin message into the library.
	 */

        buf = mkdesc(mkstr(MSG_LEN));
	fseek(file, 0, 0);	/* Top of file. */
        *lin_cnt = 0;

        while (fgets(buf->dsc$a_pointer, MSG_LEN, file) != NULL) {

		ignore_cr(buf->dsc$a_pointer);		
                buf->dsc$w_length = strlen(buf->dsc$a_pointer);
                lbr$put_record(lib_indx, buf, mod_rfa);
                (*lin_cnt)++;
        }

        lbr$put_end(lib_indx);
}

ignore_cr(buf)

char	*buf;
{
	char	*c;

	for (c = buf; *c; c++) {
   		switch (*c) {	/* Ignore <CR><LF>. */

		case '\012':
	       	case '\015':
			*c = ' ';
			break;

		default:
		 	break;
		}
	}
}

delete_lib(lib_indx, msg_id)

long	*lib_indx,
	msg_id;
{
        long    mod_rfa[2];
        struct  dsc$descriptor  *key;

	/*
	 * Delete a bulletin message.
	 */
                
        key = mkdesc(mkstr(KEY_LEN));
        key->dsc$w_length = sprintf(key->dsc$a_pointer, "%s_%d", KEY_PREFX, 
				    msg_id);

        lbr$lookup_key(lib_indx, key, mod_rfa);
        lbr$delete_key(lib_indx, key);
        lbr$delete_data(lib_indx, mod_rfa);
}

replace_lib(lib_indx, file, title, msg_id, lin_cnt)

char	*title;
long	*lib_indx,
	*lin_cnt,
	msg_id;
FILE	*file;
{
        long    old_rfa[2], 
		new_rfa[2];
	struct	dsc$descriptor	*buf,
				*key;

        key = mkdesc(mkstr(KEY_LEN));	
        key->dsc$w_length = sprintf(key->dsc$a_pointer, "%s_%d", KEY_PREFX, 
				    msg_id);

        lbr$lookup_key(lib_indx, key, old_rfa);

        buf = mkdesc(mkstr(MSG_LEN));
        lbr$put_record(lib_indx, mkdesc(title), new_rfa);

	/*
	 *      Get records from input file and put them in the library.
	 */

        *lin_cnt = 0;

        while (fgets(buf->dsc$a_pointer, MSG_LEN, file) != NULL) {

		ignore_cr(buf->dsc$a_pointer);
                buf->dsc$w_length = strlen(buf->dsc$a_pointer);
                lbr$put_record(lib_indx, buf, new_rfa);
                (*lin_cnt)++;
        }

	/*
	 *      Replace old text module with the new one.
	 */

        lbr$replace_key(lib_indx, key, old_rfa, new_rfa);
        lbr$put_end(lib_indx);
        lbr$delete_data(lib_indx, old_rfa);
}

upd_bull(file, bull_indx, bull_nam, lst_pos, cur_lst)

char	*bull_indx,
	*bull_nam;
long	file,
	*lst_pos;
struct	lnk_lst_struct	*cur_lst;
{
	char	*c,
		*get_lst(),
		marker,
		nam[FILENAM_LEN],
		scratch[80],
		upd_lst[CTRLSTR_LEN];
	long	end,
		i,
		ovrflw = FALSE,
		start,
		status;
	struct	lnk_lst_struct	*cur,
				*gen_dislst(),
				*lnk_lst,
				*old;
	struct	data_file_struct	buf;

	/*
	 *      Generate a list which contains status of all 
	 *	read/unread messages.
	 */

	lnk_lst = gen_dislst(0, "", bull_indx, 
			     get_lst(file, bull_nam, lst_pos), ALL, "");

	/*
	 *      Merge the current list with the old list.
	 */

        old = lnk_lst->fwd;
        cur = cur_lst->fwd;

        if (old != lnk_lst) {
                while (cur != cur_lst) {
                        if (cur->flag != UNMARK) {
                                while (old->msg_id < cur->msg_id) {
                                        if (old->fwd != lnk_lst)
                                                old = old->fwd;
                                        else    break;
                                }

                                if (old->msg_id == cur->msg_id) {
                                        if (cur->flag == NEVER)
                                                old->marker = NEVER;
                                        else if (cur->read == YES)
                                                old->marker = READ;
					else	old->marker = UNMARK;
                                }
                        }

                        cur = cur->fwd;
                }
        }

	/*
	 *      Make a new list.
	 */

        cur = lnk_lst->fwd;
	*upd_lst = '\0';
        c = upd_lst;

        while (cur != lnk_lst) {
                while (cur != lnk_lst && cur->marker == UNMARK)
                        cur = cur->fwd;

                if (cur != lnk_lst) {
                        marker = cur->marker;
                        start = cur->msg_id;

                        while (cur->fwd != lnk_lst && 
			       cur->fwd->marker == marker)
                                cur = cur->fwd;

                        end = cur->msg_id;

                        if (start == end) {
				i = sprintf(scratch, "%c%d ", marker, start);

				if (strlen(upd_lst) + i >= CTRLSTR_LEN) {
			       		ovrflw = TRUE;
					sscanf(upd_lst, "%*c%d", &start);
					*upd_lst = '\0';
					c = upd_lst;
					c += sprintf(c, "R%d-%d ", start,
						     end);
				}
				else	c += sprintf(c, "%c%d ", marker, start);
                        }
                        else {
				i = sprintf(scratch, "%c%d-%d ", marker, start,
					    end);

	  			if (strlen(upd_lst) + i >= CTRLSTR_LEN) {
					ovrflw = TRUE;
					sscanf(upd_lst, "%*c%d", &start);
					*upd_lst = '\0';
					c = upd_lst;
					c += sprintf(c, "R%d-%d ", start, end);
				}
				else   	c += sprintf(c, "%c%d-%d ", marker,
						     start, end);
                        }

                        cur = cur->fwd;
                }
        }

	/*
	 *	Record read/unread bulletin message if possible.
	 */

	if (file != -1 && *upd_lst) {
		strcpy(buf.bull_nam, bull_nam);	
		strcpy(buf.upd_lst, upd_lst);
	        lseek(file, *lst_pos, 0);

	        if (write(file, (char *) &buf, DAT_LEN) == -1)
			sys$exit(BULL_CANTUPD);

	}
 
	return ovrflw;
}

char
*get_lst(file, bull_nam, lst_pos)

char    *bull_nam;
long    file,
	*lst_pos;
{
        char    *c, 
		nam[FILENAM_LEN];
        long    found;
        struct  data_file_struct	buf;

	if (file == -1) {
		*lst_pos = 0;
	        *buf.upd_lst = '\0';
	}
	else {

		/*
		 * Search for the right bull_nam. If found,
		 * remember the lst_pos.
		 */

		lseek(file, 0, 0);	/* Top of file. */
		found = FALSE; 

		while (read(file, (char *) &buf, DAT_LEN) != 0) {
	       		if (strcmp(buf.bull_nam, bull_nam) == 0) {
                                found = TRUE;

                                if ((*lst_pos = lseek(file, 0, 1)) == -1)
                                        *lst_pos = lseek(file, 0, 2);

                                *lst_pos -= DAT_LEN;
                                break;
                        }
		}

                if (!found) {
                        *lst_pos = 0;
                        *buf.upd_lst = '\0';
		}
	}

        c = mkstr(strlen(buf.upd_lst));
        strcpy(c, buf.upd_lst);

        return c;	/* Return the list. */
}

struct	lnk_lst_struct
*gen_dislst(presnt_lib, bull_lib, bull_indx, upd_lst, how, key)

char	*bull_indx,
	*bull_lib,
	how,
	*key,
	*upd_lst;
long	presnt_lib;
{
 	char	*c,
		m,
		marker,
		scratch[80];
        long    end,
		file,
		lib_indx, 
		start, 
		status;
	struct	lnk_lst_struct	*init_lst(),
				*insert_lst(),
				*lnk_lst;
	struct	indx_file_struct	buf;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);
	$DESCRIPTOR(tmp, scratch);

        marker = UNMARK;
        start = end = 0;
        lnk_lst = init_lst();

        file = open(bull_indx, O_RDONLY, 0, "shr=get,put,upd", "tmo=30");

	FILE_FAIL(bull_indx, file);

        if (how == LIB) {
                lbr$ini_control(&lib_indx, &LBR$C_READ);

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

		STATUS_FAIL(bull_lib, status);
        }

	/*
	 * 	Generate link list.
	 */

        while (read(file, (char *) &buf, INDX_LEN) != 0) {
                if (buf.status != DELETE) {
                        while (buf.msg_id > end && *upd_lst) {
                                if ((c = strchr(upd_lst, ' ')) == 0)
                                        break;

                                *c = '\0';

                                if (strchr(upd_lst, '-') != 0)
                                        if (sscanf(upd_lst,"%c%d-%d", &marker,
					    &start, &end) != 3)
                                                break;
                                        else;
                                else {
                                        if (sscanf(upd_lst, "%c%d", &marker, 
					    &start) != 2)
                                                break;
                                        else    end = start;
                                }

                                upd_lst = c + 1;
                        }

                        m = (buf.msg_id >= start && buf.msg_id <= end) ? 
			     marker : UNMARK;

                        if (how == LIB)
                                if (in_lib(&lib_indx, buf.msg_id, key))
					lnk_lst = insert_lst(presnt_lib,
	 						lnk_lst,
							buf.msg_id, buf.title,
						  	buf.date, buf.exp_date,
							buf.lin_cnt, buf.poster,
							m);
                                else;
                        else if (how == ALL) {
				tmp.dsc$w_length = sprintf(tmp.dsc$a_pointer,
							   "%s", buf.title);
				lowcase(tmp.dsc$a_pointer);

                                if (*key)
					if (lib$index(&tmp, mkdesc(key)))
						lnk_lst = insert_lst(presnt_lib,
							lnk_lst,
							buf.msg_id, buf.title,
							buf.date, buf.exp_date,
							buf.lin_cnt, buf.poster,
							m);
					else;
				else	lnk_lst = insert_lst(presnt_lib,
							lnk_lst,
							buf.msg_id, buf.title,
						  	buf.date, buf.exp_date,
							buf.lin_cnt, buf.poster,
							m);
			}
                        else if (!(buf.msg_id >= start && buf.msg_id <= end)) 
				lnk_lst = insert_lst(presnt_lib, 
						     lnk_lst, 
						     buf.msg_id, buf.title,
					       	     buf.date, buf.exp_date,
					  	     buf.lin_cnt, buf.poster,
						     UNMARK);
                }
        }

        close(file);

        if (how == LIB)
                lbr$close(&lib_indx);

        return lnk_lst;
}

in_lib(lib_indx, msg_id, str)

char    *str;
long    *lib_indx, msg_id;
{
        long    found,
		mod_rfa[2];
        struct  dsc$descriptor  *buf, 
				*key, 
				*srch_str;

	/*
	 *      Find the correct module in the library.
	 */

        key = mkdesc(mkstr(KEY_LEN));
        key->dsc$w_length = sprintf(key->dsc$a_pointer, "%s_%d", KEY_PREFX, 
				    msg_id);

        lbr$lookup_key(lib_indx, key, mod_rfa);

        srch_str = mkdesc(str);
        buf = mkdesc(mkstr(MSG_LEN));
        found = FALSE;

        while (!found) {
                buf->dsc$w_length = MSG_LEN;
                if (lbr$get_record(lib_indx, buf, buf) & SS$_NORMAL) {
			lowcase(buf->dsc$a_pointer);
                        if (lib$index(buf, srch_str))
                                found = TRUE;
                }
                else    break;
        }

        return found;
}

struct  lnk_lst_struct
*init_lst()
{
        struct  lnk_lst_struct	*c;

	/*
	 * 	Make a header link list.
	 */

        c = (struct lnk_lst_struct *) malloc(sizeof(struct lnk_lst_struct));
        c->msg_id = 0;  
	c->flag = UNMARK;
        c->bck = c->fwd = c->up = c->dwn = (char *) c;

        return c;
}

struct  lnk_lst_struct
*insert_lst(presnt_lib, head, msg_id, title, date, exp_date, lin_cnt, poster,
	    marker)

char    marker,
	*poster,
	*title;
unsigned date[],
	 exp_date[];
long    lin_cnt,
	msg_id, 
	presnt_lib;
struct  lnk_lst_struct	*head;
{
        struct  lnk_lst_struct	*c;

	/*
	 * 	Insert to the link list.
	 */

        c = (struct lnk_lst_struct *) malloc(sizeof(struct lnk_lst_struct));

        c->msg_id = msg_id;
        c->title = mkstr(strlen(title));
        strcpy(c->title, title);
        c->date[0] = date[0];
        c->date[1] = date[1];
	c->exp_date[0] = exp_date[0];
	c->exp_date[1] = exp_date[1];
        c->lin_cnt = lin_cnt;
	strcpy(c->poster, poster);
        c->marker = marker;
        c->flag = UNMARK;
        c->read = NO;
	c->presnt_lib = presnt_lib;
        c->bck = (char *) head->bck;
        c->fwd = (char *) head;
        c->up = c->dwn = NULL;
        head->bck->fwd = (char *) c;
        head->bck = (char *) c;
        
        return head;
}                                 

cpy_bull(bull_lib, nam, cur)

char	*bull_lib,
	*nam;
struct  lnk_lst_struct	*cur;
{
	char	key[KEY_LEN];
	long	lib_indx,
		mod_rfa[2],
		status;
	FILE	*file;
	struct	dsc$descriptor	*buf;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 * 	Copy a bulletin message to a file or sys$print.
	 */

	lbr$ini_control(&lib_indx, &LBR$C_READ);

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

	STATUS_FAIL(bull_lib, status);

	if ((file = fopen(nam, "w", "shr=get,put")) == NULL) 
    		return FALSE;

	buf = mkdesc(mkstr(MSG_LEN));
	fprintf(file, "%s\n%s\n", capital(bull_lib), get_ascdate(cur->date));
	sprintf(key, "%s_%d", KEY_PREFX, cur->msg_id);

	status = lbr$lookup_key(&lib_indx, mkdesc(key), mod_rfa);

	if (status & RMS$_NORMAL)
		lbr$get_record(&lib_indx, buf, buf);
	else 	buf->dsc$w_length = sprintf(buf->dsc$a_pointer, "%s",
					    cur->title);

	buf->dsc$a_pointer[buf->dsc$w_length] = '\0';
	fprintf(file, "%s\n", buf->dsc$a_pointer);

	buf->dsc$w_length = MSG_LEN;

	if (status & RMS$_NORMAL) {
		while (lbr$get_record(&lib_indx, buf, buf) & SS$_NORMAL) {
			buf->dsc$a_pointer[buf->dsc$w_length] = '\0';
			ignore_cr(buf->dsc$a_pointer);

			status = fprintf(file, "%s\n", buf->dsc$a_pointer);

			if (status == -1) {
				fclose(file);
				lbr$close(&lib_indx);
				delete(nam);
				return FALSE;
			}

			buf->dsc$w_length = MSG_LEN;
		}
	}
	else	fprintf(file, "\t%s\n", OBSOLETE);

	fclose(file);
	lbr$close(&lib_indx);
}
