AmendHub

Download

jcs

/

wallops

/

util.c

 

(View History)

jcs   util: Add an implementation of strcasestr Latest amendment: 124 on 2024-09-20

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 /* strcasecmp and strncasecmp:
21 *
22 * Copyright (c) 1987, 1993
23 * The Regents of the University of California. All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 * 1. Redistributions of source code must retain the above copyright
29 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer in the
32 * documentation and/or other materials provided with the distribution.
33 * 3. Neither the name of the University nor the names of its contributors
34 * may be used to endorse or promote products derived from this software
35 * without specific prior written permission.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 */
49
50 /* THINK C Project must have a header of "#include <MacHeaders>" */
51
52 #include <GestaltEqu.h>
53 #include <Folders.h>
54 #include <Script.h>
55 #include <SetUpA4.h>
56 #include <Traps.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include "util.h"
62
63 /* ALRT resources */
64 #define ASK_ALERT_ID 130
65
66 #define ERROR_STRING_SIZE 1024
67 static char err_str[ERROR_STRING_SIZE];
68
69 /* basic DITL with an ok button (1), text area (2), and icon (3) */
70 #define ALERT_DITL_OK 1
71 #define ALERT_DITL_ICON 3
72 static const char alert_ditl[] = {
73 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E,
74 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02,
75 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
76 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02,
77 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
78 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02,
79 0x00, 0x01
80 };
81 static Handle alert_ditl_h = NULL;
82
83 /* ICN# to show for APPICON_ALERT */
84 #define APPICON_ICN_ID 128
85
86 /* DITL with a Yes button (1), No button (2), text (3), and icon (4) */
87 static const char ask_ditl[] = {
88 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
89 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x03,
90 0x59, 0x65, 0x73, 0x21, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x46, 0x00, 0xA0, 0x00, 0x5A, 0x00, 0xDA,
92 0x04, 0x02, 0x4E, 0x6F, 0x00, 0x00, 0x00, 0x00,
93 0x00, 0x0A, 0x00, 0x32, 0x00, 0x41, 0x01, 0x22,
94 0x08, 0x02, 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00,
95 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A,
96 0xA0, 0x02, 0x00, 0x01
97 };
98 static Handle ask_ditl_h = NULL;
99
100 /* DITL with just a text view */
101 static const char progress_ditl[] = {
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
103 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02,
104 0x5E, 0x30
105 };
106 static Handle progress_ditl_h = NULL;
107 static DialogPtr progress_dialog = NULL;
108
109 static TEHandle track_control_te = NULL;
110
111 /*
112 * strcasecmp: This array is designed for mapping upper and lower case
113 * letter together for a case independent comparison. The mappings are
114 * based upon ascii character sequences.
115 */
116 static const unsigned char strcasecmp_charmap[] = {
117 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
118 '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
119 '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
120 '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
121 '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
122 '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
123 '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
124 '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
125 '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
126 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
127 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
128 '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
129 '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
130 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
131 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
132 '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
133 '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
134 '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
135 '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
136 '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
137 '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
138 '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
139 '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
140 '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
141 '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
142 '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
143 '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
144 '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
145 '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
146 '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
147 '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
148 '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
149 };
150
151 enum {
152 STOP_ALERT,
153 CAUTION_ALERT,
154 NOTE_ALERT,
155 APPICON_ALERT
156 };
157
158 void vwarn(short alert_func, const char *format, va_list ap);
159
160 /*
161 * Util helper needed to be called at program startup, to pre-allocate
162 * some things that we can't do during errors.
163 */
164
165 void
166 util_init(void)
167 {
168 alert_ditl_h = xNewHandle(sizeof(alert_ditl));
169 HLock(alert_ditl_h);
170 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
171 HUnlock(alert_ditl_h);
172 }
173
174 /*
175 * Memory functions
176 */
177
178 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
179
180 #ifdef MALLOC_DEBUG
181 /*
182 * malloc tracking, storing a stack trace of each xmalloc call and its
183 * size, which can be dumped with xalloc_print(). xfree also gains the
184 * ability to detect junk frees.
185 *
186 * Requires project options 'Always generate stack frames',
187 * 'Macsbug Names', and 'Long format' to be enabled.
188 */
189 #if !__option(force_frame) || !__option(long_macsbug_names)
190 #error
191 #endif
192
193 #define XALLOC_SYMBOL_SIZE 32
194 #define XALLOC_NSYMBOLS 64
195 #define XALLOC_MAX_STACK_DEPTH 10
196 static char *symbols = NULL;
197 struct xalloc {
198 char *stack[XALLOC_MAX_STACK_DEPTH];
199 unsigned long addr;
200 unsigned long size;
201 };
202 #define XALLOC_NXALLOCS 512
203 static struct xalloc *xallocs = NULL;
204 #endif
205
206 void *
207 xmalloc(size_t size)
208 {
209 void *ptr;
210 #ifdef MALLOC_DEBUG
211 static char cursym[XALLOC_SYMBOL_SIZE], *tsym, *sym;
212 struct xalloc *xa;
213 unsigned long _a6, ret;
214 unsigned char *code;
215 size_t n, cn, j;
216 short nframe;
217 #endif
218
219 if (size == 0)
220 panic("xmalloc: zero size");
221
222 ptr = NewPtr(size);
223 if (ptr == NULL)
224 return NULL;
225
226 #ifdef MALLOC_DEBUG
227 asm {
228 move.l a6,_a6
229 };
230
231 if (symbols == NULL) {
232 symbols = NewPtr(n = XALLOC_SYMBOL_SIZE * XALLOC_NSYMBOLS);
233 if (symbols == NULL)
234 panic("NewPtr failed for xalloc symbols");
235 memset(symbols, 0, n);
236 }
237 if (xallocs == NULL) {
238 xallocs = (struct xalloc *)NewPtr(n = (XALLOC_NXALLOCS *
239 sizeof(struct xalloc)));
240 if (xallocs == NULL)
241 panic("NewPtr failed for xallocs");
242 memset(xallocs, 0, n);
243 }
244
245 xa = NULL;
246 for (n = 0; n < XALLOC_NXALLOCS; n++) {
247 if (xallocs[n].addr == 0) {
248 xa = &xallocs[n];
249 break;
250 }
251 }
252 if (xa == NULL)
253 panic("out of xalloc space");
254
255 /*
256 * A6 register points to previous stack A6. Above A6 is our return
257 * address. Walk the code after the return address looking for an RTS
258 * instruction, then walk 3 bytes after that and we should have that
259 * function's symbol. Then jump to the previous A6 that our A6 points
260 * to and do it again to find the previous function symbol, repeating
261 * until we hit "main" or "uthread_begin".
262 */
263 for (nframe = 0; nframe < XALLOC_MAX_STACK_DEPTH; nframe++) {
264 ret = *(unsigned long *)(_a6 + 4);
265 code = (unsigned char *)ret;
266
267 cursym[0] = '\0';
268 for (; ; code += 2) {
269 if (code[0] == 0x4e && code[1] == 0x75) { /* RTS */
270 code += 3;
271 strlcpy(cursym, (char *)code, sizeof(cursym));
272 break;
273 }
274 }
275
276 if (!cursym[0])
277 break;
278
279 sym = NULL;
280 for (n = 0; n < XALLOC_NSYMBOLS; n++) {
281 tsym = symbols + (XALLOC_SYMBOL_SIZE * n);
282
283 if (tsym[0] == '\0')
284 break;
285 if (strcmp(tsym, cursym) == 0) {
286 sym = tsym;
287 break;
288 }
289 }
290 if (sym == NULL) {
291 for (n = 0; n < XALLOC_NSYMBOLS; n++) {
292 tsym = symbols + (XALLOC_SYMBOL_SIZE * n);
293 if (tsym[0] != '\0')
294 continue;
295
296 strlcpy(tsym, cursym, XALLOC_SYMBOL_SIZE);
297 sym = tsym;
298 break;
299 }
300 }
301 if (sym == NULL)
302 break;
303
304 xa->stack[nframe] = sym;
305
306 if (strcmp(sym, "main") == 0 || strcmp(sym, "uthread_begin") == 0)
307 break;
308
309 /* walk back up the chain */
310 _a6 = *(unsigned long *)_a6;
311 }
312
313 xa->addr = (unsigned long)ptr;
314 xa->size = size;
315 #endif
316
317 return ptr;
318 }
319
320 void
321 xfree(void *ptrptr)
322 {
323 unsigned long *addr = (unsigned long *)ptrptr;
324 void *ptr;
325 #ifdef MALLOC_DEBUG
326 size_t n;
327 #endif
328
329 if (ptrptr == NULL)
330 panic("xfree(NULL)");
331
332 ptr = (void *)*addr;
333 if (ptr == NULL)
334 panic("xfree(&NULL) likely a double-free");
335
336 #ifdef MALLOC_DEBUG
337 for (n = 0; n < XALLOC_NXALLOCS; n++) {
338 if (xallocs[n].addr == (unsigned long)ptr) {
339 memset(&xallocs[n], 0, sizeof(struct xalloc));
340 break;
341 }
342 }
343 if (n == XALLOC_NXALLOCS)
344 panic("xfree() without xalloc, possibly a junk free",
345 (unsigned long)ptr);
346 #endif
347
348 DisposePtr(ptr);
349 *addr = 0L;
350 }
351
352 void *
353 xmalloczero(size_t size)
354 {
355 void *ptr;
356
357 ptr = xmalloc(size);
358 if (ptr != NULL)
359 memset(ptr, 0, size);
360
361 return ptr;
362 }
363
364 void *
365 xcalloc(size_t nmemb, size_t size)
366 {
367 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
368 nmemb > 0 && SIZE_MAX / nmemb < size)
369 panic("xcalloc(%lu, %lu) overflow", nmemb, size);
370
371 return xmalloczero(nmemb * size);
372 }
373
374 void *
375 xrealloc(void *src, size_t size)
376 {
377 void *ptr, *tsrc;
378
379 ptr = xmalloc(size);
380 if (ptr != NULL && src != NULL) {
381 memcpy(ptr, src, size);
382 tsrc = src;
383 xfree(&tsrc);
384 }
385
386 return ptr;
387 }
388
389 void *
390 xreallocarray(void *optr, size_t nmemb, size_t size)
391 {
392 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
393 nmemb > 0 && SIZE_MAX / nmemb < size)
394 panic("xreallocarray(%lu, %lu) failed", nmemb, size);
395
396 return xrealloc(optr, size * nmemb);
397 }
398
399 char *
400 xstrdup(const char *str)
401 {
402 char *cp;
403 size_t len;
404
405 len = strlen(str);
406 cp = xmalloc(len + 1);
407 if (cp != NULL)
408 strlcpy(cp, str, len + 1);
409
410 return cp;
411 }
412
413 char *
414 xstrndup(const char *str, size_t maxlen)
415 {
416 char *copy;
417 const char *cp;
418 size_t len;
419
420 /* strnlen */
421 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
422 ;
423
424 len = (size_t)(cp - str);
425 copy = xmalloc(len + 1);
426 if (copy != NULL) {
427 (void)memcpy(copy, str, len);
428 copy[len] = '\0';
429 }
430
431 return copy;
432 }
433
434 #ifdef MALLOC_DEBUG
435 void
436 xalloc_print(size_t (*printer)(const char *, ...))
437 {
438 ssize_t n, j, len;
439 size_t total = 0, nallocs = 0;
440 char chain[128];
441
442 for (n = 0; n < XALLOC_NXALLOCS; n++) {
443 if (xallocs[n].addr == 0)
444 continue;
445
446 total += xallocs[n].size;
447 nallocs++;
448
449 chain[0] = '\0';
450 len = 0;
451 for (j = XALLOC_MAX_STACK_DEPTH - 1; j >= 0; j--) {
452 if (xallocs[n].stack[j] == 0)
453 continue;
454
455 if (chain[0] != '\0') {
456 if (len >= sizeof(chain) - 2)
457 break;
458 chain[len++] = ' ';
459 chain[len++] = ':';
460 chain[len++] = ' ';
461 chain[len++] = '\0';
462 }
463
464 len = strlcat(chain, xallocs[n].stack[j], sizeof(chain));
465 if (len >= sizeof(chain))
466 break;
467 }
468
469 printer("%ld @ %s", xallocs[n].size, chain);
470 }
471
472 printer("%ld bytes in %ld allocations in use", total, nallocs);
473 }
474 #endif
475
476 /*
477 * String functions
478 */
479
480 short
481 getline(char *str, size_t len, char **ret)
482 {
483 short i;
484
485 for (i = 0; i < len; i++) {
486 if (str[i] == '\r' || i == len - 1) {
487 if (*ret == NULL)
488 *ret = xmalloc(i + 1);
489 memcpy(*ret, str, i + 1);
490 (*ret)[i] = '\0';
491 return i + 1;
492 }
493 }
494
495 return 0;
496 }
497
498 const char *
499 ordinal(unsigned short n)
500 {
501 switch (n % 100) {
502 case 11:
503 case 12:
504 case 13:
505 return "th";
506 default:
507 switch (n % 10) {
508 case 1:
509 return "st";
510 case 2:
511 return "nd";
512 case 3:
513 return "rd";
514 default:
515 return "th";
516 }
517 }
518 }
519
520 size_t
521 rtrim(char *str, char *chars)
522 {
523 size_t len, rlen, n, j;
524
525 rlen = len = strlen(str);
526
527 for (n = len; n > 0; n--) {
528 for (j = 0; chars[j] != '\0'; j++) {
529 if (str[n - 1] == chars[j]) {
530 rlen--;
531 str[n - 1] = '\0';
532 goto next_in_str;
533 }
534 }
535
536 break;
537 next_in_str:
538 continue;
539 }
540
541 return rlen;
542 }
543
544 long
545 strpos_quoted(char *str, char c)
546 {
547 long pos;
548 unsigned char quot = 0;
549
550 for (pos = 0; str[pos] != '\0'; pos++) {
551 if (quot) {
552 if (str[pos] == '\\') {
553 pos++;
554 continue;
555 }
556 if (str[pos] == quot)
557 quot = 0;
558 continue;
559 } else {
560 if (str[pos] == '"') {
561 quot = str[pos];
562 continue;
563 }
564 if (str[pos] == c)
565 return pos;
566 }
567 }
568
569 return -1;
570 }
571
572 size_t
573 hexify(unsigned char *input, size_t len, unsigned char *output)
574 {
575 static const char hexchars[] = "0123456789abcdef";
576 size_t n, retlen = 0;
577
578 for (n = 0; n < len; n++) {
579 *output++ = hexchars[input[n] >> 4];
580 *output++ = hexchars[input[n] & 0xf];
581 retlen += 2;
582 }
583
584 return retlen;
585 }
586
587 size_t
588 unhexify(unsigned char *input, size_t len, unsigned char *output)
589 {
590 size_t n, retlen = 0;
591 uint8_t c;
592
593 for (n = 0; n < len; n++, input++) {
594 if (*input >= '0' && *input <= '9')
595 c = *input - '0';
596 else if (*input >= 'a' && *input <= 'f')
597 c = *input - 'a' + 10;
598 else if (*input >= 'A' && *input <= 'F')
599 c = *input - 'A' + 10;
600 else
601 continue;
602
603 if (n % 2 == 0)
604 *output = c << 4;
605 else {
606 *output |= c;
607 retlen++;
608 output++;
609 }
610 }
611
612 return retlen;
613 }
614
615 char *
616 OSTypeToString(OSType type)
617 {
618 static char ostype_s[5];
619
620 ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
621 ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
622 ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
623 ostype_s[3] = (unsigned char)(type & 0xff);
624 ostype_s[4] = 0;
625
626 return ostype_s;
627 }
628
629 short
630 strcasecmp(const char *s1, const char *s2)
631 {
632 const unsigned char *cm = strcasecmp_charmap;
633 const unsigned char *us1 = (const unsigned char *)s1;
634 const unsigned char *us2 = (const unsigned char *)s2;
635
636 while (cm[*us1] == cm[*us2++])
637 if (*us1++ == '\0')
638 return (0);
639 return (cm[*us1] - cm[*--us2]);
640 }
641
642 short
643 strncasecmp(const char *s1, const char *s2, size_t n)
644 {
645 if (n != 0) {
646 const unsigned char *cm = strcasecmp_charmap;
647 const unsigned char *us1 = (const unsigned char *)s1;
648 const unsigned char *us2 = (const unsigned char *)s2;
649
650 do {
651 if (cm[*us1] != cm[*us2++])
652 return (cm[*us1] - cm[*--us2]);
653 if (*us1++ == '\0')
654 break;
655 } while (--n != 0);
656 }
657 return (0);
658 }
659
660 char *
661 strcasestr(const char *s, const char *find)
662 {
663 const unsigned char *cm = strcasecmp_charmap;
664 const unsigned char *us = (const unsigned char *)s;
665 const unsigned char *ufind = (const unsigned char *)find;
666 const unsigned char *tus, *tufind;
667
668 if (find == NULL)
669 return NULL;
670 if (find[0] == '\0')
671 return (char *)s;
672
673 while (*us) {
674 if (cm[*us] == cm[*ufind]) {
675 tus = us;
676 tufind = ufind;
677
678 while (*tufind) {
679 if (*tus == '\0')
680 goto advance_us;
681 if (cm[*tus] != cm[*tufind])
682 goto advance_us;
683 tus++;
684 tufind++;
685 }
686
687 return (char *)us;
688 }
689
690 advance_us:
691 us++;
692 }
693
694 return NULL;
695 }
696
697 /*
698 * BSD warn(3) functions
699 */
700
701 void
702 vwarn(short alert_func, const char *format, va_list ap)
703 {
704 Rect bounds;
705 short hit, itype;
706 WindowPtr win, dialog;
707 Handle ihandle, okhandle;
708 Rect irect;
709
710 progress(NULL);
711 GetPort(&win);
712
713 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
714 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
715
716 center_in_screen(320, 110, false, &bounds);
717 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
718 (WindowPtr)-1L, false, 0, alert_ditl_h);
719
720 GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect);
721 switch (alert_func) {
722 case CAUTION_ALERT:
723 ihandle = GetIcon(cautionIcon);
724 break;
725 case NOTE_ALERT:
726 ihandle = GetIcon(noteIcon);
727 break;
728 case APPICON_ALERT:
729 ihandle = GetResource('ICN#', APPICON_ICN_ID);
730 if (ihandle)
731 break;
732 default:
733 ihandle = GetIcon(stopIcon);
734 }
735 SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect);
736
737 ShowWindow(dialog);
738
739 for (;;) {
740 ModalDialog(ModalDialogFilter, &hit);
741 if (hit == ok)
742 break;
743 }
744 ReleaseResource(ihandle);
745 DisposDialog(dialog);
746
747 /*
748 * Pre-allocate for the next one while we have memory. We can't use
749 * xNewHandle because we might already be alerting about an OOM
750 * condition.
751 */
752 DisposHandle(alert_ditl_h);
753 alert_ditl_h = NewHandle(sizeof(alert_ditl));
754 if (alert_ditl_h == NULL)
755 ExitToShell();
756 HLock(alert_ditl_h);
757 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
758 HUnlock(alert_ditl_h);
759
760 SetPort(win);
761 }
762
763 void
764 panic(const char *format, ...)
765 {
766 va_list ap;
767
768 va_start(ap, format);
769 vwarn(STOP_ALERT, format, ap);
770 va_end(ap);
771
772 ExitToShell();
773 }
774
775 void
776 warn(const char *format, ...)
777 {
778 va_list ap;
779
780 va_start(ap, format);
781 vwarn(CAUTION_ALERT, format, ap);
782 va_end(ap);
783 }
784
785 void
786 note(const char *format, ...)
787 {
788 va_list ap;
789
790 va_start(ap, format);
791 vwarn(NOTE_ALERT, format, ap);
792 va_end(ap);
793 }
794
795 void
796 appicon_note(const char *format, ...)
797 {
798 va_list ap;
799
800 va_start(ap, format);
801 vwarn(APPICON_ALERT, format, ap);
802 va_end(ap);
803 }
804
805 short
806 ask(const char *format, ...)
807 {
808 Rect bounds;
809 WindowPtr win, dialog;
810 va_list ap;
811 short hit;
812
813 GetPort(&win);
814
815 va_start(ap, format);
816 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
817 va_end(ap);
818
819 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
820
821 ask_ditl_h = xNewHandle(sizeof(ask_ditl));
822 HLock(ask_ditl_h);
823 memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl));
824 HUnlock(ask_ditl_h);
825
826 center_in_screen(300, 100, false, &bounds);
827 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
828 (WindowPtr)-1L, false, 0, ask_ditl_h);
829
830 ShowWindow(dialog);
831 for (;;) {
832 ModalDialog(ModalDialogFilter, &hit);
833 if (hit == 1 || hit == 2)
834 break;
835 }
836 DisposDialog(dialog);
837 DisposHandle(ask_ditl_h);
838
839 SetPort(win);
840
841 return (hit == 1);
842 }
843
844 void
845 about(char *program_name)
846 {
847 VersRecHndl vers;
848 char vers_s[255];
849 char short_vers[255] = { 0 };
850 short vlen, n;
851
852 if ((vers = (VersRecHndl)GetResource('vers', 1))) {
853 /*
854 * vers "long version string" is a pascal string after the
855 * short version pascal string
856 */
857 HLock(vers);
858 vlen = (*vers)->shortVersion[0];
859 memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
860 sizeof((*vers)->shortVersion) - vlen - 1);
861 PtoCstr(short_vers);
862 snprintf(vers_s, sizeof(vers_s), "%s %s", program_name,
863 short_vers);
864 for (n = 0; n < sizeof(vers_s); n++) {
865 if (vers_s[n] == '©') {
866 vers_s[n - 1] = '\r';
867 break;
868 }
869 }
870 ReleaseResource(vers);
871 appicon_note("%s", vers_s);
872 } else
873 warn("Can't find version number!");
874 }
875
876 void
877 progress(char *format, ...)
878 {
879 static Str255 progress_s;
880 Handle thandle;
881 va_list argptr;
882 Rect bounds;
883 Rect trect;
884 short ttype;
885
886 if (format == NULL) {
887 if (progress_dialog != NULL) {
888 DisposDialog(progress_dialog);
889 DisposHandle(progress_ditl_h);
890 progress_dialog = NULL;
891 }
892 return;
893 }
894
895 va_start(argptr, format);
896 vsnprintf((char *)progress_s, 256, format, argptr);
897 va_end(argptr);
898 CtoPstr(progress_s);
899
900 if (progress_dialog == NULL) {
901 progress_ditl_h = xNewHandle(sizeof(progress_ditl));
902 HLock(progress_ditl_h);
903 memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl));
904 HUnlock(progress_ditl_h);
905
906 center_in_screen(330, 60, false, &bounds);
907 progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
908 (WindowPtr)-1L, false, 0, progress_ditl_h);
909 }
910
911 GetDItem(progress_dialog, 1, &ttype, &thandle, &trect);
912 SetIText(thandle, progress_s);
913
914 ShowWindow(progress_dialog);
915 DrawDialog(progress_dialog);
916 }
917
918 void
919 window_rect(WindowPtr win, Rect *ret)
920 {
921 *ret = (*(((WindowPeek)win)->strucRgn))->rgnBBox;
922 ret->left += 1;
923 ret->right -= 2;
924 ret->top -= 1;
925 ret->bottom -= 1;
926 }
927
928 void
929 center_in_screen(short width, short height, bool titlebar, Rect *b)
930 {
931 b->left = ((screenBits.bounds.right - screenBits.bounds.left) / 2) -
932 (width / 2);
933 b->top = ((screenBits.bounds.bottom - screenBits.bounds.top) / 2) -
934 (height / 2) + (titlebar ? GetMBarHeight() : 0);
935 b->right = b->left + width;
936 b->bottom = b->top + height;
937 }
938
939 Point
940 centered_sfget_dialog(void)
941 {
942 Point p;
943 Rect r;
944
945 center_in_screen(364, 216, false, &r);
946 p.h = r.left;
947 p.v = r.top;
948
949 return p;
950 }
951
952 Point
953 centered_sfput_dialog(void)
954 {
955 Point p;
956 Rect r;
957
958 center_in_screen(320, 200, false, &r);
959 p.h = r.left;
960 p.v = r.top;
961
962 return p;
963 }
964
965 void
966 outline_button(ControlHandle button)
967 {
968 PenState pen;
969 Rect rect;
970
971 SetPort((*button)->contrlOwner);
972 GetPenState(&pen);
973 PenNormal();
974
975 rect = (*button)->contrlRect;
976 InsetRect(&rect, -4, -4);
977 PenSize(3, 3);
978 FrameRoundRect(&rect, (rect.bottom - rect.top) / 2,
979 (rect.bottom - rect.top) / 2);
980 SetPenState(&pen);
981 }
982
983 /*
984 * General Mac-specific non-GUI functions
985 */
986
987 static unsigned long _xorshift_state = 0;
988 unsigned long
989 xorshift32(void)
990 {
991 unsigned long x = _xorshift_state;
992 if (x == 0)
993 x = Ticks;
994 x ^= x << 13;
995 x ^= x >> 17;
996 x ^= x << 5;
997 return _xorshift_state = x;
998 }
999
1000
1001 /*
1002 * Error checking wrappers for Mac toolkit functions
1003 */
1004
1005 Handle
1006 xNewHandle(size_t size)
1007 {
1008 Handle h;
1009
1010 if (size == 0)
1011 panic("Zero xNewHandle size");
1012
1013 h = NewHandle(size);
1014 if (h == NULL)
1015 panic("Failed to NewHandle(%lu)", size);
1016
1017 return h;
1018 }
1019
1020 Handle
1021 xGetResource(ResType type, short id)
1022 {
1023 Handle h;
1024
1025 h = GetResource(type, id);
1026 if (h == NULL)
1027 panic("Failed to find resource %d", id);
1028
1029 return h;
1030 }
1031
1032 StringHandle
1033 xGetString(short id)
1034 {
1035 StringHandle h;
1036
1037 h = GetString(id);
1038 if (h == NULL)
1039 panic("Failed to find STR resource %d", id);
1040
1041 return h;
1042 }
1043
1044 char *
1045 xGetStringAsChar(short id)
1046 {
1047 StringHandle h;
1048 char *out;
1049 size_t l;
1050
1051 h = xGetString(id);
1052 HLock(h);
1053 l = (*h)[0];
1054 out = xmalloc(l + 1);
1055 memcpy((void *)out, (void *)(*h + 1), l);
1056 out[l] = '\0';
1057 ReleaseResource(h);
1058
1059 return out;
1060 }
1061
1062 long
1063 xGetStringAsLong(short id)
1064 {
1065 char *c;
1066 long r;
1067
1068 c = xGetStringAsChar(id);
1069 r = atol(c);
1070 xfree(&c);
1071 return r;
1072 }
1073
1074 void
1075 xSetHandleSize(Handle h, Size s)
1076 {
1077 SetHandleSize(h, s);
1078 if (MemError())
1079 panic("Failed to SetHandleSize to %ld", s);
1080 }
1081
1082 /*
1083 * Filesystem utilities
1084 */
1085
1086 short
1087 getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file)
1088 {
1089 WDPBRec wdir;
1090 HVolumeParam wvol;
1091 DirInfo wcinfo;
1092 char *name = NULL;
1093 size_t retlen = 0, len;
1094 char *tmpret = NULL, *tmp = NULL;
1095 char *lastcolon;
1096
1097 if (strchr((char *)fileName + 1, ':') != NULL) {
1098 /* already a full path */
1099 memcpy(*ret, fileName, 256);
1100 if (!include_file) {
1101 PtoCstr(*ret);
1102 lastcolon = strrchr((char *)*ret, ':');
1103 lastcolon[0] = '\0';
1104 CtoPstr(*ret);
1105 }
1106 return 0;
1107 }
1108
1109 name = xmalloc(FILENAME_MAX);
1110
1111 wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
1112 wdir.ioWDIndex = 0;
1113 wdir.ioWDProcID = 0;
1114 wdir.ioNamePtr = (StringPtr)name;
1115 if (PBGetWDInfo(&wdir, 0) != noErr) {
1116 warn("Failed looking up directory");
1117 xfree(&name);
1118 return 1;
1119 }
1120
1121 wvol.ioNamePtr = (StringPtr)name;
1122 wvol.ioVRefNum = vRefNum;
1123 wvol.ioVolIndex = 0;
1124
1125 if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
1126 warn("Failed getting volume info");
1127 xfree(&name);
1128 return 1;
1129 }
1130
1131 if (wvol.ioVSigWord != 0x4244) {
1132 warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
1133 xfree(&name);
1134 return 1;
1135 }
1136
1137 wcinfo.ioVRefNum = vRefNum;
1138 wcinfo.ioNamePtr = (StringPtr)name;
1139 wcinfo.ioFDirIndex = -1;
1140 wcinfo.ioDrParID = wdir.ioWDDirID;
1141 wcinfo.ioDrDirID = wdir.ioWDDirID;
1142
1143 tmp = xmalloc(FILENAME_MAX);
1144 tmpret = xmalloc(FILENAME_MAX);
1145
1146 /* go backwards, prepending each folder's parent */
1147 while (wcinfo.ioDrParID != 1) {
1148 wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
1149
1150 if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
1151 break;
1152
1153 len = name[0];
1154 PtoCstr(name);
1155 if (retlen == 0) {
1156 retlen = len;
1157 strlcpy(tmpret, (char *)name, FILENAME_MAX);
1158 } else {
1159 strlcpy(tmp, tmpret, FILENAME_MAX);
1160 snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp);
1161 }
1162 }
1163
1164 if (include_file) {
1165 /* append the original path */
1166 memcpy(name, fileName, FILENAME_MAX);
1167 PtoCstr(name);
1168 if (retlen == 0)
1169 strlcpy(tmpret, name, FILENAME_MAX);
1170 else {
1171 strlcat(tmpret, ":", FILENAME_MAX);
1172 strlcat(tmpret, name, FILENAME_MAX);
1173 }
1174 } else if (retlen == 0) {
1175 (*ret)[0] = 0;
1176 xfree(&tmp);
1177 xfree(&tmpret);
1178 xfree(&name);
1179 return 0;
1180 }
1181
1182 CtoPstr(tmpret);
1183 memcpy(*ret, tmpret, FILENAME_MAX);
1184 xfree(&tmp);
1185 xfree(&tmpret);
1186 xfree(&name);
1187
1188 return 0;
1189 }
1190
1191 short
1192 stat(char *path, struct stat *sb)
1193 {
1194 char *ppath;
1195 short ret;
1196
1197 ppath = xstrdup(path);
1198 CtoPstr(ppath);
1199 ret = FStat((unsigned char *)ppath, sb);
1200 xfree(&ppath);
1201
1202 return ret;
1203 }
1204
1205 short
1206 FStat(Str255 path, struct stat *sb)
1207 {
1208 CInfoPBRec catblock = { 0 };
1209 short ret;
1210
1211 catblock.hFileInfo.ioNamePtr = path;
1212 ret = PBGetCatInfo(&catblock, 0);
1213 if (ret != noErr)
1214 return -1;
1215
1216 /* data fork logical length + resource fork logical length */
1217 sb->st_size = catblock.hFileInfo.ioFlLgLen +
1218 catblock.hFileInfo.ioFlRLgLen;
1219 sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
1220 sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
1221 sb->st_flags = catblock.hFileInfo.ioFlAttrib;
1222
1223 return 0;
1224 }
1225
1226 bool
1227 FIsDir(Str255 path)
1228 {
1229 struct stat st;
1230 short ret;
1231
1232 if (FStat(path, &st) != 0)
1233 return false;
1234
1235 /* bit 4 is set in ioFlAttrib if the item is a directory */
1236 if (st.st_flags & (1 << 4))
1237 return true;
1238
1239 return false;
1240 }
1241
1242 OSErr
1243 copy_file(Str255 source, Str255 dest, bool overwrite)
1244 {
1245 FInfo fi;
1246 IOParam pb;
1247 short error, source_ref, dest_ref;
1248
1249 /* copy data fork */
1250
1251 error = GetFInfo(source, 0, &fi);
1252 if (error)
1253 return error;
1254
1255 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1256 if (error == dupFNErr && overwrite) {
1257 error = FSDelete(dest, 0);
1258 if (error)
1259 return error;
1260 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1261 }
1262 if (error)
1263 return error;
1264
1265 memset(&pb, 0, sizeof(pb));
1266 pb.ioNamePtr = source;
1267 pb.ioPermssn = fsRdPerm;
1268 error = PBOpen(&pb, false);
1269 if (error)
1270 return error;
1271 source_ref = pb.ioRefNum;
1272
1273 error = FSOpen(dest, 0, &dest_ref);
1274 if (error) {
1275 FSClose(source_ref);
1276 return error;
1277 }
1278
1279 error = copy_file_contents(source_ref, dest_ref);
1280
1281 FSClose(source_ref);
1282 FSClose(dest_ref);
1283
1284 if (error)
1285 return error;
1286
1287 /*
1288 * Copy resource fork, open source as shared read/write in case it's
1289 * an open resource file.
1290 */
1291 source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
1292
1293 if (source_ref == -1 && ResError() == eofErr) {
1294 /* no resource fork */
1295 FSClose(source_ref);
1296 FSClose(dest_ref);
1297 return 0;
1298 }
1299
1300 if (source_ref == -1)
1301 return ResError();
1302
1303 CreateResFile(dest);
1304 if (ResError()) {
1305 FSClose(source_ref);
1306 return ResError();
1307 }
1308 error = OpenRF(dest, 0, &dest_ref);
1309 if (error) {
1310 FSClose(source_ref);
1311 return error;
1312 }
1313
1314 error = copy_file_contents(source_ref, dest_ref);
1315
1316 FSClose(source_ref);
1317 FSClose(dest_ref);
1318
1319 return error;
1320 }
1321
1322 OSErr
1323 copy_file_contents(short source_ref, short dest_ref)
1324 {
1325 char *buf;
1326 short error;
1327 long source_size, count;
1328
1329 GetEOF(source_ref, &source_size);
1330 error = SetFPos(source_ref, fsFromStart, 0);
1331 if (error)
1332 return error;
1333 error = SetEOF(dest_ref, source_size);
1334 if (error)
1335 return error;
1336 error = SetFPos(dest_ref, fsFromStart, 0);
1337 if (error)
1338 return error;
1339
1340 buf = xmalloc(1024);
1341
1342 while (source_size > 0) {
1343 count = 1024;
1344 if (count > source_size)
1345 count = source_size;
1346 error = FSRead(source_ref, &count, buf);
1347 if (error && error != eofErr)
1348 break;
1349 source_size -= count;
1350 error = FSWrite(dest_ref, &count, buf);
1351 if (error && error != eofErr)
1352 break;
1353 }
1354
1355 xfree(&buf);
1356
1357 if (error && error != eofErr)
1358 return error;
1359
1360 return 0;
1361 }
1362
1363 /* read a \r-terminated line or the final non-line bytes of an open file */
1364 OSErr
1365 FSReadLine(short frefnum, char *buf, size_t buflen)
1366 {
1367 char tbuf;
1368 size_t pos, fsize, rlen = 1, total_read = 0;
1369 short error;
1370
1371 GetFPos(frefnum, &pos);
1372 GetEOF(frefnum, &fsize);
1373
1374 for (; pos <= fsize; pos++) {
1375 if (total_read > buflen)
1376 return -1;
1377
1378 error = FSRead(frefnum, &rlen, &tbuf);
1379 if (error)
1380 return -1;
1381
1382 if (tbuf == '\r')
1383 return total_read;
1384
1385 buf[total_read++] = tbuf;
1386 }
1387
1388 /* nothing found until the end of the file */
1389 return total_read;
1390 }
1391
1392 TrapType
1393 GetTrapType(unsigned long theTrap)
1394 {
1395 if (BitAnd(theTrap, 0x0800) > 0)
1396 return ToolTrap;
1397
1398 return OSTrap;
1399 }
1400
1401 bool
1402 TrapAvailable(unsigned long trap)
1403 {
1404 TrapType trapType = ToolTrap;
1405 unsigned long numToolBoxTraps;
1406
1407 if (NGetTrapAddress(_InitGraf, ToolTrap) ==
1408 NGetTrapAddress(0xAA6E, ToolTrap))
1409 numToolBoxTraps = 0x200;
1410 else
1411 numToolBoxTraps = 0x400;
1412
1413 trapType = GetTrapType(trap);
1414 if (trapType == ToolTrap) {
1415 trap = BitAnd(trap, 0x07FF);
1416 if (trap >= numToolBoxTraps)
1417 trap = _Unimplemented;
1418 }
1419
1420 return (NGetTrapAddress(trap, trapType) !=
1421 NGetTrapAddress(_Unimplemented, ToolTrap));
1422 }
1423
1424 void
1425 GetSystemFolder(short *vRefNumP, long *dirIDP)
1426 {
1427 SysEnvRec info;
1428 long wdProcID;
1429
1430 SysEnvirons(1, &info);
1431 if (GetWDInfo(info.sysVRefNum, vRefNumP, dirIDP, &wdProcID) != noErr) {
1432 *vRefNumP = 0;
1433 *dirIDP = 0;
1434 }
1435 }
1436
1437 void
1438 GetSystemSubfolder(OSType folder, bool create, short *vRefNumP, long *dirIDP)
1439 {
1440 bool hasFolderMgr = false;
1441 long feature;
1442
1443 if (TrapAvailable(_GestaltDispatch) &&
1444 Gestalt(gestaltFindFolderAttr, &feature) == noErr)
1445 hasFolderMgr = true;
1446
1447 if (!hasFolderMgr) {
1448 GetSystemFolder(vRefNumP, dirIDP);
1449 return;
1450 }
1451
1452 if (FindFolder(kOnSystemDisk, folder, create, vRefNumP,
1453 dirIDP) != noErr) {
1454 *vRefNumP = 0;
1455 *dirIDP = 0;
1456 }
1457 }
1458
1459 /*
1460 * Gestalt functions
1461 */
1462 char *
1463 gestalt_machine_type(void)
1464 {
1465 short error;
1466 long resp;
1467
1468 error = Gestalt(gestaltMachineType, &resp);
1469 if (error)
1470 return NULL;
1471 switch (resp) {
1472 case gestaltClassic:
1473 return "Macintosh 128K";
1474 case gestaltMacXL:
1475 return "Macintosh XL";
1476 case gestaltMac512KE:
1477 return "Macintosh 512Ke";
1478 case gestaltMacPlus:
1479 return "Macintosh Plus";
1480 case gestaltMacSE:
1481 return "Macintosh SE";
1482 case gestaltMacII:
1483 return "Macintosh II";
1484 case gestaltMacIIx:
1485 return "Macintosh IIx";
1486 case gestaltMacIIcx:
1487 return "Macintosh IIcx";
1488 case gestaltMacSE030:
1489 return "Macintosh SE/30";
1490 case gestaltPortable:
1491 return "Macintosh Portable";
1492 case gestaltMacIIci:
1493 return "Macintosh IIci";
1494 case gestaltMacIIfx:
1495 return "Macintosh IIfx";
1496 case gestaltMacClassic:
1497 return "Macintosh Classic";
1498 case gestaltMacIIsi:
1499 return "Macintosh IIsi";
1500 case gestaltMacLC:
1501 return "Macintosh LC";
1502 }
1503
1504 return NULL;
1505 }
1506
1507 char *
1508 get_version(bool long_version)
1509 {
1510 static char vers_s[256] = { 0 };
1511 char *v;
1512 VersRecHndl vers;
1513 short len;
1514
1515 vers = (VersRecHndl)GetResource('vers', 1);
1516 if (!vers)
1517 return "?.?";
1518
1519 HLock(vers);
1520 v = (char *)&(*vers)->shortVersion;
1521 len = v[0];
1522 if (long_version) {
1523 v += len + 1;
1524 len = v[0];
1525 }
1526 memcpy(vers_s, v, len + 1);
1527 ReleaseResource(vers);
1528
1529 PtoCstr(vers_s);
1530 return vers_s;
1531 }
1532
1533 bool
1534 screen_saver_running(void)
1535 {
1536 short error;
1537 long resp;
1538
1539 error = Gestalt('SAVR', &resp); /* gestaltScreenSaverAttr */
1540 if (error == 0 && (resp & (1 << 1))) /* gestaltSaverAsleep */
1541 return true;
1542
1543 return false;
1544 }
1545
1546 /*
1547 * General Mac-specific GUI functions
1548 */
1549
1550 short
1551 FontHeight(short font_id, short size)
1552 {
1553 FMInput f_in;
1554 FMOutput *f_out;
1555
1556 if (font_id == -1)
1557 font_id = thePort->txFont;
1558 if (size == -1)
1559 size = thePort->txSize;
1560
1561 f_in.family = font_id;
1562 f_in.size = size;
1563 f_in.face = 0;
1564 f_in.needBits = false;
1565 f_in.device = 0;
1566 f_in.numer.h = f_in.numer.v = 1;
1567 f_in.denom.h = f_in.denom.v = 1;
1568
1569 f_out = FMSwapFont(&f_in);
1570
1571 return (((f_out->leading + f_out->ascent + f_out->descent) *
1572 f_out->numer.v) / f_out->denom.v);
1573 }
1574
1575 void
1576 DrawGrowIconOnly(WindowPtr win)
1577 {
1578 Rect r;
1579 RgnHandle tmp;
1580
1581 GetClip(tmp = NewRgn());
1582 r = win->portRect;
1583 r.top = r.bottom - SCROLLBAR_WIDTH;
1584 r.left = r.right - SCROLLBAR_WIDTH + 1;
1585 ClipRect(&r);
1586 DrawGrowIcon(win);
1587 SetClip(tmp);
1588 DisposeRgn(tmp);
1589 }
1590
1591 /* assumes a fixed-width style */
1592 short
1593 TEGetWidth(short off, TEHandle te)
1594 {
1595 TextStyle style;
1596 short fheight, fwidth, ascent, old_font, old_size;
1597
1598 TEGetStyle(off, &style, &fheight, &ascent, te);
1599
1600 old_font = thePort->txFace;
1601 old_size = thePort->txSize;
1602 thePort->txFace = style.tsFont;
1603 thePort->txSize = style.tsSize;
1604 fwidth = CharWidth('m');
1605 thePort->txFace = old_font;
1606 thePort->txSize = old_size;
1607
1608 return fwidth;
1609 }
1610
1611 #define ceildiv(a,b) ((a / b) + (!!(a % b)))
1612
1613 void
1614 UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te, bool reset)
1615 {
1616 size_t vlines, telines;
1617 TERec *ter;
1618 RgnHandle rgn;
1619 short fheight, fwidth, max, val, per_line, horiz, max_chars, n;
1620
1621 HLock(control);
1622 HLock(te);
1623 ter = *te;
1624
1625 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1626 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1627
1628 if (horiz) {
1629 fwidth = TEGetWidth(0, te);
1630 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1631 fwidth) - 1;
1632 for (max_chars = 0, n = 1; n < ter->nLines; n++) {
1633 if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
1634 max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
1635 }
1636
1637 if (max_chars <= per_line)
1638 /* don't enable the scrollbar */
1639 max = 1;
1640 else
1641 max = max_chars - per_line + 1;
1642
1643 if (reset) {
1644 val = 1;
1645 TESetSelect(0, 0, te);
1646 } else {
1647 val = (ter->viewRect.left - ter->destRect.left);
1648
1649 if (val < 0)
1650 val = 1;
1651 else {
1652 val = ceildiv(val, fwidth) + 1;
1653 if (val > max)
1654 max = val;
1655 }
1656 }
1657 } else {
1658 fheight = TEGetHeight(0, 0, te);
1659 vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
1660 telines = ter->nLines;
1661 /* telines is inaccurate if the last line doesn't have any chars */
1662 //if (telines >= vlines)
1663 // telines++;
1664 max = telines - vlines + 1;
1665 if (max < 1)
1666 max = 1;
1667
1668 if (reset) {
1669 val = 1;
1670 TESetSelect(0, 0, te);
1671 } else {
1672 val = (ter->viewRect.top - ter->destRect.top);
1673 if (val < 0)
1674 val = 1;
1675 else {
1676 val = ceildiv(val, fheight) + 1;
1677 if (val > max)
1678 max = val;
1679 }
1680 }
1681 }
1682
1683 /*
1684 * Avoid SetCtlMax because it will redraw and then SetCtlValue will
1685 * redraw again, which can cause a jump if we're trying to keep the
1686 * scrollbar position in the same place (like at the bottom).
1687 */
1688 (*control)->contrlMax = max;
1689 SetCtlValue(control, val);
1690
1691 rgn = NewRgn();
1692 RectRgn(rgn, &(*control)->contrlRect);
1693 UpdtControl(win, rgn);
1694 CloseRgn(rgn);
1695
1696 HUnlock(te);
1697 HUnlock(control);
1698 }
1699
1700 void
1701 SetTrackControlTE(TEHandle te)
1702 {
1703 track_control_te = te;
1704 }
1705
1706 pascal void
1707 TrackMouseDownInControl(ControlHandle control, short part)
1708 {
1709 TERec *ter;
1710 short page, val, adj, fheight, fwidth, horiz, per_line;
1711
1712 if (track_control_te == NULL)
1713 panic("TrackMouseDownInControl without SetTrackControlTE");
1714
1715 HLock(track_control_te);
1716 ter = *track_control_te;
1717
1718 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1719 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1720
1721 if (horiz) {
1722 fwidth = TEGetWidth(0, track_control_te);
1723 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1724 fwidth) - 1;
1725 page = ceildiv(GetCtlMax(control) + per_line,
1726 ((per_line * 75) / 100)) + 1;
1727 } else {
1728 /* keep 1 line of context between pages */
1729 fheight = TEGetHeight(0, 0, track_control_te);
1730 page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
1731 }
1732
1733 adj = 0;
1734 switch (part) {
1735 case inUpButton:
1736 adj = -1;
1737 break;
1738 case inPageUp:
1739 adj = -page;
1740 break;
1741 case inDownButton:
1742 adj = 1;
1743 break;
1744 case inPageDown:
1745 adj = page;
1746 break;
1747 }
1748
1749 val = GetCtlValue(control);
1750 if (val + adj < GetCtlMin(control))
1751 adj = -(val - GetCtlMin(control));
1752 if (val + adj > GetCtlMax(control))
1753 adj = (GetCtlMax(control) - val);
1754 if (adj == 0)
1755 return;
1756
1757 if (horiz)
1758 TEScroll(-adj * fwidth, 0, track_control_te);
1759 else
1760 TEScroll(0, -adj * fheight, track_control_te);
1761
1762 SetCtlValue(control, val + adj);
1763
1764 HUnlock(track_control_te);
1765 }
1766
1767 pascal bool
1768 ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
1769 {
1770 WindowPtr event_win;
1771 short event_in, itype;
1772 char key;
1773 ControlHandle ihandle;
1774 Rect irect;
1775
1776 switch (event->what) {
1777 case nullEvent:
1778 /*
1779 * TODO: support passing this through to a function the caller
1780 * can specify to do something while we're staying modal
1781 */
1782 break;
1783 case updateEvt:
1784 GetDItem(dlg, ok, &itype, &ihandle, &irect);
1785 outline_button(ihandle);
1786 break;
1787 case mouseDown:
1788 event_in = FindWindow(event->where, &event_win);
1789
1790 switch (event_in) {
1791 case inGoAway:
1792 if (TrackGoAway(dlg, event->where)) {
1793 *hit = -1;
1794 return true;
1795 }
1796 }
1797 break;
1798 case keyDown:
1799 key = event->message & charCodeMask;
1800
1801 if (event->modifiers & cmdKey) {
1802 switch (key) {
1803 case 'x':
1804 ZeroScrap();
1805 DlgCut(dlg);
1806 break;
1807 case 'c':
1808 ZeroScrap();
1809 DlgCopy(dlg);
1810 break;
1811 case 'v':
1812 if (TEFromScrap() == noErr)
1813 DlgPaste(dlg);
1814 break;
1815 }
1816 event->what = nullEvent;
1817 return false;
1818 } else if (key == 13 || key == 3) {
1819 /* OK button */
1820 *hit = OK;
1821 return true;
1822 }
1823
1824 break;
1825 }
1826
1827 return false;
1828 }
1829
1830 static short _password_dialog_ditl_id = -1;
1831 static char *_password_dialog_storage = NULL;
1832 static size_t _password_dialog_storage_len = 0;
1833
1834 void
1835 PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len)
1836 {
1837 _password_dialog_ditl_id = ditl_id;
1838 _password_dialog_storage = storage;
1839 _password_dialog_storage_len = len;
1840
1841 memset(_password_dialog_storage, 0, len);
1842 }
1843
1844 pascal bool
1845 PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit)
1846 {
1847 DialogPeek dlgp;
1848 short sel_start, sel_end;
1849 char key;
1850
1851 dlgp = (DialogPeek)dlg;
1852 if (dlgp->editField == _password_dialog_ditl_id - 1) {
1853 sel_start = (*(dlgp->textH))->selStart;
1854 sel_end = (*(dlgp->textH))->selEnd;
1855
1856 switch (event->what) {
1857 case keyDown:
1858 case autoKey:
1859 key = event->message & charCodeMask;
1860 if (event->modifiers & cmdKey) {
1861 /* TODO: implement DlgPaste for cmd+v? */
1862 event->what = nullEvent;
1863 return false;
1864 }
1865
1866 if (key == 8) {
1867 /* backspace */
1868 if (sel_start == sel_end && sel_start > 0)
1869 memmove(_password_dialog_storage + sel_start - 1,
1870 _password_dialog_storage + sel_start,
1871 _password_dialog_storage_len - sel_start - 1);
1872 else if (sel_start != sel_end)
1873 memmove(_password_dialog_storage + sel_start,
1874 _password_dialog_storage + sel_end,
1875 _password_dialog_storage_len - sel_end - 1);
1876 } else if (sel_start >= _password_dialog_storage_len) {
1877 event->what = nullEvent;
1878 return false;
1879 } else if (key >= ' ' && key <= '~') {
1880 if (sel_start != sel_end)
1881 /* delete selection before making space for new char */
1882 memmove(_password_dialog_storage + sel_start,
1883 _password_dialog_storage + sel_end,
1884 _password_dialog_storage_len - sel_end - 1);
1885 memmove(_password_dialog_storage + sel_start + 1,
1886 _password_dialog_storage + sel_start,
1887 _password_dialog_storage_len - sel_start - 1);
1888 _password_dialog_storage[sel_start] = key;
1889 event->message = '•';
1890 }
1891 _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0';
1892 sel_start = 0;
1893 break;
1894 }
1895 }
1896
1897 return ModalDialogFilter(dlg, event, hit);
1898 }
1899
1900 void
1901 PasswordDialogFieldFinish(void)
1902 {
1903 _password_dialog_ditl_id = -1;
1904 _password_dialog_storage = NULL;
1905 _password_dialog_storage_len = 0;
1906 }
1907
1908 /* (*(some_te))->caretHook = NullCaretHook; */
1909 pascal void
1910 NullCaretHook(void)
1911 {
1912 asm {
1913 move.l (a7)+,d0
1914 rts
1915 }
1916 }
1917
1918 static bool menu_bar_hidden = false;
1919 static short old_mbar_height;
1920 static Rect mbar_rect;
1921 static RgnHandle mbar_save_region;
1922
1923 void
1924 HideMenuBar(void)
1925 {
1926 RgnHandle mbar_region;
1927 WindowPeek win;
1928
1929 old_mbar_height = GetMBarHeight();
1930 SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top,
1931 screenBits.bounds.right, screenBits.bounds.top + old_mbar_height);
1932
1933 mbar_save_region = NewRgn();
1934 mbar_region = NewRgn();
1935
1936 MBarHeight = 0;
1937 CopyRgn(GetGrayRgn(), mbar_save_region);
1938 RectRgn(mbar_region, &mbar_rect);
1939 UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn());
1940
1941 win = (WindowPeek)FrontWindow();
1942 PaintOne(win, mbar_region);
1943 PaintBehind(win, mbar_region);
1944 CalcVis(win);
1945 CalcVisBehind(win, mbar_region);
1946 DisposeRgn(mbar_region);
1947
1948 menu_bar_hidden = true;
1949 }
1950
1951 void
1952 RestoreHiddenMenuBar(void)
1953 {
1954 WindowPeek win;
1955
1956 if (!menu_bar_hidden)
1957 return;
1958
1959 CopyRgn(mbar_save_region, GetGrayRgn());
1960 MBarHeight = old_mbar_height;
1961
1962 RectRgn(mbar_save_region, &mbar_rect);
1963 win = (WindowPeek)FrontWindow();
1964 CalcVis(win);
1965 CalcVisBehind(win, mbar_save_region);
1966 DisposeRgn(mbar_save_region);
1967
1968 menu_bar_hidden = false;
1969
1970 HiliteMenu(0);
1971 DrawMenuBar();
1972 }
1973
1974 bool
1975 CommandPeriodPressed(void)
1976 {
1977 EventRecord event;
1978
1979 if (!GetNextEvent(keyDownMask | autoKeyMask, &event))
1980 return false;
1981
1982 if ((event.modifiers & cmdKey) &&
1983 ((event.message & charCodeMask) == '.'))
1984 return true;
1985
1986 return false;
1987 }
1988
1989 /* C Extensions */
1990
1991 /*
1992 * Appends src to string dst of size dsize (unlike strncat, dsize is the
1993 * full size of dst, not space left). At most dsize-1 characters
1994 * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
1995 * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
1996 * If retval >= dsize, truncation occurred.
1997 */
1998 size_t
1999 strlcat(char *dst, const char *src, size_t dsize)
2000 {
2001 const char *odst = dst;
2002 const char *osrc = src;
2003 size_t n = dsize;
2004 size_t dlen;
2005
2006 /* Find the end of dst and adjust bytes left but don't go past end. */
2007 while (n-- != 0 && *dst != '\0')
2008 dst++;
2009 dlen = dst - odst;
2010 n = dsize - dlen;
2011
2012 if (n-- == 0)
2013 return(dlen + strlen(src));
2014 while (*src != '\0') {
2015 if (n != 0) {
2016 *dst++ = *src;
2017 n--;
2018 }
2019 src++;
2020 }
2021 *dst = '\0';
2022
2023 return(dlen + (src - osrc)); /* count does not include NUL */
2024 }
2025
2026 /*
2027 * Copy string src to buffer dst of size dsize. At most dsize-1
2028 * chars will be copied. Always NUL terminates (unless dsize == 0).
2029 * Returns strlen(src); if retval >= dsize, truncation occurred.
2030 */
2031 size_t
2032 strlcpy(char *dst, const char *src, size_t dsize)
2033 {
2034 const char *osrc = src;
2035 size_t nleft = dsize;
2036
2037 /* Copy as many bytes as will fit. */
2038 if (nleft != 0) {
2039 while (--nleft != 0) {
2040 if ((*dst++ = *src++) == '\0')
2041 break;
2042 }
2043 }
2044
2045 /* Not enough room in dst, add NUL and traverse rest of src. */
2046 if (nleft == 0) {
2047 if (dsize != 0)
2048 *dst = '\0'; /* NUL-terminate dst */
2049 while (*src++)
2050 ;
2051 }
2052
2053 return(src - osrc - 1); /* count does not include NUL */
2054 }
2055
2056 char *
2057 strndup(const char *str, size_t maxlen)
2058 {
2059 char *copy;
2060 const char *cp;
2061 size_t len;
2062
2063 /* strnlen */
2064 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
2065 ;
2066
2067 len = (size_t)(cp - str);
2068 copy = malloc(len + 1);
2069 if (copy != NULL) {
2070 (void)memcpy(copy, str, len);
2071 copy[len] = '\0';
2072 }
2073
2074 return copy;
2075 }
2076
2077 /*
2078 * Get next token from string *stringp, where tokens are possibly-empty
2079 * strings separated by characters from delim.
2080 *
2081 * Writes NULs into the string at *stringp to end tokens.
2082 * delim need not remain constant from call to call.
2083 * On return, *stringp points past the last NUL written (if there might
2084 * be further tokens), or is NULL (if there are definitely no more tokens).
2085 *
2086 * If *stringp is NULL, strsep returns NULL.
2087 */
2088 char *
2089 strsep(char **stringp, const char *delim)
2090 {
2091 char *s;
2092 const char *spanp;
2093 int c, sc;
2094 char *tok;
2095
2096 if ((s = *stringp) == NULL)
2097 return (NULL);
2098 for (tok = s;;) {
2099 c = *s++;
2100 spanp = delim;
2101 do {
2102 if ((sc = *spanp++) == c) {
2103 if (c == 0)
2104 s = NULL;
2105 else
2106 s[-1] = 0;
2107 *stringp = s;
2108 return (tok);
2109 }
2110 } while (sc != 0);
2111 }
2112 return (NULL);
2113 }
2114
2115 static struct format {
2116 unsigned leftJustify : 1;
2117 unsigned forceSign : 1;
2118 unsigned altForm : 1;
2119 unsigned zeroPad : 1;
2120 unsigned havePrecision : 1;
2121 unsigned hSize : 1;
2122 unsigned lSize : 1;
2123 unsigned LSize : 1;
2124 char sign;
2125 char exponent;
2126 int fieldWidth;
2127 int precision;
2128 } default_format;
2129
2130 short bounded_vfprintf(FILE *fp, const char *fmt, va_list arg);
2131 static int nullio(FILE *fp, int i);
2132
2133 short
2134 bounded_vfprintf(FILE *fp, const char *fmt, va_list arg)
2135 {
2136 register int c, i, j, nwritten = 0;
2137 register unsigned long n;
2138 long double x;
2139 register char *s;
2140 #define VFPRINTF_BUFLEN 512
2141 static char buf[VFPRINTF_BUFLEN], *digits, *t;
2142 struct format F;
2143
2144 for (c = *fmt; c; c = *++fmt) {
2145 if (c != '%')
2146 goto copy1;
2147 F = default_format;
2148
2149 /* decode flags */
2150
2151 for (;;) {
2152 c = *++fmt;
2153 if (c == '-')
2154 F.leftJustify = TRUE;
2155 else if (c == '+')
2156 F.forceSign = TRUE;
2157 else if (c == ' ')
2158 F.sign = ' ';
2159 else if (c == '#')
2160 F.altForm = TRUE;
2161 else if (c == '0')
2162 F.zeroPad = TRUE;
2163 else
2164 break;
2165 }
2166
2167 /* decode field width */
2168
2169 if (c == '*') {
2170 if ((F.fieldWidth = va_arg(arg, int)) < 0) {
2171 F.leftJustify = TRUE;
2172 F.fieldWidth = -F.fieldWidth;
2173 }
2174 c = *++fmt;
2175 }
2176 else {
2177 for (; c >= '0' && c <= '9'; c = *++fmt)
2178 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
2179 }
2180
2181 /* decode precision */
2182
2183 if (c == '.') {
2184 if ((c = *++fmt) == '*') {
2185 F.precision = va_arg(arg, int);
2186 c = *++fmt;
2187 }
2188 else {
2189 for (; c >= '0' && c <= '9'; c = *++fmt)
2190 F.precision = (10 * F.precision) + (c - '0');
2191 }
2192 if (F.precision >= 0)
2193 F.havePrecision = TRUE;
2194 }
2195
2196 /* perform appropriate conversion */
2197
2198 s = &buf[VFPRINTF_BUFLEN];
2199 if (F.leftJustify)
2200 F.zeroPad = FALSE;
2201 conv: switch (c) {
2202
2203 /* 'h' size modifier */
2204
2205 case 'h':
2206 F.hSize = TRUE;
2207 c = *++fmt;
2208 goto conv;
2209
2210 /* 'l' size modifier */
2211
2212 case 'l':
2213 F.lSize = TRUE;
2214 c = *++fmt;
2215 goto conv;
2216
2217 /* 'L' size modifier */
2218
2219 case 'L':
2220 F.LSize = TRUE;
2221 c = *++fmt;
2222 goto conv;
2223
2224 /* decimal (signed) */
2225
2226 case 'd':
2227 case 'i':
2228 if (F.lSize)
2229 n = va_arg(arg, long);
2230 else
2231 n = va_arg(arg, int);
2232 if (F.hSize)
2233 n = (short) n;
2234 if ((long) n < 0) {
2235 n = -n;
2236 F.sign = '-';
2237 }
2238 else if (F.forceSign)
2239 F.sign = '+';
2240 goto decimal;
2241
2242 /* decimal (unsigned) */
2243
2244 case 'u':
2245 if (F.lSize)
2246 n = va_arg(arg, unsigned long);
2247 else
2248 n = va_arg(arg, unsigned int);
2249 if (F.hSize)
2250 n = (unsigned short) n;
2251 F.sign = 0;
2252 goto decimal;
2253
2254 /* decimal (common code) */
2255
2256 decimal:
2257 if (!F.havePrecision) {
2258 if (F.zeroPad) {
2259 F.precision = F.fieldWidth;
2260 if (F.sign)
2261 --F.precision;
2262 }
2263 if (F.precision < 1)
2264 F.precision = 1;
2265 }
2266 for (i = 0; n; n /= 10, i++)
2267 *--s = n % 10 + '0';
2268 for (; i < F.precision; i++)
2269 *--s = '0';
2270 if (F.sign) {
2271 *--s = F.sign;
2272 i++;
2273 }
2274 break;
2275
2276 /* octal (unsigned) */
2277
2278 case 'o':
2279 if (F.lSize)
2280 n = va_arg(arg, unsigned long);
2281 else
2282 n = va_arg(arg, unsigned int);
2283 if (F.hSize)
2284 n = (unsigned short) n;
2285 if (!F.havePrecision) {
2286 if (F.zeroPad)
2287 F.precision = F.fieldWidth;
2288 if (F.precision < 1)
2289 F.precision = 1;
2290 }
2291 for (i = 0; n; n /= 8, i++)
2292 *--s = n % 8 + '0';
2293 if (F.altForm && i && *s != '0') {
2294 *--s = '0';
2295 i++;
2296 }
2297 for (; i < F.precision; i++)
2298 *--s = '0';
2299 break;
2300
2301 /* hexadecimal (unsigned) */
2302
2303 case 'p':
2304 F.havePrecision = F.lSize = TRUE;
2305 F.precision = 8;
2306 /* ... */
2307 case 'X':
2308 digits = "0123456789ABCDEF";
2309 goto hexadecimal;
2310 case 'x':
2311 digits = "0123456789abcdef";
2312 /* ... */
2313 hexadecimal:
2314 if (F.lSize)
2315 n = va_arg(arg, unsigned long);
2316 else
2317 n = va_arg(arg, unsigned int);
2318 if (F.hSize)
2319 n = (unsigned short) n;
2320 if (!F.havePrecision) {
2321 if (F.zeroPad) {
2322 F.precision = F.fieldWidth;
2323 if (F.altForm)
2324 F.precision -= 2;
2325 }
2326 if (F.precision < 1)
2327 F.precision = 1;
2328 }
2329 for (i = 0; n; n /= 16, i++)
2330 *--s = digits[n % 16];
2331 for (; i < F.precision; i++)
2332 *--s = '0';
2333 if (F.altForm) {
2334 *--s = c;
2335 *--s = '0';
2336 i += 2;
2337 }
2338 break;
2339
2340 /* character */
2341
2342 case 'c':
2343 *--s = va_arg(arg, int);
2344 i = 1;
2345 break;
2346
2347 /* string */
2348
2349 case 's':
2350 s = va_arg(arg, char *);
2351 if (F.altForm) {
2352 i = (unsigned char) *s++;
2353 if (F.havePrecision && i > F.precision)
2354 i = F.precision;
2355 }
2356 else {
2357 if (!F.havePrecision)
2358 i = strlen(s);
2359 else if (t = memchr(s, '\0', F.precision))
2360 i = t - s;
2361 else
2362 i = F.precision;
2363 }
2364 break;
2365
2366 /* store # bytes written so far */
2367
2368 case 'n':
2369 s = va_arg(arg, void *);
2370 if (F.hSize)
2371 * (short *) s = nwritten;
2372 else if (F.lSize)
2373 * (long *) s = nwritten;
2374 else
2375 * (int *) s = nwritten;
2376 continue;
2377
2378 /* oops - unknown conversion, abort */
2379
2380 default:
2381 if (c != '%')
2382 goto done;
2383 copy1:
2384 putc(c, fp); /* disregard EOF */
2385 ++nwritten;
2386 continue;
2387 }
2388
2389 /* pad on the left */
2390
2391 if (i < F.fieldWidth && !F.leftJustify) {
2392 do {
2393 putc(' ', fp); /* disregard EOF */
2394 ++nwritten;
2395 } while (i < --F.fieldWidth);
2396 }
2397
2398 /* write the converted result */
2399
2400 fwrite(s, 1, i, fp); /* disregard EOF */
2401 nwritten += i;
2402
2403 /* pad on the right */
2404
2405 for (; i < F.fieldWidth; i++) {
2406 putc(' ', fp); /* disregard EOF */
2407 ++nwritten;
2408 }
2409 }
2410
2411 /* all done! */
2412
2413 done:
2414 return(nwritten);
2415 }
2416
2417 short
2418 snprintf(char *s, size_t size, const char *fmt, ...)
2419 {
2420 return(vsnprintf(s, size, fmt, __va(fmt)));
2421 }
2422
2423 static int
2424 nullio(FILE *fp, int i)
2425 {
2426 return(EOF);
2427 }
2428
2429 short
2430 vsnprintf(char *s, size_t size, const char *fmt, void *p)
2431 {
2432 FILE f;
2433 int n;
2434 unsigned char zb;
2435
2436 memset(&f, 0, sizeof(f));
2437 f.refnum = -1;
2438 f.ptr = (unsigned char *)s;
2439 f.cnt = size;
2440 f.proc = nullio;
2441 f.dirty = 1;
2442
2443 if (size == 0)
2444 zb = s[0];
2445
2446 if ((n = bounded_vfprintf(&f, fmt, p)) >= 0) {
2447 if (n < size)
2448 s[n] = 0;
2449 else
2450 s[size - 1] = 0;
2451 }
2452
2453 if (size == 0)
2454 s[0] = zb;
2455
2456 return(n);
2457 }