#module NEWSEXTRACT "V5.6"

/*
**++
**  FACILITY:
**
**      NEWSEXTRACT
**
**  ABSTRACT:
**
**      Extract NEWS items to a VMS text file.
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.5     7-Oct-1988     GIH
**          Correct informational message text
**
**      V5.6    11-Nov-1988     GIH
**        - Add additional qualifiers to EXTRACT call, modifying the logic
**          of the code to implement the qualifiers.
**        - Add additional qualifiers to PRINT call, implementing a similar
**          qualifier set to EXTRACT.
**        - Add /DIRECTORY qualifier to EXTRACT and PRINT to extract/print
**          newsgroup / newsitem directories.
**        - Reorganise functions to increase common code in these two commands.
**
**--
**/

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

#include "sjcdef.h"

extern int *c$_tmphead;

static int append_ofile,
           m_seen,
           i_header,
           n_extracted,
           more_submit,
           print_directory;
static char ofnam[256];
static FILE *fpextract = 0,
            *pdf;
static $DESCRIPTOR(ofnam_dsc,ofnam);

static char author[132],
            auth_1[132],
            auth_2[132],
            keyword[132],
            kw1[132],
            kw2[132],
            kw3[132],
            kw4[132],
            title[132],
            ttl1[132],
            before[132],
            since[132];

static int unseen;

static unsigned int bef_time,
                    sin_time;

static $DESCRIPTOR(author_dsc,author);
static $DESCRIPTOR(keyword_dsc,keyword);
static $DESCRIPTOR(title_dsc,title);
static $DESCRIPTOR(before_dsc,before);
static $DESCRIPTOR(since_dsc,since);

get_filter()
{
    short author_len,
          keyword_len,
          title_len,
          before_len,
          since_len;

    if (cli$get_value(c$dsc("FROM"),&author_dsc,&author_len) & 1) {
        author[author_len] = '\0';
        s_to_lower(author);
        }
    else if (cli$get_value(c$dsc("AUTHOR"),&author_dsc,&author_len) & 1) {
        author[author_len] = '\0';
        s_to_lower(author);
        }
    else *author = '\0';
    if (*author) {
        s_to_lower(author);
        sprintf(auth_1,"* %s*",author);
        sprintf(auth_2,"*<%s*",author);
        }

    if (cli$get_value(c$dsc("KEYWORD"),&keyword_dsc,&keyword_len) & 1) {
        keyword[keyword_len] = '\0';
        s_to_lower(keyword);
        }
    else *keyword = '\0';
    if (*keyword) {
        s_to_lower(keyword);
        sprintf(kw1,"* %s\n",keyword);
        sprintf(kw2,"* %s,*",keyword);
        sprintf(kw3,"*,%s\n",keyword);
        sprintf(kw4,"*,%s,*",keyword);
        }

    if (cli$get_value(c$dsc("SUBJECT"),&title_dsc,&title_len) & 1) {
        title[title_len] = '\0';
        s_to_lower(title);
        }
    else if (cli$get_value(c$dsc("TITLE"),&title_dsc,&title_len) & 1) {
        title[title_len] = '\0';
        s_to_lower(title);
        }
    else *title = '\0';
    if (*title) {
        s_to_lower(title);
        sprintf(ttl1,"*%s*",title);
        }

    time(&bef_time);
    before_len = 0;
    if (cli$get_value(c$dsc("BEFORE"),&before_dsc,&before_len) & 1) {
        before[before_len] = '\0';
        if (!(bef_time = cvt_date_val(before))) time(&bef_time);
        }

    sin_time = 0;
    since_len = 0;
    if (cli$get_value(c$dsc("SINCE"),&since_dsc,&since_len) & 1) {
        since[since_len] = '\0';
        sin_time = cvt_date_val(since);
        }

    unseen = ((cli$present(c$dsc("UNSEEN")) & 1) ||
              (cli$present(c$dsc("UNREAD")) & 1));
}

apply_filter(g,m,action)
    int g,
        m,
        (*action)();
{
    char xbuf[512];
    ITM_MEM_PTR iap;
    int kw = *keyword,
        tt = *title,
        au = *author;

    iap = &(ga[g]->grp_ia[m]);
    if (unseen && !iap->itm_unread) return;
    if ((iap->itm_date < sin_time) || (iap->itm_date > bef_time)) return;
    if (au || kw || tt) {
        if (!(fp = do_open_header(g,m,"r",fp_open))) return;
        while (fgets(xbuf,512,fp)) {
            if (*xbuf == '\n') break;
            s_to_lower(xbuf);
            if ((au) && (!strncmp(xbuf,"from:",5))) {
                if (wild_match(xbuf,auth_1) || wild_match(xbuf,auth_2)) au = 0;
                }
            else if ((tt) && (!strncmp(xbuf,"subject:",8))) {
                if (wild_match(xbuf,ttl1)) tt = 0;
                }
            else if ((kw) && (!strncmp(xbuf,"keywords:",9))) {
                if (wild_match(xbuf,kw1) || wild_match(xbuf,kw2) ||
                    wild_match(xbuf,kw3) || wild_match(xbuf,kw4)) kw = 0;
                }
            }
        fclose(fp);
        if (*fp_open > 1) while (!(delete(fp_open)));
        *fp_open = '\0';
        if (au || kw || tt) return;
        }
    (*action)(g,m);
}

dir_entry(g,m)
    int g, m;
{
    char *fdate;
    ITM_MEM_PTR iap;

    iap = ga[g]->grp_ia;
    fdate = gendate(iap[m].itm_date);
    if (iap[m].itm_size) fprintf(pdf,"   %-5d %c %-*.*s %6d %s",
            iap[m].itm_num,(iap[m].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,
            iap[m].itm_title,iap[m].itm_size,fdate);
    else fprintf(pdf,"   %-5d %c %-*.*s        %s",iap[m].itm_num,
            (iap[m].itm_locdate ? ' ' : '_'),SUBJLEN,SUBJLEN,iap[m].itm_title,
            fdate);
    if (iap[m].itm_unread) fprintf(pdf," U");
    fprintf(pdf,"\n");
    return(1);
}

extract_it(g,m)
{
    FILE *fpr;
    char  xfrbuf[512];
    int w_ok = i_header;

    if (print_directory) return(dir_entry(g,m));
    if (!(fpr = do_open_item(g,m,"r",fp_open))) {
        sprintf(err_oline,"Error: Extract - file read error %s:#%d",
            ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
        err_line(err_oline);
        return(0);
        }
    fpextract = fopen(ofnam,((append_ofile) ? "a" : "w"),"rat=cr","rfm=var");
    if (!fpextract) {
        sprintf(err_oline,"Error: Extract - cannot open %s",ofnam);
        err_line(err_oline);
        return(0);
        }
    if (append_ofile) fprintf(fpextract,"\f\n");
    append_ofile = 1;
    sprintf(xfrbuf,"X-NEWS: %s %s: %d\n",
        usr_nodename,ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
    fputs(xfrbuf,fpextract);
    err_line(xfrbuf);
    if (m_seen) item_update(g,m);
    while (fgets(xfrbuf,510,fpr)) {
        if (*xfrbuf == '\n') w_ok = 1;
        if (w_ok) fputs(xfrbuf,fpextract);
        }
    fclose(fpr);
    fclose(fpextract);
    fpextract = 0;
    ++n_extracted;
    return(1);
}

#define JBC$_NOSUCHQUE 294970
#define FQM_NOACCSTOP 0
#define FQM_NOACCESS  2
#define FQM_ACCESS    1

#define strparam(v,c)       if (cli$get_value(c$dsc(v),&gvd,&gvl) & 1) \
    {                                                  \
    gvs[gvl] = '\0';                                   \
    itm_list[++itm_count].bl = gvl;                     \
    itm_list[itm_count].code = c;                       \
    itm_list[itm_count].ba = c$alloc_tmp(gvl + 1);      \
    itm_list[itm_count].rl = 0;                         \
    strcpy(itm_list[itm_count].ba,gvs);                 \
    }

#define bftparam(v,c,oc,nc) if (((i = cli$present(c$dsc(v))) & 1) || (i == CLI$_NEGATED)) \
    {                                                   \
    char bftval[10];                                    \
    $DESCRIPTOR(bftval_dsc,bftval);                     \
    if (  (i & 1)                                       \
        &&(cli$get_value(c$dsc(v),&bftval_dsc,0) & 1)   \
        &&(!strncmp(bftval,"ONE",3)))                   \
        itm_list[++itm_count].code = oc;                \
    else itm_list[++itm_count].code = (i & 1) ? c : nc ;\
    itm_list[itm_count].bl = 0;                         \
    itm_list[itm_count].ba = 0;                         \
    itm_list[itm_count].rl = 0;                         \
    }

#define boolparam(v,c,nc)   if (((i = cli$present(c$dsc(v))) & 1) || (i == CLI$_NEGATED)) \
    {                                                  \
    itm_list[++itm_count].bl = 0;                       \
    itm_list[itm_count].code = (i & 1) ? c : nc ;       \
    itm_list[itm_count].ba = 0;                         \
    itm_list[itm_count].rl = 0;                         \
    }

#define intparam(v,c)       if (cli$get_value(c$dsc(v),&gvd,&gvl) & 1)  \
    {                                                  \
    gvs[gvl] = '\0';                                   \
    if (sscanf(gvs,"%d",&gvv) == 1) {                  \
        itm_list[++itm_count].bl = 4;                   \
        itm_list[itm_count].code = c;                   \
        itm_list[itm_count].ba = c$alloc_tmp(4);        \
        itm_list[itm_count].rl = 0;                     \
        *(itm_list[itm_count].ba) = gvv;                \
        }                                              \
    }

static char stat_output[256];

static short stat_output_len;

static int confirm_specified = 0,
           pri_maxq_val = 0,
           af_count = -1;

static struct it {
    short bl;
    short code;
    int *ba;
    short *rl;
    } *cj_itm,
      af_itm[20],
      cl_itm[]  = {{255,SJC$_JOB_STATUS_OUTPUT,stat_output + 1,&stat_output_len},
                   {0,0,0,0}},
      pri_item[]= {{4,SYI$_MAXQUEPRI,&pri_maxq_val,0},
                   {0,0,0,0}};

static struct sjcaf {
    struct it fitm[20];
    struct ajcaf *fnext;
    } *fhead = 0, *flast = 0;

static struct print_job {
    struct it *pcj;
    struct sjcaf *paf;
    struct print_job *pnext;
    int *alloc_list;
    } *pjobs = 0;

static short param_name[] = {SJC$_PARAMETER_1,SJC$_PARAMETER_2,SJC$_PARAMETER_3,
                      SJC$_PARAMETER_4,SJC$_PARAMETER_5,SJC$_PARAMETER_6,
                      SJC$_PARAMETER_7,SJC$_PARAMETER_8};

/*
 *  fill_qual_block
 *
 *  Fill out parameters in print command
 */

static fill_qual_block()
{
    struct it *itm_list = cj_itm;
    int itm_count = -1,
        i,
        gvv,
        gvt[2],
        param_count;
    char gvs[512];
    short gvl;
    $DESCRIPTOR(gvd,gvs);

    if (cli$present(c$dsc("QUEUE")) & 1) {
        strparam("QUEUE",SJC$_QUEUE);
        }
    else {
        strcpy(gvs,"SYS$PRINT");
        itm_list[++itm_count].bl = gvl = strlen(gvs);
        itm_list[itm_count].code = SJC$_QUEUE;
        itm_list[itm_count].ba = c$alloc_tmp(gvl + 1);
        itm_list[itm_count].rl = 0;
        strcpy(itm_list[itm_count].ba,gvs);
        }
    if (cli$get_value(c$dsc("AFTER"),&gvd,&gvl) & 1) {
        gvs[gvl] = '\0';
        if (sys$bintim(c$dsc(gvs),gvt) & 1) {
            itm_list[++itm_count].bl = 8;
            itm_list[itm_count].code = SJC$_AFTER_TIME;
            itm_list[itm_count].ba = c$alloc_tmp(8);
            itm_list[itm_count].rl = 0;
            *(itm_list[itm_count].ba) = *gvt;
            *(itm_list[itm_count].ba + 1) = *(gvt + 1);
            }
        }
    bftparam("BURST",SJC$_FILE_BURST,SJC$_FILE_BURST_ONE,SJC$_NO_FILE_BURST);
    while (cli$get_value(c$dsc("CHARACTERISTICS"),&gvd,&gvl) & 1) {
        gvs[gvl] = '\0';
        if (sscanf(gvs,"%d",&gvv) == 1) {
            itm_list[++itm_count].bl = 4;
            itm_list[itm_count].code = SJC$_CHARACTERISTIC_NUMBER;
            itm_list[itm_count].ba = c$alloc_tmp(4);
            itm_list[itm_count].rl = 0;
            *(itm_list[itm_count].ba) = gvv;
            }
        else {
            itm_list[++itm_count].bl = gvl;
            itm_list[itm_count].code = SJC$_CHARACTERISTIC_NAME;
            itm_list[itm_count].ba = c$alloc_tmp(gvl + 1);
            itm_list[itm_count].rl = 0;
            strcpy(itm_list[itm_count].ba,gvs);
            }
        }
    bftparam("FLAG",SJC$_FILE_FLAG,SJC$_FILE_FLAG_ONE,SJC$_NO_FILE_FLAG);
    if (cli$get_value(c$dsc("FORM"),&gvd,&gvl) & 1) {
        gvs[gvl] = '\0';
        if (sscanf(gvs,"%d",&gvv) == 1) {
            itm_list[++itm_count].bl = 4;
            itm_list[itm_count].code = SJC$_FORM_NUMBER;
            itm_list[itm_count].ba = c$alloc_tmp(4);
            itm_list[itm_count].rl = 0;
            *(itm_list[itm_count].ba) = gvv;
            }
        else {
            itm_list[++itm_count].bl = gvl;
            itm_list[itm_count].code = SJC$_FORM_NAME;
            itm_list[itm_count].ba = c$alloc_tmp(gvl + 1);
            itm_list[itm_count].rl = 0;
            strcpy(itm_list[itm_count].ba,gvs);
            }
        }
    boolparam("HOLD",SJC$_HOLD,SJC$_NO_HOLD);
    intparam("JOB_COUNT",SJC$_JOB_COPIES);
    boolparam("LOWERCASE",SJC$_LOWERCASE,SJC$_NO_LOWERCASE);
    strparam("NAME",SJC$_JOB_NAME);
    strparam("NOTE",SJC$_NOTE);
    boolparam("NOTIFY",SJC$_NOTIFY,SJC$_NO_NOTIFY);
    strparam("OPERATOR",SJC$_OPERATOR_REQUEST);
    param_count = 0;
    while (cli$get_value(c$dsc("PARAMETERS"),&gvd,&gvl) & 1) {
        if (++param_count > 8) break;
        gvs[gvl] = '\0';
        itm_list[++itm_count].bl = gvl;
        itm_list[itm_count].code = param_name[param_count];
        itm_list[itm_count].ba = c$alloc_tmp(gvl + 1);
        itm_list[itm_count].rl = 0;
        strcpy(itm_list[itm_count].ba,gvs);
        }
    if (cli$get_value(c$dsc("PRIORITY"),&gvd,&gvl) & 1) {
        gvs[gvl] = '\0';
        if (sscanf(gvs,"%d",&gvv) == 1) {
            if (!pri_maxq_val) sys$getsyiw(0,0,0,pri_item,0,0,0);
            if (gvv > pri_maxq_val) {
                unsigned int curprivs[2];

                lib$getjpi(c$rfi(JPI$_CURPRIV),0,0,&curprivs,0,0);
                if (!(curprivs[0] & ((1 << PRV$V_OPER) | (1 << PRV$V_ALTPRI)))) exit(SS$_NOPRIV);
                }
            itm_list[++itm_count].bl = 4;
            itm_list[itm_count].code = SJC$_PRIORITY;
            itm_list[itm_count].ba = c$alloc_tmp(4);
            itm_list[itm_count].rl = 0;
            *(itm_list[itm_count].ba) = gvv;
            }
        }
    boolparam("RESTART",SJC$_RESTART,SJC$_NO_RESTART);
    bftparam("TRAILER",SJC$_FILE_TRAILER,SJC$_FILE_TRAILER_ONE,SJC$_NO_FILE_TRAILER);
    itm_list[++itm_count].bl = 0;
    itm_list[itm_count].code = 0;
    itm_list[itm_count].ba = 0;
    itm_list[itm_count].rl = 0;
}

/*
 *  fill_local_block
 *
 *  Fill out positional qualifiers in the command
 */

static fill_local_block()
{
    struct it *itm_list = af_itm;
    int itm_count = -1,
        i,
        s_val,
        gvv;
    char gvs[512],
         *setup_list;
    short gvl;
    $DESCRIPTOR(gvd,gvs);

    intparam("COPIES",SJC$_FILE_COPIES);
    boolparam("FEED",SJC$_PAGINATE,SJC$_NO_PAGINATE);
    boolparam("HEADER",SJC$_PAGE_HEADER,SJC$_NO_PAGE_HEADER);
    boolparam("PASSALL",SJC$_PASSALL,SJC$_NO_PASSALL);
    setup_list = 0;
    while ((s_val = cli$get_value(c$dsc("SETUP"),&gvd,&gvl)) & 1) {
        gvs[gvl] = '\0';
        if (!setup_list) {
            setup_list = c$alloc_tmp(gvl + 1);
            *setup_list = '\0';
            }
        else {
            setup_list = realloc(strlen(setup_list) + 1 + gvl);
            strcat(setup_list,",");
            }
        strcat(setup_list,gvs);
        }
    if (setup_list) {
        itm_list[++itm_count].bl = strlen(setup_list);
        itm_list[itm_count].code = SJC$_FILE_SETUP_MODULES;
        itm_list[itm_count].ba = setup_list;
        itm_list[itm_count].rl = 0;
        }
    else if (s_val == CLI$_NEGATED) {
        itm_list[++itm_count].bl = 0;
        itm_list[itm_count].code = SJC$_NO_FILE_SETUP_MODULES;
        itm_list[itm_count].ba = 0;
        itm_list[itm_count].rl = 0;
        }
    boolparam("SPACE",SJC$_DOUBLE_SPACE,SJC$_NO_DOUBLE_SPACE);

    af_count = ++itm_count;
}

/*
 *  ask_confirm
 */

static ask_confirm(p)
    char *p;
{
    int sts;
    char input_line[80],
         prompt[256];
    short i_len;
    $DESCRIPTOR(idsc,input_line);

    if (!confirm_specified) return(FQM_ACCESS);
    sprintf(prompt,"%s, Print? [N]",p);

    for(;;) {
        sts = get_input(&idsc,c$dsc(prompt),&i_len);
        if (sts == RMS$_EOF) return(FQM_NOACCSTOP);
        if (!i_len) *input_line = 'N';
        if (!(sts & 1)) *input_line = 'X';
        switch(*input_line) {
            case 'Y':               /* affirmative response */
            case 'y':
            case 'T':
            case 't':
            case '1':
                return(FQM_ACCESS);

            case 'N':               /* negative response */
            case 'n':
            case 'F':
            case 'f':
            case '\0':
            case '0':
                return(FQM_NOACCESS);

            case 'Q':               /* quit */
            case 'q':
                return(FQM_NOACCSTOP);

            case 'A':               /* all */
            case 'a':
                confirm_specified = 0;
                return(FQM_ACCESS);

            default :               /* illegal response, try again */
                err_line("\tResponses: Y[es], N[o], Q[uit], A[ll]\n");
            }
        }
}

/*
 *  sjc_add_file
 *
 *  Create the item list to add a file to the job
 */

static sjc_add_file(file_name,delete_it)
    char *file_name;
    int delete_it;
{
    struct it *itm_list;
    int i;

    if (!fhead) fhead = flast = c$alloc_tmp(sizeof *fhead);
    else {
        flast->fnext = c$alloc_tmp(sizeof *fhead);
        flast = flast->fnext;
        }
    flast->fnext = 0;
    itm_list = flast->fitm;
    for (i = 0; i < af_count; ++i) itm_list[i] = af_itm[i];
    itm_list[af_count].code = SJC$_FILE_SPECIFICATION;
    strcpy((itm_list[af_count].ba = c$alloc_tmp((itm_list[af_count].bl = strlen(file_name)) + 1)),file_name);
    itm_list[af_count].rl = 0;
    itm_list[af_count + 1].code = (delete_it ? SJC$_DELETE_FILE : SJC$_NO_DELETE_FILE);
    itm_list[af_count + 1].ba = 0;
    itm_list[af_count + 1].bl = 0;
    itm_list[af_count + 1].rl = 0;
    itm_list[af_count + 2].code = 0;
    itm_list[af_count + 2].ba = 0;
    itm_list[af_count + 2].bl = 0;
    itm_list[af_count + 2].rl = 0;

    return(1);
}

/*
 *  sjc_close_job
 *
 *  Create the item list to close the job, then call sndjbc to make the
 *  job entries from the generated item lists.
 *  Returns the number of successful SJCs executed
 */

static sjc_close_job()
{
    char queuename[132];
    int l_count;

    unsigned int iosb[2];
    struct sjcaf *ftmp;

    if (!fhead) {
        err_line("\tPrint: No items selected to print\n");
        return;
        }
    sys$sndjbcw(0,SJC$_CREATE_JOB,0,cj_itm,iosb,0,0);
    if (iosb[0] == JBC$_NOSUCHQUE) {
        err_line("\tPrint: Queue %s is not defined\n",cj_itm[0].ba);
        return;
        }
     else if (!(iosb[0] & 1)) {
        err_line("\tPRINT: Cannot submit to queue %s\n",cj_itm[0].ba);
        return;
        }
    ftmp = fhead;
    while (ftmp) {
        sys$sndjbcw(0,SJC$_ADD_FILE,0,ftmp->fitm,iosb,0,0);
        ftmp = ftmp->fnext;
        }
    sys$sndjbcw(0,SJC$_CLOSE_JOB,0,cl_itm,iosb,0,0);
    stat_output[stat_output_len + 1] = '\0';
    *stat_output = '\t';
    strcat(stat_output,"\n");
    err_line(stat_output);
}

submit_it(g,m)
    int g,
        m;
{
    char full_name[256],
         prompt[256];
    int i,
        fd;
    FILE *fp;

    if (!more_submit) return;
    if (!ga[g]->grp_ia) map_items(g);
    if (!ga[g]->grp_ia) return;
    if (print_directory) return(dir_entry(g,m));

    if (!(fp = do_open_item(g,m,"r",fp_open))) return;
    fd = fileno(fp);
    getname(fd,full_name);
    fclose(fp);
    sprintf(prompt,"%s #%d",ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
    if ((i = ask_confirm(prompt)) & 1) {
        sjc_add_file(full_name,(*fp_open > 1));
        ++n_extracted;
        }
    else if (*fp_open > 1) while (!(delete(fp_open)));
    *fp_open = '\0';
    more_submit = i;
    return(i);
}

do_dirwrite(fname)
    char *fname;
{
    int g, sg, eg;
    short pdl;
    char pdv[132];
    $DESCRIPTOR(pdd,pdv);

    print_directory = 0;
    if (cli$present(c$dsc("DIRECTORY")) & 1) {
        print_directory = 2;
        if (cli$get_value(c$dsc("DIRECTORY"),&pdd,&pdl) & 1) {
            pdv[pdl] = '\0';
            if (!strcmp(pdv,"NEWSGROUPS")) print_directory = 2;
            else if (!strcmp(pdv,"GROUPS")) print_directory = 2;
            else if (!strcmp(pdv,"NEWSITEMS")) print_directory = 1;
            else if (!strcmp(pdv,"ITEMS")) print_directory = 1;
            else if (!strcmp(pdv,"NOTES")) print_directory = 1;
            }
        else if (news_context > 1) print_directory = 1;
        pdf = fopen(fname,((append_ofile) ? "a" : "w"),"rat=cr","rfm=var");
        }

    if (print_directory == 2) {
        fprintf(pdf,"NEWS       NEWSGROUP DIRECTORY\n\n");
        fprintf(pdf,"        Newsgroup                                             Count    Unread  (Reg)\n");
        if (!(cli$get_value(c$dsc("NOTERANGE"),&pdd,&pdl) & 1))  {
            pdl = 1;
            *pdv = '*';
            }
        do {
            pdv[pdl] = '\0';
            util_cvrt(pdv,pdv);
            if (strchr(pdv,'*') || strchr(pdv,'%')) {
                sg = 1;
                eg = ga_size;
                }
            else sg = eg = ga_exact_name(pdv);
            if (sg) {
                for (g = sg; g <= eg; ++g) {
                    if (wild_match(ga[g]->grp_name,pdv)) {
                        if (ga[g]->grp_unread) fprintf(pdf,"   %-5d %c %-*.*s  %5d    %5d",
                            g,(*(ga[g]->grp_server) ? '_' : ' '),SUBJLEN,SUBJLEN,ga[g]->grp_name,ga[g]->grp_count,ga[g]->grp_unread);
                        else fprintf(pdf,"   %-5d %c %-*.*s  %5d",
                            g,(*(ga[g]->grp_server) ? '_' : ' '),SUBJLEN,SUBJLEN,ga[g]->grp_name,ga[g]->grp_count);
                        if (ga[g]->grp_reg) fprintf(pdf,"  (%5d)",ga[g]->grp_reg);
                        fprintf(pdf,"\n");
                        }
                    }
                }
            } while (cli$get_value(c$dsc("NOTERANGE"),&pdd,&pdl) & 1);
        return(0);
        }
    else return(1);
}

loop_search(func)
    int (*func)();
{
    int og = 0, di, li, fi, i, sg, eg, g;
    short pdl;
    char pdv[132];
    $DESCRIPTOR(pdd,pdv);

    if (!getnoterange()) return(err_line("\tNo items selected\n"),0);
    get_filter();

    if (print_directory) {
        if (!((cli$present(c$dsc("MARKER")) & 1) ||
            (cli$present(c$dsc("NOTERANGE")) & 1))) {
            if (!(cli$get_value(c$dsc("NEWSGROUPS"),&pdd,&pdl) & 1))  {
                d_itm[0].ngrp = curr_g;
                d_itm[0].fitm = -1 ;
                d_itm[0].litm = d_itm[1].fitm = 0;
                }
            else {
                i = 0;
                do {
                    pdv[pdl] = '\0';
                    util_cvrt(pdv,pdv);
                    if (strchr(pdv,'*') || strchr(pdv,'%')) {
                        sg = 1;
                        eg = ga_size;
                        }
                    else sg = eg = ga_exact_name(pdv);
                    if (sg) {
                        for (g = sg; g <= eg; ++g) {
                            if (wild_match(ga[g]->grp_name,pdv)) {
                                d_itm[i].ngrp = g;
                                d_itm[i].fitm = -1 ;
                                d_itm[i].litm = d_itm[i + 1].fitm = 0;
                                }
                            }
                        }
                    } while (cli$get_value(c$dsc("NEWSGROUPS"),&pdd,&pdl) & 1);
                }
            }
        }

    for (di = 0; (li = d_itm[di].litm), (fi = d_itm[di].fitm) ; ++di) {
        if (print_directory && (og != d_itm[di].ngrp)) {
            og = d_itm[di].ngrp;
            if (ga[og]->grp_count)
                fprintf(pdf,"Newsgroup: %s  (%d items: #%d - #%d)\n",
                    ga[og]->grp_name,ga[og]->grp_count,
                    ga[og]->grp_ia[1].itm_num,
                    ga[og]->grp_ia[ga[og]->grp_count].itm_num);
            else fprintf(pdf,"Newsgroup: %s  (%d items)\n",
                    ga[og]->grp_name,ga[og]->grp_count);

            fprintf(pdf,"/Expiry=");
            if (ga[og]->grp_itmlife == 65535) fprintf(pdf,"Perm");
            else if (ga[og]->grp_itmlife) fprintf(pdf,"%u",ga[og]->grp_itmlife);
            else if (ga[0]->grp_itmlife == 65535) fprintf(pdf,"[Perm]");
            else if (ga[0]->grp_itmlife) fprintf(pdf,"[%u]",ga[0]->grp_itmlife);
            else fprintf(pdf,"%d",EXP_TIME);

            if (ga[og]->grp_local & 1) fprintf(pdf,"/Local");
            else fprintf(pdf,"/Net");

            if (ga[og]->grp_moderator)
                fprintf(pdf,"/Mod=%s",moderator_address(ga[og]->grp_name));
            if (*(ga[og]->grp_server)) {
                fprintf(pdf,"/Serv=%s",ga[og]->grp_server);
                if (ga[og]->grp_srvproto == 1) fprintf(pdf,"/Proto=TCP");
                else fprintf(pdf,"/Proto=DECNET");
                if (!ga[og]->grp_holdcop) fprintf(pdf,"/NoHold");
                else fprintf(pdf,"/Hold=%d",ga[og]->grp_loccoplife);
                }
            if (ga[og]->grp_reg) fprintf(pdf,",/Reg=%u",ga[og]->grp_reg);
            fprintf(pdf,"\n\n");
            fprintf(pdf,"            Title                                               Lines Date\n");
            }

        if ((fi > 0) && !li) apply_filter(d_itm[di].ngrp,fi,func);
        else if (fi < 0) {
            for (i = 1; i <= ga[d_itm[di].ngrp]->grp_count; ++i)
                apply_filter(d_itm[di].ngrp,i,func);
            }
        else {
            for (i = fi; i <= li; ++i)
                apply_filter(d_itm[di].ngrp,i,func);
            }
        }
}

print_exit()
{
    struct print_job *p, *t;

    c$cks(1);
    while (p = pjobs) {
        cj_itm = p->pcj;
        fhead = p->paf;
        sjc_close_job();
        c$cks(1);
        c$_tmphead = t->alloc_list;
        c$cks(1);
        pjobs = p->pnext;
        free(p);
        }
}

/*
 *  do_extract
 *
 *  Extract current newsitem into news file
 */

do_extract()
{
    short ofnam_len = 0;

    fpextract = 0;
    n_extracted = 0;
    append_ofile = (cli$present(c$dsc("APPEND")) & 1);
    m_seen = (cli$present(c$dsc("SEEN")) & 1);
    i_header = (cli$present(c$dsc("HEADER")) & 1);
    if (!(cli$get_value(c$dsc("FILE"),&ofnam_dsc,&ofnam_len) & 1)) {
        status = get_input_dflt(&ofnam_dsc,c$dsc("Extract_File: "),&ofnam_len,c$dsc(Extract_file),0);
        if (status == RMS$_EOF) return(0);
        }
    ofnam[ofnam_len] = '\0';
    if (!*ofnam) strcpy(ofnam,Extract_file);

    if (do_dirwrite(ofnam)) loop_search(extract_it);

    if (!print_directory) {
        if (n_extracted) sprintf(err_oline,"\tExtract: %d items extracted to %s\n",n_extracted,ofnam);
        else sprintf(err_oline,"\tExtract: No items extracted\n");
        err_line(err_oline);
        }
    else {
        fclose(pdf);
        sprintf(err_oline,"\tExtract: Directory listing extracted to %s\n",ofnam);
        err_line(err_oline);
        }
    return(0);
}

/*
 *  do_print
 *
 */

do_print()
{
    int fd;
    char full_name[256];

    more_submit = 1;
    append_ofile = n_extracted = 0;
    fhead = flast = 0;

    confirm_specified = (cli$present(c$dsc("CONFIRM")) & 1);
    m_seen = (cli$present(c$dsc("SEEN")) & 1);
    c$cks(1);
    cj_itm = c$alloc_tmp(60 * (sizeof *cj_itm));
    fill_qual_block();
    fill_local_block();

    if (do_dirwrite("SYS$SCRATCH:NEWS_PRINT.TMP")) loop_search(submit_it);

    if (print_directory) {
        fd = fileno(pdf);
        getname(fd,full_name);
        fclose(pdf);
        sjc_add_file(full_name,1);
        ++n_extracted;
        }

    if (!fhead) n_extracted = 0;
    if (n_extracted) {
        sprintf(err_oline,"\tPrint: %d items selected ",n_extracted);
        if (cli$present(c$dsc("WAIT")) & 1) {
            struct print_job *p, *t;

            strcat(err_oline,"(Print on EXIT)\n");
            if (p = pjobs)
                while (p->pnext) p = p->pnext;
            t = malloc(sizeof *t);
            if (p) p->pnext = t;
            else pjobs = t;
            t->alloc_list = c$_tmphead;
            c$_tmphead = 0;
            t->pcj = cj_itm;
            t->paf = fhead;
            t->pnext = 0;
            }
        else {
            sjc_close_job();
            strcat(err_oline,"\n");
            }
        }
    else sprintf(err_oline,"\tPrint: No items selected\n");
    err_line(err_oline);
    c$cks(1);
    return(0);
}
