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 | } |