commit 4cd0864

shrub  ·  2026-07-01 19:41:54 +0000 UTC
parent 4cd0864
initial commit
38 files changed,  +4605, -0
A NOTICE
A README
A ansi.c
A ansi.h
A help.c
A help.h
A keys.c
A keys.h
A main.c
A main.h
A rune.c
A term.c
A term.h
A utf.h
+8, -0
1@@ -0,0 +1,8 @@
2+AlignEscapedNewlines: DontAlign
3+AlwaysBreakAfterDefinitionReturnType: All
4+BreakBeforeBraces: Linux
5+ColumnLimit: 0
6+IndentWidth: 8
7+SortIncludes: false
8+TabWidth: 8
9+UseTab: ForIndentation
A NOTICE
+21, -0
 1@@ -0,0 +1,21 @@
 2+$Header: NOTICE,v 6.2 88/04/29 11:43:22 ceriel Exp $
 3+
 4+The software and documentation contained within this
 5+directory is copyright (c) Ceriel J.H. Jacobs, 1988.
 6+
 7+Permission is granted to reproduce and distribute
 8+this software as long as no fee is charged and
 9+this notice is included.
10+
11+Other rights are reserved except as explicitly
12+granted by written permission from the author.
13+
14+		Ceriel J.H. Jacobs
15+		Dept. of Maths and Computer Science
16+		Vrije Universiteit
17+		De Boelelaan 1081
18+		1081 HV	 Amsterdam
19+		The Netherlands
20+    email :
21+	ceriel@cs.vu.nl
22+
A README
+62, -0
 1@@ -0,0 +1,62 @@
 2+yap
 3+===
 4+
 5+this is yap, or yet another pager. it's really old, from minix 2.0, i 
 6+believe. i've modernized it, removed legacy compatibilty codepaths to ancient systems 
 7+(many of which are listed in the original readme, see below), removed other 
 8+code that is useless in the modern day (it had it's own mini virtual memory system! things
 9+were different back then), swapped from termcap to terminfo, made it somewhat UTF-8 aware
10+, and added some basic ansi escape code support, so things like git diff work. i have also 
11+fixed a lot of compilation errors, old code doesn't play nice with modern compilers. i have
12+also attempted to somewhat modernize the code style.
13+
14+it uses ninja to build. you will also need some terminfo implementation, and a reasonably
15+modern somewhat posix POSIX system.
16+
17+	ninja 
18+	ninja install
19+
20+the original readme is below:
21+----------------------------------------------------
22+
23+$Header: READ_ME,v 6.2 88/04/29 11:43:35 ceriel Exp $
24+
25+This directory contains the sources of YAP, Yet Another Pager.
26+It can do most of the things more(1) can, and much much more.
27+
28+Yap has been tested on the following systems:
29+
30+- DEC PDP 11/44 running V7
31+- DEC PDP 11/60 running V7
32+- DEC VAX 11/750 running 4.1BSD
33+- IBM PC/XT running PC/IX
34+- NCR Minitower running a System V
35+- Several 68k systems
36+- SUN-3 and SUN-4
37+
38+On other systems, you might have some problems getting yap to run, but then
39+again, you might not. You can always ask me for help, and maybe even get it.
40+If you make any changes, please
41+ - tell me about them
42+ - mark them clearly, preferably through conditional compilation.
43+
44+What you need to run yap:
45+
46+- you need termlib/termcap.
47+  It probably is'nt too hard to adapt yap to terminfo.
48+  It might even work unchanged, but I don't know about that. We don't have
49+  terminfo or the system V curses.
50+  If you adapt yap to terminfo, I would sure like to get the changes you made,
51+  so please send them to me (email address at the bottom of this note);
52+- you also need regex(III) (Either Berkeley style or USG will do).
53+  If you do not have it, you should define NOREGEX, which gives you
54+  simpleminded searches without meta-characters.
55+
56+How to install yap:
57+
58+- edit Makefile (easy)
59+- edit in_all.h (easy)
60+- type "make install"
61+
62+			Ceriel Jacobs, Vrije Universiteit Amsterdam
63+			ceriel@cs.vu.nl
A ansi.c
+75, -0
 1@@ -0,0 +1,75 @@
 2+#include "in_all.h"
 3+#include "ansi.h"
 4+
 5+static int
 6+consume_csi(const char *s, const char **next, enum ansi_kind *kind)
 7+{
 8+	const unsigned char *p = (const unsigned char *)s + 2;
 9+
10+	while (*p >= 0x30 && *p <= 0x3f) {
11+		p++;
12+	}
13+	while (*p >= 0x20 && *p <= 0x2f) {
14+		p++;
15+	}
16+	if (*p >= 0x40 && *p <= 0x7e) {
17+		*kind = (*p == 'm') ? ANSI_SGR : ANSI_DISCARD;
18+		*next = (const char *)p + 1;
19+		return 1;
20+	}
21+	return 0;
22+}
23+
24+static int
25+consume_string(const char *s, const char **next)
26+{
27+	const unsigned char *p = (const unsigned char *)s + 2;
28+
29+	while (*p) {
30+		if (*p == '\a') {
31+			*next = (const char *)p + 1;
32+			return 1;
33+		}
34+		if (*p == '\x1b' && p[1] == '\\') {
35+			*next = (const char *)p + 2;
36+			return 1;
37+		}
38+		p++;
39+	}
40+	return 0;
41+}
42+
43+static int
44+consume_esc(const char *s, const char **next)
45+{
46+	const unsigned char *p = (const unsigned char *)s + 1;
47+
48+	while (*p >= 0x20 && *p <= 0x2f) {
49+		p++;
50+	}
51+	if (*p >= 0x30 && *p <= 0x7e) {
52+		*next = (const char *)p + 1;
53+		return 1;
54+	}
55+	return 0;
56+}
57+
58+int
59+ansi_escape(const char *s, const char **next, enum ansi_kind *kind)
60+{
61+	*kind = ANSI_NONE;
62+	*next = s;
63+	if ((unsigned char)s[0] != 0x1b || s[1] == '\0') {
64+		return 0;
65+	}
66+	if (s[1] == '[') {
67+		return consume_csi(s, next, kind);
68+	}
69+	if (s[1] == ']' || s[1] == 'P' || s[1] == 'X' || s[1] == '^' ||
70+	    s[1] == '_') {
71+		*kind = ANSI_DISCARD;
72+		return consume_string(s, next);
73+	}
74+	*kind = ANSI_DISCARD;
75+	return consume_esc(s, next);
76+}
A ansi.h
+12, -0
 1@@ -0,0 +1,12 @@
 2+#ifndef ANSI_H
 3+#define ANSI_H
 4+
 5+enum ansi_kind {
 6+	ANSI_NONE = 0,
 7+	ANSI_SGR,
 8+	ANSI_DISCARD,
 9+};
10+
11+int ansi_escape(const char *s, const char **next, enum ansi_kind *kind);
12+
13+#endif
+6, -0
1@@ -0,0 +1,6 @@
2+#ifndef ASSERT_H
3+#define ASSERT_H
4+
5+#define assert(x) ((void)0)
6+
7+#endif
+49, -0
 1@@ -0,0 +1,49 @@
 2+# i know this is a ninja file, but you're allowed to edit it.
 3+# you can also pass things on the command line if you like, eg
 4+#
 5+#     CC=clang ninja
 6+
 7+cc = $${CC:-cc}
 8+cflags = $${CFLAGS:--O2 -std=c99}
 9+ldflags = $${LDFLAGS:-}
10+libs = $${LIBS:--ltinfo}
11+prefix = $${PREFIX:-/usr/local}
12+destdir = $${DESTDIR:-}
13+bindir = $${BINDIR:-$${DESTDIR:-}$${PREFIX:-/usr/local}/bin}
14+
15+rule cc
16+  command = $cc $cflags -MMD -MF $out.d -c -o $out $in
17+  deps = gcc
18+  depfile = $out.d
19+  description = cc $out
20+
21+rule link
22+  command = $cc $ldflags -o $out $in $libs
23+  description = link $out
24+
25+rule install
26+  command = mkdir -p $bindir && cp yap $bindir/
27+  description = install yap
28+
29+build ansi.o: cc ansi.c
30+build commands.o: cc commands.c
31+build display.o: cc display.c
32+build getcomm.o: cc getcomm.c
33+build getline.o: cc getline.c
34+build help.o: cc help.c
35+build keys.o: cc keys.c
36+build machine.o: cc machine.c
37+build main.o: cc main.c
38+build options.o: cc options.c
39+build output.o: cc output.c
40+build pattern.o: cc pattern.c
41+build process.o: cc process.c
42+build prompt.o: cc prompt.c
43+build rune.o: cc rune.c
44+build term.o: cc term.c
45+
46+build yap: link ansi.o commands.o display.o getcomm.o getline.o help.o keys.o machine.o main.o options.o output.o pattern.o process.o prompt.o rune.o term.o
47+build all: phony yap
48+build install: install yap
49+
50+default all
+785, -0
  1@@ -0,0 +1,785 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "commands.h"
  6+#include "output.h"
  7+#include "process.h"
  8+#include "help.h"
  9+#include "term.h"
 10+#include "prompt.h"
 11+#include "getline.h"
 12+#include "getcomm.h"
 13+#include "pattern.h"
 14+#include "display.h"
 15+#include "options.h"
 16+#include "machine.h"
 17+#include "keys.h"
 18+#include "main.h"
 19+#include "assert.h"
 20+
 21+static long lastcount; /* Save last count for '.' command */
 22+static int lastcomm;   /* Save last command for '.' command */
 23+static int searchdir;  /* Direction of last search */
 24+
 25+static int do_nocomm(long cnt);
 26+static void do_search(char *str, long cnt, int dir);
 27+static int do_fsearch(long cnt);
 28+static int do_bsearch(long cnt);
 29+static int n_or_rn_search(long cnt, int dir);
 30+static int do_nsearch(long cnt);
 31+static int do_rnsearch(long cnt);
 32+static int shell_command(int esc_ch, long cnt);
 33+static int do_shell(long cnt);
 34+static int do_pipe(long cnt);
 35+static int do_writefile(long cnt);
 36+static int do_absolute(long cnt);
 37+static int do_visit(long cnt);
 38+static int do_error(long cnt);
 39+static int prev_screen(int sz, int really);
 40+static int next_screen(int sz, int really);
 41+static int do_redraw(long cnt);
 42+static int page_size(unsigned cnt);
 43+static int do_forward(long cnt);
 44+static int do_backward(long cnt);
 45+static int do_firstline(long cnt);
 46+static int do_lline(long cnt);
 47+static int do_lf(long cnt);
 48+static int do_upline(long cnt);
 49+static int do_skiplines(long cnt);
 50+static int do_bskiplines(long cnt);
 51+static int do_fscreens(long cnt);
 52+static int do_bscreens(long cnt);
 53+static int scro_size(unsigned cnt);
 54+static int do_f_scroll(long cnt);
 55+static int do_b_scroll(long cnt);
 56+static int do_previousfile(long cnt);
 57+static int do_nextfile(long cnt);
 58+static int do_lcomm(long cnt);
 59+static int do_quit(long cnt);
 60+
 61+static int
 62+do_nocomm(long cnt)
 63+{ /* Do nothing */
 64+	(void)cnt;
 65+	return 0;
 66+}
 67+
 68+int
 69+do_chkm(long cnt)
 70+{ /* Change key map */
 71+	struct keymap *p;
 72+	(void)cnt;
 73+
 74+	if (!(p = othermap)) {
 75+		error("No other keymap");
 76+		return 0;
 77+	}
 78+	othermap = currmap;
 79+	currmap = p;
 80+	return 0;
 81+}
 82+
 83+/*
 84+ * Perform searches
 85+ */
 86+
 87+static void
 88+do_search(char *str, long cnt, int dir)
 89+{
 90+	char *p;
 91+	long lineno;
 92+
 93+	if (str) {
 94+		/*
 95+		 * We have to get a pattern, which we have to prompt for
 96+		 * with the string "str".
 97+		 */
 98+		if ((p = readline(str)) == 0) {
 99+			/*
100+			 * User cancelled command
101+			 */
102+			return;
103+		}
104+		if ((p = re_comp(p))) {
105+			/*
106+			 * There was an error in the pattern
107+			 */
108+			error(p);
109+			return;
110+		}
111+		searchdir = dir;
112+	}
113+	if (dir < 0)
114+		lineno = scr_info.firstline;
115+	else
116+		lineno = scr_info.lastline;
117+	for (;;) {
118+		p = 0;
119+		if ((lineno += dir) > 0)
120+			p = getline(lineno, 0);
121+		if (interrupt)
122+			return;
123+		if (!p) { /* End of file reached */
124+			error("pattern not found");
125+			return;
126+		}
127+		if (re_exec(p) && --cnt <= 0) {
128+			/*
129+			 * We found the pattern, and we found it often enough.
130+			 * Pity that we still don't know where the match is.
131+			 * We only know the linenumber. So, we just hope the
132+			 * following will at least bring it on the screen ...
133+			 */
134+			(void)display(lineno, 0, pagesize, 0);
135+			(void)scrollb(2, 0);
136+			redraw(0);
137+			return;
138+		}
139+	}
140+	/* NOTREACHED */
141+}
142+
143+static int
144+do_fsearch(long cnt)
145+{ /* Forward search */
146+	do_search("/", cnt, 1);
147+	return 0;
148+}
149+
150+static int
151+do_bsearch(long cnt)
152+{ /* Backward search */
153+	do_search("?", cnt, -1);
154+	return 0;
155+}
156+
157+/*
158+ * Repeat last search in direction "dir"
159+ */
160+
161+static int
162+n_or_rn_search(long cnt, int dir)
163+{
164+	char *p;
165+
166+	if (dir == 1) {
167+		p = "/\r";
168+	} else if (dir == -1) {
169+		p = "?\r";
170+	} else {
171+		error("No previous pattern");
172+		return 0;
173+	}
174+	if (!stupid)
175+		clrbline();
176+	putline(p);
177+	flush();
178+	do_search((char *)0, cnt, dir);
179+	return 0;
180+}
181+
182+static int
183+do_nsearch(long cnt)
184+{ /* Repeat search in same direction */
185+	n_or_rn_search(cnt, searchdir);
186+	return 0;
187+}
188+
189+static int
190+do_rnsearch(long cnt)
191+{ /* Repeat search in opposite direction */
192+	n_or_rn_search(cnt, -searchdir);
193+	return 0;
194+}
195+
196+static int
197+shell_command(int esc_ch, long cnt)
198+{
199+	char *p;
200+	static char buf[2];
201+
202+	buf[0] = esc_ch;
203+	buf[1] = '\0';
204+	if ((p = readline(buf)) != 0) {
205+		shellescape(p, esc_ch);
206+		if (cnt >= 0 && !hardcopy) {
207+			p = startcomm;
208+			startcomm = 0;
209+			ret_to_continue();
210+			putline(TI);
211+			if (!p) {
212+				/*
213+				 * Avoid double redraw.
214+				 * After a "startcomm", a redraw will
215+				 * take place anyway.
216+				 */
217+				redraw(1);
218+			}
219+		}
220+	}
221+	return 0;
222+}
223+
224+static int
225+do_shell(long cnt)
226+{ /* Execute a shell escape */
227+	return shell_command('!', cnt);
228+}
229+
230+static int
231+do_pipe(long cnt)
232+{ /* Execute a shell escape */
233+	return shell_command('|', cnt);
234+}
235+
236+static int
237+do_writefile(long cnt)
238+{ /* Write input to a file */
239+	char *p;
240+	int fd;
241+	(void)cnt;
242+
243+	if ((p = readline("Filename: ")) == 0 || !*p) {
244+		/*
245+		 * No file name given
246+		 */
247+		return 0;
248+	}
249+	if ((fd = open(p, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
250+		if (errno == EEXIST) {
251+			error("File exists");
252+			return 0;
253+		}
254+		error("Could not open file");
255+		return 0;
256+	}
257+	wrt_fd(fd);
258+	(void)close(fd);
259+	return 0;
260+}
261+
262+void
263+wrt_fd(int fd)
264+{
265+	long l = 1;
266+	char *p = getline(l, 0), *pbuf;
267+	char buf[1024];
268+
269+	while (p) {
270+		pbuf = buf;
271+		while (p && pbuf < &buf[1024]) {
272+			if (!*p) {
273+				*pbuf++ = '\n';
274+				p = getline(++l, 0);
275+			} else
276+				*pbuf++ = *p++;
277+		}
278+		if (write(fd, buf, pbuf - buf) < 0) {
279+			error("Write failed");
280+			break;
281+		}
282+	}
283+}
284+
285+static int
286+do_absolute(long cnt)
287+{ /* Go to linenumber "cnt" */
288+
289+	if (!getline(cnt, 0)) { /* Not there or interrupt */
290+		if (!interrupt) {
291+			/*
292+			 * User did'nt give an interrupt, so the line number
293+			 * was too high. Go to the last line.
294+			 */
295+			do_lline(cnt);
296+		}
297+		return 0;
298+	}
299+	(void)display(cnt, 0, pagesize, 1);
300+	return 0;
301+}
302+
303+static int
304+do_visit(long cnt)
305+{ /* Visit a file */
306+	char *p;
307+	static char fn[128]; /* Keep file name */
308+	(void)cnt;
309+
310+	if ((p = readline("Filename: ")) == 0) {
311+		return 0;
312+	}
313+	if (*p) {
314+		(void)strcpy(fn, p);
315+		visitfile(fn);
316+	} else {
317+		/*
318+		 * User typed a return. Visit the current file
319+		 */
320+		if (!(p = filenames[filecount])) {
321+			error("No current file");
322+			return 0;
323+		}
324+		visitfile(p);
325+	}
326+	(void)display(1L, 0, pagesize, 1);
327+	return 0;
328+}
329+
330+static int
331+do_error(long cnt)
332+{ /* Called when user types wrong key sequence */
333+	(void)cnt;
334+	error(currmap->k_help);
335+	return 0;
336+}
337+
338+/*
339+ * Interface routine for displaying previous screen,
340+ * depending on cflag.
341+ */
342+
343+static int
344+prev_screen(int sz, int really)
345+{
346+	int retval;
347+
348+	retval = scrollb(sz - 1, really && cflag);
349+	if (really && !cflag) {
350+		/*
351+		 * The previous call did not display anything, but at least we
352+		 * know where to start
353+		 */
354+		return display(scr_info.firstline, scr_info.nf, sz, 1);
355+	}
356+	return retval;
357+}
358+
359+/*
360+ * Interface routine for displaying the next screen,
361+ * dependent on cflag.
362+ */
363+
364+static int
365+next_screen(int sz, int really)
366+{
367+	int t;
368+	struct scr_info *p = &scr_info;
369+
370+	if (cflag) {
371+		return scrollf(sz - 1, really);
372+	}
373+	t = p->tail->cnt - 1;
374+	if (p->lastline == p->firstline) {
375+		t += p->nf;
376+	}
377+	return display(p->lastline, t, sz, really);
378+}
379+
380+static int
381+do_redraw(long cnt)
382+{
383+	(void)cnt;
384+	redraw(1);
385+	return 0;
386+}
387+
388+static int
389+page_size(unsigned cnt)
390+{
391+
392+	if (cnt) {
393+		if (cnt > (unsigned)maxpagesize)
394+			return maxpagesize;
395+		if (cnt < MINPAGESIZE)
396+			return MINPAGESIZE;
397+		return (int)cnt;
398+	}
399+	return pagesize;
400+}
401+
402+static int
403+do_forward(long cnt)
404+{ /* Display next page */
405+	int i;
406+
407+	i = page_size((unsigned)cnt);
408+	if (status & EOFILE) {
409+		/*
410+		 * May seem strange, but actually a visit to the next file
411+		 * has already been done here
412+		 */
413+		(void)display(1L, 0, i, 1);
414+		return 0;
415+	}
416+	(void)next_screen(i, 1);
417+	return 0;
418+}
419+
420+static int
421+do_backward(long cnt)
422+{
423+	int i, temp;
424+
425+	i = page_size((unsigned)cnt);
426+	if (!(status & START)) {
427+		(void)prev_screen(i, 1);
428+		return 0;
429+	}
430+	if (stdf < 0) {
431+		(void)display(1L, 0, i, 1);
432+		return 0;
433+	}
434+	/*
435+	 * The next part is a bit clumsy.
436+	 * We want to display the last page of the previous file (for which
437+	 * a visit has already been done), but the pagesize may temporarily
438+	 * be different because the command had a count
439+	 */
440+	temp = pagesize;
441+	pagesize = i;
442+	do_lline(cnt);
443+	pagesize = temp;
444+	return 0;
445+}
446+
447+static int
448+do_firstline(long cnt)
449+{ /* Go to start of input */
450+	(void)cnt;
451+	do_absolute(1L);
452+	return 0;
453+}
454+
455+static int
456+do_lline(long cnt)
457+{ /* Go to end of input */
458+	int i = 0;
459+	int j = pagesize - 1;
460+
461+	if ((cnt = to_lastline()) < 0) {
462+		/*
463+		 * Interrupted by the user
464+		 */
465+		return 0;
466+	}
467+	/*
468+	 * Display the page such that only the last line of the page is
469+	 * a "~", independant of the pagesize
470+	 */
471+	while (!(display(cnt, i, j, 0) & EOFILE)) {
472+		/*
473+		 * The last line could of course be very long ...
474+		 */
475+		i += j;
476+	}
477+	(void)scrollb(j - scr_info.tail->cnt, 0);
478+	redraw(0);
479+	return 0;
480+}
481+
482+static int
483+do_lf(long cnt)
484+{ /* Display next line, or go to line */
485+
486+	if (cnt) { /* Go to line */
487+		do_absolute(cnt);
488+		return 0;
489+	}
490+	(void)scrollf(1, 1);
491+	return 0;
492+}
493+
494+static int
495+do_upline(long cnt)
496+{ /* Display previous line, or go to line */
497+
498+	if (cnt) { /* Go to line */
499+		do_absolute(cnt);
500+		return 0;
501+	}
502+	(void)scrollb(1, 1);
503+	return 0;
504+}
505+
506+static int
507+do_skiplines(long cnt)
508+{ /* Skip lines forwards */
509+
510+	/* Should be interruptable ... */
511+	(void)scrollf((int)(cnt + maxpagesize - 1), 0);
512+	redraw(0);
513+	return 0;
514+}
515+
516+static int
517+do_bskiplines(long cnt)
518+{ /* Skip lines backwards */
519+
520+	/* Should be interruptable ... */
521+	(void)scrollb((int)(cnt + pagesize - 1), 0);
522+	redraw(0);
523+	return 0;
524+}
525+
526+static int
527+do_fscreens(long cnt)
528+{ /* Skip screens forwards */
529+
530+	do {
531+		if ((next_screen(pagesize, 0) & EOFILE) || interrupt)
532+			break;
533+	} while (--cnt >= 0);
534+	redraw(0);
535+	return 0;
536+}
537+
538+static int
539+do_bscreens(long cnt)
540+{ /* Skip screens backwards */
541+
542+	do {
543+		if ((prev_screen(pagesize, 0) & START) || interrupt)
544+			break;
545+	} while (--cnt >= 0);
546+	redraw(0);
547+	return 0;
548+}
549+
550+static int
551+scro_size(unsigned cnt)
552+{
553+
554+	if (cnt >= (unsigned)maxpagesize)
555+		return maxpagesize;
556+	if (cnt)
557+		return (int)cnt;
558+	return scrollsize;
559+}
560+
561+static int
562+do_f_scroll(long cnt)
563+{ /* Scroll forwards */
564+
565+	(void)scrollf(scro_size((unsigned)cnt), 1);
566+	return 0;
567+}
568+
569+static int
570+do_b_scroll(long cnt)
571+{ /* Scroll backwards */
572+
573+	(void)scrollb(scro_size((unsigned)cnt), 1);
574+	return 0;
575+}
576+
577+static int
578+do_previousfile(long cnt)
579+{ /* Visit previous file */
580+
581+	if (nextfile(-(int)cnt)) {
582+		error("No (Nth) previous file");
583+		return 0;
584+	}
585+	redraw(0);
586+	return 0;
587+}
588+
589+static int
590+do_nextfile(long cnt)
591+{ /* Visit next file */
592+
593+	if (nextfile((int)cnt)) {
594+		error("No (Nth) next file");
595+		return 0;
596+	}
597+	redraw(0);
598+	return 0;
599+}
600+
601+static int
602+do_quit(long cnt)
603+{
604+	(void)cnt;
605+	return quit();
606+}
607+
608+/*
609+ * The next array is initialized, sorted on the first element of the structs,
610+ * so that we can perform binary search
611+ */
612+struct commands commands[] = {
613+    {"", 0, do_error, ""},
614+    {"", 0, do_nocomm, ""},
615+    {"bf", STICKY | NEEDS_COUNT,
616+     do_previousfile, "Visit previous file"},
617+    {"bl", NEEDS_SCREEN | STICKY,
618+     do_upline, "Scroll one line up, or go to line"},
619+    {"bot", STICKY,
620+     do_lline, "Go to last line of the input"},
621+    {"bp", BACK | NEEDS_SCREEN | TOPREVFILE | STICKY,
622+     do_backward, "display previous page"},
623+    {"bps", SCREENSIZE_ADAPT | BACK | NEEDS_SCREEN | TOPREVFILE | STICKY,
624+     do_backward, "Display previous page, set pagesize"},
625+    {"bs", BACK | NEEDS_SCREEN | STICKY,
626+     do_b_scroll, "Scroll backwards"},
627+    {"bse", 0, do_bsearch, "Search backwards for pattern"},
628+    {"bsl", BACK | NEEDS_SCREEN | STICKY | NEEDS_COUNT,
629+     do_bskiplines, "Skip lines backwards"},
630+    {"bsp", BACK | NEEDS_SCREEN | STICKY | NEEDS_COUNT,
631+     do_bscreens, "Skip screens backwards"},
632+    {"bss", SCROLLSIZE_ADAPT | BACK | NEEDS_SCREEN | STICKY,
633+     do_b_scroll, "Scroll backwards, set scrollsize"},
634+    {"chm", 0, do_chkm, "Switch to other keymap"},
635+    {"exg", STICKY, exgmark, "Exchange current page with mark"},
636+    {"ff", STICKY | NEEDS_COUNT,
637+     do_nextfile, "Visit next file"},
638+    {"fl", NEEDS_SCREEN | STICKY,
639+     do_lf, "Scroll one line down, or go to line"},
640+    {"fp", TONEXTFILE | AHEAD | STICKY,
641+     do_forward, "Display next page"},
642+    {"fps", SCREENSIZE_ADAPT | TONEXTFILE | AHEAD | STICKY,
643+     do_forward, "Display next page, set pagesize"},
644+    {"fs", AHEAD | NEEDS_SCREEN | STICKY,
645+     do_f_scroll, "Scroll forwards"},
646+    {"fse", 0, do_fsearch, "Search forwards for pattern"},
647+    {"fsl", AHEAD | NEEDS_SCREEN | STICKY | NEEDS_COUNT,
648+     do_skiplines, "Skip lines forwards"},
649+    {"fsp", AHEAD | NEEDS_SCREEN | STICKY | NEEDS_COUNT,
650+     do_fscreens, "Skip screens forwards"},
651+    {"fss", SCROLLSIZE_ADAPT | AHEAD | NEEDS_SCREEN | STICKY,
652+     do_f_scroll, "Scroll forwards, set scrollsize"},
653+    {"hlp", 0, do_help, "Give description of all commands"},
654+    {"mar", 0, setmark, "Set a mark on the current page"},
655+    {"nse", STICKY, do_nsearch, "Repeat the last search"},
656+    {"nsr", STICKY, do_rnsearch, "Repeat last search in other direction"},
657+    {"pip", ESC, do_pipe, "pipe input into shell command"},
658+    {"qui", 0, do_quit, "Exit from yap"},
659+    {"red", 0, do_redraw, "Redraw screen"},
660+    {"rep", 0, do_lcomm, "Repeat last command"},
661+    {"shl", ESC, do_shell, "Execute a shell escape"},
662+    {"tom", 0, tomark, "Go to mark"},
663+    {"top", STICKY, do_firstline, "Go to the first line of the input"},
664+    {"vis", 0, do_visit, "Visit a file"},
665+    {"wrf", 0, do_writefile, "Write input to a file"},
666+};
667+
668+/*
669+ * Lookup string "s" in the commands array, and return index.
670+ * return 0 if not found.
671+ */
672+
673+int
674+lookup(char *s)
675+{
676+	struct commands *l, *u, *m;
677+
678+	l = &commands[2];
679+	u = &commands[sizeof(commands) / sizeof(*u) - 1];
680+	do {
681+		/*
682+		 * Perform binary search
683+		 */
684+		m = l + (u - l) / 2;
685+		if (strcmp(s, m->c_cmd) > 0)
686+			l = m + 1;
687+		else
688+			u = m;
689+	} while (l < u);
690+	if (!strcmp(s, u->c_cmd))
691+		return u - commands;
692+	return 0;
693+}
694+
695+/*ARGSUSED*/
696+static int
697+do_lcomm(long cnt)
698+{ /* Repeat last command */
699+	(void)cnt;
700+
701+	if (!lastcomm) {
702+		error("No previous command");
703+		return 0;
704+	}
705+	do_comm(lastcomm, lastcount);
706+	return 0;
707+}
708+
709+/*
710+ * Execute a command, with optional count "count".
711+ */
712+
713+void
714+do_comm(int comm, long count)
715+{
716+	struct commands *pcomm;
717+	int temp;
718+	int flags;
719+
720+	pcomm = &commands[comm];
721+	flags = pcomm->c_flags;
722+
723+	/*
724+	 * Check the command.
725+	 * If the last line of the file is displayed and the command goes
726+	 * forwards and does'nt have the ability to go to the next file, it
727+	 * is an error.
728+	 * If the first line of the file is displayed and the command goes
729+	 * backwards and does'nt have the ability to go to the previous file,
730+	 * it is an error.
731+	 * Also check wether we need the next or previous file. If so, get it.
732+	 */
733+	if ((status & EOFILE) && (flags & AHEAD)) {
734+		if (qflag || !(flags & TONEXTFILE))
735+			return;
736+		if (nextfile(1))
737+			quit();
738+	}
739+	if ((status & START) && (flags & BACK)) {
740+		if (qflag || !(flags & TOPREVFILE))
741+			return;
742+		if (nextfile(-1))
743+			quit();
744+	}
745+	/*
746+	 * Does the command stick around for LASTCOMM?
747+	 */
748+	if (flags & STICKY) {
749+		lastcomm = comm;
750+		lastcount = count;
751+	}
752+	if (!count) {
753+		if (flags & NEEDS_COUNT)
754+			count = 1;
755+	} else {
756+		/*
757+		 * Does the command adapt the screensize?
758+		 */
759+		if (flags & SCREENSIZE_ADAPT) {
760+			temp = maxpagesize;
761+			if ((unsigned)count < (unsigned)temp) {
762+				temp = (int)count;
763+			}
764+			if (temp < MINPAGESIZE) {
765+				temp = MINPAGESIZE;
766+			}
767+			count = 0;
768+			pagesize = temp;
769+		}
770+		/*
771+		 * Does the command adapt the scrollsize?
772+		 */
773+		if (flags & SCROLLSIZE_ADAPT) {
774+			temp = maxpagesize - 1;
775+			if ((unsigned)count < (unsigned)temp) {
776+				temp = (int)count;
777+			}
778+			scrollsize = temp;
779+			count = 0;
780+		}
781+	}
782+	/*
783+	 * Now execute the command.
784+	 */
785+	(*(pcomm->c_func))(count);
786+}
+27, -0
 1@@ -0,0 +1,27 @@
 2+#ifndef COMMANDS_H
 3+#define COMMANDS_H
 4+
 5+#define SCREENSIZE_ADAPT 01
 6+#define SCROLLSIZE_ADAPT 02
 7+#define TONEXTFILE 04
 8+#define AHEAD 010
 9+#define BACK 020
10+#define NEEDS_SCREEN 040
11+#define TOPREVFILE 0100
12+#define STICKY 0200
13+#define NEEDS_COUNT 0400
14+#define ESC 01000
15+
16+extern struct commands {
17+	char *c_cmd;
18+	int c_flags;
19+	int (*c_func)(long);
20+	char *c_descr;
21+} commands[];
22+
23+int do_chkm(long cnt);
24+void do_comm(int command, long count);
25+int lookup(char *str);
26+void wrt_fd(int fd);
27+
28+#endif
+692, -0
  1@@ -0,0 +1,692 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "display.h"
  6+#include "assert.h"
  7+#include "machine.h"
  8+#include "term.h"
  9+#include "output.h"
 10+#include "options.h"
 11+#include "process.h"
 12+#include "getline.h"
 13+#include "main.h"
 14+#define getline yap_getline
 15+#include "utf.h"
 16+#undef getline
 17+#include "ansi.h"
 18+
 19+int pagesize;
 20+int maxpagesize;
 21+int scrollsize;
 22+struct scr_info scr_info;
 23+int status;
 24+
 25+static char *do_line(char *str, int reallydispl);
 26+static void flush_display_buffer(char *buf, char **p);
 27+static int rune_width(Rune r);
 28+static int decode_cell(const char *s, const char **next, int *width,
 29+                       int *is_underscore);
 30+int wcwidth(wchar_t wc);
 31+
 32+/*
 33+ * Fill n lines of the screen, each with "str".
 34+ */
 35+
 36+static void
 37+fillscr(char *str, int n)
 38+{
 39+
 40+	while (n-- > 0) {
 41+		putline(str);
 42+	}
 43+}
 44+
 45+/*
 46+ * Skip "n" screenlines of line "p", and return what's left of it.
 47+ */
 48+
 49+static char *
 50+skiplines(char *p, int n)
 51+{
 52+
 53+	while (n-- > 0) {
 54+		p = do_line(p, 0);
 55+		scr_info.currentpos--;
 56+	}
 57+	return p;
 58+}
 59+
 60+/*
 61+ * Redraw screen.
 62+ * "n" = 1 if it is a real redraw, 0 if one page must be displayed.
 63+ * It is also called when yap receives a stop signal.
 64+ */
 65+
 66+void
 67+redraw(int n)
 68+{
 69+	struct scr_info *p = &scr_info;
 70+	int i;
 71+
 72+	i = pagesize;
 73+	if (n && p->currentpos) {
 74+		i = p->currentpos;
 75+	}
 76+	(void)display(p->firstline, p->nf, i, 1);
 77+}
 78+
 79+/*
 80+ * Compute return value for the routines "display" and "scrollf".
 81+ * This return value indicates wether we are at the end of file
 82+ * or at the start, or both.
 83+ * "s" contains that part of the last line that was not displayed.
 84+ */
 85+
 86+static int
 87+compretval(const char *s)
 88+{
 89+	int i;
 90+	struct scr_info *p = &scr_info;
 91+
 92+	i = 0;
 93+	if (!s || (!*s && !getline(p->lastline + 1, 1))) {
 94+		i = EOFILE;
 95+	}
 96+	if (p->firstline == 1 && !p->nf) {
 97+		i |= START;
 98+	}
 99+	status = i;
100+	return i;
101+}
102+
103+/*
104+ * Display nlines, starting at line n, not displaying the first
105+ * nd screenlines of n.
106+ * If reallydispl = 0, the actual displaying is not performed,
107+ * only the computing associated with it is done.
108+ */
109+
110+int
111+display(long n, int nd, int nlines, int reallydispl)
112+{
113+
114+	struct scr_info *s = &scr_info;
115+	char *p; /* pointer to line to be displayed */
116+
117+	if (startcomm) { /* No displaying on a command from the
118+		          * yap command line. In this case, displaying
119+		          * will be done after executing the command,
120+		          * by a redraw.
121+		          */
122+		reallydispl = 0;
123+	}
124+	if (!n) {
125+		n = 1L;
126+		nd = 0;
127+	}
128+	if (reallydispl) { /* move cursor to starting point */
129+		if (stupid) {
130+			putline(currentfile);
131+			putline(", line ");
132+			prnum(n);
133+			nlines--;
134+		}
135+		if (cflag) {
136+			putline("\r\n");
137+		} else {
138+			home();
139+			clrscreen();
140+		}
141+	}
142+	/*
143+	 * Now, do computations and display
144+	 */
145+	s->currentpos = 0;
146+	s->nf = nd;
147+	s->head = s->tail;
148+	s->tail->cnt = 0;
149+	s->tail->line = n;
150+	p = skiplines(getline(n, 1), nd);
151+	while (nlines && p) {
152+		/*
153+		 * While there is room,
154+		 * and there is something left to display ...
155+		 */
156+		(s->tail->cnt)++;
157+		nlines--;
158+		if (*(p = do_line(p, reallydispl)) == '\0') {
159+			/*
160+			 * File-line finished, get next one ...
161+			 */
162+			p = getline(++n, 1);
163+			if (nlines && p) {
164+				s->tail = s->tail->next;
165+				s->tail->cnt = 0;
166+				s->tail->line = n;
167+			}
168+		}
169+	}
170+	if (!stupid) {
171+		s->currentpos += nlines;
172+		if (reallydispl) {
173+			fillscr("~\r\n", nlines);
174+			fillscr("\r\n", maxpagesize - s->currentpos);
175+		}
176+	}
177+	return compretval(p);
178+}
179+
180+/*
181+ * Scroll forwards n lines.
182+ */
183+
184+int
185+scrollf(int n, int reallydispl)
186+{
187+
188+	struct scr_info *s = &scr_info;
189+	char *p;
190+	long ll;
191+	int i;
192+
193+	/*
194+	 * First, find out how many screenlines of the last line were already
195+	 * on the screen, and possibly above it.
196+	 */
197+
198+	if (n <= 0 || (status & EOFILE))
199+		return status;
200+	if (startcomm)
201+		reallydispl = 0;
202+	/*
203+	 * Find out where to begin displaying
204+	 */
205+	i = s->tail->cnt;
206+	if ((ll = s->lastline) == s->firstline)
207+		i += s->nf;
208+	p = skiplines(getline(ll, 1), i);
209+	/*
210+	 * Now, place the cursor at the first free line
211+	 */
212+	if (reallydispl && !stupid) {
213+		clrbline();
214+		mgoto(s->currentpos);
215+	}
216+	/*
217+	 * Now display lines, keeping track of which lines are on the screen.
218+	 */
219+	while (n-- > 0) {  /* There are still rows to be displayed */
220+		if (!*p) { /* End of line, get next one */
221+			if (!(p = getline(++ll, 1))) {
222+				/*
223+				 * No lines left. At end of file
224+				 */
225+				break;
226+			}
227+			s->tail = s->tail->next;
228+			s->tail->cnt = 0;
229+			s->tail->line = ll;
230+		}
231+		if (s->currentpos >= maxpagesize) {
232+			/*
233+			 * No room, delete first screen-line
234+			 */
235+			s->currentpos--;
236+			s->nf++;
237+			if (--(s->head->cnt) == 0) {
238+				/*
239+				 * The first file-line on the screen is wiped
240+				 * out completely; update administration
241+				 * accordingly.
242+				 */
243+				s->nf = 0;
244+				s->head = s->head->next;
245+				assert(s->head->cnt > 0);
246+			}
247+		}
248+		s->tail->cnt++;
249+		p = do_line(p, reallydispl);
250+	}
251+	return compretval(p);
252+}
253+
254+/*
255+ * Scroll back n lines
256+ */
257+
258+int
259+scrollb(int n, int reallydispl)
260+{
261+
262+	struct scr_info *s = &scr_info;
263+	char *p; /* Holds string to be displayed */
264+	int i;
265+	int count;
266+	long ln; /* a line number */
267+	int nodispl;
268+	int cannotscroll; /* stupid or no insert-line */
269+
270+	/*
271+	 * First, find out where to start
272+	 */
273+	if ((count = n) <= 0 || (status & START))
274+		return status;
275+	if (startcomm)
276+		reallydispl = 0;
277+	cannotscroll = stupid || (!*AL && !*SR);
278+	ln = s->firstline;
279+	nodispl = s->nf;
280+	while (count) { /* While scrolling back ... */
281+		i = nodispl;
282+		if (i) {
283+			/*
284+			 * There were screen-lines of s->firstline that were not
285+			 * displayed.
286+			 * We can use them now, but only "count" of them.
287+			 */
288+			if (i > count)
289+				i = count;
290+			s->currentpos += i;
291+			nodispl -= i;
292+			count -= i;
293+		} else { /* Get previous line */
294+			if (ln == 1)
295+				break; /* isn't there ... */
296+			p = getline(--ln, 1);
297+			/*
298+			 * Make it the first line of the screen and compute
299+			 * how many screenlines it takes. These lines are not
300+			 * displayed, but nodispl is set to this count, so
301+			 * that it will be nonzero next time around
302+			 */
303+			nodispl = 0;
304+			do { /* Find out how many screenlines */
305+				nodispl++;
306+				p = skiplines(p, 1);
307+			} while (*p);
308+		}
309+	}
310+	n -= count;
311+	if ((i = s->currentpos) > maxpagesize)
312+		i = maxpagesize;
313+	if (reallydispl && hardcopy)
314+		i = n;
315+	/*
316+	 * Now that we know where to start, we can use "display" to do the
317+	 * rest of the computing for us, and maybe even the displaying ...
318+	 */
319+	i = display(ln,
320+	            nodispl,
321+	            i,
322+	            reallydispl && cannotscroll);
323+	if (cannotscroll || !reallydispl) {
324+		/*
325+		 * Yes, "display" did the displaying, or we did'nt have to
326+		 * display at all.
327+		 * I like it, but the user obviously does not.
328+		 * Let him buy another (smarter) terminal ...
329+		 */
330+		return i;
331+	}
332+	/*
333+	 * Now, all we have to do is the displaying. And we are dealing with
334+	 * a smart terminal (it can insert lines or scroll back).
335+	 */
336+	home();
337+	/*
338+	 * Insert lines all at once
339+	 */
340+	for (i = n; i; i--) {
341+		if (DB && *CE) {
342+			/*
343+			 * Grumble..., terminal retains lines below, so we have
344+			 * to clear the lines that we push off the screen
345+			 */
346+			clrbline();
347+			home();
348+		}
349+		if (*SR) {
350+			scrollreverse();
351+		} else {
352+			insert_line(0);
353+		}
354+	}
355+	p = skiplines(getline(ln = s->firstline, 1), s->nf);
356+	for (i = 0; i < n; i++) {
357+		p = do_line(p, 1);
358+		s->currentpos--;
359+		if (!*p) {
360+			p = getline(++ln, 1);
361+		}
362+	}
363+	return count;
364+}
365+
366+/*
367+ * Process a line.
368+ * If reallydispl > 0 then display it.
369+ */
370+
371+static char *
372+do_line(char *str, int reallydispl)
373+{
374+	char buf[1024];
375+	char *p = buf;
376+	int pos = COLS;
377+	int do_ul = 0, do_hl = 0;
378+	int lastmode = 0, lasthlmode = 0;
379+	int c2;
380+	int cell_width;
381+	int tab_width;
382+	int is_underscore;
383+	int cell_len;
384+	int next_len;
385+	int next_is_underscore;
386+	unsigned char uc;
387+	const char *cell_start;
388+	const char *cursor;
389+	const char *next;
390+	enum ansi_kind ansi_kind;
391+
392+	while (*str && pos > 0) {
393+		uc = (unsigned char)*str;
394+		if (uc == 0x1b && ansi_escape(str, &next, &ansi_kind)) {
395+			if (ansi_kind == ANSI_SGR) {
396+				cell_len = next - str;
397+				if (reallydispl &&
398+				    p + cell_len >= &buf[sizeof(buf) - 1]) {
399+					flush_display_buffer(buf, &p);
400+				}
401+				if (reallydispl) {
402+					(void)memcpy(p, str, (size_t)cell_len);
403+					p += cell_len;
404+				}
405+			}
406+			str = (char *)next;
407+			continue;
408+		}
409+		if (uc < ' ' && (cell_len = match(str, &c2, sppat)) > 0) {
410+			/*
411+			 * We found a string that matches, and thus must be
412+			 * echoed literally
413+			 */
414+			if ((pos - c2) <= 0) {
415+				/*
416+				 * It did not fit
417+				 */
418+				break;
419+			}
420+			pos -= c2;
421+			if (reallydispl) {
422+				flush_display_buffer(buf, &p);
423+				cell_start = str;
424+				str += cell_len;
425+				uc = (unsigned char)*str;
426+				*str = '\0';
427+				putline((char *)cell_start);
428+				*str = (char)uc;
429+			} else {
430+				str += cell_len;
431+			}
432+			continue;
433+		}
434+
435+		cell_start = str;
436+		cell_len = decode_cell(str, &next, &cell_width, &is_underscore);
437+		cursor = next;
438+		do_hl = 0;
439+		if (*cursor == '\b' && *(cursor + 1) != '\0') {
440+			next_len = decode_cell(cursor + 1, &next, &tab_width,
441+			                       &next_is_underscore);
442+			if (!(is_underscore && *(cursor + 1 + next_len) != '\b')) {
443+				while (*cursor == '\b' && *(cursor + 1) != '\0') {
444+					cell_start = cursor + 1;
445+					cell_len = decode_cell(cell_start, &next, &cell_width,
446+					                       &is_underscore);
447+					cursor = next;
448+					do_hl = 1;
449+				}
450+			}
451+		}
452+		do_ul = 1;
453+		/*
454+		 * Find underline sequences ...
455+		 */
456+		if (is_underscore && *cursor == '\b' && *(cursor + 1) != '\0') {
457+			cell_start = cursor + 1;
458+			cell_len = decode_cell(cell_start, &next, &cell_width,
459+			                       &is_underscore);
460+			cursor = next;
461+		} else {
462+			if (*cursor == '\b' && *(cursor + 1) == '_') {
463+				cursor += 2;
464+			} else {
465+				do_ul = 0;
466+			}
467+		}
468+		if (cell_width == -1) {
469+			tab_width = 8 - ((COLS - pos) & 0x07);
470+			if (pos - tab_width < 0) {
471+				break;
472+			}
473+		} else if (cell_width == -2) {
474+			if (pos <= 1) {
475+				break;
476+			}
477+		} else if (cell_width > pos) {
478+			break;
479+		}
480+		if (reallydispl && do_hl != lasthlmode) {
481+			flush_display_buffer(buf, &p);
482+			if (do_hl) {
483+				bold();
484+			} else {
485+				end_bold();
486+			}
487+		}
488+		lasthlmode = do_hl;
489+		if (reallydispl && do_ul != lastmode) {
490+			flush_display_buffer(buf, &p);
491+			if (do_ul) {
492+				underline();
493+			} else {
494+				end_underline();
495+			}
496+		}
497+		lastmode = do_ul;
498+		if (cell_width == -1) {
499+			pos -= tab_width;
500+			if (reallydispl) {
501+				if (expandtabs) {
502+					while (tab_width-- > 0) {
503+						*p++ = ' ';
504+					}
505+				} else {
506+					flush_display_buffer(buf, &p);
507+					givetab();
508+				}
509+			}
510+			str = (char *)cursor;
511+			continue;
512+		}
513+		if (reallydispl && p + cell_len >= &buf[sizeof(buf) - 1]) {
514+			flush_display_buffer(buf, &p);
515+		}
516+		if (cell_width == -2) {
517+			if (reallydispl) {
518+				if (p + 2 >= &buf[sizeof(buf) - 1]) {
519+					flush_display_buffer(buf, &p);
520+				}
521+				*p++ = '^';
522+				*p++ = *cell_start ^ 0x40;
523+			}
524+			pos -= 2;
525+			str = (char *)cursor;
526+			continue;
527+		}
528+		if (reallydispl) {
529+			(void)memcpy(p, cell_start, (size_t)cell_len);
530+			p += cell_len;
531+		}
532+		if (cell_width >= 0) {
533+			pos -= cell_width;
534+			if (reallydispl && do_ul && *UC && cell_width == 1 && pos > 0) {
535+				/*
536+				 * Underlining apparently is done one
537+				 * character at a time.
538+				 */
539+				flush_display_buffer(buf, &p);
540+				backspace();
541+				underchar();
542+			}
543+			str = (char *)cursor;
544+			continue;
545+		}
546+		str = (char *)cursor;
547+	}
548+	if (reallydispl) {
549+		flush_display_buffer(buf, &p);
550+		if (pos > 0 || (pos <= 0 && (!AM || XN))) {
551+			putline("\r\n");
552+		}
553+		/*
554+		 * The next should be here! I.e. it may not be before printing
555+		 * the newline. This has to do with XN. We don't know exactly
556+		 * WHEN the terminal will stop ignoring the newline.
557+		 * I have for example a terminal (Ampex a230) that will
558+		 * continue to ignore the newline after a clear to end of line
559+		 * sequence, but not after an end_underline sequence.
560+		 */
561+		if (lastmode) {
562+			end_underline();
563+		}
564+		if (lasthlmode) {
565+			end_bold();
566+		}
567+	}
568+	scr_info.currentpos++;
569+	return str;
570+}
571+
572+static void
573+flush_display_buffer(char *buf, char **p)
574+{
575+	if (*p == buf) {
576+		return;
577+	}
578+	**p = '\0';
579+	putline(buf);
580+	*p = buf;
581+}
582+
583+static int
584+rune_width(Rune r)
585+{
586+	int width;
587+
588+	width = wcwidth((wchar_t)r);
589+	if (width < 0) {
590+		return 1;
591+	}
592+	return width;
593+}
594+
595+static int
596+decode_cell(const char *s, const char **next, int *width, int *is_underscore)
597+{
598+	const char *p;
599+	Rune r;
600+	Rune mark;
601+	int len;
602+	int mark_len;
603+	int mark_width;
604+
605+	len = chartorune(&r, s);
606+	if (len <= 0) {
607+		len = 1;
608+		r = Runeerror;
609+	}
610+	*is_underscore = (len == 1 && *s == '_');
611+	if (*s == '\t') {
612+		*width = -1;
613+		*next = s + 1;
614+		return 1;
615+	}
616+	if ((unsigned char)*s < ' ' || (unsigned char)*s == 0x7f) {
617+		*width = -2;
618+		*next = s + 1;
619+		return 1;
620+	}
621+	*width = rune_width(r);
622+	p = s + len;
623+	while (*p) {
624+		if (*p == '\b' || *p == '\t' || (unsigned char)*p < ' ' ||
625+		    (unsigned char)*p == 0x7f) {
626+			break;
627+		}
628+		mark_len = chartorune(&mark, p);
629+		if (mark_len <= 0) {
630+			break;
631+		}
632+		mark_width = rune_width(mark);
633+		if (mark_width != 0) {
634+			break;
635+		}
636+		p += mark_len;
637+		len += mark_len;
638+	}
639+	*next = p;
640+	return len;
641+}
642+
643+/* ARGSUSED */
644+int
645+setmark(long cnt)
646+{ /* Set a mark on the current page */
647+	struct scr_info *p = &scr_info;
648+	(void)cnt;
649+
650+	p->savfirst = p->firstline;
651+	p->savnf = p->nf;
652+	return 0;
653+}
654+
655+/* ARGSUSED */
656+int
657+tomark(long cnt)
658+{ /* Go to the mark */
659+	struct scr_info *p = &scr_info;
660+	(void)cnt;
661+
662+	(void)display(p->savfirst, p->savnf, pagesize, 1);
663+	return 0;
664+}
665+
666+/* ARGSUSED */
667+int
668+exgmark(long cnt)
669+{ /* Exchange mark and current page */
670+	struct scr_info *p = &scr_info;
671+	long svfirst;
672+	int svnf;
673+	(void)cnt;
674+
675+	svfirst = p->firstline;
676+	svnf = p->nf;
677+	tomark(0L);
678+	p->savfirst = svfirst;
679+	p->savnf = svnf;
680+	return 0;
681+}
682+
683+void
684+d_clean()
685+{ /* Clean up */
686+	struct scr_info *p = &scr_info;
687+
688+	p->savnf = 0;
689+	p->savfirst = 0;
690+	p->head = p->tail;
691+	p->head->line = 0;
692+	p->currentpos = 0;
693+}
+41, -0
 1@@ -0,0 +1,41 @@
 2+#ifndef DISPLAY_H
 3+#define DISPLAY_H
 4+
 5+#define MINPAGESIZE 5
 6+
 7+extern int pagesize;
 8+extern int maxpagesize;
 9+extern int scrollsize;
10+
11+struct scr_info {
12+	struct linelist {
13+		int cnt;
14+		long line;
15+#define firstline head->line
16+#define lastline tail->line
17+		struct linelist *next;
18+		struct linelist *prev;
19+	} *tail, *head;
20+	int nf;
21+	int currentpos;
22+	struct linelist ssaavv;
23+#define savfirst ssaavv.line
24+#define savnf ssaavv.cnt
25+};
26+
27+extern struct scr_info scr_info;
28+extern int status;
29+
30+#define EOFILE 01
31+#define START 02
32+
33+void redraw(int flag);
34+int display(long first_line, int nodispl, int nlines, int really);
35+int scrollf(int nlines, int really);
36+int scrollb(int nlines, int really);
37+int tomark(long cnt);
38+int setmark(long cnt);
39+int exgmark(long cnt);
40+void d_clean(void);
41+
42+#endif
+303, -0
  1@@ -0,0 +1,303 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+/*
  5+ * Command reader, also executes shell escapes
  6+ */
  7+
  8+#include <ctype.h>
  9+#include "in_all.h"
 10+#include "term.h"
 11+#include "process.h"
 12+#include "getcomm.h"
 13+#include "commands.h"
 14+#include "prompt.h"
 15+#include "main.h"
 16+#include "output.h"
 17+#include "getline.h"
 18+#include "machine.h"
 19+#include "keys.h"
 20+#include "display.h"
 21+#include "assert.h"
 22+
 23+static int killchar(int c);
 24+static void sigquit(int signo);
 25+
 26+/*
 27+ * Read a line from the terminal, doing line editing.
 28+ * The parameter s contains the prompt for the line.
 29+ */
 30+
 31+char *
 32+readline(char *s)
 33+{
 34+
 35+	static char buf[80];
 36+	char *p = buf;
 37+	int ch;
 38+	int pos;
 39+
 40+	clrbline();
 41+	putline(s);
 42+	pos = strlen(s);
 43+	while ((ch = getch()) != '\n' && ch != '\r') {
 44+		if (ch == -1) {
 45+			/*
 46+			 * Can only occur because of an interrupted read.
 47+			 */
 48+			ch = erasech;
 49+			interrupt = 0;
 50+		}
 51+		if (ch == erasech) {
 52+			/*
 53+			 * Erase last char
 54+			 */
 55+			if (p == buf) {
 56+				/*
 57+				 * There was none, so return
 58+				 */
 59+				return (char *)0;
 60+			}
 61+			pos -= killchar(*--p);
 62+			if (*p != '\\')
 63+				continue;
 64+		}
 65+		if (ch == killch) {
 66+			/*
 67+			 * Erase the whole line
 68+			 */
 69+			if (!(p > buf && *(p - 1) == '\\')) {
 70+				while (p > buf) {
 71+					pos -= killchar(*--p);
 72+				}
 73+				continue;
 74+			}
 75+			pos -= killchar(*--p);
 76+		}
 77+		if (p > &buf[78] || pos >= COLS - 2) {
 78+			/*
 79+			 * Line does not fit.
 80+			 * Simply refuse to make it any longer
 81+			 */
 82+			pos -= killchar(*--p);
 83+		}
 84+		*p++ = ch;
 85+		if (ch < ' ' || ch >= 0177) {
 86+			fputch('^');
 87+			pos++;
 88+			ch ^= 0100;
 89+		}
 90+		fputch(ch);
 91+		pos++;
 92+	}
 93+	fputch('\r');
 94+	*p++ = '\0';
 95+	flush();
 96+	return buf;
 97+}
 98+
 99+/*
100+ * Erase a character from the command line.
101+ */
102+
103+static int
104+killchar(int c)
105+{
106+
107+	backspace();
108+	putch(' ');
109+	backspace();
110+	if (c < ' ' || c >= 0177) {
111+		(void)killchar(' ');
112+		return 2;
113+	}
114+	return 1;
115+}
116+
117+/*
118+ * Do a shell escape, after expanding '%' and '!'.
119+ */
120+
121+void
122+shellescape(char *p, int esc_char)
123+{
124+	char *p2;      /* walks through command */
125+	int id;        /* procid of child */
126+	int cnt;       /* prevent array bound errors */
127+	int lastc = 0; /* will contain the previous char */
128+#ifdef SIGTSTP
129+	void (*savetstp)(int);
130+#endif
131+	static char previous[256]; /* previous command */
132+	char comm[256];            /* space for command */
133+	int piped[2];
134+
135+	p2 = comm;
136+	*p2++ = esc_char;
137+	cnt = 253;
138+	while (*p) {
139+		/*
140+		 * expand command
141+		 */
142+		switch (*p++) {
143+		case '!':
144+			/*
145+			 * An unescaped ! expands to the previous
146+			 * command, but disappears if there is none
147+			 */
148+			if (lastc != '\\') {
149+				if (*previous) {
150+					id = strlen(previous);
151+					if ((cnt -= id) <= 0)
152+						break;
153+					(void)strcpy(p2, previous);
154+					p2 += id;
155+				}
156+			} else {
157+				*(p2 - 1) = '!';
158+			}
159+			continue;
160+		case '%':
161+			/*
162+			 * An unescaped % will expand to the current
163+			 * filename, but disappears is there is none
164+			 */
165+			if (lastc != '\\') {
166+				if (nopipe) {
167+					id = strlen(currentfile);
168+					if ((cnt -= id) <= 0)
169+						break;
170+					(void)strcpy(p2, currentfile);
171+					p2 += id;
172+				}
173+			} else {
174+				*(p2 - 1) = '%';
175+			}
176+			continue;
177+		default:
178+			lastc = *(p - 1);
179+			if (cnt-- <= 0)
180+				break;
181+			*p2++ = lastc;
182+			continue;
183+		}
184+		break;
185+	}
186+	clrbline();
187+	*p2 = '\0';
188+	if (!stupid) {
189+		/*
190+		 * Display expanded command
191+		 */
192+		cputline(comm);
193+		putline("\r\n");
194+	}
195+	flush();
196+	(void)strcpy(previous, comm + 1);
197+	resettty();
198+	if (esc_char == '|' && pipe(piped) < 0) {
199+		error("Cannot create pipe");
200+		return;
201+	}
202+	if ((id = fork()) < 0) {
203+		error("Cannot fork");
204+		return;
205+	}
206+	if (id == 0) {
207+		if (esc_char == '|') {
208+			close(piped[1]);
209+			close(0);
210+			fcntl(piped[0], F_DUPFD, 0);
211+			close(piped[0]);
212+		}
213+		execl("/bin/sh", "sh", "-c", comm + 1, (char *)0);
214+		exit(1);
215+	}
216+	(void)signal(SIGINT, SIG_IGN);
217+	(void)signal(SIGQUIT, SIG_IGN);
218+#ifdef SIGTSTP
219+	if ((savetstp = signal(SIGTSTP, SIG_IGN)) != SIG_IGN) {
220+		(void)signal(SIGTSTP, SIG_DFL);
221+	}
222+#endif
223+	if (esc_char == '|') {
224+		(void)close(piped[0]);
225+		(void)signal(SIGPIPE, SIG_IGN);
226+		wrt_fd(piped[1]);
227+		(void)close(piped[1]);
228+	}
229+	while ((lastc = wait((int *)0)) != id && lastc >= 0) {
230+		/*
231+		 * Wait for child, making sure it is the one we expected ...
232+		 */
233+	}
234+	(void)signal(SIGINT, catchdel);
235+	(void)signal(SIGQUIT, sigquit);
236+#ifdef SIGTSTP
237+	(void)signal(SIGTSTP, savetstp);
238+#endif
239+	inittty();
240+}
241+
242+static void
243+sigquit(int signo)
244+{
245+	(void)signo;
246+	quit();
247+}
248+
249+/*
250+ * Get all those commands ...
251+ */
252+
253+int
254+getcomm(long *plong)
255+{
256+	int c;
257+	long count;
258+	char *p;
259+	int i;
260+	int j;
261+	char buf[10];
262+
263+	for (;;) {
264+		count = 0;
265+		give_prompt();
266+		while (isdigit((c = getch()))) {
267+			count = count * 10 + (c - '0');
268+		}
269+		*plong = count;
270+		p = buf;
271+		for (;;) {
272+			if (c == -1) {
273+				/*
274+				 * This should never happen, but it does,
275+				 * when the user gives a TSTP signal (^Z) or
276+				 * an interrupt while the program is trying
277+				 * to read a character from the terminal.
278+				 * In this case, the read is interrupted, so
279+				 * we end up here.
280+				 * Right, we will have to read again.
281+				 */
282+				if (interrupt)
283+					return 1;
284+				break;
285+			}
286+			*p++ = c;
287+			*p = 0;
288+			if ((i = match(buf, &j, currmap->k_mach)) > 0) {
289+				/*
290+				 * The key sequence matched. We have a command
291+				 */
292+				return j;
293+			}
294+			if (i == 0)
295+				return 0;
296+			/*
297+			 * We have a prefix of a command.
298+			 */
299+			assert(i == FSM_ISPREFIX);
300+			c = getch();
301+		}
302+	}
303+	/* NOTREACHED */
304+}
+8, -0
1@@ -0,0 +1,8 @@
2+#ifndef GETCOMM_H
3+#define GETCOMM_H
4+
5+int getcomm(long *arg);
6+void shellescape(char *command, int esc_char);
7+char *readline(char *prompt);
8+
9+#endif
+475, -0
  1@@ -0,0 +1,475 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "getline.h"
  6+#include "options.h"
  7+#include "process.h"
  8+#include "term.h"
  9+#include "main.h"
 10+#include "display.h"
 11+#include "output.h"
 12+#include "prompt.h"
 13+#include "assert.h"
 14+
 15+#define BLOCKSIZE 2048 /* size of blocks */
 16+#define CHUNK 50       /* # of blockheaders allocated at a time */
 17+
 18+/*
 19+ * Blocks are kept in an array in line-number order. Each block stores the raw
 20+ * text and the offsets of the lines parsed from it.
 21+ */
 22+
 23+struct block {
 24+	int b_flags;  /* Contains the following flags: */
 25+#define PARTLY 02     /* block not filled completely (eof) */
 26+	long b_end;   /* line number of last line in block */
 27+	char *b_info; /* the block */
 28+	int *b_offs;  /* line offsets within the block */
 29+};
 30+
 31+static struct block *blocklist, /* beginning of the list of blocks */
 32+    *maxblocklist,              /* first free entry in the list */
 33+    *topblocklist;              /* end of allocated core for the list */
 34+static long lastreadline; /* lineno of last line read */
 35+static int ENDseen;
 36+
 37+static void nextblock(struct block *pblock);
 38+static char *re_alloc(char *ptr, unsigned newsize);
 39+static struct block *getblock(long n, int disable_interrupt);
 40+
 41+static struct block *
 42+new_block()
 43+{
 44+	struct block *pblock = maxblocklist - 1;
 45+
 46+	if (!maxblocklist || !(pblock->b_flags & PARTLY)) {
 47+		/*
 48+		 * There is no last block, or it was filled completely,
 49+		 * so allocate a new blockheader.
 50+		 */
 51+		int siz;
 52+
 53+		pblock = blocklist;
 54+		if (maxblocklist == topblocklist) {
 55+			/*
 56+			 * No blockheaders left. Allocate new ones
 57+			 */
 58+			siz = topblocklist - pblock;
 59+			blocklist = pblock = (struct block *)
 60+			    re_alloc((char *)pblock,
 61+			             (unsigned)((siz + CHUNK) * sizeof(*pblock)));
 62+			pblock += siz;
 63+			topblocklist = pblock + CHUNK;
 64+			maxblocklist = pblock;
 65+			for (; pblock < topblocklist; pblock++) {
 66+				pblock->b_end = 0;
 67+				pblock->b_info = 0;
 68+				pblock->b_flags = 0;
 69+			}
 70+			if (!siz) {
 71+				/*
 72+				 * Create dummy header cell.
 73+				 */
 74+				maxblocklist++;
 75+			}
 76+		}
 77+		pblock = maxblocklist++;
 78+	}
 79+	nextblock(pblock);
 80+	return pblock;
 81+}
 82+
 83+/*
 84+ * Return the block in which line 'n' of the current file can be found.
 85+ * If "disable_interrupt" = 0, the call may be interrupted, in which
 86+ * case it returns 0.
 87+ */
 88+
 89+static struct block *
 90+getblock(long n, int disable_interrupt)
 91+{
 92+	struct block *pblock;
 93+
 94+	if (stdf < 0) {
 95+		/*
 96+		 * Not file descriptor, so return end of file
 97+		 */
 98+		return 0;
 99+	}
100+	pblock = maxblocklist - 1;
101+	if (n < lastreadline ||
102+	    (n == lastreadline && !(pblock->b_flags & PARTLY))) {
103+		/*
104+		 * The line asked for has been read already.
105+		 * Perform binary search in the blocklist to find the block
106+		 * where it's in.
107+		 */
108+		struct block *min, *mid;
109+
110+		min = blocklist + 1;
111+		do {
112+			mid = min + (pblock - min) / 2;
113+			if (n > mid->b_end) {
114+				min = mid + 1;
115+			} else
116+				pblock = mid;
117+		} while (min < pblock);
118+		/* Found, pblock is now a reference to the block wanted */
119+		return pblock;
120+	}
121+
122+	/*
123+	 * The line was'nt read yet, so read blocks until found
124+	 */
125+	for (;;) {
126+		if (interrupt && !disable_interrupt)
127+			return 0;
128+		pblock = new_block();
129+		if (pblock->b_end >= n) {
130+			return pblock;
131+		}
132+		if (pblock->b_flags & PARTLY) {
133+			/*
134+			 * We did not find it, and the last block could not be
135+			 * read completely, so return 0;
136+			 */
137+			return 0;
138+		}
139+	}
140+	/* NOTREACHED */
141+}
142+
143+char *
144+getline(long n, int disable_interrupt)
145+{
146+	struct block *pblock;
147+
148+	if (!(pblock = getblock(n, disable_interrupt))) {
149+		return (char *)0;
150+	}
151+	return pblock->b_info + pblock->b_offs[n - ((pblock - 1)->b_end + 1)];
152+}
153+
154+/*
155+ * Find the last line of the input, and return its number
156+ */
157+
158+long
159+to_lastline()
160+{
161+
162+	for (;;) {
163+		if (!getline(lastreadline + 1, 0)) {
164+			/*
165+			 * "lastreadline" always contains the linenumber of
166+			 * the last line read. So, if the call to getline
167+			 * succeeds, "lastreadline" is affected
168+			 */
169+			if (interrupt)
170+				return -1L;
171+			return lastreadline;
172+		}
173+	}
174+	/* NOTREACHED */
175+}
176+
177+char *
178+alloc(unsigned size)
179+{
180+	char *pmem;
181+
182+	pmem = malloc(size);
183+	if (!pmem && size != 0) {
184+		panic("No core");
185+	}
186+	return pmem;
187+}
188+
189+/*
190+ * Re-allocate the memorychunk pointed to by ptr, to let it
191+ * grow or shrink.
192+ */
193+
194+static char *
195+re_alloc(char *ptr, unsigned newsize)
196+{
197+	char *pmem;
198+
199+	pmem = realloc(ptr, newsize);
200+	if (!pmem && newsize != 0) {
201+		panic("No core");
202+	}
203+	return pmem;
204+}
205+
206+static char *saved;
207+static long filldegree;
208+
209+/*
210+ * Try to read the block indicated by pblock
211+ */
212+
213+static void
214+nextblock(struct block *pblock)
215+{
216+	char *c,         /* Run through pblock->b_info */
217+	    *c1;                  /* indicate end of pblock->b_info */
218+	int *poff;       /* pointer in line-offset list */
219+	int cnt;         /* # of characters read */
220+	unsigned siz;    /* Size of allocated line-offset list */
221+	static unsigned savedsiz; /* saved "siz" */
222+	static int *savedpoff;    /* saved "poff" */
223+	static char *savedc1;     /* saved "c1" */
224+
225+	if (pblock->b_flags & PARTLY) {
226+		/*
227+		 * The block was already partly filled. Initialize locals
228+		 * accordingly
229+		 */
230+		poff = savedpoff;
231+		siz = savedsiz;
232+		pblock->b_flags = 0;
233+		c1 = savedc1;
234+		if (c1 == pblock->b_info || *(c1 - 1)) {
235+			/*
236+			 * We had incremented "lastreadline" temporarily,
237+			 * because the last line could not be completely read
238+			 * last time we tried. Undo this increment
239+			 */
240+			poff--;
241+			--lastreadline;
242+		}
243+	} else {
244+		if (saved) {
245+			/*
246+			 * There were leftovers from the previous block
247+			 */
248+			pblock->b_info = saved;
249+			c1 = savedc1;
250+			saved = 0;
251+		} else { /* Allocate new block */
252+			pblock->b_info = c1 = alloc(BLOCKSIZE + 1);
253+		}
254+		/*
255+		 * Allocate some space for line-offsets
256+		 */
257+		pblock->b_offs = poff = (int *)
258+		    alloc((unsigned)(100 * sizeof(int)));
259+		siz = 99;
260+		*poff++ = 0;
261+	}
262+	c = c1;
263+	for (;;) {
264+		/*
265+		 * Read loop
266+		 */
267+		cnt = read(stdf, c1, BLOCKSIZE - (c1 - pblock->b_info));
268+		if (cnt < 0) {
269+			/*
270+			 * Interrupted read
271+			 */
272+			if (errno == EINTR)
273+				continue;
274+			error("Could not read input file");
275+			cnt = 0;
276+		}
277+		c1 += cnt;
278+		if (c1 != pblock->b_info + BLOCKSIZE) {
279+			ENDseen = 1;
280+			pblock->b_flags |= PARTLY;
281+		}
282+		break;
283+	}
284+	assert(c <= c1);
285+	while (c < c1) {
286+		/*
287+		 * Now process the block
288+		 */
289+		if (*c == '\n') {
290+			/*
291+			 * Newlines are replaced by '\0', so that "getline"
292+			 * can deliver one line at a time
293+			 */
294+			*c = 0;
295+			lastreadline++;
296+			/*
297+			 * Remember the line-offset
298+			 */
299+			if (poff == pblock->b_offs + siz) {
300+				/*
301+				 * No space for it, allocate some more
302+				 */
303+				pblock->b_offs = (int *)
304+				    re_alloc((char *)pblock->b_offs,
305+				             (unsigned)((siz + 51) * sizeof(int)));
306+				poff = pblock->b_offs + siz;
307+				siz += 50;
308+			}
309+			*poff++ = c - pblock->b_info + 1;
310+		}
311+		c++;
312+	}
313+	assert(c == c1);
314+	*c = 0;
315+	if (c != pblock->b_info && *(c - 1) != 0) {
316+		/*
317+		 * The last line read does not end with a newline, so add one
318+		 */
319+		lastreadline++;
320+		*poff++ = c - pblock->b_info + 1;
321+		if (!(pblock->b_flags & PARTLY) && *(poff - 2) != 0) {
322+			/*
323+			 * Save the started line; it will be in the next block.
324+			 * Remove the newline we added just now.
325+			 */
326+			saved = c1 = alloc(BLOCKSIZE + 1);
327+			c = pblock->b_info + *(--poff - 1);
328+			while (*c)
329+				*c1++ = *c++;
330+			c = pblock->b_info + *(poff - 1);
331+			savedc1 = c1;
332+			--lastreadline;
333+		}
334+	}
335+	pblock->b_end = lastreadline;
336+	if (pblock->b_flags & PARTLY) {
337+		/*
338+		 * Take care, that we can call "nextblock" again, to fill in
339+		 * the rest of this block
340+		 */
341+		savedsiz = siz;
342+		savedpoff = poff;
343+		savedc1 = c;
344+		if (c == pblock->b_info) {
345+			lastreadline++;
346+			pblock->b_end = 0;
347+		}
348+	} else {
349+		cnt = pblock - blocklist;
350+		filldegree = ((c - pblock->b_info) + (cnt - 1) * filldegree) / cnt;
351+	}
352+	assert(pblock->b_end - (pblock - 1)->b_end <= poff - pblock->b_offs);
353+}
354+
355+/*
356+ * Called after processing a file.
357+ * Free all core.
358+ */
359+
360+void
361+do_clean()
362+{
363+
364+	struct block *pblock;
365+	char *p;
366+
367+	for (pblock = blocklist; pblock < maxblocklist; pblock++) {
368+		if ((p = pblock->b_info) != 0) {
369+			free(p);
370+			free((char *)pblock->b_offs);
371+		}
372+	}
373+	if ((p = (char *)blocklist) != 0) {
374+		free(p);
375+	}
376+	blocklist = 0;
377+	maxblocklist = 0;
378+	topblocklist = 0;
379+	lastreadline = 0;
380+	filldegree = 0;
381+	ENDseen = 0;
382+	if ((p = saved) != 0)
383+		free(p);
384+	saved = 0;
385+}
386+
387+/*
388+ * Get a character. If possible, do some workahead.
389+ */
390+
391+int
392+getch()
393+{
394+	int flags, bytes_ready, bytes_read;
395+	struct stat buf;
396+	char c;
397+
398+	flush();
399+	if (startcomm) {
400+		/*
401+		 * Command line option command
402+		 */
403+		if (*startcomm)
404+			return *startcomm++;
405+		return '\n';
406+	}
407+	if (stdf >= 0) {
408+		/*
409+		 * Make reads from the terminal non-blocking, so that
410+		 * we can see if the user typed something
411+		 */
412+		flags = fcntl(0, F_GETFL, 0);
413+		if (flags != -1 && fcntl(0, F_SETFL, flags | O_NONBLOCK) != -1) {
414+			bytes_read = 0;
415+			while (!ENDseen &&
416+			       ((bytes_read = read(0, &c, 1)) == 0
417+#ifdef EWOULDBLOCK
418+			        || (bytes_read < 0 && errno == EWOULDBLOCK)
419+#endif
420+#ifdef EAGAIN
421+			        || (bytes_read < 0 && errno == EAGAIN)
422+#endif
423+			            ) &&
424+			       (nopipe ||
425+			        (fstat(stdf, &buf) >= 0 && buf.st_size > 0))) {
426+				/*
427+				 * Do some read ahead, after making sure there
428+				 * is input and the user did not type a command
429+				 */
430+				new_block();
431+			}
432+			(void)fcntl(0, F_SETFL, flags);
433+			if (bytes_read < 0) {
434+				/*
435+				 * Could this have happened?
436+				 * I'm not sure, because the read is
437+				 * nonblocking. Can it be interrupted then?
438+				 */
439+				return -1;
440+			}
441+			if (bytes_read > 0)
442+				return c & 0x7f;
443+		}
444+	}
445+	if (ioctl(0, FIONREAD, (char *)&bytes_ready) >= 0 && stdf >= 0) {
446+		while (!ENDseen &&
447+		       bytes_ready == 0 &&
448+		       (nopipe || (fstat(stdf, &buf) >= 0 && buf.st_size > 0))) {
449+			if (interrupt)
450+				return -1;
451+			new_block();
452+			if (ioctl(0, FIONREAD, (char *)&bytes_ready) < 0) {
453+				break;
454+			}
455+		}
456+	}
457+	if (read(0, &c, 1) <= 0)
458+		return -1;
459+	return c & 0x7f;
460+}
461+
462+/*
463+ * Get the position of line "ln" in the file.
464+ */
465+
466+long
467+getpos(long ln)
468+{
469+	struct block *pblock;
470+	long i;
471+
472+	pblock = getblock(ln, 1);
473+	assert(pblock != 0);
474+	i = filldegree * (pblock - blocklist);
475+	return i - (filldegree - pblock->b_offs[ln - (pblock - 1)->b_end]);
476+}
+11, -0
 1@@ -0,0 +1,11 @@
 2+#ifndef GETLINE_H
 3+#define GETLINE_H
 4+
 5+char *getline(long ln, int disable_interrupt);
 6+char *alloc(unsigned size);
 7+void do_clean(void);
 8+int getch(void);
 9+long to_lastline(void);
10+long getpos(long line);
11+
12+#endif
A help.c
+112, -0
  1@@ -0,0 +1,112 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "help.h"
  6+#include "machine.h"
  7+#include "commands.h"
  8+#include "keys.h"
  9+#include "output.h"
 10+#include "prompt.h"
 11+#include "main.h"
 12+#include "display.h"
 13+#include "term.h"
 14+#include "options.h"
 15+
 16+static int h_cnt;            /* Count # of lines */
 17+static struct state *origin; /* Keep track of startstate */
 18+
 19+/*
 20+ * Print a key sequence.
 21+ * We arrived at an endstate. The s_next link in the state structure now
 22+ * leads us from "origin" to the current state, so that we can print the key
 23+ * sequence easily.
 24+ */
 25+
 26+static void
 27+pr_comm()
 28+{
 29+	struct state *p = origin;
 30+	char *pb;
 31+	int c;
 32+	char buf[30];
 33+	int i = 0; /* How many characters printed? */
 34+
 35+	pb = buf;
 36+	for (;;) {
 37+		c = p->s_char & 0177;
 38+		if (c < ' ' || c == 0177) {
 39+			/*
 40+			 * Will take an extra position
 41+			 */
 42+			i++;
 43+		}
 44+		*pb++ = c;
 45+		i++;
 46+		if (!p->s_match)
 47+			break;
 48+		p = p->s_next;
 49+	}
 50+	do {
 51+		*pb++ = ' ';
 52+	} while (++i < 12);
 53+	*pb = 0;
 54+	cputline(buf);
 55+}
 56+
 57+/*
 58+ * Print out a description of the keymap. This is done, by temporarily using
 59+ * the s_next field in the state structure indicate the state matching the
 60+ * next character, so that we can walk from "origin" to an endstate.
 61+ */
 62+
 63+static void
 64+pr_mach(struct state *currstate, struct state *back)
 65+{
 66+	struct state *save;
 67+
 68+	while (currstate) {
 69+		if (interrupt)
 70+			break;
 71+		if (back) {
 72+			save = back->s_next; /* Save original link */
 73+			back->s_next = currstate;
 74+		}
 75+		if (!currstate->s_match) {
 76+			/*
 77+			 * End state, print command
 78+			 */
 79+			pr_comm();
 80+			putline(commands[currstate->s_cnt].c_descr);
 81+			putline("\r\n");
 82+			if (++h_cnt >= maxpagesize) {
 83+				ret_to_continue();
 84+				h_cnt = 0;
 85+			}
 86+		} else
 87+			pr_mach(currstate->s_match, currstate);
 88+		currstate = currstate->s_next;
 89+		if (back)
 90+			back->s_next = save; /* restore */
 91+		else
 92+			origin = currstate;
 93+	}
 94+}
 95+
 96+/*ARGSUSED*/
 97+int
 98+do_help(long i)
 99+{ /* The help command */
100+	(void)i;
101+
102+	startcomm = 0;
103+	h_cnt = 2;
104+	putline("\r\nSummary of yap commands:\r\n");
105+	origin = currmap->k_mach;
106+	pr_mach(currmap->k_mach, (struct state *)0);
107+	if (h_cnt) {
108+		ret_to_continue();
109+	}
110+	if (!hardcopy && scr_info.currentpos)
111+		redraw(1);
112+	return 0;
113+}
A help.h
+6, -0
1@@ -0,0 +1,6 @@
2+#ifndef HELP_H
3+#define HELP_H
4+
5+int do_help(long cnt);
6+
7+#endif
+23, -0
 1@@ -0,0 +1,23 @@
 2+#ifndef IN_ALL_H
 3+#define IN_ALL_H
 4+
 5+#define _DEFAULT_SOURCE
 6+
 7+#include <errno.h>
 8+#include <fcntl.h>
 9+#include <libgen.h>
10+#include <locale.h>
11+#include <regex.h>
12+#include <signal.h>
13+#include <stddef.h>
14+#include <stdlib.h>
15+#include <string.h>
16+#include <sys/ioctl.h>
17+#include <sys/stat.h>
18+#include <sys/types.h>
19+#include <sys/wait.h>
20+#include <termios.h>
21+#include <unistd.h>
22+#include <wchar.h>
23+
24+#endif
A keys.c
+187, -0
  1@@ -0,0 +1,187 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include <ctype.h>
  5+#include "in_all.h"
  6+#include "machine.h"
  7+#include "keys.h"
  8+#include "commands.h"
  9+#include "prompt.h"
 10+#include "assert.h"
 11+
 12+struct keymap *currmap, *othermap;
 13+
 14+char defaultmap[] = "\
 15+bf=P:bl=k:bl=^K:bl=^[[A:bot=l:bot=$:bp=-:bs=^B:bse=?:bsl=S:bsp=F:chm=X:exg=x:\
 16+ff=N:fl=^J:fl=^M:fl=j:fl=^[[B:fp= :fs=^D:fse=/:fsl=s:fsp=f:hlp=h:nse=n:nsr=r:\
 17+red=^L:rep=.:bps=Z:bss=b:fps=z:fss=d:shl=!:tom=':top=\\^:vis=e:\
 18+wrf=w:qui=q:qui=Q:mar=m:pip=|";
 19+
 20+/*
 21+ * Construct an error message and return it
 22+ */
 23+
 24+static char *
 25+kerror(char *key, char *emess)
 26+{
 27+	static char ebuf[80]; /* Room for the error message */
 28+
 29+	(void)strcpy(ebuf, key);
 30+	(void)strcat(ebuf, emess);
 31+	return ebuf;
 32+}
 33+
 34+/*
 35+ * Compile a keymap into commtable. Returns an error message if there
 36+ * is one
 37+ */
 38+
 39+static char *
 40+compile(char *map, struct keymap *commtable)
 41+{
 42+	char *mark; /* Indicates start of mnemonic */
 43+	char *c;    /* Runs through buf */
 44+	int temp;
 45+	char *escapes = commtable->k_esc;
 46+	char buf[10]; /* Will hold key sequence */
 47+
 48+	(void)strcpy(commtable->k_help, "Illegal command");
 49+	while (*map) {
 50+		c = buf;
 51+		mark = map; /* Start of mnemonic */
 52+		while (*map && *map != '=') {
 53+			map++;
 54+		}
 55+		if (!*map) {
 56+			/*
 57+			 * Mnemonic should end with '='
 58+			 */
 59+			return kerror(mark, ": Syntax error");
 60+		}
 61+		*map++ = 0;
 62+		while (*map) {
 63+			/*
 64+			 * Get key sequence
 65+			 */
 66+			if (*map == ':') {
 67+				/*
 68+				 * end of key sequence
 69+				 */
 70+				map++;
 71+				break;
 72+			}
 73+			*c = *map++ & 0x7f;
 74+			if (*c == '^' || *c == '\\') {
 75+				if (!(temp = *map++)) {
 76+					/*
 77+					 * Escape not followed by a character
 78+					 */
 79+					return kerror(mark, ": Syntax error");
 80+				}
 81+				if (*c == '^') {
 82+					if (temp == '?')
 83+						*c = 0x7f;
 84+					else
 85+						*c = temp & 0x1f;
 86+				} else
 87+					*c = temp & 0x7f;
 88+			}
 89+			setused(*c);
 90+			c++;
 91+			if (c >= &buf[9]) {
 92+				return kerror(mark, ": Key sequence too long");
 93+			}
 94+		}
 95+		*c = 0;
 96+		if (!(temp = lookup(mark))) {
 97+			return kerror(mark, ": Nonexistent function");
 98+		}
 99+		if (c == &buf[1] && (commands[temp].c_flags & ESC) &&
100+		    escapes < &(commtable->k_esc[sizeof(commtable->k_esc) - 1])) {
101+			*escapes++ = buf[0] & 0x7f;
102+		}
103+		temp = addstring(buf, temp, &(commtable->k_mach));
104+		if (temp == FSM_ISPREFIX) {
105+			return kerror(mark, ": Prefix of other key sequence");
106+		}
107+		if (temp == FSM_HASPREFIX) {
108+			return kerror(mark, ": Other key sequence is prefix");
109+		}
110+		assert(temp == FSM_OKE);
111+		if (!strcmp(mark, "hlp")) {
112+			/*
113+			 * Create an error message to be given when the user
114+			 * types an illegal command
115+			 */
116+			(void)strcpy(commtable->k_help, "Type ");
117+			(void)strcat(commtable->k_help, buf);
118+			(void)strcat(commtable->k_help, " for help");
119+		}
120+	}
121+	*escapes = 0;
122+	return (char *)0;
123+}
124+
125+/*
126+ * Initialize the keymaps
127+ */
128+
129+void
130+initkeys()
131+{
132+	char *p;
133+	static struct keymap xx[2];
134+
135+	currmap = &xx[0];
136+	othermap = &xx[1];
137+	p = compile(defaultmap, currmap); /* Compile default map */
138+	assert(p == (char *)0);
139+	p = getenv("YAPKEYS");
140+	if (p) {
141+		if (!(p = compile(p, othermap))) {
142+			/*
143+			 * No errors in user defined keymap. So, use it
144+			 */
145+			do_chkm(0L);
146+			return;
147+		}
148+		error(p);
149+	}
150+	othermap = 0; /* No other keymap */
151+}
152+
153+int
154+is_escape(int c)
155+{
156+	char *p = currmap->k_esc;
157+
158+	while (*p) {
159+		if (c == *p++)
160+			return 1;
161+	}
162+	return 0;
163+}
164+
165+static char keyset[16]; /* bitset indicating which keys are
166+                         * used
167+                         */
168+/*
169+ * Mark key "key" as used
170+ */
171+
172+void
173+setused(int key)
174+{
175+
176+	keyset[(key & 0x7f) >> 3] |= (1 << (key & 0x07));
177+}
178+
179+/*
180+ * return non-zero if key "key" is used in a keymap
181+ */
182+
183+int
184+isused(int key)
185+{
186+
187+	return keyset[(key & 0x7f) >> 3] & (1 << (key & 0x07));
188+}
A keys.h
+15, -0
 1@@ -0,0 +1,15 @@
 2+#ifndef KEYS_H
 3+#define KEYS_H
 4+
 5+extern struct keymap {
 6+	char k_help[80];
 7+	struct state *k_mach;
 8+	char k_esc[10];
 9+} *currmap, *othermap;
10+
11+void initkeys(void);
12+void setused(int key);
13+int isused(int key);
14+int is_escape(int c);
15+
16+#endif
+134, -0
  1@@ -0,0 +1,134 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include <ctype.h>
  5+#include "in_all.h"
  6+#include "machine.h"
  7+#include "getline.h"
  8+#include "assert.h"
  9+
 10+/*
 11+ * Add part of finite state machine to recognize the string s.
 12+ */
 13+
 14+static int
 15+addtomach(char *s, int cnt, struct state **list)
 16+{
 17+
 18+	struct state *l;
 19+	int i = FSM_OKE; /* Return value */
 20+	int j;
 21+
 22+	for (;;) {
 23+		l = *list;
 24+		if (!l) {
 25+			/*
 26+			 * Create new list element
 27+			 */
 28+			*list = l = (struct state *)alloc(sizeof(*l));
 29+			l->s_char = *s;
 30+			l->s_endstate = 0;
 31+			l->s_match = 0;
 32+			l->s_next = 0;
 33+		}
 34+		if (l->s_char == *s) {
 35+			/*
 36+			 * Continue with next character
 37+			 */
 38+			if (!*++s) {
 39+				/*
 40+				 * No next character
 41+				 */
 42+				j = l->s_endstate;
 43+				l->s_endstate = 1;
 44+				if (l->s_match || j) {
 45+					/*
 46+					 * If the state already was an endstate,
 47+					 * or has a successor, the currently
 48+					 * added string is a prefix of an
 49+					 * already recognized string
 50+					 */
 51+					return FSM_ISPREFIX;
 52+				}
 53+				l->s_cnt = cnt;
 54+				return i;
 55+			}
 56+			if (l->s_endstate) {
 57+				/*
 58+				 * In this case, the currently added string has
 59+				 * a prefix that is an already recognized
 60+				 * string.
 61+				 */
 62+				i = FSM_HASPREFIX;
 63+			}
 64+			list = &(l->s_match);
 65+			continue;
 66+		}
 67+		list = &(l->s_next);
 68+	}
 69+	/* NOTREACHED */
 70+}
 71+
 72+/*
 73+ * Add a string to the FSM.
 74+ */
 75+
 76+int
 77+addstring(char *s, int cnt, struct state **machine)
 78+{
 79+
 80+	if (!s || !*s) {
 81+		return FSM_ISPREFIX;
 82+	}
 83+	return addtomach(s, cnt, machine);
 84+}
 85+
 86+/*
 87+ * Match string s with the finite state machine.
 88+ * If it matches, the number of characters actually matched is returned,
 89+ * and the count is put in the word pointed to by i.
 90+ * If the string is a prefix of a string that could be matched,
 91+ * FSM_ISPREFIX is returned. Otherwise, 0 is returned.
 92+ */
 93+
 94+int
 95+match(char *s, int *i, struct state *mach)
 96+{
 97+
 98+	char *s1 = s; /* Walk through string */
 99+	struct state *mach1 = 0;
100+	/* Keep track of previous state */
101+
102+	while (mach && *s1) {
103+		if (mach->s_char == *s1) {
104+			/*
105+			 * Current character matches. Carry on with next
106+			 * character and next state
107+			 */
108+			mach1 = mach;
109+			mach = mach->s_match;
110+			s1++;
111+			continue;
112+		}
113+		mach = mach->s_next;
114+	}
115+	if (!mach1) {
116+		/*
117+		 * No characters matched
118+		 */
119+		return 0;
120+	}
121+	if (mach1->s_endstate) {
122+		/*
123+		 * The string matched
124+		 */
125+		*i = mach1->s_cnt;
126+		return s1 - s;
127+	}
128+	if (!*s1) {
129+		/*
130+		 * The string matched a prefix
131+		 */
132+		return FSM_ISPREFIX;
133+	}
134+	return 0;
135+}
+19, -0
 1@@ -0,0 +1,19 @@
 2+#ifndef MACHINE_H
 3+#define MACHINE_H
 4+
 5+struct state {
 6+	char s_char;
 7+	char s_endstate;
 8+	struct state *s_match;
 9+	struct state *s_next;
10+	short s_cnt;
11+};
12+
13+#define FSM_OKE 0
14+#define FSM_ISPREFIX -1
15+#define FSM_HASPREFIX 1
16+
17+int addstring(char *str, int cnt, struct state **mach);
18+int match(char *str, int *p_int, struct state *mach);
19+
20+#endif
A main.c
+195, -0
  1@@ -0,0 +1,195 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "main.h"
  6+#include "term.h"
  7+#include "options.h"
  8+#include "output.h"
  9+#include "process.h"
 10+#include "commands.h"
 11+#include "display.h"
 12+#include "prompt.h"
 13+
 14+int nopipe;
 15+char *progname;
 16+int interrupt;
 17+int no_tty;
 18+
 19+static int initialize(int x);
 20+static void sigquit(int signo);
 21+#ifdef SIGTSTP
 22+static void suspsig(int signo);
 23+#endif
 24+
 25+int
 26+main(int argc, char **argv)
 27+{
 28+	char **av;
 29+
 30+	(void)setlocale(LC_CTYPE, "");
 31+	if (!isatty(1)) {
 32+		no_tty = 1;
 33+	}
 34+	argv[argc] = 0;
 35+	progname = argv[0];
 36+	if ((av = readoptions(argv)) == (char **)0 ||
 37+	    initialize(*av ? 1 : 0)) {
 38+		if (no_tty) {
 39+			close(1);
 40+			(void)dup(2);
 41+		}
 42+		putline("Usage: ");
 43+		putline(argv[0]);
 44+		putline(
 45+		    " [-c] [-u] [-n] [-q] [-number] [+command] [file ... ]\n");
 46+		flush();
 47+		exit(1);
 48+	}
 49+	if (no_tty) {
 50+		*--av = "cat";
 51+		execve("/bin/cat", av, (char *const[]){NULL});
 52+	} else
 53+		processfiles(argc - (av - argv), av);
 54+	(void)quit();
 55+	/* NOTREACHED */
 56+}
 57+
 58+/*
 59+ * Open temporary file for reading and writing.
 60+ * Panic if it fails
 61+ */
 62+
 63+/*
 64+ * Collect initializing stuff here.
 65+ */
 66+
 67+static int
 68+initialize(int x)
 69+{
 70+
 71+	if (!(nopipe = x)) {
 72+		/*
 73+		 * Reading from pipe
 74+		 */
 75+		if (isatty(0)) {
 76+			return 1;
 77+		}
 78+		stdf = dup(0); /* Duplicate file descriptor of input */
 79+		if (no_tty)
 80+			return 0;
 81+		/*
 82+		 * Make sure standard input is from the terminal.
 83+		 */
 84+		(void)close(0);
 85+		if (open("/dev/tty", O_RDONLY, 0) != 0) {
 86+			putline("Couldn't open terminal\n");
 87+			flush();
 88+			exit(1);
 89+		}
 90+	}
 91+	if (no_tty)
 92+		return 0;
 93+	/*
 94+	 * Handle signals.
 95+	 * Catch QUIT, DELETE and ^Z
 96+	 */
 97+	(void)signal(SIGQUIT, SIG_IGN);
 98+	(void)signal(SIGINT, catchdel);
 99+	ini_terminal();
100+#ifdef SIGTSTP
101+	if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
102+		(void)signal(SIGTSTP, suspsig);
103+	}
104+#endif
105+	(void)signal(SIGQUIT, sigquit);
106+	return 0;
107+}
108+
109+void
110+catchdel(int signo)
111+{
112+	(void)signo;
113+	(void)signal(SIGINT, catchdel);
114+	interrupt = 1;
115+}
116+
117+static void
118+sigquit(int signo)
119+{
120+	(void)signo;
121+	quit();
122+}
123+
124+#ifdef SIGTSTP
125+
126+/*
127+ * We had a SIGTSTP signal.
128+ * Suspend, by a call to this routine.
129+ */
130+
131+void
132+suspend()
133+{
134+
135+	nflush();
136+	resettty();
137+	(void)signal(SIGTSTP, SIG_DFL);
138+	(void)kill(0, SIGTSTP);
139+	/*
140+	 * We are not here anymore ...
141+	 *
142+
143+	 *
144+	 * But we arive here ...
145+	 */
146+	inittty();
147+	putline(TI);
148+	flush();
149+	(void)signal(SIGTSTP, suspsig);
150+}
151+
152+/*
153+ * SIGTSTP signal handler.
154+ * Just indicate that we had one, ignore further ones and return.
155+ */
156+
157+static void
158+suspsig(int signo)
159+{
160+	(void)signo;
161+
162+	suspend();
163+	if (DoneSetJmp)
164+		longjmp(SetJmpBuf, 1);
165+}
166+#endif
167+
168+/*
169+ * quit : called on exit.
170+ * I bet you guessed that much.
171+ */
172+
173+int
174+quit()
175+{
176+
177+	clrbline();
178+	resettty();
179+	flush();
180+	exit(0);
181+}
182+
183+/*
184+ * Exit, but nonvoluntarily.
185+ * At least tell the user why.
186+ */
187+
188+void
189+panic(char *s)
190+{
191+
192+	putline("\a\a\a\r\n");
193+	putline(s);
194+	putline("\r\n");
195+	quit();
196+}
A main.h
+18, -0
 1@@ -0,0 +1,18 @@
 2+#ifndef MAIN_H
 3+#define MAIN_H
 4+
 5+extern int nopipe;
 6+extern char *progname;
 7+extern int interrupt;
 8+extern int no_tty;
 9+
10+int main(int argc, char **argv);
11+void catchdel(int signo);
12+int quit(void);
13+void panic(char *s);
14+
15+#ifdef SIGTSTP
16+void suspend(void);
17+#endif
18+
19+#endif
+91, -0
 1@@ -0,0 +1,91 @@
 2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
 3+
 4+#include "in_all.h"
 5+#include "options.h"
 6+#include "output.h"
 7+#include "display.h"
 8+#include <ctype.h>
 9+
10+int cflag;
11+int uflag;
12+int nflag;
13+int qflag;
14+char *startcomm;
15+
16+static int parsopt(char *s);
17+
18+/*
19+ * Read the options. Return the argv pointer following them if there were
20+ * no errors, otherwise return 0.
21+ */
22+
23+char **
24+readoptions(char **argv)
25+{
26+
27+	char **av = argv + 1;
28+	char *p;
29+
30+	if ((p = getenv("YAP")) != 0) {
31+		(void)parsopt(p);
32+	}
33+	while (*av && **av == '-') {
34+		if (parsopt(*av)) {
35+			/*
36+			 * Error in option
37+			 */
38+			putline(*av);
39+			putline(": illegal option\n");
40+			return (char **)0;
41+		}
42+		av++;
43+	}
44+	if (*av && **av == '+') {
45+		/*
46+		 * Command in command line
47+		 */
48+		startcomm = *av + 1;
49+		av++;
50+	}
51+	return av;
52+}
53+
54+static int
55+parsopt(char *s)
56+{
57+	int i;
58+
59+	if (*s == '-')
60+		s++;
61+	if (isdigit(*s)) {
62+		/*
63+		 * pagesize option
64+		 */
65+		i = 0;
66+		do {
67+			i = i * 10 + *s++ - '0';
68+		} while (isdigit(*s));
69+		if (i < MINPAGESIZE)
70+			i = MINPAGESIZE;
71+		pagesize = i;
72+	}
73+	while (*s) {
74+		switch (*s++) {
75+		case 'c':
76+			cflag++;
77+			break;
78+		case 'n':
79+			nflag++;
80+			break;
81+		case 'u':
82+			uflag++;
83+			break;
84+		case 'q':
85+			qflag++;
86+			break;
87+		default:
88+			return 1;
89+		}
90+	}
91+	return 0;
92+}
+12, -0
 1@@ -0,0 +1,12 @@
 2+#ifndef OPTIONS_H
 3+#define OPTIONS_H
 4+
 5+extern int cflag;
 6+extern int uflag;
 7+extern int nflag;
 8+extern int qflag;
 9+extern char *startcomm;
10+
11+char **readoptions(char **argv);
12+
13+#endif
+102, -0
  1@@ -0,0 +1,102 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+/*
  5+ * Handle output to screen
  6+ */
  7+
  8+#include "in_all.h"
  9+#include "output.h"
 10+#include "main.h"
 11+
 12+int _ocnt;
 13+char *_optr;
 14+
 15+#define OBUFSIZ 64 * 128
 16+
 17+static char _outbuf[OBUFSIZ];
 18+
 19+void
 20+flush()
 21+{ /* Flush output buffer, by writing it */
 22+	char *p = _outbuf;
 23+
 24+	_ocnt = OBUFSIZ;
 25+	if (_optr)
 26+		(void)write(1, p, _optr - p);
 27+	_optr = p;
 28+}
 29+
 30+void
 31+nflush()
 32+{ /* Flush output buffer, ignoring it */
 33+
 34+	_ocnt = OBUFSIZ;
 35+	_optr = _outbuf;
 36+}
 37+
 38+int
 39+fputch(int ch)
 40+{ /* print a character */
 41+	putch(ch);
 42+	return ch;
 43+}
 44+
 45+void
 46+putline(char *s)
 47+{ /* Print string s */
 48+
 49+	if (!s)
 50+		return;
 51+	while (*s) {
 52+		putch(*s++);
 53+	}
 54+}
 55+
 56+/*
 57+ * A safe version of putline. All control characters are echoed as ^X
 58+ */
 59+
 60+void
 61+cputline(char *s)
 62+{
 63+	int c;
 64+
 65+	while ((c = *s++) != 0) {
 66+		if ((unsigned char)c < ' ' || (unsigned char)c == 0x7f) {
 67+			putch('^');
 68+			c ^= 0x40;
 69+		}
 70+		putch(c);
 71+	}
 72+}
 73+
 74+/*
 75+ * Simple minded routine to print a number
 76+ */
 77+
 78+void
 79+prnum(long n)
 80+{
 81+
 82+	putline(getnum(n));
 83+}
 84+
 85+static char *
 86+fillnum(long n, char *p)
 87+{
 88+	if (n >= 10) {
 89+		p = fillnum(n / 10, p);
 90+	}
 91+	*p++ = (int)(n % 10) + '0';
 92+	*p = '\0';
 93+	return p;
 94+}
 95+
 96+char *
 97+getnum(long n)
 98+{
 99+	static char buf[20];
100+
101+	fillnum(n, buf);
102+	return buf;
103+}
+22, -0
 1@@ -0,0 +1,22 @@
 2+#ifndef OUTPUT_H
 3+#define OUTPUT_H
 4+
 5+extern int _ocnt;
 6+extern char *_optr;
 7+
 8+#define putch(ch) \
 9+	do { \
10+		if (--_ocnt <= 0) \
11+			flush(); \
12+		*_optr++ = (ch); \
13+	} while (0)
14+
15+void flush(void);
16+void nflush(void);
17+int fputch(int c);
18+void putline(char *s);
19+void cputline(char *s);
20+void prnum(long n);
21+char *getnum(long n);
22+
23+#endif
+34, -0
 1@@ -0,0 +1,34 @@
 2+#include "in_all.h"
 3+#include "pattern.h"
 4+#include <regex.h>
 5+
 6+/*
 7+ * Interface to POSIX regular expression routines.
 8+ */
 9+
10+static regex_t pattern;
11+static int pattern_valid;
12+
13+char *
14+re_comp(char *s)
15+{
16+
17+	if (!*s) {
18+		return (char *)0;
19+	}
20+	if (pattern_valid) {
21+		regfree(&pattern);
22+		pattern_valid = 0;
23+	}
24+	if (regcomp(&pattern, s, 0) == 0) {
25+		pattern_valid = 1;
26+		return (char *)0;
27+	}
28+	return "Error in pattern";
29+}
30+
31+int
32+re_exec(char *s)
33+{
34+	return pattern_valid && regexec(&pattern, s, 0, (regmatch_t *)0, 0) == 0;
35+}
+7, -0
1@@ -0,0 +1,7 @@
2+#ifndef PATTERN_H
3+#define PATTERN_H
4+
5+char *re_comp(char *s);
6+int re_exec(char *s);
7+
8+#endif
+115, -0
  1@@ -0,0 +1,115 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "process.h"
  6+#include "commands.h"
  7+#include "display.h"
  8+#include "prompt.h"
  9+#include "getline.h"
 10+#include "getcomm.h"
 11+#include "main.h"
 12+#include "options.h"
 13+#include "output.h"
 14+
 15+jmp_buf SetJmpBuf;
 16+int DoneSetJmp;
 17+int stdf;
 18+int filecount;
 19+char **filenames;
 20+char *currentfile;
 21+long maxpos;
 22+
 23+static int nfiles; /* Number of filenames on command line */
 24+
 25+/*
 26+ * Visit a file, file name is "fn".
 27+ */
 28+
 29+void
 30+visitfile(char *fn)
 31+{
 32+	struct stat statbuf;
 33+
 34+	if (stdf > 0) {
 35+		/*
 36+		 * Close old input file
 37+		 */
 38+		(void)close(stdf);
 39+	}
 40+	currentfile = fn;
 41+	if ((stdf = open(fn, O_RDONLY, 0)) < 0) {
 42+		error(": could not open");
 43+		maxpos = 0;
 44+	} else { /* Get size for percentage in prompt */
 45+		(void)fstat(stdf, &statbuf);
 46+		maxpos = statbuf.st_size;
 47+	}
 48+	do_clean();
 49+	d_clean();
 50+}
 51+
 52+/*
 53+ * process the input files, one by one.
 54+ * If there is none, input is from a pipe.
 55+ */
 56+
 57+void
 58+processfiles(int n, char **argv)
 59+{
 60+
 61+	static char *dummies[3];
 62+	long arg;
 63+
 64+	if (!(nfiles = n)) {
 65+		/*
 66+		 * Input from pipe
 67+		 */
 68+		currentfile = "standard-input";
 69+		/*
 70+		 * Take care that *(filenames - 1) and *(filenames + 1) are 0
 71+		 */
 72+		filenames = &dummies[1];
 73+		d_clean();
 74+		do_clean();
 75+	} else {
 76+		filenames = argv;
 77+		(void)nextfile(0);
 78+	}
 79+	*--argv = 0;
 80+	if (startcomm) {
 81+		n = getcomm(&arg);
 82+		if (commands[n].c_flags & NEEDS_SCREEN) {
 83+			redraw(0);
 84+		}
 85+		do_comm(n, arg);
 86+		startcomm = 0;
 87+	}
 88+	redraw(1);
 89+	if (setjmp(SetJmpBuf)) {
 90+		nflush();
 91+		redraw(1);
 92+	}
 93+	DoneSetJmp = 1;
 94+	for (;;) {
 95+		interrupt = 0;
 96+		n = getcomm(&arg);
 97+		do_comm(n, arg);
 98+	}
 99+}
100+
101+/*
102+ * Get the next file the user asks for.
103+ */
104+
105+int
106+nextfile(int n)
107+{
108+	int i;
109+
110+	if ((i = filecount + n) >= nfiles || i < 0) {
111+		return 1;
112+	}
113+	filecount = i;
114+	visitfile(filenames[i]);
115+	return 0;
116+}
+19, -0
 1@@ -0,0 +1,19 @@
 2+#ifndef PROCESS_H
 3+#define PROCESS_H
 4+
 5+#include <setjmp.h>
 6+
 7+extern jmp_buf SetJmpBuf;
 8+extern int DoneSetJmp;
 9+
10+extern int stdf;
11+extern int filecount;
12+extern char **filenames;
13+extern char *currentfile;
14+extern long maxpos;
15+
16+void visitfile(char *fn);
17+void processfiles(int n, char **argv);
18+int nextfile(int n);
19+
20+#endif
+174, -0
  1@@ -0,0 +1,174 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+#include "in_all.h"
  5+#include "prompt.h"
  6+#include "term.h"
  7+#include "output.h"
  8+#include "options.h"
  9+#include "display.h"
 10+#include "process.h"
 11+#include "getline.h"
 12+#include "main.h"
 13+#include "getcomm.h"
 14+#include "machine.h"
 15+#include "keys.h"
 16+#include "assert.h"
 17+#include "commands.h"
 18+
 19+static char *errorgiven; /* Set to error message, if there is one */
 20+
 21+static char *display_basename(char *path, char *scratch, size_t scratch_size);
 22+
 23+char *
 24+copy(char *p, const char *ep, char *s)
 25+{
 26+	while (p < ep && *s) {
 27+		*p++ = *s++;
 28+	}
 29+	return p;
 30+}
 31+
 32+/*
 33+ * display the prompt and refresh the screen.
 34+ */
 35+
 36+void
 37+give_prompt()
 38+{
 39+
 40+	char **name;
 41+	struct scr_info *p = &scr_info;
 42+	char buf[256];
 43+	char filebuf[256];
 44+	char *pb = buf;
 45+
 46+	if (startcomm)
 47+		return;
 48+	flush();
 49+	if (window()) {
 50+		redraw(0);
 51+		flush();
 52+	}
 53+	if (!stupid) {
 54+		/*
 55+		 * fancy prompt
 56+		 */
 57+		clrbline();
 58+		standout();
 59+		pb = copy(pb, &buf[255],
 60+		          display_basename(currentfile, filebuf, sizeof(filebuf)));
 61+		if (stdf >= 0) {
 62+			pb = copy(pb, &buf[255], ", ");
 63+			pb = copy(pb, &buf[255], getnum(p->firstline));
 64+			pb = copy(pb, &buf[255], "-");
 65+			pb = copy(pb, &buf[255], getnum(p->lastline));
 66+		}
 67+	} else {
 68+		*pb++ = '\007'; /* Stupid terminal, stupid prompt */
 69+	}
 70+	if (errorgiven) {
 71+		/*
 72+		 * display error message
 73+		 */
 74+		pb = copy(pb, &buf[255], " ");
 75+		pb = copy(pb, &buf[255], errorgiven);
 76+		if (stupid) {
 77+			pb = copy(pb, &buf[255], "\r\n");
 78+		}
 79+		errorgiven = 0;
 80+	} else if (!stupid && (status || maxpos)) {
 81+		pb = copy(pb, &buf[255], " (");
 82+		name = &filenames[filecount];
 83+		if (status) {
 84+			/*
 85+			 * indicate top and/or bottom
 86+			 */
 87+			if (status & START) {
 88+				if (!*(name - 1)) {
 89+					pb = copy(pb, &buf[255], "Top");
 90+				} else {
 91+					pb = copy(pb, &buf[255], "Previous: ");
 92+					pb = copy(pb, &buf[255], display_basename(*(name - 1),
 93+					                                         filebuf,
 94+					                                         sizeof(filebuf)));
 95+				}
 96+				if (status & EOFILE) {
 97+					pb = copy(pb, &buf[255], ", ");
 98+				}
 99+			}
100+			if (status & EOFILE) {
101+				if (!*(name + 1)) {
102+					pb = copy(pb, &buf[255], "Bottom");
103+				} else {
104+					pb = copy(pb, &buf[255], "Next: ");
105+					pb = copy(pb, &buf[255], display_basename(*(name + 1),
106+					                                         filebuf,
107+					                                         sizeof(filebuf)));
108+				}
109+			}
110+		} else { /* display percentage */
111+			pb = copy(pb, &buf[255], getnum((100 * getpos(p->lastline)) / maxpos));
112+			pb = copy(pb, &buf[255], "%");
113+		}
114+		pb = copy(pb, &buf[255], ")");
115+	}
116+	*pb = '\0';
117+	if (!stupid) {
118+		buf[COLS - 1] = 0;
119+		putline(buf);
120+		standend();
121+	} else
122+		putline(buf);
123+}
124+
125+static char *
126+display_basename(char *path, char *scratch, size_t scratch_size)
127+{
128+	char *base;
129+
130+	if (!path || scratch_size == 0) {
131+		return "";
132+	}
133+	(void)strncpy(scratch, path, scratch_size - 1);
134+	scratch[scratch_size - 1] = '\0';
135+	base = basename(scratch);
136+	return base ? base : scratch;
137+}
138+
139+/*
140+ * Remember error message
141+ */
142+
143+void
144+error(char *str)
145+{
146+
147+	errorgiven = str;
148+}
149+
150+void
151+ret_to_continue()
152+{ /* Obvious */
153+	int c;
154+	static char buf[2];
155+
156+	for (;;) {
157+		clrbline();
158+		standout();
159+		if (errorgiven) {
160+			putline(errorgiven);
161+			putline(" ");
162+			errorgiven = 0;
163+		}
164+		putline("[Type anything to continue]");
165+		standend();
166+		if (is_escape(c = getch())) {
167+			buf[0] = c;
168+			(void)match(buf, &c, currmap->k_mach);
169+			assert(c > 0);
170+			do_comm(c, -1L);
171+		} else
172+			break;
173+	}
174+	clrbline();
175+}
+8, -0
1@@ -0,0 +1,8 @@
2+#ifndef PROMPT_H
3+#define PROMPT_H
4+
5+void give_prompt(void);
6+void error(char *s);
7+void ret_to_continue(void);
8+
9+#endif
A rune.c
+148, -0
  1@@ -0,0 +1,148 @@
  2+/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
  3+ *
  4+ * Permission is hereby granted, free of charge, to any person obtaining a
  5+ * copy of this software and associated documentation files (the "Software"),
  6+ * to deal in the Software without restriction, including without limitation
  7+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8+ * and/or sell copies of the Software, and to permit persons to whom the
  9+ * Software is furnished to do so, subject to the following conditions:
 10+ *
 11+ * The above copyright notice and this permission notice shall be included in
 12+ * all copies or substantial portions of the Software.
 13+ *
 14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 17+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 19+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 20+ * DEALINGS IN THE SOFTWARE.
 21+ */
 22+#include "utf.h"
 23+
 24+#define MIN(x,y)  ((x) < (y) ? (x) : (y))
 25+
 26+#define UTFSEQ(x) ((((x) & 0x80) == 0x00) ? 1 /* 0xxxxxxx */ \
 27+                 : (((x) & 0xC0) == 0x80) ? 0 /* 10xxxxxx */ \
 28+                 : (((x) & 0xE0) == 0xC0) ? 2 /* 110xxxxx */ \
 29+                 : (((x) & 0xF0) == 0xE0) ? 3 /* 1110xxxx */ \
 30+                 : (((x) & 0xF8) == 0xF0) ? 4 /* 11110xxx */ \
 31+                 : (((x) & 0xFC) == 0xF8) ? 5 /* 111110xx */ \
 32+                 : (((x) & 0xFE) == 0xFC) ? 6 /* 1111110x */ \
 33+                                          : 0 )
 34+
 35+#define BADRUNE(x) ((x) < 0 || (x) > Runemax \
 36+                || ((x) & 0xFFFE) == 0xFFFE \
 37+                || ((x) >= 0xD800 && (x) <= 0xDFFF) \
 38+                || ((x) >= 0xFDD0 && (x) <= 0xFDEF))
 39+
 40+int
 41+runetochar(char *s, const Rune *p)
 42+{
 43+	Rune r = *p;
 44+
 45+	switch(runelen(r)) {
 46+	case 1: /* 0aaaaaaa */
 47+		s[0] = r;
 48+		return 1;
 49+	case 2: /* 00000aaa aabbbbbb */
 50+		s[0] = 0xC0 | ((r & 0x0007C0) >>  6); /* 110aaaaa */
 51+		s[1] = 0x80 |  (r & 0x00003F);        /* 10bbbbbb */
 52+		return 2;
 53+	case 3: /* aaaabbbb bbcccccc */
 54+		s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */
 55+		s[1] = 0x80 | ((r & 0x000FC0) >>  6); /* 10bbbbbb */
 56+		s[2] = 0x80 |  (r & 0x00003F);        /* 10cccccc */
 57+		return 3;
 58+	case 4: /* 000aaabb bbbbcccc ccdddddd */
 59+		s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */
 60+		s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */
 61+		s[2] = 0x80 | ((r & 0x000FC0) >>  6); /* 10cccccc */
 62+		s[3] = 0x80 |  (r & 0x00003F);        /* 10dddddd */
 63+		return 4;
 64+	default:
 65+		return 0; /* error */
 66+	}
 67+}
 68+
 69+int
 70+chartorune(Rune *p, const char *s)
 71+{
 72+	return charntorune(p, s, UTFmax);
 73+}
 74+
 75+int
 76+charntorune(Rune *p, const char *s, size_t len)
 77+{
 78+	unsigned int i, n;
 79+	Rune r;
 80+
 81+	if(len == 0) /* can't even look at s[0] */
 82+		return 0;
 83+
 84+	switch((n = UTFSEQ(s[0]))) {
 85+	case 1: r = s[0];        break; /* 0xxxxxxx */
 86+	case 2: r = s[0] & 0x1F; break; /* 110xxxxx */
 87+	case 3: r = s[0] & 0x0F; break; /* 1110xxxx */
 88+	case 4: r = s[0] & 0x07; break; /* 11110xxx */
 89+	case 5: r = s[0] & 0x03; break; /* 111110xx */
 90+	case 6: r = s[0] & 0x01; break; /* 1111110x */
 91+	default: /* invalid sequence */
 92+		*p = Runeerror;
 93+		return 1;
 94+	}
 95+	/* add values from continuation bytes */
 96+	for(i = 1; i < MIN(n, len); i++)
 97+		if((s[i] & 0xC0) == 0x80) {
 98+			/* add bits from continuation byte to rune value
 99+			 * cannot overflow: 6 byte sequences contain 31 bits */
100+			r = (r << 6) | (s[i] & 0x3F); /* 10xxxxxx */
101+		}
102+		else { /* expected continuation */
103+			*p = Runeerror;
104+			return i;
105+		}
106+
107+	if(i < n) /* must have reached len limit */
108+		return 0;
109+
110+	/* reject invalid or overlong sequences */
111+	if(BADRUNE(r) || runelen(r) < (int)n)
112+		r = Runeerror;
113+
114+	*p = r;
115+	return n;
116+}
117+
118+int
119+runelen(Rune r)
120+{
121+	if(BADRUNE(r))
122+		return 0; /* error */
123+	else if(r <= 0x7F)
124+		return 1;
125+	else if(r <= 0x07FF)
126+		return 2;
127+	else if(r <= 0xFFFF)
128+		return 3;
129+	else
130+		return 4;
131+}
132+
133+size_t
134+runenlen(const Rune *p, size_t len)
135+{
136+	size_t i, n = 0;
137+
138+	for(i = 0; i < len; i++)
139+		n += runelen(p[i]);
140+	return n;
141+}
142+
143+int
144+fullrune(const char *s, size_t len)
145+{
146+	Rune r;
147+
148+	return charntorune(&r, s, len) > 0;
149+}
A term.c
+459, -0
  1@@ -0,0 +1,459 @@
  2+/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  3+
  4+/*
  5+ * Terminal handling routines, mostly initializing.
  6+ */
  7+
  8+#include "in_all.h"
  9+#include "term.h"
 10+#include "machine.h"
 11+#include "output.h"
 12+#include "display.h"
 13+#include "options.h"
 14+#include "getline.h"
 15+#include "keys.h"
 16+#include "main.h"
 17+#define getline yap_getline
 18+#define getch yap_getch
 19+#include <term.h>
 20+#undef getch
 21+#undef getline
 22+#undef insert_line
 23+
 24+int expandtabs;
 25+int stupid;
 26+int hardcopy;
 27+char *CE, *CL, *SO, *SE, *US, *UE, *UC, *MD, *ME, *TI, *TE, *CM, *TA, *SR, *AL;
 28+char *UP, *HO, *BO;
 29+int LINES, COLS, AM, XN, DB;
 30+int erasech, killch;
 31+struct state *sppat;
 32+char *BC;
 33+
 34+#ifdef TIOCGWINSZ
 35+static struct winsize w;
 36+#endif
 37+
 38+#ifdef TIOCSPGRP
 39+static int proc_id, saved_pgrpid;
 40+#endif
 41+
 42+static char *ll;
 43+
 44+struct linelist _X[100]; /* 100 is enough ? */
 45+
 46+static struct termios _tty, _svtty;
 47+
 48+static void
 49+handle(cc_t *c)
 50+{ /* if character *c is used, set it to undefined */
 51+
 52+	if (isused(*c))
 53+		*c = 0xff;
 54+}
 55+
 56+/*
 57+ * Set terminal in cbreak mode.
 58+ * Also check if tabs need expanding.
 59+ */
 60+
 61+void
 62+inittty()
 63+{
 64+	struct termios *p = &_tty;
 65+
 66+	tcgetattr(0, p);
 67+	_svtty = *p;
 68+	p->c_oflag &= ~OPOST;
 69+	p->c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON);
 70+	if (isused('S' & 0x1f) || isused('Q' & 0x1f))
 71+		p->c_iflag &= ~IXON;
 72+	handle(&p->c_cc[VINTR]);
 73+	handle(&p->c_cc[VQUIT]);
 74+	erasech = p->c_cc[VERASE];
 75+	killch = p->c_cc[VKILL];
 76+	p->c_cc[VMIN] = 1; /* Just wait for one character */
 77+	p->c_cc[VTIME] = 0;
 78+	tcsetattr(0, TCSANOW, p);
 79+}
 80+
 81+void
 82+backspace()
 83+{
 84+	putline(BC);
 85+}
 86+
 87+void
 88+clrscreen()
 89+{
 90+	tputs(CL, LINES, fputch);
 91+}
 92+
 93+void
 94+clrtoeol()
 95+{
 96+	tputs(CE, 1, fputch);
 97+}
 98+
 99+void
100+scrollreverse()
101+{
102+	tputs(SR, LINES, fputch);
103+}
104+
105+void
106+standout()
107+{
108+	tputs(SO, 1, fputch);
109+}
110+
111+void
112+standend()
113+{
114+	tputs(SE, 1, fputch);
115+}
116+
117+void
118+underline()
119+{
120+	tputs(US, 1, fputch);
121+}
122+
123+void
124+end_underline()
125+{
126+	tputs(UE, 1, fputch);
127+}
128+
129+void
130+bold()
131+{
132+	tputs(MD, 1, fputch);
133+}
134+
135+void
136+end_bold()
137+{
138+	tputs(ME, 1, fputch);
139+}
140+
141+void
142+underchar()
143+{
144+	tputs(UC, 1, fputch);
145+}
146+
147+void
148+givetab()
149+{
150+	tputs(TA, 1, fputch);
151+}
152+
153+/*
154+ * Reset the terminal to its original state
155+ */
156+
157+void
158+resettty()
159+{
160+	tcsetattr(0, TCSANOW, &_svtty);
161+	putline(TE);
162+	flush();
163+}
164+
165+/*
166+ * Get string terminal capability "cap".
167+ * If not present, return an empty string.
168+ */
169+
170+static char *
171+getcap(char *cap)
172+{
173+	char *s;
174+
175+	s = tigetstr(cap);
176+	if (!s || s == (char *)-1)
177+		return "";
178+	return s;
179+}
180+
181+static int
182+getflag(char *cap)
183+{
184+	int value;
185+
186+	value = tigetflag(cap);
187+	return value < 0 ? 0 : value;
188+}
189+
190+static int
191+getnumcap(char *cap)
192+{
193+	int value;
194+
195+	value = tigetnum(cap);
196+	return value < 0 ? -1 : value;
197+}
198+
199+/*
200+ * Initialize some terminal-dependent stuff.
201+ */
202+
203+void
204+ini_terminal()
205+{
206+
207+	char *s;
208+	struct linelist *lp, *lp1;
209+	int i;
210+	int UG, SG;
211+	char tempbuf[20];
212+	char *mb, *mh, *mr; /* attributes */
213+	int err;
214+
215+	initkeys();
216+#ifdef TIOCSPGRP
217+	proc_id = getpid();
218+	ioctl(0, TIOCGPGRP, (char *)&saved_pgrpid);
219+#endif
220+	inittty();
221+	stupid = 1;
222+	BC = "\b";
223+	TA = "\t";
224+	if (!(s = getenv("TERM")))
225+		s = "dumb";
226+	if (setupterm(s, 1, &err) != 0) {
227+		panic("No terminfo entry");
228+	}
229+	stupid = 0;
230+	hardcopy = getflag("hc"); /* Hard copy terminal?*/
231+	if (*(s = getcap("cub1"))) {
232+		/*
233+		 * Backspace if not ^H
234+		 */
235+		BC = s;
236+	}
237+	UP = getcap("cuu1");   /* move up a line */
238+	CE = getcap("el");     /* clear to end of line */
239+	CL = getcap("clear");  /* clear screen */
240+	if (!*CL)
241+		cflag = 1;
242+	TI = getcap("smcup");  /* Initialization for CM */
243+	TE = getcap("rmcup");  /* end for CM */
244+	CM = getcap("cup");    /* cursor addressing */
245+	SR = getcap("ri");     /* scroll reverse */
246+	AL = getcap("il");     /* Insert line */
247+	SO = getcap("smso");   /* standout */
248+	SE = getcap("rmso");   /* standend */
249+	SG = getnumcap("xmc"); /* blanks left by attributes */
250+	if (SG < 0)
251+		SG = 0;
252+	US = getcap("smul"); /* underline */
253+	UE = getcap("rmul"); /* end underline */
254+	UG = getnumcap("xmc"); /* blanks left by attributes */
255+	if (UG < 0)
256+		UG = 0;
257+	UC = getcap("uc");   /* underline a character */
258+	mb = getcap("blink"); /* blinking attribute */
259+	MD = getcap("bold");  /* bold attribute */
260+	ME = getcap("sgr0");  /* turn off attributes */
261+	mh = getcap("dim");   /* half bright attribute */
262+	mr = getcap("rev");   /* reversed video attribute */
263+	if (!nflag) {
264+		/*
265+		 * Recognize special strings
266+		 */
267+		(void)addstring(SO, SG, &sppat);
268+		(void)addstring(SE, SG, &sppat);
269+		(void)addstring(US, UG, &sppat);
270+		(void)addstring(UE, UG, &sppat);
271+		(void)addstring(mb, 0, &sppat);
272+		(void)addstring(MD, 0, &sppat);
273+		(void)addstring(ME, 0, &sppat);
274+		(void)addstring(mh, 0, &sppat);
275+		(void)addstring(mr, 0, &sppat);
276+		if (*UC) {
277+			(void)strcpy(tempbuf, BC);
278+			(void)strcat(tempbuf, UC);
279+			(void)addstring(tempbuf, 0, &sppat);
280+		}
281+	}
282+	if (UG > 0 || uflag) {
283+		US = "";
284+		UE = "";
285+	}
286+	if (*US || uflag)
287+		UC = "";
288+	COLS = getnumcap("cols"); /* columns on page */
289+	i = getnumcap("lines");   /* Lines on page */
290+	AM = getflag("am");       /* terminal wraps automatically? */
291+	XN = getflag("xenl");     /* and then ignores next newline? */
292+	DB = getflag("db");       /* terminal retains lines below */
293+	HO = getcap("home");
294+	if (!*HO && *CM) {
295+		HO = tiparm(CM, 0, 0); /* Another way of getting home */
296+	}
297+	if ((!*CE && !*AL) || !*HO || hardcopy) {
298+		cflag = stupid = 1;
299+	}
300+	if (*(s = getcap("ht"))) {
301+		/*
302+		 * Tab (other than ^I or padding)
303+		 */
304+		TA = s;
305+	}
306+	if (!*(ll = getcap("ll")) && *CM && i > 0) {
307+		/*
308+		 * Lower left hand corner
309+		 */
310+		BO = tiparm(CM, i - 1, 0);
311+	} else
312+		BO = ll;
313+	if (COLS <= 0 || COLS > 256) {
314+		if ((unsigned)COLS >= 65409) {
315+			/* SUN bug */
316+			COLS &= 0xffff;
317+			COLS -= (65409 - 128);
318+		}
319+		if (COLS <= 0 || COLS > 256)
320+			COLS = 80;
321+	}
322+	if (i <= 0) {
323+		i = 24;
324+		cflag = stupid = 1;
325+	}
326+	LINES = i;
327+	maxpagesize = i - 1;
328+	scrollsize = maxpagesize / 2;
329+	if (scrollsize <= 0)
330+		scrollsize = 1;
331+	if (!pagesize || pagesize >= i) {
332+		pagesize = maxpagesize;
333+	}
334+
335+	/*
336+	 * The next part does not really belong here, but there it is ...
337+	 * Initialize a circular list for the screenlines.
338+	 */
339+
340+	scr_info.tail = lp = _X;
341+	lp1 = lp + (100 - 1);
342+	for (; lp <= lp1; lp++) {
343+		/*
344+		 * Circular doubly linked list
345+		 */
346+		lp->next = lp + 1;
347+		lp->prev = lp - 1;
348+	}
349+	lp1->next = scr_info.tail;
350+	lp1->next->prev = lp1;
351+	if (stupid) {
352+		BO = "\r\n";
353+	}
354+	putline(TI);
355+	window();
356+}
357+
358+/*
359+ * Place cursor at start of line n.
360+ */
361+
362+void
363+mgoto(int n)
364+{
365+
366+	if (n == 0)
367+		home();
368+	else if (n == maxpagesize && *BO)
369+		bottom();
370+	else if (*CM) {
371+		/*
372+		 * Cursor addressing
373+		 */
374+		tputs(tiparm(CM, n, 0), 1, fputch);
375+	} else if (*BO && *UP && n >= (maxpagesize >> 1)) {
376+		/*
377+		 * Bottom and then up
378+		 */
379+		bottom();
380+		while (n++ < maxpagesize)
381+			putline(UP);
382+	} else { /* Home, and then down */
383+		home();
384+		while (n--)
385+			putline("\r\n");
386+	}
387+}
388+
389+/*
390+ * Clear bottom line
391+ */
392+
393+void
394+clrbline()
395+{
396+
397+	if (stupid) {
398+		putline("\r\n");
399+		return;
400+	}
401+	bottom();
402+	if (*CE) {
403+		/*
404+		 * We can clear to end of line
405+		 */
406+		clrtoeol();
407+		return;
408+	}
409+	ins_line(maxpagesize);
410+}
411+
412+void
413+ins_line(int l)
414+{
415+	tputs(tiparm(AL, l), maxpagesize - l, fputch);
416+}
417+
418+void
419+home()
420+{
421+
422+	tputs(HO, 1, fputch);
423+}
424+
425+void
426+bottom()
427+{
428+
429+	tputs(BO, 1, fputch);
430+	if (!*BO)
431+		mgoto(maxpagesize);
432+}
433+
434+int
435+window()
436+{
437+#ifdef TIOCGWINSZ
438+	if (ioctl(1, TIOCGWINSZ, &w) < 0)
439+		return 0;
440+
441+	if (w.ws_col == 0)
442+		w.ws_col = COLS;
443+	if (w.ws_row == 0)
444+		w.ws_row = LINES;
445+	if (w.ws_col != COLS || w.ws_row != LINES) {
446+		COLS = w.ws_col;
447+		LINES = w.ws_row;
448+		maxpagesize = LINES - 1;
449+		pagesize = maxpagesize;
450+		if (!*ll)
451+			BO = tiparm(CM, maxpagesize, 0);
452+		scr_info.currentpos = 0;
453+		scrollsize = maxpagesize / 2;
454+		if (scrollsize <= 0)
455+			scrollsize = 1;
456+		return 1;
457+	}
458+#endif
459+	return 0;
460+}
A term.h
+61, -0
 1@@ -0,0 +1,61 @@
 2+#ifndef TERM_H
 3+#define TERM_H
 4+
 5+extern int expandtabs;
 6+extern int stupid;
 7+extern int hardcopy;
 8+
 9+extern char *CE;
10+extern char *CL;
11+extern char *SO;
12+extern char *SE;
13+extern char *US;
14+extern char *UE;
15+extern char *UC;
16+extern char *MD;
17+extern char *ME;
18+extern char *TI;
19+extern char *TE;
20+extern char *CM;
21+extern char *TA;
22+extern char *SR;
23+extern char *AL;
24+extern char *UP;
25+extern char *HO;
26+extern char *BO;
27+
28+extern int LINES;
29+extern int COLS;
30+extern int AM;
31+extern int XN;
32+extern int DB;
33+
34+extern int erasech;
35+extern int killch;
36+extern struct state *sppat;
37+extern char *BC;
38+
39+void inittty(void);
40+void resettty(void);
41+void ini_terminal(void);
42+void backspace(void);
43+void clrscreen(void);
44+void clrtoeol(void);
45+void scrollreverse(void);
46+void standout(void);
47+void standend(void);
48+void underline(void);
49+void end_underline(void);
50+void bold(void);
51+void end_bold(void);
52+void underchar(void);
53+void givetab(void);
54+void mgoto(int n);
55+void clrbline(void);
56+void home(void);
57+void bottom(void);
58+int window(void);
59+void ins_line(int l);
60+#define insert_line(l) ins_line(l)
61+
62+#endif
A utf.h
+69, -0
 1@@ -0,0 +1,69 @@
 2+/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
 3+ *
 4+ * Permission is hereby granted, free of charge, to any person obtaining a
 5+ * copy of this software and associated documentation files (the "Software"),
 6+ * to deal in the Software without restriction, including without limitation
 7+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 8+ * and/or sell copies of the Software, and to permit persons to whom the
 9+ * Software is furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20+ * DEALINGS IN THE SOFTWARE.
21+ */
22+#include <stdio.h>
23+
24+typedef int Rune;
25+
26+enum {
27+	UTFmax    = 6,       /* maximum bytes per rune */
28+	Runeself  = 0x80,    /* rune and utf are equal (<) */
29+	Runeerror = 0xFFFD,  /* decoding error in utf */
30+	Runemax   = 0x10FFFF /* maximum rune value */
31+};
32+
33+int runetochar(char *, const Rune *);
34+int chartorune(Rune *, const char *);
35+int charntorune(Rune *, const char *, size_t);
36+int runelen(Rune);
37+size_t runenlen(const Rune *, size_t);
38+int fullrune(const char *, size_t);
39+char *utfecpy(char *, char *, const char *);
40+size_t utflen(const char *);
41+size_t utfnlen(const char *, size_t);
42+size_t utfmemlen(const char *, size_t);
43+char *utfrune(const char *, Rune);
44+char *utfrrune(const char *, Rune);
45+char *utfutf(const char *, const char *);
46+
47+int isalnumrune(Rune);
48+int isalpharune(Rune);
49+int isblankrune(Rune);
50+int iscntrlrune(Rune);
51+int isdigitrune(Rune);
52+int isgraphrune(Rune);
53+int islowerrune(Rune);
54+int isprintrune(Rune);
55+int ispunctrune(Rune);
56+int isspacerune(Rune);
57+int istitlerune(Rune);
58+int isupperrune(Rune);
59+int isxdigitrune(Rune);
60+
61+Rune tolowerrune(Rune);
62+Rune toupperrune(Rune);
63+
64+size_t utftorunestr(const char *, Rune *);
65+size_t utfntorunestr(const char *, size_t, Rune *);
66+
67+int fgetrune(Rune *, FILE *);
68+int efgetrune(Rune *, FILE *, const char *);
69+int fputrune(const Rune *, FILE *);
70+int efputrune(const Rune *, FILE *, const char *);