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

#include        dcdef.h 
#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	"climsgdef"
#include	"libclidef"
#include        "lbrdef"
#include        "hlpdef"

help(lib, str)

char    *lib,
	*str;
{
        long    lib$get_input(), 
		lib$put_output(), 
		status;

	/*
	 *	Display help file.
	 */

        status = lbr$output_help(&lib$put_output, 0, mkdesc(str), 
				 mkdesc(lib), &HLP$M_PROMPT, &lib$get_input);

	if (!((status & STS$M_SUCCESS) >> STS$V_SUCCESS))
                lib$signal(status);
}

add_bull(bull_lst)

struct  bull_lst_struct	*bull_lst;
{
        char    enter[2],
		exp_date[ASCTIM_LEN], 
		nam[FILENAM_LEN], 
		poster[POSTER_LEN],
		prmpt[QUAL_LEN], 
		scratch[QUAL_LEN],
		title[TITLE_LEN];
        unsigned        exp_tim[2],
			cur_tim[2];
			
        long    i, 
		lib_indx, 
		lin_cnt, 
		msg_id,
		status,
		valid;
	FILE	*file;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 * 	If /BATCH is not present, prompt for title and file.
	 */

	if (!(get_val(BATCH_QUAL, 0, 0) & SS$_NORMAL)) {

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

		fix_str(title, scratch, TITLE_LEN - 1, TRUE, FALSE, '\0');

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

		fix_str(nam, scratch, FILENAM_LEN - 1, TRUE, FALSE, '\0');
	}

	/* 
	 *	Otherwise, get title and file from /TITLE=[title] and
	 *	/FILE=[file] respectively.
	 */

	else {	
	        if (!(get_val(TITLE_QUAL, 0, 0) & SS$_NORMAL))
        	        sys$exit(BULL_NOTITLE);	/* Must have title. */

	        get_val(TITLE_QUAL, title, TITLE_LEN - 1);

	        if (!(get_val(FILE_QUAL, 0, 0) & SS$_NORMAL))	
        	        sys$exit(BULL_NOFILE);	/* Must have file. */

	        get_val(FILE_QUAL, nam, FILENAM_LEN - 1);
	}

	file = fopen(nam, "r", "shr=get,put,upd");

	FILE_FAIL(nam, file);	/* Fail if can't access the file. */

	/*
	 *      Get expiry date if /EXPIRY_DATE is present.
	 */
                
        if (get_val(EXPDATE_QUAL, 0, 0) & SS$_NORMAL) {
                get_val(EXPDATE_QUAL, exp_date, ASCTIM_LEN);
		status = sys$bintim(mkdesc(exp_date), exp_tim);

		STATUS_FAIL(exp_date, status);
        }
        else    exp_tim[0] = exp_tim[1] = 0;

	/*
	 *      Create an entry in the index file which is associated 
	 *	with the text library. The index file is assumed to be 
	 *	in the same directory as the library.
	 */

        sys$gettim(cur_tim);	/* Get current time. */

        for (i = 0; i <= bull_lst->last_bull; i++) {

		/*
		 * 	If /CONFIRM is present, expect answer 'y' or 'n'.
		 */

	        if (get_val(CONFRM_QUAL, 0, 0) & SS$_NORMAL) {
	                lowcase(nam);
	                sprintf(prmpt, "\r\nAdd %s to %s? [N] ", 
				nam, capital(bull_lst->bull_lib[i]));

	                do {
	                        if (get_str(enter, 2, prmpt, TRUE, TRUE, 
				    'n') == RMS$_EOF)
					sys$exit(SS$_NORMAL);

	                        valid = ((*enter = tolower(*enter)) == 'y' || 
					*enter == 'n');
	                } while (!valid);

	                if (*enter == 'n')
	                        continue;
		}

		/*
		 * 	Insert the bulletin message in the bulletin library
		 *	and create an entry in the bulletin index.
		 */

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

                status = lbr$open(&lib_indx, mkdesc(bull_lst->bull_lib[i]), 
				  0, &def_lib); 

		STATUS_FAIL(bull_lst->bull_lib[i], status);

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

                insert_lib(&lib_indx, file, title, 
			   get_msgid(bull_lst->bull_indx[i]), &lin_cnt);
		get_poster(poster);	/* Get poster. */
                msg_id = creat_indx(bull_lst->bull_indx[i], title, cur_tim, 
				    exp_tim, lin_cnt, poster);

                lbr$close(&lib_indx);
                lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0); 

		if (msg_id > LIMIT)	/* Too many entries already. */
			printf("\r%s %s\n\r%s\n",
				"Too many entries in", bull_lst->bull_indx[i],
				"Create new library and index now");
        }

	fclose(file);
}

edit_bull(bull_lst, video_type, status)

long    video_type, 
	status;
struct	bull_lst_struct	*bull_lst;
{
	char	deflt[QUAL_LEN],
		poster[POSTER_LEN];
        long    file,
		frst_tim, 
		i,
		lib_indx,
		unmark_bull;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;				

	/*
	 *	Form the link list.
	 */

	lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
		cur = gen_dislst(i, bull_lst->bull_lib[i],
				 bull_lst->bull_indx[i], "", ALL, "");

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst; 
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 *	If there are bulletins, let the user mark the one
	 *	which he wants to edit.
	 */

        if ((unmark_bull = total_bull(lnk_lst, UNMARK)) > 0) {

		/*
		 * 	Get /DEFAULT value. The value can be DATE which
		 * 	is the default, POSTER or SIZE.
		 */

		if (get_val(DEFAULT, 0, 0) == CLI$_PRESENT)
			get_val(DEFAULT, deflt, QUAL_LEN);
		else	*deflt = 'D';	/* Default=date. */

                if (video_type && (status & SS$_NORMAL)) {
                        setup_scr();
                        lnk_lst = scr_mrkbull(bull_lst, lnk_lst, INCR, 
					      ALL, "", *deflt);
                }
                else 	lnk_lst = lin_mrkbull(bull_lst, lnk_lst, INCR, 
					      ALL, "", *deflt);
        }       

        if (total_bull(lnk_lst, MARK) > 0) {
                if (video_type && (status & SS$_NORMAL)) {
                        smg$delete_pasteboard(&pstbrd_id, &1);
                        smg$delete_virtual_keyboard(&keybrd_id);
                }

		get_poster(poster);	/* Get poster. */
                cur = lnk_lst->dwn; 

		/*
		 *	Edit all marked bulletin messages.
		 */

                do {
			i = cur->presnt_lib;
			frst_tim = TRUE;

                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck) {
                                if (cur->flag == MARK) {
					open_lib(&frst_tim, TRUE,
						 bull_lst->bull_lib[i],
						 bull_lst->bull_indx[i],
						 &file, &lib_indx);

			 		do_edit(file, lib_indx, cur, poster);
				}
                        }

			if (!frst_tim) 
				close_lib(file, lib_indx);

                } while ((cur = cur->dwn) != lnk_lst);
        }
        else if (!unmark_bull)
		lib$signal(BULL_NOMORE);
}

open_lib(frst_tim, displ_lib, bull_lib, bull_indx, file, lib_indx)

char	*bull_indx,
	*bull_lib;	
long	displ_lib,
	*file,
	*frst_tim,		
	*lib_indx;
{
	long	status;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 * Open the bulletin library and index.
	 */

     	if (*frst_tim) {	
		*frst_tim = FALSE;
	     	lbr$ini_control(lib_indx, &LBR$C_UPDATE);
	 	status = lbr$open(lib_indx, mkdesc(bull_lib), 0, &def_lib);

		STATUS_FAIL(bull_lib, status);

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

		FILE_FAIL(bull_indx, *file);

		if (displ_lib)
			printf("\r\n%s\r\n", capital(bull_lib));
	}
}

close_lib(file, lib_indx)

long	file,
	lib_indx;
{
	close(file);	/* Close bulletin index. */
	lbr$close(&lib_indx);	/* Close bulletin library. */
}
                
do_edit(file, lib_indx, cur, poster)

char	poster[POSTER_LEN];
long	file,
	lib_indx;
struct	lnk_lst_struct	*cur;
{             
        char    exp_date[ASCTIM_LEN],
		nam[FILENAM_LEN], 		
		scratch[QUAL_LEN], 
		title[TITLE_LEN];		
        unsigned        exp_tim[2];
	long	lin_cnt, 
		status,
		valid;
	FILE	*txt;
	struct	indx_file_struct	buf;

	printf("\r%s %s\n", get_ascdate(cur->date), cur->title);

	if (get_str(scratch, TITLE_LEN - 1, "\r\n_Title: ", TRUE, 
	    FALSE, '\0') == RMS$_EOF)	/* Get title. */
		sys$exit(SS$_NORMAL);

	fix_str(title, scratch, TITLE_LEN - 1, TRUE, FALSE, '\0');

	/*
	 *      Get expiry date. Reprompt if invalid date is entered.
	 */

	do {
		valid = TRUE;

		if (get_str(scratch, ASCTIM_LEN, "\r\n_Expiry date: ", TRUE, 
		    TRUE, '\0') == RMS$_EOF)
			sys$exit(SS$_NORMAL);

		if (!*scratch)
			exp_tim[0] = exp_tim[1] = 0;
		else {
			fix_str(exp_date, scratch, ASCTIM_LEN, TRUE,
				FALSE, '\0');

		       	if (strlen(exp_date) >= 12)
				exp_date[11] = ' ';

			upcase(exp_date);
			status = sys$bintim(mkdesc(exp_date), exp_tim);

			if (!((status & STS$M_SUCCESS) >> STS$V_SUCCESS))
				valid = FALSE;
		}
	} while (!valid);

	if (get_str(scratch, FILENAM_LEN - 1, "\r\n_File: ", 
	    TRUE, FALSE, '\0') == RMS$_EOF)	/* Get text file. */
		sys$exit(SS$_NORMAL);

	fix_str(nam, scratch, FILENAM_LEN - 1, TRUE, FALSE, '\0');

	/*
	 * Editing both the library and index file.
	 */

	txt = fopen(nam, "r", "shr=get,put,upd");

	FILE_FAIL(nam, txt);	/* Fail if can't access text file. */

	lib$disable_ctrl(&LIB$M_CLI_CTRLY, &0);
	replace_lib(&lib_indx, txt, title, cur->msg_id, &lin_cnt);
	lseek(file, 0, 0);	/* Top of file. */

	while (read(file, (char *) &buf, INDX_LEN) != 0) {
		if (buf.msg_id == cur->msg_id) {

			/*
			 * 	Reposition rfa to the beginning of the record
			 * 	which was just read.
			 */

	    	    	if (lseek(file, 0, 1) == -1)	
				lseek(file, -INDX_LEN-1, 2);
			else    lseek(file, -INDX_LEN-1, 1);

			strcpy(buf.title, title);
			buf.exp_date[0] = exp_tim[0];
			buf.exp_date[1] = exp_tim[1];
			buf.lin_cnt = lin_cnt;
			strcpy(buf.poster, poster);
			write(file, (char *) &buf, INDX_LEN);
			break;
		}
	}

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

	fclose(txt);
}

del_bull(bull_lst, video_type, status)
              
long    video_type, 
	status;
struct	bull_lst_struct	*bull_lst;
{
	char	deflt[QUAL_LEN];
        long    file,
		frst_tim, 
		i,
		lib_indx,
		unmark_bull;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;
				
	/*
	 * 	Form the link list.
	 */

        lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
                cur = gen_dislst(i, bull_lst->bull_lib[i], 
				 bull_lst->bull_indx[i], "", ALL, "");

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst;
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 * 	If there are bulletins, let the user mark the one which
	 *	he wants to delete.
	 */

        if ((unmark_bull = total_bull(lnk_lst, UNMARK)) > 0) {

		/*
		 * 	Get /DEFAULT value. The value can be DATE which
		 * 	is the default, POSTER or SIZE.
		 */

		if (get_val(DEFAULT, 0, 0) == CLI$_PRESENT)
			get_val(DEFAULT, deflt, QUAL_LEN);
		else	*deflt = 'D';	/* Default=date. */

                if (video_type && (status & SS$_NORMAL)) {
                        setup_scr();
                        lnk_lst = scr_mrkbull(bull_lst, lnk_lst, INCR, 
					      ALL, "", *deflt);
                }
                else 	lnk_lst = lin_mrkbull(bull_lst, lnk_lst, INCR, 
					      ALL, "", *deflt);
        }
                                                             
        if (total_bull(lnk_lst, MARK) > 0) {
                if (video_type && (status & SS$_NORMAL)) {
                        smg$delete_pasteboard(&pstbrd_id, &1);              
                        smg$delete_virtual_keyboard(&keybrd_id);
                }

                cur = lnk_lst->dwn; 

		/*
		 * 	Delete all marked bulletin messages.
		 */

                do {
			i = cur->presnt_lib;
			frst_tim = TRUE;

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

                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck) {

                                if (cur->flag == MARK) {
					open_lib(&frst_tim, FALSE,
						 bull_lst->bull_lib[i],
						 bull_lst->bull_indx[i],
						 &file, &lib_indx);

			 		do_del(file, lib_indx, cur);
				}
                        }

			if (!frst_tim) 
				close_lib(file, lib_indx);

                        lib$enable_ctrl(&LIB$M_CLI_CTRLY, &0); 
                } while ((cur = cur->dwn) != lnk_lst);
        } 
        else if (!unmark_bull)
		lib$signal(BULL_NOMORE);
}

do_del(file, lib_indx, cur)

long	file,
	lib_indx;
struct	lnk_lst_struct	*cur;
{             
	long	status;
	struct	indx_file_struct	buf;

	delete_lib(&lib_indx, cur->msg_id);
	lseek(file, 0, 0);	/* Top of file. */

	while (read(file, (char *) &buf, INDX_LEN) != 0) {
		if (buf.msg_id == cur->msg_id) {

			/*
			 * 	Reposition rfa to the beginning of the record
			 * 	which was just read.
			 */

		    	if (lseek(file, 0, 1) == -1)
				lseek(file, -INDX_LEN-1, 2);
			else    lseek(file, -INDX_LEN-1, 1);
                            
			buf.status = DELETE;
			write(file, (char *) &buf, INDX_LEN);
			break;
		}
	}                                      
}

exp_bull(bull_lst)

struct  bull_lst_struct	*bull_lst;
{
        char    enter[2],
		prmpt[QUAL_LEN];		
        long    file, 
		i, 
		lib_indx, 		
		status,
		valid;
        struct  indx_file_struct	buf;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);
        
        for (i = 0; i <= bull_lst->last_bull; i++) {

		/*
		 * 	If /CONFIRM is present, expect answer 'y' or 'n'.
		 */

	        if (get_val(CONFRM_QUAL, 0, 0) & SS$_NORMAL) {
	                sprintf(prmpt, 
				"\r\nExpire bulletin messages in %s? [N] ", 
				capital(bull_lst->bull_lib[i]));

	                do {
	                        if (get_str(enter, 2, prmpt, TRUE, 
				    TRUE, 'n') == RMS$_EOF)
	                                sys$exit(SS$_NORMAL);

	                        valid = ((*enter = tolower(*enter)) == 'y' || 
					*enter == 'n');
	                } while (!valid);

	                if (*enter == 'n')
	                        continue;
		}

		lbr$ini_control(&lib_indx, &LBR$C_UPDATE);
	 	status = lbr$open(&lib_indx, mkdesc(bull_lst->bull_lib[i]), 0, 
				  &def_lib);

		STATUS_FAIL(bull_lst->bull_lib[i], status);

		file = open(bull_lst->bull_indx[i], O_RDWR, 0, 
		            "shr=get,put,upd", "tmo=30");

		FILE_FAIL(bull_lst->bull_indx[i], file);

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

                while (read(file, (char *) &buf, INDX_LEN) != 0) {
                        if (buf.status != DELETE && expire(buf.exp_date)) {
                                delete_lib(&lib_indx, buf.msg_id);
	
				/*
				 * 	Reposition rfa to the beginning of 
				 *	the record which was just read.
				 */

                                if (lseek(file, 0, 1) == -1)
                                        lseek(file, -INDX_LEN-1, 2);
                                else    lseek(file, -INDX_LEN-1, 1);
                                    
                                buf.status = DELETE;
                                write(file, (char *) &buf, INDX_LEN);
                        }
                }

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

                close(file);
                lbr$close(&lib_indx);
        }                                         
}

scr_brwsbull(bull_lst, key, how)

char    how,
	*key;
struct	bull_lst_struct	*bull_lst;
{
        char    *c,
		deflt[QUAL_LEN];
        long    *arg[3], 
		chro_seq, 
		enter,
		i, 
		lib_indx, 
		mark_bull, 
                scr_updbull(), 
		status,
		unmark_bull;		
	struct	lnk_lst_struct	*cur,
				*lnk_lst;			    	
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 *	Form the link list.
	 */

	lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
                cur = gen_dislst(i, bull_lst->bull_lib[i], 
				 bull_lst->bull_indx[i], 
				 get_lst(bull_lst->file, bull_lst->bull_nam[i], 
	     			 &bull_lst->lst_pos[i]), how, key);

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst;
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 *	If there are bulletins, let the user mark the one he
	 *	wants to browse.
	 */

        if ((unmark_bull = total_bull(lnk_lst, UNMARK)) != 0)
                setup_scr();

#ifdef CHRONO
		chro_seq = TRUE;
#else	
		chro_seq = FALSE;
#endif

	if (get_val(REVRS_QUAL, 0, 0) == CLI$_PRESENT)
		chro_seq = !chro_seq;

        if ((get_val(ALL_QUAL, 0, 0) & SS$_NORMAL) && unmark_bull) {
                cur = lnk_lst->dwn;

                do {
                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck)
                                cur->flag = MARK;
                } while ((cur = cur->dwn) != lnk_lst);
        }
        else if (unmark_bull == 1) {
                cur = lnk_lst->dwn;

                do {
                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck)
                                cur->flag = MARK;
                } while ((cur = cur->dwn) != lnk_lst);
        }
        else if (unmark_bull > 1) {

		/*
		 * 	Get /DEFAULT value. The value can be DATE which
		 * 	is the default, POSTER or SIZE.
		 */

		if (get_val(DEFAULT, 0, 0) == CLI$_PRESENT)
			get_val(DEFAULT, deflt, QUAL_LEN);
		else	*deflt = 'D';	/* Default=date. */


		if (chro_seq)
			lnk_lst = scr_mrkbull(bull_lst, lnk_lst, INCR, 
					      how, key, *deflt);
                else	lnk_lst = scr_mrkbull(bull_lst, lnk_lst, DECR, 
					      how, key, *deflt);
	}

	mark_bull = total_bull(lnk_lst, MARK);

	arg[0] = bull_lst; 	
	arg[1] = lnk_lst; 
	arg[2] = (mark_bull == 1 && 
		 total_lin(lnk_lst, MARK) <= END_ROW - 2) ? TRUE : FALSE;
	exit_handler(scr_updbull, arg);	/* Establish exit handler. */

        if (mark_bull > 0) {	/* Display bulletin messages. */
                for (cur = lnk_lst->dwn; 
		    cur != lnk_lst && enter != CTRL_Z; 
		    cur = cur->dwn) {
                        c = bull_lst->bull_lib[cur->presnt_lib];
                        lbr$ini_control(&lib_indx, &LBR$C_READ);

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

			STATUS_FAIL(c, status);

                        smg$set_display_scroll_region(&txt_id, &3, &END_ROW);

			for (cur = (chro_seq) ? cur->fwd : cur->bck; 
			     cur->dwn == NULL && enter != CTRL_Z;
			     cur = (chro_seq) ? cur->fwd : cur->bck) {

                                if (cur->flag == MARK) {
                                        mark_bull--;
					enter = scr_displ(c, &lib_indx, cur);

					if (enter == 'n' & 0xffffffff ||
					    enter == 'N' & 0xffffffff)
						continue;
					else 
					if (enter == 's' & 0xffffffff ||
					    enter == 'S' & 0xffffffff ||
					    enter == CTRL_Z)
						break;
					else 
					if (enter == 'u' & 0xffffffff ||
					    enter == 'U' & 0xffffffff) {
						cur->flag = 'C'; /* Cancel. */
						cur->read = NO;
					}
					else {
						enter = scr_getcmmd(bull_lst,
								lnk_lst, &cur,
								&c, &lib_indx,
								&mark_bull,
								chro_seq);
						if (enter == 's' & 0xffffffff ||
						    enter == 'S' & 0xffffffff)
							break;
					    	else 
						if (enter == 'u' & 0xffffffff ||
						    enter == 'U' & 0xffffffff) {
							cur->flag = 'C';
							cur->read = NO;
						}
					}
                                }
                        }

                        smg$set_display_scroll_region(&txt_id, &TOP_ROW, 
						      &END_ROW);
                        lbr$close(&lib_indx);

			if (enter == 's' & 0xffffffff ||
			    enter == 'S' & 0xffffffff) {
				for (cur = (chro_seq) ? cur->fwd : cur->bck; 
				    cur->dwn == NULL; 
				    cur = (chro_seq) ? cur->fwd : cur->bck)
                                        mark_bull = (cur->flag == MARK) ? 
						    mark_bull - 1 : mark_bull;
                        }
                }
        }
	else {
		switch (how) {

		case ALL:
		case LIB:
			if (*key && !unmark_bull)
				lib$signal(BULL_NOTFND);
			else if (!unmark_bull)
				lib$signal(BULL_NOMORE);
			break;

		default:
			if (!unmark_bull)  
				lib$signal(BULL_NOMORE);
			break;
		}
	}
}

lin_brwsbull(bull_lst, key, how)

char    how,
	*key;
struct	bull_lst_struct	*bull_lst;
{
        char    *c, 
 		deflt[QUAL_LEN],
		enter;
        long    *arg[2], 
		chro_seq, 
		i, 
		lib_indx, 
		lin_updbull(), 
		mark_bull, 
		status,
		unmark_bull;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;
	$DESCRIPTOR(def_lib, DEF_LIBNAM);

	/*
	 * 	Form the link list.
	 */

        lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
                cur = gen_dislst(i, bull_lst->bull_lib[i], 
				 bull_lst->bull_indx[i], 
                                 get_lst(bull_lst->file, bull_lst->bull_nam[i], 
				 &bull_lst->lst_pos[i]), how, key);

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst;
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 * 	If there are bulletins, let the user mark the one which
	 * 	he wants to browse.
	 */

        if ((unmark_bull = total_bull(lnk_lst, UNMARK)) != 0)
		setup_lin();

#ifdef CHRONO
		chro_seq = TRUE;
#else	
		chro_seq = FALSE;
#endif

	if (get_val(REVRS_QUAL, 0, 0) == CLI$_PRESENT)
		chro_seq = !chro_seq;

        if ((get_val(ALL_QUAL, 0, 0) & SS$_NORMAL) && unmark_bull) {
                cur = lnk_lst->dwn;

                do {
                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck)
                                cur->flag = MARK;
                } while ((cur = cur->dwn) != lnk_lst);
        }
        else if (unmark_bull == 1) {
                cur = lnk_lst->dwn;

                do {
                        for (cur = cur->bck; cur->dwn == NULL; cur = cur->bck)
                                cur->flag = MARK;
                } while ((cur = cur->dwn) != lnk_lst);
        }
        else if (unmark_bull > 1) {

		/*
		 * 	Get /DEFAULT value. The value can be DATE which
		 * 	is the default, POSTER or SIZE.
		 */

		if (get_val(DEFAULT, 0, 0) == CLI$_PRESENT)
			get_val(DEFAULT, deflt, QUAL_LEN);
		else	*deflt = 'D';	/* Default=date. */

		if (chro_seq)
			lnk_lst = lin_mrkbull(bull_lst, lnk_lst, INCR, 
					      how, key, *deflt);
                else	lnk_lst = lin_mrkbull(bull_lst, lnk_lst, DECR, 
					      how, key, *deflt);
	}

	arg[0] = bull_lst; 
	arg[1] = lnk_lst; 
	exit_handler(lin_updbull, arg);	/* Establish exit handler. */

	/*
	 * Display bulletin messages.
	 */

        if ((mark_bull = total_bull(lnk_lst, MARK)) > 0) {
                for (cur = lnk_lst->dwn; 
		    cur != lnk_lst && enter != CTRL_Z; 
		    cur = cur->dwn) {
                        c = bull_lst->bull_lib[cur->presnt_lib];
                        lbr$ini_control(&lib_indx, &LBR$C_READ);
                        status = lbr$open(&lib_indx, mkdesc(c), 0, &def_lib);

			STATUS_FAIL(c, status);

			for (cur = (chro_seq) ? cur->fwd : cur->bck; 
			    cur->dwn == NULL && enter != CTRL_Z;
			    cur = (chro_seq) ? cur->fwd : cur->bck) {
                                if (cur->flag == MARK) {
                                        mark_bull--;
					enter = lin_displ(c, &lib_indx, cur);

					if (enter == 'n')
						continue;
					else
					if (enter == 's' || enter == CTRL_Z)
						break;
					else
					if (enter == 'u') {
						cur->flag = 'C'; /* Cancel. */
						cur->read = NO;
					}
					else {
						enter = lin_getcmmd(bull_lst,
							lnk_lst, &cur, &c,
							&lib_indx, &mark_bull,
							chro_seq);

						if (enter == 's')
							break;
						else
						if (enter == 'u') {
							cur->flag = 'C';
							cur->read = NO;
						}
					}
                                }
                        }

                        lbr$close(&lib_indx);

                        if (enter == 's') {
				for (cur = (chro_seq) ? cur->fwd : cur->bck; 
				    cur->dwn == NULL; 
				    cur = (chro_seq) ? cur->fwd : cur->bck)
                                        mark_bull = (cur->flag == MARK) ? 
						    mark_bull - 1 : 
                                                    mark_bull;
                        }
                }
        }
	else {
		switch (how) {

		case ALL:
		case LIB:
			if (*key && !unmark_bull)
				lib$signal(BULL_NOTFND);
			else if (!unmark_bull)
				lib$signal(BULL_NOMORE);
			break;

		default:
			if (!unmark_bull)  
				lib$signal(BULL_NOMORE);
			break;
		}
	}
}

brief_bull(bull_lst)

struct	bull_lst_struct	*bull_lst;
{
        long    i;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;

	/*
	 * 	Form the link list.
	 */

	lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
                cur = gen_dislst(i, bull_lst->bull_lib[i], 
				 bull_lst->bull_indx[i], 
                                 get_lst(bull_lst->file, bull_lst->bull_nam[i], 
				 &bull_lst->lst_pos[i]), NEW, "");

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst;
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 * 	If there are bulletins, display them briefly.
	 */

        if (total_bull(lnk_lst, UNMARK) > 0) {
                cur = lnk_lst->dwn;
                printf("\r\n");

                do {
                        for (i = 0, cur = cur->bck;  
			     cur->dwn == NULL; 
			     cur = cur->bck, i++);

                        printf("\rThere %s %d new bulletin%s in %s\n", 
			       (i == 1) ? "is" : "are", i, (i == 1) ? "" : "s", 
			       capital(bull_lst->bull_lib[cur->presnt_lib]));
                } while ((cur = cur->dwn) != lnk_lst);
        }
	else	lib$signal(BULL_NOMORE);
}

new_bull(bull_lst)

struct	bull_lst_struct	*bull_lst;
{
        long    chro_seq,
		i;
	struct	lnk_lst_struct	*cur,
				*lnk_lst;

	/*
	 * 	Form the link list.
	 */

	lnk_lst = init_lst();

        for (i = 0; i <= bull_lst->last_bull; i++) {
                cur = gen_dislst(i, bull_lst->bull_lib[i], 
				 bull_lst->bull_indx[i], 
                                 get_lst(bull_lst->file, bull_lst->bull_nam[i], 
				 &bull_lst->lst_pos[i]), NEW, "");

                if (cur->fwd == cur)
                        continue;

		cur->presnt_lib = i;
                cur->up = lnk_lst->up;
                cur->dwn = lnk_lst;
                lnk_lst->up->dwn = cur;
                lnk_lst->up = cur;
        }

	/*
	 * 	If there are bulletins, display the title only.
	 */

        if (total_bull(lnk_lst, UNMARK) > 0) {

#ifdef CHRONO
		chro_seq = TRUE;
#else	
		chro_seq = FALSE;
#endif

		if (get_val(REVRS_QUAL, 0, 0) == CLI$_PRESENT)
			chro_seq = !chro_seq;

                cur = lnk_lst->dwn;

                do {
                        printf("\r\n%s\r\n", 
			       capital(bull_lst->bull_lib[cur->presnt_lib]));

			for (cur = (chro_seq) ? cur->fwd : cur->bck; 
			    cur->dwn == NULL;
			    cur = (chro_seq) ? cur->fwd : cur->bck) 
                                printf("\r%s %s\n", get_ascdate(cur->date), 
				       cur->title);
                } while ((cur = cur->dwn) != lnk_lst);
        }
	else	lib$signal(BULL_NOMORE);
}
