/*
**++
**  FACILITY:
**      NEWSEXTRACT
**
**  ABSTRACT:
**      Extract NEWS items to a VMS text file.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990,1991
**
**  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.
**	V5.9	22-Jun-1989	GIH
**	  - Correct inconsistency in interpretation of d_itm array
**	V6.1	 5-Feb-1992	rankin@eql.caltech.edu
**	  - Lint cleanup
**      V6.1	19-Sep-1992	winter@vision.rs.ch
**	  - added /[NO]HEADER[=(keyword,[,...])] to extract and print
**	V6.1b7	15-May-1993	bailey@genetics.upenn.edu
**	  - optimize item number lookup
**	V6.1b8	04-Oct-1993	bailey@genetics.upenn.edu
**	  - add moderation status to output of Extract/Dir=NewsGroupTitles
**	V6.1b7	29-Jul-1993	Dave Costa	newsmgr@drunivac.drew.edu
**	  - Added /Local qualifier to print to support terminal printers
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
**	V6.1b10	9-Dec-1996      Mark Pizzolato	mark@infocomm.com
**	  - minor DEC C cleanup.
**	V6.1b11	21-Feb-1997     Mark Martinec   mark.martinec@ijs.si
**	  - fp_open is now int;  pass it by ref;
**	    don't try to delete_file_versions(fp_open);
**	V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace several calls to strcpy/strcat with safer strnncpy/strnncat
**	  - replace pattern strcat(err_oline,x) with err_oline_cat(x)
**	  - change %s to %.<n>s in sprintf to guard against overwriting buffer
**--
**/

#ifdef vaxc
#module NEWSEXTRACT "V6.2"
#endif

#define _NEWSEXTRACT_C
#define module_name "NEWSEXTRACT"

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

#if NAKED_INCLUDES
#include sjcdef
#include ttdef
#include tt2def
#include iodef
#else
#include <sjcdef.h>
#include <ttdef.h>
#include <tt2def.h>
#include <iodef.h>
#endif

#define FILTER_SELECT_FROM         0          /* From:                */
#define FILTER_SELECT_DATE         1          /* Date:                */
#define FILTER_SELECT_NEWSGROUPS   2          /* Newsgroups:          */
#define FILTER_SELECT_SUBJECT      3          /* Subject:             */
#define FILTER_SELECT_MESSAGEID    4          /* Message-Id:          */
#define FILTER_SELECT_PATH         5          /* Path:                */
#define FILTER_SELECT_FOLLOWUPTO   6          /* Followup-To:         */
#define FILTER_SELECT_EXPIRES      7          /* Expires:             */
#define FILTER_SELECT_REPLYTO      8          /* Reply-To:            */
#define FILTER_SELECT_SENDER       9          /* Sender:              */
#define FILTER_SELECT_REFERENCES   10         /* References:          */
#define FILTER_SELECT_CONTROL      11         /* Control:             */
#define FILTER_SELECT_DISTRIBUTION 12         /* Distribution:        */
#define FILTER_SELECT_KEYWORDS     13         /* Keywords:            */
#define FILTER_SELECT_SUMMARY      14         /* Summary:             */
#define FILTER_SELECT_APPROVED     15         /* Approved:            */
#define FILTER_SELECT_LINES        16         /* Lines:               */
#define FILTER_SELECT_XREF         17         /* Xref:                */
#define FILTER_SELECT_ORGANIZATION 18         /* Organization:        */
#define FILTER_SELECT_XNEWS        19         /* X-NEWS:   (ANU-news) */

#define FILTER_SELECT_ALL          30         /* all lines, no checks */
#define FILTER_SELECT_NONE         31         /* no header lines      */

#define FILTER_MAX_HEADERLINES     20

static
struct  filter_header_line_struct
          { 
          char hdr_line[5]; /* 4 chars are enough to recognize header 5 is null termination */
          } filter_header_line[FILTER_MAX_HEADERLINES] =
            { {{"from"}}, {{"date"}}, {{"news"}}, {{"subj"}}, {{"mess"}}, {{"path"}},
              {{"foll"}}, {{"expi"}}, {{"repl"}}, {{"send"}}, {{"refe"}}, {{"cont"}},
              {{"dist"}}, {{"keyw"}}, {{"summ"}}, {{"appr"}}, {{"line"}}, {{"xref"}},
              {{"orga"}}, {{"xnew"}} };

static
int append_ofile,
    m_seen,
    ofnam_valid = 0,
    n_extracted,
    more_submit,
    print_directory,
    rot13_sw = 0;

static
char ofnam[256];

static
$DESCRIPTOR(ofnam_dsc,ofnam);

static
FILE *fpextract = 0,
     *pdf;

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
time_t 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);


#define pnews_malloc(s)	precord(news_malloc(s))

static struct pmem {
  void *ptr[100];
  struct pmem *next;
  } *pmalloc_head = 0;

static void *precord(p)
  void *p;
{
  register struct pmem *tmp;
  register int i = 0;

  if (!p) return(0);
  for (tmp=pmalloc_head; tmp; tmp=tmp->next) {
    for (i = 0; i < 100; ++i) if (tmp->ptr[i] == 0) break;
    if (i < 100) break;
    }
  if (!tmp) {
    tmp = news_malloc(sizeof *tmp);
    for (i = 0; i < 100; ++i) tmp->ptr[i] = 0;
    tmp->next = pmalloc_head;
    pmalloc_head = tmp;
    i = 0;
    }
  tmp->ptr[i] = p;
  return(p);
}

static void punrecord(p)
  void *p;
{
  register struct pmem *tmp;
  register int i = 0;

  for (tmp=pmalloc_head; tmp; tmp=tmp->next) {
    for (i = 0; i < 100; ++i) if (tmp->ptr[i] == p) break;
    if (i < 100) break;
    }
  if (tmp) tmp->ptr[i] = 0;
}

static void *pnews_realloc(p,s)
  void *p;
  unsigned s;
{
  punrecord(p);
  return precord(p ? news_realloc(p,s) : news_malloc(s));
}

static void pnews_free(p)
  struct pmem *p;
{
  register struct pmem *tmp = p;
  register int i;

  while (tmp) {
    for (i = 0; i < 100; ++i) if (tmp->ptr[i]) news_free(tmp->ptr[i]);
    p = tmp;
    tmp = tmp->next;
    news_free(p);
    }
}  

void get_filter(init_mode)
int init_mode;
{
  unsigned short author_len,
        keyword_len,
        title_len,
        before_len,
        since_len;

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

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

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

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

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

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

  if (!find_itm_by_num(g,m,&m)) return(0);
  iap = &(ga[g]->grp_ia[m]);

  if (unseen && !(iap->itm_flags & NEWS_M_UNREAD)) return(0);
  if ((iap->itm_recvdate < sin_time) || (bef_time && (iap->itm_recvdate > bef_time)))
    return(0);
  if (au || kw || tt) {
    if (!(fp = do_open_header(g,m,"r",&fp_open))) return(0);
    while (fgets(xbuf,sizeof(xbuf),fp)) {
      if (*xbuf == '\n') break;
      lower_case(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) delete_file_versions(fp_open); */
    fp_open = 0;
    if (au || kw || tt) return(0);
    }
  return(m);
}

void apply_filter(g,m,filter,action)
  int g, m, (*action)();
  unsigned int filter;
{
  if ((m = scan_filter(g,m)) != 0) (*action)(g,m,filter);
}

int dir_entry(g,m)
    int g, m;
{
  char *fdate;
  ITM_PTR iap;

  iap = ga[g]->grp_ia;
  fdate = gendate(iap[m].itm_recvdate);
  fprintf(pdf,"   %-6d%c %-*.*s ",iap[m].itm_num,
              (iap[m].itm_cachedate ? ' ' : '_'),
              displ_subjlen,displ_subjlen,iap[m].itm_title);
  if (iap[m].itm_flags & NEWS_M_LINESVALID) fprintf(pdf,"%6d",iap[m].itm_lines);
  else fprintf(pdf,"      ");
  fprintf(pdf," %s",fdate);
  if (iap[m].itm_flags & NEWS_M_UNREAD) fprintf(pdf," U");
  fprintf(pdf,"\n");
  return(1);
}

static void init_filter(filter)
  unsigned int *filter;
{
  char uv[256];
  unsigned short ul, i;
  $DESCRIPTOR(ud,uv);

  if (cli$present(c$dsc("HEADER")) == CLI$_PRESENT)
    {
    *filter = 0;
    while (cli$get_value(c$dsc("HEADER"),&ud,&ul) & 1) 
       {
       uv[ul] = '\0';
       lower_case(uv);
       if (strncmp(uv, "all", ul) == 0)   /* check for ALL */
         {
         *filter = (unsigned int)1 << FILTER_SELECT_ALL;
         break;
         }
       if (strncmp(uv, "none", ul) == 0)  /* check for NONE */
         {
         *filter = (unsigned int)1 << FILTER_SELECT_NONE;
         break;
         }
       for (i=0; i<FILTER_MAX_HEADERLINES; i++)      /* check fo the others */
         if (strncmp(uv, filter_header_line[i].hdr_line, 4) == 0)
           {
           *filter |= (unsigned int)1 << i;
           break;
           }
       }    
    }
  else if (cli$present(c$dsc("HEADER")) == CLI$_NEGATED)
    { 
    *filter = (unsigned int)1 << FILTER_SELECT_NONE;
    }
  else /* qualifier absent */
    {
    *filter = (unsigned int)1 << FILTER_SELECT_ALL;
    }
}

static int get_header_line(str)
  char *str;
{
  int i;
  char headline[5];
  
  strncpy(headline, str, 4);
  headline[4] = '\0';
  lower_case(headline);
  for (i=0; i<FILTER_MAX_HEADERLINES; i++)      /* check fo the others */
     {
     if (strncmp(headline, filter_header_line[i].hdr_line, 4) == 0)
        return(i);
     }
  return (-1);   /* this line is not a headerline */
}

static int check_header_line(filter, header_line)
   unsigned int filter;
   int header_line;
{
   if ((header_line > 31) || (header_line < 0) 
     || ((filter & ((unsigned int)1 << FILTER_SELECT_NONE)) != 0))
     return(0);

   if (((filter & ((unsigned int)1 << header_line)) != 0) 
     || ((filter & ((unsigned int)1 << FILTER_SELECT_ALL)) != 0))
      return(1);
   
   return(0);
}

int extract_it(g,m,filter)
  int g, m;
  unsigned int filter;
{
  char xfrbuf[512], *cp1, *cp2;
  int do_rotit = 0;
  char header;
  int header_line;

  if (print_directory) return(dir_entry(g,m));
  if (!(fp = do_open_item(g,m,"r",&fp_open))) {
    sprintf(err_oline,"Error: Extract - file read error %.200s:#%d",
            ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
    err_line(err_oline);
    return(0);
    }

  if (!ofnam_valid) {
    int aln_first = 0;

    strnncpy(ofnam,sizeof(ofnam), Extract_file,-1, 0);
    cp1 = &ofnam[strlen(ofnam)];
    for (cp2 = ga[g]->grp_name; *cp2; cp2++) {
      if (isalnum(*cp2)) {
        *cp1++ = *cp2;
        aln_first = 1;
        }
      else if (aln_first) {
        if (*cp2 == '-') *cp1++ = '-';
        else if (*cp2 == '_') *cp1++ = '_';
        else if (*cp2 == '$') *cp1++ = '$';
        else if (*cp2 == '.') *cp1++ = '_';
        }
      }
    strcpy(cp1,".LIS");
    append_ofile = 1;
    }

  if ((cp1 = strrchr(ofnam,']')) != 0) {
    xfrbuf[0] = *++cp1;
    *cp1 = '\0';
    lib$create_dir(c$dsc(ofnam),0,0,0,0,0);
    *cp1 = xfrbuf[0];
    }

  fpextract = fopen(ofnam,((append_ofile) ? "a" : "w"),"rat=cr","rfm=var");
  if (!fpextract) {
    strcpy(err_oline,"Error: Extract - cannot open "); err_oline_cat(ofnam);
    err_line(err_oline);
    return(0);
    }
  if (append_ofile) fprintf(fpextract,"\f\n");
  append_ofile = 1;
  if (check_header_line(filter, FILTER_SELECT_XNEWS)) {
    sprintf(xfrbuf,"X-NEWS: %.48s %.200s: %d",
                  news_node,ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
    fputs(xfrbuf,fpextract);
    fputs("\n", fpextract);
    }
  err_line(xfrbuf);
  if (m_seen) markasread(g,m,1);
  header = 1;
  header_line = -1;
  while (fgets(xfrbuf,sizeof(xfrbuf)-2,fp)) {
    if (*xfrbuf == '\n') {
      header = 0;
      do_rotit = rot13_sw;
      }
    if (header) {
      if ((filter & ((unsigned int)1 << FILTER_SELECT_ALL)) != 0)
        fputs(xfrbuf,fpextract);                    /* ALL headerlines */
      else if ((filter & ((unsigned int)1 << FILTER_SELECT_NONE)) == 0)
        {                                           /* not NONE */
        /* try to check if new headerline: headerline contains ':' and */
        /* the first occurence of a ':' is before the first occurence  */
        /* of a ' ' (if there is a space)                              */
        if ((strchr(xfrbuf, ':') != 0) && (((strchr(xfrbuf,' ') != 0) 
           && (strchr(xfrbuf, ':') < strchr(xfrbuf,' '))) 
           || (strchr(xfrbuf,' ') == 0)))
           header_line = get_header_line(xfrbuf);
        if (check_header_line(filter, header_line))  
           fputs(xfrbuf, fpextract);   /* processing recognized headerline */
        }
      }
    if (!header) { 
      if (do_rotit) {
        char *a;
	for (a=xfrbuf; *a; a++) {
	  if ((*a >= 'A') && (*a <= 'Z')) *a = ((*a - 'A' + 13) % 26) + 'A';
	  else if ((*a >= 'a') && (*a <= 'z')) *a = ((*a - 'a' + 13) % 26) + 'a';
	  }
	}
      fputs(xfrbuf,fpextract);
      }
    }
  fclose(fp);
/*if (*fp_open > 1) delete_file_versions(fp_open); */
  fp_open = 0;
  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 = (char *) pnews_malloc(++gvl);	\
    itm_list[itm_count].rl = 0;                    	\
    strcpy(itm_list[itm_count].ba,gvs);			\
    };							\
  c$free_tmp();						\
  }

#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,(unsigned short *) 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 = (char *) 0;                \
    itm_list[itm_count].rl = 0;                         \
    }                                                   \
  c$free_tmp();						\
  }

#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 = (char *) 0;                \
    itm_list[itm_count].rl = 0;                         \
    }                                                   \
  c$free_tmp();						\
  }

#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 = (char *) pnews_malloc(4);\
      itm_list[itm_count].rl = 0;              		\
      *((int *) itm_list[itm_count].ba) = gvv; 		\
      }                                        		\
    }							\
  c$free_tmp();						\
  }

static
char stat_output[255+1];

static
unsigned short stat_output_len;

static
int pri_maxq_val = 0,
    af_count = -1;

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - this is a VMS structure */
#endif
static
struct it {
    unsigned short bl;
    unsigned short code;
    void *ba;
    unsigned short *rl;
    } *cj_itm,
      af_itm[20],
      cl_itm[]  = {{sizeof stat_output - 1,SJC$_JOB_STATUS_OUTPUT,
			stat_output,&stat_output_len},
                   {0,0,0,0}},
      pri_item[]= {{sizeof pri_maxq_val,SYI$_MAXQUEPRI,&pri_maxq_val,0},
                   {0,0,0,0}};
#ifdef __DECC
#pragma member_alignment restore
#endif

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

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

static
struct local_print_job {
     struct local_print_job *next;
     char filename[256];
     int delete_it;
     int copies;
     } *lpjobs = 0;

int local = 1;

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 void
fill_qual_block()
{
  struct it *itm_list = cj_itm;
  int itm_count = -1,
      i,
      gvv,
      gvt[2],
      param_count;
  char gvs[512];
  unsigned 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 = (char *) pnews_malloc(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) {
    int *tmp;
    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;
      tmp = pnews_malloc(8);
      itm_list[itm_count].ba = (char *) tmp;
      itm_list[itm_count].rl = 0;
      tmp[0] = gvt[0];
      tmp[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 = (char *) pnews_malloc(4);
      itm_list[itm_count].rl = 0;
      *((int *) 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 = (char *) pnews_malloc(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 = (char *) pnews_malloc(4);
      itm_list[itm_count].rl = 0;
      *((int *) 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 = (char *) pnews_malloc(gvl + 1);
      itm_list[itm_count].rl = 0;
      strcpy((char *) 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 = (char *) pnews_malloc(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] & (PRV$M_OPER | PRV$M_ALTPRI))) exit(SS$_NOPRIV);
        }
      itm_list[++itm_count].bl = 4;
      itm_list[itm_count].code = SJC$_PRIORITY;
      itm_list[itm_count].ba = (char *) pnews_malloc(4);
      itm_list[itm_count].rl = 0;
      *((int *) 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 = (char *) 0;
  itm_list[itm_count].rl = 0;
}

/*
 *  fill_local_block
 *
 *  Fill out positional qualifiers in the command
 */

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

  intparam("COPIES",SJC$_FILE_COPIES);
  boolparam("FEED",SJC$_PAGINATE,SJC$_NO_PAGINATE);
  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 = (char *) pnews_malloc(gvl + 1);
      *setup_list = '\0';
      }
    else {
      setup_list = (char *) pnews_realloc(setup_list,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 = (char *) 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 = (char *) 0;
    itm_list[itm_count].rl = 0;
    }
  boolparam("SPACE",SJC$_DOUBLE_SPACE,SJC$_NO_DOUBLE_SPACE);

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

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

	/* Next line stops PRINT command working for NEWS/CAPTIVE */
  if (news_captive) return(0);

  if (!confirm_specified) return(FQM_ACCESS);

  for(;;) {
    sts = get_input(&idsc,c$dsc(p),&i_len);
    if (sts == RMS$_EOF) return(FQM_NOACCSTOP);
    if (!i_len) *input_line = def;
    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("Responses: Y[es], N[o], Q[uit], A[ll]");
      }
    }
  news_assert_nonfatal(0); return(FQM_NOACCSTOP); /* should never happen */
}

/*
 *  sjc_add_file
 *
 *  Create the item list to add a file to the job
 */

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

  if (!fhead) fhead = flast = (struct sjcaf *) pnews_malloc(sizeof *fhead);
  else {
    flast->fnext = (struct sjcaf *) pnews_malloc(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 = (char *) pnews_malloc((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 = (char *) 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 = (char *) 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 void sjc_close_job()
{
  unsigned int iosb[2];
  struct sjcaf *ftmp;

  if (!fhead) {
    err_line("Print: No items selected to print");
    return;
    }
  sys$sndjbcw(0,SJC$_CREATE_JOB,0,cj_itm,iosb,0,0);
  if (iosb[0] == JBC$_NOSUCHQUE) {
    sprintf(err_oline,"Print: Queue %.255s is not defined",(char *)cj_itm[0].ba);
    err_line(err_oline);
    return;
    }
  else if (!(iosb[0] & 1)) {
    strcpy(err_oline,"PRINT: Cannot submit to queue ");
    err_oline_cat((char *)cj_itm[0].ba);
    err_line(err_oline);
    return;
    }
  sysprv();
  for (ftmp=fhead; ftmp; ftmp=ftmp->fnext) {
    sys$sndjbcw(0,SJC$_ADD_FILE,0,ftmp->fitm,iosb,0,0);
    if (!(iosb[0] & 1)) {
      struct it *itm_list;
      int i;

      itm_list = ftmp->fitm;
      for (i = 0; itm_list[i].code; ++i) {
        if (itm_list[i].code == SJC$_FILE_SPECIFICATION) {
          strncpy(stat_output,itm_list[i].ba,itm_list[i].bl);
          stat_output[itm_list[i].bl] = '\0';
	  sprintf(err_oline,"Cannot Print: %.200s [Status %X]",stat_output,iosb[0]);        
	  err_line(err_oline);
          break;
          }
        }
      }
    }
  nosysprv();
  sys$sndjbcw(0,SJC$_CLOSE_JOB,0,cl_itm,iosb,0,0);
  stat_output[stat_output_len] = '\0';
  if (!(iosb[0] & 1))
    sprintf(&stat_output[stat_output_len]," [Status %X]",iosb[0]);
  err_line(stat_output);
}

void printer_on(void)
{
  printf("\033[5i");
}

void printer_off(void)
{
  printf("\033[4i");
}

void dbgw(message)
  char *message;
{
  FILE *test;
  test = fopen("news_manager:test.dat","a");
  fputs(message,test);
  fclose(test);
}

void ljc_add_file(file_name,delete_it)
  char *file_name;
  int delete_it;
{
  char strn[256];
  int p_val = 1;
  unsigned short p_len = 0;
  struct local_print_job *l;
  $DESCRIPTOR(desc,strn);

  l = (struct local_print_job *) news_malloc (sizeof *l);
  strcpy(l->filename,file_name);
  l->delete_it = delete_it;
  if (cli$get_value(c$dsc("COPIES"),&desc,&p_len) & 1)
     {
     	strn[p_len] = '\0';
     	sscanf(strn,"%d",&p_val);
     	l->copies = p_val;
     }
  else l->copies = 1;
  l->next = lpjobs;
  lpjobs = l;
}

void ljc_close_job(job)
   struct local_print_job *job;
{
  FILE *fpr;
  char buff[512];
  short channel;
  int *iostat,*term_mode, change_mode = 0;

  iostat = (int *) news_malloc(8);
  term_mode = (int *) news_malloc(8);
  fpr = fopen(job->filename,"r");
  if (sys$assign(c$dsc("SYS$COMMAND"),&channel,0,0) & 1) {
   if (sys$qiow(0,channel,IO$_SENSEMODE,iostat,0,0,term_mode,8,0,0,0,0) & 1)
    if (!(term_mode[1] & TT$M_MECHFORM)) {
     term_mode[1] = term_mode[1] | TT$M_MECHFORM;
     change_mode = (sys$qiow(0,channel,IO$_SETMODE,iostat,0,0,term_mode,8,0,0,0,0) & 1);
    }
  }
  printer_on();
  while (job->copies--)
    {
     rewind(fpr);
     while (fgets(buff,sizeof(buff),fpr)) puts(buff);
     printf("\f");
    }
  printer_off();
  if (change_mode) {
     term_mode[1] = term_mode[1] ^ TT$M_MECHFORM;
     change_mode = (sys$qiow(0,channel,IO$_SETMODE,iostat,0,0,term_mode,0,0,0,0) & 1);
  }
  fclose(fpr);
  if (job->delete_it) delete(job->filename);
  news_free(iostat);
  news_free(term_mode);
}

int submit_it(g,m,filter)
  int g, m;
  unsigned int filter;
{
  char full_name[256], prompt[256];
  int i, del_file = 0;
  FILE *fp;

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

  if (!(fp = do_open_item(g,m,"r",&fp_open))) return 0;
  sprintf(prompt,"%.200s #%d, Print? [N]",
          ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
  if (((i = ask_confirm(prompt,'N')) & 1) || 
      ((filter & ((unsigned int)1 << FILTER_SELECT_ALL)) == 0)) {
    FILE *fpw;
    int do_rotit = 0;
    char  xfrbuf[512], new_file_name[256];
    char header;
    int header_line;

    fgetname(fp,full_name);
    if ((rot13_sw || ga[g]->grp_flags & NEWS_M_RESTRICT_SET) ||
        ((filter & ((unsigned int)1 << FILTER_SELECT_ALL)) == 0)) {
      sprintf(new_file_name,"SYS$SCRATCH:NEWS_%X_%lX_PRINT.TMP",getpid(),time(0));
      fpw = fopen(new_file_name,"w");
      
      if ((check_header_line(filter, FILTER_SELECT_XNEWS))) {
        sprintf(xfrbuf,"X-NEWS: %.48s %.200s: %d",
                news_node,ga[g]->grp_name,ga[g]->grp_ia[m].itm_num);
        fputs(xfrbuf,fpw);
        fputs("\n", fpw);
        }
      header = 1;
      header_line = -1;
      while (fgets(xfrbuf,sizeof(xfrbuf)-2,fp)) {
        if (*xfrbuf == '\n') {
          do_rotit = rot13_sw;
          header = 0;
          }
        if (header) {
          if ((filter & ((unsigned int)1 << FILTER_SELECT_ALL)) != 0)
            fputs(xfrbuf,fpw);                          /* ALL headerlines */
          else if ((filter & ((unsigned int)1 << FILTER_SELECT_NONE)) == 0)
            {                                           /* not NONE */
            /* try to check if new headerline: headerline contains ':' and */
            /* the first occurence of a ':' is before the first occurence  */
            /* of a ' ' (if there is a space)                              */
            if ((strchr(xfrbuf, ':') != 0) && (((strchr(xfrbuf,' ') != 0) 
              && (strchr(xfrbuf, ':') < strchr(xfrbuf,' '))) 
              || (strchr(xfrbuf,' ') == 0)))
              header_line = get_header_line(xfrbuf);
            if (check_header_line(filter, header_line))  
              fputs(xfrbuf, fpw);   /* processing recognized headerline */
            }
          }
        else {
          if (do_rotit) {
            char *a;
            for (a=xfrbuf; *a; a++) {  
  	      if ((*a >= 'A') && (*a <= 'Z')) *a = ((*a - 'A' + 13) % 26) + 'A';
	      else if ((*a >= 'a') && (*a <= 'z')) *a = ((*a - 'a' + 13) % 26) + 'a';
	      }  
	    }
          fputs(xfrbuf,fpw);
          }
        }
      fclose(fpw);
      fclose(fp);
      strnncpy(full_name,sizeof(full_name), new_file_name,-1, 0);
      del_file = 1;
      }
    else if (nntp_client || fp_open > 1) {
      fclose(fp);
      sprintf(new_file_name,"SYS$SCRATCH:NEWS_%X_%lX_PRINT.TMP",getpid(),time(0));
      if (rename(full_name,new_file_name)) {
        fpw = fopen(new_file_name,"w");
        fp = fopen(full_name,"r");
        while (fgets(xfrbuf,sizeof(xfrbuf)-2,fp)) fputs(xfrbuf,fpw);
        fclose(fpw);
        fclose(fp);
        }
      strnncpy(full_name,sizeof(full_name), new_file_name,-1, 0);
      del_file = 1;
      }
    else fclose(fp);
    if (local) ljc_add_file(full_name,del_file);
    else sjc_add_file(full_name,del_file);
    ++n_extracted;
    }
  else {
    fclose(fp);
 /* if (*fp_open > 1) delete(fp_open); */
    }
  fp_open = 0;
  return(more_submit = i);
}

int do_dirwrite(fname,valid)
  const char *fname;
  int valid;
{
  const int file_subjlen = GRPNAMELEN_F;
  int g, sg, eg;
  unsigned short pdl;
  char pdv[132], ch, *cp;
  $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,"NEWSGROUPTITLES")) print_directory = 3;
      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;
    if (!valid) {
      strnncpy(ofnam,sizeof(ofnam), Extract_file,-1, 0);
      if (print_directory >= 2) strcpy(ofnam,"GROUPS-DIRECTORY.LIS");
      else strcpy(ofnam,"ITEMS-DIRECTORY.LIS");
      append_ofile = 0;
      fname = ofnam;
      }

    if ((cp = strrchr(fname,']')) != 0) {
      ch = *++cp;
      *cp = '\0';
      lib$create_dir(c$dsc(fname),0,0,0,0,0);
      *cp = ch;
      }
    pdf = fopen(fname,((append_ofile) ? "a" : "w"),"rat=cr","rfm=var");
    }

  if (print_directory >= 2) {
    if (print_directory == 2) {
      fprintf(pdf,"NEWS       NEWSGROUP DIRECTORY\n\n");
      fprintf(pdf,"           %-*.*s  Count   Unread  (Reg)\n",
                  file_subjlen,file_subjlen,"Newsgroup");
      }
    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 (print_directory == 2) {
              fprintf(pdf,"   %-5d %c %-*.*s  %5d    %5d",g,
                    ((ga[g]->grp_flags & NEWS_M_NNTPSRV) ? '_' : ' '),
                    file_subjlen,file_subjlen,ga[g]->grp_name,
                    ga[g]->grp_count, ga[g]->grp_unread);
              if (ga[g]->grp_reg) fprintf(pdf,"  (%3d)\n",ga[g]->grp_reg);
	      else                fprintf(pdf,"\n");
	      }
	    else {
	      strcpy(err_oline,""); err_oline_cat(ga[g]->grp_name);
              err_oline_cat("\t");
              if (strlen(err_oline) <= 8)  err_oline_cat("\t");
              if (strlen(err_oline) <= 16) err_oline_cat("\t");
              if (strlen(err_oline) <= 24) err_oline_cat("\t");
              fprintf(pdf,"%s%s%s\n",err_oline,
                ga[g]->grp_topic?ga[g]->grp_topic:"",
                ((ga[g]->grp_flags & NEWS_M_MAILMODERATE) && 
                 ga[g]->grp_topic && !substrcasecmp(ga[g]->grp_topic,"(Moderated)")) ?
                 " (Moderated)" : "");
              }
            }
          }
        }
      } while (cli$get_value(c$dsc("NOTERANGE"),&pdd,&pdl) & 1);
    return(0);
    }
  return(1);
}

void loop_search(func)
  int (*func)();
{
  int og = 0, di, li, fi, i, sg, eg, g, j;
  unsigned short pdl;
  char pdv[132];
  unsigned int header_filter;
  $DESCRIPTOR(pdd,pdv);

  if (!getnoterange()) { err_line("No items selected"); return; }
  get_filter(1);

  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);
        }
      }
    }

  init_filter(&header_filter);

  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_ia) map_items(og);
      if (!ga[og]->grp_ia) ga[og]->grp_count = 0;
      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_flags & NEWS_M_LOCAL) fprintf(pdf,"/Local");
      else fprintf(pdf,"/Net");

      if (ga[og]->grp_flags & NEWS_M_MAILMODERATE) {
        const char *ma;
        if (!(ma = moderator_address(ga[og]->grp_name)))
          ma = "\"<address not locally defined>\"";
        fprintf(pdf,"/Mod=%s",ma);
        }
      if ((ga[og]->grp_flags & NEWS_M_NNTPSRV)) {
        fprintf(pdf,"/Serv=%s",
          (ga[og]->grp_srvnode && *(ga[og]->grp_srvnode) ?
            ga[og]->grp_srvnode : (ga[0]->grp_srvnode?ga[0]->grp_srvnode:"")));
        if ((j = ga[og]->grp_srvproto) == -1) j = ga[0]->grp_srvproto;
        fprintf(pdf,"/Proto=%s",proto_name(j));
        if (!ga[og]->grp_flags & NEWS_M_NNTPCACHE) fprintf(pdf,"/NoHold");
        else fprintf(pdf,"/Hold=%d",ga[og]->grp_srvcache);
        }
      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,header_filter,func);
    else if (fi < 0) {
      for (i = 1; i <= ga[d_itm[di].ngrp]->grp_count; ++i)
        apply_filter(d_itm[di].ngrp,ga[d_itm[di].ngrp]->grp_ia[i].itm_num,
                     header_filter,func);
      }
    else {
      for (i = fi; i <= li; ++i) 
        apply_filter(d_itm[di].ngrp,i,header_filter,func);
      }
    }
}

/*
 *  print_exit
 *  Remove all print jobs from the list pjobs and call sjc_close_job to create
 *  the necessary print job entry in the VMS queues.
 *  Bug Fix: remove use of variable t  29/5/89
 */
/*  Also, check the list lpjobs and call ljc_close_job to print the 
 *  file on the user's local printer.
 */

void print_exit()
{
  struct print_job *p;
  struct local_print_job *l;

  c$free_tmp();
  while ((p = pjobs) != 0) {
    cj_itm = p->pcj;
    fhead = p->paf;
    sjc_close_job();
    pnews_free(p->alloc_list);
    pjobs = p->pnext;
    news_free(p);
    }
   while ((l = lpjobs) != 0) {
     ljc_close_job(l);
     lpjobs = l->next;
     news_free(l);
    }
}

/*
 *  do_extract
 *
 *  Extract current newsitem into news file
 */

static int dextr()
{
  unsigned short ofnam_len = 0;

	/* Next line stops EXTRACT command working for NEWS/CAPTIVE */
  if (news_captive) return(0);
  fpextract = 0;
  n_extracted = 0;
  append_ofile = (cli$present(c$dsc("APPEND")) & 1);
  m_seen = (cli$present(c$dsc("SEEN")) & 1);
  rot13_sw = (cli$present(c$dsc("ROT13")) & 1);
  if (!(cli$get_value(c$dsc("FILE"),&ofnam_dsc,&ofnam_len) & 1)) ofnam_len = 0;
  ofnam[ofnam_len] = '\0';
  ofnam_valid = *ofnam;
  if (do_dirwrite(ofnam,ofnam_valid)) loop_search(extract_it);
  if (!print_directory) {
    if (n_extracted) sprintf(err_oline,"Extract: %d items (%.255s)",
                                       n_extracted,ofnam);
    else strcpy(err_oline,"Extract: NO items extracted");
    err_line(err_oline);
    }
  else {
    fclose(pdf);
    strcpy(err_oline,"Extract: Directory listing extracted to ");
    err_oline_cat(ofnam);
    err_line(err_oline);
    }
  return(0);
}

int do_extract()
{
  return(unwind_display(I_DISPLAY_LOOP,dextr));
}

static int list_equal(a,b)
  struct it *a, *b;
{
  int i = 0,
      ci;
  char *a_ba, *b_ba;

  for (;;) {
    if (a[i].bl != b[i].bl) return(0);
    if (a[i].code != b[i].code) return(0);
    if ((!a[i].bl) && (!a[i].code)) return(1);
    if (a[i].rl != b[i].rl) return(0);
    ci = 0;
    a_ba = a[i].ba;
    b_ba = b[i].ba;
    if (a_ba && b_ba) {
      for (ci = 0; ci < a[i].bl; ++ci)
        if (a_ba[ci] != b_ba[ci]) return(0);
      }
    else if (a_ba || b_ba) return(0);
    ++i;
    }
  news_assert_nonfatal(0); return(0);  /* should never happen */
}

int prn_port(void)
{
 unsigned short channel = 0;
 unsigned int *iostat,*term_mode;

 if (!(sys$assign(c$dsc("SYS$COMMAND"),&channel,0,0) & 1)) return(0);
 iostat = (unsigned int *) news_malloc(8);
 term_mode = (unsigned int *) news_malloc(12);
 if (!(sys$qiow(0,channel,IO$_SENSEMODE,iostat,0,0,term_mode,12,0,0,0,0) 
& 1)) return(0);
 return(term_mode[2] & TT2$M_PRINTER);
}

/*
 *  do_print

 *
 */

static int dprint()
{
  int fd,wait;
  char full_name[256],
       retry_command[512],
       *c,
       *x;

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

  x = cmd;
  if (*print_constant) {
    c = strchr(cmd,'/');
    while (*x && !isspace(*x)) x++;
    if (c && (c < x)) x = c;
    { int ll;
      strcpy(retry_command,"PRINT");
      strnncat(retry_command,sizeof(retry_command),-1, print_constant,-1, &ll);
      strnncat(retry_command,sizeof(retry_command),ll, x,-1, 0);
    }
    if (!(cli$dcl_parse(c$dsc(retry_command),CLICMDTBL,
			get_input,get_input,c$dsc("NEWS PRINT>")) & 1)) {
      strcpy(err_oline,"\tPrint: Cannot parse "); err_oline_cat(retry_command);
      return(0);
      }
    }
  confirm_specified = (cli$present(c$dsc("CONFIRM")) & 1);
  rot13_sw = (cli$present(c$dsc("ROT13")) & 1);
  m_seen = (cli$present(c$dsc("SEEN")) & 1);
  c$free_tmp();
  cj_itm = (struct it *) pnews_malloc(60 * (sizeof *cj_itm));
  fill_qual_block();
  fill_local_block();
  if ((local = cli$present(c$dsc("LOCAL"))) & 1)
     local = !(cli$present(c$dsc("QUEUE")) & 1);
  else local = local & 1;
  if (local && !prn_port()) {
     strcpy(err_oline,"No printer port!");
     err_line(err_oline);
     return(0);
     }
  if (do_dirwrite("SYS$SCRATCH:NEWS_PRINT.TMP",1)) loop_search(submit_it);
  if (print_directory) {
    fd = fileno(pdf);
    getname(fd,full_name);
    fclose(pdf);
    if (local) ljc_add_file(full_name,1);
    else sjc_add_file(full_name,1);
    ++n_extracted;
    }

  wait = cli$present(c$dsc("WAIT")) & 1;
  if (n_extracted) {
    sprintf(err_oline,"Print: %d items selected ",n_extracted);
    if (!wait)
      if (local)
           {
             struct local_print_job *l;
     	     l = lpjobs;
     	     ljc_close_job(l);
     	     lpjobs = l->next;
     	     news_free(l);
     	   }
      else sjc_close_job();
    else if (!local) {
      struct print_job *p, *t;

      err_oline_cat("(Print on EXIT)");
      for (p=pjobs; p; p=p->pnext) {
        if (list_equal(cj_itm,p->pcj)) {
          struct sjcaf *t = p->paf;
          int i, ci;
          char *cp1, *cp2;

          for (; t->fnext; t = t->fnext) {}
          for (; fhead; fhead = fhead->fnext) {
            t->fnext = (struct sjcaf *) news_malloc(sizeof *t);
            t = t->fnext;
            for (i = 0; i < 20; ++i) {
              t->fitm[i] = fhead->fitm[i];
              if (!t->fitm[i].bl && !t->fitm[i].code) i = 20;
              else if (t->fitm[i].ba) {
                cp1 = (char *) news_malloc(t->fitm[i].bl);
                cp2 = fhead->fitm[i].ba;
                t->fitm[i].ba = cp1;
                for (ci = 0; ci < t->fitm[i].bl; ++ci) *cp1++ = *cp2++;
                }
              }
            t->fnext = 0;
            }
          err_line(err_oline);
          c$free_tmp();
          return(0);
          }
        }
      t = (struct print_job *) news_malloc(sizeof *t);
      t->alloc_list = pmalloc_head;
      pmalloc_head = 0;
      if (p) p->pnext = t;
      else pjobs = t;
      t->pcj = cj_itm;
      t->paf = fhead;
      t->pnext = (struct print_job *) 0;
      }
    }
  else strcpy(err_oline,"Print: No items selected");
  err_line(err_oline);
  c$free_tmp();
  return(0);
}

int do_print()
{
  if (news_captive) return(0);
  return(unwind_display(I_DISPLAY_LOOP,dprint));
}
