AmendHub

jcs

/

amend

/

amendments

/

93

util: Move strlcat, strlcpy, strndup, and snprintf here

This lets us build with the standard THINK C ANSI library.
Also make malloc map dynamic to avoid a huge data segment.

jcs made amendment 93 about 1 month ago
--- repo.c Thu Aug 18 22:53:33 2022 +++ repo.c Wed Aug 31 22:03:43 2022 @@ -22,6 +22,7 @@ #include "bile.h" #include "diff.h" #include "repo.h" +#include "strnatcmp.h" #include "util.h" struct repo * repo_init(struct bile *bile, short is_new); --- strnatcmp.c Wed Oct 13 17:21:09 2021 +++ strnatcmp.c Wed Aug 31 22:04:17 2022 @@ -0,0 +1,157 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +/* partial change history: + * + * 2004-10-10 mbp: Lift out character type dependencies into macros. + * + * Eric Sosman pointed out that ctype functions take a parameter whose + * value must be that of an unsigned int, even on platforms that have + * negative chars in their default char type. + * + * 2021-10-13 jcs: Modified to compile in THINK C 5 + */ + +#include <stddef.h> /* size_t */ +#include <ctype.h> + +#include "strnatcmp.h" + +int compare_right(char const *a, char const *b); + +int +compare_right(char const *a, char const *b) +{ + int bias = 0; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + for (;; a++, b++) { + if (!isdigit((unsigned char)*a) && !isdigit((unsigned char)*b)) + return bias; + if (!isdigit((unsigned char)*a)) + return -1; + if (!isdigit((unsigned char)*b)) + return +1; + if (*a < *b) { + if (!bias) + bias = -1; + } else if (*a > *b) { + if (!bias) + bias = +1; + } else if (!*a && !*b) + return bias; + } + + return 0; +} + + +static int +compare_left(char const *a, char const *b) +{ + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++) { + if (!isdigit((unsigned char)*a) && !isdigit((unsigned char)*b)) + return 0; + if (!isdigit((unsigned char)*a)) + return -1; + if (!isdigit((unsigned char)*b)) + return +1; + if (*a < *b) + return -1; + if (*a > *b) + return +1; + } + + return 0; +} + + +static int +strnatcmp0(char const *a, char const *b, int fold_case) +{ + int ai, bi; + char ca, cb; + int fractional, result; + + ai = bi = 0; + while (1) { + ca = a[ai]; cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (isspace((unsigned char)ca)) + ca = a[++ai]; + + while (isspace((unsigned char)cb)) + cb = b[++bi]; + + /* process run of digits */ + if (isdigit((unsigned char)ca) && isdigit((unsigned char)cb)) { + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + if ((result = compare_left(a+ai, b+bi)) != 0) + return result; + } else { + if ((result = compare_right(a+ai, b+bi)) != 0) + return result; + } + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + return 0; + } + + if (fold_case) { + ca = toupper((unsigned char)ca); + cb = toupper((unsigned char)cb); + } + + if (ca < cb) + return -1; + + if (ca > cb) + return +1; + + ++ai; ++bi; + } +} + + +int +strnatcmp(char const *a, char const *b) { + return strnatcmp0(a, b, 0); +} + + +/* Compare, recognizing numeric string and ignoring case. */ +int +strnatcasecmp(char const *a, char const *b) { + return strnatcmp0(a, b, 1); +} --- strnatcmp.h Wed Aug 31 22:04:05 2022 +++ strnatcmp.h Wed Aug 31 22:04:05 2022 @@ -0,0 +1,7 @@ +#ifndef __STRNATCMP_H__ +#define __STRNATCMP_H__ + +int strnatcmp(char const *a, char const *b); +int strnatcasecmp(char const *a, char const *b); + +#endif --- util.c Thu Aug 18 23:17:52 2022 +++ util.c Wed Aug 31 22:54:33 2022 @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org> - * + * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org> + * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. @@ -14,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* THINK C Project must have a header of "#include <MacHeaders>" */ + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -73,30 +76,26 @@ enum { NOTE_ALERT }; -#ifdef MALLOC_DEBUG +/* + * Define to audit each malloc and free and verify that a pointer isn't + * double-freed. The list of outstanding allocations can be checked by + * looking through malloc_map. + */ +//#define MALLOC_DEBUG +#ifdef MALLOC_DEBUG /* * List of allocations, updated at xmalloc() and xfree(). If an address * passed to xfree() isn't in the list, it indicates a double-free. */ -#define MALLOC_MAP_SIZE 512 +#define MALLOC_MAP_CHUNK_SIZE 1024 struct malloc_map_e { unsigned long addr; unsigned long size; char note[MALLOC_NOTE_SIZE]; -} malloc_map[MALLOC_MAP_SIZE]; - -/* - * On xfree(&), the pointer will be updated to point here. xfree_verify() - * should be called periodically to detect any writes to it, indicating - * a use-after-free. - */ -#define MALLOC_UAF_SIZE 256 -static const char malloc_uaf_zero[MALLOC_UAF_SIZE] = { 0 }; -static char malloc_uaf[MALLOC_UAF_SIZE]; - +} *malloc_map = NULL; +unsigned long malloc_map_size = 0; static bool malloc_map_compact = false; - #endif void vwarn(short alert_func, const char *format, va_list ap); @@ -115,8 +114,12 @@ util_init(void) HUnlock(alert_ditl_h); #ifdef MALLOC_DEBUG - memset(&malloc_map, 0, sizeof(malloc_map)); - memset(&malloc_uaf, 0, sizeof(malloc_uaf)); + malloc_map_size = MALLOC_MAP_CHUNK_SIZE; + malloc_map = (struct malloc_map_e *)NewPtr(malloc_map_size * + sizeof(struct malloc_map_e)); + if (malloc_map == NULL) + panic("NewPtr(%lu) failed", MALLOC_MAP_CHUNK_SIZE); + memset(malloc_map, 0, malloc_map_size); #endif } @@ -131,6 +134,7 @@ xmalloc(size_t size, char *note) { void *ptr; #ifdef MALLOC_DEBUG + struct malloc_map_e *new_malloc_map; unsigned short n, j; #endif @@ -143,16 +147,16 @@ xmalloc(size_t size, char *note) #ifdef MALLOC_DEBUG if (malloc_map_compact) { - for (n = 0; n < MALLOC_MAP_SIZE; n++) { + for (n = 0; n < malloc_map_size; n++) { if (malloc_map[n].addr != 0) continue; - for (j = n + 1; j < MALLOC_MAP_SIZE; j++) { + for (j = n + 1; j < malloc_map_size; j++) { if (malloc_map[j].addr == 0) continue; malloc_map[n] = malloc_map[j]; - memset(&malloc_map[j], 0, sizeof(malloc_map[j])); + memset(&malloc_map[j], 0, sizeof(struct malloc_map_e)); break; } } @@ -160,10 +164,21 @@ xmalloc(size_t size, char *note) malloc_map_compact = false; } - for (n = 0; n <= MALLOC_MAP_SIZE; n++) { - if (n == MALLOC_MAP_SIZE) - panic("xmalloc(%lu): out of malloc map entries, likely a " - "memory leak", size); + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) { + malloc_map_size += MALLOC_MAP_CHUNK_SIZE; + warn("xmalloc(%lu): out of malloc map entries, maybe a " + "memory leak, resizing to %ld", size, malloc_map_size); + new_malloc_map = (struct malloc_map_e *)NewPtr( + malloc_map_size * sizeof(struct malloc_map_e)); + if (new_malloc_map == NULL) + panic("out of memory resizing malloc map"); + memcpy(new_malloc_map, malloc_map, + (malloc_map_size - MALLOC_MAP_CHUNK_SIZE) * + sizeof(struct malloc_map_e)); + DisposePtr(malloc_map); + malloc_map = new_malloc_map; + } if (malloc_map[n].addr == 0) { malloc_map[n].addr = (unsigned long)ptr; malloc_map[n].size = size; @@ -184,13 +199,10 @@ xfree(void *ptrptr) void *ptr = (void *)*addr; #ifdef MALLOC_DEBUG unsigned long n; - - if (*addr == (unsigned long)&malloc_uaf) - panic("xfree(%lu): double free()", *addr); - - for (n = 0; n <= MALLOC_MAP_SIZE; n++) { - if (n == MALLOC_MAP_SIZE) - panic("xfree(%lu): can't find in alloc map, likely " + + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) + panic("xfree(0x%lx): can't find in alloc map, likely " "double free()", *addr); if (malloc_map[n].addr == *addr) { malloc_map[n].addr = 0; @@ -203,22 +215,9 @@ xfree(void *ptrptr) DisposePtr(ptr); -#ifdef MALLOC_DEBUG - *addr = (unsigned long)&malloc_uaf; -#else *addr = 0L; -#endif } -#ifdef MALLOC_DEBUG -void -xfree_verify(void) -{ - if (memcmp(malloc_uaf, malloc_uaf_zero, sizeof(malloc_uaf)) != 0) - panic("xfree_verify: use-after-free detected"); -} -#endif - void * xmalloczero(size_t size, char *note) { @@ -254,8 +253,8 @@ xrealloc(void *src, size_t size) #ifdef MALLOC_DEBUG if (src != NULL) { - for (n = 0; n <= MALLOC_MAP_SIZE; n++) { - if (n == MALLOC_MAP_SIZE) { + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) { panic("xrealloc(%lu): can't find in alloc map, likely " "double free()", (unsigned long)src); return NULL; @@ -419,23 +418,16 @@ void vwarn(short alert_func, const char *format, va_list ap) { Rect bounds; - short height, width, hit; + short hit; WindowPtr win, dialog; GetPort(&win); vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); - width = 300; - height = 100; - bounds.left = (screenBits.bounds.right - width) / 2; - bounds.right = bounds.left + width; - bounds.top = GetMBarHeight() + - ((screenBits.bounds.bottom - height) / 2.5); - bounds.bottom = bounds.top + height; - ParamText(CtoPstr(err_str), "\p", "\p", "\p"); + center_in_screen(300, 100, false, &bounds); dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, (WindowPtr)-1L, false, 0, alert_ditl_h); @@ -538,7 +530,7 @@ short ask(const char *format, ...) { Rect bounds; - short height, width, hit; + short hit; WindowPtr win, dialog; va_list ap; @@ -548,14 +540,6 @@ ask(const char *format, ...) vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); va_end(ap); - width = 300; - height = 100; - bounds.left = (screenBits.bounds.right - width) / 2; - bounds.right = bounds.left + width; - bounds.top = GetMBarHeight() + - ((screenBits.bounds.bottom - height) / 2.5); - bounds.bottom = bounds.top + height; - ParamText(CtoPstr(err_str), "\p", "\p", "\p"); ask_ditl_h = xNewHandle(sizeof(ask_ditl)); @@ -563,6 +547,7 @@ ask(const char *format, ...) memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl)); HUnlock(ask_ditl_h); + center_in_screen(300, 100, false, &bounds); dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, (WindowPtr)-1L, false, 0, ask_ditl_h); @@ -586,7 +571,7 @@ progress(char *format, ...) static Str255 progress_s; Handle thandle; va_list argptr; - Rect bounds = { 100, 90, 160, 420 }; /* tlbr */ + Rect bounds; Rect trect; short ttype; @@ -610,6 +595,7 @@ progress(char *format, ...) memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl)); HUnlock(progress_ditl_h); + center_in_screen(330, 60, false, &bounds); progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, (WindowPtr)-1L, false, 0, progress_ditl_h); } @@ -631,6 +617,17 @@ window_rect(WindowPtr win, Rect *ret) ret->bottom -= 1; } +void +center_in_screen(short width, short height, bool titlebar, Rect *b) +{ + b->left = ((screenBits.bounds.right - screenBits.bounds.left) / 2) - + (width / 2); + b->top = ((screenBits.bounds.bottom - screenBits.bounds.top) / 2) - + (height / 2) + (titlebar ? GetMBarHeight() : 0); + b->right = b->left + width; + b->bottom = b->top + height; +} + /* * General Mac-specific non-GUI functions */ @@ -1520,4 +1517,424 @@ RestoreHiddenMenuBar(void) HiliteMenu(0); DrawMenuBar(); +} + +/* C Extensions */ + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + +char * +strndup(const char *str, size_t maxlen) +{ + char *copy; + const char *cp; + size_t len; + + /* strnlen */ + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + len = (size_t)(cp - str); + copy = malloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } + + return copy; +} + +static struct format { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned altForm : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + unsigned LSize : 1; + char sign; + char exponent; + int fieldWidth; + int precision; +} default_format; + +int bounded_vfprintf(FILE *fp, const char *fmt, va_list arg); +static int nullio(FILE *fp, int i); + +int +bounded_vfprintf(FILE *fp, const char *fmt, va_list arg) +{ + register int c, i, j, nwritten = 0; + register unsigned long n; + long double x; + register char *s; +#define VFPRINTF_BUFLEN 512 + char buf[VFPRINTF_BUFLEN], *digits, *t; + struct format F; + + for (c = *fmt; c; c = *++fmt) { + if (c != '%') + goto copy1; + F = default_format; + + /* decode flags */ + + for (;;) { + c = *++fmt; + if (c == '-') + F.leftJustify = TRUE; + else if (c == '+') + F.forceSign = TRUE; + else if (c == ' ') + F.sign = ' '; + else if (c == '#') + F.altForm = TRUE; + else if (c == '0') + F.zeroPad = TRUE; + else + break; + } + + /* decode field width */ + + if (c == '*') { + if ((F.fieldWidth = va_arg(arg, int)) < 0) { + F.leftJustify = TRUE; + F.fieldWidth = -F.fieldWidth; + } + c = *++fmt; + } + else { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + /* decode precision */ + + if (c == '.') { + if ((c = *++fmt) == '*') { + F.precision = va_arg(arg, int); + c = *++fmt; + } + else { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + } + if (F.precision >= 0) + F.havePrecision = TRUE; + } + + /* perform appropriate conversion */ + + s = &buf[VFPRINTF_BUFLEN]; + if (F.leftJustify) + F.zeroPad = FALSE; +conv: switch (c) { + + /* 'h' size modifier */ + + case 'h': + F.hSize = TRUE; + c = *++fmt; + goto conv; + + /* 'l' size modifier */ + + case 'l': + F.lSize = TRUE; + c = *++fmt; + goto conv; + + /* 'L' size modifier */ + + case 'L': + F.LSize = TRUE; + c = *++fmt; + goto conv; + + /* decimal (signed) */ + + case 'd': + case 'i': + if (F.lSize) + n = va_arg(arg, long); + else + n = va_arg(arg, int); + if (F.hSize) + n = (short) n; + if ((long) n < 0) { + n = -n; + F.sign = '-'; + } + else if (F.forceSign) + F.sign = '+'; + goto decimal; + + /* decimal (unsigned) */ + + case 'u': + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + F.sign = 0; + goto decimal; + + /* decimal (common code) */ + + decimal: + if (!F.havePrecision) { + if (F.zeroPad) { + F.precision = F.fieldWidth; + if (F.sign) + --F.precision; + } + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 10, i++) + *--s = n % 10 + '0'; + for (; i < F.precision; i++) + *--s = '0'; + if (F.sign) { + *--s = F.sign; + i++; + } + break; + + /* octal (unsigned) */ + + case 'o': + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + if (!F.havePrecision) { + if (F.zeroPad) + F.precision = F.fieldWidth; + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 8, i++) + *--s = n % 8 + '0'; + if (F.altForm && i && *s != '0') { + *--s = '0'; + i++; + } + for (; i < F.precision; i++) + *--s = '0'; + break; + + /* hexadecimal (unsigned) */ + + case 'p': + F.havePrecision = F.lSize = TRUE; + F.precision = 8; + /* ... */ + case 'X': + digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x': + digits = "0123456789abcdef"; + /* ... */ + hexadecimal: + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + if (!F.havePrecision) { + if (F.zeroPad) { + F.precision = F.fieldWidth; + if (F.altForm) + F.precision -= 2; + } + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 16, i++) + *--s = digits[n % 16]; + for (; i < F.precision; i++) + *--s = '0'; + if (F.altForm) { + *--s = c; + *--s = '0'; + i += 2; + } + break; + + /* character */ + + case 'c': + *--s = va_arg(arg, int); + i = 1; + break; + + /* string */ + + case 's': + s = va_arg(arg, char *); + if (F.altForm) { + i = (unsigned char) *s++; + if (F.havePrecision && i > F.precision) + i = F.precision; + } + else { + if (!F.havePrecision) + i = strlen(s); + else if (t = memchr(s, '\0', F.precision)) + i = t - s; + else + i = F.precision; + } + break; + + /* store # bytes written so far */ + + case 'n': + s = va_arg(arg, void *); + if (F.hSize) + * (short *) s = nwritten; + else if (F.lSize) + * (long *) s = nwritten; + else + * (int *) s = nwritten; + continue; + + /* oops - unknown conversion, abort */ + + default: + if (c != '%') + goto done; + copy1: + putc(c, fp); /* disregard EOF */ + ++nwritten; + continue; + } + + /* pad on the left */ + + if (i < F.fieldWidth && !F.leftJustify) { + do { + putc(' ', fp); /* disregard EOF */ + ++nwritten; + } while (i < --F.fieldWidth); + } + + /* write the converted result */ + + fwrite(s, 1, i, fp); /* disregard EOF */ + nwritten += i; + + /* pad on the right */ + + for (; i < F.fieldWidth; i++) { + putc(' ', fp); /* disregard EOF */ + ++nwritten; + } + } + + /* all done! */ + +done: + return(nwritten); +} + +int +snprintf(char *s, size_t size, const char *fmt, ...) +{ + return(vsnprintf(s, size, fmt, __va(fmt))); +} + +static int +nullio(FILE *fp, int i) +{ + return(EOF); +} + +int +vsnprintf(char *s, size_t size, const char *fmt, void *p) +{ + FILE f; + int n; + + memset(&f, 0, sizeof(f)); + f.refnum = -1; + f.ptr = (unsigned char *) s; + f.cnt = size; + f.proc = nullio; + f.dirty = 1; + + if ((n = bounded_vfprintf(&f, fmt, p)) >= 0) + s[n] = 0; + return(n); } --- util.h Thu Aug 18 22:36:49 2022 +++ util.h Wed Aug 31 22:47:04 2022 @@ -21,9 +21,6 @@ #include <limits.h> #include <time.h> -#define MALLOC_DEBUG -#define MALLOC_NOTE_SIZE 32 - #ifndef SIZE_MAX #define SIZE_MAX ULONG_MAX #endif @@ -31,6 +28,8 @@ #define nitems(what) (sizeof((what)) / sizeof((what)[0])) #define member_size(type, member) sizeof(((type *)0)->member) +#define MALLOC_NOTE_SIZE 32 + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define BOUND(a, min, max) ((a) > (max) ? (max) : ((a) < (min) ? (min) : (a))) @@ -100,8 +99,6 @@ void * xreallocarray(void *, size_t, size_t); char * xstrdup(const char *, char *note); short getline(char *str, size_t len, char **ret); -size_t strlcpy(char *dst, const char *src, size_t dsize); -size_t strlcat(char *dst, const char *src, size_t dsize); const char * ordinal(unsigned short n); size_t rtrim(char *str, char *chars); long strpos_quoted(char *str, char c); @@ -119,6 +116,7 @@ short ask(const char *format, ...); #define ASK_NO 2 void progress(char *format, ...); void window_rect(WindowPtr win, Rect *ret); +void center_in_screen(short width, short height, bool titlebar, Rect *b); Handle xNewHandle(size_t size); Handle xGetResource(ResType type, short id); @@ -154,5 +152,12 @@ void PasswordDialogFieldFinish(void); pascal void NullCaretHook(void); void HideMenuBar(void); void RestoreHiddenMenuBar(void); + +size_t strlcat(char *dst, const char *src, size_t dsize); +size_t strlcpy(char *dst, const char *src, size_t dsize); +char * strndup(const char *str, size_t maxlen); +int snprintf(char *s, size_t size, const char *fmt, ...); +int vsnprintf(char *s, size_t size, const char *fmt, void *p); + #endif