AmendHub

Download

jcs

/

wikipedia

/

util.c

 

(View History)

jcs   util: Wrap Latest amendment: 47 on 2023-10-16

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