home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / src / showmsg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-08  |  19.1 KB  |  602 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: showmsg.c,v 5.13 1993/05/08 20:25:33 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.13 $   $State: Exp $
  6.  *
  7.  *             Copyright (c) 1988-1992 USENET Community Trust
  8.  *             Copyright (c) 1986,1987 Dave Taylor
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log: showmsg.c,v $
  17.  * Revision 5.13  1993/05/08  20:25:33  syd
  18.  * Add sleepmsg to control transient message delays
  19.  * From: Syd
  20.  *
  21.  * Revision 5.12  1993/02/03  19:06:31  syd
  22.  * Remove extra strchr/strcat/strcpy et al declarations
  23.  * From: Syd
  24.  *
  25.  * Revision 5.11  1993/01/20  04:01:07  syd
  26.  * Adds a new integer parameter builtinlines.
  27.  * if (builtinlines < 0) and (the length of the message < LINES on
  28.  *       screen + builtinlines) use internal.
  29.  * if (builtinlines > 0) and (length of message < builtinlines)
  30.  *     use internal pager.
  31.  * if (builtinlines = 0) or none of the above conditions hold, use the
  32.  * external pager if defined.
  33.  * From: "John P. Rouillard" <rouilj@ra.cs.umb.edu>
  34.  *
  35.  * Revision 5.10  1993/01/19  05:11:45  syd
  36.  * Elm switches screens prematurely when calling metamail. It switches
  37.  * before writing the "Press any key..." message, thus losing metamail output.
  38.  * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
  39.  *
  40.  * Revision 5.9  1992/12/07  04:29:12  syd
  41.  * add missing err declare
  42.  * From: Syd
  43.  *
  44.  * Revision 5.8  1992/11/26  01:46:26  syd
  45.  * add Decode option to copy_message, convert copy_message to
  46.  * use bit or for options.
  47.  * From: Syd and bjoerns@stud.cs.uit.no (Bjoern Stabell)
  48.  *
  49.  * Revision 5.7  1992/11/26  00:46:50  syd
  50.  * Fix how errno is used so err is inited and used instead
  51.  * as errno gets overwritten by print system call
  52.  * From: Syd
  53.  *
  54.  * Revision 5.6  1992/11/15  01:29:37  syd
  55.  * Clear the screen before displaying MIME:
  56.  * From: marius@rhi.hi.is (Marius Olafsson)
  57.  *
  58.  * Revision 5.5  1992/11/07  16:23:48  syd
  59.  * fix null dereferences from patch 5
  60.  * From: Jukka Ukkonen <ukkonen@csc.fi>
  61.  *
  62.  * Revision 5.4  1992/10/30  21:47:30  syd
  63.  * Use copy_message in mime shows to get encode processing
  64.  * From: bjoerns@stud.cs.uit.no (Bjoern Stabell)
  65.  *
  66.  * Revision 5.3  1992/10/25  01:47:45  syd
  67.  * fixed a bug were elm didn't call metamail on messages with a characterset,
  68.  * which could be displayed by elm itself, but message is encoded with QP
  69.  * or BASE64
  70.  * From: Klaus Steinberger <Klaus.Steinberger@Physik.Uni-Muenchen.DE>
  71.  *
  72.  * Revision 5.2  1992/10/24  13:35:39  syd
  73.  * changes found by using codecenter on Elm 2.4.3
  74.  * From: Graham Hudspith <gwh@inmos.co.uk>
  75.  *
  76.  * Revision 5.1  1992/10/03  22:58:40  syd
  77.  * Initial checkin as of 2.4 Release at PL0
  78.  *
  79.  *
  80.  ******************************************************************************/
  81.  
  82. /** This file contains all the routines needed to display the specified
  83.     message.
  84. **/
  85.  
  86. #include "headers.h"
  87. #include "s_elm.h"
  88. #include <ctype.h>
  89. #include <errno.h>
  90.  
  91. #ifdef BSD
  92. # include <sys/wait.h>
  93. # undef       tolower
  94. #endif
  95.  
  96. extern int errno;
  97.  
  98. extern char *elm_date_str(), *error_description();
  99. void   _exit();
  100.  
  101. int    memory_lock = FALSE;    /* is it available?? */
  102. int    pipe_abort  = FALSE;    /* did we receive a SIGNAL(SIGPIPE)? */
  103.  
  104. FILE *pipe_wr_fp;        /* file pointer to write to external pager */
  105. extern int lines_displayed,    /* defined in "builtin" */    
  106.        lines_put_on_screen;    /*    ditto too!        */
  107.  
  108. int
  109. show_msg(number)
  110. int number;
  111. {
  112.     /*** Display number'th message.  Get starting and ending lines
  113.          of message from headers data structure, then fly through
  114.          the file, displaying only those lines that are between the
  115.          two!
  116.  
  117.          Return 0 to return to the index screen or a character entered
  118.          by the user to initiate a command without returning to
  119.          the index screen (to be processed via process_showmsg_cmd()).
  120.     ***/
  121.  
  122.     char title1[SLEN], title2[SLEN], title3[SLEN], titlebuf[SLEN];
  123.     char who[LONG_STRING], buffer[VERY_LONG_STRING];
  124. #if defined(BSD) && !defined(WEXITSTATUS)
  125.     union wait wait_stat;
  126. #else
  127.     int wait_stat;
  128. #endif
  129.  
  130.     int crypted = 0;            /* encryption */
  131.     int weed_header, weeding_out = 0;    /* weeding    */ 
  132.     int using_to,                /* misc use   */
  133.         pipe_fd[2],                /* pipe file descriptors */
  134.         new_pipe_fd,            /* dup'ed pipe fil des */
  135.         lines,                /* num lines in msg */
  136.         fork_ret,                /* fork return value */
  137.         wait_ret,                /* wait return value */
  138.         form_letter = FALSE,        /* Form ltr?  */
  139.         form_letter_section = 0,        /* section    */
  140.         padding = 0,            /*   counter  */
  141.         builtin = FALSE,            /* our pager? */
  142.         val = 0,                /* return val */
  143.         buf_len,                /* line length */
  144.         err;                /* place holder for errno */
  145.     struct header_rec *current_header = headers[number-1];
  146. #ifdef    SIGTSTP
  147.     SIGHAND_TYPE    (*oldstop)(), (*oldcont)();
  148. #endif
  149.  
  150.     lines = current_header->lines; 
  151.  
  152.     dprint(4, (debugfile,"displaying %d lines from message %d using %s\n", 
  153.         lines, number, pager));
  154.  
  155.     if (number > message_count || number < 1)
  156.       return(val);
  157.  
  158.     if(ison(current_header->status, NEW)) {
  159.       clearit(current_header->status, NEW);   /* it's been read now! */
  160.       current_header->status_chgd = TRUE;
  161.     }
  162.     if(ison(current_header->status, UNREAD)) {
  163.       clearit(current_header->status, UNREAD);   /* it's been read now! */
  164.       current_header->status_chgd = TRUE;
  165.     }
  166.  
  167.     memory_lock = FALSE;
  168.  
  169.     /* some explanation for that last one - We COULD use memory locking
  170.        to speed up the paging, but the action of "ClearScreen" on a screen
  171.        with memory lock turned on seems to vary considerably (amazingly so)
  172.        so it's safer to only allow memory lock to be a viable bit of
  173.        trickery when dumping text to the screen in scroll mode.
  174.        Philosophical arguments should be forwarded to Bruce at the 
  175.        University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
  176.  
  177. #ifdef MIME
  178.     if ((current_header->status & MIME_MESSAGE) &&
  179.         ((current_header->status & MIME_NEEDDECOD) ||
  180.          (current_header->status & MIME_NOTPLAIN)) &&
  181.         !getenv("NOMETAMAIL") ) {
  182.         char fname[STRING], Cmd[SLEN], line[VERY_LONG_STRING];
  183.         int code, err;
  184.         long lines = current_header->lines;
  185.         FILE *fpout;
  186.  
  187.         if (fseek(mailfile, current_header->offset, 0) != -1) {
  188.         sprintf(fname, "%semm.%d.%d", temp_dir, getpid(), getuid());
  189.         if ((fpout = fopen(fname, "w")) != NULL) {
  190.             copy_message("", fpout, CM_DECODE);
  191.             (void) fclose (fpout);
  192.             sprintf(Cmd, "metamail -p -z -m Elm %s", fname);
  193.             Raw(OFF);
  194.                   ClearScreen();
  195.             code = system_call(Cmd, SY_ENAB_SIGINT);
  196.             Raw(ON | NO_TITE);    /* Raw on but don't switch screen */
  197.             (void) unlink (fname);
  198.             PutLine0(LINES,0, catgets(elm_msg_cat, ElmSet, ElmPressAnyKeyIndex,
  199.                  "Press any key to return to index."));
  200.             (void) ReadCh();
  201.             printf("\r\n");
  202.             Raw(OFF | NO_TITE); /* Raw off so raw on takes effect */
  203.             Raw(ON); /* Finally raw on and switch screen */
  204.             return(0);
  205.         }
  206.         }
  207.     }
  208. #endif
  209.  
  210.     if (fseek(mailfile, current_header->offset, 0) == -1) {
  211.       err = errno;
  212.       dprint(1, (debugfile,
  213.           "Error: seek %d bytes into file, errno %s (show_message)\n",
  214.           current_header->offset, error_description(err)));
  215.       error2(catgets(elm_msg_cat, ElmSet, ElmSeekFailedFile,
  216.           "ELM [seek] couldn't read %d bytes into file (%s)."),
  217.           current_header->offset, error_description(err));    
  218.       return(val);
  219.     }
  220.     if(current_header->encrypted)
  221.       getkey(OFF);
  222.  
  223.     if(builtin=(first_word(pager,"builtin")||
  224.        first_word(pager,"internal"))||
  225.        ( builtin_lines < 0 ? lines < LINES + builtin_lines :
  226.         lines < builtin_lines))
  227.  
  228.       start_builtin(lines);
  229.  
  230.     else {
  231.  
  232.       /* put terminal out of raw mode so external pager has normal env */
  233.       Raw(OFF);
  234.  
  235.       /* create pipe for external pager and fork */
  236.  
  237.       if(pipe(pipe_fd) == -1) {
  238.         err = errno;
  239.         dprint(1, (debugfile, "Error: pipe failed, errno %s (show_msg)\n",
  240.           error_description(err)));
  241.         error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerPipe,
  242.           "Could not prepare for external pager(pipe()-%s)."),
  243.           error_description(err));    
  244.         Raw(ON);
  245.         return(val);
  246.       }
  247.  
  248.       if((fork_ret = fork()) == -1) {
  249.  
  250.         err = errno;
  251.         dprint(1, (debugfile, "Error: fork failed, errno %s (show_msg)\n",
  252.           error_description(err)));
  253.         error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerFork,
  254.           "Could not prepare for external pager(fork()-%s)."),
  255.           error_description(err));    
  256.         Raw(ON);
  257.         return(val);
  258.  
  259.       } else if (fork_ret == 0) {
  260.  
  261.         /* child fork */
  262.  
  263.         /* close write-only pipe fd and fit read-only pipe fd to stdin */
  264.  
  265.         close(pipe_fd[1]);
  266.         close(fileno(stdin));
  267.         if((new_pipe_fd = dup(pipe_fd[0])) == -1) {
  268.           err = errno;
  269.           dprint(1, (debugfile, "Error: dup failed, errno %s (show_msg)\n",
  270.         error_description(err)));
  271.           error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerDup,
  272.             "Could not prepare for external pager(dup()-%s)."),
  273.         error_description(err));    
  274.           _exit(err);
  275.         }
  276.         close(pipe_fd[0]);    /* original pipe fd no longer needed */
  277.  
  278.         /* use stdio on new pipe fd */
  279.         if(fdopen(new_pipe_fd, "r") == NULL) {
  280.           err = errno;
  281.           dprint(1,
  282.         (debugfile, "Error: child fdopen failed, errno %s (show_msg)\n",
  283.         error_description(err)));
  284.           error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerChildFdopen,
  285.         "Could not prepare for external pager(child fdopen()-%s)."),
  286.         error_description(err));    
  287.           _exit(err);
  288.         }
  289.  
  290.         /* now execute pager and exit */
  291.         
  292.         /* system_call() will return user to user's normal permissions.
  293.          * This is what makes this pipe secure - user won't have elm's
  294.          * special setgid permissions (if so configured) and will only
  295.          * be able to execute a pager that user normally has permission
  296.          * to execute */
  297.  
  298.         _exit(system_call(pager, SY_ENAB_SIGINT));
  299.       
  300.       } /* else this is the parent fork */
  301.  
  302.       /* close read-only pipe fd and do write-only with stdio */
  303.       close(pipe_fd[0]);
  304.  
  305.       if((pipe_wr_fp = fdopen(pipe_fd[1], "w")) == NULL) {
  306.         err = errno;
  307.         dprint(1,
  308.           (debugfile, "Error: parent fdopen failed, errno %s (show_msg)\n",
  309.           error_description(err)));
  310.         error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerParentFdopen,
  311.           "Could not prepare for external pager(parent fdopen()-%s)."),
  312.           error_description(err));    
  313.  
  314.         /* Failure - must close pipe and wait for child */
  315.         close(pipe_fd[1]);
  316.         while ((wait_ret = wait(&wait_stat)) != fork_ret && wait_ret!= -1)
  317.           ;
  318.  
  319.         Raw(OFF);
  320.         return(val);    /* pager may have already touched the screen */
  321.       }
  322.  
  323.       /* and that's it! */
  324.       lines_displayed = 0;
  325. #ifdef    SIGTSTP
  326.       oldstop = signal(SIGTSTP,SIG_DFL);
  327.       oldcont = signal(SIGCONT,SIG_DFL);
  328. #endif 
  329.     }
  330.  
  331.     ClearScreen();
  332.  
  333.     if (cursor_control) transmit_functions(OFF);
  334.  
  335.     pipe_abort = FALSE;
  336.  
  337.     if (form_letter = (current_header->status&FORM_LETTER)) {
  338.       if (filter)
  339.         form_letter_section = 1;    /* initialize to section 1 */
  340.     }
  341.  
  342.     if (title_messages && filter) {
  343.  
  344.       using_to =
  345.         tail_of(current_header->from, who, current_header->to);
  346.  
  347.       MCsprintf(title1, "%s %d/%d  ",
  348.             headers[current-1]->status & DELETED ? nls_deleted :
  349.             form_letter ? nls_form: nls_message,
  350.             number, message_count);
  351.       MCsprintf(title2, "%s %s", using_to? nls_to : nls_from, who);
  352.       elm_date_str(title3, current_header->time_sent + current_header->tz_offset);
  353.       strcat(title3, " ");
  354.       strcat(title3, current_header->time_zone);
  355.  
  356.       /* truncate or pad title2 portion on the right
  357.        * so that line fits exactly */
  358.       padding =
  359.         COLUMNS -
  360.         (strlen(title1) + (buf_len=strlen(title2)) + strlen(title3));
  361.  
  362.       sprintf(titlebuf, "%s%-*.*s%s\n", title1, buf_len+padding,
  363.           buf_len+padding, title2, title3);
  364.  
  365.       if (builtin)
  366.         display_line(titlebuf, strlen(titlebuf));
  367.       else
  368.         fprintf(pipe_wr_fp, "%s", titlebuf);
  369.  
  370.       /** if there's a subject, let's output it next,
  371.           centered if it fits on a single line.  **/
  372.  
  373.       if ((buf_len = strlen(current_header->subject)) > 0 && 
  374.         matches_weedlist("Subject:")) {
  375.         padding = (buf_len < COLUMNS ? COLUMNS - buf_len : 0);
  376.         sprintf(buffer, "%*s%s\n", padding/2, "", current_header->subject);
  377.       } else
  378.         strcpy(buffer, "\n");
  379.  
  380.       if (builtin)
  381.         display_line(buffer, strlen(buffer));
  382.       else
  383.         fprintf(pipe_wr_fp, "%s", buffer);
  384.       
  385.       /** was this message address to us?  if not, then to whom? **/
  386.  
  387.       if (! using_to && matches_weedlist("To:") && filter &&
  388.           strcmp(current_header->to,username) != 0 &&
  389.           strlen(current_header->to) > 0) {
  390.         sprintf(buffer, catgets(elm_msg_cat, ElmSet, ElmMessageAddressedTo,
  391.           "%s(message addressed to %.60s)\n"), 
  392.               strlen(current_header->subject) > 0 ? "\n" : "",
  393.           current_header->to);
  394.         if (builtin)
  395.           display_line(buffer, strlen(buffer));
  396.         else
  397.           fprintf(pipe_wr_fp, "%s", buffer);
  398.       }
  399.     
  400.       /** The test above is: if we didn't originally send the mail
  401.           (e.g. we're not reading "mail.sent") AND the user is currently
  402.           weeding out the "To:" line (otherwise they'll get it twice!)
  403.           AND the user is actually weeding out headers AND the message 
  404.           wasn't addressed to the user AND the 'to' address is non-zero 
  405.           (consider what happens when the message doesn't HAVE a "To:" 
  406.           line...the value is NULL but it doesn't match the username 
  407.           either.  We don't want to display something ugly like 
  408.           "(message addressed to )" which will just clutter up the 
  409.           screen!).
  410.  
  411.           And you thought programming was EASY!!!!
  412.       **/
  413.  
  414.       /** one more friendly thing - output a line indicating what sort
  415.           of status the message has (e.g. Urgent etc).  Mostly added
  416.           for X.400 support, this is nonetheless generally useful to
  417.           include...
  418.       **/
  419.  
  420.       buffer[0] = '\0';
  421.  
  422.       /* we want to flag Urgent, Confidential, Private and Expired tags */
  423.  
  424.       if (current_header->status & PRIVATE)
  425.         strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedPrivate,
  426.         "\n(** This message is tagged Private"));
  427.       else if (current_header->status & CONFIDENTIAL) 
  428.         strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedCompanyConfidential,
  429.         "\n(** This message is tagged Company Confidential"));
  430.  
  431.       if (current_header->status & URGENT) {
  432.         if (buffer[0] == '\0')
  433.           strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedUrgent,
  434.         "\n(** This message is tagged Urgent"));
  435.         else if (current_header->status & EXPIRED)
  436.           strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmCommaUrgent, ", Urgent"));
  437.         else
  438.           strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmAndUrgent, " and Urgent"));
  439.       }
  440.  
  441.       if (current_header->status & EXPIRED) {
  442.         if (buffer[0] == '\0')
  443.           strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmMessageHasExpired,
  444.         "\n(** This message has Expired"));
  445.         else
  446.           strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmAndHasExpired,
  447.         ", and has Expired"));
  448.       }
  449.  
  450.       if (buffer[0] != '\0') {
  451.         strcat(buffer, " **)\n");
  452.         if (builtin)
  453.           display_line(buffer, strlen(buffer));
  454.         else
  455.           fprintf(pipe_wr_fp, buffer);
  456.       }
  457.  
  458.       if (builtin)            /* this is for a one-line blank    */
  459.         display_line("\n",1);    /*   separator between the title   */
  460.       else                /*   stuff and the actual message  */
  461.         fprintf(pipe_wr_fp, "\n");    /*   we're trying to display       */
  462.  
  463.     }
  464.  
  465.     weed_header = filter;    /* allow us to change it after header */
  466.  
  467.     while (lines > 0 && pipe_abort == FALSE) {
  468.  
  469.         if ((buf_len = mail_gets(buffer, VERY_LONG_STRING, mailfile)) == 0) {
  470.  
  471.           dprint(1, (debugfile,
  472.         "Premature end of file! Lines left = %d msg = %s (show_msg)\n",
  473.         lines, number));
  474.  
  475.           error(catgets(elm_msg_cat, ElmSet, ElmPrematureEndOfFile,
  476.         "Premature end of file!"));
  477.           if (sleepmsg > 0)
  478.             sleep(sleepmsg);
  479.           break;
  480.         }
  481.         if (buf_len > 0)  {
  482.           if(buffer[buf_len - 1] == '\n') {
  483.             lines--;
  484.             lines_displayed++;
  485.         }
  486.           while(buf_len > 0 && (buffer[buf_len - 1] == '\n'
  487.                     ||buffer[buf_len - 1] == '\r'))
  488.         --buf_len;
  489.         }
  490.  
  491.           if (buf_len == 0) {
  492.           weed_header = 0;        /* past header! */
  493.           weeding_out = 0;
  494.         }
  495.  
  496.         if (form_letter && weed_header)
  497.         /* skip it.  NEVER display random headers in forms! */;
  498.         else if (weed_header && matches_weedlist(buffer)) 
  499.           weeding_out = 1;     /* aha!  We don't want to see this! */
  500.         else if (buffer[0] == '[') {
  501.           if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0)
  502.             crypted = ON;
  503.           else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0)
  504.             crypted = OFF;
  505.           else if (crypted) {
  506.                 encode(buffer);
  507.             val = show_line(buffer, buf_len, builtin);
  508.           }
  509.           else
  510.             val = show_line(buffer, buf_len, builtin);
  511.         } 
  512.         else if (crypted) {
  513.           encode(buffer);
  514.           val = show_line(buffer, buf_len, builtin); 
  515.         }
  516.         else if (weeding_out) {
  517.           weeding_out = (whitespace(buffer[0]));    /* 'n' line weed */
  518.           if (! weeding_out)     /* just turned on! */
  519.             val = show_line(buffer, buf_len, builtin);
  520.         } 
  521.         else if (form_letter && first_word(buffer,"***") && filter) {
  522.           strcpy(buffer,
  523. "\n------------------------------------------------------------------------------\n");
  524.           val = show_line(buffer, buf_len, builtin);    /* hide '***' */
  525.           form_letter_section++;
  526.         }
  527.         else if (form_letter_section == 1 || form_letter_section == 3)
  528.           /** skip this stuff - we can't deal with it... **/;
  529.         else
  530.           val = show_line(buffer, buf_len, builtin);
  531.     
  532.         if (val != 0)    /* discontinue the display */
  533.           break;
  534.     }
  535.  
  536.         if (cursor_control) transmit_functions(ON);
  537.  
  538.     if (!builtin) {
  539.       fclose(pipe_wr_fp);
  540.       while ((wait_ret = wait(&wait_stat)) != fork_ret
  541.           && wait_ret!= -1)
  542.         ;
  543.       /* turn raw on **after** child terminates in case child
  544.        * doesn't put us back to cooked mode after we return ourselves
  545.        * to raw.
  546.        */
  547.       Raw(ON);
  548. #ifdef    SIGTSTP
  549.       (void)signal(SIGTSTP,oldstop);
  550.       (void)signal(SIGCONT,oldcont);
  551. #endif
  552.     }
  553.  
  554.     /* If we are to prompt for a user input command and we don't
  555.      * already have one */
  556.     if ((prompt_after_pager || builtin) && val == 0) {
  557.       MoveCursor(LINES,0);
  558.       StartBold();
  559.       Write_to_screen(catgets(elm_msg_cat, ElmSet, ElmCommandIToReturn,
  560.         " Command ('i' to return to index): "), 0);
  561.       EndBold();
  562.       fflush(stdout);
  563.       val = ReadCh();
  564.     }
  565.     
  566.     if (memory_lock) EndMemlock();    /* turn it off!! */
  567.  
  568.     /* 'q' means quit current operation and pop back up to previous level -
  569.      * in this case it therefore means return to index screen.
  570.      */
  571.     return(val == 'i' || val == 'q' ? 0 : val);
  572. }
  573.  
  574. int
  575. show_line(buffer, buf_len, builtin)
  576. char *buffer;
  577. int  buf_len;
  578. int  builtin;
  579. {
  580.     /** Hands the given line to the output pipe.  'builtin' is true if
  581.         we're using the builtin pager.
  582.         Return the character entered by the user to indicate
  583.         a command other than continuing with the display (only possible
  584.         with the builtin pager), otherwise 0. **/
  585.  
  586.     strcpy(buffer + buf_len, "\n");
  587.     ++buf_len;
  588. #ifdef MMDF
  589.     if (strncmp(buffer, MSG_SEPARATOR, buf_len) == 0)
  590.       return(0);    /* no reason to show the ending terminator */
  591. #endif /* MMDF */
  592.     if (builtin) {
  593.       return(display_line(buffer, buf_len));
  594.     }
  595.     errno = 0;
  596.     fprintf(pipe_wr_fp, "%s", buffer);
  597.     if (errno != 0)
  598.       dprint(1, (debugfile, "\terror %s hit!\n", error_description(errno)));
  599.     return(0);
  600. }
  601.  
  602.