1 /*
2 * MicroEMACS 4.00
3 * written by Daniel M. Lawrence
4 * based on code by Dave G. Conroy.
5 *
6 * (C)Copyright 1988-1995 by Daniel M. Lawrence
7 * MicroEMACS 4.00 can be copied and distributed freely for any
8 * non-commercial purposes. MicroEMACS 4.00 can only be incorporated
9 * into commercial software with the permission of the current author.
10 *
11 * This file contains the main driving routine, and some keyboard processing
12 * code, for the MicroEMACS screen editor.
13 *
14 */
15
16 #include <stdio.h>
17
18 /* make global definitions not external */
19 #define maindef
20
21 #include "estruct.h" /* global structures and defines */
22 #include "eproto.h" /* variable prototype definitions */
23 #include "efunc.h" /* function declarations and name table */
24 #include "edef.h" /* global definitions */
25 #include "elang.h" /* human language definitions */
26 #include "ebind.h" /* default key bindings */
27
28 /* for many different systems, increase the default stack space */
29
30 #if MSDOS && MSC
31 #if _MSC_VER < 700
32 unsigned _stackavail = 40000;
33 #endif
34 #endif
35
36 #if MSDOS && LATTICE
37 unsigned _stack = 20000;
38 #endif
39
40 #if MSDOS && ZTC
41 int _okbigbuf = 0; /* Only allocate memory when needed.*/
42 int _stack = 20000; /* Reset the ol' stack size.*/
43 #endif
44
45 #if TOS && MWC
46 long _stksize = 20000L; /* reset stack size (must be even) */
47 #endif
48
49 #if MSDOS && TURBO
50 extern unsigned int _stklen = 10000;
51 #endif
52
53 /* make VMS happy... */
54
55 #if VMS
56 #include <ssdef.h>
57 #define GOOD (SS$_NORMAL)
58 #endif
59
60 #ifndef GOOD
61 #define GOOD 0
62 #endif
63
64 /*
65 * Systems that handle window size changes via signals.
66 */
67 #if HANDLE_WINCH
68 #include <signal.h>
69 #endif
70
71 /*
72 This is the primary entry point that is used by command line
73 invocation, and by applications that link with microemacs in
74 such a way that each invocation of Emacs is a fresh environment.
75
76 There is another entry point in VMS.C that is used when
77 microemacs is "linked" (In quotes, because it is a run-time link
78 rather than a link-time link.) with applications that wish Emacs
79 to preserve it's context across invocations. (For example,
80 EMACS.RC is only executed once per invocation of the
81 application, instead of once per invocation of Emacs.)
82
83 Note that re-entering an Emacs that is saved in a kept
84 subprocess would require a similar entrypoint.
85 */
86
87 #if CALLED
emacs(argc,argv)88 int emacs(argc, argv)
89 #else
90 #if XVT
91 called_main(argc, argv)
92 #else
93 main(argc, argv)
94 #endif
95 #endif
96
97 int argc; /* # of arguments */
98 char *argv[]; /* argument strings */
99
100 {
101 register int status;
102
103 #if HANDLE_WINCH
104 signal(SIGWINCH,winch_changed);
105 #endif
106
107 /* the room mechanism would deallocate undo info no failure....
108 its not set up yet, so make sure it doesn't try until the
109 editor is initialized */
110 bheadp = (BUFFER *) NULL;
111
112 /* Initialize the editor */
113 eexitflag = FALSE;
114 #if !WINDOW_MSWIN
115 vtinit(); /* Terminal */
116 #endif
117
118 if (eexitflag)
119 goto abortrun;
120 edinit(mainbuf); /* Buffers, windows, screens */
121 ab_init(); /* initialize the abbreviation behavior */
122 varinit(); /* user variables */
123 initchars(); /* character set definitions */
124
125 #if MAGIC
126 mcdeltapat[0].mc_type = tapatledcm[0].mc_type = JMPTABLE;
127 mcdeltapat[0].u.jmptable = &deltapat;
128 tapatledcm[0].u.jmptable = &tapatled;
129 mcdeltapat[1].mc_type = tapatledcm[1].mc_type = MCNIL;
130 #endif
131
132 /* Process the command line and let the user edit */
133 #if VMS
134 expandargs(&argc, &argv); /* expand VMS wildcards.*/
135 #endif
136 dcline(argc, argv, TRUE);
137 edit:
138 status = editloop();
139 abortrun:
140
141 /* execute the macro the user had bound to $exithook */
142 eexitflag = FALSE;
143 execkey(&exithook, FALSE, 1);
144 if ((gflags & GFEXIT) == 0) {
145 eexitflag = FALSE;
146 goto edit;
147 }
148 eexitflag = TRUE;
149
150 /* close things down */
151 vttidy();
152 #if CLEAN
153 clean();
154 #endif
155 #if CALLED
156 return(status);
157 #else
158 exit(status);
159 #endif
160 }
161
162 #if CLEAN
163 /*
164 On some primitive operation systems, and when emacs is used as
165 a subprogram to a larger project, emacs needs to de-alloc its
166 own used memory, otherwise we just exit.
167 */
168
clean()169 PASCAL NEAR clean()
170
171 {
172 register BUFFER *bp; /* buffer list pointer */
173 register SCREEN *scrp; /* ptr to screen to dump */
174
175 /* first clean up the screens */
176 scrp = first_screen;
177 while (scrp) {
178 first_screen = scrp->s_next_screen;
179 free_screen(scrp);
180 scrp = first_screen;
181 }
182 wheadp = NULL;
183
184 /* then the buffers */
185 bp = bheadp;
186 while (bp) {
187 bp->b_nwnd = 0;
188 bp->b_flag = 0; /* don't say anything about a changed buffer! */
189 zotbuf(bp);
190 bp = bheadp;
191 }
192
193 /* and the kill buffers */
194 clear_ring(FALSE, 0);
195
196 /* clear some search variables */
197 #if MAGIC
198 mcclear();
199 rmcclear();
200 #endif
201 if (patmatch != NULL) {
202 free(patmatch);
203 patmatch = NULL;
204 }
205
206 /* dump the abbreviation list */
207 ab_clean();
208
209 /* dealloc the user variables */
210 varclean(uv_head);
211
212 /* and the video buffers */
213 vtfree();
214 }
215 #endif
216
217 /* Process a command line. May be called any time. */
218
dcline(argc,argv,firstflag)219 VOID PASCAL NEAR dcline(argc, argv, firstflag)
220
221 int argc;
222 char *argv[];
223 int firstflag; /* is this the first time in? */
224
225 {
226 register BUFFER *bp; /* temp buffer pointer */
227 register int firstfile; /* first file flag */
228 register int carg; /* current arg to scan */
229 register int startflag; /* startup executed flag */
230 BUFFER *firstbp = NULL; /* ptr to first buffer in cmd line */
231 int viewflag; /* are we starting in view mode? */
232 int gotoflag; /* do we need to goto a line at start? */
233 int gline; /* if so, what line? */
234 int gchar; /* and what character? */
235 int searchflag; /* Do we need to search at start? */
236 int errflag; /* C error processing? */
237 VDESC vd; /* variable num/type */
238 char bname[NBUFN]; /* buffer name of file to read */
239 #if MSDOS | OS2
240 unsigned char *scan; /* scan pointer for filenames */
241 #endif
242
243 #if CRYPT
244 int cryptflag; /* encrypting on the way in? */
245 char ekey[NPAT]; /* startup encryption key */
246 #endif
247 NOSHARE CONST extern char *pathname[]; /* startup file path/name array */
248
249 viewflag = FALSE; /* view mode defaults off in command line */
250 gotoflag = FALSE; /* set to off to begin with */
251 gline = 1;
252 gchar = 1; /* line and character to go to */
253 searchflag = FALSE; /* set to off to begin with */
254 firstfile = TRUE; /* no file to edit yet */
255 startflag = FALSE; /* startup file not executed yet */
256 errflag = FALSE; /* not doing C error parsing */
257 exec_error = FALSE; /* no macro error pending */
258 #if CRYPT
259 cryptflag = FALSE; /* no encryption by default */
260 #endif
261 disphigh = FALSE; /* don't escape high bit characters */
262 lterm[0] = 0; /* standard line terminators */
263
264 /* Parse a command line */
265 for (carg = 1; carg < argc; ++carg) {
266
267 /* Process Switches */
268 #if WMCS
269 if (argv[carg][0] == ':') {
270 #else
271 if (argv[carg][0] == '-') {
272 #endif
273 /* Process Startup macroes */
274 switch (argv[carg][1]) {
275
276 case 'c': /* -c for changable file */
277 case 'C':
278 viewflag = FALSE;
279 break;
280 case 'e': /* -e process error file */
281 case 'E':
282 errflag = TRUE;
283 break;
284 case 'g': /* -g for initial goto line */
285 case 'G':
286 gotoflag = TRUE;
287 gline = asc_int(&argv[carg][2]);
288 break;
289 case 'i': /* -i<var> <value> set an initial */
290 case 'I': /* value for a variable */
291 bytecopy(bname, &argv[carg][2], NVSIZE);
292 findvar(bname, &vd, NVSIZE + 1, VT_GLOBAL);
293 if (vd.v_type == -1) {
294 mlwrite(TEXT52, bname);
295 /* "%%No such variable as '%s'" */
296 break;
297 }
298 svar(&vd, argv[++carg]);
299 break;
300 #if CRYPT
301 case 'k': /* -k<key> for code key */
302 case 'K':
303 cryptflag = TRUE;
304 strcpy(ekey, &argv[carg][2]);
305 break;
306 #endif
307 case 'p': /* -p for initial goto char position */
308 case 'P':
309 gotoflag = TRUE;
310 gchar = asc_int(&argv[carg][2]);
311 break;
312 case 'r': /* -r restrictive use */
313 case 'R':
314 restflag = TRUE;
315 break;
316 case 's': /* -s for initial search string */
317 case 'S':
318 searchflag = TRUE;
319 bytecopy((char *) pat, &argv[carg][2], NPAT);
320 setjtable();
321 break;
322 case 'v': /* -v for View File */
323 case 'V':
324 viewflag = TRUE;
325 break;
326 default: /* unknown switch */
327 /* ignore this for now */
328 break;
329 }
330
331 } else if (argv[carg][0] == '+') {
332
333 /* +<line num> */
334 gotoflag = TRUE;
335 gline = asc_int(&argv[carg][1]);
336
337 } else if (argv[carg][0] == '@') {
338
339 /* Process Startup macroes */
340 if (startup(&argv[carg][1]) == TRUE)
341 /* don't execute emacs.rc */
342 startflag = TRUE;
343
344 #if WINDOW_MSWIN32
345 } else if ((argv[carg][0] != ' ') ||
346 (argv[carg][1] != '\0')) {
347 /* WinNT PDK2 causes spurious space arguments */
348 #else
349 } else {
350 #endif
351 /* Process an input file */
352 #if MSDOS | OS2
353 /* change forward slashes to back */
354 scan = (unsigned char *) argv[carg];
355 while (*scan) {
356 if (*scan == '/')
357 *scan = DIRSEPCHAR;
358 ++scan;
359 }
360 #endif
361 /* set up a buffer for this file */
362 makename(bname, argv[carg]);
363 unqname(bname);
364
365 /* set this to inactive */
366 bp = bfind(bname, TRUE, 0);
367 strcpy(bp->b_fname, argv[carg]);
368 #if WINDOW_MSWIN
369 fullpathname (bp->b_fname, NFILEN);
370 #endif
371 bp->b_active = FALSE;
372 if (firstfile) {
373 firstbp = bp;
374 firstfile = FALSE;
375 }
376
377 /* set the modes appropriatly */
378 if (viewflag)
379 bp->b_mode |= MDVIEW;
380 #if CRYPT
381 if (cryptflag) {
382 bp->b_mode |= MDCRYPT;
383 ecrypt((char *) NULL, 0);
384 ecrypt(ekey, strlen(ekey));
385 bytecopy(bp->b_key, ekey, NPAT);
386 }
387 #endif
388 }
389 }
390
391 /* if we are C error parsing... run it! */
392 if (errflag) {
393 if (startup("error.cmd") == TRUE)
394 startflag = TRUE;
395 }
396
397 /* if invoked with no other startup files,
398 run the system startup file here */
399 if (firstflag && startflag == FALSE)
400 startup("");
401
402 /* if there are any files to read, read the first one! */
403 if (firstflag) {
404 bp = bfind(mainbuf, FALSE, 0);
405 if (firstfile == FALSE && (gflags & GFREAD)) {
406 swbuffer(firstbp);
407 curbp->b_mode |= gmode;
408 update(TRUE);
409 mlwrite(lastmesg);
410 zotbuf(bp);
411 } else
412 bp->b_mode |= gmode;
413 } else {
414 swbuffer(firstbp);
415 curbp->b_mode |= gmode;
416 update(TRUE);
417 mlwrite(lastmesg);
418 }
419
420 /* Deal with startup gotos and searches */
421 if (gotoflag && searchflag) {
422 update(FALSE);
423 mlwrite(TEXT101);
424 /* "[Can not search and goto at the same time!]" */
425 } else if (gotoflag) {
426 if ((gotoline(TRUE, gline) == FALSE) ||
427 (forwchar(TRUE, gchar - 1) == FALSE)) {
428 update(FALSE);
429 mlwrite(TEXT102);
430 /* "[Bogus goto argument]" */
431 }
432 } else if (searchflag) {
433 if (forwhunt(FALSE, 0) == FALSE)
434 update(FALSE);
435 }
436 }
437
438 #if WINDOW_MSWIN
439 #define GETBASEKEY getbasekey
getbasekey()440 static int PASCAL NEAR getbasekey()
441
442 {
443 register int c;
444
445 notquiescent = -1; /* will be <= 0 only if get_key() is called
446 directly from editloop(). This is used to
447 restrict some windows-specific actions
448 (menus, sizing, etc...) when not called from
449 the lowest level of the editor */
450 c = get_key();
451 notquiescent = 1;
452 return c;
453 }
454 #else
455 #define GETBASEKEY get_key
456 #endif
457
458 /*
459 This is called to let the user edit something. Note that if you
460 arrange to be able to call this from a macro, you will have
461 invented the "recursive-edit" function.
462 */
463
editloop()464 PASCAL NEAR editloop()
465
466 {
467 register int c; /* command character */
468 register int f; /* default flag */
469 register int n; /* numeric repeat count */
470 register int mflag; /* negative flag on repeat */
471 register int basec; /* c stripped of meta character */
472 register int oldflag; /* old last flag value */
473 char time[6]; /* current display time */
474
475 /* setup to process commands */
476 lastflag = 0; /* Fake last flags. */
477
478 loop:
479 /* if a macro error is pending, wait for a character */
480 if (exec_error) {
481 #if WINDOW_MSWIN
482 mlhistory();
483 #else
484 mlforce(TEXT227);
485 /* "\n--- Press any key to Continue ---" */
486 tgetc();
487 #endif
488 sgarbf = TRUE;
489 update(FALSE);
490 mlferase();
491 exec_error = FALSE;
492 }
493
494 /* if we were called as a subroutine and want to leave, do so */
495 if (eexitflag)
496 return(eexitval);
497
498 /* execute the "command" macro...normally null */
499 oldflag = lastflag; /* preserve lastflag through this */
500 execkey(&cmdhook, FALSE, 1);
501 lastflag = oldflag;
502
503 /* Notify user of any intercepted system messages.
504 * VMS only right now.
505 */
506 #if VMS
507 if (pending_msg) {
508 pending_msg = FALSE;
509 echostring(brdcstbuf, 0, NSTRING);
510 }
511 #endif
512
513 /* update time on the bottom modeline? */
514 if (timeflag)
515 #if TYPEAH || WINDOW_MSWIN
516 if (!typahead())
517 #endif
518 {
519 getdtime(time);
520 if (strcmp(lasttime, time) != 0)
521 upmode();
522 }
523
524 /* update position on current modeline? */
525 if (posflag)
526 #if TYPEAH || WINDOW_MSWIN
527 if (!typahead())
528 #endif
529 upmode();
530
531 /*
532 * Did our window get resized?
533 */
534 #if HANDLE_WINCH
535 if (winch_flag) winch_new_size();
536 #endif
537 /* Fix up the screen */
538 update(FALSE);
539
540 /* get the next command from the keyboard */
541 discmd = TRUE;
542 disinp = TRUE;
543 c = GETBASEKEY();
544
545 /* if there is something on the command line, clear it */
546 if (mpresf != FALSE) {
547 mlerase();
548 update(FALSE);
549 }
550
551 /* override the arguments if prefixed */
552 if (prefix) {
553 if (is_lower(c & 255))
554 c = (c & ~255) | upperc(c & 255);
555 c |= prefix;
556 f = predef;
557 n = prenum;
558 prefix = 0;
559 } else {
560 f = FALSE;
561 n = 1;
562 }
563
564 /* do META-# processing if needed */
565
566 basec = c & ~META; /* strip meta char off if there */
567 if ((c & META) && ((basec >= '0' && basec <= '9') || basec == '-') &&
568 (getbind(c) == NULL)) {
569 f = TRUE; /* there is a # arg */
570 n = 0; /* start with a zero default */
571 mflag = 1; /* current minus flag */
572 c = basec; /* strip the META */
573 while ((c >= '0' && c <= '9') || (c == '-')) {
574 if (c == '-') {
575 /* already hit a minus or digit? */
576 if ((mflag == -1) || (n != 0))
577 break;
578 mflag = -1;
579 } else {
580 n = n * 10 + (c - '0');
581 }
582 if ((n == 0) && (mflag == -1)) /* lonely - */
583 mlwrite("Arg:");
584 else
585 mlwrite("Arg: %d", n * mflag);
586
587 c = GETBASEKEY(); /* get the next key */
588 }
589 n = n * mflag; /* figure in the sign */
590 }
591
592 /* do ^U repeat argument processing */
593
594 if (c == reptc) { /* ^U, start argument */
595 f = TRUE;
596 n = 4; /* with argument of 4 */
597 mflag = 0; /* that can be discarded. */
598 mlwrite("Arg: 4");
599 while ((c = GETBASEKEY()) >= '0' && c <= '9' ||
600 c == reptc || c == '-') {
601 if (c == reptc)
602 if ((n > 0) == ((n * 4) > 0))
603 n = n * 4;
604 else
605 n = 1;
606 /*
607 * If dash, and start of argument string, set arg.
608 * to -1. Otherwise, insert it.
609 */
610 else if (c == '-') {
611 if (mflag)
612 break;
613 n = 0;
614 mflag = -1;
615 }
616
617 /*
618 * If first digit entered, replace previous argument
619 * with digit and set sign. Otherwise, append to arg.
620 */
621 else {
622 if (!mflag) {
623 n = 0;
624 mflag = 1;
625 }
626 n = 10 * n + c - '0';
627 }
628 mlwrite("Arg: %d", (mflag >= 0) ? n : (n ? -n : -1));
629 }
630
631 /*
632 * Make arguments preceded by a minus sign negative and change
633 * the special argument "^U -" to an effective "^U -1".
634 */
635 if (mflag == -1) {
636 if (n == 0)
637 n++;
638 n = -n;
639 }
640 }
641
642 /* and execute the command */
643 execute(c, f, n);
644 goto loop;
645 }
646
647 /*
648 * Initialize all of the buffers, windows and screens. The buffer name is
649 * passed down as an argument, because the main routine may have been told
650 * to read in a file by default, and we want the buffer name to be right.
651 */
652
edinit(bname)653 VOID PASCAL NEAR edinit(bname)
654
655 char bname[]; /* name of buffer to initialize */
656
657 {
658 register BUFFER *bp;
659 register int index;
660
661 /* init the kill ring */
662 for (index = 0; index < NRING; index++) {
663 kbufp[index] = (KILL *) NULL;
664 kbufh[index] = (KILL *) NULL;
665 kskip[index] = 0;
666 kused[index] = KBLOCK;
667 }
668 kill_index = 0;
669
670 /* initialize some important globals */
671
672 readhook.k_ptr.fp = nullproc; /* set internal hooks to OFF */
673 readhook.k_type = BINDFNC;
674 wraphook.k_ptr.fp = wrapword;
675 wraphook.k_type = BINDFNC;
676 cmdhook.k_ptr.fp = nullproc;
677 cmdhook.k_type = BINDFNC;
678 writehook.k_ptr.fp = nullproc;
679 writehook.k_type = BINDFNC;
680 bufhook.k_ptr.fp = nullproc;
681 bufhook.k_type = BINDFNC;
682 exbhook.k_ptr.fp = nullproc;
683 exbhook.k_type = BINDFNC;
684 exithook.k_ptr.fp = nullproc;
685 exithook.k_type = BINDFNC;
686
687 /* allocate the first buffer */
688 bp = bfind(bname, TRUE, 0); /* First buffer */
689 blistp = bfind("[Buffers]", TRUE, BFINVS); /* Buffer list buffer */
690 slistp = bfind("[Screens]", TRUE, BFINVS); /* screen list buffer */
691 ulistp = bfind("[Undos]", TRUE, BFINVS); /* undo list buffer */
692 if (bp == NULL || blistp == NULL)
693 meexit(1);
694
695 /* and allocate the default screen */
696 first_screen = (SCREEN *) NULL;
697 init_screen("MAIN", bp);
698 if (first_screen == (SCREEN *) NULL)
699 meexit(1);
700
701 /* set the current default screen/buffer/window */
702 curbp = bp;
703 curwp = wheadp = first_screen->s_cur_window = first_screen->s_first_window;
704 }
705
706 /*
707 * This is the general command execution routine. It handles the fake binding
708 * of all the keys to "self-insert". It also clears out the "thisflag" word,
709 * and arranges to move it to the "lastflag", so that the next command can
710 * look at it. Return the status of command.
711 */
712
execute(c,f,n)713 PASCAL NEAR execute(c, f, n)
714
715 int c; /* key to execute */
716 int f; /* prefix argument flag */
717 int n; /* prefix value */
718
719 {
720 register int status;
721 KEYTAB *key; /* key entry to execute */
722 #if DBCS
723 int schar; /* second key in 2 byte sequence */
724 #endif
725
726 #if WINDOW_MSWIN
727 /* if it is a menu command, go for it... */
728 if ((c & MENU) == MENU) {
729 thisflag = 0;
730 status = execmenu(f, n);
731 lastflag = thisflag;
732 return(status);
733 }
734 #endif
735 /* if the keystroke is a bound function...do it */
736 key = getbind(c);
737 if (key != NULL) {
738
739 /* before a command, we attempt to expand abbreviations */
740 ab_expand();
741
742 /* Don't reset the function type flags on a prefix */
743 if ((key->k_type == BINDFNC) &&
744 ((key->k_ptr.fp == meta) || (key->k_ptr.fp == cex)))
745 status = execkey(key, f, n);
746 else {
747 thisflag = 0;
748 status = execkey(key, f, n);
749 lastflag = thisflag;
750 }
751
752 return(status);
753 }
754
755 /* since the keystroke is not a command, */
756 if (isinword(c))
757 /* in a word, we save it */
758 ab_save(c);
759 else
760 /* not in a word, we attempt an expansion */
761 ab_expand();
762
763 /*
764 * If a space was typed, fill column is defined, the argument is non-
765 * negative, wrap mode is enabled, and we are now past fill column,
766 * and we are not read-only, perform word wrap.
767 */
768 if (c == ' ' && (curwp->w_bufp->b_mode & MDWRAP) && fillcol > 0 &&
769 n >= 0 && getccol(FALSE) > fillcol &&
770 (curwp->w_bufp->b_mode & MDVIEW) == FALSE)
771 execkey(&wraphook, FALSE, 1);
772
773 if ((c >= 0x20 && c <= 0xFF)) { /* Self inserting. */
774 if (n <= 0) { /* Fenceposts. */
775 lastflag = 0;
776 return(n < 0 ? FALSE : TRUE);
777 }
778 thisflag = 0; /* For the future. */
779
780
781 #if DBCS
782 /* Get the second half of a double-byte character.*/
783 if (is2char(c))
784 schar = get_key();
785 #endif
786
787 /* replace or overwrite mode, not at the end of a string */
788 if (curwp->w_bufp->b_mode & (MDREPL | MDOVER) &&
789 curwp->w_doto < lused(curwp->w_dotp)) {
790 do {
791 /* if we are in replace mode, or
792 (next char is not a tab or we are at a tab stop) */
793 if (curwp->w_bufp->b_mode & MDREPL ||
794 ((lgetc(curwp->w_dotp, curwp->w_doto) != '\t' || tabsize == 0) ||
795 getccol(FALSE) % tabsize == (tabsize - 1))) {
796
797 /* make sure the cursor gets back to
798 the right place on an undo */
799 undo_insert(OP_CPOS, 0L, obj);
800
801 ldelete(1L, FALSE);
802 }
803
804 /* do the appropriate insertion */
805 if (c == '}' && (curbp->b_mode & MDCMOD) != 0)
806 status = insbrace(1, c);
807 else if (c == '#' && (curbp->b_mode & MDCMOD) != 0)
808 status = inspound();
809 else {
810 status = linsert(1, c);
811 #if DBCS
812 /* Insert the second half of a double-byte character.*/
813 if (is2char(c))
814 status = linsert(1, schar);
815 #endif
816 }
817 } while (--n > 0 && curwp->w_doto < lused(curwp->w_dotp) && status == TRUE);
818 }
819
820 /* do the appropriate insertion */
821 if (n > 0) {
822 if (c == '}' && (curbp->b_mode & MDCMOD) != 0)
823 status = insbrace(n, c);
824 else if (c == '#' && (curbp->b_mode & MDCMOD) != 0)
825 status = inspound();
826 #if DBCS
827 else if (is2char(c)) {
828 status = TRUE;
829 while (n--) {
830 if (linsert(1, c) == FALSE)
831 status = FALSE;
832 if (linsert(1, schar) == FALSE)
833 status = FALSE;
834 }
835 }
836 #endif
837 else
838 status = linsert(n, c);
839 }
840
841 /* In ABBREV mode, if we are doing aggressive expansion and
842 the current buffer is a symbol in the abbreviation table */
843 if (((curbp->b_mode & MDABBR) != 0) &&
844 (ab_quick && (ab_lookup(ab_word) != NULL)))
845 ab_expand();
846
847 /* check for CMODE fence matching */
848 if ((c == '}' || c == ')' || c == ']') &&
849 (curbp->b_mode & MDCMOD) != 0)
850 fmatch(c);
851
852 /* check auto-save mode */
853 if (curbp->b_mode & MDASAVE)
854
855 if (--gacount == 0) {
856
857 /* and save the file if needed */
858 upscreen(FALSE, 0);
859 filesave(FALSE, 0);
860 gacount = gasave;
861 }
862
863 lastflag = thisflag;
864 return(status);
865 }
866 TTbeep();
867 mlwrite(TEXT19); /* complain */
868 /* "[Key not bound]" */
869 lastflag = 0; /* Fake last flags. */
870 return(FALSE);
871 }
872
873 /*
874 Fancy quit command, as implemented by Norm. If the any buffer
875 has changed do a write on that buffer and exit emacs, otherwise simply
876 exit.
877 */
878
quickexit(f,n)879 PASCAL NEAR quickexit(f, n)
880
881 int f, n; /* prefix flag and argument */
882
883 {
884 register BUFFER *bp; /* scanning pointer to buffers */
885 register BUFFER *oldcb; /* original current buffer */
886 register int status;
887
888 oldcb = curbp; /* save in case we fail */
889
890 #if TIPC
891 mlwrite("\n\n");
892 #endif
893 bp = bheadp;
894 while (bp != NULL) {
895
896 if ((bp->b_flag & BFCHG) != 0 /* Changed. */
897 && (bp->b_flag & BFINVS) == 0) { /* Real. */
898 curbp = bp; /* make that buffer cur */
899 mlwrite(TEXT103, bp->b_fname);
900 /* "[Saving %s]" */
901 mlwrite("\n");
902 if ((status = filesave(f, n)) != TRUE) {
903 curbp = oldcb; /* restore curbp */
904 return(status);
905 }
906 }
907 bp = bp->b_bufp; /* on to the next buffer */
908 }
909 quit(f, n); /* conditionally quit */
910 return(TRUE);
911 }
912
913 /*
914 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
915 * has been changed and not written out. Normally bound to "C-X C-C".
916 */
917
quit(f,n)918 PASCAL NEAR quit(f, n)
919
920 int f, n; /* prefix flag and argument */
921 {
922 register int status; /* return status */
923
924 if (f != FALSE /* Argument forces it. */
925 || anycb() == FALSE /* All buffers clean or user says it's OK. */
926 || (status = mlyesno(TEXT104)) == TRUE) {
927 /* "Modified buffers exist. Leave anyway" */
928 #if FILOCK
929 if (lockrel() != TRUE) {
930 TTputc('\n');
931 TTputc('\r');
932 TTclose();
933 TTkclose();
934 status = meexit(1);
935 }
936 #endif
937 if (f)
938 status = meexit(n);
939 else
940 status = meexit(GOOD);
941 }
942 mlerase();
943 return(status);
944 }
945
meexit(status)946 PASCAL NEAR meexit(status)
947 int status; /* return status of emacs */
948 {
949 eexitflag = TRUE; /* flag a program exit */
950 gflags |= GFEXIT;
951 eexitval = status;
952
953 /* and now.. we leave and let the main loop kill us */
954 return(TRUE);
955 }
956
957 /*
958 * Begin a keyboard macro.
959 * Error if not at the top level in keyboard processing. Set up variables and
960 * return.
961 */
962
ctlxlp(f,n)963 PASCAL NEAR ctlxlp(f, n)
964
965 int f, n; /* prefix flag and argument */
966
967 {
968 if (kbdmode != STOP) {
969 mlwrite(TEXT105);
970 /* "%%Macro already active" */
971 return(FALSE);
972 }
973 mlwrite(TEXT106);
974 /* "[Start macro]" */
975 kbdptr = &kbdm[0];
976 kbdend = kbdptr;
977 kbdmode = RECORD;
978 return(TRUE);
979 }
980
981 /*
982 * End keyboard macro. Check for the same limit conditions as the above
983 * routine. Set up the variables and return to the caller.
984 */
985
ctlxrp(f,n)986 PASCAL NEAR ctlxrp(f, n)
987
988 int f, n; /* prefix flag and argument */
989
990 {
991 if (kbdmode == STOP) {
992 mlwrite(TEXT107);
993 /* "%%Macro not active" */
994 return(FALSE);
995 }
996 if (kbdmode == RECORD) {
997 mlwrite(TEXT108);
998 /* "[End macro]" */
999 kbdmode = STOP;
1000 }
1001 return(TRUE);
1002 }
1003
1004 /*
1005 * Execute a macro.
1006 * The command argument is the number of times to loop. Quit as soon as a
1007 * command gets an error. Return TRUE if all ok, else FALSE.
1008 */
1009
ctlxe(f,n)1010 PASCAL NEAR ctlxe(f, n)
1011
1012 int f, n; /* prefix flag and argument */
1013
1014 {
1015 if (kbdmode != STOP) {
1016 mlwrite(TEXT105);
1017 /* "%%Macro already active" */
1018 return(FALSE);
1019 }
1020 if (n <= 0)
1021 return(TRUE);
1022 kbdrep = n; /* remember how many times to execute */
1023 kbdmode = PLAY; /* start us in play mode */
1024 kbdptr = &kbdm[0]; /* at the beginning */
1025 return(TRUE);
1026 }
1027
1028 /*
1029 * Abort.
1030 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
1031 * Sometimes called as a routine, to do general aborting of stuff.
1032 */
1033
ctrlg(f,n)1034 PASCAL NEAR ctrlg(f, n)
1035
1036 int f, n; /* prefix flag and argument */
1037
1038 {
1039 TTbeep();
1040 kbdmode = STOP;
1041 mlwrite(TEXT8);
1042 /* "[Aborted]" */
1043 return(ABORT);
1044 }
1045
1046 /* tell the user that this command is illegal while we are in
1047 VIEW (read-only) mode */
1048
rdonly()1049 PASCAL NEAR rdonly()
1050
1051 {
1052 TTbeep();
1053 mlwrite(TEXT109);
1054 /* "[Key illegal in VIEW mode]" */
1055 return(FALSE);
1056 }
1057
resterr()1058 PASCAL NEAR resterr()
1059
1060 {
1061 TTbeep();
1062 mlwrite(TEXT110);
1063 /* "[That command is RESTRICTED]" */
1064 return(FALSE);
1065 }
1066
nullproc(f,n)1067 int PASCAL NEAR nullproc(f, n) /* user function that does NOTHING */
1068
1069 int n, f; /* yes, these are default and never used.. but MUST be here */
1070
1071 {
1072 return(TRUE);
1073 }
1074
meta(f,n)1075 PASCAL NEAR meta(f, n) /* set META prefixing pending */
1076
1077 int f, n; /* prefix flag and argument */
1078
1079 {
1080 prefix |= META;
1081 prenum = n;
1082 predef = f;
1083 return(TRUE);
1084 }
1085
cex(f,n)1086 PASCAL NEAR cex(f, n) /* set ^X prefixing pending */
1087
1088 int f, n; /* prefix flag and argument */
1089
1090 {
1091 prefix |= CTLX;
1092 prenum = n;
1093 predef = f;
1094 return(TRUE);
1095 }
1096
unarg()1097 int PASCAL NEAR unarg() /* dummy function for binding to universal-argument */
1098 {
1099 return(TRUE);
1100 }
1101
1102 /* bytecopy: copy a string...with length restrictions
1103 ALWAYS null terminate
1104 */
1105
bytecopy(dst,src,maxlen)1106 char *PASCAL NEAR bytecopy(dst, src, maxlen)
1107
1108 char *dst; /* destination of copied string */
1109 char *src; /* source */
1110 int maxlen; /* maximum length */
1111
1112 {
1113 char *dptr; /* ptr into dst */
1114
1115 dptr = dst;
1116 while ((maxlen-- > 0) && *src)
1117 *dptr++ = *src++;
1118 *dptr = 0;
1119 return(dst);
1120 }
1121
1122 /* copystr: make another copy of the argument
1123
1124 */
1125
copystr(sp)1126 char *PASCAL NEAR copystr(sp)
1127
1128 char *sp; /* string to copy */
1129
1130 {
1131 char *dp; /* copy of string */
1132
1133 /* make room! */
1134 dp = room(strlen(sp) + 1);
1135 if (dp == NULL)
1136 return(NULL);
1137 strcpy(dp, sp);
1138 return(dp);
1139 }
1140
1141 /***** Compiler specific Library functions ****/
1142
1143 #if RAMSIZE
1144 /* These routines will allow me to track memory usage by placing
1145 a layer on top of the standard system malloc() and free() calls.
1146 with this code defined, the environment variable, $RAM, will
1147 report on the number of bytes allocated via malloc.
1148
1149 with SHOWRAM defined, the number is also posted on the
1150 end of the bottom mode line and is updated whenever it is changed.
1151 */
1152
1153 #undef malloc
1154 #undef free
1155
1156 #if VMS & OPTMEM /* these routines are faster! */
1157 #define malloc VAXC$MALLOC_OPT
1158 #define free VAXC$FREE_OPT
1159 #endif
1160
allocate(nbytes)1161 char *allocate(nbytes) /* allocate nbytes and track */
1162
1163 unsigned nbytes; /* # of bytes to allocate */
1164
1165 {
1166 char *mp; /* ptr returned from malloc */
1167 char *malloc();
1168 FILE *track; /* malloc track file */
1169
1170 mp = malloc(nbytes);
1171
1172 #if RAMTRCK
1173 track = fopen("emacs.log", "a");
1174 fprintf(track, "Allocating %u bytes at %u:%u\n", nbytes,
1175 FP_SEG(mp), FP_OFF(mp));
1176 fclose(track);
1177 #endif
1178
1179 if (mp) {
1180 #if MSC
1181 envram += nbytes;
1182 #else
1183 envram += 1;
1184 #endif
1185 #if RAMSHOW
1186 dspram();
1187 #endif
1188 }
1189
1190 return(mp);
1191 }
1192
release(mp)1193 release(mp) /* release malloced memory and track */
1194
1195 char *mp; /* chunk of RAM to release */
1196
1197 {
1198 unsigned *lp; /* ptr to the long containing the block size */
1199 #if RAMTRCK
1200 FILE *track; /* malloc track file */
1201
1202 track = fopen("emacs.log", "a");
1203 fprintf(track, "Freeing %u:%u\n",
1204 FP_SEG(mp), FP_OFF(mp));
1205 fclose(track);
1206 #endif
1207
1208 if (mp) {
1209 /* update amount of ram currently malloced */
1210 #if MSC
1211 lp = ((unsigned *)mp) - 1;
1212 envram -= (long)*lp - 2;
1213 #else
1214 envram -= 1;
1215 #endif
1216 free(mp);
1217 #if RAMSHOW
1218 dspram();
1219 #endif
1220 }
1221 }
1222
1223 #if RAMSHOW
dspram()1224 dspram() /* display the amount of RAM currently malloced */
1225
1226 {
1227 char mbuf[20];
1228 char *sp;
1229 FILE *track; /* malloc track file */
1230
1231 TTmove(term.t_nrow - 0, 70);
1232 #if COLOR
1233 TTforg(7);
1234 TTbacg(0);
1235 #endif
1236 sprintf(mbuf, "[%lu]", envram);
1237 sp = &mbuf[0];
1238 while (*sp)
1239 TTputc(*sp++);
1240 TTmove(term.t_nrow, 0);
1241 movecursor(term.t_nrow, 0);
1242 #if RAMTRCK & LOGFLG
1243 track = fopen("emacs.log", "a");
1244 fprintf(track, "Total allocation at %lu bytes\n", envram);
1245 fprintf(track, "Stack space at %u bytes\n", stackavail());
1246 fclose(track);
1247 #endif
1248 }
1249 #endif
1250 #endif
1251