AmendHub

Download

jcs

/

amend

/

util.c

 

(View History)

jcs   util: xGetStringAsChar: Properly release resource after use Latest amendment: 77 on 2022-06-15

1 /*
2 * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "util.h"
23
24 /* ALRT resources */
25 #define ASK_ALERT_ID 130
26
27 #define ERROR_STRING_SIZE 1024
28 static char err_str[ERROR_STRING_SIZE];
29
30 /* basic DITL with an ok button (1), text area (2), and icon (3) */
31 #define ALERT_DITL_ICON 3
32 static const char alert_ditl[] = {
33 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
34 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x02,
35 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
36 0x00, 0x32, 0x00, 0x40, 0x01, 0x21, 0x08, 0x02,
37 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
38 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A, 0xA0, 0x02,
39 0x00, 0x02
40 };
41 static Handle alert_ditl_h = NULL;
42
43 /* DITL with a Yes button (1), No button (2), text (3), and icon (4) */
44 static const char ask_ditl[] = {
45 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
46 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x03,
47 0x59, 0x65, 0x73, 0x21, 0x00, 0x00, 0x00, 0x00,
48 0x00, 0x46, 0x00, 0xA0, 0x00, 0x5A, 0x00, 0xDA,
49 0x04, 0x02, 0x4E, 0x6F, 0x00, 0x00, 0x00, 0x00,
50 0x00, 0x0A, 0x00, 0x32, 0x00, 0x41, 0x01, 0x22,
51 0x08, 0x02, 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00,
52 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A,
53 0xA0, 0x02, 0x00, 0x01
54 };
55 static Handle ask_ditl_h = NULL;
56
57 /* DITL with just a text view */
58 static const char progress_ditl[] = {
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
60 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02,
61 0x5E, 0x30
62 };
63 static Handle progress_ditl_h = NULL;
64 static DialogPtr progress_dialog = NULL;
65
66 enum {
67 STOP_ALERT,
68 CAUTION_ALERT,
69 NOTE_ALERT
70 };
71
72 static TEHandle track_control_te = NULL;
73
74 void vwarn(short alert_func, const char *format, va_list ap);
75
76 /*
77 * Memory functions
78 */
79
80 void *
81 xmalloc(size_t size)
82 {
83 void *ptr;
84
85 if (size == 0)
86 panic("xmalloc: zero size");
87 ptr = malloc(size);
88 if (ptr == NULL)
89 panic("xmalloc(%lu) failed", size);
90 return ptr;
91 }
92
93 void *
94 xmalloczero(size_t size)
95 {
96 void *ptr = xmalloc(size);
97 memset(ptr, 0, size);
98 return ptr;
99 }
100
101 void *
102 xcalloc(size_t nmemb, size_t size)
103 {
104 void *ptr;
105
106 ptr = calloc(nmemb, size);
107 if (ptr == NULL)
108 panic("xcalloc(%lu, %lu) failed", nmemb, size);
109 return ptr;
110 }
111
112 void *
113 xrealloc(void *src, size_t size)
114 {
115 void *ret;
116
117 ret = realloc(src, size);
118 if (ret == NULL)
119 panic("realloc(%lu) failed", size);
120 return ret;
121 }
122
123 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
124
125 void *
126 xmallocarray(size_t nmemb, size_t size)
127 {
128 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
129 nmemb > 0 && SIZE_MAX / nmemb < size)
130 panic("xmallocarray(%lu, %lu) failed", nmemb, size);
131 return xmalloc(size * nmemb);
132 }
133
134 void *
135 xreallocarray(void *optr, size_t nmemb, size_t size)
136 {
137 void *new_ptr;
138
139 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
140 nmemb > 0 && SIZE_MAX / nmemb < size)
141 panic("xreallocarray(%lu, %lu) failed", nmemb, size);
142 return xrealloc(optr, size * nmemb);
143 }
144
145 char *
146 xstrdup(const char *str)
147 {
148 char *cp;
149 size_t len;
150
151 len = strlen(str);
152
153 cp = xmalloc(len + 1);
154 strlcpy(cp, str, len + 1);
155
156 return cp;
157 }
158
159 /*
160 * String functions
161 */
162
163 short
164 getline(char *str, size_t len, char **ret)
165 {
166 short i;
167
168 for (i = 0; i < len; i++) {
169 if (str[i] == '\r' || i == len - 1) {
170 if (*ret == NULL)
171 *ret = xmalloc(i + 1);
172 memcpy(*ret, str, i + 1);
173 (*ret)[i] = '\0';
174 return i + 1;
175 }
176 }
177
178 return 0;
179 }
180
181 const char *
182 ordinal(unsigned short n)
183 {
184 switch (n % 100) {
185 case 11:
186 case 12:
187 case 13:
188 return "th";
189 default:
190 switch (n % 10) {
191 case 1:
192 return "st";
193 case 2:
194 return "nd";
195 case 3:
196 return "rd";
197 default:
198 return "th";
199 }
200 }
201 }
202
203 long
204 strpos_quoted(char *str, char c)
205 {
206 long pos;
207 unsigned char quot = 0;
208
209 for (pos = 0; str[pos] != '\0'; pos++) {
210 if (quot) {
211 if (str[pos] == '\\') {
212 pos++;
213 continue;
214 }
215 if (str[pos] == quot)
216 quot = 0;
217 continue;
218 } else {
219 if (str[pos] == '"') {
220 quot = str[pos];
221 continue;
222 }
223 if (str[pos] == c)
224 return pos;
225 }
226 }
227
228 return -1;
229 }
230
231 char *
232 OSTypeToString(OSType type)
233 {
234 static char ostype_s[5];
235
236 ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
237 ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
238 ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
239 ostype_s[3] = (unsigned char)(type & 0xff);
240 ostype_s[4] = 0;
241
242 return ostype_s;
243 }
244
245 /*
246 * BSD err(3) and warn(3) functions
247 */
248
249 void
250 vwarn(short alert_func, const char *format, va_list ap)
251 {
252 Rect bounds, irect;
253 short quit = 0, height, width, hit;
254 WindowPtr win, dialog;
255 OSType itype;
256 Handle ihandle;
257
258 GetPort(&win);
259
260 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
261
262 width = 300;
263 height = 100;
264 bounds.left = (screenBits.bounds.right - width) / 2;
265 bounds.right = bounds.left + width;
266 bounds.top = GetMBarHeight() +
267 ((screenBits.bounds.bottom - height) / 2.5);
268 bounds.bottom = bounds.top + height;
269
270 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
271
272 alert_ditl_h = xNewHandle(sizeof(alert_ditl));
273 HLock(alert_ditl_h);
274 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
275 HUnlock(alert_ditl_h);
276
277 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
278 (WindowPtr)-1L, false, 0, alert_ditl_h);
279
280 #if 0
281 /* XXX: why doesn't changing this work? */
282 GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect);
283 switch (alert_func) {
284 case CAUTION_ALERT:
285 ihandle = GetIcon(cautionIcon);
286 break;
287 case NOTE_ALERT:
288 ihandle = GetIcon(noteIcon);
289 break;
290 default:
291 ihandle = GetIcon(stopIcon);
292 }
293 ihandle = GetIcon(cautionIcon);
294 SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect);
295 #endif
296
297 ShowWindow(dialog);
298 for (;;) {
299 ModalDialog(0, &hit);
300 if (hit == ok)
301 break;
302 }
303 DisposDialog(dialog);
304 DisposHandle(alert_ditl_h);
305
306 SetPort(win);
307
308 if (quit)
309 ExitToShell();
310 }
311
312 void
313 panic(const char *format, ...)
314 {
315 va_list ap;
316
317 va_start(ap, format);
318 vwarn(STOP_ALERT, format, ap);
319 va_end(ap);
320
321 ExitToShell();
322 }
323
324 void
325 err(short ret, const char *format, ...)
326 {
327 va_list ap;
328
329 va_start(ap, format);
330 vwarn(STOP_ALERT, format, ap);
331 va_end(ap);
332
333 ExitToShell();
334 }
335
336 void
337 warn(const char *format, ...)
338 {
339 va_list ap;
340
341 va_start(ap, format);
342 vwarn(CAUTION_ALERT, format, ap);
343 va_end(ap);
344 }
345
346 void
347 warnx(const char *format, ...)
348 {
349 va_list ap;
350
351 va_start(ap, format);
352 vwarn(CAUTION_ALERT, format, ap);
353 va_end(ap);
354 }
355
356 void
357 note(const char *format, ...)
358 {
359 va_list ap;
360
361 va_start(ap, format);
362 vwarn(NOTE_ALERT, format, ap);
363 va_end(ap);
364 }
365
366 short
367 ask(const char *format, ...)
368 {
369 Rect bounds, irect;
370 short height, width, hit;
371 WindowPtr win, dialog;
372 OSType itype;
373 Handle ihandle;
374 va_list ap;
375
376 GetPort(&win);
377
378 va_start(ap, format);
379 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
380 va_end(ap);
381
382 width = 300;
383 height = 100;
384 bounds.left = (screenBits.bounds.right - width) / 2;
385 bounds.right = bounds.left + width;
386 bounds.top = GetMBarHeight() +
387 ((screenBits.bounds.bottom - height) / 2.5);
388 bounds.bottom = bounds.top + height;
389
390 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
391
392 ask_ditl_h = xNewHandle(sizeof(ask_ditl));
393 HLock(ask_ditl_h);
394 memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl));
395 HUnlock(ask_ditl_h);
396
397 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
398 (WindowPtr)-1L, false, 0, ask_ditl_h);
399
400 ShowWindow(dialog);
401 for (;;) {
402 ModalDialog(0, &hit);
403 if (hit == 1 || hit == 2)
404 break;
405 }
406 DisposDialog(dialog);
407 DisposHandle(alert_ditl_h);
408
409 SetPort(win);
410
411 return (hit == 1);
412 }
413
414 void
415 progress(char *format, ...)
416 {
417 static Str255 progress_s;
418 Handle thandle;
419 va_list argptr;
420 Rect bounds = { 100, 90, 160, 420 }; /* tlbr */
421 Rect trect;
422 short ttype;
423
424 if (format == NULL) {
425 if (progress_dialog != NULL) {
426 DisposDialog(progress_dialog);
427 DisposHandle(progress_ditl_h);
428 progress_dialog = NULL;
429 }
430 return;
431 }
432
433 va_start(argptr, format);
434 vsnprintf((char *)progress_s, 256, format, argptr);
435 va_end(argptr);
436 CtoPstr(progress_s);
437
438 if (progress_dialog == NULL) {
439 progress_ditl_h = xNewHandle(sizeof(progress_ditl));
440 HLock(progress_ditl_h);
441 memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl));
442 HUnlock(progress_ditl_h);
443
444 progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
445 (WindowPtr)-1L, false, 0, progress_ditl_h);
446 }
447
448 GetDItem(progress_dialog, 1, &ttype, &thandle, &trect);
449 SetIText(thandle, progress_s);
450
451 ShowWindow(progress_dialog);
452 DrawDialog(progress_dialog);
453 }
454
455 /*
456 * General Mac-specific non-GUI functions
457 */
458 static unsigned long _xorshift_state = 0;
459 unsigned long
460 xorshift32(void)
461 {
462 unsigned long x = _xorshift_state;
463 if (x == 0)
464 x = Ticks;
465 x ^= x << 13;
466 x ^= x >> 17;
467 x ^= x << 5;
468 return _xorshift_state = x;
469 }
470
471
472 /*
473 * Error checking wrappers for Mac toolkit functions
474 */
475
476 Handle
477 xNewHandle(size_t size)
478 {
479 Handle h;
480
481 if (size == 0)
482 panic("Zero xNewHandle size");
483
484 h = NewHandle(size);
485 if (h == NULL)
486 panic("Failed to NewHandle(%lu)", size);
487
488 return h;
489 }
490
491 Handle
492 xGetResource(ResType type, short id)
493 {
494 Handle h;
495
496 h = GetResource(type, id);
497 if (h == NULL)
498 panic("Failed to find resource %d", id);
499
500 return h;
501 }
502
503 StringHandle
504 xGetString(short id)
505 {
506 StringHandle h;
507
508 h = GetString(id);
509 if (h == NULL)
510 panic("Failed to find STR resource %d", id);
511
512 return h;
513 }
514
515 char *
516 xGetStringAsChar(short id)
517 {
518 StringHandle h;
519 char *out;
520 size_t l;
521
522 h = xGetString(id);
523 HLock(h);
524 l = (*h)[0];
525 out = xmalloc(l + 1);
526 memcpy((void *)out, (void *)(*h + 1), l);
527 out[l] = '\0';
528 ReleaseResource(h);
529
530 return out;
531 }
532
533 long
534 xGetStringAsLong(short id)
535 {
536 char *c;
537 long r;
538
539 c = xGetStringAsChar(id);
540 r = atol(c);
541 free(c);
542 return r;
543 }
544
545 void
546 xSetHandleSize(Handle h, Size s)
547 {
548 SetHandleSize(h, s);
549 if (MemError())
550 panic("Failed to SetHandleSize to %ld", s);
551 }
552
553 /*
554 * Filesystem utilities
555 */
556
557 short
558 getpath(const short vRefNum, const Str255 fileName, Str255 *ret,
559 bool include_file)
560 {
561 WDPBRec wdir;
562 HVolumeParam wvol;
563 DirInfo wcinfo;
564 Str255 name;
565 size_t retlen = 0, len;
566 char tmpret[256], tmp[256];
567 char *lastcolon;
568
569 if (strchr((char *)fileName + 1, ':') != NULL) {
570 /* already a full path */
571 memcpy(*ret, fileName, 256);
572 if (!include_file) {
573 PtoCstr(*ret);
574 lastcolon = strrchr((char *)*ret, ':');
575 lastcolon[0] = '\0';
576 CtoPstr(*ret);
577 }
578 return 0;
579 }
580
581 wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
582 wdir.ioWDIndex = 0;
583 wdir.ioWDProcID = 0;
584 wdir.ioNamePtr = (StringPtr)&name;
585 if (PBGetWDInfo(&wdir, 0) != noErr) {
586 warn("Failed looking up directory");
587 return 1;
588 }
589
590 wvol.ioNamePtr = (StringPtr)&name;
591 wvol.ioVRefNum = vRefNum;
592 wvol.ioVolIndex = 0;
593
594 if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
595 warn("Failed getting volume info");
596 return 1;
597 }
598
599 if (wvol.ioVSigWord != 0x4244) {
600 warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
601 return 1;
602 }
603
604 wcinfo.ioVRefNum = vRefNum;
605 wcinfo.ioNamePtr = (StringPtr)&name;
606 wcinfo.ioFDirIndex = -1;
607 wcinfo.ioDrParID = wdir.ioWDDirID;
608 wcinfo.ioDrDirID = wdir.ioWDDirID;
609
610 /* go backwards, prepending each folder's parent */
611 while (wcinfo.ioDrParID != 1) {
612 wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
613
614 if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
615 break;
616
617 len = name[0];
618 PtoCstr(name);
619 if (retlen == 0) {
620 retlen = len;
621 strlcpy(tmpret, (char *)name, sizeof(tmpret));
622 } else {
623 strlcpy(tmp, tmpret, sizeof(tmp));
624 snprintf(tmpret, sizeof(tmpret), "%s:%s", name, tmp);
625 }
626 }
627
628 if (include_file) {
629 /* append the original path */
630 memcpy(name, fileName, sizeof(name));
631 PtoCstr(name);
632 if (retlen == 0)
633 strlcpy(tmpret, (char *)name, sizeof(tmpret));
634 else {
635 strlcat(tmpret, ":", sizeof(tmpret));
636 strlcat(tmpret, (char *)name, sizeof(tmpret));
637 }
638 } else if (retlen == 0) {
639 (*ret)[0] = 0;
640 return 0;
641 }
642
643 CtoPstr(tmpret);
644 memcpy(*ret, tmpret, sizeof(tmpret));
645
646 return 0;
647 }
648
649 short
650 stat(char *path, struct stat *sb)
651 {
652 char *ppath;
653 short ret;
654
655 ppath = xstrdup(path);
656 CtoPstr(ppath);
657 ret = FStat((unsigned char *)ppath, sb);
658 free(ppath);
659
660 return ret;
661 }
662
663 short
664 FStat(Str255 path, struct stat *sb)
665 {
666 CInfoPBRec catblock = { 0 };
667 short ret;
668
669 catblock.hFileInfo.ioNamePtr = path;
670 ret = PBGetCatInfo(&catblock, 0);
671 if (ret != noErr)
672 return -1;
673
674 /* data fork logical length + resource fork logical length */
675 sb->st_size = catblock.hFileInfo.ioFlLgLen +
676 catblock.hFileInfo.ioFlRLgLen;
677 sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
678 sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
679 sb->st_flags = catblock.hFileInfo.ioFlAttrib;
680
681 return 0;
682 }
683
684 bool
685 FIsDir(Str255 path)
686 {
687 struct stat st;
688 short ret;
689
690 if ((ret = FStat(path, &st)) != 0)
691 return ret;
692
693 /* bit 4 is set in ioFlAttrib if the item is a directory */
694 if (st.st_flags & (1 << 4))
695 return true;
696
697 return false;
698 }
699
700 OSErr
701 copy_file(Str255 source, Str255 dest, bool overwrite)
702 {
703 FInfo fi;
704 IOParam pb;
705 short error, source_ref, dest_ref;
706
707 /* copy data fork */
708
709 error = GetFInfo(source, 0, &fi);
710 if (error)
711 return error;
712
713 error = Create(dest, 0, fi.fdCreator, fi.fdType);
714 if (error == dupFNErr && overwrite) {
715 error = FSDelete(dest, 0);
716 if (error)
717 return error;
718 error = Create(dest, 0, fi.fdCreator, fi.fdType);
719 }
720 if (error)
721 return error;
722
723 memset(&pb, 0, sizeof(pb));
724 pb.ioNamePtr = source;
725 pb.ioPermssn = fsRdPerm;
726 error = PBOpen(&pb, false);
727 if (error)
728 return error;
729 source_ref = pb.ioRefNum;
730
731 error = FSOpen(dest, 0, &dest_ref);
732 if (error) {
733 FSClose(source_ref);
734 return error;
735 }
736
737 error = copy_file_contents(source_ref, dest_ref);
738
739 FSClose(source_ref);
740 FSClose(dest_ref);
741
742 if (error)
743 return error;
744
745 /*
746 * Copy resource fork, open source as shared read/write in case it's
747 * an open resource file.
748 */
749 source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
750
751 if (source_ref == -1 && ResError() == eofErr) {
752 /* no resource fork */
753 FSClose(source_ref);
754 FSClose(dest_ref);
755 return 0;
756 }
757
758 if (source_ref == -1)
759 return ResError();
760
761 CreateResFile(dest);
762 if (ResError()) {
763 FSClose(source_ref);
764 return ResError();
765 }
766 error = OpenRF(dest, 0, &dest_ref);
767 if (error) {
768 FSClose(source_ref);
769 return error;
770 }
771
772 error = copy_file_contents(source_ref, dest_ref);
773
774 FSClose(source_ref);
775 FSClose(dest_ref);
776
777 return error;
778 }
779
780 OSErr
781 copy_file_contents(short source_ref, short dest_ref)
782 {
783 char buf[1024];
784 short error;
785 long source_size, count;
786
787 GetEOF(source_ref, &source_size);
788 error = SetFPos(source_ref, fsFromStart, 0);
789 if (error)
790 return error;
791 error = SetEOF(dest_ref, source_size);
792 if (error)
793 return error;
794 error = SetFPos(dest_ref, fsFromStart, 0);
795 if (error)
796 return error;
797 while (source_size > 0) {
798 count = sizeof(buf);
799 if (count > source_size)
800 count = source_size;
801 error = FSRead(source_ref, &count, &buf);
802 if (error && error != eofErr)
803 break;
804 source_size -= count;
805 error = FSWrite(dest_ref, &count, &buf);
806 if (error && error != eofErr)
807 break;
808 }
809
810 if (error && error != eofErr)
811 return error;
812
813 return 0;
814 }
815
816 /* read a \r-terminated line or the final non-line bytes of an open file */
817 OSErr
818 FSReadLine(short frefnum, char *buf, size_t buflen)
819 {
820 char tbuf;
821 size_t pos, fsize, rlen = 1, total_read = 0;
822 short error, found = -1, i;
823
824 GetFPos(frefnum, &pos);
825 GetEOF(frefnum, &fsize);
826
827 for (; pos <= fsize; pos++) {
828 if (total_read > buflen)
829 return -1;
830
831 error = FSRead(frefnum, &rlen, &tbuf);
832 if (error)
833 return -1;
834
835 if (tbuf == '\r')
836 return total_read;
837
838 buf[total_read++] = tbuf;
839 }
840
841 /* nothing found until the end of the file */
842 return total_read;
843 }
844
845
846 /*
847 * General Mac-specific GUI functions
848 */
849
850 short
851 FontHeight(short font_id, short size)
852 {
853 FMInput f_in;
854 FMOutput *f_out;
855
856 if (font_id == -1)
857 font_id = thePort->txFont;
858 if (size == -1)
859 size = thePort->txSize;
860
861 f_in.family = font_id;
862 f_in.size = size;
863 f_in.face = 0;
864 f_in.needBits = false;
865 f_in.device = 0;
866 f_in.numer.h = f_in.numer.v = 1;
867 f_in.denom.h = f_in.denom.v = 1;
868
869 f_out = FMSwapFont(&f_in);
870
871 return (((f_out->leading + f_out->ascent + f_out->descent) *
872 f_out->numer.v) / f_out->denom.v);
873 }
874
875 void
876 DrawGrowIconOnly(WindowPtr win)
877 {
878 Rect r;
879 RgnHandle tmp;
880
881 GetClip(tmp = NewRgn());
882 r = win->portRect;
883 r.top = r.bottom - SCROLLBAR_WIDTH;
884 r.left = r.right - SCROLLBAR_WIDTH + 1;
885 ClipRect(&r);
886 DrawGrowIcon(win);
887 SetClip(tmp);
888 DisposeRgn(tmp);
889 }
890
891 /* assumes a fixed-width style */
892 short
893 TEGetWidth(short off, TEHandle te)
894 {
895 TextStyle style;
896 short fheight, fwidth, ascent, old_font, old_size;
897
898 TEGetStyle(off, &style, &fheight, &ascent, te);
899
900 old_font = thePort->txFace;
901 old_size = thePort->txSize;
902 thePort->txFace = style.tsFont;
903 thePort->txSize = style.tsSize;
904 fwidth = CharWidth('m');
905 thePort->txFace = old_font;
906 thePort->txSize = old_size;
907
908 return fwidth;
909 }
910
911 #define ceildiv(a,b) ((a / b) + (!!(a % b)))
912
913 void
914 UpdateScrollbarForTE(ControlHandle control, TEHandle te, bool reset)
915 {
916 size_t vlines, telines;
917 TERec *ter;
918 short fheight, fwidth, max, val, per_page, per_line, horiz, max_chars,
919 n;
920
921 HLock(te);
922 ter = *te;
923
924 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
925 ((*control)->contrlRect.right - (*control)->contrlRect.left));
926
927 if (horiz) {
928 fwidth = TEGetWidth(0, te);
929 per_line = ((ter->viewRect.right - ter->viewRect.left) /
930 fwidth) - 1;
931 for (max_chars = 0, n = 1; n < ter->nLines; n++) {
932 if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
933 max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
934 }
935
936 if (max_chars <= per_line)
937 /* don't enable the scrollbar */
938 max = 1;
939 else
940 max = max_chars - per_line + 1;
941
942 if (reset) {
943 val = 1;
944 TESetSelect(0, 0, te);
945 } else {
946 val = (ter->viewRect.left - ter->destRect.left);
947
948 if (val < 0)
949 val = 1;
950 else {
951 val = ceildiv(val, fwidth) + 1;
952 if (val > max)
953 max = val;
954 }
955 }
956 } else {
957 fheight = TEGetHeight(0, 0, te);
958 vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
959 telines = ter->nLines;
960 /* telines is inaccurate if the last line doesn't have any chars */
961 if (telines >= vlines)
962 telines++;
963 max = telines - vlines + 1;
964 if (max < 1)
965 max = 1;
966
967 if (reset) {
968 val = 1;
969 TESetSelect(0, 0, te);
970 } else {
971 val = (ter->viewRect.top - ter->destRect.top);
972 if (val < 0)
973 val = 1;
974 else {
975 val = ceildiv(val, fheight) + 1;
976 if (val > max)
977 max = val;
978 }
979 }
980 }
981 SetCtlMax(control, max);
982 SetCtlValue(control, val);
983
984 HUnlock(te);
985 }
986
987 void
988 SetTrackControlTE(TEHandle te)
989 {
990 track_control_te = te;
991 }
992
993 pascal void
994 TrackMouseDownInControl(ControlHandle control, short part)
995 {
996 TERec *ter;
997 short page, val, adj, fheight, fwidth, horiz, per_line;
998
999 if (track_control_te == NULL)
1000 panic("TrackMouseDownInControl without SetTrackControlTE");
1001
1002 HLock(track_control_te);
1003 ter = *track_control_te;
1004
1005 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1006 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1007
1008 if (horiz) {
1009 fwidth = TEGetWidth(0, track_control_te);
1010 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1011 fwidth) - 1;
1012 page = ceildiv(GetCtlMax(control) + per_line,
1013 ((per_line * 75) / 100)) + 1;
1014 } else {
1015 /* keep 1 line of context between pages */
1016 fheight = TEGetHeight(0, 0, track_control_te);
1017 page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
1018 }
1019
1020 adj = 0;
1021 switch (part) {
1022 case inUpButton:
1023 adj = -1;
1024 break;
1025 case inPageUp:
1026 adj = -page;
1027 break;
1028 case inDownButton:
1029 adj = 1;
1030 break;
1031 case inPageDown:
1032 adj = page;
1033 break;
1034 }
1035
1036 val = GetCtlValue(control);
1037 if (val + adj < GetCtlMin(control))
1038 adj = -(val - GetCtlMin(control));
1039 if (val + adj > GetCtlMax(control))
1040 adj = (GetCtlMax(control) - val);
1041 if (adj == 0)
1042 return;
1043
1044 if (horiz)
1045 TEScroll(-adj * fwidth, 0, track_control_te);
1046 else
1047 TEScroll(0, -adj * fheight, track_control_te);
1048
1049 SetCtlValue(control, val + adj);
1050
1051 HUnlock(track_control_te);
1052 }
1053
1054 pascal bool
1055 ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
1056 {
1057 char key;
1058
1059 switch (event->what) {
1060 case keyDown:
1061 key = event->message & charCodeMask;
1062
1063 if (event->modifiers & cmdKey) {
1064 switch (key) {
1065 case 'x':
1066 ZeroScrap();
1067 DlgCut(dlg);
1068 break;
1069 case 'c':
1070 ZeroScrap();
1071 DlgCopy(dlg);
1072 break;
1073 case 'v':
1074 if (TEFromScrap() == noErr)
1075 DlgPaste(dlg);
1076 break;
1077 }
1078 event->what = nullEvent;
1079 return false;
1080 } else if (key == 13 || key == 3) {
1081 /* OK button */
1082 *hit = 1;
1083 return true;
1084 }
1085
1086 break;
1087 }
1088
1089 return false;
1090 }
1091
1092 /* (*(some_te))->caretHook = NullCaretHook; */
1093 pascal void
1094 NullCaretHook(void)
1095 {
1096 asm {
1097 move.l (a7)+,d0
1098 rts
1099 }
1100 }