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