AmendHub

Download (as of this amendment):

jcs

/

amend

/

amendments

/

181

*: Add author e-mail to settings and amendments

Migrate settings to a new version and prompt the user to add their e-mail
address, also rename "Author" to "Author Name" to make it more of a name
field than username like it was before.
 
When opening a v4 repo, migrate it to add an author e-mail to each record,
using the user's new name and e-mail if the username matches the first in
the repo (which is assumed to be the user's own username).

jcs made amendment 181 about 23 hours ago
--- amend.π.rsrc Mon Sep 28 21:19:01 2026 +++ amend.π.rsrc Thu Oct 1 10:16:35 2026 @@ -26,7 +26,7 @@ data 'MENU' (131) { $"6570 6F0B 4E65 7720 5265 706F 2E2E 2E00" /* epo.New Repo.... */ $"4E00 000C 4F70 656E 2052 6570 6F2E 2E2E" /* N...Open Repo... */ $"004F 0000 012D 0000 0000 0E41 6464 2074" /* .O...-.....Add t */ - $"6F20 5265 706F 2E2E 2E00 0000 000D 4170" /* o Repo.......¬Ap */ + $"6F20 5265 706F 2E2E 2E00 4100 000D 4170" /* o Repo....A..¬Ap */ $"706C 7920 4469 6666 2E2E 2E00 0000 0000" /* ply Diff........ */ }; @@ -54,19 +54,23 @@ data 'DITL' (130, "ASK") { }; data 'DITL' (131, "SETTINGS") { - $"0008 0000 0000 006E 0050 0082 008A 0404" /* .......n.P.Ç.ä.. */ - $"5361 7665 0000 0000 006E 0096 0082 00D0" /* Save.....n.ñ.Ç.– */ + $"000A 0000 0000 008C 006E 00A0 00A8 0404" /* .......å.n.†.®.. */ + $"5361 7665 0000 0000 008C 00B4 00A0 00EE" /* Save.....å.¥.†.. */ $"0406 4361 6E63 656C 0000 0000 000F 00AA" /* ..Cancel.......™ */ - $"001F 0113 1007 756E 6B6E 6F77 6E61 0000" /* ......unknowna.. */ - $"0000 002D 00AA 003D 00C5 1001 346F 0000" /* ...-.™.=.≈..4o.. */ - $"0000 004B 00AA 005B 00D2 1003 3130 3068" /* ...K.™.[.“..100h */ - $"0000 0000 000F 000A 0020 003D 8807 4175" /* ......... .=à.Au */ - $"7468 6F72 3A0A 0000 0000 002D 000A 003D" /* thor:......-...= */ - $"0051 880A 5461 6220 7769 6474 683A 0000" /* .Qà.Tab width:.. */ - $"0000 004B 000A 005C 009E 8813 416D 656E" /* ...K...\.ûà.Amen */ - $"646D 656E 7473 2074 6F20 6C6F 6164 3A09" /* dments to load:∆ */ - $"0000 0000 004B 00DC 005B 0115 8809 2830" /* .....K...[..à∆(0 */ - $"203D 2041 6C6C 2900" /* = All). */ + $"001F 0151 1007 756E 6B6E 6F77 6E61 0000" /* ...Q..unknowna.. */ + $"0000 002D 00AA 003D 0151 1013 756E 6B6E" /* ...-.™.=.Q..unkn */ + $"6F77 6E40 6578 616D 706C 652E 636F 6D68" /* own@example.comh */ + $"0000 0000 004B 00AA 005B 00C5 1001 3475" /* .....K.™.[.≈..4u */ + $"0000 0000 0069 00AA 0079 00D2 1003 3130" /* .....i.™.y.“..10 */ + $"300A 0000 0000 000F 000A 0020 0085 880C" /* 0.......... .Öà. */ + $"4175 7468 6F72 204E 616D 653A 0000 0000" /* Author Name:.... */ + $"002D 000A 003E 008A 880E 4175 7468 6F72" /* .-...>.äà.Author */ + $"2045 2D4D 6169 6C3A 0000 0000 004B 000A" /* E-Mail:.....K.. */ + $"005B 0051 880A 5461 6220 7769 6474 683A" /* .[.Qà.Tab width: */ + $"0000 0000 0069 000A 007A 009E 8813 416D" /* .....i...z.ûà.Am */ + $"656E 646D 656E 7473 2074 6F20 6C6F 6164" /* endments to load */ + $"3A34 0000 0000 0069 00DC 0079 0115 8809" /* :4.....i...y..à∆ */ + $"2830 203D 2041 6C6C 293A" /* (0 = All): */ }; data 'DITL' (132, "ALERT") { @@ -97,7 +101,7 @@ data 'LDEF' (129, "file list", purgeable) { }; data 'DLOG' (130, "SETTINGS_DLOG_ID") { - $"005E 0070 00EA 0194 0010 0100 0000 0000" /* .^.p...î........ */ + $"005C 0052 0106 01B2 0010 0100 0000 0000" /* .\.R...≤........ */ $"0000 0083 1141 6D65 6E64 2050 7265 6665" /* ...É.Amend Prefe */ $"7265 6E63 6573 280A" /* rences(. */ }; --- amend.h Mon Sep 28 21:26:32 2026 +++ amend.h Thu Oct 1 10:16:59 2026 @@ -55,9 +55,10 @@ #define SETTINGS_DLOG_ID 130 #define SETTINGS_SAVE_ID 1 #define SETTINGS_CANCEL_ID 2 -#define SETTINGS_AUTHOR_ID 3 -#define SETTINGS_TABSTOP_ID 4 -#define SETTINGS_MAX_AMENDMENTS_ID 5 +#define SETTINGS_AUTHOR_NAME_ID 3 +#define SETTINGS_AUTHOR_EMAIL_ID 4 +#define SETTINGS_TABSTOP_ID 5 +#define SETTINGS_MAX_AMENDMENTS_ID 6 #define DIFF_FONT monaco #define DIFF_FONT_SIZE 9 --- browser.c Mon Sep 28 21:32:03 2026 +++ browser.c Wed Sep 30 16:56:33 2026 @@ -211,9 +211,10 @@ browser_init(struct repo *repo) bounds.top--; browser->diff_scroller = NewControl(browser->win, &bounds, "\p", true, 1, 1, 1, scrollBarProc, 0L); - + browser_update_menu(browser); browser_add_files(browser); + TVSetScrollbar(browser->diff_tv, browser->diff_scroller); TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller); focusable = xmalloczero(sizeof(struct focusable)); --- committer.c Mon Sep 28 21:26:58 2026 +++ committer.c Thu Oct 1 12:29:05 2026 @@ -137,7 +137,8 @@ committer_init(struct browser *browser) bounds.top--; committer->diff_scroller = NewControl(committer->win, &bounds, "\p", true, 1, 1, 1, scrollBarProc, 0L); - + TVSetScrollbar(committer->diff_tv, committer->diff_scroller); + /* commit button */ bounds.left = committer->win->portRect.right - PADDING - 100; bounds.right = bounds.left + 100; @@ -535,7 +536,7 @@ committer_commit(struct committer *committer) repo_amend(committer->browser->repo, committer->diffed_files, committer->ndiffed_files, committer->diff_adds, - committer->diff_subs, settings.author, + committer->diff_subs, settings.author, settings.author_email, (*(committer->log_te))->hText, loglen, diff, committer->diff_len); } --- commit_list.c Mon Sep 28 14:25:02 2026 +++ commit_list.c Thu Oct 1 11:50:35 2026 @@ -105,7 +105,7 @@ amendment_list_draw_cell(ListHandle theList, Cell theC snprintf(tmp, sizeof(tmp), "%d (+), %d (-)", amendment->adds, amendment->subs); - MoveTo(textRect.left + 220, textRect.top + height + 6); + MoveTo(textRect.left + 260, textRect.top + height + 6); DrawText(tmp, 0, strlen(tmp)); if (selected) --- editor.c Mon Sep 28 21:27:23 2026 +++ editor.c Thu Oct 1 12:23:49 2026 @@ -80,12 +80,12 @@ editor_init(struct browser *browser, struct repo_amend panic("Can't create editor window"); SetPort(editor->win); - /* author */ + /* author name */ bounds.top = PADDING; - bounds.left = 55; + bounds.left = 90; fh = FontHeight(LABEL_FONT, LABEL_FONT_SIZE); - bounds.bottom = bounds.top + fh + 4; - bounds.right = 140; + bounds.bottom = bounds.top + fh + 2; + bounds.right = 240; te_bounds = bounds; InsetRect(&te_bounds, 2, 2); TextFont(LABEL_FONT); @@ -96,10 +96,23 @@ editor_init(struct browser *browser, struct repo_amend TEInsert(amendment->author, strlen(amendment->author), editor->author_te); + /* author e-mail */ + bounds.top = bounds.bottom + PADDING; + bounds.bottom = bounds.top + fh + 2; + bounds.right = 240; + te_bounds = bounds; + InsetRect(&te_bounds, 2, 2); + TextFont(LABEL_FONT); + TextSize(LABEL_FONT_SIZE); + editor->author_email_te = TENew(&te_bounds, &bounds); + TEAutoView(true, editor->author_email_te); + TEInsert(amendment->author_email, strlen(amendment->author_email), + editor->author_email_te); + /* date */ bounds.top = bounds.bottom + PADDING; bounds.bottom = bounds.top + fh + 2; - bounds.right = 200; + bounds.right = 240; te_bounds = bounds; InsetRect(&te_bounds, 2, 2); TextFont(LABEL_FONT); @@ -114,7 +127,8 @@ editor_init(struct browser *browser, struct repo_amend TEInsert(date, strlen(date), editor->date_te); /* log message */ - bounds.top = bounds.bottom + PADDING; + bounds.left = PADDING; + bounds.top = bounds.bottom + PADDING + PADDING + fh; bounds.bottom = editor->win->portRect.bottom - 20 - (PADDING * 2); bounds.right = editor->win->portRect.right - SCROLLBAR_WIDTH - PADDING; @@ -171,6 +185,7 @@ editor_close(struct focusable *focusable) struct editor *editor = (struct editor *)focusable->cookie; TEDispose(editor->author_te); + TEDispose(editor->author_email_te); TEDispose(editor->date_te); TEDispose(editor->log_te); DisposeWindow(editor->win); @@ -206,10 +221,19 @@ editor_update(struct focusable *focusable, EventRecord MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); TextFont(LABEL_FONT); TextSize(LABEL_FONT_SIZE); - DrawText("Author:", 0, 7); + DrawText("Author Name:", 0, 12); InsetRect(&r, -1, -1); FrameRect(&r); TEUpdate(&r, editor->author_te); + + r = (*(editor->author_email_te))->viewRect; + MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); + TextFont(LABEL_FONT); + TextSize(LABEL_FONT_SIZE); + DrawText("Author E-Mail:", 0, 14); + InsetRect(&r, -1, -1); + FrameRect(&r); + TEUpdate(&r, editor->author_email_te); r = (*(editor->date_te))->viewRect; MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); @@ -221,7 +245,8 @@ editor_update(struct focusable *focusable, EventRecord TEUpdate(&r, editor->date_te); r = (*(editor->log_te))->viewRect; - MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); + MoveTo(PADDING, r.top - + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); TextFont(LABEL_FONT); TextSize(LABEL_FONT_SIZE); DrawText("Log:", 0, 4); @@ -241,9 +266,7 @@ editor_suspend(struct focusable *focusable) { struct editor *editor = (struct editor *)focusable->cookie; - TEDeactivate(editor->author_te); - TEDeactivate(editor->date_te); - TEDeactivate(editor->log_te); + TEDeactivate(editor->last_te); } void @@ -251,9 +274,7 @@ editor_resume(struct focusable *focusable) { struct editor *editor = (struct editor *)focusable->cookie; - TEActivate(editor->author_te); - TEActivate(editor->date_te); - TEActivate(editor->log_te); + TEActivate(editor->last_te); } void @@ -264,8 +285,7 @@ editor_key_down(struct focusable *focusable, EventReco k = (event->message & charCodeMask); - if (k == '\r' && (editor->last_te == editor->author_te || - editor->last_te == editor->date_te)) + if (k == '\r' && editor->last_te != editor->log_te) return; TEKey(k, editor->last_te); @@ -291,6 +311,7 @@ editor_mouse_down(struct focusable *focusable, EventRe if (PtInRect(p, &r)) { if (editor->last_te != editor->author_te) { editor->last_te = editor->author_te; + TEDeactivate(editor->author_email_te); TEDeactivate(editor->date_te); TEDeactivate(editor->log_te); TEActivate(editor->author_te); @@ -300,6 +321,20 @@ editor_mouse_down(struct focusable *focusable, EventRe return; } + r = (*(editor->author_email_te))->viewRect; + if (PtInRect(p, &r)) { + if (editor->last_te != editor->author_email_te) { + editor->last_te = editor->author_email_te; + TEDeactivate(editor->author_te); + TEDeactivate(editor->date_te); + TEDeactivate(editor->log_te); + TEActivate(editor->author_email_te); + } + TEClick(p, ((event->modifiers & shiftKey) != 0), + editor->author_email_te); + editor_update_menu(editor); + return; + } r = (*(editor->date_te))->viewRect; if (PtInRect(p, &r)) { @@ -481,6 +516,13 @@ editor_save(struct editor *editor) memcpy(editor->amendment->author, *(*(editor->author_te))->hText, len); editor->amendment->author[len] = '\0'; + + len = sizeof(editor->amendment->author_email) - 1; + if ((*(editor->author_email_te))->teLength < len) + len = (*(editor->author_email_te))->teLength; + memcpy(editor->amendment->author_email, + *(*(editor->author_email_te))->hText, len); + editor->amendment->author_email[len] = '\0'; editor->amendment->log_len = (*(editor->log_te))->teLength; if (editor->amendment->log) --- editor.h Sat Mar 4 22:39:12 2023 +++ editor.h Thu Oct 1 11:59:46 2026 @@ -29,6 +29,7 @@ struct editor { ControlHandle log_scroller; TEHandle date_te; TEHandle author_te; + TEHandle author_email_te; ControlHandle save_button; TEHandle last_te; struct repo_amendment *amendment; --- repo.c Mon Sep 28 21:07:13 2026 +++ repo.c Thu Oct 1 13:13:50 2026 @@ -192,7 +192,6 @@ repo_load_amendments(struct repo *repo, bool fill_in) if (!fill_in) { repo->amendments = xcalloc(repo->namendments, sizeof(Ptr)); - repo->unloaded_amendments = (settings.max_amendments > 0 && repo->namendments > settings.max_amendments); } @@ -322,7 +321,7 @@ repo_parse_amendment(unsigned long id, unsigned char * struct repo_amendment *amendment; unsigned short len, i; - amendment = xmalloc(sizeof(struct repo_amendment)); + amendment = xmalloczero(sizeof(struct repo_amendment)); amendment->id = id; /* date */ @@ -373,6 +372,16 @@ repo_parse_amendment(unsigned long id, unsigned char * HUnlock(amendment->log); data += len; + if (len < size) { + /* author email, pstr */ + len = data[0]; + if (len > sizeof(amendment->author_email) - 1) + len = sizeof(amendment->author_email) - 1; + memcpy(amendment->author_email, data + 1, len); + amendment->author_email[len] = '\0'; + data += (data[0] + 1); + } + /* TODO: use datapos and check against size like repo_parse_file */ return amendment; @@ -394,8 +403,9 @@ unsigned short repo_diff_header(struct repo *repo, struct repo_amendment *amendment, char **ret) { + char uname[150]; struct tm *ttm = NULL; - unsigned short header_len, ncrs = 0; + unsigned short header_len, ncrs = 0, size; short i; HLock(amendment->log); @@ -405,19 +415,26 @@ repo_diff_header(struct repo *repo, struct repo_amendm ncrs++; } - *ret = xmalloc(128 + amendment->log_len + (ncrs * 2) + 2); + if (amendment->author_email && amendment->author_email[0] != '\0') + snprintf(uname, sizeof(uname), "%s <%s>", amendment->author, + amendment->author_email); + else + strlcpy(uname, amendment->author, sizeof(uname)); + + size = 256; + *ret = xmalloc(size + amendment->log_len + (ncrs * 2) + 2); ttm = localtime(&amendment->date); - header_len = snprintf(*ret, 128, + header_len = snprintf(*ret, size, "Amendment: %d\r" "Author: %s\r" "Date: %04d-%02d-%02d %02d:%02d:%02d\r" "\r ", amendment->id, - amendment->author, + uname, ttm->tm_year + 1900, ttm->tm_mon + 1, ttm->tm_mday, ttm->tm_hour, ttm->tm_min, ttm->tm_sec); - if (header_len > 127) - header_len = 127; + if (header_len > size - 1) + header_len = size - 1; /* copy log, indenting each line */ for (i = 0; i < amendment->log_len; i++) { @@ -1041,8 +1058,8 @@ repo_export_amendment(struct repo *repo, struct repo_a void repo_amend(struct repo *repo, struct diffed_file *diffed_files, - short nfiles, short adds, short subs, char *author, Handle log, - short loglen, Handle diff, size_t difflen) + short nfiles, short adds, short subs, char *author, char *author_email, + Handle log, short loglen, Handle diff, size_t difflen) { Str255 tfilename; struct repo_amendment *amendment; @@ -1072,6 +1089,8 @@ repo_amend(struct repo *repo, struct diffed_file *diff nfiles); strlcpy(amendment->author, author, sizeof(amendment->author)); + strlcpy(amendment->author_email, author_email, + sizeof(amendment->author_email)); amendment->adds = adds; amendment->subs = subs; @@ -1196,6 +1215,9 @@ repo_marshall_amendment(struct repo_amendment *amendme /* log (wstr) */ len += sizeof(short) + amendment->log_len; + /* author_email (pstr) */ + len += 1 + strlen(amendment->author_email); + *retdata = xmalloc(len); data = *retdata; @@ -1228,6 +1250,11 @@ repo_marshall_amendment(struct repo_amendment *amendme memcpy(data + pos, *(amendment->log), amendment->log_len); pos += amendment->log_len; HUnlock(amendment->log); + + clen = strlen(amendment->author_email); + data[pos++] = clen; + for (i = 0; i < clen; i++) + data[pos++] = amendment->author_email[i]; if (pos != len) panic("repo_marshall_amendment: accumulated len %d != expected %d", @@ -1332,6 +1359,60 @@ repo_migrate(struct repo *repo, bool is_new) } xfree(&diff); + } + + xfree(&ids); + break; + } + case 4: { + /* + * 4->5 extended author and added author_email + * + * assume every past amendment with the author of the first is + * what the user has just saved in settings, and update to those + */ + size_t namendments, i, size, retsize; + struct repo_amendment *amendment; + char first_author[64]; + char *data, *retdata; + unsigned long *ids; + bool bad; + + namendments = bile_sorted_ids_by_type(repo->bile, + REPO_AMENDMENT_RTYPE, &ids); + + for (i = 0; i < namendments; i++) { + progress("Updating amendment %lu/%lu...", ids[i], + namendments); + + size = bile_read_alloc(repo->bile, REPO_AMENDMENT_RTYPE, + ids[i], &data); + if (size == 0) + panic("failed fetching amendment %ld", ids[i]); + + amendment = repo_parse_amendment(ids[i], + (unsigned char *)data, size); + xfree(&data); + + if (i == 0) + strlcpy(first_author, amendment->author, + sizeof(first_author)); + + if (strncmp(first_author, amendment->author, + sizeof(first_author)) == 0) { + strlcpy(amendment->author, settings.author, + sizeof(amendment->author)); + strlcpy(amendment->author_email, settings.author_email, + sizeof(amendment->author_email)); + } else + amendment->author_email[0] = '\0'; + + repo_marshall_amendment(amendment, &retdata, &retsize); + bile_write(repo->bile, REPO_AMENDMENT_RTYPE, + ids[i], retdata, retsize); + + xfree(&amendment); + xfree(&retdata); } xfree(&ids); --- repo.h Mon Sep 28 17:40:00 2026 +++ repo.h Thu Oct 1 11:13:08 2026 @@ -33,7 +33,7 @@ #define REPO_DIFF_TOO_BIG "\r[ Diff too large to view, %lu bytes not shown ]" -#define REPO_CUR_VERS 4 +#define REPO_CUR_VERS 5 struct repo_file { short id; @@ -66,7 +66,8 @@ struct diffed_file { struct repo_amendment { short id; time_t date; - char author[32]; + char author[64]; + char author_email[64]; short nfiles; short *file_ids; short adds; @@ -106,8 +107,8 @@ short repo_checkout_file(struct repo *repo, struct rep void repo_export_amendment(struct repo *repo, struct repo_amendment *amendment, short vrefnum, Str255 filename); void repo_amend(struct repo *repo, struct diffed_file *diffed_files, - short nfiles, short adds, short subs, char *author, Handle log, - short loglen, Handle diff, size_t difflen); + short nfiles, short adds, short subs, char *author, char *author_email, + Handle log, short loglen, Handle diff, size_t difflen); void repo_marshall_amendment(struct repo_amendment *amendment, char **retdata, unsigned long *retlen); void repo_backup(struct repo *repo); --- settings.c Mon Sep 28 14:27:13 2026 +++ settings.c Thu Oct 1 11:46:41 2026 @@ -53,10 +53,14 @@ struct setting { unsigned long size; char password_storage[256]; } settings_defs[] = { - { "Author", SETTING_STRING, 0, + { "Author Name", SETTING_STRING, 0, 1, 0, "unknown", - SETTINGS_AUTHOR_ID, offsetof(struct settings, author), + SETTINGS_AUTHOR_NAME_ID, offsetof(struct settings, author), member_size(struct settings, author) }, + { "Author E-Mail", SETTING_STRING, 0, + 1, 0, "unknown@example.com", + SETTINGS_AUTHOR_EMAIL_ID, offsetof(struct settings, author_email), + member_size(struct settings, author_email) }, { "Tab Stop Width", SETTING_USHORT, 0, 1, 32, "4", SETTINGS_TABSTOP_ID, offsetof(struct settings, tabstop), @@ -80,7 +84,7 @@ settings_load(void) short error, vrefnum, prefrefnum; long len, rlen, dirid; Handle h; - bool use_defaults = true; + bool use_defaults = true, show_edit = false; GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid); @@ -97,14 +101,31 @@ settings_load(void) return; } - rlen = len = sizeof(struct settings); + rlen = len = sizeof(tsettings); FSRead(pb.ioParam.ioRefNum, &rlen, &tsettings); while (tsettings.version < SETTINGS_VERSION) { switch (tsettings.version) { - case 1: - /* 1->2 added amendhub token, leave blank */ + case 1: { + /* extended author, added author_email */ + struct settings_v1 s1 = { 0 }; + + /* re-read prefs file into settings_v1 struct */ + rlen = len = sizeof(s1); + SetFPos(pb.ioParam.ioRefNum, fsFromStart, 0); + FSRead(pb.ioParam.ioRefNum, &rlen, &s1); + + strlcpy(tsettings.author, s1.author, sizeof(tsettings.author)); + tsettings.tabstop = s1.tabstop; + tsettings.max_amendments = s1.max_amendments; + + /* save but also let the user edit the new e-mail field */ + settings_save(&tsettings); + show_edit = true; break; + } + case 2: + break; default: FSClose(pb.ioParam.ioRefNum); @@ -122,6 +143,9 @@ settings_load(void) FSClose(pb.ioParam.ioRefNum); settings = tsettings; + + if (show_edit) + settings_edit(false); } void --- settings.h Thu Oct 24 21:38:41 2024 +++ settings.h Thu Oct 1 10:21:19 2026 @@ -21,10 +21,19 @@ #define SETTINGS_FILENAME "\pAmend Preferences" -struct settings { +struct settings_v1 { short version; -#define SETTINGS_VERSION 1 char author[32]; + short tabstop; + short max_amendments; +}; + +struct settings { + /* version must always be first */ + short version; +#define SETTINGS_VERSION 2 + char author[64]; + char author_email[64]; short tabstop; short max_amendments; };