AmendHub

Download

vkoskiv

/

MacNTP

/

util.c

 

(View History)

vkoskiv   Large rewrite, restructure the project into an INIT Latest amendment: 11 on 2023-09-07

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_sf_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
716 /*
717 * General Mac-specific non-GUI functions
718 */
719
720 static unsigned long _xorshift_state = 0;
721 unsigned long
722 xorshift32(void)
723 {
724 unsigned long x = _xorshift_state;
725 if (x == 0)
726 x = Ticks;
727 x ^= x << 13;
728 x ^= x >> 17;
729 x ^= x << 5;
730 return _xorshift_state = x;
731 }
732
733
734 /*
735 * Error checking wrappers for Mac toolkit functions
736 */
737
738 Handle
739 xNewHandle(size_t size)
740 {
741 Handle h;
742
743 if (size == 0)
744 panic("Zero xNewHandle size");
745
746 h = NewHandle(size);
747 if (h == NULL)
748 panic("Failed to NewHandle(%lu)", size);
749
750 return h;
751 }
752
753 Handle
754 xGetResource(ResType type, short id)
755 {
756 Handle h;
757
758 h = GetResource(type, id);
759 if (h == NULL)
760 panic("Failed to find resource %d", id);
761
762 return h;
763 }
764
765 StringHandle
766 xGetString(short id)
767 {
768 StringHandle h;
769
770 h = GetString(id);
771 if (h == NULL)
772 panic("Failed to find STR resource %d", id);
773
774 return h;
775 }
776
777 char *
778 xGetStringAsChar(short id)
779 {
780 StringHandle h;
781 char *out;
782 size_t l;
783
784 h = xGetString(id);
785 HLock(h);
786 l = (*h)[0];
787 out = xmalloc(l + 1, "xGetStringAsChar");
788 memcpy((void *)out, (void *)(*h + 1), l);
789 out[l] = '\0';
790 ReleaseResource(h);
791
792 return out;
793 }
794
795 long
796 xGetStringAsLong(short id)
797 {
798 char *c;
799 long r;
800
801 c = xGetStringAsChar(id);
802 r = atol(c);
803 xfree(&c);
804 return r;
805 }
806
807 void
808 xSetHandleSize(Handle h, Size s)
809 {
810 SetHandleSize(h, s);
811 if (MemError())
812 panic("Failed to SetHandleSize to %ld", s);
813 }
814
815 /*
816 * Filesystem utilities
817 */
818
819 short
820 getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file)
821 {
822 WDPBRec wdir;
823 HVolumeParam wvol;
824 DirInfo wcinfo;
825 char *name = NULL;
826 size_t retlen = 0, len;
827 char *tmpret = NULL, *tmp = NULL;
828 char *lastcolon;
829
830 if (strchr((char *)fileName + 1, ':') != NULL) {
831 /* already a full path */
832 memcpy(*ret, fileName, 256);
833 if (!include_file) {
834 PtoCstr(*ret);
835 lastcolon = strrchr((char *)*ret, ':');
836 lastcolon[0] = '\0';
837 CtoPstr(*ret);
838 }
839 return 0;
840 }
841
842 name = xmalloc(FILENAME_MAX, "getpath");
843
844 wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
845 wdir.ioWDIndex = 0;
846 wdir.ioWDProcID = 0;
847 wdir.ioNamePtr = (StringPtr)name;
848 if (PBGetWDInfo(&wdir, 0) != noErr) {
849 warn("Failed looking up directory");
850 xfree(&name);
851 return 1;
852 }
853
854 wvol.ioNamePtr = (StringPtr)name;
855 wvol.ioVRefNum = vRefNum;
856 wvol.ioVolIndex = 0;
857
858 if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
859 warn("Failed getting volume info");
860 xfree(&name);
861 return 1;
862 }
863
864 if (wvol.ioVSigWord != 0x4244) {
865 warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
866 xfree(&name);
867 return 1;
868 }
869
870 wcinfo.ioVRefNum = vRefNum;
871 wcinfo.ioNamePtr = (StringPtr)name;
872 wcinfo.ioFDirIndex = -1;
873 wcinfo.ioDrParID = wdir.ioWDDirID;
874 wcinfo.ioDrDirID = wdir.ioWDDirID;
875
876 tmp = xmalloc(FILENAME_MAX, "getpath");
877 tmpret = xmalloc(FILENAME_MAX, "getpath");
878
879 /* go backwards, prepending each folder's parent */
880 while (wcinfo.ioDrParID != 1) {
881 wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
882
883 if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
884 break;
885
886 len = name[0];
887 PtoCstr(name);
888 if (retlen == 0) {
889 retlen = len;
890 strlcpy(tmpret, (char *)name, FILENAME_MAX);
891 } else {
892 strlcpy(tmp, tmpret, FILENAME_MAX);
893 snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp);
894 }
895 }
896
897 if (include_file) {
898 /* append the original path */
899 memcpy(name, fileName, FILENAME_MAX);
900 PtoCstr(name);
901 if (retlen == 0)
902 strlcpy(tmpret, name, FILENAME_MAX);
903 else {
904 strlcat(tmpret, ":", FILENAME_MAX);
905 strlcat(tmpret, name, FILENAME_MAX);
906 }
907 } else if (retlen == 0) {
908 (*ret)[0] = 0;
909 xfree(&tmp);
910 xfree(&tmpret);
911 xfree(&name);
912 return 0;
913 }
914
915 CtoPstr(tmpret);
916 memcpy(*ret, tmpret, FILENAME_MAX);
917 xfree(&tmp);
918 xfree(&tmpret);
919 xfree(&name);
920
921 return 0;
922 }
923
924 short
925 stat(char *path, struct stat *sb)
926 {
927 char *ppath;
928 short ret;
929
930 ppath = xstrdup(path, "stat");
931 CtoPstr(ppath);
932 ret = FStat((unsigned char *)ppath, sb);
933 xfree(&ppath);
934
935 return ret;
936 }
937
938 short
939 FStat(Str255 path, struct stat *sb)
940 {
941 CInfoPBRec catblock = { 0 };
942 short ret;
943
944 catblock.hFileInfo.ioNamePtr = path;
945 ret = PBGetCatInfo(&catblock, 0);
946 if (ret != noErr)
947 return -1;
948
949 /* data fork logical length + resource fork logical length */
950 sb->st_size = catblock.hFileInfo.ioFlLgLen +
951 catblock.hFileInfo.ioFlRLgLen;
952 sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
953 sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
954 sb->st_flags = catblock.hFileInfo.ioFlAttrib;
955
956 return 0;
957 }
958
959 bool
960 FIsDir(Str255 path)
961 {
962 struct stat st;
963 short ret;
964
965 if (FStat(path, &st) != 0)
966 return false;
967
968 /* bit 4 is set in ioFlAttrib if the item is a directory */
969 if (st.st_flags & (1 << 4))
970 return true;
971
972 return false;
973 }
974
975 OSErr
976 copy_file(Str255 source, Str255 dest, bool overwrite)
977 {
978 FInfo fi;
979 IOParam pb;
980 short error, source_ref, dest_ref;
981
982 /* copy data fork */
983
984 error = GetFInfo(source, 0, &fi);
985 if (error)
986 return error;
987
988 error = Create(dest, 0, fi.fdCreator, fi.fdType);
989 if (error == dupFNErr && overwrite) {
990 error = FSDelete(dest, 0);
991 if (error)
992 return error;
993 error = Create(dest, 0, fi.fdCreator, fi.fdType);
994 }
995 if (error)
996 return error;
997
998 memset(&pb, 0, sizeof(pb));
999 pb.ioNamePtr = source;
1000 pb.ioPermssn = fsRdPerm;
1001 error = PBOpen(&pb, false);
1002 if (error)
1003 return error;
1004 source_ref = pb.ioRefNum;
1005
1006 error = FSOpen(dest, 0, &dest_ref);
1007 if (error) {
1008 FSClose(source_ref);
1009 return error;
1010 }
1011
1012 error = copy_file_contents(source_ref, dest_ref);
1013
1014 FSClose(source_ref);
1015 FSClose(dest_ref);
1016
1017 if (error)
1018 return error;
1019
1020 /*
1021 * Copy resource fork, open source as shared read/write in case it's
1022 * an open resource file.
1023 */
1024 source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
1025
1026 if (source_ref == -1 && ResError() == eofErr) {
1027 /* no resource fork */
1028 FSClose(source_ref);
1029 FSClose(dest_ref);
1030 return 0;
1031 }
1032
1033 if (source_ref == -1)
1034 return ResError();
1035
1036 CreateResFile(dest);
1037 if (ResError()) {
1038 FSClose(source_ref);
1039 return ResError();
1040 }
1041 error = OpenRF(dest, 0, &dest_ref);
1042 if (error) {
1043 FSClose(source_ref);
1044 return error;
1045 }
1046
1047 error = copy_file_contents(source_ref, dest_ref);
1048
1049 FSClose(source_ref);
1050 FSClose(dest_ref);
1051
1052 return error;
1053 }
1054
1055 OSErr
1056 copy_file_contents(short source_ref, short dest_ref)
1057 {
1058 char *buf;
1059 short error;
1060 long source_size, count;
1061
1062 GetEOF(source_ref, &source_size);
1063 error = SetFPos(source_ref, fsFromStart, 0);
1064 if (error)
1065 return error;
1066 error = SetEOF(dest_ref, source_size);
1067 if (error)
1068 return error;
1069 error = SetFPos(dest_ref, fsFromStart, 0);
1070 if (error)
1071 return error;
1072
1073 buf = xmalloc(1024, "copy_file_contents");
1074
1075 while (source_size > 0) {
1076 count = 1024;
1077 if (count > source_size)
1078 count = source_size;
1079 error = FSRead(source_ref, &count, buf);
1080 if (error && error != eofErr)
1081 break;
1082 source_size -= count;
1083 error = FSWrite(dest_ref, &count, buf);
1084 if (error && error != eofErr)
1085 break;
1086 }
1087
1088 xfree(&buf);
1089
1090 if (error && error != eofErr)
1091 return error;
1092
1093 return 0;
1094 }
1095
1096 /* read a \r-terminated line or the final non-line bytes of an open file */
1097 OSErr
1098 FSReadLine(short frefnum, char *buf, size_t buflen)
1099 {
1100 char tbuf;
1101 size_t pos, fsize, rlen = 1, total_read = 0;
1102 short error;
1103
1104 GetFPos(frefnum, &pos);
1105 GetEOF(frefnum, &fsize);
1106
1107 for (; pos <= fsize; pos++) {
1108 if (total_read > buflen)
1109 return -1;
1110
1111 error = FSRead(frefnum, &rlen, &tbuf);
1112 if (error)
1113 return -1;
1114
1115 if (tbuf == '\r')
1116 return total_read;
1117
1118 buf[total_read++] = tbuf;
1119 }
1120
1121 /* nothing found until the end of the file */
1122 return total_read;
1123 }
1124
1125
1126 /*
1127 * Gestalt functions
1128 */
1129 char *
1130 gestalt_machine_type(void)
1131 {
1132 short error;
1133 long resp;
1134
1135 error = Gestalt(gestaltMachineType, &resp);
1136 if (error)
1137 return NULL;
1138 switch (resp) {
1139 case gestaltClassic:
1140 return "Macintosh 128K";
1141 case gestaltMacXL:
1142 return "Macintosh XL";
1143 case gestaltMac512KE:
1144 return "Macintosh 512Ke";
1145 case gestaltMacPlus:
1146 return "Macintosh Plus";
1147 case gestaltMacSE:
1148 return "Macintosh SE";
1149 case gestaltMacII:
1150 return "Macintosh II";
1151 case gestaltMacIIx:
1152 return "Macintosh IIx";
1153 case gestaltMacIIcx:
1154 return "Macintosh IIcx";
1155 case gestaltMacSE030:
1156 return "Macintosh SE/30";
1157 case gestaltPortable:
1158 return "Macintosh Portable";
1159 case gestaltMacIIci:
1160 return "Macintosh IIci";
1161 case gestaltMacIIfx:
1162 return "Macintosh IIfx";
1163 case gestaltMacClassic:
1164 return "Macintosh Classic";
1165 case gestaltMacIIsi:
1166 return "Macintosh IIsi";
1167 case gestaltMacLC:
1168 return "Macintosh LC";
1169 }
1170
1171 return NULL;
1172 }
1173
1174 char *
1175 get_version(bool long_version)
1176 {
1177 static char vers_s[256] = { 0 };
1178 char *v;
1179 VersRecHndl vers;
1180 short len;
1181
1182 vers = (VersRecHndl)GetResource('vers', 1);
1183 if (!vers)
1184 return "?.?";
1185
1186 HLock(vers);
1187 v = (char *)&(*vers)->shortVersion;
1188 len = v[0];
1189 if (long_version) {
1190 v += len + 1;
1191 len = v[0];
1192 }
1193 memcpy(vers_s, v, len + 1);
1194 ReleaseResource(vers);
1195
1196 PtoCstr(vers_s);
1197 return vers_s;
1198 }
1199
1200 /*
1201 * General Mac-specific GUI functions
1202 */
1203
1204 short
1205 FontHeight(short font_id, short size)
1206 {
1207 FMInput f_in;
1208 FMOutput *f_out;
1209
1210 if (font_id == -1)
1211 font_id = thePort->txFont;
1212 if (size == -1)
1213 size = thePort->txSize;
1214
1215 f_in.family = font_id;
1216 f_in.size = size;
1217 f_in.face = 0;
1218 f_in.needBits = false;
1219 f_in.device = 0;
1220 f_in.numer.h = f_in.numer.v = 1;
1221 f_in.denom.h = f_in.denom.v = 1;
1222
1223 f_out = FMSwapFont(&f_in);
1224
1225 return (((f_out->leading + f_out->ascent + f_out->descent) *
1226 f_out->numer.v) / f_out->denom.v);
1227 }
1228
1229 void
1230 DrawGrowIconOnly(WindowPtr win)
1231 {
1232 Rect r;
1233 RgnHandle tmp;
1234
1235 GetClip(tmp = NewRgn());
1236 r = win->portRect;
1237 r.top = r.bottom - SCROLLBAR_WIDTH;
1238 r.left = r.right - SCROLLBAR_WIDTH + 1;
1239 ClipRect(&r);
1240 DrawGrowIcon(win);
1241 SetClip(tmp);
1242 DisposeRgn(tmp);
1243 }
1244
1245 /* assumes a fixed-width style */
1246 short
1247 TEGetWidth(short off, TEHandle te)
1248 {
1249 TextStyle style;
1250 short fheight, fwidth, ascent, old_font, old_size;
1251
1252 TEGetStyle(off, &style, &fheight, &ascent, te);
1253
1254 old_font = thePort->txFace;
1255 old_size = thePort->txSize;
1256 thePort->txFace = style.tsFont;
1257 thePort->txSize = style.tsSize;
1258 fwidth = CharWidth('m');
1259 thePort->txFace = old_font;
1260 thePort->txSize = old_size;
1261
1262 return fwidth;
1263 }
1264
1265 #define ceildiv(a,b) ((a / b) + (!!(a % b)))
1266
1267 void
1268 UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te, bool reset)
1269 {
1270 size_t vlines, telines;
1271 TERec *ter;
1272 RgnHandle rgn;
1273 short fheight, fwidth, max, val, per_line, horiz, max_chars, n;
1274
1275 HLock(control);
1276 HLock(te);
1277 ter = *te;
1278
1279 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1280 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1281
1282 if (horiz) {
1283 fwidth = TEGetWidth(0, te);
1284 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1285 fwidth) - 1;
1286 for (max_chars = 0, n = 1; n < ter->nLines; n++) {
1287 if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
1288 max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
1289 }
1290
1291 if (max_chars <= per_line)
1292 /* don't enable the scrollbar */
1293 max = 1;
1294 else
1295 max = max_chars - per_line + 1;
1296
1297 if (reset) {
1298 val = 1;
1299 TESetSelect(0, 0, te);
1300 } else {
1301 val = (ter->viewRect.left - ter->destRect.left);
1302
1303 if (val < 0)
1304 val = 1;
1305 else {
1306 val = ceildiv(val, fwidth) + 1;
1307 if (val > max)
1308 max = val;
1309 }
1310 }
1311 } else {
1312 fheight = TEGetHeight(0, 0, te);
1313 vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
1314 telines = ter->nLines;
1315 /* telines is inaccurate if the last line doesn't have any chars */
1316 //if (telines >= vlines)
1317 // telines++;
1318 max = telines - vlines + 1;
1319 if (max < 1)
1320 max = 1;
1321
1322 if (reset) {
1323 val = 1;
1324 TESetSelect(0, 0, te);
1325 } else {
1326 val = (ter->viewRect.top - ter->destRect.top);
1327 if (val < 0)
1328 val = 1;
1329 else {
1330 val = ceildiv(val, fheight) + 1;
1331 if (val > max)
1332 max = val;
1333 }
1334 }
1335 }
1336
1337 /*
1338 * Avoid SetCtlMax because it will redraw and then SetCtlValue will
1339 * redraw again, which can cause a jump if we're trying to keep the
1340 * scrollbar position in the same place (like at the bottom).
1341 */
1342 (*control)->contrlMax = max;
1343 SetCtlValue(control, val);
1344
1345 rgn = NewRgn();
1346 RectRgn(rgn, &(*control)->contrlRect);
1347 UpdtControl(win, rgn);
1348 CloseRgn(rgn);
1349
1350 HUnlock(te);
1351 HUnlock(control);
1352 }
1353
1354 void
1355 SetTrackControlTE(TEHandle te)
1356 {
1357 track_control_te = te;
1358 }
1359
1360 pascal void
1361 TrackMouseDownInControl(ControlHandle control, short part)
1362 {
1363 TERec *ter;
1364 short page, val, adj, fheight, fwidth, horiz, per_line;
1365
1366 if (track_control_te == NULL)
1367 panic("TrackMouseDownInControl without SetTrackControlTE");
1368
1369 HLock(track_control_te);
1370 ter = *track_control_te;
1371
1372 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1373 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1374
1375 if (horiz) {
1376 fwidth = TEGetWidth(0, track_control_te);
1377 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1378 fwidth) - 1;
1379 page = ceildiv(GetCtlMax(control) + per_line,
1380 ((per_line * 75) / 100)) + 1;
1381 } else {
1382 /* keep 1 line of context between pages */
1383 fheight = TEGetHeight(0, 0, track_control_te);
1384 page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
1385 }
1386
1387 adj = 0;
1388 switch (part) {
1389 case inUpButton:
1390 adj = -1;
1391 break;
1392 case inPageUp:
1393 adj = -page;
1394 break;
1395 case inDownButton:
1396 adj = 1;
1397 break;
1398 case inPageDown:
1399 adj = page;
1400 break;
1401 }
1402
1403 val = GetCtlValue(control);
1404 if (val + adj < GetCtlMin(control))
1405 adj = -(val - GetCtlMin(control));
1406 if (val + adj > GetCtlMax(control))
1407 adj = (GetCtlMax(control) - val);
1408 if (adj == 0)
1409 return;
1410
1411 if (horiz)
1412 TEScroll(-adj * fwidth, 0, track_control_te);
1413 else
1414 TEScroll(0, -adj * fheight, track_control_te);
1415
1416 SetCtlValue(control, val + adj);
1417
1418 HUnlock(track_control_te);
1419 }
1420
1421 pascal bool
1422 ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
1423 {
1424 WindowPtr event_win;
1425 short event_in;
1426 char key;
1427
1428 switch (event->what) {
1429 case mouseDown:
1430 event_in = FindWindow(event->where, &event_win);
1431
1432 switch (event_in) {
1433 case inGoAway:
1434 if (TrackGoAway(dlg, event->where)) {
1435 *hit = -1;
1436 return true;
1437 }
1438 }
1439 break;
1440 case keyDown:
1441 key = event->message & charCodeMask;
1442
1443 if (event->modifiers & cmdKey) {
1444 switch (key) {
1445 case 'x':
1446 ZeroScrap();
1447 DlgCut(dlg);
1448 break;
1449 case 'c':
1450 ZeroScrap();
1451 DlgCopy(dlg);
1452 break;
1453 case 'v':
1454 if (TEFromScrap() == noErr)
1455 DlgPaste(dlg);
1456 break;
1457 }
1458 event->what = nullEvent;
1459 return false;
1460 } else if (key == 13 || key == 3) {
1461 /* OK button */
1462 *hit = OK;
1463 return true;
1464 }
1465
1466 break;
1467 }
1468
1469 return false;
1470 }
1471
1472 static short _password_dialog_ditl_id = -1;
1473 static char *_password_dialog_storage = NULL;
1474 static size_t _password_dialog_storage_len = 0;
1475
1476 void
1477 PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len)
1478 {
1479 _password_dialog_ditl_id = ditl_id;
1480 _password_dialog_storage = storage;
1481 _password_dialog_storage_len = len;
1482
1483 memset(_password_dialog_storage, 0, len);
1484 }
1485
1486 pascal bool
1487 PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit)
1488 {
1489 DialogPeek dlgp;
1490 short sel_start, sel_end;
1491 char key;
1492
1493 dlgp = (DialogPeek)dlg;
1494 if (dlgp->editField == _password_dialog_ditl_id - 1) {
1495 sel_start = (*(dlgp->textH))->selStart;
1496 sel_end = (*(dlgp->textH))->selEnd;
1497
1498 switch (event->what) {
1499 case keyDown:
1500 case autoKey:
1501 key = event->message & charCodeMask;
1502 if (event->modifiers & cmdKey) {
1503 /* TODO: implement DlgPaste for cmd+v? */
1504 event->what = nullEvent;
1505 return false;
1506 }
1507
1508 if (key == 8) {
1509 /* backspace */
1510 if (sel_start == sel_end && sel_start > 0)
1511 memmove(_password_dialog_storage + sel_start - 1,
1512 _password_dialog_storage + sel_start,
1513 _password_dialog_storage_len - sel_start - 1);
1514 else if (sel_start != sel_end)
1515 memmove(_password_dialog_storage + sel_start,
1516 _password_dialog_storage + sel_end,
1517 _password_dialog_storage_len - sel_end - 1);
1518 } else if (sel_start >= _password_dialog_storage_len) {
1519 event->what = nullEvent;
1520 return false;
1521 } else if (key >= ' ' && key <= '~') {
1522 if (sel_start != sel_end)
1523 /* delete selection before making space for new char */
1524 memmove(_password_dialog_storage + sel_start,
1525 _password_dialog_storage + sel_end,
1526 _password_dialog_storage_len - sel_end - 1);
1527 memmove(_password_dialog_storage + sel_start + 1,
1528 _password_dialog_storage + sel_start,
1529 _password_dialog_storage_len - sel_start - 1);
1530 _password_dialog_storage[sel_start] = key;
1531 event->message = '•';
1532 }
1533 _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0';
1534 sel_start = 0;
1535 break;
1536 }
1537 }
1538
1539 return ModalDialogFilter(dlg, event, hit);
1540 }
1541
1542 void
1543 PasswordDialogFieldFinish(void)
1544 {
1545 _password_dialog_ditl_id = -1;
1546 _password_dialog_storage = NULL;
1547 _password_dialog_storage_len = 0;
1548 }
1549
1550 /* (*(some_te))->caretHook = NullCaretHook; */
1551 pascal void
1552 NullCaretHook(void)
1553 {
1554 asm {
1555 move.l (a7)+,d0
1556 rts
1557 }
1558 }
1559
1560 static bool menu_bar_hidden = false;
1561 static short old_mbar_height;
1562 static Rect mbar_rect;
1563 static RgnHandle mbar_save_region;
1564
1565 void
1566 HideMenuBar(void)
1567 {
1568 RgnHandle mbar_region;
1569 WindowPeek win;
1570
1571 old_mbar_height = GetMBarHeight();
1572 SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top,
1573 screenBits.bounds.right, screenBits.bounds.top + old_mbar_height);
1574
1575 mbar_save_region = NewRgn();
1576 mbar_region = NewRgn();
1577
1578 MBarHeight = 0;
1579 CopyRgn(GetGrayRgn(), mbar_save_region);
1580 RectRgn(mbar_region, &mbar_rect);
1581 UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn());
1582
1583 win = (WindowPeek)FrontWindow();
1584 PaintOne(win, mbar_region);
1585 PaintBehind(win, mbar_region);
1586 CalcVis(win);
1587 CalcVisBehind(win, mbar_region);
1588 DisposeRgn(mbar_region);
1589
1590 menu_bar_hidden = true;
1591 }
1592
1593 void
1594 RestoreHiddenMenuBar(void)
1595 {
1596 WindowPeek win;
1597
1598 if (!menu_bar_hidden)
1599 return;
1600
1601 CopyRgn(mbar_save_region, GetGrayRgn());
1602 MBarHeight = old_mbar_height;
1603
1604 RectRgn(mbar_save_region, &mbar_rect);
1605 win = (WindowPeek)FrontWindow();
1606 CalcVis(win);
1607 CalcVisBehind(win, mbar_save_region);
1608 DisposeRgn(mbar_save_region);
1609
1610 menu_bar_hidden = false;
1611
1612 HiliteMenu(0);
1613 DrawMenuBar();
1614 }
1615
1616 /* C Extensions */
1617
1618 /*
1619 * Appends src to string dst of size dsize (unlike strncat, dsize is the
1620 * full size of dst, not space left). At most dsize-1 characters
1621 * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
1622 * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
1623 * If retval >= dsize, truncation occurred.
1624 */
1625 size_t
1626 strlcat(char *dst, const char *src, size_t dsize)
1627 {
1628 const char *odst = dst;
1629 const char *osrc = src;
1630 size_t n = dsize;
1631 size_t dlen;
1632
1633 /* Find the end of dst and adjust bytes left but don't go past end. */
1634 while (n-- != 0 && *dst != '\0')
1635 dst++;
1636 dlen = dst - odst;
1637 n = dsize - dlen;
1638
1639 if (n-- == 0)
1640 return(dlen + strlen(src));
1641 while (*src != '\0') {
1642 if (n != 0) {
1643 *dst++ = *src;
1644 n--;
1645 }
1646 src++;
1647 }
1648 *dst = '\0';
1649
1650 return(dlen + (src - osrc)); /* count does not include NUL */
1651 }
1652
1653 /*
1654 * Copy string src to buffer dst of size dsize. At most dsize-1
1655 * chars will be copied. Always NUL terminates (unless dsize == 0).
1656 * Returns strlen(src); if retval >= dsize, truncation occurred.
1657 */
1658 size_t
1659 strlcpy(char *dst, const char *src, size_t dsize)
1660 {
1661 const char *osrc = src;
1662 size_t nleft = dsize;
1663
1664 /* Copy as many bytes as will fit. */
1665 if (nleft != 0) {
1666 while (--nleft != 0) {
1667 if ((*dst++ = *src++) == '\0')
1668 break;
1669 }
1670 }
1671
1672 /* Not enough room in dst, add NUL and traverse rest of src. */
1673 if (nleft == 0) {
1674 if (dsize != 0)
1675 *dst = '\0'; /* NUL-terminate dst */
1676 while (*src++)
1677 ;
1678 }
1679
1680 return(src - osrc - 1); /* count does not include NUL */
1681 }
1682
1683 char *
1684 strndup(const char *str, size_t maxlen)
1685 {
1686 char *copy;
1687 const char *cp;
1688 size_t len;
1689
1690 /* strnlen */
1691 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
1692 ;
1693
1694 len = (size_t)(cp - str);
1695 copy = malloc(len + 1);
1696 if (copy != NULL) {
1697 (void)memcpy(copy, str, len);
1698 copy[len] = '\0';
1699 }
1700
1701 return copy;
1702 }
1703
1704 /*
1705 * Get next token from string *stringp, where tokens are possibly-empty
1706 * strings separated by characters from delim.
1707 *
1708 * Writes NULs into the string at *stringp to end tokens.
1709 * delim need not remain constant from call to call.
1710 * On return, *stringp points past the last NUL written (if there might
1711 * be further tokens), or is NULL (if there are definitely no more tokens).
1712 *
1713 * If *stringp is NULL, strsep returns NULL.
1714 */
1715 char *
1716 strsep(char **stringp, const char *delim)
1717 {
1718 char *s;
1719 const char *spanp;
1720 int c, sc;
1721 char *tok;
1722
1723 if ((s = *stringp) == NULL)
1724 return (NULL);
1725 for (tok = s;;) {
1726 c = *s++;
1727 spanp = delim;
1728 do {
1729 if ((sc = *spanp++) == c) {
1730 if (c == 0)
1731 s = NULL;
1732 else
1733 s[-1] = 0;
1734 *stringp = s;
1735 return (tok);
1736 }
1737 } while (sc != 0);
1738 }
1739 return (NULL);
1740 }
1741
1742 static struct format {
1743 unsigned leftJustify : 1;
1744 unsigned forceSign : 1;
1745 unsigned altForm : 1;
1746 unsigned zeroPad : 1;
1747 unsigned havePrecision : 1;
1748 unsigned hSize : 1;
1749 unsigned lSize : 1;
1750 unsigned LSize : 1;
1751 char sign;
1752 char exponent;
1753 int fieldWidth;
1754 int precision;
1755 } default_format;
1756
1757 int bounded_vfprintf(FILE *fp, const char *fmt, va_list arg);
1758 static int nullio(FILE *fp, int i);
1759
1760 int
1761 bounded_vfprintf(FILE *fp, const char *fmt, va_list arg)
1762 {
1763 register int c, i, j, nwritten = 0;
1764 register unsigned long n;
1765 long double x;
1766 register char *s;
1767 #define VFPRINTF_BUFLEN 512
1768 char buf[VFPRINTF_BUFLEN], *digits, *t;
1769 struct format F;
1770
1771 for (c = *fmt; c; c = *++fmt) {
1772 if (c != '%')
1773 goto copy1;
1774 F = default_format;
1775
1776 /* decode flags */
1777
1778 for (;;) {
1779 c = *++fmt;
1780 if (c == '-')
1781 F.leftJustify = TRUE;
1782 else if (c == '+')
1783 F.forceSign = TRUE;
1784 else if (c == ' ')
1785 F.sign = ' ';
1786 else if (c == '#')
1787 F.altForm = TRUE;
1788 else if (c == '0')
1789 F.zeroPad = TRUE;
1790 else
1791 break;
1792 }
1793
1794 /* decode field width */
1795
1796 if (c == '*') {
1797 if ((F.fieldWidth = va_arg(arg, int)) < 0) {
1798 F.leftJustify = TRUE;
1799 F.fieldWidth = -F.fieldWidth;
1800 }
1801 c = *++fmt;
1802 }
1803 else {
1804 for (; c >= '0' && c <= '9'; c = *++fmt)
1805 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1806 }
1807
1808 /* decode precision */
1809
1810 if (c == '.') {
1811 if ((c = *++fmt) == '*') {
1812 F.precision = va_arg(arg, int);
1813 c = *++fmt;
1814 }
1815 else {
1816 for (; c >= '0' && c <= '9'; c = *++fmt)
1817 F.precision = (10 * F.precision) + (c - '0');
1818 }
1819 if (F.precision >= 0)
1820 F.havePrecision = TRUE;
1821 }
1822
1823 /* perform appropriate conversion */
1824
1825 s = &buf[VFPRINTF_BUFLEN];
1826 if (F.leftJustify)
1827 F.zeroPad = FALSE;
1828 conv: switch (c) {
1829
1830 /* 'h' size modifier */
1831
1832 case 'h':
1833 F.hSize = TRUE;
1834 c = *++fmt;
1835 goto conv;
1836
1837 /* 'l' size modifier */
1838
1839 case 'l':
1840 F.lSize = TRUE;
1841 c = *++fmt;
1842 goto conv;
1843
1844 /* 'L' size modifier */
1845
1846 case 'L':
1847 F.LSize = TRUE;
1848 c = *++fmt;
1849 goto conv;
1850
1851 /* decimal (signed) */
1852
1853 case 'd':
1854 case 'i':
1855 if (F.lSize)
1856 n = va_arg(arg, long);
1857 else
1858 n = va_arg(arg, int);
1859 if (F.hSize)
1860 n = (short) n;
1861 if ((long) n < 0) {
1862 n = -n;
1863 F.sign = '-';
1864 }
1865 else if (F.forceSign)
1866 F.sign = '+';
1867 goto decimal;
1868
1869 /* decimal (unsigned) */
1870
1871 case 'u':
1872 if (F.lSize)
1873 n = va_arg(arg, unsigned long);
1874 else
1875 n = va_arg(arg, unsigned int);
1876 if (F.hSize)
1877 n = (unsigned short) n;
1878 F.sign = 0;
1879 goto decimal;
1880
1881 /* decimal (common code) */
1882
1883 decimal:
1884 if (!F.havePrecision) {
1885 if (F.zeroPad) {
1886 F.precision = F.fieldWidth;
1887 if (F.sign)
1888 --F.precision;
1889 }
1890 if (F.precision < 1)
1891 F.precision = 1;
1892 }
1893 for (i = 0; n; n /= 10, i++)
1894 *--s = n % 10 + '0';
1895 for (; i < F.precision; i++)
1896 *--s = '0';
1897 if (F.sign) {
1898 *--s = F.sign;
1899 i++;
1900 }
1901 break;
1902
1903 /* octal (unsigned) */
1904
1905 case 'o':
1906 if (F.lSize)
1907 n = va_arg(arg, unsigned long);
1908 else
1909 n = va_arg(arg, unsigned int);
1910 if (F.hSize)
1911 n = (unsigned short) n;
1912 if (!F.havePrecision) {
1913 if (F.zeroPad)
1914 F.precision = F.fieldWidth;
1915 if (F.precision < 1)
1916 F.precision = 1;
1917 }
1918 for (i = 0; n; n /= 8, i++)
1919 *--s = n % 8 + '0';
1920 if (F.altForm && i && *s != '0') {
1921 *--s = '0';
1922 i++;
1923 }
1924 for (; i < F.precision; i++)
1925 *--s = '0';
1926 break;
1927
1928 /* hexadecimal (unsigned) */
1929
1930 case 'p':
1931 F.havePrecision = F.lSize = TRUE;
1932 F.precision = 8;
1933 /* ... */
1934 case 'X':
1935 digits = "0123456789ABCDEF";
1936 goto hexadecimal;
1937 case 'x':
1938 digits = "0123456789abcdef";
1939 /* ... */
1940 hexadecimal:
1941 if (F.lSize)
1942 n = va_arg(arg, unsigned long);
1943 else
1944 n = va_arg(arg, unsigned int);
1945 if (F.hSize)
1946 n = (unsigned short) n;
1947 if (!F.havePrecision) {
1948 if (F.zeroPad) {
1949 F.precision = F.fieldWidth;
1950 if (F.altForm)
1951 F.precision -= 2;
1952 }
1953 if (F.precision < 1)
1954 F.precision = 1;
1955 }
1956 for (i = 0; n; n /= 16, i++)
1957 *--s = digits[n % 16];
1958 for (; i < F.precision; i++)
1959 *--s = '0';
1960 if (F.altForm) {
1961 *--s = c;
1962 *--s = '0';
1963 i += 2;
1964 }
1965 break;
1966
1967 /* character */
1968
1969 case 'c':
1970 *--s = va_arg(arg, int);
1971 i = 1;
1972 break;
1973
1974 /* string */
1975
1976 case 's':
1977 s = va_arg(arg, char *);
1978 if (F.altForm) {
1979 i = (unsigned char) *s++;
1980 if (F.havePrecision && i > F.precision)
1981 i = F.precision;
1982 }
1983 else {
1984 if (!F.havePrecision)
1985 i = strlen(s);
1986 else if (t = memchr(s, '\0', F.precision))
1987 i = t - s;
1988 else
1989 i = F.precision;
1990 }
1991 break;
1992
1993 /* store # bytes written so far */
1994
1995 case 'n':
1996 s = va_arg(arg, void *);
1997 if (F.hSize)
1998 * (short *) s = nwritten;
1999 else if (F.lSize)
2000 * (long *) s = nwritten;
2001 else
2002 * (int *) s = nwritten;
2003 continue;
2004
2005 /* oops - unknown conversion, abort */
2006
2007 default:
2008 if (c != '%')
2009 goto done;
2010 copy1:
2011 putc(c, fp); /* disregard EOF */
2012 ++nwritten;
2013 continue;
2014 }
2015
2016 /* pad on the left */
2017
2018 if (i < F.fieldWidth && !F.leftJustify) {
2019 do {
2020 putc(' ', fp); /* disregard EOF */
2021 ++nwritten;
2022 } while (i < --F.fieldWidth);
2023 }
2024
2025 /* write the converted result */
2026
2027 fwrite(s, 1, i, fp); /* disregard EOF */
2028 nwritten += i;
2029
2030 /* pad on the right */
2031
2032 for (; i < F.fieldWidth; i++) {
2033 putc(' ', fp); /* disregard EOF */
2034 ++nwritten;
2035 }
2036 }
2037
2038 /* all done! */
2039
2040 done:
2041 return(nwritten);
2042 }
2043
2044 int
2045 snprintf(char *s, size_t size, const char *fmt, ...)
2046 {
2047 return(vsnprintf(s, size, fmt, __va(fmt)));
2048 }
2049
2050 static int
2051 nullio(FILE *fp, int i)
2052 {
2053 return(EOF);
2054 }
2055
2056 int
2057 vsnprintf(char *s, size_t size, const char *fmt, void *p)
2058 {
2059 FILE f;
2060 int n;
2061
2062 memset(&f, 0, sizeof(f));
2063 f.refnum = -1;
2064 f.ptr = (unsigned char *) s;
2065 f.cnt = size;
2066 f.proc = nullio;
2067 f.dirty = 1;
2068
2069 if ((n = bounded_vfprintf(&f, fmt, p)) >= 0)
2070 s[n] = 0;
2071 return(n);
2072 }