[comp.emacs] tenexbuf

neitzel@infbs (01/08/88)

This is tenexbuf(), a function for MicroEmacs to allow input of a
buffer name using simple TENEX-style expansion.  I put it in input.c
right behind getname(), which served as model for it.

Together with my changes to make the bufferlist Least-Recently-Used-
orded (shall I post these, too?), MicroEmacs now behaves a lot more
like the big one.  Perhaps we should rename it to 'MilliEmacs' :-)

-----
			Martin Neitzel
			UUCP: ..!uunet!mcvax!unido!infbs!neitzel
(this is cheaper! -->)	BITNET: neitzel@dbsinf6.bitnet



---- application example --------------------------------------------

Use it where you like, I put it into usebuffer(), nextbuffer() and
killbuffer().  For an example, here is what my usebuffer() now looks
like.

usebuffer(f, n)
{
	register BUFFER *bp, *lru_buffer;
	extern BUFFER *tenexbuf();

	/* get the least recently used, yet undisplayed, non-hidden buffer */
	lru_buffer = [... 4 lines to find the LRU buffer ...];
	bp = tenexbuf (TRUE, "use buffer ",
		       lru_buffer ? lru_buffer->b_bname : NULL);
	if (!bp)
		return (ABORT);
	else
		return swbuffer(bp);
}



---- tenexbuf() ---- put into input.c -----------------------------------

/*
 * Read in the name of an buffer using TENEX style expansion and
 * return the pointer to it.  If argument 'createf' is non-zero, a new
 * buffer will be created, if requested.  'prompt' can be NULL, in
 * which case neither a prompt or the default is printed in the
 * minibuffer.  'def_name' may be a default buffer name or NULL.  If
 * 'prompt' is present, it will be printed in the minibuffer and can
 * be chosen by simply hitting RETURN.
 *
 * returns NULL, iff the user ABORTs or can't / isn't allowed to
 * create a new buffer.
 */

BUFFER *tenexbuf(createf, prompt, def_bname)
int createf;
char *prompt, *def_bname;
{
#if	ST520 & LATTICE
#define register		
#endif
	register int cpos;	/* current column on screen output */
	register int c;
	register char *sp;	/* pointer to string for output */
	char bufname[NBUFN];	/* buffer to hold tentative buffer name */
	extern BUFFER *bfind();

	/* starting at the beginning of the string buffer */
	cpos = 0;

	/* if we are executing a command line get the next arg and match it */
	if (clexec) {
		if (macarg(bufname) != TRUE)
			return(NULL);
		return bfind(&bufname[0], createf, 0);
	}

	if (prompt)
		mlwrite ("%s [%s] ", prompt,
			 def_bname ? def_bname : "no default");

	/* build a name string from the keyboard */
	while (TRUE) {
		c = tgetc();

		/* if we are at the end, just match it */
		if (c == '\n'  ||  c == '\r') {
			if (def_bname && cpos==0)
				return bfind (def_bname, createf, 0);
			else {
				bufname[cpos] = 0;
				return bfind (&bufname[0], createf, 0);
			}

		} else if (c == ectoc(abortc)) {	/* Bell, abort */
			ctrlg(FALSE, 0);
			TTflush();
			return NULL;

		} else if (c == 0x7F || c == 0x08) {	/* rubout/erase */
			if (cpos != 0) {
				TTputc('\b');
				TTputc(' ');
				TTputc('\b');
				--ttcol;
				--cpos;
				TTflush();
			}

		} else if (c == 0x15) {	/* C-U, kill */
			while (cpos != 0) {
				TTputc('\b');
				TTputc(' ');
				TTputc('\b');
				--cpos;
				--ttcol;
			}

			TTflush();

		} else if (c == ' ') {		/* attempt a completion */

			BUFFER *bp, *expbuf;
			int exp_pos, i;
			
			bufname[cpos] = 0;	/* terminate it for us */
			/*
			 * Search for the next common expansion.  expbuf points
			 * to the first matching buffer, exp_pos gets
			 * initialized to the full buffer name.  Each further
			 * matching buffer will shrink (via exp_pos) the most
			 * common name prefix.
			 */
			for (expbuf=NULL, exp_pos=0, bp = bheadp;
			     bp;
			     bp = bp->b_bufp) {
				if ( strlen (bp->b_bname) > cpos+1  &&
				     0==strncmp (bufname, bp->b_bname, cpos)) {
					/*
					 * If you want a more
					 * 'agressive' completion, you
					 * could count the matches
					 * right here after this
					 * comment.  Then, if we found
					 * exactly one matching
					 * buffer, we could directly
					 * switch to it (expbuf).
					 */
					if ( ! expbuf ) {
						/* install first match */
						expbuf = bp;
						exp_pos = strlen
							(expbuf->b_bname) -1;
						continue;
					}
					/* another match; reduce prefix size */
					i=cpos;
					while (i<=exp_pos)
						if (bp->b_bname[i] !=
						    expbuf->b_bname[i])
							break;
						else
							++i;
					exp_pos = i-1;
				}
			}

			if (!expbuf) {
				TTbeep();
				TTflush();
				continue;
			}
			
			while (cpos <= exp_pos) {
				/* add the next char in */
				bufname[cpos] = expbuf->b_bname[cpos];
				TTputc(bufname[cpos++]);
			}
			TTflush();
		} else {
			if (cpos < NBUFN-1 && c > ' ') {
				bufname[cpos++] = c;
				TTputc(c);
			}

			++ttcol;
			TTflush();
		}
	}
}