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 
9 /* (C) 1986, 1987, 1988 Ken Mitchum.  This code is intended only for use with Jove. */
10 
11 /* In 1995 December, D. Hugh Redelmeier hacked on the code to make
12  * it work again.  The environment was Think C 5.0 under System 7.1.
13  *
14  * Obligatory excuses:
15  * - Hugh is not a Mac expert
16  * - Think C 5.0 is quite obsolete (1991)
17  * - The only goal was to get the code working, not working well.
18  *
19  * Known issues:
20  * - the keyboard routines were designed for the Mac Plus keyboard.
21  *   + "Command" is taken as "Control" and ` is taken as ESC.
22  *   + There should be support for a distinct Command keymap
23  *     with Mac-like default bindings.
24  * - "macify" ought to be extended to find-file and perhaps
25  *   other commands
26  * - perhaps there are newer MacOS facilities that ought to be
27  *   exploited.  Apple Events?
28  * - the hacky way the command keystrokes are described in About Jove
29  *   ought to be improved.
30  * - Highlighting ought to be supported.
31  * - Mouse support ought to be better.  For example, selecting text
32  *   ought to be at least as well done as under XTerm!
33  * - [supposition] Because double-clicking is supported, nothing
34  *   is done for a single click until the double-click timeout
35  *   happens.  Since the double-click action is a superset of
36  *   the single-click action, the single click action ought to
37  *   be immediately performed and then augmented if a double-click
38  *   happens.
39  * - see also comments containing ???
40  */
41 
42 #include "tune.h"
43 
44 #ifdef MAC	/* the body is the rest of this file */
45 
46 #include "jove.h"
47 
48 #include <Controls.h>
49 #include <Desk.h>
50 #include <Dialogs.h>
51 #include <Errors.h>
52 #include <Events.h>
53 #include <Files.h>
54 #include <Fonts.h>
55 #include <Lists.h>
56 #include <LoMem.h>
57 #include <Menus.h>
58 #include <Quickdraw.h>
59 #include <Resources.h>
60 #include <SegLoad.h>
61 #include <StandardFile.h>
62 #include <ToolUtils.h>
63 #include <Types.h>
64 #include <Windows.h>
65 
66 #include <errno.h>
67 #include <pascal.h>
68 
69 #include "mac.h"
70 #include "ask.h"
71 #include "chars.h"
72 #include "disp.h"
73 #include "extend.h"
74 #include "fp.h"	/* for flushscreen() */
75 #include "commands.h"
76 #include "fmt.h"
77 #include "marks.h"
78 #include "misc.h"
79 #include "move.h"
80 #include "screen.h"
81 #include "scandir.h"
82 #include "term.h"
83 #include "vars.h"
84 #include "version.h"
85 #include "wind.h"
86 
87 extern struct menu Menus[NMENUS];   /* from menumaps.txt => menumaps.c */
88 
89 private	EventRecord the_Event;
90 
91 private void SetBounds proto((void));
92 private void Set_std proto((void));
93 private void Reset_std proto((void));
94 private bool findtext proto((void));
95 
96 
97 /* tn.h Modified for variable screen size 11/21/87. K. Mitchum */
98 
99 #define SCREENSIZE (wc->w_rows * ROWSIZE)
100 #define FONT monaco
101 #define TEXTSIZE 9
102 
103 #define HEIGHT 11
104 #define WIDTH 6
105 #define DESCENT 2
106 #define TWIDTH CO * WIDTH
107 #define THEIGHT LI * HEIGHT
108 
109 /* window specs */
110 
111 #define SCROLLWIDTH 16	/* width of scroll bar control in pixels */
112 #define WINDWIDTH (wc->w_width - SCROLLWIDTH + 1)	/* local coordinates */
113 #define WINDHEIGHT (wc->w_height)	/* local coordinates */
114 #define MAXROW ILI
115 #define MAXCOL (CO - 1)
116 
117 
118 /* for keyboard routines */
119 #define MCHARS 32	/* length of circular buffer -- must be a power of two */
120 #define NCHMASK (MCHARS - 1)	/* mask for modulo MCHARS */
121 
122 
123 /***************************************************/
124 
125 private void
126 	putcurs proto((unsigned row, unsigned col, bool vis)),
127 	curset proto((bool desired)),
128 	dellines proto((int n, int bot)),
129 	inslines proto((int n, int bot));
130 
131 private Rect LimitRect;	/* bounds we can't move past */
132 
133 struct wind_config {
134 	int w_width;	/* pixel width of the Mac window */
135 	int	w_height;
136 	int	w_rows;	/* rows of characters which fit the window */
137 	int	w_cols;
138 } wc_std, wc_user, *wc;
139 
140 private WindowPtr theScreen;
141 
142 bool
143 	Windchange,
144 	EventCmd,
145 	Keyonly,
146 	Bufchange,
147 	Modechange;
148 
149 bool
150 	Macmode = NO;	/* VAR: use Mac file selector */
151 
152 /* Initialization Routines. */
153 
154 void
getTERM()155 getTERM()
156 {
157 }
158 
159 /* For each binding, mark the command with the binding.
160  * We use it for "About Jove ...".
161  * ??? This is faster than using find_binds, but it has problems:
162  * - it only notes the last binding of each command
163  * - it only reflects the three listed keymaps
164  * - it requires a wart on struct cmd
165  * - it is MAC-only
166  */
167 
168 private void
InitMapBinds(km,kmc)169 InitMapBinds(km, kmc)
170 data_obj	**km;
171 char	kmc;
172 {
173 	ZXchar i;
174 
175 	for (i = 0; i < NCHARS; i++) {
176 		if (*km != NULL && obj_type(*km) == COMMAND) {
177 			struct cmd	*c = (struct cmd *) *km;
178 
179 			c->c_map = kmc;
180 			c->c_key = i;
181 		}
182 		km += 1;
183 	}
184 }
185 
186 private void
InitBinds()187 InitBinds()
188 {
189 	InitMapBinds(MainKeys, F_MAINMAP);
190 	InitMapBinds(EscKeys, F_PREF1MAP);
191 	InitMapBinds(CtlxKeys, F_PREF2MAP);
192 }
193 
194 private	WindowPtr window;
195 private	Rect r;
196 private CursHandle cross;
197 
198 private void InitSysMenu proto((void));
199 
200 void
InitEvents()201 InitEvents()
202 {
203 	window = theScreen;
204 	InitSysMenu();
205 	SetRect(&r, window->portRect.left,
206 		window->portRect.top,
207 		window->portRect.right - SCROLLWIDTH,
208 		window->portRect.bottom - SCROLLWIDTH);
209 	cross = GetCursor(crossCursor);
210 }
211 
212 private void	tn_init proto((void));
213 private int	getdir proto((void));
214 
215 void
MacInit()216 MacInit()
217 {
218 	tn_init();
219 	getdir();
220 	strcpy(TmpDir, gethome());
221 	strcpy(ShareDir, TmpDir);
222 	InitBinds();
223 }
224 
225 void
ttysetattr(n)226 ttysetattr(n)
227 bool	n;	/* also used as subscript! */
228 {
229 }
230 
231 /* Surrogate unix-style file i/o routines for Jove.  These replace the
232    routines distributed in the libraries.  They work with Jove, but may
233    not be general enough for other purposes. */
234 
235 #define NFILES 10
236 
237 private int cur_vol;	/* Disk or volume number */
238 private long cur_dir;	/* Directory number */
239 private int cur_vref;	/* ugh.. Vref for volume + directory */
240 
241 struct ftab {
242 	bool inuse;
243 	int refnum;	/* Mac file reference number */
244 } ft[NFILES];
245 
246 private void
fsetup(p)247 fsetup(p)
248 HParmBlkPtr p;
249 {
250 	byte_zero(p, sizeof(HParamBlockRec));
251 	p->fileParam.ioVRefNum = cur_vol;
252 	p->fileParam.ioDirID = cur_dir;
253 	/* p->fileParam.ioFVersNum = 0; */
254 }
255 
256 private void
isetup(p)257 isetup(p)
258 IOParam *p;
259 {
260 	byte_zero(p, sizeof(IOParam));
261 	p->ioVRefNum = cur_vol;
262 }
263 
264 
265 /* Kludge to convert Macintosh error codes to something like Unix. */
266 
267 private int
cvt_err(err)268 cvt_err(err)	/* some of these don't make sense... */
269 int	err;
270 {
271 	switch(err) {
272 	case noErr:
273 		errno = 0;
274 		return 0;
275 	case dirFulErr:
276 	case dskFulErr:
277 		errno = ENOSPC;
278 		break;
279 	/* case nsvErr: */
280 	/* case mFulErr: */
281 	/* case tmfoErr: */
282 	/* case fnfErr: */
283 	default:
284 		errno = ENOENT;
285 		break;
286 	case ioErr:
287 		errno = EIO;
288 		break;
289 	case bdNamErr:
290 	case opWrErr:
291 	case paramErr:
292 		errno = EINVAL;
293 		break;
294 	case fnOpnErr:				/* dubious... */
295 	case rfNumErr:
296 		errno = EBADF;
297 		break;
298 	case eofErr:				/* ditto */
299 	case posErr:
300 		errno = /* no longer defined: ESPIPE */ EIO;
301 		break;
302 	case wPrErr:
303 		errno = EROFS;
304 		break;
305 	case fLckdErr:
306 	case permErr:
307 		errno = EACCES;
308 		break;
309 	case fBsyErr:
310 		errno = EBUSY;
311 		break;
312 	case dupFNErr:
313 		errno = EEXIST;
314 		break;
315 	case gfpErr:
316 	case volOffLinErr:
317 	case volOnLinErr:
318 	case nsDrvErr:
319 		errno = ENODEV;
320 		break;
321 	case noMacDskErr:
322 	case extFSErr:
323 		errno = EIO;
324 		break;
325 	case fsRnErr:
326 	case badMDBErr:
327 	case wrPermErr:
328 		errno = /* no longer defined: EPERM */ EACCES;
329 		break;
330 	}
331 	return -1;
332 }
333 
334 private StringPtr
cvt_fnm(file)335 cvt_fnm(file)
336 const char *file;
337 {
338 	static char nm[255];
339 	char *t;
340 
341 	if (*file == '/') {
342 		strcpy(nm, file + 1);	/* full path */
343 	} else {
344 		if (strchr(file + 1, '/') != NULL)
345 			strcpy(nm, "/");	/* make a partial pathname */
346 		else
347 			nm[0] = '\0';
348 		strcat(nm, file);
349 	}
350 	for (t = nm; (t = strchr(t, '/')) != NULL; )
351 		*t++ = ':';
352 	return CtoPstr(nm);
353 }
354 
355 private int do_creat proto((HParmBlkPtr p, StringPtr nm));
356 
357 int
creat(name,perm)358 creat(name, perm)	/* permission mode is irrelevant on a Mac */
359 const char	*name;
360 int	perm;
361 {
362 	int fd, err;
363 	StringPtr nm;
364 	HParamBlockRec p;
365 
366 	nm = cvt_fnm(name);	/* convert filename to Mac type name */
367 	for (fd = 0; ft[fd].inuse; fd++) {
368 		if (fd == NFILES-1) {
369 			errno = EMFILE;
370 			return -1;
371 		}
372 	}
373 	fsetup(&p);	/* try to delete it, whether it is there or not. */
374 	p.fileParam.ioNamePtr = nm;
375 	if ((err = PBHDelete(&p, 0)) != noErr && err != fnfErr)
376 		return cvt_err(err);
377 	if (do_creat(&p, nm) != 0)
378 		return -1;
379 	ft[fd].inuse = YES;
380 	ft[fd].refnum = p.ioParam.ioRefNum;
381 	return fd + 1;
382 }
383 
384 #ifdef USE_PROTOTYPES
385 int
open(const char * path,int flags,...)386 open(const char *path, int flags, ...)
387 #else
388 int
389 open(path, flags)
390 const char	*path;
391 int	flags;
392 #endif
393 {
394 	int fd, err;
395 	StringPtr nm;
396 	HParamBlockRec p;
397 
398 	nm = cvt_fnm(path);	/* convert filename to Mac type name */
399 	for (fd = 0; ft[fd].inuse; fd++) {
400 		if (fd == NFILES-1) {
401 			errno = EMFILE;
402 			return -1;
403 		}
404 	}
405 	fsetup(&p);
406 	switch (flags & 3) {
407 	case 0:	/* O_RDONLY */
408 		p.ioParam.ioPermssn = fsRdPerm;
409 		break;
410 	case 1:	/* O_WRONLY */
411 		p.ioParam.ioPermssn = fsWrPerm;
412 		break;
413 	case 2:	/* O_RDWR */
414 		p.ioParam.ioPermssn = fsRdWrPerm;
415 		break;
416 	}
417 	p.ioParam.ioNamePtr = nm;
418 	p.ioParam.ioMisc = 0;
419 	if ((err = PBHOpen(&p, 0)) != noErr)
420 		return cvt_err(err);
421 	ft[fd].refnum = p.ioParam.ioRefNum;
422 	p.ioParam.ioPosMode = fsFromStart;
423 	p.ioParam.ioPosOffset = 0;
424 	if ((err = PBSetFPos((ParamBlockRec *) &p, 0)) != noErr)
425 		return cvt_err(err);
426 	ft[fd].inuse = YES;
427 	errno = 0;
428 	return fd + 1;
429 }
430 
431 private int
do_creat(p,nm)432 do_creat(p, nm)
433 HParmBlkPtr p;
434 StringPtr nm;
435 {
436 	int err;
437 
438 	fsetup(p);
439 	p->fileParam.ioNamePtr = nm;
440 	if ((err = PBHCreate(p, 0)) != noErr)
441 		return cvt_err(err);
442 
443 	fsetup(p);
444 	p->fileParam.ioNamePtr = nm;
445 	p->fileParam.ioFDirIndex = 0;
446 	if ((err = PBHGetFInfo(p, 0)) != noErr)
447 		return cvt_err(err);
448 
449 	p->fileParam.ioDirID = cur_dir;
450 	p->fileParam.ioFlFndrInfo.fdType = 'TEXT';
451 	p->fileParam.ioFlFndrInfo.fdCreator = 'JV01';
452 	p->fileParam.ioFlFndrInfo.fdFlags = 0;
453 	p->fileParam.ioFVersNum = 0;
454 	if ((err = PBHSetFInfo(p, 0)) != noErr)
455 		return cvt_err(err);
456 
457 	fsetup(p);
458 	p->ioParam.ioNamePtr = nm;
459 	p->ioParam.ioPermssn = fsRdWrPerm;
460 	p->ioParam.ioMisc = 0;
461 	if (cvt_err(PBHOpen(p, 0)))
462 		return -1;
463 
464 	return 0;
465 }
466 
467 
468 int
close(fd)469 close(fd)
470 int	fd;
471 {
472 	int err;
473 	HParamBlockRec p;
474 
475 	if (!ft[--fd].inuse) {
476 		errno = EBADF;
477 		return -1;
478 	}
479 
480 	fsetup(&p);
481 	p.ioParam.ioRefNum = ft[fd].refnum;
482 	ft[fd].inuse = NO;
483 	if (cvt_err(PBClose((ParamBlockRec *) &p, 0)) < 0)
484 		return -1;
485 
486 	fsetup(&p);
487 	p.ioParam.ioNamePtr = NULL;
488 	if (cvt_err(PBFlushVol((ParamBlockRec *) &p, 0)) < 0)
489 		return -1;
490 
491 	return 0;
492 }
493 
494 private SSIZE_T con_read proto((char *buf, size_t size));
495 
496 /* Raw UNIX-like read */
497 
498 SSIZE_T
read(fd,ubuf,n)499 read(fd, ubuf, n)
500 int	fd;
501 UnivPtr	ubuf;
502 size_t	n;
503 {
504 	char	*buf = ubuf;	/* char * is more useful */
505 	int err;
506 	IOParam p;
507 
508 	if (fd == 0) {
509 		return con_read(buf, n);
510 	} else {
511 		if (!ft[--fd].inuse) {
512 			errno = EBADF;
513 			return -1;
514 		}
515 		isetup(&p);
516 		p.ioRefNum = ft[fd].refnum;
517 		p.ioBuffer = buf;
518 		p.ioReqCount = n;
519 		p.ioPosMode = fsFromMark;
520 		p.ioPosOffset = 0;
521 		if ((err = PBRead((ParamBlockRec *)&p, 0)) != noErr && err != eofErr)
522 			return cvt_err(err);
523 
524 		errno = 0;
525 		return p.ioActCount;
526 	}
527 }
528 
529 /* Raw UNIX-like write */
530 
531 SSIZE_T
write(fd,ubuf,n)532 write(fd, ubuf, n)
533 int	fd;
534 UnivConstPtr	ubuf;
535 size_t	n;
536 {
537 	const char	*buf = ubuf;	/* char * is more convenient */
538 
539 	if (fd == 0) {
540 		writetext((unsigned char *)buf, n);
541 		return n;
542 	} else {
543 		IOParam p;
544 		int	err;
545 		const char	*ebuf = buf + n;
546 
547 		if (!ft[--fd].inuse) {
548 			errno = EBADF;
549 			return -1;
550 		}
551 		isetup(&p);
552 		p.ioRefNum = ft[fd].refnum;
553 		p.ioPosMode = fsFromMark;
554 		p.ioReqCount = n;
555 		p.ioBuffer = (Ptr)buf;
556 		p.ioPosOffset = 0L;	/* bidirectional */
557 		if ((err = PBWrite((ParamBlockRec *)&p, 0)) != noErr)
558 			return cvt_err(err);
559 		return p.ioActCount;
560 	}
561 }
562 
563 long
lseek(fd,offset,whence)564 lseek(fd, offset, whence)
565 int	fd;
566 long	offset;
567 int	whence;
568 {
569 	int err;
570 	long cur_mark, leof, new_mark;
571 	IOParam p;
572 
573 	if (!ft[--fd].inuse) {
574 		errno = EBADF;
575 		return -1;
576 	}
577 
578 	isetup(&p);
579 	p.ioRefNum = ft[fd].refnum;
580 	if ((err = PBGetFPos((ParamBlockRec *)&p, 0)) != noErr)
581 		return cvt_err(err);
582 
583 	cur_mark = p.ioPosOffset;
584 	isetup(&p);
585 	p.ioRefNum = ft[fd].refnum;
586 	if ((err = PBGetEOF((ParamBlockRec *)&p, 0)) != noErr)
587 		return cvt_err(err);
588 
589 	leof = (long) p.ioMisc;
590 	switch(whence) {
591 	case 0:
592 		new_mark = offset;
593 		break;
594 	case 1:
595 		new_mark = offset + cur_mark;
596 		break;
597 	case 2:
598 		new_mark = offset + leof;
599 		break;
600 	default:
601 		errno = EINVAL;
602 		return -1;
603 	}
604 	if (new_mark > leof) {
605 		/* need more space in file -- grow it */
606 		isetup(&p);
607 		p.ioRefNum = ft[fd].refnum;
608 		p.ioMisc = (Ptr) new_mark;
609 		if ((err = PBSetEOF((ParamBlockRec *)&p, 0)) != noErr)
610 			return cvt_err(err);
611 	}
612 	isetup(&p);
613 	p.ioRefNum = ft[fd].refnum;
614 	p.ioPosOffset = new_mark;
615 	p.ioPosMode = fsFromStart;
616 	if ((err = PBSetFPos((ParamBlockRec *)&p, 0)) != noErr)
617 		return cvt_err(err);
618 	errno = 0;
619 	return p.ioPosOffset;
620 }
621 
622 /* delete file, if it exists */
623 
624 int
unlink(name)625 unlink(name)
626 const char *name;
627 {
628 	int fd, err;
629 	HParamBlockRec p;
630 
631 	fsetup(&p);
632 	p.fileParam.ioNamePtr = cvt_fnm(name);
633 	if ((err = PBHDelete(&p, 0)) != noErr && err != fnfErr)
634 		return cvt_err(err);
635 	return 0;
636 }
637 
638 /* Console read routine */
639 
640 private ZXchar rawgetc proto((void));
641 
642 private SSIZE_T
con_read(buf,size)643 con_read(buf, size)
644 char *buf;
645 size_t size;
646 {
647 	size_t n;
648 	ZXchar p;
649 
650 
651 	n = 0;
652 	do {
653 		p = rawgetc();
654 		*buf++ = p;
655 		n++;
656 	} while (rawchkc() && n <= size);
657 	return n;
658 }
659 
660 void
dobell(n)661 dobell(n)	/* declared in term.h */
662 int	n;
663 {
664 	while (--n >= 0)
665 		SysBeep(5);
666 	flushscreen();
667 }
668 
669 /* Simplified stat() routine emulates what is needed most. */
670 
671 int
stat(fname,buf)672 stat(fname, buf)
673 const char *fname;
674 struct stat *buf;
675 {
676 	CInfoPBRec p;
677 	StringPtr nm;
678 
679 	nm = cvt_fnm(fname);
680 	byte_zero(&p, sizeof(CInfoPBRec));
681 	p.hFileInfo.ioCompletion = 0;
682 	p.hFileInfo.ioNamePtr = nm;
683 	p.hFileInfo.ioFVersNum = 0;
684 	p.hFileInfo.ioFDirIndex = 0;
685 	p.hFileInfo.ioVRefNum = cur_vol;
686 	p.hFileInfo.ioDirID = cur_dir;
687 
688 	switch (PBGetCatInfo(&p, 0)) {
689 	case noErr:
690 		errno = 0;
691 		buf->st_dev = p.hFileInfo.ioVRefNum + 1;	/* don't want 0 */
692 		buf->st_ino = p.hFileInfo.ioDirID;
693 		buf->st_size = p.hFileInfo.ioFlLgLen;
694 		buf->st_mtime = p.hFileInfo.ioFlMdDat;
695 		buf->st_mode = (p.hFileInfo.ioFlAttrib & 0x10) ? S_IFDIR : S_IFREG;
696 		return 0;
697 	case nsvErr:
698 	case paramErr:
699 	case bdNamErr:
700 	case fnfErr:
701 		errno = ENOENT;
702 		break;
703 	case ioErr:
704 		errno = EIO;
705 		break;
706 	default:
707 		errno = ENOENT;
708 		break;
709 	}
710 	return -1;
711 }
712 
713 /* Directory related routines.  Jove keeps track of the true Volume (disk)
714    number and directory number, and avoids "Working Directory Reference
715    Numbers", which are confusing. */
716 
717 private int
getdir()718 getdir()	/* call this only once, during startup. */
719 {
720 	WDPBRec p;
721 
722 	p.ioCompletion = 0;
723 	p.ioNamePtr = NULL;
724 	if (PBHGetVol(&p, 0) != noErr)
725 		return -1;	/* BIG trouble (but caller never checks returned value!) */
726 	cur_vol = p.ioWDVRefNum;
727 	cur_dir = p.ioWDDirID;
728 	SFSaveDisk = 0 - cur_vol;	/* these are for SF dialogs */
729 	CurDirStore = cur_dir;
730 	return 0;
731 }
732 
733 private int
setdir(vol,dir)734 setdir(vol, dir)
735 int	vol;
736 long	dir;
737 {
738 	WDPBRec p;
739 
740 	p.ioCompletion = 0;
741 	p.ioNamePtr = NULL;
742 	p.ioVRefNum = vol;
743 	p.ioWDDirID = dir;
744 	if (PBHSetVol(&p, 0) != noErr)
745 		return -1;
746 
747 	cur_vol = vol;
748 	cur_dir = dir;
749 	SFSaveDisk = 0 - vol;	/* these are for SF dialogs */
750 	CurDirStore = dir;
751 	return 0;
752 }
753 
754 private bool
lookupdir(dir,d)755 lookupdir(dir, d)
756 const char	*dir;	/* UNIX-like pathname for directory */
757 CInfoPBPtr	d;	/* info from directory */
758 {
759 	char
760 		nm[FILESIZE + 1],
761 		*t;
762 
763 	if (strcmp(dir, ".") == 0)
764 		getcwd(nm, sizeof(nm) - 1);
765 	else
766 		strcpy(nm, dir);
767 
768 	for (t = nm; (t = strchr(t, '/')) != NULL; )
769 		*t++ = ':';
770 
771 	t = nm;	/* get rid of initial slashes */
772 	while (*t == ':')
773 		t++;
774 
775 	strcat(t, ":");	/* force trailing ':', signifying directory */
776 
777 	byte_zero(d, sizeof(*d));
778 	/* d->dirInfo.ioCompletion = 0; */
779 	d->dirInfo.ioNamePtr = CtoPstr(t);
780 	d->dirInfo.ioVRefNum = cur_vol;
781 	/* d->dirInfo.ioFDirIndex = 0; */
782 	/* d->dirInfo.ioDrDirID = 0; */
783 	PBGetCatInfo(d, 0);
784 	return d->dirInfo.ioResult == noErr
785 		&& (d->dirInfo.ioFlAttrib & 0x10) != 0;
786 }
787 
788 int
chdir(dir)789 chdir(dir)
790 const char *dir;
791 {
792 	CInfoPBRec d;
793 
794 	if (strcmp(dir, "/") == 0	/* There is no root... */
795 	|| !lookupdir(dir, &d)
796 	|| setdir(d.dirInfo.ioVRefNum, d.dirInfo.ioDrDirID) < 0)
797 		return -1;
798 	return 0;
799 }
800 
801 /* Scandir returns the number of entries or -1 if the directory cannot
802    be opened or malloc fails.
803    Note: if we ever support RECOVER, this code will have to be moved
804    to scandir.c */
805 
806 int
jscandir(dir,nmptr,qualify,sorter)807 jscandir(dir, nmptr, qualify, sorter)
808 char	*dir;
809 char	***nmptr;
810 bool	(*qualify) ptrproto((char *));
811 int	(*sorter) ptrproto((UnivConstPtr, UnivConstPtr));
812 {
813 	long DirID;
814 	char	**ourarray;
815 	unsigned int	nalloc = 10,
816 			nentries = 0,
817 			index = 1;
818 
819 	if (strcmp(dir, "/") == 0) {
820 		/* we are enumerating volumes */
821 		DirID = 0;
822 	} else {
823 		/* we are enumerating the contents of a volume or directory */
824 		CInfoPBRec	d;
825 
826 		if (!lookupdir(dir, &d))
827 			return -1;
828 		DirID = d.dirInfo.ioDrDirID;
829 	}
830 
831 	ourarray = (char **) emalloc(nalloc * sizeof (char *));
832 	for (;;) {
833 		Str32 name;	/* 31 is limit, but we might add a '/' */
834 
835 		if (DirID == 0) {
836 			/* we are enumerating volumes */
837 			ParamBlockRec	d;
838 
839 			byte_zero(&d, sizeof(d));
840 			d.volumeParam.ioCompletion = 0;
841 			d.volumeParam.ioNamePtr = name;
842 			d.volumeParam.ioVRefNum = 0;
843 			d.volumeParam.ioVolIndex = index++;
844 			if (PBGetVInfo(&d, 0) != noErr)
845 				break;	/* we are done, then */
846 			PtoCstr(name);
847 #ifdef DIRECTORY_ADD_SLASH
848 			/* I *think* this has got to be a volume */
849 			strcat((char *)name, "/");
850 #endif
851 
852 		} else {
853 			/* we are enumerating the contents of a volume or directory */
854 			CInfoPBRec	d;
855 
856 			byte_zero(&d, sizeof(d));
857 			d.dirInfo.ioCompletion = 0;
858 			d.dirInfo.ioNamePtr = name;
859 			d.dirInfo.ioVRefNum = cur_vol;
860 			d.dirInfo.ioFDirIndex = index++;
861 			d.dirInfo.ioDrDirID = DirID;	/* .ioDirID == .ioDrDirID */
862 			if (PBGetCatInfo(&d, 0) != noErr)
863 				break;	/* we are done, then */
864 			PtoCstr(name);
865 #ifdef DIRECTORY_ADD_SLASH
866 			if (d.dirInfo.ioFlAttrib & 0x10)	/* see Inside Mac IV-122 */
867 				strcat((char *)name, "/");
868 #endif
869 		}
870 		if (qualify != NULL && !(*qualify)((char *) name))
871 			continue;
872 		/* note: test ensures one space left in ourarray for NULL */
873 		if (nentries+1 == nalloc)
874 			ourarray = (char **) erealloc((char *) ourarray, (nalloc += 10) * sizeof (char *));
875 		ourarray[nentries++] = copystr((char *)name);
876 	}
877 	ourarray[nentries] = NULL;
878 
879 	if (sorter != NULL)
880 		qsort((char *) ourarray, nentries, sizeof (char **), sorter);
881 	*nmptr = ourarray;
882 
883 	return nentries;
884 }
885 
886 
887 char *
getcwd(buf,size)888 getcwd(buf, size)
889 char	*buf;
890 size_t	size;
891 {
892 	CInfoPBRec d;
893 	Str31 nm;
894 	char	*p = buf + size;	/* build from right */
895 
896 	if (p == buf)
897 		return NULL;	/* not even room for NUL */
898 
899 	*--p = '\0';
900 
901 	for (d.dirInfo.ioDrDirID = cur_dir; ; d.dirInfo.ioDrDirID = d.dirInfo.ioDrParID) {
902 		d.dirInfo.ioCompletion = 0;
903 		d.dirInfo.ioNamePtr = nm;
904 		d.dirInfo.ioVRefNum = cur_vol;
905 		d.dirInfo.ioFDirIndex = -1;
906 		PBGetCatInfo(&d, 0);
907 		if (d.dirInfo.ioResult != noErr)
908 			return NULL;
909 
910 		if (p - buf <= Length(nm))
911 			return NULL;	/* insufficient room for / and name */
912 
913 		p -= Length(nm);
914 		memcpy((UnivPtr)p, (UnivPtr) (nm+1), Length(nm));
915 		*--p = '/';
916 
917 		if (d.dirInfo.ioDrDirID == 2)
918 			break;	/* home directory */
919 	}
920 	strcpy(buf, p);	/* left justify */
921 	return buf;
922 }
923 
924 char *
gethome()925 gethome()		/* this will be startup directory */
926 {
927 	static char *ret = NULL;
928 	char	space[FILESIZE];
929 
930 	if (ret == NULL)
931 		ret = copystr(getcwd(space, sizeof(space)));
932 	return ret;
933 }
934 
935 
936 
937 /* Routines that put up and manipulate the "About Jove" dialog. */
938 
939 
940 /* (ORIGINALLY IN) about_j.c. */
941 
942 
943 #define DLOGNAME "\pABOUT_JDLOG"
944 
945 #define DONE_ITEM 1
946 #define LIST_ITEM 2
947 
948 
949 #define DWIDTH 460		/* there should be an easy way to get this */
950 #define DHEIGHT 240		/* from the resource file! */
951 
952 WindowPtr makedisplay();
953 ListHandle makelist();
954 
955 
956 private WindowPtr theWindow;
957 private ListHandle theList;
958 private Rect theListRect;
959 private EventRecord theEvent;
960 
961 
962 
963 private void
964 	do_list proto((void)),
965 	do_events proto((void));
966 
967 private WindowPtr
968 	makedisplay proto((void));
969 
970 private ListHandle
971 	makelist proto((void));
972 
973 private void
about_j()974 about_j()
975 {
976 	WindowPtr OldWindow;
977 
978 	GetPort(&OldWindow);
979 
980 	if ((theWindow = makedisplay()) == 0)
981 		return;
982 	SetPort(theWindow);
983 	if (theList = makelist()) {
984 		LActivate(1, theList);
985 		do_list();
986 		ShowWindow(theWindow);
987 		do_events();
988 	}
989 	SetPort(OldWindow);
990 	LDispose(theList);
991 	DisposDialog(theWindow);
992 }
993 
994 
995 private WindowPtr
makedisplay()996 makedisplay()
997 {
998 	static short dlogid = 0;
999 
1000 	DialogPtr theDialog;
1001 	Handle theHandle;
1002 	Handle theResource;
1003 	Str255 buf;
1004 	ResType resType;
1005 	short itemType;
1006 	Rect theRect;
1007 	short dh, dv;	/* to center dialog on the screen */
1008 	Str255 nostring;
1009 
1010 	if (dlogid == 0) {
1011 		if ((theResource = GetNamedResource('DLOG', DLOGNAME)) == NULL)
1012 			return (WindowPtr)NULL;
1013 		itemType = 'DLOG';
1014 		GetResInfo(theResource, &dlogid, &resType, buf);
1015 	}
1016 
1017 	theDialog = GetNewDialog(dlogid, 0L, (WindowPtr) -1);
1018 	nostring[0] = 0;	/* set length of Pascal String to 0 */
1019 	ParamText(
1020 		"\pMacJove - Copyright (C) 1986-1996 J. Payne, K. Gegenfurtner,",
1021 		"\pK. Mitchum. Portions (C) THINK Technologies, Inc.",
1022 		nostring, nostring);
1023 
1024 	dh = qd.screenBits.bounds.left + (qd.screenBits.bounds.right - DWIDTH) / 2;
1025 	dv = qd.screenBits.bounds.top  + (qd.screenBits.bounds.bottom - DHEIGHT) / 2;
1026 	MoveWindow((WindowPtr)theDialog, dh, dv, 0);
1027 	ShowWindow((WindowPtr)theDialog);
1028 
1029 
1030 	GetDItem(theDialog, LIST_ITEM, &itemType, &theHandle, &theRect);
1031 	theListRect = theRect;
1032 	theListRect.right -= 15;
1033 	((WindowPtr)theDialog)->txFont = FONT;
1034 	((WindowPtr)theDialog)->txSize = TEXTSIZE;
1035 
1036 	return (WindowPtr) theDialog;
1037 }
1038 
1039 private void
do_display()1040 do_display()		/* draw necessary controls, lines */
1041 {
1042 	Rect rViewF;		/* framing rect for list */
1043 	int offset;
1044 
1045 	rViewF = theListRect;
1046 
1047 	rViewF.left--;
1048 	rViewF.top--;
1049 	rViewF.right++;
1050 	rViewF.bottom++;
1051 	FrameRect(&rViewF);
1052 
1053 	DrawControls(theWindow);
1054 }
1055 
1056 private ListHandle
makelist()1057 makelist()
1058 {
1059 	Point csize;
1060 	Rect dataBounds, rView;	/* list boundaries */
1061 
1062 	csize.h = csize.v = 0;
1063 	SetRect(&dataBounds, 0, 0, 1, 0);
1064 	return LNew(&theListRect, &dataBounds, csize, 0, theWindow, 0, 0, 0, 1);
1065 }
1066 
1067 private void
printbind(f,buf)1068 printbind(f, buf)
1069 const struct cmd *f;
1070 char *buf;
1071 {
1072 	char c;
1073 
1074 	if (f->c_map == 0 || (c = f->c_key) == 0x7f) {
1075 		strcpy(buf, "        ");
1076 		return;
1077 	}
1078 	switch(f->c_map) {
1079 	case F_MAINMAP:
1080 		strcpy(buf, "     ");
1081 		break;
1082 
1083 	case F_PREF1MAP:
1084 		strcpy(buf, " ESC ");
1085 		break;
1086 
1087 	case F_PREF2MAP:
1088 		strcpy(buf, "  ^X ");
1089 		break;
1090 	}
1091 	if (c < ' ') {
1092 		buf[5] = '^';		/* control char */
1093 		c |= 0x40;
1094 	} else {
1095 		buf[5] = ' ';
1096 	}
1097 	if ('a' <= c && c <= 'z')
1098 		c &= 0x5f;
1099 	buf[6] = c;
1100 	buf[7] = ' ';
1101 	buf[8] = '\0';
1102 }
1103 
1104 private void
do_list()1105 do_list()
1106 {
1107 	int row, col;
1108 	const struct cmd *f;
1109 	char buf[255];
1110 	Point theCell;
1111 
1112 	theCell.h = 0;
1113 
1114 	for (f = commands, row = 0; f->Name; f++, row++) {
1115 		LAddRow(1, row, theList);
1116 		theCell.v = row;
1117 
1118 		printbind(f, buf);
1119 		strcat(buf, f->Name);
1120 		LSetCell(buf, strlen(buf), theCell, theList);
1121 	}
1122 }
1123 
1124 
1125 
1126 private pascal Boolean
ProcFilter(theDialog,event,itemHit)1127 ProcFilter(theDialog, event, itemHit)
1128 DialogPtr theDialog;
1129 EventRecord *event;
1130 short *itemHit;
1131 {
1132 	theEvent = *event;
1133 	if (theEvent.what == keyDown && theEvent.message & charCodeMask == '\r') {
1134 		*itemHit = 1;
1135 		return YES;
1136 	}
1137 	if (theEvent.what == activateEvt && (WindowPtr) theEvent.message == theWindow) {
1138 		LDoDraw(1, theList);
1139 		LActivate(1, theList);
1140 	}
1141 	if (theEvent.what == updateEvt && (WindowPtr) theEvent.message == theWindow) {
1142 		BeginUpdate(theWindow);
1143 		do_display();
1144 		DrawDialog(theWindow);
1145 		LUpdate(theWindow->visRgn, theList);
1146 		EndUpdate(theWindow);
1147 	}
1148 
1149 	return NO;
1150 }
1151 
1152 
1153 void
do_events()1154 do_events()
1155 {
1156 	short item;
1157 	bool done = NO;
1158 	Point p;
1159 
1160 	while (!done) {
1161 		ModalDialog(ProcFilter, &item);
1162 		switch(item) {
1163 		case DONE_ITEM:
1164 			done = YES;
1165 			/* ??? fall through? -- DHR */
1166 		case LIST_ITEM:
1167 			p = theEvent.where;
1168 			GlobalToLocal(&p);
1169 			LClick(p, theEvent.modifiers, theList);
1170 			break;
1171 		}
1172 	}
1173 }
1174 
1175 /* Window and Control related routines. */
1176 
1177 /* (ORIGINALLY IN) tcon.c.
1178    control handler routines for Jove. K. Mitchum 12/86 */
1179 
1180 
1181 #define MINC 0
1182 #define MAXC 100
1183 #define INITC 0
1184 #define EVENTLIST (mDownMask | keyDownMask )
1185 
1186 private Point p;
1187 private bool wc_adjust proto((int, int, struct wind_config *, int));
1188 
1189 private void
1190 	MakeScrollBar proto((Window *w)),
1191 	AdjustScrollBar proto((Window *w)),
1192 	drawfluff proto((void));
1193 
1194 void
docontrols()1195 docontrols()	/* called from redisplay routines */
1196 {
1197 	Window *w;
1198 	int top;
1199 
1200 	w = fwind;
1201 	top = 0;
1202 	do {
1203 		if (w->w_control != NULL)
1204 			HideControl(w->w_control);
1205 		w = w->w_next;
1206 	} while (w != fwind);
1207 	w = fwind;
1208 	do {
1209 		w->w_topline = top;
1210 		if (w->w_control != NULL)
1211 			AdjustScrollBar(w);
1212 		else
1213 			MakeScrollBar(w);
1214 		ShowControl(w->w_control);
1215 		top += w->w_height;
1216 		w = w->w_next;
1217 	} while (w != fwind);
1218 	Windchange = NO;
1219 	drawfluff();
1220 }
1221 
1222 
1223 private void
MakeScrollBar(w)1224 MakeScrollBar(w)	/* set up control */
1225 Window *w;
1226 {
1227 	Rect BarRect;
1228 	int wheight, wtop;
1229 	WindowPtr window = theScreen;
1230 
1231 	wheight = w->w_height;
1232 	wtop = w->w_topline;
1233 	SetRect(&BarRect, window->portRect.right - SCROLLWIDTH + 1,
1234 		window->portRect.top -2 + wtop * HEIGHT,
1235 		window->portRect.right +1,
1236 		window->portRect.top + ((wheight + wtop) * HEIGHT + 1));
1237 	w->w_control = NewControl(window, &BarRect, "\psbar", 1, INITC,
1238 		MINC, MAXC, scrollBarProc, (long)w);
1239 }
1240 
1241 private void
AdjustScrollBar(w)1242 AdjustScrollBar(w)	/* redo existing control */
1243 Window *w;
1244 {
1245 	ControlHandle handle = w->w_control;;
1246 
1247 	if (handle != NULL) {
1248 		int	wtop = w->w_topline;
1249 		int	wheight = w->w_height;
1250 		WindowPtr	window = (*handle)->contrlOwner;
1251 
1252 		SizeControl(handle, SCROLLWIDTH, wheight * HEIGHT + 1);
1253 
1254 		MoveControl(handle, window->portRect.right - SCROLLWIDTH + 1,
1255 			window->portRect.top - 1 + wtop * HEIGHT);
1256 	}
1257 }
1258 
1259 private int ltoc proto((void));	/* calculate ctlvalue for line position */
1260 
1261 void
SetScrollBar(w)1262 SetScrollBar(w)	/* set value of the bar */
1263 Window *w;
1264 {
1265 	SetCtlValue(w->w_control, ltoc());
1266 }
1267 
1268 private void
drawfluff()1269 drawfluff()		/* draw controls and dividers */
1270 {
1271 	Window *w = fwind;
1272 
1273 	DrawControls(theScreen);
1274 	DrawGrowIcon(theScreen);
1275 }
1276 
1277 void
RemoveScrollBar(w)1278 RemoveScrollBar(w)
1279 Window *w;
1280 {
1281 	if (w->w_control != NULL)
1282 		DisposeControl(w->w_control);
1283 	w->w_control = NULL;
1284 }
1285 
1286 private pascal void
DScroll(control,part)1287 DScroll(control, part)
1288 ControlHandle control;
1289 int part;
1290 {
1291 	DownScroll();
1292 	redisplay();
1293 }
1294 
1295 private pascal void
UScroll(control,part)1296 UScroll(control, part)
1297 ControlHandle control;
1298 int part;
1299 {
1300 	UpScroll();
1301 	redisplay();
1302 }
1303 
1304 private pascal void
NPage(control,part)1305 NPage(control, part)
1306 ControlHandle control;
1307 int part;
1308 {	NextPage();
1309 	redisplay();
1310 }
1311 
1312 private pascal void
PPage(control,part)1313 PPage(control, part)
1314 ControlHandle control;
1315 int part;
1316 {	PrevPage();
1317 	redisplay();
1318 }
1319 
1320 private long npos;	/* number of lines in buffer */
1321 
1322 private int
ltoc()1323 ltoc()	/* calculate ctlvalue for line position */
1324 {
1325 	long ipos = LinesTo(curbuf->b_first, curline) + 1;
1326 
1327 	npos = ipos + LinesTo(curline, (LinePtr)NULL) - 1;
1328 	return (int) ((ipos * MAXC) / npos);
1329 }
1330 
1331 private LinePtr
ctol(ctlv)1332 ctol(ctlv)	/* find buffer line for ctlvalue */
1333 int ctlv;
1334 {
1335 	return next_line(curbuf->b_first, (int) ((npos * ctlv)/MAXC));
1336 }
1337 
1338 private void
doWind(event,window)1339 doWind(event, window)
1340 EventRecord *event;
1341 WindowPtr window;
1342 {
1343 	p = event->where;
1344 	GlobalToLocal(&p);
1345 
1346 	if (event->what == mouseDown) {
1347 		ControlHandle whichControl;
1348 		Window
1349 			*jwind,
1350 			*cwind;
1351 		bool	notcurwind = NO;
1352 		int	cpart;	/* control part */
1353 
1354 		if ((cpart = FindControl(p, window, &whichControl)) == 0)
1355 			return;
1356 
1357 		if ((jwind = (Window *) (*whichControl)->contrlRfCon) != curwind) {
1358 			notcurwind = YES;
1359 			cwind = curwind;
1360 			SetWind(jwind);
1361 		}
1362 		switch (cpart) {
1363 		case inUpButton:
1364 			TrackControl(whichControl, p, (ProcPtr) DScroll);
1365 			break;
1366 		case inDownButton:
1367 			TrackControl(whichControl, p, (ProcPtr) UScroll);
1368 			break;
1369 		case inPageUp:
1370 			TrackControl(whichControl, p, (ProcPtr) PPage);
1371 			break;
1372 		case inPageDown:
1373 			TrackControl(whichControl, p, (ProcPtr) NPage);
1374 			break;
1375 		case inThumb:
1376 			if (TrackControl(whichControl, p, (ProcPtr)NULL)) {
1377 				int	newval = GetCtlValue(whichControl);
1378 
1379 				if (newval == MAXC)
1380 					Eof();
1381 				else if (newval == MINC)
1382 					Bof();
1383 				else
1384 					SetLine(ctol(newval));
1385 			}
1386 			break;
1387 		}
1388 		if (notcurwind) {
1389 			SetWind(cwind);
1390 			redisplay();
1391 		}
1392 		redisplay();	/* again, to set the cursor */
1393 	} else {
1394 		if (findtext())
1395 			redisplay();
1396 	}
1397 }
1398 
1399 #define std_state(w) (*((WStateData **)((WindowPeek)((w)))->dataHandle))->stdState
1400 #define user_state(w) (*((WStateData **)((WindowPeek)((w)))->dataHandle))->userState
1401 
1402 private void
doDrag(event,window)1403 doDrag(event, window)
1404 EventRecord *event;
1405 WindowPtr window;
1406 {
1407 	Rect old_std = std_state(window);
1408 
1409 	DragWindow(window, event->where, &LimitRect);
1410 	if (wc == &wc_std) {
1411 		wc_user = wc_std;
1412 		user_state(theScreen) = std_state(theScreen);
1413 		ZoomWindow(window, 7, 1);
1414 		wc = &wc_user;
1415 		Reset_std();
1416 	}
1417 }
1418 
1419 private void
doGrow(event,window)1420 doGrow(event, window)
1421 EventRecord *event;
1422 WindowPtr window;
1423 {
1424 	long size;
1425 
1426 	/* zero means user didn't change anything */
1427 	if ((size = GrowWindow(window, event->where, &LimitRect)) != 0) {
1428 		if (wc == &wc_std) {
1429 			wc_user = wc_std;
1430 			user_state(theScreen) = std_state(theScreen);
1431 			ZoomWindow(window, 7, 1);
1432 			wc = &wc_user;
1433 			Reset_std();
1434 		}
1435 		if (wc_adjust(LoWord(size), HiWord(size), wc, 0)) {
1436 			EraseRect(&window->portRect);
1437 			SizeWindow(window, wc->w_width, wc->w_height, YES);
1438 			win_reshape(0);	/* no signals here... */
1439 		}
1440 	}
1441 }
1442 
1443 private void
doZoomIn(event,window)1444 doZoomIn(event, window)
1445 EventRecord *event;
1446 WindowPtr window;
1447 {
1448 	if (TrackBox(window, event->where, 7)) {
1449 			EraseRect(&window->portRect);
1450 			ZoomWindow(window, 7, 1);
1451 			wc = &wc_user;
1452 			win_reshape(0);	/* we do our own toggle, not ZoomWindow() */
1453 		}
1454 }
1455 
1456 private void
doZoomOut(event,window)1457 doZoomOut(event, window)
1458 EventRecord *event;
1459 WindowPtr window;
1460 {
1461 	if (TrackBox(window, event->where, 8)) {
1462 			EraseRect(&window->portRect);
1463 			ZoomWindow(window, 8, 1);
1464 			wc = &wc_std;
1465 			win_reshape(0);	/* we do our own toggle, not ZoomWindow() */
1466 		}
1467 }
1468 
1469 private void
doGoAway(event,window)1470 doGoAway(event, window)
1471 EventRecord *event;
1472 WindowPtr window;
1473 {
1474 	if (TrackGoAway(window, event->where))
1475 		Leave();
1476 }
1477 
1478 private Window *
rtowind(row)1479 rtowind(row)	/* return jove window row is in */
1480 int row;
1481 {
1482 	Window *w = fwind;
1483 
1484 	do {
1485 		if ((w->w_topline <= row) && ((w->w_height + w->w_topline) > row))
1486 			return w;
1487 		w = w->w_next;
1488 	} while (w != fwind);
1489 	return NULL;
1490 }
1491 
1492 private LinePtr
windtol(w,row)1493 windtol(w, row)		/* return line for row in window */
1494 Window *w;
1495 int row;
1496 {
1497 	LinePtr l = w->w_top;
1498 
1499 	while (row-- && l != NULL)
1500 		l = l->l_next;
1501 	return l;
1502 }
1503 
1504 private int	ptoxy proto((Point, int *, int *));	/* convert Point to terminal x, y coordinate */
1505 
1506 private bool
findtext()1507 findtext()		/* locate and move the point to match the mouse */
1508 {
1509 	int row, col;
1510 	int offset;
1511 	long ticks;
1512 	EventRecord event;
1513 	Window *w;
1514 	LinePtr l;
1515 
1516 	ticks = Ticks;
1517 	ptoxy(p, &row, &col);
1518 	if ((w = rtowind(row)) == NULL)
1519 		return NO;
1520 
1521 	if (w != curwind)
1522 		SetWind(w);
1523 	offset = PhysScreen[row].s_offset;	/* account for horizontal scrolling and */
1524 	offset += SIWIDTH(offset) + W_NUMWIDTH(w);	/* line number */
1525 	row -= w->w_topline;		/* now have row number in window */
1526 	if (row >= w->w_height -1)
1527 		return NO;
1528 
1529 	if ((l = windtol(w, row)) == NULL)
1530 		return NO;
1531 
1532 	if (l->l_dline == NULL_DADDR)
1533 		return NO;
1534 
1535 	this_cmd = LINECMD;
1536 	SetLine(l);		/* Curline is in linebuf now */
1537 	col -= offset;
1538 	if (col < 0)
1539 		col = 0;
1540 	curchar = how_far(curline, col);
1541 	do {
1542 		if (GetNextEvent(mUpMask, &event) && (event.when < ticks + DoubleTime)) {
1543 			set_mark();
1544 			break;
1545 		}
1546 	} while ((Ticks - ticks) < DoubleTime);
1547 	return YES;
1548 }
1549 
1550 
1551 private int
ptoxy(p,row,col)1552 ptoxy(p, row, col)	/* convert Point to terminal x, y coordinate */
1553 Point p;
1554 int *row, *col;
1555 {
1556 	*row = (p.v / HEIGHT);
1557 	*col = (p.h / WIDTH );
1558 	if ((*row > MAXROW) || (*col > MAXCOL))
1559 		return JMP_ERROR;
1560 	return 0;
1561 }
1562 
1563 /* Event-related routines.  The Event loop is CheckEvents(), and is called
1564    whenever a console read occurs or a call to charp().  During certain
1565    activities, such as ask(), etc. non-keyboard events are ignored.
1566    This is set by the variable Keyonly.  As an update or activate event
1567    generates a call to redisplay(), it is important that redisplay() and
1568    related routines NOT check for keyboard characters. */
1569 
1570 /* (ORIGINALLY IN) tevent.c
1571 	event handler for Jove. K Mitchum 12/86 */
1572 
1573 
1574 #define SYS_ID 100
1575 #define NOFUNC ((void (*) ptrproto((EventRecord *event)))NULL)
1576 #define NEVENTS 16
1577 
1578 private void
1579 	doMouse proto((EventRecord *event)),
1580 	dokeyDown proto((EventRecord *event)),
1581 	doUpdate proto((EventRecord *event)),
1582 	doActivate proto((EventRecord *event));
1583 
1584 private void p_refresh proto((void));
1585 
1586 private MenuHandle SysMenu;
1587 
1588 private void (*eventlist[]) ptrproto((EventRecord *event)) =
1589 {
1590 	NOFUNC,	/* nullEvent */
1591 	doMouse,	/* mouseDown */
1592 	doMouse,	/* mouseUp */
1593 	dokeyDown,	/* keyDown */
1594 	NOFUNC,	/* keyUp */
1595 	dokeyDown,	/* autoKey */
1596 	doUpdate,	/* updateEvt */
1597 	NOFUNC,	/* diskEvt */
1598 	doActivate,	/* activateEvt */
1599 	NOFUNC,	/* not  used */
1600 	NOFUNC,	/* networkEvt = 10 */
1601 	NOFUNC,	/* driverEvt */
1602 	NOFUNC,	/* app1Evt */
1603 	NOFUNC,	/* app2Evt */
1604 	NOFUNC,	/* app3Evt */
1605 	NOFUNC	/* app4Ev */
1606 };
1607 
1608 
1609 private void
1610 	SetBufMenu proto((void)),
1611 	MarkModes proto((void));
1612 
1613 private void
CheckEvents()1614 CheckEvents()
1615 {
1616 	EventRecord theEvent;
1617 	static long time = 0;
1618 
1619 	static void (*fptr) ptrproto((EventRecord *event));
1620 
1621 	if (FrontWindow() == window) {
1622 		Point Mousep;
1623 
1624 		GetMouse(&Mousep);
1625 		if (PtInRect(Mousep, &r))
1626 			SetCursor(*cross);
1627 		else
1628 			SetCursor(&qd.arrow);
1629 	}
1630 
1631 	SystemTask();
1632 	if (EventCmd && !Keyonly)
1633 		return;
1634 	if (Bufchange)
1635 		SetBufMenu();
1636 	if (Modechange)
1637 		MarkModes();
1638 	while (GetNextEvent(everyEvent, &theEvent)) {
1639 		if ((theEvent.what < NEVENTS) && (fptr = eventlist[theEvent.what])) {
1640 			(*fptr)(&theEvent);
1641 		}
1642 		SystemTask();
1643 	}
1644 	if (TimeDisplayed && (Ticks - time) > 3600) {
1645 		time = Ticks;
1646 		UpdModLine = YES;
1647 		redisplay();
1648 	}
1649 }
1650 
1651 private void InitLocalMenus proto((void));
1652 
1653 private void
InitSysMenu()1654 InitSysMenu()
1655 {
1656 	SysMenu = NewMenu(SYS_ID, "\p\24");
1657 	AppendMenu(SysMenu, "\pAbout Jove");
1658 	AddResMenu(SysMenu, 'DRVR');
1659 	InsertMenu(SysMenu, 0);
1660 	InitLocalMenus();
1661 	DrawMenuBar();
1662 }
1663 
1664 private void
1665 	doWind proto((EventRecord *event, WindowPtr window)),
1666 	doGoAway proto((EventRecord *event, WindowPtr window)),
1667 	doSysMenu proto((EventRecord *event, WindowPtr window)),
1668 	doSysClick proto((EventRecord *event, WindowPtr window)),
1669 	doDrag proto((EventRecord *event, WindowPtr window)),
1670 	doGrow proto((EventRecord *event, WindowPtr window)),
1671 	doZoomIn proto((EventRecord *event, WindowPtr window)),
1672 	doZoomOut proto((EventRecord *event, WindowPtr window));
1673 
1674 #define NMEVENTS 9
1675 
1676 private void (*mouselist[]) ptrproto((EventRecord *event, WindowPtr window)) =
1677 {
1678 	(void (*) ptrproto((EventRecord *event, WindowPtr window)))NULL,	/* inDesk */
1679 	doSysMenu,	/* inMenuBar */
1680 	doSysClick,	/* inSysWindow */
1681 	doWind,	/* inContent */
1682 	doDrag,	/* inDrag */
1683 	doGrow,	/* inGrow */
1684 	doGoAway,	/* inGoAway */
1685 	doZoomIn,	/* inZoomIn */
1686 	doZoomOut	/* inZoomOut */
1687 };
1688 
1689 
1690 private void
doMouse(event)1691 doMouse(event)
1692 EventRecord *event;
1693 {
1694 	if (Keyonly) {
1695 		if (event->what == mouseDown)
1696 			SysBeep(2);
1697 	} else {
1698 		WindowPtr theWindow;
1699 		int wpart = FindWindow(event->where, &theWindow);
1700 		void (*fptr) ptrproto((EventRecord *event, WindowPtr window));
1701 
1702 		if (wpart < NMEVENTS && (fptr = mouselist[wpart]) != NULL)
1703 			(*fptr)(event, theWindow);
1704 	}
1705 }
1706 
1707 private void ProcMenu proto((int menuno, int itemno));
1708 
1709 private void
doSysMenu(event,window)1710 doSysMenu(event, window)
1711 EventRecord *event;
1712 WindowPtr window;
1713 {
1714 	long result = MenuSelect(event->where);
1715 	int	Menu = (result >> 16) & 0xffff;
1716 	int	Item = result & 0xffff;
1717 
1718 	if (Item == 0)
1719 		return;	/* no choice made */
1720 
1721 	if (Menu == SYS_ID) {			/* apple menu */
1722 		Str255 Name;
1723 		GrafPtr Port;
1724 
1725 		if (Item == 1) {
1726 			about_j();
1727 		} else {
1728 			GetItem(SysMenu, Item, Name);
1729 			GetPort(&Port);
1730 			OpenDeskAcc(Name);
1731 			SetPort(Port);
1732 		}
1733 	} else {
1734 		ProcMenu(Menu, Item);
1735 	}
1736 	HiliteMenu(0);
1737 	EventCmd = YES;
1738 	menus_on();
1739 }
1740 
1741 private void
doSysClick(event,window)1742 doSysClick(event, window)
1743 EventRecord *event;
1744 WindowPtr window;
1745 {
1746 	SystemClick(event, window);
1747 }
1748 
1749 
1750 private void
doUpdate(event)1751 doUpdate(event)
1752 EventRecord *event;
1753 {
1754 	WindowPtr
1755 		theWindow = (WindowPtr) event->message,
1756 		oldPort;
1757 
1758 	GetPort(&oldPort);
1759 	SetPort(theWindow);
1760 	BeginUpdate(theWindow);
1761 	p_refresh();
1762 	drawfluff();
1763 	EndUpdate(theWindow);
1764 	SetPort(oldPort);
1765 }
1766 
1767 private void
doActivate(event)1768 doActivate(event)
1769 EventRecord *event;
1770 {
1771 	WindowPtr theWindow = (WindowPtr) event->message;
1772 	ControlHandle control;
1773 	int hilite;
1774 
1775 	SetPort(theWindow);
1776 	hilite = (event->modifiers & activeFlag)? 0 : 255;
1777 	for (control = (ControlHandle) (((WindowPeek) theWindow)->controlList)
1778 	; (control != 0); control = (*control)->nextControl)
1779 	{
1780 			HiliteControl(control, hilite);
1781 	}
1782 }
1783 
1784 /* Keyboard routines. */
1785 
1786 /* Keycodes (from Inside MacIntosh I-251).  This table is ONLY used when
1787  * we are trying to make the Option key work as a Meta key.  When we are
1788  * doing this, the system-supplied character is wrong, so we retranslate
1789  * the key code to a character code.
1790  *
1791  * Since we only use this table when the character generated by an
1792  * option-modified key is greater than DEL, and since the Option
1793  * modifier does not so affect keypad keys, we need not provide for
1794  * them in this table.
1795  *
1796  * ??? This may need to be updated to reflect keyboards newer than the Mac+!
1797  */
1798 
1799 #define NOKEY '?'
1800 
1801 private char nsh_keycodes[] = {
1802 	'a','s','d','f','h',					/* 00 - 04 */
1803 	'g','z','x','c','v',					/* 05 - 09 */
1804 	NOKEY,'b','q','w','e',					/* 0A - 0E */
1805 	'r','y','t','1','2',					/* 0F - 13 */
1806 	'3','4','6','5','=',					/* 14 - 18 */
1807 	'9','7','-','8','0',					/* 19 - 1D */
1808 	']','O','u','[','i',					/* 1E - 22 */
1809 	'p',CR,'l','j','\'',					/* 23 - 27 */
1810 	'k',';','\\',',','/',					/* 28 - 2C */
1811 	'n','m','.','\t',NOKEY,					/* 2D - 31 */
1812 	'`',DEL									/* 32 - 33*/
1813 };
1814 
1815 private char sh_keycodes[] = {
1816 	'A','S','D','F','H',					/* 00 - 04 */
1817 	'G','Z','X','C','V',					/* 05 - 09 */
1818 	NOKEY,'B','Q','W','E',					/* 0A - 0E */
1819 	'R','Y','T','!','@',					/* 0F - 13 */
1820 	'#','$','^','%','+',					/* 14 - 18 */
1821 	'(','&','_','*',')',					/* 19 - 1D */
1822 	'}','O','U','{','I',					/* 1E - 22 */
1823 	'P',CR,'L','J','\'',					/* 23 - 27 */
1824 	'K',';','|','<','?',					/* 28 - 2C */
1825 	'N','M','>','\t',NOKEY,					/* 2D - 31 */
1826 	'~',DEL									/* 32 - 33 */
1827 };
1828 
1829 /* (ORIGINALLY IN) tkey.c
1830    keyboard routines for Macintosh. K Mitchum 12/86 */
1831 
1832 jmp_buf auxjmp;
1833 
1834 private nchars = 0;
1835 private char charbuf[MCHARS];
1836 
1837 /* The following kludges a meta key out of the option key by
1838    sending an escape sequence back to the dispatch routines.  This is
1839    not elegant but it works, and doesn't alter escape sequences for
1840    those that prefer them.  To remap the control or meta keys,
1841    see mackeys.h. */
1842 
1843 private void
dokeyDown(event)1844 dokeyDown(event)
1845 EventRecord *event;
1846 {
1847 	unsigned mods;
1848 	int c;
1849 	static int cptr = 0;
1850 
1851 	if (MCHARS - nchars < 2)
1852 		return;
1853 
1854 	c  = event->message & charCodeMask;
1855 
1856 	mods = event->modifiers;
1857 
1858 	if (MetaKey && (mods & optionKey)) {
1859 		/* Treat the Option key as a Meta key.
1860 		 * We have to "undo" the normal option key effect.
1861 		 * This means that, if the character is greater than DEL
1862 		 * and the code is known to our table, we retranslate the
1863 		 * code into a character.
1864 		 * This seems pretty dubious.  I wonder if "KeyTrans" would
1865 		 * be a better tool.
1866 		 */
1867 		int	code = (event->message & keyCodeMask) >> 8;
1868 
1869 		if (c > DEL && code < elemsof(sh_keycodes))
1870 			c  = ((mods & shiftKey)? sh_keycodes : nsh_keycodes)[code];
1871 
1872 		/* jam an ESC prefix */
1873 		charbuf[cptr++] = ESC;
1874 		cptr &= NCHMASK;
1875 		nchars++;
1876 	}
1877 
1878 	if (mods & (cmdKey | controlKey)) {
1879 		/* control key (command key is treated as a control key too) */
1880 		if (c == '@' || c == '2' || c == ' ')
1881 			c = '\0';	/* so we have a null char */
1882 		if (c != '`')
1883 			c = CTL(c);		/* make a control char */
1884 	} else if (c == '`') {
1885 		c = ESC;	/* for those used to escapes */
1886 	}
1887 
1888 	charbuf[cptr++] = c;
1889 	cptr &= NCHMASK;
1890 	nchars++;
1891 }
1892 
1893 private ZXchar
rawgetc()1894 rawgetc()
1895 {
1896 	static int cptr = 0;
1897 	ZXchar c;
1898 
1899 	if (EventCmd)
1900 		longjmp(auxjmp, 1);
1901 
1902 	while (nchars <= 0) {
1903 		nchars = 0;
1904 		if (EventCmd)
1905 			longjmp(auxjmp, 1);
1906 
1907 		CheckEvents();	/* ugh! WAIT for a character */
1908 	}
1909 	nchars--;
1910 	c = ZXRC(charbuf[cptr++]);
1911 	cptr &= NCHMASK;		/* zero if necessary */
1912 	return c;
1913 }
1914 
1915 bool
rawchkc()1916 rawchkc()
1917 {
1918 	if (EventCmd)
1919 		longjmp(auxjmp, 1);
1920 
1921 	if (nchars == 0)
1922 		CheckEvents();	/* this should NOT be necessary! */
1923 	return nchars > 0;
1924 }
1925 
1926 /* Routines for calling the standard file dialogs, when macify is YES.
1927    If the user changes the directory using the file dialogs, Jove's notion
1928    of the current directory is updated. */
1929 
1930 
1931 /* (ORIGINALLY IN) tmacf.c. K. Mitchum 12/86.
1932    Macify routines for jove. */
1933 
1934 int CurrentVol;			/* see tfile.c */
1935 
1936 #define TYPES  (-1)
1937 
1938 private Point px = {100, 100};
1939 private unsigned char pmess[] = "\pSave file as: ";
1940 
1941 private pascal Boolean
Ffilter(p)1942 Ffilter(p)
1943 ParmBlkPtr p;
1944 {
1945 	Boolean r;
1946 	char	*name;
1947 
1948 	if (p->fileParam.ioFlFndrInfo.fdType == 'APPL')
1949 		return YES;
1950 
1951 	/* Filter out our tempfiles.
1952 	 * ??? the test doesn't check to see if the directories match.
1953 	 */
1954 	name = PtoCstr(p->fileParam.ioNamePtr);
1955 	r = strcmp(name, ".joveXXX") == 0
1956 #ifdef ABBREV
1957 		|| strcmp(name, ".jabbXXX") == 0
1958 #endif
1959 #ifdef RECOVER
1960 		|| strcmp(name, ".jrecXXX") == 0
1961 #endif
1962 		;
1963 	CtoPstr(name);
1964 	return r;
1965 }
1966 
1967 private void
check_dir()1968 check_dir()
1969 {
1970 	if (cur_vol != 0 - SFSaveDisk || cur_dir != CurDirStore) {
1971 		char	space[FILESIZE];
1972 
1973 		setdir(0 - SFSaveDisk, CurDirStore);
1974 		UpdModLine = YES;	/* make sure jove knows the change */
1975 		Modechange = YES;
1976 		setCWD(getcwd(space, sizeof(space)));
1977 	}
1978 }
1979 
1980 char *
gfile(namebuf)1981 gfile(namebuf)	/* return a filename to get */
1982 char *namebuf;
1983 {
1984 	SFReply frec;
1985 	char ans[FILESIZE];
1986 
1987 	SFSaveDisk = 0 - cur_vol;	/* in case a Desk Accessory changed them */
1988 	CurDirStore = cur_dir;
1989 	SFGetFile(px, 0L, Ffilter, TYPES, 0L, 0L, &frec);
1990 	check_dir();	/* see if any change, set if so */
1991 	if (frec.good) {
1992 		EventRecord theEvent;
1993 
1994 		do; while (GetNextEvent(updateMask, &theEvent) == 0);
1995 		doUpdate(&theEvent);
1996 		strcpy(ans, PtoCstr(frec.fName));
1997 		CtoPstr((char *)frec.fName);
1998 		PathParse(ans, namebuf);
1999 		return namebuf;
2000 	}
2001 	return NULL;
2002 }
2003 
2004 char *
pfile(namebuf)2005 pfile(namebuf)
2006 char *namebuf;
2007 {
2008 	SFReply frec;
2009 	StringPtr nm;
2010 
2011 	SFSaveDisk = 0 - cur_vol;	/* in case a Desk Accessory changed them */
2012 	CurDirStore = cur_dir;
2013 	strncpy(namebuf, filename(curbuf), FILESIZE-1);
2014 	nm = cvt_fnm(namebuf);
2015 	SFPutFile(px, pmess, nm, 0L, &frec);
2016 	check_dir();	/* see if any change, set if so */
2017 	if (frec.good) {
2018 		EventRecord theEvent;
2019 		char *h, *p;
2020 
2021 		do; while (GetNextEvent(updateMask, &theEvent) == 0);
2022 		doUpdate(&theEvent);
2023 		h = PtoCstr(frec.fName);
2024 		while (*h == ':')
2025 			h++;	/* convert to unix style */
2026 		for (p = h; (p = strchr(p, ':')) != NULL; )
2027 			*p++ = '/';
2028 		PathParse(h, namebuf);
2029 		return namebuf;
2030 	}
2031 	return NULL;
2032 }
2033 
2034 
2035 /* getArgs() returns an argument list based on documents clicked on by the user. */
2036 
2037 int
getArgs(avp)2038 getArgs(avp)
2039 char ***avp;
2040 {
2041 	int argc, old_vol;
2042 	short nargs, type;
2043 	long old_dir;
2044 	char **argv;
2045 	char *pathname;
2046 	AppFile p;
2047 	WDPBRec d;
2048 
2049 	old_vol = cur_vol;
2050 	old_dir = cur_dir;
2051 
2052 	CountAppFiles(&type, &nargs);
2053 	if (nargs > 0) {	/* files to open... */
2054 		argv = (char **) emalloc((nargs + 2) * sizeof(char *));
2055 		for (argc = 1; argc <= nargs; argc++) {
2056 			GetAppFiles(argc, &p);
2057 			if (type == 0) {
2058 				char	space[FILESIZE];
2059 
2060 				PtoCstr((StringPtr)p.fName);
2061 				d.ioCompletion = 0;
2062 				d.ioNamePtr = NULL;
2063 				d.ioVRefNum = p.vRefNum;
2064 				d.ioWDIndex = 0;
2065 				PBGetWDInfo(&d, 0);
2066 				cur_vol = d.ioWDVRefNum;
2067 				cur_dir = d.ioWDDirID;
2068 				pathname = getcwd(space, sizeof(space));
2069 				argv[argc] = emalloc(strlen((char *)p.fName) + strlen(pathname) + 2);
2070 				strcpy(argv[argc], pathname);
2071 				strcat(argv[argc], "/");
2072 				strcat(argv[argc], (char *)p.fName);
2073 			}
2074 			ClrAppFiles(argc);
2075 		}
2076 		if (type != 0)
2077 			argc = 1;
2078 	} else {
2079 		argv = (char **) emalloc(2 * sizeof(char*));
2080 		argc = 1;
2081 	}
2082 	argv[0] = "jove";
2083 
2084 	argv[argc] = NULL;
2085 	*avp = argv;
2086 	cur_dir = old_dir;
2087 	cur_vol = old_vol;
2088 	return argc;
2089 }
2090 
2091 char *
mktemp(name)2092 mktemp(name)
2093 char *name;
2094 {
2095 	return name;	/* what, me check? */
2096 }
2097 
2098 
2099 /* Menu routines.  The menus items are set up in a similar manner as keys, and
2100    are bound prior to runtime.  See menumaps.txt, which must be run through
2101    setmaps.  Unlike keys, menu items may be bound to variables, and to
2102    buffers.  Buffer binding is only done at runtime. */
2103 
2104 private void
2105 	InitMenu proto((struct menu *M)),
2106 	make_edits proto((int menu));
2107 
2108 private void
InitLocalMenus()2109 InitLocalMenus()
2110 {
2111 	int i;
2112 
2113 	for (i = 0; i < NMENUS; i++) {
2114 		InitMenu(&Menus[i]);
2115 		if (i == 0)
2116 			make_edits(Menus[i].menu_id + 1);
2117 	}
2118 }
2119 
2120 private void
InitMenu(M)2121 InitMenu(M)
2122 struct menu *M;
2123 {
2124 	int i;
2125 	StringPtr ps;
2126 
2127 	if (M->menu_id == 0)
2128 		return;
2129 
2130 	M->Mn = NewMenu(M->menu_id, ps=CtoPstr(M->Name));
2131 	PtoCstr(ps);
2132 
2133 	for (i = 0; i < NMENUITEMS; i++) {
2134 		data_obj *d = M->m[i];
2135 
2136 		if (d == NULL)
2137 			break;	/* last item... */
2138 
2139 		switch (d->Type & TYPEMASK) {
2140 		case STRING:
2141 			AppendMenu(M->Mn, ps=CtoPstr(d->Name));
2142 			PtoCstr(ps);
2143 			break;
2144 		case VARIABLE:
2145 			AppendMenu(M->Mn, ps=CtoPstr(d->Name));
2146 			PtoCstr(ps);
2147 			if ((((struct variable *)d)->v_flags & V_TYPEMASK) == V_BOOL
2148 			&& *(bool *)(((struct variable *)d)->v_value))
2149 				CheckItem(M->Mn, i + 1, YES);
2150 			break;
2151 		case COMMAND:
2152 			AppendMenu(M->Mn, ps=CtoPstr(d->Name));
2153 			PtoCstr(ps);
2154 			break;
2155 		}
2156 	}
2157 	InsertMenu(M->Mn, 0);
2158 }
2159 
2160 private void	MacSetVar proto((struct variable *vp, int mnu, int itm));
2161 
2162 private void
ProcMenu(menuno,itemno)2163 ProcMenu(menuno, itemno)
2164 int menuno, itemno;
2165 {
2166 	int i;
2167 	data_obj *d;
2168 
2169 	for (i = 0; i < NMENUS; i++) {
2170 		if (Menus[i].menu_id == menuno) {
2171 			itemno--;
2172 			d = Menus[i].m[itemno];
2173 			switch(d->Type & TYPEMASK) {
2174 			case COMMAND:
2175 				ExecCmd((data_obj *) d);
2176 				break;
2177 			case BUFFER:
2178 				SetABuf(curbuf);
2179 				tiewind(curwind, (Buffer *) d);
2180 				SetBuf((Buffer *) d);
2181 				break;
2182 			case VARIABLE:
2183 				MacSetVar((struct variable *) d, i, itemno);
2184 				break;
2185 			}
2186 			break;
2187 		}
2188 	}
2189 }
2190 
2191 
2192 private void
make_edits(menu)2193 make_edits(menu)	/* add dummy edit menu */
2194 int menu;
2195 {
2196 	MenuHandle M;
2197 	int item;
2198 	char *fname;
2199 
2200 	M = NewMenu((menu), "\pEdit");
2201 	AppendMenu(M,
2202 		"\pUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear;Select All;(-;Show Clipboard");
2203 	InsertMenu(M, 0);
2204 	DisableItem(M, 0);
2205 }
2206 
2207 void
menus_off()2208 menus_off()
2209 {
2210 	int i;
2211 
2212 	if (Keyonly || EventCmd)
2213 		return;
2214 
2215 #ifdef MENU_DISABLE		/* NOBODY likes this, but it's here if you want it... */
2216 	DisableItem(SysMenu, 0);
2217 	for (i = 0; i < NMENUS; i++)
2218 		if (Menus[i].Mn)
2219 			DisableItem(Menus[i].Mn, 0);
2220 	DrawMenuBar();
2221 #endif
2222 	Keyonly = YES;
2223 }
2224 
2225 void
menus_on()2226 menus_on()
2227 {
2228 	int i;
2229 
2230 	if (!Keyonly)
2231 		return;
2232 
2233 #ifdef MENU_DISABLE
2234 	EnableItem(SysMenu, 0);
2235 	for (i = 0; i < NMENUS; i++)
2236 		if (Menus[i].Mn)
2237 			EnableItem(Menus[i].Mn, 0);
2238 	DrawMenuBar();
2239 #endif
2240 	Keyonly = NO;
2241 }
2242 
2243 private char *
BufMPrint(b,i)2244 BufMPrint(b, i)
2245 Buffer	*b;
2246 int	i;
2247 {
2248 	char *p;
2249 	char *nm = filename(b);
2250 	char t[35];
2251 
2252 	if (strlen(nm) > 30) {
2253 		strcpy(t, "...");
2254 		strcat(t, nm + strlen(nm) - 30);
2255 	} else {
2256 		strcpy(t, nm);
2257 	}
2258 	nm = t;
2259 	while (*nm) {
2260 		switch(*nm) {	/* ugh... these are metacharacter for Menus */
2261 		case '/':
2262 			*nm = ':';
2263 			break;
2264 		case '^':
2265 		case '!':
2266 		case '<':
2267 		case '(':
2268 		case ';':
2269 			*nm = '.';
2270 			break;	/* that will confuse everybody */
2271 		}
2272 		nm++;
2273 	}
2274 	p = sprint("%-2d %-11s \"%-s\"", i, b->b_name, t);
2275 	return p;
2276 }
2277 
2278 private void
SetBufMenu()2279 SetBufMenu()
2280 {
2281 	Buffer *b;
2282 	int i, j, stop;
2283 	struct menu *M;
2284 
2285 	Bufchange = NO;
2286 	for (i = 0; i < NMENUS; i++) {
2287 		if (strcmp(Menus[i].Name, "Buffer") == 0) {
2288 			M = &Menus[i];
2289 			for (j = 0; j < NMENUITEMS; j++) {
2290 				data_obj *d = Menus[i].m[j];
2291 
2292 				if (d == NULL)
2293 					break;
2294 
2295 				if ((d->Type & TYPEMASK) == BUFFER) {
2296 					for (i = j, b = world; i < NMENUITEMS && b != NULL; i++, b = b->b_next) {
2297 
2298 						if (M->m[i] == NULL)
2299 							AppendMenu(M->Mn, CtoPstr(BufMPrint(b, i-j+1)));	/* add the item */
2300 						else
2301 							SetItem(M->Mn, i + 1, CtoPstr(BufMPrint(b, i-j+1)));	/* or change it */
2302 						M->m[i] = (data_obj *) b;
2303 					}
2304 					stop = i;
2305 					/* out of buffers? */
2306 					for (; i < NMENUITEMS && M->m[i]; i++) {
2307 						DelMenuItem(M->Mn, stop + 1);	/* take off last item */
2308 						M->m[i] = NULL;
2309 					}
2310 					break;
2311 				}
2312 			}
2313 			break;
2314 		}
2315 	}
2316 }
2317 
2318 private void
MacSetVar(vp,mnu,itm)2319 MacSetVar(vp, mnu, itm)	/* Set a variable from the menu */
2320 struct variable *vp;
2321 int mnu, itm;
2322 {
2323 	if ((vp->v_flags & V_TYPEMASK) == V_BOOL) {
2324 		/* toggle the value */
2325 		*((bool *) vp->v_value) = !*((bool *) vp->v_value);
2326 		MarkVar(vp, mnu, itm);
2327 	} else {
2328 		char	prompt[128];
2329 
2330 		swritef(prompt, sizeof(prompt), "Set %s: ", vp->Name);
2331 		vset_aux(vp, prompt);
2332 	}
2333 }
2334 
2335 private void
MarkModes()2336 MarkModes()
2337 {
2338 	int mnu, itm;
2339 	data_obj *d;
2340 
2341 	Modechange = NO;
2342 	for (mnu = 0; mnu < NMENUS; mnu++) {
2343 		for (itm = 0; itm < NMENUITEMS; itm++) {
2344 			if ((d = Menus[mnu].m[itm]) == NULL)
2345 				break;
2346 
2347 			if ((d->Type & (MAJOR_MODE | MINOR_MODE))
2348 			|| ((d->Type & TYPEMASK) == BUFFER))
2349 			{
2350 				bool	checked;
2351 
2352 				if (d->Type & (MAJOR_MODE))
2353 					checked = curbuf->b_major == (d->Type >> 8);
2354 				else if (d->Type & (MINOR_MODE))
2355 					checked = (curbuf->b_minor & (d->Type >> 8)) != 0;
2356 				else
2357 					checked = d == (data_obj *) curbuf;
2358 				CheckItem(Menus[mnu].Mn, itm + 1, checked);
2359 			}
2360 		}
2361 	}
2362 }
2363 
2364 void
MarkVar(vp,mnu,itm)2365 MarkVar(vp, mnu, itm)	/* mark a boolean menu item */
2366 const struct variable *vp;
2367 int mnu, itm;
2368 {
2369 	if (mnu == -1) {		/* we don't know the item... slow */
2370 		for (mnu = 0; ; mnu++) {
2371 			if (mnu >= NMENUS)
2372 				return;	/* not found */
2373 			for (itm = 0; (itm < NMENUITEMS); itm++) {
2374 				if ((struct variable *) (Menus[mnu].m[itm]) == vp)
2375 					break;
2376 			}
2377 			if (itm < NMENUITEMS)
2378 				break;
2379 		}
2380 	}
2381 	CheckItem(Menus[mnu].Mn, itm + 1, *(bool *)vp->v_value);
2382 }
2383 
2384 /* Screen routines and driver. The Macinitosh Text Edit routines are not utilized,
2385    as they are slow and cumbersome for a terminal emulator. Instead, direct QuickDraw
2386    calls are used. The fastest output is obtained writing a line at a time, rather
2387    than on a character basis, so the major output routine is writechr(), which takes
2388    a pascal-style string as an argument. See do_sputc() in screen.c. */
2389 
2390 void
Placur(line,col)2391 Placur(line, col)
2392 int line, col;
2393 {
2394 	CapCol = col;
2395 	CapLine = line;
2396 	putcurs(line, col, YES);
2397 }
2398 
2399 void
NPlacur(line,col)2400 NPlacur(line, col)
2401 int line, col;
2402 {
2403 	CapCol = col;
2404 	CapLine = line;
2405 	putcurs(line, col, NO);
2406 }
2407 
2408 void
i_lines(top,bottom,num)2409 i_lines(top, bottom, num)
2410 int top, bottom, num;
2411 {
2412 	Placur(bottom - num + 1, 0);
2413 	dellines(num, bottom);
2414 	Placur(top, 0);
2415 	inslines(num, bottom);
2416 }
2417 
2418 void
d_lines(top,bottom,num)2419 d_lines(top, bottom, num)
2420 int top, bottom, num;
2421 {
2422 	Placur(top, 0);
2423 	dellines(num, bottom);
2424 	Placur(bottom + 1 - num, 0);
2425 	inslines(num, bottom);
2426 }
2427 
2428 /* (ORIGINALLY IN) tn.c   */
2429 /* window driver for MacIntosh using windows. */
2430 /* K. Mitchum 9/86 */
2431 
2432 
2433 /*#define VARFONT*/
2434 #ifdef VARFONT
2435 private height, width, theight, twidth, descent;
2436 #else
2437 # define height HEIGHT
2438 # define width WIDTH
2439 # define theight THEIGHT
2440 # define twidth TWIDTH
2441 # define descent DESCENT
2442 #endif
2443 
2444 private int trow, tcol;
2445 private bool	cursvis;
2446 #ifdef NEVER
2447 private bool insert;
2448 #endif
2449 private Rect cursor_rect;
2450 private char *p_scr, *p_curs;	/* physical screen and cursor */
2451 private int p_size;
2452 
2453 private Rect  vRect;
2454 private WindowRecord myWindowRec;
2455 
2456 #define active() SetPort(theScreen)
2457 #define maxadjust(r) OffsetRect((r), 0, 2)
2458 
2459 private char *
conv_p_curs(row,col)2460 conv_p_curs(row, col)
2461 int	row,
2462 	col;
2463 {
2464 	return p_scr + (row * (CO)) + col;
2465 }
2466 
2467 #ifdef NEVER
2468 private void
INSmode(new)2469 INSmode(new)
2470 bool new;
2471 {
2472 	insert = new;
2473 }
2474 #endif
2475 
2476 void
SO_effect(new)2477 SO_effect(new)
2478 bool new;
2479 {
2480 	theScreen->txMode = new? notSrcCopy : srcCopy;
2481 }
2482 
2483 private void	init_slate proto((void));
2484 
2485 private void
tn_init()2486 tn_init()
2487 {
2488 #ifdef NEVER
2489 	INSmode(NO);
2490 #endif
2491 	init_slate();
2492 	SO_off();
2493 	ShowPen();
2494 }
2495 
2496 void
clr_page()2497 clr_page()	/* clear and home function */
2498 {
2499 	Rect r;
2500 
2501 	memset(p_scr, ' ', p_size);
2502 	active();
2503 	SetRect(&r, 0, 0, WINDWIDTH, WINDHEIGHT);
2504 	EraseRect(&r);
2505 	putcurs(0, 0, NO);	/* ??? "NO" guess by DHR */
2506 	drawfluff();
2507 }
2508 
2509 private void
putcurs(row,col,vis)2510 putcurs(row, col, vis)
2511 unsigned	row, col;
2512 bool	vis;
2513 {
2514 	active();
2515 	curset(NO);
2516 	trow = row;
2517 	tcol = col;
2518 	curset(vis);
2519 }
2520 
2521 private void
curset(invert)2522 curset(invert)
2523 bool	invert;
2524 {
2525 	int
2526 		colpix = tcol * width,
2527 		rowpix = trow * height;
2528 
2529 	if (trow == MAXROW)
2530 		rowpix += 2;	/* leave space for 2 pixel rule */
2531 	p_curs = conv_p_curs(trow, tcol);
2532 	MoveTo(colpix, rowpix + height - descent);
2533 	DrawChar(*p_curs);
2534 	cursvis = invert;
2535 	if (invert) {
2536 		SetRect(&cursor_rect, colpix, rowpix,
2537 			colpix + width - 1, rowpix + height - 1);
2538 		InvertRect(&cursor_rect);
2539 	}
2540 	MoveTo(colpix, rowpix + height - descent);
2541 }
2542 
2543 void
clr_eoln()2544 clr_eoln()
2545 {
2546 		Rect r;
2547 
2548 		active();
2549 		SetRect(&r, tcol * width, trow * height, WINDWIDTH, (trow +1) * height);
2550 		if (trow == MAXROW)
2551 			maxadjust(&r);
2552 		EraseRect(&r);
2553 		memset(p_curs, ' ', CO - tcol);
2554 		curset(YES);
2555 }
2556 
2557 #ifdef NEVER
2558 private void
delchars()2559 delchars()
2560 {
2561 	Rect r;
2562 	RgnHandle updateRgn;
2563 
2564 	active();
2565 	curset(NO);
2566 	updateRgn = NewRgn();
2567 	SetRect(&r, tcol * width, trow * height, twidth - width, (trow+1) * height);
2568 	if (trow == MAXROW)
2569 		maxadjust(&r);
2570 	ScrollRect(&r, -width, 0, updateRgn);
2571 	DisposeRgn(updateRgn);
2572 	BlockMove(p_curs + 1, p_curs, (long) (MAXCOL - tcol));
2573 	*conv_p_curs(trow, MAXCOL) = ' ';
2574 	curset(YES);
2575 }
2576 #endif /* NEVER */
2577 
2578 private void
dellines(n,bot)2579 dellines(n, bot)
2580 int n, bot;
2581 {
2582 	RgnHandle updateRgn = NewRgn();
2583 	Rect r;
2584 	long len;
2585 
2586 	active();
2587 	curset(NO);
2588 	SetRect(&r, 0, ((trow) * height), WINDWIDTH, ((bot + 1) * height));
2589 	ScrollRect(&r, 0, 0 - (n * height), updateRgn);
2590 	DisposeRgn(updateRgn);
2591 	len = ((bot - trow - n + 1) * CO);
2592 	BlockMove(conv_p_curs(trow + n, 0), conv_p_curs(trow, 0), len);
2593 	memset(conv_p_curs(bot - n + 1, 0), ' ', n * CO);
2594 	putcurs(trow, 0, YES);	/* ??? "YES" guess by DHR */
2595 }
2596 
2597 private void
inslines(n,bot)2598 inslines(n, bot)
2599 int n, bot;
2600 {
2601 	RgnHandle updateRgn = NewRgn();
2602 	Rect r;
2603 	long len;
2604 
2605 	active();
2606 	curset(NO);
2607 	SetRect(&r, 0, trow * height, WINDWIDTH, (bot +1) * height);
2608 	ScrollRect(&r, 0, (n * height), updateRgn);
2609 	DisposeRgn(updateRgn);
2610 	len = ((bot - trow - n +1) * CO);
2611 	BlockMove(conv_p_curs(trow, 0), conv_p_curs(trow + n, 0), len);
2612 	memset(conv_p_curs(trow, 0), ' ', (n * CO));
2613 	putcurs(trow, 0, YES);	/* ??? "YES" guess by DHR */
2614 }
2615 
2616 void
writetext(str,len)2617 writetext(str, len)
2618 const unsigned char *str;
2619 size_t	len;
2620 {
2621 	active();
2622 	curset(NO);
2623 #ifdef NEVER
2624 	if (insert) {
2625 		RgnHandle updateRgn = NewRgn();
2626 		Rect r;
2627 
2628 		SetRect(&r, tcol * width, trow * height, twidth - width * len, (trow +1) * height -1);
2629 		if (trow == MAXROW)
2630 			maxadjust(&r);
2631 		ScrollRect(&r, width * len, 0, updateRgn);
2632 		DisposeRgn(updateRgn);
2633 	}
2634 #endif
2635 	DrawText(str, (short)0, (short)len);
2636 #ifdef NEVER
2637 	if (insert)
2638 		BlockMove(p_curs, p_curs + len, (long) (CO - tcol - len));
2639 #endif
2640 	memcpy((UnivPtr)p_curs, (UnivPtr)str, len);
2641 	putcurs(trow, tcol+len <= MAXCOL? tcol+len : MAXCOL, YES);	/* ??? "YES" guess by DHR */
2642 }
2643 
2644 private Rect myBoundsRect;
2645 
2646 private void
init_slate()2647 init_slate()
2648 {
2649 	FontInfo f;
2650 
2651 	char *Name = "Jove ";
2652 	char *Title;
2653 
2654 	InitGraf(&qd.thePort);
2655 	InitWindows();
2656 	InitCursor();
2657 	InitFonts();
2658 	InitMenus();
2659 	InitDialogs((ProcPtr)NULL);		/* no restart proc */
2660 
2661 	/* figure limiting rectangle for window moves */
2662 	SetRect(&LimitRect,
2663 		qd.screenBits.bounds.left + 3,
2664 		qd.screenBits.bounds.top + 20,
2665 		qd.screenBits.bounds.right - 3,
2666 		qd.screenBits.bounds.bottom -3);
2667 
2668 	Set_std();
2669 	SetBounds();
2670 
2671 	/* initialize char array for updates */
2672 	p_scr = emalloc(p_size = wc_std.w_cols * wc_std.w_rows);	/* only once */
2673 	p_curs = p_scr;
2674 
2675 	Title = sprint("%s%s", Name, jversion);
2676 	theScreen = NewWindow(&myWindowRec, &myBoundsRect, CtoPstr(Title),
2677 		1, 8, (WindowPtr) -1, 1, 0L);
2678 
2679 	/* figure an initial window configuration and adjust it */
2680 	wc = &wc_std;
2681 	wc_user = wc_std;	/* initially, only one configuration to toggle */
2682 	user_state(theScreen) = std_state(theScreen);
2683 	SetPort(theScreen);
2684 
2685 	theScreen->txFont = FONT;
2686 	theScreen->txSize = TEXTSIZE;
2687 
2688 #ifdef VARFONT
2689 	GetFontInfo(&f);
2690 	height = f.ascent+f.descent+f.leading;
2691 	width = f.widMax;
2692 	twidth = width * wc->w_cols;
2693 	theight = height * wc->w_rows;
2694 	descent = f.descent;
2695 #endif
2696 
2697 	theScreen->txMode = srcCopy;
2698 	theScreen->pnMode = patCopy;
2699 	PenNormal();
2700 }
2701 
2702 private void
p_refresh()2703 p_refresh()
2704 {
2705 	int lineno;
2706 
2707 	for (lineno = 0; lineno < LI; lineno++) {
2708 		char *curs = conv_p_curs(lineno, 0);
2709 
2710 		MoveTo(0, (lineno+1) * height - descent + (lineno == MAXROW? 2 : 0));
2711 		/* The following kludgy line is to get SO right.  It depends on:
2712 		 * - !defined(HIGHLIGHTING)
2713 		 * - this routine not being called at an inauspicious time
2714 		 *   i.e. in the middle of a SO output.
2715 		 * - the fact that the last line will non-SO so that the text
2716 		 *   mode will be left non-SO.
2717 		 */
2718 		SO_effect(Screen[lineno].s_effects);
2719 		DrawText(curs, (short)0, (short)CO);
2720 	}
2721 	curset(cursvis);
2722 }
2723 
2724 
2725 private bool
wc_adjust(w,h,wcf,init)2726 wc_adjust(w, h, wcf, init)		/* adjust window config to look nice */
2727 int w, h;
2728 struct wind_config *wcf;
2729 int init;
2730 {
2731 	static int LIMIT_R, LIMIT_C;
2732 	int rows, cols;
2733 
2734 	if (init) {
2735 		LIMIT_R = (h - 4) / HEIGHT;
2736 		LIMIT_C = (w - SCROLLWIDTH - 1) / WIDTH + 1;
2737 	}
2738 	if ((w < WIDTH * 40) ||(h < HEIGHT * 10)	/* too small */
2739 	|| ((rows = (h - 4) / HEIGHT) > LIMIT_R)	/* too big */
2740 	|| ((cols = (w - SCROLLWIDTH - 1) / WIDTH + 1) > LIMIT_C))
2741 		return NO;
2742 
2743 	wcf->w_rows = rows;
2744 	wcf->w_cols = cols;
2745 	wcf->w_width = wcf->w_cols * WIDTH + 1 + SCROLLWIDTH;
2746 	wcf->w_height = wcf->w_rows * HEIGHT + 4;
2747 	return YES;
2748 }
2749 
2750 private int
getCO()2751 getCO()	/* so that jove knows params */
2752 {
2753 	return wc->w_cols;
2754 }
2755 
2756 private int
getLI()2757 getLI()
2758 {
2759 	return wc->w_rows;
2760 }
2761 
2762 void
ttsize()2763 ttsize()
2764 {
2765 	/* ??? We really ought to wait until the screen is big enough:
2766 	 * at least three lines high (one line each for buffer, mode,
2767 	 * and message) and at least twelve columns wide (eight for
2768 	 * line number, one for content, two for overflow indicators,
2769 	 * and one blank at end).
2770 	 */
2771 	/* ??? This should be made more like UNIX version */
2772 	CO = getCO();
2773 	if (CO > MAXCOLS)
2774 		CO = MAXCOLS;
2775 	LI = getLI();
2776 	Windchange = YES;
2777 	clr_page();
2778 	ILI = LI - 1;
2779 }
2780 
2781 private void
SetBounds()2782 SetBounds()
2783 {
2784 	SetRect(&myBoundsRect,
2785 		qd.screenBits.bounds.left + 3,
2786 		qd.screenBits.bounds.top + 40,
2787 		qd.screenBits.bounds.left + 3 + wc_std.w_width,
2788 		qd.screenBits.bounds.top + 40 + wc_std.w_height);
2789 }
2790 
2791 private void
Set_std()2792 Set_std()
2793 {
2794 	(void) wc_adjust(qd.screenBits.bounds.right - qd.screenBits.bounds.left - 6,
2795 		qd.screenBits.bounds.bottom - qd.screenBits.bounds.top - 42,
2796 		&wc_std, 1);
2797 }
2798 
2799 private void
Reset_std()2800 Reset_std()
2801 {
2802 	Set_std();
2803 	std_state(theScreen) = myBoundsRect;
2804 }
2805 #endif /* MAC */
2806