/*
**++
**  FACILITY:
**      NEWSDISPLAY
**
**  ABSTRACT:
**      This module displays the contents of an item file on the screen
**      using a paged display mechanism.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990
**
**  Modification History:
**    6.1b5  Charles Bailey  bailey@genetics.upenn.edu
**      - changed 'unseenitems' to 'unreaditems' throughout
**    6.1b5  22-Feb-1993  Marlys Nelson  acs_mn@rivers.acc.uwrf.edu
**      - enable sysprv before calling fstat() on items in restricted groups
**    6.1b7  15-May-1993  Charles Bailey  bailey@genetics.upenn.edu
**      - optimize lookup by item number in do_display()
**    6.1b8   4-Aug-1993  EWILTS
**      - moved call to log_to_usage_file from newsread.c to newsdisplay.c
**    6.1b8  29-Dec-1993  Mark Martinec  mark.martinec@ijs.si
**      - check condition code returned from all SMG calls;
**      - remove nonprintable characters from text to avoid SMG warnings
**    6.1b8  24-Jan-1994  Mark Martinec  mark.martinec@ijs.si
**      - replace calls to smg$put_chars with calls to smg_put_chars 
**    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	30-May-1995	Mark Martinec   mark.martinec@ijs.si
**	- call new routine fit_line to cope with the problem of truncated
**	  'Unread:' count in the 'Group:' header line of the items display
**    V6.1b11	21-Feb-1997     Mark Martinec   mark.martinec@ijs.si
**	- fpd_open/fp_open is now int;  pass it by ref;
**        don't try to delete_file_versions(fpd_open);
**    V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	- change %s to %.<n>s in sprintf to guard against overwriting buffer
**	- replace several calls to strcpy/strcat with safer strnncpy/strnncat
**	- replace pattern strcpy(news_malloc(strlen(x)+1),x) with news_strdup()
**	- fix the year 2000 problem when generating the missing 'Date:' header
**    V6.2.0   6-Aug-1997 	Mark Martinec   mark.martinec@ijs.si
**	- replace smg$put_with_scroll() with smg$put_line()
**--
**/

#ifdef vaxc
#module NEWSDISPLAY "V6.2"
#endif

#define _NEWSDISPLAY_C
#define module_name "NEWSDISPLAY"

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

#if MENU_SUPPORT
#include "newsmenu.h"
#endif

static
char subjline[90],
     fromline[256],
     orgline[90],
     dateline[90],
     summline[90],
     keywline[90],
     lineline[90],
     *markline;
int header_started = 0;


/*
 *  start_header
 *
 *  Start collecting page head lines
 */

void start_header(level,code)
  int level, code;
{
  int status;
  DPTR dptr;

  header_started = 1;
  if (level == I_DISPLAY_LOOP) {
    dptr = &itmptr ;
    textptr.display_code = 0;
    }
  else dptr = &textptr;
  dptr->display_code = code;
  dptr->header_line[0] = '\0';
  dptr->dispf_stat.st_size = 0;
  dptr->do_header = 1;
  dptr->usr_line = 0;
  dptr->gets_size = dptr->disp_size = 0;
  dptr->text_lines = dptr->fdepth = 0;
  if (smg_active) {
    _c$cks(smg$change_virtual_display(&(dptr->shdid),c$ac(80),c$ac(devcol),0,0,0));
    _c$cks(smg$erase_display(&(dptr->sdid)));
    _c$cks(smg$erase_display(&(dptr->shdid)));
    _c$cks(smg$paste_virtual_display(&(dptr->shdid),&pid,c$ac(1),c$ac(1)));
    dptr->text_displayed = 1;
    }
  dptr->str_found = 1;
}

/*
 *  end_header
 *
 *  Mark end of page header text
 */

void end_header(level)
  int level;
{
  int status;
  DPTR dptr;

  header_started = 0;
  if (level == I_DISPLAY_LOOP) dptr = &itmptr ;
  else dptr = &textptr;
  dptr->do_header = 0;
  if (smg_active) {
    _c$cks(smg$change_virtual_display(&(dptr->shdid),&(dptr->usr_line),c$ac(devcol),0,0,0));
    _c$cks(smg$paste_virtual_display(&(dptr->sdid),&pid,c$rfi(dptr->usr_line + 1),c$ac(1)));
    _c$cks(smg$repaste_virtual_display(&(dptr->shdid),&pid,c$ac(1),c$ac(1)));
    _c$cks(smg$repaste_virtual_display(&trailer_vd,&pid,c$rfi(devrow - 2),c$ac(1)));
    }
  dptr->sheader_line = dptr->usr_line;
  dptr->sdid_home = dptr->sdid_paste = dptr->usr_line + 1;
  dptr->sdid_baseline = dptr->sdid_line = 0;
  dptr->sdid_dsize = (devrow - 2) - dptr->sdid_home;
  dptr->target_paste = dptr->sdid_paste;
}

/*
 *  put_line
 *
 *  Display a line on the screen
 */

void
put_line(s,last_screen,level)
  const char *s;
  int last_screen;
  int level;
{
  volatile DPTR dptr;	/*(volatile to make setjmp/longjmp safer)*/
  volatile int screen_size, vdisp_size, e_displayed = 0, act = 0;
  volatile int get_status;
  int dl_len, i, n, setenv_val = 0;
  int status;
  int (*f)();
  unsigned short trm;
  char mprompt[80], resp[80], dline[256], *cp;
  int mprompt_l; /* strlen(mprompt) */
  $DESCRIPTOR(dline_dsc,dline);
  int s_l = strlen(s);

  parse_level = level;
  dptr = (level == I_DISPLAY_LOOP) ? &itmptr : &textptr;

  if (!last_screen) {
    ++(dptr->usr_line);
    if (dptr->do_header) {
      if (smg_active)
        smg_put_chars(dptr->shdid,s,dptr->usr_line,1,0,SMG$M_BOLD);
      else {
        int dptr_header_line_l = strlen(dptr->header_line);
        memcpy(&(dptr->header_line)[dptr_header_line_l],s,min(s_l,devcol));
        dptr_header_line_l += min(s_l,devcol);
        (dptr->header_line)[dptr_header_line_l++] = '\n';
        (dptr->header_line)[dptr_header_line_l]   = '\0';
        printf("%.*s\n",min(s_l,devcol),s);
        }
      dptr->disp_size = dptr->gets_size;
      return;
      }
    }

  if (!smg_active) {
    if (last_screen) return;
    if (   (dptr->usr_line == (devrow - 2))
        || (   dptr->fdepth
	    && ((dptr->text_cline) ?
		(dptr->text_cline >= dptr->fdepth) :
      		(dptr->usr_line > dptr->fdepth)))) {
      dptr->fdepth = 0;
      strnncpy(mprompt,sizeof(mprompt), "<RETURN for more>",-1, &mprompt_l);
      if (dptr->text_lines && dptr->text_cline)
        mprompt_l += sprintf(&mprompt[--mprompt_l], " - %d/%d Lines>",
                             dptr->text_cline,dptr->text_lines);
      if (dptr->dispf_stat.st_size)
        mprompt_l += sprintf(&mprompt[--mprompt_l], " (%d%%)>",
                     (int)((dptr->disp_size*100) / dptr->dispf_stat.st_size));
      for (;;) {
        if (news_lock_alarm) {
          printf("NEWS - Manager has requested shutdown\n");
          closefiles();
          exit(1);
          }
        printf("\n%s\n",mprompt);
        get_status = get_input_general(&cmd_dsc,c$dsc("NEWS> "),&cmd_len,
                                       0,0, 1,line_editing);
        c$free_tmp();
        if (((get_status == SS$_ABORT) || (get_status == SS$_CANCEL)) && news_lock_alarm) {
          closefiles();
          exit(1);
          }
        if ((get_status == SMG$_EOF) || (get_status == RMS$_EOF)) {
          closefiles();
          exit(1);
          }
        cmd[cmd_len] = '\0';
        strnncpy(resp,sizeof(resp), cmd,cmd_len, 0);
        lower_case(resp);
        if (   (cmd_len > 2)
            && (!strncmp(resp,"close",strlen(resp)))) {
          strcpy(cmd,"dir");
          cmd_len = 3;
          longjmp(env,2);
          }
        if (cmd_len) {
          if (do_parse(cmd)) {
            closefiles();
            exit(1);
            }
          }
        else break;
        }
      if (*(dptr->header_line)) {
        printf("%s",dptr->header_line);
        dptr->usr_line = dptr->sheader_line + 1;
        }
      else dptr->usr_line = 1;
      }
    if (dptr->text_lines && (++(dptr->text_cline) > dptr->text_lines))
      dptr->text_lines = dptr->text_cline;
    printf("%.*s\n",min(s_l,devcol),s);
    dptr->disp_size = dptr->gets_size;
    return;
    }

  if (last_screen > 1) return;

  if (   last_screen
      || (dptr->usr_line == (devrow - 2))
      || (   dptr->fdepth
          && ((dptr->text_cline) ?
	      (dptr->text_cline >= dptr->fdepth) :
	      (dptr->usr_line > dptr->fdepth)))) {
    screen_size = dptr->sdid_dsize - 1;

    if (   !last_screen
        && (dptr->usr_line < (devrow - 2))
        && dptr->fdepth
        && ((dptr->text_cline) ?
	      (dptr->text_cline >= dptr->fdepth) :
	      (dptr->usr_line > dptr->fdepth)))
      screen_size = devrow - (dptr->usr_line + 3);

    dptr->sdid_current = dptr->sdid_paste;
    if (last_screen) {
      dptr->str_found = 1;
      while (dptr->sdid_paste > dptr->target_paste) {
        ++(dptr->sdid_baseline);
        --(dptr->sdid_paste);
        _c$cks(smg$move_virtual_display(&(dptr->sdid),&pid,&(dptr->sdid_paste),
                                        c$ac(1),0));
        }
      }
    dptr->fdepth = 0;
    vdisp_size = dptr->disp_size;
    for (;;) {
      if (level == I_DISPLAY_LOOP) setenv_val = setjmp(envdisp);
      parse_level = level;
      if (!setenv_val) {
        if (dptr->str_found) {
          brdcst_col = 7;
        display_brdcst(3);
        brdcst_line = 3;
          e_displayed = 1;
          if (last_screen && (dptr->sdid_paste <= dptr->sdid_current))
            strnncpy(mprompt,sizeof(mprompt), "<End of Display>",-1, &mprompt_l);
          else {
            e_displayed = 0;
            strnncpy(mprompt,sizeof(mprompt), "<RETURN for more>",-1, &mprompt_l);
            if (dptr->text_lines && dptr->text_cline)
              mprompt_l += sprintf(&mprompt[--mprompt_l], " - %d/%d Lines>",
                                   dptr->text_cline,dptr->text_lines);
            if (dptr->dispf_stat.st_size)
              mprompt_l += sprintf(&mprompt[--mprompt_l], " (%d%%)>",
                            (int)((vdisp_size*100) / dptr->dispf_stat.st_size));
            }
          smg_put_chars(trailer_vd,mprompt,2,1,1,SMG$M_REVERSE);
#if MENU_SUPPORT
          if (!auto_restarting())
#endif
          get_status = get_input_general(&cmd_dsc,c$dsc("NEWS> "),&cmd_len,
                                         0,&trm, 1,line_editing);
#if MENU_SUPPORT
	  if (((cmd_len >= 3 ) && (toupper(cmd[0]) == 'M')
		&& (toupper(cmd[1]) == 'E') && (toupper(cmd[2]) == 'N')
		&& (toupper(cmd[3]) == 'U')) || auto_restarting()) {
	    strcpy(cmd,"        " );
            { int ll;
	      strnncpy(cmd,cmd_m, get_menu_input(),-1, &ll); cmd_len = ll;
            }
            trm = 0;
            };
#endif
          c$free_tmp();
          if (((get_status == SS$_ABORT) || (get_status == SS$_CANCEL)) && news_lock_alarm) {
            closefiles();
            exit(1);
            }
          brdcst_line = 0;
          clear_err_line();
          if ((get_status == SMG$_EOF) || (get_status == RMS$_EOF)) {
            closefiles();
            exit(1);
            }
          cmd[cmd_len] = '\0';
          cp = cmd;
          while (*cp && isspace(*cp)) cp++;
          i = strlen(cp);
          while (i && isspace(cp[i-1])) --i;
          cp[i] = '\0';
          strnncpy(resp,sizeof(resp), cp,i, &i);
          strnncpy(cmd,cmd_m, resp,i, &i); cmd_len = i;
          lower_case(resp);
          if (!cmd_len) {
            if (!(last_screen && (dptr->sdid_paste <= dptr->sdid_current)))
              sprintf(resp,"down %d",screen_size);
            else if (parse_level == T_DISPLAY_LOOP) {
              strcpy(cmd,"noop");
              cmd_len = 4;
	      _c$cks(smg$unpaste_virtual_display(&(textptr.sdid),&pid));
	      _c$cks(smg$unpaste_virtual_display(&(textptr.shdid),&pid));
              textptr.text_displayed = 0;
              if (itmptr.text_displayed) longjmp(envdisp,2);
              longjmp(env,2);
              }
	    else longjmp(env,2);
            }
          if ((trm == SMG$K_TRM_PREV_SCREEN) || (trm == SMG$K_TRM_UP)) {
            int repeat;

            if (!cmd_len) repeat = 1;
            else if (sscanf(cmd,"%d",&repeat) != 1) repeat = 1;
            if (!strcmp(resp,"top")) repeat = 1000;
            if (trm == SMG$K_TRM_PREV_SCREEN) repeat *= screen_size;
            sprintf(resp,"%d",repeat);
            trm = SMG$K_TRM_UP;
            }
          else if ((trm == SMG$K_TRM_NEXT_SCREEN) || (trm == SMG$K_TRM_DOWN)) {
            int repeat;

            if (!cmd_len) repeat = 1;
            else if (sscanf(cmd,"%d",&repeat) != 1) repeat = 1;
            if (!strcmp(resp,"bottom")) repeat = 10000;
            if (trm == SMG$K_TRM_NEXT_SCREEN) repeat *= screen_size;
            sprintf(resp,"%d",repeat);
            trm = SMG$K_TRM_DOWN;
            }
        else if (!strncmp(resp,"bottom",min(4,strlen(resp))) &&
            (*(resp + 1) == 'o')) {
            strcpy(resp,"10000");
            trm = SMG$K_TRM_DOWN;
            }
          else if (   !strncmp(resp,"top",min(3,strlen(resp)))
                   && (*(resp + 1) == 'o')
                   && (strlen(resp) <= 3)) {
            strcpy(resp,"10000");
            trm = SMG$K_TRM_UP;
            }
          else if (*resp == 'u') {
            char *c;
            int repeat;

	    if ((c = strchr(resp,' ')) != 0
	     || (c = strchr(resp,'\t')) != 0) *c++ = '\0';
	    if ((strlen(resp) < 3) &&
              (!strncmp(resp,"up",min(2,strlen(resp))))) {
              if (!c || (sscanf(c,"%d",&repeat) != 1)) repeat = 1;
              sprintf(resp,"%d",repeat);
              trm = SMG$K_TRM_UP;
              }
            else trm = 0;
            }
          else if (*resp == 'd') {
            char *c;
            int repeat;

	    if ((c = strchr(resp,' ')) != 0
	     || (c = strchr(resp,'\t')) != 0) *c++ = '\0';
	    if ((strlen(resp) < 5) &&
              (!strncmp(resp,"down",min(4,strlen(resp))))) {
              if (!c || (sscanf(c,"%d",&repeat) != 1)) repeat = 1;
              sprintf(resp,"%d",repeat);
              trm = SMG$K_TRM_DOWN;
              }
            else trm = 0;
            }
          }
        else {
          trm = SMG$K_TRM_DOWN;
          strcpy(resp,"6");
          }
        if (trm == SMG$K_TRM_UP) {
          if (sscanf(resp,"%d",&n) != 1) n = 1;
          if (n < (screen_size)) {
            _c$cks(smg$end_pasteboard_update(&pid));
            act = 1;
            }
          for (; n; n--) {
            if (dptr->sdid_paste == dptr->sdid_home) {
              if (!e_displayed)
                smg_put_chars(trailer_vd,"<Top of Display>",3,1,1,SMG$M_REVERSE);
              break;
              }
            if (dptr->sdid_paste >= dptr->sdid_current) --(dptr->text_cline);
            ++(dptr->sdid_paste);
            _c$cks(smg$move_virtual_display(&(dptr->sdid),&pid,&(dptr->sdid_paste),c$ac(1),0));
            _c$cks(smg$read_from_display(&(dptr->sdid),&dline_dsc,0,c$ac(dptr->sdid_baseline)));
            --(dptr->sdid_baseline);
            dl_len = dline_dsc.dsc$w_length;
            while (dl_len && (dline[dl_len - 1] == ' ')) --dl_len;
            vdisp_size -= dl_len;
            }
          if (act) _c$cks(smg$begin_pasteboard_update(&pid));
          act = 0;
          }
        else if (trm == SMG$K_TRM_DOWN) {
          int break_it = 0;

          if (sscanf(resp,"%d",&n) != 1) n = 1;
          if (dptr->str_found && (n < screen_size)) {
            _c$cks(smg$end_pasteboard_update(&pid));
            act = 1;
            }
          for (; n; n--) {
            if (dptr->sdid_paste <= dptr->sdid_current) {
              int quant = (devrow - 2) - dptr->usr_line;
              n -= quant;
              if (n > 0) {
                dptr->usr_line -= n;
                dptr->target_paste = dptr->sdid_paste - min(dptr->sdid_dsize,n);
                }
              else dptr->target_paste = dptr->sdid_paste;
              break_it = 1;
              break;
              }
            --(dptr->sdid_paste);
            _c$cks(smg$move_virtual_display(&(dptr->sdid),&pid,&(dptr->sdid_paste),c$ac(1),0));
            ++(dptr->sdid_baseline);
            _c$cks(smg$read_from_display(&(dptr->sdid),&dline_dsc,0,c$ac(dptr->sdid_baseline)));
            dl_len = dline_dsc.dsc$w_length;
            while (dl_len && (dline[dl_len - 1] == ' ')) --dl_len;
            vdisp_size += dl_len;
            ++(dptr->text_cline);
            }
          if ((!last_screen) && break_it) break;
          if (act) _c$cks(smg$begin_pasteboard_update(&pid));
          act = 0;
          }
        else if (!strncmp(resp,"refresh",min(7,strlen(resp)))) do_refresh();
        else if (!strncmp(resp,"help",min(4,strlen(resp)))) {
          char *c;

          if ((c = strchr(resp,' ')) != 0) while (isspace(*c)) c++;
          else c = 0;
          call_help(c);
          }
        else if (   (cmd_len > 2)
                 && !strncmp(resp,"close",strlen(resp))) {
          strcpy(cmd,"noop");
          cmd_len = 4;
          if (parse_level == T_DISPLAY_LOOP) {
	    _c$cks(smg$unpaste_virtual_display(&(textptr.sdid),&pid));
	    _c$cks(smg$unpaste_virtual_display(&(textptr.shdid),&pid));
	    textptr.text_displayed = 0;
            if (itmptr.text_displayed) longjmp(envdisp,2);
            longjmp(env,2);
            }
          set_level(2);
          longjmp(env,2);
          }
        else if (do_parse(cmd)) {
           closefiles();
           exit(1);
           }
        }
      else {
        if (textptr.text_displayed) {
	  _c$cks(smg$unpaste_virtual_display(&(textptr.sdid),&pid));
	  _c$cks(smg$unpaste_virtual_display(&(textptr.shdid),&pid));
	  textptr.text_displayed = 0;
          }
        if (setenv_val > 2) {
          f = (int (*)())setenv_val;
          (*f)();
          if (textptr.text_displayed) {
            _c$cks(smg$unpaste_virtual_display(&(textptr.sdid),&pid));
	    _c$cks(smg$unpaste_virtual_display(&(textptr.shdid),&pid));
	    textptr.text_displayed = 0;
            }
          }
        }
      }
    }
  if (last_screen) return;
  dptr->sdid_line = smg$cursor_row(&(dptr->sdid));
  if (dptr->sdid_line >= DISP_BUF_SIZE) ++(dptr->target_paste);
  if (dptr->sdid_baseline < DISP_BUF_SIZE) ++(dptr->sdid_baseline);
  while ((dptr->sdid_paste + dptr->sdid_line) > (devrow - 2)) {
    --(dptr->sdid_paste);
    _c$cks(smg$move_virtual_display(&(dptr->sdid),&pid,&(dptr->sdid_paste),c$ac(1),0));
    }
  if (dptr->text_lines && (++(dptr->text_cline) > dptr->text_lines))
    dptr->text_lines = dptr->text_cline;
  last_screen = smg$cursor_row(&(dptr->sdid));

/* smg_put_chars(dptr->sdid,s, -1,1, 0,0);
 * c$cks(smg$put_line(&(dptr->sdid),c$dsc(""), 0, 0,0, 0,0,0));
 *
 * The following block does the same thing as the above two lines,
 * but it optimizes on the number of calls to SMG for the common plain text.
 * It is based on the code from smg_put_chars().
 */
  { struct dsc$descriptor_const_s *text_dsc;
    const int one = 1; /* column 1 */
    int first;
    int eol = 0;    /* line terminated? */
    int more;       /* more unprocessed text still in the input buffer */
    int xrend = 0;  /* SMG rendition bits to be set for the unprocessed text
                       (to be applied with the next call) */
    int used_l;     /* length of the processed part of string,
                       as returned by make_printable_dsc() */
    int ul = 0;     /* number of underline characters held up at the tail */
    int req_ul = 0; /* number of following characters to be underlined */
    const char *text = s;

    for (first=more=1;  more;  first=0, text+=used_l) {
      int r = xrend;                /* take xrend from the previous iteration */
      text_dsc = make_printable_dsc(text, &used_l, &more, &xrend, &ul, &req_ul);
      if (first && !more) {
        _c$cks(smg$put_line(&(dptr->sdid),text_dsc, 0, 0,0, 0,0,0)); eol = 1;
        }
      else if (first || text_dsc->dsc$w_length>0) {
        _c$cks(smg$put_chars(&(dptr->sdid),text_dsc,
                             0,  first?&one:0,  0, &r, 0,0));
        }
      }
    if (!eol) {
      text_dsc->dsc$w_length = 0;
      _c$cks(smg$put_line(&(dptr->sdid),text_dsc, 0, 0,0, 0,0,0));
      }
  }

  if (dptr->str_searching) {
    char *p, str_line[256];
    $DESCRIPTOR(sline_dsc,str_line);

    _c$cks(smg$read_from_display(&(dptr->sdid),&sline_dsc,0,&last_screen));
    str_line[sline_dsc.dsc$w_length] = '\0';
    if (!dptr->str_case_sensitive) lower_case(str_line);
    for (p=str_line; (p = strchr(p,*(dptr->str_target))) != 0; p++) {
      if (!strncmp(p,dptr->str_target,dptr->str_length)) {
        int col = (p - str_line) + 1;

        dptr->str_found = 1;
        _c$cks(smg$change_rendition(&(dptr->sdid),&last_screen,&col,c$ac(1),
                                    &(dptr->str_length),c$ac(SMG$M_BOLD),0));
        }
      }
    }
  dptr->disp_size = dptr->gets_size;
}

/*
 *  put_parse
 *
 *  Perform a simple parse of the mail line, remove header lines
 */

static
int colon_expected = 0;

static void
put_parse(s,h,disp_h,level)
  const char *s;
  int *h,
      disp_h,
      level;
{
  int i = strlen(s),
      colon,
      space;
  DPTR dptr;

  parse_level = level;
  if (level == I_DISPLAY_LOOP) dptr = &itmptr ;
  else dptr = &textptr;

  if ((!i) || (strspn(s," \t\r") == i)) {
    if (++(*h)) {
      if (!disp_h) {
        if (*subjline) put_line(subjline,0,level);
        if (*fromline) {
          if (*orgline) {
            int ll;
            strnncat(fromline,sizeof(fromline),-1, ",",1, &ll);
            strnncat(fromline,sizeof(fromline),ll, orgline,-1, 0);
            }
          put_line(fromline,0,level);
          }
        if (!smg_active) put_line("",0,level);
        end_header(level);
        if (*summline) put_line(summline,0,level);
        if (*keywline) put_line(keywline,0,level);
        if (*markline) put_line(markline,0,level);
        if (*dateline) put_line(dateline,0,level);
        put_line("",0,level);
        }
      else put_line(s,0,level);
      colon_expected = 0;
      }
    else colon_expected = 1;
    return;
    }

  colon = strcspn(s,":");
  space = strcspn(s," \t");
  if ((colon) && (space == (colon + 1)) && (space < i)) {
    if (!strncmp(s,"From:",5)) strnncpy(fromline,sizeof(fromline), s,-1, 0);
    else if (!strncmp(s,"Date:",5)) {
      int tmp, dateline_l;
      char *p;
      struct tm *stm;

      if ((tmp = parse_usenet_date(&s[6])) == 0)
        strnncpy(dateline,sizeof(dateline), s,-1, 0);
      else {
        p = ctime((time_t *)&tmp);
        p += 4;
        stm = localtime((time_t *)&tmp);
        dateline_l = sprintf(dateline, "Date: %d %.3s %04d %02d:%02d:%02d ",
                             stm->tm_mday, p, stm->tm_year + 1900,
                             stm->tm_hour, stm->tm_min, stm->tm_sec);
        strnncat(dateline,sizeof(dateline),dateline_l, news_timezone,-1, 0);
        }
      }
    else if (!strncmp(s,"Summary:",8))
      strnncpy(summline,sizeof(summline), s,-1, 0);
    else if (!strncmp(s,"Keywords:",9))
      strnncpy(keywline,sizeof(keywline), s,-1, 0);
    else if (!strncmp(s,"Lines:",6)) {
      strnncpy(lineline,sizeof(lineline), s,-1, 0);
      if (!sscanf(s,"Lines: %d",&(dptr->text_lines))) dptr->text_lines = 0;
      }
    else if (!strncmp(s,"Organization:",13))
      strnncpy(orgline,sizeof(orgline), s+13,-1, 0);
    else if (!strncmp(s,"Subj",4))
      strnncpy(subjline,sizeof(subjline), s,-1, 0);
    }
  else if (colon_expected) {
    colon_expected = 0;
    put_parse("",h,disp_h,level);
    put_line(s,0,level);
    }
  colon_expected = 0;
  if (disp_h) put_line(s,0,level);
}

/*
 *  put_rot
 *
 *  Apply rot13 algorithm to text body
 */

void
put_rot(inpline,level)
  char *inpline;
  int level;
{
  char *a;

  for (a=inpline; *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';
    }
  put_line(inpline,0,level);
}

off_t *file_byte_size_ptr;

static char dtunch, dtunfl = 0;

void
dtungetc(c,fp)
  int c; FILE *fp;
{
  dtunch = (char)c;
  dtunfl = 1;
}

char *
detabfgets(s,m,f)
  char *s; int m; FILE *f;
{
  static char *oflow = 0;
  char *cp = s, *lstart = s, *lend;
  int ocp, omalloc_sz, olen, i, om = m, adj_count = 0;

  *cp = '\0';
  if (dtunfl) {
    *cp++ = dtunch;
    *cp = '\0';
    if (dtunch == '\n') return(s);
    dtunfl = 0;
    m--;
    }
  if (oflow) {
    if (strlen(oflow) >= m) {
      strncpy(cp,oflow,m-1);
      cp[m-1] = '\0';
      cp = oflow + (m - 1);
      cp = news_strdup(cp,-1);
      news_free(oflow);
      oflow = cp;
      return(s);
      }
    else {
      strnncpy(cp,sizeof(cp), oflow,-1, 0);
      news_free(oflow);
      oflow = 0;
      if (strchr(s,'\n')) return(s);
      m -= strlen(cp);
      cp += strlen(cp);
      }
    }
  if (!fgets(cp,m,f))
    { _ck_get(f); return(*s ? s : 0); }
  else if (!strchr(s,'\t'))
    return(*s ? s : 0);
  ocp = 0;
  oflow = news_malloc(omalloc_sz = (strlen(s) + 40));
  olen = strlen(s) + 1;
  while ((lend = chop_str(lstart,'\t')) != 0) {
    --adj_count;
    strcpy(&oflow[ocp],lstart);
    ocp += strlen(lstart);
    if ((i = 8 - ((lend - lstart) & 7)) == 1) {
      oflow[ocp++] = ' ';
      ++adj_count;
      }
    else {
      if ((olen + (i - 1)) > omalloc_sz) oflow = news_realloc(oflow,omalloc_sz += 40);
      olen += (i - 1);
      while (--i >= 0) {
        oflow[ocp++] = ' ';
        ++adj_count;
        }
      }
    lstart = ++lend;
    }

  if (adj_count && file_byte_size_ptr) *file_byte_size_ptr += adj_count;
  strcpy(&oflow[ocp],lstart);

  if (strlen(oflow) >= om) {
    strncpy(s,oflow,om-1);
    s[om-1] = '\0';
    cp = oflow + (om -1);
    cp = news_strdup(cp,-1);
    news_free(oflow);
    oflow = cp;
    return(s);
    }
  strcpy(s,oflow);
  news_free(oflow);
  oflow = 0;
  return(s);
}

/*
 *  do_display
 *
 *  Display a news item on the screen page at a time
 */

int do_display(h,tpucall,rot13)
  int h, tpucall, rot13;
{
  int status;
  char tpucallstr[256], inpline[512], itm_fname[256],
       *p1;
  int line_cont = 0, i = 0, first_loop = 0, fd, rd_body = 0, save_displ, sxref = 1;

  itmptr.fdepth = 0;
  if (!smg_active) printf("\n");
  set_level(2);
  news_context = 3;
  rpush(curr_g,curr_i);
#if ARBITRON
  ga[curr_g]->grp_flags |= NEWS_M_READTHISSESSION;
  if (ga[curr_g]->grp_ia[curr_i].itm_num > ga[curr_g]->grp_topreadnum)
    ga[curr_g]->grp_topreadnum = ga[curr_g]->grp_ia[curr_i].itm_num;
#endif
  log_to_usage_file(-1,curr_g);         /* EWILTS */
  if (!tpucall) {
    if (ga[curr_g]->grp_ia[curr_i].itm_flags & NEWS_M_MAILITEM) --rd_body;
    *subjline = *fromline = *dateline = '\0';
    *summline = *keywline = *lineline = *orgline = '\0';
 /* sprintf(inpline,"Group: %s, Item %d   (Range: #%d - #%d, Unread: %d)", */
    sprintf(inpline,
      "Group[004]:[111]%.200s,[131]Item[003]:[111]%d[171](Range[004]:[111]#%d[111]-[111]#%d,[121]Unread[005]:[111]%d)",
            ga[curr_g]->grp_name,ga[curr_g]->grp_ia[curr_i].itm_num,
            ga[curr_g]->grp_ia[1].itm_num,
            ga[curr_g]->grp_ia[ga[curr_g]->grp_count].itm_num,
            (((ga[curr_g]->grp_ia[curr_i].itm_flags & NEWS_M_UNREAD) && (ga[curr_g]->grp_unread > 0)) ? ga[curr_g]->grp_unread - 1 : ga[curr_g]->grp_unread));
    fit_line(inpline, -1, sizeof inpline, (devcol?devcol:80));
    markline = mark_list(ga[curr_g]->grp_num,ga[curr_g]->grp_ia[curr_i].itm_num);
    if (!(fpd = do_open_item(curr_g,curr_i,"r",&fpd_open)))
      return(err_line("Error: Display - Cannot access item text"),0);
    fd = fileno(fpd);
    file_byte_size_ptr = NULL;
    if (ga[curr_g]->grp_flags & NEWS_M_RESTRICT_SET) sysprv();
    if (fstat(fd,&(itmptr.dispf_stat))) itmptr.dispf_stat.st_size = 0;
    else file_byte_size_ptr = &(itmptr.dispf_stat.st_size);
    if (ga[curr_g]->grp_flags & NEWS_M_RESTRICT_SET) nosysprv();
    if (profile_scansize < 0) {
      char *ge;

      if ((ge = news_getenv("NEWS_SCANSIZE",0)) && (sscanf(ge,"%d",&profile_scansize) != 1))
	profile_scansize = 0;
      }
    itmptr.text_cline = 0;
    start_header(I_DISPLAY_LOOP,SHOW_ITEM);
    parse_level = I_DISPLAY_LOOP;
    if (itmptr.str_searching) itmptr.str_found = 0;
    else itmptr.str_found = 1;
    file_byte_size_ptr = NULL;
    if (ga[curr_g]->grp_flags & NEWS_M_RESTRICT_SET) sysprv();
    if (fstat(fd,&(itmptr.dispf_stat))) itmptr.dispf_stat.st_size = 0;
    else file_byte_size_ptr = &(itmptr.dispf_stat.st_size);
    if (ga[curr_g]->grp_flags & NEWS_M_RESTRICT_SET) nosysprv();
    put_line(inpline,0,I_DISPLAY_LOOP);
    if (h) {
      put_line("",0,I_DISPLAY_LOOP);
      end_header(I_DISPLAY_LOOP);
      }
    while (detabfgets(inpline,devcol+2,fpd)) {
      int next_line_cont = 0;

      if (nntp_client && sxref) {
        if (*inpline == '\n') sxref = 0;
        else {
          char cmp[6];

          strncpy(cmp,inpline,5);
          cmp[5] = '\0';
          lower_case(cmp);
          if (!strcmp(cmp,"xref:")) {
            char ngroup[132], *n, *d;
            int itmnum, gindx, gi;

            n = &inpline[5];
            while (*n && (d = strchr(n,':')) != 0) {
              itmnum = 0;
              n = (d-1);
              while (!isspace(*(n-1))) --n;
              *d = '\0';
              strnncpy(ngroup,sizeof(ngroup), n,-1, 0);
              *d++ = ':';
              while (isdigit(*d)) itmnum = (10 * itmnum) + *d++ - '0';
              n = d;
              lower_case(ngroup);

	      if ((gindx = ga_exact_name(ngroup)) != 0) {
                if (ga[gindx]->grp_ia && find_itm_by_num(gindx,itmnum,&gi))
                  markasread(gindx,gi,1);
                else {
                  sprintf(ngroup,"%d",itmnum);
                  if (ga[gindx]->grp_reg_text) {
                    char *cp;

                    cp = (char *) news_malloc(strlen(ga[gindx]->grp_reg_text) + strlen(ngroup) + 2);
                    sprintf(cp,"%s %s",ga[gindx]->grp_reg_text,ngroup);
                    news_free(ga[gindx]->grp_reg_text);
                    ga[gindx]->grp_reg_text = cp;
                    }
                  else ga[gindx]->grp_reg_text = news_strdup(ngroup,-1);
                  }
                }
              }
            }
          }
        }

      itmptr.gets_size += strlen(inpline);
      if (!(p1 = strchr(inpline,'\n')) && ((i = strlen(inpline)) < (devcol+1))) {
        detabfgets(&inpline[i],(devcol+2) - i,fpd);
        itmptr.gets_size += strlen(&inpline[i]);
        if (!(p1 = strchr(inpline,'\n'))) i = strlen(inpline);
        }
      if (p1) *p1 = '\0';
      else {
        next_line_cont = 1;
        p1 = &inpline[i - 1];
        dtungetc(*p1,fpd);
        --(itmptr.gets_size);
        *p1 = '\0';
        }

      if (rd_body < 1) {
        if (!line_cont) {
          put_parse(inpline,&rd_body,h,I_DISPLAY_LOOP);
          if (rd_body > 0) itmptr.text_cline = 0;
          }
        else if (h) put_line(inpline,0,I_DISPLAY_LOOP);
        line_cont = next_line_cont;
        }
      else {
        if (rot13) put_rot(inpline,I_DISPLAY_LOOP);
        else put_line(inpline,0,I_DISPLAY_LOOP);
        if ((!first_loop++) && profile_scansize > 0) itmptr.fdepth = profile_scansize;
        }
      if ((rd_body > 0)  && next_line_cont && itmptr.text_lines)
        ++(itmptr.text_lines);
      }
    if (header_started) end_header(I_DISPLAY_LOOP);
    fclose(fpd);
 /* if (*fpd_open > 1) delete_file_versions(fpd_open); */
    fpd_open = 0;
    itmptr.fdepth = 0;
    put_line("",1,I_DISPLAY_LOOP);
    }
  else {
    if (*Viewer) {
      strcpy(tpucallstr,Viewer);
      if (!strncmp(Viewer,"tpu",3)) {
        if (vms_major > 4) {
          if (itmptr.str_searching)
            sprintf(tpucallstr,"%s/READ_ONLY/NOMODIFY/START_POSITION=(%d,%d)",
                    Viewer,itmptr.str_line,itmptr.str_col);
          else strcat(tpucallstr,"/READ_ONLY/NOMODIFY");
          }
        else strcat(tpucallstr,"/READ_ONLY");
        }
      }
    else if (!strncmp(Editor,"tpu",3)) {
      strcpy(tpucallstr,Editor);
      if (vms_major > 4) {
        if (itmptr.str_searching)
          sprintf(tpucallstr,"%s/READ_ONLY/NOMODIFY/START_POSITION=(%d,%d)",
                  Editor,itmptr.str_line,itmptr.str_col);
        else strcat(tpucallstr,"/READ_ONLY/NOMODIFY");
        }
      else strcat(tpucallstr,"/READ_ONLY");
      }
    else {
      if (vms_major > 4) {
        if (itmptr.str_searching)
          sprintf(tpucallstr,"TPU/READ_ONLY/NOMODIFY/START_POSITION=(%d,%d)",
                  itmptr.str_line,itmptr.str_col);
        else strcpy(tpucallstr,"TPU/READ_ONLY/NOMODIFY");
        }
      else strcpy(tpucallstr,"TPU/READ_ONLY");
      }

    strcat(tpucallstr," ");
    if (!(fpd = do_open_item(curr_g,curr_i,"r",&fpd_open)))
      return(err_line("Error: Display - Cannot access item text"),0);
    fgetname(fpd,itm_fname);
    fclose(fpd);
    strnncat(tpucallstr,sizeof(tpucallstr),-1, itm_fname,-1, 0);
    save_displ = leave_screen(0);
    (void) invoke_editor(1,Viewer,itm_fname,tpucallstr);
    join_screen(save_displ);
#if !MEM_DYNAMIC_QUOTA
    _c$cks(set_mem_ceiling());
#endif
 /* if (*fpd_open > 1) delete_file_versions(fpd_open); */
    fpd_open = 0;
    }
  return(1);
}

static int ddf_1()
{
  int fd, i = 0;
  char v[256], xbuf[512], *p1;
  unsigned short v_len;
  $DESCRIPTOR(v_dsc,v);
  FILE *fp;

  if (!(cli$get_value(c$dsc("FILE"),&v_dsc,&v_len) & 1)) {
    if (get_input(&v_dsc,c$dsc("Display File:"),&v_len) == RMS$_EOF) return(0);
    }
  v[v_len] = '\0';
  if (!*v) return(0);
  if (!(fp = fopen(v,"r"))) {
    sprintf(err_oline,"File: %.255s - cannot access file",v);
    err_line(err_oline);
    return(0);
    }
  textptr.text_cline = 0;
  start_header(T_DISPLAY_LOOP,SHOW_FILE);
  fd = fileno(fp);
  file_byte_size_ptr = NULL;
  if (fstat(fd,&(textptr.dispf_stat))) textptr.dispf_stat.st_size = 0;
  else file_byte_size_ptr = &(textptr.dispf_stat.st_size);
  sprintf(xbuf,"Display File: %.255s",v);
  put_line(xbuf,0,T_DISPLAY_LOOP);
  put_line("",0,T_DISPLAY_LOOP);
  end_header(T_DISPLAY_LOOP);
  textptr.text_lines = 0;
  while (fgets(xbuf,devcol+2,fp)) ++(textptr.text_lines);
  fseek(fp,0,0);
  while (detabfgets(xbuf,devcol+2,fp)) {
    textptr.gets_size += strlen(xbuf);
    if (!(p1 = strchr(xbuf,'\n')) && ((i = strlen(xbuf)) < (devcol+1))) {
      detabfgets(&xbuf[i],(devcol+2) - i,fp);
      textptr.gets_size += strlen(&xbuf[i]);
      if (!(p1 = strchr(xbuf,'\n'))) i = strlen(xbuf);
      }
    if (p1) *p1 = '\0';
    else {
      p1 = &xbuf[i - 1];
      dtungetc(*p1,fp);
      --(textptr.gets_size);
      *p1 = '\0';
      }
    put_line(xbuf,0,T_DISPLAY_LOOP);
    }
  fclose(fp);
  textptr.fdepth = 0;
  put_line("",1,T_DISPLAY_LOOP);
  return(0);
}

int
do_display_file()
{
  return(unwind_display(I_DISPLAY_LOOP,ddf_1));
}

/*
 *  do_set_display
 *
 *  set first page scan size
 */

int do_set_display()
{
  char v[20];
  unsigned short v_len;
  int cu = 0, fromtmp, dsastk = 0, sts;
  $DESCRIPTOR(v_dsc,v);

  if (cli$get_value(c$dsc("SCANSIZE"),&v_dsc,&v_len) & 1) {
    v[v_len] = '\0';
    if (sscanf(v,"%d",&profile_scansize) != 1) profile_scansize = 0;
    }

  if (cli$get_value(c$dsc("FROMSIZE"),&v_dsc,&v_len) & 1) {
    v[v_len] = '\0';
    if (sscanf(v,"%d",&fromtmp) == 1) minfromlen = fromtmp;
    }

  if (cli$present(c$dsc("UNSEENSTACK")) & 1) {
    cu = 1;
    if (cli$get_value(c$dsc("UNSEENSTACK"),&v_dsc,&v_len) & 1) {
      v[v_len] = '\0';
      if (sscanf(v,"%d",&dsastk) != 1) dsastk = 0;
      }
    }
  else if (cli$present(c$dsc("UNREADITEMS")) & 1) cu = 2;
  else if (cli$present(c$dsc("ALLITEMS")) & 1) cu = 3;
  if ((sts = cli$present(c$dsc("SUBJECTORDER"))) == CLI$_NEGATED) cu = -1;
  else if (sts == CLI$_PRESENT) cu = -2;
  if (!cu) return(0);
  do_set_display_type(cu,dsastk);
  return(0);
}

static int dsn_1()
{
  int noteid, nlc = 0;
  char inpline[512], credate[80], delstr[80], holdstr[80], deldate[80],
       *c;

  if (!curr_g || (news_context < 2))
    return(err_line("Show Note: No open newsgroup"),0);
  if (curr_i < 1)
    return(err_line("Show Note: Newsgroup is empty"),0);
  if (!(noteid = getnoteid())) noteid = curr_i;
  if (noteid < 0) return(err_line("Show Note: no note specified"),0);
  sprintf(inpline,"Show Note %.200s:#%d",ga[curr_g]->grp_name,
          ga[curr_g]->grp_ia[noteid].itm_num);
  markline = mark_list(ga[curr_g]->grp_num,ga[curr_g]->grp_ia[noteid].itm_num);
  if (!(fp = do_open_item(curr_g,noteid,"r",&fp_open))) {
    err_line("Show Note: Cannot access note");
    return(0);
    }
  start_header(T_DISPLAY_LOOP,SHOW_NOTE);
  put_line(inpline,0,T_DISPLAY_LOOP);
  put_line("",0,T_DISPLAY_LOOP);
  end_header(T_DISPLAY_LOOP);
  while (detabfgets(inpline,devcol+2,fp)) {
    if (!nlc && !strcmp(inpline,"\n")) break;
    if ((c = strchr(inpline,'\n')) != 0) nlc = *c = '\0';
    else nlc = 1;
    put_line(inpline,0,T_DISPLAY_LOOP);
    }
  fclose(fp);
/*if (*fp_open > 1) delete_file_versions(fp_open); */
  fp_open = 0;
  if (*markline) put_line(markline,0,T_DISPLAY_LOOP);
  strnncpy(credate,sizeof(credate),
           gendate(ga[curr_g]->grp_ia[noteid].itm_recvdate),-1, 0);
  *delstr = '\0';
  if (ga[curr_g]->grp_ia[noteid].itm_life) {
    if (ga[curr_g]->grp_ia[noteid].itm_life == 65535) strcpy(holdstr,"Permanent");
    else {
      sprintf(holdstr,"%u",ga[curr_g]->grp_ia[noteid].itm_life);
      strnncpy(deldate,sizeof(deldate),
               gendate(ga[curr_g]->grp_ia[noteid].itm_recvdate +
                       (ga[curr_g]->grp_ia[noteid].itm_life * DAY_SECS)), -1,0);
      sprintf(delstr,"Local-Item-Expiration: %.50s, ",deldate);
      }
    }
  else {
    if (ga[curr_g]->grp_itmlife) {
      if (ga[curr_g]->grp_itmlife == 65535) strcpy(holdstr,"Permanent - Newsgroup default");
      else {
        sprintf(holdstr,"[%u]",ga[curr_g]->grp_itmlife);
        strnncpy(deldate,sizeof(deldate),
                 gendate(ga[curr_g]->grp_ia[noteid].itm_recvdate +
                         (ga[curr_g]->grp_itmlife * DAY_SECS)), -1, 0);
        sprintf(delstr,"Local-Newsgroup-Expiration: %.50s, ",deldate);
        }
      }
    else {
      if (!ga[0]->grp_itmlife) ga[0]->grp_itmlife = EXP_TIME;
      if (ga[0]->grp_itmlife == 65535) strcpy(holdstr,"Permanent - Local site default");
      else {
        sprintf(holdstr,"[%u]",ga[0]->grp_itmlife);
        strnncpy(deldate,sizeof(deldate),
                 gendate(ga[curr_g]->grp_ia[noteid].itm_recvdate +
                         (ga[0]->grp_itmlife * DAY_SECS)), -1, 0);
        sprintf(delstr,"Local-Site-Expiration: %.50s, ",deldate);
        }
      }
    }
  strcpy(err_oline,"Local-Postmark: "); err_oline_cat(credate);
  put_line(err_oline,0,T_DISPLAY_LOOP);
  if (*delstr) put_line(delstr,0,T_DISPLAY_LOOP);
  strcpy(err_oline,"Local-Retention: "); err_oline_cat(holdstr);
  put_line(err_oline,0,T_DISPLAY_LOOP);
  put_line("",1,T_DISPLAY_LOOP);
  return(0);
}

int do_show_note()
{
  return(unwind_display(I_DISPLAY_LOOP,dsn_1));
}
