|
|
1.1 root 1: /*
2: * This program is in public domain; written by Dave G. Conroy.
3: * This file contains the main driving routine, and some keyboard processing
4: * code, for the MicroEMACS screen editor.
5: * Modified by W. L. Sheldon for use with the COHERENT Operating System -
6: * VT100 terminal key bindings.
7: */
8:
9: #include <stdio.h>
10: #include "ed.h"
11:
12: #if VMS
13: #include <ssdef.h>
14: #define GOOD (SS$_NORMAL)
15: #define BAD (SS$_ABORT)
16: #endif
17:
18: #ifndef GOOD
19: #define GOOD 0
20: #define BAD 1
21: #endif
22:
23: int currow; /* Working cursor row */
24: int curcol; /* Working cursor column */
25: int curgoal; /* Goal column */
26: BUFFER *curbp; /* Current buffer */
27: WINDOW *curwp; /* Current window */
28: BUFFER *bheadp; /* BUFFER listhead */
29: WINDOW *wheadp; /* WINDOW listhead */
30: BUFFER *blistp; /* Buffer list BUFFER */
31: #if LIBHELP
32: BUFFER *helpbp; /* Help buffer */
33: #endif
34: BUFFER *errbp; /* Error file BUFFER */
35: uchar pat[NPAT]; /* Pattern */
36:
37: uchar errfile[NFILEN]; /* Error file name */
38:
39: #if LIBHELP
40: uchar *helpfile=0; /* Help file name ptr. */
41: uchar *helpindex=0; /* Help index file name ptr. */
42: uchar hfname[NFILEN]; /* Help file name place */
43: uchar hiname[NFILEN]; /* Help index file name place */
44: #endif
45: extern char *strcpy(); /* copy string */
46: extern char *getenv();
47:
48: /*
49: * File-name list for command line...
50: */
51: uchar *cfiles[NCFILES]; /* Command line specified files */
52: int cfilecnt; /* File count... */
53:
54: /*
55: * Command line switch flags...
56: */
57: unsigned int runswitch;
58:
59: #if LK201
60: /*
61: * Mapping table for all of the funny keys with the numeric parameters
62: * on the LK201.
63: * Indexed by the code, which is between 0 (unused) and 34 (F20).
64: * An entry of 0 means no mapping. The map goes to command keys.
65: * If I had a "special" bit, I could use the code in the escape sequence
66: * as a key code, and return (for example) "do" as SPECIAL + 29.
67: * Then the dispatch would be done by the default keymap. This is probably a
68: * better way to go.
69: */
70: short lkmap[] = {
71: #if EXKEYS
72: 0,
73: FN18, /* 1 Find */
74: FN19, /* 2 Insert here */
75: FN1A, /* 3 Remove */
76: FN1B, /* 4 Select */
77: FN1C, /* 5 Previous screen */
78: FN1D, /* 6 Next screen */
79: 0,
80: 0,
81: 0,
82: FN17, /* 10 Compose */
83: 0,
84: FN3, /* 12 Print screen */
85: 0,
86: FN4, /* 14 F4 */
87: 0,
88: 0,
89: FN6, /* 17 F6 */
90: FN7, /* 18 F7 */
91: FN8, /* 19 F8 */
92: FN9, /* 20 F9 */
93: FNA, /* 21 F10 */
94: 0,
95: 0,
96: 0,
97: 0,
98: FNE, /* 26 F14 */
99: 0,
100: FN15, /* 28 Help */
101: FN16, /* 29 Do C-X E */
102: 0,
103: FN11, /* 31 F17 C-X P */
104: FN12, /* 32 F18 C-X N */
105: FN13, /* 33 F19 C-X Z */
106: FN14 /* 34 F20 C-X C-Z */
107: #else
108: 0,
109: OBND|CTRL|'S', /* 1 Find */
110: OBND|CTRL|'Y', /* 2 Insert here */
111: OBND|CTRL|'W', /* 3 Remove */
112: OBND|CTRL|'@', /* 4 Select */
113: OBND|META|'V', /* 5 Previous screen */
114: OBND|CTRL|'V', /* 6 Next screen */
115: 0,
116: 0,
117: 0,
118: 0, /* 10 Compose */
119: 0,
120: 0, /* 12 Print screen */
121: 0,
122: 0, /* 14 F4 */
123: 0,
124: 0,
125: 0, /* 17 F6 */
126: 0, /* 18 F7 */
127: 0, /* 19 F8 */
128: 0, /* 20 F9 */
129: 0, /* 21 F10 */
130: 0,
131: 0,
132: 0,
133: 0,
134: 0, /* 26 F14 */
135: 0,
136: 0, /* 28 Help */
137: OBND|PFX1|'E', /* 29 Do C-X E */
138: 0,
139: OBND|PFX1|'P', /* 31 F17 C-X P */
140: OBND|PFX1|'N', /* 32 F18 C-X N */
141: OBND|PFX1|'Z', /* 33 F19 C-X Z */
142: OBND|PFX1|CTRL|'Z' /* 34 F20 C-X C-Z */
143: #endif
144: };
145: #endif
146:
147: main(argc, argv)
148: uchar *argv[];
149: {
150: register int c;
151: register int f;
152: register int n;
153: uchar bname[NBUFN];
154:
155: #if MSDOS
156: setkeys();
157: #endif
158: #if IBM
159: vidnit();
160: #endif
161: for (c = 0; c < MAXREB; c++) /* nothing in the new table */
162: bind.table[c].k_synonym = bind.table[c].k_code = -1;
163: runswitch = 0; /* Initialize the switches */
164: bind.ffold = FALSE; /* initialize the fold flg */
165: bind.bracket = 1; /* initialize bracket mode */
166: bind.pfx1 = CTRL|'X'; /* initialize prefix keys */
167: bind.pfx2 = bind.pfx3 = -1;
168: bind.repeat = CTRL|'U';
169: argproc(argc, argv); /* Parse the arg list */
170: #if GEM
171: if (runswitch & CF_GRABMEM) /* Get largest chunk of */
172: grabmem(0, 0); /* memory (ST only) */
173: #endif
174: #if _I386
175: {
176: /*
177: * Speed up processing on 80386 for small files.
178: */
179: char *junk;
180:
181: if (NULL != (junk = malloc(128L * 1024L)))
182: free(junk);
183: }
184: #endif
185: topen(); /* Force the length setup */
186: strcpy(bname, "main"); /* Work out the name of */
187: if (cfilecnt > 0) /* the default buffer. */
188: makename(bname, cfiles[0]); /* Make a buffer name */
189: edinit(bname); /* Buffers, windows. */
190: vtinit(); /* Displays. */
191: if (cfilecnt > 0) { /* If there are files */
192: update(); /* You have to update */
193: readin(cfiles[0]); /* in case "[New file]" */
194: }
195: if (cfilecnt > 1) { /* If more than one */
196: n = (term.t_nrow - cfilecnt - 1) / cfilecnt;
197: for (c = 1; c < cfilecnt ; c++) { /* For all other files... */
198: splitwind(0,0); /* Split this window... */
199: if ((f=curwp->w_ntrows-n) != 0)
200: shrinkwind(0,f); /* Even out the windows */
201: nextwind(0,0); /* Go on to the next one */
202: visitfile(cfiles[c]); /* Read in that file */
203: }
204: }
205: if ((runswitch & CF_ERROR) != 0) {
206: splitwind(0,0); /* Split this window */
207: f = curwp->w_ntrows - ERRLINES; /* Make error window small */
208: if (f > 0)
209: shrinkwind(0,f);
210: readerr();
211: nextwind();
212: mlerase();
213: update();
214: nexterr(0,1);
215: update();
216: }
217: lastflag = 0; /* Fake last flags. */
218: if (NULL != bind.macs[MAXMAC]) /* initialization macro */
219: doMac(bind.macs + MAXMAC, FALSE, 1);
220:
221: for (;;) {
222: update(); /* Fix up the screen */
223: c = getbind(0); /* Get a key */
224: if (mpresf != FALSE) { /* If a message there */
225: mlerase(); /* get rid of it... */
226: update(); /* Fix screen */
227: if (c == ' ') /* ITS EMACS does this */
228: continue; /* (eat a space) */
229: }
230: f = FALSE;
231: n = 1;
232: if (c == bind.repeat) { /* ^U, start argument */
233: int ctmp;
234:
235: f = TRUE; /* We have a count */
236: n = getnum("Arg", 4, &ctmp); /* get the count */
237: c = ctmp; /* And get the last chr */
238: }
239: if (kbdmip != NULL) { /* Save macro strokes. */
240: if (kbdmip > (kbdm + ((NKBDM - 3)/2))) {
241: ctrlg(FALSE, 0);
242: continue;
243: }
244: if (f != FALSE) {
245: *kbdmip++ = bind.repeat;
246: *kbdmip++ = n;
247: }
248: *kbdmip++ = c;
249: }
250: bracketoff();
251: execute(c, f, n); /* Do it. */
252: }
253: }
254:
255: /*
256: * Initialize all of the buffers and windows.
257: * The buffer name is passed down as an argument, because the main routine may
258: * have been told to read in a file by default, and we want the buffer name to
259: * be right.
260: */
261: edinit(bname)
262: uchar bname[];
263: {
264: register BUFFER *bp;
265: register WINDOW *wp;
266:
267: bp = bfind(bname, TRUE, 0); /* First buffer */
268: blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer */
269: #if LIBHELP
270: helpbp = bfind("[Help]", TRUE, BFTEMP|BFHELP); /* Help buffer */
271: #endif
272: wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
273: if (bp==NULL || wp==NULL || blistp==NULL)
274: abort();
275: curbp = bp; /* Make this current */
276: wheadp = wp;
277: curwp = wp;
278: wp->w_wndp = NULL; /* Initialize window */
279: wp->w_bufp = bp;
280: bp->b_nwnd = 1; /* Displayed. */
281: wp->w_linep = bp->b_linep;
282: wp->w_dotp = bp->b_linep;
283: wp->w_doto = 0;
284: wp->w_markp = NULL;
285: wp->w_marko = 0;
286: wp->w_toprow = 0;
287: wp->w_ntrows = term.t_nrow-1; /* "-1" for mode line. */
288: wp->w_force = 0;
289: wp->w_flag = WFMODE|WFHARD; /* Full. */
290: }
291:
292:
293: /*
294: * Read in a key. Do the standard keyboard preprocessing.
295: * Convert the keys to the internal character set. On the LK201, which lacks
296: * a reasonable ESC key, make the grave accent a meta key too; this is a fairly
297: * common customization around Digital. Also read and decode the arrow keys,
298: * and other special keys. This is done in Rainbow mode; does this work on all
299: * the terminals with LK201 keyboards?
300: */
301: getkey()
302: {
303: register int c;
304: #if LK201
305: register int n;
306: for (;;) {
307: if ((c = tgetc()) == AGRAVE) /* Alternate M- prefix. */
308: return (META | getCtl());
309: if (c == METACH) { /* M-, or special key. */
310: if ((c = tgetc()) == '[') { /* Arrows and extras. */
311: switch (c = tgetc()) {
312: case 'A':
313: return (OBND | CTRL | 'P');
314: case 'B':
315: return (OBND | CTRL | 'N');
316: case 'C':
317: return (OBND | CTRL | 'F');
318: case 'D':
319: return (OBND | CTRL | 'B');
320: }
321: if (c >= '0' && c <= '9') {
322: n = 0;
323: do {
324: n = 10*n + c - '0';
325: }
326: while ((c = tgetc()) >= '0' && c <= '9');
327: if (c == '~' && n <= 34 && (c = lkmap[n]))
328: return (c);
329: }
330: continue;
331: }
332: if (c == 'O') {
333: switch (tgetc()) {
334: case 'P': /* PF1 => M-X (Future) */
335: return (OBND | META | 'X');
336: case 'Q': /* PF2 => C-Q */
337: return (OBND | CTRL | 'Q');
338: case 'R': /* PF3 => C-S */
339: return (OBND | CTRL | 'S');
340: case 'S': /* PF4 => C-R */
341: return (OBND | CTRL | 'R');
342: }
343: continue;
344: }
345: return (META | toCtl(c));
346: }
347: break;
348: }
349: #else
350: #if VT100
351: for (;;) {
352: if ((c = tgetc()) == METACH) { /* Apply M- prefix */
353: if ((c = tgetc()) == '[') { /* Arrow keys. */
354: switch (tgetc()) {
355: case 'A': /* up arrow */
356: return (OBND | CTRL | 'P');
357: case 'B': /* down arrow */
358: return (OBND | CTRL | 'N');
359: case 'C': /* right arrow */
360: return (OBND | CTRL | 'F');
361: case 'D': /* left arrow */
362: return (OBND | CTRL | 'B');
363: case 'F': /* END key */
364: return (OBND | CTRL | 'E');
365: case 'H': /* home key */
366: return (OBND | CTRL | 'A');
367: case 'M': /* F1 key, help */
368: return (OBND | META | '?');
369: case 'N': /* F2 key, newfile */
370: return (OBND | PFX1 | CTRL | 'V');
371: case 'I': /* PGUP key */
372: return (OBND | META | 'V');
373: case 'G': /* PGDN key */
374: return (OBND | CTRL | 'V');
375: case 'O': /* F3 search forward */
376: return (OBND | META | 'S');
377: case 'P': /* F4 search backward */
378: return (OBND | META | 'R');
379: case 'Q': /* F5 search & replace */
380: return (OBND | META | '%');
381: case 'R': /* F6 next window */
382: return (OBND | PFX1 | 'N');
383: case 'T': /* F8 save/exit */
384: return (OBND | CTRL | 'Z');
385: case 'V': /* F10 close other wndws */
386: return (OBND | PFX1 | '1');
387: }
388: continue;
389: }
390: if (c == 'O') { /* function keys */
391: switch (tgetc()) {
392: case 'P': /* PF1 => M-X (Future) */
393: return (OBND | META | 'X');
394: case 'Q': /* PF2 => C-Q */
395: return (OBND | CTRL | 'Q');
396: case 'R': /* PF3 => C-S */
397: return (OBND | CTRL | 'S');
398: case 'S': /* PF4 => C-R */
399: return (OBND | CTRL | 'R');
400: }
401: continue;
402: }
403: return (META | toCtl(c));
404: }
405: break;
406: }
407: #else
408: if ((c = tgetc()) == METACH) /* Apply M- prefix */
409: return (META | getCtl());
410: #endif
411: #endif
412: if (c == 0x0D && bind.autoindent)
413: return (OBND | CTRL | 'J');
414: if (c >= 0x00 && c <= 0x1F) /* C0 control -> C- */
415: return (CTRL | '@' | c);
416: if (c == 127)
417: return (OBND | CTRL | 'D');
418: return (c);
419: }
420:
421: /*
422: * Apply control modifications to a key
423: */
424: toCtl(c)
425: register int c;
426: {
427: if (c>='a' && c<='z') /* Force to upper */
428: return (c - 0x20);
429: if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
430: return (c | CTRL | '@');
431: return (c);
432: }
433:
434: getCtl()
435: {
436: return (toCtl(tgetc()));
437: }
438:
439: /*
440: * Fancy quit command, as implemented by Norm.
441: * If the current buffer has changed do a write current buffer and exit emacs,
442: * otherwise simply exit.
443: */
444: quickexit(f, n)
445: {
446: if ((curbp->b_flag&BFCHG) != 0 /* Changed. */
447: && (curbp->b_flag&(BFTEMP|BFERROR|BFNOWRT)) == 0)
448: /* Real and not R/O... */
449: filesave(f, n);
450: quit(f, n); /* conditionally quit */
451: }
452:
453: /*
454: * Quit command. If an argument, always quit. Otherwise confirm
455: * if a buffer has been changed and not written out.
456: * Normally bound to "C-X C-C".
457: */
458: quit(f, n)
459: {
460: register int s = FALSE;
461:
462: if (f != FALSE /* Argument forces it. */
463: || anycb() == FALSE /* All buffers clean. */
464: || (s=mlyesno("Quit")) == TRUE) { /* User says it's OK. */
465: vttidy();
466: #if MSDOS
467: resetkeys();
468: #endif
469: if (f != FALSE || s != FALSE)
470: exit(BAD);
471: else
472: exit(GOOD);
473: }
474: #if MSDOS
475: setkeys();
476: #endif
477: return (s);
478: }
479:
480: /*
481: * Get binding char. Turn prefix chars into or'ed bits.
482: */
483: getbind(bound)
484: register int bound;
485: {
486: register int c;
487:
488: if (((c = getkey()) == bind.pfx1) && !(bound & PFX1))
489: return (PFX1 | toCtl(getbind(bound | PFX1)));
490: if ((c == bind.pfx2) && !(bound & PFX2))
491: return (PFX2 | toCtl(getbind(bound | PFX2)));
492: if ((c == bind.pfx3) && !(bound & PFX3))
493: return (PFX3 | toCtl(getbind(bound | PFX3)));
494: return (c);
495: }
496:
497: /*
498: * Abort.
499: * Beep the beeper.
500: * Kill off any keyboard macro,
501: * etc., that is in progress.
502: * Sometimes called as a routine,
503: * to do general aborting of
504: * stuff.
505: */
506: ctrlg(f, n)
507: {
508: tbeep();
509: if (kbdmip != NULL) {
510: if (NULL != kbdm) {
511: free(kbdm);
512: kbdm = NULL;
513: }
514: kbdmip = NULL;
515: }
516: return (ABORT);
517: }
518:
519: #if MSDOS
520: setkeys() /* redefine cursor keys */
521: /* so that they make */
522: /* sense to microEMACS */
523: /* as described in IBM */
524: /* DOS tech. reference */
525: /* manual */
526: {
527: #if !IBM
528: static uchar *ctlseq[] = {
529: "\033[0;72;16p", /* up = <ctrl-p> */
530: "\033[0;77;6p", /* right = <ctrl-f> */
531: "\033[0;75;2p", /* left = <ctrl-b> */
532: "\033[0;80;14p", /* down = <ctrl-n> */
533: "\033[0;81;22p", /* pg dn = <ctrl-v> */
534: "\033[0;73;27;86p", /* pg up = <esc>V */
535: "\033[0;71;27;60p", /* home = <esc>< */
536: "\033[0;79;27;62p", /* end = <esc>> */
537: "\033[0;83;127p", /* del = del */
538: "\033[0;3;27;46p", /* <ctrl-@> = <exc>. */
539: NULL
540: };
541: register uchar **ctlp;
542:
543: for (ctlp = ctlseq; NULL != *ctlp; ctlp++)
544: mlwrite(*ctlp);
545: #endif
546: }
547:
548:
549:
550: resetkeys() /* redefine cursor keys */
551: /* to default values */
552: {
553: #if !IBM
554: static uchar *ctlseq[] = {
555: "\033[0;72;0;72p",
556: "\033[0;77;0;77p",
557: "\033[0;75;0;75p",
558: "\033[0;80;0;80p",
559: "\033[0;81;0;81p",
560: "\033[0;73;0;73p",
561: "\033[0;71;0;71p",
562: "\033[0;79;0;79p",
563: "\033[0;83;0;83p",
564: "\033[0;3;0;3p",
565: NULL
566: };
567: register uchar **ctlp;
568:
569: for (ctlp = ctlseq; NULL != *ctlp; ctlp++)
570: mlwrite(*ctlp);
571: #endif
572: }
573: #endif
574:
575: argproc(argc, argv)
576: int argc;
577: uchar **argv;
578: {
579: int i;
580: uchar *flexn;
581: extern uchar *flexName();
582: uchar *ptr;
583:
584: if (!bind.tabsiz &&
585: (NULL == (ptr = getenv("TABSIZ")) ||
586: !(bind.tabsiz = atoi(ptr))))
587: bind.tabsiz = 8;
588: cfilecnt = 0;
589: flexn = NULL;
590: for (i = 1; i < argc; i++) {
591: ptr = argv[i]; /* Get this argument... */
592: if (*ptr == '-') { /* Is this a switch? */
593: switch (ptr[1]) {
594: case 'd':
595: runswitch |= CF_DEBUG;
596: break;
597: case 'e':
598: runswitch |= CF_ERROR;
599: if (ptr[2] == 0) {
600: if (++i == argc) {
601: runswitch &= ~CF_ERROR;
602: continue;
603: }
604: strcpy(errfile, argv[i]);
605: }
606: else {
607: strcpy(errfile, &ptr[2]);
608: }
609: break;
610: case 'f':
611: if (ptr[2] == 0) {
612: if (++i == argc)
613: continue;
614: flexn = argv[i];
615: }
616: else
617: flexn = ptr + 1;
618: break;
619: #if LIBHELP
620: case 'h': /* Alternate help */
621: if (ptr[2] == 0) {
622: if (++i == argc) {
623: continue;
624: }
625: strcpy(hfname, argv[i]);
626: }
627: else {
628: strcpy(hfname, &ptr[2]);
629: }
630: strcpy(hiname, hfname);
631: #if GEM || MSDOS
632: strcat(hfname, ".hlp");
633: #endif
634: strcat(hiname, ".idx");
635: helpfile = hfname;
636: helpindex = hiname;
637: break;
638: #endif
639: case 'w':
640: runswitch |= CF_WARN;
641: break;
642: #if NATIVE && GEM
643: case 'l': /* long screen */
644: runswitch |= CF_LONGSCR;
645: break;
646: case 't': /* very long screen */
647: runswitch |= CF_VLONG;
648: break;
649: #endif
650: #if GEM
651: case 'x': /* grab all memory */
652: runswitch |= CF_GRABMEM;
653: break;
654: #endif
655: default:
656: break;
657: }
658: /* Process this switch */
659: }
660: else { /* Otherwise... */
661: if (cfilecnt >= NCFILES)
662: continue;
663: cfiles[cfilecnt++] = ptr; /* This is a file. */
664: #if GEM
665: fixname(cfiles[cfilecnt-1]);
666: #endif
667: }
668: }
669: if (NULL == flexn)
670: loadBup(flexName(), TRUE);
671: else
672: loadBup(flexn, ABORT);
673: }
674:
675: #if EXKEYS
676: /*
677: * Do nothing. ("Dead")
678: */
679: ignore()
680: {
681: return TRUE;
682: }
683: #endif
684:
685: /*
686: * Get a numeric argument...
687: */
688: getnum(prompt, n, lastc)
689: register uchar *prompt;
690: register int n;
691: register int *lastc;
692: {
693: register int mflag = 0;
694: register int c;
695:
696: mflag = 0; /* that can be discarded. */
697: mlwrite("%s: %d", prompt, n);
698: while ((c = getbind(0)) >='0' && c<='9'
699: || c==bind.repeat || c=='-'){
700: if (c == bind.repeat)
701: n = n*4;
702: /*
703: * If dash, and start of argument string, set arg.
704: * to -1. Otherwise, insert it.
705: */
706: else if (c == '-') {
707: if (mflag)
708: break;
709: n = 0;
710: mflag = -1;
711: }
712: /*
713: * If first digit entered, replace previous argument
714: * with digit and set sign. Otherwise, append to arg.
715: */
716: else {
717: if (!mflag) {
718: n = 0;
719: mflag = 1;
720: }
721: n = 10*n + c - '0';
722: }
723: mlwrite("%s: %d", prompt, (mflag >=0) ? n : (n ? -n : -1));
724: }
725: *lastc = c; /* Return the terminal char. */
726: /*
727: * Make arguments preceded by a minus sign negative and change
728: * the special argument "^U -" to an effective "^U -1".
729: */
730: if (mflag == -1) {
731: if (n == 0)
732: n++;
733: n = -n;
734: }
735: return n;
736: }
737:
738: #if GEM
739: /*
740: * The following routine gets around a problem with GEMDOS Malloc(),
741: * it forces a single, very large Malloc() so very large files can
742: * be read.
743: * It is available only on the Atari ST, and is bound to M-+
744: * it can also be specified using the -x switch
745: */
746: #include <osbind.h>
747:
748: grabmem(f, n)
749: int f, n;
750: {
751: extern char *lmalloc();
752: register char *p = NULL;
753: register long t;
754:
755: t = Malloc(-1L); /* How big is free memory? */
756: while (p == NULL) { /* Until we have a block */
757: t -= 4096L; /* shrink the block */
758: if (t < 2048) { /* if too small, tell user */
759: mlwrite( "[Cannot allocate memory]" );
760: return 1; /* and fail */
761: }
762: p = lmalloc(t); /* Try to get this chunk */
763: } /* loop until success or fail */
764: free(p); /* return chunk to arena */
765: mlwrite( "[Allocated %ld bytes]", t ); /* tell user how much */
766: return 0; /* and return success */
767: }
768: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.