#module NEWSREAD "V5.6"

/*
**++
**  FACILITY:
**
**      NEWSREAD
**
**  ABSTRACT:
**
**      Select a news item for display on the screen.
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.5     7-Oct-1988     GIH
**      V5.6    11-Nov-1988     GIH
**        - Add support for BACK command set
**
**--
**/

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

static int tpucall;

/*
 *  item_update
 *
 *  update the uaf record for a read item
 */

item_update(g,i)
    int g,
        i;
{
    if (ga[g]->grp_ia && ga[g]->grp_ia[i].itm_unread) {
        if (ga[g]->grp_unread > 0) {
            --ga[g]->grp_unread;
            screen_update_gread(g);
            }
        ga[g]->grp_ia[i].itm_unread = 0;
        screen_update_iread(g,i);
        if (xref_enabled) xrefs(ga[g]->grp_num,ga[g]->grp_ia[i].itm_id);
        }
}

/*
 *  xrefs
 *
 *  Locate all items with the same message-id as the current item
 *  and mark tham as read.
 */

xrefs(g,id)
    unsigned short g;
    char *id;
{
    char loc_id[IDLEN + 2];
    unsigned short *gr,
                   itmnum;
    int gm,
        i;
    ITM_MEM_PTR iap;

    xref_enabled = 0;
    util_idcpy(loc_id,id);
    gr = &(loc_id[IDLEN]);
    *gr = 0;

    itmrab.rab$l_kbf = loc_id;
    itmrab.rab$b_ksz = IDLEN + 2;
    itmrab.rab$b_krf = 1;
    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) {
        itmrab.rab$b_rac = RAB$C_SEQ;
        if (strcmp(id,newsitm.itm_id)) break;
        if (newsitm.itm_grp == g) continue;
        itmnum = newsitm.itm_num;
        if (gm = ga_locate(newsitm.itm_grp)) {
            if (!(ga[gm]->grp_ia)) map_items(gm);
            iap = ga[gm]->grp_ia;
            for (i = 1; i <= ga[gm]->grp_count; ++i) {
                if (iap[i].itm_num > itmnum) break;
                if (iap[i].itm_num == itmnum) {
                    item_update(gm,i);
                    break;
                    }
                }
            }
        }
    xref_enabled = 1;
}

#define RSTACK 20

struct rcontxt {
    int grp,
        mbm;
    } rcxt[RSTACK];

int rnxt = 0,
    rcnt = 0;

rpush(g,i)
    int g,
        i;
{
    rcxt[rnxt].grp = g;
    rcxt[rnxt].mbm = i;
    if (rcnt < RSTACK) ++rcnt;
    rnxt++;
    rnxt %= RSTACK;
}

rpop(g,i)
    int *g,
         *i;
{
    if (!rcnt) return(0);
    rnxt = (rnxt + (RSTACK - 1)) % RSTACK;
    --rcnt;
    *g = rcxt[rnxt].grp;
    *i = rcxt[rnxt].mbm;
    return(1);
}

/*
 *  do_readfind
 *
 *  Read a selected item
 */

do_readfind(s,rheader,rot13)
    char *s;
    int rheader,
        rot13;
{
    int g,
        i;

    if (!curr_g) {
        err_line("Error: Read - No Newsgroup selected");
        return(0);
        }

    if (news_context == 1) {
        set_level(2);
        if (!curr_i) {
            err_line("\tRead: Newsgroup is empty\n");
            return(0);
            }
        }

    if (!(i = item_find(s))) return(0);

    cur_set_itm(curr_g,i);
    item_update(curr_g,curr_i);
    do_display(rheader,tpucall,rot13);
    return(0);
}

/*
 *  do_rnew
 *
 *  Pick out the next unread news item - select that group and read the
 *  first unread item from the group
 */

static do_rnew(rheader,rot13,reset)
    int rheader,
        rot13,
        reset;
{
    int i;

    if (!no_more_news) {
        if ((curr_g) && (ga[curr_g]->grp_reg) && (ga[curr_g]->grp_unread) && (news_context != 1)) {
            set_level(2);
            if (!reset) {
                i = curr_i;
                while ((i <= ga[curr_g]->grp_count) && (!(ga[curr_g]->grp_ia[i].itm_unread))) ++i;
                if (i > ga[curr_g]->grp_count) i = 1;
                }
            else i = 1;
            while ((i <= ga[curr_g]->grp_count) && (!(ga[curr_g]->grp_ia[i].itm_unread))) ++i;
            if (i <= ga[curr_g]->grp_count) {
                cur_set_itm(curr_g,i);
                item_update(curr_g,curr_i);
                if (kill_filter(curr_g,curr_i)) {
                    err_line("\tSkipping next unread item - KILL filter\n");
                    if (screen_active) {
                        smg$end_pasteboard_update(&pid);
                        smg$begin_pasteboard_update(&pid);
                        }
                    return(1);
                    }
                do_display(rheader,tpucall,rot13);
                return(0);
                }
            else {
                set_level(1);
                ga[curr_g]->grp_unread = 0;
                if (screen_active && ga[curr_g]->grp_display_indx) {
                    sprintf(err_oline,"%5d",ga[curr_g]->grp_unread);
                    smg$put_chars(&grp_vd,c$dsc(err_oline),c$rfi(ga[curr_g]->grp_display_indx),c$rfi(73),0,0,0,0);
                    }
                }
            }

        set_level(1);
        if (selnew(2)) {
            item_update(curr_g,curr_i);
            if (kill_filter(curr_g,curr_i))  {
                err_line("\tSkipping next unread item - KILL filter\n");
                if (screen_active) {
                    smg$end_pasteboard_update(&pid);
                    smg$begin_pasteboard_update(&pid);
                    }
                return(1);
                }
            do_display(rheader,tpucall,rot13);
            return(0);
            }
        }
    else err_line("\tNo unread items in registered newsgroups\n");
    return(0);
}

/*
 *  do_readnew
 *
 *  pick out the next unread news item and display it
 */

do_readnew(rheader,rot13,reset)
    int rheader,
        rot13,
        reset;
{
    while (do_rnew(rheader,rot13,reset)) reset = 0;
    return(0);
}

/*
 *  do_readnext
 *
 *  Use the current read context to pick out the next item to display.
 */

do_readnext(rheader,rot13,tpucall)
    int rheader,
        rot13,
        tpucall;
{
    if (!curr_g) {
        err_line("Error: Read - No Newsgroup selected");
        return;
        }

    if (news_context == 1) {
        set_level(2);
        if (!curr_i) {
            err_line("\tRead: Newsgroup is empty\n");
            return(0);
            }
        }
    else if (cur_down_itm(curr_g,1,0) != 1) {
        if (!ga[curr_g]->grp_count) err_line("\tRead: Newsgroup is empty\n");
        else err_line("\tRead: No more items in Newsgroup\n");
        return(0);
        }

    item_update(curr_g,curr_i);
    do_display(rheader,tpucall,rot13);
    return(0);
}

/*
 *  do_readparent
 *
 *  Display the parent item of the current item - pick off the rightmost
 *  reference item
 */

static do_readparent(rheader,rot13)
    int rheader,
        rot13;
{
    FILE *fp;
    char inpline[152],
         pref[IDLEN+2];
    unsigned short *gr;
    int i,
        g;

    if ((news_context > 1) && curr_g && curr_i) {
        if (!(fp = do_open_item(curr_g,curr_i,"r",fp_open))) {
            err_line("\tError: Display - Cannot access item text\n");
            return;
            }
        while (fgets(inpline,512,fp)) {
            if (*inpline == '\n') {
                fclose(fp);
                if (*fp_open > 1) while (!delete(fp_open));
                *fp_open = '\0';
                err_line("\tError: Read/Parent - This item has no parent reference\n");
                return;
                }
            if (!strncmp(inpline,"References: ",12)) {
                char *ref;

                fclose(fp);
                if (*fp_open > 1) while (!delete(fp_open));
                *fp_open = '\0';
                if (ref = strrchr(inpline,'>')) {
                    *(ref+1) = '\0';
                    if (ref = strrchr(inpline,'<')) {
                                /* ref is the parent reference ID ! */
                        strcpy(pref,ref);
                        s_to_lower(pref);
                        util_idcpy(pref,pref);
                        gr = &(pref[IDLEN]);
                        *gr = ga[curr_g]->grp_num;

                        itmrab.rab$l_kbf = pref;
                        itmrab.rab$b_ksz = IDLEN + 2;
                        itmrab.rab$b_krf = 1;
                        itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
                        itmrab.rab$b_rac = RAB$C_KEY;

                        if (!(sys$get(&itmrab) & 1)) {
                            *gr = 0;
                            itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
                            if ((!(sys$get(&itmrab) & 1)) || (strcmp(pref,newsitm.itm_id))) {
                                err_line("\tError: Read/Parent - Parent item not found\n");
                                return;
                                }
                            if (curr_g != newsitm.itm_grp) {
                                if (!(g = ga_locate(newsitm.itm_grp))) {
                                    err_line("\tError: Read/Parent - Parent not located\n");
                                    return;
                                    }
                                do_select("%",g,2);
                                }
                            }
                        for (i = 1; i <= ga[curr_g]->grp_count; ++i)
                            if (ga[curr_g]->grp_ia[i].itm_num == newsitm.itm_num) break;
                        if (i > ga[curr_g]->grp_count)
                            return(err_line("\tError: Read/Parent - Parent not located\n"));
                        cur_set_itm(curr_g,i);
                        item_update(curr_g,curr_i);
                        do_display(rheader,tpucall,rot13);
                        return(0);
                        }
                    }
                return(err_line("\tError: Read/Parent - Parent reference unreadable\n"));
                }
            }
        fclose(fp);
        if (*fp_open > 1) while (!delete(fp_open));
        *fp_open = '\0';
        return(err_line("\tError: Read/Parent - This item has no parent reference\n"));
        }
    return(err_line("\tError: Read/Parent - No current item selected\n"));
}

/*
 *  do_readident
 *
 *  Display the item selected by the supplied identifier
 */

static do_readident(rheader,rot13)
    int rheader,
        rot13;
{
    FILE *fp;
    char id[152];
    unsigned short *gr,
                   id_len = 0,
                   inum;
    int i,
        g;
    $DESCRIPTOR(id_dsc,id);

    if ((!(cli$get_value(c$dsc("IDENTIFIER"),&id_dsc,&id_len) & 1)) &&
        ((status = get_input(&id_dsc,c$dsc("Message-ID: "),&id_len)) == RMS$_EOF)) longjmp(env,2);
    if (!id_len) return;
    id[id_len] = '\0';
    s_to_lower(id);
    util_idcpy(id,id);

    gr = &(id[IDLEN]);
    itmrab.rab$l_kbf = id;
    itmrab.rab$b_ksz = IDLEN + 2;
    itmrab.rab$b_krf = 1;
    itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    if (curr_g) *gr = ga[curr_g]->grp_num;
    if ((!curr_g) || (!(sys$get(&itmrab) & 1))) {
        *gr = 0;
        itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
        if ((!(sys$get(&itmrab) & 1)) || (strcmp(id,newsitm.itm_id))) return(err_line("\tError: Read/Id - Message-ID not located\n"));
        }
    inum = newsitm.itm_num;
    if (!(g = ga_locate(newsitm.itm_grp))) return(err_line("\tError: Read/Id - Message-ID not located\n"));
    do_select("%",g,2);
    for (i = 1; i <= ga[curr_g]->grp_count; ++i) if (ga[curr_g]->grp_ia[i].itm_num == inum) break;
    if (i > ga[curr_g]->grp_count) return(err_line("\tError: Read/Id - Message-ID not located\n"));
    cur_set_itm(curr_g,i);
    item_update(curr_g,curr_i);
    do_display(rheader,tpucall,rot13);
    return(0);
}

/*
 *  do_readmark
 *
 *  Read the next Marked item
 */

static char savtg[80] = "*";
static short fg = 0,
             fm = 0;

static do_readmark(rheader,rot13)
    int rheader,
        rot13;
{             
    int i;
    unsigned int gm;
    unsigned short g,
                   m;
    char stg[80],
         *tag = 0;
    short tag_len;
    $DESCRIPTOR(tag_dsc,stg);

    if ((cli$get_value(c$dsc("MARKER"),&tag_dsc,&tag_len) & 1) ||
        (cli$get_value(c$dsc("NEWSITEM"),&tag_dsc,&tag_len) & 1)) {
        stg[tag_len] = '\0';
        s_to_lower(stg);
        if (strcmp(savtg,stg)) {
            strcpy(savtg,stg);
            fg = fm = 0;
            }
        tag = savtg;
        }
    else tag = savtg;
     
    if (!curr_g) g = m = 0;
    else g = ga[curr_g]->grp_num;
    if ((g) && ((curr_i <= 0) || (news_context < 2))) m = 0;
    else if (g) m = ga[curr_g]->grp_ia[curr_i].itm_num;

    for (;;) {
        if (!mark_find(g,m,tag,&gm)) return(err_line("\tInfo: Read/Mark - No more items with this tag\n"),0);

        g = gm >> 16;
        m = gm & 0xFFFF;
        if ((fg == g) && (fm == m)) return(err_line("\tInfo: Read/Mark - No more items with this tag\n"),0);
        if (!(gm = ga_locate(g))) continue;
        if (!ga[gm]->grp_count) continue;
        if (!ga[gm]->grp_ia) map_items(gm);
        for (i = 1; i <= ga[gm]->grp_count; ++i)
            if (ga[gm]->grp_ia[i].itm_num == m) break;
        if (i > ga[gm]->grp_count) continue;
        break;
        }
    if (!fg) {
        fg = g;
        fm = m;
        }
    do_select("%",gm,2);
    cur_set_itm(curr_g,i);
    item_update(curr_g,curr_i);
    do_display(rheader,tpucall,rot13);
    return(0);
}

/*
 *  do_readfollow
 *
 *  Read a followup posting (if any)
 */

do_readfollow(rheader,rot13,new_qual)
    int rheader,
        rot13,
        new_qual;
{
    char title[SUBJLEN+1],
         *cp;
    int i;
    ITM_MEM_PTR iap;

    if ((!curr_g) || (news_context < 2) || (curr_i < 1))
        return(((new_qual) ? do_readnew(rheader,rot13,1) : err_line("\tError: READ/Followup - No current item selected\n")), 0);
    iap = ga[curr_g]->grp_ia;
    strcpy(title,iap[curr_i].itm_title);
    s_to_lower(title);
    if (!strncmp(title,"re:",3)) {
        strcpy(title,iap[curr_i].itm_title);
        cp = &title[3];
        while ((*cp) && (isspace(*cp))) cp++;
        if (!*cp) return(((new_qual) ? do_readnew(rheader,rot13,1) : err_line("\tError: Read/Followup - No subject!\n")), 0);
        }
    else cp = strcpy(title,iap[curr_i].itm_title);
    strip(cp,strlen(cp));
    if (strlen(cp) > (SUBJLEN - 5)) cp[SUBJLEN - 5] = '\0';
    for (i = curr_i + 1; i <= ga[curr_g]->grp_count; ++i)
        if (((!new_qual) || iap[i].itm_unread) && substrcmp(iap[i].itm_title,cp)) {
            cur_set_itm(curr_g,i);
            item_update(curr_g,curr_i);
            if (new_qual && kill_filter(curr_g,curr_i)) {
                err_line("\tSkipping next unread item - KILL filter\n");
                if (screen_active) {
                    smg$end_pasteboard_update(&pid);
                    smg$begin_pasteboard_update(&pid);
                    }
                continue;
                }
            do_display(rheader,tpucall,rot13);
            return(0);
            }
    if (new_qual) return(do_readnew(rheader,rot13,1),0);
    err_line("\tREAD/Followup: No followup items posted\n");
    return(0);
}

/*
 *  do_readprev
 *
 *  Read a followup posting (if any)
 */

do_readprev(rheader,rot13)
    int rheader,
        rot13;
{
    int ng,
        ni;

    do {
        if (!rpop(&ng,&ni)) return(err_line("\tError: Read/Prev - Prev stack is empty\n"),0);
        } while ((ng == curr_g) && (ni == curr_i));
    do_select("%",ng,2);
    cur_set_itm(curr_g,ni);
    item_update(curr_g,curr_i);
    do_display(rheader,tpucall,rot13);
}

/*
 *  do_read
 *
 *  read command processing
 */                       

do_read()
{
    char item[SUBJLEN];
    $DESCRIPTOR(item_dsc,item);
    short item_len;
    int h = 0,
        rot13 = 0;

    tpucall = 0;
    if (cli$present(c$dsc("HEADER")) & 1) h = 1;
    if (cli$present(c$dsc("TPU")) & 1) tpucall = 1;
    if (cli$present(c$dsc("EDITOR")) & 1) tpucall = 1;
    else if (cli$present(c$dsc("ROT13")) & 1) rot13 = 1;
    if (cli$present(c$dsc("FOLLOWUP")) & 1) return(do_readfollow(h,rot13,(cli$present(c$dsc("NEW")) & 1)),0);
    if (cli$present(c$dsc("NEW")) & 1) return(do_readnew(h,rot13,0),0);
    if (cli$present(c$dsc("NEXT")) & 1) return(do_readnext(h,rot13,tpucall),0);
    if (cli$present(c$dsc("PARENT")) & 1) return(do_readparent(h,rot13),0);
    if (cli$present(c$dsc("IDENTIFIER")) & 1) return(do_readident(h,rot13),0);
    if (cli$present(c$dsc("MARKER")) & 1) return(do_readmark(h,rot13),0);
    if (cli$present(c$dsc("PREV")) & 1) return(do_readprev(h,rot13),0);
    if ((cli$present(c$dsc("LAST")) & 1) || (cli$present(c$dsc("BACK")) & 1)) {
        if ((news_context > 1) && curr_g && (curr_i > 0)) {
            if (cur_up_itm(curr_g,1,0) == 1) {
                item_update(curr_g,curr_i);
                do_display(h,tpucall,rot13);
                }
            }
        return(0);
        }
    if (cli$get_value(c$dsc("NEWSITEM"),&item_dsc,&item_len) != CLI$_ABSENT) {
        if (curr_g) {
            item[item_len] = '\0';
            do_readfind(item,h,rot13);
            }
        }
    else if ((curr_g) && (curr_i > 0) && (news_context > 1)) {
        item_update(curr_g,curr_i);
        do_display(h,tpucall,rot13);
        }
    return(0);
}

/*
 *  do_unread
 *
 *  Mark an item as unread
 */

do_unread()
{
    int i;
    if (cli$present(c$dsc("ALL")) & 1) {
        if (!curr_g) return(err_line("\tError: Unread/All - No current newsgroup selected\n"),0);
        if (!ga[curr_g]->grp_count) return(err_line("\tError: Unread/All - Newsgroup is empty\n"),0);
        if (!ga[curr_g]->grp_ia) map_items(curr_g);
        for (i = 1; i <= ga[curr_g]->grp_count; ++i)
            if (!(ga[curr_g]->grp_ia[i].itm_unread)) {
                ++ga[curr_g]->grp_unread;
                ga[curr_g]->grp_ia[i].itm_unread = 1;
                screen_update_iread(curr_g,i);
            }
            screen_update_gread(curr_g);
        return(0);
        }

    if ((!curr_g) || (news_context < 2) || (curr_i < 1)) return(err_line("\tError: Unread - No current item selected\n"),0);
    if (ga[curr_g]->grp_ia && !(ga[curr_g]->grp_ia[curr_i].itm_unread)) {
        ++ga[curr_g]->grp_unread;
        screen_update_gread(curr_g);
        ga[curr_g]->grp_ia[curr_i].itm_unread = 1;
        screen_update_iread(curr_g,curr_i);
        }
    return(0);
}

do_back_note()
{
    int h = 0,
        rot13 = 0;

    tpucall = 0;
    if (cli$present(c$dsc("HEADER")) & 1) h = 1;
    if (cli$present(c$dsc("TPU")) & 1) tpucall = 1;
    if (cli$present(c$dsc("EDITOR")) & 1) tpucall = 1;
    else if (cli$present(c$dsc("ROT13")) & 1) rot13 = 1;
    if ((news_context > 1) && curr_g && (curr_i > 0)) {
        if (cur_up_itm(curr_g,1,0) == 1) {
            item_update(curr_g,curr_i);
            do_display(h,tpucall,rot13);
            }
        else err_line("\tBACK: No previous item\n");
        }
    else err_line("\tBACK: No newsgroup open\n");
    return(0);
}

do_back_reply()
{
    int h = 0,
        rot13 = 0;

    tpucall = 0;
    if (cli$present(c$dsc("HEADER")) & 1) h = 1;
    if (cli$present(c$dsc("TPU")) & 1) tpucall = 1;
    if (cli$present(c$dsc("EDITOR")) & 1) tpucall = 1;
    else if (cli$present(c$dsc("ROT13")) & 1) rot13 = 1;
    if ((news_context > 1) && curr_g && (curr_i > 0)) 
        return(do_readparent(h,rot13),0);
    else err_line("\tBACK: No newsgroup open\n");
    return(0);
}
