#module NEWSDELETE "V5.6"

/*
**++
**  FACILITY:
**
**      NEWSDELETE.C
**
**  ABSTRACT:
**
**      This module deletes newsitems and newsgroups from the local database.
**      The module also contains the implementation of a history file of item
**      identifiers, used in prevention of NEWS loops.
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.5     7-Oct-1988     GIH
**          Add support for history file
**          Alter history file organisation to indexed sequential to improve
**          performance of lookup.
**          Add timezone to date field
**          Remove automatic ".*" for delete/newsgroup
**
**      V5.6    11-Nov-1988     GIH
**        - Allow a moderator to cancel postings from the moderated newsgroup
**        - Alter ^Z input to abort from delete command only, not NEWS itself
**
**--
**/

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

extern char dir_sincestr[];

static int no_confirm = 0;

/*
 *  HISTORY routines
 *
 *  All the necessary history file operations have been placed in this module
 */

static int hist_file_open = 0;

static struct FAB histfab;

static struct RAB histrab;

static struct XABKEY histkey_1,
                     histkey_2;

static struct XABPRO histpro_1;

static struct hist {
                    char hist_id[IDLEN];
                    unsigned int hist_date;
                   } newshist;

open_hist_file()
{
    if (hist_file_open) return(1);
    histfab = cc$rms_fab;
    histfab.fab$b_bks = 4;
    histfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_DEL;
    histfab.fab$l_fna = HIST_FILE;
    histfab.fab$b_fns = strlen(histfab.fab$l_fna);
    histfab.fab$l_fop = FAB$M_CIF;
    histfab.fab$w_mrs = sizeof newshist;
    histfab.fab$b_org = FAB$C_IDX;
    histfab.fab$b_rat = FAB$M_CR;
    histfab.fab$b_rfm = FAB$C_FIX;
    histfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;
    histfab.fab$l_xab = &histkey_1;

    histkey_1 = cc$rms_xabkey;
    histkey_1.xab$b_dtp = XAB$C_STG;
    histkey_1.xab$b_flg = 0;
    histkey_1.xab$w_pos0 = (char *) &newshist.hist_id - (char *) &newshist;
    histkey_1.xab$b_ref = 0;
    histkey_1.xab$b_siz0 = IDLEN;
    histkey_1.xab$l_nxt = &histkey_2;

    histkey_2 = cc$rms_xabkey;
    histkey_2.xab$b_dtp = XAB$C_BN4;
    histkey_2.xab$b_flg = XAB$M_DUP;
    histkey_2.xab$w_pos0 = (char *) &newshist.hist_date - (char *) &newshist;
    histkey_2.xab$b_ref = 1;
    histkey_2.xab$b_siz0 = 4;
    histkey_2.xab$l_nxt = &histpro_1;

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

    histrab = cc$rms_rab;
    histrab.rab$b_krf = 0;
    histrab.rab$l_fab = &histfab;
    histrab.rab$l_ubf = &newshist;
    histrab.rab$w_usz = sizeof newshist;
    histrab.rab$l_rbf = &newshist;
    histrab.rab$w_rsz = sizeof newshist;

    sysprv();
    if (!((status = sys$create(&histfab)) & 1)) return(nosysprv(),0);
    if (!((status = sys$connect(&histrab)) & 1)) {
        sys$close(&histfab);
        nosysprv();
        return(0);
        }
    nosysprv();
    return(hist_file_open = 1);
}

close_hist_file()
{
    sysprv();
    if (hist_file_open) sys$close(&histfab);
    nosysprv();
    hist_file_open = 0;
}

hist_check(id)
    char *id;
{
    char l_id[IDLEN];

    if (!open_hist_file()) return(0);
    util_idcpy(l_id,id);
    s_to_lower(l_id);
    histrab.rab$l_kbf = l_id;
    histrab.rab$b_krf = 0;
    histrab.rab$b_ksz = IDLEN;
    histrab.rab$l_rop= RAB$M_RRL | RAB$M_NLK ;
    histrab.rab$b_rac = RAB$C_KEY;
    return(sys$find(&histrab) & 1);
}

hist_add(id)
    char *id;
{
    if (!open_hist_file()) return(0);
    time(&newshist.hist_date);
    util_idcpy(newshist.hist_id,id);
    s_to_lower(newshist.hist_id);
    return(sys$put(&histrab) & 1);
}

hist_skim(deldate)
    unsigned int deldate;
{
    if (!open_hist_file()) return(0);
    histrab.rab$b_rac = RAB$C_SEQ;
    histrab.rab$b_krf = 1;
    histrab.rab$l_rop = RAB$M_WAT;
    sys$rewind(&histrab);
    while (sys$get(&histrab) & 1) {
        if (newshist.hist_date < deldate) sys$delete(&histrab);
        else break;
        }
    return(1);
}

/*
 *  parse_items
 *
 *  Parse a list of item numbers of the form: num, num-num, or '*', placing
 *  the result in the structure d_itm
 */

parse_items(s,d_itm)
    char *s;
    NOTE_RANGE *d_itm;
{
    char *cp1,
         *cp2,
         *cp3;
    int locnum1,
        locnum2,
        l,
        di = 0;

    d_itm[0].fitm = 0;
    if (!curr_g) {
        err_line("\tNo Group Selected\n");
        return;
        }

    map_items(curr_g);
    if ((!*s) || (!ga[curr_g]->grp_count)) return;
    s_to_lower(s);
    cp1 = s;
    do {
        d_itm[di].fitm = 0;
        d_itm[di].litm = 0;
        d_itm[di].ngrp = curr_g;

        if (cp3 = strchr(cp1,',')) *cp3++ = '\0';
        else cp3 = 0;

        if (cp2 = strchr(cp1,'-')) {
            *cp2++ = '\0';
            if (sscanf(cp1,"%d",&locnum1));
            else if (!(l = strlen(cp1))) locnum1 = ga[curr_g]->grp_ia[curr_i].itm_num;
            else if (!strncmp(cp1,"first",min(l,5))) locnum1 = ga[curr_g]->grp_ia[1].itm_num;
            else if (!strncmp(cp1,"last",min(l,4))) locnum1 = ga[curr_g]->grp_ia[(ga[curr_g]->grp_count)].itm_num;
            else if (!strcmp(cp1,".")) locnum1 = ga[curr_g]->grp_ia[curr_i].itm_num;
            else if (!strcmp(cp1,"*")) locnum1 = ga[curr_g]->grp_ia[(ga[curr_g]->grp_count)].itm_num;
            else continue;

            if (sscanf(cp2,"%d",&locnum2));
            else if (!(l = strlen(cp2))) locnum2 = ga[curr_g]->grp_ia[curr_i].itm_num;
            else if (!strncmp(cp2,"first",min(l,5))) locnum2 = ga[curr_g]->grp_ia[1].itm_num;
            else if (!strncmp(cp2,"last",min(l,4))) locnum2 = ga[curr_g]->grp_ia[(ga[curr_g]->grp_count)].itm_num;
            else if (!strcmp(cp2,".")) locnum2 = ga[curr_g]->grp_ia[curr_i].itm_num;
            else if (!strcmp(cp2,"*")) locnum2 = ga[curr_g]->grp_ia[(ga[curr_g]->grp_count)].itm_num;
            else continue;

            if (locnum1 > locnum2) {
                int t = locnum1;
                locnum1 = locnum2;
                locnum2 = t;
                }
            if (!locnum1) locnum1 = 1;
            d_itm[di].fitm = locnum1;
            d_itm[di++].litm = locnum2;
            }
        else if (*cp1 == '*') d_itm[di++].fitm = -1;
        else if ((sscanf(cp1,"%d",&locnum1)) && locnum1)
            d_itm[di++].fitm = locnum1;
        else if (!(l = strlen(cp1)));
        else if (!strncmp(cp1,"first",min(l,5)))
            d_itm[di++].fitm = ga[curr_g]->grp_ia[1].itm_num;
        else if (!strncmp(cp1,"last",min(l,4)))
            d_itm[di++].fitm = ga[curr_g]->grp_ia[(ga[curr_g]->grp_count)].itm_num;
        else if (!strcmp(cp1,"."))
            d_itm[di++].fitm = ga[curr_g]->grp_ia[curr_i].itm_num;
        else if (!strncmp(cp1,"all",min(l,3))) d_itm[di++].fitm = -1;
        d_itm[di].fitm = 0;
        } while (cp1 = cp3);
}

/*
 *  wild_match
 *
 *  String equality routine, including matching the '*' character.
 */

wild_match(l,p)
    char *l,
         *p;
{
    if (!*l) {
        if (!*p) return(1);
        else if (*p == '*') return(wild_match(l,p+1));
        else return(0);
        }
    if (*p == '*') {
        while (!wild_match(l,p+1)) {
            l++;
            if (!*l) {
                if (!*(p+1)) return(1);
                else return(0);
                }
            }
        return(1);
        }
    if (*p == '%') return(wild_match(l+1,p+1));
    return((*l == *p) && wild_match(l+1,p+1));
}

/*
 *  news_delete_file
 *
 *  Delete the file associated with a news item
 */

news_delete_file(itm,grp_name)
    int itm;
    char *grp_name;
{
    sprintf(itm_fname,Itm_template,util_dir(grp_name),itm);
    sysprv();
    while (!delete(itm_fname));
    nosysprv();
}

/*
 *  delgrp_mem
 *
 *  delete a newsgroup from mem arrays and the screen
 */

static delgrp_mem(g)
    int g;
{
    int i,
        j,
        dsp,
        sg,
        rend;
    GRP_MEM_PTR gap;
    char dir_type[120];

    --(ga[0]->grp_count);

    set_level(1);
    if ((curr_g == g) && (cur_down_grp(1,0) != 1)) cur_up_grp(1,0);

    gap = ga[g];
    if (gap->grp_ia) free(gap->grp_ia);
    if (gap->grp_iavdsize) smg$delete_virtual_display(&(gap->grp_iavd));
    if ((dsp = gap->grp_display_indx) && (curr_g > g)) --curr_g;
    for (j = g + 1; j <= ga_size; ++j) {
        ga[j-1] = ga[j];
        if ((dsp) && (ga[j]->grp_display_indx)) {
            if (screen_active) {
                sprintf(err_oline,"%-5d",ga[j]->grp_display_indx - 1);
                rend = ((ga[j]->grp_reg) && (ga[j]->grp_unread)) ? SMG$M_BOLD : 0;
                smg$put_chars(&grp_vd,c$dsc(err_oline),c$rfi(ga[j]->grp_display_indx),c$rfi(4),0,&rend,0,0);
                }
            --ga[j]->grp_display_indx;
            }
        }
    if (dsp) {
        if (screen_active) {
            if (g == grp_display_size) smg$erase_display(&grp_vd,&grp_display_size,c$rfi(1));
            else smg$scroll_display_area(&grp_vd,&dsp,c$rfi(1),0,0,c$rfi(SMG$M_UP),c$rfi(1));
            }
        if (!--grp_display_size) {
            curr_g = 0;
            g_arrow = 0;
            }
        }
    --ga_size;
    free(gap);
    if (screen_active) {
        if (curr_g) cur_set_grp(curr_g);
        sg = grp_display_size;
        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);
        }
}

/*
 *  delgrp_file
 *
 *  delete a newsgroup from index files
 */

delgrp_file(g)
    int g;
{
    unsigned int key;
    int grp_in_file;

    grprab.rab$l_kbf = &(ga[g]->grp_num);
    grprab.rab$b_ksz = 2;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;
    grp_in_file = (sys$get(&grprab) & 1);

    key = ((ga[g]->grp_num) << 16);
    itmrab.rab$l_kbf = &key;
    itmrab.rab$l_rop = RAB$M_WAT | RAB$M_KGE;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$b_ksz = 4;
    itmrab.rab$b_krf = 0;
    while (sys$get(&itmrab) & 1) {
        if (newsitm.itm_grp != ga[g]->grp_num) {
            sys$free(&itmrab);
            break;
            }
        itmrab.rab$l_rop = RAB$M_WAT;
        itmrab.rab$b_rac = RAB$C_SEQ;
        sys$delete(&itmrab);
        hist_add(newsitm.itm_id);
        news_delete_file(newsitm.itm_num,ga[g]->grp_name);
        }
    if (grp_in_file) {
        sys$delete(&grprab);

        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;
        c$cks(sys$update(&grprab));
        }
    delgrp_mem(g);
}

/*
 *  del_grp
 *
 *  Delete the group indexed by g
 */

del_grp(g)
    int g;
{
    if (no_confirm) return(delgrp_file(g),1);
    if (!screen_active) {
        printf("\nNewsgroup to delete:\n");
        printf("\tName   : %s\n",ga[g]->grp_name);
        printf("\tCount  : %d\n",ga[g]->grp_count);
        printf("\tCreated: %s",ctime(&(ga[g]->grp_credate)));
        printf("\tUpdated: %s\n",ctime(&(ga[g]->grp_entdate)));
        }

    if (ga[g]->grp_count) {
        sprintf(err_oline,"\tWARNING - Newsgroup contains %d items\007\007\n",ga[g]->grp_count);
        err_line(err_oline);
        }
    else clear_err_line();

    sprintf(err_oline,"Delete Newsgroup: %s? [n]:",ga[g]->grp_name);
    status = get_input(&command,c$dsc(err_oline),&response_length);
    clear_err_line();
    if (status == RMS$_EOF) return(-1);
    if ((status & 1) && (response_length) && (tolower(*response) == 'y'))
        return(delgrp_file(g),1);
    else return(0);
}

/*
 *  delitm_mem
 *
 *  Delete a single news item from mem and screen
 */

static delitm_mem(g,i)
    int g,
        i;
{
    ITM_MEM_PTR iap;
    GRP_MEM_PTR gap;
    int j,
        s_mapped,
        rend,
        uread;

    gap = ga[g];
    if (!gap->grp_ia) map_items(g);
    iap = gap->grp_ia;

    for (j = 1; j <= ga[g]->grp_count; ++j) {
        if (i < iap[j].itm_num) return(0);
        if (i == iap[j].itm_num) break;
        }
    if (j > ga[g]->grp_count) return(0);

    i = j;

    uread = iap[i].itm_unread;

    if ((gap->grp_c_itm == i) && (cur_down_itm(g,1,0) != 1)) cur_up_itm(g,1,0);

    s_mapped = gap->grp_iavdsize;

    for (j = i+1; j <= gap->grp_count; ++j) iap[j-1] = iap[j];
    if (gap->grp_c_itm > i) {
        --gap->grp_c_itm;
        if ((news_context > 1) && (curr_g == g)) curr_i = gap->grp_c_itm;
        }
    if (s_mapped) {
        if (i == gap->grp_count) smg$erase_display(&(gap->grp_iavd),&i,c$rfi(1));
        else {
            smg$scroll_display_area(&(gap->grp_iavd),&i,c$rfi(1),0,0,c$rfi(SMG$M_UP),c$rfi(1));
            position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0);
            }
        }
    if ((!gap->grp_count) || (!--gap->grp_count)) {
        if (s_mapped) {
            smg$erase_display(&(gap->grp_iavd));
            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);
            if (gap->grp_iapaste != 4) {
                if ((news_context > 1) && (screen_active) && (curr_g == g))
                    smg$move_virtual_display(&(gap->grp_iavd),&pid,c$rfi(4),c$rfi(1),0);
                gap->grp_iapaste = 4;
                }
            }
        gap->grp_c_itm = 0;
        if ((news_context > 1) && (curr_g == g)) curr_i = 0;
        }

    if ((uread) && (gap->grp_unread)) --gap->grp_unread;
    if ((screen_active) && (gap->grp_display_indx)) {
        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);
        }
    if (uread) screen_update_gread(g);
}

/*
 *  delitm_file
 *
 *  Delete a single news item from files
 */

static delitm_file(g,i)
    int g,
        i;
{
    int ikey;
    int grp_in_file;

    ikey = i;
    ikey += (ga[g]->grp_num << 16);

    itmrab.rab$l_kbf = &ikey;
    itmrab.rab$l_rop = RAB$M_WAT;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$b_ksz = 4;
    itmrab.rab$b_krf = 0;

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

    grp_in_file = (sys$get(&grprab) & 1);
    if (sys$get(&itmrab) & 1) {
        sys$delete(&itmrab);
        hist_add(newsitm.itm_id);
        news_delete_file(newsitm.itm_num,ga[g]->grp_name);
        }

    if (grp_in_file) {
        --newsgrp.grp_count;
        _c$cks(sys$update(&grprab));
        }
    delitm_mem(g,i);
}

/*
 *  del_id
 *
 *  Delete an item, given the identifier value
 */

del_id(id,sender)
    char *id,
         *sender;
{
    char mref[IDLEN + 2];
    int g,
        i,
        ret_val = 0,
        del_loop;

    s_to_lower(id);
    util_idcpy(mref,id);
    mref[IDLEN] = mref[IDLEN + 1] = '\0';

    itmrab.rab$l_kbf = mref;
    itmrab.rab$b_ksz = IDLEN + 2;
    itmrab.rab$b_krf = 1;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_WAT;
    itmrab.rab$b_rac = RAB$C_KEY;

    grprab.rab$l_kbf = &(newsitm.itm_grp);
    grprab.rab$b_ksz = 2;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    while (sys$get(&itmrab) & 1) {
        status = del_loop = 0;
        itmrab.rab$b_rac = RAB$C_SEQ;

        if (strcmp(mref,newsitm.itm_id)) {
            sys$free(&itmrab);
            return(ret_val);
            }
        if (!(sys$get(&grprab) & 1)) *(newsgrp.grp_name) = '\0';
        if (!sender) {
            if (no_confirm) del_loop = 1;
            else {
                if (screen_active) sprintf(err_oline,"Delete Item %s:%d? [n]:",newsgrp.grp_name,newsitm.itm_num);
                else {
                    printf("\nNewsitem to delete:\n");
                    printf("\tNewsgroup: %s\n",newsgrp.grp_name);
                    printf("\t#        : %d\n",newsitm.itm_num);
                    printf("\tTitle    : %s\n",newsitm.itm_title);
                    }
                status = get_input(&command,c$dsc(err_oline),&response_length);
                if (status == RMS$_EOF) {
                    if (*(newsgrp.grp_name)) sys$free(&grprab);
                    sys$free(&itmrab);
                    return(ret_val);
                    }
                }
            }
        else if (sender == 1) del_loop = 1;
        else if (*(newsgrp.grp_name)) {
            FILE *fp;
            char inpline[512],
                 *cp,
                 *cp1;

            if (fp = do_oitem(&newsitm,"r",newsgrp.grp_name,0,(newsgrp.grp_local & NEWS_RESTRICT_SET))) {
                while (fgets(inpline,512,fp)) {
                    if (*inpline == '\n') break;
                    if ((!strncmp(inpline,"From:",5))  || (!strncmp(inpline,"Sender:",7))) {
                        if (cp = strchr(inpline,'<')) {
                            ++cp;
                            if (cp1 = strchr(cp1,'>')) *cp = '\0';
                            }
                        else {
                            cp = strchr(inpline,':');
                            cp++;

                            while (isspace(*cp)) cp++;
                            cp1 = cp;
                            while ((*cp1) && (!isspace(*cp1))) cp1++;
                            *cp1 = '\0';
                            if (cp1 = strchr(cp,'(')) *cp1 = '\0';
                            }
                        if (!strcmp(sender,cp)) {
                            del_loop = 1;
                            break;
                            }
                        }
                    }
                fclose(fp);
                }
            }
        if (del_loop || ((status & 1) && (response_length) && (tolower(*response) == 'y'))) {
            ++ret_val;
            sys$delete(&itmrab);
            hist_add(newsitm.itm_id);
            if (*(newsgrp.grp_name)) news_delete_file(newsitm.itm_num,newsgrp.grp_name);
            if (*(newsgrp.grp_name)) {
                --newsgrp.grp_count;
                sys$update(&grprab);
                }
            if (g = ga_locate(newsitm.itm_grp)) {
                if (ga[g]->grp_ia) {
                    for (i = 1; i <= ga[g]->grp_count; ++i) {
                        if (ga[g]->grp_ia[i].itm_num == newsitm.itm_num) break;
                        }
                    if (i <= ga[g]->grp_count) delitm_mem(g,newsitm.itm_num);
                    }
                else {
                    if (!--ga[g]->grp_count) {
                        if (ga[g]->grp_iavdsize) {
                            smg$erase_display(&(ga[g]->grp_iavd));
                            smg$put_chars(&(ga[g]->grp_iavd),c$dsc("NEWSGROUP is empty"),c$rfi(1),c$rfi(20),0,c$rfi(SMG$M_REVERSE),0,0);
                            if (ga[g]->grp_iapaste != 4) {
                                if ((news_context > 1) && (screen_active) && (curr_g == g))
                                smg$move_virtual_display(&(ga[g]->grp_iavd),&pid,c$rfi(4),c$rfi(1),0);
                                ga[g]->grp_iapaste = 4;
                                }
                            }
                        ga[g]->grp_c_itm = 0;
                        if ((news_context > 1) && (curr_g == g)) curr_i = 0;
                        }

                    if (ga[g]->grp_unread) --ga[g]->grp_unread;
                    if ((screen_active) && (ga[g]->grp_display_indx)) {
                        sprintf(err_oline,"%5d",ga[g]->grp_count);
                        smg$put_chars(&grp_vd,c$dsc(err_oline),c$rfi(ga[g]->grp_display_indx),c$rfi(14+SUBJLEN),0,0,0,0);
                        }
                    screen_update_gread(g);
                    }
                }
            }
        else {
            if (*(newsgrp.grp_name)) sys$free(&grprab);
            sys$free(&itmrab);
            }
        }
}

do_ditem()
{
    char s[80];
    short s_len;
    $DESCRIPTOR(s_dsc,s);
    int di,
        li,
        fi;

    no_confirm = (cli$present(c$dsc("CONFIRM")) == CLI$_NEGATED);

    if (cli$present(c$dsc("IDENTIFIER")) & 1) {
        if (no_priv()) return(err_line("\tError: Delete - No privilege\n"),0);
        if (!(cli$get_value(c$dsc("IDENTIFIER"),&s_dsc,&s_len) & 1))
            get_input(&s_dsc,c$dsc("Message-Id: "),&s_len);
        if (!s_len) return(0);
        s[s_len] = '\0';
        del_id(s,0);
        return(0);
        }

    if (no_priv()) {
        sprintf(s,"%s@%s",usr_username,Node_address);
        if ((strcmp(s,moderator_address(ga[curr_g]->grp_name))) &&
           (!(ga[curr_g]->grp_local & NEWS_MOD_ACCESS)))
            return(err_line("\tError: Delete - No privilege\n"),0);
        }

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

    if (!(cli$get_value(c$dsc("ITEM"),&s_dsc,&s_len) & 1)) {
        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("ITEM"),&s_dsc,&s_len) & 1);
        temp_s[strlen(temp_s) - 1] = '\0';
        strcpy(s,temp_s);
        parse_items(temp_s,d_itm);
        }

    if ((!d_itm[0].fitm) || (!ga[curr_g]->grp_ia))
        return(err_line("\tDelete - Cannot locate newsitem\n"),0);

    strcpy(err_oline,"Delete Newsitem? [n]:");
    for (di = 0; (li = d_itm[di].litm), (fi = d_itm[di].fitm) ; ++di) {
        if (!no_confirm) {
            if ((fi > 0) && (!li)) {
                if (screen_active) sprintf(err_oline,"Delete Newsitem #%d? [n]:",fi);
                else {
                    printf("\nNewsitem to delete:\n");
                    printf("\tNewsgroup: %s\n",ga[curr_g]->grp_name);
                    printf("\t#        : %d\n",fi);
                    }
                }
            else if (fi < 0) {
                if (screen_active) sprintf(err_oline,"Delete ALL Newsitems? [n]:");
                else {
                    printf("\nNewsitem to delete:\n");
                    printf("\tNewsgroup: %s\n",ga[curr_g]->grp_name);
                    printf("\tALL ITEMS\n");
                    }
                }
            else {
                if (screen_active) sprintf(err_oline,"Delete Newsitems #%d-%d? [n]:",fi,li);
                else {
                    printf("\nNewsitem to delete:\n");
                    printf("\tNewsgroup: %s\n",ga[curr_g]->grp_name);
                    printf("\t#        : %d - %d\n",fi,li);
                    }
                }
            status = get_input(&command,c$dsc(err_oline),&response_length);
            if (status == RMS$_EOF) return(0);
            }
        if ((no_confirm) ||
             ((status & 1) && (response_length) && (tolower(*response)== 'y'))){
            if ((fi > 0) && (!li)) delitm_file(curr_g,fi);
            else if (fi < 0) {
                set_level(2);
                if (ga[curr_g]->grp_count) {
                    for (li = ga[curr_g]->grp_ia[1].itm_num; li <= ga[curr_g]->grp_topnum; ++li)
                    delitm_file(curr_g,li);
                    }
                }
            else for (;fi<=li;++fi) delitm_file(curr_g,fi);
            }
        }
    return(0);
}

/*
 *  do_dgroup
 *
 *  delete a news newsgroup
 */

do_dgroup()
{
    char ngroup[SUBJLEN];
    short ngroup_len = 0;
    $DESCRIPTOR(ngroup_dsc,ngroup);
    int g,
        dgr = 1;

    if (no_priv()) return(err_line("\tError: Delete - No privilege\n"),0);

    no_confirm = (cli$present(c$dsc("CONFIRM")) == CLI$_NEGATED);

    if (!(cli$get_value(c$dsc("GROUP"),&ngroup_dsc,&ngroup_len) & 1))  {
        if (g = curr_g) del_grp(g);
        return(0);
        }
    do {
        ngroup[ngroup_len] = '\0';
        util_cvrt(ngroup,ngroup);
        if (strchr(ngroup,'*') || strchr(ngroup,'%')) {
            for (g = 1; g <= ga_size; ++g) {
                if ((wild_match(ga[g]->grp_name,ngroup))
                      && (dgr = del_grp(g))) {
                    if (dgr < 0) break;
                    --g;
                    }
                }
            }
        else if (g = ga_exact_name(ngroup)) dgr = del_grp(g);
        } while ((dgr >= 0)
             && (cli$get_value(c$dsc("GROUP"),&ngroup_dsc,&ngroup_len) & 1));
    return(0);
}

/*
 *  do_dcontext
 *
 *  delete either the current newsgroup or newsitem depending on current
 *  context level
 */

do_dcontext()
{
    if (no_priv()) return(err_line("\tError: Delete - No privilege\n"),0);

    if ((news_context == 1) && curr_g) return(do_dgroup());
    if ((news_context > 1) && curr_g && (curr_i > 0)) return(do_ditem());
    return(0);
}

/*
 *  do_cancel
 *
 *  CANCEL a news item - delete the local copy, and also send a cancel
 *  control news item through the net.
 */

do_cancel()
{
    int cur_time;
    char inpline[256],
         mail_from[132],
         mail_groups[132],
         mail_dist[132],
         mail_id[132],
         post_path[132],
         post_dist[132],
         loc_id[IDLEN],
         id[132],
         ngroup[132],
         *p;
    struct tm *stm;


                            /* preconditions - must be positioned on the item to cancel */
    if (!((curr_g) && (curr_i) && (news_context > 1))) {
        err_line("\tError: Cancel - No current item to cancel\n");
        return(0);
        }

                            /* check if - local posting and user is newsmgr or
                               local posting by this user or is the moderator
                               of the newsgroup. Get the From: field for the
                               check */
    if (!(fp = do_open_item(curr_g,curr_i,"r",fp_open))) {
        err_line("\tError: Cancel - Cannot access item text\n");
        return(0);
        }
    *mail_from = *mail_groups = *mail_dist = *mail_id = *id = '\0';
    while (fgets(inpline,256,fp)) {
        if (*inpline == '\n') break;
        if (!strncmp(inpline,"From:",5)) strcpy(mail_from,inpline);
        else if (!strncmp(inpline,"Newsgroups:",11)) strcpy(mail_groups,inpline);
        else if (!strncmp(inpline,"Distribution:",13)) strcpy(mail_dist,inpline);
        else if (!strncmp(inpline,"Message-ID:",11)) strcpy(mail_id,inpline);
        }
    fclose(fp);
    if (*fp_open > 1) while (!(delete(fp_open)));
    *fp_open = '\0';

    sscanf(mail_id,"Message-ID: %s",id);

    if ((!*mail_from) || (!*mail_groups) || (!*id)) {
        err_line("\tError: Cancel - cannot read sender of item\n");
        return(0);
        }

    sprintf(inpline,"%s@%s",usr_username,Node_address);
    if (strcmp(inpline,moderator_address(ga[curr_g]->grp_name))) {
        sprintf(inpline,"*%s@%s*\n",usr_username,Node_address);
        if (!(wild_match(mail_from,inpline))) {
            if (no_priv() && !(ga[curr_g]->grp_local & NEWS_MOD_ACCESS)) {
                err_line("\Error: Cancel - Only the original poster of the item may cancel the item\n");
                return(0);
                }
            else {
                sprintf(inpline,"*@%s*\n",Node_address);
                if (!(wild_match(mail_from,inpline))) {
                    err_line("\Error: Cancel - Item was not posted from this node\n");
                    return(0);
                    }
                }
            }
        }

    strcpy(err_oline,"Delete Newsitem? [n]:");
    status = get_input(&command,c$dsc(err_oline),&response_length);
    if (status == RMS$_EOF) return(0);
    if ((status & 1) && (response_length) && (tolower(*response) == 'y')) {
        if (fp = fopen(Post_file,"w")) {
            fprintf(fp,"Path: %s!%s\n",usr_nodename,usr_username);
            sprintf(post_path," %s!%s",usr_nodename,usr_username);
            fprintf(fp,mail_from);
            sscanf(mail_groups,"Newsgroups: %s",ngroup);
            fprintf(fp,mail_groups);
            fprintf(fp,"Subject: cancel %s\n",id);
            strcpy(loc_id,gen_id());
            fprintf(fp,"Message-ID: %s\n",loc_id);
            time(&cur_time);
            p = ctime(&cur_time);
            p += 4;
            stm = localtime(&cur_time);
            fprintf(fp,"Date: %d %.3s %d %02d:%02d:%02d %s\n",
                stm->tm_mday,p,stm->tm_year,stm->tm_hour,stm->tm_min,stm->tm_sec,
                news_timezone);
            fprintf(fp,"Control: cancel %s\n",id);
            if (*mail_dist) {
                sscanf(mail_dist,"Distribution: %s",post_dist);
                fprintf(fp,mail_dist);
                }
            fprintf(fp,"Lines: 1\n\ncancel %s\n",id);
            fclose(fp);

            sys_remote_send(post_path,ngroup,post_dist,Post_file,loc_id,0);
            while (!(delete(Post_file)));
            }
        delitm_file(curr_g,ga[curr_g]->grp_ia[curr_i].itm_num);
        }
    return(0);
}
