AmendHub

Download

jcs

/

subtext

/

util.c

 

(View History)

jcs   util: In progress, if string looks like it will wrap, move text up Latest amendment: 476 on 2023-04-10

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