1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 /* Contains the main loop initializations, and some system dependent
9    type things, e.g. putting terminal in CBREAK mode, etc. */
10 
11 #include "jove.h"
12 #include "fp.h"
13 #include "jctype.h"
14 #include "chars.h"
15 #include "disp.h"
16 #include "re.h"	/* for dosearch() */
17 #include "reapp.h"	/* for find_tag(), UseRE */
18 #include "sysprocs.h"
19 #include "rec.h"
20 #include "ask.h"
21 #include "extend.h"
22 #include "fmt.h"
23 #include "macros.h"
24 #include "marks.h"
25 #include "mouse.h"
26 #ifndef MAC	/* Mac does without! */
27 # include "paths.h"
28 #endif
29 #include "proc.h"
30 #include "screen.h"
31 #include "term.h"
32 #include "version.h"
33 #include "wind.h"
34 
35 #ifdef IPROCS
36 # include "iproc.h"
37 #endif
38 
39 #ifdef USE_SELECT
40 #  include <sys/time.h>
41 #  include "select.h"
42 #endif
43 
44 #ifdef SCO	/* ??? what is this for? */
45 # include <sys/stream.h>
46 # include <sys/ptem.h>
47 #endif
48 
49 #include <signal.h>
50 #include <errno.h>
51 
52 #ifdef MAC
53 # include "mac.h"
54 #else /* !MAC */
55 # include <sys/stat.h>
56 #endif /* !MAC */
57 
58 #ifdef MSDOS
59 # include <bios.h>
60 # include <dos.h>
61 # include <time.h>
62 # include <process.h>
63 # define SIGHUP	99
64 # if defined(__WATCOMC__)
65 #  include <malloc.h>	/* for _heapgrow */
66 # endif
67 #endif /* MSDOS */
68 
69 #ifdef WIN32
70 # include <windows.h>	/* ??? is this needed? */
71 # undef FIONREAD	 /* This is defined but ioctl isn't so we cannot use it. */
72 #endif
73 
74 #ifdef STACK_DECL	/* provision for setting up appropriate stack */
75 STACK_DECL
76 #endif
77 
78 private void
79 	UnsetTerm proto((bool)),
80 	DoKeys proto((bool firsttime)),
81 	ShowKeyStrokes proto((void));
82 
83 #ifdef NONBLOCKINGREAD
84 private void	setblock proto((bool on));
85 #endif
86 
87 #ifdef POSIX_SIGS
88 SIGHANDLERTYPE
setsighandler(signo,handler)89 setsighandler(signo, handler)	/* simulate BSD's safe signal() */
90 int	signo;
91 SIGHANDLERTYPE	handler;
92 {
93 	static struct sigaction	act;	/* static so unspecified fields are 0 */
94 	struct sigaction	oact;
95 
96 	act.sa_handler = handler;
97 	sigemptyset(&act.sa_mask);
98 	sigaddset(&act.sa_mask, signo);
99 	sigaction(signo, &act, &oact);
100 	return oact.sa_handler;
101 }
102 #endif
103 
104 bool	TimeDisplayed = YES;	/* is time actually displayed in modeline? */
105 
106 #ifdef UNIX
107 
108 /* set things up to update the modeline every UpdFreq seconds */
109 
110 int	UpdFreq = 30;	/* VAR: how often to update modeline */
111 bool	InSlowRead = NO;
112 
113 void
SetClockAlarm(unset)114 SetClockAlarm(unset)
115 bool	unset;	/* unset alarm if none needed */
116 {
117 	if (TimeDisplayed && UpdFreq != 0)
118 		(void) alarm((unsigned) (UpdFreq - (time((time_t *)NULL) % UpdFreq)));
119 	else if (unset)
120 		alarm((unsigned)0);
121 }
122 
123 /* AlarmHandler gets all SIGALRMs.  It decides, based on InWaitChar,
124  * whether this is an alarm for updating the mode line or for showing
125  * keystrokes.  Theoretically, InWaitChar is a state variable that ought
126  * to be reset in complain and other exceptional cases, but waitchar
127  * is called often enough that it turns out to be self-correcting.
128  */
129 
130 private volatile bool	InWaitChar = NO;
131 
132 /*ARGSUSED*/
133 private SIGRESTYPE
AlarmHandler(junk)134 AlarmHandler(junk)
135 int	junk;	/* passed in on signal; of no interest */
136 {
137 	int save_errno = errno;	/* Subtle, but necessary! */
138 
139 	resetsighandler(SIGALRM, AlarmHandler);
140 	if (InWaitChar) {
141 		if (InSlowRead) {
142 			InSlowRead = NO;
143 			ShowKeyStrokes();
144 			redisplay();
145 			InSlowRead = YES;
146 			InWaitChar = NO;	/* might as well allow modeline updates */
147 			SetClockAlarm(NO);
148 		} else {
149 			alarm((unsigned)1);	/* try again later */
150 		}
151 	} else {
152 		UpdModLine = YES;
153 		if (InSlowRead) {
154 			/* needed because of stupid BSD restartable I/O */
155 			InSlowRead = NO;
156 			redisplay();
157 			InSlowRead = YES;
158 		}
159 		SetClockAlarm(NO);
160 	}
161 	errno = save_errno;
162 	return SIGRESVALUE;
163 }
164 
165 #endif /* UNIX */
166 
167 bool	stickymsg;	/* the last message should stick around */
168 
169 char	NullStr[] = "";
170 jmp_buf	mainjmp;
171 
172 #ifdef USE_SELECT
173 fd_set	global_fd;	/* set of file descriptors of interest (for select) */
174 int	global_maxfd;
175 #endif
176 
177 /* paths */
178 
179 /* path of machine-independent library */
180 #ifdef SHAREDIR
181 char	ShareDir[FILESIZE] = SHAREDIR;
182 #else
183 char	ShareDir[FILESIZE];
184 #endif
185 
186 /* VAR: directory/device to store tmp files */
187 #ifdef TMPDIR
188 char	TmpDir[FILESIZE] = TMPDIR;
189 #else
190 char	TmpDir[FILESIZE];
191 #endif
192 
193 #ifdef SUBSHELL
194 char
195 	Shell[FILESIZE] = DFLTSHELL,	/* VAR: shell to use */
196 # ifdef MSFILESYSTEM
197 	ShFlags[sizeof(ShFlags)] = "/c";	/* VAR: flags to shell */
198 # else
199 	ShFlags[sizeof(ShFlags)] = "-c";	/* VAR: flags to shell */
200 # endif /* MSFILESYSTEM */
201 #endif
202 
203 /* LibDir: path of machine-dependent library (for Portsrv and Recover) */
204 
205 #if defined(SUBSHELL) || defined(PIPEPROCS)
206 # define NEED_LIBDIR	1
207 private char	LibDir[FILESIZE] = LIBDIR;
208 #endif
209 
210 /* finish: handle bad-news signals.
211  * It also handles internally generated requests for termination.
212  * For most values of code (signal types) an attempt
213  * is made to save the buffers for recovery by "recover".
214  * For most values of code, this routine stops JOVE
215  * by calling abort, which causes a core dump under UNIX.
216  *
217  * - code -1 is an internally generated request to die with an
218  *   attempt to save the buffers.  It had better not be the code
219  *   for some real signal (it cannot be one under UNIX).
220  *
221  * - code 0 is an internally generated request to die quietly.
222  *   It had better not be the code for some real signal
223  *   (it cannot be one under UNIX).  This is the only code
224  *   for which buffers are not saved.
225  *
226  * - SIGINT is caused by the user hitting the INTR key.
227  *   We give him a choice of death or continuation.
228  *
229  * - SIGHUP is caused by loss of connection.  This code and
230  *   code 0 are the only ones which don't cause an abort.
231  *   Generated by OS and by JOVE itself.
232  */
233 
234 SIGRESTYPE
finish(code)235 finish(code)
236 int	code;
237 {
238 	int save_errno = errno;	/* Subtle, but necessary! */
239 	bool	DelTmps = YES;		/* Usually we delete them. */
240 
241 	if (code == SIGINT) {
242 		char	c;
243 #ifdef WIN32
244 		c = FatalErrorMessage("Fatal interrupt encountered. Abort?");
245 #else /* !WIN32 */
246 # ifdef PIPEPROCS
247 		bool	started;
248 # endif
249 
250 		resetsighandler(SIGINT, finish);
251 		f_mess("Abort (Type 'n' if you're not sure)? ");
252 		Placur(ILI, min(CO - 2, calc_pos(mesgbuf, MAXCOLS)));
253 		flushscreen();
254 # ifdef UNIX
255 #  ifdef PIPEPROCS
256 		started = kbd_stop();
257 #  endif
258 		/*
259 		 * Yuk!  This doesn't deal with all cases, we really need a
260 		 * standard jove input routine that's lower than kbd_getch so
261 		 * that this can use it.  The code that this replaces was even
262 		 * more ugly.  What about nonblocking reads? -- MM.
263 		 */
264 #  ifdef NONBLOCKINGREAD
265 		setblock(YES);	/* turn blocking on (in case it was off) */
266 #  endif
267 		for (;;) {
268 			c = 'n';
269 			if (read(0, (UnivPtr) &c, sizeof(c)) < 0) {
270 				switch (errno) {
271 				case EINTR:
272 #  if EWOULDBLOCK != EAGAIN	/* aliases in System Vr4 */
273 				case EWOULDBLOCK:
274 #  endif
275 				case EAGAIN:
276 					continue;
277 				}
278 			}
279 			break;
280 		}
281 #  ifdef PIPEPROCS
282 		if (started)
283 			kbd_strt();
284 #  endif /* PIPEPROCS */
285 # endif /* UNIX */
286 # ifdef MSDOS
287 		c = getrawinchar();
288 # endif /* MSDOS */
289 		message(NullStr);
290 #endif /* !WIN32 */
291 		if (c != 'y') {
292 			redisplay();
293 			errno = save_errno;
294 			return SIGRESVALUE;
295 		}
296 	}
297 	DisabledRedisplay = YES;
298 #ifndef MAC
299 	UnsetTerm(NO);
300 #endif
301 #ifdef PIPEPROCS
302 	kbd_kill();		/* kill the keyboard process */
303 #endif
304 #ifdef RECOVER
305 	if (code != 0) {
306 		static bool	Crashing = NO;	/* we are in the middle of crashing */
307 
308 		if (!Crashing) {
309 			Crashing = YES;
310 			lsave();
311 			SyncRec();
312 			writef("JOVE CRASH!! (code %d; last errno %d)\n",
313 				code, save_errno);
314 			if (ModBufs(YES)) {
315 				writef("Your buffers have been saved.\n");
316 				writef("Use \"jove -r\" to have a look at them.\n");
317 				DelTmps = NO;	/* Don't delete anymore. */
318 			} else {
319 				writef("No buffers needed saving: you didn't lose any work.\n");
320 			}
321 		} else {
322 			writef("\r\nYou may have lost your work!\n");
323 		}
324 	}
325 #endif /* RECOVER */
326 	flushscreen();
327 	if (DelTmps) {
328 		tmpremove();
329 #ifdef RECOVER
330 		recremove();
331 #endif /* RECOVER */
332 	}
333 #ifdef UNIX
334 	if (code != 0 && code != SIGHUP)
335 		abort();
336 # ifdef USE_EXIT
337 	exit(0);
338 # else
339 	_exit(0);
340 # endif
341 #else /* !UNIX */
342 	exit(0);
343 #endif /* !UNIX */
344 	/*NOTREACHED*/
345 }
346 
347 private char	smbuf[20],
348 		*bp = smbuf;
349 private int	nchars = 0;
350 
351 #ifdef NONBLOCKINGREAD
352 
353 # include <fcntl.h>
354 
355 private void
setblock(on)356 setblock(on)	/* turn blocking on or off */
357 bool	on;
358 {
359 	static int blockf, nonblockf;
360 	static bool	first = YES;
361 
362 	if (first) {
363 		int flags;
364 
365 		first = NO;
366 		if ((flags = fcntl(0, F_GETFL, 0)) == -1)
367 			finish(SIGHUP);
368 # ifdef O_NONBLOCK	/* POSIX form */
369 		blockf = flags & ~O_NONBLOCK;	/* make sure O_NONBLOCK is off */
370 		nonblockf = flags | O_NONBLOCK;	/* make sure O_NONBLOCK is on */
371 # else	/* pre-POSIX form */
372 		blockf = flags & ~O_NDELAY;	/* make sure O_NDELAY is off */
373 		nonblockf = flags | O_NDELAY;	/* make sure O_NDELAY is on */
374 # endif
375 	}
376 	if (fcntl(0, F_SETFL, on ? blockf : nonblockf) == -1)
377 		finish(SIGHUP);
378 }
379 
380 #endif /* NONBLOCKINGREAD */
381 
382 /* To optimize screen refreshing, we try to detect if there is pending input.
383  * If there is, we defer screen updating, hoping that when we eventually
384  * do an update it will be more efficient.  To implement this, we use
385  * charp to poll for pending input (from any source but macro body).
386  * "InputPending" records the last value returned by charp, or what
387  * was known by kbd_getch or kbd_ungetch.  Note that the kbd_* routines
388  * only consider keyboard input, but charp considers other sources of
389  * input.  These are the only routines that should set InputPending
390  * (currently, a fudge to redisplay for the mac also sets it --
391  * this should be fixed).
392  *
393  * This heuristic confuses the user if a command takes a long time:
394  * the screen may be "frozen" in an inaccurate state until the command
395  * completes.  The variable "SlowCmd" is a count of the nesting of slow
396  * commands.  If it is positive, display updating is not pre-empted.
397  * The macros PreEmptOutput() and CheapPreEmptOutput() implement the tests.
398  */
399 
400 int	SlowCmd = 0;	/* depth of nesting of slow commands */
401 
402 bool	InputPending = NO;	/* is there input waiting to be processed? */
403 
404 /* Inputp is used to jam a NUL-terminated string into JOVE's input stream.
405  * It is used to feed each line of the joverc file, to fill in the default
406  * make_cmd in compile-it, and to fill in the default i-search string.
407  * To make this work, we prevent i-search and compile-it from using Inputp
408  * when it is already in use.
409  */
410 char	*Inputp = NULL;
411 
412 /* kbd_ungetch must only be used to push back a character that
413  * was just returned by kbd_getch.
414  */
415 private ZXchar	kbdpeek = EOF;
416 
417 void
kbd_ungetch(c)418 kbd_ungetch(c)
419 ZXchar	c;
420 {
421 	InputPending = YES;
422 	kbdpeek = c;
423 }
424 
425 ZXchar
kbd_getch()426 kbd_getch()
427 {
428 	register ZXchar	c;
429 
430 	if (kbdpeek != EOF) {
431 		c = kbdpeek;
432 		kbdpeek = EOF;
433 		InputPending = nchars > 0;
434 		return c;
435 	}
436 #if NCHARS != UCHAR_ROOF
437 	do {
438 #endif
439 		while (nchars <= 0) {
440 			bp = smbuf;
441 #ifdef MSDOS
442 			*bp = getrawinchar();
443 			nchars = 1;
444 #else /* !MSDOS */
445 # ifdef WIN32
446 			if (!charp()) {
447 				redisplay();
448 				flushscreen();
449 			}
450 			nchars = getInputEvents(smbuf, sizeof(smbuf));
451 # else /* !WIN32 */
452 #  ifdef PTYPROCS
453 			/* Get a character from the keyboard, first checking for
454 			   any input from a process.  Handle that first, and then
455 			   deal with the terminal input. */
456 			{
457 				fd_set	reads;
458 				int
459 					nfds,
460 					fd;
461 
462 				bp = smbuf;
463 				for (;;) {
464 					while (procs_to_reap)
465 							reap_procs();	/* synchronous process reaping */
466 					reads = global_fd;
467 					InSlowRead = YES;
468 					nfds = select(global_maxfd,
469 						&reads, (fd_set *)NULL, (fd_set *)NULL,
470 						(struct timeval *)NULL);
471 					InSlowRead = NO;
472 					if (nfds >= 0)
473 						break;
474 
475 					if (errno != EINTR) {
476 						complain("\rerror in select: %s", strerror(errno));
477 						/* NOTREACHED */
478 					}
479 				}
480 
481 				if (FD_ISSET(0, &reads)) {
482 					do {
483 						nchars = read(0, (UnivPtr) smbuf, sizeof(smbuf));
484 					} while (nchars < 0 && errno == EINTR);
485 					if (nchars <= 0)
486 						finish(SIGHUP);
487 					nfds -= 1;
488 				}
489 				for (fd=1; nfds != 0; fd += 1) {
490 					if (FD_ISSET(fd, &reads)) {
491 						nfds -= 1;
492 						read_pty_proc(fd);
493 						if (UpdModLine)
494 							redisplay();
495 					}
496 				}
497 			}
498 #  else /* !PTYPROCS */
499 #   ifdef PIPEPROCS
500 			if (NumProcs > 0) {
501 				/* Handle process input until kbd input arrives */
502 				struct header	header;
503 				size_t	n;
504 
505 				InSlowRead = YES;
506 				n = f_readn(ProcInput, (char *) &header, sizeof(header));
507 				InSlowRead = NO;
508 
509 				if (n != sizeof(header)) {
510 					raw_complain("\r\nError reading kbd process, expected %d, got %d bytes", sizeof header, n);
511 					finish(SIGHUP);
512 				}
513 				if (header.pid == kbd_pid) {
514 					/* data is from the keyboard process */
515 					nchars = f_readn(ProcInput, smbuf, (size_t)header.nbytes);
516 					if (nchars != header.nbytes) {
517 						raw_complain("\r\nError reading kbd process, expected %d, got %d bytes.", header.nbytes, nchars);
518 						finish(SIGHUP);
519 					}
520 				} else {
521 					/* data is from an interactive process */
522 					read_pipe_proc(header.pid, header.nbytes);
523 					if (NumProcs == 0)
524 						(void) kbd_stop();
525 					if (UpdModLine)
526 						redisplay();
527 				}
528 			} else /*...*/
529 #   endif /* PIPEPROCS */
530 			/*...*/ {
531 				do {
532 #   ifdef UNIX
533 					InSlowRead = YES;
534 #   endif
535 					nchars = read(0, (UnivPtr) smbuf, sizeof smbuf);
536 #   ifdef UNIX
537 					InSlowRead = NO;
538 #   endif
539 				} while (nchars < 0 && errno == EINTR);
540 				if (nchars <= 0)
541 					finish(SIGHUP);
542 			}
543 #  endif /* !PTYPROCS */
544 # endif /* !WIN32 */
545 #endif /* !MSDOS */
546 		}
547 		c = ZXRC(*bp++);
548 #if !defined(PCNONASCII) && !defined(MAC)	/* if not done elsewhere */
549 		if ((c & 0200) && MetaKey) {
550 			*--bp = c & ~0200;
551 			nchars += 1;
552 			c = ESC;
553 		}
554 #endif /* !defined(PCNONASCII) && !defined(MAC) */
555 		InputPending = --nchars > 0;
556 #if NCHARS != UCHAR_ROOF
557 	} while (c >= NCHARS);	/* discard c if it is a bad char */
558 #endif
559 	return c;
560 }
561 
562 /* Returns YES if a character waiting (excluding macro body) */
563 
564 bool
charp()565 charp()
566 {
567 	if (InJoverc != 0 || kbdpeek != EOF || nchars > 0 || Inputp != NULL)
568 		return InputPending = YES;
569 #ifdef FIONREAD
570 	{
571 		/*
572 		 * Some manual pages, notably SunOS4.1.3 say 'c' should be
573 		 * 'long', but that's a lie -- it's an 'int' according to all
574 		 * kernels I've seen (including SunOS4.1.3) and most other
575 		 * manual pages.  At any rate, 'int' works correctly on 32- and
576 		 * 64-bit architectures, whereas long breaks on the 64
577 		 * bitters. -- MM.
578 		 */
579 		int c;
580 
581 		if (ioctl(0, FIONREAD, (UnivPtr) &c) == -1)
582 			c = 0;
583 		return InputPending = c > 0;
584 	}
585 #else /* !FIONREAD */
586 # ifdef NONBLOCKINGREAD
587 	setblock(NO);		/* turn blocking off */
588 	nchars = read(0, (UnivPtr) smbuf, sizeof smbuf);	/* Is anything there? */
589 	setblock(YES);		/* turn blocking on */
590 	bp = smbuf;			/* make sure bp points to it */
591 	return InputPending = nchars > 0;	/* just say we found something */
592 # else /* !NONBLOCKINGREAD */
593 #  ifdef USE_SELECT
594 	{
595 		struct timeval	timer;
596 		fd_set	readfds;
597 
598 		timer.tv_sec = 0;
599 		timer.tv_usec = 0;
600 		FD_ZERO(&readfds);
601 		FD_SET(0, &readfds);
602 		return InputPending = select(1,
603 			&readfds, (fd_set *)NULL, (fd_set *)NULL,
604 			&timer) > 0;
605 	}
606 #  else /* !USE_SELECT */
607 #   ifdef MSDOS
608 	return InputPending = rawkey_ready();
609 #   else /* !MSDOS */
610 #    ifdef MAC
611 	return InputPending = rawchkc();
612 #    else
613 #     ifdef WIN32
614 	return InputPending = inputEventWaiting(0);
615 #     else
616 	return InputPending = NO;	/* who knows? */
617 #     endif /* !WIN32 */
618 #    endif /* !MAC */
619 #   endif /* !MSDOS */
620 #  endif /* !USE_SELECT */
621 # endif /* !NONBLOCKINGREAD */
622 #endif /* !FIONREAD */
623 }
624 
625 /*
626  * Tries to pause for delay/10 seconds OR until a character is typed at the
627  * keyboard.  This works well on systems with select() and not so well on the
628  * rest.
629  */
630 
631 #ifdef MAC
632 # include <LoMem.h>	/* defines Ticks */
633 #endif
634 
635 void
SitFor(delay)636 SitFor(delay)
637 int	delay;
638 {
639 #ifdef MAC
640 	long
641 		start,
642 		end;
643 
644 	Keyonly = YES;
645 	redisplay();
646 	start = Ticks;
647 
648 	end = start + delay * 6;
649 	do ; while (!charp() && Ticks < end);
650 
651 #else /* !MAC */
652 
653 # ifndef MSDOS
654 	if (!charp()) {
655 #  ifdef USE_SELECT
656 		struct timeval	timer;
657 		fd_set	readfds;
658 
659 		/* So messages that aren't error messages don't
660 		 * hang around forever.
661 		 * Gross that I had to snarf this from getch()
662 		 */
663 		if (!UpdMesg && !Asking && mesgbuf[0] && !stickymsg)
664 			message(NullStr);
665 		redisplay();
666 
667 		timer.tv_sec = (delay / 10);
668 		timer.tv_usec = (delay % 10) * 100000;
669 		FD_ZERO(&readfds);
670 		FD_SET(0, &readfds);
671 		(void) select(1,
672 			&readfds, (fd_set *)NULL, (fd_set *)NULL,
673 			&timer);
674 #  else /* ! USE_SELECT */
675 #   ifdef WIN32
676 		redisplay();
677 		inputEventWaiting(delay*100);
678 #   else /* ! WIN32 */
679 		/* Pause by spitting NULs at the terminal.  Ugh! */
680 		static const int cps[] = {
681 			0,
682 			5,
683 			7,
684 			11,
685 			13,
686 			15,
687 			20,
688 			30,
689 			60,
690 			120,
691 			180,
692 			240,
693 			480,
694 			960,
695 			1920,
696 			1920,
697 		};
698 		register int	nchars,
699 				check_cnt;
700 
701 		nchars = (delay * cps[ospeed]) / 10;
702 		check_cnt = ScrBufSize;
703 		redisplay();
704 		if (!NP) {
705 			while ((--nchars > 0) && !InputPending) {
706 				scr_putchar(PC);
707 				if (--check_cnt == 0) {
708 					check_cnt = ScrBufSize;
709 					(void) charp();
710 				}
711 			}
712 		}
713 #   endif /* !WIN32 */
714 #  endif /* !USE_SELECT */
715 	}
716 # else /* MSDOS */
717 	/* All time representations must wrap eventually.
718 	 * Since all delays are much less than a minute, we represent
719 	 * time as hundredths of a second past the minute we start.
720 	 * NOTE: this is a busy wait.  I know of no alternative.
721 	 */
722 	int	start,
723 		now,
724 		end;
725 	struct dostime_t tc;
726 
727 	redisplay();
728 	_dos_gettime(&tc);
729 	start = tc.second * 100 + tc.hsecond;
730 	end = start + delay * 10;
731 	for (;;)  {
732 		if (charp())
733 			break;
734 		_dos_gettime(&tc);
735 		now = tc.second * 100 + tc.hsecond;
736 		if (now < start)
737 			now += 60 * 100;	/* must be in next minute */
738 		if (now >= end)
739 			break;
740 	}
741 # endif /* MSDOS */
742 #endif /* !MAC */
743 }
744 
745 #define WAITCHAR_CURSOR_DOWN	1	/* during slow keying, cursor is after displayed prefix */
746 
747 private char
748 	key_strokes[100],
749 	*keys_p = key_strokes;
750 
751 private bool	in_ask_ks;
752 
753 private volatile bool	slow_keying = NO;	/* for waitchar() */
754 
755 void
cmd_sync()756 cmd_sync()
757 {
758 	if (this_cmd != ARG_CMD) {
759 		clr_arg_value();
760 		last_cmd = this_cmd;
761 		slow_keying = NO;
762 		in_ask_ks = NO;
763 		keys_p = key_strokes;
764 	}
765 }
766 
767 ZXchar
ask_ks()768 ask_ks()
769 {
770 	in_ask_ks = YES;
771 	keys_p = key_strokes;
772 	return waitchar();
773 }
774 
775 void
add_stroke(c)776 add_stroke(c)
777 ZXchar	c;
778 {
779 	if (keys_p < &key_strokes[sizeof (key_strokes) - 1])
780 		*keys_p++ = c;
781 }
782 
783 void
pp_key_strokes(buffer,size)784 pp_key_strokes(buffer, size)
785 char	*buffer;
786 size_t	size;
787 {
788 	char
789 		*buf_end = buffer + size - 1,
790 		*kp = key_strokes;
791 
792 	*buffer = '\0';
793 	while (kp != keys_p) {
794 		swritef(buffer, (size_t) (buf_end-buffer), "%p ", *kp++);
795 		buffer += strlen(buffer);
796 	}
797 }
798 
799 private void
ShowKeyStrokes()800 ShowKeyStrokes()
801 {
802 	char	buffer[100];
803 
804 	slow_keying = YES;
805 	pp_key_strokes(buffer, sizeof (buffer));
806 	f_mess(in_ask_ks? ": %f %s" : "%s", buffer);
807 #ifdef WAITCHAR_CURSOR_DOWN
808 	Asking = YES;
809 	AskingWidth = strlen(mesgbuf);
810 #endif
811 }
812 
813 #define N_SEC	1	/* will be precisely 1 second on 4.2 */
814 
815 ZXchar
waitchar()816 waitchar()
817 {
818 	ZXchar	c;
819 #ifdef WAITCHAR_CURSOR_DOWN
820 	bool	oldAsking;
821 	int	oldAskingWidth;
822 #endif
823 
824 	/* short circuit, if we can */
825 	if (InJoverc || (!Interactive && in_macro()) || InputPending)
826 		return getch();
827 
828 #ifdef WAITCHAR_CURSOR_DOWN
829 	oldAsking = Asking;
830 	oldAskingWidth = AskingWidth;
831 #endif
832 
833 #ifdef MAC
834 	Keyonly = YES;
835 #endif
836 	if (!slow_keying) {
837 		/* not yet slow_keying */
838 #ifdef UNIX
839 		/* set up alarm */
840 		InWaitChar = YES;
841 		(void) alarm((unsigned) N_SEC);
842 #else /* !UNIX */
843 # ifdef WIN32
844 		if (!charp()) {
845 			if ((slow_keying = !inputEventWaiting(N_SEC*1000))) {
846 				ShowKeyStrokes();
847 			}
848 		}
849 # else /* !WIN32 */
850 		/* NOTE: busy wait (until char typed or timeout)! */
851 		time_t sw = N_SEC + time((time_t *)NULL);
852 
853 		while (!slow_keying && !charp()) {
854 			if (time((time_t *)NULL) > sw) {
855 				/* transition to slow_keying */
856 #  ifdef MAC
857 				menus_off();
858 #  endif
859 				ShowKeyStrokes();
860 			}
861 		}
862 # endif /* !WIN32 */
863 #endif /* !UNIX */
864 #ifdef WAITCHAR_CURSOR_DOWN
865 	} else {
866 		/* Already slow_keying: presume bottom line has old keystrokes.
867 		 * Tell refresh(?) to place cursor at end of them.
868 		 */
869 		Asking = YES;
870 		AskingWidth = strlen(mesgbuf);
871 #endif
872 	}
873 	c = getch();
874 	if (slow_keying) {
875 		ShowKeyStrokes();
876 #ifdef UNIX
877 	} else {
878 		/* not yet slow_keying: tear down alarm */
879 		InWaitChar = NO;
880 		SetClockAlarm(YES);
881 #endif
882 	}
883 
884 #ifdef WAITCHAR_CURSOR_DOWN
885 	Asking = oldAsking;
886 	AskingWidth = oldAskingWidth;
887 #endif
888 	return c;
889 }
890 
891 private void
SetTerm()892 SetTerm()
893 {
894 #ifdef IBMPCDOS
895 	pcSetTerm();
896 #endif
897 	ttysetattr(YES);
898 #ifdef TERMCAP
899 	putpad(TI, 1);	/* Cursor addressing start */
900 	putpad(VS, 1);	/* Visual start */
901 	putpad(KS, 1);	/* Keypad mode start */
902 # ifdef MOUSE
903 	MouseOn();
904 # endif
905 #endif
906 #ifdef UNIX
907 	(void) chkmail(YES);	/* force it to check so we can be accurate */
908 #endif
909 }
910 
911 private void
UnsetTerm(WarnUnwritten)912 UnsetTerm(WarnUnwritten)
913 bool	WarnUnwritten;
914 {
915 #ifdef TERMCAP
916 # ifdef ID_CHAR
917 	INSmode(NO);
918 # endif /* ID_CHAR */
919 # ifdef MOUSE
920 	MouseOff();
921 # endif
922 	putpad(KE, 1);
923 	putpad(VE, 1);
924 	Placur(ILI, 0);
925 	putpad(CE, 1);
926 	putpad(TE, 1);
927 #else /* !TERMCAP */
928 	Placur(ILI, 0);
929 	clr_eoln();
930 #endif /* !TERMCAP */
931 	flushscreen();
932 #ifdef MSDOS
933 	pcUnsetTerm();
934 #endif
935 	ttysetattr(NO);
936 	if (WarnUnwritten && ModBufs(NO))
937 		raw_complain("[There are modified buffers]");
938 }
939 
940 #ifdef JOB_CONTROL
941 void
PauseJove()942 PauseJove()
943 {
944 	UnsetTerm(YES);
945 	(void) kill(0, SIGTSTP);
946 	SetTerm();
947 # ifdef WINRESIZE
948 	/* Some systems (eg System V Release 4) don't give us SIGWINCHes
949 	 * that happen while we are away.
950 	 */
951 	ResizePending = YES;
952 # endif
953 	ClAndRedraw();
954 }
955 #endif /* JOB_CONTROL */
956 
957 #ifdef SUBSHELL
958 
959 # ifndef MSDOS
960 void
jcloseall()961 jcloseall()
962 {
963 	tmpclose();
964 #  ifdef RECOVER
965 	recclose();
966 #  endif
967 #  ifdef IPROCS
968 	closeiprocs();
969 #  endif
970 }
971 # endif /* !MSDOS */
972 
973 void
Push()974 Push()
975 {
976 # ifdef MSDOS_PROCS
977 #  ifdef MSDOS
978 	UnsetTerm(YES);
979 	if (spawnl(0, Shell, basename(Shell), (char *)NULL) == -1)
980 		s_mess("[Spawn failed %d]", errno);
981 	SetTerm();
982 #   ifdef WINRESIZE
983 	/* Some systems (eg System V Release 4) don't give us SIGWINCHes
984 	 * that happen while we are away.
985 	 */
986 	ResizePending = YES;
987 #   endif
988 	ClAndRedraw();
989 	getCWD();
990 #  else /* !MSDOS */
991 #   ifdef WIN32
992 	STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
993 	PROCESS_INFORMATION procinfo;
994 	CreateProcess(Shell, NULL, NULL, NULL,
995 		FALSE, CREATE_NEW_CONSOLE,
996 		NULL, NULL,
997 		&startinfo, &procinfo);
998 #   endif /* WIN32 */
999 #  endif /* !MSDOS */
1000 # else /* !MSDOS_PROCS */
1001 	/* UNIX, or something like it */
1002 	SIGHANDLERTYPE	old_int = setsighandler(SIGINT, SIG_IGN);
1003 	int	forkerr = 0;
1004 #  ifdef PIPEPROCS
1005 	bool	started = kbd_stop();
1006 #  endif
1007 
1008 	UnsetTerm(YES);
1009 	switch (ChildPid = fork()) {
1010 	case -1:
1011 		/* parent, fork failed */
1012 		forkerr = errno;
1013 		break;
1014 
1015 	default:
1016 		/* parent, fork worked */
1017 		dowait((wait_status_t *) NULL);
1018 		break;
1019 
1020 	case 0:
1021 		/* child */
1022 		/* (void) setsighandler(SIGTERM, SIG_DFL); */
1023 		(void) setsighandler(SIGINT, SIG_DFL);
1024 		jcloseall();
1025 		/* note that curbuf->bfname may be NULL */
1026 		execl(Shell, basename(Shell), "-is", pr_name(curbuf->b_fname, NO),
1027 			(char *)NULL);
1028 		raw_complain("[Execl failed: %s]", strerror(errno));
1029 		_exit(1);
1030 		/*NOTREACHED*/
1031 	}
1032 	SetTerm();
1033 #  ifdef WINRESIZE
1034 	/* Some systems (eg System V Release 4) don't give us SIGWINCHes
1035 	 * that happen while we are away.
1036 	 */
1037 	ResizePending = YES;
1038 #  endif
1039 	ClAndRedraw();
1040 	(void) setsighandler(SIGINT, old_int);
1041 	SetClockAlarm(NO);
1042 #  ifdef PIPEPROCS
1043 	if (started)
1044 		kbd_strt();
1045 #  endif
1046 	if (forkerr != 0)
1047 		complain("[Fork failed: %s]", strerror(errno));
1048 # endif /* !MSDOS_PROCS */
1049 }
1050 
1051 #endif /* SUBSHELL */
1052 
1053 /* adjust the tty to reflect possible change to JOVE variables */
1054 void
tty_adjust()1055 tty_adjust()
1056 {
1057 	ttysetattr(YES);
1058 #ifdef MOUSE
1059 	MouseOn();	/* XtermMouse might have changed */
1060 #endif
1061 }
1062 
1063 bool	Interactive = NO;	/* True when we invoke with the command handler? */
1064 
1065 ZXchar
1066 	peekchar = EOF,	/* holds pushed-back getch output */
1067 	LastKeyStruck;	/* used by SelfInsert and friends */
1068 
1069 bool
1070 	MetaKey = NO;		/* VAR: this terminal has a meta key */
1071 
1072 void
Ungetc(c)1073 Ungetc(c)
1074 ZXchar	c;
1075 {
1076 	peekchar = c;
1077 }
1078 
1079 ZXchar
getch()1080 getch()
1081 {
1082 	register ZXchar	c;
1083 
1084 	if (Inputp != NULL) {
1085 		if ((c = ZXC(*Inputp++)) != '\0')
1086 			return LastKeyStruck = c;
1087 		Inputp = NULL;
1088 	}
1089 
1090 	if (InJoverc) {
1091 		/* somethings wrong if Inputp runs out while
1092 		   we're reading a .joverc file. */
1093 		complain("[command line too short]");
1094 	}
1095 
1096 #ifdef RECOVER
1097 	if (ModCount >= SyncFreq) {
1098 		ModCount = 0;
1099 		SyncRec();
1100 	}
1101 #endif /* RECOVER */
1102 
1103 	if ((c = peekchar) != EOF) {
1104 		/* got input from pushback */
1105 		peekchar = EOF;
1106 	} else {
1107 		if (!Interactive && (c = mac_getc()) != EOF) {
1108 			/* got input from macro */
1109 		} else {
1110 			/* So messages that aren't error messages don't
1111 			 * hang around forever.
1112 			 * Note: this code is duplicated in SitFor()!
1113 			 */
1114 			if (!UpdMesg && !Asking && mesgbuf[0] != '\0' && !stickymsg)
1115 				message(NullStr);
1116 			redisplay();
1117 			c = kbd_getch();
1118 			if (!Interactive && InMacDefine)
1119 				mac_putc(c);
1120 		}
1121 		add_stroke(c);
1122 	}
1123 	return LastKeyStruck = c;
1124 }
1125 
1126 #if defined(SUBSHELL) && defined(RECOVER)
1127 private void
dorecover()1128 dorecover()
1129 {
1130 	char	Recover[FILESIZE];	/* path to recover program (in LibDir) */
1131 
1132 	/* Since recover is a normal cooked mode program, reset the terminal */
1133 	UnsetTerm(NO);
1134 # ifdef PIPEPROCS
1135 	kbd_kill();		/* kill the keyboard process */
1136 # endif
1137 	swritef(Recover, sizeof(Recover), "%s/recover", LibDir);
1138 	execl(Recover, "recover", "-d", TmpDir, (char *) NULL);
1139 	writef("%s: execl failed! %s\n", Recover, strerror(errno));
1140 	flushscreen();
1141 	_exit(-1);
1142 	/* NOTREACHED */
1143 }
1144 #endif /* defined(SUBSHELL) && defined(RECOVER) */
1145 
1146 void
ShowVersion()1147 ShowVersion()
1148 {
1149 	s_mess("Jonathan's Own Version of Emacs (%s)", jversion);
1150 }
1151 
1152 private void
UNIX_cmdline(argc,argv)1153 UNIX_cmdline(argc, argv)
1154 int	argc;
1155 char	*argv[];
1156 {
1157 	int	lineno = 0,
1158 		nwinds = 1;
1159 	char	*pattern = NULL;
1160 	Buffer	*b;
1161 
1162 	while (argc > 1) {
1163 		switch (argv[1][0]) {
1164 		case '+':
1165 			if ('0' <= argv[1][1] && argv[1][1] <= '9') {
1166 				(void) chr_to_int(&argv[1][1], 10, NO, &lineno);
1167 				break;
1168 			} else switch (argv[1][1]) {
1169 			case '\0':
1170 				/* goto end of file just like some people's favourite editor */
1171 				lineno = -1;
1172 				break;
1173 			case '/':	/* search for pattern */
1174 				/* check if syntax is +/pattern or +/ pattern */
1175 				if (argv[1][2] != 0) {
1176 					pattern = &argv[1][2];
1177 				} else {
1178 					argv += 1;
1179 					argc -= 1;
1180 					if (argv[1] != 0)
1181 						pattern = &argv[1][0];
1182 				}
1183 				break;
1184 			default:
1185 				error("Invalid switch %s",argv[1]);
1186 				break;
1187 			}
1188 			break;
1189 		case '-':
1190 			switch (argv[1][1]) {
1191 			case 'd':	/* Ignore current directory path */
1192 			case 'l':	/* Ignore libdir path */
1193 			case 's':	/* Ignore sharedir path */
1194 				argv += 1;
1195 				argc -= 1;
1196 				break;
1197 
1198 			case 'J':	/* Ignore jove.rc in SHAREDIR */
1199 			case 'j':	/* Ignore ~/.joverc */
1200 				break;
1201 			case 'p':	/* parse errors in file */
1202 				argv += 1;
1203 				argc -= 1;
1204 				if (argv[1] != NULL) {
1205 					SetBuf(do_find(curwind, argv[1], YES, YES));
1206 					ErrParse();
1207 					nwinds = 0;
1208 				}
1209 				break;
1210 			case 't':	/* find tag */
1211 				/* check if syntax is -tTag or -t Tag */
1212 				if (argv[1][2] != '\0') {
1213 					find_tag(&(argv[1][2]), YES);
1214 				} else {
1215 					argv += 1;
1216 					argc -= 1;
1217 					if (argv[1] != NULL)
1218 						find_tag(argv[1], YES);
1219 				}
1220 				break;
1221 
1222 			case 'w':	/* multiple windows */
1223 				if (argv[1][2] == '\0')
1224 					nwinds += 1;
1225 				else {
1226 					int	n;
1227 
1228 					(void) chr_to_int(&argv[1][2], 10, NO, &n);
1229 					nwinds += -1 + n;
1230 				}
1231 				(void) div_wind(curwind, nwinds - 1);
1232 				break;
1233 			default:
1234 				error("Invalid switch %s",argv[1]);
1235 				break;
1236 			}
1237 			break;
1238 		default:
1239 			{
1240 			bool	force = (nwinds > 0 || lineno != 0 || pattern != NULL);
1241 
1242 			minib_add(argv[1], force);
1243 			b = do_find(nwinds > 0 ? curwind : (Window *) NULL,
1244 				    argv[1], force, YES);
1245 			if (force) {
1246 				SetABuf(curbuf);
1247 				SetBuf(b);
1248 				if (lineno > 0)
1249 					SetLine(next_line(curbuf->b_first, lineno - 1));
1250 				else if (lineno == -1)
1251 					SetLine(curbuf->b_last);
1252 				if (pattern != NULL) {
1253 					Bufpos	*bufp;
1254 
1255 					if ((bufp = dosearch(pattern, FORWARD, UseRE)) != NULL
1256 					|| (bufp = dosearch(pattern, BACKWARD, UseRE)) != NULL)
1257 						SetDot(bufp);
1258 					else
1259 						complain("Couldn't match pattern in file.");
1260 				}
1261 				if (nwinds > 1)
1262 					NextWindow();
1263 				if (nwinds > 0)
1264 					nwinds -= 1;
1265 			}
1266 			lineno = 0;
1267 			pattern = NULL;
1268 			}
1269 			break;
1270 		}
1271 		argv += 1;
1272 		argc -= 1;
1273 	}
1274 }
1275 
1276 #ifdef STDARGS
1277 void
error(const char * fmt,...)1278 error(const char *fmt, ...)
1279 #else
1280 /*VARARGS1*/ void
1281 error(fmt, va_alist)
1282 	const char	*fmt;
1283 	va_dcl
1284 #endif
1285 {
1286 	va_list	ap;
1287 
1288 	if (fmt) {
1289 		va_init(ap, fmt);
1290 		format(mesgbuf, sizeof mesgbuf, fmt, ap);
1291 		va_end(ap);
1292 		UpdMesg = YES;
1293 	}
1294 	rbell();
1295 	longjmp(mainjmp, JMP_ERROR);
1296 }
1297 
1298 #ifdef STDARGS
1299 void
complain(const char * fmt,...)1300 complain(const char *fmt, ...)
1301 #else
1302 /*VARARGS1*/ void
1303 complain(fmt, va_alist)
1304 	const char	*fmt;
1305 	va_dcl
1306 #endif
1307 {
1308 	va_list	ap;
1309 
1310 	if (fmt) {
1311 		va_init(ap, fmt);
1312 		format(mesgbuf, sizeof mesgbuf, fmt, ap);
1313 		va_end(ap);
1314 		UpdMesg = YES;
1315 	}
1316 	rbell();
1317 	longjmp(mainjmp, JMP_COMPLAIN);
1318 }
1319 
1320 /* format and display a message without using the normal display mechanisms */
1321 
1322 #ifdef STDARGS
1323 void
raw_complain(const char * fmt,...)1324 raw_complain(const char *fmt, ...)
1325 #else
1326 /*VARARGS1*/ void
1327 raw_complain(fmt, va_alist)
1328 	const char	*fmt;
1329 	va_dcl
1330 #endif
1331 {
1332 	char	buf[MESG_SIZE];
1333 	va_list	ap;
1334 
1335 	va_init(ap, fmt);
1336 	format(buf, sizeof(buf) - 2, fmt, ap);
1337 	va_end(ap);
1338 	strcat(buf, "\r\n");	/* \r *may* be redundant */
1339 	(void) write(2, (UnivConstPtr)buf, strlen(buf));
1340 }
1341 
1342 #ifdef STDARGS
1343 void
confirm(const char * fmt,...)1344 confirm(const char *fmt, ...)
1345 #else
1346 /*VARARGS1*/ void
1347 confirm(fmt, va_alist)
1348 	const char	*fmt;
1349 	va_dcl
1350 #endif
1351 {
1352 	va_list	ap;
1353 
1354 	va_init(ap, fmt);
1355 	format(mesgbuf, sizeof mesgbuf, fmt, ap);
1356 	va_end(ap);
1357 	if (!yes_or_no_p("%s", mesgbuf))
1358 		longjmp(mainjmp, JMP_COMPLAIN);
1359 }
1360 
1361 /* Recursive edit.
1362  * Guarantee: current buffer will still be current and
1363  * it will be in the current window.  If not, complain!
1364  */
1365 
1366 int	RecDepth = 0;
1367 
1368 void
Recur()1369 Recur()
1370 {
1371 	Buffer	*b = curbuf;
1372 	Mark	*m;
1373 
1374 	m = MakeMark(curline, curchar);
1375 
1376 	RecDepth += 1;
1377 	UpdModLine = YES;
1378 	DoKeys(NO);	/* NO means not first time */
1379 	UpdModLine = YES;
1380 	RecDepth -= 1;
1381 	if (!valid_bp(b))
1382 		complain("Buffer gone!");
1383 	tiewind(curwind, b);
1384 	SetBuf(b);
1385 	if (!is_an_arg())
1386 		ToMark(m);
1387 	DelMark(m);
1388 }
1389 
1390 private int	iniargc;	/* main sets these for DoKeys() */
1391 private char	**iniargv;
1392 
1393 private void
DoKeys(firsttime)1394 DoKeys(firsttime)
1395 bool	firsttime;
1396 {
1397 	jmp_buf	savejmp;
1398 
1399 	push_env(savejmp);
1400 
1401 	switch (setjmp(mainjmp)) {
1402 	case 0:
1403 		if (firsttime)
1404 			UNIX_cmdline(iniargc, iniargv);
1405 		break;
1406 
1407 	case JMP_QUIT:
1408 		if (RecDepth == 0) {
1409 			if (ModMacs()) {
1410 				rbell();
1411 				if (!yes_or_no_p("Some MACROS haven't been saved; leave anyway? "))
1412 					break;
1413 			}
1414 			if (ModBufs(NO)) {
1415 				rbell();
1416 				if (!yes_or_no_p("Some buffers haven't been saved; leave anyway? "))
1417 					break;
1418 			}
1419 #ifdef IPROCS
1420 			if (!KillProcs())
1421 				break;	/* user chickened out */
1422 #endif
1423 		}
1424 		pop_env(savejmp);
1425 		return;
1426 
1427 	case JMP_ERROR:
1428 		getDOT();	/* God knows what state linebuf was in */
1429 		/*FALLTHROUGH*/
1430 	case JMP_COMPLAIN:
1431 		{
1432 		gc_openfiles();		/* close any files we left open */
1433 		stickymsg = YES;
1434 		unwind_macro_stack();
1435 		Asking = NO;
1436 		curwind->w_bufp = curbuf;
1437 		DisabledRedisplay = NO;
1438 		SlowCmd = 0;
1439 		redisplay();
1440 		break;
1441 		}
1442 	}
1443 
1444 	this_cmd = last_cmd = OTHER_CMD;
1445 
1446 	for (;;) {
1447 #ifdef MAC
1448 		setjmp(auxjmp);
1449 #endif
1450 		cmd_sync();
1451 #ifdef MAC
1452 		HiliteMenu(0);
1453 		EventCmd = NO;
1454 		menus_on();
1455 #endif
1456 		dispatch(getch());
1457 	}
1458 }
1459 
1460 private char **
scanvec(args,str)1461 scanvec(args, str)
1462 register char	**args,
1463 		*str;
1464 {
1465 	while (*args) {
1466 		if (strcmp(*args, str) == 0)
1467 			return args;
1468 		args += 1;
1469 	}
1470 	return NULL;
1471 }
1472 
1473 #ifdef WINRESIZE
1474 /*ARGSUSED*/
1475 SIGRESTYPE
win_reshape(junk)1476 win_reshape(junk)
1477 int	junk;	/* passed in when invoked by a signal; of no interest */
1478 {
1479 	int save_errno = errno;	/* Subtle, but necessary! */
1480 
1481 	ResizePending = YES;
1482 #ifdef UNIX
1483 	resetsighandler(SIGWINCH, win_reshape);
1484 	if (InSlowRead) {
1485 		/* needed because of stupid BSD restartable I/O */
1486 		InSlowRead = NO;
1487 		redisplay();
1488 		InSlowRead = YES;
1489 	}
1490 #else
1491 	redisplay();
1492 #endif
1493 	errno = save_errno;
1494 	return SIGRESVALUE;
1495 }
1496 #endif /* WINRESIZE */
1497 
1498 private bool
carefulcpy(to,from,maxsize,mess,raw)1499 carefulcpy(to, from, maxsize, mess, raw)
1500 char	*to,*from;
1501 size_t	maxsize;
1502 char	*mess;
1503 bool	raw;
1504 {
1505 	if (from != NULL) {
1506 		char	*ugh;
1507 
1508 		if (strlen(from) >= maxsize)
1509 			ugh = "too long";
1510 		else if (*from == '\0')
1511 			ugh = "empty";
1512 		else {
1513 			strcpy(to, from);
1514 			return YES;
1515 		}
1516 		if (raw) {
1517 			raw_complain("\r\n%s %s", mess, ugh);
1518 		} else {
1519 			swritef(mesgbuf, sizeof mesgbuf, "%s %s", mess, ugh);
1520 			message(mesgbuf);
1521 		}
1522 	}
1523 	return NO;
1524 }
1525 
1526 private void
dojovercs(dosys,dousr)1527 dojovercs(dosys, dousr)
1528 bool	dosys;
1529 bool	dousr;
1530 {
1531 	char	Joverc[FILESIZE];
1532 
1533 	if (dosys) {
1534 		swritef(Joverc, sizeof(Joverc), "%s/jove.rc", ShareDir);
1535 		(void) joverc(Joverc);	/* system wide jove.rc */
1536 	}
1537 
1538 	if (dousr) {
1539 #ifdef MSFILESYSTEM
1540 		/* We don't want to run the same rc file twice */
1541 		if (!dosys || strcmp(HomeDir, ShareDir) != 0) {
1542 			swritef(Joverc, sizeof(Joverc), "%s/jove.rc", HomeDir);
1543 			(void) joverc(Joverc);	/* jove.rc in home directory */
1544 		}
1545 #else
1546 		swritef(Joverc, sizeof(Joverc), "%s/.joverc", HomeDir);
1547 		(void) joverc(Joverc);	/* .joverc in home directory */
1548 #endif
1549 	}
1550 }
1551 
1552 int
main(argc,argv)1553 main(argc, argv)
1554 int	argc;
1555 char	*argv[];
1556 {
1557 	char	**argp;
1558 #ifdef AUTO_BUFS
1559 	/* allocate these usually static buffers on the stack:
1560 	 * preserves addressability on some systems.
1561 	 */
1562 	char	s_iobuff[LBSIZE],
1563 		s_genbuf[LBSIZE],
1564 		s_linebuf[LBSIZE];
1565 
1566 	iobuff = s_iobuff;
1567 	genbuf = s_genbuf;
1568 	linebuf = s_linebuf;
1569 #endif
1570 
1571 #ifdef MAC
1572 	MacInit();		/* initializes all */
1573 	argc = getArgs(&argv);
1574 #endif
1575 
1576 #if defined(__WATCOMC__) && defined(FAR_LINES)
1577 	/* Watcom C under DOS won't grow the near heap after any far
1578 	 * allocation, so we must bump it up to the full 64K now.
1579 	 */
1580 	_heapgrow();
1581 #endif
1582 
1583 	iniargc = argc;
1584 	iniargv = argv;
1585 
1586 	if (setjmp(mainjmp)) {
1587 		ttysetattr(NO);
1588 		writef("\nAck! I can't deal with error \"%s\" now.\n", mesgbuf);
1589 		flushscreen();
1590 		return 1;
1591 	}
1592 
1593 #if defined(USE_CTYPE) && !defined(NO_SETLOCALE)
1594 	/* make ctype reflect "native environment" */
1595 	locale_adjust();
1596 #endif
1597 
1598 	getTERM();	/* Get terminal. */
1599 #ifndef MAC	/* no environment in MacOS */
1600 	if (getenv("METAKEY"))
1601 		MetaKey = YES;
1602 #endif
1603 	ttysetattr(YES);
1604 	ttsize();
1605 
1606 #ifdef UNIX
1607 # ifdef WINRESIZE
1608 	(void) setsighandler(SIGWINCH, win_reshape);
1609 # endif
1610 #endif
1611 
1612 #ifdef MAC
1613 	InitEvents();
1614 #endif
1615 
1616 	d_cache_init();		/* initialize the disk buffer cache */
1617 
1618 	make_scr();
1619 	flushscreen();	/* kludge: prevent interleaving output with diagnostic */
1620 	mac_init();	/* Initialize Macros */
1621 	winit();	/* Initialize Window */
1622 #ifdef PTYPROCS
1623 # ifdef SIGCHLD
1624 	(void) setsighandler(SIGCHLD, sigchld_handler);
1625 # endif
1626 #endif
1627 #ifdef USE_SELECT
1628 	FD_ZERO(&global_fd);
1629 	FD_SET(0, &global_fd);
1630 	global_maxfd = 1;
1631 #endif
1632 	buf_init();
1633 
1634 	if ((argp = scanvec(argv, "-d")) != NULL && chkCWD(argp[1]))
1635 		setCWD(argp[1]);
1636 	else
1637 		getCWD();	/* After we setup curbuf in case we have to getwd() */
1638 
1639 #ifdef MAC
1640 	HomeDir = gethome();
1641 #else /* !MAC */
1642 	HomeDir = getenv("HOME");
1643 	if (HomeDir == NULL) {
1644 # ifdef MSDOS
1645 		HomeDir = copystr(pwd());	/* guess at current (initial) directory */
1646 # else
1647 #  ifdef WIN32
1648 		/* Following are set up automatically by NT on logon. */
1649 		char *homedrive = getenv("HOMEDRIVE");
1650 		char *homepath = getenv("HOMEPATH");
1651 
1652 		if (homedrive != NULL && homepath != NULL) {
1653 			HomeDir = emalloc(strlen(homedrive) + strlen(homepath) + 1);
1654 			strcpy(HomeDir, homedrive);
1655 			strcat(HomeDir, homepath);
1656 		} else {
1657 			HomeDir = copystr(pwd());
1658 		}
1659 #  else /* !WIN32 */
1660 		HomeDir = "/";
1661 #  endif /* !WIN32 */
1662 # endif /* !MSDOS */
1663 	}
1664 #endif /* !MAC */
1665 	HomeLen = strlen(HomeDir);
1666 
1667 	InitKeymaps();
1668 
1669 	settout();	/* not until we know baudrate */
1670 	SetTerm();
1671 
1672 #ifndef MAC	/* no environment in MacOS */
1673 	/* Handle overrides for ShareDir and LibDir.
1674 	 * We take care to use the last specification.
1675 	 * Even if we don't use LibDir, we accept it.
1676 	 */
1677 	 {
1678 		char
1679 			*so = getenv("JOVESHARE");
1680 # ifdef NEED_LIBDIR
1681 		char
1682 			*lo = getenv("JOVELIB");
1683 # endif
1684 
1685 		for (argp = argv; argp[0] != NULL && argp[1] != NULL; argp++) {
1686 			if (strcmp(*argp, "-s") == 0)
1687 				so = *++argp;
1688 # ifdef NEED_LIBDIR
1689 			else if (strcmp(*argp, "-l") == 0)
1690 				lo = *++argp;
1691 			else if (strcmp(*argp, "-ls") == 0 || strcmp(*argp, "-sl") == 0)
1692 				lo = so = *++argp;
1693 # endif
1694 		}
1695 		if (so != NULL)
1696 			if (!carefulcpy(ShareDir, so, sizeof(ShareDir)-9, "ShareDir", YES))
1697 				finish(0);
1698 # ifdef NEED_LIBDIR
1699 		if (lo != NULL)
1700 			if (!carefulcpy(LibDir, lo, sizeof(LibDir)-9, "LibDir", YES))
1701 				finish(0);
1702 #  ifdef PIPEPROCS
1703 		swritef(Portsrv, sizeof(Portsrv), "%s/portsrv", LibDir);
1704 #  endif
1705 # endif /* NEED_LIBDIR */
1706 	}
1707 #endif /* !MAC */
1708 
1709 	ShowVersion();	/* but the 'carefulcpy's which follow might overwrite it */
1710 
1711 	/* import the temporary file path from the environment
1712 	   and fix the string, so that we can append a slash
1713 	   safely	*/
1714 #ifdef MSFILESYSTEM
1715 	carefulcpy(TmpDir, getenv("TEMP"), sizeof(TmpDir), "TEMP", NO);
1716 #endif
1717 #ifndef MAC	/* no environment in MacOS */
1718 	carefulcpy(TmpDir, getenv("TMPDIR"), sizeof(TmpDir), "TMPDIR", NO);
1719 #endif
1720 	{
1721 		char	*cp = &TmpDir[strlen(TmpDir)];
1722 
1723 		do ; while (cp != TmpDir && (*--cp == '/'
1724 #ifdef MSFILESYSTEM
1725 			|| *cp == '\\'
1726 #endif
1727 			));
1728 		cp[1] = '\0';
1729 	}
1730 
1731 #ifdef SUBSHELL
1732 # ifdef MSFILESYSTEM	/* ??? Is this the right test? */
1733 	carefulcpy(Shell, getenv("COMSPEC"), sizeof(Shell), "COMSPEC", NO);
1734 	/* SHELL, if present in DOS environment, will take precedence over COMSPEC */
1735 # endif /* MSFILESYSTEM */
1736 	carefulcpy(Shell, getenv("SHELL"), sizeof(Shell), "SHELL", NO);
1737 #endif /* SUBSHELL */
1738 
1739 #ifdef UNIX
1740 	carefulcpy(Mailbox, getenv("MAIL"), sizeof(Mailbox), "MAIL", NO);
1741 #endif
1742 
1743 	dojovercs(scanvec(argv, "-J") == NULL, scanvec(argv, "-j") == NULL);
1744 
1745 #if defined(SUBSHELL) && defined(RECOVER)
1746 	if (scanvec(argv, "-r") != NULL)
1747 		dorecover();
1748 #endif
1749 
1750 #ifdef UNIX
1751 # ifndef DEBUGCRASH
1752 	(void) setsighandler(SIGHUP, finish);
1753 	(void) setsighandler(SIGINT, finish);
1754 #  ifdef SIGBUS
1755 	(void) setsighandler(SIGBUS, finish);
1756 #  endif /* SIGBUS */
1757 	(void) setsighandler(SIGSEGV, finish);
1758 	(void) setsighandler(SIGPIPE, finish);
1759 	/* ??? Why should we ignore SIGTERM? */
1760 	/* (void) setsighandler(SIGTERM, SIG_IGN); */
1761 # endif /* DEBUGCRASH */
1762 	(void) setsighandler(SIGALRM, AlarmHandler);
1763 	SetClockAlarm(NO);
1764 #endif /* UNIX */
1765 	ClAndRedraw();
1766 	flushscreen();
1767 	RedrawDisplay();	/* start the redisplay process. */
1768 	DoKeys(YES);
1769 	finish(0);
1770 	/* NOTREACHED*/
1771 }
1772