AmendHub

Download

jcs

/

amend

/

util.c

 

(View History)

jcs   util: SFGetFile and SFPutFile have different dialog dimensions Latest amendment: 101 on 2022-09-12

1 /*
2 * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org>
3 * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /* THINK C Project must have a header of "#include <MacHeaders>" */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <GestaltEqu.h>
27 #include <Script.h>
28 #include <SetUpA4.h>
29 #include "util.h"
30
31 /* ALRT resources */
32 #define ASK_ALERT_ID 130
33
34 #define ERROR_STRING_SIZE 1024
35 static char err_str[ERROR_STRING_SIZE];
36
37 /* basic DITL with an ok button (1), text area (2), and icon (3) */
38 #define ALERT_DITL_OK 1
39 #define ALERT_DITL_ICON 3
40 static const char alert_ditl[] = {
41 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E,
42 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02,
43 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
44 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02,
45 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
46 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02,
47 0x00, 0x01
48 };
49 static Handle alert_ditl_h = NULL;
50
51 /* ICN# to show for APPICON_ALERT */
52 #define APPICON_ICN_ID 128
53
54 /* DITL with a Yes button (1), No button (2), text (3), and icon (4) */
55 static const char ask_ditl[] = {
56 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E,
57 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02,
58 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
59 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02,
60 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
61 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02,
62 0x00, 0x01
63 };
64 static Handle ask_ditl_h = NULL;
65
66 /* DITL with just a text view */
67 static const char progress_ditl[] = {
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
69 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02,
70 0x5E, 0x30
71 };
72 static Handle progress_ditl_h = NULL;
73 static DialogPtr progress_dialog = NULL;
74
75 static TEHandle track_control_te = NULL;
76
77 enum {
78 STOP_ALERT,
79 CAUTION_ALERT,
80 NOTE_ALERT,
81 APPICON_ALERT
82 };
83
84 /*
85 * Define to audit each malloc and free and verify that a pointer isn't
86 * double-freed. The list of outstanding allocations can be checked by
87 * looking through malloc_map.
88 */
89 //#define MALLOC_DEBUG
90
91 #ifdef MALLOC_DEBUG
92 /*
93 * List of allocations, updated at xmalloc() and xfree(). If an address
94 * passed to xfree() isn't in the list, it indicates a double-free.
95 */
96 #define MALLOC_MAP_CHUNK_SIZE 1024
97 struct malloc_map_e {
98 unsigned long addr;
99 unsigned long size;
100 char note[MALLOC_NOTE_SIZE];
101 } *malloc_map = NULL;
102 unsigned long malloc_map_size = 0;
103 static bool malloc_map_compact = false;
104 #endif
105
106 void vwarn(short alert_func, const char *format, va_list ap);
107
108 /*
109 * Util helper needed to be called at program startup, to pre-allocate
110 * some things that we can't do during errors.
111 */
112
113 void
114 util_init(void)
115 {
116 alert_ditl_h = xNewHandle(sizeof(alert_ditl));
117 HLock(alert_ditl_h);
118 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
119 HUnlock(alert_ditl_h);
120
121 #ifdef MALLOC_DEBUG
122 malloc_map_size = MALLOC_MAP_CHUNK_SIZE;
123 malloc_map = (struct malloc_map_e *)NewPtr(malloc_map_size *
124 sizeof(struct malloc_map_e));
125 if (malloc_map == NULL)
126 panic("NewPtr(%lu) failed", MALLOC_MAP_CHUNK_SIZE);
127 memset(malloc_map, 0, malloc_map_size);
128 #endif
129 }
130
131 /*
132 * Memory functions
133 */
134
135 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
136
137 void *
138 xmalloc(size_t size, char *note)
139 {
140 void *ptr;
141 #ifdef MALLOC_DEBUG
142 struct malloc_map_e *new_malloc_map;
143 unsigned short n, j;
144 #endif
145
146 if (size == 0)
147 panic("xmalloc: zero size");
148
149 ptr = NewPtr(size);
150 if (ptr == NULL)
151 panic("xmalloc(%lu) failed", size);
152
153 #ifdef MALLOC_DEBUG
154 if (malloc_map_compact) {
155 for (n = 0; n < malloc_map_size; n++) {
156 if (malloc_map[n].addr != 0)
157 continue;
158
159 for (j = n + 1; j < malloc_map_size; j++) {
160 if (malloc_map[j].addr == 0)
161 continue;
162
163 malloc_map[n] = malloc_map[j];
164 memset(&malloc_map[j], 0, sizeof(struct malloc_map_e));
165 break;
166 }
167 }
168
169 malloc_map_compact = false;
170 }
171
172 for (n = 0; n <= malloc_map_size; n++) {
173 if (n == malloc_map_size) {
174 malloc_map_size += MALLOC_MAP_CHUNK_SIZE;
175 warn("xmalloc(%lu): out of malloc map entries, maybe a "
176 "memory leak, resizing to %ld", size, malloc_map_size);
177 new_malloc_map = (struct malloc_map_e *)NewPtr(
178 malloc_map_size * sizeof(struct malloc_map_e));
179 if (new_malloc_map == NULL)
180 panic("out of memory resizing malloc map");
181 memcpy(new_malloc_map, malloc_map,
182 (malloc_map_size - MALLOC_MAP_CHUNK_SIZE) *
183 sizeof(struct malloc_map_e));
184 DisposePtr(malloc_map);
185 malloc_map = new_malloc_map;
186 }
187 if (malloc_map[n].addr == 0) {
188 malloc_map[n].addr = (unsigned long)ptr;
189 malloc_map[n].size = size;
190 strlcpy(malloc_map[n].note, note, sizeof(malloc_map[n].note));
191 break;
192 }
193 n = n;
194 }
195 #endif
196
197 return ptr;
198 }
199
200 void
201 xfree(void *ptrptr)
202 {
203 unsigned long *addr = (unsigned long *)ptrptr;
204 void *ptr = (void *)*addr;
205 #ifdef MALLOC_DEBUG
206 unsigned long n;
207
208 for (n = 0; n <= malloc_map_size; n++) {
209 if (n == malloc_map_size)
210 panic("xfree(0x%lx): can't find in alloc map, likely "
211 "double free()", *addr);
212 if (malloc_map[n].addr == *addr) {
213 malloc_map[n].addr = 0;
214 malloc_map[n].size = 0;
215 malloc_map[n].note[0] = '\0';
216 break;
217 }
218 }
219 #endif
220
221 DisposePtr(ptr);
222
223 *addr = 0L;
224 }
225
226 void *
227 xmalloczero(size_t size, char *note)
228 {
229 void *ptr;
230
231 ptr = xmalloc(size, note);
232 memset(ptr, 0, size);
233
234 return ptr;
235 }
236
237 void *
238 xcalloc(size_t nmemb, size_t size, char *note)
239 {
240 void *ptr;
241
242 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
243 nmemb > 0 && SIZE_MAX / nmemb < size)
244 panic("xcalloc(%lu, %lu) overflow", nmemb, size);
245 ptr = xmalloczero(nmemb * size, note);
246 if (ptr == NULL)
247 panic("xcalloc(%lu, %lu) failed", nmemb, size);
248
249 return ptr;
250 }
251
252 void *
253 xrealloc(void *src, size_t size)
254 {
255 void *ptr, *tsrc;
256 unsigned long n;
257 char note[MALLOC_NOTE_SIZE] = "realloc from null";
258
259 #ifdef MALLOC_DEBUG
260 if (src != NULL) {
261 for (n = 0; n <= malloc_map_size; n++) {
262 if (n == malloc_map_size) {
263 panic("xrealloc(%lu): can't find in alloc map, likely "
264 "double free()", (unsigned long)src);
265 return NULL;
266 }
267 if (malloc_map[n].addr == (unsigned long)src) {
268 strlcpy(note, malloc_map[n].note, sizeof(note));
269 break;
270 }
271 }
272 }
273 #endif
274
275 ptr = xmalloc(size, note);
276 if (src != NULL) {
277 memcpy(ptr, src, size);
278 tsrc = src;
279 xfree(&tsrc);
280 }
281
282 return ptr;
283 }
284
285 void *
286 xreallocarray(void *optr, size_t nmemb, size_t size)
287 {
288 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
289 nmemb > 0 && SIZE_MAX / nmemb < size)
290 panic("xreallocarray(%lu, %lu) failed", nmemb, size);
291 return xrealloc(optr, size * nmemb);
292 }
293
294 char *
295 xstrdup(const char *str, char *note)
296 {
297 char *cp;
298 size_t len;
299
300 len = strlen(str);
301
302 cp = xmalloc(len + 1, note);
303 strlcpy(cp, str, len + 1);
304
305 return cp;
306 }
307
308 char *
309 xstrndup(const char *str, size_t maxlen, char *note)
310 {
311 char *copy;
312 const char *cp;
313 size_t len;
314
315 /* strnlen */
316 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
317 ;
318
319 len = (size_t)(cp - str);
320 copy = xmalloc(len + 1, note);
321 (void)memcpy(copy, str, len);
322 copy[len] = '\0';
323
324 return copy;
325 }
326
327
328 /*
329 * String functions
330 */
331
332 short
333 getline(char *str, size_t len, char **ret)
334 {
335 short i;
336
337 for (i = 0; i < len; i++) {
338 if (str[i] == '\r' || i == len - 1) {
339 if (*ret == NULL)
340 *ret = xmalloc(i + 1, "getline");
341 memcpy(*ret, str, i + 1);
342 (*ret)[i] = '\0';
343 return i + 1;
344 }
345 }
346
347 return 0;
348 }
349
350 const char *
351 ordinal(unsigned short n)
352 {
353 switch (n % 100) {
354 case 11:
355 case 12:
356 case 13:
357 return "th";
358 default:
359 switch (n % 10) {
360 case 1:
361 return "st";
362 case 2:
363 return "nd";
364 case 3:
365 return "rd";
366 default:
367 return "th";
368 }
369 }
370 }
371
372 size_t
373 rtrim(char *str, char *chars)
374 {
375 size_t len, rlen, n, j;
376
377 rlen = len = strlen(str);
378
379 for (n = len; n > 0; n--) {
380 for (j = 0; chars[j] != '\0'; j++) {
381 if (str[n - 1] == chars[j]) {
382 rlen--;
383 str[n - 1] = '\0';
384 goto next_in_str;
385 }
386 }
387
388 break;
389 next_in_str:
390 continue;
391 }
392
393 return rlen;
394 }
395
396 long
397 strpos_quoted(char *str, char c)
398 {
399 long pos;
400 unsigned char quot = 0;
401
402 for (pos = 0; str[pos] != '\0'; pos++) {
403 if (quot) {
404 if (str[pos] == '\\') {
405 pos++;
406 continue;
407 }
408 if (str[pos] == quot)
409 quot = 0;
410 continue;
411 } else {
412 if (str[pos] == '"') {
413 quot = str[pos];
414 continue;
415 }
416 if (str[pos] == c)
417 return pos;
418 }
419 }
420
421 return -1;
422 }
423
424 char *
425 OSTypeToString(OSType type)
426 {
427 static char ostype_s[5];
428
429 ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
430 ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
431 ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
432 ostype_s[3] = (unsigned char)(type & 0xff);
433 ostype_s[4] = 0;
434
435 return ostype_s;
436 }
437
438 /*
439 * BSD err(3) and warn(3) functions
440 */
441
442 void
443 vwarn(short alert_func, const char *format, va_list ap)
444 {
445 Rect bounds;
446 short hit, itype;
447 WindowPtr win, dialog;
448 Handle ihandle;
449 Rect irect;
450
451 GetPort(&win);
452
453 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
454
455 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
456
457 center_in_screen(320, 110, false, &bounds);
458 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
459 (WindowPtr)-1L, false, 0, alert_ditl_h);
460
461 GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect);
462 switch (alert_func) {
463 case CAUTION_ALERT:
464 ihandle = GetIcon(cautionIcon);
465 break;
466 case NOTE_ALERT:
467 ihandle = GetIcon(noteIcon);
468 break;
469 case APPICON_ALERT:
470 ihandle = GetResource('ICN#', APPICON_ICN_ID);
471 if (ihandle)
472 break;
473 default:
474 ihandle = GetIcon(stopIcon);
475 }
476 SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect);
477
478 ShowWindow(dialog);
479
480 for (;;) {
481 ModalDialog(0, &hit);
482 if (hit == ok)
483 break;
484 }
485 ReleaseResource(ihandle);
486 DisposDialog(dialog);
487
488 /*
489 * Pre-allocate for the next one while we have memory. We can't use
490 * xNewHandle because we might already be alerting about an OOM
491 * condition.
492 */
493 DisposHandle(alert_ditl_h);
494 alert_ditl_h = NewHandle(sizeof(alert_ditl));
495 if (alert_ditl_h == NULL)
496 ExitToShell();
497 HLock(alert_ditl_h);
498 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
499 HUnlock(alert_ditl_h);
500
501 SetPort(win);
502 }
503
504 void
505 panic(const char *format, ...)
506 {
507 va_list ap;
508
509 va_start(ap, format);
510 vwarn(STOP_ALERT, format, ap);
511 va_end(ap);
512
513 ExitToShell();
514 }
515
516 void
517 err(short ret, const char *format, ...)
518 {
519 va_list ap;
520
521 va_start(ap, format);
522 vwarn(STOP_ALERT, format, ap);
523 va_end(ap);
524
525 ExitToShell();
526 }
527
528 void
529 warn(const char *format, ...)
530 {
531 va_list ap;
532
533 va_start(ap, format);
534 vwarn(CAUTION_ALERT, format, ap);
535 va_end(ap);
536 }
537
538 void
539 warnx(const char *format, ...)
540 {
541 va_list ap;
542
543 va_start(ap, format);
544 vwarn(CAUTION_ALERT, format, ap);
545 va_end(ap);
546 }
547
548 void
549 note(const char *format, ...)
550 {
551 va_list ap;
552
553 va_start(ap, format);
554 vwarn(NOTE_ALERT, format, ap);
555 va_end(ap);
556 }
557
558 void
559 appicon_note(const char *format, ...)
560 {
561 va_list ap;
562
563 va_start(ap, format);
564 vwarn(APPICON_ALERT, format, ap);
565 va_end(ap);
566 }
567
568 short
569 ask(const char *format, ...)
570 {
571 Rect bounds;
572 short hit;
573 WindowPtr win, dialog;
574 va_list ap;
575
576 GetPort(&win);
577
578 va_start(ap, format);
579 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
580 va_end(ap);
581
582 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
583
584 ask_ditl_h = xNewHandle(sizeof(ask_ditl));
585 HLock(ask_ditl_h);
586 memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl));
587 HUnlock(ask_ditl_h);
588
589 center_in_screen(300, 100, false, &bounds);
590 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
591 (WindowPtr)-1L, false, 0, ask_ditl_h);
592
593 ShowWindow(dialog);
594 for (;;) {
595 ModalDialog(0, &hit);
596 if (hit == 1 || hit == 2)
597 break;
598 }
599 DisposDialog(dialog);
600 DisposHandle(ask_ditl_h);
601
602 SetPort(win);
603
604 return (hit == 1);
605 }
606
607 void
608 about(char *program_name)
609 {
610 VersRecHndl vers;
611 char vers_s[255];
612 char short_vers[255] = { 0 };
613 short vlen, n;
614
615 if ((vers = (VersRecHndl)GetResource('vers', 1))) {
616 /*
617 * vers "long version string" is a pascal string after the
618 * short version pascal string
619 */
620 HLock(vers);
621 vlen = (*vers)->shortVersion[0];
622 memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
623 sizeof((*vers)->shortVersion) - vlen - 1);
624 PtoCstr(short_vers);
625 snprintf(vers_s, sizeof(vers_s), "%s %s", program_name,
626 short_vers);
627 for (n = 0; n < sizeof(vers_s); n++) {
628 if (vers_s[n] == '©') {
629 vers_s[n - 1] = '\r';
630 break;
631 }
632 }
633 ReleaseResource(vers);
634 appicon_note("%s", vers_s);
635 } else
636 warnx("Can't find version number!");
637 }
638
639 void
640 progress(char *format, ...)
641 {
642 static Str255 progress_s;
643 Handle thandle;
644 va_list argptr;
645 Rect bounds;
646 Rect trect;
647 short ttype;
648
649 if (format == NULL) {
650 if (progress_dialog != NULL) {
651 DisposDialog(progress_dialog);
652 DisposHandle(progress_ditl_h);
653 progress_dialog = NULL;
654 }
655 return;
656 }
657
658 va_start(argptr, format);
659 vsnprintf((char *)progress_s, 256, format, argptr);
660 va_end(argptr);
661 CtoPstr(progress_s);
662
663 if (progress_dialog == NULL) {
664 progress_ditl_h = xNewHandle(sizeof(progress_ditl));
665 HLock(progress_ditl_h);
666 memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl));
667 HUnlock(progress_ditl_h);
668
669 center_in_screen(330, 60, false, &bounds);
670 progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
671 (WindowPtr)-1L, false, 0, progress_ditl_h);
672 }
673
674 GetDItem(progress_dialog, 1, &ttype, &thandle, &trect);
675 SetIText(thandle, progress_s);
676
677 ShowWindow(progress_dialog);
678 DrawDialog(progress_dialog);
679 }
680
681 void
682 window_rect(WindowPtr win, Rect *ret)
683 {
684 *ret = (*(((WindowPeek)win)->strucRgn))->rgnBBox;
685 ret->left += 1;
686 ret->right -= 2;
687 ret->top -= 1;
688 ret->bottom -= 1;
689 }
690
691 void
692 center_in_screen(short width, short height, bool titlebar, Rect *b)
693 {
694 b->left = ((screenBits.bounds.right - screenBits.bounds.left) / 2) -
695 (width / 2);
696 b->top = ((screenBits.bounds.bottom - screenBits.bounds.top) / 2) -
697 (height / 2) + (titlebar ? GetMBarHeight() : 0);
698 b->right = b->left + width;
699 b->bottom = b->top + height;
700 }
701
702 Point
703 centered_sfget_dialog(void)
704 {
705 Point p;
706 Rect r;
707
708 center_in_screen(364, 216, false, &r);
709 p.h = r.left;
710 p.v = r.top;
711
712 return p;
713 }
714
715 Point
716 centered_sfput_dialog(void)
717 {
718 Point p;
719 Rect r;
720
721 center_in_screen(320, 200, false, &r);
722 p.h = r.left;
723 p.v = r.top;
724
725 return p;
726 }
727
728
729 /*
730 * General Mac-specific non-GUI functions
731 */
732
733 static unsigned long _xorshift_state = 0;
734 unsigned long
735 xorshift32(void)
736 {
737 unsigned long x = _xorshift_state;
738 if (x == 0)
739 x = Ticks;
740 x ^= x << 13;
741 x ^= x >> 17;
742 x ^= x << 5;
743 return _xorshift_state = x;
744 }
745
746
747 /*
748 * Error checking wrappers for Mac toolkit functions
749 */
750
751 Handle
752 xNewHandle(size_t size)
753 {
754 Handle h;
755
756 if (size == 0)
757 panic("Zero xNewHandle size");
758
759 h = NewHandle(size);
760 if (h == NULL)
761 panic("Failed to NewHandle(%lu)", size);
762
763 return h;
764 }
765
766 Handle
767 xGetResource(ResType type, short id)
768 {
769 Handle h;
770
771 h = GetResource(type, id);
772 if (h == NULL)
773 panic("Failed to find resource %d", id);
774
775 return h;
776 }
777
778 StringHandle
779 xGetString(short id)
780 {
781 StringHandle h;
782
783 h = GetString(id);
784 if (h == NULL)
785 panic("Failed to find STR resource %d", id);
786
787 return h;
788 }
789
790 char *
791 xGetStringAsChar(short id)
792 {
793 StringHandle h;
794 char *out;
795 size_t l;
796
797 h = xGetString(id);
798 HLock(h);
799 l = (*h)[0];
800 out = xmalloc(l + 1, "xGetStringAsChar");
801 memcpy((void *)out, (void *)(*h + 1), l);
802 out[l] = '\0';
803 ReleaseResource(h);
804
805 return out;
806 }
807
808 long
809 xGetStringAsLong(short id)
810 {
811 char *c;
812 long r;
813
814 c = xGetStringAsChar(id);
815 r = atol(c);
816 xfree(&c);
817 return r;
818 }
819
820 void
821 xSetHandleSize(Handle h, Size s)
822 {
823 SetHandleSize(h, s);
824 if (MemError())
825 panic("Failed to SetHandleSize to %ld", s);
826 }
827
828 /*
829 * Filesystem utilities
830 */
831
832 short
833 getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file)
834 {
835 WDPBRec wdir;
836 HVolumeParam wvol;
837 DirInfo wcinfo;
838 char *name = NULL;
839 size_t retlen = 0, len;
840 char *tmpret = NULL, *tmp = NULL;
841 char *lastcolon;
842
843 if (strchr((char *)fileName + 1, ':') != NULL) {
844 /* already a full path */
845 memcpy(*ret, fileName, 256);
846 if (!include_file) {
847 PtoCstr(*ret);
848 lastcolon = strrchr((char *)*ret, ':');
849 lastcolon[0] = '\0';
850 CtoPstr(*ret);
851 }
852 return 0;
853 }
854
855 name = xmalloc(FILENAME_MAX, "getpath");
856
857 wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
858 wdir.ioWDIndex = 0;
859 wdir.ioWDProcID = 0;
860 wdir.ioNamePtr = (StringPtr)name;
861 if (PBGetWDInfo(&wdir, 0) != noErr) {
862 warn("Failed looking up directory");
863 xfree(&name);
864 return 1;
865 }
866
867 wvol.ioNamePtr = (StringPtr)name;
868 wvol.ioVRefNum = vRefNum;
869 wvol.ioVolIndex = 0;
870
871 if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
872 warn("Failed getting volume info");
873 xfree(&name);
874 return 1;
875 }
876
877 if (wvol.ioVSigWord != 0x4244) {
878 warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
879 xfree(&name);
880 return 1;
881 }
882
883 wcinfo.ioVRefNum = vRefNum;
884 wcinfo.ioNamePtr = (StringPtr)name;
885 wcinfo.ioFDirIndex = -1;
886 wcinfo.ioDrParID = wdir.ioWDDirID;
887 wcinfo.ioDrDirID = wdir.ioWDDirID;
888
889 tmp = xmalloc(FILENAME_MAX, "getpath");
890 tmpret = xmalloc(FILENAME_MAX, "getpath");
891
892 /* go backwards, prepending each folder's parent */
893 while (wcinfo.ioDrParID != 1) {
894 wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
895
896 if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
897 break;
898
899 len = name[0];
900 PtoCstr(name);
901 if (retlen == 0) {
902 retlen = len;
903 strlcpy(tmpret, (char *)name, FILENAME_MAX);
904 } else {
905 strlcpy(tmp, tmpret, FILENAME_MAX);
906 snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp);
907 }
908 }
909
910 if (include_file) {
911 /* append the original path */
912 memcpy(name, fileName, FILENAME_MAX);
913 PtoCstr(name);
914 if (retlen == 0)
915 strlcpy(tmpret, name, FILENAME_MAX);
916 else {
917 strlcat(tmpret, ":", FILENAME_MAX);
918 strlcat(tmpret, name, FILENAME_MAX);
919 }
920 } else if (retlen == 0) {
921 (*ret)[0] = 0;
922 xfree(&tmp);
923 xfree(&tmpret);
924 xfree(&name);
925 return 0;
926 }
927
928 CtoPstr(tmpret);
929 memcpy(*ret, tmpret, FILENAME_MAX);
930 xfree(&tmp);
931 xfree(&tmpret);
932 xfree(&name);
933
934 return 0;
935 }
936
937 short
938 stat(char *path, struct stat *sb)
939 {
940 char *ppath;
941 short ret;
942
943 ppath = xstrdup(path, "stat");
944 CtoPstr(ppath);
945 ret = FStat((unsigned char *)ppath, sb);
946 xfree(&ppath);
947
948 return ret;
949 }
950
951 short
952 FStat(Str255 path, struct stat *sb)
953 {
954 CInfoPBRec catblock = { 0 };
955 short ret;
956
957 catblock.hFileInfo.ioNamePtr = path;
958 ret = PBGetCatInfo(&catblock, 0);
959 if (ret != noErr)
960 return -1;
961
962 /* data fork logical length + resource fork logical length */
963 sb->st_size = catblock.hFileInfo.ioFlLgLen +
964 catblock.hFileInfo.ioFlRLgLen;
965 sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
966 sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
967 sb->st_flags = catblock.hFileInfo.ioFlAttrib;
968
969 return 0;
970 }
971
972 bool
973 FIsDir(Str255 path)
974 {
975 struct stat st;
976 short ret;
977
978 if (FStat(path, &st) != 0)
979 return false;
980
981 /* bit 4 is set in ioFlAttrib if the item is a directory */
982 if (st.st_flags & (1 << 4))
983 return true;
984
985 return false;
986 }
987
988 OSErr
989 copy_file(Str255 source, Str255 dest, bool overwrite)
990 {
991 FInfo fi;
992 IOParam pb;
993 short error, source_ref, dest_ref;
994
995 /* copy data fork */
996
997 error = GetFInfo(source, 0, &fi);
998 if (error)
999 return error;
1000
1001 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1002 if (error == dupFNErr && overwrite) {
1003 error = FSDelete(dest, 0);
1004 if (error)
1005 return error;
1006 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1007 }
1008 if (error)
1009 return error;
1010
1011 memset(&pb, 0, sizeof(pb));
1012 pb.ioNamePtr = source;
1013 pb.ioPermssn = fsRdPerm;
1014 error = PBOpen(&pb, false);
1015 if (error)
1016 return error;
1017 source_ref = pb.ioRefNum;
1018
1019 error = FSOpen(dest, 0, &dest_ref);
1020 if (error) {
1021 FSClose(source_ref);
1022 return error;
1023 }
1024
1025 error = copy_file_contents(source_ref, dest_ref);
1026
1027 FSClose(source_ref);
1028 FSClose(dest_ref);
1029
1030 if (error)
1031 return error;
1032
1033 /*
1034 * Copy resource fork, open source as shared read/write in case it's
1035 * an open resource file.
1036 */
1037 source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
1038
1039 if (source_ref == -1 && ResError() == eofErr) {
1040 /* no resource fork */
1041 FSClose(source_ref);
1042 FSClose(dest_ref);
1043 return 0;
1044 }
1045
1046 if (source_ref == -1)
1047 return ResError();
1048
1049 CreateResFile(dest);
1050 if (ResError()) {
1051 FSClose(source_ref);
1052 return ResError();
1053 }
1054 error = OpenRF(dest, 0, &dest_ref);
1055 if (error) {
1056 FSClose(source_ref);
1057 return error;
1058 }
1059
1060 error = copy_file_contents(source_ref, dest_ref);
1061
1062 FSClose(source_ref);
1063 FSClose(dest_ref);
1064
1065 return error;
1066 }
1067
1068 OSErr
1069 copy_file_contents(short source_ref, short dest_ref)
1070 {
1071 char *buf;
1072 short error;
1073 long source_size, count;
1074
1075 GetEOF(source_ref, &source_size);
1076 error = SetFPos(source_ref, fsFromStart, 0);
1077 if (error)
1078 return error;
1079 error = SetEOF(dest_ref, source_size);
1080 if (error)
1081 return error;
1082 error = SetFPos(dest_ref, fsFromStart, 0);
1083 if (error)
1084 return error;
1085
1086 buf = xmalloc(1024, "copy_file_contents");
1087
1088 while (source_size > 0) {
1089 count = 1024;
1090 if (count > source_size)
1091 count = source_size;
1092 error = FSRead(source_ref, &count, buf);
1093 if (error && error != eofErr)
1094 break;
1095 source_size -= count;
1096 error = FSWrite(dest_ref, &count, buf);
1097 if (error && error != eofErr)
1098 break;
1099 }
1100
1101 xfree(&buf);
1102
1103 if (error && error != eofErr)
1104 return error;
1105
1106 return 0;
1107 }
1108
1109 /* read a \r-terminated line or the final non-line bytes of an open file */
1110 OSErr
1111 FSReadLine(short frefnum, char *buf, size_t buflen)
1112 {
1113 char tbuf;
1114 size_t pos, fsize, rlen = 1, total_read = 0;
1115 short error;
1116
1117 GetFPos(frefnum, &pos);
1118 GetEOF(frefnum, &fsize);
1119
1120 for (; pos <= fsize; pos++) {
1121 if (total_read > buflen)
1122 return -1;
1123
1124 error = FSRead(frefnum, &rlen, &tbuf);
1125 if (error)
1126 return -1;
1127
1128 if (tbuf == '\r')
1129 return total_read;
1130
1131 buf[total_read++] = tbuf;
1132 }
1133
1134 /* nothing found until the end of the file */
1135 return total_read;
1136 }
1137
1138
1139 /*
1140 * Gestalt functions
1141 */
1142 char *
1143 gestalt_machine_type(void)
1144 {
1145 short error;
1146 long resp;
1147
1148 error = Gestalt(gestaltMachineType, &resp);
1149 if (error)
1150 return NULL;
1151 switch (resp) {
1152 case gestaltClassic:
1153 return "Macintosh 128K";
1154 case gestaltMacXL:
1155 return "Macintosh XL";
1156 case gestaltMac512KE:
1157 return "Macintosh 512Ke";
1158 case gestaltMacPlus:
1159 return "Macintosh Plus";
1160 case gestaltMacSE:
1161 return "Macintosh SE";
1162 case gestaltMacII:
1163 return "Macintosh II";
1164 case gestaltMacIIx:
1165 return "Macintosh IIx";
1166 case gestaltMacIIcx:
1167 return "Macintosh IIcx";
1168 case gestaltMacSE030:
1169 return "Macintosh SE/30";
1170 case gestaltPortable:
1171 return "Macintosh Portable";
1172 case gestaltMacIIci:
1173 return "Macintosh IIci";
1174 case gestaltMacIIfx:
1175 return "Macintosh IIfx";
1176 case gestaltMacClassic:
1177 return "Macintosh Classic";
1178 case gestaltMacIIsi:
1179 return "Macintosh IIsi";
1180 case gestaltMacLC:
1181 return "Macintosh LC";
1182 }
1183
1184 return NULL;
1185 }
1186
1187 char *
1188 get_version(bool long_version)
1189 {
1190 static char vers_s[256] = { 0 };
1191 char *v;
1192 VersRecHndl vers;
1193 short len;
1194
1195 vers = (VersRecHndl)GetResource('vers', 1);
1196 if (!vers)
1197 return "?.?";
1198
1199 HLock(vers);
1200 v = (char *)&(*vers)->shortVersion;
1201 len = v[0];
1202 if (long_version) {
1203 v += len + 1;
1204 len = v[0];
1205 }
1206 memcpy(vers_s, v, len + 1);
1207 ReleaseResource(vers);
1208
1209 PtoCstr(vers_s);
1210 return vers_s;
1211 }
1212
1213 /*
1214 * General Mac-specific GUI functions
1215 */
1216
1217 short
1218 FontHeight(short font_id, short size)
1219 {
1220 FMInput f_in;
1221 FMOutput *f_out;
1222
1223 if (font_id == -1)
1224 font_id = thePort->txFont;
1225 if (size == -1)
1226 size = thePort->txSize;
1227
1228 f_in.family = font_id;
1229 f_in.size = size;
1230 f_in.face = 0;
1231 f_in.needBits = false;
1232 f_in.device = 0;
1233 f_in.numer.h = f_in.numer.v = 1;
1234 f_in.denom.h = f_in.denom.v = 1;
1235
1236 f_out = FMSwapFont(&f_in);
1237
1238 return (((f_out->leading + f_out->ascent + f_out->descent) *
1239 f_out->numer.v) / f_out->denom.v);
1240 }
1241
1242 void
1243 DrawGrowIconOnly(WindowPtr win)
1244 {
1245 Rect r;
1246 RgnHandle tmp;
1247
1248 GetClip(tmp = NewRgn());
1249 r = win->portRect;
1250 r.top = r.bottom - SCROLLBAR_WIDTH;
1251 r.left = r.right - SCROLLBAR_WIDTH + 1;
1252 ClipRect(&r);
1253 DrawGrowIcon(win);
1254 SetClip(tmp);
1255 DisposeRgn(tmp);
1256 }
1257
1258 /* assumes a fixed-width style */
1259 short
1260 TEGetWidth(short off, TEHandle te)
1261 {
1262 TextStyle style;
1263 short fheight, fwidth, ascent, old_font, old_size;
1264
1265 TEGetStyle(off, &style, &fheight, &ascent, te);
1266
1267 old_font = thePort->txFace;
1268 old_size = thePort->txSize;
1269 thePort->txFace = style.tsFont;
1270 thePort->txSize = style.tsSize;
1271 fwidth = CharWidth('m');
1272 thePort->txFace = old_font;
1273 thePort->txSize = old_size;
1274
1275 return fwidth;
1276 }
1277
1278 #define ceildiv(a,b) ((a / b) + (!!(a % b)))
1279
1280 void
1281 UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te, bool reset)
1282 {
1283 size_t vlines, telines;
1284 TERec *ter;
1285 RgnHandle rgn;
1286 short fheight, fwidth, max, val, per_line, horiz, max_chars, n;
1287
1288 HLock(control);
1289 HLock(te);
1290 ter = *te;
1291
1292 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1293 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1294
1295 if (horiz) {
1296 fwidth = TEGetWidth(0, te);
1297 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1298 fwidth) - 1;
1299 for (max_chars = 0, n = 1; n < ter->nLines; n++) {
1300 if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
1301 max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
1302 }
1303
1304 if (max_chars <= per_line)
1305 /* don't enable the scrollbar */
1306 max = 1;
1307 else
1308 max = max_chars - per_line + 1;
1309
1310 if (reset) {
1311 val = 1;
1312 TESetSelect(0, 0, te);
1313 } else {
1314 val = (ter->viewRect.left - ter->destRect.left);
1315
1316 if (val < 0)
1317 val = 1;
1318 else {
1319 val = ceildiv(val, fwidth) + 1;
1320 if (val > max)
1321 max = val;
1322 }
1323 }
1324 } else {
1325 fheight = TEGetHeight(0, 0, te);
1326 vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
1327 telines = ter->nLines;
1328 /* telines is inaccurate if the last line doesn't have any chars */
1329 //if (telines >= vlines)
1330 // telines++;
1331 max = telines - vlines + 1;
1332 if (max < 1)
1333 max = 1;
1334
1335 if (reset) {
1336 val = 1;
1337 TESetSelect(0, 0, te);
1338 } else {
1339 val = (ter->viewRect.top - ter->destRect.top);
1340 if (val < 0)
1341 val = 1;
1342 else {
1343 val = ceildiv(val, fheight) + 1;
1344 if (val > max)
1345 max = val;
1346 }
1347 }
1348 }
1349
1350 /*
1351 * Avoid SetCtlMax because it will redraw and then SetCtlValue will
1352 * redraw again, which can cause a jump if we're trying to keep the
1353 * scrollbar position in the same place (like at the bottom).
1354 */
1355 (*control)->contrlMax = max;
1356 SetCtlValue(control, val);
1357
1358 rgn = NewRgn();
1359 RectRgn(rgn, &(*control)->contrlRect);
1360 UpdtControl(win, rgn);
1361 CloseRgn(rgn);
1362
1363 HUnlock(te);
1364 HUnlock(control);
1365 }
1366
1367 void
1368 SetTrackControlTE(TEHandle te)
1369 {
1370 track_control_te = te;
1371 }
1372
1373 pascal void
1374 TrackMouseDownInControl(ControlHandle control, short part)
1375 {
1376 TERec *ter;
1377 short page, val, adj, fheight, fwidth, horiz, per_line;
1378
1379 if (track_control_te == NULL)
1380 panic("TrackMouseDownInControl without SetTrackControlTE");
1381
1382 HLock(track_control_te);
1383 ter = *track_control_te;
1384
1385 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1386 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1387
1388 if (horiz) {
1389 fwidth = TEGetWidth(0, track_control_te);
1390 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1391 fwidth) - 1;
1392 page = ceildiv(GetCtlMax(control) + per_line,
1393 ((per_line * 75) / 100)) + 1;
1394 } else {
1395 /* keep 1 line of context between pages */
1396 fheight = TEGetHeight(0, 0, track_control_te);
1397 page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
1398 }
1399
1400 adj = 0;
1401 switch (part) {
1402 case inUpButton:
1403 adj = -1;
1404 break;
1405 case inPageUp:
1406 adj = -page;
1407 break;
1408 case inDownButton:
1409 adj = 1;
1410 break;
1411 case inPageDown:
1412 adj = page;
1413 break;
1414 }
1415
1416 val = GetCtlValue(control);
1417 if (val + adj < GetCtlMin(control))
1418 adj = -(val - GetCtlMin(control));
1419 if (val + adj > GetCtlMax(control))
1420 adj = (GetCtlMax(control) - val);
1421 if (adj == 0)
1422 return;
1423
1424 if (horiz)
1425 TEScroll(-adj * fwidth, 0, track_control_te);
1426 else
1427 TEScroll(0, -adj * fheight, track_control_te);
1428
1429 SetCtlValue(control, val + adj);
1430
1431 HUnlock(track_control_te);
1432 }
1433
1434 pascal bool
1435 ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
1436 {
1437 WindowPtr event_win;
1438 short event_in;
1439 char key;
1440
1441 switch (event->what) {
1442 case mouseDown:
1443 event_in = FindWindow(event->where, &event_win);
1444
1445 switch (event_in) {
1446 case inGoAway:
1447 if (TrackGoAway(dlg, event->where)) {
1448 *hit = -1;
1449 return true;
1450 }
1451 }
1452 break;
1453 case keyDown:
1454 key = event->message & charCodeMask;
1455
1456 if (event->modifiers & cmdKey) {
1457 switch (key) {
1458 case 'x':
1459 ZeroScrap();
1460 DlgCut(dlg);
1461 break;
1462 case 'c':
1463 ZeroScrap();
1464 DlgCopy(dlg);
1465 break;
1466 case 'v':
1467 if (TEFromScrap() == noErr)
1468 DlgPaste(dlg);
1469 break;
1470 }
1471 event->what = nullEvent;
1472 return false;
1473 } else if (key == 13 || key == 3) {
1474 /* OK button */
1475 *hit = OK;
1476 return true;
1477 }
1478
1479 break;
1480 }
1481
1482 return false;
1483 }
1484
1485 static short _password_dialog_ditl_id = -1;
1486 static char *_password_dialog_storage = NULL;
1487 static size_t _password_dialog_storage_len = 0;
1488
1489 void
1490 PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len)
1491 {
1492 _password_dialog_ditl_id = ditl_id;
1493 _password_dialog_storage = storage;
1494 _password_dialog_storage_len = len;
1495
1496 memset(_password_dialog_storage, 0, len);
1497 }
1498
1499 pascal bool
1500 PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit)
1501 {
1502 DialogPeek dlgp;
1503 short sel_start, sel_end;
1504 char key;
1505
1506 dlgp = (DialogPeek)dlg;
1507 if (dlgp->editField == _password_dialog_ditl_id - 1) {
1508 sel_start = (*(dlgp->textH))->selStart;
1509 sel_end = (*(dlgp->textH))->selEnd;
1510
1511 switch (event->what) {
1512 case keyDown:
1513 case autoKey:
1514 key = event->message & charCodeMask;
1515 if (event->modifiers & cmdKey) {
1516 /* TODO: implement DlgPaste for cmd+v? */
1517 event->what = nullEvent;
1518 return false;
1519 }
1520
1521 if (key == 8) {
1522 /* backspace */
1523 if (sel_start == sel_end && sel_start > 0)
1524 memmove(_password_dialog_storage + sel_start - 1,
1525 _password_dialog_storage + sel_start,
1526 _password_dialog_storage_len - sel_start - 1);
1527 else if (sel_start != sel_end)
1528 memmove(_password_dialog_storage + sel_start,
1529 _password_dialog_storage + sel_end,
1530 _password_dialog_storage_len - sel_end - 1);
1531 } else if (sel_start >= _password_dialog_storage_len) {
1532 event->what = nullEvent;
1533 return false;
1534 } else if (key >= ' ' && key <= '~') {
1535 if (sel_start != sel_end)
1536 /* delete selection before making space for new char */
1537 memmove(_password_dialog_storage + sel_start,
1538 _password_dialog_storage + sel_end,
1539 _password_dialog_storage_len - sel_end - 1);
1540 memmove(_password_dialog_storage + sel_start + 1,
1541 _password_dialog_storage + sel_start,
1542 _password_dialog_storage_len - sel_start - 1);
1543 _password_dialog_storage[sel_start] = key;
1544 event->message = '•';
1545 }
1546 _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0';
1547 sel_start = 0;
1548 break;
1549 }
1550 }
1551
1552 return ModalDialogFilter(dlg, event, hit);
1553 }
1554
1555 void
1556 PasswordDialogFieldFinish(void)
1557 {
1558 _password_dialog_ditl_id = -1;
1559 _password_dialog_storage = NULL;
1560 _password_dialog_storage_len = 0;
1561 }
1562
1563 /* (*(some_te))->caretHook = NullCaretHook; */
1564 pascal void
1565 NullCaretHook(void)
1566 {
1567 asm {
1568 move.l (a7)+,d0
1569 rts
1570 }
1571 }
1572
1573 static bool menu_bar_hidden = false;
1574 static short old_mbar_height;
1575 static Rect mbar_rect;
1576 static RgnHandle mbar_save_region;
1577
1578 void
1579 HideMenuBar(void)
1580 {
1581 RgnHandle mbar_region;
1582 WindowPeek win;
1583
1584 old_mbar_height = GetMBarHeight();
1585 SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top,
1586 screenBits.bounds.right, screenBits.bounds.top + old_mbar_height);
1587
1588 mbar_save_region = NewRgn();
1589 mbar_region = NewRgn();
1590
1591 MBarHeight = 0;
1592 CopyRgn(GetGrayRgn(), mbar_save_region);
1593 RectRgn(mbar_region, &mbar_rect);
1594 UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn());
1595
1596 win = (WindowPeek)FrontWindow();
1597 PaintOne(win, mbar_region);
1598 PaintBehind(win, mbar_region);
1599 CalcVis(win);
1600 CalcVisBehind(win, mbar_region);
1601 DisposeRgn(mbar_region);
1602
1603 menu_bar_hidden = true;
1604 }
1605
1606 void
1607 RestoreHiddenMenuBar(void)
1608 {
1609 WindowPeek win;
1610
1611 if (!menu_bar_hidden)
1612 return;
1613
1614 CopyRgn(mbar_save_region, GetGrayRgn());
1615 MBarHeight = old_mbar_height;
1616
1617 RectRgn(mbar_save_region, &mbar_rect);
1618 win = (WindowPeek)FrontWindow();
1619 CalcVis(win);
1620 CalcVisBehind(win, mbar_save_region);
1621 DisposeRgn(mbar_save_region);
1622
1623 menu_bar_hidden = false;
1624
1625 HiliteMenu(0);
1626 DrawMenuBar();
1627 }
1628
1629 /* C Extensions */
1630
1631 /*
1632 * Appends src to string dst of size dsize (unlike strncat, dsize is the
1633 * full size of dst, not space left). At most dsize-1 characters
1634 * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
1635 * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
1636 * If retval >= dsize, truncation occurred.
1637 */
1638 size_t
1639 strlcat(char *dst, const char *src, size_t dsize)
1640 {
1641 const char *odst = dst;
1642 const char *osrc = src;
1643 size_t n = dsize;
1644 size_t dlen;
1645
1646 /* Find the end of dst and adjust bytes left but don't go past end. */
1647 while (n-- != 0 && *dst != '\0')
1648 dst++;
1649 dlen = dst - odst;
1650 n = dsize - dlen;
1651
1652 if (n-- == 0)
1653 return(dlen + strlen(src));
1654 while (*src != '\0') {
1655 if (n != 0) {
1656 *dst++ = *src;
1657 n--;
1658 }
1659 src++;
1660 }
1661 *dst = '\0';
1662
1663 return(dlen + (src - osrc)); /* count does not include NUL */
1664 }
1665
1666 /*
1667 * Copy string src to buffer dst of size dsize. At most dsize-1
1668 * chars will be copied. Always NUL terminates (unless dsize == 0).
1669 * Returns strlen(src); if retval >= dsize, truncation occurred.
1670 */
1671 size_t
1672 strlcpy(char *dst, const char *src, size_t dsize)
1673 {
1674 const char *osrc = src;
1675 size_t nleft = dsize;
1676
1677 /* Copy as many bytes as will fit. */
1678 if (nleft != 0) {
1679 while (--nleft != 0) {
1680 if ((*dst++ = *src++) == '\0')
1681 break;
1682 }
1683 }
1684
1685 /* Not enough room in dst, add NUL and traverse rest of src. */
1686 if (nleft == 0) {
1687 if (dsize != 0)
1688 *dst = '\0'; /* NUL-terminate dst */
1689 while (*src++)
1690 ;
1691 }
1692
1693 return(src - osrc - 1); /* count does not include NUL */
1694 }
1695
1696 char *
1697 strndup(const char *str, size_t maxlen)
1698 {
1699 char *copy;
1700 const char *cp;
1701 size_t len;
1702
1703 /* strnlen */
1704 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
1705 ;
1706
1707 len = (size_t)(cp - str);
1708 copy = malloc(len + 1);
1709 if (copy != NULL) {
1710 (void)memcpy(copy, str, len);
1711 copy[len] = '\0';
1712 }
1713
1714 return copy;
1715 }
1716
1717 /*
1718 * Get next token from string *stringp, where tokens are possibly-empty
1719 * strings separated by characters from delim.
1720 *
1721 * Writes NULs into the string at *stringp to end tokens.
1722 * delim need not remain constant from call to call.
1723 * On return, *stringp points past the last NUL written (if there might
1724 * be further tokens), or is NULL (if there are definitely no more tokens).
1725 *
1726 * If *stringp is NULL, strsep returns NULL.
1727 */
1728 char *
1729 strsep(char **stringp, const char *delim)
1730 {
1731 char *s;
1732 const char *spanp;
1733 int c, sc;
1734 char *tok;
1735
1736 if ((s = *stringp) == NULL)
1737 return (NULL);
1738 for (tok = s;;) {
1739 c = *s++;
1740 spanp = delim;
1741 do {
1742 if ((sc = *spanp++) == c) {
1743 if (c == 0)
1744 s = NULL;
1745 else
1746 s[-1] = 0;
1747 *stringp = s;
1748 return (tok);
1749 }
1750 } while (sc != 0);
1751 }
1752 return (NULL);
1753 }
1754
1755 static struct format {
1756 unsigned leftJustify : 1;
1757 unsigned forceSign : 1;
1758 unsigned altForm : 1;
1759 unsigned zeroPad : 1;
1760 unsigned havePrecision : 1;
1761 unsigned hSize : 1;
1762 unsigned lSize : 1;
1763 unsigned LSize : 1;
1764 char sign;
1765 char exponent;
1766 int fieldWidth;
1767 int precision;
1768 } default_format;
1769
1770 int bounded_vfprintf(FILE *fp, const char *fmt, va_list arg);
1771 static int nullio(FILE *fp, int i);
1772
1773 int
1774 bounded_vfprintf(FILE *fp, const char *fmt, va_list arg)
1775 {
1776 register int c, i, j, nwritten = 0;
1777 register unsigned long n;
1778 long double x;
1779 register char *s;
1780 #define VFPRINTF_BUFLEN 512
1781 char buf[VFPRINTF_BUFLEN], *digits, *t;
1782 struct format F;
1783
1784 for (c = *fmt; c; c = *++fmt) {
1785 if (c != '%')
1786 goto copy1;
1787 F = default_format;
1788
1789 /* decode flags */
1790
1791 for (;;) {
1792 c = *++fmt;
1793 if (c == '-')
1794 F.leftJustify = TRUE;
1795 else if (c == '+')
1796 F.forceSign = TRUE;
1797 else if (c == ' ')
1798 F.sign = ' ';
1799 else if (c == '#')
1800 F.altForm = TRUE;
1801 else if (c == '0')
1802 F.zeroPad = TRUE;
1803 else
1804 break;
1805 }
1806
1807 /* decode field width */
1808
1809 if (c == '*') {
1810 if ((F.fieldWidth = va_arg(arg, int)) < 0) {
1811 F.leftJustify = TRUE;
1812 F.fieldWidth = -F.fieldWidth;
1813 }
1814 c = *++fmt;
1815 }
1816 else {
1817 for (; c >= '0' && c <= '9'; c = *++fmt)
1818 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1819 }
1820
1821 /* decode precision */
1822
1823 if (c == '.') {
1824 if ((c = *++fmt) == '*') {
1825 F.precision = va_arg(arg, int);
1826 c = *++fmt;
1827 }
1828 else {
1829 for (; c >= '0' && c <= '9'; c = *++fmt)
1830 F.precision = (10 * F.precision) + (c - '0');
1831 }
1832 if (F.precision >= 0)
1833 F.havePrecision = TRUE;
1834 }
1835
1836 /* perform appropriate conversion */
1837
1838 s = &buf[VFPRINTF_BUFLEN];
1839 if (F.leftJustify)
1840 F.zeroPad = FALSE;
1841 conv: switch (c) {
1842
1843 /* 'h' size modifier */
1844
1845 case 'h':
1846 F.hSize = TRUE;
1847 c = *++fmt;
1848 goto conv;
1849
1850 /* 'l' size modifier */
1851
1852 case 'l':
1853 F.lSize = TRUE;
1854 c = *++fmt;
1855 goto conv;
1856
1857 /* 'L' size modifier */
1858
1859 case 'L':
1860 F.LSize = TRUE;
1861 c = *++fmt;
1862 goto conv;
1863
1864 /* decimal (signed) */
1865
1866 case 'd':
1867 case 'i':
1868 if (F.lSize)
1869 n = va_arg(arg, long);
1870 else
1871 n = va_arg(arg, int);
1872 if (F.hSize)
1873 n = (short) n;
1874 if ((long) n < 0) {
1875 n = -n;
1876 F.sign = '-';
1877 }
1878 else if (F.forceSign)
1879 F.sign = '+';
1880 goto decimal;
1881
1882 /* decimal (unsigned) */
1883
1884 case 'u':
1885 if (F.lSize)
1886 n = va_arg(arg, unsigned long);
1887 else
1888 n = va_arg(arg, unsigned int);
1889 if (F.hSize)
1890 n = (unsigned short) n;
1891 F.sign = 0;
1892 goto decimal;
1893
1894 /* decimal (common code) */
1895
1896 decimal:
1897 if (!F.havePrecision) {
1898 if (F.zeroPad) {
1899 F.precision = F.fieldWidth;
1900 if (F.sign)
1901 --F.precision;
1902 }
1903 if (F.precision < 1)
1904 F.precision = 1;
1905 }
1906 for (i = 0; n; n /= 10, i++)
1907 *--s = n % 10 + '0';
1908 for (; i < F.precision; i++)
1909 *--s = '0';
1910 if (F.sign) {
1911 *--s = F.sign;
1912 i++;
1913 }
1914 break;
1915
1916 /* octal (unsigned) */
1917
1918 case 'o':
1919 if (F.lSize)
1920 n = va_arg(arg, unsigned long);
1921 else
1922 n = va_arg(arg, unsigned int);
1923 if (F.hSize)
1924 n = (unsigned short) n;
1925 if (!F.havePrecision) {
1926 if (F.zeroPad)
1927 F.precision = F.fieldWidth;
1928 if (F.precision < 1)
1929 F.precision = 1;
1930 }
1931 for (i = 0; n; n /= 8, i++)
1932 *--s = n % 8 + '0';
1933 if (F.altForm && i && *s != '0') {
1934 *--s = '0';
1935 i++;
1936 }
1937 for (; i < F.precision; i++)
1938 *--s = '0';
1939 break;
1940
1941 /* hexadecimal (unsigned) */
1942
1943 case 'p':
1944 F.havePrecision = F.lSize = TRUE;
1945 F.precision = 8;
1946 /* ... */
1947 case 'X':
1948 digits = "0123456789ABCDEF";
1949 goto hexadecimal;
1950 case 'x':
1951 digits = "0123456789abcdef";
1952 /* ... */
1953 hexadecimal:
1954 if (F.lSize)
1955 n = va_arg(arg, unsigned long);
1956 else
1957 n = va_arg(arg, unsigned int);
1958 if (F.hSize)
1959 n = (unsigned short) n;
1960 if (!F.havePrecision) {
1961 if (F.zeroPad) {
1962 F.precision = F.fieldWidth;
1963 if (F.altForm)
1964 F.precision -= 2;
1965 }
1966 if (F.precision < 1)
1967 F.precision = 1;
1968 }
1969 for (i = 0; n; n /= 16, i++)
1970 *--s = digits[n % 16];
1971 for (; i < F.precision; i++)
1972 *--s = '0';
1973 if (F.altForm) {
1974 *--s = c;
1975 *--s = '0';
1976 i += 2;
1977 }
1978 break;
1979
1980 /* character */
1981
1982 case 'c':
1983 *--s = va_arg(arg, int);
1984 i = 1;
1985 break;
1986
1987 /* string */
1988
1989 case 's':
1990 s = va_arg(arg, char *);
1991 if (F.altForm) {
1992 i = (unsigned char) *s++;
1993 if (F.havePrecision && i > F.precision)
1994 i = F.precision;
1995 }
1996 else {
1997 if (!F.havePrecision)
1998 i = strlen(s);
1999 else if (t = memchr(s, '\0', F.precision))
2000 i = t - s;
2001 else
2002 i = F.precision;
2003 }
2004 break;
2005
2006 /* store # bytes written so far */
2007
2008 case 'n':
2009 s = va_arg(arg, void *);
2010 if (F.hSize)
2011 * (short *) s = nwritten;
2012 else if (F.lSize)
2013 * (long *) s = nwritten;
2014 else
2015 * (int *) s = nwritten;
2016 continue;
2017
2018 /* oops - unknown conversion, abort */
2019
2020 default:
2021 if (c != '%')
2022 goto done;
2023 copy1:
2024 putc(c, fp); /* disregard EOF */
2025 ++nwritten;
2026 continue;
2027 }
2028
2029 /* pad on the left */
2030
2031 if (i < F.fieldWidth && !F.leftJustify) {
2032 do {
2033 putc(' ', fp); /* disregard EOF */
2034 ++nwritten;
2035 } while (i < --F.fieldWidth);
2036 }
2037
2038 /* write the converted result */
2039
2040 fwrite(s, 1, i, fp); /* disregard EOF */
2041 nwritten += i;
2042
2043 /* pad on the right */
2044
2045 for (; i < F.fieldWidth; i++) {
2046 putc(' ', fp); /* disregard EOF */
2047 ++nwritten;
2048 }
2049 }
2050
2051 /* all done! */
2052
2053 done:
2054 return(nwritten);
2055 }
2056
2057 int
2058 snprintf(char *s, size_t size, const char *fmt, ...)
2059 {
2060 return(vsnprintf(s, size, fmt, __va(fmt)));
2061 }
2062
2063 static int
2064 nullio(FILE *fp, int i)
2065 {
2066 return(EOF);
2067 }
2068
2069 int
2070 vsnprintf(char *s, size_t size, const char *fmt, void *p)
2071 {
2072 FILE f;
2073 int n;
2074
2075 memset(&f, 0, sizeof(f));
2076 f.refnum = -1;
2077 f.ptr = (unsigned char *) s;
2078 f.cnt = size;
2079 f.proc = nullio;
2080 f.dirty = 1;
2081
2082 if ((n = bounded_vfprintf(&f, fmt, p)) >= 0)
2083 s[n] = 0;
2084 return(n);
2085 }