/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 *
 * The following software modules were created and are Copyrighted by
 * National Semiconductor Corporation:
 *
 *  1. regerr:
 *  2. compile_pattern:
 *  3. match: and
 *  4. update_times.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */

#define	PARSER

#include "global.h"
#include "xmailregexp.h"
#include <ctype.h>
#include <utime.h>
#include "MailwatchP.h"

#if defined(sun) && defined(SVR4)
#define USE_REGEXP
#endif

extern	void	reset_mailbox(Widget gw, int down);
static	char	MailPrompt[80];		/* overkill on size, but to be safe */

#if defined(USE_REGEXP)
#define	INIT		register char *sp=instring; int sed=1;
#define	GETC()		(*sp++)
#define	PEEKC()		(*sp)
#define	UNGETC(c)	(--sp)
#define	RETURN(c)	return
#define	ERROR(c)	regerr(c)

#include <regexp.h>

int	regexp_indx;

static char *regexp_error[] = {
          "","","","","","","","","","","",
 /* 11 */ "Range endpoint too large", "","","","",
 /* 16 */ "Bad number", "","","","","","","","",
 /* 25 */ "``\\digit'' out of range", "","","","","","","","","","",
 /* 36 */ "Illegal or missing delimiter", "","","","",
 /* 41 */ "No remembered search string",
 /* 42 */ "\\(\\) imbalance",
 /* 43 */ "Too many \\(",
 /* 44 */ "More than 2 numbers given in \\{\\}",
 /* 45 */ "} expected after \\",
 /* 46 */ "First number exceeds second in \\{\\}", "", "",
 /* 49 */ "[] imbalance",
 /* 50 */ "Regular expression too long",
 NULL
};


regerr(c)
int	c;
{
 (void) fprintf(stderr, "xmail: %s (item %d).\n", regexp_error[c], regexp_indx);
 exit(0);
}


/*
**  @(#)compile_pattern() - compile regular expression patterns in a table.
**  A pattern table is an array of pattern records.  Each pattern record
**  consists of a regular expression, and a buffer for the compiled expression.
*/
void
compile_pattern(patternTable)
PatternRecPtr	patternTable;
{
 String		bp;
 PatternRecPtr	cp;


 for (regexp_indx=0, cp=patternTable; cp->pat||cp->buf; regexp_indx++, cp++) {
     if (! cp->pat) continue;		/* skip over grouping separations */
     bp = (String) XtMalloc((unsigned) BUFSIZ);
     (void) compile(cp->pat, bp, &bp[BUFSIZ], '\0');
     cp->buf = bp;
    }
}
#endif


/*
** @(#)match() - string against regular expressions in pattern table.
*/
int
match(PatternRecPtr patternTable, char *string)
{
 PatternRecPtr	cp;
 char		*bp;
 int		n, id;


 if (strcmp(string, "") == 0) return -1;

 for (id = 0, cp = patternTable; cp->pat || cp->buf; cp++) {
     if (! cp->pat) { id++; continue; }

#if defined(USE_REGEXP)
     if (advance(string, cp->buf))
        return (id);
#else
     if (bp = (char *)re_comp(cp->pat)) {
        (void) fprintf(stderr, "xmail: re_comp: %s\n", bp);
        return -1;
       } else {
        if ((n = re_exec(string)) == 1)
           return (id);
        else if (n < 0) {
                (void) fprintf(stderr, "xmail: re_exec: internal error\n");
                return -1;
               }
       }
#endif
    }
 return (-1);
}


/*
** @(#)parser_init()- compile command and output pattern tables.
*/
void
parser_init(void)
{
#if defined(USE_REGEXP)
 (void) compile_pattern(command_pattern);
 (void) compile_pattern(output_pattern);
#else
 ;
#endif
}

/*
** @(#)update_times - update previous folder's timestamps, so SetNewness()
**                    will restore no unread message flag for that folder.
*/
static
update_times(void)
{
 MailwatchWidget mb = (MailwatchWidget) XtNameToWidget(toplevel,"icon.mailbox");
 char		*p, *q, tmp[BUFSIZ];

/*
** we need to strip the foldername from the previous title-bar contents
*/
 p = strrchr(lastFolder, '"');
 q = strchr(lastFolder, '"');
 if (p && q && p != q) {
    *p = '\0';
    if (strcmp(++q, mb->mailbox.filename) != 0) { /* not system folder */
       if (*q == '+') {			/* the majority of the times */
          initfoldir();
          (void) sprintf(tmp, "%s%s", foldir, ++q);
          q = tmp;
         }
       (void) utime(q, (struct utimbuf *) 0);
      }
   }

} /* end - update_times */


/*
** @(#)Reissue_Is_Valid - true/false reissue of Copy/Save to author was valid
*/
int
Reissue_Is_Valid(void)
{
 int	n;
 String	p, h, strchr(const char *, int), strrchr(const char *, int);
 char	buf[BUFSIZ], tmp[BUFSIZ];


 if (Command[0] != 'C' && Command[0] != 'S')
    return(False);

 SetCursor(WATCH);
 if (1 == sscanf(Command, "%*s %d", &n)) {
    Command[0] = (Command[0] == 'C' ? 'c' : 's');
    LASTCH(Command) = ' ';
   } else {
 n = SelectionNumber(False);
    sprintf(buf, "%c %d ", Command[0] == 'C' ? 'c' : 's', n);
 (void) strcpy(Command, buf);
   }

 (void) sprintf(buf, "f %d", n);
 p = QueryMail(buf);

 buf[0] = '\0';
 if (1 != sscanf(p, "> %*d %s", buf))
    (void) sscanf(p, " %*d %s", buf);

 if (strchr(buf, '<')) {
    if (! sscanf(buf, "%*[^<]<%[^>]>", tmp))
       (void) sscanf(buf, "<%[^>]>", tmp);
    (void) strcpy(buf, tmp);
   } else
 if (strchr(buf, '(')) {
    for (h = buf; *h && strchr(" \t", *h); h++);
    if (1 != sscanf(h, "%*[^)]) %s", tmp))
       (void) sscanf(h, "%s", tmp);
    (void) strcpy(buf, tmp);
   }
 if ((int)strlen(buf)) {
    if (h = strrchr(buf, '!'))		/* UUCP - host![host!]user[@host]... */
       bcopy(h + 1, buf, (int)strlen(h));

       if (h = strchr(buf, '@')) {	/* ARPA address - user[%host]@host */
          *h = '\0';
          if (h = strchr(buf, '%'))
             *h = '\0';
          if (h = strrchr(buf, ':'))	/* DECNet form - host::user%host@host */
             bcopy(h + 1, buf, (int)strlen(h));
          if (strchr("'\"", buf[0]))	/* or - ["']user[@host]["']%host@host */
             bcopy(buf + 1, buf, (int)strlen(buf));
         }
   }

 if (h = GetMailEnv("outfolder")) {
    XtFree((String) h);
    (void) strcat(Command, "+");
   }
 (void) strcat(Command, buf);
 (void) strcat(Command, "\n");
 writeMail(Command);
 return (True);
} /* end - Reissue_Is_Valid */

#include "unset.h"

char *checkOptions( char *optionStr ) 
{
	char *holdPtr, *linePtr, *eolPtr;
	int	x, y;
	static char optionsToUnset[128];
	
	/* handle a NULL optionStr by returning NULL */
	if (optionStr == NULL) return( NULL );

	holdPtr = optionStr;
	while (holdPtr) {

		/* get a line, by getting the 
		 * address of the carriage return 
		 */
		linePtr = holdPtr;
		eolPtr = strchr(holdPtr,(int)'\n');

		/* advance our holdPtr to one character after
		 * the carriage return 	
		 */
		if (eolPtr) 
			holdPtr = eolPtr + 1;
		else {

			/* if its NULL, then its got to be the last line in the 
			 * string and we'll set holdPtr to be NULL to break
			 * out of the loop 
			 */

			 eolPtr = strchr(holdPtr,(int)'\0');
			 /* Whoa! What's going on here .. its got to be garbage to 
			  * get out of the loop
			  */
			 if (!eolPtr) break;
			 holdPtr = NULL;
		}

 
                /* this is an optimization to get out of checking the
                 * options if we've already found them all
                 */
                for (x = 0, y = numOptions ; x < numOptions; x++ ) {
                        if (!options[x].found) {
                            if (strncmp( linePtr, options[x].name, options[x].len) == 0) {
                                options[x].found = TRUE;
                                y--;
                                break;
                            }
                        }
                } /* end for */
                if (!y) break;

	} /* end while */

	/* now we'll build a string of the options to unset comprised
         * of the ones we found above 
	 */

	holdPtr = optionsToUnset;
	for (x = 0 ; x < numOptions; x++ ) {
		if ( options[x].found ) {
		 	sprintf( holdPtr, " %s", options[x].name );
			holdPtr += options[x].len + 1;
			*holdPtr = '\0';
		}
	}

	/* if you didn't find any, then return NULL */
	if (holdPtr == optionsToUnset) return(NULL);
	
	return( optionsToUnset );
}
			
/*
** @(#)parse() - command and output and call appropriate handler
*/
void
parse(String msg)
{
 static	Boolean	first_time = True;
 int		j, k, msgnum;
 String		c, s;
 char		tmp[128], *toUnset, *endOfLine;
 Widget		button, icon, iw;


 j = match(command_pattern, Command);
 switch (j) {
    /*
    ** If error on startup, mail has terminated connection.  Remove our input
    ** handler, close the file from our end, and indicate that no connection
    ** exists.  Otherwise, establish conversation requirements and display the
    ** current message.  If *Show_Last: resource is NOT False, show latest, if
    ** none are newer.  We delay setting the icon event handler until now, to
    ** ensure mail startup completed before being interrupted by a map event,
    ** for the case where we have been started iconic.
    */
    case XM_C_START : /*
                   ** test to ensure we are not running binmail by mistake
                   */
		   if (((int)strlen(msg) >= 17) &&
		      (! strcmp("unknown option N\n", &msg[(int)strlen(msg)-17]))) {
		      Bell(msg);
                      XCloseDisplay(XtDisplay(toplevel));
                      (void) fprintf(stderr,
      "\007xmail: The specified (or default) mail handler is not supported.\n");
                      _exit(0);
                      }

                    if (XM_O_BELL == match(output_pattern, msg)) {
                       if (mailpid) {
                          XtRemoveInput(mailInputId);
                          (void) close(mail_fd);
                          mailpid = 0;
                          MailPrompt[0] = '\0';
                         }

		       if (strncmp(msg, "No mail for ", 12) == 0)
			  Bell("No mail in your system mailbox\n");
		       else Bell(msg);
		       (void) UpdateTitleBar("No current folder");
		       *msg = '\0';
                      } else {
                       /*
                       ** To prevent a race condition (?) when starting the
                       ** application iconic, (which would cause this loop to
                       ** repeat multiple times), test the value of the mail
                       ** environment variable ``screen''.  If its value is
                       ** 10,000 then we're done.
                       */
                       if (c = GetMailEnv("screen")) {
                          j = (strcmp("10000", c) != 0) ? True : False;
                          XtFree((String) c);
                         } else j = True;

                       if (j) {			/* if not yet set to 10000... */
                          (void) strcpy(tmp, "set screen=10000 toplines=100 ");

                          /* default action for xmail is hold (ala mailtool) */

                          if (c = GetMailrc("nohold")) XtFree((String) c);
                          else (void) strcat(tmp, "hold");

                          msg = QueryMail(tmp);

			  /* let's see what options are set. If we
			   * find crt, replyall or cmd in the list, then
			   * we will unset them
			   */

			  *msg = '\0';
			  (void) strcpy(tmp, "set");
		   	  msg = QueryMail(tmp);

			  toUnset = checkOptions( msg );

			  if (toUnset) {
				sprintf(tmp,"unset %s",toUnset );
                                msg = QueryMail(tmp);
			  }

		          *msg = '\0';

                          if (msgnum = file_handler()) {
                             (void) sprintf(tmp, "%d", msgnum);
                             msg = QueryMail(tmp);
                            }
                          Bell("");		/* reset any worthy-ness flag */
                          Bell(Default_Status_Info);
                         } else *msg = '\0';
                      }
                    if (first_time) {		/* only need to do this once */
                       Waiting = FALSE;
                       first_time = False;
                       icon = XtNameToWidget(toplevel, "icon");
                       XtAddEventHandler(icon, StructureNotifyMask, False,
                                         icon_handler, (XtPointer)NULL);
                       if (In_Bogus_Mail_File) {
                          (void) sprintf(tmp, "%s+", tmpName);
                          (void) unlink(tmp);
		          *msg = '\0';		/* ignore 'skipping' message */
                         }
                      }
	            break;
/*
** In response to normal or change folder commands, test output.  If an
** error, display message in status window, and ring bell unless command
** was save, Save to author, or undelete.  If Save or Copy results in an
** error, and the error response begins with 'Unknown', then this is not
** Sun mail, and we should try and compensate for these missing functions.
** If our current mail message folder is the bogus folder, erase any text
** and index and note state.  If we have text of next mail message, tell
** index_handler to mark its number, else retrieve the appropriate message
** text (which also marks its number).  If *Show_Last: resource is NOT False,
** file cmd shows latest, if none new.
*/
    case XM_C_EXEC  :
    case XM_C_FILE  :
		    /* This crass hack speeds up the error message search
		     * by taking advantage of the observation that the
		     * error messages occur on the first line of output.
		     * So we put in a temporary null to prevent the FSM
		     * from searching the whole message.
		     */
		    if (endOfLine = strchr(msg, '\n'))
		      {
			*endOfLine = 0;
			j = match(output_pattern, msg);
			*endOfLine = '\n';
		      }
		    else
		      j = match(output_pattern, msg);

		    switch (j) {
		       case XM_O_BELL:
		                    if (strchr("SsCcw", Command[0]) != NULL) {
		                       if (strncmp(msg, "Unknown ", 8) == 0 &&
                                          Reissue_Is_Valid()) {
		                          *msg = '\0';
                                          break;
                                         }
		                       if (isdigit(msg[(int)strlen(msg) - 2]))
		                          LASTCH(msg) = '\0';
		                       else Command[0] = '\0';
		                      }
				    if (strncmp(msg, tmpName, (int)strlen(tmpName)) == 0)
				       Bell("No mail in your system mailbox");
				    else {
                                       Bell(msg);
		                       *msg = '\0';
                                      }
/*
** Save our current message number, because asking about our 'file' status
** will reset mail's idea of the 'current message' count to one, regardless.
*/
                                    msgnum = SelectionNumber(False);
/*
** Now ask for 'file' status to determine if we are still caught in our 'bogus'
** mailfile, in order to generate a more meaningful title-bar status message.
*/
                                    msg = QueryMail("file");
                                    if (strncmp(&msg[1], tmpName, (int)strlen(tmpName)) == 0) {
		                       (void) UpdateTitleBar("No current folder");
                                       iw = XtNameToWidget(toplevel, "topBox");
                                       writeTo(XtNameToWidget(iw, "indexWindow"), " ", REPLACE);
                                       writeText(" ");
                                      } else {
                                       (void) UpdateTitleBar(msg);
/*
** If not in our bogus mail folder, reset the current message
** number in Mail by again pointing at that numbered message.
*/
                                       (void) sprintf(tmp, "f %d", msgnum);
                                       msg = QueryMail(tmp);
                                       if (Command[0] != '\0')
                                          if (strchr("Ssw", Command[0]))
                                             markIndex(">S");
                                      }
                                    *msg = '\0';
                                    break;
		       case XM_O_EXEC: writeText(msg);
		                    if (strchr("-np", Command[0]))
		                       msgnum = index_handler(0, 0);
                                    else markIndex("> ");
                                    *msg = '\0';
                                    break;
		       case XM_O_FILE: c = strchr(msg, '\n');
		                    if ((int)(c - msg) < (int)strlen(msg) - 1) {
                                       *c = '\0';	/* don't bell this */
				       Bell("");	/* clear worthy flag */
				       Bell(msg);
		                      }
                                    *msg = '\0';
		                    msgnum = file_handler();
		                    update_times();	/* now that we're out */
                                    if (msgnum) {
                                       (void) sprintf(tmp, "%d", msgnum);
                                       msg = QueryMail(tmp);
                                      }
		                    break;
	              }
                    break;
/*
** When deleting a message, simply mark that index line with a capital 'D'.
** If un-deleting, mark that index line with a blank ' '.  If no current
** index, restore it.  If autoprinting, mail will include text of next msg.
*/
    case XM_C_DELETE : if (XM_O_BELL == match(output_pattern, msg)) {
                       Bell(msg);
                       *msg = '\0';
                      } else {
                       writeText(msg);
                       msgnum = SelectionNumber(False);
                       if (Command[0] == 'd') {
                          if (msgnum) {
                             markIndex("D");
		             msgnum = index_handler(msgnum + 1, 0);
                            }
                         } else {		/* if we are 'undeleting' */
		          c = QueryMail("=");
		          (void) sscanf(c, "%d", &msgnum);
                          iw = XtNameToWidget(toplevel, "topBox.indexWindow");
                          if (TextGetLastPos(iw) < (XawTextPosition) 4) {
                             c = QueryMail("h");
                             writeTo(iw, c, REPLACE);
                            }
		          msgnum = index_handler(msgnum, 1);
                         }
                       c = QueryMail("file");	/* resets current msg to 1 */
                       (void) UpdateTitleBar(c);

                       if (msgnum == 0)		/* if this was the last msg */
                          writeText(" ");	/* in folder, erase its text */
                       else {
                          /*
                          ** reset mail's idea of what is the current message
                          */
                          (void) sprintf(tmp, "f %d", msgnum);
                          msg = QueryMail(tmp);
                         }
                       *msg = '\0';
                      }
		    break;
/*
** In response to a request to view new mail, first reset the mailbox flag.
** If mail came back with an error, complain to the user.  Otherwise, clear
** the Newmail command button highlighting. Then display any new mail text.
*/
    case XM_C_NEWMAIL:
		    button = XtNameToWidget(toplevel, "topBox.commandPanel.Folder");
		    UnsetNewmail(button, NULL, NULL);
                    if (XM_O_BELL == match(output_pattern, msg)) {
                       if (strncmp(msg, "No mail for ", 12) == 0) {
                          Bell("No mail in your system mailbox\n");
                         } else { Bell(msg); }
                       *msg = '\0';
                      } else {
                       Bell("");		/* reset any worthy-ness flag */
                       Bell(Default_Status_Info);
		       if (*msg) {
		          s = msg;
		          c = strchr(msg, '\n');
		          if ((c - s) + 1 < (int)strlen(msg)) {
                             *c = '\0';	/* don't bell this */
			     Bell(msg);
                            }
                         }
                       msgnum = file_handler();
		       update_times();		/* we're out of prev. folder */
                       *msg = '\0';
                       if (msgnum) {
                          (void) sprintf(tmp, "%d", msgnum);
                          msg = QueryMail(tmp);
                         }
                      }
		    break;

           default: j = match(output_pattern, msg);
		    switch (j) {
                       case XM_O_BELL:
                            Bell(msg);
                            *msg = '\0';
		            break;
/*
** If output is from the print command, display a status message
*/
		       case XM_O_PRINT:
                            (void) sscanf(&Command[2], "%d", &j);
                            c = strrchr(msg, '/');
                            (void) sscanf(c, "/%d", &k);
                            (void) sprintf(tmp,
                             "Message %d sent to printer -- %d bytes\n", j, k);
                            Bell(tmp);
                            *msg = '\0';
		            break;
/*
** If we didn't specifically ask for it, and its not an error, just toss it.
** Preserve post processing moved here, in case we weren't allowed to do it.
*/
                       default:
                            if (strncmp(Command, "preserve", 8) == 0)
                               markIndex("P");
                            *msg = '\0';
		            break;
                      }
		    break;
   }
/*
** If any text remains, display it in the bottom (text) window.
*/
 if (*msg) {
    for (j = (int)strlen(msg)-1; j > 1 && msg[j] == '\n' && msg[j-1] == '\n'; j--);
    msg[++j] = '\0';			/* drop all but the last newline */
    writeText(msg);
    *msg = '\0';
   }
 SetCursor(NORMAL);
} /* parse */

#define	FILEBUF	8192

/*
** @(#)QueryMail() - Sends a command and returns corresponding output.
** If called by the Xt input procedure readMail, no command is included.
** In an attempt to eliminate the occasional segmentation violation in
** either malloc or free, we no longer free this buffer each time it is
** used.  Instead we simply allow it to grow to the maximum size of a
** read ever performed on the mail pipe.
*/
char *
QueryMail(char *cmd)
{
 static int	outputsize = 0;
 int		j, size, length = 0;
 static String	output;
 String		temp;
 char		s[FILEBUF];
 char		*endOfLine;


 if (MailPrompt[0] != '\0') length = (int)strlen(MailPrompt);
/*
** allocate one block to 'output' to begin with
*/
 if (outputsize <= 0) {
    outputsize = FILEBUF;
    output = (String) XtMalloc((unsigned) outputsize);
   }
 output[0] = '\0';

 if (! mailpid)
    Bell("No current mail program connection\n");
 else {
    if (*cmd) {				/* allow us to be called by readMail */
       if (LASTCH(cmd) != '\n')
          (void) sprintf(s, "%s\n", cmd);
       else
          (void) strcpy(s, cmd);
       writeMail(s);
      }

    for (;;) {
        if ((size = read(mail_fd, s, FILEBUF)) < 1) {	/* EOF or an error? */
           if (! *output) {		/* For some reason Mail has gone away */
              (void) strcpy(output, "No current mail service connection\n");
              if (mailpid) {
                 XtRemoveInput(mailInputId);
                 (void) close(mail_fd);
                 mailpid = 0;
                 MailPrompt[0] = '\0';
                 (void) strcpy(Command, "Start");	/* Focus our errors */
                 writeText(" ");	/* erase any current message display */
                 writeTo(XtNameToWidget(toplevel, "topBox.indexWindow"), " ", REPLACE);
                }
             }
           break;
          }

        if (size < FILEBUF)
           s[size] = '\0';

        if ((int)strlen(output) + size >= outputsize) {
           outputsize += FILEBUF;
           temp = (String) XtRealloc(output, outputsize);
           output = temp;
          }
        (void) strcat(output, s);

        if (strcmp(Command, "Start") == 0) {
           /*
           ** Check for no message response before we try to catch the prompt.
           */
	   /* Once again, the crass hack for grepping just the first line. */
	   if (endOfLine = strchr(output,'\n'))
	     {
	       *endOfLine = 0;
	       j = match(output_pattern, output);
	       *endOfLine = '\n';
	     }
	   else
	     j = match(output_pattern, output);

	 if (XM_O_BELL == j)
	     break;

           /*
           ** If we have no prompt and are just starting, get the prompt string.
           ** This makes the GROSS assumption that we will have gotten it here.
           */
           if (*output && ! *MailPrompt) {
              if ((temp = strrchr(output, '\n')) != NULL)
                 (void) strcpy(MailPrompt, temp + 1);
              else
                 (void) strcpy(MailPrompt, output);
              length = (int)strlen(MailPrompt);
             }
          }

        j = (int)strlen(output) - length;
        if (j < 0) j = 0;		/* no references before zero */
        if (*MailPrompt && strcmp(&output[j], MailPrompt) == 0) {
           output[j] = '\0';		/* Drop prompt from end of text */
           break;			/* and return from read routine */
          }
       }

    if (*cmd) 
       LASTCH(output) = '\0';		/* drop newline for normal queries */
   }

 return(output);
} /* QueryMail */
