1 /* @(#)io.c	1.46 18/10/14 Copyright 1984-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)io.c	1.46 18/10/14 Copyright 1984-2018 J. Schilling";
6 #endif
7 /*
8  *	Low level routines for Input from keyboard and output to screen.
9  *
10  *	Copyright (c) 1984-2018 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * All usual input of the editor is done in edit() which uses gchar() to get
28  * the next mapped and macro expanded character. If for some reason a hard EOF
29  * condition is reached, gchar() exits.
30  *
31  * To be able to recover a crashed edit session, a recover protocol file is
32  * maintained by the low level input routine getnextc().
33  *
34  * To improve the output, all outout is buffered. This usually done by our
35  * private buffering routines (if BFSIZE) is defined.
36  *
37  * The following output routines are available:
38  *
39  *	putoutchar	- for buffered simple output, used by very few funtions
40  *			  that don't want side effects (e.g. terminal.c)
41  *	addchar		- for buffered output that maintaines cursor posiotion
42  *			  and currectly handles alternate video
43  *			  The alternate video buffer is currently too small
44  *			  to efficiently handle more than printing the mark.
45  *	output		- string version of addchar()
46  *	onmark		- to start alternalte video
47  *	offmark		- to end alternalte video
48  *	printfield	- used only to output the fields of the system line
49  *	printstring	- prints a non null terminated string. Used only by the
50  *			  command line module.
51  *	ringbell	- ring the bell on screen
52  *
53  */
54 
55 #include "ved.h"
56 #include "terminal.h"
57 #include <schily/setjmp.h>
58 #include <schily/jmpdefs.h>
59 #include <schily/termios.h>		/* For WIN-DOS test and USE_GETCH */
60 #include <schily/errno.h>
61 
62 #define	RECOVERBUFSIZE	512
63 #define	RECOVERNAMESIZE	FNAMESIZE	/* Must be equal */
64 #define	PROTBUFSIZE	512
65 #define	PROTMARGIN	8	/* Nach PROTMARGIN �nderungen erfolgt sync */
66 
67 typedef	union {
68 		Uchar	r_buf[FNAMESIZE+RECOVERBUFSIZE];
69 		struct r_header {
70 			Uchar	r_name[RECOVERNAMESIZE];
71 #ifdef	OLDPROT
72 			Uchar	r_pos[12];
73 			Uchar	r_col[12];
74 #else
75 			Uchar	r_pos[22];	/* enough for 64 bits */
76 			Uchar	r_col[22];	/* enough for 64 bits */
77 			Uchar	r_version[12];	/* e.g. ved-1.5	    */
78 #endif
79 		} r_head;
80 } _RBUF, *RBUF;
81 
82 LOCAL	FILE	*protfile;		/* FILE * of current protocol file   */
83 extern	Uchar	protname[TMPNSIZE];	/* file name of current protocol file */
84 LOCAL	Uchar	protbuf[PROTBUFSIZE];	/* Buffer for protocol		    */
85 LOCAL	Uchar	*protp;			/* Actual protocol buffer write ptr */
86 
87 LOCAL	FILE	*rec_file;		/* FILE * of current recover file   */
88 LOCAL	Uchar	*rec_name;		/* file name of current recover file */
89 
90 EXPORT	BOOL	markon;		/* if true: we are just printing the mark   */
91 LOCAL	Uchar	Markbuffer[128]; /* temp space for alt video printing	    */
92 LOCAL	Uchar	*markbuf;	/* write ptr for printing alt video	    */
93 
94 EXPORT	iobuf_t	_bb;
95 
96 EXPORT	echar_t	gchar		__PR((ewin_t *wp));
97 EXPORT	echar_t	nigchar		__PR((ewin_t *wp));
98 LOCAL	int	inchar		__PR((ewin_t *wp));
99 EXPORT	int	getnextc	__PR((ewin_t *wp));
100 EXPORT	int	nigetnextc	__PR((ewin_t *wp));
101 EXPORT	void	flushprot	__PR((ewin_t *wp));
102 EXPORT	void	deleteprot	__PR((void));
103 EXPORT	void	newprot		__PR((ewin_t *wp));
104 EXPORT	void	openrecoverfile	__PR((ewin_t *wp, char *name));
105 EXPORT	Uchar*	getrecoverfile	__PR((epos_t *posp, int *colp));
106 EXPORT	int	putoutchar	__PR((int c));
107 LOCAL	void	pchar		__PR((int c));
108 EXPORT	void	addchar		__PR((int c));
109 EXPORT	void	onmark		__PR((void));
110 EXPORT	void	offmark		__PR((void));
111 EXPORT	void	addstr		__PR((Uchar *s));
112 EXPORT	void	output		__PR((Uchar *s));
113 EXPORT	void	printfield	__PR((Uchar *str, int len));
114 EXPORT	void	printstring	__PR((Uchar *str, int nchars));
115 EXPORT	void	ringbell	__PR((void));
116 EXPORT	int	_bflush		__PR((int c));
117 EXPORT	int	_bufflush	__PR((void));
118 
119 /*---------------------------------------------------------------------------
120 |
121 | Input routines
122 |
123 +---------------------------------------------------------------------------*/
124 
125 /*
126  * Read the next character from the terminal (stdin). Exit on read error or EOF
127  *
128  * -	Used by macro.c (for internal use)
129  * -	The only 'real' user is edit().
130  *
131  * Expands the input first by the mapper and then by the macro package.
132  *
133  * Never returns EOF since EOF ic checked here.
134  */
135 EXPORT echar_t
gchar(wp)136 gchar(wp)
137 	ewin_t	*wp;
138 {
139 	int	c;
140 #if	MB_LEN_MAX > 1
141 static	Uchar	mbuf[MB_LEN_MAX+1];
142 static	Uchar	*bp = mbuf;
143 static	size_t	mblen;
144 	echar_t	wc;
145 	size_t	wclen;
146 
147 again:
148 #endif
149 	if (mflag == 0) {
150 		c = inchar(wp);
151 	} else if ((c = gmacro()) == 0) {
152 		c = inchar(wp);
153 	}
154 #if	MB_LEN_MAX <= 1
155 	if (c >= 0)
156 		return ((echar_t)c);
157 #else
158 	if (c != EOF) {
159 		*bp++ = c;
160 		mblen++;
161 		if ((wclen = mbtowc(&wc, C mbuf, mblen)) < 0) {
162 			mbtowc(NULL, NULL, 0);
163 			if (mblen < MB_LEN_MAX)
164 				goto again;
165 
166 			/*
167 			 * Deliver one byte and give the rest another try.
168 			 */
169 			wc = mbuf[0];
170 			*bp-- = '\0';
171 			ovstrcpy(C mbuf, C &mbuf[1]);
172 			mblen--;
173 			return (wc);
174 		} else {
175 			if (wclen == mblen) {
176 				bp = mbuf;
177 				mblen = 0;
178 			} else {
179 				/*
180 				 * Left over bytes from a previus failure.
181 				 */
182 				*bp = '\0';
183 				ovstrcpy(C mbuf, C &mbuf[wclen]);
184 				mblen -= wclen;
185 				bp = &mbuf[wclen];
186 			}
187 			return (wc);
188 		}
189 	} else {
190 		if (mblen > 0) {
191 			/*
192 			 * Deliver the characters left over in mbuf after
193 			 * we get EOF from the input.
194 			 */
195 			*bp = '\0';
196 			wc = mbuf[0];
197 			ovstrcpy(C mbuf, C &mbuf[1]);
198 			mblen--;
199 			return (wc);
200 		}
201 	}
202 #endif
203 
204 	eexit(wp);		/* Prepare quit without write back */
205 	exit(0);		/* No Return */
206 	return (0);		/* Keep lint happy */
207 }
208 
209 EXPORT	sigjmps_t	*sjp;
210 EXPORT	BOOL	interrupted;
211 extern	int	intrchar;
212 
213 /*
214  * Non-interruptable version of gchar()
215  *
216  * -	used by command line input and all other places where
217  *	only one additional character needs to be read.
218  *
219  * Catches the interrupt and maps the interrupt character back
220  * to a usable input character.
221  *
222  * Never returns EOF since EOF ic checked in gchar().
223  */
224 EXPORT echar_t
nigchar(wp)225 nigchar(wp)
226 	ewin_t	*wp;
227 {
228 static	sigjmps_t	gcjmp;
229 	sigjmps_t	*savjp = sjp;
230 	Uchar	c;
231 
232 	if (sigsetjmp(gcjmp.jb, 1)) {
233 		if (intrchar > 0)
234 			interrupted++;
235 	} else {
236 		sjp = &gcjmp;
237 	}
238 	c = gchar(wp);
239 	sjp = savjp;
240 	return (c);
241 }
242 
243 #include "map.h"
244 /*
245  * Internal function used by gchar()
246  * to get the next character from mapped input stream
247  * maintained by map.c
248  *
249  * Input is either taken from the mapper output or from
250  * terminal inpout (see explanation im map.c).
251  */
252 LOCAL int
inchar(wp)253 inchar(wp)
254 	ewin_t	*wp;
255 /*int nextc()*/
256 {
257 	register int	c;
258 
259 	if (!mapflag) {
260 #ifdef MAPESC
261 		if ((c = mapgetc()) == mapesc)
262 			c = mapgetc();
263 		else if (rmap(wp, c))
264 #else
265 		c = mapgetc(wp);
266 		if (c < 0)
267 			return (c);
268 		if (rmap(wp, c))
269 #endif
270 			c = gmap();
271 	} else if ((c = gmap()) == 0) {
272 /*		return (nextc());*/
273 		return (inchar(wp));
274 	}
275 	return (c);
276 }
277 
278 LOCAL	long	pmodflg;
279 
280 /*
281  * Low level function to read the next character from either the input
282  * terminal or the recover protocol file of a crashed edit session.
283  * This function is only used by map.c to get the next character.
284  *
285  * Since this is the low level input routine used below the mapper,
286  * it will never be called when there is no character in the map
287  * recover buffer anymore.
288  */
289 EXPORT int
getnextc(wp)290 getnextc(wp)
291 	ewin_t	*wp;
292 /*int inchar()*/
293 {
294 	int	c;
295 
296 	if (recover) {
297 		c = getc(rec_file);
298 		if (c == EOF) {			/* Recover protocol used up */
299 			fclose(rec_file);
300 			recover = 0;
301 			return (getnextc(wp));	/* Retry with input from tty */
302 		} else {
303 			return (c);
304 		}
305 	} else {
306 		if (interrupted) {
307 			interrupted = FALSE;
308 			c = intrchar;
309 		} else {
310 #ifdef	USE_GETCH
311 			c = getch();	/* DOS console input */
312 #else
313 /*#define	USE_GETCHAR*/
314 #ifdef	USE_GETCHAR
315 			/*
316 			 * Cannot use getchar() because of
317 			 * a bug in Linux stdio that repeates
318 			 * the last character after receiving a signal.
319 			 */
320 			while ((c = getchar()) < 0) {
321 				if (geterrno() != EINTR)
322 					break;
323 				clearerr(stdin);
324 			}
325 #else
326 			Uchar cc;
327 
328 			while ((c = read(STDIN_FILENO, &cc, 1)) < 0) {
329 				c = -1;
330 				if (geterrno() != EINTR)
331 					break;
332 			}
333 			if (c == 0)
334 				c = -1;
335 			else
336 				c = cc;
337 #endif
338 #endif	/* USE_GETCH */
339 		}
340 		*protp++ = (Uchar) c;
341 		if (wp->modflg - pmodflg >= PROTMARGIN || protp >= &protbuf[PROTBUFSIZE])
342 			flushprot(wp);
343 		return (c);
344 	}
345 }
346 
347 /*
348  * Non-interruptable version of getnextc() used by map.c
349  * Catches the interrupt and maps the interrupt character back
350  * to a usable input character.
351  */
352 EXPORT int
nigetnextc(wp)353 nigetnextc(wp)
354 	ewin_t	*wp;
355 {
356 static	sigjmps_t	nxjmp;
357 	sigjmps_t	*savjp = sjp;
358 	int	c;
359 
360 	if (sigsetjmp(nxjmp.jb, 1)) {
361 		if (intrchar > 0)
362 			interrupted++;
363 	} else {
364 		sjp = &nxjmp;
365 	}
366 	c = getnextc(wp);
367 	sjp = savjp;
368 	return (c);
369 }
370 
371 /*
372  * Flush the recover protocol file
373  */
374 EXPORT void
flushprot(wp)375 flushprot(wp)
376 	ewin_t	*wp;
377 {
378 	filewrite(protfile, C protbuf, protp - protbuf);
379 	fflush(protfile);
380 	protp = protbuf;
381 	pmodflg = wp->modflg;
382 }
383 
384 /*
385  * Close and delete the current recover protocol file
386  */
387 EXPORT void
deleteprot()388 deleteprot()
389 {
390 	if (protfile)
391 		fclose(protfile);
392 	protfile = NULL;
393 	if (protname[0] != '\0')
394 		unlink(C protname);
395 }
396 
397 /*
398  * Open/create a new recover protocol file.
399  */
400 EXPORT void
newprot(wp)401 newprot(wp)
402 	ewin_t	*wp;
403 {
404 		_RBUF	nbuf;
405 	register Uchar	*rbuf;
406 	register int	i;
407 
408 	for (i = 0, rbuf = (Uchar *) &nbuf; i < sizeof (nbuf); i++)
409 		*rbuf++ = '\0';
410 	if (strlen(C wp->curfile) > ((unsigned)(RECOVERNAMESIZE - 1)))
411 		writeerr(wp, "FILE NAME TO LONG");
412 	sprintf(C nbuf.r_head.r_name, "%.*s", RECOVERNAMESIZE - 1, wp->curfile);
413 #ifdef	OLDPROT
414 	sprintf(C nbuf.r_head.r_pos, "%11ld", wp->dot);
415 	sprintf(C nbuf.r_head.r_col, "%11d", wp->column);
416 #else
417 	js_snprintf(C nbuf.r_head.r_pos, sizeof (nbuf.r_head.r_pos),
418 						"%21lld", (Llong)wp->dot);
419 	js_snprintf(C nbuf.r_head.r_col, sizeof (nbuf.r_head.r_col),
420 						"%21lld", (Llong)wp->column);
421 	js_snprintf(C nbuf.r_head.r_version, sizeof (nbuf.r_head.r_version),
422 						"ved-%s", ved_version);
423 #endif
424 
425 	if (protfile) fclose(protfile);
426 	protfile = tmpfopen(wp, protname, "cwtub");
427 	filewrite(protfile, C &nbuf, sizeof (nbuf));
428 	protp = protbuf;
429 }
430 
431 /*
432  * Open the recover protocol file for read access.
433  */
434 EXPORT void
openrecoverfile(wp,name)435 openrecoverfile(wp, name)
436 	ewin_t	*wp;
437 	char	*name;
438 {
439 	rec_name = UC name;
440 	rec_file = opencomerr(wp, rec_name, "rb");
441 }
442 
443 /*
444  * Get the name of the file to recover as well as the file offset and
445  * cursor column at start of the crashed edit session.
446  */
447 EXPORT Uchar *
getrecoverfile(posp,colp)448 getrecoverfile(posp, colp)
449 	epos_t	*posp;
450 	int	*colp;
451 {
452 	_RBUF	rbuf;
453 	char	*p;
454 	char	vbuf[32];
455 	Llong	ll;
456 
457 	fileread(rec_file, C &rbuf, sizeof (rbuf));
458 
459 	strncpy(C curfname, C rbuf.r_head.r_name, sizeof (curfname));
460 	curfname[sizeof (curfname)-1] = '\0';
461 
462 #ifdef	OLDPROT
463 	astol(C rbuf.r_head.r_pos, posp);
464 	astoi(C rbuf.r_head.r_col, colp);
465 #else
466 	p = astoll(C rbuf.r_head.r_pos, &ll);
467 	*posp = ll;
468 	astoll(++p, &ll);
469 	*colp = ll;
470 	js_snprintf(vbuf, sizeof (vbuf), "ved-%s", ved_version);
471 	if (!streql(vbuf, C rbuf.r_head.r_version)) {
472 		errmsgno(EX_BAD, "Warning: recoverfile is from '%s', this is '%s'.\n",
473 				C rbuf.r_head.r_version, vbuf);
474 		sleep(1);
475 	}
476 #endif
477 
478 	return (curfname);
479 }
480 
481 
482 /*---------------------------------------------------------------------------
483 |
484 | Output routines
485 |
486 +---------------------------------------------------------------------------*/
487 
488 /*
489  * A putchar that is callable via function pointers.
490  * Does not maintain 'cpos' values.
491  */
492 EXPORT int
putoutchar(c)493 putoutchar(c)
494 	Uchar	c;
495 {
496 	return (putchar(c));
497 }
498 
499 #define	_pchar(c)	(((c) == '\n'?\
500 				(putchar('\r'), cpos.vp++, cpos.hp = 0) \
501 			:\
502 				cpos.hp++), \
503 			putchar(c))
504 
505 /*
506  * Output a character and maintain 'cpos' (cpos.hp and cpos.vp) values.
507  * pchar() should only be called from functions that deal with cursor
508  * positioning. Other functions use putoutchar() instead.
509  * putchar() is usually not the standard c-library/stdio character output
510  * macro. It uses our private increased buffering instead.
511  */
512 LOCAL void
pchar(c)513 pchar(c)
514 	Uchar	c;
515 {
516 #ifdef	_pchar
517 	_pchar(c);
518 #else
519 	if (c == '\n') {
520 		putchar('\r');
521 		cpos.vp++;	/* we are at the beginning of the next line */
522 		cpos.hp = 0;
523 	} else {
524 		cpos.hp++;
525 	}
526 	putchar(c);
527 #endif
528 }
529 
530 /*
531  * External interface to pchar() that handles alternate video properly
532  */
533 EXPORT void
addchar(c)534 addchar(c)
535 	Uchar	c;
536 {
537 	if (markon) {
538 		if (c == '\n') {
539 			cpos.vp++;
540 			cpos.hp = 0;
541 		} else {
542 			cpos.hp++;
543 		}
544 		*markbuf++ = c;
545 		if (markbuf >= Markbuffer + sizeof (Markbuffer) - 1) {
546 			offmark();
547 			onmark();
548 		}
549 	} else {
550 #ifdef	_pchar
551 		_pchar(c);
552 #else
553 		pchar(c);
554 #endif
555 	}
556 }
557 
558 /*
559  * Call this to start writing in aternate video
560  */
561 EXPORT void
onmark()562 onmark()
563 {
564 	if (! f_alternate_video)
565 		return;
566 	markbuf = Markbuffer;
567 	markon = 1;
568 }
569 
570 /*
571  * Call this to stop writing in aternate video and flush the altvideo buffer
572  */
573 EXPORT void
offmark()574 offmark()
575 {
576 	if (! markon)
577 		return;
578 	*markbuf = '\0';
579 	WRITE_ALT(C Markbuffer);
580 	markon = 0;
581 }
582 
583 /*
584  * String interface for addchar()
585  */
586 #ifdef	__used__
587 EXPORT void
addstr(s)588 addstr(s)
589 	register Uchar	*s;
590 {
591 	while (*s) {
592 		addchar(*s++);
593 	}
594 }
595 #endif
596 
597 /*
598  * String interface for pchar()
599  */
600 EXPORT void
output(s)601 output(s)
602 	register Uchar	*s;
603 {
604 	while (*s) {
605 		pchar(*s++);
606 	}
607 }
608 
609 /*
610  * Print a (null terminated) string.
611  * Use the character table to expand the string.
612  * Use pchar() to output the character to maintain 'cpos' values.
613  * Print exactly 'len' "real" characters by either truncating the string or
614  * padding with space characters.
615  */
616 EXPORT void
printfield(str,len)617 printfield(str, len)
618 	register Uchar	*str;
619 	register int	len;
620 {
621 	extern   Uchar	*ctab[];
622 	register Uchar	*s;
623 	register Uchar	**rctab = ctab;
624 
625 	while (len > 0)
626 		if (*str) {
627 			s = rctab[*str++];
628 			while (*s && --len >= 0)
629 				pchar(*s++);
630 		} else {
631 			pchar((Uchar) ' ');
632 			len--;
633 		}
634 }
635 
636 /*
637  * Print a (non null terminated) string.
638  * Use the character table to expand the string.
639  * Use pchar() to output the character to maintain 'cpos' values.
640  */
641 EXPORT void
printstring(str,len)642 printstring(str, len)
643 	register Uchar	*str;
644 	register int	len;
645 {
646 	extern   Uchar	*ctab[];
647 	register Uchar	*s;
648 	register Uchar	**rctab = ctab;
649 
650 	while (--len >= 0) {
651 		s = rctab[*str++];
652 		while (*s)
653 			pchar(*s++);
654 	}
655 }
656 
657 /*
658  * Ring the bell on screen
659  */
660 EXPORT void
ringbell()661 ringbell()
662 {
663 	if (streql(getenv("BEEP"), "off"))
664 		return;
665 	putoutchar(7);
666 }
667 
668 #ifdef	BFSIZE
669 /*
670  * Flush our private outpout buffer, take next character as arg
671  */
672 EXPORT int
_bflush(c)673 _bflush(c)
674 	Uchar	c;
675 {
676 	if (_bb._ptr)
677 		filewrite(stdout, C _bb._buf, BFSIZE - ++_bb._count);
678 	_bb._ptr = _bb._buf;
679 	_bb._count = BFSIZE - 1;
680 	*_bb._ptr++ = c;
681 	return (0);
682 }
683 
684 /*
685  * Flush our private outpout buffer (no arg version)
686  */
687 EXPORT int
_bufflush()688 _bufflush()
689 {
690 	if (_bb._ptr)
691 		filewrite(stdout, C _bb._buf, BFSIZE - _bb._count);
692 	_bb._ptr = _bb._buf;
693 	_bb._count = BFSIZE;
694 	return (fflush(stdout));
695 }
696 #endif
697