AmendHub

Download:

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