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