AmendHub

Download

jcs

/

wallops

/

util.c

 

(View History)

jcs   main: Center connection dialog on the screen Latest amendment: 36 on 2022-09-06

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