Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.9 To 0.10
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] | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 29 30 31 32 | **fnc 0.10** 2022-03-24 - fix gcc 9.3 compiler warnings (i.e., unused variable) (reported by stephan) - restrict `C` key map for diffing local changes to check-in artifacts - ensure timeline --branch option ignores cancelled branches (reported by sean) - fix landlock initialisation of handled fs access perms (patch by Ashish) - tighten landlock ruleset depending on which fnc command is called - improve branch view rendering of last modified date and hash id - colour branch header and make (in)active behaviour consistent with other views - improve branch parsing of imported repositories (reported by Dan) - fix OB1 error when branch entry length exceeds COLUMNS - add timezone path to landlock ruleset (reported by Ashish) - implement `P` key map to write a patch file of the currently viewed diff - fix invalid write in landlock initialisation code (reported by Ashish) - ensure unveil(2) initialisation handles `-R|--repo` invocations - add create file to landlock ruleset for permitted dirs (reported by Ashish) - make mandir if needed in makefile install target (reported by Dan) - document `backspace` key map to cancel tl search/traversal (reported by Dan) - return to blocking on user input when tl search is aborted (reported by Dan) - implement persistent diff options for global and per-repo defaults - implement `--whitespace-eol` and `W` key map to only ignore eol whitespace - implement horizontal scroll in the in-app help - add `Q` key map to in-app help to directly quit fnc - implement blame navigation from diff view with `C-{j,k}` key maps **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.10 ## 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 38 39 40 41 | # Install * **OpenBSD** - `doas pkg_add fnc` * **macOS** - `sudo port install fnc` * **FreeBSD** - <u>package</u>: `pkg install fnc` - <u>port</u>: `cd /usr/ports/devel/fnc/ && make install clean` * **Linux** - [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.10 # 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 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 | > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | ${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: mkdir -p -m 0755 ${PREFIX}${MANDIR}/man1 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) \ 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 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), \ | > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | _(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, DIFF_CONTEXT), \ _(pfx, DIFF_FLAGS), \ _(pfx, VIEW_SPLIT_MODE), \ _(pfx, VIEW_SPLIT_WIDTH), \ _(pfx, VIEW_SPLIT_HEIGHT), \ _(pfx, EOF_SETTINGS) #define LINE_ATTR_ENUM(pfx, _) \ _(pfx, AUTO), \ |
︙ | ︙ | |||
89 90 91 92 93 94 95 | _(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) | < < < < < < | 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) |
Deleted signify/fnc-08-release.pub.
|
| < < |
Added signify/fnc-10-release.pub.
> > | 1 2 | untrusted comment: fnc 0.10 public key RWS6x8wL/CQL9o5F+7/DWH43PNhA+TqWxODvTKDcgslIuGfRUslHXb+W |
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 CilPqsWw .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 412 413 414 415 | 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 \ | > > > > > > > > > | > | | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | 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. .It Cm Backspace Cancel the current search or timeline traversal .Po i.e., .Sy / , .Sy G , or .Sy End .Pc . .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-eol 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, |
︙ | ︙ | |||
472 473 474 475 476 477 478 | 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 | | | > > > > > > < < < | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | 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., C/C++, Java, Python, JavaScript, Rust ; however, Lisps and non-source code .Pq e.g., Markdown, reStructuredText will return meaningless results. Function prototype cannot be displayed in the chunk header with either .Fl l|-line-numbers or .Fl s|-sbs formatted diffs. This option can be toggled in-session with the .Sy p key binding as documented below. .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 |
︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 | .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 | > > > > > | | > > > > > > > > > > > | 524 525 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 559 560 561 | .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. .Po Mutually exclusive with .Fl l , -line-numbers .Pc .It Fl W , -whitespace-eol Ignore end-of-line whitespace-only changes when displaying the diff. .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 such that 0 \*(Le n \*(Le 64. By default, 5 context lines are shown. Illegal values are a no-op. .El .Pp All the above options .Po sans .Fl h and .Fl R .Pc can be made persistent as global or per-repo settings. See .Sx ENVIRONMENT for details. .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. |
︙ | ︙ | |||
561 562 563 564 565 566 567 | .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, <, \&, | | > | | > > | > | | > > > > > > > > > > > > > | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | .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, <, \&, If the diff is derived from a .Cm timeline view, move up the timeline to the previous (i.e., newer) commit and display its diff. If the diff is derived from a .Cm blame view, display the commit diff of the previous line in the annotated file. .It Cm C-j, J, >, \&. If the diff is derived from a .Cm timeline view, move down the timeline to the next (i.e., older) commit and display its diff. If the diff is derived from a .Cm blame view, display the commit diff of the next line in the annotated file. .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 Write the currently viewed diff to a patch file. .Nm will prompt the user for a file path, which must be absolute or relative to the current working directory. If no path is input and the .Sy return key is entered, the patch will be written to the current working directory using the first ten characters of the current artifact hash as the filename with a .Sy .patch extension. .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 |
︙ | ︙ | |||
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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1136 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 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | .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 Similarly, .Cm diff options can be persistently applied to all diff views .Po i.e., whether directly accessed with .Nm Cm diff or via the .Cm timeline or .Cm branch commands' corresponding key maps .Pc by configuring the following options: .Bl -tag -width FNC_DIFF_CONTEXT .It Ev FNC_DIFF_FLAGS String containing any or all of the available short form .Cm diff boolean flag options documented above .Po i.e., CilPqsWw .Pc . If mutually exclusive options .Qq l and .Qq s are both specified, whichever is last will take precedence; for example, .Qq lqs will display side-by-side formatted diffs. Default: NULL. .It Ev FNC_DIFF_CONTEXT Numeric value as per the above documented .Fl x|--context option .Po i.e., 0 \*(Le n \*(Le 64 .Pc specifying the number of context lines. Illegal values are a no-op. Default: 5. .El .Pp Any options passed to .Nm Cm diff will override the above settings. .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 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 | > > > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | #include <langinfo.h> #include "libfossil.h" #include "diff.h" #define FNC_VERSION VERSION /* cf. Makefile */ /* User options: include/settings.h:29 */ #define STR_INFO(_) _(fnc_opt_name, FNC, USER_OPTIONS) #define GEN_STRINGS(name, pfx, info) GEN_STR(name, pfx, info) STR_INFO(GEN_STRINGS) /* 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 |
︙ | ︙ | |||
149 150 151 152 153 154 155 | */ #ifdef __OpenBSD__ # ifndef STAILQ_HEAD # define STAILQ SIMPLEQ # endif /* STAILQ_HEAD */ #endif /* OpenBSD */ | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | */ #ifdef __OpenBSD__ # ifndef STAILQ_HEAD # define STAILQ SIMPLEQ # endif /* STAILQ_HEAD */ #endif /* OpenBSD */ #ifdef __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__ */ |
︙ | ︙ | |||
206 207 208 209 210 211 212 | 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. */ | > > > | > | > | | | | > | > | 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 274 275 276 277 278 279 280 281 282 283 284 285 | 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. */ union { const char *str; long num; } context; /* Number of context lines. */ bool sbs; /* Display side-by-side diff. */ bool ws; /* Ignore whitespace-only changes. */ bool eol; /* Ignore eol whitespace-only changes */ bool nocolour; /* Disable colour in diff output. */ bool verbose; /* Disable verbose diff output. */ bool invert; /* Toggle inverted diff output. */ bool showln; /* Display line numbers in diff. */ bool proto; /* Display function prototype. */ /* 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[12]; /* 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. */ {NULL}, /* 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 all whitespace. */ false, /* eol defaults to acknowledge eol whitespace. */ false, /* nocolour defaults to off (i.e., use diff colours). */ true, /* verbose defaults to on. */ false, /* invert diff defaults to off. */ false, /* showln in diff defaults to off. */ true, /* proto in diff chunk header defaults to on. */ 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. */ |
︙ | ︙ | |||
366 367 368 369 370 371 372 | "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."), | > > > | > > > > | 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | "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_INVERT("P", "no-prototype", &fnc_init.proto, "Disable display of the enclosing function prototype in diff chunk" "headers."), FCLI_FLAG_BOOL_INVERT("q", "quiet", &fnc_init.verbose, "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-eol", &fnc_init.eol, "Ignore end-of-line whitespace-only changes when displaying diff.\n" " This option can also be toggled with the 'W' 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 |
︙ | ︙ | |||
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; | > > | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | }; enum fnc_search_state { SEARCH_WAITING, SEARCH_CONTINUE, SEARCH_COMPLETE, SEARCH_NO_MATCH, SEARCH_ABORTED, 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 #define SR_ALL SR_CLREOL | SR_UPDATE | SR_SLEEP | SR_RESET char buf[BUFSIZ]; long ret; }; struct fnc_colour { STAILQ_ENTRY(fnc_colour) entries; regex_t regex; |
︙ | ︙ | |||
717 718 719 720 721 722 723 | size_t *lineno; off_t *offset; uint32_t n; uint32_t idx; }; struct fnc_diff_view_state { | | | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | size_t *lineno; off_t *offset; uint32_t n; uint32_t idx; }; struct fnc_diff_view_state { struct fnc_view *parent_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; |
︙ | ︙ | |||
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. */ | > | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 | enum line_type *dlines; enum line_attr sline; off_t *line_offsets; bool eof; bool colour; bool showmeta; bool showln; bool patch; }; 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. */ |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | 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 *, | | < | > > | 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 | 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 *, struct fnc_pathlist_head *, struct fnc_view *, bool); static void set_diff_opt(struct fnc_diff_view_state *); 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 *); |
︙ | ︙ | |||
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 *); | > > > > > > | 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 | 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); #ifndef HAVE_LANDLOCK static int init_unveil(const char *, const char *, bool); #else static int init_landlock(const char **, const int); static const char *gettzfile(void); #define init_unveil(_r, _c, _s) \ init_landlock((const char*[]){_r, _c, P_tmpdir, gettzfile()}, 4) #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); static void free_colours(struct fnc_colours *); |
︙ | ︙ | |||
1333 1334 1335 1336 1337 1338 1339 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) | < < < | 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); |
︙ | ︙ | |||
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) | > > | 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 | 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'" " AND EXISTS(SELECT 1 FROM tagxref" " WHERE tag.tagid = tagxref.tagid AND tagtype > 0)" " 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) |
︙ | ︙ | |||
2469 2470 2471 2472 2473 2474 2475 | 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) ? | > | | > > | > | > > > > | 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 | 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..." : view->search_status == SEARCH_ABORTED ? "aborted" : "loading...")) == NULL) { rc = RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); goto end; } } else { if (view->searching) { switch (view->search_status) { case SEARCH_COMPLETE: search_str = "no more matches"; break; case SEARCH_NO_MATCH: search_str = "no matches found"; break; case SEARCH_WAITING: search_str = "searching..."; /* FALL THROUGH */ default: break; } } 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) { |
︙ | ︙ | |||
2941 2942 2943 2944 2945 2946 2947 | switch (ch) { case '\t': rc = cycle_view(view); break; case KEY_F(1): case 'H': case '?': | | > > > > | 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 | switch (ch) { case '\t': rc = cycle_view(view); break; case KEY_F(1): case 'H': case '?': rc = help(view); if (rc == FSL_RC_BREAK) { rc = FSL_RC_OK; *done = 1; } 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; |
︙ | ︙ | |||
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 | {" 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❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, | > > > > > < < < < > > < < < < | 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 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 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 | {" 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❭ "}, {" l<Right> ", " ❬l❭❬→❭ "}, {" h<Left> ", " ❬h❭❬←❭ "}, {" $ ", " ❬$❭ "}, {" 0 ", " ❬0❭ "}, {" 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❭ "}, {" <BS> ", " ❬⌫❭ "}, {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" C-e ", " ❬C-e❭ "}, {" C-y ", " ❬C-y❭ "}, {" C-n ", " ❬C-n❭ "}, {" C-p ", " ❬C-p❭ "}, {" b ", " ❬b❭ "}, {" F ", " ❬F❭ "}, {" i ", " ❬i❭ "}, {" L ", " ❬L❭ "}, {" P ", " ❬P❭ "}, {" p ", " ❬p❭ "}, {" S ", " ❬S❭ "}, {" v ", " ❬v❭ "}, {" W ", " ❬W❭ "}, {" 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❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" b ", " ❬b❭ "}, {" p ", " ❬p❭ "}, {" B ", " ❬B❭ "}, {" T ", " ❬T❭ "}, {""}, {""}, /* Branch */ {" Enter,Space ", " ❬Enter❭❬Space❭ "}, |
︙ | ︙ | |||
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 | "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", | > > > > > < < < < > > | | < < < < | 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 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 | "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", "Scroll the view right (diff, blame, help)", "Scroll the view left (diff, blame, help)", "Scroll right to the end of the longest line (diff, blame, help)", "Scroll left to the beginning of the line (diff, blame, help)", "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", "Cancel the current search or timeline traversal", "", "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 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", "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", "Prompt for path to write a patch of the currently viewed diff", "Toggle display of function name in chunk header", "Display side-by-side formatted diff", "Toggle verbosity of diff output", "Toggle ignore end-of-line whitespace-only changes in diff", "Toggle ignore whitespace-only changes in diff", "Decrease the number of context lines", "Increase the number of context lines", "Display commit diff of next line in the file / timeline entry", "Display commit diff of previous line in the file / timeline entry", "", "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", "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", |
︙ | ︙ | |||
3284 3285 3286 3287 3288 3289 3290 | 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; | | | | | 3322 3323 3324 3325 3326 3327 3328 3329 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 | 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, curx, end, wy, wx, x0, y0, rc = FSL_RC_OK; x0 = 4; /* Number of columns to border window. */ y0 = 2; /* Number of lines to border window. */ cury = curx = 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, wx, 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) { |
︙ | ︙ | |||
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 | 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); | > > > > > > > > > > > > > > > > > > | | | | 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 | case ' ': if (cury < end) { cury += wy - 3; if (cury > end) cury = end; } break; case '0': curx = 0; break; case '$': curx = MAX(width - wx / 2, 0); break; case KEY_LEFT: case 'h': curx -= MIN(curx, 2); break; case KEY_RIGHT: case 'l': if (curx + wx / 2 < width) curx += 2; 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 'Q': rc = FSL_RC_BREAK; /* FALL THROUGH */ case ERR: default: break; } werase(win); box(win, 0, 0); wnoutrefresh(win); pnoutrefresh(content, cury, curx, y0 + 1, x0 + 1, wy, wx); doupdate(); } while (!rc && (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 rc; } static void centerprint(WINDOW *win, int starty, int startx, int cols, const char *str, chtype colour) { int x, y; |
︙ | ︙ | |||
3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 | !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) { | > > > > > | < | 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 | !s->thread_cx.eotl) { s->thread_cx.ncommits_needed += (view->nlines - 1) - s->commits.ncommits; rc = signal_tl_thread(view, 1); } break; case 'C': { if (s->selected_entry->commit->type[0] != 'c') { rc = sitrep(view, "-- requires check-in artifact --", SR_ALL); break; } 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_ALL); 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; |
︙ | ︙ | |||
3505 3506 3507 3508 3509 3510 3511 | 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) { | | < | 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 | 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_ALL); } break; } case 't': if (s->selected_entry == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), |
︙ | ︙ | |||
4079 4080 4081 4082 4083 4084 4085 | 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) { | | | | 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 | 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_ABORTED; goto end; } 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) { |
︙ | ︙ | |||
4114 4115 4116 4117 4118 4119 4120 | 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; | | < | 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 | 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; goto end; } /* * 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); |
︙ | ︙ | |||
4142 4143 4144 4145 4146 4147 4148 | else entry = TAILQ_PREV(entry, commit_tailhead, entries); } if (s->matched_commit) { int cur = s->selected_entry->idx; while (cur < s->matched_commit->idx) { | | > | > > < | 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 | else entry = TAILQ_PREV(entry, commit_tailhead, entries); } if (s->matched_commit) { int cur = s->selected_entry->idx; while (cur < s->matched_commit->idx) { rc = tl_input_handler(NULL, view, KEY_DOWN); if (rc) return rc; ++cur; } while (cur > s->matched_commit->idx) { rc = tl_input_handler(NULL, view, KEY_UP); if (rc) return rc; --cur; } } s->search_commit = NULL; end: cbreak(); return rc; } static bool find_commit_match(struct fnc_commit_artifact *commit, regex_t *regex) { |
︙ | ︙ | |||
4317 4318 4319 4320 4321 4322 4323 | fsl_free(ffa); return 0; } static int init_diff_view(struct fnc_view **new_view, int start_col, int start_ln, | | | < < < < | > < | < < < | < < < < < < < < | < | | | 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 | 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 *parent_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, NULL, parent_view, showmeta); if (!rc) *new_view = diff_view; return rc; } static int open_diff_view(struct fnc_view *view, struct fnc_commit_artifact *commit, struct fnc_pathlist_head *paths, struct fnc_view *parent_view, bool showmeta) { struct fnc_diff_view_state *s = &view->state.diff; int rc = FSL_RC_OK; set_diff_opt(s); 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->parent_view = parent_view; s->showmeta = showmeta; if (s->colour) { STAILQ_INIT(&s->colours); rc = set_colours(&s->colours, FNC_VIEW_DIFF); if (rc) return rc; } if (parent_view && screen_is_split(view)) show_timeline_view(parent_view); /* draw vborder */ show_diff_status(view); s->line_offsets = NULL; s->nlines = 0; s->dlines = NULL; s->ndlines = 0; s->ncols = view->ncols; |
︙ | ︙ | |||
4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 | view->input = diff_input_handler; 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); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 | view->input = diff_input_handler; view->close = close_diff_view; view->grep_init = diff_grep_init; view->grep = find_next_match; return rc; } /* * Set diff options. Precedence is: * 1. CLI options passed to 'fnc diff' (see: fnc diff -h) * 2. global options set via envvars * - FNC_DIFF_CONTEXT: n * - FNC_DIFF_FLAGS: CilPqsw (see: fnc diff -h for all boolean flags) * - FNC_COLOUR_HL_LINE: mono, auto * 3. repo options set via 'fnc set' * - same as (2) global * 4. fnc default options * Input is validated; supplant bogus values with defaults. */ 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; s->colour = !fnc_init.nocolour && has_colors(); if (fnc_init.context.str) /* fnc diff -x|--context */ opt = fsl_strdup(fnc_init.context.str); else /* fnc set option */ opt = fnc_conf_getopt(FNC_DIFF_CONTEXT, false); if (opt) strtonumcheck(&ctx, opt, 0, MAX_DIFF_CTX); s->context = ctx; fsl_free(opt); /* Persistent options (i.e., 'fnc set' or envvars). */ opt = fnc_conf_getopt(FNC_COLOUR_HL_LINE, false); if (!fsl_stricmp(opt, "mono")) s->sline = SLINE_MONO; fsl_free(opt); opt = fnc_conf_getopt(FNC_DIFF_FLAGS, false); while (opt && (ch = opt[i++])) { switch (ch) { case 'C': s->colour = false; break; case 'i': FLAG_SET(s->diff_flags, FNC_DIFF_INVERT); break; case 'l': if (FLAG_CHK(s->diff_flags, FNC_DIFF_SIDEBYSIDE)) FLAG_CLR(s->diff_flags, FNC_DIFF_SIDEBYSIDE); FLAG_SET(s->diff_flags, FNC_DIFF_LINENO); break; case 'P': FLAG_CLR(s->diff_flags, FNC_DIFF_PROTOTYPE); break; case 'q': FLAG_CLR(s->diff_flags, FNC_DIFF_VERBOSE); break; case 's': if (FLAG_CHK(s->diff_flags, FNC_DIFF_LINENO)) FLAG_CLR(s->diff_flags, FNC_DIFF_LINENO); FLAG_SET(s->diff_flags, FNC_DIFF_SIDEBYSIDE); break; case 'W': FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_EOLWS); break; case 'w': FLAG_SET(s->diff_flags, FNC_DIFF_IGNORE_ALLWS); /* FALL THROUGH */ default: break; } } fsl_free(opt); fnc_init.sbs ? FLAG_SET(s->diff_flags, FNC_DIFF_SIDEBYSIDE) : 0; if (fnc_init.showln) { /* Can't be activated if sbs is already set so clear it. */ if (FLAG_CHK(s->diff_flags, FNC_DIFF_SIDEBYSIDE)) FLAG_CLR(s->diff_flags, FNC_DIFF_SIDEBYSIDE); FLAG_SET(s->diff_flags, FNC_DIFF_LINENO); } } static void show_diff_status(struct fnc_view *view) { mvwaddstr(view->window, 0, 0, "generating diff..."); updatescreen(view->window, true, true); } |
︙ | ︙ | |||
4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 | * checked-in versions: the former compares on disk file content with * 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; | > > > > > > > > > > > > > > > > | 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 | * checked-in versions: the former compares on disk file content with * 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); if (s->patch) { char *dflt = fsl_mprintf("path [%.10s.patch]: ", s->id2); struct input in = {NULL, dflt, INPUT_ALPHA, SR_CLREOL}; fnc_prompt_input(s->parent_view ? s->parent_view->child : NULL, &in); fsl_free(dflt); if (!in.buf[0]) { fsl_strlcpy(in.buf, s->id2, 11); fsl_strlcat(in.buf, ".patch", sizeof(in.buf)); } s->patch = false; rc = fsl_buffer_to_filename(&s->buf, in.buf); if (rc) goto end; } /* * 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; |
︙ | ︙ | |||
4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 | 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; | > > > < < | | 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 | 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; rc = add_line_type(&s->dlines, LINE_DIFF_META, &s->ndlines, n, true); if (rc) goto end; if ((n = fprintf(s->f,"%s %s\n", s->selected_entry->type, s->selected_entry->uuid)) < 0) goto end; off += n; 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); |
︙ | ︙ | |||
5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 | } 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; | > | | 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 | } 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_blame_view_state *bs; 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 down = false; nlines = s->nlines; s->lineno = s->first_line_onscreen - 1 + s->selected_line; switch (ch) { case '0': view->pos.col = 0; |
︙ | ︙ | |||
6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 | 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; | > > > > > | > > | 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 | return rc; view_set_child(view, branch_view); view->focus_child = true; } else *new_view = branch_view; break; } case 'P': s->patch = true; rc = create_diff(s); break; case 'c': case 'i': case 'L': case 'p': case 'S': case 'v': case 'W': case 'w': if (ch == 'c') s->colour = !s->colour; /* LSipvWw 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_EOLWS); 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) { |
︙ | ︙ | |||
6157 6158 6159 6160 6161 6162 6163 | rc = reset_diff_view(view, true); } break; case CTRL('j'): case '>': case '.': case 'J': | | | > | | | | | | | | | > > > > > > | | > > > > > > > > > | | < > > | 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 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 | rc = reset_diff_view(view, true); } break; case CTRL('j'): case '>': case '.': case 'J': down = true; /* FALL THROUGH */ case CTRL('k'): case '<': case ',': case 'K': if (s->parent_view == NULL) break; if (s->parent_view->vid == FNC_VIEW_TIMELINE) { tlstate = &s->parent_view->state.timeline; previous_selection = tlstate->selected_entry; rc = tl_input_handler(NULL, s->parent_view, down ? KEY_DOWN : KEY_UP); if (rc) break; if (previous_selection == tlstate->selected_entry) break; rc = set_selected_commit(s, tlstate->selected_entry); } else if (s->parent_view->vid == FNC_VIEW_BLAME) { bs = &s->parent_view->state.blame; fsl_uuid_cstr 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) { s->selected_line = 1; rc = reset_diff_view(view, false); } /* FALL THROUGH */ default: break; } return rc; } |
︙ | ︙ | |||
6625 6626 6627 6628 6629 6630 6631 | } static void usage_diff(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s diff [-C|--no-colour] [-R path] [-h|--help] " | | > | < | 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 | } 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] [-P|--no-prototype] " "[-q|--quiet] [-s|--sbs] [-W|--whitespace-eol] [-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) { |
︙ | ︙ | |||
6686 6687 6688 6689 6690 6691 6692 | 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; | < | 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))) |
︙ | ︙ | |||
6834 6835 6836 6837 6838 6839 6840 | rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) goto end; | < < < < < < < < < < | < < | 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 | 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; } rc = open_diff_view(view, commit, &paths, NULL, showmeta); if (!rc) rc = view_loop(view); end: fsl_free(path0); fsl_deck_finalize(&d); fsl_stmt_finalize(q); if (commit) |
︙ | ︙ | |||
6946 6947 6948 6949 6950 6951 6952 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) | < < < | 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; } |
︙ | ︙ | |||
7190 7191 7192 7193 7194 7195 7196 | *tree = NULL; *tree = fsl_malloc(sizeof(**tree)); if (*tree == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_malloc"); memset(*tree, 0, sizeof(**tree)); | > | > > > > | 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 | *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. * If dir is the root of the repository tree (i.e., "/"), only tree * nodes (tn) with no parent_dir belong to this tree. Otherwise, tree * nodes whose parent_dir matches dir will comprise the requested tree. */ 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)); |
︙ | ︙ | |||
8235 8236 8237 8238 8239 8240 8241 | const char *opt = NULL, *value = NULL; char *prev, *v; enum fnc_opt_id setid; int rc = FSL_RC_OK; rc = init_unveil(REPODIR, CKOUTDIR, true); if (rc) | < < < | 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 | const char *opt = NULL, *value = NULL; 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); |
︙ | ︙ | |||
8431 8432 8433 8434 8435 8436 8437 | }; rc = set_colour_scheme(s, pairs_blame, regexp_blame, nitems(regexp_blame)); break; } case FNC_VIEW_BRANCH: { static const char *regexp_branch[] = { | | | 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 | }; 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, |
︙ | ︙ | |||
8752 8753 8754 8755 8756 8757 8758 | } rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) | < < < | 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; } |
︙ | ︙ | |||
9415 9416 9417 9418 9419 9420 9421 | *gtl : (view->nlines - 3) / 2 + 1; *gtl = 0; return true; } static int | | | 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 | *gtl : (view->nlines - 3) / 2 + 1; *gtl = 0; return true; } static int blame_input_handler(struct fnc_view **alt_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) { |
︙ | ︙ | |||
9440 9441 9442 9443 9444 9445 9446 | break; case KEY_LEFT: case 'h': view->pos.col -= MIN(view->pos.col, 2); break; case 'q': s->done = true; | < < | 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; |
︙ | ︙ | |||
9631 9632 9633 9634 9635 9636 9637 | 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 | | | 9788 9789 9790 9791 9792 9793 9794 9795 9796 9797 9798 9799 9800 9801 9802 | 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 *alt_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; |
︙ | ︙ | |||
9655 9656 9657 9658 9659 9660 9661 | 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; } | > > > > > > | | | | | | | | > | < < > > | < | | > | < < | 9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 | 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 (*alt_view) { /* release diff resources before opening new */ rc = close_diff_view(*alt_view); if (rc) break; diff_view = *alt_view; } else { 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, NULL, view, true); s->selected_entry = commit; if (rc) { fnc_commit_artifact_close(commit); view_close(diff_view); break; } if (*alt_view) /* view is already active */ break; view->active = false; diff_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (!rc) { view_set_child(view, diff_view); view->focus_child = true; } } else *alt_view = diff_view; break; } case KEY_RESIZE: if (s->selected_line > view->nlines - 2) { s->selected_line = MIN(s->blame.nlines, view->nlines - 2); } |
︙ | ︙ | |||
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) { | > > | 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 | 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); if (s->selected_entry) fnc_commit_artifact_close(s->selected_entry); return rc; } static int stop_blame(struct fnc_blame *blame) { |
︙ | ︙ | |||
9889 9890 9891 9892 9893 9894 9895 | glob = fsl_strdup(fcli_next_arg(true)); rc = init_curses(); if (rc) goto end; rc = init_unveil(REPODB, CKOUTDIR, false); if (rc) | < < < | 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; } |
︙ | ︙ | |||
10031 10032 10033 10034 10035 10036 10037 | 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); | | | | 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 | 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 (!brname || !*brname || (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; |
︙ | ︙ | |||
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 | { 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; } | > | > > > > > | < | | | < < | | | > > > | 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 | { 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; attr_t rx = A_BOLD; 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) || view->active) rx |= A_REVERSE; if (s->colour) c = get_colour(&s->colours, FNC_COLOUR_BRANCH_CURRENT); if (c) rx |= COLOR_PAIR(c->scheme); wattron(view->window, rx); waddwstr(view->window, wline); while (width < view->ncols) { waddch(view->window, ' '); ++width; } wattroff(view->window, rx); 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%s", be->branch->open ? '+' : '-', s->show_id ? be->branch->id : "", s->show_id ? " " : "", s->show_date ? be->branch->date : "", s->show_date ? " " : "", be->branch->name, be->branch->private ? "*" : "", be->branch->current ? "@" : ""); 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); |
︙ | ︙ | |||
10251 10252 10253 10254 10255 10256 10257 | 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); | | | 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 | 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) 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; |
︙ | ︙ | |||
10744 10745 10746 10747 10748 10749 10750 | *ret = n; return FSL_RC_OK; } static int fnc_prompt_input(struct fnc_view *view, struct input *input) { | | | > | < | < | 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 | *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 ? view->window : stdscr); 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_ALL); else if (rc == FSL_RC_RANGE || n < min || n > max) rc = sitrep(view, "-- line outside range --", SR_ALL); else input->ret = n; } return rc; } |
︙ | ︙ | |||
10792 10793 10794 10795 10796 10797 10798 | return rc == ERR ? FSL_RC_ERROR : FSL_RC_OK; } static int sitrep(struct fnc_view *view, const char *msg, int flags) { | > > > | | | | | 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 | return rc == ERR ? FSL_RC_ERROR : FSL_RC_OK; } static int sitrep(struct fnc_view *view, const char *msg, int flags) { WINDOW *win = view ? view->window : stdscr; int line = (view ? view->nlines : LINES) - 1; wattr_on(win, A_BOLD, NULL); mvwaddstr(win, line, 0, msg); if (FLAG_CHK(flags, SR_CLREOL)) wclrtoeol(win); wattr_off(win, 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)) |
︙ | ︙ | |||
10912 10913 10914 10915 10916 10917 10918 | 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"); } | | | 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095 | 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 FSL_RC_OK; } 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; |
︙ | ︙ | |||
10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 | * 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(). */ | > | | | > > > > > > > > > > > > > > > > > > > > > > > > | 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 | * 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. */ #ifndef HAVE_LANDLOCK 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 (ckoutdir && unveil(ckoutdir, "rwc") == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "unveil(%s, \"rwc\")", 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; } #else /* HAVE_LANDLOCK */ static const char * gettzfile(void) { static char ret[PATH_MAX]; const char *tzdir, *tz; size_t n; if ((tz = getenv("TZ"))) { if ((tzdir = getenv("TZDIR"))) { n = fsl_strlcpy(ret, tzdir, sizeof(ret)); if (ret[n - 1] != '/') fsl_strlcat(ret, "/", sizeof(ret)); } else fsl_strlcpy(ret, "/usr/share/zoneinfo/", sizeof(ret)); n = fsl_strlcat(ret, tz, sizeof(ret)); } else n = fsl_strlcpy(ret, "/etc/localtime", sizeof(ret)); if (n >= sizeof(ret) || fsl_file_size(ret) == -1) return NULL; /* bogus $TZ{DIR} or file doesn't exist */ return ret; } /* * 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, |
︙ | ︙ | |||
11001 11002 11003 11004 11005 11006 11007 | #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 | < | > > > | | | | < | < < < < < < | > > > | | > > > | | > > > > < > | 11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 11210 11211 11212 11213 11214 11215 11216 11217 11218 11219 11220 11221 11222 11223 11224 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 11271 11272 | #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) { #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) /* * Define default block list of _all_ possible operations. * XXX Due to landlock's fail-open design, set all the bits to avoid * following Landlock for new ops to add to this deny-by-default list. */ struct landlock_ruleset_attr attr = { .handled_access_fs = ((LANDLOCK_ACCESS_FS_MAKE_SYM << 1) - 1) }; struct landlock_path_beneath_attr path_beneath; int i, rfd, rc = FSL_RC_OK; 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) { struct stat sb; if (paths[i] == NULL) continue; path_beneath.parent_fd = open(paths[i], O_RDONLY | O_CLOEXEC); if (path_beneath.parent_fd == -1 || (rc = fstat(path_beneath.parent_fd, &sb))) { if (rc) close(path_beneath.parent_fd); rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "landlock: failed to read '%s'", paths[i]); } else { path_beneath.allowed_access = LANDLOCK_ACCESS_DIR; if (!S_ISDIR(sb.st_mode)) path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE; 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); return rc; } #endif /* HAVE_LANDLOCK */ |