jcs
/wifi_da
/amendments
/6
util: Import from upstream
I'm copying more and more from this, so just take it wholesale
jcs made amendment 6 10 months ago
--- util.c Wed Aug 30 18:01:45 2023
+++ util.c Wed Aug 30 18:01:45 2023
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org>
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * 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>
+#include <string.h>
+#include <GestaltEqu.h>
+#include <Script.h>
+#include <SetUpA4.h>
+#include "util.h"
+
+/*
+ * Define to enable malloc debugging which creates an allocation larger
+ * than the requested size, then embeds the allocated size and 2 canary
+ * bytes before and after the allocation. On xfree(), the canary bytes
+ * are verified and if any are not correct, the program panics.
+ */
+/* #define MALLOC_DEBUG 1 */
+
+/* ALRT resources */
+#define ASK_ALERT_ID 130
+
+#define ERROR_STRING_SIZE 1024
+static char err_str[ERROR_STRING_SIZE];
+
+/* basic DITL with an ok button (1), text area (2), and icon (3) */
+#define ALERT_DITL_OK 1
+#define ALERT_DITL_ICON 3
+static const char alert_ditl[] = {
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E,
+ 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02,
+ 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
+ 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02,
+ 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
+ 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02,
+ 0x00, 0x01
+};
+static Handle alert_ditl_h = NULL;
+
+/* ICN# to show for APPICON_ALERT */
+#define APPICON_ICN_ID 128
+
+/* DITL with a Yes button (1), No button (2), text (3), and icon (4) */
+static const char ask_ditl[] = {
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x03,
+ 0x59, 0x65, 0x73, 0x21, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x46, 0x00, 0xA0, 0x00, 0x5A, 0x00, 0xDA,
+ 0x04, 0x02, 0x4E, 0x6F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0A, 0x00, 0x32, 0x00, 0x41, 0x01, 0x22,
+ 0x08, 0x02, 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A,
+ 0xA0, 0x02, 0x00, 0x01
+};
+static Handle ask_ditl_h = NULL;
+
+/* DITL with just a text view */
+static const char progress_ditl[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02,
+ 0x5E, 0x30
+};
+static Handle progress_ditl_h = NULL;
+static DialogPtr progress_dialog = NULL;
+
+static TEHandle track_control_te = NULL;
+
+enum {
+ STOP_ALERT,
+ CAUTION_ALERT,
+ NOTE_ALERT,
+ APPICON_ALERT
+};
+
+void vwarn(short alert_func, const char *format, va_list ap);
+
+/*
+ * Util helper needed to be called at program startup, to pre-allocate
+ * some things that we can't do during errors.
+ */
+
+void
+util_init(void)
+{
+ alert_ditl_h = xNewHandle(sizeof(alert_ditl));
+ if (alert_ditl_h == NULL)
+ ExitToShell();
+ HLock(alert_ditl_h);
+ memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
+ HUnlock(alert_ditl_h);
+}
+
+/*
+ * Memory functions
+ */
+
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+xmalloc(size_t size)
+{
+#ifdef MALLOC_DEBUG
+ void *ptr;
+ unsigned char *cptr;
+#endif
+
+ if (size == 0)
+ panic("xmalloc: zero size");
+
+#ifdef MALLOC_DEBUG
+ ptr = NewPtr(size + 8);
+
+ cptr = (unsigned char *)ptr;
+ cptr[0] = 0xff;
+ cptr[1] = 0xff;
+ cptr[2] = (size >> 24) & 0xff;
+ cptr[3] = (size >> 16) & 0xff;
+ cptr[4] = (size >> 8) & 0xff;
+ cptr[5] = size & 0xff;
+
+ cptr[6 + size] = 0xff;
+ cptr[6 + size + 1] = 0xff;
+
+ return cptr + 6;
+#else
+ return NewPtr(size);
+#endif
+}
+
+void
+xfree(void *ptrptr)
+{
+ unsigned long *addr = (unsigned long *)ptrptr;
+#ifdef MALLOC_DEBUG
+ size_t size;
+ unsigned char *cptr;
+#endif
+ void *ptr;
+
+ if (ptrptr == NULL)
+ panic("xfree(NULL)");
+
+ ptr = (void *)*addr;
+ if (ptr == NULL)
+ panic("xfree(&NULL) likely a double-free");
+
+#ifdef MALLOC_DEBUG
+ cptr = (unsigned char *)ptr - 6;
+ if (cptr[0] != 0xff || cptr[1] != 0xff)
+ panic("xfree() pre-buf canary dead");
+
+ size = ((unsigned long)(cptr[2]) << 24) |
+ ((unsigned long)(cptr[3]) << 16) |
+ ((unsigned long)(cptr[4]) << 8) |
+ (unsigned long)(cptr[5]);
+
+ if (cptr[6 + size] != 0xff || cptr[6 + size + 1] != 0xff)
+ panic("xfree() post-buf canary dead");
+
+ DisposePtr(cptr);
+#else
+ DisposePtr(ptr);
+#endif
+
+ *addr = 0L;
+}
+
+void *
+xmalloczero(size_t size)
+{
+ void *ptr;
+
+ ptr = xmalloc(size);
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+
+ return ptr;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size)
+ panic("xcalloc(%lu, %lu) overflow", nmemb, size);
+
+ return xmalloczero(nmemb * size);
+}
+
+void *
+xrealloc(void *src, size_t size)
+{
+ void *ptr, *tsrc;
+
+ ptr = xmalloc(size);
+ if (ptr != NULL && src != NULL) {
+ memcpy(ptr, src, size);
+ tsrc = src;
+ xfree(&tsrc);
+ }
+
+ return ptr;
+}
+
+void *
+xreallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size)
+ panic("xreallocarray(%lu, %lu) failed", nmemb, size);
+
+ return xrealloc(optr, size * nmemb);
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *cp;
+ size_t len;
+
+ len = strlen(str);
+ cp = xmalloc(len + 1);
+ if (cp != NULL)
+ strlcpy(cp, str, len + 1);
+
+ return cp;
+}
+
+char *
+xstrndup(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 = xmalloc(len + 1);
+ if (copy != NULL) {
+ (void)memcpy(copy, str, len);
+ copy[len] = '\0';
+ }
+
+ return copy;
+}
+
+/* if var having used_size can't fit add, grow it by grow_amount */
+bool
+grow_to_fit(void *var, size_t *var_size, size_t used_size, size_t add,
+ size_t grow_amount)
+{
+ char **p = var;
+ char *curp = *p;
+ char *newp;
+ size_t new_size;
+
+ if (used_size + add < *var_size)
+ return true;
+
+ new_size = *var_size;
+ while (new_size < used_size + add)
+ new_size += grow_amount;
+
+ newp = xrealloc(curp, new_size);
+ if (newp == NULL)
+ return false;
+
+ *var_size = new_size;
+ *p = newp;
+
+ return true;
+}
+
+/*
+ * String functions
+ */
+
+short
+getline(char *str, size_t len, char **ret)
+{
+ short i;
+
+ for (i = 0; i < len; i++) {
+ if (str[i] == '\r' || i == len - 1) {
+ if (*ret == NULL) {
+ *ret = xmalloc(i + 1);
+ if (*ret == NULL)
+ return -1;
+ }
+ memcpy(*ret, str, i + 1);
+ (*ret)[i] = '\0';
+ return i + 1;
+ }
+ }
+
+ return 0;
+}
+
+const char *
+ordinal(unsigned short n)
+{
+ switch (n % 100) {
+ case 11:
+ case 12:
+ case 13:
+ return "th";
+ default:
+ switch (n % 10) {
+ case 1:
+ return "st";
+ case 2:
+ return "nd";
+ case 3:
+ return "rd";
+ default:
+ return "th";
+ }
+ }
+}
+
+size_t
+rtrim(char *str, char *chars)
+{
+ size_t len, rlen, n, j;
+
+ rlen = len = strlen(str);
+
+ for (n = len; n > 0; n--) {
+ for (j = 0; chars[j] != '\0'; j++) {
+ if (str[n - 1] == chars[j]) {
+ rlen--;
+ str[n - 1] = '\0';
+ goto next_in_str;
+ }
+ }
+
+ break;
+next_in_str:
+ continue;
+ }
+
+ return rlen;
+}
+
+long
+strpos_quoted(char *str, char c)
+{
+ long pos;
+ unsigned char quot = 0;
+
+ for (pos = 0; str[pos] != '\0'; pos++) {
+ if (quot) {
+ if (str[pos] == '\\') {
+ pos++;
+ continue;
+ }
+ if (str[pos] == quot)
+ quot = 0;
+ continue;
+ } else {
+ if (str[pos] == '"') {
+ quot = str[pos];
+ continue;
+ }
+ if (str[pos] == c)
+ return pos;
+ }
+ }
+
+ return -1;
+}
+
+char *
+OSTypeToString(OSType type)
+{
+ static char ostype_s[5];
+
+ ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
+ ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
+ ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
+ ostype_s[3] = (unsigned char)(type & 0xff);
+ ostype_s[4] = 0;
+
+ return ostype_s;
+}
+
+/*
+ * BSD err(3) and warn(3) functions
+ */
+
+void
+vwarn(short alert_func, const char *format, va_list ap)
+{
+ Rect bounds;
+ short hit, itype;
+ WindowPtr win, dialog;
+ Handle ihandle;
+ Rect irect;
+
+ GetPort(&win);
+
+ vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
+
+ ParamText(CtoPstr(err_str), "\p", "\p", "\p");
+
+ center_in_screen(320, 110, false, &bounds);
+ dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
+ (WindowPtr)-1L, false, 0, alert_ditl_h);
+
+ GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect);
+ switch (alert_func) {
+ case CAUTION_ALERT:
+ ihandle = GetIcon(cautionIcon);
+ break;
+ case NOTE_ALERT:
+ ihandle = GetIcon(noteIcon);
+ break;
+ case APPICON_ALERT:
+ ihandle = GetResource('ICN#', APPICON_ICN_ID);
+ if (ihandle)
+ break;
+ default:
+ ihandle = GetIcon(stopIcon);
+ }
+ SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect);
+
+ ShowWindow(dialog);
+
+ for (;;) {
+ ModalDialog(0, &hit);
+ if (hit == ok)
+ break;
+ }
+ ReleaseResource(ihandle);
+ DisposDialog(dialog);
+
+ /*
+ * Pre-allocate for the next one while we have memory. We can't use
+ * xNewHandle because we might already be alerting about an OOM
+ * condition.
+ */
+ DisposHandle(alert_ditl_h);
+ alert_ditl_h = NewHandle(sizeof(alert_ditl));
+ if (alert_ditl_h == NULL)
+ ExitToShell();
+ HLock(alert_ditl_h);
+ memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
+ HUnlock(alert_ditl_h);
+
+ SetPort(win);
+}
+
+void
+panic(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(STOP_ALERT, format, ap);
+ va_end(ap);
+
+ ExitToShell();
+}
+
+void
+err(short ret, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(STOP_ALERT, format, ap);
+ va_end(ap);
+
+ ExitToShell();
+}
+
+void
+warn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(CAUTION_ALERT, format, ap);
+ va_end(ap);
+}
+
+void
+warnx(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(CAUTION_ALERT, format, ap);
+ va_end(ap);
+}
+
+void
+note(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(NOTE_ALERT, format, ap);
+ va_end(ap);
+}
+
+void
+appicon_note(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(APPICON_ALERT, format, ap);
+ va_end(ap);
+}
+
+short
+ask(const char *format, ...)
+{
+ Rect bounds;
+ short hit;
+ WindowPtr win, dialog;
+ va_list ap;
+
+ GetPort(&win);
+
+ va_start(ap, format);
+ vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
+ va_end(ap);
+
+ ParamText(CtoPstr(err_str), "\p", "\p", "\p");
+
+ ask_ditl_h = xNewHandle(sizeof(ask_ditl));
+ HLock(ask_ditl_h);
+ 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);
+
+ ShowWindow(dialog);
+ for (;;) {
+ ModalDialog(0, &hit);
+ if (hit == 1 || hit == 2)
+ break;
+ }
+ DisposDialog(dialog);
+ DisposHandle(ask_ditl_h);
+
+ SetPort(win);
+
+ return (hit == 1);
+}
+
+void
+about(char *program_name)
+{
+ VersRecHndl vers;
+ char vers_s[255];
+ char short_vers[255] = { 0 };
+ short vlen, n;
+
+ if ((vers = (VersRecHndl)GetResource('vers', 1))) {
+ /*
+ * vers "long version string" is a pascal string after the
+ * short version pascal string
+ */
+ HLock(vers);
+ vlen = (*vers)->shortVersion[0];
+ memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
+ sizeof((*vers)->shortVersion) - vlen - 1);
+ PtoCstr(short_vers);
+ snprintf(vers_s, sizeof(vers_s), "%s %s", program_name,
+ short_vers);
+ for (n = 0; n < sizeof(vers_s); n++) {
+ if (vers_s[n] == '©') {
+ vers_s[n - 1] = '\r';
+ break;
+ }
+ }
+ ReleaseResource(vers);
+ appicon_note("%s", vers_s);
+ } else
+ warnx("Can't find version number!");
+}
+
+void
+progress(char *format, ...)
+{
+ static Str255 progress_s;
+ Handle thandle;
+ va_list argptr;
+ Rect bounds;
+ Rect trect;
+ short ttype;
+
+ if (format == NULL) {
+ if (progress_dialog != NULL) {
+ DisposDialog(progress_dialog);
+ DisposHandle(progress_ditl_h);
+ progress_dialog = NULL;
+ }
+ return;
+ }
+
+ va_start(argptr, format);
+ vsnprintf((char *)progress_s, 256, format, argptr);
+ va_end(argptr);
+ CtoPstr(progress_s);
+
+ if (progress_dialog == NULL) {
+ progress_ditl_h = xNewHandle(sizeof(progress_ditl));
+ HLock(progress_ditl_h);
+ 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);
+ }
+
+ GetDItem(progress_dialog, 1, &ttype, &thandle, &trect);
+ SetIText(thandle, progress_s);
+
+ ShowWindow(progress_dialog);
+ DrawDialog(progress_dialog);
+}
+
+void
+window_rect(WindowPtr win, Rect *ret)
+{
+ *ret = (*(((WindowPeek)win)->strucRgn))->rgnBBox;
+ ret->left += 1;
+ ret->right -= 2;
+ ret->top -= 1;
+ 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;
+}
+
+Point
+centered_sfget_dialog(void)
+{
+ Point p;
+ Rect r;
+
+ center_in_screen(364, 216, false, &r);
+ p.h = r.left;
+ p.v = r.top;
+
+ return p;
+}
+
+Point
+centered_sfput_dialog(void)
+{
+ Point p;
+ Rect r;
+
+ center_in_screen(320, 200, false, &r);
+ p.h = r.left;
+ p.v = r.top;
+
+ return p;
+}
+
+/*
+ * General Mac-specific non-GUI functions
+ */
+
+static unsigned long _xorshift_state = 0;
+unsigned long
+xorshift32(void)
+{
+ unsigned long x = _xorshift_state;
+ if (x == 0)
+ x = Ticks;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return _xorshift_state = x;
+}
+
+
+/*
+ * Error checking wrappers for Mac toolkit functions
+ */
+
+Handle
+xNewHandle(size_t size)
+{
+ Handle h;
+
+ if (size == 0)
+ panic("Zero xNewHandle size");
+
+ h = NewHandle(size);
+ if (h == NULL)
+ panic("Failed to NewHandle(%lu)", size);
+
+ return h;
+}
+
+Handle
+xGetResource(ResType type, short id)
+{
+ Handle h;
+
+ h = GetResource(type, id);
+ if (h == NULL)
+ panic("Failed to find resource %d", id);
+
+ return h;
+}
+
+StringHandle
+xGetString(short id)
+{
+ StringHandle h;
+
+ h = GetString(id);
+ if (h == NULL)
+ panic("Failed to find STR resource %d", id);
+
+ return h;
+}
+
+char *
+xGetStringAsChar(short id)
+{
+ StringHandle h;
+ char *out;
+ size_t l;
+
+ h = xGetString(id);
+ HLock(h);
+ l = (*h)[0];
+ out = xmalloc(l + 1);
+ if (out != NULL) {
+ memcpy((void *)out, (void *)(*h + 1), l);
+ out[l] = '\0';
+ }
+ ReleaseResource(h);
+
+ return out;
+}
+
+bool
+xGetStringAsLong(short id, long *ret)
+{
+ char *c;
+
+ c = xGetStringAsChar(id);
+ if (c == NULL)
+ return false;
+ *ret = atol(c);
+ xfree(&c);
+ return true;
+}
+
+bool
+xSetHandleSize(Handle h, Size s)
+{
+ SetHandleSize(h, s);
+ if (MemError()) {
+ warn("Failed to SetHandleSize to %ld", s);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Filesystem utilities
+ */
+
+short
+getpath(short vRefNum, Str255 fileName, Str255 ret, bool include_file)
+{
+ WDPBRec wdir;
+ HVolumeParam wvol;
+ DirInfo wcinfo;
+ char *name = NULL;
+ size_t retlen = 0, len;
+ char *tmpret = NULL, *tmp = NULL;
+ char *lastcolon;
+
+ ret[0] = 0;
+
+ if (strchr((char *)fileName + 1, ':') != NULL) {
+ /* already a full path */
+ memcpy(ret, fileName, 256);
+ if (!include_file) {
+ PtoCstr(ret);
+ lastcolon = strrchr((char *)ret, ':');
+ lastcolon[0] = '\0';
+ CtoPstr(ret);
+ }
+ return 0;
+ }
+
+ name = xmalloc(FILENAME_MAX);
+ if (name == NULL)
+ return 1;
+
+ wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
+ wdir.ioWDIndex = 0;
+ wdir.ioWDProcID = 0;
+ wdir.ioNamePtr = (StringPtr)name;
+ if (PBGetWDInfo(&wdir, 0) != noErr) {
+ warn("Failed looking up directory");
+ xfree(&name);
+ return 1;
+ }
+
+ wvol.ioNamePtr = (StringPtr)name;
+ wvol.ioVRefNum = vRefNum;
+ wvol.ioVolIndex = 0;
+
+ if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
+ warn("Failed getting volume info");
+ xfree(&name);
+ return 1;
+ }
+
+ if (wvol.ioVSigWord != 0x4244) {
+ warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
+ xfree(&name);
+ return 1;
+ }
+
+ wcinfo.ioVRefNum = vRefNum;
+ wcinfo.ioNamePtr = (StringPtr)name;
+ wcinfo.ioFDirIndex = -1;
+ wcinfo.ioDrParID = wdir.ioWDDirID;
+ wcinfo.ioDrDirID = wdir.ioWDDirID;
+
+ tmp = xmalloc(FILENAME_MAX);
+ if (tmp == NULL) {
+ xfree(&name);
+ return 1;
+ }
+ tmpret = xmalloc(FILENAME_MAX);
+ if (tmp == NULL) {
+ xfree(&tmp);
+ xfree(&name);
+ return 1;
+ }
+
+ /* go backwards, prepending each folder's parent */
+ while (wcinfo.ioDrParID != 1) {
+ wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
+
+ if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
+ break;
+
+ len = name[0];
+ PtoCstr(name);
+ if (retlen == 0) {
+ retlen = len;
+ strlcpy(tmpret, (char *)name, FILENAME_MAX);
+ } else {
+ strlcpy(tmp, tmpret, FILENAME_MAX);
+ snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp);
+ }
+ }
+
+ if (include_file) {
+ /* append the original path */
+ memcpy(name, fileName, FILENAME_MAX);
+ PtoCstr(name);
+ if (retlen == 0)
+ strlcpy(tmpret, name, FILENAME_MAX);
+ else {
+ strlcat(tmpret, ":", FILENAME_MAX);
+ strlcat(tmpret, name, FILENAME_MAX);
+ }
+ } else if (retlen == 0) {
+ ret[0] = 0;
+ xfree(&tmp);
+ xfree(&tmpret);
+ xfree(&name);
+ return 0;
+ }
+
+ CtoPstr(tmpret);
+ memcpy(ret, tmpret, FILENAME_MAX);
+ xfree(&tmp);
+ xfree(&tmpret);
+ xfree(&name);
+
+ return 0;
+}
+
+short
+stat(char *path, struct stat *sb)
+{
+ char *ppath;
+ short ret;
+
+ ppath = xstrdup(path);
+ if (ppath == NULL)
+ return -1;
+ CtoPstr(ppath);
+ ret = FStat((unsigned char *)ppath, sb);
+ xfree(&ppath);
+
+ return ret;
+}
+
+short
+FStat(Str255 path, struct stat *sb)
+{
+ CInfoPBRec catblock = { 0 };
+ short ret;
+
+ catblock.hFileInfo.ioNamePtr = path;
+ ret = PBGetCatInfo(&catblock, 0);
+ if (ret != noErr)
+ return -1;
+
+ /* data fork logical length + resource fork logical length */
+ sb->st_size = catblock.hFileInfo.ioFlLgLen +
+ catblock.hFileInfo.ioFlRLgLen;
+ sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
+ sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
+ sb->st_flags = catblock.hFileInfo.ioFlAttrib;
+
+ return 0;
+}
+
+bool
+FIsDir(Str255 path)
+{
+ struct stat st;
+
+ if (FStat(path, &st) != 0)
+ return false;
+
+ /* bit 4 is set in ioFlAttrib if the item is a directory */
+ if (st.st_flags & (1 << 4))
+ return true;
+
+ return false;
+}
+
+OSErr
+copy_file(Str255 source, Str255 dest, bool overwrite)
+{
+ FInfo fi;
+ IOParam pb;
+ short error, source_ref, dest_ref;
+
+ /* copy data fork */
+
+ error = GetFInfo(source, 0, &fi);
+ if (error)
+ return error;
+
+ error = Create(dest, 0, fi.fdCreator, fi.fdType);
+ if (error == dupFNErr && overwrite) {
+ error = FSDelete(dest, 0);
+ if (error)
+ return error;
+ error = Create(dest, 0, fi.fdCreator, fi.fdType);
+ }
+ if (error)
+ return error;
+
+ memset(&pb, 0, sizeof(pb));
+ pb.ioNamePtr = source;
+ pb.ioPermssn = fsRdPerm;
+ error = PBOpen(&pb, false);
+ if (error)
+ return error;
+ source_ref = pb.ioRefNum;
+
+ error = FSOpen(dest, 0, &dest_ref);
+ if (error) {
+ FSClose(source_ref);
+ return error;
+ }
+
+ error = copy_file_contents(source_ref, dest_ref);
+
+ FSClose(source_ref);
+ FSClose(dest_ref);
+
+ if (error)
+ return error;
+
+ /*
+ * Copy resource fork, open source as shared read/write in case it's
+ * an open resource file.
+ */
+ source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
+
+ if (source_ref == -1 && ResError() == eofErr) {
+ /* no resource fork */
+ FSClose(source_ref);
+ FSClose(dest_ref);
+ return 0;
+ }
+
+ if (source_ref == -1)
+ return ResError();
+
+ CreateResFile(dest);
+ if (ResError()) {
+ FSClose(source_ref);
+ return ResError();
+ }
+ error = OpenRF(dest, 0, &dest_ref);
+ if (error) {
+ FSClose(source_ref);
+ return error;
+ }
+
+ error = copy_file_contents(source_ref, dest_ref);
+
+ FSClose(source_ref);
+ FSClose(dest_ref);
+
+ return error;
+}
+
+OSErr
+copy_file_contents(short source_ref, short dest_ref)
+{
+ char *buf;
+ short error;
+ long source_size, count;
+
+ GetEOF(source_ref, &source_size);
+ error = SetFPos(source_ref, fsFromStart, 0);
+ if (error)
+ return error;
+ error = SetEOF(dest_ref, source_size);
+ if (error)
+ return error;
+ error = SetFPos(dest_ref, fsFromStart, 0);
+ if (error)
+ return error;
+
+ buf = xmalloc(1024);
+ if (buf == NULL)
+ return -1;
+
+ while (source_size > 0) {
+ count = 1024;
+ if (count > source_size)
+ count = source_size;
+ error = FSRead(source_ref, &count, buf);
+ if (error && error != eofErr)
+ break;
+ source_size -= count;
+ error = FSWrite(dest_ref, &count, buf);
+ if (error && error != eofErr)
+ break;
+ }
+
+ xfree(&buf);
+
+ if (error && error != eofErr)
+ return error;
+
+ return 0;
+}
+
+/* read a \r-terminated line or the final non-line bytes of an open file */
+OSErr
+FSReadLine(short frefnum, char *buf, size_t buflen)
+{
+ char tbuf;
+ size_t pos, fsize, rlen = 1, total_read = 0;
+ short error;
+
+ GetFPos(frefnum, &pos);
+ GetEOF(frefnum, &fsize);
+
+ for (; pos <= fsize; pos++) {
+ if (total_read > buflen)
+ return -1;
+
+ error = FSRead(frefnum, &rlen, &tbuf);
+ if (error)
+ return -1;
+
+ if (tbuf == '\r')
+ return total_read;
+
+ buf[total_read++] = tbuf;
+ }
+
+ /* nothing found until the end of the file */
+ return total_read;
+}
+
+
+/*
+ * Gestalt functions
+ */
+char *
+gestalt_machine_type(void)
+{
+ short error;
+ long resp;
+
+ error = Gestalt(gestaltMachineType, &resp);
+ if (error)
+ return NULL;
+ switch (resp) {
+ case gestaltClassic:
+ return "Macintosh 128K";
+ case gestaltMacXL:
+ return "Macintosh XL";
+ case gestaltMac512KE:
+ return "Macintosh 512Ke";
+ case gestaltMacPlus:
+ return "Macintosh Plus";
+ case gestaltMacSE:
+ return "Macintosh SE";
+ case gestaltMacII:
+ return "Macintosh II";
+ case gestaltMacIIx:
+ return "Macintosh IIx";
+ case gestaltMacIIcx:
+ return "Macintosh IIcx";
+ case gestaltMacSE030:
+ return "Macintosh SE/30";
+ case gestaltPortable:
+ return "Macintosh Portable";
+ case gestaltMacIIci:
+ return "Macintosh IIci";
+ case gestaltMacIIfx:
+ return "Macintosh IIfx";
+ case gestaltMacClassic:
+ return "Macintosh Classic";
+ case gestaltMacIIsi:
+ return "Macintosh IIsi";
+ case gestaltMacLC:
+ return "Macintosh LC";
+ }
+
+ return NULL;
+}
+
+char *
+get_version(bool long_version)
+{
+ static char vers_s[256] = { 0 };
+ char *v;
+ VersRecHndl vers;
+ short len;
+
+ vers = (VersRecHndl)GetResource('vers', 1);
+ if (!vers)
+ return "?.?";
+
+ HLock(vers);
+ v = (char *)&(*vers)->shortVersion;
+ len = v[0];
+ if (long_version) {
+ v += len + 1;
+ len = v[0];
+ }
+ memcpy(vers_s, v, len + 1);
+ ReleaseResource(vers);
+
+ PtoCstr(vers_s);
+ return vers_s;
+}
+
+/*
+ * General Mac-specific GUI functions
+ */
+
+short
+FontHeight(short font_id, short size)
+{
+ FMInput f_in;
+ FMOutput *f_out;
+
+ if (font_id == -1)
+ font_id = thePort->txFont;
+ if (size == -1)
+ size = thePort->txSize;
+
+ f_in.family = font_id;
+ f_in.size = size;
+ f_in.face = 0;
+ f_in.needBits = false;
+ f_in.device = 0;
+ f_in.numer.h = f_in.numer.v = 1;
+ f_in.denom.h = f_in.denom.v = 1;
+
+ f_out = FMSwapFont(&f_in);
+
+ return (((f_out->leading + f_out->ascent + f_out->descent) *
+ f_out->numer.v) / f_out->denom.v);
+}
+
+void
+DrawGrowIconOnly(WindowPtr win)
+{
+ Rect r;
+ RgnHandle tmp;
+
+ GetClip(tmp = NewRgn());
+ if (tmp == NULL) {
+ warn("NewRgn() failed");
+ return;
+ }
+ r = win->portRect;
+ r.top = r.bottom - SCROLLBAR_WIDTH;
+ r.left = r.right - SCROLLBAR_WIDTH + 1;
+ ClipRect(&r);
+ DrawGrowIcon(win);
+ SetClip(tmp);
+ DisposeRgn(tmp);
+}
+
+/* assumes a fixed-width style */
+short
+TEGetWidth(short off, TEHandle te)
+{
+ TextStyle style;
+ short fheight, fwidth, ascent, old_font, old_size;
+
+ TEGetStyle(off, &style, &fheight, &ascent, te);
+
+ old_font = thePort->txFace;
+ old_size = thePort->txSize;
+ thePort->txFace = style.tsFont;
+ thePort->txSize = style.tsSize;
+ fwidth = CharWidth('m');
+ thePort->txFace = old_font;
+ thePort->txSize = old_size;
+
+ return fwidth;
+}
+
+#define ceildiv(a,b) ((a / b) + (!!(a % b)))
+
+void
+UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te,
+ bool reset)
+{
+ size_t vlines, telines;
+ TERec *ter;
+ RgnHandle rgn;
+ short fheight, fwidth, max, val, per_line, horiz, max_chars, n;
+
+ HLock(control);
+ HLock(te);
+ ter = *te;
+
+ horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
+ ((*control)->contrlRect.right - (*control)->contrlRect.left));
+
+ if (horiz) {
+ fwidth = TEGetWidth(0, te);
+ per_line = ((ter->viewRect.right - ter->viewRect.left) /
+ fwidth) - 1;
+ for (max_chars = 0, n = 1; n < ter->nLines; n++) {
+ if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
+ max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
+ }
+
+ if (max_chars <= per_line)
+ /* don't enable the scrollbar */
+ max = 1;
+ else
+ max = max_chars - per_line + 1;
+
+ if (reset) {
+ val = 1;
+ TESetSelect(0, 0, te);
+ } else {
+ val = (ter->viewRect.left - ter->destRect.left);
+
+ if (val < 0)
+ val = 1;
+ else {
+ val = ceildiv(val, fwidth) + 1;
+ if (val > max)
+ max = val;
+ }
+ }
+ } else {
+ fheight = TEGetHeight(0, 0, te);
+ vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
+ telines = ter->nLines;
+ /* telines is inaccurate if the last line doesn't have any chars */
+ //if (telines >= vlines)
+ // telines++;
+ max = telines - vlines + 1;
+ if (max < 1)
+ max = 1;
+
+ if (reset) {
+ val = 1;
+ TESetSelect(0, 0, te);
+ } else {
+ val = (ter->viewRect.top - ter->destRect.top);
+ if (val < 0)
+ val = 1;
+ else {
+ val = ceildiv(val, fheight) + 1;
+ if (val > max)
+ max = val;
+ }
+ }
+ }
+
+ /*
+ * Avoid SetCtlMax because it will redraw and then SetCtlValue will
+ * redraw again, which can cause a jump if we're trying to keep the
+ * scrollbar position in the same place (like at the bottom).
+ */
+ (*control)->contrlMax = max;
+ SetCtlValue(control, val);
+
+ rgn = NewRgn();
+ RectRgn(rgn, &(*control)->contrlRect);
+ UpdtControl(win, rgn);
+ CloseRgn(rgn);
+
+ HUnlock(te);
+ HUnlock(control);
+}
+
+void
+SetTrackControlTE(TEHandle te)
+{
+ track_control_te = te;
+}
+
+pascal void
+TrackMouseDownInControl(ControlHandle control, short part)
+{
+ TERec *ter;
+ short page, val, adj, fheight, fwidth, horiz, per_line;
+
+ if (track_control_te == NULL)
+ panic("TrackMouseDownInControl without SetTrackControlTE");
+
+ HLock(track_control_te);
+ ter = *track_control_te;
+
+ horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
+ ((*control)->contrlRect.right - (*control)->contrlRect.left));
+
+ if (horiz) {
+ fwidth = TEGetWidth(0, track_control_te);
+ per_line = ((ter->viewRect.right - ter->viewRect.left) /
+ fwidth) - 1;
+ page = ceildiv(GetCtlMax(control) + per_line,
+ ((per_line * 75) / 100)) + 1;
+ } else {
+ /* keep 1 line of context between pages */
+ fheight = TEGetHeight(0, 0, track_control_te);
+ page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
+ }
+
+ adj = 0;
+ switch (part) {
+ case inUpButton:
+ adj = -1;
+ break;
+ case inPageUp:
+ adj = -page;
+ break;
+ case inDownButton:
+ adj = 1;
+ break;
+ case inPageDown:
+ adj = page;
+ break;
+ }
+
+ val = GetCtlValue(control);
+ if (val + adj < GetCtlMin(control))
+ adj = -(val - GetCtlMin(control));
+ if (val + adj > GetCtlMax(control))
+ adj = (GetCtlMax(control) - val);
+ if (adj == 0)
+ return;
+
+ if (horiz)
+ TEScroll(-adj * fwidth, 0, track_control_te);
+ else
+ TEScroll(0, -adj * fheight, track_control_te);
+
+ SetCtlValue(control, val + adj);
+
+ HUnlock(track_control_te);
+}
+
+pascal bool
+ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
+{
+ WindowPtr event_win;
+ short event_in;
+ char key;
+
+ switch (event->what) {
+ case mouseDown:
+ event_in = FindWindow(event->where, &event_win);
+
+ switch (event_in) {
+ case inGoAway:
+ if (TrackGoAway(dlg, event->where)) {
+ *hit = -1;
+ return true;
+ }
+ }
+ break;
+ case keyDown:
+ key = event->message & charCodeMask;
+
+ if (event->modifiers & cmdKey) {
+ switch (key) {
+ case 'x':
+ ZeroScrap();
+ DlgCut(dlg);
+ break;
+ case 'c':
+ ZeroScrap();
+ DlgCopy(dlg);
+ break;
+ case 'v':
+ if (TEFromScrap() == noErr)
+ DlgPaste(dlg);
+ break;
+ }
+ event->what = nullEvent;
+ return false;
+ } else if (key == 13 || key == 3) {
+ /* OK button */
+ *hit = OK;
+ return true;
+ }
+
+ break;
+ }
+
+ return false;
+}
+
+static short _password_dialog_ditl_id = -1;
+static char *_password_dialog_storage = NULL;
+static size_t _password_dialog_storage_len = 0;
+
+void
+PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len)
+{
+ _password_dialog_ditl_id = ditl_id;
+ _password_dialog_storage = storage;
+ _password_dialog_storage_len = len;
+
+ memset(_password_dialog_storage, 0, len);
+}
+
+pascal bool
+PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit)
+{
+ DialogPeek dlgp;
+ short sel_start, sel_end;
+ char key;
+
+ dlgp = (DialogPeek)dlg;
+ if (dlgp->editField == _password_dialog_ditl_id - 1) {
+ sel_start = (*(dlgp->textH))->selStart;
+ sel_end = (*(dlgp->textH))->selEnd;
+
+ switch (event->what) {
+ case keyDown:
+ case autoKey:
+ key = event->message & charCodeMask;
+ if (event->modifiers & cmdKey) {
+ /* TODO: implement DlgPaste for cmd+v? */
+ event->what = nullEvent;
+ return false;
+ }
+
+ if (key == 8) {
+ /* backspace */
+ if (sel_start == sel_end && sel_start > 0)
+ memmove(_password_dialog_storage + sel_start - 1,
+ _password_dialog_storage + sel_start,
+ _password_dialog_storage_len - sel_start - 1);
+ else if (sel_start != sel_end)
+ memmove(_password_dialog_storage + sel_start,
+ _password_dialog_storage + sel_end,
+ _password_dialog_storage_len - sel_end - 1);
+ } else if (sel_start >= _password_dialog_storage_len) {
+ event->what = nullEvent;
+ return false;
+ } else if (key >= ' ' && key <= '~') {
+ if (sel_start != sel_end)
+ /* delete selection before making space for new char */
+ memmove(_password_dialog_storage + sel_start,
+ _password_dialog_storage + sel_end,
+ _password_dialog_storage_len - sel_end - 1);
+ memmove(_password_dialog_storage + sel_start + 1,
+ _password_dialog_storage + sel_start,
+ _password_dialog_storage_len - sel_start - 1);
+ _password_dialog_storage[sel_start] = key;
+ event->message = '•';
+ }
+ _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0';
+ sel_start = 0;
+ break;
+ }
+ }
+
+ return ModalDialogFilter(dlg, event, hit);
+}
+
+void
+PasswordDialogFieldFinish(void)
+{
+ _password_dialog_ditl_id = -1;
+ _password_dialog_storage = NULL;
+ _password_dialog_storage_len = 0;
+}
+
+/* (*(some_te))->caretHook = NullCaretHook; */
+pascal void
+NullCaretHook(void)
+{
+ asm {
+ move.l (a7)+,d0
+ rts
+ }
+}
+
+static bool menu_bar_hidden = false;
+static short old_mbar_height;
+static Rect mbar_rect;
+static RgnHandle mbar_save_region;
+
+void
+HideMenuBar(void)
+{
+ RgnHandle mbar_region;
+ WindowPeek win;
+
+ old_mbar_height = GetMBarHeight();
+ SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top,
+ screenBits.bounds.right, screenBits.bounds.top + old_mbar_height);
+
+ mbar_save_region = NewRgn();
+ mbar_region = NewRgn();
+
+ MBarHeight = 0;
+ CopyRgn(GetGrayRgn(), mbar_save_region);
+ RectRgn(mbar_region, &mbar_rect);
+ UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn());
+
+ win = (WindowPeek)FrontWindow();
+ PaintOne(win, mbar_region);
+ PaintBehind(win, mbar_region);
+ CalcVis(win);
+ CalcVisBehind(win, mbar_region);
+ DisposeRgn(mbar_region);
+
+ menu_bar_hidden = true;
+}
+
+void
+RestoreHiddenMenuBar(void)
+{
+ WindowPeek win;
+
+ if (!menu_bar_hidden)
+ return;
+
+ CopyRgn(mbar_save_region, GetGrayRgn());
+ MBarHeight = old_mbar_height;
+
+ RectRgn(mbar_save_region, &mbar_rect);
+ win = (WindowPeek)FrontWindow();
+ CalcVis(win);
+ CalcVisBehind(win, mbar_save_region);
+ DisposeRgn(mbar_save_region);
+
+ menu_bar_hidden = false;
+
+ 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;
+}
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(char **stringp, const char *delim)
+{
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ return (NULL);
+}
+
+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, nwritten = 0;
+ register unsigned long n;
+ 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)) != NULL)
+ 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 Mon Mar 27 21:33:13 2023
+++ util.h Mon Mar 27 21:33:13 2023
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020-2022 joshua stein <jcs@jcs.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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ULONG_MAX
+#endif
+
+#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)))
+
+#define CHARS_TO_LONG(a,b,c,d) (unsigned long)(\
+ ((unsigned long)((unsigned char)(a)) << 24) | \
+ ((unsigned long)((unsigned char)(b)) << 16) | \
+ ((unsigned long)((unsigned char)(c)) << 8) | \
+ (unsigned long)((unsigned char)(d)) )
+#define CHARS_TO_SHORT(a,b) (unsigned short)(\
+ ((unsigned short)((unsigned char)(a)) << 8) | \
+ (unsigned short)((unsigned char)(b)) )
+
+#define SCROLLBAR_WIDTH 16
+
+/* GetMBarHeight() is not very useful */
+#define MENUBAR_HEIGHT 20
+
+#define TICKS_PER_SEC 60L
+
+#define MAX_TEXTEDIT_SIZE 32767L
+
+#ifndef bool
+typedef Boolean bool;
+#endif
+typedef signed long off_t;
+typedef signed long ssize_t;
+typedef unsigned char u_char;
+typedef unsigned long u_int;
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned long u_int32_t;
+
+#define BYTE_ORDER BIG_ENDIAN
+
+typedef struct {
+ short push[2], rts;
+ void *addr;
+} tCodeStub;
+
+struct stat {
+ short st_mode;
+ ssize_t st_size;
+ time_t st_ctime;
+ time_t st_mtime;
+ unsigned char st_flags;
+};
+
+void util_init(void);
+
+void * xmalloc(size_t);
+void xfree(void *ptrptr);
+void xfree_verify(void);
+void * xmalloczero(size_t);
+void * xcalloc(size_t, size_t);
+void * xrealloc(void *src, size_t size);
+void * xreallocarray(void *, size_t, size_t);
+char * xstrdup(const char *);
+char * xstrndup(const char *str, size_t maxlen);
+
+bool grow_to_fit(void *var, size_t *var_size, size_t used_size, size_t add,
+ size_t grow_amount);
+
+short getline(char *str, size_t len, char **ret);
+const char * ordinal(unsigned short n);
+size_t rtrim(char *str, char *chars);
+long strpos_quoted(char *str, char c);
+char * OSTypeToString(OSType type);
+
+unsigned long xorshift32(void);
+
+void panic(const char *format, ...);
+void err(short ret, const char *format, ...);
+void warnx(const char *format, ...);
+void warn(const char *format, ...);
+void note(const char *format, ...);
+void appicon_note(const char *format, ...);
+short ask(const char *format, ...);
+#define ASK_YES 1
+#define ASK_NO 2
+void about(char *program_name);
+void progress(char *format, ...);
+void window_rect(WindowPtr win, Rect *ret);
+void center_in_screen(short width, short height, bool titlebar, Rect *b);
+Point centered_sfget_dialog(void);
+Point centered_sfput_dialog(void);
+
+Handle xNewHandle(size_t size);
+Handle xGetResource(ResType type, short id);
+StringHandle xGetString(short id);
+char * xGetStringAsChar(short id);
+bool xGetStringAsLong(short id, long *ret);
+bool xSetHandleSize(Handle h, Size s);
+
+short getpath(short vRefNum, Str255 fileName, Str255 ret, bool include_file);
+bool FIsDir(Str255 path);
+short stat(char *path, struct stat *sb);
+short FStat(Str255 path, struct stat *sb);
+OSErr copy_file(Str255 source, Str255 dest, bool overwrite);
+OSErr copy_file_contents(short source_ref, short dest_ref);
+OSErr FSReadLine(short frefnum, char *buf, size_t buflen);
+
+char * gestalt_machine_type(void);
+char * get_version(bool long_version);
+
+short FontHeight(short font_id, short size);
+void DrawGrowIconOnly(WindowPtr win);
+short TEGetWidth(short off, TEHandle te);
+void UpdateScrollbarForTE(GrafPtr win, ControlHandle scroller, TEHandle te,
+ bool reset);
+void SetTrackControlTE(TEHandle te);
+pascal void TrackMouseDownInControl(ControlHandle control, short part);
+pascal bool ModalDialogFilter(DialogPtr dlg, EventRecord *event,
+ short *hit);
+void PasswordDialogFieldFilterSetup(short ditl_id, char *storage,
+ size_t len);
+pascal bool PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event,
+ short *hit);
+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);
+char * strsep(char **stringp, const char *delim);
+int snprintf(char *s, size_t size, const char *fmt, ...);
+int vsnprintf(char *s, size_t size, const char *fmt, void *p);
+
+#endif