Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.10 To 0.9
2022-03-23 14:12 | Bump version number: 0.11 (check-in: 96169cac63 user: mark tags: trunk) | |
2022-03-23 14:07 | CHANGES for 0.10 (check-in: dc8f00b193 user: mark tags: trunk, 0.10) | |
2022-03-22 14:54 | Expand diff view C-{j,k} key maps to navigate parent blame view. (check-in: d05828fbb7 user: mark tags: trunk) | |
2022-03-04 05:17 | Bump version number: 0.10 (check-in: a761c1f066 user: mark tags: trunk) | |
2022-03-04 05:14 | CHANGES for 0.9 (check-in: aef65fd1a9 user: mark tags: trunk, 0.9) | |
2022-03-04 04:53 | Plug small memleak when interactively changing diff format. (check-in: d64682655a user: mark tags: trunk) | |
Changes to CHANGES.md.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 | **fnc 0.9** 2022-03-04 - Add blame command `--line` option to open annotated file at the specified line - merge upstream libfossil changes that eliminate gcc compiler warnings - adopt libfossil diff v1 implementation into fnc tree to replace v2 API - refactor diff implementation to comport with code style - fix `--whitespace` option (`w` keymap) diffv2 regression from 0.7 [105123b40e] |
︙ | ︙ |
Changes to README.md.
1 2 | # README | | | 1 2 3 4 5 6 7 8 9 10 | # README # fnc 0.9 ## An interactive ncurses browser for [Fossil][0] repositories. `fnc` uses [libfossil][1] to create a [`fossil ui`][2] experience in the terminal. Tested and confirmed to run on the following amd64 systems (additional platforms |
︙ | ︙ | |||
23 24 25 26 27 28 29 | # Install * **OpenBSD** - `doas pkg_add fnc` * **macOS** - `sudo port install fnc` | < < < < | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # Install * **OpenBSD** - `doas pkg_add fnc` * **macOS** - `sudo port install fnc` * **[Download](/uv/download.html)** and install the binary on your path # Build 1. clone the repository - `fossil clone https://fnc.bsdbox.org` 2. move into the repository checkout - `cd fnc` |
︙ | ︙ |
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.9 # FLAGS NEEDED TO BUILD SQLITE3 SQLITE_CFLAGS = ${CFLAGS} -Wall -Werror -Wno-sign-compare -pedantic -std=c99 \ -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ |
︙ | ︙ | |||
63 64 65 66 67 68 69 | ${CC} ${FNC_CFLAGS} -c $< -o $@ src/fnc: src/fnc.o src/diff.o lib/libfossil.o lib/sqlite3.o fnc.bld.mk ${CC} -o $@ src/fnc.o src/diff.o lib/libfossil.o lib/sqlite3.o \ ${FNC_LDFLAGS} install: | < | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | ${CC} ${FNC_CFLAGS} -c $< -o $@ src/fnc: src/fnc.o src/diff.o lib/libfossil.o lib/sqlite3.o fnc.bld.mk ${CC} -o $@ src/fnc.o src/diff.o lib/libfossil.o lib/sqlite3.o \ ${FNC_LDFLAGS} install: install -s -m 0755 src/fnc ${PREFIX}/bin/fnc install -m 0644 src/fnc.1 ${PREFIX}${MANDIR}/man1/fnc.1 uninstall: rm -f ${PREFIX}/bin/fnc ${PREFIX}${MANDIR}/man1/fnc.1 clean: rm -f lib/*.o src/*.o src/fnc release: clean tar czvf ../fnc-${VERSION}.tgz -C .. fnc-${VERSION} .PHONY: clean release |
Changes to include/settings.h.
︙ | ︙ | |||
16 17 18 19 20 21 22 | #define GEN_ENUM_SYM(pfx, id) pfx##_##id #define GEN_ENUM(name, pfx, info) \ enum name { info(pfx, GEN_ENUM_SYM) }; #define GEN_STR_SYM(pfx, id) #pfx"_"#id #define GEN_STR(name, pfx, info) \ | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #define GEN_ENUM_SYM(pfx, id) pfx##_##id #define GEN_ENUM(name, pfx, info) \ enum name { info(pfx, GEN_ENUM_SYM) }; #define GEN_STR_SYM(pfx, id) #pfx"_"#id #define GEN_STR(name, pfx, info) \ static const char *name[] = { info(pfx, GEN_STR_SYM) }; /* * All configurable fnc settings, which can be stored in either the fossil(1) * repository (e.g., ./repo.fossil) or shell envvars with `export SETTING=val`. */ #define USER_OPTIONS(pfx, _) \ _(pfx, START_SETTINGS), \ |
︙ | ︙ | |||
42 43 44 45 46 47 48 | _(pfx, COLOUR_TREE_EXEC), \ _(pfx, COLOUR_BRANCH_OPEN), \ _(pfx, COLOUR_BRANCH_CLOSED), \ _(pfx, COLOUR_BRANCH_CURRENT), \ _(pfx, COLOUR_BRANCH_PRIVATE), \ _(pfx, COLOUR_HL_LINE), \ _(pfx, COLOUR_HL_SEARCH), \ | < < | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | _(pfx, COLOUR_TREE_EXEC), \ _(pfx, COLOUR_BRANCH_OPEN), \ _(pfx, COLOUR_BRANCH_CLOSED), \ _(pfx, COLOUR_BRANCH_CURRENT), \ _(pfx, COLOUR_BRANCH_PRIVATE), \ _(pfx, COLOUR_HL_LINE), \ _(pfx, COLOUR_HL_SEARCH), \ _(pfx, VIEW_SPLIT_MODE), \ _(pfx, VIEW_SPLIT_WIDTH), \ _(pfx, VIEW_SPLIT_HEIGHT), \ _(pfx, EOF_SETTINGS) #define LINE_ATTR_ENUM(pfx, _) \ _(pfx, AUTO), \ |
︙ | ︙ | |||
91 92 93 94 95 96 97 | _(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) #define GEN_ENUMS(name, pfx, info) GEN_ENUM(name, pfx, info) ENUM_INFO(GEN_ENUMS) | > > > > > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 | _(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) #define GEN_ENUMS(name, pfx, info) GEN_ENUM(name, pfx, info) ENUM_INFO(GEN_ENUMS) #define STR_INFO(_) \ _(fnc_opt_name, FNC, USER_OPTIONS) #define GEN_STRINGS(name, pfx, info) GEN_STR(name, pfx, info) STR_INFO(GEN_STRINGS) |
Added signify/fnc-08-release.pub.
> > | 1 2 | untrusted comment: fnc 0.8 public key RWRvIlnJtmD7IOR9LK/Ed1KrC+OsoACBv6wlQvDm0d117Lbvn48ez+2L |
Deleted signify/fnc-10-release.pub.
|
| < < |
Changes to src/diff.c.
︙ | ︙ | |||
1300 1301 1302 1303 1304 1305 1306 | /* * Extract column width for side-by-side diff from flags. Return appropriate * default if no width is specified. */ int sbsdiff_width(uint64_t flags) { | | | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 | /* * Extract column width for side-by-side diff from flags. Return appropriate * default if no width is specified. */ int sbsdiff_width(uint64_t flags) { int w = (flags & FNC_DIFF_WIDTH_MASK - 0xf) / FNC_DIFF_CONTEXT_MASK; if (!w) w = 80; return w; } |
︙ | ︙ |
Changes to src/fnc.1.
︙ | ︙ | |||
42 43 44 45 46 47 48 | .Op Fl R Ar path .Op Fl T Ar tag .Op Fl t Ar type .Op Fl u Ar user .Op Ar path .Nm .Cm diff | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | .Op Fl R Ar path .Op Fl T Ar tag .Op Fl t Ar type .Op Fl u Ar user .Op Ar path .Nm .Cm diff .Op Fl CilPqsw .Op Fl R Ar path .Op Fl x Ar number .Op Ar artifact1 Op Ar artifact2 .Op Ar path ... .Nm .Cm tree .Op Fl C |
︙ | ︙ | |||
405 406 407 408 409 410 411 | Find the next commit that matches the current search term. The search will continue until either a match is found or the earliest commit on the timeline is consumed. .It Cm N Find the previous commit that matches the current search term. The search will continue until either a match is found or the latest commit on the timeline is consumed. | < < < < < < < < < | < | | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | Find the next commit that matches the current search term. The search will continue until either a match is found or the earliest commit on the timeline is consumed. .It Cm N Find the previous commit that matches the current search term. The search will continue until either a match is found or the latest commit on the timeline is consumed. .El .Tg di .It Cm diff Oo Fl C | -no-colour Oc Oo Fl h | -help Oc Oo Fl i | -invert Oc \ Oo Fl l | -line-numbers Oc Oo Fl P | -no-prototype Oc Oo Fl q | -quiet Oc \ Oo Fl R | -repo Ar path Oc Oo Fl s | -sbs Oc Oo Fl w | -whitespace Oc \ Oo Fl x | -context Ar n Oc Oo Ar artifact1 Oo Ar artifact2 Oc Oc Op Ar path ... .Dl Pq alias: Cm di Display the differences between two repository artifacts, or between the local changes on disk and a given commit. If neither .Ar artifact1 nor .Ar artifact2 are specified, |
︙ | ︙ | |||
482 483 484 485 486 487 488 | be toggled with the .Sy L diff view key binding. .It Fl P , -no-prototype Disable chunk header display of which function or scope each change is in, which is enabled by default. The heuristic will produce reliable results for all C-like languages | | | < < < < < < > > > | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | be toggled with the .Sy L diff view key binding. .It Fl P , -no-prototype Disable chunk header display of which function or scope each change is in, which is enabled by default. The heuristic will produce reliable results for all C-like languages .Pq e.g., Java, Python, JavaScript, Rust ; however, Lisps and non-source code .Pq e.g., Markdown, reStructuredText will return meaningless results. This option can be toggled in-session with the .Sy p key binding as documented below. .Po Mutually exclusive with .Fl l , -line-numbers .Pc .It Fl q , -quiet Disable verbose output; that is, do not output complete content of newly added or deleted files, which are displayed by default. Verbosity can also be toggled with the .Sy v key binding as documented below. .It Fl R , -repo Ar path |
︙ | ︙ | |||
524 525 526 527 528 529 530 | .Sy artifact operands. .It Fl s , -sbs Display a side-by-side formatted diff. As documented below, this option can also be toggled in-session with the .Sy s key binding. | < < < < < | | < < < < < < < < < < < | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 | .Sy artifact operands. .It Fl s , -sbs Display a side-by-side formatted diff. As documented below, this option can also be toggled in-session with the .Sy s key binding. .It Fl w , -whitespace Ignore whitespace-only changes when displaying the diff. .It Fl x , -context Ar n Set .Ar n context lines to be shown in the diff. By default, 5 context lines are shown. Negative values are a no-op. .El .Pp Key bindings for .Cm fnc diff are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move the selection cursor down one line. .It Cm Arrow-up, k |
︙ | ︙ | |||
590 591 592 593 594 595 596 | .It Cm C-u Scroll diff view half a page upwards in the buffer. .It Cm G, End Scroll to the end of the view (i.e., last line of diff output). .It Cm gg, Home Scroll to the top of the view (i.e., first line of diff output). .It Cm C-k, K, <, \&, | | < | | < < | < | | < < < < < < < < < < < < < | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | .It Cm C-u Scroll diff view half a page upwards in the buffer. .It Cm G, End Scroll to the end of the view (i.e., last line of diff output). .It Cm gg, Home Scroll to the top of the view (i.e., first line of diff output). .It Cm C-k, K, <, \&, Move up the .Cm timeline to the previous (i.e., newer) commit and display its diff. (Only available if the diff was accessed from the timeline.) .It Cm C-j, J, >, \&. Move down the .Cm timeline to the next (i.e., older) commit and display its diff. (Only available if the diff was accessed from the timeline.) .It Cm \&-, \&_ Decrease the number of context lines shown in diff output. .It Cm \&=, \&+ Increase the number of context lines shown in diff output. .It Cm # Toggle display of diff view line numbers. .It Cm @ Open prompt to enter line number and navigate to that line in the view. .It Cm b Open and populate branch view with all repository branches. .It Cm c Toggle coloured diff output. On supported terminals, .Nm will default to displaying changes and diff metadata in colour. .It Cm F Open prompt to enter file number and navigate to that file in the diff. .It Cm i Toggle inversion of diff output. .It Cm L Toggle display of file line numbers in the diff. .It Cm p In the diff chunk header, toggle display of which function each change is in; for example: .Sy @@ -2360,10 +2361,11 @@ draw_commits(struct fnc_view *view) .It Cm S Toggle display of a side-by-side formatted diff. .It Cm v |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | .It Ev FNC_VIEW_SPLIT_WIDTH Minimum width of the child view when opening in a vertical split. Currently a no-op. Default: .Qq 80 . .El .Pp | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | .It Ev FNC_VIEW_SPLIT_WIDTH Minimum width of the child view when opening in a vertical split. Currently a no-op. Default: .Qq 80 . .El .Pp .Nm displays coloured output by default in supported terminals. Each colour object identified below can be defined by either exporting environment variables .Po e.g., .Cm export FNC_COLOUR_COMMIT=red .Pc , or with |
︙ | ︙ |
Changes to src/fnc.c.
︙ | ︙ | |||
74 75 76 77 78 79 80 | #include <langinfo.h> #include "libfossil.h" #include "diff.h" #define FNC_VERSION VERSION /* cf. Makefile */ | < < < < < | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #include <langinfo.h> #include "libfossil.h" #include "diff.h" #define FNC_VERSION VERSION /* cf. Makefile */ /* Utility macros. */ #define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) #define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) #define ABS(_n) ((_n) >= 0 ? (_n) : -(_n)) #ifndef CTRL #define CTRL(key) ((key) & 037) /* CTRL+<key> input. */ #endif |
︙ | ︙ | |||
154 155 156 157 158 159 160 | */ #ifdef __OpenBSD__ # ifndef STAILQ_HEAD # define STAILQ SIMPLEQ # endif /* STAILQ_HEAD */ #endif /* OpenBSD */ | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | */ #ifdef __OpenBSD__ # ifndef STAILQ_HEAD # define STAILQ SIMPLEQ # endif /* STAILQ_HEAD */ #endif /* OpenBSD */ #if defined __linux__ # ifndef strlcat # define strlcat(_d, _s, _sz) fsl_strlcat(_d, _s, _sz) # endif /* strlcat */ # ifndef strlcpy # define strlcpy(_d, _s, _sz) fsl_strlcpy(_d, _s, _sz) # endif /* strlcpy */ #endif /* __linux__ */ |
︙ | ︙ | |||
211 212 213 214 215 216 217 | const char *glob; /* Only load commits containing glob */ bool utc; /* Display UTC sans user local time. */ /* Blame options. */ const char *lineno; /* Line to open blame view. */ /* Diff options. */ | < < < | < | < | | | | < | < | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | const char *glob; /* Only load commits containing glob */ bool utc; /* Display UTC sans user local time. */ /* Blame options. */ const char *lineno; /* Line to open blame view. */ /* Diff options. */ const char *context; /* Number of context lines. */ bool sbs; /* Display side-by-side diff. */ bool ws; /* Ignore whitespace-only changes. */ bool nocolour; /* Disable colour in diff output. */ bool quiet; /* Disable verbose diff output. */ bool invert; /* Toggle inverted diff output. */ bool showln; /* Display line numbers in diff. */ /* Branch options. */ const char *before; /* Last branch change before date. */ const char *after; /* Last branch change after date. */ const char *sort; /* Lexicographical, MRU, open/closed. */ bool closed; /* Show only closed branches. */ bool open; /* Show only open branches */ bool noprivate; /* Don't show private branches. */ /* Config options. */ bool lsconf; /* List all defined settings. */ bool unset; /* Unset the specified setting. */ /* Command line flags and help. */ fcli_help_info fnc_help; /* Global help. */ fcli_cliflag cliflags_global[3]; /* Global options. */ fcli_command cmd_args[7]; /* App commands. */ fcli_cliflag cliflags_timeline[13]; /* Timeline options. */ fcli_cliflag cliflags_diff[10]; /* Diff options. */ fcli_cliflag cliflags_tree[5]; /* Tree options. */ fcli_cliflag cliflags_blame[8]; /* Blame options. */ fcli_cliflag cliflags_branch[11]; /* Branch options. */ fcli_cliflag cliflags_config[5]; /* Config options. */ } fnc_init = { NULL, /* cmdarg copy of argv[1] to aid usage/error report. */ NULL, /* sym(bolic name) of commit to open defaults to tip. */ NULL, /* path for tree to open or timeline to find commits. */ 0, /* err fnc error state. */ false, /* hflag if --help is requested. */ false, /* vflag if --version is requested. */ false, /* reverse branch sort/annotation defaults to off. */ {NULL, 0}, /* filter_types defaults to indiscriminate. */ {0}, /* nrecords defaults to all commits. */ NULL, /* filter_tag defaults to indiscriminate. */ NULL, /* filter_branch defaults to indiscriminate. */ NULL, /* filter_user defaults to indiscriminate. */ NULL, /* filter_type temp placeholder for filter_types cb. */ NULL, /* glob filter defaults to off; all commits are shown */ false, /* utc defaults to off (i.e., show user local time). */ NULL, /* lineno default: open blame at the first line. */ NULL, /* context defaults to five context lines. */ false, /* sbs diff defaults to false (show unified diff). */ false, /* ws defaults to acknowledge whitespace. */ false, /* nocolour defaults to off (i.e., use diff colours). */ false, /* quiet defaults to off (i.e., verbose diff is on). */ false, /* invert diff defaults to off. */ false, /* showln in diff defaults to off. */ NULL, /* before defaults to any time. */ NULL, /* after defaults to any time. */ NULL, /* sort by MRU or open/closed (dflt: lexicographical) */ false, /* closed only branches is off (defaults to all). */ false, /* open only branches is off by (defaults to all). */ false, /* noprivate is off (default to show private branch). */ false, /* do not list all defined settings by default. */ |
︙ | ︙ | |||
378 379 380 381 382 383 384 | "Display diff command help and usage."), FCLI_FLAG_BOOL("i", "invert", &fnc_init.invert, "Invert difference between artifacts. Inversion can also be " "toggled\n with the 'i' key binding in diff view."), FCLI_FLAG_BOOL("l", "line-numbers", &fnc_init.showln, "Show file line numbers in diff output. Line numbers can also be " "toggled\n with the 'L' key binding in diff view."), | < < < | < < < < | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | "Display diff command help and usage."), FCLI_FLAG_BOOL("i", "invert", &fnc_init.invert, "Invert difference between artifacts. Inversion can also be " "toggled\n with the 'i' key binding in diff view."), FCLI_FLAG_BOOL("l", "line-numbers", &fnc_init.showln, "Show file line numbers in diff output. Line numbers can also be " "toggled\n with the 'L' key binding in diff view."), FCLI_FLAG_BOOL("q", "quiet", &fnc_init.quiet, "Disable verbose diff output; that is, do not output complete" " content\n of newly added or deleted files. Verbosity can also" " be toggled with\n the 'v' key binding in diff view."), FCLI_FLAG_CSTR("R", "repo", "<path>", NULL, "Use the fossil(1) repository located at <path> for this diff\n " "invocation."), FCLI_FLAG_BOOL("s", "sbs", &fnc_init.sbs, "Display a side-by-side, rather than the default unified, diff. " "This\n option can alse be toggled with the 'S' key binding in " "diff view."), FCLI_FLAG_BOOL("w", "whitespace", &fnc_init.ws, "Ignore whitespace-only changes when displaying diff. This option " "can\n also be toggled with the 'w' key binding in diff view."), FCLI_FLAG("x", "context", "<n>", &fnc_init.context, "Show <n> context lines when displaying diff; <n> is capped at 64." "\n Negative values are a no-op."), fcli_cliflag_empty_m |
︙ | ︙ | |||
545 546 547 548 549 550 551 | }; enum fnc_search_state { SEARCH_WAITING, SEARCH_CONTINUE, SEARCH_COMPLETE, SEARCH_NO_MATCH, | < < | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | }; enum fnc_search_state { SEARCH_WAITING, SEARCH_CONTINUE, SEARCH_COMPLETE, SEARCH_NO_MATCH, SEARCH_FOR_END }; enum fnc_diff_type { FNC_DIFF_CKOUT, FNC_DIFF_COMMIT, FNC_DIFF_BLOB, FNC_DIFF_WIKI }; struct input { void *data; char *prompt; enum input_type type; int flags; #define SR_CLREOL 1 << 0 #define SR_UPDATE 1 << 1 #define SR_SLEEP 1 << 2 #define SR_RESET 1 << 3 char buf[BUFSIZ]; long ret; }; struct fnc_colour { STAILQ_ENTRY(fnc_colour) entries; regex_t regex; |
︙ | ︙ | |||
738 739 740 741 742 743 744 | size_t *lineno; off_t *offset; uint32_t n; uint32_t idx; }; struct fnc_diff_view_state { | | | 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | size_t *lineno; off_t *offset; uint32_t n; uint32_t idx; }; struct fnc_diff_view_state { struct fnc_view *timeline_view; struct fnc_commit_artifact *selected_entry; struct fnc_pathlist_head *paths; fsl_buffer buf; struct fnc_colours colours; struct index index; FILE *f; fsl_uuid_str id1; |
︙ | ︙ | |||
767 768 769 770 771 772 773 | enum line_type *dlines; enum line_attr sline; off_t *line_offsets; bool eof; bool colour; bool showmeta; bool showln; | < | 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | enum line_type *dlines; enum line_attr sline; off_t *line_offsets; bool eof; bool colour; bool showmeta; bool showln; }; TAILQ_HEAD(fnc_parent_trees, fnc_parent_tree); struct fnc_tree_view_state { /* Parent trees of the- */ struct fnc_parent_trees parents; /* -current subtree. */ struct fnc_repository_tree *repo; /* The repository tree. */ struct fnc_tree_object *root; /* Top level repo tree. */ |
︙ | ︙ | |||
1020 1021 1022 1023 1024 1025 1026 | static int tl_search_next(struct fnc_view *); static bool find_commit_match(struct fnc_commit_artifact *, regex_t *); static int init_diff_view(struct fnc_view **, int, int, struct fnc_commit_artifact *, struct fnc_view *, bool); static int open_diff_view(struct fnc_view *, | | > | < < | 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | static int tl_search_next(struct fnc_view *); static bool find_commit_match(struct fnc_commit_artifact *, regex_t *); static int init_diff_view(struct fnc_view **, int, int, struct fnc_commit_artifact *, struct fnc_view *, bool); static int open_diff_view(struct fnc_view *, struct fnc_commit_artifact *, int, bool, bool, bool, bool, bool, struct fnc_view *, bool, struct fnc_pathlist_head *); static void show_diff_status(struct fnc_view *); static int create_diff(struct fnc_diff_view_state *); static int create_changeset(struct fnc_commit_artifact *); static int write_commit_meta(struct fnc_diff_view_state *); static int countlines(const char *); static int wrapline(char *, fsl_size_t, struct fnc_diff_view_state *, off_t *); |
︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 | static int fnc_date_to_mtime(double *, const char *, int); static int cook_input(char *, int, WINDOW *); static int sitrep(struct fnc_view *, const char *, int); static char *fnc_strsep (char **, const char *); static bool fnc_str_has_upper(const char *); static int fnc_make_sql_glob(char **, char **, const char *, bool); | < < < < < < | 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | static int fnc_date_to_mtime(double *, const char *, int); static int cook_input(char *, int, WINDOW *); static int sitrep(struct fnc_view *, const char *, int); static char *fnc_strsep (char **, const char *); static bool fnc_str_has_upper(const char *); static int fnc_make_sql_glob(char **, char **, const char *, bool); static int init_unveil(const char *, const char *, bool); static int init_landlock(const char **, const int); 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); static void free_colours(struct fnc_colours *); |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_timeline_view(&v, 0, 0, rid, path, glob); if (!rc) rc = view_loop(v); end: fsl_free(glob); | > > > | 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) goto end; rc = init_timeline_view(&v, 0, 0, rid, path, glob); if (!rc) rc = view_loop(v); end: fsl_free(glob); |
︙ | ︙ | |||
1814 1815 1816 1817 1818 1819 1820 | if (fnc_init.filter_branch) { rc = fnc_make_sql_glob(&op, &str, fnc_init.filter_branch, !fnc_str_has_upper(fnc_init.filter_branch)); if (rc) goto end; idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname %q 'sym-%q'" | < < | 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 | if (fnc_init.filter_branch) { rc = fnc_make_sql_glob(&op, &str, fnc_init.filter_branch, !fnc_str_has_upper(fnc_init.filter_branch)); if (rc) goto end; idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname %q 'sym-%q'" " ORDER BY tagid DESC", op, str); if (idtag) { rc = fsl_buffer_appendf(&sql, " AND EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%"FSL_ID_T_PFMT " AND tagtype > 0 AND rid=blob.rid)", idtag); if (rc) |
︙ | ︙ | |||
2497 2498 2499 2500 2501 2502 2503 | type = fsl_strdup(s->selected_entry->commit->type); } if (tcx->ncommits_needed > 0 && !tcx->eotl) { if ((idxstr = fsl_mprintf(" [%d/%d] %s", entry ? entry->idx + 1 : 0, s->commits.ncommits, (view->searching && !view->search_status) ? | < | | < < | < | < < < < | 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 | type = fsl_strdup(s->selected_entry->commit->type); } if (tcx->ncommits_needed > 0 && !tcx->eotl) { if ((idxstr = fsl_mprintf(" [%d/%d] %s", entry ? entry->idx + 1 : 0, s->commits.ncommits, (view->searching && !view->search_status) ? "searching..." : "loading...")) == NULL) { rc = RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); goto end; } } else { if (view->searching) { if (view->search_status == SEARCH_COMPLETE) search_str = "no more matches"; else if (view->search_status == SEARCH_NO_MATCH) search_str = "no matches found"; else if (view->search_status == SEARCH_WAITING) search_str = "searching..."; } if ((idxstr = fsl_mprintf("%s [%d/%d] %s", !fsl_strcmp(uuid, s->curr_ckout_uuid) ? " [current]" : "", entry ? entry->idx + 1 : 0, s->commits.ncommits, search_str ? search_str : (branch ? branch : ""))) == NULL) { |
︙ | ︙ | |||
2977 2978 2979 2980 2981 2982 2983 | switch (ch) { case '\t': rc = cycle_view(view); break; case KEY_F(1): case 'H': case '?': | | < < < < | 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 | switch (ch) { case '\t': rc = cycle_view(view); break; case KEY_F(1): case 'H': case '?': help(view); break; case 'q': if (view->parent && view->parent->vid == FNC_VIEW_TIMELINE && view->mode == VIEW_SPLIT_HRZN) { /* May need more commits to fill fullscreen. */ rc = request_tl_commits(view->parent); view->parent->mode = VIEW_SPLIT_NONE; |
︙ | ︙ | |||
3110 3111 3112 3113 3114 3115 3116 | {" j,<Down> ", " ❬↓❭❬j❭ "}, {" C-b,PgUp ", " ❬C-b❭❬PgUp❭ "}, {" C-f,PgDn ", " ❬C-f❭❬PgDn❭ "}, {" C-u, ", " ❬C-u❭ "}, {" C-d, ", " ❬C-d❭ "}, {" gg,Home ", " ❬gg❭❬Home❭ "}, {" G,End ", " ❬G❭❬End❭ "}, | < < < < < > > > > < < > > > > | 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 | {" j,<Down> ", " ❬↓❭❬j❭ "}, {" C-b,PgUp ", " ❬C-b❭❬PgUp❭ "}, {" C-f,PgDn ", " ❬C-f❭❬PgDn❭ "}, {" C-u, ", " ❬C-u❭ "}, {" C-d, ", " ❬C-d❭ "}, {" gg,Home ", " ❬gg❭❬Home❭ "}, {" G,End ", " ❬G❭❬End❭ "}, {" Tab ", " ❬TAB❭ "}, {" c ", " ❬c❭ "}, {" f ", " ❬f❭ "}, {" / ", " ❬/❭ "}, {" n ", " ❬n❭ "}, {" N ", " ❬N❭ "}, {" q ", " ❬q❭ "}, {" Q ", " ❬Q❭ "}, {""}, {""}, /* Timeline */ {" <,, ", " ❬<❭❬,❭ "}, {" >,. ", " ❬>❭❬.❭ "}, {" Enter ", " ❬Enter❭ "}, {" Space ", " ❬Space❭ "}, {" b ", " ❬b❭ "}, {" C ", " ❬C❭ "}, {" F ", " ❬F❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" $ ", " ❬$❭ "}, {" 0 ", " ❬0❭ "}, {" C-e ", " ❬C-e❭ "}, {" C-y ", " ❬C-y❭ "}, {" C-n ", " ❬C-n❭ "}, {" C-p ", " ❬C-p❭ "}, {" l<Right> ", " ❬l❭❬→❭ "}, {" h<Left> ", " ❬h❭❬←❭ "}, {" b ", " ❬b❭ "}, {" F ", " ❬F❭ "}, {" i ", " ❬i❭ "}, {" L ", " ❬L❭ "}, {" p ", " ❬p❭ "}, {" S ", " ❬S❭ "}, {" v ", " ❬v❭ "}, {" w ", " ❬w❭ "}, {" -,_ ", " ❬-❭❬_❭ "}, {" +,= ", " ❬+❭❬=❭ "}, {" C-k,K,<,, ", " ❬C-k❭❬K❭❬<❭❬,❭ "}, {" C-j,J,>,. ", " ❬C-j❭❬J❭❬>❭❬.❭ "}, {""}, {""}, /* Tree */ {" l,Enter,<Right> ", " ❬→❭❬l❭❬Enter❭ "}, {" h,<BS>,<Left> ", " ❬←❭❬h❭❬⌫❭ "}, {" b ", " ❬b❭ "}, {" d ", " ❬d❭ "}, {" i ", " ❬i❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Blame */ {" Space ", " ❬Space❭ "}, {" Enter ", " ❬Enter❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" $ ", " ❬$❭ "}, {" 0 ", " ❬0❭ "}, {" l<Right> ", " ❬l❭❬→❭ "}, {" h<Left> ", " ❬h❭❬←❭ "}, {" b ", " ❬b❭ "}, {" p ", " ❬p❭ "}, {" B ", " ❬B❭ "}, {" T ", " ❬T❭ "}, {""}, {""}, /* Branch */ {" Enter,Space ", " ❬Enter❭❬Space❭ "}, |
︙ | ︙ | |||
3198 3199 3200 3201 3202 3203 3204 | "Move selection cursor or page down one line", "Scroll view up one page", "Scroll view down one page", "Scroll view up one half page", "Scroll view down one half page", "Jump to first line or start of the view", "Jump to last line or end of the view", | < < < < < > > > > < < | | > > > > | 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 | "Move selection cursor or page down one line", "Scroll view up one page", "Scroll view down one page", "Scroll view up one half page", "Scroll view down one half page", "Jump to first line or start of the view", "Jump to last line or end of the view", "Switch focus between open views", "Toggle coloured output", "Toggle fullscreen", "Open prompt to enter search term (not available in this view)", "Find next line or token matching the current search term", "Find previous line or token matching the current search term", "Quit the active view", "Quit the program", "", "Timeline", "Move selection cursor up one commit", "Move selection cursor down one commit", "Open diff view of the selected commit", "(Un)tag (or diff) the selected (against the tagged) commit", "Open and populate branch view with all repository branches", "Diff local changes in the checkout against selected commit", "Open prompt to enter term with which to filter new timeline view", "Display a tree reflecting the state of the selected commit", "", "Diff", "Scroll down one page of diff output", "Toggle display of diff view line numbers", "Open prompt to enter line number and navigate to line", "Scroll the view right to the end of the longest line", "Scroll the view left to the beginning of the line", "Scroll the view down in the buffer", "Scroll the view up in the buffer", "Navigate to next file in the diff", "Navigate to previous file in the diff", "Scroll the view right", "Scroll the view left", "Open and populate branch view with all repository branches", "Open prompt to enter file number and navigate to file", "Toggle inversion of diff output", "Toggle display of file line numbers", "Toggle display of function name in chunk header", "Display side-by-side formatted diff", "Toggle verbosity of diff output", "Toggle ignore whitespace-only changes in diff", "Decrease the number of context lines", "Increase the number of context lines", "Display diff of next (newer) commit in the timeline", "Display diff of previous (older) commit in the timeline", "", "Tree", "Move into the selected directory", "Return to the parent directory", "Open and populate branch view with all repository branches", "Toggle ISO8601 modified timestamp display for each tree entry", "Toggle display of file artifact SHA hash ID", "Display timeline of all commits modifying the selected entry", "", "Blame", "Scroll down one page", "Display the diff of the commit corresponding to the selected line", "Toggle display of file line numbers", "Open prompt to enter line number and navigate to line", "Scroll the view right to the end of the longest line", "Scroll the view left to the beginning of the line", "Scroll the view right", "Scroll the view left", "Blame the version of the file found in the selected line's commit", "Blame the version of the file found in the selected line's parent " "commit", "Reload the previous blamed version of the file", "Open and populate branch view with all repository branches", "", "Branch", |
︙ | ︙ | |||
3322 3323 3324 3325 3326 3327 3328 | padpopup(struct fnc_view *view, int width, int height, FILE *txt, const char *title) { WINDOW *win, *content; char *line = NULL; ssize_t linelen; size_t linesz; | | | | | 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 | padpopup(struct fnc_view *view, int width, int height, FILE *txt, const char *title) { WINDOW *win, *content; char *line = NULL; ssize_t linelen; size_t linesz; int ch, cury, end, wy, wx, x0, y0; x0 = 4; /* Number of columns to border window. */ y0 = 2; /* Number of lines to border window. */ cury = 0; wx = getmaxx(view->window) - ((x0 + 1) * 2); /* Width of window. */ wy = getmaxy(view->window) - ((y0 + 1) * 2); /* Height of window */ ch = ERR; if ((win = newwin(wy, wx, y0, x0)) == 0) return RC(FSL_RC_ERROR, "%s", "newwin"); if ((content = newpad(height + 1, width + 1)) == 0) { delwin(win); return RC(FSL_RC_ERROR, "%s", "newpad"); } doupdate(); keypad(content, TRUE); /* Write text content to pad. */ if (title) centerprint(content, 0, 0, width, title, 0); while ((linelen = getline(&line, &linesz, txt)) != -1) waddstr(content, line); fsl_free(line); end = (getcury(content) - (wy - 3)); /* No. lines past end of pad. */ do { switch (ch) { |
︙ | ︙ | |||
3378 3379 3380 3381 3382 3383 3384 | case ' ': if (cury < end) { cury += wy - 3; if (cury > end) cury = end; } break; | < < < < < < < < < < < < < < < < < < | | | | 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 | case ' ': if (cury < end) { cury += wy - 3; if (cury > end) cury = end; } break; case 'g': if (!fnc_home(view)) break; /* FALL THROUGH */ case KEY_HOME: cury = 0; break; case KEY_END: case 'G': cury = end; break; case ERR: default: break; } werase(win); box(win, 0, 0); wnoutrefresh(win); pnoutrefresh(content, cury, 0, y0 + 1, x0 + 1, wy, wx); doupdate(); } while ((ch = wgetch(content)) != 'q' && ch != KEY_ESCAPE && ch != ERR); /* Destroy window. */ werase(win); wrefresh(win); delwin(win); delwin(content); /* Restore fnc window content. */ touchwin(view->window); wnoutrefresh(view->window); doupdate(); return 0; } static void centerprint(WINDOW *win, int starty, int startx, int cols, const char *str, chtype colour) { int x, y; |
︙ | ︙ | |||
3518 3519 3520 3521 3522 3523 3524 | !s->thread_cx.eotl) { s->thread_cx.ncommits_needed += (view->nlines - 1) - s->commits.ncommits; rc = signal_tl_thread(view, 1); } break; case 'C': { | < < < < < | > | 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 | !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(); /* * 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 in-session. Clear it and notify user. */ rc = fsl_ckout_changes_scan(f); if (rc == FSL_RC_DB) { rc = sitrep(view, "-- checkout db busy --", SR_CLREOL | SR_UPDATE | SR_SLEEP | SR_RESET); break; } else if (rc) return RC(rc, "%s", "fsl_ckout_changes_scan"); if (!fsl_ckout_has_changes(f)) { sitrep(view, "-- no local changes --", SR_CLREOL | SR_UPDATE | SR_SLEEP); break; |
︙ | ︙ | |||
3565 3566 3567 3568 3569 3570 3571 | struct input input = {NULL, "filter: ", INPUT_ALPHA, SR_CLREOL}; rc = fnc_prompt_input(view, &input); if (rc) return rc; s->glob = input.buf; rc = request_view(new_view, view, FNC_VIEW_TIMELINE); if (rc == FSL_RC_BREAK) { | | > | 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 | struct input input = {NULL, "filter: ", INPUT_ALPHA, SR_CLREOL}; rc = fnc_prompt_input(view, &input); if (rc) return rc; s->glob = input.buf; rc = request_view(new_view, view, FNC_VIEW_TIMELINE); if (rc == FSL_RC_BREAK) { rc = sitrep(view, "-- no matching commits --", SR_CLREOL | SR_UPDATE | SR_SLEEP | SR_RESET); } break; } case 't': if (s->selected_entry == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), |
︙ | ︙ | |||
4138 4139 4140 4141 4142 4143 4144 | return RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_mutex_unlock"); ch = wgetch(view->window); if ((rc = pthread_mutex_lock(&fnc_mutex))) return RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_mutex_lock"); if (ch == KEY_BACKSPACE) { | | | | 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 | return RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_mutex_unlock"); ch = wgetch(view->window); if ((rc = pthread_mutex_lock(&fnc_mutex))) return RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_mutex_lock"); if (ch == KEY_BACKSPACE) { view->search_status = SEARCH_CONTINUE; return rc; } if (view->searching == SEARCH_FORWARD) entry = TAILQ_NEXT(s->search_commit, entries); else entry = TAILQ_PREV(s->search_commit, commit_tailhead, entries); } else if (s->matched_commit) { |
︙ | ︙ | |||
4173 4174 4175 4176 4177 4178 4179 | break; } if (s->thread_cx.eotl || view->searching == SEARCH_REVERSE) { view->search_status = (s->matched_commit == NULL ? SEARCH_NO_MATCH : SEARCH_COMPLETE); s->search_commit = NULL; | | > | 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 | break; } if (s->thread_cx.eotl || view->searching == SEARCH_REVERSE) { view->search_status = (s->matched_commit == NULL ? SEARCH_NO_MATCH : SEARCH_COMPLETE); s->search_commit = NULL; cbreak(); return rc; } /* * Wake the timeline thread to produce more commits. * Search will resume at s->search_commit upon return. */ ++s->thread_cx.ncommits_needed; return signal_tl_thread(view, 0); |
︙ | ︙ | |||
4200 4201 4202 4203 4204 4205 4206 | else entry = TAILQ_PREV(entry, commit_tailhead, entries); } if (s->matched_commit) { int cur = s->selected_entry->idx; while (cur < s->matched_commit->idx) { | | < | < < > | 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 | else entry = TAILQ_PREV(entry, commit_tailhead, entries); } if (s->matched_commit) { int cur = s->selected_entry->idx; while (cur < s->matched_commit->idx) { if ((rc = tl_input_handler(NULL, view, KEY_DOWN))) return rc; ++cur; } while (cur > s->matched_commit->idx) { if ((rc = tl_input_handler(NULL, view, KEY_UP))) return rc; --cur; } } s->search_commit = NULL; cbreak(); return rc; } static bool find_commit_match(struct fnc_commit_artifact *commit, regex_t *regex) { |
︙ | ︙ | |||
4377 4378 4379 4380 4381 4382 4383 | fsl_free(ffa); return 0; } static int init_diff_view(struct fnc_view **new_view, int start_col, int start_ln, | | | > > > > | < > | > > > | > > > > > > > > | > | | | 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 | fsl_free(ffa); return 0; } static int init_diff_view(struct fnc_view **new_view, int start_col, int start_ln, struct fnc_commit_artifact *commit, struct fnc_view *timeline_view, bool showmeta) { struct fnc_view *diff_view; int rc = 0; diff_view = view_open(0, 0, start_ln, start_col, FNC_VIEW_DIFF); if (diff_view == NULL) return RC(FSL_RC_ERROR, "%s", "view_open"); rc = open_diff_view(diff_view, commit, DEF_DIFF_CTX, fnc_init.sbs, fnc_init.ws, fnc_init.invert, !fnc_init.quiet, fnc_init.showln, timeline_view, showmeta, NULL); if (!rc) *new_view = diff_view; return rc; } static int open_diff_view(struct fnc_view *view, struct fnc_commit_artifact *commit, int context, bool sbs, bool ignore_ws, bool invert, bool verbosity, bool showln, struct fnc_view *timeline_view, bool showmeta, struct fnc_pathlist_head *paths) { struct fnc_diff_view_state *s = &view->state.diff; char *opt; int rc = 0; opt = fnc_conf_getopt(FNC_COLOUR_HL_LINE, false); if (!fsl_stricmp(opt, "mono")) s->sline = SLINE_MONO; fsl_free(opt); s->index.n = 0; s->index.idx = 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->context = context; s->sbs = 0; FLAG_SET(s->diff_flags, FNC_DIFF_PROTOTYPE); sbs ? FLAG_SET(s->diff_flags, FNC_DIFF_SIDEBYSIDE) : 0; verbosity ? FLAG_SET(s->diff_flags, FNC_DIFF_VERBOSE) : 0; ignore_ws ? FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_ALLWS) : 0; invert ? FLAG_SET(s->diff_flags, FNC_DIFF_INVERT) : 0; showln ? FLAG_SET(s->diff_flags, FNC_DIFF_LINENO) : 0; s->timeline_view = timeline_view; s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = showmeta; if (s->colour) { STAILQ_INIT(&s->colours); rc = set_colours(&s->colours, FNC_VIEW_DIFF); if (rc) return rc; } if (timeline_view && screen_is_split(view)) show_timeline_view(timeline_view); /* draw vborder */ show_diff_status(view); s->line_offsets = NULL; s->nlines = 0; s->dlines = NULL; s->ndlines = 0; s->ncols = view->ncols; |
︙ | ︙ | |||
4447 4448 4449 4450 4451 4452 4453 | view->close = close_diff_view; view->grep_init = diff_grep_init; view->grep = find_next_match; return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 | view->close = close_diff_view; view->grep_init = diff_grep_init; view->grep = find_next_match; return rc; } static void show_diff_status(struct fnc_view *view) { mvwaddstr(view->window, 0, 0, "generating diff..."); updatescreen(view->window, true, true); } |
︙ | ︙ | |||
4637 4638 4639 4640 4641 4642 4643 | * file artifacts; the latter compares file artifact blobs only. */ if (s->selected_entry->diff_type == FNC_DIFF_COMMIT) diff_commit(s); else if (s->selected_entry->diff_type == FNC_DIFF_CKOUT) diff_checkout(s); | < < < < < < < < < < < < < < < < | 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 | * file artifacts; the latter compares file artifact blobs only. */ if (s->selected_entry->diff_type == FNC_DIFF_COMMIT) diff_commit(s); else if (s->selected_entry->diff_type == FNC_DIFF_CKOUT) diff_checkout(s); /* * Parse the diff buffer line-by-line to record byte offsets of each * line for scrolling and searching in diff view. */ st0 = fsl_strdup(fsl_buffer_str(&s->buf)); st = st0; off = (s->line_offsets)[s->nlines - 1]; |
︙ | ︙ | |||
4757 4758 4759 4760 4761 4762 4763 | char *line = NULL, *st0 = NULL, *st = NULL; fsl_size_t linelen, idx = 0; off_t off = 0; int rc = FSL_RC_OK, n = 7; /* Min lines in commit meta */ n += countlines(s->selected_entry->comment); n += s->selected_entry->changeset.used; | < < < > > | | 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 | char *line = NULL, *st0 = NULL, *st = NULL; fsl_size_t linelen, idx = 0; off_t off = 0; int rc = FSL_RC_OK, n = 7; /* Min lines in commit meta */ n += countlines(s->selected_entry->comment); n += s->selected_entry->changeset.used; if ((n = fprintf(s->f,"%s %s\n", s->selected_entry->type, s->selected_entry->uuid)) < 0) goto end; off += n; rc = add_line_type(&s->dlines, LINE_DIFF_META, &s->ndlines, n, true); if (!rc) rc = add_line_offset(&s->line_offsets, &s->nlines, off); if (rc) goto end; if ((n = fprintf(s->f,"user: %s\n", s->selected_entry->user)) < 0) goto end; off += n; rc = add_line_type(&s->dlines, LINE_DIFF_USER, &s->ndlines, 0, true); |
︙ | ︙ | |||
6044 6045 6046 6047 6048 6049 6050 | } 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; | < | | 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 | } 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; struct fnc_tl_view_state *tlstate; struct commit_entry *previous_selection; 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 tl_down = false; nlines = s->nlines; s->lineno = s->first_line_onscreen - 1 + s->selected_line; switch (ch) { case '0': view->pos.col = 0; |
︙ | ︙ | |||
6263 6264 6265 6266 6267 6268 6269 | return rc; view_set_child(view, branch_view); view->focus_child = true; } else *new_view = branch_view; break; } | < < < < < | < < | 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 | return rc; view_set_child(view, branch_view); view->focus_child = true; } else *new_view = branch_view; break; } case 'c': case 'i': case 'L': case 'p': case 'S': case 'v': case 'w': if (ch == 'c') s->colour = !s->colour; /* LSipvw key maps don't apply to tag or ticket artifacts. */ if (*s->selected_entry->type == 't' && (s->selected_entry->type[1] == 'a' || s->selected_entry->type[1] == 'i')) break; else if (ch == 'i') FLAG_TOG(s->diff_flags, FNC_DIFF_INVERT); else if (ch == 'L') FLAG_TOG(s->diff_flags, FNC_DIFF_LINENO); else if (ch == 'p') FLAG_TOG(s->diff_flags, FNC_DIFF_PROTOTYPE); else if (ch == 'S') FLAG_TOG(s->diff_flags, FNC_DIFF_SIDEBYSIDE); else if (ch == 'v') FLAG_TOG(s->diff_flags, FNC_DIFF_VERBOSE); else if (ch == 'w') FLAG_TOG(s->diff_flags, FNC_DIFF_IGNORE_ALLWS); rc = reset_diff_view(view, true); break; case '-': case '_': if (s->context > 0) { |
︙ | ︙ | |||
6316 6317 6318 6319 6320 6321 6322 | rc = reset_diff_view(view, true); } break; case CTRL('j'): case '>': case '.': case 'J': | | | < | | | | | | | | | < < < < < < | | < < < < < < < < < | | < < > | 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 | rc = reset_diff_view(view, true); } break; case CTRL('j'): case '>': case '.': case 'J': tl_down = true; /* FALL THROUGH */ case CTRL('k'): case '<': case ',': case 'K': if (s->timeline_view == NULL) break; tlstate = &s->timeline_view->state.timeline; previous_selection = tlstate->selected_entry; rc = tl_input_handler(NULL, s->timeline_view, tl_down ? KEY_DOWN : KEY_UP); if (rc) break; if (previous_selection == tlstate->selected_entry) break; rc = set_selected_commit(s, tlstate->selected_entry); if (rc) break; s->selected_line = 1; reset_diff_view(view, false); break; default: break; } return rc; } |
︙ | ︙ | |||
6801 6802 6803 6804 6805 6806 6807 | } static void usage_diff(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s diff [-C|--no-colour] [-R path] [-h|--help] " | | | | | 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 | } static void usage_diff(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s diff [-C|--no-colour] [-R path] [-h|--help] " "[-i|--invert] [-l|--line-numbers] [-q|--quiet] [-s|--sbs] " "[-w|--whitespace] [-x|--context n] [artifact1 [artifact2]] " "[path ...]\n" " e.g.: %s diff --sbs d34db33f c0ff33 src/*.c\n\n", fcli_progname(), fcli_progname()); } static void usage_tree(void) { |
︙ | ︙ | |||
6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 | struct fnc_pathlist_head paths; struct fnc_pathlist_entry *pe; fsl_deck d = fsl_deck_empty; fsl_stmt *q = NULL; const char *artifact1 = NULL, *artifact2 = NULL; char *path0 = NULL; fsl_id_t prid = -1, rid = -1; int rc = FSL_RC_OK; unsigned short blob = 0; enum fnc_diff_type diff_type = FNC_DIFF_CKOUT; bool showmeta = false; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) | > | 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 | struct fnc_pathlist_head paths; struct fnc_pathlist_entry *pe; fsl_deck d = fsl_deck_empty; fsl_stmt *q = NULL; const char *artifact1 = NULL, *artifact2 = NULL; char *path0 = NULL; fsl_id_t prid = -1, rid = -1; long context = DEF_DIFF_CTX; int rc = FSL_RC_OK; unsigned short blob = 0; enum fnc_diff_type diff_type = FNC_DIFF_CKOUT; bool showmeta = false; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) |
︙ | ︙ | |||
7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 | rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_DIFF); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } | > > > > > > > > > > | > > | 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 | rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) goto end; if (fnc_init.context) { if ((rc = strtonumcheck(&context, fnc_init.context, INT_MIN, INT_MAX))) goto end; context = MIN(MAX_DIFF_CTX, context); } view = view_open(0, 0, 0, 0, FNC_VIEW_DIFF); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } rc = open_diff_view(view, commit, context, fnc_init.sbs, fnc_init.ws, fnc_init.invert, !fnc_init.quiet, fnc_init.showln, NULL, showmeta, &paths); if (!rc) rc = view_loop(view); end: fsl_free(path0); fsl_deck_finalize(&d); fsl_stmt_finalize(q); if (commit) |
︙ | ︙ | |||
7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_TREE); if (view == NULL) { RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } | > > > | 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_TREE); if (view == NULL) { RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } |
︙ | ︙ | |||
7350 7351 7352 7353 7354 7355 7356 | *tree = NULL; *tree = fsl_malloc(sizeof(**tree)); if (*tree == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_malloc"); memset(*tree, 0, sizeof(**tree)); | < | < < < < | 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 | *tree = NULL; *tree = fsl_malloc(sizeof(**tree)); if (*tree == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_malloc"); memset(*tree, 0, sizeof(**tree)); /* Count how many elements will comprise the tree to be allocated. */ for(tn = repo->head; tn; tn = tn->next) { if ((!tn->parent_dir && fsl_strcmp(dir, "/")) || (tn->parent_dir && fsl_strcmp(dir, tn->parent_dir->path))) continue; ++i; } (*tree)->entries = calloc(i, sizeof(struct fnc_tree_entry)); |
︙ | ︙ | |||
8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 | char *prev, *v; enum fnc_opt_id setid; int rc = FSL_RC_OK; rc = init_unveil(REPODIR, CKOUTDIR, true); if (rc) return rc; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; opt = fcli_next_arg(true); if (opt == NULL || fnc_init.lsconf) { | > > > | 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 | char *prev, *v; enum fnc_opt_id setid; int rc = FSL_RC_OK; rc = init_unveil(REPODIR, CKOUTDIR, true); if (rc) return rc; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) return rc; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; opt = fcli_next_arg(true); if (opt == NULL || fnc_init.lsconf) { |
︙ | ︙ | |||
8593 8594 8595 8596 8597 8598 8599 | }; rc = set_colour_scheme(s, pairs_blame, regexp_blame, nitems(regexp_blame)); break; } case FNC_VIEW_BRANCH: { static const char *regexp_branch[] = { | | | 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 | }; rc = set_colour_scheme(s, pairs_blame, regexp_blame, nitems(regexp_blame)); break; } case FNC_VIEW_BRANCH: { static const char *regexp_branch[] = { "^\\ +", "^ -", "@$", "\\*$" }; const int pairs_branch[][2] = { {FNC_COLOUR_BRANCH_OPEN, init_colour(FNC_COLOUR_BRANCH_OPEN)}, {FNC_COLOUR_BRANCH_CLOSED, init_colour(FNC_COLOUR_BRANCH_CLOSED)}, {FNC_COLOUR_BRANCH_CURRENT, |
︙ | ︙ | |||
8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_BLAME); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", view_open); goto end; } | > > > | 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_BLAME); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", view_open); goto end; } |
︙ | ︙ | |||
9574 9575 9576 9577 9578 9579 9580 | *gtl : (view->nlines - 3) / 2 + 1; *gtl = 0; return true; } static int | | | 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 | *gtl : (view->nlines - 3) / 2 + 1; *gtl = 0; return true; } static int blame_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_view *branch_view, *diff_view; struct fnc_blame_view_state *s = &view->state.blame; int start_col = 0, rc = FSL_RC_OK; uint16_t nscroll = view->nlines - 2; switch (ch) { |
︙ | ︙ | |||
9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 | break; case KEY_LEFT: case 'h': view->pos.col -= MIN(view->pos.col, 2); break; case 'q': s->done = true; break; case 'c': s->colour = !s->colour; break; case 'g': if (!fnc_home(view)) break; | > > | 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 | break; case KEY_LEFT: case 'h': view->pos.col -= MIN(view->pos.col, 2); break; case 'q': s->done = true; if (s->selected_entry) fnc_commit_artifact_close(s->selected_entry); break; case 'c': s->colour = !s->colour; break; case 'g': if (!fnc_home(view)) break; |
︙ | ︙ | |||
9788 9789 9790 9791 9792 9793 9794 | if (view_is_parent(view)) { rc = view_close_child(view); if (rc) return rc; view_set_child(view, branch_view); view->focus_child = true; } else | | | 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 | if (view_is_parent(view)) { rc = view_close_child(view); if (rc) return rc; view_set_child(view, branch_view); view->focus_child = true; } else *new_view = branch_view; break; case KEY_ENTER: case '\r': { fsl_cx *const f = fcli_cx(); struct fnc_commit_artifact *commit = NULL; fsl_stmt *q = NULL; fsl_uuid_cstr id = NULL; |
︙ | ︙ | |||
9812 9813 9814 9815 9816 9817 9818 | q = fsl_stmt_malloc(); rc = commit_builder(&commit, fsl_uuid_to_rid(f, id), q); fsl_stmt_finalize(q); if (rc) { fnc_commit_artifact_close(commit); break; } | < < < < < < | | | | | | | | < | > > < < | > | | < | > > | 9655 9656 9657 9658 9659 9660 9661 9662 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 | q = fsl_stmt_malloc(); rc = commit_builder(&commit, fsl_uuid_to_rid(f, id), q); fsl_stmt_finalize(q); if (rc) { fnc_commit_artifact_close(commit); break; } if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); diff_view = view_open(0, 0, 0, start_col, FNC_VIEW_DIFF); if (diff_view == NULL) { fnc_commit_artifact_close(commit); rc = RC(FSL_RC_ERROR, "%s", "view_open"); break; } rc = open_diff_view(diff_view, commit, DEF_DIFF_CTX, fnc_init.sbs, fnc_init.ws, fnc_init.invert, !fnc_init.quiet, fnc_init.showln, NULL, true, NULL); s->selected_entry = commit; if (rc) { fnc_commit_artifact_close(commit); view_close(diff_view); break; } view->active = false; diff_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (rc) break; view_set_child(view, diff_view); view->focus_child = true; } else *new_view = diff_view; if (rc) break; break; } case KEY_RESIZE: if (s->selected_line > view->nlines - 2) { s->selected_line = MIN(s->blame.nlines, view->nlines - 2); } |
︙ | ︙ | |||
9918 9919 9920 9921 9922 9923 9924 | blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(blamed_commit); } fsl_free(s->path); free_colours(&s->colours); | < < | 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 | blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(blamed_commit); } fsl_free(s->path); free_colours(&s->colours); return rc; } static int stop_blame(struct fnc_blame *blame) { |
︙ | ︙ | |||
10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 | glob = fsl_strdup(fcli_next_arg(true)); rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_BRANCH); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } | > > > | 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 | glob = fsl_strdup(fcli_next_arg(true)); rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; rc = init_landlock((const char*[]){REPODIR, CKOUTDIR, P_tmpdir}, 3); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_BRANCH); if (view == NULL) { rc = RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } |
︙ | ︙ | |||
10192 10193 10194 10195 10196 10197 10198 | struct fnc_branch *new_branch; struct fnc_branchlist_entry *be; const char *brname = fsl_stmt_g_text(stmt, 0, NULL); bool priv = (curr_branch && fsl_stmt_g_int32(stmt, 1) == 1); bool open = fsl_stmt_g_int32(stmt, 2) == 0; double mtime = fsl_stmt_g_int64(stmt, 3); bool curr = curr_branch && !fsl_strcmp(curr_branch, brname); | | | | 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 | struct fnc_branch *new_branch; struct fnc_branchlist_entry *be; const char *brname = fsl_stmt_g_text(stmt, 0, NULL); bool priv = (curr_branch && fsl_stmt_g_int32(stmt, 1) == 1); bool open = fsl_stmt_g_int32(stmt, 2) == 0; double mtime = fsl_stmt_g_int64(stmt, 3); bool curr = curr_branch && !fsl_strcmp(curr_branch, brname); if ((s->when > 0 && mtime < s->dateline) || (s->when < 0 && mtime > s->dateline)) continue; rc = alloc_branch(&new_branch, brname, mtime, open, priv, curr); if (rc) goto end; rc = fnc_branchlist_insert(&be, &s->branches, new_branch); if (rc) goto end; |
︙ | ︙ | |||
10342 10343 10344 10345 10346 10347 10348 | { struct fnc_branch_view_state *s = &view->state.branch; struct fnc_branchlist_entry *be; struct fnc_colour *c = NULL; char *line = NULL; wchar_t *wline; int limit, n, width, rc = 0; | < | < < < < < | > | | | > > | | | < < < | 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 | { struct fnc_branch_view_state *s = &view->state.branch; struct fnc_branchlist_entry *be; struct fnc_colour *c = NULL; char *line = NULL; wchar_t *wline; int limit, n, width, rc = 0; werase(view->window); s->ndisplayed = 0; limit = view->nlines; if (limit == 0) return rc; be = s->first_branch_onscreen; if ((line = fsl_mprintf("branches [%d/%d]", be->idx + s->selected + 1, s->nbranches)) == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); rc = formatln(&wline, &width, line, view->ncols, 0, false); if (rc) { fsl_free(line); return rc; } if (screen_is_shared(view)) wattron(view->window, A_REVERSE); waddwstr(view->window, wline); while (width < view->ncols) { waddch(view->window, ' '); ++width; } if (screen_is_shared(view)) wattroff(view->window, A_REVERSE); fsl_free(wline); wline = NULL; fsl_free(line); line = NULL; if (width < view->ncols - 1) waddch(view->window, '\n'); if (--limit <= 0) return rc; n = 0; while (be && limit > 0) { char *line = NULL; line = fsl_mprintf(" %c%s%s%s%s%s%s%c %s", be->branch->open ? '+' : '-', be->branch->name, be->branch->private ? "*" : "", be->branch->current ? "@" : "", s->show_id ? " [" : "", s->show_id ? be->branch->id : "", s->show_id ? "]" : "", s->show_date ? ':' : 0, s->show_date ? be->branch->date : 0); if (line == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); if (s->colour) c = match_colour(&s->colours, line); rc = formatln(&wline, &width, line, view->ncols, 0, false); |
︙ | ︙ | |||
10418 10419 10420 10421 10422 10423 10424 | s->selected_entry = be; } if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wline); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); | | | 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 | s->selected_entry = be; } if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wline); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (width < view->ncols - 1) waddch(view->window, '\n'); if (n == s->selected && view->active) wattr_off(view->window, A_REVERSE, NULL); fsl_free(line); fsl_free(wline); wline = NULL; |
︙ | ︙ | |||
10911 10912 10913 10914 10915 10916 10917 | *ret = n; return FSL_RC_OK; } static int fnc_prompt_input(struct fnc_view *view, struct input *input) { | | | < | > | > | 10744 10745 10746 10747 10748 10749 10750 10751 10752 10753 10754 10755 10756 10757 10758 10759 10760 10761 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 | *ret = n; return FSL_RC_OK; } static int fnc_prompt_input(struct fnc_view *view, struct input *input) { int rc = FSL_RC_OK; if (input->prompt) sitrep(view, input->prompt, input->flags); rc = cook_input(input->buf, sizeof(input->buf), view->window); if (rc || !input->buf[0]) return rc; if (input->type == INPUT_NUMERIC) { long n = 0; int min = INT_MIN, max = INT_MAX; if (input->data) { min = *(int *)input->data; max = ((int *)input->data)[1]; } rc = strtonumcheck(&n, input->buf, min, max); if (rc == FSL_RC_MISUSE) rc = sitrep(view, "-- numeric input only --", SR_CLREOL | SR_UPDATE | SR_SLEEP | SR_RESET); else if (rc == FSL_RC_RANGE || n < min || n > max) rc = sitrep(view, "-- line outside range --", SR_CLREOL | SR_UPDATE | SR_SLEEP | SR_RESET); else input->ret = n; } return rc; } |
︙ | ︙ | |||
10958 10959 10960 10961 10962 10963 10964 | return rc == ERR ? FSL_RC_ERROR : FSL_RC_OK; } static int sitrep(struct fnc_view *view, const char *msg, int flags) { | < < < | | | | | 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 | return rc == ERR ? FSL_RC_ERROR : FSL_RC_OK; } static int sitrep(struct fnc_view *view, const char *msg, int flags) { wattr_on(view->window, A_BOLD, NULL); mvwaddstr(view->window, view->nlines - 1, 0, msg); if (FLAG_CHK(flags, SR_CLREOL)) wclrtoeol(view->window); wattr_off(view->window, A_BOLD, NULL); if (FLAG_CHK(flags, SR_UPDATE)) { update_panels(); doupdate(); } if (FLAG_CHK(flags, SR_RESET)) fcli_err_reset(); if (FLAG_CHK(flags, SR_SLEEP)) |
︙ | ︙ | |||
11081 11082 11083 11084 11085 11086 11087 | if (*op == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); *glob = fsl_mprintf("*%s*", str); if (*glob == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); } | | | 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 | if (*op == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); *glob = fsl_mprintf("*%s*", str); if (*glob == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); } return 0; } static const char * getdirname(const char *path, fsl_int_t len, bool slash) { fsl_size_t n = (len > 0) ? (fsl_size_t)len : fsl_strlen(path); const char *p = path + n; |
︙ | ︙ | |||
11114 11115 11116 11117 11118 11119 11120 | * Read permissions for the below unveil() calls are self-evident; we need * to read the repository and ckout databases, and ckout dir for most all fnc * operations. Write and create permissions are briefly listed inline, but we * effectively veil the entire fs except the repo db, ckout, and /tmp dirs. * The create permissions for the repository and checkout dirs are (perhaps * unintuitively) needed as fossil(1) creates temporary journal files in both. */ | < | | | < < < < < < < < < < < < < < < < < < < < < < < < | 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 | * Read permissions for the below unveil() calls are self-evident; we need * to read the repository and ckout databases, and ckout dir for most all fnc * operations. Write and create permissions are briefly listed inline, but we * effectively veil the entire fs except the repo db, ckout, and /tmp dirs. * The create permissions for the repository and checkout dirs are (perhaps * unintuitively) needed as fossil(1) creates temporary journal files in both. */ static int init_unveil(const char *repodb, const char *ckoutdir, bool cfg) { #ifdef __OpenBSD__ /* wc repo db for 'fnc config' command: fnc_conf_setopt(). */ if (unveil(repodb, cfg ? "rwc" : "rw") == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "unveil(%s, \"rw\")", repodb); /* wc .fslckout for fsl_ckout_changes_scan() in cmd_diff(). */ if (unveil(ckoutdir, "rwc") == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "unveil(%s, \"rw\")", ckoutdir); /* rwc /tmp for tmpfile() in help(), create_diff(), and run_blame(). */ if (unveil(P_tmpdir, "rwc") == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "unveil(%s, \"rwc\")", P_tmpdir); if (unveil(NULL, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "%s", "unveil"); #endif /* __OpenBSD__ */ return FSL_RC_OK; } #ifdef HAVE_LANDLOCK /* * Sans libc wrappers, use the following shims provided by Landlock authors. * https://www.kernel.org/doc/html/latest/userspace-api/landlock.html */ #ifndef landlock_create_ruleset static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, |
︙ | ︙ | |||
11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 | #ifndef landlock_restrict_self static inline int landlock_restrict_self(const int rfd, const __u32 flags) { return syscall(__NR_landlock_restrict_self, rfd, flags); } #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 int n) { | > | < < < | | | | > | > > > > > > | < < < | | < < < | | < < < < > < | 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 11048 11049 11050 11051 11052 11053 11054 11055 11056 11057 11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 | #ifndef landlock_restrict_self static inline int landlock_restrict_self(const int rfd, const __u32 flags) { return syscall(__NR_landlock_restrict_self, rfd, flags); } #endif #endif /* HAVE_LANDLOCK */ /* * 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 int n) { int rc = FSL_RC_OK; #ifdef HAVE_LANDLOCK /* * Define default block list of _all_ possible operations. * XXX Due to the fail-open design, set all the bits to avoid following * Landlock for new ops to be added to this deny-by-default list. */ struct landlock_ruleset_attr attr = { .handled_access_fs = 0xffffffffffffffffULL }; /* Define allowed operations. */ struct landlock_path_beneath_attr path_beneath { .allowed_access = 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 } int rfd; rfd = landlock_create_ruleset(&attr, sizeof(attr), 0); if (rfd == -1) { /* Landlock is not supported or disabled by the kernel. */ if (errno == ENOSYS || errno == EOPNOTSUPP) return rc; return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: %s", "failed to create ruleset"); } /* Iterate paths to grant fs permissions. */ for (i = 0; !rc && i < n; ++i) { path_beneath.parent_fd = open(paths[i], O_PATH | O_CLOEXEC); if (path_beneath.parent_fd == -1) rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: failed to open dir '%s'", paths[i]); else { if (landlock_add_rule(rfd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: %s", "failed to update ruleset"); close(path_beneath.parent_fd); } } if (!rc && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: %s", "failed to restrict privileges"); if (!rc && landlock_restrict_self(rfd, 0)) { rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: %s", "failed to enforce ruleset"); } close(rfd); #endif /* HAVE_LANDLOCK */ return rc; } |