#module NEWSREGISTER "V5.6"

/*
**++
**  FACILITY:
**
**      NEWSREGISTER
**
**  ABSTRACT:
**
**      Manage the per-user newsrc context file for read/unread items,
**      kill filters and marks.
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.5     7-Oct-1988     GIH
**          Change profile name to NEWSRC
**      V5.6    11-Nov-1988     GIH
**
**--
**/

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

static int sys_reg_time = 0,
           new_vals,
           new_ci,
           old_dir_lev,
           reg_confirm = 0;

globalref int newscmd;

/*
 *  read_reg_file
 *  reg_context
 *
 *  Read the register text file and fill out the ga array
 *
 *  register file format:
 *  line 1: <reg/new time in hex digits>
 *  line n: <newsgroup name>: (prio) [unread,topnum] <item numbers>
 *
 *          <item numbers> ::= n      single item
 *                             <n     all up to item n (incl n)
 *                             |n1=n2   range from n1 to n2 (incl)
 *          space char separates <item numbers>
 *
 *  reg_context finishes re-establishing the old news context
 *  (The separation into a second procedure is necessary as there is
 *  a call to init_screen after read_reg_file is invoked)
 */

read_reg_file()
{
    FILE *regr;
    char inline[512],
         *itm_list,
         new_name[128],
         *old_dir_typ;
    int systime,
        f_itm,
        l_itm,
        g = 0,
        i,
        new_cg;
    GRP_MEM_PTR l_ga;

    if (!(regr = fopen(news_register,"r"))) {
        FILE *fpold;
        if (fpold = fopen("SYS$LOGIN:NEWS_GROUPS.REGISTER","r")) {
            regr = fopen(news_register,"w");
            while (fgets(inline,500,fpold)) fputs(inline,regr);
            fclose(fpold);
            fclose(regr);
            if (!(regr = fopen(news_register,"r"))) return(0);
            }
        else return(0);
        }
    fgets(inline,500,regr);

    old_dir_lev = 0;
    if (old_dir_typ = strchr(inline,'^')) {
        *old_dir_typ++ = '\0';
        if (sscanf(old_dir_typ,"%d",&old_dir_lev) != 1) old_dir_lev = 0;
        }

    if ((new_vals = sscanf(inline,"%X %s %d",&systime,new_name,&new_ci)) >= 1) sys_reg_time = systime;
    while (fgets(inline,510,regr)) {
        if (!strcmp(inline,"MARKLIST\n")) {
            mark_read(regr);
            break;
            }
        if (!strcmp(inline,"KILLLIST\n")) {
            kill_read(regr);
            break;
            }
        if (itm_list = strchr(inline,'\n')) *itm_list = '\0';
        if (itm_list = strchr(inline,':')) {
            *itm_list++ = '\0';
            itm_list++;
            }
        if (!(g = ga_exact_name(inline))) continue;
        l_ga = ga[g];

        if (!curr_g) curr_g = g;
        if ((itm_list) && (*itm_list == '(')) {
            if (*++itm_list == '-') l_ga->grp_reg = 0;
            else {
                f_itm = 0;
                sscanf(itm_list,"%d",&f_itm);
                if (f_itm > 255) f_itm = 255;
                l_ga->grp_reg = f_itm;
                }
            itm_list = strchr(itm_list,')');
            itm_list += 2;
            }
        else l_ga->grp_reg = 1;

        if ((itm_list) && (*itm_list == '[')) {
            f_itm = l_itm = 0;
            sscanf(itm_list,"[%d,%d]",&f_itm,&l_itm);
            itm_list = strchr(itm_list,']');
            itm_list += 2;
            }
        else {
            f_itm = l_ga->grp_count;
            l_itm = l_ga->grp_topnum;
            }

        if (!l_ga->grp_count) {
            l_ga->grp_unread = 0;
            continue;
            }

        strcpy((l_ga->grp_iapaste = malloc(strlen(itm_list) + 1)),itm_list);
        l_ga->grp_unread = f_itm + (l_ga->grp_topnum - l_itm);
        if (l_ga->grp_unread > l_ga->grp_count) l_ga->grp_unread = l_ga->grp_count;
        }
    fclose(regr);

                        /* re-establish previous context */
    if ((new_vals >= 2) && (new_cg = ga_exact_name(new_name))) curr_g = new_cg;

    set_level(1);
    return(0);
}

reg_context()
{
    int i;
    ITM_MEM_PTR iap;
    int save_curr_g = curr_g;
    char *gotenv;
    int get_input();

    if (screen_active) {
        if (gotenv = getenv("NEWS_DEFAULT_DIRECTORY")) {
            strcpy(response,"DIRECTORY/");
            strcat(response,gotenv);
            if (cli$dcl_parse(c$dsc(response),&newscmd,get_input,get_input,c$dsc("NEWS_DEF_DIR> ")) & 1) cli$dispatch();
            else {
                sprintf(err_oline,"Error - Cannot parse NEWS_DEFAULT_DIRECTORY: DIR/%s\n",gotenv);
                err_line(err_oline);
                }
            }
        else if (old_dir_lev) do_dir(old_dir_lev,0);
        if (save_curr_g != curr_g) return(0);
        }

    if ((new_vals == 3) && (ga[curr_g]->grp_count)) {
        set_level(2);
        if (new_ci <= ga[curr_g]->grp_ia[1].itm_num) return(cur_up_itm(curr_g,ga[curr_g]->grp_count,0));
        iap = ga[curr_g]->grp_ia;
        for (i = 1; i <= ga[curr_g]->grp_count; ++i) {
            if (new_ci <= iap[i].itm_num) {
                cur_set_itm(curr_g,i);
                return;
                }
            }
        return(cur_set_itm(curr_g,ga[curr_g]->grp_count));
        }
    if (new_vals < 2) selnew(1);
    return(0);
}

/*
 *  write_reg_file
 *
 *  Write the register text file from the ga array
 */

write_reg_file()
{
    FILE *regr;
    char inline[512];
    int systime,
        f_itm,
        l_itm,
        i,
        g;
    ITM_MEM_PTR l_ia;
    GRP_MEM_PTR l_ga;

                        /* open a new version of the register file */
    if (!(regr = fopen(news_register,"w"))) {
        err_line("Cannot access NEWS register file");
        return(0);
        }

                        /* write out the system time */
    fprintf(regr,"%X",sys_reg_time);
    if (curr_g) {
        fprintf(regr," %s",ga[curr_g]->grp_name);
        if ((news_context > 1) && (curr_i > 0))
            fprintf(regr," %d",ga[curr_g]->grp_ia[curr_i].itm_num);
        fprintf(regr,"^%d",cur_dir_type+1);
        }
    fprintf(regr,"\n");

                        /* write out an entry for every newsgroup */
    for (g = 1; g <= ga_size; ++g) {
        l_ga = ga[g];
        l_ia = l_ga->grp_ia;

                        /* write out name, reg prio, unread count, topnum */
        fprintf(regr,"%s: (%d) [%d,%d]",l_ga->grp_name,l_ga->grp_reg,l_ga->grp_unread,l_ga->grp_topnum);

        if (!l_ga->grp_count) {
            fprintf(regr," <%d\n",l_ga->grp_topnum);
            continue;
            }

                        /* if not mapped into memory then check if the
                           old reg string still exists - if so then
                           write it out - (this is a bit of a fudge - If the
                           paste value is greater than 511 then it is the
                           return value of the malloc in read_reg_file()!) */
        if (!l_ia) {
            if (l_ga->grp_iapaste > 511) fprintf(regr," %s\n",l_ga->grp_iapaste);
            else fprintf(regr," <0\n");
            continue;
            }

                        /* Otherwise write out the gory details */

        f_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) fprintf(regr," <%d",l_itm);
                else if (f_itm != l_itm) fprintf(regr," |%d=%d",f_itm,l_itm);
                else fprintf(regr," %d",l_itm);
                while ((i <= l_ga->grp_count) && (l_ia[i].itm_unread)) ++i;
                if (i > l_ga->grp_count) {
                    fprintf(regr,"\n");
                    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) fprintf(regr," <%d\n",l_itm);
            else if (f_itm != l_itm) fprintf(regr," |%d=%d\n",f_itm,l_itm);
            else fprintf(regr," %d\n",l_itm);
            }
        }
    mark_write(regr);
    kill_write(regr);
    fclose(regr);
    while (!delete(news_register_d));       /* purge the register file */
    return(0);
}

/*
 *  screen_dereg
 *  screen_reg
 *
 *  Deregister / Register from a newsgroup
 */

screen_dereg(g)
    int g;
{
    int indx;
    if (indx = ga[g]->grp_display_indx) {
        if (ga[g]->grp_unread) smg$change_rendition(&grp_vd,&indx,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(0),0);
        smg$change_rendition(&grp_vd,&indx,c$rfi(12),c$rfi(1),c$rfi(SUBJLEN),c$rfi(0),0);
        }
}

screen_reg(g)
    int g;
{
    int indx;

    if (indx = ga[g]->grp_display_indx) {
        if (ga[g]->grp_unread) smg$change_rendition(&grp_vd,&indx,c$rfi(4),c$rfi(1),c$rfi(5),c$rfi(SMG$M_BOLD),0);
        smg$change_rendition(&grp_vd,&indx,c$rfi(12),c$rfi(1),c$rfi(SUBJLEN),c$rfi(SMG$M_BOLD),0);
        }
}

/*
 *  do_reg_all
 *
 *  register user in all newsgroups
 */

do_reg_all(p,cretime,type)
    int p,
        cretime,
        type;
{
    int g,
        reg_groups = 0;

    if (!cretime) time(&sys_reg_time);
    for (g = 1; g <= ga_size; ++g) {
        switch (type) {
            case 0 : if (ga[g]->grp_credate < cretime) continue; break;
            case 1 : if (ga[g]->grp_credate >= cretime) continue; break;
            case 2 : if (!(ga[g]->grp_local & 1)) continue; break;
            case 3 : if (ga[g]->grp_local & 1) continue; break;
            }

        if (!ga[g]->grp_reg) {
            if (reg_confirm) {
                sprintf(err_oline,"Register %s? [y]:",ga[g]->grp_name);
                status = get_input(&command,c$dsc(err_oline),&response_length);
                if (status == SMG$_EOF) longjmp(env,2);
                if ((status & 1) && ((!response_length) || (tolower(*response) == 'y'))) status = 1;
                else status = 0;
                }
            else status = 1;
            if (status) {
                no_more_news = 0;
                ++reg_groups;
                ga[g]->grp_reg = p;
                if (screen_active) screen_reg(g);
                else {
                    sprintf(err_oline,"\tRegister: Newsgroup %s\n",ga[g]->grp_name);
                    err_line(err_oline);
                    }
                }
            }
        }
    sprintf(err_oline,"\tRegister: %d Newsgroups registered\n",reg_groups);
    err_line(err_oline);
    if ((reg_groups) && ((cur_dir_type == DIR_NEW) || (cur_dir_type == DIR_REGISTER))) do_dir(cur_dir_type + 1, 1);
    return(0);
}

/*
 *  add_reg
 *
 *  Register a group to a user
 */

add_reg(g,p)
    int g,
        p;
{
    if (!ga[g]->grp_reg) {
        if (reg_confirm) {
            sprintf(err_oline,"Register %s? [y]:",ga[g]->grp_name);
            status = get_input(&command,c$dsc(err_oline),&response_length);
            if (status == SMG$_EOF) longjmp(env,2);
            if ((status & 1) && ((!response_length) || (tolower(*response) == 'y'))) status = 1;
            else status = 0;
            }
        else status = 1;
        if (status) {
            no_more_news = 0;
            ga[g]->grp_reg = p;
            if (screen_active) screen_reg(g);
            else {
                sprintf(err_oline,"\tRegister: Newsgroup %s\n",ga[g]->grp_name);
                err_line(err_oline);
                }
            if ((cur_dir_type == DIR_NEW) || (cur_dir_type == DIR_REGISTER)) do_dir(cur_dir_type + 1, 1);
            }
        }
    else ga[g]->grp_reg = p;
}

/*
 *  do_dereg_all
 *
 *  register user in all newsgroups
 */

do_dereg_all()
{
    int g;

    for (g = 1; g <= ga_size; ++g) {
        if (ga[g]->grp_reg) {
            ga[g]->grp_reg = 0;
            if (screen_active) screen_dereg(g);
            else {
                sprintf(err_oline,"\tDeregister - Remove newsgroup %s\n",ga[g]->grp_name);
                err_line(err_oline);
                }
            }
        }
    if ((cur_dir_type == DIR_NEW) || (cur_dir_type == DIR_REGISTER)) do_dir(cur_dir_type + 1, 1);
    return(0);
}

/*
 *  rem_reg
 *
 *  Deregister a group to a user
 */

rem_reg(g)
    int g;
{
    if (ga[g]->grp_reg) {
        ga[g]->grp_reg = 0;
        if (screen_active) screen_dereg(g);
        else {
            sprintf(err_oline,"\tDeregister - Remove newsgroup %s\n",ga[g]->grp_name);
            err_line(err_oline);
            }
        if ((cur_dir_type == DIR_NEW) || (cur_dir_type == DIR_REGISTER)) do_dir(cur_dir_type + 1, 1);
        }
}

/*
 *  do_deregister
 *
 *  Remove a group from the user list - 1 if successful - 0 on failure
 */

do_deregister()
{
    int i,
        g;
    char ngroup[SUBJLEN];
    $DESCRIPTOR(ngroup_dsc,ngroup);
    short ngroup_len;

    if (cli$present(c$dsc("ALL")) == CLI$_PRESENT) return(do_dereg_all(),0);
    if (cli$get_value(c$dsc("NEWSGROUP"),&ngroup_dsc,&ngroup_len) == CLI$_ABSENT) return((curr_g) ? (rem_reg(curr_g),0) : 0);
    ngroup[ngroup_len] = '\0';
    util_cvrt(ngroup,ngroup);
    if (!(g = ga_search_name(ngroup))) {
        sprintf(err_oline,"\tError: Register - no such Newsgroup: %s\n",ngroup);
        err_line(err_oline);
        return(0);
        }
    rem_reg(g);
    return(0);
}

/*
 *  do_register
 *
 *  register newsgroup(s)
 */

do_register()
{
    int g,
        since_time,
        reg_prio = 1;
    char ngroup[SUBJLEN],
         sincestr[132];
    short ngroup_len,
          since_len;
    $DESCRIPTOR(since_dsc,sincestr);
    $DESCRIPTOR(ngroup_dsc,ngroup);

    if (cli$get_value(c$dsc("PRIORITY"),&ngroup_dsc,&ngroup_len) & 1) {
        ngroup[ngroup_len] = '\0';
        if (sscanf(ngroup,"%d",&reg_prio) != 1) reg_prio = 1;
        }
    if (reg_prio > 255) reg_prio = 255;
    if (reg_prio < 1) reg_prio = 1;
    reg_confirm = cli$present(c$dsc("CONFIRM")) & 1;
    if (cli$present(c$dsc("NEW"))  & 1) {
        do_reg_all(reg_prio,sys_reg_time,0);
        time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("ALL"))  & 1) {
        do_reg_all(reg_prio,0,0);
        time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("LOCAL")) & 1) {
        do_reg_all(reg_prio,0,2);
        if (cli$present(c$dsc("RECORD")) & 1) time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("NETGROUPS")) & 1) {
        do_reg_all(reg_prio,0,3);
        if (cli$present(c$dsc("RECORD")) & 1) time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("SINCE"),&since_dsc,&since_len) & 1) {
        sincestr[since_len] = '\0';
        since_time = cvt_date_val(sincestr);
        do_reg_all(reg_prio,since_time,0);
        if (cli$present(c$dsc("RECORD")) & 1) time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("BEFORE"),&since_dsc,&since_len) & 1) {
        sincestr[since_len] = '\0';
        since_time = cvt_date_val(sincestr);
        do_reg_all(reg_prio,since_time,1);
        if (cli$present(c$dsc("RECORD")) & 1) time(&sys_reg_time);
        return(0);
        }
    if (cli$present(c$dsc("RECORD")) & 1) return(time(&sys_reg_time),0);

    if (cli$get_value(c$dsc("NEWSGROUP"),&ngroup_dsc,&ngroup_len) == CLI$_ABSENT) return((curr_g) ? (add_reg(curr_g,reg_prio),0) : 0);
    ngroup[ngroup_len] = '\0';
    util_cvrt(ngroup,ngroup);
    if (!(g = ga_search_name(ngroup))) {
        sprintf(err_oline,"\tError: Register - no such Newsgroup: %s\n",ngroup);
        err_line(err_oline);
        return(0);
        }
    add_reg(g,reg_prio);
    return(0);
}

static struct marks {
    int m_type;
    char *m_tag;
    unsigned int *m_itms;
    int m_size;
    int m_malloc;
    char *k_grp_name;
    char *k_itm_title;
    char *k_itm_from;
    char *k_itm_header;
    int k_test_from;
    int k_test_header;
    int k_test;
    struct marks *m_next;
    } *m_head = 0, *mk_head = 0;

/*
 *  find_tag
 *
 *  Return a pointer to the record which has a matching tag - start the
 *  search from tag record c
 */

static struct marks *find_tag(t,c,wild)
    char *t;
    struct marks *c;
    int wild;
{

    if (!c) return(0);
    while (c) {
        if ((wild) && (wild_match(c->m_tag,t))) return(c);
        if ((!wild) && (!strcmp(c->m_tag,t))) return(c);
        c = c->m_next;
        }
    return(0);
}

/*
 *  find_itm
 *
 *  Find the item index value itm in the item list associated with the
 *  tag record c
 */

static find_itm(c,itm)
    struct marks *c;
    unsigned int itm;
{
    int i = 0;

    while ((i < c->m_size) && (c->m_itms[i] < itm)) ++i;
    return(((i < c->m_size) && (c->m_itms[i] == itm)) ? i + 1 : 0);
}

/*
 *  mark_read
 *
 *  Read the mark item list from a text file - check for the presence of
 *  all marked items.
 */

mark_read(f)
    FILE *f;
{
    char inline[512],
         *cpd,
         *cp;
    unsigned int gi;
    unsigned short g,
                   i;

    itmrab.rab$l_kbf = &gi;
    itmrab.rab$b_ksz = 4;
    itmrab.rab$b_krf = 0;
    itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    while (fgets(inline,510,f)) {
        if (!strcmp(inline,"KILLLIST\n")) {
            kill_read(f);
            break;
            }
        if (cp = strchr(inline,' ')) {
            *cp++ = '\0';
            do {
                cpd = cp;
                if (cp = strchr(cpd,' ')) *cp++ = '\0';
                if (sscanf(cpd,"%d",&gi) == 1) {
                    g = gi >> 16;
                    i = gi & 0xFFFF;
                    if (sys$find(&itmrab) & 1) mark_set(g,i,inline);
                    }
                } while (cp);
            }
        }
}

/*
 *  mark_write
 *
 *  Write the mark list out to a text file
 */

mark_write(f)
    FILE *f;
{
    struct marks *c = m_head;
    int i,
        init = 0;

    while (c) {
        if (c->m_size) {
            if (!init++) fprintf(f,"MARKLIST\n");
            fprintf(f,"%s",c->m_tag);
            for (i = 0; i < c->m_size; ++i) fprintf(f," %u",c->m_itms[i]);
            fprintf(f,"\n");
            }
        c = c->m_next;
        }
}

/*
 *  mark_set
 *
 *  Set item (g,i) to have tag "tag"
 */

mark_set(g,i,tag)
    unsigned short g,i;
    char *tag;
{
    struct marks *c = m_head;
    unsigned int itm = (g << 16) + i;
    int indx,
        j;
    char *cp;

    if (!tag) tag = "mark";
    s_to_lower(tag);
    if (cp = strchr(tag,' ')) *cp = '\0';
    if (!*tag) tag = "mark";
    if (!(c = find_tag(tag,c,0))) {
        c = malloc(sizeof *c);
        c->m_next = m_head ;
        m_head = c;
        strcpy((c->m_tag = malloc(strlen(tag) + 1)),tag);
        c->m_itms = malloc((c->m_malloc = 10) * (sizeof *(c->m_itms)));
        c->m_size = 1;
        c->m_type = 0;
        c->k_grp_name = 0;
        c->k_itm_title = 0;
        c->k_itm_from = 0;
        c->k_itm_header = 0;
        *(c->m_itms) = itm;
        return;
        }
    if (!find_itm(c,itm)) {
        if (c->m_malloc == c->m_size) c->m_itms = malloc((c->m_malloc += 10) * (sizeof *(c->m_itms)));
        indx = 0;
        while ((indx < c->m_size) && (c->m_itms[indx] < itm)) ++indx;
        for (j = c->m_size; j > indx; --j) c->m_itms[j-1] = c->m_itms[j] ;
        c->m_itms[indx] = itm;
        ++(c->m_size);
        }
}

/*
 *  mark_clear
 *
 *  Clear all items with a mark of tag value "tag"
 */

mark_clear(tag,h)
    char *tag;
    struct marks *h;
{
    struct marks *p = 0, *c = h;
    char *cp;

    if (!c) return;
    if (tag) {
        s_to_lower(tag);
        if (cp = strchr(tag,' ')) *cp = '\0';
        while (c = find_tag(tag,c,1)) {
            if (c != h) {
                p = h;
                while (p->m_next != c) p = p->m_next;
                }
            else if (h == m_head) h = m_head = m_head->m_next;
            else h = mk_head = mk_head->m_next;
            if (c->m_itms) free(c->m_itms);
            if (c->m_tag) free(c->m_tag);
            if (c->k_grp_name) free(c->k_grp_name);
            if (c->k_itm_title) free(c->k_itm_title);
            if (c->k_itm_from) free(c->k_itm_from);
            if (c->k_itm_header) free(c->k_itm_header);
            if (p) p->m_next = c->m_next;
            p = c;
            c = c->m_next;
            free(p);
            }
        return;
        }
    while (c) {
        if (c->m_itms) free(c->m_itms);
        if (c->m_tag) free(c->m_tag);
        if (c->k_grp_name) free(c->k_grp_name);
        if (c->k_itm_title) free(c->k_itm_title);
        if (c->k_itm_from) free(c->k_itm_from);
        if (c->k_itm_header) free(c->k_itm_header);
        p = c;
        c = c->m_next;
        free(p);
        }
    if (h == m_head) m_head = 0;
    else mk_head = 0;
}

/*
 *  mark_del
 *
 *  Delete the entry for (g,i) with tag "tag"
 */

mark_del(g,i,tag)
    unsigned short g,i;
    char *tag;
{
    struct marks *c = m_head;
    unsigned int itm = (g << 16) + i;
    int indx,
        j;
    char *cp;

    if (tag) {
        s_to_lower(tag);
        if (cp = strchr(tag,' ')) *cp = '\0';
        if ((c = find_tag(tag,c,0)) && (indx = find_itm(c,itm))) {
            for (j = indx; j < c->m_size; ++j) c->m_itms[j] = c->m_itms[j-1] ;
            if (!--(c->m_size)) {
                if (c == m_head) m_head = m_head->m_next;
                else {
                    struct marks *t = m_head;

                    while (t->m_next != c) t = t->m_next;
                    t->m_next = c->m_next;
                    }
                if (c->m_itms) free(c->m_itms);
                if (c->m_tag) free(c->m_tag);
                if (c->k_grp_name) free(c->k_grp_name);
                if (c->k_itm_title) free(c->k_itm_title);
                if (c->k_itm_from) free(c->k_itm_from);
                if (c->k_itm_header) free(c->k_itm_header);
                free(c);
                }
            }
        return;
        }
    while (c) {
        if (i = find_itm(c,itm)) {
            for (j = i; j < c->m_size; ++j) c->m_itms[j] = c->m_itms[j-1] ;
            if (!--(c->m_size)) {
                struct marks *t = m_head;

                if (c == m_head) m_head = m_head->m_next;
                else {
                    while (t->m_next != c) t = t->m_next;
                    t->m_next = c->m_next;
                    }
                if (c->m_itms) free(c->m_itms);
                if (c->m_tag) free(c->m_tag);
                if (c->k_grp_name) free(c->k_grp_name);
                if (c->k_itm_title) free(c->k_itm_title);
                if (c->k_itm_from) free(c->k_itm_from);
                if (c->k_itm_header) free(c->k_itm_header);
                t = c;
                c = c->m_next;
                free(t);
                continue;
                }
            }
        c = c->m_next;
        }
}

/*
 *  mark_find
 *
 *  Find the next item AFTER g,i with tag "tag" - return (g,i) in *ret
 */

mark_find(g,i,tag,ret)
    unsigned short g,i;
    char *tag;
    unsigned int *ret;
{
    struct marks *c = m_head;
    unsigned int itm = ((g << 16) + i),
                 high_val = 0,
                 low_val = 0;
    int indx;
    char *cp;

    if (!tag) tag = "*";
    s_to_lower(tag);
    if (cp = strchr(tag,' ')) *cp = '\0';
    while (c = find_tag(tag,c,1)) {
        if (c->m_size) {
            if ((c->m_itms[0] < itm) && ((!low_val) || (c->m_itms[0] < low_val))) low_val = c->m_itms[0];
            indx = 0;
            while ((indx < c->m_size) && (c->m_itms[indx] <= itm)) indx++;
            if ((indx < c->m_size) && ((!high_val) || (c->m_itms[indx] < high_val))) high_val = c->m_itms[indx];
            }
        c = c->m_next;
        }
    if (high_val) return(*ret = high_val);
    return(*ret = low_val);
}

/*
 *  mark_list
 *
 *  Return a list of tags for a news item
 */

char mark_list_ret[256];

mark_list(g,m)
    unsigned short g,m;
{
    struct marks *c = m_head;
    unsigned int itm = ((g << 16) + m);

    *mark_list_ret = '\0';

    while (c) {
        if (find_itm(c,itm)) {
            if (!*mark_list_ret) strcpy(mark_list_ret,"Mark: ");
            else strcat(mark_list_ret,",");
            strcat(mark_list_ret,c->m_tag);
            }
        c = c->m_next;
        }
    return(mark_list_ret);
}

/*
 *  mark_show
 *
 *  Generate a display of marked items
 */

mark_show(tag,h,one)
    char *tag;
    struct marks *h;
    int one;
{
    struct marks *c = h;
    int i,
        j,
        init = 0,
        gi;
    unsigned short lst_grp = 0,
                   locitm,
                   g;
    char *cp;
    ITM_MEM_PTR iap;

    if (!tag) tag = "*";
    s_to_lower(tag);
    if (cp = strchr(tag,' ')) *cp = '\0';
    while (c = find_tag(tag,c,1)) {
        lst_grp = 0;
        if (c->m_type || c->m_size) {
            if (!init) {
                start_header();
                if (!c->m_type) {
                    sprintf(err_oline,"MARKed Newsitems listing for TAG %s",tag);
                    put_line(err_oline,0);
                    }
                else {
                    put_line("KILL Filter display:",0);
                    put_line("",0);
                    put_line("Newsgroup S:subject F:from H:header",0);
                    }
                put_line("",0);
                end_header();
                init = 1;
                }

            put_line("",0);
            sprintf(err_oline,"TAG: %s",c->m_tag);
            put_line(err_oline,0);
            if (!c->m_type) {
                for (i = 0; i < c->m_size; ++i) {
                    locitm = c->m_itms[i] & 0xFFFF;
                    if ((g = (c->m_itms[i] >> 16)) != lst_grp) {
                        if (gi = ga_locate(g)) sprintf(err_oline,"  Newsgroup: %s",ga[gi]->grp_name);
                        else continue;
                        put_line(err_oline,0);
                        if (!ga[gi]->grp_ia) map_items(gi);
                        if (!(iap = ga[gi]->grp_ia)) continue;
                        lst_grp = g;
                        }
                    for (j = 1; j <= ga[gi]->grp_count; ++j) {
                        if (locitm == iap[j].itm_num) {
                            sprintf(err_oline,"    %-5d %-*.*s",locitm,SUBJLEN,SUBJLEN,iap[j].itm_title);
                            put_line(err_oline,0);
                            break;
                            }
                        if (locitm < iap[j].itm_num) {
                            sprintf(err_oline,"    %-5d -Item not located-",locitm);
                            put_line(err_oline,0);
                            break;
                            }
                        }
                    }
                }
            else {
                strcpy(err_oline,c->k_grp_name);
                if (c->k_itm_title) {
                    strcat(err_oline," S:");
                    strcat(err_oline,c->k_itm_title);
                    }
                if (c->k_itm_from) {
                    strcat(err_oline," F:");
                    strcat(err_oline,c->k_itm_from);
                    }
                if (c->k_itm_header) {
                    strcat(err_oline," H:");
                    strcat(err_oline,c->k_itm_header);
                    }
                put_line(err_oline,0);
                }
            }
        if (one) break;
        c = c->m_next;
        }
    if (!init) {
        if (h == m_head) {
            sprintf(err_oline,"\tMARK - NO Items marked with TAG = %s\n",tag);
            err_line(err_oline);
            }
        else err_line("\tShow Kill - No Kill Filters defined\n");
        }
    else put_line("",1);
}

do_mark()
{
    int noteid;
    char tag[80];
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);

    if ((!curr_g) || (news_context < 2) || (curr_i < 1))
        return(err_line("\tError: MARK no item specified\n"),0);
    if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) tag[tag_len] = '\0';
    else strcpy(tag,"mark");
    if (!(noteid = getnoteid())) noteid = curr_i;
    if (noteid < 0)
        return(err_line("\tError: MARK no item specified\n"),0);
    mark_set(ga[curr_g]->grp_num,ga[curr_g]->grp_ia[noteid].itm_num,tag);
    sprintf(err_oline,"\tCurrent item marked with Tag: %s\n",tag);
    err_line(err_oline);
    return(0);
}

do_unmark()
{
    char tag[80];
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);

    if ((!curr_g) || (news_context < 2) || (curr_i < 1))
        err_line("\tError: MARK no current newsitem to mark\n");
    if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) {
        tag[tag_len] = '\0';
        mark_del(ga[curr_g]->grp_num,ga[curr_g]->grp_ia[curr_i].itm_num,tag);
        sprintf(err_oline,"\tCurrent item unmarked with Tag: %s\n",tag);
        }
    else {
        mark_del(ga[curr_g]->grp_num,ga[curr_g]->grp_ia[curr_i].itm_num,0);
        sprintf(err_oline,"\tCurrent item unmarked\n");
        }
    err_line(err_oline);
    return(0);
}

do_markshow()
{
    char tag[80];
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);

    if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) tag[tag_len] = '\0';
    else strcpy(tag,"*");
    mark_show(tag,m_head,0);
    return(0);
}

do_markclear()
{
    char tag[80];
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);

    if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) {
        tag[tag_len] = '\0';
        mark_clear(tag,m_head);
        sprintf(err_oline,"\tALL items cleared with Tag: %s\n",tag);
        }
    else {
        mark_clear(0,m_head);
        sprintf(err_oline,"\tALL Marks cleared\n");
        }
    err_line(err_oline);
    return(0);
}

kill_read(f)
    FILE *f;
{
    int i;
    char inline[512],
         *cp,
         *subj,
         *from,
         *header;
    struct marks *n, *c = mk_head;

    if (mk_head) return;

    while (fgets(inline,510,f)) {
        from = subj = header = 0;
        if (cp = strchr(inline,'\n')) *cp = '\0';
        if (cp = strchr(inline,' ')) {
            if (i = substrcmp(cp," S:")) {
                cp[i-1] = '\0';
                subj = cp + (i + 2);
                }
            if (i = substrcmp(cp," F:")) {
                cp[i-1] = '\0';
                from = cp + (i + 2);
                }
            if (i = substrcmp(cp," H:")) {
                cp[i-1] = '\0';
                header = cp + (i + 2);
                }
            *cp = '\0';

            if (!subj && !from && !header) continue;
            while ((c) && (c->m_next)) c = c->m_next;
            n = malloc(sizeof *n);
            n->m_type = 1;
            n->m_next = 0;
            if (!c) {
                mk_head = n;
                strcpy((n->m_tag = malloc(2)),"1");
                }
            else {
                int tag_num = 1;
                char tag_val[4];
                if (sscanf(c->m_tag,"%d",&tag_num) == 1) ++tag_num;
                c->m_next = n;
                sprintf(tag_val,"%d",tag_num);
                strcpy((n->m_tag = malloc(strlen(tag_val) + 1)),tag_val);
                }
            c = n;
            strcpy((c->k_grp_name = malloc(strlen(inline) + 1)),inline);
            if (subj) {
                s_to_lower(subj);
                strcpy((c->k_itm_title = malloc(strlen(subj) + 1)),subj);
                }
            else c->k_itm_title = 0;
            if (from) {
                s_to_lower(from);
                strcpy((c->k_itm_from = malloc(strlen(from) + 1)),from);
                }
            else c->k_itm_from = 0;
            if (header) {
                s_to_lower(header);
                strcpy((c->k_itm_header = malloc(strlen(header) + 1)),header);
                }
            else c->k_itm_header = 0;
            c->m_itms = 0;
            }
        }
}

/*
 *  kill_write
 *
 *  Write the kill list out to a text file
 */

kill_write(f)
    FILE *f;
{
    struct marks *c = mk_head;
    int i,
        init = 0;

    while (c) {
        if (!init++) fprintf(f,"KILLLIST\n");
        fprintf(f,"%s",c->k_grp_name);
        if (c->k_itm_title) fprintf(f," S:%s",c->k_itm_title);
        if (c->k_itm_from) fprintf(f," F:%s",c->k_itm_from);
        if (c->k_itm_header) fprintf(f," H:%s",c->k_itm_header);
        fprintf(f,"\n");
        c = c->m_next;
        }
}

kill_filter(g,i)
    int g,
        i;
{
    struct marks *c = mk_head;
    char *ngroup,
         subj[SUBJLEN],
         to_test = 0,
         filter_result = 0,
         *cp,
         inpline[256];

    if (!c) return(0);

    ngroup = ga[g]->grp_name;
    strcpy(subj,ga[g]->grp_ia[i].itm_title);
    s_to_lower(subj);

    do {
        c->k_test = 0;
        if (!wild_match(ngroup,c->k_grp_name)) continue;
        if ((c->k_itm_title) && (!wild_match(subj,c->k_itm_title))) continue;
        if (!(c->k_test_from = c->k_itm_from) && !(c->k_test_header = c->k_itm_header)) return(1);
        to_test = c->k_test = 1;
        } while (c = c->m_next);
    if (!to_test) return(0);
    if (!(fp = do_open_item(g,i,"r",fp_open))) return(0);
    while (fgets(inpline,256,fp)) {
        if (*inpline == '\n') break;
        s_to_lower(inpline);
        if (cp = strchr(inpline,'\n')) *cp = '\0';
        strip(inpline,strlen(inpline));
        c = mk_head;
        do {
            if (!c->k_test) continue;
            if (c->k_test_header) {
                if (wild_match(inpline,c->k_itm_header)) {
                    if (!c->k_test_from) {
                        filter_result = 1;
                        break;
                        }
                    c->k_test_header = 0;
                    }
                }
            if (c->k_test_from && !strncmp(inpline,"from:",5)) {
                if (wild_match(inpline,c->k_itm_from)) {
                    if (!c->k_test_header) {
                        filter_result = 1;
                        break;
                        }
                    c->k_test_from = 0;
                    }
                }
            } while (c = c->m_next);
        if (filter_result) break;
        }
    fclose(fp);
    if (*fp_open > 1) while (!(delete(fp_open)));
    *fp_open = '\0';
    return(filter_result);
}

/*
 *  do_killshow
 *
 *  Generate a display of marked items
 */

do_killshow()
{
    mark_show("*",mk_head,0);
    return(0);
}

/*
 *  do_kill
 *
 *  Add an entry into the kill filter list
 */

do_kill()
{
    struct marks *n, *c = mk_head;
    short kh_len;
    int ks = 0,
        kf = 0,
        kh = 0;
    char *dflt,
         newslist[256],
         inpline[256],
         from[256],
         *subj,
         title[132],
         kht[132],
         *cp,
         *cp1;
    $DESCRIPTOR(kh_dsc,kht);


    if ((!curr_g) || (news_context < 2) || (curr_i < 1)) return(err_line("\tError: Kill - No current item selected\n"),0);

    if (cli$present(c$dsc("SUBJECT")) & 1) ks = 1;
    if (cli$present(c$dsc("FROM")) & 1) kf = 1;
    if ((cli$get_value(c$dsc("HEADER"),&kh_dsc,&kh_len) & 1) && kh_len) {
        kht[kh_len] = '\0';
        strcat(kht,"*");
        s_to_lower(kht);
        kh = 1;
        }
    else kh_len = 0;

    if (!(ks + kf + kh)) ks = 1;
    dflt = ga[curr_g]->grp_name;
    parse_newsgroups(newslist,dflt,1);
    if (!*newslist) return(err_line("\tError: Kill - No newsgroup specified\n"),0);
    if (ks) {
        ks = 1;
        strncpy(title,ga[curr_g]->grp_ia[curr_i].itm_title,5);
        s_to_lower(title);
        if (!strncmp(title,"re:",3)) {
            strcpy(title,ga[curr_g]->grp_ia[curr_i].itm_title);
            cp = &title[3];
            while ((*cp) && (isspace(*cp))) cp++;
            if (!*cp) return(err_line("\tError: Kill/Subject - No subject!\n"),0);
            *--cp = '*';
            subj = cp;
            }
        else {
            *title = '*';
            subj = strcpy(&title[1],ga[curr_g]->grp_ia[curr_i].itm_title);
            strcat(title,"*");
            }
        strip(subj,strlen(subj));
        }
    if (kf || kh) {
        *from = '\0';
        if (fp = do_open_item(curr_g,curr_i,"r",fp_open)) {
            while (fgets(inpline,256,fp)) {
                if (*inpline == '\n') break;
                if (cp = strchr(inpline,'\n')) *cp = '\0';
                if (!strncmp(inpline,"From:",5)) {
                    strcpy(from,inpline);
                    if (!kh_len) break;
                    }
                if (kh_len) {
                    s_to_lower(inpline);
                    if (wild_match(inpline,kht)) {
                        strcpy(kht,inpline);
                        kh_len = 0;
                        if (*from) break;
                        }
                    }
                }
            fclose(fp);
            if (*fp_open > 1) while (!(delete(fp_open)));
            *fp_open = '\0';
            }
        if (*from) strip(from,strlen(from));
        else kf = 0;
        if (kh && !kh_len) strip(kht,strlen(kht));
        else kh = 0;
        }
    if (!(ks + kf + kh)) return(err_line("\tError: Kill - Cannot locate item text\n"),0);
    while ((c) && (c->m_next)) c = c->m_next;
    cp = newslist;
    do {
        if (cp1 = strchr(cp,',')) *cp1++ = '\0';
        n = malloc(sizeof *n);
        n->m_type = 1;
        n->m_next = 0;
        if (!c) {
            mk_head = n;
            strcpy((n->m_tag = malloc(2)),"1");
            }
        else {
            int tag_num = 1;
            char tag_val[4];
            if (sscanf(c->m_tag,"%d",&tag_num) == 1) ++tag_num;
            c->m_next = n;
            sprintf(tag_val,"%d",tag_num);
            strcpy((n->m_tag = malloc(strlen(tag_val) + 1)),tag_val);
            }
        c = n;
        strcpy((c->k_grp_name = malloc(strlen(cp) + 1)),cp);
        if (ks) {
            s_to_lower(subj);
            strcpy((c->k_itm_title = malloc(strlen(subj) + 1)),subj);
            }
        else c->k_itm_title = 0;
        if (kf) {
            s_to_lower(from);
            strcpy((c->k_itm_from = malloc(strlen(from) + 1)),from);
            }
        else c->k_itm_from = 0;
        if (kh) strcpy((c->k_itm_header = malloc(strlen(kht) + 1)),kht);
        else c->k_itm_header = 0;
        c->m_itms = 0;
        } while (cp = cp1);
    if (mk_head) mark_show("*",mk_head,0);
    err_line("\tKILL Filter added to list\n");
    return(0);
}

do_killclear()
{
    char tag[80];
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);

    if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) {
        tag[tag_len] = '\0';
        mark_clear(tag,mk_head);
        sprintf(err_oline,"\tCleared Kill Filter with Tag: %s\n",tag);
        if (mk_head) mark_show("*",mk_head,0);
        else err_line("\tALL Kill Filters cleared\n");
        }
    else {
        mark_clear(0,mk_head);
        err_line("\tALL Kill Filters cleared\n");
        }
    return(0);
}

do_killmod()
{
    char tag[80],
         newslist[256],
         *kng,
         *kt,
         *kf;
    short tag_len;
    $DESCRIPTOR(tag_dsc,tag);
    struct marks *p = 0,
                 *c = mk_head;
    char *cp;

    if (!mk_head) err_line("\tNo KILL filters defined\n");
    else if (cli$get_value(c$dsc("TAG"),&tag_dsc,&tag_len) & 1) {
        tag[tag_len] = '\0';
        s_to_lower(tag);
        if (cp = strchr(tag,' ')) *cp = '\0';
        killmod_recurse(c,tag);
        }
    if (mk_head) {
        if (c = find_tag(tag,mk_head,1)) mark_show(tag,mk_head,0);
        else mark_show("*",mk_head,0);
        }
    else err_line("\tALL Kill Filters cleared\n");
    return(0);
}

killmod_recurse(c,tag)
    struct marks *c;
    char *tag;
{
    struct marks *p = 0,
                 *n;
    char *kng,
         *kt,
         *kf,
         *kh,
         *cp,
         *cp1,
         newslist[256],
         nkt[132],
         nkf[132],
         nkh[132];
    short kt_len,
          trm;
    $DESCRIPTOR(kt_dsc,nkt);
    $DESCRIPTOR(kf_dsc,nkf);
    $DESCRIPTOR(kh_dsc,nkh);

    if (c = find_tag(tag,c,1)) {
        mark_show("*",c,1);
        if (c != mk_head) {
            p = mk_head;
            while (p->m_next != c) p = p->m_next;
            }
        else mk_head = mk_head->m_next;
        if (c->m_itms) free(c->m_itms);
        if (c->m_tag) free(c->m_tag);
        kng = c->k_grp_name;
        kt = c->k_itm_title;
        kf = c->k_itm_from;
        kh = c->k_itm_header;
        err_line("\tNewsgroups to which this kill filter applies\n");
        parse_newsgroups(newslist,kng,1);
        err_line("\tSubject: line to filter (empty line if not used)\n");
        get_input_dflt(&kt_dsc,c$dsc("Kill_Subject: "),&kt_len,(kt ? c$dsc(kt) : 0),&trm);
        nkt[kt_len] = '\0';
        err_line("\tFrom: to filter (empty line if not used)\n");
        get_input_dflt(&kf_dsc,c$dsc("Kill_From: "),&kt_len,(kf ? c$dsc(kf) : 0),&trm);
        nkf[kt_len] = '\0';
        err_line("\tHeader line to filter (empty line if not used)\n");
        get_input_dflt(&kh_dsc,c$dsc("Kill_Header: "),&kt_len,(kh ? c$dsc(kh) : 0),&trm);
        nkh[kt_len] = '\0';
        s_to_lower(nkt);
        s_to_lower(nkf);
        s_to_lower(nkh);
        if (c->k_grp_name) free(c->k_grp_name);
        if (c->k_itm_title) free(c->k_itm_title);
        if (c->k_itm_from) free(c->k_itm_from);
        if (c->k_itm_header) free(c->k_itm_header);
        if (p) p->m_next = c->m_next;
        p = c;
        c = c->m_next;
        free(p);
        killmod_recurse(c,tag);
        c = mk_head;
        while ((c) && (c->m_next)) c = c->m_next;
        cp = newslist;
        if (!*cp || (!*nkt && !*nkf && !*nkh)) return(err_line("\tKill filter cleared\n"));
        do {
            if (cp1 = strchr(cp,',')) *cp1++ = '\0';
            n = malloc(sizeof *n);
            n->m_type = 1;
            n->m_next = 0;
            if (!c) {
                mk_head = n;
                strcpy((n->m_tag = malloc(2)),"1");
                }
            else {
                int tag_num = 1;
                char tag_val[4];
                if (sscanf(c->m_tag,"%d",&tag_num) == 1) ++tag_num;
                c->m_next = n;
                sprintf(tag_val,"%d",tag_num);
                strcpy((n->m_tag = malloc(strlen(tag_val) + 1)),tag_val);
                }
            c = n;
            strcpy((c->k_grp_name = malloc(strlen(cp) + 1)),cp);
            if (*nkt) strcpy((c->k_itm_title = malloc(strlen(nkt) + 1)),nkt);
            else c->k_itm_title = 0;
            if (*nkf) strcpy((c->k_itm_from = malloc(strlen(nkf) + 1)),nkf);
            else c->k_itm_from = 0;
            if (*nkh) strcpy((c->k_itm_header = malloc(strlen(nkh) + 1)),nkh);
            else c->k_itm_header = 0;
            c->m_itms = 0;
            } while (cp = cp1);
        err_line("\tKILL Filter modified\n");
        }
}

getnoteid()
{
    int locnum,
        i;
    char note[80];
    short note_len;
    $DESCRIPTOR(note_dsc,note);
    ITM_MEM_PTR iap;

    if (cli$get_value(c$dsc("NOTEID"),&note_dsc,&note_len) & 1) {
        if (!curr_g || !(ga[curr_g]->grp_count) || (news_context < 2) || !note_len)
            return(-1);
        note[note_len] = '\0';
        s_to_lower(note);
        if (isdigit(*note)) {
            if (sscanf(note,"%d",&locnum) != 1) return(-1);
            iap = ga[curr_g]->grp_ia;
            for (i = 1; i <= ga[curr_g]->grp_count; ++i) {
                if (locnum < iap[i].itm_num) return(-1);
                if (locnum == iap[i].itm_num) return(i);
                }
            return(-1);
            }
        else if (!strncmp(note,"first",min(note_len,5))) return(1);
        else if (!strncmp(note,"last",min(note_len,4))) return(ga[curr_g]->grp_count);
        else if (!strcmp(note,".")) return(curr_i);
        else if (!strcmp(note,"*")) return(ga[curr_g]->grp_count);
        else return(-1);
        }
    else return(0);
}

getnoterange()
{
    char s[80];
    short s_len;
    $DESCRIPTOR(s_dsc,s);
    int di,
        pi = 0,
        li,
        fi,
        i;
    unsigned int ret;
    unsigned short g,m,fg,fm;
    ITM_MEM_PTR iap;

    d_itm[0].fitm = 0;

    if (cli$get_value(c$dsc("MARK"),&s_dsc,&s_len) & 1) {
        do {
            s[s_len] = '\0';
            fg = fm = g = m = 0;
            while (mark_find(g,m,s,&ret)) {
                g = ret >> 16;
                m = ret & 0xFFFF;
                if (!fg) {
                    fg = g;
                    fm = m;
                    }
                else if ((fg == g) && (fm == m)) break;
                if (!(ret = ga_locate(g))) continue;
                if (!ga[ret]->grp_count) continue;
                if (!ga[ret]->grp_ia) map_items(ret);
                for (i = 1; i <= ga[ret]->grp_count; ++i)
                    if (ga[ret]->grp_ia[i].itm_num == m) break;
                if (i > ga[ret]->grp_count) continue;
                d_itm[pi].fitm = i;
                d_itm[pi].litm = 0;
                d_itm[pi].ngrp = ret;
                d_itm[++pi].fitm = 0;
                }
            } while (cli$get_value(c$dsc("MARK"),&s_dsc,&s_len) & 1);
        return(d_itm[0].fitm);
        }

    d_itm[0].fitm = 0;
    if ((curr_i > 0) && (curr_g) && (news_context > 1))
        d_itm[0].fitm = ga[curr_g]->grp_ia[curr_i].itm_num;
    d_itm[0].litm = 0;
    d_itm[0].ngrp = curr_g;
    d_itm[1].fitm = 0;
    if (!curr_g) return(0);

    if (cli$get_value(c$dsc("NOTERANGE"),&s_dsc,&s_len) == CLI$_ABSENT) {
        if (!screen_active) {
            sprintf(err_oline,"%d",curr_i);
            get_input_dflt(&s_dsc,c$dsc("Item: "),&s_len,c$dsc(err_oline),0);
            d_itm[0].fitm = 0;
            s[s_len] = '\0';
            parse_items(s,d_itm);
            }
        }
    else {
        char temp_s[256];

        *temp_s = '\0';
        do {
            s[s_len] = '\0';
            strcat(temp_s,s);
            strcat(temp_s,",");
            } while (cli$get_value(c$dsc("NOTERANGE"),&s_dsc,&s_len) & 1);
        temp_s[strlen(temp_s) - 1] = '\0';
        strcpy(s,temp_s);
        parse_items(temp_s,d_itm);
        }

    map_items(curr_g);
    if ((!(ga[curr_g]->grp_count)) || (!(iap = ga[curr_g]->grp_ia)))
        return(d_itm[0].fitm = d_itm[0].litm = 0);
    for (pi = di = 0; (li = d_itm[di].litm), (fi = d_itm[di].fitm) ; ++di) {
        if ((fi > 0) && ((!li) || (li == fi))) {
            for (i = 1; i <= ga[curr_g]->grp_count; ++i) {
                if (fi < iap[i].itm_num) break;
                if (fi == iap[i].itm_num) {
                    d_itm[pi].litm = 0;
                    d_itm[pi].ngrp = curr_g;
                    d_itm[pi++].fitm = i;
                    break;
                    }
                }
            }
        else if (fi < 0) {
            d_itm[0].fitm = -1;
            d_itm[0].ngrp = curr_g;
            d_itm[0].litm = d_itm[1].fitm = 0;
            return(-1);
            }
        else {
            int nfi = 0,
                nli = 0;
            for (i = 1; i <= ga[curr_g]->grp_count; ++i) {
                if ((fi >= iap[i].itm_num) && !nfi) nfi = i;
                if (li >= iap[i].itm_num) nli = i;
                else break;
                }
            if (nfi == nli) nli = 0;
            if (nfi) {
                d_itm[pi].litm = nli;
                d_itm[pi].ngrp = curr_g;
                d_itm[pi++].fitm = nfi;
                }
            }
        }
    d_itm[pi].fitm = 0;
    return(d_itm[0].fitm);
}
/* THIS CODE COMMENTED OUT DELIBERATELY -- GIH
struct st_entry {
    char *e_name;
    char *e_node;
    char *e_file;
    int type;
    int proto;
    int prio;
    int unread;
    int topnum;
    char *seen_string;
    char **member_class;
    struct st_entry *e_next;
    } *e_head = 0;

newsrc file format
ENTRY
ename[ ~node!file@type#proto%classes]: (prio) [unread,topnum]  seen_string
CLASS
* cname
ename

read_entries(f)
    FILE *f;
{
    char inline[512],
         cclass[132],
         *nodep,
         *filep,
         *typep,
         *protp,
         *clasp,
         *itm_list;
    struct st_entry *tmp, *et = 0;
    struct st_entry *ctmp, *ec = 0;

    while (fgets(inline,512,f)) {
        if (itm_list = strchr(inline,'\n')) *itm_list = '\0';
        if (entries) {
            tmp = malloc(sizeof *tmp);
            if (!itm_list = strchr(inline,':')) continue;
            *itm_list++ = '\0';
            if (specs = strchr(inline,' ')) {
                *specs++ = '\0';
                if (clasp = strchr(specs,'%')) *clasp++ = '\0';
                if (protp = strchr(specs,'#')) *protp++ = '\0';
                if (typep = strchr(specs,'@')) *typep++ = '\0';
                if (filep = strchr(specs,'!')) *filep++ = '\0';
                if (nodep = strchr(specs,'~')) *nodep++ = '\0';
                }
            *itm_list++ = '\0';
            itm_list++;
            }
            if (*itm_list == '(') {
                if (*++itm_list == '-') tmp->prio = 0;
                else {
                    tmp->prio = 0;
                    sscanf(itm_list,"%d",&(tmp->prio));
                    if (tmp->prio > 255) tmp->prio = 255;
                    }
                itm_list = strchr(itm_list,')');
                itm_list += 2;
                }
            else tmp->prio = 0;
            tmp->unread = tmp->topnum = 0;
            if ((itm_list) && (*itm_list == '[')) {
                sscanf(itm_list,"[%d,%d]",&(tmp->unread),&(tmp->topnum));
                itm_list = strchr(itm_list,']');
                itm_list += 2;
                }
            strcpy((tmp->e_name = malloc(strlen(inline) + 1)),inline);
            if (nodep) strcpy((tmp->e_node = malloc(strlen(nodep) + 1)),nodep);
            else tmp->e_node = 0;
            if (nodep) strcpy((tmp->e_file = malloc(strlen(filep) + 1)),filep);
            else tmp->e_file = 0;
            tmp->type = 0;
            sscanf(typep,"%d",&(tmp->type));
            tmp->proto = 0;
            sscanf(protp,"%d",&(tmp->proto));
            if (clasp) add_class_list(tmp,clasp);
            strcpy((tmp->seen_string = malloc(strlen(itm_list) + 1)),itm_list);
            tmp->e_next = 0;
            if (et) et->e_next = tmp;
            else e_head = tmp;
            et = tmp;
            }
        else {
            if (!strncmp(inline,"* ",2)) {
                ctmp = 0;
                strncpy(cclass,inline,131);
                cclass[131] = '\0';
                }
            else {
                if (!ctmp) {
                    ctmp = malloc(sizeof *ctmp);
                    ctmp->c_nect = 0;
                    if (ct) et->c_next = tmp;
                    else c_head = ctmp;
                    ct = ctmp;
                    }
                }
            }
        }
}

add_class_list(tmp,clasp)
    struct st_entry *tmp;
    char *p;
{
    char *c,
         *cp,
         **ca;
    int ecount = 2,
        i;

    c = clasp;
    while (c = strchr(c,',')) {
        c++;
        ++ecount;
        }
    ca = tmp->member_class = malloc(ecount * (sizeof c));
    i = 0;
    cp = clasp;
    if (c = strchr(cp,',')) *c++ = '\0';
    while (cp) {
        strcpy((ca[i] = malloc(strlen(cp) + 1)),cp);
        cp = c;
        if (c = strchr(cp,',')) *c++ = '\0';
        ++i;
        }
    ca[i] = 0;
}

write_entries(f)
    FILE *f;
{
    struct st_entry *tmp = e_head;
    if (!e_head) return;
    fprintf(f,"ENTRY\n");
    while (tmp) {
        fprintf(f,"%s",tmp->e_name);
        if (tmp->e_node || tmp->e_file || tmp->type || tmp->proto || tmp->member_class)
            fprintf(f," ");
        if (tmp->e_node) fprintf(f,"~%s",tmp->e_node);
        if (tmp->e_file) fprintf(f,"!%s",tmp->e_file);
        if (tmp->type) fprintf(f,@%d",tmp->type);
        if (tmp->proto) fprintf(f,"#%d",tmp->proto);
        if (tmp->member_class) {
            int i = 0;
            char **ca = tmp->member_class;
            fprintf(f,"%%");
            while (ca[i]) {
                if (i) fprintf(f,",");
                fprintf(f,"%s",ca[i]);
                ++i;
                }
            }
        fprintf(f,": (%d) [%d,%d] %s\n",tmp->prio,tmp->unread,tmp->topnum,
            tmp->seen_string;
        tmp = tmp->e_next;
        }
    }
}
*/
