#module NEWSUTILITY "V5.6"

/*
**++
**  FACILITY:
**
**      NEWSUTILITY
**
**  ABSTRACT:
**
**      This module contain common support routines used by all other modules
**      of NEWS.
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.3    16-Jun-1988     GIH
**          Add creation of help output screen display, remove ENTER keypad
**          definition, change do_new_group to inherit default server
**          attributes
**
**      V5.5     7-Oct-1988     GIH
**          Change do_open_item to record local caching of served item only
**          if /KEEPREQUEST is sepcified.
**          Add write_access and auth_list as first pass of restricted
**          access controls to posting to newsgroups
**
**      V5.6    11-Nov-1988     GIH
**        - Add check_moderator to check if a user moderates at least 1
**          newsgroup.
**        - Add do_open_header to open the item header text.
**        - Add support for restricted newsgroups
**        - Speed up initial load of NEWS database
**
**--
**/

#include "newsinclude.h"
#include "newsdefine.h"
#include "newsextern.h"

#include jpidef
#include prvdef

globalref int newscmd;

static struct XABKEY xabkey_1,
                     xabkey_2,
                     xabkey_3,
                     xabkey_4;

static struct XABPRO xabpro_1,
                     xabpro_2;

static int screen_displays_setup = 0;

int *c$_tmphead = 0;         /* head of side list */

extern char dir_sincestr[];

/*
 *  c$alloc_tmp
 *
 *  Allocate temporary storage for use with c$ calls
 */

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

    c$_tmp = malloc(size + 4);
    *c$_tmp = c$_tmphead;
    c$_tmphead = c$_tmp;
    return(c$_tmphead + 1);
}

/*
 *  c$rfi
 *
 *  Generate a reference to a temp integer value
 */

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

    *tmp = value;
    return(tmp);
}

/*
 *  c$dsc
 *
 * Allocate a string descriptor in the temp arg area
 */

c$dsc(str)
    char *str;
{
    struct dsc$descriptor *tmpdsc;

    tmpdsc = c$alloc_tmp(8);
    tmpdsc->dsc$w_length = strlen(str);
    tmpdsc->dsc$b_dtype = DSC$K_DTYPE_T;
    tmpdsc->dsc$b_class = DSC$K_CLASS_S;
    tmpdsc->dsc$a_pointer = str;
    return(tmpdsc);
}

/*
 *  c$cks
 *
 *  Check return status, signal errors, and free the temp argument area
 */

c$cks(status)
    int status;
{
    int *tmp;

    if (!(status & 1)) lib$signal(status);
    while (c$_tmphead) {
        tmp = *c$_tmphead;
        free(c$_tmphead);
        c$_tmphead = tmp;
        }
    return(status);
}

/*
 *  nosysprv
 *
 *  Turn off installed sysprv - BUT if user already had sysprv from AUTHORIZE
 *  then leave it on (as there is no change in user functionality)
 */

nosysprv()
{
    unsigned int authprivs[2], msysprv[2] ;
    int item = JPI$_AUTHPRIV;

    msysprv[0] = (1 << PRV$V_SYSPRV);
    msysprv[1] = 0;
    c$cks(lib$getjpi(&item,0,0,&authprivs,0,0));
    if (!(authprivs[0] & (1 << PRV$V_SYSPRV))) c$cks(sys$setprv(0,msysprv,0,0));
}

/*
 *  sysprv
 *
 *  Turn sysprv on as a temp priv - assumes image was installed with SYSPRV
 */

sysprv()
{
    unsigned int msysprv[2];

    msysprv[0] = (1 << PRV$V_SYSPRV);
    msysprv[1] = 0;
    c$cks(sys$setprv(1,msysprv,0,0));
}

/*
 *  s_to_lower
 *
 *  convert a string to lower case
 */

char *s_to_lower(s)
    char *s;
{
    char *save = s;
    while (*s) {
        *s = tolower(*s);
        s++;
        }
    return(save);
}

/*
 *  clear_err_line
 *
 *  error line management
 */

clear_err_line()
{
    if (screen_active) smg$erase_line(&trailer_vd,c$rfi(3),c$rfi(1));
}

/*
 *  cli_catch
 *
 *  Condition handler for CLI signals
 */

cli_catch(sigargs,mechargs)
    unsigned int sigargs[],
                 mechargs[];
{
    globalvalue CLI$_FACILITY;
    char buf1[80],buf2[80];
    $DESCRIPTOR(desc1,buf1);
    $DESCRIPTOR(desc2,buf2);
    unsigned char otherargs[4];
    unsigned short strlen ;

    if ((sigargs[1] >> 16 & 0XFFF) == CLI$_FACILITY) {
        sys$getmsg(sigargs[1],&strlen,&desc1,15,otherargs);
        desc1.dsc$w_length = strlen ;
        sys$faol(&desc1,&strlen,&desc2,&sigargs[2]);
        desc2.dsc$w_length = strlen ;
        if (screen_active) smg$put_chars(&trailer_vd,&desc2,c$rfi(3),c$rfi(1),c$rfi(1),c$rfi(SMG$M_REVERSE),0,0);
        else lib$put_output(&desc2);
        return(SS$_CONTINUE);
        }
    else return(SS$_RESIGNAL);
}

/*
 *  closefiles
 *
 *  Close rms files, write the register file, shutdown the screen, and
 *  send any printer output to a nominated queue.
 */

closefiles()
{
    sysprv();
    sys$close(&itmfab);
    sys$close(&grpfab);
    nosysprv();
    close_hist_file();
    write_reg_file();
    noscreen();
    print_exit();
}

/*
 *  create_keydefs
 *
 *  Create key definitions
 */

create_keydefs()
{
    smg$add_key_def(&keytab,c$dsc("PF1"),0,0,0,c$dsc("GOLD"));
    smg$add_key_def(&keytab,c$dsc("PF2"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("HELP"),0);
    smg$add_key_def(&keytab,c$dsc("PF3"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("SKIP"),0);
    smg$add_key_def(&keytab,c$dsc("PF4"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("SKIP/NEWSGROUP"),0);
    smg$add_key_def(&keytab,c$dsc("PF4"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE),c$dsc("SKIP/FOLLOWUP"),0);
    smg$add_key_def(&keytab,c$dsc("KP0"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/NEW"),0);
    smg$add_key_def(&keytab,c$dsc("KP0"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/HEADER/NEW"),0);
    smg$add_key_def(&keytab,c$dsc("KP1"),0,c$rfi(0),c$dsc("READ "),0);
    smg$add_key_def(&keytab,c$dsc("KP1"),c$dsc("GOLD"),c$rfi(0),c$dsc("READ/HEADER "),0);
    smg$add_key_def(&keytab,c$dsc("KP2"),0,c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("DOWN"),0);
    smg$add_key_def(&keytab,c$dsc("KP2"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("BOTTOM"),0);
    smg$add_key_def(&keytab,c$dsc("KP3"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/NEXT"),0);
    smg$add_key_def(&keytab,c$dsc("KP3"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/NEXT/HEADER"),0);
    smg$add_key_def(&keytab,c$dsc("KP4"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR"),0);
    smg$add_key_def(&keytab,c$dsc("KP5"),0,c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("UP"),0);
    smg$add_key_def(&keytab,c$dsc("KP5"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("TOP"),0);
    smg$add_key_def(&keytab,c$dsc("KP6"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR/REG"),0);
    smg$add_key_def(&keytab,c$dsc("KP7"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR"),0);
    smg$add_key_def(&keytab,c$dsc("KP7"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE),c$dsc("PRINT"),0);
    smg$add_key_def(&keytab,c$dsc("KP8"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR/NEW"),0);
    smg$add_key_def(&keytab,c$dsc("KP8"),c$dsc("GOLD"),c$rfi(0),c$dsc("EXTRACT "),0);
    smg$add_key_def(&keytab,c$dsc("KP9"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR/REG"),0);
    smg$add_key_def(&keytab,c$dsc("KP9"),c$dsc("GOLD"),c$rfi(0),c$dsc("EXTRACT/ALL "),0);
    smg$add_key_def(&keytab,c$dsc("MINUS"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR/ALL"),0);
    smg$add_key_def(&keytab,c$dsc("MINUS"),c$dsc("GOLD"),c$rfi(0),c$dsc("EXTRACT/APPEND "),0);
    smg$add_key_def(&keytab,c$dsc("COMMA"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DIR/ALL"),0);
    smg$add_key_def(&keytab,c$dsc("PERIOD"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/NEW/FOLLOWUP"),0);
    smg$add_key_def(&keytab,c$dsc("PERIOD"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE),c$dsc("READ/NEW/FOLLOWUP/HEADER"),0);
    smg$add_key_def(&keytab,c$dsc("HELP"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("HELP"),0);
    smg$add_key_def(&keytab,c$dsc("FIND"),0,c$rfi(0),c$dsc("SEARCH"),0);
    smg$add_key_def(&keytab,c$dsc("INSERT_HERE"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("REGISTER"),0);
    smg$add_key_def(&keytab,c$dsc("REMOVE"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("DEREGISTER"),0);
    smg$add_key_def(&keytab,c$dsc("SELECT"),0,c$rfi(SMG$M_KEY_TERMINATE),c$dsc("SELECT"),0);
    smg$add_key_def(&keytab,c$dsc("PREV_SCREEN"),0,c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("UP 18"),0);
    smg$add_key_def(&keytab,c$dsc("PREV_SCREEN"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("TOP"),0);
    smg$add_key_def(&keytab,c$dsc("NEXT_SCREEN"),0,c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("DOWN 18"),0);
    smg$add_key_def(&keytab,c$dsc("NEXT_SCREEN"),c$dsc("GOLD"),c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("BOTTOM"),0);
    smg$add_key_def(&keytab,c$dsc("CTRLW"),0,c$rfi(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO),c$dsc("REFRESH"),0);
}

/*
 *  cur_down_grp
 *
 *  Move the group cursor down by 1 newsgroup - (set the level to 1!)
 */

cur_down_grp(n,refresh)
    int n;
    int refresh;
{
    int ng,
        png = curr_g,
        ret_val = 0;

    set_level(1);
    if (n <= 0) return(0);

    if (!curr_g) return(0);
    if (ga[curr_g]->grp_display_indx) {
        for (ng = curr_g+1; ng <= ga_size; ++ng) {
            if (ga[ng]->grp_display_indx) {
                png = ng;
                ++ret_val;
                if (!--n) break;
                }
            }
        curr_g = png;
        if (screen_active) {
            if (g_arrow) smg$put_chars(&grp_vd,c$dsc("  "),&g_arrow,c$rfi(1),0,0,0,0);
            g_arrow = ga[curr_g]->grp_display_indx;
            smg$put_chars(&grp_vd,c$dsc("->"),&g_arrow,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&grp_vd,&grp_paste,g_arrow,refresh,grp_display_size);
            }
        return(ret_val);
        }
    else return(0);
}

/*
 *  cur_down_itm
 *
 *  Move the item cursor down by n items
 */

cur_down_itm(g,n,refresh)
    int g,
        n;
    int refresh;
{
    int ng,
        png,
        ret_val = 0;

    if (n <= 0) return(0);

    if ((g) && (ga[g]->grp_ia)) {
        if (!(png = ga[g]->grp_c_itm)) return(0);
        for (ng = ga[g]->grp_c_itm+1; ng <= ga[g]->grp_count; ++ng) {
            png = ng;
            ++ret_val;
            if (!--n) break;
            }
        if ((screen_active) && (ga[g]->grp_iavdsize)) {
            if (ga[g]->grp_c_itm) smg$put_chars(&ga[g]->grp_iavd,c$dsc("  "),c$rfi(ga[g]->grp_c_itm),c$rfi(1),0,0,0,0);
            smg$put_chars(&ga[g]->grp_iavd,c$dsc("->"),&png,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,png,refresh,ga[g]->grp_count);
            }
        ga[g]->grp_c_itm = png;
        if ((curr_g == g) && (news_context > 1)) {
            curr_i = png;
            set_level(2);
            }
        return(ret_val);
        }
    else return(0);
}

/*
 *  cur_set_grp
 *
 *  set the group pointer to g
 */

cur_set_grp(g)
    int g;
{
    curr_g = g;
    if (screen_active) {
        if ((curr_g) && (g_arrow == ga[curr_g]->grp_display_indx)) {
            position_display(&grp_vd,&grp_paste,g_arrow,0,grp_display_size);
            return;
            }
        if (g_arrow) smg$put_chars(&grp_vd,c$dsc("  "),&g_arrow,c$rfi(1),0,0,0,0);
        if (curr_g) {
            g_arrow = ga[curr_g]->grp_display_indx;
            if (g_arrow) {
                smg$put_chars(&grp_vd,c$dsc("->"),&g_arrow,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                position_display(&grp_vd,&grp_paste,g_arrow,0,grp_display_size);
                }
            }
        else g_arrow = 0;
        }
}

/*
 *  cur_set_itm
 *
 *  set the item pointer to item number i
 */

cur_set_itm(g,i)
    int g,
        i;
{
    if (i < ga[g]->grp_c_itm) cur_up_itm(g,ga[g]->grp_c_itm - i,0);
    else if (i > ga[g]->grp_c_itm) cur_down_itm(g,i - ga[g]->grp_c_itm,0);
    else {
        if ((screen_active) && (ga[g]->grp_iavdsize)) {
            smg$put_chars(&ga[g]->grp_iavd,c$dsc("->"),c$rfi(ga[g]->grp_c_itm),c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,ga[g]->grp_c_itm,0,ga[g]->grp_count);
            }
        if ((curr_g == g) && (news_context > 1)) {
            curr_i = ga[g]->grp_c_itm;
            set_level(2);
            }
        }
}

/*
 *  cur_up_grp
 *
 *  Move the group cursor down by 1 newsgroup - (set the level to 1!)
 */

cur_up_grp(n,refresh)
    int n;
    int refresh;
{
    int ng,
        png = curr_g,
        ret_val = 0;

    set_level(1);
    if (n <= 0) return(0);

    if (!curr_g) return(0);
    if (ga[curr_g]->grp_display_indx) {
        for (ng = curr_g-1; ng > 0 ; --ng) {
            if (ga[ng]->grp_display_indx) {
                png = ng;
                ++ret_val;
                if (!--n) break;
                }
            }
        curr_g = png;
        if (screen_active) {
            if (g_arrow) smg$put_chars(&grp_vd,c$dsc("  "),&g_arrow,c$rfi(1),0,0,0,0);
            g_arrow = ga[curr_g]->grp_display_indx;
            smg$put_chars(&grp_vd,c$dsc("->"),&g_arrow,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&grp_vd,&grp_paste,g_arrow,refresh,grp_display_size);
            }
        return(ret_val);
        }
    else return(0);
}

/*
 *  cur_up_itm
 *
 *  Move the item cursor up by n items
 */

cur_up_itm(g,n,refresh)
    int g,
        n;
    int refresh;
{
    int ng,
        png,
        ret_val = 0;

    if (n <= 0) return(0);

    if ((g) && (ga[g]->grp_ia) && (ga[g]->grp_count)) {
        png = ga[g]->grp_c_itm;
        for (ng = ga[g]->grp_c_itm-1; ng > 0; --ng) {
            png = ng;
            ++ret_val;
            if (!--n) break;
            }
        if ((screen_active) && (ga[g]->grp_iavdsize)) {
            if (ga[g]->grp_c_itm) smg$put_chars(&ga[g]->grp_iavd,c$dsc("  "),c$rfi(ga[g]->grp_c_itm),c$rfi(1),0,0,0,0);
            smg$put_chars(&ga[g]->grp_iavd,c$dsc("->"),&png,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,png,refresh,ga[g]->grp_count);
            }
        ga[g]->grp_c_itm = png;
        if ((curr_g == g) && (news_context > 1)) {
            curr_i = png;
            set_level(2);
            }
        return(ret_val);
        }
    else return(0);
}

/*
 *  get_input
 *
 *  much the same as lib$get_input - but uses SMG display
 */

get_input(get_str,prompt_str,out_len)
    struct dsc$descriptor *get_str,
                          *prompt_str;
    unsigned short *out_len;
{
    return(get_input_dflt(get_str,prompt_str,out_len,0,0));
}

/*
 *  get_input_dflt
 *
 *  get input, with default input string supplied
 */

get_input_dflt(get_str,prompt_str,out_len,inistr,trm)
    struct dsc$descriptor *get_str,
                          *prompt_str,
                          *inistr;
    unsigned short *out_len,
                   *trm;
{
    int status;

    if (screen_active) {
        smg$erase_display(&trailer_vd,c$rfi(2),c$rfi(1),c$rfi(2),c$rfi(80));
        smg$set_cursor_abs(&trailer_vd,c$rfi(2),c$rfi(1));
        smg$end_pasteboard_update(&pid);
        edit_m_on();
        }
    status = smg$read_composed_line(&kid,&keytab,get_str,prompt_str,out_len,((screen_active) ? &trailer_vd : 0),0,inistr,0,0,0,trm);
    if (screen_active) {
        edit_m_off();
        smg$begin_pasteboard_update(&pid);
        }
    return((status == SMG$_EOF) ? RMS$_EOF : status);
}

/*
 *  do_newg
 *
 *  Create newsgroup if it does not already exist.
 */

unsigned short do_newg(g,prompt)
    char *g;
    int prompt;
{
    GRP savegrp;
    int new_mem = 0;
    char s[SUBJLEN];

    if ((!g) || (!*g)) return(0);

    util_cvrt(s,g);
    grprab.rab$l_kbf = s;
    grprab.rab$b_ksz = SUBJLEN;
    grprab.rab$b_krf = 0;
    grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    grprab.rab$b_rac = RAB$C_KEY;

    if (!(sys$get(&grprab) & 1)) {
        int acpt;

        if (no_priv()) return(0);
        if (!auto_cre_grp) return(0);

        acpt = sys_accept_group(s);

        if (prompt) {
            sprintf(err_oline,"\tNewsgroup: %s",s);
            if (!acpt) strcat(err_oline," (NEWS.SYS Filter rejects newsgroup name)");
            strcat(err_oline,"\n");
            err_line(err_oline);
            status = get_input(&command,c$dsc("Create Newsgroup? [n]:"),&response_length);
            if ((!(status & 1)) || (!response_length)) return(0);
            if (toupper(*response) != 'Y') return(0);
            }
        else if (!acpt) return(0);

        sprintf(itm_fname,Grp_template,util_dir(s));
        sysprv();
        if (!(lib$create_dir(c$dsc(itm_fname),0,0,0,0,0) & 1)) return(0);
        nosysprv();
        new_mem = 1;
        savegrp = newsgrp;
        grprab.rab$l_kbf = c$rfi(0);
        grprab.rab$b_ksz = 2;
        grprab.rab$b_krf = 1;
        grprab.rab$l_rop = RAB$M_WAT;
        grprab.rab$b_rac = RAB$C_KEY;
        _c$cks(sys$get(&grprab));

        ++newsgrp.grp_count;
        time(&newsgrp.grp_entdate);
        savegrp.grp_num = ++newsgrp.grp_topnum;
        savegrp.grp_loccoplife = newsgrp.grp_loccoplife;
        savegrp.grp_srvproto = newsgrp.grp_srvproto;
        savegrp.grp_holdcop = newsgrp.grp_holdcop;
        strcpy(savegrp.grp_server,newsgrp.grp_server);
        _c$cks(sys$update(&grprab));

        newsgrp = savegrp;
        util_cpy(newsgrp.grp_name,s);
        newsgrp.grp_topnum = 0;
        newsgrp.grp_count = 0;
        time(&newsgrp.grp_credate);
        time(&newsgrp.grp_entdate);
        newsgrp.grp_life = newsgrp.grp_itmlife = 0;
        newsgrp.grp_reg = newsgrp.grp_unread = 0;
        newsgrp.grp_ia = newsgrp.grp_iasize = newsgrp.grp_iavd = 0;
        newsgrp.grp_iavdsize = newsgrp.grp_iapaste = newsgrp.grp_c_itm = 0;
        newsgrp.grp_display_indx = newsgrp.grp_local = 0;
        newsgrp.grp_moderator = 0;

        grprab.rab$w_rsz = sizeof newsgrp;
        _c$cks(sys$put(&grprab));
        }
    else if (!ga_search_name(s)) new_mem = 1;
    if (new_mem) do_new_mem_grp();
    return(newsgrp.grp_num);
}

/*
 *  do_new_group
 *
 *  Create newsgroup if it does not already exist.
 */

do_new_group(g,prompt,retg)
    char *g;
    int prompt;
    unsigned short *retg;
{
    char locg[2048],
         *cp1 = locg,
         *cp2;

    strcpy(locg,g);
    *retg = 0;
    while (cp2 = strchr(cp1,',')) {
        *cp2++ = '\0';
        if (*retg = do_newg(cp1,prompt)) *++retg = 0;
        cp1 = cp2;
        }
    if (*retg = do_newg(cp1,prompt)) *++retg = 0;
}

/*
 *  do_openi
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

FILE *do_openi(mode,server_name,protected)
    char *mode,
         *server_name;
    int protected;
{
    FILE *tmp_fpr;
    int nmask = 022, omask;

    if (protected) nmask = 077;
    if (*mode == 'w') {
        omask = umask(nmask);
        sysprv();
        }
    else if (protected) sysprv();

    if (server_name) *server_name = 0;
    if ((tmp_fpr = fopen(itm_fname,mode,"rat=cr","rfm=var")) && (server_name)) *server_name = 1;
    if (*mode == 'w') {
        umask(omask);
        nosysprv();
        }
    else if (protected) nosysprv();
    return(tmp_fpr);
}

/*
 *  do_oitem
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

FILE *do_oitem(itm,mode,grp_name,server_name,protected)
    ITM *itm;
    char *mode, *grp_name, server_name;
    int protected;
{
    sprintf(itm_fname,Itm_template,util_dir(grp_name),itm->itm_num);
    return(do_openi(mode,server_name,protected));
}

/*
 *  file_copy
 *
 *  Use RMS to copy a file.
 */

file_copy(from,to)
char *from,
     *to;
{
    struct FAB infab,
        outfab;
    struct RAB inrab,
        outrab;

    struct XABDAT inxabdat,
        outxabdat;

    struct XABFHC inxabfhc,
        outxabfhc;

    struct XABRDT inxabrdt,
        outxabrdt;

    struct XABPRO inxabpro,
        outxabpro;

    int status;

    char rec_buff[4096];

    short rec_size = 4096;

    infab = cc$rms_fab;
    infab.fab$l_fna = from;
    infab.fab$b_fns = strlen(from);
    infab.fab$b_fac = FAB$M_BIO | FAB$M_GET ;
    infab.fab$l_xab = &inxabdat;

    inxabdat = cc$rms_xabdat;
    inxabdat.xab$l_nxt = &inxabfhc;

    inxabfhc = cc$rms_xabfhc;
    inxabfhc.xab$l_nxt = &inxabrdt;

    inxabrdt = cc$rms_xabrdt;
    inxabrdt.xab$l_nxt = &inxabpro;

    inxabpro = cc$rms_xabpro;

    inrab = cc$rms_rab;
    inrab.rab$l_fab = &infab;
    inrab.rab$l_bkt = 0;
    inrab.rab$l_ubf = rec_buff;
    inrab.rab$w_usz = rec_size;

    outfab = cc$rms_fab;
    outfab.fab$l_fna = to;
    outfab.fab$b_fns = strlen(to);
    outfab.fab$l_fop = FAB$M_CBT;
    outfab.fab$w_mrs = rec_size;
    outfab.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
    outfab.fab$b_rat = FAB$M_CR;
    outfab.fab$l_xab = &outxabdat;

    outxabdat = cc$rms_xabdat;
    outxabdat.xab$l_nxt = &outxabfhc;

    outxabfhc = cc$rms_xabfhc;
    outxabfhc.xab$l_nxt = &outxabrdt;

    outxabrdt = cc$rms_xabrdt;
    outxabrdt.xab$l_nxt = &outxabpro;

    outxabpro = cc$rms_xabpro;

    outrab = cc$rms_rab;
    outrab.rab$l_fab = &outfab;
    outrab.rab$l_bkt = 0;
    outrab.rab$l_rbf = rec_buff;

    sysprv();

    if (!((status = sys$open(&infab)) & 1)) return(status);

    outfab.fab$l_alq = infab.fab$l_alq;
    outfab.fab$w_deq = infab.fab$w_deq;
    outfab.fab$b_fsz = infab.fab$b_fsz;
    outfab.fab$l_mrn = infab.fab$l_mrn;
    outfab.fab$w_mrs = infab.fab$w_mrs;
    outfab.fab$b_org = infab.fab$b_org;
    outfab.fab$b_rat = infab.fab$b_rat;
    outfab.fab$b_rfm = infab.fab$b_rfm;

    outxabdat.xab$w_rvn = inxabdat.xab$w_rvn + 1;

    outxabfhc.xab$b_rfo = inxabfhc.xab$b_rfo;
    outxabfhc.xab$b_atr = inxabfhc.xab$b_atr;
    outxabfhc.xab$w_lrl = inxabfhc.xab$w_lrl;
    outxabfhc.xab$b_bkz = inxabfhc.xab$b_bkz;
    outxabfhc.xab$b_hsz = inxabfhc.xab$b_hsz;
    outxabfhc.xab$w_mrz = inxabfhc.xab$w_mrz;
    outxabfhc.xab$w_dxq = inxabfhc.xab$w_dxq;
    outxabfhc.xab$l_sbn = 0;

    outxabrdt.xab$w_rvn = inxabrdt.xab$w_rvn + 1;

    outxabpro.xab$w_pro = inxabpro.xab$w_pro;

    if (!((status = sys$connect(&inrab)) & 1)) return(sys$close(&infab),nosysprv(),status);
    if (!((status = sys$create(&outfab)) & 1)) return(sys$close(&infab),nosysprv(),status);
    if (!((status = sys$connect(&outrab)) & 1)) return(sys$close(&infab),sys$close(&outfab),nosysprv(),status);
    do {
        status = sys$read(&inrab);

        if (status & 1) {
            outrab.rab$w_rsz = inrab.rab$w_rsz;
            status = sys$write(&outrab);
            }
        } while (status & 1);
    if (status == RMS$_EOF) status = SS$_NORMAL;

    sys$close(&infab);
    sys$close(&outfab);
    nosysprv();
    return(status);
}

struct rms_file {
    struct FAB f;
    struct RAB r;
    struct XABPRO p;
    };

rms_open(nam)
    char *nam;
{
    struct rms_file *i;

    i = malloc(sizeof *i);
    i->f = cc$rms_fab;
    i->f.fab$b_fac = FAB$M_GET;
    i->f.fab$l_fna = nam;
    i->f.fab$b_fns = strlen(nam);

    i->r = cc$rms_rab;
    i->r.rab$l_fab = &(i->f);

    if (sys$open(&(i->f)) & 1) {
        if (sys$connect(&(i->r)) & 1) return(i);
        else return(sys$close(&(i->f)),free(i),0);
        }
    else return(free(i),0);
}

rms_get(b,size,i)
    char *b;
    int size;
    struct rms_file *i;
{
    i->r.rab$l_ubf = b;
    i->r.rab$w_usz = size;
    status = sys$get(&(i->r));
    if (status & 1) b[i->r.rab$w_rsz] = '\0';
    return(status & 1);
}

rms_create(nam,prot)
    char *nam;
    unsigned int prot;
{
    struct rms_file *i;

    i = malloc(sizeof *i);
    i->f = cc$rms_fab;
    i->f.fab$b_fac = FAB$M_PUT;
    i->f.fab$l_fna = nam;
    i->f.fab$b_fns = strlen(nam);
    i->f.fab$b_org = FAB$C_SEQ;
    i->f.fab$b_rat = FAB$M_CR;
    i->f.fab$b_rfm = FAB$C_VAR;
    i->f.fab$l_xab = &(i->p);
    i->p = cc$rms_xabpro;
    i->p.xab$w_pro = prot;

    i->r = cc$rms_rab;
    i->r.rab$l_fab = &(i->f);

    sysprv();
    if (sys$create(&(i->f)) & 1) {
        if (sys$connect(&(i->r)) & 1) return(nosysprv(),i);
        else return(sys$close(&(i->f)),free(i),nosysprv(),0);
        }
    else return(free(i),nosysprv(),0);
}

rms_put(s,i)
    char *s;
    struct rms_file *i;
{
    i->r.rab$l_rbf = s;
    i->r.rab$w_rsz = strlen(s);
    return(sys$put(&(i->r)) & 1);
}

rms_close(i)
    struct rms_file *i;
{
    sys$close(&(i->f));
    free(i);
}

/*
 *  do_new_item
 *
 *  Create new item in newsgroup
 */

static char nst[] = "";

unsigned int do_new_item(g,id,subj,fnam,new_flag,skip_history,linecount)
    short *g;
    char *id,
         *subj,
         *fnam;
    int new_flag,
        skip_history,
        linecount;
{
    ITM savitm;
    struct stat sbuffer;
    short cre_grp[100],
          cre_itm[100];
    int cur_time,
        ga_indx,
        i,
        g_count = 0;
    int ofile = 0,
        ifile = 0;

    unsigned short *gptr;

    GRP_MEM_PTR gap = 0;
    char xrefline[250],
         ibuf[1024],
         mod[100],
         app_line[132],
         id_key[IDLEN + 2];

    status = 1;
    *mod = '\0';
    *app_line = '\0';
    if ((!fnam) || (!*fnam)) {
        strcpy(no_new_item,"No input filename specified");
        return(0X10000000);
        }

    if ((!id) || (!*id)) {
        strcpy(no_new_item,"No message identifier string given");
        return(0X20000000);
        }
    s_to_lower(id);

    if (!*g) {
        strcpy(no_new_item,"No newsgroup index value specified");
        return(0X3000000);
        }

    sysprv();
    if (stat(fnam,&sbuffer)) {
        strcpy(no_new_item,"Cannot access input file");
        nosysprv();
        return(0X40000000);
        }
    nosysprv();

    if (!sbuffer.st_size) {
        strcpy(no_new_item,"Input file is empty");
        return(0X50000000);
        }

    sysprv();
    if (!(ifile = rms_open(fnam))) {
        strcpy(no_new_item,"Cannot open input file (read access)");
        nosysprv();
        return(0X60000000);
        }
    nosysprv();
    util_idcpy(id_key,id);

    if ((!skip_history) && (hist_check(id_key))) {
        strcpy(no_new_item,"Cannot add to Newsitem index file");
        return(0xb0010001);
        }

    if  (!subj) subj = nst;
    sprintf(xrefline,"Xref: %s",usr_nodename);
    time(&cur_time);
    newsitm.itm_date = cur_time;
    newsitm.itm_new = new_flag ? 1 : 0;
    newsitm.itm_size = linecount;
    newsitm.itm_life = mail_add_expiry;
    newsitm.itm_unread = 1;
    newsitm.itm_locdate = cur_time;
    util_cpy(newsitm.itm_title,subj);
    util_idcpy(newsitm.itm_id,id);

    itmrab.rab$b_krf = 1;
    itmrab.rab$l_kbf = id_key;
    itmrab.rab$b_ksz = IDLEN + 2;
    itmrab.rab$l_rop = RAB$M_WAT;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$w_rsz = sizeof newsitm;

    grprab.rab$b_ksz = 2;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    for (; *g ; ++g) {
        grprab.rab$l_kbf = g;

        if (!((status = sys$get(&grprab)) & 1)) {
            status &= 0Xfffffff;
            status += 0X70000000;
            strcpy(no_new_item,"Newsgroup not located");
            continue;
            }

        if ((!net_news) && newsgrp.grp_moderator) {
            sprintf(mod,"%s@%s",usr_username,Node_address);
            forward_posting = 0;
            if (!strcmp(mod,moderator_address(newsgrp.grp_name))) {
                forward_posting = 1;
                sprintf(app_line,"Approved: %s",mod);
                }
            else {
                char mail_cmd[132];

                rms_close(ifile);
                sprintf(mail_cmd,"MAIL/NOEDIT/SELF %s/SUBJECT=\"NEWS Posting (to Newsgroup Moderator)\" %s",
                    fnam,add_transform(moderator_address(newsgrp.grp_name)));
                err_line("Post to Moderator - Spawning MAIL task .. please wait");
                if (screen_active) {
                    display_brdcst(2);
                    no_broad_trap();
                    smg$end_pasteboard_update(&pid);
                    }
                _c$cks(lib$spawn(c$dsc(mail_cmd),0,0,0,0,0,0,0,0,0,0,0));
                if (screen_active) {
                    smg$begin_pasteboard_update(&pid);
                    broad_trap();
                    }
                sysprv();
                ifile = rms_open(fnam);
                nosysprv();
                continue;
                }
            }
        if ((net_news) && newsgrp.grp_moderator && (!itm_approved)) {
            sys$free(&grprab);
            status = 2;
            strcpy(no_new_item,"Non-Approved posting to Moderated Newsgroup");
            continue;
            }

        if ((ga_indx = ga_locate(*g)) > 0) gap = ga[ga_indx];
        else {
            do_new_mem_grp();
            if (!(ga_indx = ga_locate(*g))) {
                sys$free(&grprab);
                status = 2;
                strcpy(no_new_item,"Newsgroup not located (in local store)");
                continue;
                }
            gap = ga[ga_indx];
            }

        newsitm.itm_grp = newsgrp.grp_num;
        gptr = &id_key[IDLEN];
        *gptr =newsitm.itm_grp;

        savitm = newsitm;

        if (sys$get(&itmrab) & 1) {
            if (!newsitm.itm_locdate) {
                time(&newsitm.itm_locdate);
                sys$update(&newsitm);
                savitm.itm_num = newsitm.itm_num;
                newsitm = savitm;
                }
            else {
                status &= 0Xfffffff;
                status += 0XB0000000;
                strcpy(no_new_item,"Cannot add to Newsitem index file");
                newsitm = savitm;
                continue;
                }
            }
        else {
            newsitm = savitm;
            ++newsgrp.grp_count;
            newsgrp.grp_entdate = cur_time;
            newsitm.itm_num = ++newsgrp.grp_topnum;
            itmrab.rab$l_rbf = &newsitm;
            itmrab.rab$w_rsz = sizeof newsitm;
            if (!((status = sys$put(&itmrab)) & 1)) {
                status &= 0Xfffffff;
                status += 0XB0000000;
                strcpy(no_new_item,"Cannot add to Newsitem index file");
                continue;
                }
            if (!((status = sys$update(&grprab)) & 1)) {
                itmrab.rab$l_kbf = &(newsitm.itm_num);
                itmrab.rab$b_ksz = 4;
                itmrab.rab$b_krf = 0;
                itmrab.rab$l_rop = RAB$M_WAT;
                itmrab.rab$b_rac = RAB$C_KEY;
                if (sys$find(&itmrab) & 1) sys$delete(&itmrab);

                status &= 0Xfffffff;
                status += 0XA0000000;
                strcpy(no_new_item,"Cannot update Newsgroup index file");
                continue;
                }
            gap->grp_topnum = newsgrp.grp_topnum;
            savitm.itm_num = newsitm.itm_num;
            do_new_mem_itm(ga_indx,gap);
            newsitm.itm_num = savitm.itm_num;
            }
        cre_itm[g_count] = newsitm.itm_num;
        cre_grp[g_count++] = ga_indx;
        sprintf(err_oline," %s:%d",gap->grp_name,newsitm.itm_num);
        strcat(xrefline,err_oline);
        }

    *itm_fname = '\0';
    if (!g_count) {
        if (ifile) rms_close(ifile);
        return((!status) ? (strcpy(no_new_item,"No Valid Newsgroups"),status = 1) : status);
        }

    for (i = 0 ; i < g_count; ++i) {
        forward_posting = 1;
        if (!*itm_fname) {
            int header = 1;

            sprintf(itm_fname,Itm_template,util_dir(ga[cre_grp[i]]->grp_name),cre_itm[i]);
            if (!(ofile = rms_create(itm_fname,0xa000))) {
                strcpy(no_new_item,"Cannot open output file (write access)");
                status = 0X90000000;
                *itm_fname = '\0';
                continue;
                }
            while (rms_get(ibuf,1023,ifile)) {
                if ((!*ibuf) && (*app_line)) {
                    rms_put(app_line,ofile);
                    *app_line = '\0';
                    }
                if (header && (!*ibuf)) {
                    if ((g_count > 1) && (header)) rms_put(xrefline,ofile);
                    rms_put(ibuf,ofile);
                    header = 0;
                    }
                else if (header && (!strncmp(ibuf,"Xref:",5))) {
                    if (g_count > 1) rms_put(xrefline,ofile);
                    header = 0;
                    }
                else rms_put(ibuf,ofile);
                }
            rms_close(ifile); ifile = 0;
            rms_close(ofile);
            if (ga[cre_grp[i]]->grp_local & NEWS_RESTRICT_SET) {
                sysprv();
                chmod(itm_fname,0700);
                nosysprv();
                }
            status = 0;
            strcpy(no_new_item,"Newsitem added");
            no_more_news = 0;
            }
        else {
            char fline[256];

            sprintf(fline,Itm_template,util_dir(ga[cre_grp[i]]->grp_name),cre_itm[i]);
            file_copy(itm_fname,fline);
            if (ga[cre_grp[i]]->grp_local & NEWS_RESTRICT_SET) {
                sysprv();
                chmod(fline,0700);
                nosysprv();
                }
            status = 0;
            strcpy(no_new_item,"Newsitem added");
            no_more_news = 0;
            }
        }
    if (ifile) rms_close(ifile);
    return(status);
}

/*
 *  do_new_mem_grp
 *
 *  create a new group in the mem array
 */

do_new_mem_grp()
{
    GRP_MEM_PTR nentry;
    int i,
        j;

    ++(ga[0]->grp_count);
    nentry = malloc(sizeof newsgrp);
    *nentry = newsgrp;
    for (i = 1; i <= ga_size; ++i) if (strcmp(ga[i]->grp_name,newsgrp.grp_name) > 0) break;
    if (ga_malloc <= (ga_size + 2)) {
        ga_malloc += 4;
        ga = realloc(ga,ga_malloc * (sizeof nentry));
        }
    if (i <= ga_size) for (j = ga_size; j >= i; --j) ga[j+1] = ga[j];
    if (curr_g >= i) ++curr_g;
    ga[i] = nentry;
    ga[i]->grp_display_indx = (cur_dir_type < DIR_NEW);
    ++ga_size;

    if ((gv_size) && (ga[0]->grp_count > gv_size)) smg$change_virtual_display(&grp_vd,c$rfi(gv_size += 4),c$rfi(80),0,0,0);
    if ((screen_active) && (cur_dir_type < DIR_NEW)) screen_map_dir();
}

/*
 *  gendate
 *
 *  generate a reasonable date string!!
 */

static char fmt_date[26];

char *gendate(i) unsigned int i;
{
    strncpy(fmt_date,ctime(&i),24);
    fmt_date[1]=fmt_date[8];fmt_date[2]=fmt_date[9];fmt_date[3]='-';fmt_date[7]='-';
    fmt_date[8]=fmt_date[22];fmt_date[9]=fmt_date[23];fmt_date[10]='\0';
    return(&fmt_date[1]);
}

/*
 *  gen_id
 *
 *  generate a unique message id of the form <seq num>@<internet address>
 */
char genid[132];

char *gen_id()
{
    GRP savegrp;
    int seq;

    savegrp = newsgrp;

    grprab.rab$l_kbf = c$rfi(0);
    grprab.rab$b_ksz = 2;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;
    _c$cks(sys$get(&grprab));
    seq = ++newsgrp.grp_iavd;
    _c$cks(sys$update(&grprab));
    sprintf(genid,"<%d@%s>",seq,Node_address);
    s_to_lower(genid);
    return(genid);
}

/*
 *  do_new_mem_itm
 *
 *  Add new item to mem array
 */

do_new_mem_itm(g,gap)
    int g;
    GRP_MEM_PTR gap;
{
    ITM_MEM_PTR iap;
    char *fdate;
    int i;

    i = ++gap->grp_count;
    ++gap->grp_unread;
    if ((gap->grp_display_indx) && (screen_active)) {
        sprintf(err_oline,"%5d",gap->grp_count);
        smg$put_chars(&grp_vd,c$dsc(err_oline),c$rfi(gap->grp_display_indx),c$rfi(14+SUBJLEN),0,0,0,0);
        screen_update_gread(g);
        }

    if (gap->grp_ia) {
        if (gap->grp_iasize <= gap->grp_count) gap->grp_ia = realloc(gap->grp_ia,(gap->grp_iasize += 4) * (sizeof newsitm));
        iap = gap->grp_ia;

        gap->grp_ia[gap->grp_count] = newsitm;
        if ((gap->grp_iavdsize) && (screen_active)) {
            if (gap->grp_count > gap->grp_iavdsize) smg$change_virtual_display(&gap->grp_iavd,c$rfi(gap->grp_iavdsize += 4),c$rfi(80),0,0,0);
            fdate = gendate(iap[i].itm_date);
            sprintf(err_oline,"   %-5d %c %-*.*s %6d %s",iap[i].itm_num,(iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,iap[i].itm_size,fdate);
            status = smg$put_chars(&(gap->grp_iavd),c$dsc(err_oline),c$rfi(gap->grp_count),c$rfi(1),0,0,0,0);
            status = smg$change_rendition(&(gap->grp_iavd),c$rfi(gap->grp_count),c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
            if (!gap->grp_c_itm) {
                gap->grp_c_itm = 1;
                smg$put_chars(&gap->grp_iavd,c$dsc("->"),c$rfi(1),c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0,gap->grp_count);
                if (g == curr_g) curr_i = 1;
                }
            }
        }
    else if ((screen_active) && (gap->grp_iavdsize)) {
        map_items(g);
        iap = gap->grp_ia;

        if (gap->grp_count > gap->grp_iavdsize) smg$change_virtual_display(&gap->grp_iavd,c$rfi(gap->grp_iavdsize += 4),c$rfi(80),0,0,0);
        for (i = 1; i <= gap->grp_count; ++i) {
            fdate = gendate(iap[i].itm_date);
            sprintf(err_oline,"   %-5d %c %-*.*s %6d %s",iap[i].itm_num,(iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,iap[i].itm_size,fdate);
            smg$put_chars(&gap->grp_iavd,c$dsc(err_oline),&i,c$rfi(1),0,0,0,0);
            if (iap[i].itm_unread) {
                smg$change_rendition(&gap->grp_iavd,&i,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
                if (!gap->grp_c_itm) {
                    gap->grp_c_itm = i;
                    smg$put_chars(&gap->grp_iavd,c$dsc("->"),&i,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                    if (g == curr_g) curr_i = i;
                    }
                }
            }
        if (!gap->grp_c_itm) {
            for (i = 1; i < gap->grp_count; ++i) if (iap[i].itm_unread) break;
            gap->grp_c_itm = i;
            if (gap->grp_iavdsize) smg$put_chars(&gap->grp_iavd,c$dsc("->"),&i,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0,gap->grp_count);
            if (g == curr_g) curr_i = i;
            }
        }
}

/*
 *  do_open_item
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

extern char get_server_title[];
extern int  get_server_size;

FILE *do_open_item(g,i,mode,server_name)
    int g,
        i;
    char *mode,
         *server_name;
{
    FILE *tmp;
    sprintf(itm_fname,Itm_template,util_dir(ga[g]->grp_name),ga[g]->grp_ia[i].itm_num);
    if (tmp = do_openi(mode,server_name,(ga[g]->grp_local & NEWS_RESTRICT_SET))) return(tmp);
    if (strcmp(mode,"r") || (!(*(ga[g]->grp_server)))) return(tmp);
    if (tmp = get_server_file(ga[g]->grp_ia[i].itm_id,ga[g]->grp_server,
     server_name,(ga[g]->grp_holdcop ? itm_fname : 0),ga[g]->grp_srvproto)) {
        if (!(ga[g]->grp_ia[i].itm_size)) {
            itmrab.rab$l_kbf = &(ga[g]->grp_ia[i].itm_num);
            itmrab.rab$b_ksz = 4;
            itmrab.rab$b_krf = 0;
            itmrab.rab$l_rop = RAB$M_WAT;
            itmrab.rab$b_rac = RAB$C_KEY;
            if (sys$get(&itmrab) & 1) {
                newsitm.itm_size = get_server_size;
                util_cpy(newsitm.itm_title,get_server_title);
                if (ga[g]->grp_holdcop) time(&newsitm.itm_locdate);
                sys$update(&itmrab);
                }
            ga[g]->grp_ia[i].itm_size = get_server_size;
            util_cpy(ga[g]->grp_ia[i].itm_title,get_server_title);
            }
        if (ga[g]->grp_holdcop) time(&(ga[g]->grp_ia[i].itm_locdate));
        else ga[g]->grp_ia[i].itm_locdate = 0;
        if (screen_active && ga[g]->grp_iavdsize) {
            ITM_MEM_PTR iap = ga[g]->grp_ia;
            char *fdate = gendate(iap[i].itm_date);

            sprintf(err_oline,"%-5d %c %-*.*s %6d %s",iap[i].itm_num,
             (iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,
             iap[i].itm_size,fdate);
            smg$put_chars(&ga[g]->grp_iavd,c$dsc(err_oline),&i,c$rfi(4),0,0,0,0);
            if (iap[i].itm_unread) smg$change_rendition(&ga[g]->grp_iavd,&i,
             c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
            }
        }
    return(tmp);
}

/*
 *  do_open_header
 *
 *  Given an item record, open the associated header of the message.
 */

extern char get_server_title[];
extern int  get_server_size;

FILE *do_open_header(g,i,mode,server_name)
    int g,
        i;
    char *mode,
         *server_name;
{
    FILE *tmp;
    sprintf(itm_fname,Itm_template,util_dir(ga[g]->grp_name),ga[g]->grp_ia[i].itm_num);
    if (tmp = do_openi(mode,server_name,(ga[g]->grp_local & NEWS_RESTRICT_SET))) return(tmp);
    if (strcmp(mode,"r") || (!(*(ga[g]->grp_server)))) return(tmp);
    if (tmp = get_server_header(ga[g]->grp_ia[i].itm_id,ga[g]->grp_server,server_name,ga[g]->grp_srvproto)) {
        if (!(ga[g]->grp_ia[i].itm_size)) {
            itmrab.rab$l_kbf = &(ga[g]->grp_ia[i].itm_num);
            itmrab.rab$b_ksz = 4;
            itmrab.rab$b_krf = 0;
            itmrab.rab$l_rop = RAB$M_WAT;
            itmrab.rab$b_rac = RAB$C_KEY;
            if (sys$get(&itmrab) & 1) {
                newsitm.itm_size = get_server_size;
                util_cpy(newsitm.itm_title,get_server_title);
                sys$update(&itmrab);
                }
            ga[g]->grp_ia[i].itm_size = get_server_size;
            util_cpy(ga[g]->grp_ia[i].itm_title,get_server_title);
            }
        if (screen_active && ga[g]->grp_iavdsize) {
            ITM_MEM_PTR iap = ga[g]->grp_ia;
            char *fdate = gendate(iap[i].itm_date);

            sprintf(err_oline,"%-5d %c %-*.*s %6d %s",iap[i].itm_num,(iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,iap[i].itm_size,fdate);
            smg$put_chars(&ga[g]->grp_iavd,c$dsc(err_oline),&i,c$rfi(4),0,0,0,0);
            if (iap[i].itm_unread) smg$change_rendition(&ga[g]->grp_iavd,&i,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
            }
        }
    return(tmp);
}

/*
 *  do_parse
 *
 *  command parser
 */

do_parse(s)
    char *s;
{
    if (!*s) {
        if ((news_context == 1) && (curr_g)) return(do_select("%",curr_g,3),0);
        else if (old_context == 3) return(do_readnext(0,0,0),0);
        else {
            if ((curr_g) && (curr_i)) {
                item_update(curr_g,curr_i);
                rpush(curr_g,curr_i);
                do_display(0,0,0);
                }
            return(0);
            }
        }
    if (*s == '!') return(0);
    if (isdigit(*s)) {
        if (news_context > 1) {
            int i;
            if (!curr_g) return(0);
            if (!(i = item_find(s))) return(0);
            cur_set_itm(curr_g,i);
            return(0);
            }
        else if (screen_active && curr_g && ga[curr_g]->grp_display_indx) {
            int num;

            sscanf(s,"%d",&num);
            if (num < ga[curr_g]->grp_display_indx) cur_up_grp(ga[curr_g]->grp_display_indx - num,1);
            else if (num > ga[curr_g]->grp_display_indx) cur_down_grp(num - ga[curr_g]->grp_display_indx,1);
            return(0);
            }
        else return(do_error(),0);
        }
    if ((*s == '.') && (!*(s+1))) {
        if (curr_i <= 0) return(do_error(),0);
        return(do_readfind(s,0,0),0);
        }
    if (cli$dcl_parse(c$dsc(s),&newscmd,get_input,get_input,c$dsc("NEWS> ")) & 1) return(cli$dispatch());
    return(0);
}

/*
 *  err_line
 *
 *  error line management
 */

err_line(s)
    char *s;
{
    if (!screen_active) printf(s);
    else {
        int i = strlen(s);

        if (s[--i] == '\n') s[i] = '\0';
        if (*s == '\t') ++s;
        smg$put_chars(&trailer_vd,c$dsc(s),c$rfi(3),c$rfi(1),c$rfi(1),c$rfi(SMG$M_REVERSE),0,0);
        }
}

write_err_line(s)
    char *s;
{
    err_line(s);
    if (screen_active) {
        smg$end_pasteboard_update(&pid);
        smg$begin_pasteboard_update(&pid);
        }
}

/*
 *  ga_locate
 *
 *  locate newsgroup index from newsgroup num
 */

int ga_locate(g)
    short g;
{
    int i;

    for (i = 1; i <= ga_size; ++i)
        if (g == ga[i]->grp_num) return(i);
    return(0);
}

/*
 *  ga_search_name
 *
 *  locate a newsgroup by name
 */

ga_search_name(s)
    char *s;
{
    int i,
        l = strlen(s);

    for (i = 1 ; i <= ga_size ; ++i)
        if (!strncmp(s,ga[i]->grp_name,l)) return(i);
    return(0);
}

/*
 *  ga_exact_name
 *
 *  locate a newsgroup by name - exact match
 */

ga_exact_name(s)
    char *s;
{
    int i;

    for (i = 1 ; i <= ga_size ; ++i)
        if (!strcmp(s,ga[i]->grp_name)) return(i);
    return(0);
}

/*
 *  substrcmp
 *
 *  return 1 if the second arg is a substring of the first arg
 */

substrcmp(s,sub)
    char *s,
         *sub;
{
    char *cp = s;
    int i,
        bl = strlen(sub),
        sl = strlen(s);

    for (i = 0; i <= (sl - bl); ++i)
        if (!strncmp(&s[i],sub,bl)) return(i+1);
    return(0);
}

/*
 *  create_pb
 *
 *  create the pastebaord
 */

struct {
    unsigned long int SMG$L_DEVCHAR;
    unsigned long int SMG$L_DEVDEPEND;
    unsigned long int SMG$L_DEVDEPEND2;
    unsigned char SMG$B_DEVCLASS;
    unsigned char SMG$B_SMG_DEVTYPE;
    unsigned char SMG$B_PHY_DEVTYPE;
    unsigned char SMG$B_ROWS;
    unsigned short int SMG$W_WIDTH;
    unsigned char SMG$B_COLOR;
    unsigned char SMG$B_PARITY;
    unsigned short int SMG$W_SPEED;
    unsigned short int SMG$W_FILL;
    unsigned short int SMG$W_CURSOR_ROW;
    unsigned short int SMG$W_CURSOR_COL;
    unsigned long int SMG$L_CURSOR_DID;
    } pb_info;

static int smg_term = -1;

display_brdcst(line)
{
    char bm[512];
    unsigned short bm_len;
    $DESCRIPTOR(bm_dsc,bm);

    int sts,
        c_row,
        c_col;

    if (((sts = smg$get_broadcast_message(&pid,&bm_dsc,&bm_len)) & 1)
        && (sts != SMG$_NO_MORMSG)) {
        bm[bm_len] = '\0';
        smg$erase_line(&trailer_vd,&line,c$rfi(1));
        smg$put_chars(&trailer_vd,c$dsc(bm),&line,c$rfi(1),c$rfi(1),c$rfi(SMG$M_BOLD),0,0);
        smg$set_cursor_abs(&trailer_vd,c$rfi(1),c$rfi(brdcst_col));
        }
}

b_trap()
{
    if (brdcst_line) display_brdcst(brdcst_line);
}

create_pb()
{
    if (pid_created) return(1);
    if (smg_term >= 0) return(smg_term);
    smg$create_pasteboard(&pid,c$dsc("SYS$OUTPUT"),&devrow,&devcol,c$rfi(0));
    pid_created = 1;
    _c$cks(smg$get_pasteboard_attributes(&pid,&pb_info,c$rfi(sizeof pb_info)));
    if (!pb_info.SMG$W_SPEED) {
        printf("\n\tNEWS: Not a terminal - /NOSCREEN mode used\n");
        pid_created = 0;
        }
    else if ((pb_info.SMG$B_SMG_DEVTYPE != SMG$K_VTFOREIGN) && (pb_info.SMG$B_SMG_DEVTYPE != SMG$K_VTTERMTABLE)) {
        printf("\n\tNEWS: Not an SMG supported terminal - /NOSCREEN mode used\n");
        pid_created = 0;
        }
    if (!pid_created) smg$delete_pasteboard(&pid,c$rfi(0));
    return(smg_term = pid_created);
}

/*
 *  init_screen
 *
 *  set up the screen display
 */

init_screen()
{
    int num_grps;

    if (screen_active) return(0);
    if (!create_pb()) {
        printf("\nError: SCREEN - Not an SMG terminal\n");
        return(0);
        }
    set_level(1);
    broad_trap();
    screen_active = 1;
    num_grps = ga[0]->grp_count;
    c$cks(smg$create_virtual_display(c$rfi(num_grps + 40),c$rfi(80),&grp_vd,0,0,0));
    gv_size = num_grps + 40;
    c$cks(smg$create_virtual_display(c$rfi(3),c$rfi(80),&grp_header_vd,0,c$rfi(SMG$M_REVERSE + SMG$M_BOLD),0));
    c$cks(smg$create_virtual_display(c$rfi(3),c$rfi(80),&itm_header_vd,0,c$rfi(SMG$M_REVERSE + SMG$M_BOLD),0));
    c$cks(smg$create_virtual_display(c$rfi(3),c$rfi(80),&trailer_vd,0,0,0));
    c$cks(smg$create_virtual_display(c$rfi(DISP_BUF_SIZE + devrow),c$rfi(80),&sdid,0,0,0));
    smg$set_display_scroll_region(&sdid,c$rfi(1),c$rfi(DISP_BUF_SIZE));
    c$cks(smg$create_virtual_display(c$rfi(devrow - 3),c$rfi(80),&shdid,0,0,0));

    smg$put_chars(&grp_header_vd,c$dsc("NEWS       NEWSGROUP LISTING"),c$rfi(1),c$rfi(4),0,0,0,0);
    smg$put_chars(&grp_header_vd,c$dsc("        Newsgroup                                             Count    Unread"),c$rfi(3),c$rfi(4),0,0,0,0);

    smg$put_chars(&itm_header_vd,c$dsc("NEWS ITEMS"),c$rfi(1),c$rfi(4),0,0,0,0);
    smg$put_chars(&itm_header_vd,c$dsc("        Title                                               Lines Date"),c$rfi(3),c$rfi(4),0,0,0,0);

    smg$begin_pasteboard_update(&pid);
    smg$erase_pasteboard(&pid);
    do_refresh();
    c$cks(smg$paste_virtual_display(&grp_vd,&pid,c$rfi(4),c$rfi(1)));
    c$cks(smg$paste_virtual_display(&grp_header_vd,&pid,c$rfi(1),c$rfi(1)));
    c$cks(smg$paste_virtual_display(&trailer_vd,&pid,c$rfi(devrow - 2),c$rfi(1)));

    screen_map_dir();

    chg_mode();
    screen_displays_setup = 1;
    return(0);
}

/*
 *  item_find
 *
 *  locate a news item as specified by item number
 */

item_find(s)
char *s;
{
    int locnum,
        i;
    ITM_MEM_PTR iap;

    if (!curr_g) {
        err_line("\tNo Group Selected\n");
        return(0);
        }

    if (!ga[curr_g]->grp_count) return(0);

    if ((s) && (isdigit(*s))) {
        set_level(2);
        if (sscanf(s,"%d",&locnum) != 1) return(0);
        if (locnum <= ga[curr_g]->grp_ia[1].itm_num) {
            cur_up_itm(curr_g,ga[curr_g]->grp_count,0);
            return(curr_i);
            }
        iap = ga[curr_g]->grp_ia;
        for (i = 1; i <= ga[curr_g]->grp_count; ++i)
            if (locnum <= iap[i].itm_num) {
                cur_set_itm(curr_g,i);
                return(curr_i);
                }
        cur_set_itm(curr_g,ga[curr_g]->grp_count);
        return(curr_i);
        }
    if ((s) && (!strcmp(s,"*"))) {
        set_level(2);
        cur_set_itm(curr_g,ga[curr_g]->grp_count);
        return(curr_i);
        }
    return(0);
}

/*
 *  map_items
 *
 *  Map items into the mem array
 */

map_items(g)
{
    unsigned short key[2] = {0,0};
    GRP_MEM_PTR gap;
    ITM_MEM_PTR l_ia;
    char *itm_list;
    int f_itm,
        l_itm,
        i;

    if ((!g) || (ga[g]->grp_ia)) return;
    gap = ga[g];
    gap->grp_iasize = gap->grp_count + 2;
    gap->grp_ia = malloc((gap->grp_iasize + 1) * (sizeof newsitm));
    gap->grp_count = 0;

    key[1] = gap->grp_num;
    itmrab.rab$l_kbf = key;
    itmrab.rab$b_ksz = 4;
    itmrab.rab$b_krf = 0;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    while (sys$get(&itmrab) & 1) {
        if (newsitm.itm_grp != key[1]) break;
        itmrab.rab$b_rac = RAB$C_SEQ;
        itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
        if (gap->grp_iasize == gap->grp_count) {
            gap->grp_iasize += 10;
            gap->grp_ia = realloc(gap->grp_ia,(gap->grp_iasize + 1) * (sizeof newsitm));
            }
        newsitm.itm_unread = 1;
        gap->grp_ia[++gap->grp_count] = newsitm;
        }
    gap->grp_unread = gap->grp_count;
    l_ia = gap->grp_ia;
    itm_list = gap->grp_iapaste;
    if (itm_list > 511) {
        while (itm_list) {
            if ((*itm_list == '<') || (*itm_list == '|')) {
                if (*itm_list == '|') {
                    sscanf(++itm_list,"%d",&f_itm);
                    itm_list = strchr(itm_list,'=');
                    }
                else f_itm = 1;
                sscanf(++itm_list,"%d",&l_itm);
                itm_list = strchr(itm_list,' ');
                if (itm_list) ++itm_list;
                for (i = 1; i <= gap->grp_count; ++i) {
                    if (l_ia[i].itm_num > l_itm) break;
                    if (l_ia[i].itm_num < f_itm) continue;
                    l_ia[i].itm_unread = 0;
                    }
                }
            else {
                sscanf(itm_list,"%d",&f_itm);
                itm_list = strchr(itm_list,' ');
                if (itm_list) ++itm_list;
                for (i = 1; i <= gap->grp_count; ++i) {
                    if (l_ia[i].itm_num > f_itm) break;
                    if (l_ia[i].itm_num < f_itm) continue;
                    l_ia[i].itm_unread = 0;
                    }
                }
            }
        gap->grp_unread = 0;
        for (i = 1; i <= gap->grp_count; ++i) if (l_ia[i].itm_unread) ++(gap->grp_unread);
        free(gap->grp_iapaste);
        }
    gap->grp_iapaste = 0;
}

/*
 *  mem_reset
 *
 *  reset the memory status to match the current file status
 */

mem_reset(cd)
    int cd;
{
    int g,
        num_grps,
        ssa;

    set_level(1);
    ssa = screen_active;
    write_reg_file();

    for (g = 1; g <= ga_size; ++g) {
        if (ga[g]->grp_ia) free(ga[g]->grp_ia);
        if (ga[g]->grp_iavdsize) smg$delete_virtual_display(&ga[g]->grp_iavd);
        free(ga[g]);
        }
    free(ga);

    curr_g = 0;
    open_groups(cd);
    if (gv_size) {
        num_grps = ga[0]->grp_count;
        if (num_grps > gv_size)
        c$cks(smg$change_virtual_display(&grp_vd,&num_grps,c$rfi(80)));
        gv_size = num_grps;
        }
    if (ssa && !screen_displays_setup) {
        screen_active = 0;
        init_screen();
        }
    else if (screen_active) screen_map_dir();
}

/*
 *  noscreen
 *
 *  Revert to line mode interface
 */

noscreen()
{
    int g;

    if (screen_active) {
        no_broad_trap();
        smg$begin_pasteboard_update(&pid);
        set_level(1);
        for (g = 1; g <= ga_size; ++g) {
            if (ga[g]->grp_iavdsize) {
                smg$delete_virtual_display(&ga[g]->grp_iavd);
                ga[g]->grp_iavdsize = 0;
                }
            }
        smg$delete_virtual_display(&grp_vd);
        gv_size = 0;
        smg$delete_virtual_display(&grp_header_vd);
        smg$delete_virtual_display(&itm_header_vd);
        smg$delete_virtual_display(&trailer_vd);
        smg$delete_virtual_display(&sdid);
        smg$delete_virtual_display(&shdid);
        while (smg$end_pasteboard_update(&pid) == SMG$_BATSTIPRO);
        reset_mode();
        screen_active = 0;
        screen_displays_setup = 0;
        }
    return(0);
}

/*
 *  openfiles
 *
 *  Open the item and uaf files for sharing
 */

openfiles()
{
    itmfab = cc$rms_fab;
    itmfab.fab$b_bks = 4;
    itmfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD | FAB$M_DEL ;
    itmfab.fab$l_fna = Item_file;
    itmfab.fab$b_fns = strlen(itmfab.fab$l_fna);
    itmfab.fab$l_fop = FAB$M_CIF;
    itmfab.fab$w_mrs = sizeof newsitm;
    itmfab.fab$b_org = FAB$C_IDX;
    itmfab.fab$b_rat = FAB$M_CR;
    itmfab.fab$b_rfm = FAB$C_FIX;
    itmfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;
    itmfab.fab$l_xab = &xabkey_1;

    xabkey_1 = cc$rms_xabkey;
    xabkey_1.xab$b_dtp = XAB$C_BN4;
    xabkey_1.xab$b_flg = 0;
    xabkey_1.xab$w_pos0 = (char *) &newsitm.itm_num - (char *) &newsitm;
    xabkey_1.xab$b_ref = 0;
    xabkey_1.xab$b_siz0 = 4;
    xabkey_1.xab$l_nxt = &xabkey_2;

    xabkey_2 = cc$rms_xabkey;
    xabkey_2.xab$b_dtp = XAB$C_STG;
    xabkey_2.xab$b_flg = 0;
    xabkey_2.xab$w_pos0 = (char *) &newsitm.itm_id - (char *) &newsitm;
    xabkey_2.xab$w_pos1 = (char *) &newsitm.itm_grp - (char *) &newsitm;
    xabkey_2.xab$b_ref = 1;
    xabkey_2.xab$b_siz0 = IDLEN;
    xabkey_2.xab$b_siz1 = 2;
    xabkey_2.xab$l_nxt = &xabpro_1;

    xabpro_1 = cc$rms_xabpro;
    xabpro_1.xab$w_pro = 0xEE00;

    itmrab = cc$rms_rab;
    itmrab.rab$l_fab = &itmfab;
    itmrab.rab$b_krf = 0;
    itmrab.rab$b_ksz = 4;
    itmrab.rab$l_ubf = &newsitm;
    itmrab.rab$w_usz = sizeof newsitm;
    itmrab.rab$l_rbf = &newsitm;
    itmrab.rab$w_rsz = sizeof newsitm;

    sysprv();
    if (no_priv()) status = sys$open(&itmfab);
    else status = sys$create(&itmfab);
    if (!(status & 1)) _c$cks(status);
    else if (status == RMS$_CREATED) printf("\tNEWS - create new ITEM file\n");
    _c$cks(sys$connect(&itmrab));
    nosysprv();

    /* open the index newsgroup descriptor file for full access */
    grpfab = cc$rms_fab;
    grpfab.fab$b_bks = 4;
    grpfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD | FAB$M_DEL ;
    grpfab.fab$l_fna = Group_file;
    grpfab.fab$b_fns = strlen(grpfab.fab$l_fna);
    grpfab.fab$l_fop = FAB$M_CIF;
    grpfab.fab$w_mrs = sizeof newsgrp;
    grpfab.fab$b_org = FAB$C_IDX;
    grpfab.fab$b_rat = FAB$M_CR;
    grpfab.fab$b_rfm = FAB$C_FIX;
    grpfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;
    grpfab.fab$l_xab = &xabkey_3;

    xabkey_3 = cc$rms_xabkey;
    xabkey_3.xab$b_dtp = XAB$C_STG;
    xabkey_3.xab$b_flg = 0;
    xabkey_3.xab$w_pos0 = (char *) &newsgrp.grp_name - (char *) &newsgrp;
    xabkey_3.xab$b_ref = 0;
    xabkey_3.xab$b_siz0 = SUBJLEN;
    xabkey_3.xab$l_nxt = &xabkey_4;

    xabkey_4 = cc$rms_xabkey;
    xabkey_4.xab$b_dtp = XAB$C_BN2;
    xabkey_4.xab$b_flg = 0;
    xabkey_4.xab$w_pos0 = (char *) &newsgrp.grp_num - (char *) &newsgrp;
    xabkey_4.xab$b_ref = 1;
    xabkey_4.xab$b_siz0 = 2;
    xabkey_4.xab$l_nxt = &xabpro_2;

    xabpro_2 = cc$rms_xabpro;
    xabpro_2.xab$w_pro = 0xEE00;

    grprab = cc$rms_rab;
    grprab.rab$l_fab = &grpfab;
    grprab.rab$l_ubf = &newsgrp;
    grprab.rab$w_usz = sizeof newsgrp;
    grprab.rab$l_rbf = &newsgrp;
    grprab.rab$w_rsz = sizeof newsgrp;

    sysprv();
    if (no_priv()) status = sys$open(&grpfab);
    else status = sys$create(&grpfab);
    if (!(status & 1)) _c$cks(status);
    if (status == RMS$_CREATED) {
        char fname[100];

        _c$cks(sys$connect(&grprab));
        printf("\tNEWS - first time installation...\n");
        first_time();
        printf("\tNEWS - create new GROUP file\n");
        strcpy(newsgrp.grp_name,"NEWSGROUP INDEX");
        newsgrp.grp_num = 0;
        newsgrp.grp_topnum = 0;
        newsgrp.grp_count = 0;
        newsgrp.grp_life = GRP_TIME;
        newsgrp.grp_itmlife = EXP_TIME;
        time(&newsgrp.grp_credate);
        newsgrp.grp_entdate = newsgrp.grp_credate;
        newsgrp.grp_reg = newsgrp.grp_unread = 0;
        newsgrp.grp_ia = newsgrp.grp_iasize = newsgrp.grp_iavd = 0;
        newsgrp.grp_iavdsize = newsgrp.grp_iapaste = newsgrp.grp_c_itm = 0;
        newsgrp.grp_display_indx = newsgrp.grp_local = 0;
        newsgrp.grp_moderator = 0;
        *(newsgrp.grp_server) = '\0';
        newsgrp.grp_holdcop = 1;
        newsgrp.grp_loccoplife = 2;

        grprab.rab$w_rsz = sizeof newsgrp;
        _c$cks(sys$put(&grprab));
        }
    else _c$cks(sys$connect(&grprab));

    nosysprv();
    open_groups(DIR_ALL);
}

/*
 *  open_groups
 *
 *  Set up the newsgroup array
 */

open_groups(cd)
    int cd;
{
    char *cp, *ap, *op,
         acline[256], cknode[132];
    FILE *fpd;

    grprab.rab$l_kbf = c$rfi(0);
    grprab.rab$b_ksz = 2;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    grprab.rab$b_rac = RAB$C_KEY;
    _c$cks(sys$get(&grprab));

    ga_malloc = (newsgrp.grp_topnum + 5);
    ga = (GRP_MEM_PTR *) malloc(ga_malloc * (sizeof *ga));

    cur_dir_type = DIR_ALL;
    ga[0] = malloc(sizeof newsgrp);
    *ga[0] = newsgrp;
    ga_size = 0;
    grp_display_size = 0;

    grprab.rab$b_krf = 0;
    grprab.rab$b_rac = RAB$C_SEQ;
    sys$rewind(&grprab);
    while ((status = sys$get(&grprab)) & 1) {
        int dir_access = 1;

        if (!newsgrp.grp_num) continue;

        newsgrp.grp_local &= ~(NEWS_WRITE_ACCESS | NEWS_MOD_ACCESS | NEWS_MOD_USER | NEWS_ACCESS_CHECKED);
        if (no_priv() && newsgrp.grp_local & NEWS_RESTRICT_SET) {
            newsgrp.grp_local |= NEWS_ACCESS_CHECKED;
            sprintf(itm_fname,Access_template,util_dir(newsgrp.grp_name));
            sysprv();
            if (!(fpd = fopen(itm_fname,"r"))) {
                nosysprv();
                sprintf(itm_fname,Dir_template,util_dir(newsgrp.grp_name));
                cp = strrchr(itm_fname,'.');
                *cp = ']';
                cp = strchr(itm_fname,'^');
                *cp = '.';
                if (fpd = fopen(itm_fname,"r")) fclose(fpd);
                else continue;
                }
            else {
                int non_member = 1;

                if (!(newsgrp.grp_local & NEWS_NOWRITE_SET))  newsgrp.grp_local |= NEWS_WRITE_ACCESS;
                while (fgets(acline,256,fpd)) {
                    if (*acline == '#') continue;
                    if (cp = strchr(acline,'\n')) *cp = ' ';
                    s_to_lower(acline);
                    if (cp = strchr(acline,' ')) *cp = '\0';
                    if (ap = strchr(acline,'@')) *ap = '\0';
                    if (op = strchr(acline,':')) *op = '\0';
                    if (!strcmp(acline,usr_username) || wild_match(usr_username,acline) || idmatch(acline)) {
                        if (cp) *cp = ' ';
                        if (ap) *ap = '@';
                        if (strchr(acline,'@')) {
                            sprintf(cknode,"@%s ",usr_nodename);
                            if (!substrcmp(cp,cknode)) continue;
                            }
                        if (substrcmp(cp," write ")) newsgrp.grp_local |= NEWS_WRITE_ACCESS;
                        if (substrcmp(cp," nowrite ")) newsgrp.grp_local &= ~NEWS_WRITE_ACCESS;
                        if (substrcmp(cp," moderator ")) newsgrp.grp_local |= (NEWS_MOD_USER | NEWS_WRITE_ACCESS);
                        non_member = 0;
                        }
                    }
                fclose(fpd);
                nosysprv();
                if (non_member) continue;
                }
            }

        ga[++ga_size] = malloc(sizeof newsgrp);
        newsgrp.grp_unread = newsgrp.grp_count;
        newsgrp.grp_reg = 0;
        newsgrp.grp_ia = newsgrp.grp_iasize = 0;
        newsgrp.grp_iavd = newsgrp.grp_iavdsize = newsgrp.grp_iapaste = 0;
        newsgrp.grp_c_itm = 0;
        *ga[ga_size] = newsgrp;
        ga[ga_size]->grp_display_indx = ++grp_display_size;
        }
    if (status != RMS$_EOF) c$cks(status);
    read_reg_file();
}

/*
 *  position_display
 *
 *  reposition the virtual display vd (currently pasted at *cp) to display
 *  line p
 */

position_display(vd,cp,p,refresh,maxsize)
    int *vd,
        *cp,
        p,
        refresh,
        maxsize;
{
    if ((maxsize - p < 7) || (p < 7)) {
        if ((p >= (5 - *cp)) && (p <= (devrow - (2 + *cp)))) return;
        if (p < (5 - *cp)) *cp = (5 - p);
        else *cp = (devrow - (2 + p));
        }
    else {
        if ((p >= (11 - *cp)) && (p <= (devrow - (9 + *cp)))) return;
        if (p < (11 - *cp)) *cp = (11 - p);
        else *cp = (devrow - (9 + p));
        }
    if (refresh) c$cks(smg$end_pasteboard_update(&pid));
    smg$move_virtual_display(vd,&pid,cp,c$rfi(1),0);
    if (refresh) c$cks(smg$begin_pasteboard_update(&pid));
}

/*
 *  screen_map_dir
 *
 *  Redraw the group level screen virtual display
 */

screen_map_dir()
{
    int g,
        sg = 0;
    char dir_type[120];

    g_arrow = 0;
    smg$erase_display(&grp_vd);
    if ((!curr_g) && ga_size) curr_g = 1;

    for (g = 1; g <= ga_size; ++g) {
        if (ga[g]->grp_display_indx) {
            ga[g]->grp_display_indx = ++sg;

            if (ga[g]->grp_unread) sprintf(err_oline,"   %-5d %c %-*.*s  %5d    %5d",
                sg,(*(ga[g]->grp_server) ? '_' : ' '),SUBJLEN,SUBJLEN,ga[g]->grp_name,ga[g]->grp_count,ga[g]->grp_unread);
            else sprintf(err_oline,"   %-5d %c %-*.*s  %5d",
                sg,(*(ga[g]->grp_server) ? '_' : ' '),SUBJLEN,SUBJLEN,ga[g]->grp_name,ga[g]->grp_count);
            smg$put_chars(&grp_vd,c$dsc(err_oline),&sg,c$rfi(1),0,0,0,0);
            if (g == curr_g) {
                g_arrow = sg;
                smg$put_chars(&grp_vd,c$dsc("->"),&sg,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                }
            if (ga[g]->grp_reg) {
                smg$change_rendition(&grp_vd,&sg,c$rfi(12),c$rfi(1),c$rfi(SUBJLEN),c$rfi(SMG$M_BOLD),0);
                if (ga[g]->grp_unread) smg$change_rendition(&grp_vd,&sg,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
                }
            }
        }
    grp_display_size = sg;
    if (cur_dir_type == DIR_DATE) sprintf(dir_type,"NEWS       NEWSGROUP LISTING     [SINCE=%s, %d Newsgroups]",dir_sincestr,sg);
    else if (cur_dir_type == DIR_NEW) sprintf(dir_type,"NEWS       NEWSGROUP LISTING     [NEW, %d Newsgroups]",sg);
    else if (cur_dir_type == DIR_REGISTER)  sprintf(dir_type,"NEWS       NEWSGROUP LISTING     [REGISTERED, %d Newsgroups]",sg);
    else sprintf(dir_type,"NEWS       NEWSGROUP LISTING     [ALL, %d Newsgroups]",sg);
    smg$put_chars(&grp_header_vd,c$dsc(dir_type),c$rfi(1),c$rfi(4),c$rfi(1),0,0,0);

    if ((!g_arrow) || (grp_display_size <= devrow - 4)) grp_paste = 4;
    else {
        grp_paste = 12 - g_arrow;
        if (grp_paste > 4) grp_paste = 4;
        if (grp_paste < ((devrow - 2) - grp_display_size)) grp_paste = (devrow - 2) - grp_display_size;
        }
    smg$move_virtual_display(&grp_vd,&pid,&grp_paste,c$rfi(1),0);
}

/*
 *  set_level
 *
 *  Set the screen display level
 */

set_level(l)
    int l;
{
    int i;
    GRP_MEM_PTR gap = ga[curr_g];
    ITM_MEM_PTR iap;

    if (news_context == 3) {
        news_context = 2;
        if (screen_active) {
            screen_active = 2;
            smg$unpaste_virtual_display(&sdid,&pid);
            smg$unpaste_virtual_display(&shdid,&pid);
            text_displayed = 0;
            }
        }
    if (news_context == l) return;
    if (l == 1) {
        news_context = 1;
        curr_i = -1;
        if (screen_active) {
            screen_active = 1;
            if ((!curr_g) || (!gap->grp_iavdsize)) return;
            smg$unpaste_virtual_display(&itm_header_vd,&pid);
            smg$unpaste_virtual_display(&(gap->grp_iavd),&pid);
            }
        return;
        }


    if (!curr_g) return;
    if (!gap->grp_count) {
        if (screen_active && (!gap->grp_iavdsize)) {
            smg$create_virtual_display(&devrow,c$rfi(80),&(gap->grp_iavd),0,0,0);
            smg$put_chars(&(gap->grp_iavd),c$dsc("NEWSGROUP is empty"),c$rfi(1),c$rfi(20),0,c$rfi(SMG$M_REVERSE),0,0);
            gap->grp_iavdsize = devrow;
            gap->grp_iapaste = 4;
            }
        gap->grp_c_itm = 0;
        }
    else {
        if (!gap->grp_ia) map_items(curr_g);
        if (!gap->grp_count) {
            if (screen_active && (!gap->grp_iavdsize)) {
                smg$create_virtual_display(&devrow,c$rfi(80),&(gap->grp_iavd),0,0,0);
                smg$put_chars(&(gap->grp_iavd),c$dsc("NEWSGROUP is empty"),c$rfi(1),c$rfi(20),0,c$rfi(SMG$M_REVERSE),0,0);
                gap->grp_iavdsize = devrow;
                gap->grp_iapaste = 4;
                }
            gap->grp_c_itm = 0;
            }
        else {
            iap = gap->grp_ia;
            if (screen_active && (!gap->grp_iavdsize)) {
                char *fdate;

                smg$create_virtual_display(c$rfi(gap->grp_iavdsize = max(devrow,gap->grp_count)),c$rfi(80),&(gap->grp_iavd),0,0,0);
                for (i = 1; i <= gap->grp_count; ++i) {
                    fdate = gendate(iap[i].itm_date);
                    if (iap[i].itm_size) sprintf(err_oline,"   %-5d %c %-*.*s %6d %s",iap[i].itm_num,(iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,iap[i].itm_size,fdate);
                    else sprintf(err_oline,"   %-5d %c %-*.*s        %s",iap[i].itm_num,(iap[i].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[i].itm_title,fdate);
                    smg$put_chars(&gap->grp_iavd,c$dsc(err_oline),&i,c$rfi(1),0,0,0,0);
                    if (iap[i].itm_unread) {
                        smg$change_rendition(&gap->grp_iavd,&i,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
                        if (!gap->grp_c_itm) {
                            gap->grp_c_itm = i;
                            smg$put_chars(&gap->grp_iavd,c$dsc("->"),&i,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                            }
                        }
                    }
                }
            if (!gap->grp_c_itm) {
                for (i = 1; i < gap->grp_count; ++i)
                    if (iap[i].itm_unread) break;
                gap->grp_c_itm = i;
                if (gap->grp_iavdsize) smg$put_chars(&gap->grp_iavd,c$dsc("->"),&i,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
                }
            }
        }
    curr_i = gap->grp_c_itm;
    if (screen_active) {
        char itemhold[40];

        gap->grp_iapaste = 4;
        if (curr_i) {
            position_display(&gap->grp_iavd,&gap->grp_iapaste,curr_i,0,gap->grp_count);
            smg$put_chars(&gap->grp_iavd,c$dsc("->"),&curr_i,c$rfi(1),0,c$rfi(SMG$M_REVERSE+SMG$M_BOLD),0,0);
            }
        if (gap->grp_count) sprintf(err_oline,"Newsgroup: %s  (%d items: #%d - #%d)",gap->grp_name,gap->grp_count,iap[1].itm_num,iap[gap->grp_count].itm_num);
        else sprintf(err_oline,"Newsgroup: %s  (%d items)",gap->grp_name,gap->grp_count);
        smg$put_chars(&itm_header_vd,c$dsc(err_oline),c$rfi(1),c$rfi(1),c$rfi(1),c$rfi(SMG$M_BOLD),0,0);

        sprintf(err_oline,"Expry: ");
        if (gap->grp_itmlife == 65535) strcpy(itemhold,"Perm");
        else if (gap->grp_itmlife) sprintf(itemhold,"%u",gap->grp_itmlife);
        else if (ga[0]->grp_itmlife == 65535) strcpy(itemhold,"[Perm]");
        else if (ga[0]->grp_itmlife) sprintf(itemhold,"[%u]",ga[0]->grp_itmlife);
        else sprintf(itemhold,"%d",EXP_TIME);
        strcat(err_oline,itemhold);
        if (gap->grp_local & 1) strcat(err_oline,", Loc");
        if (gap->grp_moderator) {
            strcat(err_oline,", Mod: ");
            strcat(err_oline,moderator_address(gap->grp_name));
            }
        if (*(gap->grp_server)) {
            strcat(err_oline,",[ Srv: ");
            if (gap->grp_srvproto == 1) strcat(err_oline,"@");
            strcat(err_oline,gap->grp_server);
            if (gap->grp_srvproto != 1) strcat(err_oline,"::");
            if (!gap->grp_holdcop) strcat(err_oline," Nohold]");
            else sprintf(&err_oline[strlen(err_oline)]," Hold: %d ]",gap->grp_loccoplife);
        }
        if (gap->grp_reg) {
            sprintf(itemhold,", Reg: %u",gap->grp_reg);
            strcat(err_oline,itemhold);
            }
        smg$put_chars(&itm_header_vd,c$dsc(err_oline),c$rfi(2),c$rfi(2),c$rfi(1),c$rfi(SMG$M_BOLD),0,0);

        c$cks(smg$paste_virtual_display(&(gap->grp_iavd),&pid,&(gap->grp_iapaste),c$rfi(1)));
        c$cks(smg$paste_virtual_display(&itm_header_vd,&pid,c$rfi(1),c$rfi(1)));
        c$cks(smg$repaste_virtual_display(&trailer_vd,&pid,c$rfi(devrow - 2),c$rfi(1)));
        screen_active = 2;
        }
    news_context = 2;
}

/*
 *  util_cpy
 *
 *  Block copy
 */

util_cpy(result,input)
    char *result,
         *input;
{
    int i;

    for (i = 0 ; i < SUBJLEN ; ++i) result[i] = input[i];
}

/*
 *  util_cvrt
 *
 *  Convert a string into standard newsgroup format
 */

util_cvrt(result, input)
    char *result,
         *input;
{
    char *p = result,
         *in = input;
    int i;
    while ((*in) && (*in == ' ')) in++;
    strncpy(result,in,SUBJLEN);
    result[SUBJLEN - 1] = '\0';
    strip(result,strlen(result));
    while (*p) {
        if (isgraph(*p)) *p = tolower(*p);
        else *p = '_';
        p++;
        }
    i = strlen(result);
    while (i < SUBJLEN) result[i++] = '\0';
}

/*
 *  util_dir
 *
 *  Convert a newsgroup name to a VMS directory string
 */

char dir_result[SUBJLEN + SUBJLEN];

util_dir(input)
    char *input;
{
    char *p = dir_result,
         *in = input;

    while (*in) {
        if (isalnum(*in) || (*in == '-') || (*in == '.')) *p++ = *in++;
        else {
            *p++ = '_';
            if (*in == '_') *p++ = '_';
            else if (*in < '0') *p++ = (*in - '!') + 'A';
            else if (*in < 'A') *p++ = (*in - ':') + '0';
            else if (*in < 'a') *p++ = (*in - '[') + 'P';
            else *p++ = (*in - '{') + 'V';
            in++;
            }
        }
    *p = '\0';
    return(dir_result);
}

/*
 *  util_undir
 *
 *  Convert a directory name to a newsgroup string
 */

char undir_result[SUBJLEN];

util_undir(input)
    char *input;
{
    char *p = undir_result,
         *in = input;

    while (*in) {
        if (*in == '_') {
            in++;
            if (*in < 'A') *p++ = (*in - '0') + ':';
            else if (*in < 'P') *p++ = (*in - 'A') + '!';
            else if (*in < 'V') *p++ = (*in - 'P') + '[';
            else *p++ = (*in - 'V') + '{';
            }
        else *p++ = tolower(*in);
        in++;
        }
    *p = '\0';
    return(undir_result);
}

/*
 *  strip
 *
 *  remove trailing blanks
 */

strip(p,n)
    char *p;
    int n;
{
    do {
        n--;
        } while ((n > 0) && (p[n] == ' '));
    p[n+1] = '\0';
}

/*
 *  util_idcpy
 *
 *  block copy (including padding nulls)
 */

util_idcpy(result,input)
    char *result,
         *input;
{
    int i,
        j;

    j = min(strlen(input),IDLEN);
    for (i = 0 ; i < IDLEN ; ++i) result[i] = (i < j) ? input[i] : '\0';
}

/*
 *  do_refresh
 *
 *  rewrite the SMG screen
 */

do_refresh()
{
    int save_vd;

    if (screen_active) {
        smg$end_pasteboard_update(&pid);
        smg$save_physical_screen(&pid,&save_vd);
        smg$restore_physical_screen(&pid,&save_vd);
        smg$begin_pasteboard_update(&pid);
        }
    return(0);
}

/*
 *  do_error
 *
 *  CLI problem
 */

do_error()
{
    err_line("\tError: News - unrecognised command\n");
    return(0);
}

/*
 *  do_exit
 *
 *  bye for now
 */

do_exit()
{
    return(1);
}

/*
 *  do_quit
 *
 *  Close rms files, DONT write the register file, shutdown the screen, and
 *  send any printer output to a nominated queue.
 */

do_quit()
{
    err_line("QUIT will NOT save any REGISTER, READ, MARK, SKIP commands from this session\n");
    status = get_input(&command,c$dsc("Quit? [y]"),&response_length);
    if (!(status & 1)) return(1);
    if (!(!response_length || (*response == 'Y') || (*response == 'y'))) return(0);
    sysprv();
    sys$close(&itmfab);
    sys$close(&grpfab);
    nosysprv();
    noscreen();
    print_exit();
    exit(1);
}

/*
 *  screen_grp_display
 *
 *  NEWSGROUPS command - set the screen to the newsgroup directory
 */

screen_grp_display()
{
    set_level(1);
    return(0);
}

/*
 *  screen_cur_down
 *
 *  Move the cursor down
 */

screen_cur_down()
{
    char line[132];
    short line_len;
    int lines;
    $DESCRIPTOR(line_dsc,line);

    if (cli$get_value(c$dsc("LINES"),&line_dsc,&line_len) & 1) {
        line[line_len] = '\0';
        if (sscanf(line,"%d",&lines) != 1) lines = 1;
        }
    else lines = 1;
    if (!lines) return(0);
    if (lines < 0) {
        if (news_context == 1) cur_up_grp(lines,1);
        else {
            set_level(2);
            cur_up_itm(curr_g,lines,1);
            }
        return(0);
        }
    if (news_context == 1) cur_down_grp(lines,1);
    else {
        set_level(2);
        cur_down_itm(curr_g,lines,1);
        }
    return(0);
}

/*
 *  screen_cur_up
 *
 *  move the cursor up by n lines on the screen
 */

screen_cur_up()
{
    char line[132];
    short line_len;
    int lines;
    $DESCRIPTOR(line_dsc,line);

    if (cli$get_value(c$dsc("LINES"),&line_dsc,&line_len) & 1) {
        line[line_len] = '\0';
        if (sscanf(line,"%d",&lines) != 1) lines = 1;
        }
    else lines = 1;
    if (!lines) return(0);
    if (lines < 0) {
        if (news_context == 1) cur_down_grp(lines,1);
        else {
            set_level(2);
            cur_down_itm(curr_g,lines,1);
            }
        return(0);
        }
    if (news_context == 1) cur_up_grp(lines,1);
    else {
        set_level(2);
        cur_up_itm(curr_g,lines,1);
        }
    return(0);
}

/*
 *  do_spawn
 *
 *  Spawn a subprocess
 */

do_spawn()
{
    int save_vd;

    if (screen_active) {
        display_brdcst(2);
        no_broad_trap();
        smg$end_pasteboard_update(&pid);
        smg$save_physical_screen(&pid,&save_vd);
        smg$erase_pasteboard(&pid);
        reset_mode();
        }
    printf("\nSpawning NEWS subjob\n");
    lib$spawn(0,0,0,0,0,0,0,0,0,0,0,0);
    if (screen_active) {
        chg_mode();
        smg$restore_physical_screen(&pid,&save_vd);
        smg$begin_pasteboard_update(&pid);
        broad_trap();
        }
    return(0);
}

/*
 *  d o _ d e f i n e
 *
 *  Fill out a key definition into the local keytable
 */

do_define()
{
    if (!((status = smg$define_key(&keytab,c$dsc(response))) & 1)) {
        sprintf(err_oline,"\tError: Define/Key not successful (error code %X)\n",status);
        err_line(err_oline);
        }
    return(0);
}

/*
 *  add_to_mod
 *  read_mod_file
 *  moderator_address
 *  add_moderator
 *
 *  Use mailpaths file to resolve the mailpath address
 */

static struct mail_list {
    char *group,
         *mod_address;
    struct mail_list *mod_next;
    } *mailpaths = 0;

static char mail_rtn[256];

static add_tail_mod(name,address)
    char *name,
         *address;
{
    struct mail_list *h = mailpaths,
                     *t;

    if (!h) {
        mailpaths = malloc(sizeof *mailpaths);
        strcpy((mailpaths->group = malloc(strlen(name) + 1)),name);
        strcpy((mailpaths->mod_address = malloc(strlen(address) + 1)),address);
        mailpaths->mod_next = 0;
        return;
        }
    while (h->mod_next) h = h->mod_next;
    t = malloc(sizeof *mailpaths);
    strcpy((t->group = malloc(strlen(name) + 1)),name);
    strcpy((t->mod_address = malloc(strlen(address) + 1)),address);
    t->mod_next = 0;
    h->mod_next = t;
}

/*
 *  read_mod_file
 *
 *  Read the mialpaths file into a mem structure
 */

read_mod_file()
{
    FILE *fpr;
    char lg[132],
         inpline[256],
         name[256],
         address[256];

    strcpy(lg,newsmgr_dir);
    strcat(lg,"MAILPATHS");
    sysprv();
    if (!(fpr = fopen(lg,"r"))) return(nosysprv(),0);
    nosysprv();
    while (fgets(inpline,256,fpr)) {
        if (*inpline == '#') continue;
        s_to_lower(inpline);
        if (sscanf(inpline,"%s %s",name,address) == 2) {
            if ((strlen(name) > 4) && !strcmp(&name[strlen(name) - 4],".all")) name[strlen(name) - 4] = '\0';
            if ((strlen(name) > 2) && !strcmp(&name[strlen(name) - 2],".*")) name[strlen(name) - 2] = '\0';
            else if (!strcmp(name,"backbone")) strcpy(name,"*");
            else if (!strcmp(name,"internet")) continue;
            add_tail_mod(name,address);
            if (strcmp(&name[strlen(name)-2],".*")) {
                strcat(name,".*");
                add_tail_mod(name,address);
                }
            }
        }
    fclose(fpr);
    return(1);
}

/*
 *  moderator_address
 *
 *  Returns the address of the moderator of a nominated newsgroup
 */

moderator_address(newsgroup)
    char *newsgroup;
{
    char lg[132],
         *p;
    struct mail_list *mhead;

    *mail_rtn = '\0';
    if (!mailpaths) read_mod_file();
    if (!mailpaths) return(mail_rtn);
    mhead = mailpaths;
    while (mhead) {
        if (wild_match(newsgroup,mhead->group)) {
            strcpy(lg,newsgroup);
            p = lg;
            while (*p) {
                if (*p == '.') *p = '-';
                p++;
                }
            sprintf(mail_rtn,mhead->mod_address,lg);
            return(mail_rtn);
            }
        mhead = mhead->mod_next;
        }
    return(mail_rtn);
}
/*
 *  check_moderator
 *
 *  Check if the supplied address moderates any newsgroups
 */

check_moderator(address)
    char *address;
{
    char la[256],
         ma[256];
    int g;
    struct mail_list *mhead;

    strcpy(la,address);
    s_to_lower(la);
    if (!mailpaths) read_mod_file();
    if (!(mhead = mailpaths)) return(0);
    while (mhead) {
        strcpy(ma,mhead->mod_address);
        s_to_lower(ma);
        if (!strcmp(ma,la)) return(1);
        mhead = mhead->mod_next;
        }
    for (g = 1; g <= ga_size; ++g)
        if (ga[g]->grp_local & NEWS_MOD_ACCESS) return(1);
    return(0);
}

/*
 *  add_moderator
 *
 *  Add moderator address to mailpaths file
 */

add_moderator(name,address)
    char *name,
         *address;
{
    FILE *fpr,
         *fpw;
    char lg[132],
         inpline[256],
         lm[256],
         *p1,
         *p2,
         nm[256],
         ad[256];
    int not_written = 1;
    struct mail_list *h = mailpaths,
                     *t;

    p1 = lm;
    p2 = address;

    while (*p2) {
        *p1++ = *p2;
        if (*p2 == '%') *p1++ = '%';
        p2++;
        }
    *p1 = '\0';

    strcpy(lg,newsmgr_dir);
    strcat(lg,"MAILPATHS");
    sysprv();
    if (!(fpr = fopen(lg,"r"))) fpr = 0;
    if (!(fpw = fopen(lg,"w"))) {
        if (fpr) fclose(fpr);
        nosysprv();
        return(0);
        }

    nosysprv();
    if (!fpr) {
        fprintf(fpw,"%s %s\n",name,lm);
        }
    else {
        while (fgets(inpline,256,fpr)) {
            if (*inpline == '#') fputs(inpline,fpw);
            else {
                if (not_written) {
                    fprintf(fpw,"%s %s\n",name,lm);
                    not_written = 0;
                    }
                if ((sscanf(inpline,"%s %s",nm,ad) == 2) && (!strcmp(name,nm))) continue;
                fputs(inpline,fpw);
                }
            }
        fclose(fpr);
        }
    fclose(fpw);
    strcat(lg,".;-1");
    while (!delete(lg));
    while (h) {
        t = h->mod_next;
        free(h->group);
        free(h->mod_address);
        free(h);
        h = t;
        }
    mailpaths = 0;
}

/*
 *  cvt_date_val
 *
 *  Convert from VMS date format to unix integer date value
 */

cvt_date_val(str)
    char *str;
{
    char locstr[132],
         *l = locstr,
         *p = str;
    int ctime,
        vdate[2],
        tmp[2],
        offset[2] = {0X4BEB4000, 0X007C9567},
        adjtim[2],
        divisor = 10000000,
        udate;
    struct tm *stm;

    do {
        *l++ = toupper(*p++);
        } while (*p);
    *l = '\0';

    if (!strcmp(locstr,"TODAY")) {
        time(&ctime);
        stm = localtime(&ctime);
        return(ctime - (stm->tm_sec + (stm->tm_min * 60) + (stm->tm_hour * 3600)));
        }
    if (!strcmp(locstr,"YESTERDAY")) {
        time(&ctime);
        stm = localtime(&ctime);
        return(ctime - (stm->tm_sec + (stm->tm_min * 60) + (stm->tm_hour * 3600) + 86400));
        }
    if (!strcmp(locstr,"TOMORROW")) {
        time(&ctime);
        stm = localtime(&ctime);
        return(ctime - (stm->tm_sec + (stm->tm_min * 60) + (stm->tm_hour * 3600)) + 86400);
        }
    if (!(sys$bintim(c$dsc(locstr),vdate) & 1)) return(0);
    if (vdate[1] < 0) {
        int now[2],
            then[2],
            len = 2;

        sys$gettim(now);
        lib$subx(now,vdate,then, &len);
        *vdate = *then;
        *(vdate + 1) = *(then + 1);
        }
    lib$subx(vdate, offset, adjtim, c$rfi(2));
    lib$ediv(&divisor, adjtim, &udate, vdate);
    return(udate);
}

/*
 *  do_version
 *
 *  Display NEWS Version
 */

do_version()
{
    sprintf(err_oline,"\t%s - Created on %s\n",NEWS_VERSION,NEWS_VDATE);
    err_line(err_oline);
    return(0);
}

/*
 *  do_setaccess
 *
 *  Enter the ACL editor to set access to a newsgroup
 */
do_setaccess()
{
    char ngroup[132],
         *cp;
    $DESCRIPTOR(ngroup_dsc,ngroup);
    int save_vd;
    short ngroup_len = 0;
    FILE *fpc;

    if (no_priv()) return(err_line("\tError: SET ACCESS - No privilege for operation\n"),0);
    if (cli$get_value(c$dsc("NEWSGROUP"),&ngroup_dsc,&ngroup_len) == CLI$_ABSENT) {
        if (curr_g) get_input_dflt(&ngroup_dsc,c$dsc("Newsgroup: "),&ngroup_len,c$dsc(ga[curr_g]->grp_name),0);
        else get_input_dflt(&ngroup_dsc,c$dsc("Newsgroup: "),&ngroup_len,0,0);
        }
    ngroup[ngroup_len] ='\0';
    sprintf(itm_fname,Dir_template,util_dir(ngroup));
    cp = strrchr(itm_fname,'.');
    *cp = ']';
    cp = strchr(itm_fname,'^');
    *cp = '.';
    if (fpc = fopen(itm_fname,"r")) {
        fclose(fpc);
        if (screen_active) {
            display_brdcst(2);
            no_broad_trap();
            smg$end_pasteboard_update(&pid);
            smg$save_physical_screen(&pid,&save_vd);
            edit_m_on();
            }
        sprintf(ngroup,"EDIT/ACL %s",itm_fname);
        lib$spawn(c$dsc(ngroup),0,0,0,0,0,0,0,0,0,0,0);
        if (screen_active) {
            edit_m_off();
            smg$restore_physical_screen(&pid,&save_vd);
            smg$begin_pasteboard_update(&pid);
            broad_trap();
            }
        }
    return(0);
}

/*
 *  write_access
 *
 *  Return 1 if a user may post to the specified newsgroup
 */

write_access(s)
    char *s;
{
    int g;

                    /* unrestricted user - post anywhere ! */
    if (!no_priv()) return(1);
    if (g = ga_exact_name(s)) {
        if (!(ga[g]->grp_local & NEWS_ACCESS_CHECKED)) check_access(g);
        if (!(ga[g]->grp_local & NEWS_WRITE_ACCESS)) return(0);
        if (ga[g]->grp_local & NEWS_LOCAL) return(1);
        }
    if (!nout_priv()) return(g);
    if (g) ga[g]->grp_local &= ~(NEWS_WRITE_ACCESS | NEWS_MOD_ACCESS | NEWS_MOD_USER);
    return(0);
}

/*
 *  auth_list
 *
 *  Check a newsgroup list, and edit out unauthorized entries
 */

auth_list(gl)
    char *gl;
{
    char s[SUBJLEN],
         locg[1024],
         *cp1 = locg,
         *cp2 = gl,
         *cp3;

    strcpy(locg,gl);
    while (cp3 = strchr(cp1,',')) {
        *cp3++ = '\0';
        util_cvrt(s,cp1);
        if (write_access(s)) {
            strcpy(cp2,s);
            cp2 += strlen(cp2);
            *cp2++ = ',';
            }
        cp1 = cp3;
        }
    util_cvrt(s,cp1);
    if (write_access(s)) {
        strcpy(cp2,s);
        cp2 += strlen(cp2);
        }
    if (cp2 != gl) {
        cp3 = cp2;
        if (*--cp3 == ',') cp2 = cp3;
        }
    *cp2 = '\0';
    return(strlen(gl));
}

do_update()
{
    int slevel=news_context,
        sg = curr_g,
        si = curr_i,
        g;
    char en[256];
    short en_len;
    $DESCRIPTOR(en_dsc,en);

    if (cli$get_value(c$dsc("ENTRYNAME"),&en_dsc,&en_len) & 1) {
        en[en_len] = '\0';
        s_to_lower(en);
        if (g = ga_search_name(en)) update_newsgroup(g);
        else err_line("\tError: Update - No such newsgroup located\n");
        return(0);
        }
    else {
        mem_reset();
        do_select("%",sg,slevel);
        if (slevel > 1) cur_set_itm(sg,si);
        return(0);
        }
}

update_newsgroup(g)
    int g;
{
    ITM_MEM_PTR l_ia;
    GRP_MEM_PTR l_ga;
    int slevel=news_context,
        sg = curr_g,
        si = curr_i,
        l_itm,
        f_itm,
        i;
    char rl[2048],
         il[20];

    do_select("%",g,3);
    set_level(2);
    l_ga = ga[g];
    l_ia = l_ga->grp_ia;
    *rl = '\0';
    if (!l_ga->grp_count) sprintf(rl," <%d",l_ga->grp_topnum);
    else if (!l_ia) sprintf(rl," <0");
    else {
        f_itm = l_itm = 0;
        i = 1;
        while (i <= l_ga->grp_count) {
            if (l_ia[i].itm_unread) {
                l_itm = l_ia[i].itm_num - 1;
                if (!f_itm) sprintf(il," <%d",l_itm);
                else if (f_itm != l_itm) sprintf(il," |%d=%d",f_itm,l_itm);
                else sprintf(il," %d",l_itm);
                strcat(rl,il);
                while ((i <= l_ga->grp_count) && (l_ia[i].itm_unread)) ++i;
                if (i > l_ga->grp_count) {
                    i = 0;
                    break;
                    }
                else f_itm = l_ia[i++].itm_num;
                }
            else ++i;
            }
        if (i > l_ga->grp_count) {
            l_itm = l_ia[i-1].itm_num;
            if (!f_itm) sprintf(il," <%d\n",l_itm);
            else if (f_itm != l_itm) sprintf(il," |%d=%d\n",f_itm,l_itm);
            else sprintf(il," %d\n",l_itm);
            strcat(rl,il);
            }
        }
    strcpy((l_ga->grp_iapaste = malloc(strlen(rl))),rl + 1);
    if (l_ga->grp_ia) free(l_ga->grp_ia);
    l_ga->grp_ia = 0;
    if (l_ga->grp_iavdsize) smg$delete_virtual_display(&l_ga->grp_iavd);
    l_ga->grp_iavdsize = 0;
    set_level(3);
    do_select("%",sg,slevel);
    if (slevel > 1) cur_set_itm(sg,si);
}
