mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			568 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			568 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * input.c - read and store lines of input
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1992-1997 Paul Falstad
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Paul Falstad or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Paul Falstad and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Paul Falstad and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * This file deals with input buffering, supplying characters to the
 | |
|  * history expansion code a character at a time.  Input is stored on a
 | |
|  * stack, which allows insertion of strings into the input, possibly with
 | |
|  * flags marking the end of alias expansion, with minimal copying of
 | |
|  * strings.  The same stack is used to record the fact that the input
 | |
|  * is a history or alias expansion and to store the alias while it is in use.
 | |
|  * 
 | |
|  * Input is taken either from zle, if appropriate, or read directly from
 | |
|  * the input file, or may be supplied by some other part of the shell (such
 | |
|  * as `eval' or $(...) substitution).  In the last case, it should be
 | |
|  * supplied by pushing a new level onto the stack, via inpush(input_string,
 | |
|  * flag, alias); if the current input really needs to be altered, use
 | |
|  * inputsetline(input_string, flag).  `Flag' can include or's of INP_FREE
 | |
|  * (if the input string is to be freed when used), INP_CONT (if the input
 | |
|  * is to continue onto what's already in the input queue), INP_ALIAS
 | |
|  * (push supplied alias onto stack) or INP_HIST (ditto, but used to
 | |
|  * mark history expansion).  `alias' is ignored unless INP_ALIAS or
 | |
|  * INP_HIST is supplied.  INP_ALIAS is always set if INP_HIST is.
 | |
|  * 
 | |
|  * Note that the input string is itself used as the input buffer: it is not
 | |
|  * copied, nor is it every written back to, so using a constant string
 | |
|  * should work.  Consequently, when passing areas of memory from the heap
 | |
|  * it is necessary that that heap last as long as the operation of reading
 | |
|  * the string.  After the string is read, the stack should be popped with
 | |
|  * inpop(), which effectively flushes any unread input as well as restoring
 | |
|  * the previous input state.
 | |
|  *
 | |
|  * The internal flag INP_ALCONT shows that the stack element was pushed
 | |
|  * by an alias expansion; it should not be needed elsewhere.
 | |
|  *
 | |
|  * The global variable inalmore is set to indicate aliases should
 | |
|  * continue to be expanded because the last alias expansion ended
 | |
|  * in a space.  It is only reset after a complete word was read
 | |
|  * without expanding a new alias, in exalias().
 | |
|  *
 | |
|  * PWS 1996/12/10
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_STDIO_H
 | |
| #include <stdio.h>
 | |
| #endif
 | |
| 
 | |
| #include "zsh.mdh"
 | |
| #include "input.pro"
 | |
| 
 | |
| /* the shell input fd */
 | |
| 
 | |
| /**/
 | |
| int SHIN;
 | |
| 
 | |
| /* buffered shell input for non-interactive shells */
 | |
| 
 | |
| /**/
 | |
| FILE *bshin;
 | |
| 
 | |
| /* != 0 means we are reading input from a string */
 | |
|  
 | |
| /**/
 | |
| int strin;
 | |
|  
 | |
| /* total # of characters waiting to be read. */
 | |
| 
 | |
| /**/
 | |
| mod_export int inbufct;
 | |
| 
 | |
| /* the flags controlling the input routines in input.c: see INP_* in zsh.h */
 | |
| 
 | |
| /**/
 | |
| int inbufflags;
 | |
| 
 | |
| static char *inbuf;		/* Current input buffer */
 | |
| static char *inbufptr;		/* Pointer into input buffer */
 | |
| static char *inbufpush;		/* Character at which to re-push alias */
 | |
| static int inbufleft;		/* Characters left in current input
 | |
| 				   stack element */
 | |
| 
 | |
| 
 | |
|  /* Input must be stacked since the input queue is used by
 | |
|   * various different parts of the shell.
 | |
|   */
 | |
| 
 | |
| struct instacks {
 | |
|     char *buf, *bufptr;
 | |
|     Alias alias;
 | |
|     int bufleft, bufct, flags;
 | |
| };
 | |
| static struct instacks *instack, *instacktop;
 | |
| /*
 | |
|  * Input stack size.  We need to push the stack for aliases, history
 | |
|  * expansion, and reading from internal strings: only if these operations
 | |
|  * are nested do we need more than one extra level.  Thus we shouldn't need
 | |
|  * too much space as a rule.  Initially, INSTACK_INITIAL is allocated; if
 | |
|  * more is required, an extra INSTACK_EXPAND is added each time.
 | |
|  */
 | |
| #define INSTACK_INITIAL	4
 | |
| #define INSTACK_EXPAND	4
 | |
| 
 | |
| static int instacksz = INSTACK_INITIAL;
 | |
| 
 | |
| /* Read a line from bshin.  Convert tokens and   *
 | |
|  * null characters to Meta c^32 character pairs. */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| shingetline(void)
 | |
| {
 | |
|     char *line = NULL;
 | |
|     int ll = 0;
 | |
|     int c;
 | |
|     char buf[BUFSIZ];
 | |
|     char *p;
 | |
| 
 | |
|     p = buf;
 | |
|     for (;;) {
 | |
| 	do {
 | |
| 	    errno = 0;
 | |
| 	    c = fgetc(bshin);
 | |
| 	} while (c < 0 && errno == EINTR);
 | |
| 	if (c < 0 || c == '\n') {
 | |
| 	    if (c == '\n')
 | |
| 		*p++ = '\n';
 | |
| 	    if (p > buf) {
 | |
| 		*p++ = '\0';
 | |
| 		line = zrealloc(line, ll + (p - buf));
 | |
| 		memcpy(line + ll, buf, p - buf);
 | |
| 	    }
 | |
| 	    return line;
 | |
| 	}
 | |
| 	if (imeta(c)) {
 | |
| 	    *p++ = Meta;
 | |
| 	    *p++ = c ^ 32;
 | |
| 	} else
 | |
| 	    *p++ = c;
 | |
| 	if (p >= buf + BUFSIZ - 1) {
 | |
| 	    line = zrealloc(line, ll + (p - buf) + 1);
 | |
| 	    memcpy(line + ll, buf, p - buf);
 | |
| 	    ll += p - buf;
 | |
| 	    line[ll] = '\0';
 | |
| 	    p = buf;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Get the next character from the input.
 | |
|  * Will call inputline() to get a new line where necessary.
 | |
|  */
 | |
|   
 | |
| /**/
 | |
| int
 | |
| ingetc(void)
 | |
| {
 | |
|     int lastc;
 | |
| 
 | |
|     if (lexstop)
 | |
| 	return ' ';
 | |
|     for (;;) {
 | |
| 	if (inbufleft) {
 | |
| 	    inbufleft--;
 | |
| 	    inbufct--;
 | |
| 	    if (itok(lastc = STOUC(*inbufptr++)))
 | |
| 		continue;
 | |
| 	    if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
 | |
| 		lineno++;
 | |
| 	    return lastc;
 | |
| 	}
 | |
| 
 | |
| 	/* If the next element down the input stack is a continuation of
 | |
| 	 * this, use it.
 | |
| 	 */ 
 | |
| 	if (inbufflags & INP_CONT) {
 | |
| 	    inpoptop();
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * Otherwise, see if we have reached the end of input
 | |
| 	 * (due to an error, or to reading from a single string).
 | |
| 	 */
 | |
| 	if (strin || errflag) {
 | |
| 	    lexstop = 1;
 | |
| 	    return ' ';
 | |
| 	}
 | |
| 	/* As a last resort, get some more input */
 | |
| 	if (inputline())
 | |
| 	    return ' ';
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Read a line from the current command stream and store it as input */
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| inputline(void)
 | |
| {
 | |
|     char *ingetcline, **ingetcpmptl = NULL, **ingetcpmptr = NULL;
 | |
|     int context = ZLCON_LINE_START;
 | |
| 
 | |
|     /* If reading code interactively, work out the prompts. */
 | |
|     if (interact && isset(SHINSTDIN)) {
 | |
| 	if (!isfirstln) {
 | |
| 	    ingetcpmptl = &prompt2;
 | |
| 	    if (rprompt2)
 | |
| 		ingetcpmptr = &rprompt2;
 | |
| 	    context = ZLCON_LINE_CONT;
 | |
| 	}
 | |
| 	else {
 | |
| 	    ingetcpmptl = &prompt;
 | |
| 	    if (rprompt)
 | |
| 		ingetcpmptr = &rprompt;
 | |
| 	}
 | |
|     }
 | |
|     if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) {
 | |
| 	/*
 | |
| 	 * If not using zle, read the line straight from the input file.
 | |
| 	 * Possibly we don't get the whole line at once:  in that case,
 | |
| 	 * we get another chunk with the next call to inputline().
 | |
| 	 */
 | |
| 
 | |
| 	if (interact && isset(SHINSTDIN)) {
 | |
| 	    /*
 | |
| 	     * We may still be interactive (e.g. running under emacs),
 | |
| 	     * so output a prompt if necessary.  We don't know enough
 | |
| 	     * about the input device to be able to handle an rprompt,
 | |
| 	     * though.
 | |
| 	     */
 | |
| 	    char *pptbuf;
 | |
| 	    int pptlen;
 | |
| 	    pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL,
 | |
| 					   0, NULL, NULL), &pptlen);
 | |
| 	    write(2, (WRITE_ARG_2_T)pptbuf, pptlen);
 | |
| 	    free(pptbuf);
 | |
| 	}
 | |
| 	ingetcline = shingetline();
 | |
|     } else {
 | |
| 	/*
 | |
| 	 * Since we may have to read multiple lines before getting
 | |
| 	 * a complete piece of input, we tell zle not to restore the
 | |
| 	 * original tty settings after reading each chunk.  Instead,
 | |
| 	 * this is done when the history mechanism for the current input
 | |
| 	 * terminates, which is not until we have the whole input.
 | |
| 	 * This is supposed to minimise problems on systems that clobber
 | |
| 	 * typeahead when the terminal settings are altered.
 | |
| 	 *                     pws 1998/03/12
 | |
| 	 */
 | |
| 	int flags = ZLRF_HISTORY|ZLRF_NOSETTY;
 | |
| 	if (isset(IGNOREEOF))
 | |
| 	    flags |= ZLRF_IGNOREEOF;
 | |
| 	ingetcline = zlereadptr(ingetcpmptl, ingetcpmptr, flags, context);
 | |
| 	histdone |= HISTFLAG_SETTY;
 | |
|     }
 | |
|     if (!ingetcline) {
 | |
| 	return lexstop = 1;
 | |
|     }
 | |
|     if (errflag) {
 | |
| 	free(ingetcline);
 | |
| 	return lexstop = errflag = 1;
 | |
|     }
 | |
|     if (isset(VERBOSE)) {
 | |
| 	/* Output the whole line read so far. */
 | |
| 	zputs(ingetcline, stderr);
 | |
| 	fflush(stderr);
 | |
|     }
 | |
|     if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n' &&
 | |
| 	interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
 | |
| 	SHTTY != -1 && *ingetcline && ingetcline[1] &&
 | |
| 	ingetcline[strlen(ingetcline) - 2] == '`') {
 | |
| 	/* Junk an unmatched "`" at the end of the line. */
 | |
| 	int ct;
 | |
| 	char *ptr;
 | |
| 
 | |
| 	for (ct = 0, ptr = ingetcline; *ptr; ptr++)
 | |
| 	    if (*ptr == '`')
 | |
| 		ct++;
 | |
| 	if (ct & 1) {
 | |
| 	    ptr[-2] = '\n';
 | |
| 	    ptr[-1] = '\0';
 | |
| 	}
 | |
|     }
 | |
|     isfirstch = 1;
 | |
|     /* Put this into the input channel. */
 | |
|     inputsetline(ingetcline, INP_FREE);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Put a string in the input queue:
 | |
|  * inbuf is only freeable if the flags include INP_FREE.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| inputsetline(char *str, int flags)
 | |
| {
 | |
|     if ((inbufflags & INP_FREE) && inbuf) {
 | |
| 	free(inbuf);
 | |
|     }
 | |
|     inbuf = inbufptr = str;
 | |
|     inbufleft = strlen(inbuf);
 | |
| 
 | |
|     /*
 | |
|      * inbufct must reflect the total number of characters left,
 | |
|      * as it used by other parts of the shell, so we need to take account
 | |
|      * of whether the input stack continues, and whether there
 | |
|      * is an extra space to add on at the end.
 | |
|      */
 | |
|     if (flags & INP_CONT)
 | |
| 	inbufct += inbufleft;
 | |
|     else
 | |
| 	inbufct = inbufleft;
 | |
|     inbufflags = flags;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Backup one character of the input.
 | |
|  * The last character can always be backed up, provided we didn't just
 | |
|  * expand an alias or a history reference.
 | |
|  * In fact, the character is ignored and the previous character is used.
 | |
|  * (If that's wrong, the bug is in the calling code.  Use the #ifdef DEBUG
 | |
|  * code to check.) 
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| inungetc(int c)
 | |
| {
 | |
|     if (!lexstop) {
 | |
| 	if (inbufptr != inbuf) {
 | |
| #ifdef DEBUG
 | |
| 	    /* Just for debugging: enable only if foul play suspected. */
 | |
| 	    if (inbufptr[-1] != (char) c)
 | |
| 		fprintf(stderr, "Warning: backing up wrong character.\n");
 | |
| #endif
 | |
| 	    /* Just decrement the pointer:  if it's not the same
 | |
| 	     * character being pushed back, we're in trouble anyway.
 | |
| 	     */
 | |
| 	    inbufptr--;
 | |
| 	    inbufct++;
 | |
| 	    inbufleft++;
 | |
| 	    if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
 | |
| 		lineno--;
 | |
| 	}
 | |
| #ifdef DEBUG
 | |
|         else if (!(inbufflags & INP_CONT)) {
 | |
| 	    /* Just for debugging */
 | |
| 	    fprintf(stderr, "Attempt to inungetc() at start of input.\n");
 | |
| 	}
 | |
| #endif
 | |
| 	else {
 | |
| 	    /*
 | |
| 	     * The character is being backed up from a previous input stack
 | |
| 	     * layer.  However, there was an expansion in the middle, so we
 | |
| 	     * can't back up where we want to.  Instead, we just push it
 | |
| 	     * onto the input stack as an extra character.
 | |
| 	     */
 | |
| 	    char *cback = (char *)zshcalloc(2);
 | |
| 	    cback[0] = (char) c;
 | |
| 	    inpush(cback, INP_FREE|INP_CONT, NULL);
 | |
| 	}
 | |
| 	/* If we are back at the start of a segment,
 | |
| 	 * we may need to restore an alias popped from the stack.
 | |
| 	 * Note this may be a dummy (history expansion) entry.
 | |
| 	 */
 | |
| 	if (inbufptr == inbufpush && inbufflags & INP_ALCONT) {
 | |
| 	    /*
 | |
| 	     * Go back up the stack over all entries which were alias
 | |
| 	     * expansions and were pushed with nothing remaining to read.
 | |
| 	     */
 | |
| 	    do {
 | |
| 		if (instacktop->alias)
 | |
| 		    instacktop->alias->inuse = 1;
 | |
| 		instacktop++;
 | |
| 	    } while ((instacktop->flags & INP_ALCONT) && !instacktop->bufleft);
 | |
| 	    inbufflags = INP_CONT|INP_ALIAS;
 | |
| 	    inbufleft = 0;
 | |
| 	    inbuf = inbufptr = "";
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* stuff a whole file into the input queue and print it */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| stuff(char *fn)
 | |
| {
 | |
|     FILE *in;
 | |
|     char *buf;
 | |
|     off_t len;
 | |
| 
 | |
|     if (!(in = fopen(unmeta(fn), "r"))) {
 | |
| 	zerr("can't open %s", fn);
 | |
| 	return 1;
 | |
|     }
 | |
|     fseek(in, 0, 2);
 | |
|     len = ftell(in);
 | |
|     fseek(in, 0, 0);
 | |
|     buf = (char *)zalloc(len + 1);
 | |
|     if (!(fread(buf, len, 1, in))) {
 | |
| 	zerr("read error on %s", fn);
 | |
| 	fclose(in);
 | |
| 	zfree(buf, len + 1);
 | |
| 	return 1;
 | |
|     }
 | |
|     fclose(in);
 | |
|     buf[len] = '\0';
 | |
|     fwrite(buf, len, 1, stderr);
 | |
|     fflush(stderr);
 | |
|     inputsetline(metafy(buf, len, META_REALLOC), INP_FREE);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* flush input queue */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| inerrflush(void)
 | |
| {
 | |
|     while (!lexstop && inbufct)
 | |
| 	ingetc();
 | |
| }
 | |
| 
 | |
| /* Set some new input onto a new element of the input stack */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| inpush(char *str, int flags, Alias inalias)
 | |
| {
 | |
|     if (!instack) {
 | |
| 	/* Initial stack allocation */
 | |
| 	instack = (struct instacks *)zalloc(instacksz*sizeof(struct instacks));
 | |
| 	instacktop = instack;
 | |
|     }
 | |
| 
 | |
|     instacktop->buf = inbuf;
 | |
|     instacktop->bufptr = inbufptr;
 | |
|     instacktop->bufleft = inbufleft;
 | |
|     instacktop->bufct = inbufct;
 | |
|     inbufflags &= ~INP_ALCONT;
 | |
|     if (flags & (INP_ALIAS|INP_HIST)) {
 | |
| 	/*
 | |
| 	 * Text is expansion for history or alias, so continue
 | |
| 	 * back to old level when done.  Also mark stack top
 | |
| 	 * as alias continuation so as to back up if necessary,
 | |
| 	 * and mark alias as in use.
 | |
| 	 */
 | |
| 	flags |= INP_CONT|INP_ALIAS;
 | |
| 	instacktop->flags = inbufflags | INP_ALCONT;
 | |
| 	if ((instacktop->alias = inalias))
 | |
| 	    inalias->inuse = 1;
 | |
|     } else {
 | |
| 	/* If we are continuing an alias expansion, record the alias
 | |
| 	 * expansion in new set of flags (do we need this?)
 | |
| 	 */
 | |
| 	if (((instacktop->flags = inbufflags) & INP_ALIAS) &&
 | |
| 	    (flags & INP_CONT))
 | |
| 	    flags |= INP_ALIAS;
 | |
|     }
 | |
| 
 | |
|     instacktop++;
 | |
|     if (instacktop == instack + instacksz) {
 | |
| 	/* Expand the stack */
 | |
| 	instack = (struct instacks *)
 | |
| 	    realloc(instack,
 | |
| 		    (instacksz + INSTACK_EXPAND)*sizeof(struct instacks));
 | |
| 	instacktop = instack + instacksz;
 | |
| 	instacksz += INSTACK_EXPAND;
 | |
|     }
 | |
|     /*
 | |
|      * We maintain the entry above the highest one with real
 | |
|      * text as a flag to inungetc() that it can stop re-pushing the stack.
 | |
|      */
 | |
|     instacktop->flags = 0;
 | |
| 
 | |
|     inbufpush = inbuf = NULL;
 | |
| 
 | |
|     inputsetline(str, flags);
 | |
| }
 | |
| 
 | |
| /* Remove the top element of the stack */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| inpoptop(void)
 | |
| {
 | |
|     if (inbuf && (inbufflags & INP_FREE))
 | |
| 	free(inbuf);
 | |
| 
 | |
|     instacktop--;
 | |
| 
 | |
|     inbuf = instacktop->buf;
 | |
|     inbufptr = inbufpush = instacktop->bufptr;
 | |
|     inbufleft = instacktop->bufleft;
 | |
|     inbufct = instacktop->bufct;
 | |
|     inbufflags = instacktop->flags;
 | |
| 
 | |
|     if (!(inbufflags & INP_ALCONT))
 | |
| 	return;
 | |
| 
 | |
|     if (instacktop->alias) {
 | |
| 	char *t = instacktop->alias->text;
 | |
| 	/* a real alias:  mark it as unused. */
 | |
| 	instacktop->alias->inuse = 0;
 | |
| 	if (*t && t[strlen(t) - 1] == ' ') {
 | |
| 	    inalmore = 1;
 | |
| 	    histbackword();
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Remove the top element of the stack and all its continuations. */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| inpop(void)
 | |
| {
 | |
|     int remcont;
 | |
| 
 | |
|     do {
 | |
| 	remcont = inbufflags & INP_CONT;
 | |
| 
 | |
| 	inpoptop();
 | |
|     } while (remcont);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Expunge any aliases from the input stack; they shouldn't appear
 | |
|  * in the history and need to be flushed explicitly when we encounter
 | |
|  * an error.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| inpopalias(void)
 | |
| {
 | |
|     while (inbufflags & INP_ALIAS)
 | |
| 	inpoptop();
 | |
| }
 |