AmendHub

Download:

jcs

/

amend

/

amendments

/

41

util: Lots of changes from other projects

Make all filesystem functions work on Str255s for filenames, not
char *s
 
Make vwarn() (and new progress() function) use new hard-coded DITLs
to avoid needing to have these in every project's resource fork
 
Use panic() instead of err(1, ...)

jcs made amendment 41 over 2 years ago
--- util.c Thu Dec 30 16:39:45 2021 +++ util.c Mon Jan 10 22:12:47 2022 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 joshua stein <jcs@jcs.org> + * 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 @@ -22,18 +22,38 @@ #include "util.h" /* ALRT resources */ -#define ERROR_ALERT_ID 129 #define ASK_ALERT_ID 130 -#define ERROR_STRING_SIZE 1024 +#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_ICON 3 +static char alert_ditl[] = { + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, + 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x02, + 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x32, 0x00, 0x40, 0x01, 0x21, 0x08, 0x02, + 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A, 0xA0, 0x02, + 0x00, 0x02 +}; +static Handle alert_ditl_h = NULL; + +static 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; + enum { STOP_ALERT, CAUTION_ALERT, NOTE_ALERT }; -Handle err_str; static TEHandle track_control_te = NULL; void vwarn(short alert_func, const char *format, va_list ap); @@ -48,10 +68,10 @@ xmalloc(size_t size) void *ptr; if (size == 0) - err(2, "xmalloc: zero size"); + panic("xmalloc: zero size"); ptr = malloc(size); if (ptr == NULL) - err(2, "xmalloc: allocating %zu bytes", size); + panic("xmalloc: allocating %zu bytes", size); return ptr; } @@ -70,7 +90,7 @@ xcalloc(size_t nmemb, size_t size) ptr = calloc(nmemb, size); if (ptr == NULL) - err(2, "xcalloc: allocating %zu * %zu bytes", nmemb, size); + panic("xcalloc: allocating %zu * %zu bytes", nmemb, size); return ptr; } @@ -81,25 +101,30 @@ xrealloc(void *src, size_t size) ret = realloc(src, size); if (ret == NULL) - err(2, "Couldn't realloc %lu bytes of memory", size); + panic("Couldn't realloc %lu bytes of memory", size); return ret; } #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * +xmallocarray(size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) + panic("xmallocarray"); + return xmalloc(size * nmemb); +} + +void * xreallocarray(void *optr, size_t nmemb, size_t size) { void *new_ptr; if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && - nmemb > 0 && SIZE_MAX / nmemb < size) { - err(2, "reallocarray"); - } - if ((new_ptr = realloc(optr, size * nmemb)) == NULL) - err(2, "realloc"); - - return new_ptr; + nmemb > 0 && SIZE_MAX / nmemb < size) + panic("xreallocarray"); + return xrealloc(optr, size * nmemb); } char * @@ -111,8 +136,7 @@ xstrdup(const char *str) len = strlen(str); cp = xmalloc(len + 1); - strncpy(cp, str, len); - cp[len] = '\0'; + strlcpy(cp, str, len + 1); return cp; } @@ -139,48 +163,103 @@ getline(char *str, size_t len, char **ret) return 0; } -/* - * BSD err(3) and warn(3) functions, must call err_init() before using - */ - -void -err_init(void) +const char * +ordinal(unsigned short n) { - if (!(err_str = NewHandle(ERROR_STRING_SIZE))) { - SysBeep(20); - ExitToShell(); + 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"; + } } } +static char ostype_s[5]; + +char * +OSTypeToString(OSType type) +{ + 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) { - size_t len; - short quit = 0; - WindowPtr win; + Rect bounds, irect; + short quit = 0, height, width, hit; + WindowPtr win, dialog; + OSType itype; + Handle ihandle; GetPort(&win); - HLock(err_str); - len = vsprintf(*err_str, format, ap); - if (len >= ERROR_STRING_SIZE) { - sprintf(*err_str, "raise_error string overflow!"); - quit = 1; - } + vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); - ParamText(CtoPstr(*err_str), "\p", "\p", "\p"); + width = 300; + height = 100; + bounds.left = (screenBits.bounds.right - width) / 2; + bounds.right = bounds.left + width; + bounds.top = GetMBarHeight() + + ((screenBits.bounds.bottom - height) / 2.5); + bounds.bottom = bounds.top + height; + + ParamText(CtoPstr(err_str), "\p", "\p", "\p"); + + alert_ditl_h = xNewHandle(sizeof(alert_ditl)); + HLock(alert_ditl_h); + memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl)); + HUnlock(alert_ditl_h); + + dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, + (WindowPtr)-1L, false, 0, alert_ditl_h); + +#if 0 + /* XXX: why doesn't changing this work? */ + GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect); switch (alert_func) { case CAUTION_ALERT: - CautionAlert(ERROR_ALERT_ID, nil); + ihandle = GetIcon(cautionIcon); break; case NOTE_ALERT: - NoteAlert(ERROR_ALERT_ID, nil); + ihandle = GetIcon(noteIcon); break; default: - StopAlert(ERROR_ALERT_ID, nil); + ihandle = GetIcon(stopIcon); } - HUnlock(err_str); - + ihandle = GetIcon(cautionIcon); + SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect); +#endif + + ShowWindow(dialog); + for (;;) { + ModalDialog(0, &hit); + if (hit == ok) + break; + } + DisposDialog(dialog); + DisposHandle(alert_ditl_h); + SetPort(win); if (quit) @@ -188,35 +267,47 @@ vwarn(short alert_func, const char *format, va_list ap } void -warn(const char *format, ...) +panic(const char *format, ...) { va_list ap; va_start(ap, format); - vwarn(CAUTION_ALERT, format, ap); + vwarn(STOP_ALERT, format, ap); va_end(ap); + + ExitToShell(); } void -warnx(const char *format, ...) +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 -err(short retcode, const char *format, ...) +warnx(const char *format, ...) { va_list ap; va_start(ap, format); - vwarn(STOP_ALERT, format, ap); + vwarn(CAUTION_ALERT, format, ap); va_end(ap); - - ExitToShell(); } void @@ -239,34 +330,94 @@ ask(const char *format, ...) GetPort(&win); - HLock(err_str); va_start(ap, format); - len = vsprintf(*err_str, format, ap); + len = vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); va_end(ap); - if (len >= ERROR_STRING_SIZE) - err(1, "ask string overflow!"); - ParamText(CtoPstr(*err_str), "\p", "\p", "\p"); + ParamText(CtoPstr(err_str), "\p", "\p", "\p"); ret = StopAlert(ASK_ALERT_ID, nil); - HUnlock(err_str); SetPort(win); return ret; } +void +progress(char *format, ...) +{ + static Str255 progress_s; + Handle thandle; + va_list argptr; + Rect bounds = { 100, 90, 160, 420 }; /* tlbr */ + Rect trect; + static WindowPtr progress_win; + short ttype; + if (format == NULL) { + if (progress_dialog != NULL) { + DisposDialog(progress_dialog); + DisposHandle(progress_ditl_h); + progress_dialog = NULL; + } + SetPort(progress_win); + return; + } + + va_start(argptr, format); + vsnprintf((char *)progress_s, 256, format, argptr); + va_end(argptr); + CtoPstr(progress_s); + + if (progress_dialog == NULL) { + GetPort(&progress_win); + + progress_ditl_h = xNewHandle(sizeof(progress_ditl)); + HLock(progress_ditl_h); + memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl)); + HUnlock(progress_ditl_h); + + 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); +} + /* + * 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(unsigned long size) +xNewHandle(size_t size) { Handle h; + if (size == 0) + panic("Zero xNewHandle size"); + h = NewHandle(size); if (h == NULL) - err(1, "Failed to NewHandle(%lu)", size); + panic("Failed to NewHandle(%lu)", size); return h; } @@ -278,7 +429,7 @@ xGetResource(ResType type, short id) h = GetResource(type, id); if (h == NULL) - err(1, "Failed to find resource %d", id); + panic("Failed to find resource %d", id); return h; } @@ -290,7 +441,7 @@ xGetString(short id) h = GetString(id); if (h == NULL) - err(1, "Failed to find STR resource %d", id); + panic("Failed to find STR resource %d", id); return h; } @@ -325,84 +476,27 @@ xGetStringAsLong(short id) return r; } +void +xSetHandleSize(Handle h, Size s) +{ + SetHandleSize(h, s); + if (MemError()) + panic("Failed to SetHandleSize to %ld", s); +} + /* * Filesystem utilities */ -/* - * With SFGetFile, when the user has a folder selected and clicks Open, - * File Manager will change to the folder and expect the user to select a - * file in it. This triggers sfHookOpenFolder, but when the user double- - * clicks on a folder to browse into it, the same result code is returned. - * - * To be able to return the folder itself, we need to know whether the user - * double-clicked a folder, or clicked Open. When we get our hook callback, - * we check where the mouse is in relation to the Open button rect. If - * the user clicked Open, we return the folder itself, otherwise we assume - * the user double-clicked a folder and we should browse into it. - * - * Finally, we return the full path of the file/folder as a char *, rather - * than just a ref id. - */ -char * -askfileordirpath(void) -{ - Point pt = { 75, 75 }; - SFReply reply; - HParamBlockRec hpbr; - Str63 fName = { 0 }; - char *ret = NULL; - - SFGetFile(pt, "\p", NULL, -1, 0, open_dialog_hook, &reply); - if (!reply.good) - return NULL; - - if (reply.fName[0] == 0) { - /* selected a folder, look it up */ - hpbr.fileParam.ioCompletion = 0; - hpbr.fileParam.ioNamePtr = (StringPtr)&fName; - hpbr.fileParam.ioVRefNum = reply.vRefNum; - hpbr.fileParam.ioDirID = reply.fType; - hpbr.fileParam.ioFDirIndex = -1; - PBGetCatInfo(&hpbr, false); - memcpy(reply.fName, fName, sizeof(fName)); - } - - getpath(reply.vRefNum, reply.fName, &ret, true); - - return ret; -} - -pascal short -open_dialog_hook(short theItem, DialogPtr theDialog) -{ - short item_type; - Handle item; - Rect item_rect; - Point mouse; - - if (theItem == sfHookOpenFolder) { - GetDItem(theDialog, getOpen, &item_type, &item, &item_rect); - GetMouse(&mouse); - - if (PtInRect(mouse, &item_rect)) { - /* clicked open */ - return getOpen; - } - } - - return theItem; -} - short -getpath(short vRefNum, Str255 fileName, char **ret, bool include_file) +getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file) { WDPBRec wdir; HVolumeParam wvol; DirInfo wcinfo; Str255 name; - size_t retlen = 0; - char *tmp; + size_t retlen = 0, len; + char tmpret[256], tmp[256]; wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum; wdir.ioWDIndex = 0; @@ -440,17 +534,14 @@ getpath(short vRefNum, Str255 fileName, char **ret, bo if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr) break; + len = name[0]; + PtoCstr(name); if (retlen == 0) { - retlen = name[0]; - *ret = xmalloc(retlen + 1); - sprintf(*ret, "%s", PtoCstr(name)); + retlen = len; + strlcpy(tmpret, (char *)name, sizeof(tmpret)); } else { - tmp = xstrdup(*ret); - free(*ret); - *ret = xmalloc(retlen + 1 + name[0] + 1); - retlen += 1 + name[0]; - sprintf(*ret, "%s:%s", PtoCstr(name), tmp); - free(tmp); + strlcpy(tmp, tmpret, sizeof(tmp)); + snprintf(tmpret, sizeof(tmpret), "%s:%s", name, tmp); } } @@ -459,31 +550,44 @@ getpath(short vRefNum, Str255 fileName, char **ret, bo memcpy(name, fileName, sizeof(name)); PtoCstr(name); if (retlen == 0) - *ret = xstrdup((char *)name); + strlcpy(tmpret, (char *)name, sizeof(tmpret)); else { - *ret = xrealloc(*ret, retlen + 1 + fileName[0] + 1); - sprintf(*ret + retlen, ":%s", (char *)name); + strlcat(tmpret, ":", sizeof(tmpret)); + strlcat(tmpret, (char *)name, sizeof(tmpret)); } } else if (retlen == 0) { - *ret = NULL; + (*ret)[0] = 0; + return 0; } + CtoPstr(tmpret); + memcpy(*ret, tmpret, sizeof(tmpret)); + return 0; } short -stat(const char *path, struct stat *sb) +stat(char *path, struct stat *sb) { - CInfoPBRec catblock = { 0 }; + char *ppath; short ret; - char *tpath; - tpath = xstrdup(path); - CtoPstr(tpath); + ppath = xstrdup(path); + CtoPstr(ppath); + ret = FStat((unsigned char *)ppath, sb); + free(ppath); + + return ret; +} - catblock.hFileInfo.ioNamePtr = (StringPtr)tpath; +short +FStat(Str255 path, struct stat *sb) +{ + CInfoPBRec catblock = { 0 }; + short ret; + + catblock.hFileInfo.ioNamePtr = path; ret = PBGetCatInfo(&catblock, 0); - free(tpath); if (ret != noErr) return -1; @@ -498,12 +602,12 @@ stat(const char *path, struct stat *sb) } bool -is_dir(char *path) +FIsDir(Str255 path) { struct stat st; short ret; - if ((ret = stat(path, &st)) != 0) + if ((ret = FStat(path, &st)) != 0) return ret; /* bit 4 is set in ioFlAttrib if the item is a directory */ @@ -514,10 +618,10 @@ is_dir(char *path) } OSErr -copy_file(Str255 source, Str255 dest, bool overwrite, - bool keep_source_open) +copy_file(Str255 source, Str255 dest, bool overwrite) { FInfo fi; + IOParam pb; short error, source_ref, dest_ref; /* copy data fork */ @@ -536,21 +640,23 @@ copy_file(Str255 source, Str255 dest, bool overwrite, if (error) return error; - error = FSOpen(source, 0, &source_ref); + 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) { - if (!keep_source_open) - FSClose(source_ref); + FSClose(source_ref); return error; } error = copy_file_contents(source_ref, dest_ref); - if (!keep_source_open) - FSClose(source_ref); + FSClose(source_ref); FSClose(dest_ref); if (error) @@ -561,26 +667,31 @@ copy_file(Str255 source, Str255 dest, bool overwrite, * 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()) { - if (!keep_source_open) - FSClose(source_ref); + FSClose(source_ref); return ResError(); } error = OpenRF(dest, 0, &dest_ref); if (error) { - if (!keep_source_open) - FSClose(source_ref); + FSClose(source_ref); return error; } error = copy_file_contents(source_ref, dest_ref); - if (!keep_source_open) - FSClose(source_ref); + FSClose(source_ref); FSClose(dest_ref); return error; @@ -589,7 +700,7 @@ copy_file(Str255 source, Str255 dest, bool overwrite, OSErr copy_file_contents(short source_ref, short dest_ref) { - char buf[512]; + char buf[1024]; short error; long source_size, count; @@ -651,6 +762,7 @@ FSReadLine(short frefnum, char *buf, size_t buflen) return total_read; } + /* * General Mac-specific GUI functions */ @@ -751,7 +863,7 @@ TrackMouseDownInControl(ControlHandle control, short p short page, val, adj, lheight; if (track_control_te == NULL) - err(1, "TrackMouseDownInControl without SetTrackControlTE"); + panic("TrackMouseDownInControl without SetTrackControlTE"); lheight = TEGetHeight(0, 0, track_control_te); --- util.h Thu Dec 30 16:24:50 2021 +++ util.h Mon Jan 10 16:40:54 2022 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 joshua stein <jcs@jcs.org> + * 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 @@ -25,12 +25,19 @@ #define SIZE_MAX ULONG_MAX #endif +#define nitems(what) (sizeof((what)) / sizeof((what)[0])) + +#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 SCROLLBAR_WIDTH 16 +/* GetMBarHeight() is not very useful */ +#define MENUBAR_HEIGHT 20 + #define MAX_TEXTEDIT_SIZE 32767L -#define nitems(what) (sizeof((what)) / sizeof((what)[0])) - #ifndef bool typedef Boolean bool; #endif @@ -57,43 +64,43 @@ void *xmalloc(size_t); void *xmalloczero(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *src, size_t size); +void *xmallocarray(size_t nmemb, size_t size); void *xreallocarray(void *, size_t, size_t); char *xstrdup(const char *); short getline(char *str, size_t len, char **ret); -/* from strnatcmp.c */ -int strnatcmp(char const *a, char const *b); -int strnatcasecmp(char const *a, char const *b); +size_t strlcpy(char *dst, const char *src, size_t dsize); +size_t strlcat(char *dst, const char *src, size_t dsize); +const char *ordinal(unsigned short n); +char *OSTypeToString(OSType type); -void err_init(void); +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 err(short retcode, const char *format, ...); void note(const char *format, ...); short ask(const char *format, ...); #define ASK_YES 1 #define ASK_NO 2 +void progress(char *format, ...); -Handle xNewHandle(unsigned long size); +Handle xNewHandle(size_t size); Handle xGetResource(ResType type, short id); StringHandle xGetString(short id); char *xGetStringAsChar(short id); long xGetStringAsLong(short id); +void xSetHandleSize(Handle h, Size s); -char *askfileordirpath(void); -short getpath(short vRefNum, Str255 fileName, char **ret, +short getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file); -pascal short open_dialog_hook(short theItem, DialogPtr theDialog); -pascal Boolean open_dialog_filter(DialogPtr theDialog, - EventRecord *theEvent, short *itemHit); -bool is_dir(char *path); -short stat(const char *path, struct stat *sb); -OSErr copy_file(Str255 source, Str255 dest, bool overwrite, - bool keep_source_open); +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); - -Handle SetResSize(Handle res, size_t size); short FontHeight(short font_id, short size); void DrawGrowIconOnly(WindowPtr win);