Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.13 To 0.14
2023-04-17 15:29 | bump version number: 0.15 (check-in: c6cd3983d8 user: mark tags: trunk) | |
2023-04-17 14:10 | CHANGES for 0.14 (check-in: 571ae0eed4 user: mark tags: trunk, 0.14) | |
2023-04-17 13:40 | latest upstream libfossil: 9a8269d737 (check-in: b324288c18 user: mark tags: trunk) | |
2022-11-26 06:01 | bump version number: 0.14 (check-in: be94040192 user: mark tags: trunk) | |
2022-11-26 06:00 | CHANGES for 0.13 (check-in: 0b85faeb8a user: mark tags: trunk, 0.13) | |
2022-11-25 05:15 | latest upstream libfossil: 46008704a620 (check-in: f745a2bcb8 user: mark tags: trunk) | |
Changes to CHANGES.md.
1 2 3 4 5 6 7 | **fnc 0.13** 2022-11-26 [[history][0.13a] / [diff][0.13b]] - improve reporting of invalid diff command arguments (reported by Dan Shearer) - accept libfossil global `-V|--verbose` option for all commands - plug memleak when using the `p` keymap in blame view - fix `fnc blame -r` and make it behave like `fossil blame -o` for familiarity - improve `fnc timeline <path>` lookup of repository paths not in the work tree | > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | **fnc 0.14** 2023-04-18 [[history][0.14a] / [diff][0.14b]] - show msg rather than error if timeline C keymap is used outside a work tree - check path is in the requested version if T keymap is used with `fnc tl path` - display the work tree path in checkout diff headlines - fix diff display bug by resetting diff mode after timeline C keymap is used - list only user-defined settings with `fnc config`, use --ls flag to show all - fix unexpected behaviour when switching between diff and timeline views after requesting a diff by tagging arbitrary commits with the `space` keymap - disallow tagging different wiki pages to request a diff - warn user rather than silently ignore attempts to tag non-diffable artifacts - fix bug if timeline C keymap is used when a commit is already tagged - change diff flags that modify diff views to resolve conflicts with libfossil - catch trailing `\r` in CRLF files when finding scope for hunk headers - show unchanged work tree report for all stash abort cases - fix bug where the stash 'm' option was not available for the first hunk - display "[current]" label with `fnc timeline -c <symbol>` - disable landlock support for the `fnc stash` command to fix reparenting bug - zap dead code - latest upstream libfossil: [libf:9a8269d737] **fnc 0.13** 2022-11-26 [[history][0.13a] / [diff][0.13b]] - improve reporting of invalid diff command arguments (reported by Dan Shearer) - accept libfossil global `-V|--verbose` option for all commands - plug memleak when using the `p` keymap in blame view - fix `fnc blame -r` and make it behave like `fossil blame -o` for familiarity - improve `fnc timeline <path>` lookup of repository paths not in the work tree |
︙ | ︙ | |||
292 293 294 295 296 297 298 299 300 301 302 303 304 305 | - wrap commit comments to the current view width - implement cmd_diff() to provide the 'fnc diff' interface - tailor help/usage output to the specified command - fix invalid memory read in diff routine - add support for repository fingerprint version 1 - fix line wrap bug that truncated lines in fullscreen mode [0.13a]: https://fnc.bsdbox.org/timeline?p=0.13&bt=0.12 [0.13b]: https://fnc.bsdbox.org/vdiff?from=0.12&to=0.13 [0.12a]: https://fnc.bsdbox.org/timeline?p=0.12&bt=0.11 [0.12b]: https://fnc.bsdbox.org/vdiff?from=0.11&to=0.12 [0.11a]: https://fnc.bsdbox.org/timeline?p=0.11&bt=0.10 [0.11b]: https://fnc.bsdbox.org/vdiff?from=0.10&to=0.11 [0.10a]: https://fnc.bsdbox.org/timeline?p=0.10&bt=0.9 | > > | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | - wrap commit comments to the current view width - implement cmd_diff() to provide the 'fnc diff' interface - tailor help/usage output to the specified command - fix invalid memory read in diff routine - add support for repository fingerprint version 1 - fix line wrap bug that truncated lines in fullscreen mode [0.14a]: https://fnc.bsdbox.org/timeline?p=0.14&bt=0.13 [0.14b]: https://fnc.bsdbox.org/vdiff?from=0.13&to=0.14 [0.13a]: https://fnc.bsdbox.org/timeline?p=0.13&bt=0.12 [0.13b]: https://fnc.bsdbox.org/vdiff?from=0.12&to=0.13 [0.12a]: https://fnc.bsdbox.org/timeline?p=0.12&bt=0.11 [0.12b]: https://fnc.bsdbox.org/vdiff?from=0.11&to=0.12 [0.11a]: https://fnc.bsdbox.org/timeline?p=0.11&bt=0.10 [0.11b]: https://fnc.bsdbox.org/vdiff?from=0.10&to=0.11 [0.10a]: https://fnc.bsdbox.org/timeline?p=0.10&bt=0.9 |
︙ | ︙ |
Changes to fnc.bld.mk.
1 2 3 4 5 6 7 8 | # # FNC Common Build # # CONFIGURATION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # # FNC Common Build # # CONFIGURATION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man VERSION ?= 0.14 HASH != cut -f 1 manifest.uuid DATE != sed '2q;d' manifest | cut -d ' ' -f 2 | tr T ' ' # FLAGS NEEDED TO BUILD SQLITE3 SQLITE_CFLAGS = ${CFLAGS} -Wall -Werror -Wno-sign-compare -pedantic -std=c99 \ -DNDEBUG=1 \ -DSQLITE_DQS=0 \ |
︙ | ︙ |
Changes to include/diff.h.
︙ | ︙ | |||
21 22 23 24 25 26 27 | * Flags set by callers of the below diff APIs to determine diff output. */ enum fnc_diff_flag { FNC_DIFF_IGNORE_EOLWS = 0x01, FNC_DIFF_IGNORE_ALLWS = 0x03, FNC_DIFF_SIDEBYSIDE = 1 << 2, /* output side-by-side diff */ FNC_DIFF_VERBOSE = 1 << 3, /* show added/rm'd file content */ | | | | | > | | | < | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | * Flags set by callers of the below diff APIs to determine diff output. */ enum fnc_diff_flag { FNC_DIFF_IGNORE_EOLWS = 0x01, FNC_DIFF_IGNORE_ALLWS = 0x03, FNC_DIFF_SIDEBYSIDE = 1 << 2, /* output side-by-side diff */ FNC_DIFF_VERBOSE = 1 << 3, /* show added/rm'd file content */ FNC_DIFF_BRIEF = 1 << 4, /* no content, just index lines */ FNC_DIFF_HTML = 1 << 5, FNC_DIFF_LINENO = 1 << 6, /* show file line numbers */ FNC_DIFF_NOOPT = 1 << 8, /* suppress optimisations (debug) */ FNC_DIFF_INVERT = 1 << 9, FNC_DIFF_PROTOTYPE = 1 << 10, /* show scope in hunk header */ FNC_DIFF_NOTTOOBIG = 1 << 11, /* don't compute "large" diffs */ FNC_DIFF_STRIP_EOLCR = 1 << 12, /* strip trailing '\r' */ FNC_DIFF_ANSI_COLOR = 1 << 13 #define FNC_DIFF_CONTEXT_EX (((uint64_t)0x04) << 32) /* Allow 0 context */ #define FNC_DIFF_CONTEXT_MASK ((uint64_t)0x0000ffff) /* Default context */ #define FNC_DIFF_WIDTH_MASK ((uint64_t)0x00ff0000) /* SBS column width */ }; /* * Compute the diff of changes to convert the file in fsl_buffer parameter 1 |
︙ | ︙ |
Changes to include/settings.h.
︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 | _(pfx, DIFF_HUNK), \ _(pfx, DIFF_SEPARATOR) #define VIEW_MODE_ENUM(pfx, _) \ _(pfx, NONE), \ _(pfx, VERT), \ _(pfx, HRZN) #define ENUM_INFO(_) \ _(fnc_opt_id, FNC, USER_OPTIONS) \ _(line_attr, SLINE, LINE_ATTR_ENUM) \ _(input_type, INPUT, INPUT_TYPE_ENUM) \ _(line_type, LINE, LINE_TYPE_ENUM) \ | > > > > > > | > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | _(pfx, DIFF_HUNK), \ _(pfx, DIFF_SEPARATOR) #define VIEW_MODE_ENUM(pfx, _) \ _(pfx, NONE), \ _(pfx, VERT), \ _(pfx, HRZN) #define STASH_MVMT_ENUM(pfx, _) \ _(pfx, NONE), \ _(pfx, DOWN), \ _(pfx, UP), \ _(pfx, UPDOWN) #define ENUM_INFO(_) \ _(fnc_opt_id, FNC, USER_OPTIONS) \ _(line_attr, SLINE, LINE_ATTR_ENUM) \ _(input_type, INPUT, INPUT_TYPE_ENUM) \ _(line_type, LINE, LINE_TYPE_ENUM) \ _(view_mode, VIEW_SPLIT, VIEW_MODE_ENUM) \ _(stash_mvmt, STASH_MVMT, STASH_MVMT_ENUM) #define GEN_ENUMS(name, pfx, info) GEN_ENUM(name, pfx, info) ENUM_INFO(GEN_ENUMS) |
Changes to lib/libfossil-config.h.
︙ | ︙ | |||
79 80 81 82 83 84 85 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #endif #endif /* _WIN32 */ | | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #endif #endif /* _WIN32 */ #define FSL_LIB_VERSION_HASH "9a8269d7377f77c179c4bfb05cda36c9f01976e8" #define FSL_LIB_VERSION_TIMESTAMP "2023-03-07 14:32:32.997 UTC" #define FSL_LIB_CONFIG_TIME "2023-04-17 13:37 GMT" #if defined(_MSC_VER) #define FSL_PLATFORM_OS "windows" #define FSL_PLATFORM_IS_WINDOWS 1 #define FSL_PLATFORM_IS_UNIX 0 #define FSL_PLATFORM_PLATFORM "windows" #define FSL_PLATFORM_PATH_SEPARATOR ";" #define FSL_CHECKOUTDB_NAME "./_FOSSIL_" |
︙ | ︙ |
Changes to lib/libfossil.c.
︙ | ︙ | |||
6512 6513 6514 6515 6516 6517 6518 | "CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash," " merge" " FROM vmerge" " WHERE (vmerge.id=-1 OR vmerge.id=-2)" " ORDER BY 1"); while( !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(&q)) ){ fsl_id_t const mid = fsl_stmt_g_id(&q, 1); | > > > > > > > > > > > | | 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 | "CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash," " merge" " FROM vmerge" " WHERE (vmerge.id=-1 OR vmerge.id=-2)" " ORDER BY 1"); while( !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(&q)) ){ fsl_id_t const mid = fsl_stmt_g_id(&q, 1); /* The is-private check is taken from: https://fossil-scm.org/home/info/52a66829d655ff35dc52: noting that that patch also has (!g.markPrivate), a flag we do not currently have in fsl_cx but perhaps should, so that we can flag privacy at a higher level when doing a checkin. As of this writing (2023-02-02), we do not have any infrastructure for making private checkins. */ if( mid != basedOnVid && !fsl_content_is_private(f, mid) ){ const char *zCherrypickUuid = fsl_stmt_g_text(&q, 0, NULL); int const qType = '+'==*(zCherrypickUuid++) ? 1 : -1; rc = fsl_deck_Q_add( d, qType, zCherrypickUuid, NULL ); } } fsl_stmt_finalize(&q); RC; |
︙ | ︙ | |||
14302 14303 14304 14305 14306 14307 14308 | void fsl_cx_caches_reset(fsl_cx * const f){ fsl__bccache_reset(&f->cache.blobContent); fsl__cx_mcache_clear(f); fsl__cx_clear_mf_seen(f, false); f->cache.allowSymlinks = f->cache.caseInsensitive = | < | > > | 14313 14314 14315 14316 14317 14318 14319 14320 14321 14322 14323 14324 14325 14326 14327 14328 14329 | void fsl_cx_caches_reset(fsl_cx * const f){ fsl__bccache_reset(&f->cache.blobContent); fsl__cx_mcache_clear(f); fsl__cx_clear_mf_seen(f, false); f->cache.allowSymlinks = f->cache.caseInsensitive = f->cache.manifestSetting = f->cache.searchIndexExists = f->cache.seenDeltaManifest = -1; if(fsl_cx_db_ckout(f)){ fsl__ckout_version_fetch(f) /* FIXME: this "really should" be fsl__cx_ckout_clear(), but that data is not fetched on demand (maybe it should be?). */; }else{ fsl__cx_ckout_clear(f); } |
︙ | ︙ | |||
14618 14619 14620 14621 14622 14623 14624 | break; default: fsl__fatal(FSL_RC_ERROR, "Cannot happen. Really."); } fsl__cx_finalize_cached_stmt(f); fsl__db_cached_clear_role(f->dbMain, r) /* Make sure that we destroy any cached statements which are | | | 14630 14631 14632 14633 14634 14635 14636 14637 14638 14639 14640 14641 14642 14643 14644 | break; default: fsl__fatal(FSL_RC_ERROR, "Cannot happen. Really."); } fsl__cx_finalize_cached_stmt(f); fsl__db_cached_clear_role(f->dbMain, r) /* Make sure that we destroy any cached statements which are known to be tied to this db role. */; if(db->dbh){ /* This is our MAIN db. CLOSE it. If we still have a secondary/counterpart db open, we'll detach it first. */ fsl_dbrole_e const counterpart = fsl__dbrole_counterpart(r); assert(f->dbMain == db); if(db->role & counterpart){ /* When closing the main db, detach the counterpart db first |
︙ | ︙ | |||
14759 14760 14761 14762 14763 14764 14765 | if(fsl_cx_transaction_level(f)){ /* TODO???: force a full rollback and close it */ //if(f->repo.db.dbh) fsl_db_rollback_force(&f->repo.db); //if(f->ckout.db.dbh) fsl_db_rollback_force(&f->ckout.db); return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close repo or checkout with an " "opened transaction."); | > | | | 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 14790 14791 14792 14793 14794 14795 14796 14797 | if(fsl_cx_transaction_level(f)){ /* TODO???: force a full rollback and close it */ //if(f->repo.db.dbh) fsl_db_rollback_force(&f->repo.db); //if(f->ckout.db.dbh) fsl_db_rollback_force(&f->ckout.db); return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close repo or checkout with an " "opened transaction."); } if(!f->dbMain){ // Make sure that all string resources are cleaned up... fsl_db_close(&f->repo.db); fsl_db_close(&f->ckout.db); return 0; }else{ fsl_db * const dbR = &f->repo.db; return fsl__cx_detach_role(f, f->dbMain == dbR ? FSL_DBROLE_REPO : FSL_DBROLE_CKOUT) /* Will also close the counterpart db. */; } } int fsl_repo_close( fsl_cx * const f ){ return fsl_close_scm_dbs(f); } int fsl_ckout_close( fsl_cx * const f ){ |
︙ | ︙ | |||
15121 15122 15123 15124 15125 15126 15127 15128 15129 15130 15131 15132 15133 15134 | /** To be called after a repo or checkout/repo combination has been opened. This updates some internal cached info based on the checkout and/or repo. */ static int fsl_cx_after_open(fsl_cx * f){ int rc = fsl__ckout_version_fetch(f); if(!rc) rc = fsl_cx_load_glob_lists(f); return rc; } static void fsl_cx_fetch_hash_policy(fsl_cx * f){ | > | 15134 15135 15136 15137 15138 15139 15140 15141 15142 15143 15144 15145 15146 15147 15148 | /** To be called after a repo or checkout/repo combination has been opened. This updates some internal cached info based on the checkout and/or repo. */ static int fsl_cx_after_open(fsl_cx * f){ f->cache.searchIndexExists = -1; int rc = fsl__ckout_version_fetch(f); if(!rc) rc = fsl_cx_load_glob_lists(f); return rc; } static void fsl_cx_fetch_hash_policy(fsl_cx * f){ |
︙ | ︙ | |||
35250 35251 35252 35253 35254 35255 35256 | if(!rc){ rc = fsl_db_exec(db, "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))" " WHERE login=%Q", defaultUser); if( !rc && !addOnlyUser ){ fsl_db_exec_multi(db, "INSERT OR IGNORE INTO user(login,pw,cap,info)" | | | | 35264 35265 35266 35267 35268 35269 35270 35271 35272 35273 35274 35275 35276 35277 35278 35279 35280 35281 35282 35283 35284 35285 35286 35287 35288 35289 | if(!rc){ rc = fsl_db_exec(db, "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))" " WHERE login=%Q", defaultUser); if( !rc && !addOnlyUser ){ fsl_db_exec_multi(db, "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('anonymous',hex(randomblob(8)),'hz'," " 'Anon');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('nobody','','gjor','Nobody');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('developer','','dei','Dev');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('reader','','kptw','Reader');" ); } } return rc; } int fsl_repo_create(fsl_cx * f, fsl_repo_create_opt const * opt ){ fsl_db * db = 0; fsl_cx F = fsl_cx_empty /* used if !f */; int rc = 0; char const * userName = 0; |
︙ | ︙ | |||
36974 36975 36976 36977 36978 36979 36980 | /* Only for debugging */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) | > > > > > > | > | > > | < | > > > > | | > | > | > | > > | | | | | | | 36988 36989 36990 36991 36992 36993 36994 36995 36996 36997 36998 36999 37000 37001 37002 37003 37004 37005 37006 37007 37008 37009 37010 37011 37012 37013 37014 37015 37016 37017 37018 37019 37020 37021 37022 37023 37024 37025 37026 37027 37028 37029 37030 37031 37032 37033 37034 37035 37036 37037 37038 37039 37040 37041 37042 37043 37044 37045 37046 37047 37048 37049 37050 37051 37052 37053 37054 37055 37056 37057 37058 37059 37060 37061 37062 37063 37064 37065 37066 37067 37068 37069 37070 37071 37072 37073 37074 37075 37076 | /* Only for debugging */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) /** Returns 0 if f has no search index or a repo is not opened, else 4 for an FTS4 index and 5 for an FTS5. If forceRecheck is false then a cached values is returned after the first call. If forceRecheck is true, the cached value is updated. */ static int fsl__search_ndx_exists(fsl_cx * const f, bool forceRecheck){ fsl_db * const db = fsl_cx_db_repo(f); if(db && (f->cache.searchIndexExists<0 || forceRecheck) ){ const bool b = fsl_db_table_exists(db, FSL_DBROLE_REPO, "ftsdocs"); if(b){ f->cache.searchIndexExists = fsl_db_table_has_column(db, "ftsdocs", "rowid" ) ? 5 // FTS5 (fossil as of 2023-01-24) : (fsl_db_table_has_column(db, "ftsdocs", "docid" ) ? 4 : 0); assert(f->cache.searchIndexExists==4 || f->cache.searchIndexExists==5); }else{ f->cache.searchIndexExists = 0; } } return f->cache.searchIndexExists; } static char fsl_satype_letter(fsl_satype_e t){ switch(t){ case FSL_SATYPE_CHECKIN: return 'c'; case FSL_SATYPE_WIKI: return 'w'; case FSL_SATYPE_TICKET: return 't'; case FSL_SATYPE_FORUMPOST: return 'f'; case FSL_SATYPE_TECHNOTE: return 'e'; default: assert(!"Internal misuse of fsl_satype_letter()"); return 0; } } int fsl__search_doc_touch(fsl_cx * const f, fsl_satype_e saType, fsl_id_t rid, const char * docName){ const int ftsVers = fsl__search_ndx_exists(f, false); if(!ftsVers || fsl_content_is_private(f, rid)) return 0; assert(ftsVers==4 || ftsVers==5 || !"If this fails then our search-index-exists check is wrong."); char zType[2] = {0,0}; zType[0] = fsl_satype_letter(saType); #if 0 /* See MARKER() call in the #else block */ assert(*zType); return *zType ? 0 : FSL_RC_MISUSE; #else /* Reminder: fossil(1) does some once-per-connection init here which installs UDFs used by the search process. Those will be significant for us if we add the search features to the library. */ assert(zType[0] && "Misuse of fsl__search_doc_touch()'s 2nd parameter."); fsl_db * const db = fsl_cx_db_repo(f); int rc = 0; char const * zDocId = 4==ftsVers ? "docid" : "rowid"; rc = fsl_db_exec(db, "DELETE FROM ftsidx WHERE %s IN" " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%"FSL_ID_T_PFMT" AND idxed)", zDocId, zType, rid ); if(rc){ // For reasons i don't understand, this query fails with "SQL logic error" // when run from here, but succeeds fine in fossil and fossil's SQL shell. MARKER(("type=%s rid=%d rc=%s\n",zType, (int)rid, fsl_rc_cstr(rc))); goto end; } rc = fsl_db_exec(db, "REPLACE INTO ftsdocs(type,rid,name,idxed)" " VALUES(%Q,%"FSL_ID_T_PFMT",%Q,0)", zType, rid, docName ); if(rc) goto end; if( FSL_SATYPE_WIKI==saType || FSL_SATYPE_TECHNOTE==saType ){ rc = fsl_db_exec(db, "DELETE FROM ftsidx WHERE %s IN" " (SELECT rowid FROM ftsdocs WHERE type=%Q AND name=%Q AND idxed)", zDocId, zType, docName ); if(!rc) rc = fsl_db_exec(db, "DELETE FROM ftsdocs WHERE type=%Q AND name=%Q AND rid!=%"FSL_ID_T_PFMT, zType, docName, rid ); } /* All forum posts are always indexed */ end: return rc; |
︙ | ︙ |
Changes to lib/libfossil.h.
︙ | ︙ | |||
11908 11909 11910 11911 11912 11913 11914 | /** Adds a Q-card record to the given deck. The type argument must be negative for a backed-out change, positive for a cherrypicked change. target must be a valid UUID string. If baseline is not NULL then it also must be a valid UUID. | | | | | | | 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 | /** Adds a Q-card record to the given deck. The type argument must be negative for a backed-out change, positive for a cherrypicked change. target must be a valid UUID string. If baseline is not NULL then it also must be a valid UUID. Returns 0 on success, non-0 on error. FSL_RC_MISUSE if !mf or !target, FSL_RC_SYNTAX if type is 0 or target/baseline are not valid UUID strings (baseline may be NULL). */ FSL_EXPORT int fsl_deck_Q_add( fsl_deck * const mf, int type, fsl_uuid_cstr target, fsl_uuid_cstr baseline ); /** Functionally identical to fsl_deck_B_set() except that it sets the R-card. Returns 0 on succes, FSL_RC_RANGE if md5 is not NULL or exactly FSL_STRLEN_MD5 bytes long (not including trailing NUL). If md5==NULL the current R value is cleared. |
︙ | ︙ | |||
15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 | fsl_checkin_queue_f callback; /** Opaque client-side state for use as the 2nd argument to this->callback. */ void * callbackState; }; /** Convenience typedef. */ typedef struct fsl_checkin_queue_opt fsl_checkin_queue_opt; /** Initialized-with-defaults fsl_checkin_queue_opt structure, intended for const-copy initialization. */ | > > > > | 15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 16006 16007 16008 16009 | fsl_checkin_queue_f callback; /** Opaque client-side state for use as the 2nd argument to this->callback. */ void * callbackState; /* TODO (2023-02-02): any state necessary for indicating that this checking should be flagged as "private". */ }; /** Convenience typedef. */ typedef struct fsl_checkin_queue_opt fsl_checkin_queue_opt; /** Initialized-with-defaults fsl_checkin_queue_opt structure, intended for const-copy initialization. */ |
︙ | ︙ |
Deleted signify/fnc-12-release.pub.
|
| < < |
Added signify/fnc-14-release.pub.
> > | 1 2 | untrusted comment: fnc 0.14 public key RWTOJ8pPCpW9YMEsLrgLmTRk0huOrEnyiLZRYo36C/9JQF/H3JnCGpFc |
Changes to src/diff.c.
︙ | ︙ | |||
96 97 98 99 100 101 102 | struct diff_out_state { fsl_output_f out; /* Output callback */ void *state; /* State for this->out() */ enum line_type *lines; /* Diff line type (e.g., minus, plus) */ uint32_t nlines; /* Index into this->lines */ int rc; /* Error reporting */ char ansi; /* ANSI colour code */ | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | struct diff_out_state { fsl_output_f out; /* Output callback */ void *state; /* State for this->out() */ enum line_type *lines; /* Diff line type (e.g., minus, plus) */ uint32_t nlines; /* Index into this->lines */ int rc; /* Error reporting */ char ansi; /* ANSI colour code */ struct hunk_scope { const fsl_buffer *file; /* Diffed file */ char *sig; /* Matching function */ char *spec; /* C++ specifier */ uint32_t lastmatch; /* Match line index */ uint32_t lastline; /* Last line scanned */ fsl_size_t offset; /* Match byte offset */ } proto; |
︙ | ︙ | |||
130 131 132 133 134 135 136 | fsl_output_f, void *, enum line_type **, uint32_t *, /* void *regex, */ uint16_t, short, int, int **); static int fnc_output_f_diff_out(void *, void const *, fsl_size_t); static int diff_outf(struct diff_out_state *, char const *, ... ); static int diff_out(struct diff_out_state * const, void const *, fsl_int_t); static int validate_scope_line(const char *); | | < | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | fsl_output_f, void *, enum line_type **, uint32_t *, /* void *regex, */ uint16_t, short, int, int **); static int fnc_output_f_diff_out(void *, void const *, fsl_size_t); static int diff_outf(struct diff_out_state *, char const *, ... ); static int diff_out(struct diff_out_state * const, void const *, fsl_int_t); static int validate_scope_line(const char *); static int match_hunk_function(struct hunk_scope *, fsl_int_t); static int buffer_copy_lines_from(fsl_buffer *const, const fsl_buffer *const, fsl_size_t *, fsl_size_t, fsl_size_t); /* static uint64_t fnc_diff_flags_convert(int); */ /* static int diff_context_lines(uint64_t); */ static int match_dline(fsl_dline *, fsl_dline *); static bool find_lcs(const char *z, int, const char *, int, int *); |
︙ | ︙ | |||
648 649 650 651 652 653 654 | * Starting from seek lines, copy n lines from src to dst. The seek starts * from offset bytes into src->mem. This routine does _not_ modify src. */ static int buffer_copy_lines_from(fsl_buffer *const dst, const fsl_buffer *const src, fsl_size_t *offset, fsl_size_t seek, fsl_size_t n) { | | | < | > > > > > > > > | > > > > | > | | | < | < | < < | > | | 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | * Starting from seek lines, copy n lines from src to dst. The seek starts * from offset bytes into src->mem. This routine does _not_ modify src. */ static int buffer_copy_lines_from(fsl_buffer *const dst, const fsl_buffer *const src, fsl_size_t *offset, fsl_size_t seek, fsl_size_t n) { const char *z = (const char *)src->mem; fsl_size_t i, start, ln; if (!n) return FSL_RC_OK; if (dst == NULL) return FSL_RC_MISUSE; if (*offset > src->used) return FSL_RC_RANGE; i = *offset; ln = start = 0; for (;;) { if (i == src->used) { if (ln == seek && start > 0) break; /* no EOF '\n' so this is seek + n */ return FSL_RC_NOT_FOUND; } if (z[i] == '\n') { ++ln; if (ln == seek) start = i + 1; /* skip '\n' */ if (ln == seek + n) break; } ++i; } *offset = start; return fsl_buffer_append(dst, &src->mem[start], i - start); } /* * If str is a valid scope line (i.e., function prototype; class, namespace, * enum, struct, or union declaration; or C++ class specifier) return the * corresponding int value. GNU C and MSVC allow '$' in identifier names: * https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html * https://docs.microsoft.com/en-us/cpp/cpp/identifiers-cpp */ static int validate_scope_line(const char *str) { if (str && (fsl_isalpha(*str) || *str == '_' || *str == '$')) { if (strchr(str, '(') || strchr(str, '{') || strstr(str, "struct ") || strstr(str, "union ") || strstr(str, "enum ") || strstr(str, "class ") || strstr(str, "namespace ")) return VSL_TRUE; if (starts_with(str, "private")) return VSL_PRIVATE; |
︙ | ︙ | |||
707 708 709 710 711 712 713 | /* * Back scan the diffed file dst->proto.file from line pos, which is the first * changed line of the current hunk, for the enclosing function in which the * hunk resides. Point dst->proto.sig to the heap-allocated matching line, * which the caller must eventually free. Return FSL_RC_OK on success. */ static int | | | | | | | | | < < | | | | | | > > | | | | | | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | /* * Back scan the diffed file dst->proto.file from line pos, which is the first * changed line of the current hunk, for the enclosing function in which the * hunk resides. Point dst->proto.sig to the heap-allocated matching line, * which the caller must eventually free. Return FSL_RC_OK on success. */ static int match_hunk_function(struct hunk_scope *hs, fsl_int_t pos) { fsl_buffer buf = fsl_buffer_empty; char *line = NULL; fsl_size_t offset; uint32_t last = hs->lastline; int rc = FSL_RC_OK; hs->lastline = pos; offset = hs->offset; /* begin seek from last match */ while (pos >= 0 && pos >= last) { rc = buffer_copy_lines_from(&buf, hs->file, &offset, pos - hs->lastmatch, 1); if (rc && rc != FSL_RC_NOT_FOUND) return rc; line = fsl_buffer_take(&buf); switch (validate_scope_line(line)) { case VSL_FALSE: break; case VSL_PRIVATE: if (!hs->spec) hs->spec = " (private)"; break; case VSL_PROTECT: if (!hs->spec) hs->spec = " (protected)"; break; case VSL_PUBLIC: if (!hs->spec) hs->spec = " (public)"; break; case VSL_TRUE: line[strcspn(line, "\n")] = '\0'; line[strcspn(line, "\r")] = '\0'; /* fossil wiki */ fsl_free(hs->sig); hs->sig = line; /* * It's expensive to seek from the start of the * file for each hunk when diffing large files, * so save offset and line index of this match. */ hs->lastmatch = pos; hs->offset = offset; return FSL_RC_OK; } /* No match, revert to last offset. */ fsl_free(line); offset = hs->offset; --pos; } return FSL_RC_OK; } /* |
︙ | ︙ | |||
1363 1364 1365 1366 1367 1368 1369 | if (html) { rc = diff_outf(dst, "</span>"); if (rc) return rc; } if (proto && li + skip > 1) { | > | | < > | | | < | | < | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 | if (html) { rc = diff_outf(dst, "</span>"); if (rc) return rc; } if (proto && li + skip > 1) { struct hunk_scope *hs = &dst->proto; int n = 55; rc = match_hunk_function(hs, li + skip + context); if (rc) return rc; if (hs->spec) n -= fsl_strlen(hs->spec); if (hs->sig) { rc = diff_outf(dst, " %.*s", n, hs->sig); if (rc) return rc; } if (hs->spec) { rc = diff_outf(dst, " %s", hs->spec); if (rc) return rc; } } rc = diff_out(dst, "\n", 1); if (rc) return rc; |
︙ | ︙ |
Changes to src/fnc.1.
︙ | ︙ | |||
165 166 167 168 169 170 171 | .Ar setting , otherwise .Nm will display the current value of .Ar setting . With no arguments, .Nm Cm config | | | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | .Ar setting , otherwise .Nm will display the current value of .Ar setting . With no arguments, .Nm Cm config will display a list of all user-defined settings. See .Sx ENVIRONMENT for a detailed list of available settings used in the display or processing of data. When no value is defined for a given setting in the local repository, environment variables will be searched. If still not found, .Nm will fallback to default values. Unless the .Sy --repo option is used, this command must be invoked from within a work tree; that is, .Nm assumes a local checkout is open in or above the current working directory. Options for .Nm Cm config are as follows: .Bl -tag -width Ds .It Fl h , -help Display config command help and usage information then exit. .It Fl -ls List all available settings. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository at the specified .Ar path for the current .Nm Cm config |
︙ | ︙ |
Changes to src/fnc.c.
︙ | ︙ | |||
48 49 50 51 52 53 54 | #include <sys/queue.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> | < < < < < < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <sys/queue.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> #include <ctype.h> #include <curses.h> #include <panel.h> #include <locale.h> #include <stdlib.h> #include <stdarg.h> |
︙ | ︙ | |||
564 565 566 567 568 569 570 | fcli_cliflag_empty_m }, /* End cliflags_blame. */ { /* cliflags_config config command related options. */ FCLI_FLAG_BOOL("h", "help", NULL, "Display config command help and usage."), FCLI_FLAG_BOOL(NULL, "ls", &fnc_init.lsconf, | | | 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | fcli_cliflag_empty_m }, /* End cliflags_blame. */ { /* cliflags_config config command related options. */ FCLI_FLAG_BOOL("h", "help", NULL, "Display config command help and usage."), FCLI_FLAG_BOOL(NULL, "ls", &fnc_init.lsconf, "Display a list of all available settings."), FCLI_FLAG_CSTR("R", "repo", "<path>", NULL, "Use the fossil(1) repository located at <path> for this config\n" " invocation."), FCLI_FLAG_BOOL("u", "unset", &fnc_init.unset, "Unset (i.e., remove) the specified repository setting."), fcli_cliflag_empty_m }, /* End cliflags_tree. */ |
︙ | ︙ | |||
802 803 804 805 806 807 808 | struct commit_queue commits; struct commit_entry *first_commit_onscreen; struct commit_entry *last_commit_onscreen; struct commit_entry *selected_entry; struct commit_entry *matched_commit; struct commit_entry *search_commit; struct fnc_colours colours; | | | > | < < | | | | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | struct commit_queue commits; struct commit_entry *first_commit_onscreen; struct commit_entry *last_commit_onscreen; struct commit_entry *selected_entry; struct commit_entry *matched_commit; struct commit_entry *search_commit; struct fnc_colours colours; struct timeline_tag { struct fnc_commit_artifact *one; /* 1st tagged entry */ struct fnc_commit_artifact *two; /* 2nd tagged entry */ fsl_uuid_str ogid; /* parent uuid of two */ fsl_id_t ogrid; /* parent rid of two */ } tag; const char *ckout_id; const char *glob; /* Match commits containing glob. */ char *path; /* Match commits involving path. */ int selected; int nscrolled; uint16_t maxx; sig_atomic_t quit; pthread_t thread_id; |
︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 | static int write_commit_line(struct fnc_view *, struct fnc_commit_artifact *, int); static int view_input(struct fnc_view **, int *, struct fnc_view *, struct view_tailhead *); static int cycle_view(struct fnc_view *); static int toggle_fullscreen(struct fnc_view **, struct fnc_view *); | | | | | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 | static int write_commit_line(struct fnc_view *, struct fnc_commit_artifact *, int); static int view_input(struct fnc_view **, int *, struct fnc_view *, struct view_tailhead *); static int cycle_view(struct fnc_view *); static int toggle_fullscreen(struct fnc_view **, struct fnc_view *); static int stash_help(struct fnc_view *, enum stash_mvmt); static int help(struct fnc_view *); static int padpopup(struct fnc_view *, const char *[][2], const char **, const char *, enum stash_mvmt); static int centerprint(WINDOW *, size_t, size_t, size_t, const char *, chtype); static int tl_input_handler(struct fnc_view **, struct fnc_view *, int); static int move_tl_cursor_down(struct fnc_view *, uint16_t); static void move_tl_cursor_up(struct fnc_view *, uint16_t, bool); static int timeline_scroll_down(struct fnc_view *, int); static void timeline_scroll_up(struct fnc_tl_view_state *, int); static int tag_timeline_entry(struct fnc_tl_view_state *); static void select_commit(struct fnc_tl_view_state *); static int request_view(struct fnc_view **, struct fnc_view *, enum fnc_view_id); static int init_view(struct fnc_view **, struct fnc_view *, enum fnc_view_id, int, int); static enum view_mode view_get_split(struct fnc_view *, int *, int *); static int split_view(struct fnc_view *, int *); |
︙ | ︙ | |||
1205 1206 1207 1208 1209 1210 1211 | static int diff_file(struct fnc_diff_view_state *, fsl_buffer *, const char *, const char *, fsl_uuid_cstr, const char *, const fsl_ckout_change_e); static int diff_non_checkin(struct fnc_diff_view_state *); static int diff_file_artifact(struct fnc_diff_view_state *, fsl_id_t, const fsl_card_F *, const fsl_card_F *, const fsl_ckout_change_e); | | | < | 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 | static int diff_file(struct fnc_diff_view_state *, fsl_buffer *, const char *, const char *, fsl_uuid_cstr, const char *, const fsl_ckout_change_e); static int diff_non_checkin(struct fnc_diff_view_state *); static int diff_file_artifact(struct fnc_diff_view_state *, fsl_id_t, const fsl_card_F *, const fsl_card_F *, const fsl_ckout_change_e); static int show_diff_view(struct fnc_view *); static int draw_diff_headln(struct fnc_view *); static int match_line(const char *, regex_t *, size_t, regmatch_t *); static int draw_matched_line(struct fnc_view *, const char *, int *, int, int, regmatch_t *, attr_t); static void drawborder(struct fnc_view *); static int diff_input_handler(struct fnc_view **, struct fnc_view *, int); |
︙ | ︙ | |||
1234 1235 1236 1237 1238 1239 1240 | static int fnc_stash_get(bool); static int select_hunks(struct fnc_view *); static int stash_input_handler(struct fnc_view *, bool *); static void set_choice(struct fnc_diff_view_state *, bool *, struct input *, struct index *, uint32_t *, size_t *, size_t *, bool *, enum stash_opt *); static unsigned char *alloc_bitstring(size_t); | | | | | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | static int fnc_stash_get(bool); static int select_hunks(struct fnc_view *); static int stash_input_handler(struct fnc_view *, bool *); static void set_choice(struct fnc_diff_view_state *, bool *, struct input *, struct index *, uint32_t *, size_t *, size_t *, bool *, enum stash_opt *); static unsigned char *alloc_bitstring(size_t); static int generate_prompt(char *, char *, size_t, enum stash_mvmt); static bool valid_input(const char, char *); static int revert_ckout(bool, bool); static int rm_vfile_renames_cb(fsl_stmt *, void *); static int fnc_patch(struct patch_cx *, const char *); static int scan_patch(struct patch_cx *, FILE *); static int find_patch_file(struct fnc_patch_file **, struct patch_cx *, FILE *); static int parse_filename(const char *, char **, int); |
︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 | static bool view_is_parent(struct fnc_view *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); static int close_diff_view(struct fnc_view *); static void free_index(struct index *, bool); | | | 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 | static bool view_is_parent(struct fnc_view *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); static int close_diff_view(struct fnc_view *); static void free_index(struct index *, bool); static int reset_tags(struct fnc_tl_view_state *); static int view_resize(struct fnc_view *, bool); static bool screen_is_split(struct fnc_view *); static bool screen_is_shared(struct fnc_view *); static void updatescreen(WINDOW *, bool, bool); static void fnc_resizeterm(void); static int join_tl_thread(struct fnc_tl_view_state *); static void fnc_free_commits(struct commit_queue *); |
︙ | ︙ | |||
1421 1422 1423 1424 1425 1426 1427 | static bool fnc_str_has_upper(const char *); static int fnc_make_sql_glob(char **, char **, const char *, bool); static const char *gettzfile(void); #ifndef HAVE_LANDLOCK static int init_unveil(const char **, const char **, int, bool); #else | | | | 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 | static bool fnc_str_has_upper(const char *); static int fnc_make_sql_glob(char **, char **, const char *, bool); static const char *gettzfile(void); #ifndef HAVE_LANDLOCK static int init_unveil(const char **, const char **, int, bool); #else static int init_landlock(const char **, const char **, const int); #define init_unveil(_p, _m, _n, _d) init_landlock(_p, _m, _n) #endif /* HAVE_LANDLOCK */ static const char *getdirname(const char *, fsl_int_t, bool); static int set_colours(struct fnc_colours *, enum fnc_view_id); static int set_colour_scheme(struct fnc_colours *, const int (*)[2], const char **, int); static int init_colour(enum fnc_opt_id); static int default_colour(enum fnc_opt_id); |
︙ | ︙ | |||
1452 1453 1454 1455 1456 1457 1458 | int main(int argc, const char **argv) { const fcli_command *cmd = NULL; char *path = NULL; int rc = FSL_RC_OK; | < < < < | < | | < | < | 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | int main(int argc, const char **argv) { const fcli_command *cmd = NULL; char *path = NULL; int rc = FSL_RC_OK; if (!isatty(fileno(stdin))) errx(1, "stdin is not a tty"); setlocale(LC_CTYPE, ""); fnc_init.cmdarg = argv[1]; /* Which cmd to show usage if needed. */ #if DEBUG fcli.clientFlags.verbose = 2; /* Verbose error reporting. */ #endif rc = fcli_setup_v2(argc, argv, fnc_init.cliflags_global, &fnc_init.fnc_help); |
︙ | ︙ | |||
1807 1808 1809 1810 1811 1812 1813 | rc = RC(FSL_RC_RANGE, "strlcpy"); fsl_free(*child); *child = NULL; } return rc; } | < < < < < < < < | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 | rc = RC(FSL_RC_RANGE, "strlcpy"); fsl_free(*child); *child = NULL; } return rc; } static int init_curses(void) { int rc; rc = fnc_set_signals(); if (rc) |
︙ | ︙ | |||
1924 1925 1926 1927 1928 1929 1930 | if (path != s->path) { fsl_free(s->path); s->path = fsl_strdup(path); if (s->path == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); } | < < < < < < < < < < < < < | > < < | | 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 | if (path != s->path) { fsl_free(s->path); s->path = fsl_strdup(path); if (s->path == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); } TAILQ_INIT(&s->commits.head); fsl_ckout_version_info(f, NULL, &s->ckout_id); if (rid) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); /* * In 'fnc tl -R <repo> <path>' or 'fnc tl -c <sym> <path>' case, * check that path is a valid path in the tree as at either the * latest checkin or the specified commit. */ if (path) { fsl_uuid_str id = NULL; if (rid) id = fsl_rid_to_uuid(f, rid); rc = valid_path(path, id ? id : "tip"); fsl_free(id); |
︙ | ︙ | |||
2713 2714 2715 2716 2717 2718 2719 | /* FALL THROUGH */ default: break; } } if ((idxstr = fsl_mprintf("%s [%d/%d] %s", | | | 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 | /* FALL THROUGH */ default: break; } } if ((idxstr = fsl_mprintf("%s [%d/%d] %s", !fsl_strcmp(uuid, s->ckout_id) ? " [current]" : "", entry ? entry->idx + 1 : 0, s->commits.ncommits, search_str ? search_str : (branch ? branch : ""))) == NULL) { rc = RC(FSL_RC_RANGE, "fsl_mprintf"); goto end; } } |
︙ | ︙ | |||
2813 2814 2815 2816 2817 2818 2819 2820 2821 | entry = TAILQ_NEXT(entry, entries); } ncommits = 0; entry = s->first_commit_onscreen; s->last_commit_onscreen = s->first_commit_onscreen; while (entry) { if (ncommits >= MIN(view->nlines - 1, view->lines - 1)) break; | > > > > | < > | < > | 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 | entry = TAILQ_NEXT(entry, entries); } ncommits = 0; entry = s->first_commit_onscreen; s->last_commit_onscreen = s->first_commit_onscreen; while (entry) { struct timeline_tag *t = &s->tag; if (ncommits >= MIN(view->nlines - 1, view->lines - 1)) break; if ((t->one && !t->two && entry->commit == t->one) || ncommits == s->selected) wattr_on(view->window, A_REVERSE, NULL); rc = write_commit_line(view, entry->commit, maxlen); if ((t->one && !t->two && entry->commit == t->one) || ncommits == s->selected) wattr_off(view->window, A_REVERSE, NULL); ++ncommits; s->last_commit_onscreen = entry; entry = TAILQ_NEXT(entry, entries); } drawborder(view); end: |
︙ | ︙ | |||
3243 3244 3245 3246 3247 3248 3249 | break; case KEY_F(1): case 'H': case '?': rc = help(view); break; case 'q': | | > > > | | | > > | > | 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 | break; case KEY_F(1): case 'H': case '?': rc = help(view); break; case 'q': if (view->parent && view->parent->vid == FNC_VIEW_TIMELINE) { rc = reset_tags(&view->parent->state.timeline); if (rc) return rc; if (view->mode == VIEW_SPLIT_HRZN) { /* may need more commits to fill fullscreen */ rc = request_tl_commits(view->parent); if (rc) return rc; view->parent->mode = VIEW_SPLIT_NONE; } } rc = view->input(new, view, ch); view->egress = true; break; case 'f': rc = toggle_fullscreen(new, view); break; |
︙ | ︙ | |||
3357 3358 3359 3360 3361 3362 3363 | rc = offset_selected_line(view); } return rc; } static int | | | | | | | | 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 | rc = offset_selected_line(view); } return rc; } static int stash_help(struct fnc_view *view, enum stash_mvmt scroll) { char *title = NULL; static const char *keys[][2] = { {"", ""}, {"", ""}, {" b ", " ❬b❭ "}, {" m ", " ❬m❭ "}, {" y ", " ❬y❭ "}, {" n ", " ❬n❭ "}, {" a ", " ❬a❭ "}, {" k ", " ❬k❭ "}, {" A ", " ❬A❭ "}, {" K ", " ❬K❭ "}, {" ? ", " ❬?❭ "}, {" q ", " ❬q❭ "}, {" Q ", " ❬Q❭ "}, {"", ""}, {"", ""}, {NULL, NULL} }; static const char *desc[] = { "", "Stash", "- scroll back to the previous page^", "- show more of this hunk on the next page^", "- stash this hunk", |
︙ | ︙ | |||
3614 3615 3616 3617 3618 3619 3620 | /* * Create popup pad in which to write the supplied txt string and optional * title. The pad is contained within a window that is offset four columns in * and two lines down from the parent window. */ static int padpopup(struct fnc_view *view, const char *keys[][2], const char **desc, | | | 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 | /* * Create popup pad in which to write the supplied txt string and optional * title. The pad is contained within a window that is offset four columns in * and two lines down from the parent window. */ static int padpopup(struct fnc_view *view, const char *keys[][2], const char **desc, const char *title, enum stash_mvmt stash) { WINDOW *win, *content; FILE *txt; char *line = NULL; ssize_t linelen; size_t linesz; int ch, cury, curx, end, ln, width, wy, wx, x0, y0; |
︙ | ︙ | |||
3636 3637 3638 3639 3640 3641 3642 | /* * Format help text, and compute longest line and total number of * lines in text to be displayed to determine pad dimensions. */ width = fsl_strlen(title); for (ln = 0; keys[ln][0]; ++ln) { | | > | > > | | 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 | /* * Format help text, and compute longest line and total number of * lines in text to be displayed to determine pad dimensions. */ width = fsl_strlen(title); for (ln = 0; keys[ln][0]; ++ln) { if ((stash == STASH_MVMT_NONE && (ln == 2 || ln == 3)) || (stash == STASH_MVMT_NONE && ln == 14) || (stash == STASH_MVMT_DOWN && ln == 2) || (stash == STASH_MVMT_UP && ln == 3)) continue; /* only show available stash keymaps */ if (keys[ln][1]) { width = MAX((fsl_size_t)width, fsl_strlen(keys[ln][cs]) + fsl_strlen(desc[ln])); } fprintf(txt, "%s%s%c", keys[ln][cs], desc[ln], keys[ln + 1][0] ? '\n' : 0); } ++width; rewind(txt); x0 = 4; /* column number at which to start the help window */ y0 = 2; /* line number at which to start the help window */ |
︙ | ︙ | |||
3792 3793 3794 3795 3796 3797 3798 | static int tl_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_tl_view_state *s = &view->state.timeline; int rc = FSL_RC_OK; uint16_t nscroll = view->nlines - 2; | < < | 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 | static int tl_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_tl_view_state *s = &view->state.timeline; int rc = FSL_RC_OK; uint16_t nscroll = view->nlines - 2; switch (ch) { case '0': view->pos.col = 0; break; case '$': view->pos.col = MAX(s->maxx - view->ncols / 2, 0); break; |
︙ | ︙ | |||
3864 3865 3866 3867 3868 3869 3870 | !s->thread_cx.eotl) { s->thread_cx.ncommits_needed += (view->nlines - 1) - s->commits.ncommits; rc = signal_tl_thread(view, 1); } break; case 'C': { | > > | | | > > | < | < < | | > > > | | < | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | < > > > | 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 | !s->thread_cx.eotl) { s->thread_cx.ncommits_needed += (view->nlines - 1) - s->commits.ncommits; rc = signal_tl_thread(view, 1); } break; case 'C': { fsl_cx *const f = fcli_cx(); if (*s->selected_entry->commit->type != 'c') return sitrep(view, SR_ALL, "-- requires check-in artifact --"); if (!fsl_needs_ckout(f)) return sitrep(view, SR_ALL, "-- requires work tree --"); /* * XXX This is not good but I can't think of an alternative * without patching libf: fsl_ckout_changes_scan() returns a * db lock error via fsl_vfile_changes_scan() when versioned * files are modified at runtime. Clear it and notify user. */ rc = fsl_ckout_changes_scan(f); if (rc) { if (rc != FSL_RC_DB) return RC(rc, "fsl_ckout_changes_scan"); return sitrep(view, SR_ALL, "-- checkout db busy --"); } if (!fsl_ckout_has_changes(f)) return sitrep(view, SR_CLREOL | SR_UPDATE | SR_SLEEP, "-- no local changes --"); rc = reset_tags(s); if (rc) return rc; s->selected_entry->commit->diff_type = FNC_DIFF_CKOUT; } /* FALL THROUGH */ case ' ': if (*s->selected_entry->commit->type != 'c' && *s->selected_entry->commit->type != 'w') return sitrep(view, SR_ALL, "-- requires check-in or wiki artifact --"); if (s->tag.two != NULL) { rc = reset_tags(s); if (rc) return rc; } rc = tag_timeline_entry(s); if (rc) { if (rc == FSL_RC_BREAK) return FSL_RC_OK; if (rc == FSL_RC_TYPE) return sitrep(view, SR_ALL, "-- tag must be another %s artifact -- ", *s->tag.one->type == 'c' ? "check-in" : "wiki"); if (rc == FSL_RC_MISUSE) return sitrep(view, SR_ALL, "-- tag must be another %s wiki page -- ", strchr(s->tag.one->comment, ':') + 2); return rc; } rc = request_view(new_view, view, FNC_VIEW_DIFF); break; case KEY_ENTER: case '\r': rc = reset_tags(s); if (rc) return rc; rc = request_view(new_view, view, FNC_VIEW_DIFF); break; case 'b': rc = request_view(new_view, view, FNC_VIEW_BRANCH); break; case 'c': s->colour = !s->colour; |
︙ | ︙ | |||
3918 3919 3920 3921 3922 3923 3924 3925 3926 | rc = sitrep(view, SR_ALL, "-- no matching commits --"); } break; } case 't': if (s->selected_entry == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), s->selected_entry->commit->rid)) | > | | > > > > > > > > > > > | | | < < < < | < > | < < | | | | | < < < < | < | > > > > | | > > > | > > > > | > | > > > | | | | 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 | rc = sitrep(view, SR_ALL, "-- no matching commits --"); } break; } case 't': if (s->selected_entry == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), s->selected_entry->commit->rid)) return sitrep(view, SR_CLREOL | SR_UPDATE | SR_SLEEP, "-- tree requires check-in artifact --"); if (s->path) { rc = valid_path(s->path, s->selected_entry->commit->uuid); if (rc) { if (rc != FSL_RC_UNKNOWN_RESOURCE) return rc; return sitrep(view, SR_ALL, "-- '%s' not in requested tree --", s->path); } } rc = request_view(new_view, view, FNC_VIEW_TREE); break; case 'q': s->quit = 1; break; default: break; } return rc; } static int tag_timeline_entry(struct fnc_tl_view_state *s) { if (s->selected_entry->commit->diff_type == FNC_DIFF_CKOUT || s->tag.one == NULL) { s->tag.one = s->selected_entry->commit; if (s->tag.one->diff_type != FNC_DIFF_CKOUT) return FSL_RC_BREAK; } else if (*s->selected_entry->commit->type != *s->tag.one->type) return FSL_RC_TYPE; else if (s->selected_entry->commit->rid == s->tag.one->rid) { s->tag.one = NULL; /* untag selected entry */ return FSL_RC_BREAK; } if (*s->tag.one->type == 'w' && fsl_strcmp(strchr(s->tag.one->comment, ':') + 2, strchr(s->selected_entry->commit->comment, ':') + 2)) return FSL_RC_MISUSE; /* don't diff different wiki pages */ if (s->selected_entry->commit->prid != s->tag.one->rid) s->showmeta = false; if (s->selected_entry->commit->puuid != NULL) { /* initial commits have no parent, hence the check */ s->tag.ogid = fsl_strdup(s->selected_entry->commit->puuid); if (s->tag.ogid == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); } s->tag.ogrid = s->selected_entry->commit->prid; fsl_free(s->selected_entry->commit->puuid); s->selected_entry->commit->puuid = fsl_strdup(s->tag.one->uuid); if (s->selected_entry->commit->puuid == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); s->selected_entry->commit->prid = s->tag.one->rid; s->tag.two = s->selected_entry->commit; return FSL_RC_OK; } static int move_tl_cursor_down(struct fnc_view *view, uint16_t page) { struct fnc_tl_view_state *s = &view->state.timeline; struct commit_entry *first; |
︙ | ︙ | |||
4602 4603 4604 4605 4606 4607 4608 | return rc; } static int close_timeline_view(struct fnc_view *view) { struct fnc_tl_view_state *s = &view->state.timeline; | | | | | < < < < < < < < < < < < < < | 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 | return rc; } static int close_timeline_view(struct fnc_view *view) { struct fnc_tl_view_state *s = &view->state.timeline; int rc1, rc2; rc1 = join_tl_thread(s); fsl_stmt_finalize(s->thread_cx.q); rc2 = reset_tags(s); /* must be before fnc_free_commits() */ fnc_free_commits(&s->commits); free_colours(&s->colours); fsl_free(s->path); s->path = NULL; return rc1 ? rc1 : rc2; } static int join_tl_thread(struct fnc_tl_view_state *s) { void *err; int rc = 0; |
︙ | ︙ | |||
4748 4749 4750 4751 4752 4753 4754 | set_diff_opt(s); s->index.n = 0; s->index.idx = 0; s->hundex.n = 0; s->paths = paths; s->selected_entry = commit; | < < | 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 | set_diff_opt(s); s->index.n = 0; s->index.idx = 0; s->hundex.n = 0; s->paths = paths; s->selected_entry = commit; s->first_line_onscreen = 1; s->last_line_onscreen = view->nlines; s->selected_line = 1; s->f = NULL; s->view = view; s->parent_view = parent_view; s->diff_mode = mode; |
︙ | ︙ | |||
4782 4783 4784 4785 4786 4787 4788 | rc = create_diff(s); if (rc) { if (s->colour) free_colours(&s->colours); return rc; } | | | 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 | rc = create_diff(s); if (rc) { if (s->colour) free_colours(&s->colours); return rc; } view->show = show_diff_view; view->input = diff_input_handler; view->close = close_diff_view; view->grep_init = diff_grep_init; view->grep = find_next_match; return rc; } |
︙ | ︙ | |||
4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 | static void set_diff_opt(struct fnc_diff_view_state *s) { char *opt; char ch; long ctx = DEF_DIFF_CTX; int i = 0; /* Command line options. */ fnc_init.verbose ? FLAG_SET(s->diff_flags, FNC_DIFF_VERBOSE) : 0; fnc_init.proto ? FLAG_SET(s->diff_flags, FNC_DIFF_PROTOTYPE) : 0; fnc_init.ws ? FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_ALLWS) : 0; fnc_init.eol ? FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_EOLWS) : 0; fnc_init.invert ? FLAG_SET(s->diff_flags, FNC_DIFF_INVERT) : 0; | > > | 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 | static void set_diff_opt(struct fnc_diff_view_state *s) { char *opt; char ch; long ctx = DEF_DIFF_CTX; int i = 0; FLAG_SET(s->diff_flags, FNC_DIFF_STRIP_EOLCR); /* Command line options. */ fnc_init.verbose ? FLAG_SET(s->diff_flags, FNC_DIFF_VERBOSE) : 0; fnc_init.proto ? FLAG_SET(s->diff_flags, FNC_DIFF_PROTOTYPE) : 0; fnc_init.ws ? FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_ALLWS) : 0; fnc_init.eol ? FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_EOLWS) : 0; fnc_init.invert ? FLAG_SET(s->diff_flags, FNC_DIFF_INVERT) : 0; |
︙ | ︙ | |||
4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 | if (!rc && s->diff_mode == COMMIT_META) rc = write_commit_meta(s); if (!rc && s->selected_entry->diff_type == FNC_DIFF_WIKI) rc = diff_non_checkin(s); if (rc) goto end; if (s->diff_mode != COMMIT_META) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = 0; } /* | > > > > > > > > > > > > > > > > > > > > > > > > | 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 | if (!rc && s->diff_mode == COMMIT_META) rc = write_commit_meta(s); if (!rc && s->selected_entry->diff_type == FNC_DIFF_WIKI) rc = diff_non_checkin(s); if (rc) goto end; /* * diff headline label (i.e., diff id1 id2) assignment is delayed till * now because wiki parent commits are obtained in diff_non_checkin() */ if (s->selected_entry->puuid) { fsl_free(s->id1); s->id1 = fsl_strdup(s->selected_entry->puuid); if (s->id1 == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else s->id1 = NULL; /* initial commit, tag, technote, etc. */ if (s->selected_entry->uuid) { fsl_free(s->id2); s->id2 = fsl_strdup(s->selected_entry->uuid); if (s->id2 == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else s->id2 = NULL; /* local work tree */ if (s->diff_mode != COMMIT_META) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = 0; } /* |
︙ | ︙ | |||
6184 6185 6186 6187 6188 6189 6190 | fsl_free(xplus0); fsl_buffer_clear(&fbuf1); fsl_buffer_clear(&fbuf2); return rc; } static int | | < < < < < < < < < < < < < < < < | < < < | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 | fsl_free(xplus0); fsl_buffer_clear(&fbuf1); fsl_buffer_clear(&fbuf2); return rc; } static int show_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; regmatch_t *regmatch = &view->regmatch; struct fnc_colour *c = NULL; wchar_t *wcstr; char *line = NULL; size_t lidx, linesz = 0; ssize_t linelen; off_t line_offset; attr_t rx = 0; int col, wstrlen, max_lines = view->nlines; int nlines = s->nlines; int nprinted = 0, rc = FSL_RC_OK; bool selected; s->lineno = s->first_line_onscreen - 1; line_offset = s->line_offsets[s->lineno]; if (fseeko(s->f, line_offset, SEEK_SET)) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "fseeko"); werase(view->window); rc = draw_diff_headln(view); if (rc) return rc; if (--max_lines < 1) return rc; s->eof = false; while (max_lines > 0 && nprinted < max_lines) { col = wstrlen = 0; linelen = getline(&line, &linesz, s->f); |
︙ | ︙ | |||
6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 | wstandout(view->window); waddstr(view->window, "(END)"); wstandend(view->window); } return rc; } static bool screen_is_shared(struct fnc_view *view) { if (view_is_parent(view)) { if (view->child == NULL || view->child->active || !screen_is_split(view->child)) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 | wstandout(view->window); waddstr(view->window, "(END)"); wstandend(view->window); } return rc; } static int draw_diff_headln(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; wchar_t *wstr; const char *id1, *id2; char hdr[view->ncols + 1], pct[MAX_PCT_LEN]; double percent; fsl_size_t len = 0; int col, dlen, ln, n, npct, rc; attr_t rx = 0; bool wt = false; ln = s->gtl ? s->gtl : s->lineno + s->selected_line; percent = 100.00 * ln / s->nlines; npct = snprintf(pct, MAX_PCT_LEN, "%.*lf%%", percent > 99.99 ? 0 : 2, percent); if (npct < 0 || npct >= MAX_PCT_LEN) return RC(FSL_RC_RANGE, "snprintf"); /* some diffs (e.g., technote, tag) have no parent hash to display */ id1 = s->id1 ? s->id1 : "/dev/null"; /* if diffing the work tree, display work tree path (trim trailing /) */ if (s->selected_entry->diff_type == FNC_DIFF_CKOUT || fsl_strcmp(s->id1, s->id2) == 0) { id2 = fsl_cx_ckout_dir_name(fcli_cx(), &len); wt = true; } else if (s->id2) id2 = s->id2; else id2 = "/dev/null"; dlen = len; n = snprintf(hdr, view->ncols + 1, "[%d/%zu] diff %.40s %.*s", ln, s->nlines, id1, wt ? dlen - 1 : 40, id2); if (n < 0) /* ignore truncation */ return RC(FSL_RC_RANGE, "snprintf"); rc = formatln(&wstr, &n, hdr, view->ncols, 0, false); if (rc) return rc; if (screen_is_shared(view) || view->active) rx = FNC_HIGHLIGHT; wattron(view->window, rx); waddwstr(view->window, wstr); fsl_free(wstr); col = n; while (col++ < view->ncols) waddch(view->window, ' '); if (n < view->ncols - npct) mvwaddstr(view->window, 0, view->ncols - npct, pct); wattroff(view->window, rx); return FSL_RC_OK; } static bool screen_is_shared(struct fnc_view *view) { if (view_is_parent(view)) { if (view->child == NULL || view->child->active || !screen_is_split(view->child)) |
︙ | ︙ | |||
6489 6490 6491 6492 6493 6494 6495 | } static int diff_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_view *branch_view; struct fnc_diff_view_state *s = &view->state.diff; | < < < | 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 | } static int diff_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_view *branch_view; struct fnc_diff_view_state *s = &view->state.diff; char *line = NULL; ssize_t linelen; size_t linesz = 0; int nlines, i = 0, rc = FSL_RC_OK; uint16_t nscroll = view->nlines - 2; bool down = false; |
︙ | ︙ | |||
6762 6763 6764 6765 6766 6767 6768 6769 | /* FALL THROUGH */ case CTRL('k'): case '<': case ',': case 'K': if (s->parent_view == NULL) break; if (s->parent_view->vid == FNC_VIEW_TIMELINE) { | > > > > | | | | | > > > | > > > > > | > > < | | < < > | 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 | /* FALL THROUGH */ case CTRL('k'): case '<': case ',': case 'K': if (s->parent_view == NULL) break; if (s->parent_view->vid == FNC_VIEW_TIMELINE) { struct fnc_tl_view_state *ts; struct commit_entry *prev; ts = &s->parent_view->state.timeline; prev = ts->selected_entry; rc = tl_input_handler(NULL, s->parent_view, down ? KEY_DOWN : KEY_UP); if (rc) break; if (prev == ts->selected_entry) break; if (s->diff_mode == DIFF_PLAIN) s->diff_mode = COMMIT_META; rc = set_selected_commit(s, ts->selected_entry); if (rc) break; } else if (s->parent_view->vid == FNC_VIEW_BLAME) { struct fnc_blame_view_state *bs; fsl_uuid_cstr prev_id; bs = &s->parent_view->state.blame; prev_id = bs->selected_entry->uuid; rc = blame_input_handler(&view, s->parent_view, down ? KEY_DOWN : KEY_UP); if (rc) break; if (!fsl_uuidcmp(get_selected_commit_id(bs->blame.lines, bs->blame.nlines, bs->first_line_onscreen, bs->selected_line), prev_id)) break; rc = blame_input_handler(&view, s->parent_view, KEY_ENTER); if (rc) break; } s->selected_line = 1; rc = reset_diff_view(view, false); break; default: break; } return rc; } |
︙ | ︙ | |||
7218 7219 7220 7221 7222 7223 7224 | /* 3. revert ckout to apply patches; vfile scanned in cmd_stash() */ rc = revert_ckout(true, false); if (rc) { rc = RC(rc, "revert_ckout"); goto end; } | > > > > > > > > > > | | > | > > | 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 | /* 3. revert ckout to apply patches; vfile scanned in cmd_stash() */ rc = revert_ckout(true, false); if (rc) { rc = RC(rc, "revert_ckout"); goto end; } /* * XXX Older landlock versions do not allow file reparenting (i.e., * linking and renaming files into different directories). This makes * landlock unusable with our interactive stash implementation (see * apply_patch()) where patches are made to a temporary file in the * work tree root and renamed into the versioned file, which could be * in any number of project subdirectories. Disable landlock support * for the stash command until the newer landlock is ubiquitous. */ #ifndef HAVE_LANDLOCK /* with revert_ckout() finished, we can revoke root dir perms */ rc = init_unveil( ((const char *[]){ REPODIR, CKOUTDIR, P_tmpdir, gettzfile() }), ((const char *[]){ "rwc", "rwc", "rwc", "r" }), 4, true ); if (rc) goto end; #endif scx->pcx.context = s->context; scx->pcx.report = true; /* report files with changes stashed */ /* 4. apply patch of hunks selected to stash */ rc = fnc_patch(&scx->pcx, scx->patch[0]); if (rc) |
︙ | ︙ | |||
7280 7281 7282 7283 7284 7285 7286 7287 | static int select_hunks(struct fnc_view *view) { int rc = FSL_RC_OK; bool stashing = false; rc = stash_input_handler(view, &stashing); | > > | | < | | 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 | static int select_hunks(struct fnc_view *view) { int rc = FSL_RC_OK; bool stashing = false; rc = stash_input_handler(view, &stashing); if (rc && rc != FSL_RC_BREAK) return rc; if (!stashing || rc == FSL_RC_BREAK) return RC(FSL_RC_BREAK, "no hunks stashed, work tree unchanged"); return FSL_RC_OK; } /* * Iterate each hunk of changes in the local checkout, and prompt the user * for their choice with: "stash this hunk (b,m,y,n,a,k,A,K,?)? [y]" * b - scroll back (only available if hunk occupies previous page) * m - show more (only available if hunk occupies following page) |
︙ | ︙ | |||
7319 7320 7321 7322 7323 7324 7325 | uint32_t *nh; int hl, rc = FSL_RC_OK; enum stash_opt choice = NO_CHOICE; bool lastfile = false; hunks = &s->hundex; nh = &hunks->idx; | | | | | | | | 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 | uint32_t *nh; int hl, rc = FSL_RC_OK; enum stash_opt choice = NO_CHOICE; bool lastfile = false; hunks = &s->hundex; nh = &hunks->idx; in = (struct input){NULL, NULL, INPUT_ALPHA, SR_CLREOL, ""}; /* Iterate hunks and prompt user to stash or keep in ckout. */ while (*nh < hunks->n) { char ans[10]; char prompt[64]; int len; enum stash_mvmt scroll; /* * If not yet in the last file of the diff and the next hunk * to choose is in the next file, reset any sticky file choice. */ if (!lastfile && nxt && hunks->lineno[*nh] >= nxt) { nxt = 0; *in.buf = 'X'; |
︙ | ︙ | |||
7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 | s->first_line_onscreen = hunks->lineno[*nh] - 5; s->selected_line = 1; hl = s->first_line_onscreen; /* current hunk start line */ redraw: rc = view->show(view); if (rc) return rc; updatescreen(view->window, true, true); keypad(view->window, false); /* don't accept arrow keys */ last = s->last_line_onscreen; len = snprintf(prompt, sizeof(prompt), "[%u/%u] stash this hunk (", hunks->idx + 1, hunks->n); if (len < 0 || (len > 0 && (size_t)len >= sizeof(prompt))) | > > > | | | | | | < < | | | | < | < < < | > > > > > > | | > > | | | | | > | | | < | | < | | < < | < > | < | | < | | < | | | < | | | | < < | < < | < < | < > | < < | | | | 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 | s->first_line_onscreen = hunks->lineno[*nh] - 5; s->selected_line = 1; hl = s->first_line_onscreen; /* current hunk start line */ redraw: rc = view->show(view); if (rc) return rc; updatescreen(view->window, true, true); keypad(view->window, false); /* don't accept arrow keys */ memset(prompt, '\0', sizeof(prompt)); last = s->last_line_onscreen; len = snprintf(prompt, sizeof(prompt), "[%u/%u] stash this hunk (", hunks->idx + 1, hunks->n); if (len < 0 || (len > 0 && (size_t)len >= sizeof(prompt))) return RC(FSL_RC_RANGE, "snprintf"); scroll = STASH_MVMT_NONE; /* Enable 'b,m' answers if hunk occupies multiple pages. */ if (s->first_line_onscreen > hl) scroll = STASH_MVMT_UP; if (*nh < hunks->n - 1 && hunks->lineno[*nh + 1] > last) ++scroll; else if (*nh == hunks->n - 1 && last < s->nlines) ++scroll; rc = generate_prompt(ans, prompt, sizeof(prompt), scroll); if (rc) return RC(rc, "generate_prompt(%s)", STRINGIFY(scroll)); in.prompt = prompt; while (!choice && !valid_input(*in.buf, ans)) { rc = fnc_prompt_input(view, &in); if (rc) return rc; if (*in.buf == '\0') *in.buf = 'y'; else if (strlen(in.buf) > 1) *in.buf = '\0'; else if (*in.buf == '?' || *in.buf == 'H' || (int)*in.buf == (KEY_F(1))) { rc = stash_help(view, scroll); if (rc) return rc; } } if (*in.buf == 'm' || *in.buf == 'b') { /* scroll pgup/pgdn */ s->first_line_onscreen = *in.buf == 'm' ? s->last_line_onscreen : MAX(hl, s->first_line_onscreen - view->nlines + 2); *in.buf = '\0'; goto redraw; } set_choice(s, stashing, &in, hunks, nh, &nxt, &nf, &lastfile, &choice); } return FSL_RC_OK; } /* * Construct string ans of valid answers based on scroll, and generate the * corresponding prompt with the available answers from which to choose. */ static int generate_prompt(char *ans, char *prompt, size_t sz, enum stash_mvmt scroll) { char a[] = "bmynakAK"; size_t ai, pi; /* Set valid answers. */ switch (scroll) { case STASH_MVMT_UPDOWN: break; case STASH_MVMT_UP: memmove(a + 1, a + 2, 7); break; case STASH_MVMT_DOWN: memmove(a, a + 1, 8); break; case STASH_MVMT_NONE: memmove(a, a + 2, 7); break; } /* generate prompt string */ pi = strlen(prompt); for (ai = 0; a[ai]; ++ai) { prompt[pi++] = a[ai]; prompt[pi++] = ','; } if (memccpy(prompt + pi, "?)? [y] ", '\0', sz) == NULL) return RC(FSL_RC_RANGE, "memccpy"); if (memccpy(ans, a, '\0', sizeof(a)) == NULL) return RC(FSL_RC_RANGE, "memccpy"); return FSL_RC_OK; } /* * Return true if in is found in valid, else return false. * valid must be terminated with a sentinel (NULL) pointer. */ static bool valid_input(const char in, char *valid) { size_t idx; for (idx = 0; valid[idx]; ++idx) if (in == valid[idx]) return true; return false; } /* * Set or clear the corresponding bit in bitstring s->scx.stash based on the |
︙ | ︙ | |||
7532 7533 7534 7535 7536 7537 7538 | while (*nf < files->n - 1 && *nh < hunks->n - 1 && files->lineno[*nf] + 5 <= hunks->lineno[*nh + 1]) ++(*nf); ++(*nh); } | < < < < < < < < < < | 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 | while (*nf < files->n - 1 && *nh < hunks->n - 1 && files->lineno[*nf] + 5 <= hunks->lineno[*nh + 1]) ++(*nf); ++(*nh); } /* * Revert the current checkout. If renames is set, don't revert files that are * renamed with _no_ changes. If scan is set, scan for changes before reverting. */ static int revert_ckout(bool renames, bool scan) { |
︙ | ︙ | |||
8669 8670 8671 8672 8673 8674 8675 | fsl_free(h->lines); fsl_free(h); h = NULL; } fsl_free(p); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 | fsl_free(h->lines); fsl_free(h); h = NULL; } fsl_free(p); } /* * Create new stash of changes in checkout vid with stash message msg. */ static int f__stash_create(const char *msg, int vid) { fsl_cx *const f = fcli_cx(); |
︙ | ︙ | |||
9034 9035 9036 9037 9038 9039 9040 | return rc; } static int set_selected_commit(struct fnc_diff_view_state *s, struct commit_entry *entry) { | > | > > > > | > > > > > > | | 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 | return rc; } static int set_selected_commit(struct fnc_diff_view_state *s, struct commit_entry *entry) { fsl_free(s->id2); s->id2 = fsl_strdup(entry->commit->uuid); if (s->id2 == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); fsl_free(s->id1); if (entry->commit->puuid) { s->id1 = fsl_strdup(entry->commit->puuid); if (s->id1 == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); } else s->id1 = NULL; s->selected_entry = entry->commit; return FSL_RC_OK; } static void diff_grep_init(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; |
︙ | ︙ | |||
9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 | close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "fclose"); fsl_free(s->line_offsets); free_colours(&s->colours); s->line_offsets = NULL; s->nlines = 0; free_index(&s->index, true); free_index(&s->hundex, false); fsl_free(s->dlines); | > > > > | 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 | close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "fclose"); fsl_free(s->id1); s->id1 = NULL; fsl_free(s->id2); s->id2 = NULL; fsl_free(s->line_offsets); free_colours(&s->colours); s->line_offsets = NULL; s->nlines = 0; free_index(&s->index, true); free_index(&s->hundex, false); fsl_free(s->dlines); |
︙ | ︙ | |||
9211 9212 9213 9214 9215 9216 9217 | index->n = 0; fsl_free(index->lineno); index->lineno = NULL; fsl_free(index->offset); index->offset = NULL; } | | | > > | > > > | > > | | < > | < < | | | < > < | > | | > | > | 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 | index->n = 0; fsl_free(index->lineno); index->lineno = NULL; fsl_free(index->offset); index->offset = NULL; } static int reset_tags(struct fnc_tl_view_state *s) { struct timeline_tag *t = &s->tag; struct fnc_commit_artifact *c = t->two; t->one = NULL; if (!t->ogrid) return FSL_RC_OK; fsl_free(c->puuid); c->puuid = NULL; /* restore commit's original parent */ if (t->ogid != NULL) { c->puuid = fsl_strdup(t->ogid); fsl_free(t->ogid); t->ogid = NULL; if (c->puuid == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); } c->diff_type = *c->type == 'c' ? FNC_DIFF_COMMIT : FNC_DIFF_WIKI; c->prid = t->ogrid; t->ogrid = 0; t->two = NULL; s->showmeta = true; return FSL_RC_OK; } static void fnc_resizeterm(void) { struct winsize size; int cols, lines; |
︙ | ︙ | |||
9629 9630 9631 9632 9633 9634 9635 9636 | commit->uuid = fsl_strdup(commit->puuid); commit->type = fsl_strdup("checkin"); commit->diff_type = diff_type; rc = init_curses(); if (rc) goto end; /* | > > > | | | > | | 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 9675 9676 9677 9678 9679 9680 9681 9682 9683 9684 9685 9686 9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 | commit->uuid = fsl_strdup(commit->puuid); commit->type = fsl_strdup("checkin"); commit->diff_type = diff_type; rc = init_curses(); if (rc) goto end; /* XXX see comment in fnc_stash() for why landlock is disabled */ #ifndef HAVE_LANDLOCK /* * XXX revert_ckout:fsl_ckout_revert()^ walks the tree from * / to the ckout stat(2)ing every dir so we need rx on root. * Revoke root privileges after returning from revert_ckout(). * ^fsl__vfile_to_ckout:fsl_mkdir_for_file:fsl_dir_check() */ rc = init_unveil(((const char *[]){"/", REPODIR, CKOUTDIR, P_tmpdir, gettzfile()}), ((const char *[]){"rx", "rwc", "rwc", "rwc", "r"}), 5, false); if (rc) goto end; #endif view = view_open(0, 0, 0, 0, FNC_VIEW_DIFF); if (view == NULL) { rc = RC(FSL_RC_ERROR, "view_open"); goto end; } rc = open_diff_view(view, commit, NULL, NULL, diff_mode); if (rc) goto end; rc = show_diff_view(view); if (rc) goto end; rc = fnc_stash(view); if (rc) goto end; rc = fsl_vfile_changes_scan(f, rid, FSL_VFILE_CKSIG_HASH); end: |
︙ | ︙ | |||
9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 | */ static int valid_path(const char *path, const char *sym) { fsl_cx *const f = fcli_cx(); fsl_deck d = fsl_deck_empty; const fsl_card_F *cf; int rc; bool valid = false; | > > | > > > > | 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 | */ static int valid_path(const char *path, const char *sym) { fsl_cx *const f = fcli_cx(); fsl_deck d = fsl_deck_empty; const fsl_card_F *cf; fsl_uuid_str id; fsl_id_t rid; int rc; bool valid = false; rc = fsl_sym_to_uuid(f, sym, FSL_SATYPE_CHECKIN, &id, &rid); if (rc) goto end; rc = fsl_deck_load_rid(f, &d, rid, FSL_SATYPE_CHECKIN); if (rc) goto end; valid = !!(cf = fsl_deck_F_search(&d, path)); if (valid) goto end; |
︙ | ︙ | |||
9873 9874 9875 9876 9877 9878 9879 | valid = true; } while (cf && !valid); end: fsl_deck_finalize(&d); if (!valid && !rc) | | | > | 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 | valid = true; } while (cf && !valid); end: fsl_deck_finalize(&d); if (!valid && !rc) rc = RC(FSL_RC_UNKNOWN_RESOURCE, "path not found in [%S]: %s", id, path); fsl_free(id); return rc; } static int browse_commit_tree(struct fnc_view **new_view, int start_col, int start_ln, struct commit_entry *entry, const char *path) { |
︙ | ︙ | |||
10253 10254 10255 10256 10257 10258 10259 | te->idx = i++; } (*tree)->nentries = i; return FSL_RC_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < | 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 | te->idx = i++; } (*tree)->nentries = i; return FSL_RC_OK; } /* * This routine inserts nodes into the doubly-linked repository tree. Each * path component of path (i.e., tokens delimited by '/') becomes a node in * tree. The final path component of each segment is the node's .basename, and * its full repository relative path its .path. All files in a given directory * will comprise the directory node's .children list, and each file node's * .sibling list; said directory will be each file node's .parent_dir. The |
︙ | ︙ | |||
11287 11288 11289 11290 11291 11292 11293 | return rc; opt = fcli_next_arg(true); if (opt == NULL || fnc_init.lsconf) { if (fnc_init.unset) return RC(FSL_RC_MISSING_INFO, "-u|--unset requires <setting>"); | | | 11306 11307 11308 11309 11310 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 | return rc; opt = fcli_next_arg(true); if (opt == NULL || fnc_init.lsconf) { if (fnc_init.unset) return RC(FSL_RC_MISSING_INFO, "-u|--unset requires <setting>"); return fnc_conf_lsopt(fnc_init.lsconf); } setid = fnc_conf_str2enum(opt); if (!setid) return RC(FSL_RC_NOT_FOUND, "invalid setting: %s", opt); value = fcli_next_arg(true); |
︙ | ︙ | |||
11329 11330 11331 11332 11333 11334 11335 | last = (value = fnc_conf_getopt(idx, true)) ? idx : last; maxlen = MAX(fsl_strlen(fnc_opt_name[idx]), maxlen); fsl_free(value); } if (!last && !all) { f_out("No user-defined settings: " | | | 11348 11349 11350 11351 11352 11353 11354 11355 11356 11357 11358 11359 11360 11361 11362 | last = (value = fnc_conf_getopt(idx, true)) ? idx : last; maxlen = MAX(fsl_strlen(fnc_opt_name[idx]), maxlen); fsl_free(value); } if (!last && !all) { f_out("No user-defined settings: " "'%s config --ls' for list of available settings.", fcli_progname()); return 0; } for (idx = FNC_START_SETTINGS + 1; idx < FNC_EOF_SETTINGS; ++idx) { value = fnc_conf_getopt(idx, true); if (value || all) |
︙ | ︙ | |||
14008 14009 14010 14011 14012 14013 14014 | #endif /* * Similar to unveil(), grant read and write permissisions to the repo and * ckout files, and create permissions to the ckout, repo, and tmp dirs. */ static int | | | 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 14039 14040 14041 | #endif /* * Similar to unveil(), grant read and write permissisions to the repo and * ckout files, and create permissions to the ckout, repo, and tmp dirs. */ static int init_landlock(const char **paths, const char **perms, const int n) { #define LANDLOCK_ACCESS_DIR (LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_REMOVE_FILE | \ LANDLOCK_ACCESS_FS_READ_DIR | \ LANDLOCK_ACCESS_FS_MAKE_REG) /* |
︙ | ︙ |