AmendHub

Download

jcs

/

subtext

/

util.c

 

(View History)

jcs   util: getpath doesn't need a Str255 *, it's not allocating anything Latest amendment: 454 on 2023-03-27

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