AmendHub

Download:

jcs

/

amend

/

amendments

/

31

Repo: Support file deletion, repo versioning and migration

I broke my own rule of always adding versions to things, so start
now and migrate all unversioned repos to version 2 which adds file
flags.
 
When a file is deleted, doing a diff of it will diff against a blank
file and then the file in the repo is marked as deleted. For now this
just draws the file with tildes in the file list, but a custom LDEF
will draw these as strucken through.
 
Also fix a few bugs along the way...

jcs made amendment 31 over 2 years ago
--- amend.π.r Thu Nov 11 11:18:12 2021 +++ amend.π.r Sun Nov 21 11:10:49 2021 @@ -21,11 +21,11 @@ data 'MENU' (130) { data 'MENU' (131) { $"0083 0000 0000 0000 0000 FFFF FFFF 0452" /* .É.............R */ - $"6570 6F0A 4164 6420 4669 6C65 2E2E 0000" /* epo.Add File.... */ - $"0000 1852 6576 6572 7420 5365 6C65 6374" /* ...Revert Select */ - $"6564 2046 696C 6573 2E2E 2E00 0000 000E" /* ed Files........ */ - $"4170 706C 7920 5061 7463 682E 2E2E 0000" /* Apply Patch..... */ - $"0000 00" /* ... */ + $"6570 6F0B 4164 6420 4669 6C65 2E2E 2E00" /* epo.Add File.... */ + $"0000 0018 5265 7665 7274 2053 656C 6563" /* ....Revert Selec */ + $"7465 6420 4669 6C65 732E 2E2E 0000 0000" /* ted Files....... */ + $"0E41 7070 6C79 2050 6174 6368 2E2E 2E00" /* .Apply Patch.... */ + $"0000 0000" /* .... */ }; data 'MENU' (132) { @@ -42,9 +42,12 @@ data 'ALRT' (129) { $"0078 006C 00DA 0196 0081 5555 300A" /* .x.l...ñ.ÅUU0. */ }; +data 'ALRT' (130) { + $"0078 006C 00DA 0196 0082 5555 300A" /* .x.l...ñ.ÇUU0. */ +}; + data 'DITL' (128) { - $"0001 0000 0000 0046 012C 005A 0166 0402" /* .......F.,.Z.f.. */ - $"4F4B 0000 0000 0014 0023 004A 0143 0802" /* OK.......#.J.C.. */ + $"0000 0000 0000 0014 001E 0032 013B 0802" /* ...........2.;.. */ $"5E30" /* ^0 */ }; @@ -54,6 +57,13 @@ data 'DITL' (129) { $"5E30" /* ^0 */ }; +data 'DITL' (130) { + $"0002 0000 0000 0046 00E6 005A 0120 0403" /* .......F...Z. .. */ + $"5965 7321 0000 0000 0046 00A0 005A 00DA" /* Yes!.....F.†.Z.. */ + $"0402 4E6F 0000 0000 000A 003C 0040 011E" /* ..No.......<.@.. */ + $"0802 5E30" /* ..^0 */ +}; + data 'WIND' (128) { $"002B 0060 012B 01A0 0003 0100 0000 0000" /* .+.`.+.†........ */ $"0000 0A41 626F 7574 2043 6172 6C56 280A" /* ...About CarlV(. */ @@ -103,11 +113,12 @@ data 'TMPL' (129, "AFIL") { $"7065 544E 414D 0763 7265 6174 6F72 544E" /* peTNAM.creatorTN */ $"414D 0D63 7265 6174 696F 6E20 6461 7465" /* AM¬creation date */ $"444C 4E47 116D 6F64 6966 6963 6174 696F" /* DLNG.modificatio */ - $"6E20 6461 7465 444C 4E47" /* n dateDLNG */ + $"6E20 6461 7465 444C 4E47 0566 6C61 6773" /* n dateDLNG.flags */ + $"4842 5954" /* HBYT */ }; -data 'DLOG' (128) { - $"0064 0083 00A0 017D 0001 0100 0100 0000" /* .d.É.†.}........ */ +data 'DLOG' (128, "WAIT_DLOG_ID") { + $"0066 0059 00A0 01A4 0001 0100 0100 0000" /* .f.Y.†.§........ */ $"0000 0080 001A 280A" /* ...Ä..(. */ }; --- amend.h Mon Oct 18 13:09:14 2021 +++ amend.h Wed Nov 17 17:43:23 2021 @@ -44,4 +44,13 @@ #define TABWIDTH_ID 130 +struct tmpl { + Handle h; + ResType type; + Str255 name; + short id; +}; +extern struct tmpl tmpls[10]; +extern short ntmpls; + extern MenuHandle file_menu, edit_menu, repo_menu, commit_menu; --- browser.c Mon Oct 18 13:09:06 2021 +++ browser.c Mon Nov 22 09:48:36 2021 @@ -126,6 +126,7 @@ browser_init(struct repo *repo) browser->win, true, true, false, true); if (!browser->file_list) err(1, "Can't create file list"); + LAddColumn(1, 0, browser->file_list); (*(browser->file_list))->selFlags = lNoNilHilite; /* diff button */ @@ -147,6 +148,7 @@ browser_init(struct repo *repo) COMMIT_LDEF_ID, browser->win, true, true, false, true); if (!browser->commit_list) err(1, "Can't create commit list"); + LAddColumn(1, 0, browser->commit_list); (*(browser->commit_list))->selFlags = lOnlyOne; /* diff text */ @@ -202,13 +204,13 @@ browser_close_committer(struct browser *browser) committer_close(browser->committer); browser->committer = NULL; } - browser->state = BROWSER_STATE_IDLE; SetPort(browser->win); } void browser_add_files(struct browser *browser) { + char filename[256]; Cell cell = { 0, 0 }; short i; @@ -226,9 +228,11 @@ browser_add_files(struct browser *browser) /* fill in files */ for (i = 0; i < browser->repo->nfiles; i++) { LAddRow(1, cell.v, browser->file_list); - LSetCell(&browser->repo->files[i]->filename, - strlen(browser->repo->files[i]->filename), cell, - browser->file_list); + if (browser->repo->files[i]->flags & REPO_FILE_DELETED) + sprintf(filename, "~%s~", browser->repo->files[i]->filename); + else + strcpy(filename, browser->repo->files[i]->filename); + LSetCell(&filename, strlen(filename), cell, browser->file_list); cell.v++; } @@ -285,7 +289,7 @@ browser_filter_commits(struct browser *browser) LDoDraw(false, browser->commit_list); LDelRow(0, 0, browser->commit_list); browser_show_commit(browser, NULL); - + /* fill in commits for selected files */ nselected_files = browser_selected_file_ids(browser, &selected_files); cell.v = 0; @@ -312,7 +316,7 @@ browser_filter_commits(struct browser *browser) browser->commit_list); cell.v++; } - + LDoDraw(true, browser->commit_list); InvalRect(&(*(browser->commit_list))->rView); --- browser.h Mon Oct 18 13:09:23 2021 +++ browser.h Wed Nov 17 15:46:59 2021 @@ -30,6 +30,7 @@ enum { BROWSER_STATE_OPEN_COMMITTER, BROWSER_STATE_COMMITTER_DO_DIFF, BROWSER_STATE_WAITING_FOR_COMMITTER, + BROWSER_STATE_REMOVE_FILE, BROWSER_STATE_REVERT_FILE, BROWSER_STATE_EXPORT_PATCH, BROWSER_STATE_APPLY_PATCH --- committer.c Mon Oct 18 16:47:01 2021 +++ committer.c Fri Nov 19 16:56:03 2021 @@ -461,7 +461,7 @@ committer_status(char *format, ...) if (committer_dialog == NULL) committer_dialog = GetNewDialog(WAIT_DLOG_ID, NULL, (WindowPtr)-1); - GetDItem(committer_dialog, 2, &ttype, &thandle, &trect); + GetDItem(committer_dialog, 1, &ttype, &thandle, &trect); SetIText(thandle, status); } --- commit_list.c Mon Oct 18 13:13:50 2021 +++ commit_list.c Sun Nov 21 10:53:10 2021 @@ -72,18 +72,18 @@ commit_list_draw_cell(ListHandle theList, Cell theCell textRect.bottom = cellRect->bottom + 1; EraseRect(&textRect); InsetRect(&textRect, 4, 4); - - HLock(commit->log); - for (len = 1; len <= commit->log_len; len++) { - if ((*(commit->log))[len - 1] == '\r') - break; - } - + height = FontHeight(applFont, 9); TextFace(bold); TextFont(applFont); TextSize(9); MoveTo(textRect.left, textRect.top + 6); + + HLock(commit->log); + for (len = 1; len <= commit->log_len; len++) { + if ((*(commit->log))[len - 1] == '\r') + break; + } DrawText(*(commit->log), 0, len); HUnlock(commit->log); --- main.c Mon Oct 18 09:22:19 2021 +++ main.c Sun Nov 21 10:49:12 2021 @@ -28,6 +28,9 @@ Handle commit_list_ldef_h; short quitting = 0; struct browser *cur_browser = NULL; +struct tmpl tmpls[10]; +short ntmpls; + void handle_menu(long menu_id); void cur_browser_close(void); void update_menu(void); @@ -68,13 +71,26 @@ main(void) update_menu(); DrawMenuBar(); + /* load tmpls before we open a new resource file */ + ntmpls = Count1Resources('TMPL'); + if (ntmpls > nitems(tmpls)) + err(1, "ntmpls %d > %d", ntmpls, nitems(tmpls)); + for (i = 0; i < ntmpls; i++) { + tmpls[i].h = GetIndResource('TMPL', i + 1); + if (tmpls[i].h == NULL) + err(1, "Failed fetching TMPL %d", i + 1); + GetResInfo(tmpls[i].h, &tmpls[i].id, &tmpls[i].type, + &tmpls[i].name); + DetachResource(tmpls[i].h); + } + /* dynamically patch list LDEF to point to our *_list_ldef funcs */ commit_list_ldef_h = GetResource('LDEF', COMMIT_LDEF_ID); if (!commit_list_ldef_h) err(1, "Can't find commit list LDEF %d", COMMIT_LDEF_ID); HLock(commit_list_ldef_h); ((tCodeStub *)*commit_list_ldef_h)->addr = &commit_list_ldef; - + /* see if we were started by double-clicking a .repo file */ CountAppFiles(&finder_action, &finder_count); --- repo.c Mon Oct 18 17:12:17 2021 +++ repo.c Sun Nov 21 16:49:03 2021 @@ -22,8 +22,8 @@ #include "diff.h" #include "repo.h" #include "util.h" - -struct repo *repo_init(SFReply reply); + +struct repo *repo_init(SFReply reply, short is_new); void repo_sort_files(struct repo *repo); void repo_sort_commits(struct repo *repo); short repo_get_file_attrs(struct repo *repo, Str255 filename, @@ -31,6 +31,7 @@ short repo_get_file_attrs(struct repo *repo, Str255 fi short repo_file_update(struct repo *repo, struct repo_file *file); unsigned short repo_diff_header(struct repo *repo, struct repo_commit *commit, char **ret); +short repo_migrate(struct repo *repo, short is_new); struct repo * repo_open(AppFile *file) @@ -50,7 +51,7 @@ repo_open(AppFile *file) return NULL; } - return repo_init(reply); + return repo_init(reply, 0); } struct repo * @@ -58,14 +59,8 @@ repo_create(void) { Point pt = { 75, 100 }; SFReply reply; - struct tmpl { - Handle h; - ResType type; - Str255 name; - short id; - } tmpls[10] = { NULL }; char *newpath = NULL; - short error, fh, i, ntmpls; + short error, fh, i; SFPutFile(pt, "\pCreate new repository:", NULL, NULL, &reply); if (!reply.good) @@ -74,43 +69,21 @@ repo_create(void) getpath(reply.vRefNum, reply.fName, &newpath, 1); CtoPstr(newpath); - /* copy TMPL from our own resource file */ - ntmpls = Count1Resources('TMPL'); - if (ntmpls > nitems(tmpls)) - err(1, "ntmpls %d > %d", ntmpls, nitems(tmpls)); - for (i = 0; i < ntmpls; i++) { - tmpls[i].h = GetIndResource('TMPL', i + 1); - if (tmpls[i].h == NULL) - err(1, "Failed fetching TMPL %d", i + 1); - - GetResInfo(tmpls[i].h, &tmpls[i].id, &tmpls[i].type, - &tmpls[i].name); - DetachResource(tmpls[i].h); - } - error = Create(reply.fName, reply.vRefNum, AMEND_CREATOR, REPO_TYPE); - if (error != 0) + if (error != 0 && error != dupFNErr) err(1, "Failed to create %s: %d", PtoCstr(reply.fName), error); CreateResFile(newpath); if (ResError() != 0) - err(1, "Failed to open %s: %d", PtoCstr(reply.fName), ResError()); + err(1, "Failed to create res file %s: %d", newpath, ResError()); - fh = OpenResFile(newpath); - if (fh == -1) - err(1, "Failed to open %s: %d", PtoCstr(reply.fName), ResError()); - - for (i = 0; i < ntmpls; i++) - AddResource(tmpls[i].h, tmpls[i].type, tmpls[i].id, tmpls[i].name); - - CloseResFile(fh); free(newpath); - return repo_init(reply); + return repo_init(reply, 1); } struct repo * -repo_init(SFReply reply) +repo_init(SFReply reply, short is_new) { Str255 buf; struct repo *repo; @@ -134,6 +107,11 @@ repo_init(SFReply reply) memcpy(repo->filename, reply.fName, reply.fName[0] + 1); PtoCstr(repo->filename); + + if (repo_migrate(repo, is_new) != 0) { + free(repo); + return NULL; + } /* fill in file info */ repo->nfiles = Count1Resources(REPO_FILE_RTYPE); @@ -143,7 +121,6 @@ repo_init(SFReply reply) resh = GetIndResource(REPO_FILE_RTYPE, i + 1); if (resh == NULL) err(1, "failed fetching file %d", i + 1); - HLock(resh); repo->files[i] = repo_parse_file(resh); if (repo->files[i]->id >= repo->next_file_id) repo->next_file_id = repo->files[i]->id + 1; @@ -176,6 +153,7 @@ repo_close(struct repo *repo) { struct repo_file *file; struct repo_commit *commit; + Handle h; short i; for (i = 0; i < repo->ncommits; i++) { @@ -212,10 +190,13 @@ repo_parse_file(Handle hfile) struct repo_file *file; short len, i; unsigned char *data; + short datapos, hlen; Str255 buf; HLock(hfile); + hlen = GetHandleSize(hfile); data = (unsigned char *)(*hfile); + datapos = 0; file = xmalloczero(sizeof(struct repo_file)); @@ -225,27 +206,35 @@ repo_parse_file(Handle hfile) len = data[0]; memcpy(file->filename, data + 1, len); file->filename[len] = '\0'; - data += (data[0] + 1); + datapos += (len + 1); /* type fourcc, creator fourcc */ - memcpy(&file->type, data, 4); - data += 4; - memcpy(&file->creator, data, 4); - data += 4; + memcpy(&file->type, data + datapos, 4); + datapos += 4; + memcpy(&file->creator, data + datapos, 4); + datapos += 4; /* creation date, long */ - file->ctime = ((unsigned long)data[0] << 24) | - ((unsigned long)data[1] << 16) | - ((unsigned long)data[2] << 8) | - ((unsigned long)data[3]); - data += 4; + file->ctime = ((unsigned long)data[datapos] << 24) | + ((unsigned long)data[datapos + 1] << 16) | + ((unsigned long)data[datapos + 2] << 8) | + ((unsigned long)data[datapos + 3]); + datapos += 4; /* modification date, long */ - file->mtime = ((unsigned long)data[0] << 24) | - ((unsigned long)data[1] << 16) | - ((unsigned long)data[2] << 8) | - ((unsigned long)data[3]); - data += 4; + file->mtime = ((unsigned long)data[datapos] << 24) | + ((unsigned long)data[datapos + 1] << 16) | + ((unsigned long)data[datapos + 2] << 8) | + ((unsigned long)data[datapos + 3]); + datapos += 4; + + /* flags, char */ + file->flags = (unsigned char)data[datapos]; + datapos += 1; + + if (datapos != hlen) + err(1, "repo_parse_file handle len %d, data position %d", hlen, + datapos); HUnlock(hfile); @@ -477,7 +466,7 @@ repo_add_file(struct repo *repo) repo->nfiles++; repo->files = xrealloc(repo->files, repo->nfiles * sizeof(Ptr)); file = repo->files[repo->nfiles - 1] = - xmalloc(sizeof(struct repo_file)); + xmalloczero(sizeof(struct repo_file)); file->id = repo->next_file_id; repo->next_file_id++; @@ -505,24 +494,33 @@ repo_file_update(struct repo *repo, struct repo_file * CtoPstr(filename); error = repo_get_file_attrs(repo, filename, &attrs); - if (error) { + if (error && error != fnfErr) { warn("Failed to get info for %s", file->filename); return -1; } - memcpy(&file->type, &attrs.type, 4); - memcpy(&file->creator, &attrs.creator, 4); - memcpy(&file->ctime, &attrs.ctime, 4); - memcpy(&file->mtime, &attrs.mtime, 4); - /* filename len, filename, type, creator, ctime, mtime */ - len = 1 + filename[0] + 4 + 4 + 4 + 4; + if (error == fnfErr) + file->flags |= REPO_FILE_DELETED; + else { + file->flags &= ~REPO_FILE_DELETED; + memcpy(&file->type, &attrs.type, 4); + memcpy(&file->creator, &attrs.creator, 4); + memcpy(&file->ctime, &attrs.ctime, 4); + memcpy(&file->mtime, &attrs.mtime, 4); + } + + /* filename len, filename, type, creator, ctime, mtime, flags */ + len = 1 + filename[0] + 4 + 4 + 4 + 4 + 1; + fileh = Get1Resource(REPO_FILE_RTYPE, file->id); if (fileh == NULL) { /* build a new AFIL resource */ fileh = xNewHandle(len); new = 1; - } + } else if (len != GetHandleSize(fileh)) + warn("repo_file_update len %d vs resource size %d", len, + GetHandleSize(fileh)); HLock(fileh); @@ -534,17 +532,23 @@ repo_file_update(struct repo *repo, struct repo_file * data += filename[0] + 1; /* file type, creator, and dates */ - memcpy(data, &attrs.type, 4); + memcpy(data, &file->type, 4); data += 4; - memcpy(data, &attrs.creator, 4); + memcpy(data, &file->creator, 4); data += 4; - memcpy(data, &attrs.ctime, 4); + memcpy(data, &file->ctime, 4); data += 4; - memcpy(data, &attrs.mtime, 4); + memcpy(data, &file->mtime, 4); data += 4; + memcpy(data, &file->flags, 1); + data += 1; + HUnlock(fileh); + if (new) AddResource(fileh, REPO_FILE_RTYPE, file->id, filename); + else + ChangedResource(fileh); WriteResource(fileh); ReleaseResource(fileh); @@ -560,10 +564,9 @@ repo_get_file_attrs(struct repo *repo, Str255 filename char *filepath = NULL; /* lookup file type and creator */ - if (GetFInfo(filename, repo->vrefnum, &fi) != 0) { - warn("Failed to GetFInfo %s", PtoCstr(filename)); - return -1; - } + error = GetFInfo(filename, repo->vrefnum, &fi); + if (error != 0) + return error; memcpy(&attrs->type, &fi.fdType, 4); memcpy(&attrs->creator, &fi.fdCreator, 4); @@ -669,7 +672,7 @@ repo_diff_file(struct repo *repo, struct repo_file *fi char *fromfilepath, *tofilepath; char label0[255], label1[255]; long len; - short error, ret, frefnum; + short error, ret, frefnum, tofile_empty = 0; /* write out old file */ sprintf((char *)fromfilename, "%s (%s tmp)", file->filename, @@ -692,8 +695,8 @@ repo_diff_file(struct repo *repo, struct repo_file *fi getpath(repo->vrefnum, fromfilename, &fromfilepath, true); - texth = Get1Resource(REPO_TEXT_RTYPE, file->id); /* if there's no existing TEXT resource, it's a new file */ + texth = Get1Resource(REPO_TEXT_RTYPE, file->id); if (texth != NULL) { len = GetHandleSize(texth); HLock(texth); @@ -711,9 +714,23 @@ repo_diff_file(struct repo *repo, struct repo_file *fi getpath(repo->vrefnum, tofilename, &tofilepath, true); error = repo_get_file_attrs(repo, tofilename, &attrs); - if (error) + if (error == fnfErr) { + /* file no longer exists, create empty temp file */ + sprintf((char *)tofilename, "%s (%s deleted)", file->filename, + PROGRAM_NAME); + CtoPstr(tofilename); + + error = Create(tofilename, repo->vrefnum, file->creator, + file->type); + if (error && error != dupFNErr) + err(1, "Failed to create file %s: %d", PtoCstr(tofilename), + error); + + getpath(repo->vrefnum, tofilename, &tofilepath, true); + tofile_empty = 1; + } else if (error) err(1, "Failed to get info for %s", PtoCstr(tofilename)); - + /* specify diff header labels to avoid printing tmp filename */ /* (TODO: use paths relative to repo) */ label[0] = (char *)&label0; @@ -734,9 +751,18 @@ repo_diff_file(struct repo *repo, struct repo_file *fi err(1, "Failed to delete temp file %s: %d", PtoCstr(fromfilename), error); - free(fromfilepath); - free(tofilepath); + if (tofile_empty) { + error = FSDelete(tofilename, repo->vrefnum); + if (error) + err(1, "Failed to delete temp file %s: %d", + PtoCstr(tofilename), error); + } + if (fromfilepath) + free(fromfilepath); + if (tofilepath) + free(tofilepath); + if (ret == D_SAME) return 0; @@ -800,14 +826,15 @@ repo_commit(struct repo *repo, short *files, short nfi Str255 tfilename; struct repo_file *file; Handle commith, texth, fileh; - short commit_len = 0, pos = 0, len; + FInfo finfo; + short commit_len, pos = 0, len; time_t date; char author[] = "jcs"; /* XXX */ long fsize; short i, commit_id, id, error, frefnum; /* date (long) */ - commit_len += sizeof(long); + commit_len = sizeof(long); /* author (pstr) */ commit_len += 1 + strlen(author); @@ -858,19 +885,20 @@ repo_commit(struct repo *repo, short *files, short nfi HUnlock(log); if (pos != commit_len) - err(1, "Accumulated len %d != expected %d", pos, commit_len); + err(1, "repo_commit: accumulated len %d != expected %d", pos, + commit_len); commit_id = repo->next_commit_id; /* store diff */ - AddResource(diff, REPO_DIFF_RTYPE, commit_id, NULL); + AddResource(diff, REPO_DIFF_RTYPE, commit_id, "\p"); if (ResError()) err(1, "Failed storing diff in repo file (%d)", ResError()); WriteResource(diff); ReleaseResource(diff); /* store commit */ - AddResource(commith, REPO_COMMIT_RTYPE, commit_id, NULL); + AddResource(commith, REPO_COMMIT_RTYPE, commit_id, "\p"); if (ResError()) err(1, "Failed storing commit in repo file (%d)", ResError()); WriteResource(commith); @@ -880,7 +908,17 @@ repo_commit(struct repo *repo, short *files, short nfi file = repo_file_with_id(repo, files[i]); if (file == NULL) err(1, "Bogus file %d in commit", files[i]); - + + len = strlen(file->filename); + memcpy(tfilename, file->filename, len); + tfilename[len] = '\0'; + CtoPstr(tfilename); + + /* update file contents if file wasn't deleted */ + error = GetFInfo(tfilename, repo->vrefnum, &finfo); + if (error && error != fnfErr) + err(1, "Error getting file info for %s", PtoCstr(tfilename)); + SetResLoad(false); texth = Get1Resource(REPO_TEXT_RTYPE, file->id); if (texth != NULL) { @@ -889,34 +927,31 @@ repo_commit(struct repo *repo, short *files, short nfi } SetResLoad(true); - len = strlen(file->filename); - memcpy(tfilename, file->filename, len); - tfilename[len] = '\0'; - CtoPstr(tfilename); + if (error != fnfErr) { + error = FSOpen(tfilename, repo->vrefnum, &frefnum); + if (error) + err(1, "Failed to open file %s: %d", PtoCstr(tfilename), + error); + + error = GetEOF(frefnum, &fsize); + if (error) + err(1, "Failed to get size of file %s: %d", + PtoCstr(tfilename), error); + + texth = xNewHandle(fsize); + HLock(texth); + error = FSRead(frefnum, &fsize, *texth); + if (error) + err(1, "Failed to read %ul of file %s: %d", fsize, + PtoCstr(tfilename), error); + + FSClose(frefnum); + + AddResource(texth, REPO_TEXT_RTYPE, files[i], tfilename); + WriteResource(texth); + ReleaseResource(texth); + } - error = FSOpen(tfilename, repo->vrefnum, &frefnum); - if (error) - err(1, "Failed to open file %s: %d", PtoCstr(tfilename), - error); - - error = GetEOF(frefnum, &fsize); - if (error) - err(1, "Failed to get size of file %s: %d", PtoCstr(tfilename), - error); - - texth = xNewHandle(fsize); - HLock(texth); - error = FSRead(frefnum, &fsize, *texth); - if (error) - err(1, "Failed to read %ul of file %s: %d", fsize, - PtoCstr(tfilename), error); - - FSClose(frefnum); - - AddResource(texth, REPO_TEXT_RTYPE, files[i], tfilename); - WriteResource(texth); - ReleaseResource(texth); - repo_file_update(repo, file); } @@ -930,4 +965,81 @@ repo_commit(struct repo *repo, short *files, short nfi ReleaseResource(commith); repo_sort_commits(repo); + + UpdateResFile(repo->fh); +} + +short +repo_migrate(struct repo *repo, short is_new) +{ + Handle verh, resh; + short ver, add = 0, i, nfiles, size; + + if (is_new) + ver = REPO_CUR_VERS; + else { + verh = Get1Resource(REPO_VERS_RTYPE, 1); + if (verh == NULL) + ver = 1; + else { + ver = (unsigned char)((*verh)[0]); + ReleaseResource(verh); + } + + if (ver == REPO_CUR_VERS) + return 0; + + if (ask("Migrate this repo from version %d to %d to open it?", + ver, REPO_CUR_VERS) != ASK_YES) + return -1; + } + + /* per-version migrations */ + if (ver == 1) { + /* version 1 had no file flags */ + nfiles = Count1Resources(REPO_FILE_RTYPE); + for (i = 1; i <= nfiles; i++) { + resh = GetIndResource(REPO_FILE_RTYPE, i); + if (resh == NULL) + err(1, "failed fetching file %d", i); + size = GetHandleSize(resh); + ReallocHandle(resh, size + 1); + if (MemError()) + err(1, "Out of memory allocating new handle of size %ld", + size + 1); + HLock(resh); + (*resh)[size] = 0; + HUnlock(resh); + ChangedResource(resh); + WriteResource(resh); + ReleaseResource(resh); + } + ver = 2; + } + + /* store new version */ + verh = Get1Resource(REPO_VERS_RTYPE, 1); + if (verh == NULL) { + verh = NewHandle(1); + add = 1; + } + (*verh)[0] = ver; + if (add) + AddResource(verh, REPO_VERS_RTYPE, 1, "\p"); + else + ChangedResource(verh); + + /* update templates */ + for (i = 0; i < ntmpls; i++) { + resh = Get1Resource(tmpls[i].type, tmpls[i].id); + if (resh) + RmveResource(resh); + AddResource(tmpls[i].h, tmpls[i].type, tmpls[i].id, tmpls[i].name); + WriteResource(tmpls[i].h); + DetachResource(tmpls[i].h); + } + + UpdateResFile(repo->fh); + + return 0; } --- repo.h Mon Oct 18 15:08:25 2021 +++ repo.h Thu Nov 18 11:01:03 2021 @@ -27,11 +27,14 @@ #define REPO_COMMIT_RTYPE 'CMMT' #define REPO_DIFF_RTYPE 'DIFF' #define REPO_TEXT_RTYPE 'TEXT' +#define REPO_VERS_RTYPE 'RVER' #define DIFF_FILE_TYPE 'TEXT' #define REPO_DIFF_TOO_BIG "\r[ Diff too large to view, %lu bytes not shown ]" +#define REPO_CUR_VERS 2 + struct repo_file { short id; char filename[256]; @@ -39,6 +42,8 @@ struct repo_file { OSType creator; unsigned long ctime; unsigned long mtime; + unsigned char flags; +#define REPO_FILE_DELETED (1 << 0) }; struct repo_file_attrs { @@ -82,6 +87,7 @@ struct repo_file *repo_file_with_id(struct repo *repo, void repo_show_diff_text(struct repo *repo, struct repo_commit *commit, TEHandle te); struct repo_file *repo_add_file(struct repo *repo); +void repo_file_mark_for_deletion(struct repo *repo, struct repo_file *file); short repo_diff_file(struct repo *repo, struct repo_file *file); short repo_checkout_file(struct repo *repo, struct repo_file *file, short vrefnum, Str255 filename); --- util.c Mon Oct 18 13:14:58 2021 +++ util.c Mon Nov 22 09:48:09 2021 @@ -21,7 +21,10 @@ #include "util.h" +/* ALRT resources */ #define ERROR_ALERT_ID 129 +#define ASK_ALERT_ID 130 + #define ERROR_STRING_SIZE 1024 enum { @@ -226,6 +229,32 @@ note(const char *format, ...) va_end(ap); } +short +ask(const char *format, ...) +{ + size_t len; + short ret; + WindowPtr win; + va_list ap; + + GetPort(&win); + + HLock(err_str); + va_start(ap, format); + len = vsprintf(*err_str, format, ap); + va_end(ap); + if (len >= ERROR_STRING_SIZE) + err(1, "ask string overflow!"); + + ParamText(CtoPstr(*err_str), "\p", "\p", "\p"); + ret = StopAlert(ASK_ALERT_ID, nil); + HUnlock(err_str); + + SetPort(win); + return ret; +} + + /* * Error checking wrappers for Mac toolkit functions */ @@ -367,9 +396,9 @@ getpath(short vRefNum, Str255 fileName, char **ret, bo warn("Unknown filesystem type 0x%x", wvol.ioVSigWord); return 1; } - - wcinfo.ioNamePtr = (StringPtr)&name; + wcinfo.ioVRefNum = vRefNum; + wcinfo.ioNamePtr = (StringPtr)&name; wcinfo.ioFDirIndex = -1; wcinfo.ioDrParID = wdir.ioWDDirID; wcinfo.ioDrDirID = wdir.ioWDDirID; --- util.h Mon Oct 18 13:09:59 2021 +++ util.h Fri Nov 19 13:34:19 2021 @@ -70,6 +70,9 @@ 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 Handle xNewHandle(unsigned long size); Handle xGetResource(ResType type, short id); @@ -86,6 +89,8 @@ pascal Boolean open_dialog_filter(DialogPtr theDialog, bool is_dir(char *path); short stat(const char *path, struct stat *sb); 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);