Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.13 To 0.12
2022-11-26 06:01 | bump version number: 0.14 (check-in: be94040192 user: mark tags: trunk) | |
2022-11-26 06:00 | CHANGES for 0.13 (check-in: 0b85faeb8a user: mark tags: trunk, 0.13) | |
2022-11-25 05:15 | latest upstream libfossil: 46008704a620 (check-in: f745a2bcb8 user: mark tags: trunk) | |
2022-05-09 12:18 | Bump version number: 0.13 (check-in: b56d1390f5 user: mark tags: trunk) | |
2022-05-09 12:15 | CHANGES for 0.12 (check-in: 671ad5159a user: mark tags: trunk, 0.12) | |
2022-05-09 11:33 | Fix blame regression when opening child diff view in vsplit. (check-in: 5d1ac567c2 user: mark tags: trunk) | |
Changes to CHANGES.md.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | **fnc 0.12** 2022-05-09 - replace \s regexp with portable [[:space:]] character class (patch by Ashish) - fix blame->diff child split view regression from 0.9 introduced in [d05828fbb] **fnc 0.11** 2022-05-08 - handle diff of non-versioned files outside the work tree (reported by Dan) - replace getpagesize() with portable sysconf(_SC_PAGESIZE) (patch by mgagnon) - improve robustness by guarding against piped input abuse (reported by Dan) - improve documentation regarding UTF-8 character encoding and fonts - implement horizontal scroll of the log message summary line in timeline view - fix out-of-bounds UB on 32-bit builds (reported by mgagnon) |
︙ | ︙ | |||
66 67 68 69 70 71 72 | - display hunk index in `fnc stash` input prompt - documentation improvements in README - update in-tree SQLite lib to 3.38.5 with multiple bug fixes - add `apply` alias for `fnc stash get` to facilitate Fossiler muscle memory - simplify error, and tailor usage, reporting when handling invalid input - remove redundant fcli_has_unused_args() call in main() | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | - display hunk index in `fnc stash` input prompt - documentation improvements in README - update in-tree SQLite lib to 3.38.5 with multiple bug fixes - add `apply` alias for `fnc stash get` to facilitate Fossiler muscle memory - simplify error, and tailor usage, reporting when handling invalid input - remove redundant fcli_has_unused_args() call in main() **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 |
︙ | ︙ | |||
91 92 93 94 95 96 97 | - 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 | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | - 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] - implement diff `--line-numbers` opt (`L` keymap) to display file line numbers |
︙ | ︙ | |||
115 116 117 118 119 120 121 | - clean code of OS-dependent ifdefs by consolidating them in called functions - add support for landlock Linux security module in Linux builds - significant diff driver refactoring for finer granularity in view manipulation - implement `--sbs` (`S` keymap) to display side-by-side formatted diffs - add FNC_COLOUR_DIFF_SBS_EDIT option to set colour of edited lines in SBS diff - plug small memory leak when interactively changing diff format | | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | - clean code of OS-dependent ifdefs by consolidating them in called functions - add support for landlock Linux security module in Linux builds - significant diff driver refactoring for finer granularity in view manipulation - implement `--sbs` (`S` keymap) to display side-by-side formatted diffs - add FNC_COLOUR_DIFF_SBS_EDIT option to set colour of edited lines in SBS diff - plug small memory leak when interactively changing diff format **fnc 0.8** 2022-01-10 - fix vertical split view init regression from 0.7 when terminal < 120 cols wide - fix DB lock when opening horizontal split that signals the timeline thread **fnc 0.7** 2022-01-09 - factor out common make(1) and gmake build bits - make build depend on make file - make all commands compatible with `-R|--repository` (i.e., no checkout needed) - implement Vim-like smartcase for commands that filter repository results - improve timeline arg parsing for more informative output upon invalid input - plug small memory leak when the timeline command fails to initialise a view |
︙ | ︙ | |||
163 164 165 166 167 168 169 | - improve view request handling and optimise new view initialisation - implement `C-n/C-p` keymap to navigate to the next/previous file in the diff - implement (F)ile keymap to navigate directly to a file in the diff - apply upstream diff v2 fix for rendering untouched lines as changed - implement Vim-like `C-d/C-u` keymaps to scroll the view down/up half a page - enhance search behaviour in all views | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | - improve view request handling and optimise new view initialisation - implement `C-n/C-p` keymap to navigate to the next/previous file in the diff - implement (F)ile keymap to navigate directly to a file in the diff - apply upstream diff v2 fix for rendering untouched lines as changed - implement Vim-like `C-d/C-u` keymaps to scroll the view down/up half a page - enhance search behaviour in all views **fnc 0.6** 2021-11-22 - implement support for user-defined colours in tl, diff, tree and blame views - make branch view accessible from all other views - optimise colour scheme initialisation - implement new 'fnc config' interface to configure per-project settings - improve build by making make file make(1) compatible - rename old make file to GNUmakefile to keep gmake support |
︙ | ︙ | |||
188 189 190 191 192 193 194 | - improved branch view resource deallocation - implement new timeline command --filter option and key binding - enhance timeline --type option with support for tag control artifacts - general documentation improvements - assorted updates from upstream libfossil including compiler warning fix - expand support for user-defined colours to include the branch view | | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | - improved branch view resource deallocation - implement new timeline command --filter option and key binding - enhance timeline --type option with support for tag control artifacts - general documentation improvements - assorted updates from upstream libfossil including compiler warning fix - expand support for user-defined colours to include the branch view **fnc 0.5** 2021-11-03 - simplify the build system to make-only and remove autosetup (patch from sdk) - remove invalid fsl_errno_to_rc() call and redundant alloc error check - reduce noise on app exit by removing redundant error output - relocate fnc_strlcpy() and fnc_strlcat() routines to the library **fnc 0.4** 2021-10-31 - resolve database bug in the commit builder logic - prune dead code leftover from the initial single-threaded blame implementation - improve error handling of commands that return no records - update in-app help with blame key bindings - expand tree command's ability to display trees of versions with missing files - improve tree command error handling of non-checkin artifacts |
︙ | ︙ | |||
227 228 229 230 231 232 233 | - optimise parsing of artifacts to determine which diff routine to call - implement `fnc branch` which displays a navigable list of repository branches - expand available sort options to the branch command - simplify usage output on error or `-h|--help` with usage callback - general man page and documentation improvements - fix incorrect configure `--prefix` install path in the docs (reported by sdk) | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | - optimise parsing of artifacts to determine which diff routine to call - implement `fnc branch` which displays a navigable list of repository branches - expand available sort options to the branch command - simplify usage output on error or `-h|--help` with usage callback - general man page and documentation improvements - fix incorrect configure `--prefix` install path in the docs (reported by sdk) **fnc 0.3** 2021-10-17 - add in-app help with H|?|F1 key binding - improvements to the build system - decompose build_commits() to make reusable commit_builder() method - dynamically size header so metadata is not truncated in timeline view - fix highlight of coloured search results in tmux with A_REVERSE attribute - correct install path of man pages with 'make install' |
︙ | ︙ | |||
251 252 253 254 255 256 257 | - substantial UX and performance improvements by making 'fnc blame' threaded - fix bug in 'fnc blame --limit|-n' calls when lines were not annotated - enable handling of master branches not named 'trunk' in 'fnc blame' - enable accessing the tree interface from the timeline with new 't' key binding - add '-C|--no-colour' and 'c' key binding to 'fnc blame' - enhance and standardise parsing of path arguments for all commands | | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | - substantial UX and performance improvements by making 'fnc blame' threaded - fix bug in 'fnc blame --limit|-n' calls when lines were not annotated - enable handling of master branches not named 'trunk' in 'fnc blame' - enable accessing the tree interface from the timeline with new 't' key binding - add '-C|--no-colour' and 'c' key binding to 'fnc blame' - enhance and standardise parsing of path arguments for all commands **fnc 0.2** 2021-09-04 - fix iconv lib linking in macOS builds - use pkg-config for detecting ncurses libs - prune dead auto.def code - bypass pkg-config in configure script on macOS systems - major overhaul to use amalgamation build - add key bindings to jump to start and end of view |
︙ | ︙ | |||
273 274 275 276 277 278 279 | - enhance timeline --commit|-c option to accept symbolic check-in names - add man page installation to make install target - plug small memory leak in cmd_diff() routine - fix screen rendering bug due to late deallocation of resources - improve multibyte to wide character conversion error handling/reporting - fix bug involving renamed files in diff view | | < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | - enhance timeline --commit|-c option to accept symbolic check-in names - add man page installation to make install target - plug small memory leak in cmd_diff() routine - fix screen rendering bug due to late deallocation of resources - improve multibyte to wide character conversion error handling/reporting - fix bug involving renamed files in diff view **fnc 0.1** 2021-08-28 - initial commit of infrastructure for the timeline command - add branch names and tags to search criteria - enhance parsing of malformed RFC822 email addresses in commit usernames - display wiki page content of initial wiki commits - make display of technote and ticket commits more consistent with fossil(1) ui - fix bug where diff algorithm choked on binary files - add coloured output to diff view - replace BSD-specific getprogname() calls with libf API - enhance diff view to display more informative control artifact commits - add Linux support so we now build and run on OpenBSD, macOS, and Linux - wrap commit comments to the current view width - implement cmd_diff() to provide the 'fnc diff' interface - tailor help/usage output to the specified command - fix invalid memory read in diff routine - add support for repository fingerprint version 1 - fix line wrap bug that truncated lines in fullscreen mode |
Changes to README.md.
1 2 | # README | > | > | < | | | | | | | | | < < | < < < < | | | | | | | | | | | | | | | | | | | | | | < | | > | | | | | | | | | | | | | | | | | > | | < | | | | | > | > | < < | < < < | 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | # README # fnc 0.12 ## 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 noted inline): 1. OpenBSD 6.8-, 6.9-, and 7.0-release 2. macOS 10.15.7 (Catalina) and 11.5.2 (Big Sur) 3. Linux Mint 20.2 (32- and 64-bit ARM) 4. Ubuntu 18.04 running Linux kernel 5.11 (32-bit ARM) 5. Debian GNU/Linux 8, 9, and 10 6. CentOS 6.5 (32-bit) Alpha development notwithstanding, the `timeline`, `diff`, `tree`, `blame`, and `branch` commands are relatively stable; however, there is no commitment to refrain from breaking changes. # 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` 3. build fnc - `make` 4. install the `fnc` binary (*requires privileges*) - `doas make install` 5. move into an open Fossil checkout, and run it: - `cd ~/museum/repo && fossil open ../repo.fossil && fnc` This will install the `fnc` executable and man page into `/usr/local/bin` and `/usr/local/share/man/man1`, respectively. Alternatively, cryptographically signed tarballs of the source code and binaries for some of the abovementioned platforms are available to [download][3]. # Doc Commands available in **fnc**: 1. [**stash**](/uv/resources/doc/fnc.1.html#stash) - interactively select hunks to stash from the diff of local changes on disk 2. [**timeline**](/uv/resources/doc/fnc.1.html#timeline) - hyperlinked chronological commit history of the repository 3. [**diff**](/uv/resources/doc/fnc.1.html#diff) - diff of all changes between commits or blobs 4. [**blame**](/uv/resources/doc/fnc.1.html#blame) - annotated file displaying commit attribution history of each line 5. [**tree**](/uv/resources/doc/fnc.1.html#tree) - navigable file hierarchy of the repository tree 6. [**branch**](/uv/resources/doc/fnc.1.html#branch) - hyperlinked list of all public and private branches 7. [**config**](/uv/resources/doc/fnc.1.html#config) - configure or view fnc settings See `fnc --help` for a quick reference, and the [fnc(1)][4] manual page for more comprehensive documentation. In-app help can also be accessed with the `?`, `F1`, or `H` key binding. The following video briefly demonstrates some of the key bindings in use. [![fnc demo][5]][6] # Why `fnc` is heavily inspired by [`tog`][7], which I missed when I left [Got][8] (Git) behind and started using Fossil. The objective is to provide an alternative to `fossil ui` without leaving the terminal. # Problems & Patches Please submit bug reports via [email][9], the [forum][10], or by creating a new [ticket][11]. As a rule, all reports should include a bug reproduction recipe; that is, either (1) the series of steps beginning with `fossil init` to create a new repository through to the `fnc` command that triggers the unexpected behaviour; or, if possible, (2) a shell script that contains all necessary ingredients to reproduce the problem. Patches are thoughtfully considered and can be sent to the [mailing list][12]. While `diff -up` patches are preferred, `fossil patch create` and `fossil diff` patches are also welcomed. Please ensure code conforms to the C99 standard, and complies with OpenBSD's KNF [style(9)][13]. Any patch containing user-visible code addition, modification, or deletion (i.e., code that impacts user interfaces) should concomitantly include updating documentation affected by the change. # Screenshots ![stash](https://fnc.bsdbox.org/uv/resources/img/fnc-stash.png "fnc stash") ![stash more](https://fnc.bsdbox.org/uv/resources/img/fnc-stash-more.png "stash more") ![stash help](https://fnc.bsdbox.org/uv/resources/img/fnc-stash-help.png "stash help") ![diff vsplit](https://fnc.bsdbox.org/uv/resources/img/fnc-diff-vsplit.png "diff vertical split") ![diff hsplit renamed](https://fnc.bsdbox.org/uv/resources/img/fnc-diff-hsplit-renamed.png "diff horizontal split file renamed") ![diff vsplit added](https://fnc.bsdbox.org/uv/resources/img/fnc-diff-vsplit-added.png "diff vertical split file added") ![diff vsplit removed](https://fnc.bsdbox.org/uv/resources/img/fnc-diff-vsplit-removed.png "diff vertical split file removed") ![blame vsplit](https://fnc.bsdbox.org/uv/resources/img/fnc-blame-vsplit.png "blame vertical split") ![tree vsplit](https://fnc.bsdbox.org/uv/resources/img/fnc-tree-vsplit.png "tree vertical split") ![branch hsplit](https://fnc.bsdbox.org/uv/resources/img/fnc-branch-hsplit.png "branch horizontal split") ![in-app help](https://fnc.bsdbox.org/uv/resources/img/fnc-inapp_help.png "fnc in-app help") ![timeline help](https://fnc.bsdbox.org/uv/resources/img/fnc-timeline-help.png "fnc timeline help") # Trivia **fnc** [fɪŋk] *noun* (n.) 1. an interactive ncurses browser for [Fossil][0] repositories *verb* (v.) 2. to inform etymology From the German word *Fink*, meaning "finch", a type of bird. [0]: https://fossil-scm.org [1]: https://fossil.wanderinghorse.net/r/libfossil [2]: https://fossil-scm.org/home/help?cmd=ui [3]: https://fnc.bsdbox.org/uv/download.html [4]: https://fnc.bsdbox.org/uv/resources/doc/fnc.1.html [5]: https://fnc.bsdbox.org/uv/resources/img/fnc-timeline-fullscreen.png [6]: https://itac.bsdbox.org/fnc-demo.mp4 [7]: https://gameoftrees.org/tog.1.html [8]: https://gameoftrees.org [9]: mailto:fnc@bsdbox.org [10]: https://fnc.bsdbox.org/forum [11]: https://fnc.bsdbox.org/ticket [12]: https://itac.bsdbox.org/listinfo/fnc [13]: https://man.openbsd.org/style.9 |
Changes to fnc.bld.mk.
1 2 3 4 5 6 7 8 | # # FNC Common Build # # CONFIGURATION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # # FNC Common Build # # CONFIGURATION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man VERSION ?= 0.12 # 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 \ |
︙ | ︙ | |||
40 41 42 43 44 45 46 | FOSSIL_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 # On SOME Linux (e.g., Ubuntu 18.04.6), we have to include wchar curses from # I/.../ncursesw, but linking to -lncursesw (w/ no special -L path) works fine. # FLAGS NEEDED TO BUILD FNC FNC_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 \ -I./lib -I./include -I/usr/include/ncursesw \ | | < | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | FOSSIL_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 # On SOME Linux (e.g., Ubuntu 18.04.6), we have to include wchar curses from # I/.../ncursesw, but linking to -lncursesw (w/ no special -L path) works fine. # FLAGS NEEDED TO BUILD FNC FNC_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 \ -I./lib -I./include -I/usr/include/ncursesw \ -D_XOPEN_SOURCE_EXTENDED -DVERSION=${VERSION} FNC_LDFLAGS = ${LDFLAGS} -lm -lutil -lz -lpthread -fPIC all: bin bin: lib/sqlite3.o lib/libfossil.o src/fnc.o src/fnc |
︙ | ︙ | |||
77 78 79 80 81 82 83 | uninstall: rm -f ${PREFIX}/bin/fnc ${PREFIX}${MANDIR}/man1/fnc.1 clean: rm -f lib/*.o src/*.o src/fnc release: clean | < < | < | 74 75 76 77 78 79 80 81 82 83 | 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/diff.h.
︙ | ︙ | |||
13 14 15 16 17 18 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "libfossil.h" #include "settings.h" | < < < | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | < | | < | < | > | | | < | < < | < < | | | > > > | < < < | | > | | | | | < | | | | | | | | | | | | | | | | < < < < | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "libfossil.h" #include "settings.h" enum fnc_diff_flag { FNC_DIFF_IGNORE_EOLWS = 0x01, FNC_DIFF_IGNORE_ALLWS = 0x03, FNC_DIFF_SIDEBYSIDE = 1 << 2, FNC_DIFF_VERBOSE = 1 << 3, FNC_DIFF_BRIEF = 1 << 4, FNC_DIFF_HTML = 1 << 5, FNC_DIFF_LINENO = 1 << 6, FNC_DIFF_NOOPT = 1 << 7, /* og. 0x0100 */ FNC_DIFF_INVERT = 1 << 8, /* og. 0x0200 */ FNC_DIFF_NOTTOOBIG = 1 << 9, /* og. 0x0800 */ FNC_DIFF_STRIP_EOLCR = 1 << 10, /* og. 0x1000 */ FNC_DIFF_ANSI_COLOR = 1 << 11, /* og. 0x2000 */ FNC_DIFF_PROTOTYPE = 1 << 12 #define FNC_DIFF_CONTEXT_EX (((uint64_t)0x04) << 32) /* Allow 0 context */ #define FNC_DIFF_CONTEXT_MASK ((uint64_t)0x0000ffff) /* Default context */ #define FNC_DIFF_WIDTH_MASK ((uint64_t)0x0fff0000) /* SBS column width */ }; struct diff_out_state { fsl_output_f out; /* Output callback */ void *state; /* State for this->out() */ enum line_type *lines; /* Diff line type (e.g., minus, plus) */ uint32_t nlines; /* Index into this->lines */ int rc; /* Error reporting */ char ansi; /* ANSI colour code */ struct { const fsl_buffer *file; /* Diffed file */ char *signature; /* Matching function */ uint32_t lastmatch; /* Match line index */ uint32_t lastline; /* Last line scanned */ fsl_size_t offset; /* Match byte offset */ } proto; }; static const struct diff_out_state diff_out_state_empty = { NULL, NULL, NULL, 0, 0, 0, { NULL, NULL, 0, 0, 0 } }; struct sbsline { struct diff_out_state *output; fsl_buffer *cols[5]; /* Pointers to output columns */ const char *tag; /* <span> tag */ const char *tag2; /* <span> tag */ int idx; /* Write tag before idx */ int end; /* Close tag before end */ int idx2; /* Write tag2 before idx2 */ int end2; /* Close tag2 before end2 */ int width; /* Max column width in diff */ bool esc; /* Escape html characters */ void *regex; /* Colour matching lines */ }; int fnc_diff_text_raw(fsl_buffer const *, fsl_buffer const *, int, int **); int fnc_diff_text_to_buffer(fsl_buffer const *, fsl_buffer const *, fsl_buffer *, enum line_type **, uint32_t *, short, short, int ); int fnc_diff_text(fsl_buffer const *, fsl_buffer const *, fsl_output_f, void *, short, short, int ); int fnc_diff_blobs(fsl_buffer const *, fsl_buffer const *, fsl_output_f, void *, enum line_type **, uint32_t *, uint16_t, short, int, int **); int fnc_output_f_diff_out(void *, void const *, fsl_size_t); int diff_outf(struct diff_out_state *, char const *, ... ); int diff_out(struct diff_out_state * const, void const *, fsl_int_t); char *match_chunk_function(struct diff_out_state *const, uint32_t); int buffer_copy_lines_from(fsl_buffer *const, const fsl_buffer *const, fsl_size_t *, fsl_size_t, fsl_size_t); uint64_t fnc_diff_flags_convert(int); int diff_context_lines(uint64_t); int match_dline(fsl_dline *, fsl_dline *); bool longest_common_subsequence(const char *z, int, const char *, int, int *); int unidiff(fsl__diff_cx *, struct diff_out_state *, void *, uint16_t, uint64_t); int unidiff_lineno(struct diff_out_state *, int, int, bool); int unidiff_txt( struct diff_out_state *const, char, fsl_dline *, int, void *); int sbsdiff(fsl__diff_cx *, struct diff_out_state *, void *, uint16_t, uint64_t); int max_sbs_width(fsl__diff_cx *, int *, uint16_t); unsigned short etcount(const char *str, unsigned short n); int sbsdiff_width(uint64_t); int sbsdiff_separator(struct sbsline *, int, int); int sbsdiff_lineno(struct sbsline *, int, int); void sbsdiff_shift_left(struct sbsline *, const char *); void sbsdiff_simplify_line(struct sbsline *, const char *); int sbsdiff_column(struct diff_out_state *, fsl_buffer const *, int); int sbsdiff_txt(struct sbsline *, fsl_dline *, int); int sbsdiff_newline(struct sbsline *); int sbsdiff_space(struct sbsline *, int, int); int sbsdiff_marker(struct sbsline *, const char *, const char *); int sbsdiff_close_gap(int *); unsigned char *sbsdiff_align(fsl_dline *, int, fsl_dline *, int); int sbsdiff_write_change(struct sbsline *, fsl_dline *, int, fsl_dline *, int); int add_line_type(enum line_type **, uint32_t *, enum line_type); |
Changes to include/settings.h.
︙ | ︙ | |||
30 31 32 33 34 35 36 | _(pfx, START_SETTINGS), \ _(pfx, COLOUR_COMMIT), \ _(pfx, COLOUR_USER), \ _(pfx, COLOUR_DATE), \ _(pfx, COLOUR_DIFF_META), \ _(pfx, COLOUR_DIFF_MINUS), \ _(pfx, COLOUR_DIFF_PLUS), \ | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | _(pfx, START_SETTINGS), \ _(pfx, COLOUR_COMMIT), \ _(pfx, COLOUR_USER), \ _(pfx, COLOUR_DATE), \ _(pfx, COLOUR_DIFF_META), \ _(pfx, COLOUR_DIFF_MINUS), \ _(pfx, COLOUR_DIFF_PLUS), \ _(pfx, COLOUR_DIFF_CHUNK), \ _(pfx, COLOUR_DIFF_TAGS), \ _(pfx, COLOUR_DIFF_SBS_EDIT), \ _(pfx, COLOUR_TREE_LINK), \ _(pfx, COLOUR_TREE_DIR), \ _(pfx, COLOUR_TREE_EXEC), \ _(pfx, COLOUR_BRANCH_OPEN), \ _(pfx, COLOUR_BRANCH_CLOSED), \ |
︙ | ︙ | |||
74 75 76 77 78 79 80 | _(pfx, DIFF_CHANGESET), \ _(pfx, DIFF_INDEX), \ _(pfx, DIFF_META), \ _(pfx, DIFF_MINUS), \ _(pfx, DIFF_PLUS), \ _(pfx, DIFF_EDIT), \ _(pfx, DIFF_CONTEXT), \ | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | _(pfx, DIFF_CHANGESET), \ _(pfx, DIFF_INDEX), \ _(pfx, DIFF_META), \ _(pfx, DIFF_MINUS), \ _(pfx, DIFF_PLUS), \ _(pfx, DIFF_EDIT), \ _(pfx, DIFF_CONTEXT), \ _(pfx, DIFF_CHUNK), \ _(pfx, DIFF_SEPARATOR) #define VIEW_MODE_ENUM(pfx, _) \ _(pfx, NONE), \ _(pfx, VERT), \ _(pfx, HRZN) |
︙ | ︙ |
Changes to lib/libfossil-config.h.
︙ | ︙ | |||
79 80 81 82 83 84 85 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #endif #endif /* _WIN32 */ | | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #endif #endif /* _WIN32 */ #define FSL_LIB_VERSION_HASH "2a405470c0d32a84f8644de1b5c509635a7e78cb" #define FSL_LIB_VERSION_TIMESTAMP "2021-11-18 17:40:56.695 UTC" #define FSL_LIB_CONFIG_TIME "2021-11-18 18:02 GMT" #if defined(_MSC_VER) #define FSL_PLATFORM_OS "windows" #define FSL_PLATFORM_IS_WINDOWS 1 #define FSL_PLATFORM_IS_UNIX 0 #define FSL_PLATFORM_PLATFORM "windows" #define FSL_PLATFORM_PATH_SEPARATOR ";" #define FSL_CHECKOUTDB_NAME "./_FOSSIL_" |
︙ | ︙ |
Changes to lib/libfossil.c.
1 | #include "libfossil.h" | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 | #include "libfossil.h" /* start of file ./src/fsl.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD |
︙ | ︙ | |||
902 903 904 905 906 907 908 | const fsl_card_Q fsl_card_Q_empty = fsl_card_Q_empty_m; const fsl_card_T fsl_card_T_empty = fsl_card_T_empty_m; const fsl_checkin_opt fsl_checkin_opt_empty = fsl_checkin_opt_empty_m; const fsl_cidiff_opt fsl_cidiff_opt_empty = fsl_cidiff_opt_empty_m; const fsl_cidiff_state fsl_cidiff_state_empty = fsl_cidiff_state_empty_m; const fsl_ckout_manage_opt fsl_ckout_manage_opt_empty = fsl_ckout_manage_opt_empty_m; | < < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | const fsl_card_Q fsl_card_Q_empty = fsl_card_Q_empty_m; const fsl_card_T fsl_card_T_empty = fsl_card_T_empty_m; const fsl_checkin_opt fsl_checkin_opt_empty = fsl_checkin_opt_empty_m; const fsl_cidiff_opt fsl_cidiff_opt_empty = fsl_cidiff_opt_empty_m; const fsl_cidiff_state fsl_cidiff_state_empty = fsl_cidiff_state_empty_m; const fsl_ckout_manage_opt fsl_ckout_manage_opt_empty = fsl_ckout_manage_opt_empty_m; const fsl_ckout_unmanage_opt fsl_ckout_unmanage_opt_empty = fsl_ckout_unmanage_opt_empty_m; const fsl_ckup_opt fsl_ckup_opt_empty = fsl_ckup_opt_m; const fsl_confirmer fsl_confirmer_empty = fsl_confirmer_empty_m; const fsl_cx fsl_cx_empty = fsl_cx_empty_m; const fsl_cx_config fsl_cx_config_empty = fsl_cx_config_empty_m; const fsl_cx_init_opt fsl_cx_init_opt_default = fsl_cx_init_opt_default_m; |
︙ | ︙ | |||
985 986 987 988 989 990 991 | }else{ /* realloc() */ return FLCA.f(FLCA.state, mem, n); } #undef FLCA } | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | }else{ /* realloc() */ return FLCA.f(FLCA.state, mem, n); } #undef FLCA } void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n){ if(!mem){ return malloc(n); }else if(!n){ free(mem); return NULL; }else{ return realloc(mem, n); |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 | void fsl_error_clear( fsl_error * const err ){ fsl_buffer_clear(&err->msg); *err = fsl_error_empty; } void fsl_error_reset( fsl_error * const err ){ err->code = 0; | | > | > > > | > > > | > > | < | < < < < | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | void fsl_error_clear( fsl_error * const err ){ fsl_buffer_clear(&err->msg); *err = fsl_error_empty; } void fsl_error_reset( fsl_error * const err ){ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem) err->msg.mem[0] = 0; } int fsl_error_copy( fsl_error const * const src, fsl_error * const dest ){ if(src==dest) return FSL_RC_MISUSE; else { int rc = 0; dest->msg.used = dest->msg.cursor = 0; dest->code = src->code; if(FSL_RC_OOM!=src->code){ rc = fsl_buffer_append( &dest->msg, src->msg.mem, src->msg.used ); } return rc; } } void fsl_error_move( fsl_error * const lower, fsl_error * const higher ){ fsl_error const err = *lower; *lower = *higher; lower->code = 0; lower->msg.used = lower->msg.cursor = 0; *higher = err; } int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ){ if(!code){ /* clear error state */ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem){ err->msg.mem[0] = 0; } return 0; }else{ int rc = 0; err->msg.used = err->msg.cursor = 0; err->code = code; if(FSL_RC_OOM!=code){ rc = fmt ? fsl_buffer_appendfv(&err->msg, fmt, args) : fsl_buffer_append(&err->msg, fsl_rc_cstr(code), -1); if(rc) err->code = rc; } return rc ? rc : code; } } int fsl_error_set( fsl_error * const err, int code, char const * fmt, ... ){ int rc; va_list args; va_start(args,fmt); |
︙ | ︙ | |||
1088 1089 1090 1091 1092 1093 1094 | : NULL; if(len) *len = err->msg.used; return err->code; } char const * fsl_rc_cstr(int rc){ | | | | > > < < | 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 | : NULL; if(len) *len = err->msg.used; return err->code; } char const * fsl_rc_cstr(int rc){ fsl_rc_e const RC = (fsl_rc_e)rc /* we do this so that gcc will warn if the switch() below is missing any fsl_rc_e entries. */ ; switch(RC){ #define STR(T) case FSL_RC_##T: return "FSL_RC_" #T STR(ACCESS); STR(ALREADY_EXISTS); STR(AMBIGUOUS); STR(BREAK); STR(SYNTAX); STR(CHECKSUM_MISMATCH); STR(CONFLICT); STR(CONSISTENCY); STR(DB); STR(DELTA_INVALID_OPERATOR); STR(DELTA_INVALID_SEPARATOR); STR(DELTA_INVALID_SIZE); STR(DELTA_INVALID_TERMINATOR); STR(DIFF_BINARY); STR(DIFF_WS_ONLY); STR(end); STR(ERROR); STR(INTERRUPTED); STR(IO); STR(MISSING_INFO); STR(MISUSE); STR(NOOP); STR(NOT_A_CKOUT); STR(NOT_A_REPO); STR(NOT_FOUND); STR(NYI); |
︙ | ︙ | |||
1134 1135 1136 1137 1138 1139 1140 | STR(SIZE_MISMATCH); STR(STEP_DONE); STR(STEP_ERROR); STR(STEP_ROW); STR(TYPE); STR(UNKNOWN_RESOURCE); STR(UNSUPPORTED); | < | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | STR(SIZE_MISMATCH); STR(STEP_DONE); STR(STEP_ERROR); STR(STEP_ROW); STR(TYPE); STR(UNKNOWN_RESOURCE); STR(UNSUPPORTED); #undef STR } return "Unknown result code"; } char const * fsl_library_version(){ return FSL_LIBRARY_VERSION; } bool fsl_library_version_matches(char const * yourLibVersion){ |
︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 | char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } fsl_size_t fsl_strlcpy(char * dst, const char * src, fsl_size_t dstsz){ fsl_size_t offset = 0; | | > > | | | | | | < > < < | 399 400 401 402 403 404 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 435 | char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } fsl_size_t fsl_strlcpy(char * dst, const char * src, fsl_size_t dstsz){ fsl_size_t offset = 0; if(dstsz<1){ goto end; } while((*(dst+offset) = *(src+offset))!='\0'){ if(++offset == dstsz){ --offset; break; } } end: *(dst+offset) = '\0'; while(*(src+offset)!='\0'){ ++offset; /* Return src length. */ } return offset; } fsl_size_t fsl_strlcat(char *dst, const char *src, fsl_size_t dstsz){ fsl_size_t offset; int dstlen, srclen, idx = 0; offset = dstlen = fsl_strlen(dst); srclen = fsl_strlen(src); while((*(dst+offset++) = *(src+idx++))!='\0'){ if(offset==dstsz-1){ break; } } *(dst+offset)='\0'; |
︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 | } } } void fsl_randomness(unsigned int n, void *tgt){ sqlite3_randomness((int)n, tgt); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 | } } } void fsl_randomness(unsigned int n, void *tgt){ sqlite3_randomness((int)n, tgt); } #undef MARKER #if defined(_WIN32) || defined(WIN32) #undef isatty #endif /* end of file ./src/fsl.c */ /* start of file ./src/annotate.c */ |
︙ | ︙ | |||
2328 2329 2330 2331 2332 2333 2334 | int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; fsl_buffer * const scratch = fsl__cx_scratchpad(f); fsl_annotate_step aStep; | < | < < | | 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 | int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; fsl_buffer * const scratch = fsl__cx_scratchpad(f); fsl_annotate_step aStep; assert(opt->out); if(opt->limitMs>0) fsl_timer_start(&ann.timer); rc = fsl__annotate_file(f, &ann, opt); if(rc) goto end; memset(&aStep,0,sizeof(fsl_annotate_step)); if(opt->dumpVersions){ struct AnnVers *av; |
︙ | ︙ | |||
2474 2475 2476 2477 2478 2479 2480 | If true, the %j (JSON string) format is enabled. */ #define FSLPRINTF_ENABLE_JSON 1 /* Most C compilers handle variable-sized arrays, so we enable that by default. Some (e.g. tcc) do not, so we provide a way | | < < < < < < < < | 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | If true, the %j (JSON string) format is enabled. */ #define FSLPRINTF_ENABLE_JSON 1 /* Most C compilers handle variable-sized arrays, so we enable that by default. Some (e.g. tcc) do not, so we provide a way to disable it: set FSLPRINTF_HAVE_VARARRAY to 0 One approach would be to look at: defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) but some compilers support variable-sized arrays even when not explicitly running in c99 mode. */ #if !defined(FSLPRINTF_HAVE_VARARRAY) # if defined(__TINYC__) # define FSLPRINTF_HAVE_VARARRAY 0 # else # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define FSLPRINTF_HAVE_VARARRAY 1 /*use 1 in C99 mode */ # else # define FSLPRINTF_HAVE_VARARRAY 0 # endif # endif #endif /* Conversion types fall into various categories as defined by the following enumeration. */ enum PrintfCategory {etRADIX = 1, /* Integer types. %d, %x, %o, and so forth */ |
︙ | ︙ | |||
2891 2892 2893 2894 2895 2896 2897 | It expects varg to be a string value, which it will preceed to encode using an URL encoding algothrim (certain characters are converted to %XX, where XX is their hex value) and passes the encoded string to pf(). It returns the total length of the output string. */ static int spech_urlencode( fsl_output_f pf, void * pfArg, | | | 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 | It expects varg to be a string value, which it will preceed to encode using an URL encoding algothrim (certain characters are converted to %XX, where XX is their hex value) and passes the encoded string to pf(). It returns the total length of the output string. */ static int spech_urlencode( fsl_output_f pf, void * pfArg, unsigned int pfLen, void * varg ){ char const * str = (char const *) varg; int rc = 0; char ch = 0; char const * hex = "0123456789ABCDEF"; #define xbufsz 10 char xbuf[xbufsz]; int slen = 0; |
︙ | ︙ | |||
3401 3402 3403 3404 3405 3406 3407 | # define FSLPRINTF_CHARARRAY_STACK(V) # define FSLPRINTF_CHARARRAY(V,N) char V##2[256]; \ char * V; \ if((int)(N)<((int)sizeof(V##2))){ \ V = V##2; \ }else{ \ V = (char *)fsl_malloc(N+1); \ | | | | | 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 | # define FSLPRINTF_CHARARRAY_STACK(V) # define FSLPRINTF_CHARARRAY(V,N) char V##2[256]; \ char * V; \ if((int)(N)<((int)sizeof(V##2))){ \ V = V##2; \ }else{ \ V = (char *)fsl_malloc(N+1); \ if(!V) {FSLPRINTF_RETURN;} \ } # define FSLPRINTF_CHARARRAY_FREE(V) if(V!=V##2) fsl_free(V) #endif /* FSLPRINTF_RETURN, FSLPRINTF_CHECKERR, and FSLPRINTF_SPACES are internal helpers. */ #define FSLPRINTF_RETURN if( zExtra ) fsl_free(zExtra); return pfrc #define FSLPRINTF_CHECKERR if( 0!=pfrc ) { FSLPRINTF_RETURN; } (void)0 #define FSLPRINTF_SPACES(N) \ { \ FSLPRINTF_CHARARRAY(zSpaces,N); \ memset( zSpaces,' ',N); \ pfrc = pfAppend(pfAppendArg, zSpaces, N); \ FSLPRINTF_CHARARRAY_FREE(zSpaces); \ FSLPRINTF_CHECKERR; \ |
︙ | ︙ | |||
3522 3523 3524 3525 3526 3527 3528 | : 0; /*fprintf(stderr,"char '%c'/%d @ %d, type=%c/%d\n",c,c,FMTNDX(c),infop->fmttype,infop->type);*/ if( infop ) xtype = infop->type; #undef FMTINFO #undef FMTNDX zExtra = 0; if( (!infop) || (!infop->type) ){ | | | 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 | : 0; /*fprintf(stderr,"char '%c'/%d @ %d, type=%c/%d\n",c,c,FMTNDX(c),infop->fmttype,infop->type);*/ if( infop ) xtype = infop->type; #undef FMTINFO #undef FMTNDX zExtra = 0; if( (!infop) || (!infop->type) ){ FSLPRINTF_RETURN; } /* Limit the precision to prevent overflowing buf[] during conversion */ if( precision>FSLPRINTF_BUF_SIZE-40 && (infop->flags & FLAG_STRING)==0 ){ precision = FSLPRINTF_BUF_SIZE-40; } |
︙ | ︙ | |||
3555 3556 3557 3558 3559 3560 3561 | xtype The class of the conversion. infop Pointer to the appropriate info struct. */ switch( xtype ){ case etPOINTER: flag_longlong = sizeof(char*)==sizeof(int64_t); flag_long = sizeof(char*)==sizeof(long int); | | | 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 | xtype The class of the conversion. infop Pointer to the appropriate info struct. */ switch( xtype ){ case etPOINTER: flag_longlong = sizeof(char*)==sizeof(int64_t); flag_long = sizeof(char*)==sizeof(long int); /* Fall through into the next case */ case etORDINAL: case etRADIX: if( infop->flags & FLAG_SIGNED ){ int64_t v; if( flag_longlong ) v = va_arg(ap,int64_t); else if( flag_long ) v = va_arg(ap,long int); else v = va_arg(ap,int); |
︙ | ︙ | |||
3817 3818 3819 3820 3821 3822 3823 | /* Sanitize path-like inputs, replacing \\ with /. */ int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char const *e = va_arg(ap,char const*); if( e && *e ){ length = StrNLen32(e, limit); zExtra = bufpt = fsl_malloc(length+1); | | | | 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 | /* Sanitize path-like inputs, replacing \\ with /. */ int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char const *e = va_arg(ap,char const*); if( e && *e ){ length = StrNLen32(e, limit); zExtra = bufpt = fsl_malloc(length+1); if(!zExtra) return -1; for( i=0; i<length; i++ ){ if( e[i]=='\\' ){ bufpt[i]='/'; }else{ bufpt[i]=e[i]; } } bufpt[length]='\0'; } break; } case etSTRINGID: { precision = flag_altform2 ? -1 : 16 /* In fossil(1) this is configurable, but in this lib we don't have access to that state from here. Fossil also has the '!' flag_altform2, which indicates that it should be for a URL, and thus longer than the default. We are only roughly approximating that behaviour here. */; /* Fall through */ } case etSTRING: { bufpt = va_arg(ap,char*); length = bufpt ? StrNLen32(bufpt, (precision>0 && flag_alternateform) ? precision*4/*max bytes per char*/ |
︙ | ︙ | |||
3895 3896 3897 3898 3899 3900 3901 | int check; bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; if((limit>=0) && (length>limit)) length = limit; check = fsl_bytes_fossilize((unsigned char const *)bufpt, length, &fb); if(check){ fsl_buffer_reserve(&fb,0); | | | 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 | int check; bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; if((limit>=0) && (length>limit)) length = limit; check = fsl_bytes_fossilize((unsigned char const *)bufpt, length, &fb); if(check){ fsl_buffer_reserve(&fb,0); FSLPRINTF_RETURN; } zExtra = bufpt = (char*)fb.mem /*transfer ownership*/; length = (int)fb.used; if( precision>=0 && precision<length ) length = precision; break; } |
︙ | ︙ | |||
4009 4010 4011 4012 4013 4014 4015 | } } if( zExtra ){ fsl_free(zExtra); zExtra = 0; } }/* End for loop over the format string */ | | | 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 | } } if( zExtra ){ fsl_free(zExtra); zExtra = 0; } }/* End for loop over the format string */ FSLPRINTF_RETURN; } /* End of function */ #undef FSLPRINTF_CHARARRAY_STACK #undef FSLPRINTF_CHARARRAY #undef FSLPRINTF_CHARARRAY_FREE #undef FSLPRINTF_SPACES |
︙ | ︙ | |||
4038 4039 4040 4041 4042 4043 4044 | va_end(vargs); return ret; } int fsl_fprintfv( FILE * fp, char const * fmt, va_list args ){ return (fp && fmt) ? fsl_appendfv( fsl_output_f_FILE, fp, fmt, args ) | | | 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 | va_end(vargs); return ret; } int fsl_fprintfv( FILE * fp, char const * fmt, va_list args ){ return (fp && fmt) ? fsl_appendfv( fsl_output_f_FILE, fp, fmt, args ) : -1; } int fsl_fprintf( FILE * fp, char const * fmt, ... ){ int ret; va_list vargs; va_start( vargs, fmt ); ret = fsl_appendfv( fsl_output_f_FILE, fp, fmt, vargs ); |
︙ | ︙ | |||
4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 | typedef struct fsl_snp_state fsl_snp_state; static int fsl_output_f_snprintf( void * arg, void const * data_, fsl_size_t n ){ char const * data = (char const *)data_; fsl_snp_state * st = (fsl_snp_state*) arg; if(n==0 || (st->pos >= st->len)) return 0; else if((n + st->pos) > st->len){ n = st->len - st->pos; } memcpy(st->dest + st->pos, data, n); st->pos += n; assert(st->pos <= st->len); | > | 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 | typedef struct fsl_snp_state fsl_snp_state; static int fsl_output_f_snprintf( void * arg, void const * data_, fsl_size_t n ){ char const * data = (char const *)data_; fsl_snp_state * st = (fsl_snp_state*) arg; assert(n>=0); if(n==0 || (st->pos >= st->len)) return 0; else if((n + st->pos) > st->len){ n = st->len - st->pos; } memcpy(st->dest + st->pos, data, n); st->pos += n; assert(st->pos <= st->len); |
︙ | ︙ | |||
4479 4480 4481 4482 4483 4484 4485 | #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) | | < | < | < < < < < < < < < | | < | < > | | < < | | | < | | | | 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 | #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) #define buffer_is_external(b) (b->mem && 0==b->capacity) /** Materializes external buffer b by allocating b->used+extra+1 bytes, copying b->used bytes from b->mem to the new block, NUL-terminating the block, and replacing b->mem with the new block. Returns 0 on success, else FSL_RC_OOM. Asserts that b is an external buffer. */ static int fsl__buffer_materialize( fsl_buffer * const b, fsl_size_t extra ){ assert(buffer_is_external(b)); fsl_size_t const n = b->used + extra + 1; unsigned char * x = (unsigned char *)fsl_malloc(n); if(!x) return FSL_RC_OOM; memcpy(x, b->mem, b->used); b->capacity = n; x[b->used] = 0; b->mem = x; return 0; } int fsl_buffer_materialize( fsl_buffer * const b ){ return buffer_is_external(b) ? fsl__buffer_materialize(b, 0) : 0; } #define buffer_materialize(B,N) (buffer_is_external(B) ? fsl__buffer_materialize((B),(N)) : 0) void fsl_buffer_external( fsl_buffer * const b, void const * mem, fsl_int_t n ){ if(b->mem) fsl_buffer_clear(b); if(n<0) n =(fsl_int_t)fsl_strlen((char const *)mem); b->used = n; b->cursor = 0; b->mem = (unsigned char *)mem; b->capacity = 0; } fsl_buffer * fsl_buffer_reuse( fsl_buffer * const b ){ if(buffer_is_external(b)){ *b = fsl_buffer_empty; }else{ if(b->capacity){ assert(b->mem); b->mem[0] = 0; b->used = 0; } b->cursor = 0; } return b; } void fsl_buffer_clear( fsl_buffer * const buf ){ if(buf->capacity) fsl_free(buf->mem); *buf = fsl_buffer_empty; } int fsl_buffer_reserve( fsl_buffer * const buf, fsl_size_t n ){ if( ! buf ) return FSL_RC_MISUSE; else if( 0 == n ){ if(!buffer_is_external(buf)){ fsl_free(buf->mem); }/* else if it has memory, it's owned elsewhere */ *buf = fsl_buffer_empty; return 0; }else if( !buffer_is_external(buf) && buf->capacity >= n ){ assert(buf->mem); return 0; }else{ unsigned char * x; bool const isExt = buffer_is_external(buf); assert((buf->used < n) && "Buffer in-use greater than capacity!"); if(isExt && n<=buf->used){ /*For external buffers, always keep at least the initially-pointed-to size. */ n = buf->used + 1; } x = (unsigned char *)fsl_realloc( isExt ? NULL : buf->mem, n ); if( !x ) return FSL_RC_OOM; else if(isExt){ memcpy( x, buf->mem, buf->used ); x[buf->used] = 0; }else{ memset( x + buf->used, 0, n - buf->used ); } buf->mem = x; buf->capacity = n; return 0; } } int fsl_buffer_resize( fsl_buffer * const b, fsl_size_t n ){ if(buffer_is_external(b)){ if(n==b->used) return 0; else if(n==0){ b->capacity = 0; fsl_buffer_external(b, "", 0); return 0; } unsigned char * x = (unsigned char *)fsl_malloc( n+1/*NUL*/ ); if( !x ) return FSL_RC_OOM; memcpy(x, b->mem, n < b->used ? n : b->used); x[n] = 0; b->mem = x; b->capacity = n+1; b->used = n; return 0; }else if(n && (b->capacity == n+1)){ b->used = n; b->mem[n] = 0; return 0; }else{ unsigned char * x = (unsigned char *)fsl_realloc( b->mem, n+1/*NUL*/ ); if( ! x ) return FSL_RC_OOM; if(n > b->capacity){ /* zero-fill new parts */ memset( x + b->capacity, 0, n - b->capacity +1/*NUL*/ ); } b->capacity = n + 1 /*NUL*/; b->used = n; b->mem = x; |
︙ | ︙ | |||
4633 4634 4635 4636 4637 4638 4639 | rc = (szL==szR) ? 0 : ((szL<szR) ? -1 : 1); } return rc; } | < < < < < < < < | 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 | rc = (szL==szR) ? 0 : ((szL<szR) ? -1 : 1); } return rc; } /* Compare two blobs in constant time and return zero if they are equal. Constant time comparison only applies for blobs of the same length. If lengths are different, immediately returns 1. */ int fsl_buffer_compare_O1(fsl_buffer const * const lhs, fsl_buffer const * const rhs){ fsl_size_t const szL = lhs->used; |
︙ | ︙ | |||
4666 4667 4668 4669 4670 4671 4672 | return rc; } int fsl_buffer_append( fsl_buffer * const b, void const * const data, fsl_int_t len ){ | < | | | | | | | | | | | | | < | | > | | | | | | 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 | return rc; } int fsl_buffer_append( fsl_buffer * const b, void const * const data, fsl_int_t len ){ fsl_size_t sz = b->used; if(len<0) len = (fsl_int_t)fsl_strlen((char const *)data); if(buffer_materialize(b, (fsl_size_t)len + 1)) return FSL_RC_OOM; assert(b->capacity ? !!b->mem : !b->mem); assert(b->used <= b->capacity); sz += len + 1/*NUL*/; int const rc = b->capacity<sz ? fsl_buffer_reserve( b, sz ) : 0; if(!rc){ assert(b->capacity >= sz); if(len>0) memcpy(b->mem + b->used, data, (size_t)len); b->used += len; b->mem[b->used] = 0; } return rc; } int fsl_buffer_appendfv( fsl_buffer * const b, char const * fmt, va_list args){ return fsl_appendfv( fsl_output_f_buffer, b, fmt, args ); } int fsl_buffer_appendf( fsl_buffer * const b, char const * fmt, ... ){ if(!b || !fmt) return FSL_RC_MISUSE; else{ int rc; va_list args; va_start(args,fmt); rc = fsl_buffer_appendfv( b, fmt, args ); va_end(args); return rc; } } char const * fsl_buffer_cstr(fsl_buffer const * const b){ return b ? (char const *)b->mem : NULL; } char const * fsl_buffer_cstr2(fsl_buffer const * const b, fsl_size_t * const len){ char const * rc = NULL; if(b){ rc = (char const *)b->mem; if(len) *len = b->used; } return rc; } char * fsl_buffer_str(fsl_buffer const * const b){ return (char *)b->mem; } #if 0 fsl_size_t fsl_buffer_size(fsl_buffer const * const b){ return b->used; } |
︙ | ︙ | |||
4819 4820 4821 4822 4823 4824 4825 | : -1; } fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b){ return fsl_data_uncompressed_size(b->mem, b->used); } | | < < | 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 | : -1; } fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b){ return fsl_data_uncompressed_size(b->mem, b->used); } int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut){ unsigned int nIn = pIn->used; unsigned int nOut = 13 + nIn + (nIn+999)/1000; fsl_buffer temp = fsl_buffer_empty; int rc = fsl_buffer_resize(&temp, nOut+4); if(rc) return rc; else{ unsigned long int nOut2; |
︙ | ︙ | |||
4857 4858 4859 4860 4861 4862 4863 | assert(outSize==pOut->used); } return rc; } } int fsl_buffer_compress2(fsl_buffer const *pIn1, | | < < < | 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 | assert(outSize==pOut->used); } return rc; } } int fsl_buffer_compress2(fsl_buffer const *pIn1, fsl_buffer const *pIn2, fsl_buffer *pOut){ unsigned int nIn = pIn1->used + pIn2->used; unsigned int nOut = 13 + nIn + (nIn+999)/1000; fsl_buffer temp = fsl_buffer_empty; int rc; rc = fsl_buffer_resize(&temp, nOut+4); if(rc) return rc; else{ |
︙ | ︙ | |||
4904 4905 4906 4907 4908 4909 4910 | fsl_buffer_reserve(&temp, 0); } return rc; } } int fsl_buffer_uncompress(fsl_buffer const * const pIn, fsl_buffer * const pOut){ | < < < | 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 | fsl_buffer_reserve(&temp, 0); } return rc; } } int fsl_buffer_uncompress(fsl_buffer const * const pIn, fsl_buffer * const pOut){ unsigned int nOut; unsigned char *inBuf; unsigned int const nIn = pIn->used; fsl_buffer temp = fsl_buffer_empty; int rc; unsigned long int nOut2; if(nIn<=4 || !fsl_data_is_compressed(pIn->mem, pIn->used)){ if(pIn==pOut || !pIn->mem) rc = 0; else{ fsl_buffer_reuse(pOut); rc = fsl_buffer_append(pOut, pIn->mem, pIn->used); } return rc; } inBuf = pIn->mem; nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; /* MARKER(("decompress size: %u\n", nOut)); */ if(pIn!=pOut && pOut->capacity>=nOut+1){ assert(pIn->mem != pOut->mem); |
︙ | ︙ | |||
4940 4941 4942 4943 4944 4945 4946 | */ fsl_buffer_external(&temp, pOut->mem, pOut->capacity); #else fsl_buffer_swap(&temp, pOut); #endif }else{ rc = fsl_buffer_reserve(&temp, nOut+1); | | | 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 | */ fsl_buffer_external(&temp, pOut->mem, pOut->capacity); #else fsl_buffer_swap(&temp, pOut); #endif }else{ rc = fsl_buffer_reserve(&temp, nOut+1); if(rc) return rc; temp.mem[nOut] = 0; } nOut2 = (long int)nOut; rc = uncompress(temp.mem, &nOut2, &inBuf[4], nIn - 4) /* In some libz versions (<1.2.4, apparently), valgrind says there's an uninitialized memory access somewhere under |
︙ | ︙ | |||
4985 4986 4987 4988 4989 4990 4991 | case Z_MEM_ERROR: rc = FSL_RC_OOM; break; case Z_BUF_ERROR: assert(!"Cannot happen!"); rc = FSL_RC_RANGE; break; default: rc = FSL_RC_ERROR; break; } if(temp.mem!=pOut->mem) fsl_buffer_clear(&temp); | | | < > > | 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 | case Z_MEM_ERROR: rc = FSL_RC_OOM; break; case Z_BUF_ERROR: assert(!"Cannot happen!"); rc = FSL_RC_RANGE; break; default: rc = FSL_RC_ERROR; break; } if(temp.mem!=pOut->mem) fsl_buffer_clear(&temp); return rc; } int fsl_buffer_fill_from( fsl_buffer * const dest, fsl_input_f src, void * const state ) { int rc; enum { BufSize = 512 * 8 }; char rbuf[BufSize]; fsl_size_t total = 0; fsl_size_t rlen = 0; if( !dest || ! src ) return FSL_RC_MISUSE; fsl_buffer_reuse(dest); while(1){ rlen = BufSize; rc = src( state, rbuf, &rlen ); if( rc ) break; total += rlen; if(total<rlen){ |
︙ | ︙ | |||
5033 5034 5035 5036 5037 5038 5039 | FILE * const src ){ return fsl_buffer_fill_from( dest, fsl_input_f_FILE, src ); } int fsl_buffer_fill_from_filename( fsl_buffer * const dest, char const * filename ){ | < | | < < < < | | | | > | < | 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 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 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 | FILE * const src ){ return fsl_buffer_fill_from( dest, fsl_input_f_FILE, src ); } int fsl_buffer_fill_from_filename( fsl_buffer * const dest, char const * filename ){ int rc; FILE * src; fsl_fstat st = fsl_fstat_empty; /* This stat() is only an optimization to reserve all needed memory up front. */ rc = fsl_stat( filename, &st, 1 ); if(!rc && st.size>0){ rc = fsl_buffer_reserve(dest, st.size +1/*NUL terminator*/); if(rc) return rc; } /* Else it might not be a real file, e.g. "-", so we'll try anyway... */ src = fsl_fopen(filename,"rb"); if(!src) rc = fsl_errno_to_rc(errno, FSL_RC_IO); else { rc = fsl_buffer_fill_from( dest, fsl_input_f_FILE, src ); fsl_fclose(src); } return rc; } void fsl_buffer_swap( fsl_buffer * left, fsl_buffer * right ){ fsl_buffer const tmp = *left; *left = *right; *right = tmp; } void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, int clearWhich ){ fsl_buffer_swap(left, right); if(0 != clearWhich) fsl_buffer_reserve((clearWhich<0) ? left : right, 0); } int fsl_buffer_copy( fsl_buffer * const dest, fsl_buffer const * const src ){ fsl_buffer_reuse(dest); return src->used ? fsl_buffer_append( dest, src->mem, src->used ) : 0; } int fsl_buffer_delta_apply2( fsl_buffer const * const orig, fsl_buffer const * const pDelta, fsl_buffer * const pTarget, fsl_error * const pErr){ int rc; fsl_size_t n = 0; fsl_buffer out = fsl_buffer_empty; rc = fsl_delta_applied_size( pDelta->mem, pDelta->used, &n); if(rc){ if(pErr){ fsl_error_set(pErr, rc, "fsl_delta_applied_size() failed."); } return rc; } rc = fsl_buffer_resize( &out, n ); if(rc) return rc; rc = fsl_delta_apply2( orig->mem, orig->used, pDelta->mem, pDelta->used, out.mem, pErr); if(0==rc){ fsl_buffer_swap(&out, pTarget); } fsl_buffer_clear(&out); return rc; } int fsl_buffer_delta_apply( fsl_buffer const * const orig, fsl_buffer const * const pDelta, fsl_buffer * const pTarget){ return fsl_buffer_delta_apply2(orig, pDelta, pTarget, NULL); } void fsl_buffer_defossilize( fsl_buffer * const b ){ fsl_bytes_defossilize( b->mem, &b->used ); } int fsl_buffer_to_filename( fsl_buffer const * const b, char const * fname ){ FILE * f; int rc = 0; if(!b || !fname) return FSL_RC_MISUSE; f = fsl_fopen(fname, "wb"); if(!f) rc = fsl_errno_to_rc(errno, FSL_RC_IO); else{ if(b->used) { |
︙ | ︙ | |||
5147 5148 5149 5150 5151 5152 5153 | delta->mem, &delta->used ); } return rc; } int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n ){ | > > | | < | 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 | delta->mem, &delta->used ); } return rc; } int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n ){ return (!state || !src) ? FSL_RC_MISUSE : fsl_buffer_append((fsl_buffer*)state, src, n); } int fsl_finalizer_f_buffer( void * state, void * mem ){ fsl_buffer * b = (fsl_buffer*)mem; fsl_buffer_reserve(b, 0); *b = fsl_buffer_empty; return 0; } int fsl_buffer_strftime(fsl_buffer * const b, char const * format, const struct tm *timeptr){ if(!b || !format || !*format || !timeptr) return FSL_RC_MISUSE; else{ enum {BufSize = 128}; char buf[BufSize]; fsl_size_t const len = fsl_strftime(buf, BufSize, format, timeptr); return len ? fsl_buffer_append(b, buf, (fsl_int_t)len) : FSL_RC_RANGE; } } int fsl_buffer_stream_lines(fsl_output_f fTo, void * const toState, fsl_buffer * const pFrom, fsl_size_t N){ char *z = (char *)pFrom->mem; fsl_size_t i = pFrom->cursor; fsl_size_t n = pFrom->used; fsl_size_t cnt = 0; int rc = 0; if( N==0 ) return 0; while( i<n ){ |
︙ | ︙ | |||
5201 5202 5203 5204 5205 5206 5207 | } int fsl_buffer_copy_lines(fsl_buffer * const pTo, fsl_buffer * const pFrom, fsl_size_t N){ #if 1 | < | 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 | } int fsl_buffer_copy_lines(fsl_buffer * const pTo, fsl_buffer * const pFrom, fsl_size_t N){ #if 1 return fsl_buffer_stream_lines( pTo ? fsl_output_f_buffer : NULL, pTo, pFrom, N ); #else char *z = (char *)pFrom->mem; fsl_size_t i = pFrom->cursor; fsl_size_t n = pFrom->used; fsl_size_t cnt = 0; |
︙ | ︙ | |||
5232 5233 5234 5235 5236 5237 5238 | pFrom->cursor = i; } return rc; #endif } int fsl_input_f_buffer( void * state, void * dest, fsl_size_t * n ){ | | < | 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 | pFrom->cursor = i; } return rc; #endif } int fsl_input_f_buffer( void * state, void * dest, fsl_size_t * n ){ fsl_buffer * b = (fsl_buffer*)state; fsl_size_t const from = b->cursor; fsl_size_t to; fsl_size_t c; if(from >= b->used){ *n = 0; return 0; } |
︙ | ︙ | |||
5289 5290 5291 5292 5293 5294 5295 | #endif return rc; } } char * fsl_buffer_take(fsl_buffer * const b){ char * z = NULL; | | < | 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 | #endif return rc; } } char * fsl_buffer_take(fsl_buffer * const b){ char * z = NULL; if(0==buffer_materialize(b,0)){ z = (char *)b->mem; *b = fsl_buffer_empty; } return z; } fsl_size_t fsl_buffer_seek(fsl_buffer * const b, fsl_int_t offset, fsl_buffer_seek_e whence){ int64_t c = (int64_t)b->cursor; switch(whence){ case FSL_BUFFER_SEEK_SET: c = offset; case FSL_BUFFER_SEEK_CUR: c = (int64_t)b->cursor + offset; break; case FSL_BUFFER_SEEK_END: c = (int64_t)b->used + offset; /* ^^^^^ fossil(1) uses (used + offset - 1) but That seems somewhat arguable because (used + 0 - 1) is at the last-written byte (or 1 before the begining), not the |
︙ | ︙ | |||
5334 5335 5336 5337 5338 5339 5340 | return b->cursor; } void fsl_buffer_rewind(fsl_buffer * const b){ b->cursor = 0; } | | | | | | | | < | | | | | | < | | 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 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 | return b->cursor; } void fsl_buffer_rewind(fsl_buffer * const b){ b->cursor = 0; } int fsl_id_bag_to_buffer(fsl_id_bag const * bag, fsl_buffer * b, char const * separator){ int i = 0; fsl_int_t const sepLen = (fsl_id_t)fsl_strlen(separator); int rc = fsl_buffer_reserve(b, b->used + (bag->entryCount * 7) + (bag->entryCount * sepLen)); for(fsl_id_t e = fsl_id_bag_first(bag); !rc && e; e = fsl_id_bag_next(bag, e)){ if(i++) rc = fsl_buffer_append(b, separator, sepLen); if(!rc) rc = fsl_buffer_appendf(b, "%" FSL_ID_T_PFMT, e); } return rc; } int fsl_buffer_append_tcl_literal(fsl_buffer * const b, bool escapeSquigglies, char const * z, fsl_int_t n){ int rc; if(n<0) n = fsl_strlen(z); rc = fsl_buffer_append(b, "\"", 1); for(fsl_int_t i=0; 0==rc && i<n; ++i){ char c = z[i]; bool skipSlash = false; switch( c ){ case '\r': c = 'r'; goto slash; case '}': case '{': skipSlash = !escapeSquigglies; /* fall through */ case '[': case ']': case '$': case '"': case '\\': slash: if(!skipSlash && (rc = fsl_buffer_append(b, "\\", 1))) break; /* fall through */ default: rc = fsl_buffer_append(b, &c, 1); } } if(0==rc) rc = fsl_buffer_append(b, "\"", 1); return rc; } #undef MARKER #undef buffer_is_external #undef buffer_materialize /* end of file ./src/buffer.c */ /* start of file ./src/cache.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt |
︙ | ︙ | |||
5662 5663 5664 5665 5666 5667 5668 | int rc; fsl_buffer * sql = 0; if(!db) return FSL_RC_NOT_A_CKOUT; sql = fsl__cx_scratchpad(f); if(0>=vid) vid = f->ckout.rid; if(zName && *zName && !('.'==*zName && !zName[1])){ | | | | | | | | | > | | | | > > | > | < < | 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 | int rc; fsl_buffer * sql = 0; if(!db) return FSL_RC_NOT_A_CKOUT; sql = fsl__cx_scratchpad(f); if(0>=vid) vid = f->ckout.rid; if(zName && *zName && !('.'==*zName && !zName[1])){ rc = fsl_buffer_appendf(sql, "SELECT id FROM vfile WHERE vid=%" FSL_ID_T_PFMT " AND fsl_match_vfile_or_dir(pathname,%Q)", vid, zName); }else{ rc = fsl_buffer_appendf(sql, "SELECT id FROM vfile WHERE vid=%" FSL_ID_T_PFMT, vid); } if(rc) goto end; else if(changedOnly){ rc = fsl_buffer_append(sql, " AND (chnged OR deleted OR rid=0 " "OR (origname IS NOT NULL AND " " origname<>pathname))", -1); if(rc) goto end; } rc = fsl_buffer_appendf(sql, " /* %s() */", __func__); if(rc) goto end; rc = fsl_db_prepare(db, &st, "%b", sql); while(!rc && (FSL_RC_STEP_ROW == (rc=fsl_stmt_step(&st)))){ rc = fsl_id_bag_insert( dest, fsl_stmt_g_id(&st, 0) ); } if(FSL_RC_STEP_DONE==rc) rc = 0; end: fsl__cx_scratchpad_yield(f, sql); fsl_stmt_finalize(&st); if(rc && !f->error.code && db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } int fsl_filename_to_vfile_id( fsl_cx * f, fsl_id_t vid, char const * zName, fsl_id_t * vfid ){ fsl_db * db = fsl_needs_ckout(f); int rc; fsl_stmt st = fsl_stmt_empty; assert(db); if(!db) return FSL_RC_NOT_A_CKOUT; else if(!zName || !fsl_is_simple_pathname(zName, true)){ return fsl_cx_err_set(f, FSL_RC_RANGE, |
︙ | ︙ | |||
9710 9711 9712 9713 9714 9715 9716 | for( int i = 0; NULL!=(dbName=dbNames[i]); ++i){ fsl_strlcpy(z + sz , dbName, (fsl_size_t)BufLen - sz); if(fsl_file_size(z)>=1024) return dbName; } return NULL; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 | for( int i = 0; NULL!=(dbName=dbNames[i]); ++i){ fsl_strlcpy(z + sz , dbName, (fsl_size_t)BufLen - sz); if(fsl_file_size(z)>=1024) return dbName; } return NULL; } #undef MARKER /* end of file ./src/checkout.c */ /* start of file ./src/cli.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD SPDX-FileCopyrightText: 2021 The Libfossil Authors SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ #include <string.h> /* for strchr() */ #if !defined(ORG_FOSSIL_SCM_FSL_CORE_H_INCLUDED) /* When not in the amalgamation build, force assert() to always work... */ # if defined(NDEBUG) # undef NDEBUG # define DEBUG 1 # endif #endif #include <assert.h> /* for the benefit of test apps */ /* Only for debugging */ #include <stdio.h> |
︙ | ︙ | |||
10124 10125 10126 10127 10128 10129 10130 | 0/*helpRequested*/, \ false/*versionRequested*/\ }, \ {/*config*/ \ -1/*traceSql*/, \ fsl_outputer_empty_m \ }, \ | < | 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 | 0/*helpRequested*/, \ false/*versionRequested*/\ }, \ {/*config*/ \ -1/*traceSql*/, \ fsl_outputer_empty_m \ }, \ fsl_error_empty_m/*err*/ \ } const fcli_t fcli_empty = fcli_empty_m; fcli_t fcli = fcli_empty_m; const fcli_cliflag fcli_cliflag_empty = fcli_cliflag_empty_m; static fsl_timer_state fcliTimer = fsl_timer_state_empty_m; |
︙ | ︙ | |||
10241 10242 10243 10244 10245 10246 10247 | static void fcli_shutdown(void){ fsl_cx * const f = fcli.f; int rc = 0; fsl_error_clear(&fcli.err); fsl_free(fcli.argv)/*contents are in the FCliFree list*/; | < | 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 | static void fcli_shutdown(void){ fsl_cx * const f = fcli.f; int rc = 0; fsl_error_clear(&fcli.err); fsl_free(fcli.argv)/*contents are in the FCliFree list*/; if(f){ while(fsl_cx_transaction_level(f)){ MARKER(("WARNING: open db transaction at shutdown-time. " "Rolling back.\n")); fsl_cx_transaction_end(f, true); } |
︙ | ︙ | |||
10302 10303 10304 10305 10306 10307 10308 | } static const fcli_cliflag FCliFlagsGlobal[] = { FCLI_FLAG_BOOL_X("?","help",NULL, fcli_flag_f_help, "Show app help. Also triggered if the first non-flag is \"help\"."), FCLI_FLAG_BOOL(0,"lib-version", &fcli.transient.versionRequested, | | | 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 | } static const fcli_cliflag FCliFlagsGlobal[] = { FCLI_FLAG_BOOL_X("?","help",NULL, fcli_flag_f_help, "Show app help. Also triggered if the first non-flag is \"help\"."), FCLI_FLAG_BOOL(0,"lib-version", &fcli.transient.versionRequested, "Show app version number."), FCLI_FLAG("R","repo","REPO-FILE",&fcli.transient.repoDbArg, "Selects a specific repository database, ignoring the one " "used by the current directory's checkout (if any)."), FCLI_FLAG(NULL,"user","username",&fcli.transient.userArg, "Sets the name of the fossil user name for this session."), FCLI_FLAG_BOOL_X(NULL, "no-checkout",NULL,fcli_flag_f_nocheckoutDir, "Disable automatic attempt to open checkout."), |
︙ | ︙ | |||
10451 10452 10453 10454 10455 10456 10457 | f-ci -m="message" ... simply doesn't fit the age-old muscle memory of: svn ci -m ... cvs ci -m ... fossil ci -m ... | < | 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 | f-ci -m="message" ... simply doesn't fit the age-old muscle memory of: svn ci -m ... cvs ci -m ... fossil ci -m ... */ for( f = defs; f->flagShort || f->flagLong; ++f ){ if(!f->flagValue && !f->callback){ /* We accept these for purposes of generating the --help text, but we can't otherwise do anything sensible with them and assume the app will handle such flags downstream or ignore them altogether.*/ |
︙ | ︙ | |||
10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 | rc = fcli__error->code; break; } //MARKER(("Got?=%d flag: %s/%s %s\n",gotIt, f->flagShort, f->flagLong, v ? v : "")); if(!gotIt){ continue; } if(f->flagValue) switch(f->flagType){ case FCLI_FLAG_TYPE_BOOL: *((bool*)f->flagValue) = true; break; case FCLI_FLAG_TYPE_BOOL_INVERT: *((bool*)f->flagValue) = false; break; | > | 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 | rc = fcli__error->code; break; } //MARKER(("Got?=%d flag: %s/%s %s\n",gotIt, f->flagShort, f->flagLong, v ? v : "")); if(!gotIt){ continue; } assert(f->flagValue || f->callback); if(f->flagValue) switch(f->flagType){ case FCLI_FLAG_TYPE_BOOL: *((bool*)f->flagValue) = true; break; case FCLI_FLAG_TYPE_BOOL_INVERT: *((bool*)f->flagValue) = false; break; |
︙ | ︙ | |||
10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 | FLAG("?") { ++fcli.transient.helpRequested; continue; } FLAG("V") { fcli.clientFlags.verbose += 1; continue; } FLAG("verbose") { fcli.clientFlags.verbose += 1; continue; } } #undef FLAG | > > > > > > > > | 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 | FLAG("?") { ++fcli.transient.helpRequested; continue; } FLAG("V") { fcli.clientFlags.verbose += 1; continue; } FLAG("VV") { fcli.clientFlags.verbose += 2; continue; } FLAG("VVV") { fcli.clientFlags.verbose += 3; continue; } FLAG("verbose") { fcli.clientFlags.verbose += 1; continue; } } #undef FLAG |
︙ | ︙ | |||
10710 10711 10712 10713 10714 10715 10716 | We copy fsl_lib_configurable.allocator as a base allocator. */ static fsl_allocator fslAllocOrig; /** Proxies fslAllocOrig.f() and abort()s on OOM conditions. */ | | | 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 | We copy fsl_lib_configurable.allocator as a base allocator. */ static fsl_allocator fslAllocOrig; /** Proxies fslAllocOrig.f() and abort()s on OOM conditions. */ static void * fsl_realloc_f_failing(void * state, void * mem, fsl_size_t n){ void * rv = fslAllocOrig.f(fslAllocOrig.state, mem, n); if(n && !rv){ fsl__fatal(FSL_RC_OOM, NULL)/*does not return*/; } return rv; } |
︙ | ︙ | |||
11033 11034 11035 11036 11037 11038 11039 | /* Accept either (help command) or (command help) as help. */ /* Except that it turns out that fcli_setup() will trump the former and doesn't have the fcli_command state, so can't do this. Maybe we can change that somehow. */ helpState = 1; helpPos = orig; arg = fcli_next_arg(1); // consume it | | | 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 | /* Accept either (help command) or (command help) as help. */ /* Except that it turns out that fcli_setup() will trump the former and doesn't have the fcli_command state, so can't do this. Maybe we can change that somehow. */ helpState = 1; helpPos = orig; arg = fcli_next_arg(1); // consume it }else if(0==fsl_strcmp(arg,cmd->name) || fcli_cmd_aliascmp(cmd,arg)){ if(!cmd->f){ rc = fcli_err_set(FSL_RC_NYI, "Command [%s] has no " "callback function."); }else{ fcli_next_arg(1)/*consume it*/; if(helpState){ |
︙ | ︙ | |||
11061 11062 11063 11064 11065 11066 11067 | } break; } } if(helpState){ f_out("\n"); fcli_command_help(helpPos, true, helpState>1); | < | | | | | 9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796 9797 9798 9799 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809 9810 9811 9812 9813 9814 9815 9816 9817 | } break; } } if(helpState){ f_out("\n"); fcli_command_help(helpPos, true, helpState>1); }else if(!cmd->name){ fsl_buffer msg = fsl_buffer_empty; int rc2; if(!arg){ rc2 = FSL_RC_MISUSE; fsl_buffer_appendf(&msg, "No command provided."); }else{ rc2 = FSL_RC_NOT_FOUND; fsl_buffer_appendf(&msg, "Command not found: %s.",arg); } fsl_buffer_appendf(&msg, " Available commands: "); cmd = orig; for( ; cmd && cmd->name; ++cmd ){ fsl_buffer_appendf( &msg, "%s%s", (cmd==orig) ? "" : ", ", cmd->name); } rc = fcli_err_set(rc2, "%b", &msg); fsl_buffer_clear(&msg); } if(rc && reportErrors){ fcli_err_report(0); } return rc; } bool fcli_cmd_aliascmp(fcli_command const * cmd, char const * arg){ char const * alias = cmd->aliases; while ( alias && *alias!=0 ){ if( 0==fsl_strcmp(alias, arg) ){ return true; } alias = strchr(alias, 0) + 1; } return false; } void fcli_command_help(fcli_command const * cmd, bool showUsage, bool onlyOne){ fcli_command const * c = cmd; for( ; c->name; ++c ){ f_out("[%s] command:\n\n", c->name); if(c->briefDescription){ |
︙ | ︙ | |||
11424 11425 11426 11427 11428 11429 11430 | "Entry count=%u totaling %u byte(s).\n", f->cache.blobContent.metrics.hits, f->cache.blobContent.metrics.misses, f->cache.blobContent.used, f->cache.blobContent.szTotal); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 | "Entry count=%u totaling %u byte(s).\n", f->cache.blobContent.metrics.hits, f->cache.blobContent.metrics.misses, f->cache.blobContent.used, f->cache.blobContent.szTotal); } #undef FCLI_V3 #undef fcli_empty_m #undef fcli__error #undef MARKER #undef FCLI_USE_SIGACTION /* end of file ./src/cli.c */ /* start of file ./src/content.c */ |
︙ | ︙ | |||
13052 13053 13054 13055 13056 13057 13058 | assert(!"Invalid fsl_confdb_e value"); return NULL; } } int fsl_config_versionable_filename(fsl_cx *f, char const * key, fsl_buffer *b){ | | | | | | 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 | assert(!"Invalid fsl_confdb_e value"); return NULL; } } int fsl_config_versionable_filename(fsl_cx *f, char const * key, fsl_buffer *b){ if(!f || !fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; else if(!key || !*key || !fsl_is_simple_pathname(key, true)){ return FSL_RC_MISUSE; } fsl_buffer_reuse(b); return fsl_buffer_appendf(b, "%s.fossil-settings/%s", f->ckout.dir, key); } int fsl_config_unset( fsl_cx * const f, fsl_confdb_e mode, char const * key ){ fsl_db * db = fsl_config_for_role(f, mode); if(!db || !key || !*key) return FSL_RC_MISUSE; else if(mode==FSL_CONFDB_VERSIONABLE) return FSL_RC_UNSUPPORTED; else{ char const * table = fsl_config_table_for_role(mode); assert(table); return fsl_db_exec(db, "DELETE FROM %s WHERE name=%Q", table, key); } } int32_t fsl_config_get_int32( fsl_cx * const f, fsl_confdb_e mode, int32_t dflt, char const * key ){ int32_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = (int32_t)atoi(val); fsl_free(val); } break; } default: { fsl_db * const db = fsl_config_for_role(f, mode); char const * table = fsl_config_table_for_role(mode); assert(table); if(db){ fsl_stmt * st = NULL; fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ st->role = fsl__confdb_to_role(mode); |
︙ | ︙ | |||
13113 13114 13115 13116 13117 13118 13119 | } int64_t fsl_config_get_int64( fsl_cx * const f, fsl_confdb_e mode, int64_t dflt, char const * key ){ int64_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ | | | | | 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 | } int64_t fsl_config_get_int64( fsl_cx * const f, fsl_confdb_e mode, int64_t dflt, char const * key ){ int64_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = (int64_t)strtoll(val, NULL, 10); fsl_free(val); } break; } default: { fsl_db * db = fsl_config_for_role(f, mode); char const * table = fsl_config_table_for_role(mode); assert(table); if(db){ fsl_stmt * st = NULL; fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ st->role = fsl__confdb_to_role(mode); |
︙ | ︙ | |||
13155 13156 13157 13158 13159 13160 13161 | } double fsl_config_get_double( fsl_cx * const f, fsl_confdb_e mode, double dflt, char const * key ){ double rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ | | | | | 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 | } double fsl_config_get_double( fsl_cx * const f, fsl_confdb_e mode, double dflt, char const * key ){ double rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = strtod(val, NULL); fsl_free(val); } break; } default: { fsl_db * db = fsl_config_for_role(f, mode); if(!db) break/*e.g. global config is not opened*/; fsl_stmt * st = NULL; char const * table = fsl_config_table_for_role(mode); assert(table); fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ |
︙ | ︙ | |||
13209 13210 13211 13212 13213 13214 13215 | fsl_buffer_reuse(b); switch(mode){ case FSL_CONFDB_VERSIONABLE:{ if(!fsl_needs_ckout(f)){ rc = FSL_RC_NOT_A_CKOUT; break; } | | | | | | < | | > | | 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 11891 11892 | fsl_buffer_reuse(b); switch(mode){ case FSL_CONFDB_VERSIONABLE:{ if(!fsl_needs_ckout(f)){ rc = FSL_RC_NOT_A_CKOUT; break; } fsl_buffer * fname = fsl__cx_scratchpad(f); rc = fsl_config_versionable_filename(f, key, fname); if(!rc){ char const * zFile = fsl_buffer_cstr(fname); rc = fsl_stat(zFile, 0, false); if(rc){ rc = fsl_cx_err_set(f, rc, "Could not stat file: %s", zFile); }else{ rc = fsl_buffer_fill_from_filename(b, zFile); } } fsl__cx_scratchpad_yield(f,fname); break; } default: { char const * table = fsl_config_table_for_role(mode); assert(table); fsl_db * const db = fsl_config_for_role(f, mode); if(!db) break; fsl_stmt * st = NULL; rc = fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(rc){ rc = fsl_cx_uplift_db_error2(f, db, rc); break; } st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ fsl_size_t len = 0; char const * s = fsl_stmt_g_text(st, 0, &len); rc = s ? fsl_buffer_append(b, s, len) : 0; }else{ rc = FSL_RC_NOT_FOUND; } fsl_stmt_cached_yield(st); break; } } return rc; } bool fsl_config_get_bool( fsl_cx * const f, fsl_confdb_e mode, bool dflt, char const * key ){ bool rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = fsl_str_bool(val); fsl_free(val); } break; } default:{ int rc; fsl_stmt * st = NULL; char const * table = fsl_config_table_for_role(mode); fsl_db * db; if(!f || !key || !*key) break; db = fsl_config_for_role(f, mode); if(!db) break; assert(table); rc = fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(!rc){ st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ char const * col = fsl_stmt_g_text(st, 0, NULL); rv = col ? fsl_str_bool(col) : dflt /* 0? */; } fsl_stmt_cached_yield(st); } break; } } |
︙ | ︙ | |||
13301 13302 13303 13304 13305 13306 13307 | fsl_stmt_cached_yield() it. Returns non-0 on error. */ static int fsl_config_set_prepare( fsl_cx * const f, fsl_stmt **st, fsl_confdb_e mode, char const * key ){ char const * table = fsl_config_table_for_role(mode); | | > | 11900 11901 11902 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 11929 11930 11931 11932 11933 | fsl_stmt_cached_yield() it. Returns non-0 on error. */ static int fsl_config_set_prepare( fsl_cx * const f, fsl_stmt **st, fsl_confdb_e mode, char const * key ){ char const * table = fsl_config_table_for_role(mode); fsl_db * db = fsl_config_for_role(f,mode); assert(table); if(!db || !key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else{ const char * sql = FSL_CONFDB_REPO==mode ? "REPLACE INTO %!Q(name,value,mtime) VALUES(?,?,now())/*%s()*/" : "REPLACE INTO %!Q(name,value) VALUES(?,?)/*%s()*/"; int rc = fsl_db_prepare_cached(db, st, sql, table, __func__); if(!rc){ (*st)->role = fsl__confdb_to_role(mode); rc = fsl_stmt_bind_text(*st, 1, key, -1, 1); } if(rc && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } } /* TODO/FIXME: the fsl_config_set_xxx() routines all use the same basic structure, differing only in the concrete bind() op they call. They should be consolidated somehow. */ |
︙ | ︙ | |||
14296 14297 14298 14299 14300 14301 14302 | /sizeof(fsl__mcache_empty.aAge[0])); for(unsigned i = 0; i < cacheLen; ++i){ fsl_deck_finalize(&f->cache.mcache.decks[i]); } f->cache.mcache = fsl__mcache_empty; } | | < < < < < < < | 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 | /sizeof(fsl__mcache_empty.aAge[0])); for(unsigned i = 0; i < cacheLen; ++i){ fsl_deck_finalize(&f->cache.mcache.decks[i]); } f->cache.mcache = fsl__mcache_empty; } void fsl__cx_clear_repo_caches(fsl_cx * const f){ fsl__bccache_reset(&f->cache.blobContent); fsl__cx_mcache_clear(f); fsl__cx_clear_mf_seen(f, false); f->cache.allowSymlinks = f->cache.caseInsensitive = f->cache.seenDeltaManifest = f->cache.manifestSetting = -1; } static void fsl__cx_finalize_cached_stmt(fsl_cx * const f){ #define STMT(X) fsl_stmt_finalize(&f->cache.stmt.X) STMT(deltaSrcId); STMT(uuidToRid); STMT(uuidToRidGlob); |
︙ | ︙ | |||
14359 14360 14361 14362 14363 14364 14365 | fsl__card_J_list_free(&f->ticket.customFields, true); fsl_buffer_clear(&f->cache.fileContent); fsl_buffer_clear(&f->cache.deltaContent); for(int i = 0; i < FSL_CX_NSCRATCH; ++i){ fsl_buffer_clear(&f->scratchpads.buf[i]); f->scratchpads.used[i] = false; } | | | 12952 12953 12954 12955 12956 12957 12958 12959 12960 12961 12962 12963 12964 12965 12966 | fsl__card_J_list_free(&f->ticket.customFields, true); fsl_buffer_clear(&f->cache.fileContent); fsl_buffer_clear(&f->cache.deltaContent); for(int i = 0; i < FSL_CX_NSCRATCH; ++i){ fsl_buffer_clear(&f->scratchpads.buf[i]); f->scratchpads.used[i] = false; } fsl__cx_clear_repo_caches(f); fsl__bccache_clear(&f->cache.blobContent); fsl__cx_clear_mf_seen(f, true); fsl_id_bag_clear(&f->cache.leafCheck); fsl_id_bag_clear(&f->cache.toVerify); assert(NULL==f->cache.mfSeen.list); if(f->xlinkers.list){ fsl_free(f->xlinkers.list); |
︙ | ︙ | |||
14427 14428 14429 14430 14431 14432 14433 | fsl_error_reset(&f->error); fsl_db_err_reset(&f->repo.db); fsl_db_err_reset(&f->config.db); fsl_db_err_reset(&f->ckout.db); } int fsl_cx_err_set_e( fsl_cx * const f, fsl_error * const err ){ | > | > | > > > | | | | | | > | 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 | fsl_error_reset(&f->error); fsl_db_err_reset(&f->repo.db); fsl_db_err_reset(&f->config.db); fsl_db_err_reset(&f->ckout.db); } int fsl_cx_err_set_e( fsl_cx * const f, fsl_error * const err ){ if(!f) return FSL_RC_MISUSE; else if(!err){ return fsl_cx_err_set(f, 0, NULL); }else{ fsl_error_move(err, &f->error); fsl_error_clear(err); return f->error.code; } } int fsl_cx_err_setv( fsl_cx * const f, int code, char const * fmt, va_list args ){ return f ? fsl_error_setv( &f->error, code, fmt, args ) : FSL_RC_MISUSE; } int fsl_cx_err_set( fsl_cx * const f, int code, char const * fmt, ... ){ if(!f) return FSL_RC_MISUSE; else{ int rc; va_list args; va_start(args,fmt); rc = fsl_error_setv( &f->error, code, fmt, args ); va_end(args); return rc; } } int fsl_cx_err_get( fsl_cx * const f, char const ** str, fsl_size_t * len ){ #if 1 return fsl_error_get( &f->error, str, len ); #else /* For the docs: |
︙ | ︙ | |||
14609 14610 14611 14612 14613 14614 14615 14616 | "Cannot close/detach unattached role: %s", fsl_db_role_name(r)); }else{ fsl_db * const db = fsl__cx_db_for_role(f,r); int rc = 0; switch(r){ case FSL_DBROLE_REPO: case FSL_DBROLE_CKOUT: | > > | | 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 | "Cannot close/detach unattached role: %s", fsl_db_role_name(r)); }else{ fsl_db * const db = fsl__cx_db_for_role(f,r); int rc = 0; switch(r){ case FSL_DBROLE_REPO: fsl__cx_clear_repo_caches(f); break; case FSL_DBROLE_CKOUT: fsl__cx_ckout_clear(f); break; default: fsl__fatal(FSL_RC_ERROR, "Cannot happen. Really."); } fsl__cx_finalize_cached_stmt(f); fsl__db_cached_clear_role(f->dbMain, r) /* Make sure that we destroy any cached statements which are |
︙ | ︙ | |||
14914 14915 14916 14917 14918 14919 14920 | rc = fsl_cx_uplift_db_error2(f, db, rc); if(isAttached) fsl_db_detach(db, zPrefix); fsl_db_close(db); return rc; } int fsl_config_global_preferred_name(char ** zOut){ | | < | > < < < < < < < < < < < | < < < < < < < < < | < < | > | < < | 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 | rc = fsl_cx_uplift_db_error2(f, db, rc); if(isAttached) fsl_db_detach(db, zPrefix); fsl_db_close(db); return rc; } int fsl_config_global_preferred_name(char ** zOut){ char * zEnv = 0; char * zRc = 0; int rc = 0; fsl_buffer buf = fsl_buffer_empty; #if FSL_PLATFORM_IS_WINDOWS # error "TODO: port in fossil(1) db.c:db_configdb_name() Windows bits" #else #endif /* Option 1: $FOSSIL_HOME/.fossil */ zEnv = fsl_getenv("FOSSIL_HOME"); if(zEnv){ zRc = fsl_mprintf("%s/.fossil", zEnv); if(!zRc) rc = FSL_RC_OOM; goto end; } |
︙ | ︙ | |||
14988 14989 14990 14991 14992 14993 14994 14995 | goto end; } /* Option 5: fall back to $HOME/.fossil */ buf.used -= 8 /* "/.config" */; buf.mem[buf.used] = 0; rc = fsl_buffer_append(&buf, "/.fossil", 8); if(!rc) zRc = fsl_buffer_take(&buf); end: | > < < > | < > | 13566 13567 13568 13569 13570 13571 13572 13573 13574 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 | goto end; } /* Option 5: fall back to $HOME/.fossil */ buf.used -= 8 /* "/.config" */; buf.mem[buf.used] = 0; rc = fsl_buffer_append(&buf, "/.fossil", 8); if(!rc) zRc = fsl_buffer_take(&buf); end: if(zEnv) fsl_filename_free(zEnv); if(!rc){ assert(zRc); *zOut = zRc; } fsl_buffer_clear(&buf); return rc; } int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; |
︙ | ︙ | |||
15289 15290 15291 15292 15293 15294 15295 15296 | } int fsl__ckout_version_fetch( fsl_cx * const f ){ fsl_id_t rid = 0; int rc = 0; fsl_db * dbC = fsl_cx_db_ckout(f); fsl_db * dbR = dbC ? fsl_needs_repo(f) : NULL; fsl__cx_ckout_clear(f); | > | | > > | 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 13886 | } int fsl__ckout_version_fetch( fsl_cx * const f ){ fsl_id_t rid = 0; int rc = 0; fsl_db * dbC = fsl_cx_db_ckout(f); fsl_db * dbR = dbC ? fsl_needs_repo(f) : NULL; assert(!dbC || (dbC && dbR)); fsl__cx_ckout_clear(f); if(!dbC){ return 0; } fsl_cx_err_reset(f); rid = fsl_config_get_id(f, FSL_CONFDB_CKOUT, -1, "checkout"); //MARKER(("rc=%s rid=%d\n",fsl_rc_cstr(f->error.code), (int)rid)); if(rid>0){ f->ckout.uuid = fsl_rid_to_uuid(f, rid); if(!f->ckout.uuid){ assert(f->error.code); if(!f->error.code){ |
︙ | ︙ | |||
16377 16378 16379 16380 16381 16382 16383 | case SQLITE_TOOBIG: case SQLITE_FULL: case SQLITE_NOLFS: case SQLITE_RANGE: rc = FSL_RC_RANGE; break; case SQLITE_NOTFOUND: rc = FSL_RC_NOT_FOUND; break; case SQLITE_PERM: case SQLITE_AUTH: | | | 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971 14972 | case SQLITE_TOOBIG: case SQLITE_FULL: case SQLITE_NOLFS: case SQLITE_RANGE: rc = FSL_RC_RANGE; break; case SQLITE_NOTFOUND: rc = FSL_RC_NOT_FOUND; break; case SQLITE_PERM: case SQLITE_AUTH: case SQLITE_LOCKED: case SQLITE_READONLY: rc = FSL_RC_ACCESS; break; case SQLITE_CORRUPT: rc = FSL_RC_CONSISTENCY; break; case SQLITE_CANTOPEN: case SQLITE_IOERR: rc = FSL_RC_IO; break; default: //MARKER(("sqlite3_errcode()=0x%04x\n", rc)); |
︙ | ︙ | |||
17108 17109 17110 17111 17112 17113 17114 | break; case FSL_RC_STEP_ROW: /* Don't reset() for ROW b/c that clears the column data! */ break; default: rc = fsl_error_set(&st->db->error, rc, | | < | 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 15700 15701 15702 15703 | break; case FSL_RC_STEP_ROW: /* Don't reset() for ROW b/c that clears the column data! */ break; default: rc = fsl_error_set(&st->db->error, rc, "Error stepping statement."); break; } } return rc; } int fsl_stmt_bind_step( fsl_stmt * st, char const * fmt, ... ){ |
︙ | ︙ | |||
17728 17729 17730 17731 17732 17733 17734 | IDs which were injected as part of the being-rolled-back transaction. The only(?) reasonably sane way to deal with that is to flush all relevant caches. It is unfortunate that this bit is in the db class, as opposed to the fsl_cx class, but we currently have no hook which would allow us to trigger this from that class. */ | > > | > > > > > > | | | > | > > > > > > > > > > | > > > > > > > > > | > | | 16308 16309 16310 16311 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 | IDs which were injected as part of the being-rolled-back transaction. The only(?) reasonably sane way to deal with that is to flush all relevant caches. It is unfortunate that this bit is in the db class, as opposed to the fsl_cx class, but we currently have no hook which would allow us to trigger this from that class. */ #if 1 fsl__bccache_reset(&db->f->cache.blobContent); fsl__cx_mcache_clear(db->f); #else /* This one resets all of ^^^ plus certain repo-side config settings, but it's not yet clear whether that they will be reloaded when needed. */ fsl__cx_clear_repo_caches(d->f); #endif } fsl_db_cleanup_beforeCommit(db); fsl_db_reset_change_count(db); rc = fsl_db_exec(db, db->doRollback ? "ROLLBACK" : "COMMIT"); db->doRollback = 0; return rc; #if 0 /* original impl, for reference purposes during testing */ if( g.db==0 ) return; if( db.nBegin<=0 ) return; if( rollbackFlag ) db.doRollback = 1; db.nBegin--; if( db.nBegin==0 ){ int i; if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ while( db.nBeforeCommit ){ db.nBeforeCommit--; sqlite3_exec(g.db, db.azBeforeCommit[db.nBeforeCommit], 0, 0, 0); sqlite3_free(db.azBeforeCommit[db.nBeforeCommit]); } leaf_do_pending_checks(); } for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ db.doRollback |= db.aHook[i].xHook(); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } db_multi_exec(db.doRollback ? "ROLLBACK" : "COMMIT"); db.doRollback = 0; } #endif } int fsl_db_get_int32v( fsl_db * const db, int32_t * rv, char const * sql, va_list args){ /* Potential fixme: the fsl_db_get_XXX() funcs are 95% code duplicates. We "could" replace these with a macro or supermacro, though the latter would be problematic |
︙ | ︙ | |||
18042 18043 18044 18045 18046 18047 18048 | va_end(args); return rc; } int fsl_db_get_bufferv( fsl_db * const db, fsl_buffer * const b, bool asBlob, char const * sql, va_list args){ | | > | 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 | va_end(args); return rc; } int fsl_db_get_bufferv( fsl_db * const db, fsl_buffer * const b, bool asBlob, char const * sql, va_list args){ if(!db || !db->dbh || !b || !sql || !*sql) return FSL_RC_MISUSE; else{ fsl_stmt st = fsl_stmt_empty; int rc = 0; rc = fsl_db_preparev( db, &st, sql, args ); if(rc) return rc; rc = fsl_stmt_step( &st ); switch(rc){ case FSL_RC_STEP_ROW:{ void const * str = asBlob ? sqlite3_column_blob(st.stmt, 0) : (void const *)sqlite3_column_text(st.stmt, 0); int const len = sqlite3_column_bytes(st.stmt,0); if(len && !str){ rc = FSL_RC_OOM; }else{ rc = 0; b->used = 0; rc = fsl_buffer_append( b, str, len ); } break; } case FSL_RC_STEP_DONE: rc = 0; break; |
︙ | ︙ | |||
18187 18188 18189 18190 18191 18192 18193 | bool fsl_db_existsv(fsl_db * const db, char const * sql, va_list args ){ if(!db || !db->dbh || !sql) return 0; else if(!*sql) return 0; else{ fsl_stmt st = fsl_stmt_empty; bool rv = false; | | | 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 | bool fsl_db_existsv(fsl_db * const db, char const * sql, va_list args ){ if(!db || !db->dbh || !sql) return 0; else if(!*sql) return 0; else{ fsl_stmt st = fsl_stmt_empty; bool rv = false; if(!fsl_db_preparev(db, &st, sql, args)){ rv = FSL_RC_STEP_ROW==fsl_stmt_step(&st) ? true : false; } fsl_stmt_finalize(&st); return rv; } } |
︙ | ︙ | |||
18715 18716 18717 18718 18719 18720 18721 | } } /** fsl_list_visitor_f() impl which requires that obj be-a (fsl_card_T*), which this function passes to fsl_card_T_free(). */ | | | | | 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349 | } } /** fsl_list_visitor_f() impl which requires that obj be-a (fsl_card_T*), which this function passes to fsl_card_T_free(). */ static int fsl_list_v_card_T_free(void * obj, void * visitorState ){ if(obj) fsl_card_T_free( (fsl_card_T*)obj ); return 0; } static int fsl_list_v_card_Q_free(void * obj, void * visitorState ){ if(obj) fsl_card_Q_free( (fsl_card_Q*)obj ); return 0; } static int fsl_list_v_card_J_free(void * obj, void * visitorState ){ if(obj) fsl_card_J_free( (fsl_card_J*)obj ); return 0; } fsl_deck * fsl_deck_malloc(){ fsl_deck * rc = (fsl_deck *)fsl_malloc(sizeof(fsl_deck)); if(rc){ |
︙ | ︙ | |||
21087 21088 21089 21090 21091 21092 21093 | fsl_int_t i; assert(d); assert(zName && *zName); if(!d->F.used) return NULL; else if(FSL_CARD_F_LIST_NEEDS_SORT & d->F.flags){ fsl_card_F_list_sort(&d->F); } | < | | | | | | | 19697 19698 19699 19700 19701 19702 19703 19704 19705 19706 19707 19708 19709 19710 19711 19712 19713 19714 19715 19716 19717 19718 19719 19720 19721 19722 19723 19724 19725 19726 19727 19728 19729 19730 19731 | fsl_int_t i; assert(d); assert(zName && *zName); if(!d->F.used) return NULL; else if(FSL_CARD_F_LIST_NEEDS_SORT & d->F.flags){ fsl_card_F_list_sort(&d->F); } #define FCARD(NDX) F_at(&d->F, (NDX)) lwr = 0; upr = d->F.used-1; if( d->F.cursor>=lwr && d->F.cursor<upr ){ c = (d->f && d->f->cache.caseInsensitive) ? fsl_stricmp(FCARD(d->F.cursor+1)->name, zName) : fsl_strcmp(FCARD(d->F.cursor+1)->name, zName); if( c==0 ){ if(atNdx) *atNdx = (uint32_t)d->F.cursor+1; return FCARD(++d->F.cursor); }else if( c>0 ){ upr = d->F.cursor; }else{ lwr = d->F.cursor+1; } } while( lwr<=upr ){ i = (lwr+upr)/2; c = (d->f && d->f->cache.caseInsensitive) ? fsl_stricmp(FCARD(i)->name, zName) : fsl_strcmp(FCARD(i)->name, zName); if( c<0 ){ lwr = i+1; }else if( c>0 ){ upr = i-1; }else{ d->F.cursor = i; if(atNdx) *atNdx = (uint32_t)i; |
︙ | ︙ | |||
22000 22001 22002 22003 22004 22005 22006 | return rc; } /** Overrideable crosslink listener which updates the timeline for attachment records. */ | | | 20609 20610 20611 20612 20613 20614 20615 20616 20617 20618 20619 20620 20621 20622 20623 | return rc; } /** Overrideable crosslink listener which updates the timeline for attachment records. */ static int fsl_deck_xlink_f_attachment(fsl_deck * const d, void * state){ if(FSL_SATYPE_ATTACHMENT!=d->type) return 0; int rc; fsl_db * const db = fsl_cx_db_repo(d->f); fsl_buffer * const comment = fsl__cx_scratchpad(d->f); const bool isAdd = (d->A.src && *d->A.src) ? 1 : 0; char attachToType = 'w' /* Assume wiki until we know otherwise, keeping in mind that the |
︙ | ︙ | |||
22078 22079 22080 22081 22082 22083 22084 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ | | | 20687 20688 20689 20690 20691 20692 20693 20694 20695 20696 20697 20698 20699 20700 20701 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ static int fsl_deck_xlink_f_checkin(fsl_deck * const d, void * state){ if(FSL_SATYPE_CHECKIN!=d->type) return 0; int rc; fsl_db * db; db = fsl_cx_db_repo(d->f); assert(db); rc = fsl_db_exec(db, "REPLACE INTO event(type,mtime,objid,user,comment," |
︙ | ︙ | |||
22126 22127 22128 22129 22130 22131 22132 | (int)FSL_TAGID_BGCOLOR, d->rid, (int)FSL_TAGID_USER, d->rid, (int)FSL_TAGID_COMMENT, d->rid, d->D ); return fsl_cx_uplift_db_error2(d->f, db, rc); } | | | 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 20746 20747 20748 20749 | (int)FSL_TAGID_BGCOLOR, d->rid, (int)FSL_TAGID_USER, d->rid, (int)FSL_TAGID_COMMENT, d->rid, d->D ); return fsl_cx_uplift_db_error2(d->f, db, rc); } static int fsl_deck_xlink_f_control(fsl_deck * const d, void * state){ if(FSL_SATYPE_CONTROL!=d->type) return 0; /* Create timeline event entry for all tags in this control construct. Note that we are using a lot of historical code which hard-codes english-lanuage text and links which only work in fossil(1). i would prefer to farm this out to a crosslink callback, and provide a default implementation which more or |
︙ | ︙ | |||
22273 22274 22275 22276 22277 22278 22279 | end: fsl__cx_scratchpad_yield(d->f, comment); return rc; } | | | 20882 20883 20884 20885 20886 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 | end: fsl__cx_scratchpad_yield(d->f, comment); return rc; } static int fsl_deck_xlink_f_forum(fsl_deck * const d, void * state){ if(FSL_SATYPE_FORUMPOST!=d->type) return 0; int rc = 0; fsl_db * const db = fsl_cx_db_repo(d->f); assert(db); fsl_cx * const f = d->f; fsl_id_t const froot = d->G ? fsl_uuid_to_rid(f, d->G) : d->rid; fsl_id_t const fprev = d->P.used ? fsl_uuid_to_rid(f, (char const *)d->P.list[0]): 0; |
︙ | ︙ | |||
22359 22360 22361 22362 22363 22364 22365 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } | | | 20968 20969 20970 20971 20972 20973 20974 20975 20976 20977 20978 20979 20980 20981 20982 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } static int fsl_deck_xlink_f_technote(fsl_deck * const d, void * state){ if(FSL_SATYPE_TECHNOTE!=d->type) return 0; char buf[FSL_STRLEN_K256 + 7 /* event-UUID\0 */] = {0}; fsl_id_t tagid; char const * zTag; int rc = 0; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(d->f); |
︙ | ︙ | |||
22408 22409 22410 22411 22412 22413 22414 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } | | | 21017 21018 21019 21020 21021 21022 21023 21024 21025 21026 21027 21028 21029 21030 21031 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } static int fsl_deck_xlink_f_wiki(fsl_deck * const d, void * state){ if(FSL_SATYPE_WIKI!=d->type) return 0; int rc; char const * zWiki; fsl_size_t nWiki = 0; char cPrefix = 0; char * zTag = fsl_mprintf("wiki-%s", d->L); if(!zTag) return FSL_RC_OOM; |
︙ | ︙ | |||
24807 24808 24809 24810 24811 24812 24813 | sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); | < < < | 23416 23417 23418 23419 23420 23421 23422 23423 23424 23425 23426 23427 23428 23429 23430 23431 | sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); case 2: sum3 += (z[1] << 16); case 1: sum3 += (z[0] << 24); default: ; } return sum3; } int fsl_delta_create2( unsigned char const *zSrc, fsl_size_t lenSrc, unsigned char const *zOut, fsl_size_t lenOut, |
︙ | ︙ | |||
25040 25041 25042 25043 25044 25045 25046 25047 25048 25049 25050 25051 25052 | } int fsl_delta_create( unsigned char const *zSrc, fsl_size_t lenSrc, unsigned char const *zOut, fsl_size_t lenOut, unsigned char *zDelta, fsl_size_t * deltaSize){ int rc; DeltaOutputString os; os.mem = (unsigned char *)zDelta; os.cursor = 0; rc = fsl_delta_create2( zSrc, lenSrc, zOut, lenOut, fsl_output_f_ostring, &os ); if(!rc){ os.mem[os.cursor] = 0; | > | | 23646 23647 23648 23649 23650 23651 23652 23653 23654 23655 23656 23657 23658 23659 23660 23661 23662 23663 23664 23665 23666 23667 | } int fsl_delta_create( unsigned char const *zSrc, fsl_size_t lenSrc, unsigned char const *zOut, fsl_size_t lenOut, unsigned char *zDelta, fsl_size_t * deltaSize){ int rc; DeltaOutputString os; if(!zSrc || !zOut || !zDelta || !deltaSize) return FSL_RC_MISUSE; os.mem = (unsigned char *)zDelta; os.cursor = 0; rc = fsl_delta_create2( zSrc, lenSrc, zOut, lenOut, fsl_output_f_ostring, &os ); if(!rc){ os.mem[os.cursor] = 0; if(deltaSize) *deltaSize = os.cursor; } return rc; } /* Calculates the size (in bytes) of the output from applying a delta. On success 0 is returned and *deltaSize will be updated with |
︙ | ︙ | |||
25401 25402 25403 25404 25405 25406 25407 | uint32_t lnnoRHS, uint32_t linesRHS ){ #if 1 if(1==b->passNumber){ DICOSTATE(sst); ++sst->displayLines; return 0; } | < < < < < | 24008 24009 24010 24011 24012 24013 24014 24015 24016 24017 24018 24019 24020 24021 | uint32_t lnnoRHS, uint32_t linesRHS ){ #if 1 if(1==b->passNumber){ DICOSTATE(sst); ++sst->displayLines; return 0; } return fdb__outf(b, "@@ -%" PRIu32 ",%" PRIu32 " +%" PRIu32 ",%" PRIu32 " @@\n", lnnoLHS, linesLHS, lnnoRHS, linesRHS); #else return 0; #endif } |
︙ | ︙ | |||
25825 25826 25827 25828 25829 25830 25831 | return rc; } static int fdb__utxt_chunkHeader(fsl_dibu* const b, uint32_t lnnoLHS, uint32_t linesLHS, uint32_t lnnoRHS, uint32_t linesRHS ){ /* | < < | | | | 24427 24428 24429 24430 24431 24432 24433 24434 24435 24436 24437 24438 24439 24440 24441 24442 24443 24444 24445 | return rc; } static int fdb__utxt_chunkHeader(fsl_dibu* const b, uint32_t lnnoLHS, uint32_t linesLHS, uint32_t lnnoRHS, uint32_t linesRHS ){ /* Annoying cosmetic bug: the libf impl of this diff will sometimes render two directly-adjecent chunks with a separator, e.g.: */ // $ f-vdiff --forat u 072d63965188 a725befe5863 -l '*vdiff*' | head -30 // Index: f-apps/f-vdiff.c // ================================================================== // --- f-apps/f-vdiff.c // +++ f-apps/f-vdiff.c // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 36 36 fsl_buffer fname; // 37 37 fsl_buffer fcontent1; |
︙ | ︙ | |||
25861 25862 25863 25864 25865 25866 25867 | /* Note now the chunks before/after the second ~~~ line are consecutive lines of code. In fossil(1) that case is accounted for in the higher-level diff engine, which can not only collapse adjacent blocks but also does the rendering of chunk headers in that main algorithm (something we cannot do in the library because we need the fsl_dibu to be able to output to arbitrary | | | 24461 24462 24463 24464 24465 24466 24467 24468 24469 24470 24471 24472 24473 24474 24475 | /* Note now the chunks before/after the second ~~~ line are consecutive lines of code. In fossil(1) that case is accounted for in the higher-level diff engine, which can not only collapse adjacent blocks but also does the rendering of chunk headers in that main algorithm (something we cannot do in the library because we need the fsl_dibu to be able to output to arbitrary distinations). We can only _partially_ account for it here, eliminating the extraneous ~~~ line when we're in line-number mode. In non-line-number mode we have to output the chunk header as-is. If we skip it then the _previous_ chunk header, if any, will contain incorrect numbers for the chunk, invaliding the diff for purposes of tools which import unified-format diffs. */ int rc = fdb__utxt_flush_ins(b); |
︙ | ︙ | |||
26160 26161 26162 26163 26164 26165 26166 | BR_CLOSE; RC; rc = fdb__out(b, "\n", 1); end: #undef RC return rc; } | | | 24760 24761 24762 24763 24764 24765 24766 24767 24768 24769 24770 24771 24772 24773 24774 | BR_CLOSE; RC; rc = fdb__out(b, "\n", 1); end: #undef RC return rc; } static int fdb__tcl_finish(fsl_dibu * const b){ int rc = 0; #if 0 BR_CLOSE; if(0==rc && FSL_DIBU_TCL_BRACES & b->implFlags){ rc = fdb__out(b, "\n", 1); } #endif |
︙ | ︙ | |||
26788 26789 26790 26791 26792 26793 26794 | ** See also discussion at https://fossil-scm.org/forum/forumpost/9ba3284295 ** ** ALGORITHM (subject to change and refinement): ** ** 1. If the subsequence is larger than 1/7th of the original span, ** then consider it valid. --> return 1 ** | | < < < < | | | < < | < < < < < < < < < | < < < < < < < < < < < < < < < < < < | < | < < < < < < < < | < < < < < < < < < < < < < < < | < < | | 25388 25389 25390 25391 25392 25393 25394 25395 25396 25397 25398 25399 25400 25401 25402 25403 25404 25405 25406 25407 25408 25409 25410 25411 25412 25413 25414 25415 25416 25417 25418 25419 25420 25421 25422 25423 25424 | ** See also discussion at https://fossil-scm.org/forum/forumpost/9ba3284295 ** ** ALGORITHM (subject to change and refinement): ** ** 1. If the subsequence is larger than 1/7th of the original span, ** then consider it valid. --> return 1 ** ** 2. If the subsequence contains any charaters other than '}', '{", ** or whitespace, then consider it valid. --> return 1 ** ** 3. Otherwise, it is potentially an artifact of an indentation ** change. --> return 0 */ static bool likelyNotIndentChngArtifact( fsl__diff_cx const * const p, /* The complete diff context */ int iS1, /* Start of the main segment */ int iSX, /* Start of the subsequence */ int iEX, /* First row past the end of the subsequence */ int iE1 /* First row past the end of the main segment */ ){ int i, j; if( (iEX-iSX)*7 >= (iE1-iS1) ) return true; for(i=iSX; i<iEX; i++){ const char *z = p->aFrom[i].z; for(j=p->aFrom[i].n-1; j>=0; j--){ char c = z[j]; if( c!='}' && c!='{' && !diff_isspace(c) ) return true; } } return false; } /** Do a single step in the difference. Compute a sequence of copy/delete/insert steps that will convert lines iS1 through iE1-1 of the input into lines iS2 through iE2-1 of the output and write that sequence into the difference context. |
︙ | ︙ | |||
27729 27730 27731 27732 27733 27734 27735 | fsl_dline * const aRight, int nRight, fsl_dibu_opt const * pOpt, unsigned char **pResult, unsigned *pNResult ); /* | | | 26270 26271 26272 26273 26274 26275 26276 26277 26278 26279 26280 26281 26282 26283 26284 | fsl_dline * const aRight, int nRight, fsl_dibu_opt const * pOpt, unsigned char **pResult, unsigned *pNResult ); /* ** Make a copy of a list of nLine DLine objects from one array to ** another. Hash the new array to ignore whitespace. */ static void diffDLineXfer( fsl_dline *aTo, const fsl_dline *aFrom, int nLine ){ |
︙ | ︙ | |||
28807 28808 28809 28810 28811 28812 28813 | */ /************************************************************************ This file implements technote (formerly known as event)-related parts of the library. */ #include <assert.h> | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < | > > | | < < < < > | < < < < | < | < < < < | < < < | < < < | < < < < < < < < | | | < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 27348 27349 27350 27351 27352 27353 27354 27355 27356 27357 27358 27359 27360 27361 27362 27363 27364 27365 27366 27367 27368 27369 27370 27371 27372 27373 27374 27375 27376 27377 27378 27379 27380 27381 27382 27383 27384 27385 27386 27387 27388 27389 | */ /************************************************************************ This file implements technote (formerly known as event)-related parts of the library. */ #include <assert.h> /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) int fsl_event_ids_get( fsl_cx * f, fsl_list * tgt ){ fsl_db * db = fsl_needs_repo(f); if(!f || !tgt) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; else { int rc = fsl_db_select_slist( db, tgt, "SELECT substr(tagname,7) AS n " "FROM tag " "WHERE tagname GLOB 'event-*' " "ORDER BY n"); if(rc && db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } } #undef MARKER /* end of file ./src/event.c */ /* start of file ./src/fs.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD |
︙ | ︙ | |||
29187 29188 29189 29190 29191 29192 29193 29194 29195 29196 29197 29198 29199 29200 29201 29202 29203 29204 | #endif #include <assert.h> #include <string.h> /* strlen() */ #include <stddef.h> /* NULL on linux */ #include <ctype.h> #include <errno.h> #if FSL_PLATFORM_IS_WINDOWS # if !defined(ELOOP) # define ELOOP 114 /* Missing in MinGW */ # endif #else # include <unistd.h> /* access(2), readlink(2) */ # include <sys/types.h> # include <sys/time.h> #endif #include <sys/stat.h> | > > > > > > > > > < < > | 27398 27399 27400 27401 27402 27403 27404 27405 27406 27407 27408 27409 27410 27411 27412 27413 27414 27415 27416 27417 27418 27419 27420 27421 27422 27423 27424 27425 27426 27427 27428 27429 27430 27431 27432 27433 27434 27435 27436 27437 27438 | #endif #include <assert.h> #include <string.h> /* strlen() */ #include <stddef.h> /* NULL on linux */ #include <ctype.h> #include <errno.h> #include <dirent.h> #if FSL_PLATFORM_IS_WINDOWS # define DIR _WDIR # define dirent _wdirent # define opendir _wopendir # define readdir _wreaddir # define closedir _wclosedir # include <direct.h> # include <windows.h> # include <sys/utime.h> # if !defined(ELOOP) # define ELOOP 114 /* Missing in MinGW */ # endif #else # include <unistd.h> /* access(2), readlink(2) */ # include <sys/types.h> # include <sys/time.h> #endif #include <sys/stat.h> /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) FILE *fsl_fopen(const char *zName, const char *zMode){ FILE *f; if(zName && ('-'==*zName && !zName[1])){ f = (strchr(zMode, 'w') || strchr(zMode,'+')) ? stdout : stdin |
︙ | ︙ | |||
29681 29682 29683 29684 29685 29686 29687 | return (doBreak && leaveSlash) ? fsl_buffer_append(pOut, zFilename, 1) : fsl_buffer_append(pOut, "", 0) /* ensure a NUL terminator */; }else{ return fsl_buffer_append(pOut, zFilename, z-zFilename + 1); } } | | | < < < < < < | < < < < | | | 27900 27901 27902 27903 27904 27905 27906 27907 27908 27909 27910 27911 27912 27913 27914 27915 27916 27917 27918 27919 27920 | return (doBreak && leaveSlash) ? fsl_buffer_append(pOut, zFilename, 1) : fsl_buffer_append(pOut, "", 0) /* ensure a NUL terminator */; }else{ return fsl_buffer_append(pOut, zFilename, z-zFilename + 1); } } } int fsl_find_home_dir( fsl_buffer * tgt, bool requireWriteAccess ){ char * zHome = NULL; int rc = 0; tgt->used = 0; #if defined(_WIN32) || defined(__CYGWIN__) zHome = fsl_getenv("LOCALAPPDATA"); if( zHome==0 ){ zHome = fsl_getenv("APPDATA"); if( zHome==0 ){ char *zDrive = fsl_getenv("HOMEDRIVE"); zHome = fsl_getenv("HOMEPATH"); |
︙ | ︙ | |||
30107 30108 30109 30110 30111 30112 30113 30114 30115 30116 30117 30118 30119 30120 | fsl_fstat fst; return ( 0 != fsl_stat(zFilename, &fst, 0) ) ? -1 : fst.size; } #endif int fsl_file_mtime_set(const char *zFilename, fsl_time_t newMTime){ if(!zFilename || !*zFilename) return FSL_RC_MISUSE; else{ int rc; void * zMbcs; #if !defined(_WIN32) struct timeval tv[2]; | > > > | 28316 28317 28318 28319 28320 28321 28322 28323 28324 28325 28326 28327 28328 28329 28330 28331 28332 | fsl_fstat fst; return ( 0 != fsl_stat(zFilename, &fst, 0) ) ? -1 : fst.size; } #endif /* Set the mtime for a file. */ int fsl_file_mtime_set(const char *zFilename, fsl_time_t newMTime){ if(!zFilename || !*zFilename) return FSL_RC_MISUSE; else{ int rc; void * zMbcs; #if !defined(_WIN32) struct timeval tv[2]; |
︙ | ︙ | |||
30146 30147 30148 30149 30150 30151 30152 | fsl_list_visit_free(&pf->ext, 1); fsl_list_visit_free(&pf->dirs, 1); fsl_buffer_clear(&pf->buf); *pf = fsl_pathfinder_empty; } } | | < | | | < < < < < < < | < < < < < < < | | < | | > | 28358 28359 28360 28361 28362 28363 28364 28365 28366 28367 28368 28369 28370 28371 28372 28373 28374 28375 28376 28377 28378 28379 28380 28381 28382 28383 28384 28385 28386 28387 28388 28389 28390 28391 28392 28393 28394 28395 28396 28397 28398 28399 28400 28401 28402 28403 28404 28405 28406 28407 28408 28409 28410 28411 28412 28413 28414 28415 28416 28417 | fsl_list_visit_free(&pf->ext, 1); fsl_list_visit_free(&pf->dirs, 1); fsl_buffer_clear(&pf->buf); *pf = fsl_pathfinder_empty; } } static int fsl_pathfinder_add(fsl_list * const li, char const * str){ char * cp = fsl_strdup(str); int rc; if(!cp) rc = FSL_RC_OOM; else{ rc = fsl_list_append(li, cp); if(rc) fsl_free(cp); } return rc; } int fsl_pathfinder_dir_add(fsl_pathfinder * const pf, char const * const dir){ return (pf && dir) ? fsl_pathfinder_add(&pf->dirs, dir) : FSL_RC_MISUSE; } int fsl_pathfinder_ext_add(fsl_pathfinder * const pf, char const * const ext){ return (pf && ext) ? fsl_pathfinder_add(&pf->ext, ext) : FSL_RC_MISUSE; } int fsl_pathfinder_search(fsl_pathfinder * const pf, char const * const base, char const ** pOut, fsl_size_t * const outLen ){ fsl_buffer * buf = pf ? &pf->buf : NULL; fsl_list * ext; fsl_list * dirs; int rc = 0; fsl_size_t d, x, nD, nX, resetLen = 0; fsl_size_t baseLen; static char const pathSep = #if defined(_WIN32) '\\' #else '/' #endif ; if(!buf || !base || !*base) return FSL_RC_MISUSE; else if(!*base) return FSL_RC_RANGE; else if(0==fsl_file_access( base, 0 )){ /* Special case: if base is found as-is, without a path search, use it. This is arguable behaviour, though. */ if(pOut) *pOut = base; if(outLen) *outLen = fsl_strlen(base); return 0; } baseLen = fsl_strlen(base); ext = &pf->ext; dirs = &pf->dirs; |
︙ | ︙ | |||
30250 30251 30252 30253 30254 30255 30256 30257 30258 30259 30260 30261 30262 30263 | assert(buf->used < buf->capacity); buf->mem[buf->used] = 0; if(0==fsl_file_access( (char const *)buf->mem, 0 )){ goto gotone; } } } return FSL_RC_NOT_FOUND; gotone: if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 28447 28448 28449 28450 28451 28452 28453 28454 28455 28456 28457 28458 28459 28460 28461 28462 28463 28464 28465 28466 28467 28468 28469 | assert(buf->used < buf->capacity); buf->mem[buf->used] = 0; if(0==fsl_file_access( (char const *)buf->mem, 0 )){ goto gotone; } } } return FSL_RC_NOT_FOUND; gotone: if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } char * fsl__file_without_drive_letter(char * zIn){ #ifdef _WIN32 if( zIn && fsl_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; #endif return zIn; } |
︙ | ︙ | |||
30481 30482 30483 30484 30485 30486 30487 | *z = 0; ++rc; } } return rc; } | | | 28618 28619 28620 28621 28622 28623 28624 28625 28626 28627 28628 28629 28630 28631 28632 | *z = 0; ++rc; } } return rc; } void fsl_buffer_strip_slashes(fsl_buffer * b){ b->used -= fsl_strip_trailing_slashes((char *)b->mem, (fsl_int_t)b->used); } int fsl_file_rename(const char *zFrom, const char *zTo){ int rc; #if defined(_WIN32) |
︙ | ︙ | |||
30706 30707 30708 30709 30710 30711 30712 | to overwrite the symlinked-to file */; rc = fsl_buffer_to_filename(&content, zLinkFile); } } return rc; } | | < < < < < < < < < < < < | 28843 28844 28845 28846 28847 28848 28849 28850 28851 28852 28853 28854 28855 28856 28857 28858 28859 28860 28861 28862 28863 28864 28865 28866 | to overwrite the symlinked-to file */; rc = fsl_buffer_to_filename(&content, zLinkFile); } } return rc; } int fsl__symlink_copy(char const *zFrom, char const *zTo, bool realLink){ int rc; fsl_buffer b = fsl_buffer_empty; rc = fsl_symlink_read(&b, zFrom); if(0==rc){ rc = fsl_symlink_create(fsl_buffer_cstr(&b), zTo, realLink); } fsl_buffer_clear(&b); return rc; } #if 0 int fsl_file_relative_name( char const * zRoot, char const * zPath, fsl_buffer * pOut, char retainSlash ){ int rc = FSL_RC_NYI; char * zPath; fsl_size_t rootLen; |
︙ | ︙ | |||
30786 30787 30788 30789 30790 30791 30792 30793 30794 30795 30796 30797 30798 30799 | int fsl_repo_install_schema_forum(fsl_cx *f){ int rc; fsl_db * db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; if(fsl_db_table_exists(db, FSL_DBROLE_REPO, "forumpost")){ return 0; } rc = fsl_db_exec_multi(db, "%s",fsl_schema_forum()); if(rc){ rc = fsl_cx_uplift_db_error(f, db); } return rc; } | > | 28911 28912 28913 28914 28915 28916 28917 28918 28919 28920 28921 28922 28923 28924 28925 | int fsl_repo_install_schema_forum(fsl_cx *f){ int rc; fsl_db * db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; if(fsl_db_table_exists(db, FSL_DBROLE_REPO, "forumpost")){ return 0; } MARKER(("table not exists?\n")); rc = fsl_db_exec_multi(db, "%s",fsl_schema_forum()); if(rc){ rc = fsl_cx_uplift_db_error(f, db); } return rc; } |
︙ | ︙ | |||
31050 31051 31052 31053 31054 31055 31056 | FILE * f = (FILE*) state; *n = (fsl_size_t)fread( dest, 1, *n, f ); return *n ? 0 : (feof(f) ? 0 : FSL_RC_IO); } | | | 29176 29177 29178 29179 29180 29181 29182 29183 29184 29185 29186 29187 29188 29189 29190 | FILE * f = (FILE*) state; *n = (fsl_size_t)fread( dest, 1, *n, f ); return *n ? 0 : (feof(f) ? 0 : FSL_RC_IO); } void fsl_finalizer_f_FILE( void * state, void * mem ){ if(mem){ fsl_fclose((FILE*)mem); } } int fsl_stream( fsl_input_f inF, void * inState, fsl_output_f outF, void * outState ){ |
︙ | ︙ | |||
31534 31535 31536 31537 31538 31539 31540 | if(rc) return rc; } self->list[self->used++] = cp; if(self->used<self->capacity) self->list[self->used]=NULL; return 0; } | | | 29660 29661 29662 29663 29664 29665 29666 29667 29668 29669 29670 29671 29672 29673 29674 | if(rc) return rc; } self->list[self->used++] = cp; if(self->used<self->capacity) self->list[self->used]=NULL; return 0; } int fsl_list_v_fsl_free(void * obj, void * visitorState ){ if(obj) fsl_free( obj ); return 0; } int fsl_list_clear( fsl_list * const self, fsl_list_visitor_f childFinalizer, void * finalizerState ){ /* |
︙ | ︙ | |||
32865 32866 32867 32868 32869 32870 32871 | ); } #define fsl_merge_state_empty_m { \ NULL/*f*/, \ NULL/*opt*/, \ NULL/*filename*/, \ | | | < | 30991 30992 30993 30994 30995 30996 30997 30998 30999 31000 31001 31002 31003 31004 31005 31006 | ); } #define fsl_merge_state_empty_m { \ NULL/*f*/, \ NULL/*opt*/, \ NULL/*filename*/, \ NULL/*prevName*/, \ FSL_MERGE_FCHANGE_NONE/*fileChangeType*/ \ } /** Initialized-with-defaults fsl_merge_state instance, intended for use in non-const copy initialization. */ const fsl_merge_state fsl_merge_state_empty = fsl_merge_state_empty_m; |
︙ | ︙ | |||
33360 33361 33362 33363 33364 33365 33366 | if( fsl_file_size(zFullNewPath)>=0 ){ rc = fsl_file_tempname(bTmp, "", NULL); if(rc) goto merge_rename_end; rc = fsl_cx_exec(f, "INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%B)", zNewName, bTmp); if(rc) goto merge_rename_end; rc = fsl_is_symlink(zFullNewPath) | | | | 31485 31486 31487 31488 31489 31490 31491 31492 31493 31494 31495 31496 31497 31498 31499 31500 31501 31502 31503 31504 31505 31506 31507 31508 | if( fsl_file_size(zFullNewPath)>=0 ){ rc = fsl_file_tempname(bTmp, "", NULL); if(rc) goto merge_rename_end; rc = fsl_cx_exec(f, "INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%B)", zNewName, bTmp); if(rc) goto merge_rename_end; rc = fsl_is_symlink(zFullNewPath) ? fsl__symlink_copy(zFullNewPath, fsl_buffer_cstr(bTmp), realSymlinks) : fsl_file_copy(zFullNewPath, fsl_buffer_cstr(bTmp)); if(rc){ rc = fsl_cx_err_set(f, rc, "Error copying file [%s].", zFullNewPath); } if(rc) goto merge_rename_end; } rc = fsl_is_symlink(zFullOldPath) ? fsl__symlink_copy(zFullOldPath, zFullNewPath, realSymlinks) : fsl_file_copy(zFullOldPath, zFullNewPath); if(0==rc){ fsl_file_exec_set(zFullNewPath, !!isExe); fsl_file_unlink(zFullOldPath); /* ^^^ Ignore errors: not critical here */ } merge_rename_end: |
︙ | ︙ | |||
34456 34457 34458 34459 34460 34461 34462 | This is a tricky query to do efficiently. If the tag is very common (ex: "trunk") then we want to use the query identified below as Q1 - which searching the most recent EVENT table entries for the most recent with the tag. But if the tag is relatively scarce (anything other than "trunk", basically) then we want to do the indexed search show below as Q2. */ | | | 32581 32582 32583 32584 32585 32586 32587 32588 32589 32590 32591 32592 32593 32594 32595 | This is a tricky query to do efficiently. If the tag is very common (ex: "trunk") then we want to use the query identified below as Q1 - which searching the most recent EVENT table entries for the most recent with the tag. But if the tag is relatively scarce (anything other than "trunk", basically) then we want to do the indexed search show below as Q2. */ static fsl_id_t fsl_morewt(fsl_cx * const f, const char *zTag, fsl_satype_e type){ char const * zType = fsl_satype_event_cstr(type); return fsl_db_g_id(fsl_cx_db_repo(f), 0, "SELECT objid FROM (" /* Q1: Begin by looking for the tag in the 30 most recent events */ "SELECT objid" " FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex" " WHERE type GLOB '%q'" |
︙ | ︙ | |||
34488 34489 34490 34491 34492 34493 34494 | ") LIMIT 1;", zType, zTag, zTag, zType ); } /** Modes for fsl_start_of_branch(). | < < < | 32613 32614 32615 32616 32617 32618 32619 32620 32621 32622 32623 32624 32625 32626 | ") LIMIT 1;", zType, zTag, zTag, zType ); } /** Modes for fsl_start_of_branch(). */ enum fsl_stobr_type { /** The check-in of the parent branch off of which the branch containing RID originally diverged. */ FSL_STOBR_ORIGIN = 0, |
︙ | ︙ | |||
34514 34515 34516 34517 34518 34519 34520 | }; /* ** Return the RID that is the "root" of the branch that contains ** check-in "rid". Details depending on eType. If not found, rid is ** returned. */ | | > | | < | > | | | < < < < < < | | < < < | > > > > | > | > | > > | | | | | 32636 32637 32638 32639 32640 32641 32642 32643 32644 32645 32646 32647 32648 32649 32650 32651 32652 32653 32654 32655 32656 32657 32658 32659 32660 32661 32662 32663 32664 32665 32666 32667 32668 32669 32670 32671 32672 32673 32674 32675 32676 32677 32678 32679 32680 32681 32682 32683 32684 32685 32686 32687 32688 32689 32690 32691 32692 32693 32694 32695 32696 32697 32698 32699 32700 32701 32702 32703 32704 32705 32706 32707 32708 32709 32710 32711 32712 32713 32714 32715 32716 32717 | }; /* ** Return the RID that is the "root" of the branch that contains ** check-in "rid". Details depending on eType. If not found, rid is ** returned. */ static fsl_id_t fsl_start_of_branch(fsl_cx * f, fsl_id_t rid, enum fsl_stobr_type eType){ fsl_db * db; fsl_stmt q = fsl_stmt_empty; int rc; fsl_id_t ans = rid; char * zBr = 0; rc = fsl_branch_of_rid(f, rid, true, &zBr); if(rc) return rc; db = fsl_cx_db_repo(f); assert(db); rc = fsl_db_prepare(db, &q, "SELECT pid, EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid)" " FROM plink" " WHERE cid=? AND isprim", FSL_TAGID_BRANCH, zBr ); fsl_free(zBr); zBr = 0; if(rc){ ans = -2; fsl_cx_uplift_db_error(f, db); MARKER(("Internal error: fsl_db_prepare() says: %s\n", fsl_rc_cstr(rc))); goto end; } do{ fsl_stmt_reset(&q); fsl_stmt_bind_id(&q, 1, ans); rc = fsl_stmt_step(&q); if( rc!=FSL_RC_STEP_ROW ) break; if( eType==FSL_STOBR_FIRST_CI && fsl_stmt_g_int32(&q,1)==0 ){ break; } ans = fsl_stmt_g_id(&q, 0); }while( fsl_stmt_g_int32(&q, 1)==1 && ans>0 ); fsl_stmt_finalize(&q); end: if( ans>0 && eType==FSL_STOBR_YOAN ){ rc = fsl_branch_of_rid(f, ans, true, &zBr); if(rc) goto oom; else{ ans = fsl_youngest_ancestor_in_branch(f, rid, zBr); fsl_free(zBr); } } return ans; oom: if(!f->error.code){ fsl_cx_err_set(f, FSL_RC_OOM, NULL); }/* Else assume the OOM is really a misleading side-effect of another failure. */ return -1; } int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ){ fsl_id_t rid = 0; fsl_id_t vid; fsl_size_t symLen; /* fsl_int_t i; */ fsl_db * dbR = fsl_cx_db_repo(f); fsl_db * dbC = fsl_cx_db_ckout(f); bool startOfBranch = 0; int rc = 0; if(!f || !sym || !*sym || !rv) return FSL_RC_MISUSE; else if(!dbR) return FSL_RC_NOT_A_REPO; if(FSL_SATYPE_BRANCH_START==type){ /* The original implementation takes a (char const *) for the type, and treats "b" (branch?) as a special case of FSL_SATYPE_CHECKIN, resets the type to "ci", then sets startOfBranch to 1. We introduced the FSL_SATYPE_BRANCH |
︙ | ︙ | |||
34657 34658 34659 34660 34661 34662 34663 | if(rid>0) goto gotit; } /* Deprecated time formats elided: local:..., utc:... */ /* "tag:" + symbolic-name */ if( memcmp(sym, "tag:", 4)==0 ){ | | < < < < < < < < < | > > | 32779 32780 32781 32782 32783 32784 32785 32786 32787 32788 32789 32790 32791 32792 32793 32794 32795 32796 32797 32798 32799 32800 32801 32802 32803 32804 32805 32806 32807 32808 32809 32810 32811 32812 32813 32814 32815 32816 32817 32818 32819 32820 32821 32822 32823 32824 32825 32826 32827 32828 32829 32830 32831 | if(rid>0) goto gotit; } /* Deprecated time formats elided: local:..., utc:... */ /* "tag:" + symbolic-name */ if( memcmp(sym, "tag:", 4)==0 ){ rid = fsl_morewt(f, sym+4, type); if(rid>0 && startOfBranch){ rid = fsl_start_of_branch(f, rid, FSL_STOBR_FIRST_CI); } goto gotit; } /* root:TAG -> The origin of the branch */ if( memcmp(sym, "root:", 5)==0 ){ rc = fsl_sym_to_rid(f, sym+5, type, &rid); if(!rc && rid>0){ rid = fsl_start_of_branch(f, rid, FSL_STOBR_ORIGIN); } goto gotit; } /* merge-in:TAG -> Most recent merge-in for the branch */ if( memcmp(sym, "merge-in:", 9)==0 ){ rc = fsl_sym_to_rid(f, sym+9, type, &rid); if(!rc){ rid = fsl_start_of_branch(f, rid, FSL_STOBR_YOAN); } goto gotit; } symLen = fsl_strlen(sym); /* SHA1/SHA3 hash or prefix */ if( symLen>=4 && symLen<=FSL_STRLEN_K256 && fsl_validate16(sym, symLen) ){ fsl_stmt q = fsl_stmt_empty; char zUuid[FSL_STRLEN_K256+1]; memcpy(zUuid, sym, symLen); zUuid[symLen] = 0; fsl_canonical16(zUuid, symLen); rid = 0; /* Reminder to self: caching these queries would be cool but it can't work with the GLOBs. */ if( FSL_SATYPE_ANY==type ){ fsl_db_prepare(dbR, &q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); }else{ fsl_db_prepare(dbR, &q, "SELECT blob.rid" |
︙ | ︙ | |||
34746 34747 34748 34749 34750 34751 34752 | " WHERE tag.tagname='sym-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", sym, fsl_satype_event_cstr(type) ); }else{ | | | | 32861 32862 32863 32864 32865 32866 32867 32868 32869 32870 32871 32872 32873 32874 32875 32876 32877 32878 32879 32880 32881 32882 32883 32884 32885 | " WHERE tag.tagname='sym-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", sym, fsl_satype_event_cstr(type) ); }else{ rid = fsl_morewt(f, sym, type); //MARKER(("morewt(%s,%s) == %d\n", sym, fsl_satype_cstr(type), (int)rid)); } if( rid>0 ){ if(startOfBranch) rid = fsl_start_of_branch(f, rid, FSL_STOBR_FIRST_CI); goto gotit; } /* Undocumented: rid:### ==> rid */ if(symLen>4 && 0==fsl_strncmp("rid:",sym,4)){ int i; char const * oldSym = sym; sym += 4; for(i=0; fsl_isdigit(sym[i]); i++){} if( sym[i]==0 ){ if( FSL_SATYPE_ANY==type ){ |
︙ | ︙ | |||
34818 34819 34820 34821 34822 34823 34824 | assert(f->error.code); rv = -3; } return rv; } } | | | | 32933 32934 32935 32936 32937 32938 32939 32940 32941 32942 32943 32944 32945 32946 32947 32948 | assert(f->error.code); rv = -3; } return rv; } } int fsl_sym_to_uuid( fsl_cx * f, char const * sym, fsl_satype_e type, fsl_uuid_str * rv, fsl_id_t * rvId ){ fsl_id_t rid = 0; fsl_db * dbR = fsl_needs_repo(f); fsl_uuid_str rvv = NULL; int rc = dbR ? fsl_sym_to_rid(f, sym, type, &rid) : FSL_RC_NOT_A_REPO; if(!rc){ |
︙ | ︙ | |||
35022 35023 35024 35025 35026 35027 35028 | case FSL_RC_STEP_ROW: rc = 0; *rv = fsl_stmt_g_id(q, 0); break; case 0: rc = 0; *rv = 0; | < | 33137 33138 33139 33140 33141 33142 33143 33144 33145 33146 33147 33148 33149 33150 | case FSL_RC_STEP_ROW: rc = 0; *rv = fsl_stmt_g_id(q, 0); break; case 0: rc = 0; *rv = 0; default: fsl_cx_uplift_db_error(f, q->db); break; } fsl_stmt_reset(q); return rc; } |
︙ | ︙ | |||
35311 35312 35313 35314 35315 35316 35317 | if(rc){ rc = fsl_cx_err_set(f, rc, "Cannot unlink existing repo file: %s", opt->filename); goto end2; } } rc = fsl__cx_attach_role(f, opt->filename, FSL_DBROLE_REPO, true); | | | 33425 33426 33427 33428 33429 33430 33431 33432 33433 33434 33435 33436 33437 33438 33439 | if(rc){ rc = fsl_cx_err_set(f, rc, "Cannot unlink existing repo file: %s", opt->filename); goto end2; } } rc = fsl__cx_attach_role(f, opt->filename, FSL_DBROLE_REPO, true); MARKER(("attach role rc=%s\n", fsl_rc_cstr(rc))); if(rc){ goto end2; } db = fsl_cx_db(f); if(!f->repo.user){ f->repo.user = fsl_user_name_guess() /* Ignore OOM error here - we'll use 'root' |
︙ | ︙ | |||
36006 36007 36008 36009 36010 36011 36012 | * The original fingerprint algorithm used "quote(mtime)". But this could * give slightly different answers depending on how the floating-point * hardware is configured. For example, it gave different answers on * native Linux versus running under valgrind. */ if(0==version){ fsl_stmt_finalize(&q); | | | | | | | | | > | 34120 34121 34122 34123 34124 34125 34126 34127 34128 34129 34130 34131 34132 34133 34134 34135 34136 34137 34138 34139 34140 34141 34142 34143 34144 34145 | * The original fingerprint algorithm used "quote(mtime)". But this could * give slightly different answers depending on how the floating-point * hardware is configured. For example, it gave different answers on * native Linux versus running under valgrind. */ if(0==version){ fsl_stmt_finalize(&q); rc = fsl_buffer_append(sql, "SELECT rcvid, quote(uid), quote(mtime), " "quote(nonce), quote(ipaddr) " "FROM rcvfrom ", -1); }else{ assert(1==version); rc = fsl_buffer_append(sql, "SELECT rcvid, quote(uid), datetime(mtime), " "quote(nonce), quote(ipaddr) " "FROM rcvfrom ", -1); } if(rc) goto end; rc = (rcvid>0) ? fsl_buffer_appendf(sql, "WHERE rcvid=%" FSL_ID_T_PFMT, rcvid) : fsl_buffer_append(sql, "ORDER BY rcvid DESC LIMIT 1", -1); if(rc) goto end; rc = fsl_db_prepare(db, &q, "%b", sql); if(rc) goto end; rc = fsl_stmt_step(&q); |
︙ | ︙ | |||
36100 36101 36102 36103 36104 36105 36106 | rc = fsl_rid_to_uuid2(f, manifestRid, bHash); if(rc) goto end; ridHash = (char *)bHash->mem; }else{ ridHash = f->ckout.uuid; } assert(ridHash); | | | > | 34215 34216 34217 34218 34219 34220 34221 34222 34223 34224 34225 34226 34227 34228 34229 34230 34231 | rc = fsl_rid_to_uuid2(f, manifestRid, bHash); if(rc) goto end; ridHash = (char *)bHash->mem; }else{ ridHash = f->ckout.uuid; } assert(ridHash); rc = fsl_buffer_append(pHash, ridHash, -1); if(!rc) rc = fsl_buffer_append(pHash, "\n", 1); if(rc) goto end; } if(pTags){ fsl_stmt q = fsl_stmt_empty; fsl_db * const db = fsl_cx_db_repo(f); assert(db && "We can't have a checkout w/o a repo."); str = fsl_db_g_text(db, NULL, "SELECT VALUE FROM tagxref " "WHERE rid=%" FSL_ID_T_PFMT |
︙ | ︙ | |||
36123 36124 36125 36126 36127 36128 36129 | " FROM tagxref, tag" " WHERE tagxref.rid=%" FSL_ID_T_PFMT " AND tagxref.tagtype>0" " AND tag.tagid=tagxref.tagid" " AND tag.tagname GLOB 'sym-*'" " /*%s()*/", f->ckout.rid, __func__); | > | > > | > | 34239 34240 34241 34242 34243 34244 34245 34246 34247 34248 34249 34250 34251 34252 34253 34254 34255 34256 34257 34258 34259 34260 34261 34262 34263 34264 | " FROM tagxref, tag" " WHERE tagxref.rid=%" FSL_ID_T_PFMT " AND tagxref.tagtype>0" " AND tag.tagid=tagxref.tagid" " AND tag.tagname GLOB 'sym-*'" " /*%s()*/", f->ckout.rid, __func__); if(rc) goto end; while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ const char *zName = fsl_stmt_g_text(&q, 0, NULL); rc = fsl_buffer_appendf(pTags, "tag %s\n", zName); if(rc) break; } fsl_stmt_finalize(&q); } end: if(bHash){ fsl__cx_scratchpad_yield(f, bHash); } return rc; } /** Internal state for the rebuild process. */ struct FslRebuildState { |
︙ | ︙ | |||
36517 36518 36519 36520 36521 36522 36523 | frs.opt = frs.step.opt = opt; rc = fsl__rebuild_update_schema(&frs); if(!rc) rc = fsl_buffer_reserve(sql, 1024 * 4); if(rc) goto end; fsl__cx_clear_mf_seen(f, false); /* DROP all tables which are not part of our One True Vision of the | | < < < < < < < | 34637 34638 34639 34640 34641 34642 34643 34644 34645 34646 34647 34648 34649 34650 34651 | frs.opt = frs.step.opt = opt; rc = fsl__rebuild_update_schema(&frs); if(!rc) rc = fsl_buffer_reserve(sql, 1024 * 4); if(rc) goto end; fsl__cx_clear_mf_seen(f, false); /* DROP all tables which are not part of our One True Vision of the repo db... */ rc = fsl_cx_prepare(f, &q, "SELECT name FROM %!Q.sqlite_schema /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," "'purgeevent','purgeitem','unversioned'," |
︙ | ︙ | |||
41325 41326 41327 41328 41329 41330 41331 | /** SQL function to return the number of seconds since 1970. This is the same as strftime('%s','now') but is more compact. */ static void fsl_db_now_udf( sqlite3_context *context, | | | | | | 39438 39439 39440 39441 39442 39443 39444 39445 39446 39447 39448 39449 39450 39451 39452 39453 39454 39455 39456 39457 39458 39459 39460 39461 39462 39463 39464 | /** SQL function to return the number of seconds since 1970. This is the same as strftime('%s','now') but is more compact. */ static void fsl_db_now_udf( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3_result_int64(context, (sqlite3_int64)time(0)); } /** SQL function to convert a Julian Day to a Unix timestamp. */ static void fsl_db_j2u_udf( sqlite3_context *context, int argc, sqlite3_value **argv ){ double const jd = (double)sqlite3_value_double(argv[0]); sqlite3_result_int64(context, (sqlite3_int64)fsl_julian_to_unix(jd)); } /** SQL function FSL_CKOUT_DIR([bool includeTrailingSlash=1]) returns |
︙ | ︙ | |||
41384 41385 41386 41387 41388 41389 41390 | sqlite3_value **argv ){ fsl_cx * f = (fsl_cx*)sqlite3_user_data(context); fsl_time_t mtime = 0; int rc; fsl_id_t vid, fid; assert(f); | < | 39497 39498 39499 39500 39501 39502 39503 39504 39505 39506 39507 39508 39509 39510 | sqlite3_value **argv ){ fsl_cx * f = (fsl_cx*)sqlite3_user_data(context); fsl_time_t mtime = 0; int rc; fsl_id_t vid, fid; assert(f); vid = (fsl_id_t)sqlite3_value_int(argv[0]); fid = (fsl_id_t)sqlite3_value_int(argv[1]); rc = fsl_mtime_of_manifest_file(f, vid, fid, &mtime); if( rc==0 ){ sqlite3_result_int64(context, mtime); }else{ sqlite3_result_error(context, "fsl_mtime_of_manifest_file() failed", -1); |
︙ | ︙ | |||
41550 41551 41552 41553 41554 41555 41556 | /* Implement the user() SQL function. user() takes no arguments and returns the user ID of the current user. */ static void fsl_db_user_udf( sqlite3_context *context, | | | | 39662 39663 39664 39665 39666 39667 39668 39669 39670 39671 39672 39673 39674 39675 39676 39677 | /* Implement the user() SQL function. user() takes no arguments and returns the user ID of the current user. */ static void fsl_db_user_udf( sqlite3_context *context, int argc, sqlite3_value **argv ){ fsl_cx * f = (fsl_cx*)sqlite3_user_data(context); assert(f); if(f->repo.user){ sqlite3_result_text(context, f->repo.user, -1, SQLITE_STATIC); }else{ sqlite3_result_null(context); |
︙ | ︙ | |||
41647 41648 41649 41650 41651 41652 41653 | p1 = (const char*)sqlite3_value_text(argv[0]); p2 = (const char*)sqlite3_value_text(argv[1]); if(!p1 || !p2){ sqlite3_result_null(context); return; } int (*cmp)(char const *, char const *) = | | | 39759 39760 39761 39762 39763 39764 39765 39766 39767 39768 39769 39770 39771 39772 39773 | p1 = (const char*)sqlite3_value_text(argv[0]); p2 = (const char*)sqlite3_value_text(argv[1]); if(!p1 || !p2){ sqlite3_result_null(context); return; } int (*cmp)(char const *, char const *) = f->cache.caseInsensitive ? fsl_stricmp : fsl_strcmp; if(0==cmp(p1, p2)){ sqlite3_result_int(context, 1); return; } b = fsl__cx_scratchpad(f); rc = fsl_buffer_appendf(b, "%s/", p2); if(rc) goto oom; |
︙ | ︙ | |||
41692 41693 41694 41695 41696 41697 41698 | sqlite3_value **argv ){ fsl_cx * const f = (fsl_cx*)sqlite3_user_data(context); fsl_list * li = NULL; fsl_glob_category_e globType; char const * p1; char const * p2; | < | 39804 39805 39806 39807 39808 39809 39810 39811 39812 39813 39814 39815 39816 39817 | sqlite3_value **argv ){ fsl_cx * const f = (fsl_cx*)sqlite3_user_data(context); fsl_list * li = NULL; fsl_glob_category_e globType; char const * p1; char const * p2; p2 = (const char*)sqlite3_value_text(argv[1])/*value to check*/; if(NULL==p2 || 0==p2[0]){ sqlite3_result_int(context, 0); return; } p1 = (const char*)sqlite3_value_text(argv[0])/*glob set name*/; globType = fsl_glob_name_to_category(p1); |
︙ | ︙ | |||
41794 41795 41796 41797 41798 41799 41800 | SQLITE_UTF8 | SQLITE_DETERMINISTIC, /* noting that ^^^^^ it's only deterministic for a given statement execution IF no SQL triggers an effect which forces the globs to reload. That "shouldn't ever happen." */ f, fsl_db_cx_glob_udf, 0, 0 ); | < | 39905 39906 39907 39908 39909 39910 39911 39912 39913 39914 39915 39916 39917 39918 | SQLITE_UTF8 | SQLITE_DETERMINISTIC, /* noting that ^^^^^ it's only deterministic for a given statement execution IF no SQL triggers an effect which forces the globs to reload. That "shouldn't ever happen." */ f, fsl_db_cx_glob_udf, 0, 0 ); #if 0 /* functions registered in v1 by db.c:db_open(). */ /* porting cgi() requires access to the HTTP/CGI layer. i.e. this belongs downstream. */ sqlite3_create_function(dbh, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); sqlite3_create_function(dbh, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); re_add_sql_func(db) /* Requires the regex bits. */; |
︙ | ︙ | |||
41937 41938 41939 41940 41941 41942 41943 | void fsl_filename_free(void *pOld){ #if defined(_WIN32) fsl_free(pOld); #elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) fsl_free(pOld); #else | < | 40047 40048 40049 40050 40051 40052 40053 40054 40055 40056 40057 40058 40059 40060 | void fsl_filename_free(void *pOld){ #if defined(_WIN32) fsl_free(pOld); #elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) fsl_free(pOld); #else /* No-op on all other unix */ #endif } char *fsl_filename_to_utf8(const void *zFilename){ #if defined(_WIN32) int nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0); |
︙ | ︙ | |||
42014 42015 42016 42017 42018 42019 42020 | been extended since this code was ported: void *fossil_utf8_to_path(const char *zUtf8, int isDir) That isDir param is only for Windows and its only purpose is to ensure that the translated path is not within 12 bytes of MAX_PATH. That same effect can be had by simply always assuming | | | | 40123 40124 40125 40126 40127 40128 40129 40130 40131 40132 40133 40134 40135 40136 40137 40138 | been extended since this code was ported: void *fossil_utf8_to_path(const char *zUtf8, int isDir) That isDir param is only for Windows and its only purpose is to ensure that the translated path is not within 12 bytes of MAX_PATH. That same effect can be had by simply always assuming that bool is true and sacrificing those 12 bytes and that far-edge case. Also, the newer code jumps through many hoops which seem unimportant for fossil, e.g. handling UNC-style paths. Porting that latter bit over requires someone who can at least test whether it compiles. */ |
︙ | ︙ | |||
42172 42173 42174 42175 42176 42177 42178 | assert(dbC && "Must only be called when a checkout is opened."); assert(dbR && "Must only be called when a repo is opened."); if(!dbC) return FSL_RC_NOT_A_CKOUT; else if(!dbR) return FSL_RC_NOT_A_REPO; if(vid<=0) vid = f->ckout.rid; assert(vid>=0); | | | 40281 40282 40283 40284 40285 40286 40287 40288 40289 40290 40291 40292 40293 40294 40295 | assert(dbC && "Must only be called when a checkout is opened."); assert(dbR && "Must only be called when a repo is opened."); if(!dbC) return FSL_RC_NOT_A_CKOUT; else if(!dbR) return FSL_RC_NOT_A_REPO; if(vid<=0) vid = f->ckout.rid; assert(vid>=0); rc = fsl_db_transaction_begin(dbC); if(rc) return rc; alreadyHad = fsl_db_exists(dbC, "SELECT 1 FROM vfile " "WHERE vid=%" FSL_ID_T_PFMT, vid); if(clearOtherVersions){ /* Reminder to self: DO NOT clear vmerge here. Doing so will break |
︙ | ︙ | |||
42262 42263 42264 42265 42266 42267 42268 | when changing the checkout. */ if(!rc && vid>0){ if(!alreadyHad){ assert(d.rid>0); } } fsl_deck_finalize(&d); | | | | 40371 40372 40373 40374 40375 40376 40377 40378 40379 40380 40381 40382 40383 40384 40385 40386 | when changing the checkout. */ if(!rc && vid>0){ if(!alreadyHad){ assert(d.rid>0); } } fsl_deck_finalize(&d); if(rc) fsl_db_transaction_rollback(dbC); else rc = fsl_db_transaction_commit(dbC); if(rc && !f->error.code){ if(dbC->error.code) fsl_cx_uplift_db_error(f, dbC); else if(dbR->error.code) fsl_cx_uplift_db_error(f, dbR); } return rc; } |
︙ | ︙ | |||
42564 42565 42566 42567 42568 42569 42570 | } fsl_stmt_cached_yield(stUpdate); fsl_stmt_finalize(&q); return rc; } int fsl__vfile_to_ckout(fsl_cx * const f, fsl_id_t vfileId, | | | 40673 40674 40675 40676 40677 40678 40679 40680 40681 40682 40683 40684 40685 40686 40687 | } fsl_stmt_cached_yield(stUpdate); fsl_stmt_finalize(&q); return rc; } int fsl__vfile_to_ckout(fsl_cx * const f, fsl_id_t vfileId, int * wasWritten){ int rc = 0; fsl_db * const db = fsl_needs_ckout(f); fsl_stmt q = fsl_stmt_empty; int counter = 0; fsl_buffer content = fsl_buffer_empty; char const * sql; fsl_id_t qArg; |
︙ | ︙ | |||
42703 42704 42705 42706 42707 42708 42709 | } end: fsl_buffer_clear(&content); fsl_stmt_finalize(&q); return rc; } | < < < < < < < < < < < < < | 40812 40813 40814 40815 40816 40817 40818 40819 40820 40821 40822 40823 40824 40825 | } end: fsl_buffer_clear(&content); fsl_stmt_finalize(&q); return rc; } #undef MARKER /* end of file ./src/vfile.c */ /* start of file ./src/vpath.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt |
︙ | ︙ | |||
43389 43390 43391 43392 43393 43394 43395 43396 43397 43398 43399 | This copy has been modified slightly, and expanded, for use with the libfossil project. */ #include <assert.h> #include <zlib.h> #include <stdlib.h> /* atoi() and friends */ #include <memory.h> /* memset() */ /* Write a 16- or 32-bit integer as little-endian into the given buffer. */ | > > > > > > | | | | | | > | | | | | | | | | | > > | > | | | | | | | | | | | | | > < | | > | 41485 41486 41487 41488 41489 41490 41491 41492 41493 41494 41495 41496 41497 41498 41499 41500 41501 41502 41503 41504 41505 41506 41507 41508 41509 41510 41511 41512 41513 41514 41515 41516 41517 41518 41519 41520 41521 41522 41523 41524 41525 41526 41527 41528 41529 41530 41531 41532 41533 41534 41535 41536 41537 41538 41539 41540 41541 41542 41543 41544 41545 41546 41547 41548 41549 41550 41551 41552 41553 41554 41555 41556 41557 41558 41559 41560 41561 41562 41563 41564 41565 41566 41567 41568 41569 41570 41571 41572 41573 41574 41575 41576 41577 41578 41579 41580 41581 41582 41583 41584 41585 41586 41587 41588 41589 41590 41591 41592 41593 41594 41595 41596 41597 41598 41599 41600 41601 41602 41603 41604 41605 41606 41607 41608 41609 41610 41611 41612 41613 41614 41615 41616 41617 41618 41619 41620 41621 41622 41623 41624 41625 41626 41627 41628 41629 41630 41631 41632 41633 41634 41635 41636 41637 41638 41639 41640 41641 41642 41643 41644 41645 41646 41647 41648 41649 41650 41651 | This copy has been modified slightly, and expanded, for use with the libfossil project. */ #include <assert.h> #include <zlib.h> #include <stdlib.h> /* atoi() and friends */ #include <memory.h> /* memset() */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) /* Write a 16- or 32-bit integer as little-endian into the given buffer. */ static void fzip_put16(char *z, int v){ z[0] = v & 0xff; z[1] = (v>>8) & 0xff; } static void fzip_put32(char *z, int v){ z[0] = v & 0xff; z[1] = (v>>8) & 0xff; z[2] = (v>>16) & 0xff; z[3] = (v>>24) & 0xff; } /** Set the date and time values from an ISO8601 date string. */ static void fzip_timestamp_from_str(fsl_zip_writer *z, const char *zDate){ int y, m, d; int H, M, S; y = atoi(zDate); m = atoi(&zDate[5]); d = atoi(&zDate[8]); H = atoi(&zDate[11]); M = atoi(&zDate[14]); S = atoi(&zDate[17]); z->dosTime = (H<<11) + (M<<5) + (S>>1); z->dosDate = ((y-1980)<<9) + (m<<5) + d; } fsl_buffer const * fsl_zip_body( fsl_zip_writer const * const z ){ return z ? &z->body : NULL; } void fsl_zip_timestamp_set_julian(fsl_zip_writer * const z, double rDate){ char buf[20] = {0}; fsl_julian_to_iso8601(rDate, buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = (fsl_time_t)((rDate - 2440587.5)*86400.0); } void fsl_zip_timestamp_set_unix(fsl_zip_writer * const z, fsl_time_t epochTime){ char buf[20] = {0}; fsl_julian_to_iso8601(fsl_unix_to_julian(epochTime), buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = epochTime; } /** Adds all directories for the given file to the zip if they are not in there already. Returns 0 on success, non-0 on error (namely OOM). */ static int fzip_mkdir(fsl_zip_writer * const z, char const *zName); /** Adds a file entry to zw's zip output. zName is the virtual name of the file or directory. If pSrc is NULL then it is assumed that we are creating a directory, otherwise the zip's entry is populated from pSrc. mPerms specify the fossil-specific permission flags from the fsl_fileperm_e enum. If doMkDirs is true then fzip_mkdir() is called to create the directory entries for zName, otherwise they are not. */ static int fzip_file_add(fsl_zip_writer * const zw, char const * zName, fsl_buffer const * pSrc, int mPerm, char doMkDirs){ int rc = 0; z_stream stream; fsl_size_t nameLen; int toOut = 0; int iStart; int iCRC = 0; int nByte = 0; int nByteCompr = 0; int nBlob; /* Size of the blob */ int iMethod; /* Compression method. */ int iMode = 0644; /* Access permissions */ char *z; char zHdr[30]; char zExTime[13]; char zBuf[100]; char zOutBuf[/*historical: 100000*/ 1024 * 16]; /* Fill in as much of the header as we know. */ nBlob = pSrc ? (int)pSrc->used : 0; if( pSrc ){ /* a file entry */ iMethod = pSrc->used ? 8 : 0 /* don't compress 0-byte files */; switch( mPerm ){ case FSL_FILE_PERM_LINK: iMode = 0120755; break; case FSL_FILE_PERM_EXE: iMode = 0100755; break; default: iMode = 0100644; break; } }else{ /* a directory entry */ iMethod = 0; iMode = 040755; } if(doMkDirs){ rc = fzip_mkdir(zw, zName) /* This causes an extraneous run of fzip_mkdir(), but it is harmless other than the waste of search time */; if(rc) return rc; } if(zw->rootDir){ zw->scratch.used = 0; rc = fsl_buffer_appendf(&zw->scratch, "%s%s", zw->rootDir, zName); if(rc){ assert(FSL_RC_OOM==rc); return rc; } zName = fsl_buffer_cstr(&zw->scratch); } nameLen = fsl_strlen(zName); memset(zHdr, 0, sizeof(zHdr)); fzip_put32(&zHdr[0], 0x04034b50); fzip_put16(&zHdr[4], 0x000a); fzip_put16(&zHdr[6], 0x0800); fzip_put16(&zHdr[8], iMethod); fzip_put16(&zHdr[10], zw->dosTime); fzip_put16(&zHdr[12], zw->dosDate); fzip_put16(&zHdr[26], nameLen); fzip_put16(&zHdr[28], 13); fzip_put16(&zExTime[0], 0x5455); fzip_put16(&zExTime[2], 9); zExTime[4] = 3; fzip_put32(&zExTime[5], zw->unixTime); fzip_put32(&zExTime[9], zw->unixTime); /* Write the header and filename. */ iStart = (int)zw->body.used; fsl_buffer_append(&zw->body, zHdr, 30); fsl_buffer_append(&zw->body, zName, nameLen); fsl_buffer_append(&zw->body, zExTime, 13); if( nBlob>0 ){ /* Write the compressed file. Compute the CRC as we progress. */ stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = 0; stream.avail_in = pSrc->used; stream.next_in = /* (unsigned char*) */pSrc->mem; stream.avail_out = sizeof(zOutBuf); stream.next_out = (unsigned char*)zOutBuf; |
︙ | ︙ | |||
43553 43554 43555 43556 43557 43558 43559 | } do{ stream.avail_out = sizeof(zOutBuf); stream.next_out = (unsigned char*)zOutBuf; deflate(&stream, Z_FINISH); toOut = sizeof(zOutBuf) - stream.avail_out; fsl_buffer_append(&zw->body, zOutBuf, toOut); | | < | | | | | | | > | | | | | | | | | | | | | | | | | | > | | | | | 41660 41661 41662 41663 41664 41665 41666 41667 41668 41669 41670 41671 41672 41673 41674 41675 41676 41677 41678 41679 41680 41681 41682 41683 41684 41685 41686 41687 41688 41689 41690 41691 41692 41693 41694 41695 41696 41697 41698 41699 41700 41701 41702 41703 41704 41705 41706 41707 41708 41709 41710 41711 41712 41713 41714 41715 41716 41717 41718 41719 41720 41721 41722 41723 41724 41725 41726 41727 41728 41729 41730 41731 41732 41733 41734 41735 41736 41737 41738 41739 41740 41741 41742 41743 41744 41745 41746 | } do{ stream.avail_out = sizeof(zOutBuf); stream.next_out = (unsigned char*)zOutBuf; deflate(&stream, Z_FINISH); toOut = sizeof(zOutBuf) - stream.avail_out; fsl_buffer_append(&zw->body, zOutBuf, toOut); }while( stream.avail_out==0 ); nByte = stream.total_in; nByteCompr = stream.total_out; deflateEnd(&stream); /* Go back and write the header, now that we know the compressed file size. */ z = (char *)zw->body.mem + iStart/* &blob_buffer(&body)[iStart] */; fzip_put32(&z[14], iCRC); fzip_put32(&z[18], nByteCompr); fzip_put32(&z[22], nByte); } /* Make an entry in the tables of contents */ memset(zBuf, 0, sizeof(zBuf)); fzip_put32(&zBuf[0], 0x02014b50); fzip_put16(&zBuf[4], 0x0317); fzip_put16(&zBuf[6], 0x000a); fzip_put16(&zBuf[8], 0x0800); fzip_put16(&zBuf[10], iMethod); fzip_put16(&zBuf[12], zw->dosTime); fzip_put16(&zBuf[14], zw->dosDate); fzip_put32(&zBuf[16], iCRC); fzip_put32(&zBuf[20], nByteCompr); fzip_put32(&zBuf[24], nByte); fzip_put16(&zBuf[28], nameLen); fzip_put16(&zBuf[30], 9); fzip_put16(&zBuf[32], 0); fzip_put16(&zBuf[34], 0); fzip_put16(&zBuf[36], 0); fzip_put32(&zBuf[38], ((unsigned)iMode)<<16); fzip_put32(&zBuf[42], iStart); fsl_buffer_append(&zw->toc, zBuf, 46); fsl_buffer_append(&zw->toc, zName, nameLen); fzip_put16(&zExTime[2], 5); fsl_buffer_append(&zw->toc, zExTime, 9); ++zw->entryCount; return rc; } int fzip_mkdir(fsl_zip_writer * const z, char const *zName){ fsl_size_t i; fsl_size_t j; int rc = 0; char const * dirName; fsl_size_t nDir = z->dirs.used; for(i=0; zName[i]; i++){ if( zName[i]=='/' ){ while(zName[i+1]=='/') ++i /* Skip extra slashes */; for(j=0; j<nDir; j++){ /* See if we know this dir already... */ dirName = (char const *)z->dirs.list[j]; if( fsl_strncmp(zName, dirName, i)==0 ) break; } if( j>=nDir ){ char * cp = fsl_strndup(zName, (fsl_int_t)i+1); rc = cp ? fsl_list_append(&z->dirs, cp) : FSL_RC_OOM; if(cp && rc){ fsl_free(cp); }else{ rc = fzip_file_add(z, cp, NULL, 0, 0); } } } } return rc; } int fsl_zip_file_add(fsl_zip_writer * const z, char const * zName, fsl_buffer const * pSrc, int mPerm){ return fzip_file_add(z, zName, pSrc, mPerm, 1); } int fsl_zip_root_set(fsl_zip_writer * const z, char const * zRoot ){ if(!z) return FSL_RC_MISUSE; else if(zRoot && *zRoot && fsl_is_absolute_path(zRoot)){ return FSL_RC_RANGE; }else{ |
︙ | ︙ | |||
43666 43667 43668 43669 43670 43671 43672 | n = fsl_file_simplify_name(cp, (fsl_int_t)n, 1); assert(n); assert('/'==cp[n-1]); cp[n-1] = 0; rc = fsl_is_simple_pathname(cp, 1); cp[n-1] = '/'; rc = rc | | | | > | | | | | | | | | | 41774 41775 41776 41777 41778 41779 41780 41781 41782 41783 41784 41785 41786 41787 41788 41789 41790 41791 41792 41793 41794 41795 41796 41797 41798 41799 41800 41801 41802 41803 41804 41805 41806 41807 41808 41809 41810 41811 41812 41813 41814 41815 41816 41817 41818 41819 41820 41821 41822 41823 41824 41825 41826 41827 41828 41829 41830 41831 41832 41833 41834 41835 41836 41837 41838 41839 41840 41841 41842 41843 41844 41845 | n = fsl_file_simplify_name(cp, (fsl_int_t)n, 1); assert(n); assert('/'==cp[n-1]); cp[n-1] = 0; rc = fsl_is_simple_pathname(cp, 1); cp[n-1] = '/'; rc = rc ? fzip_mkdir(z, cp) : FSL_RC_RANGE; z->rootDir = cp /* transfer ownership on error as well and let normal downstream clean it up. */; return rc; } } return 0; } } static void fsl_zip_finalize_impl(fsl_zip_writer * const z, bool alsoBody){ if(z){ fsl_buffer_clear(&z->toc); fsl_buffer_clear(&z->scratch); fsl_list_visit_free(&z->dirs, 1); assert(NULL==z->dirs.list); fsl_free(z->rootDir); if(alsoBody){ fsl_buffer_clear(&z->body); *z = fsl_zip_writer_empty; }else{ fsl_buffer cp = z->body; *z = fsl_zip_writer_empty; z->body = cp; } } } void fsl_zip_finalize(fsl_zip_writer * const z){ fsl_zip_finalize_impl(z, 1); } int fsl_zip_end( fsl_zip_writer * const z ){ int rc; fsl_int_t iTocStart; fsl_int_t iTocEnd; char zBuf[30]; iTocStart = (fsl_int_t)z->body.used; rc = fsl_buffer_append(&z->body, z->toc.mem, z->toc.used); if(rc) return rc; fsl_buffer_clear(&z->toc); iTocEnd = (fsl_int_t)z->body.used; memset(zBuf, 0, sizeof(zBuf)); fzip_put32(&zBuf[0], 0x06054b50); fzip_put16(&zBuf[4], 0); fzip_put16(&zBuf[6], 0); fzip_put16(&zBuf[8], (int)z->entryCount); fzip_put16(&zBuf[10], (int)z->entryCount); fzip_put32(&zBuf[12], iTocEnd - iTocStart); fzip_put32(&zBuf[16], iTocStart); fzip_put16(&zBuf[20], 0); rc = fsl_buffer_append(&z->body, zBuf, 22); fsl_zip_finalize_impl(z, 0); assert(z->body.used); return rc; } int fsl_zip_end_take( fsl_zip_writer * const z, fsl_buffer * dest ){ if(!z) return FSL_RC_MISUSE; else{ |
︙ | ︙ | |||
43760 43761 43762 43763 43764 43765 43766 43767 43768 43769 43770 43771 43772 43773 | rc = fsl_buffer_to_filename(&z->body, filename); } } fsl_zip_finalize( z ); return rc; } } struct ZipState{ fsl_cx * f; fsl_id_t vid; fsl_card_F_visitor_f progress; void * progressState; fsl_zip_writer z; | > > | 41869 41870 41871 41872 41873 41874 41875 41876 41877 41878 41879 41880 41881 41882 41883 41884 | rc = fsl_buffer_to_filename(&z->body, filename); } } fsl_zip_finalize( z ); return rc; } } struct ZipState{ fsl_cx * f; fsl_id_t vid; fsl_card_F_visitor_f progress; void * progressState; fsl_zip_writer z; |
︙ | ︙ | |||
43781 43782 43783 43784 43785 43786 43787 | }; static int fsl_card_F_visitor_zip(fsl_card_F const * fc, void * state){ ZipState * zs = (ZipState *)state; fsl_id_t frid; int rc = 0; | > | | 41892 41893 41894 41895 41896 41897 41898 41899 41900 41901 41902 41903 41904 41905 41906 41907 | }; static int fsl_card_F_visitor_zip(fsl_card_F const * fc, void * state){ ZipState * zs = (ZipState *)state; fsl_id_t frid; int rc = 0; if(!fc->uuid) return 0 /* file was removed in this (delta) manifest */; else if(zs->progress){ rc = (*zs->progress)(fc, zs->progressState); if(rc) return rc; }else if(FSL_FILE_PERM_LINK == fc->perm){ return fsl_cx_err_set(zs->f, FSL_RC_NYI, "Symlinks are not yet supported " "in ZIP output."); } |
︙ | ︙ | |||
43804 43805 43806 43807 43808 43809 43810 | rc = fsl_mtime_of_manifest_file(zs->f, zs->vid, frid, &mTime); if(!rc){ fsl_zip_timestamp_set_unix(&zs->z, mTime); zs->cbuf.used = 0; rc = fsl_content_get(zs->f, frid, &zs->cbuf); if(!rc){ rc = fsl_zip_file_add(&zs->z, fc->name, &zs->cbuf, | | > | 41916 41917 41918 41919 41920 41921 41922 41923 41924 41925 41926 41927 41928 41929 41930 41931 41932 41933 41934 41935 41936 41937 41938 41939 41940 41941 41942 | rc = fsl_mtime_of_manifest_file(zs->f, zs->vid, frid, &mTime); if(!rc){ fsl_zip_timestamp_set_unix(&zs->z, mTime); zs->cbuf.used = 0; rc = fsl_content_get(zs->f, frid, &zs->cbuf); if(!rc){ rc = fsl_zip_file_add(&zs->z, fc->name, &zs->cbuf, FSL_FILE_PERM_REGULAR); if(rc){ fsl_cx_err_set(zs->f, rc, "Error %s adding file [%s] " "to zip.", fsl_rc_cstr(rc), fc->name); } } } } return rc; } int fsl_repo_zip_sym_to_filename( fsl_cx * const f, char const * sym, char const * rootDir, char const * fileName, fsl_card_F_visitor_f progress, void * progressState ){ int rc; |
︙ | ︙ | |||
43899 43900 43901 43902 43903 43904 43905 43906 43907 43908 43909 43910 43911 43912 | rc, fsl_rc_cstr(rc)); } fsl_buffer_clear(&zs.cbuf); fsl_zip_finalize(&zs.z); fsl_deck_clean(&mf); return rc; } /* end of file ./src/zip.c */ /* start of file ./src/difftk_cstr.c */ /** @page page_difftk_cstr difftk.tcl Binary form of file ./src/difftk.tcl. */ | > > > > | 42012 42013 42014 42015 42016 42017 42018 42019 42020 42021 42022 42023 42024 42025 42026 42027 42028 42029 | rc, fsl_rc_cstr(rc)); } fsl_buffer_clear(&zs.cbuf); fsl_zip_finalize(&zs.z); fsl_deck_clean(&mf); return rc; } #undef MARKER /* end of file ./src/zip.c */ /* start of file ./src/difftk_cstr.c */ /** @page page_difftk_cstr difftk.tcl Binary form of file ./src/difftk.tcl. */ |
︙ | ︙ |
Changes to lib/libfossil.h.
︙ | ︙ | |||
243 244 245 246 247 248 249 | /** If true, the fsl_timer_xxx() family of functions might do something useful, otherwise they do not. */ #define FSL_CONFIG_ENABLE_TIMER 1 | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | /** If true, the fsl_timer_xxx() family of functions might do something useful, otherwise they do not. */ #define FSL_CONFIG_ENABLE_TIMER 1 #endif /* ORG_FOSSIL_SCM_FSL_CONFIG_H_INCLUDED */ /* end of file ./include/fossil-scm/config.h */ /* start of file ./include/fossil-scm/util.h */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ |
︙ | ︙ | |||
765 766 767 768 769 770 771 | modification made by this routine. Thus the user may need to reset the cursor to 0 if he wishes to start consuming the buffer at its starting point. Subsequent calls to this function will increment the cursor by the number of bytes returned via *n. The buffer's "used" member is used to determine the logical end of input. | < < < | | < | | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | modification made by this routine. Thus the user may need to reset the cursor to 0 if he wishes to start consuming the buffer at its starting point. Subsequent calls to this function will increment the cursor by the number of bytes returned via *n. The buffer's "used" member is used to determine the logical end of input. Returns 0 on success and has no error conditions except for invalid arguments, which result in undefined beavhiour. Results are undefined if any argument is NULL. Tip (and warning): sometimes a routine might have a const buffer handle which it would like to use in conjunction with this routine but cannot without violating constness. Here's a crude workaround: ``` |
︙ | ︙ | |||
912 913 914 915 916 917 918 | @see fsl_buffer_append() @see fsl_buffer_appendf() @see fsl_buffer_cstr() @see fsl_buffer_size() @see fsl_buffer_capacity() @see fsl_buffer_clear() @see fsl_buffer_reuse() | < < | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 | @see fsl_buffer_append() @see fsl_buffer_appendf() @see fsl_buffer_cstr() @see fsl_buffer_size() @see fsl_buffer_capacity() @see fsl_buffer_clear() @see fsl_buffer_reuse() */ struct fsl_buffer { /** The raw memory pointed to by this buffer. There are two ways of using this member: - If `this->capacity` is non-0 then the first `this->capacity` |
︙ | ︙ | |||
969 970 971 972 973 974 975 | precondition). Most APIs ensure that (used<capacity) is always true (as opposed to used<=capacity) because they add a trailing NUL byte which is not counted in the "used" length. */ fsl_size_t used; /** Used by some routines to keep a cursor into this->mem. | < < < < < > | < | < < < < < < > > | < | | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 | precondition). Most APIs ensure that (used<capacity) is always true (as opposed to used<=capacity) because they add a trailing NUL byte which is not counted in the "used" length. */ fsl_size_t used; /** Used by some routines to keep a cursor into this->mem. TODO: factor this back out and let those cases keep their own state. This is only used by fsl_input_f_buffer() (and that function cannot be implemented unless we add the cursor here or add another layer of state type specifically for it). TODO: No, don't do ^^^^. It turns out that the merge algo wants this as well. */ fsl_size_t cursor; }; /** Empty-initialized fsl_buffer instance, intended for const-copy initialization. */ #define fsl_buffer_empty_m {NULL,0U,0U,0U} /** Empty-initialized fsl_buffer instance, intended for copy initialization. */ FSL_EXPORT const fsl_buffer fsl_buffer_empty; /** A container for storing generic error state. It is used to |
︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 | z need not be NUL terminated - this function does not read past the first invalid byte. Thus is can be used on, e.g., full ISO8601-format strings. If z is NULL, 0 is returned. */ FSL_EXPORT int fsl_str_is_date2(const char *z); | < < < < < < < < < < < < < < < < | < | | < | 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 | z need not be NUL terminated - this function does not read past the first invalid byte. Thus is can be used on, e.g., full ISO8601-format strings. If z is NULL, 0 is returned. */ FSL_EXPORT int fsl_str_is_date2(const char *z); /** Reserves at least n bytes of capacity in buf. Returns 0 on success, FSL_RC_OOM if allocation fails, FSL_RC_MISUSE if !buf. If b is an external buffer then: - If n is 0, this disassociates b->mem from b, effectively clearing the buffer's state. Else... - The buffer is materialized, transformed into a managed buffer. This happens even if n is less than b->used because this routine is always used in preparation for writing to the buffer. - If n>0 then the greater of (n, b->used) bytes of memory are allocated, b->used bytes are copied from b->mem (its external memory) to the new block, and b->mem is replaced with the new block. Afterwards, b->capacity will be non-0. This does not change b->used, nor will it shrink the buffer (reduce buf->capacity) unless n is 0, in which case it immediately frees b->mem (if b is a managed buffer) and sets b->capacity and buf->used to 0. @see fsl_buffer_resize() @see fsl_buffer_materialize() @see fsl_buffer_clear() */ FSL_EXPORT int fsl_buffer_reserve( fsl_buffer * const b, fsl_size_t n ); |
︙ | ︙ | |||
1289 1290 1291 1292 1293 1294 1295 | Results are undefined if mem is NULL, but n may be 0. Results are undefined if passed a completely uninitialized buffer object. _Always_ initialize new buffer objects by copying fsl_buffer_empty or (when appropriate) fsl_buffer_empty_m. | < < < < < < < | | | | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | Results are undefined if mem is NULL, but n may be 0. Results are undefined if passed a completely uninitialized buffer object. _Always_ initialize new buffer objects by copying fsl_buffer_empty or (when appropriate) fsl_buffer_empty_m. @see fsl_buffer_materialize() */ FSL_EXPORT void fsl_buffer_external( fsl_buffer * const b, void const * mem, fsl_int_t n ); /** Convenience equivalent of fsl_buffer_reserve(b,0). */ FSL_EXPORT void fsl_buffer_clear( fsl_buffer * const b ); /** If b is a managed buffer, this resets b->used, b->cursor, and b->mem[0] (if b->mem is not NULL) to 0. If b is an external buffer, this clears all state from the buffer, behaving like fsl_buffer_clear() (making it available for reuse as a managed or external buffer). This does not (de)allocate memory, only changes the logical "used" size of the buffer. Returns its argument. Returns b. Achtung for fossil(1) porters: this function's semantics are much different from the fossil's blob_reset(). To get those semantics, use fsl_buffer_reserve(buf, 0) or its convenience form fsl_buffer_clear(). (This function _used_ to be called fsl_buffer_reset(), but it was renamed in the hope of avoiding related confusion.) */ FSL_EXPORT fsl_buffer * fsl_buffer_reuse( fsl_buffer * const b ); /** Similar to fsl_buffer_reserve() except that... For managed buffers: - It does not free all memory when n==0. Instead it essentially makes the memory a length-0, NUL-terminated string. - It will try to shrink (realloc) buf's memory if (n<buf->capacity). - It sets buf->capacity to (n+1) and buf->used to n. This routine allocates one extra byte to ensure that buf is always NUL-terminated. - On success it always NUL-terminates the buffer at offset buf->used. For external buffers it behaves slightly differently: - If n==buf->used, this is a no-op and returns 0. |
︙ | ︙ | |||
1365 1366 1367 1368 1369 1370 1371 | @see fsl_buffer_reserve() @see fsl_buffer_materialize() @see fsl_buffer_clear() */ FSL_EXPORT int fsl_buffer_resize( fsl_buffer * const buf, fsl_size_t n ); /** | | | | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 | @see fsl_buffer_reserve() @see fsl_buffer_materialize() @see fsl_buffer_clear() */ FSL_EXPORT int fsl_buffer_resize( fsl_buffer * const buf, fsl_size_t n ); /** Swaps the contents of the left and right arguments. Results are undefined if either argument is NULL or points to uninitialized memory. */ FSL_EXPORT void fsl_buffer_swap( fsl_buffer * left, fsl_buffer * right ); /** Similar fsl_buffer_swap() but it also optionally frees one of the buffer's memories after swapping them. If clearWhich is negative then the left buffer (1st arg) is cleared _after_ swapping (i.e., the NEW left hand side gets cleared). If clearWhich is greater than 0 then the right buffer (2nd arg) is |
︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 | ``` fsl_buffer_swap_free( &b1, &b2, 1 ); ``` Swaps the contents of b1 and b2, then frees the contents of the right-side buffer (b2). */ | | < < < < | 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 | ``` fsl_buffer_swap_free( &b1, &b2, 1 ); ``` Swaps the contents of b1 and b2, then frees the contents of the right-side buffer (b2). */ FSL_EXPORT void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, int clearWhich ); /** Appends the first n bytes of src, plus a NUL byte, to b, expanding b as necessary and incrementing b->used by n. If n is less than 0 then the equivalent of fsl_strlen((char const*)src) is used to calculate the length. If b is an external buffer, it is first transformed into a managed buffer. Results are undefined if b or src are NULL. If n is 0 (or negative and !*src), this function ensures that b->mem is not NULL and is NUL-terminated, so it may allocate |
︙ | ︙ | |||
1436 1437 1438 1439 1440 1441 1442 | /** Uses fsl_appendf() to append formatted output to the given buffer. Returns 0 on success and FSL_RC_OOM if an allocation fails while expanding dest. Results are undefined if either of the first two arguments are NULL. | < < < < < < < | | < < | < < < < | < < < < | | < | 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 | /** Uses fsl_appendf() to append formatted output to the given buffer. Returns 0 on success and FSL_RC_OOM if an allocation fails while expanding dest. Results are undefined if either of the first two arguments are NULL. @see fsl_buffer_append() @see fsl_buffer_reserve() */ FSL_EXPORT int fsl_buffer_appendf( fsl_buffer * const dest, char const * fmt, ... ); /** va_list counterpart to fsl_buffer_appendf(). */ FSL_EXPORT int fsl_buffer_appendfv( fsl_buffer * const dest, char const * fmt, va_list args ); /** Compresses the first pIn->used bytes of pIn to pOut. It is ok for pIn and pOut to be the same blob. pOut must either be the same as pIn or else a properly initialized buffer. Any prior contents will be freed or their memory reused. Results are undefined if any argument is NULL. Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR if the lower-level compression routines fail. Use fsl_buffer_uncompress() to uncompress the data. The data is encoded with a big-endian, unsigned 32-bit length as the first four bytes (holding its uncomressed size), and then the data as compressed by zlib. TODO: if pOut!=pIn1 then re-use pOut's memory, if it has any. @see fsl_buffer_compress2() @see fsl_buffer_uncompress() @see fsl_buffer_is_compressed() */ FSL_EXPORT int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut); /** Compress the concatenation of a blobs pIn1 and pIn2 into pOut. pOut must be either empty (cleanly initialized or newly recycled) or must be the same as either pIn1 or pIn2. Results are undefined if any argument is NULL. Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR if the lower-level compression routines fail. TODO: if pOut!=(pIn1 or pIn2) then re-use its memory, if it has any. @see fsl_buffer_compress() @see fsl_buffer_uncompress() @see fsl_buffer_is_compressed() */ FSL_EXPORT int fsl_buffer_compress2(fsl_buffer const *pIn1, fsl_buffer const *pIn2, fsl_buffer *pOut); /** Uncompress buffer pIn and store the result in pOut. It is ok for pIn and pOut to be the same buffer. Returns 0 on success. If pIn!=pOut then on error, depending on the type of error, pOut may have been partially written so the state of its contents are unspecified (but its state as a buffer object is still valid). pOut must be either cleanly initialized/empty or the same object as pIn. If it has any current memory, it will be reused if it's large enough and it is not the same pointer as pIn. Results are undefined if any argument is NULL. Returns 0 on success, FSL_RC_OOM on allocation error, and some other code if the lower-level decompression routines fail. Note that the decompression process, though computationally costly, is a no-op if pIn is not actually compressed. As a special case, if pIn==pOut and fsl_buffer_is_compressed() returns false for pIn then this is a no-op. |
︙ | ︙ | |||
1570 1571 1572 1573 1574 1575 1576 | /** The fsl_buffer counterpart of fsl_data_uncompressed_size(). */ FSL_EXPORT fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b); /** | | < | | < < | | < | < < < | | > | 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 | /** The fsl_buffer counterpart of fsl_data_uncompressed_size(). */ FSL_EXPORT fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b); /** Equivalent to ((char const *)b->mem). The returned string is effectively b->used bytes long unless the user decides to apply his own conventions. Note that the buffer APIs generally assure that buffers are NUL-terminated, meaning that strings returned from this function can (for the vast majority of cases) assume that the returned string is NUL-terminated (with a string length of b->used _bytes_). It is, however, possible for client code to violate that convention via direct manipulation of the buffer or using non-NUL-terminated extranal buffers. @see fsl_buffer_str() @see fsl_buffer_cstr2() */ FSL_EXPORT char const * fsl_buffer_cstr(fsl_buffer const * const b); /** If b has any memory allocated to it, that memory is returned. If len is not NULL then *len is set to b->used. If b has no memory then NULL is returned and *len (if len is not NULL) is set to 0. @see fsl_buffer_str() @see fsl_buffer_cstr() */ FSL_EXPORT char const * fsl_buffer_cstr2(fsl_buffer const * const b, fsl_size_t * const len); /** Equivalent to ((char *)b->mem). The returned memory is effectively b->used bytes long unless the user decides to apply their own conventions. Care must be taken to only write to the returned pointer for memory owned or write-proxied by this buffer. More specifically, results are undefined if b is an external buffer proxying const bytes. When in doubt about whether b is external, use fsl_buffer_materialize() to transform it to a managed buffer before using this routine, noting that any of the public fsl_buffer APIs which write to a |
︙ | ︙ | |||
1658 1659 1660 1661 1662 1663 1664 | When buffers of different length match on the first N bytes, where N is the shorter of the two buffers' lengths, it treats the shorter buffer as being "less than" the longer one. */ FSL_EXPORT int fsl_buffer_compare(fsl_buffer const * const lhs, fsl_buffer const * const rhs); | < < < < < < < < < | 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 | When buffers of different length match on the first N bytes, where N is the shorter of the two buffers' lengths, it treats the shorter buffer as being "less than" the longer one. */ FSL_EXPORT int fsl_buffer_compare(fsl_buffer const * const lhs, fsl_buffer const * const rhs); /** Bitwise-compares the contents of b against the file named by zFile. Returns 0 if they have the same size and contents, else non-zero. This function has no way to report if zFile cannot be opened, and any error results in a non-0 return value. No interpretation/canonicalization of zFile is performed - it is used as-is. |
︙ | ︙ | |||
1718 1719 1720 1721 1722 1723 1724 | Apply the delta in pDelta to the original content pOriginal to generate the target content pTarget. All three pointers must point to properly initialized memory. If pTarget==pOriginal then this is a destructive operation, replacing the original's content with its new form. | < < < < < < < < < < < | > > | < | < | > | 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 | Apply the delta in pDelta to the original content pOriginal to generate the target content pTarget. All three pointers must point to properly initialized memory. If pTarget==pOriginal then this is a destructive operation, replacing the original's content with its new form. Return 0 on success. @see fsl_buffer_delta_apply() @see fsl_delta_apply() @see fsl_delta_apply2() */ FSL_EXPORT int fsl_buffer_delta_apply( fsl_buffer const * const pOriginal, fsl_buffer const * const pDelta, fsl_buffer * const pTarget); /** Identical to fsl_buffer_delta_apply() except that if delta application fails then any error messages/codes are written to pErr if it is not NULL. It is rare that delta application fails (only if the inputs are invalid, e.g. do not belong together or are corrupt), but when it does, having error information can be useful. @see fsl_buffer_delta_apply() @see fsl_delta_apply() @see fsl_delta_apply2() */ FSL_EXPORT int fsl_buffer_delta_apply2( fsl_buffer const * const pOriginal, fsl_buffer const * const pDelta, fsl_buffer * const pTarget, fsl_error * const pErr); /** Uses a fsl_input_f() function to buffer input into a fsl_buffer. dest must be a non-NULL, initialized (though possibly empty) fsl_buffer object. Its contents, if any, will be overwritten by this function, and any memory it holds might be re-used. The src function is called, and passed the state parameter, to fetch the input. If it returns non-0, this function returns that error code. src() is called, possibly repeatedly, until it reports that there is no more data. Whether or not this function succeeds, dest still owns any memory pointed to by dest->mem, and the client must eventually free it by calling fsl_buffer_reserve(dest,0). dest->mem might (and possibly will) be (re)allocated by this function, so any pointers to it held from before this call might be invalidated by this call. On error non-0 is returned and dest may bge partially populated. Errors include: dest or src are NULL (FSL_RC_MISUSE) Allocation error (FSL_RC_OOM) src() returns an error code Whether or not the state parameter may be NULL depends on the src implementation requirements. On success dest will contain the contents read from the input source. dest->used will be the length of the read-in data, and dest->mem will point to the memory. dest->mem is automatically NUL-terminated if this function succeeds, but dest->used does not count that terminator. On error the state of dest->mem must be considered incomplete, and is not guaranteed to be NUL-terminated. Example usage: ``` fsl_buffer buf = fsl_buffer_empty; int rc = fsl_buffer_fill_from( &buf, fsl_input_f_FILE, |
︙ | ︙ | |||
1842 1843 1844 1845 1846 1847 1848 | FSL_EXPORT int fsl_buffer_fill_from_FILE( fsl_buffer * const dest, FILE * const src ); /** A wrapper for fsl_buffer_fill_from_FILE() which gets its input from the given file name. | < < < < < < < < < < < < | 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | FSL_EXPORT int fsl_buffer_fill_from_FILE( fsl_buffer * const dest, FILE * const src ); /** A wrapper for fsl_buffer_fill_from_FILE() which gets its input from the given file name. It uses fsl_fopen() to open the file, so it supports the name '-' as an alias for stdin. */ FSL_EXPORT int fsl_buffer_fill_from_filename( fsl_buffer * const dest, char const * filename ); /** Writes the given buffer to the given filename. Returns 0 on success, FSL_RC_MISUSE if !b or !fname, FSL_RC_IO if opening or writing fails. Uses fsl_fopen() to open the file, so it supports the name '-' as an alias for stdout. */ FSL_EXPORT int fsl_buffer_to_filename( fsl_buffer const * const b, char const * fname ); /** Copy N lines of text from pFrom into pTo. The copy begins at the current pFrom->cursor position. pFrom->cursor is left pointing at the first character past the last `\n` copied. (Modification of the cursor is why pFrom is not const.) If pTo==NULL then this routine simply skips over N lines. Returns 0 if it copies lines or does nothing (because N is 0 or pFrom's contents have been exhausted). Copying fewer lines than requested (because of EOF) is not an error. Returns non-0 only on allocation error. Results are undefined if pFrom is NULL or not properly initialized. @see fsl_buffer_stream_lines() */ FSL_EXPORT int fsl_buffer_copy_lines(fsl_buffer * const pTo, fsl_buffer * const pFrom, fsl_size_t N); /** Works identically to fsl_buffer_copy_lines() except that it sends its output to the fTo output function. If fTo is NULL then it simply skips over N lines. @see fsl_buffer_copy_lines() */ FSL_EXPORT int fsl_buffer_stream_lines(fsl_output_f fTo, void * const toState, fsl_buffer * const pFrom, fsl_size_t N); |
︙ | ︙ | |||
2529 2530 2531 2532 2533 2534 2535 | This function does no filesystem-level validation of the the given path - only string evaluation. */ FSL_EXPORT int fsl_file_dirpart(char const * zFilename, fsl_int_t nLen, fsl_buffer * const pOut, bool leaveSlash); | < < < < < < < | 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 | This function does no filesystem-level validation of the the given path - only string evaluation. */ FSL_EXPORT int fsl_file_dirpart(char const * zFilename, fsl_int_t nLen, fsl_buffer * const pOut, bool leaveSlash); /** Writes the absolute path name of the current directory to zBuf, which must be at least nBuf bytes long (nBuf includes the space for a trailing NUL terminator). Returns FSL_RC_RANGE if the name would be too long for nBuf, |
︙ | ︙ | |||
2895 2896 2897 2898 2899 2900 2901 | The returned type is (wchar_t*) on Windows and (char*) everywhere else. */ FSL_EXPORT void *fsl_utf8_to_filename(const char *zUtf8); /** | | | | | 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 | The returned type is (wchar_t*) on Windows and (char*) everywhere else. */ FSL_EXPORT void *fsl_utf8_to_filename(const char *zUtf8); /** Deallocate pOld, which must have been allocated by fsl_filename_to_utf8(), fsl_utf8_to_filename(), fsl_getenv(), or another routine which explicitly documents this function as being the proper finalizer for its returned memory. */ FSL_EXPORT void fsl_filename_free(void *pOld); /** Returns a (possible) copy of the environment variable with the given key, or NULL if no entry is found. The returned value must be passed to fsl_filename_free() to free it. ACHTUNG: DO NOT |
︙ | ︙ | |||
3226 3227 3228 3229 3230 3231 3232 | will fail. If realLink is false or this is a Windows platform, a file is created named zLinkFile containing the string zTargetFile as its contents. If a file or symlink named zLinkFile already exists, it is removed before writing the new contents. | | | | < < < < < < < < < < < < < | 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 | will fail. If realLink is false or this is a Windows platform, a file is created named zLinkFile containing the string zTargetFile as its contents. If a file or symlink named zLinkFile already exists, it is removed before writing the new contents. In both cases, the parent directories for zLinkFile are created, if needed but that process will fail if any non-directory components with conflicting names are found in the to-be-mkdir'd path. Returns 0 on success or some lower-level result code if creation/writing of a directory, a symlink, or pseudo-symlink fails. */ FSL_EXPORT int fsl_symlink_create(char const *zTargetFile, char const * zLinkFile, bool realLink); /** Uses fsl_getenv() to look for the environment variables (FOSSIL_USER, (Windows: USERNAME), (Unix: USER, LOGNAME)). If it finds one it returns a copy of that value, which must eventually be passed to fsl_free() to free it (NOT fsl_filename_free(), though fsl_getenv() requires that one). If it finds no match, or if copying the entry fails, it returns |
︙ | ︙ | |||
3311 3312 3313 3314 3315 3316 3317 | requireWriteAccess is ignored on others. - FSL_RC_TYPE if the home (as determined via inspection of the environment) is not a directory. - FSL_RC_OOM if a memory (re)allocation fails. */ | | | 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 | requireWriteAccess is ignored on others. - FSL_RC_TYPE if the home (as determined via inspection of the environment) is not a directory. - FSL_RC_OOM if a memory (re)allocation fails. */ FSL_EXPORT int fsl_find_home_dir( fsl_buffer * tgt, bool requireWriteAccess ); /** Values for use with the fsl_fstat::type field. */ enum fsl_fstat_type_e { /** Sentinel value for unknown/invalid filesystem entry types. */ FSL_FSTAT_TYPE_UNKNOWN = 0, |
︙ | ︙ | |||
3437 3438 3439 3440 3441 3442 3443 | contain embedded NUL characters if either the zSrc or zOut files are binary. On success this function returns 0 and the length of the delta string, in bytes, excluding the final NUL terminator character, is written to *deltaSize. | > | < < | 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 | contain embedded NUL characters if either the zSrc or zOut files are binary. On success this function returns 0 and the length of the delta string, in bytes, excluding the final NUL terminator character, is written to *deltaSize. Returns FSL_RC_MISUSE if any of the pointer arguments are NULL and FSL_RC_OOM if memory allocation fails during generation of the delta. Returns FSL_RC_RANGE if lenSrc or lenOut are "too big" (if they cause an overflow in the math). Output Format: The delta begins with a base64 number followed by a newline. This number is the number of bytes in the TARGET file. Thus, given a delta file z, a program can compute the size of the output file simply by reading the first line and decoding the base-64 number found there. The fsl_delta_applied_size() |
︙ | ︙ | |||
3539 3540 3541 3542 3543 3544 3545 | /** A fsl_delta_create() wrapper which uses the first two arguments as the original and "new" content versions to delta, and outputs the delta to the 3rd argument (overwriting any existing contents and re-using any memory it had allocated). | | < < | < | | | | | 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 | /** A fsl_delta_create() wrapper which uses the first two arguments as the original and "new" content versions to delta, and outputs the delta to the 3rd argument (overwriting any existing contents and re-using any memory it had allocated). If the output buffer (delta) is the same as src or newVers, FSL_RC_MISUSE is returned, and results are undefined if delta indirectly refers to the same buffer as either src or newVers or if any argument is NULL. Returns 0 on success. */ FSL_EXPORT int fsl_buffer_delta_create( fsl_buffer const * const src, fsl_buffer const * const newVers, fsl_buffer * const delta); /** Apply a delta created using fsl_delta_create(). The output buffer must be big enough to hold the whole output file and a NUL terminator at the end. The fsl_delta_applied_size() routine can be used to determine that |
︙ | ︙ | |||
4094 4095 4096 4097 4098 4099 4100 | /** Convenience typedef. */ typedef struct fsl_dline fsl_dline; /** Initialized-with-defaults fsl_dline structure, intended for const-copy initialization. */ | | | 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 | /** Convenience typedef. */ typedef struct fsl_dline fsl_dline; /** Initialized-with-defaults fsl_dline structure, intended for const-copy initialization. */ #define fsl_dline_empty_m {NULL,0U,0U,0U,0U,0U} /** Initialized-with-defaults fsl_dline structure, intended for non-const copy initialization. */ extern const fsl_dline fsl_dline_empty; /** Maximum number of change spans for fsl_dline_change. */ |
︙ | ︙ | |||
4507 4508 4509 4510 4511 4512 4513 | NULL/*opt*/,NULL/*typeID*/, \ NULL/*start()*/,NULL/*chunkHeader()*/,NULL/*skip()*/, NULL/*common()*/, \ NULL/*insertion()*/,NULL/*deletion()*/, NULL/*replacement()*/, \ NULL/*edit()*/, NULL/*finish()*/, NULL/*finally()*/,NULL/*finalize()*/, \ false/*twoPass*/,0U/*passNumber*/, \ NULL/*pimpl*/, 0U/*pimplFlags*/,0U/*implFlags*/,0U/*fileCount*/, \ 0/*lnLHS*/,0/*lnRHS*/, \ | | | 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 | NULL/*opt*/,NULL/*typeID*/, \ NULL/*start()*/,NULL/*chunkHeader()*/,NULL/*skip()*/, NULL/*common()*/, \ NULL/*insertion()*/,NULL/*deletion()*/, NULL/*replacement()*/, \ NULL/*edit()*/, NULL/*finish()*/, NULL/*finally()*/,NULL/*finalize()*/, \ false/*twoPass*/,0U/*passNumber*/, \ NULL/*pimpl*/, 0U/*pimplFlags*/,0U/*implFlags*/,0U/*fileCount*/, \ 0/*lnLHS*/,0/*lnRHS*/, \ {/*metric*/0,0,0} \ } /** Initialized-with-defaults fsl_dibu structure, intended for non-const copy initialization. */ extern const fsl_dibu fsl_dibu_empty; /** |
︙ | ︙ | |||
4960 4961 4962 4963 4964 4965 4966 | /** Frees all memory associated with pf, but does not free pf. Is a no-op if pf is NULL. */ FSL_EXPORT void fsl_pathfinder_clear(fsl_pathfinder * const pf); /** | | > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 | /** Frees all memory associated with pf, but does not free pf. Is a no-op if pf is NULL. */ FSL_EXPORT void fsl_pathfinder_clear(fsl_pathfinder * const pf); /** Adds the given directory to pf's search path. Returns 0 on success, FSL_RC_MISUSE if !pf or !dir (dir _may_ be an empty string), FSL_RC_OOM if copying the string or adding it to the list fails. @see fsl_pathfinder_ext_add() @see fsl_pathfinder_search() */ FSL_EXPORT int fsl_pathfinder_dir_add(fsl_pathfinder * const pf, char const * const dir); /** Adds the given directory to pf's search extensions. Returns 0 on success, FSL_RC_MISUSE if !pf or !dir (dir _may_ be an empty string), FSL_RC_OOM if copying the string or adding it to the list fails. Note that the client is responsible for adding a "." to the extension, if needed, as this API does not apply any special meaning to any characters in a search extension. e.g. "-journal" and "~" are both perfectly valid extensions for this purpose. @see fsl_pathfinder_dir_add() @see fsl_pathfinder_search() */ FSL_EXPORT int fsl_pathfinder_ext_add(fsl_pathfinder * const pf, char const * const ext); /** Searches for a file whose name can be constructed by some combination of pf's directory/suffix list and the given base name. It searches for files in the following manner: |
︙ | ︙ | |||
5049 5050 5051 5052 5053 5054 5055 | extensions, so "~" and "-foo" are legal extensions). Check for a match. On success (a readable filesystem entry is found): - It returns 0. | | | | | | | | < | | | | | | | | | > | 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 | extensions, so "~" and "-foo" are legal extensions). Check for a match. On success (a readable filesystem entry is found): - It returns 0. - If pOut is not NULL then *pOut is set to the path it found. The bytes of the returned string are only valid until the next search operation on pf, so copy them if you need them. Note that the returned path is _not_ normalized via fsl_file_canonical_name() or similar, and it may very well return a relative path (if base or one of pf->dirs contains a relative path part). - If outLen is not NULL, *outLen will be set to the length of the returned string. On error: - Returns FSL_RC_MISUSE if !pf, !base, !*base. - Returns FSL_RC_OOM on allocation error (it uses a buffer to hold its path combinations and return value). - Returns FSL_RC_NOT_FOUND if it finds no entry. The host platform's customary path separator is used to separate directory/file parts ('\\' on Windows and '/' everywhere else). Note that it _is_ legal for pOut and outLen to both be NULL, in which case a return of 0 signals that an entry was found, but the client has no way of knowing what path it might be (unless, of course, he relies on internal details of the fsl_pathfinder API, which he most certainly should not do). Tip: if the client wants to be certain that this function will not allocate memory, simply use fsl_buffer_reserve() on pf->buf to reserve the desired amount of space in advance. As long as the search paths never extend that length, this function will not need to allocate. (Until/unless the following TODO is implemented...) Potential TODO: use fsl_file_canonical_name() so that the search dirs themselves do not need to be entered using platform-specific separators. The main reason it's not done now is that it requires another allocation. The secondary reason is because it's sometimes useful to use relative paths in this context (based on usage in previous trees from which this code |
︙ | ︙ | |||
5346 5347 5348 5349 5350 5351 5352 | (in the case of a heap-allocated instance) freed. */ FSL_EXPORT int fsl_zip_end_to_filename( fsl_zip_writer * const z, char const * filename ); /** | | | | 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 | (in the case of a heap-allocated instance) freed. */ FSL_EXPORT int fsl_zip_end_to_filename( fsl_zip_writer * const z, char const * filename ); /** Returns a pointer to z's ZIP content buffer. The contents are ONLY valid after fsl_zip_end() returns 0. @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @see fsl_zip_file_add() @see fsl_zip_end() @see fsl_zip_end_take() @see fsl_zip_finalize() |
︙ | ︙ | |||
5742 5743 5744 5745 5746 5747 5748 | unsigned int * const conflictCount); /** Appends the first n bytes of string z to buffer b in the form of TCL-format string literal. If n<0 then fsl_strlen() is used to determine the length. Returns 0 on success, FSL_RC_OOM on error. | < < < | 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 | unsigned int * const conflictCount); /** Appends the first n bytes of string z to buffer b in the form of TCL-format string literal. If n<0 then fsl_strlen() is used to determine the length. Returns 0 on success, FSL_RC_OOM on error. If the 2nd argument is true, squiggly braces within the string are escaped, else they are not. Whether that's required or not depends on how the resulting TCL will be used. If it will be eval'd directly, it must be escaped. If it will be read as a file and tokenized, it needn't be. */ FSL_EXPORT int fsl_buffer_append_tcl_literal(fsl_buffer * const b, |
︙ | ︙ | |||
6132 6133 6134 6135 6136 6137 6138 | /** A convenience from of fsl_strip_trailing_slashes() which strips trailing slashes from the given buffer and changes its b->used value to account for any stripping. Results are undefined if b is not properly initialized. */ | | < < < | < | 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 | /** A convenience from of fsl_strip_trailing_slashes() which strips trailing slashes from the given buffer and changes its b->used value to account for any stripping. Results are undefined if b is not properly initialized. */ FSL_EXPORT void fsl_buffer_strip_slashes(fsl_buffer * b); /** Appends each ID from the given bag to the given buffer using the given separator string. Returns FSL_RC_OOM on allocation error. */ FSL_EXPORT int fsl_id_bag_to_buffer(fsl_id_bag const * bag, fsl_buffer * b, char const * separator); /** Flags for use with the fsl_looks family of functions. */ enum fsl_lookslike_e { /* Nothing special was found. */ |
︙ | ︙ | |||
6255 6256 6257 6258 6259 6260 6261 | Populates the first n bytes of tgt with random bytes. Note that n must be 31 bits or less (2GB). The exact source of randomness is not guaranteed by the API, but the implementation currently uses sqlite3_randomness(). */ FSL_EXPORT void fsl_randomness(unsigned int n, void *tgt); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 | Populates the first n bytes of tgt with random bytes. Note that n must be 31 bits or less (2GB). The exact source of randomness is not guaranteed by the API, but the implementation currently uses sqlite3_randomness(). */ FSL_EXPORT void fsl_randomness(unsigned int n, void *tgt); #if 0 /** The UTF16 counterpart of fsl_looks_like_utf8(), with the addition that the 2nd argument, if true, specifies that the 2nd argument is true then the contents of the buffer are byte-swapped for checking purposes. This is not validate that the blob is valid UTF16. It assumes that all |
︙ | ︙ | |||
6443 6444 6445 6446 6447 6448 6449 | /** The fsl namespace is reserved for an eventual C++ wrapper for the API. */ namespace fsl {} extern "C" { #endif | < < < < < < < < < | 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 | /** The fsl namespace is reserved for an eventual C++ wrapper for the API. */ namespace fsl {} extern "C" { #endif /** @struct fsl_cx The main Fossil "context" type. This is the first argument to many Fossil library API routines, and holds all state related to a checkout and/or repository and/or global fossil configuration database(s). |
︙ | ︙ | |||
6693 6694 6695 6696 6697 6698 6699 | a mix of hash types. Maintenance ACHTUNG: this enum's values must align with those from fossil(1) because their integer values are used in the `repo.config` table. */ enum fsl_hashpolicy_e { | | | | | | | < | | 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 | a mix of hash types. Maintenance ACHTUNG: this enum's values must align with those from fossil(1) because their integer values are used in the `repo.config` table. */ enum fsl_hashpolicy_e { /* Use only SHA1 hashes. */ FSL_HPOLICY_SHA1 = 0, /* Accept SHA1 hashes but auto-promote to SHA3. */ FSL_HPOLICY_AUTO = 1, /* Use SHA3 hashes. */ FSL_HPOLICY_SHA3 = 2, /* Use SHA3 hashes exclusively. */ FSL_HPOLICY_SHA3_ONLY = 3, /* With this policy, fsl_uuid_is_shunned() will always return true for SHA1 hashes. */ FSL_HPOLICY_SHUN_SHA1 = 4 }; typedef enum fsl_hashpolicy_e fsl_hashpolicy_e; /** Most functions in this API which return an int type return error codes from the fsl_rc_e enum. None of these entries are (currently) guaranteed to have a specific value across library versions except for FSL_RC_OK, which is guaranteed to always be 0 (and the API guarantees that no other code shall have a value of zero). The only reasons numbers are hard-coded to the values (or some of them) is to simplify debugging during development. Clients may use |
︙ | ︙ | |||
6742 6743 6744 6745 6746 6747 6748 | */ FSL_RC_NYI = 101, /** Out of memory. Indicates that a resource allocation request failed. */ FSL_RC_OOM = 102, | | | 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | */ FSL_RC_NYI = 101, /** Out of memory. Indicates that a resource allocation request failed. */ FSL_RC_OOM = 102, /* API misuse (invalid args) */ FSL_RC_MISUSE = 103, /** Some range was violated (function argument, UTF character, etc.). */ FSL_RC_RANGE = 104, |
︙ | ︙ | |||
6851 6852 6853 6854 6855 6856 6857 | FSL_RC_REPO_MISMATCH = 120, /** Indicates that a checksum comparison failed, possibly indicating that corrupted or unexpected data was just read. */ FSL_RC_CHECKSUM_MISMATCH = 121, | < < < < < < | 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 | FSL_RC_REPO_MISMATCH = 120, /** Indicates that a checksum comparison failed, possibly indicating that corrupted or unexpected data was just read. */ FSL_RC_CHECKSUM_MISMATCH = 121, /** Indicates that a merge conflict, or some other context-dependent type of conflict, was detected. */ FSL_RC_CONFLICT, /** |
︙ | ︙ | |||
6971 6972 6973 6974 6975 6976 6977 | /** Intended to be used with fsl_cx_interrupt() by signal handlers and UI threads. */ FSL_RC_INTERRUPTED, | < < < < < < < < < < < < < < | 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 | /** Intended to be used with fsl_cx_interrupt() by signal handlers and UI threads. */ FSL_RC_INTERRUPTED, /** Must be the final entry in the enum. Used for creating client-side result codes which are guaranteed to live outside of this one's range. */ FSL_RC_end }; |
︙ | ︙ | |||
7021 7022 7023 7024 7025 7026 7027 | typedef enum fsl_fileperm_e fsl_fileperm_e; /** Returns a "standard" string form for a fsl_rc_e code. The string is primarily intended for debugging purposes. The returned bytes are guaranteed to be static and NUL-terminated. They are not guaranteed to contain anything useful for any purposes other than | | < | | 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 | typedef enum fsl_fileperm_e fsl_fileperm_e; /** Returns a "standard" string form for a fsl_rc_e code. The string is primarily intended for debugging purposes. The returned bytes are guaranteed to be static and NUL-terminated. They are not guaranteed to contain anything useful for any purposes other than debugging and tracking down problems. */ FSL_EXPORT char const * fsl_rc_cstr(int); /** Returns the value of FSL_LIBRARY_VERSION used to compile the library. If this value differs from the value the caller was compiled with, Chaos might ensue. The API does not yet have any mechanism for determining |
︙ | ︙ | |||
7082 7083 7084 7085 7086 7087 7088 | A part of the configuration used by fsl_cx_init() and friends. */ struct fsl_cx_config { /** If true, all SQL which goes through the fossil engine will be traced to stdout. | | | 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 | A part of the configuration used by fsl_cx_init() and friends. */ struct fsl_cx_config { /** If true, all SQL which goes through the fossil engine will be traced to stdout. TODO: replace this with a FILE pointer. */ bool traceSql; /** If true, the fsl_print() SQL function will output its output to the fsl_output()-configured channel, else it is a no-op. */ bool sqlPrint; |
︙ | ︙ | |||
7206 7207 7208 7209 7210 7211 7212 | @see fsl_cx_reset() */ FSL_EXPORT int fsl_cx_init( fsl_cx ** tgt, fsl_cx_init_opt const * param ); /** Frees all memory associated with f, which must have been allocated/initialized using fsl_cx_malloc(), fsl_cx_init(), or | | | < < | 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 | @see fsl_cx_reset() */ FSL_EXPORT int fsl_cx_init( fsl_cx ** tgt, fsl_cx_init_opt const * param ); /** Frees all memory associated with f, which must have been allocated/initialized using fsl_cx_malloc(), fsl_cx_init(), or equivalent, or created on the stack and properly initialized (via fsl_cx_init() or copy-constructed from fsl_cx_empty). This function triggers any finializers set for f's client state or output channel. This is a no-op if !f and is effectively a no-op if f has no state to destruct. */ |
︙ | ︙ | |||
8287 8288 8289 8290 8291 8292 8293 | f. Returns 0 on success or if no dbs are opened (noting that this does NOT close the separate global configuration db: see fsl_config_close()). Returns FSL_RC_MISUSE if the opened SCM db(s) have an opened transaction, but that behaviour may change in the future to force a rollback and close the database(s). */ FSL_EXPORT int fsl_close_scm_dbs(fsl_cx * const f); | < < < < < < < < < < < < < < < < | 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 | f. Returns 0 on success or if no dbs are opened (noting that this does NOT close the separate global configuration db: see fsl_config_close()). Returns FSL_RC_MISUSE if the opened SCM db(s) have an opened transaction, but that behaviour may change in the future to force a rollback and close the database(s). */ FSL_EXPORT int fsl_close_scm_dbs(fsl_cx * const f); #if 0 /** DO NOT USE - not yet tested and ready. Returns the result of either localtime(clock) or gmtime(clock), depending on f: |
︙ | ︙ | |||
9767 9768 9769 9770 9771 9772 9773 | or if no result is found, NULL is returned. */ FSL_EXPORT void * fsl_db_g_blob( fsl_db * const db, fsl_size_t * len, char const * sql, ... ); /** Similar to fsl_db_get_text() and fsl_db_get_blob(), but writes | | > < < < | 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 | or if no result is found, NULL is returned. */ FSL_EXPORT void * fsl_db_g_blob( fsl_db * const db, fsl_size_t * len, char const * sql, ... ); /** Similar to fsl_db_get_text() and fsl_db_get_blob(), but writes its result to tgt, overwriting (not appending to) any existing memory it might hold. If asBlob is true then the underlying BLOB API is used to populate the buffer, else the underlying STRING/TEXT API is used. For many purposes there will be no difference, but if you know you might have binary data, be sure to pass a true value for asBlob to avoid any potential encoding-related problems. */ FSL_EXPORT int fsl_db_get_buffer( fsl_db * const db, fsl_buffer * const tgt, bool asBlob, char const * sql, ... ); /** va_list counterpart of fsl_db_get_buffer(). |
︙ | ︙ | |||
13831 13832 13833 13834 13835 13836 13837 | Resolves client-provided symbol as an artifact's db record ID. f must have an opened repository db, and some symbols can only be looked up if it has an opened checkout (see the list below). Returns 0 and sets *rv to the id if it finds an unambiguous match. | | | > | | < < < < < | | | > > | | < | | | | 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 | Resolves client-provided symbol as an artifact's db record ID. f must have an opened repository db, and some symbols can only be looked up if it has an opened checkout (see the list below). Returns 0 and sets *rv to the id if it finds an unambiguous match. Returns FSL_RC_MISUSE if !f, !sym, !*sym, or !rv. Returns FSL_RC_NOT_A_REPO if f has no opened repository. Returns FSL_RC_AMBIGUOUS if sym is a partial UUID which matches multiple full UUIDs. Returns FSL_RC_NOT_FOUND if it cannot find anything. Symbols supported by this function: - SHA1/3 hash - SHA1/3 hash prefix of at least 4 characters - Symbolic Name - "tag:" + symbolic name - Date or date-time - "date:" + Date or date-time - symbolic-name ":" date-time - "tip" - "rid:###" resolves to the hash of blob.rid ### if that RID is in the database The following additional forms are available in local checkouts: - "current" - "prev" or "previous" - "next" The following prefix may be applied to the above to modify how they are resolved: - "root:" prefix resolves to the checkin of the parent branch from which the record's branch divered. i.e. the version from which it was branched. In the trunk this will always resolve to the first checkin. - "merge-in:" TODO - document this once its implications are understood. If type is not FSL_SATYPE_ANY then it will only match artifacts of the specified type. In order to resolve arbitrary UUIDs, e.g. those of arbitrary blob content, type needs to be FSL_SATYPE_ANY. */ FSL_EXPORT int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ); /** Similar to fsl_sym_to_rid() but on success it returns a UUID string by assigning it to *rv (if rv is not NULL). If rid is not NULL then on success the db record ID corresponding to the returned UUID is assigned to *rid. The caller must eventually free the returned string memory by passing it to fsl_free(). Returns 0 if it finds a match and any number of result codes on error. */ FSL_EXPORT int fsl_sym_to_uuid( fsl_cx * f, char const * sym, fsl_satype_e type, fsl_uuid_str * rv, fsl_id_t * rid ); /** Searches f's repo database for the a blob with the given uuid (any unique UUID prefix). On success a positive record ID is returned. On error one of several unspecified negative values is returned. If no uuid match is found 0 is returned. |
︙ | ︙ | |||
14560 14561 14562 14563 14564 14565 14566 | fsl_satype_e artifactType; }; /** Convenience typedef. */ typedef struct fsl_rebuild_step fsl_rebuild_step; /** Initialized-with-defaults fsl_rebuild_step structure, intended for const-copy initialization. */ | | | 14184 14185 14186 14187 14188 14189 14190 14191 14192 14193 14194 14195 14196 14197 14198 | fsl_satype_e artifactType; }; /** Convenience typedef. */ typedef struct fsl_rebuild_step fsl_rebuild_step; /** Initialized-with-defaults fsl_rebuild_step structure, intended for const-copy initialization. */ #define fsl_rebuild_step_empty_m {NULL,NULL,0,0,-1,false} /** Initialized-with-defaults fsl_rebuild_step structure, intended for non-const copy initialization. */ FSL_EXPORT const fsl_rebuild_step fsl_rebuild_step_empty; /** Callback for use with fsl_repo_rebuild() in order to report |
︙ | ︙ | |||
14727 14728 14729 14730 14731 14732 14733 | }; /** Convenience typedef. */ typedef struct fsl_cidiff_opt fsl_cidiff_opt; /** Initialized-with-defaults fsl_cidiff_opt structure, intended for const-copy initialization. */ | | | 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 | }; /** Convenience typedef. */ typedef struct fsl_cidiff_opt fsl_cidiff_opt; /** Initialized-with-defaults fsl_cidiff_opt structure, intended for const-copy initialization. */ #define fsl_cidiff_opt_empty_m {0,0} /** Initialized-with-defaults fsl_cidiff_opt structure, intended for non-const copy initialization. */ FSL_EXPORT const fsl_cidiff_opt fsl_cidiff_opt_empty; /** Descriptors for the type of information being reported for each |
︙ | ︙ | |||
17381 17382 17383 17384 17385 17386 17387 | fsl_is_simple_pathname()), and any number of codes for db-related errors. This function matches only vfile.pathname, not vfile.origname, because it is possible for a given name to be in both fields (in different records) at the same time. */ | | | | 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 | fsl_is_simple_pathname()), and any number of codes for db-related errors. This function matches only vfile.pathname, not vfile.origname, because it is possible for a given name to be in both fields (in different records) at the same time. */ FSL_EXPORT int fsl_filename_to_vfile_id( fsl_cx * f, fsl_id_t vid, char const * zName, fsl_id_t * vfid ); /** Searches the `vfile` table where `vfile.vid=vid` for a name which matches `zName` or all `vfile` entries found under a subdirectory named `zName` (with no trailing slash). `zName` must be relative to the checkout root. As a special case, if `zName` is `NULL`, empty, or `"."` then all files in `vfile` with the given `vid` are |
︙ | ︙ | |||
18017 18018 18019 18020 18021 18022 18023 | databases. The array is terminated by a NULL element. As of this writing, and for the foreseeable future, the list is comprised of only 3 elements, {".fslckout", "_FOSSIL_", NULL}, but the order of the non-NULL elements is unspecified by the interface. */ FSL_EXPORT char const ** fsl_ckout_dbnames(void); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 | databases. The array is terminated by a NULL element. As of this writing, and for the foreseeable future, the list is comprised of only 3 elements, {".fslckout", "_FOSSIL_", NULL}, but the order of the non-NULL elements is unspecified by the interface. */ FSL_EXPORT char const ** fsl_ckout_dbnames(void); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_CHECKOUT_H_INCLUDED */ /* end of file ./include/fossil-scm/checkout.h */ |
︙ | ︙ | |||
18619 18620 18621 18622 18623 18624 18625 | /** Fetches the preferred name of the "global" db file for the current user by assigning it to *zOut. Returns 0 on success, in which case *zOut is updated and non-0 on error, in which case *zOut is not modified. On success, ownership of *zOut is transferred to the caller, who must eventually free it using fsl_free(). | < < < < < < < | | < < < < < | < < < | < < > | 18026 18027 18028 18029 18030 18031 18032 18033 18034 18035 18036 18037 18038 18039 18040 18041 18042 18043 18044 18045 18046 18047 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 | /** Fetches the preferred name of the "global" db file for the current user by assigning it to *zOut. Returns 0 on success, in which case *zOut is updated and non-0 on error, in which case *zOut is not modified. On success, ownership of *zOut is transferred to the caller, who must eventually free it using fsl_free(). The locations searched for the database file are platform-dependent... Unix-like systems are searched in the following order: 1) If the FOSSIL_HOME environment var is set, use $FOSSIL_HOME/.fossil. 2) If $HOME/.fossil already exists, use that. 3) If XDG_CONFIG_HOME environment var is set, use $XDG_CONFIG_HOME/fossil.db. 4) If $HOME/.config is a directory, use $HOME/.config/fossil.db 5) Fall back to $HOME/.fossil (historical name). Except where listed above, this function does not check whether the file already exists or is a database. Windows: - We need a Windows port of this routine. Currently it `#error`'s out at compile-time on Windows. */ FSL_EXPORT int fsl_config_global_preferred_name(char ** zOut); /** A variant of fsl_config_get_int32() which can search through multiple config sources in an order specified by the caller. |
︙ | ︙ | |||
19007 19008 19009 19010 19011 19012 19013 | */ #if defined(__cplusplus) extern "C" { #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < | 18398 18399 18400 18401 18402 18403 18404 18405 18406 18407 18408 18409 18410 18411 | */ #if defined(__cplusplus) extern "C" { #endif typedef struct fsl__bccache fsl__bccache; typedef struct fsl__bccache_line fsl__bccache_line; typedef struct fsl__pq fsl__pq; typedef struct fsl__pq_entry fsl__pq_entry; /** @internal |
︙ | ︙ | |||
21356 21357 21358 21359 21360 21361 21362 21363 21364 21365 21366 21367 21368 21369 | approximate FSL_RC_xxx match but treats SQLITE_ROW and SQLITE_DONE as non-errors (result code 0). If non-0 is returned db's error state is updated with the current sqlite3_errmsg() string. */ int fsl__db_errcode(fsl_db * const db, int sqliteCode); /** @internal Plug in fsl_cx-specific db functionality into the given db handle. This must only be passed the MAIN db handle for the context, immediately after opening that handle, before f->dbMain is assigned. This function has very limited applicability and various | > > > > > > > > > > > | 20721 20722 20723 20724 20725 20726 20727 20728 20729 20730 20731 20732 20733 20734 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 | approximate FSL_RC_xxx match but treats SQLITE_ROW and SQLITE_DONE as non-errors (result code 0). If non-0 is returned db's error state is updated with the current sqlite3_errmsg() string. */ int fsl__db_errcode(fsl_db * const db, int sqliteCode); /** @internal Clears various internal caches and resets various internally-cached values related to a repository db, but the data cleared here are not associated directly with a db handle. This is intended primarily to be used when a db transaction is rolled back which might have introduced state into those caches which would be stale after a rollback. */ void fsl__cx_clear_repo_caches(fsl_cx * const f); /** @internal Plug in fsl_cx-specific db functionality into the given db handle. This must only be passed the MAIN db handle for the context, immediately after opening that handle, before f->dbMain is assigned. This function has very limited applicability and various |
︙ | ︙ | |||
21405 21406 21407 21408 21409 21410 21411 | /** @internal Frees/clears the non-db state of f->ckout. */ void fsl__cx_ckout_clear(fsl_cx * const f); | < < < < < < < < < < < < | 20781 20782 20783 20784 20785 20786 20787 20788 20789 20790 20791 20792 20793 20794 20795 20796 20797 20798 20799 20800 20801 20802 20803 20804 20805 20806 20807 | /** @internal Frees/clears the non-db state of f->ckout. */ void fsl__cx_ckout_clear(fsl_cx * const f); /** @internal Maximum length of a line in a text file, in bytes. (2**15 = 32k) */ #define FSL__LINE_LENGTH_MASK_SZ 15 /** @internal Bitmask which, when AND-ed with a number, will result in the bottom FSL__LINE_LENGTH_MASK_SZ bits of that number. */ #define FSL__LINE_LENGTH_MASK ((1<<FSL__LINE_LENGTH_MASK_SZ)-1) #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */ /* end of file ./include/fossil-scm/internal.h */ /* start of file ./include/fossil-scm/auth.h */ |
︙ | ︙ | |||
22430 22431 22432 22433 22434 22435 22436 22437 22438 22439 22440 22441 22442 22443 22444 22445 22446 22447 | fcli use. */ #define FCLI_V(pfexp) FCLI_VN(1,pfexp) #if defined(__cplusplus) extern "C" { #endif /** Result codes specific to the fcli API. */ enum fcli_rc_e { /** For use with fcli_flag_callback_f() implementations to indicate that the flag processor should check for that flag again. */ FCLI_RC_FLAG_AGAIN = FSL_RC_end + 1, /** | > < < < < < | 21794 21795 21796 21797 21798 21799 21800 21801 21802 21803 21804 21805 21806 21807 21808 21809 21810 21811 21812 21813 21814 21815 21816 21817 21818 21819 | fcli use. */ #define FCLI_V(pfexp) FCLI_VN(1,pfexp) #if defined(__cplusplus) extern "C" { #endif /** Result codes specific to the fcli API. */ enum fcli_rc_e { /** For use with fcli_flag_callback_f() implementations to indicate that the flag processor should check for that flag again. */ FCLI_RC_FLAG_AGAIN = FSL_RC_end + 1, /** Returned from fcli_setup() if flag processing invokes the help system. This is an indication that the app should exit immediately with a 0 result code. */ FCLI_RC_HELP }; |
︙ | ︙ | |||
22846 22847 22848 22849 22850 22851 22852 | default implementation uses fsl_outputer_FILE to output to stdout. */ fsl_outputer outputer; } config; /** | < < < < < < < | 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 22216 22217 22218 22219 | default implementation uses fsl_outputer_FILE to output to stdout. */ fsl_outputer outputer; } config; /** For holding pre-this->f-init error state. Once this->f is initialized, all errors reported via fcli_err_set() are stored in that object's error state. */ fsl_error err; }; typedef struct fcli_t fcli_t; |
︙ | ︙ | |||
23109 23110 23111 23112 23113 23114 23115 | /** Convenience macro for using fcli_err_report2(). */ #define fcli_err_report(CLEAR) fcli_err_report2((CLEAR), __FILE__, __LINE__) /** | | | < < | 22462 22463 22464 22465 22466 22467 22468 22469 22470 22471 22472 22473 22474 22475 22476 22477 22478 | /** Convenience macro for using fcli_err_report2(). */ #define fcli_err_report(CLEAR) fcli_err_report2((CLEAR), __FILE__, __LINE__) /** Peeks at or takes the next argument from the CLI args. If the argument is true, it is removed from the args list. It is owned by fcli and will be freed when the app exits. */ FSL_EXPORT const char * fcli_next_arg(bool remove); /** If fcli.argv contains what looks like any flag arguments, this updates the fossil error state and returns FSL_RC_MISUSE, else returns 0. If outputError is true and an unused flag is found |
︙ | ︙ | |||
23176 23177 23178 23179 23180 23181 23182 | }; /** Expects an array of fcli_commands which contain a trailing sentry entry with a NULL name and callback. It searches the list for a command matching fcli_next_arg(). If found, it removes that argument from the list, calls the callback, and | | < < < < | | | | 22527 22528 22529 22530 22531 22532 22533 22534 22535 22536 22537 22538 22539 22540 22541 22542 22543 22544 22545 22546 22547 22548 22549 22550 22551 22552 22553 22554 22555 22556 22557 22558 22559 22560 22561 22562 22563 22564 | }; /** Expects an array of fcli_commands which contain a trailing sentry entry with a NULL name and callback. It searches the list for a command matching fcli_next_arg(). If found, it removes that argument from the list, calls the callback, and returns its result. If no command is found FSL_RC_NOT_FOUND is returned, the argument list is not modified, and the error state is updated with a description of the problem and a list of all command names in cmdList. If reportErrors is true then on error this function outputs the error result but it keeps the error state in place for the downstream use. As a special case: when a command matches the first argument and that object has a non-NULL flags member, this function checks the _next_ argument, and if it is "help" then this function passes that flags member to fcli_command_help() to output help, then returns 0. */ FSL_EXPORT int fcli_dispatch_commands( fcli_command const * cmdList, bool reportErrors); /** Parse cmd->aliases for an alias (i.e., NUL-terminated string semantically equivalent to cmd->name) matching arg. Return true if found. Note that cmd->aliases _must_ be double-NUL terminated. */ FSL_EXPORT bool fcli_cmd_aliascmp( fcli_command const * cmd, char const * arg ); /** A minor helper function intended to be passed the pending result code of the main() routine. This function outputs any pending error state in fcli. Returns one of EXIT_SUCCESS if mainRc is 0 and fcli had no pending error report, otherwise it returns EXIT_FAILURE. This function does not clean up fcli - that is |
︙ | ︙ | |||
23432 23433 23434 23435 23436 23437 23438 | /** Returns the "tail" part of the argv[0] string which was passed to fcli_setup(), or NULL if neither of those have yet been called. The "tail" part is the part immediately after the final '/' or '\\' character. */ | | < < < < < < < < < < < < < < < < < < < | 22779 22780 22781 22782 22783 22784 22785 22786 22787 22788 22789 22790 22791 22792 22793 | /** Returns the "tail" part of the argv[0] string which was passed to fcli_setup(), or NULL if neither of those have yet been called. The "tail" part is the part immediately after the final '/' or '\\' character. */ FSL_EXPORT char const * fcli_progname(); /** Color theme IDs for use with fcli_diff_colors. */ enum fcli_diff_colors_e{ /** Tells fcli_diff_colors() to NULL out the ANSI color state of its |
︙ | ︙ | |||
23477 23478 23479 23480 23481 23482 23483 | /** Populates the given fsl_dibu_opt::ansiColors state with values dependend on the second argument. */ FSL_EXPORT void fcli_diff_colors(fsl_dibu_opt * const tgt, fcli_diff_colors_e theme); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > | 22805 22806 22807 22808 22809 22810 22811 22812 22813 22814 22815 22816 22817 22818 22819 22820 22821 22822 22823 22824 22825 22826 22827 22828 22829 22830 22831 22832 22833 22834 22835 22836 22837 22838 22839 22840 22841 22842 22843 22844 | /** Populates the given fsl_dibu_opt::ansiColors state with values dependend on the second argument. */ FSL_EXPORT void fcli_diff_colors(fsl_dibu_opt * const tgt, fcli_diff_colors_e theme); /** @internal This function is intented for use in development of libfossil. It dumps the current state of cached SQL statements to fcli_printf(). Normally its output detail level is determined by fcli_is_verbose(), but if forceVerbose is true then it cranks the detail all the way up. */ FSL_EXPORT void fcli_dump_stmt_cache(bool forceVerbose); /** @internal This pseudo-internal function causes fcli to dump any library-level caching metrics it knows about. This function exists primarily to keep fcli client code from having to access the related internal members directly. This is a no-op if fcli has not been initialized. */ FSL_EXPORT void fcli_dump_cache_metrics(void); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* _ORG_FOSSIL_SCM_FCLI_H_INCLUDED_ */ /* end of file ./include/fossil-scm/cli.h */ /* start of file ./include/fossil-scm/deprecated.h */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ |
︙ | ︙ |
Added signify/fnc-11-release.pub.
> > | 1 2 | untrusted comment: fnc 0.11 public key RWQIBA4rVoFFDlh8+FWU6Oeg79hcWqdRXv1cTOzrwTeSxKiIUhMd5rRE |
Deleted signify/fnc-13-release.pub.
|
| < < |
Changes to src/diff.c.
︙ | ︙ | |||
55 56 57 58 59 60 61 | */ #define SBS_LLINE 0 /* Left line number */ #define SBS_LTEXT 1 /* Left text */ #define SBS_MID 2 /* Middle separator column */ #define SBS_RLINE 3 /* Right line number */ #define SBS_RTEXT 4 /* Right text */ | < < < < < < < | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | */ #define SBS_LLINE 0 /* Left line number */ #define SBS_LTEXT 1 /* Left text */ #define SBS_MID 2 /* Middle separator column */ #define SBS_RLINE 3 /* Right line number */ #define SBS_RTEXT 4 /* Right text */ /* * ANSI escape codes: https://en.wikipedia.org/wiki/ANSI_escape_code */ #define ANSI_COLOR_BLACK(BOLD) ((BOLD) ? "\x1b[30m" : "\x1b[30m") #define ANSI_COLOR_RED(BOLD) ((BOLD) ? "\x1b[31;1m" : "\x1b[31m") #define ANSI_COLOR_GREEN(BOLD) ((BOLD) ? "\x1b[32;1m" : "\x1b[32m") #define ANSI_COLOR_YELLOW(BOLD) ((BOLD) ? "\x1b[33;1m" : "\x1b[33m") |
︙ | ︙ | |||
89 90 91 92 93 94 95 | #define ANSI_BG_CYAN(BOLD) ((BOLD) ? "\x1b[46;1m" : "\x1b[46m") #define ANSI_BG_WHITE(BOLD) ((BOLD) ? "\x1b[47;1m" : "\x1b[47m") #define ANSI_RESET_COLOR "\x1b[39;49m" #define ANSI_RESET_ALL "\x1b[0m" #define ANSI_RESET ANSI_RESET_ALL /* #define ANSI_BOLD ";1m" */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | | > | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #define ANSI_BG_CYAN(BOLD) ((BOLD) ? "\x1b[46;1m" : "\x1b[46m") #define ANSI_BG_WHITE(BOLD) ((BOLD) ? "\x1b[47;1m" : "\x1b[47m") #define ANSI_RESET_COLOR "\x1b[39;49m" #define ANSI_RESET_ALL "\x1b[0m" #define ANSI_RESET ANSI_RESET_ALL /* #define ANSI_BOLD ";1m" */ int fnc_diff_text_raw(fsl_buffer const *blob1, fsl_buffer const *blob2, int flags, int **out) { return fnc_diff_blobs(blob1, blob2, NULL, NULL, NULL, NULL, 0, 0, flags, out); } int fnc_diff_text_to_buffer(fsl_buffer const *blob1, fsl_buffer const *blob2, fsl_buffer *out, enum line_type **lines, uint32_t *nlines, short context, short sbswidth, int flags) { return (blob1 && blob2 && out) ? fnc_diff_blobs(blob1, blob2, fsl_output_f_buffer, out, lines, nlines, context, sbswidth, flags, NULL) : FSL_RC_MISUSE; } int fnc_diff_text(fsl_buffer const *blob1, fsl_buffer const *blob2, fsl_output_f out, void *state, short context, short sbswidth, int flags) { return fnc_diff_blobs(blob1, blob2, out, state, NULL, NULL, context, sbswidth, flags, NULL); } /* * * Diff two arbitrary blobs and either stream output to the third argument * or return an array of copy/delete/insert triples via the final argument. * The third XOR final argument must be set. * * If the third argument is not NULL: * state opaque state value passed to the third when emitting output * context number of context lines (negative values fallback to default) * sbswidth sbs diff width (0 = unidiff; negative values fallback to default) * * If the final argument is not NULL, it is assigned a pointer to the result * array of copy/delete/insert triples. Ownership is transfered to the caller, * who must eventually dispose of it with fsl_free(). * * Return 0 on success, any number of other codes on error. */ int fnc_diff_blobs(fsl_buffer const *blob1, fsl_buffer const *blob2, fsl_output_f out, void *state, enum line_type **lines, uint32_t *nlines, /* void *regex, */ uint16_t context, short sbswidth, int flags, int **rawdata) { fsl__diff_cx c = fsl__diff_cx_empty; int rc; |
︙ | ︙ | |||
272 273 274 275 276 277 278 | } } /* fsl__dump_triples(&c, __FILE__, __LINE__); */ /* DEBUG */ if (!FLAG_CHK(flags, FNC_DIFF_NOOPT)) fsl__diff_optimize(&c); /* fsl__dump_triples(&c, __FILE__, __LINE__); */ /* DEBUG */ | > | > > > | | | > > > | < | | < < < < < < | | < < | < | | | | | < < | | | | | | | | | | | | | | | < < < | < | < | | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 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 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | } } /* fsl__dump_triples(&c, __FILE__, __LINE__); */ /* DEBUG */ if (!FLAG_CHK(flags, FNC_DIFF_NOOPT)) fsl__diff_optimize(&c); /* fsl__dump_triples(&c, __FILE__, __LINE__); */ /* DEBUG */ /* Encode longest line length in flags to set max sbs diff width */ if ((!sbswidth && FLAG_CHK(flags, FNC_DIFF_SIDEBYSIDE)) || sbswidth < 0) { flags |= ((int)(sbswidth & 0xFFFF)) << 8; FLAG_SET(flags, FNC_DIFF_SIDEBYSIDE); rc = max_sbs_width(&c, &flags, context); if (rc) goto end; } if (out) { /* * Compute a context or side-by-side diff. * XXX Missing regex support. */ struct diff_out_state dos = diff_out_state_empty; if (FLAG_CHK(flags, FNC_DIFF_PROTOTYPE)) { memset(&dos.proto, 0, sizeof(dos.proto)); dos.proto.file = blob1; } dos.out = out; dos.state = state; dos.lines = *lines; dos.nlines = *nlines; dos.ansi = !!(FLAG_CHK(flags, FNC_DIFF_ANSI_COLOR)); if (FLAG_CHK(flags, FNC_DIFF_SIDEBYSIDE)) rc = sbsdiff(&c, &dos, NULL /*regex*/, context, flags); else rc = unidiff(&c, &dos, NULL /*regex*/, context, flags); *lines = dos.lines; /* realloc'd */ *nlines = dos.nlines; } else if (rawdata) { /* Return array of COPY/DELETE/INSERT triples. */ *rawdata = c.aEdit; c.aEdit = NULL; } end: fsl_free(c.aFrom); fsl_free(c.aTo); fsl_free(c.aEdit); return rc; } /* * Find the longest line in the file and encode the length in *flags. */ int max_sbs_width(fsl__diff_cx *c, int *flags, uint16_t ctxt) { int i, j, k; int endl, endr, start; /* Start and end of l+r chunks */ unsigned short sz, sbswidth; /* Size of each line and max width */ j = endl = endr = start = 0; k = c->nEdit; sbswidth = (*flags & FNC_DIFF_SIDEBYSIDE) ? sbsdiff_width(*flags) : 0; /* Find minimal copy/delete/insert triples. */ while (k > 2 && c->aEdit[k - 1] == 0 && c->aEdit[k - 2] == 0) k -= 3; /* * c[0] = first line in the diff and we accumulate each c[n += 3] to * find the start of the next change (after adding any insertions); * that is, each copy element by itself does not correspond to a line. */ do { int e, e2, s; /* * Compute number of lines in both the left and right chunk. * Both chunks will have the _same_ start line, but may have * distinct end lines. */ start += c->aEdit[j]; /* Chunk start line */ endl = endr = start; endl += MAX(c->aEdit[j + 1], 0); /* End of left side chunk */ endr += MAX(c->aEdit[j + 2], 0); /* End of right side chunk */ s = MAX(start - ctxt, 0); /* Include leading ctxt */ e = MIN(endl + ctxt, c->nFrom); /* L include trailing ctxt */ e2 = MIN(endr + ctxt, c->nTo); /* R include trailing ctxt */ /* * Now find the longest line. We'll make both the left and right * columns the width of the longest line from either side. * XXX Consider keeping max width of both sides to make each * column only as wide as its longest line; we can set a min * default in diffs with added/removed files. */ for (i = s; sbswidth && (i < e || i < e2); ++i) { if (i < e) { /* Left side line length */ sz = c->aFrom[i].n; if (sz > sbswidth) sbswidth = etcount(c->aFrom[i].z, sz); } if (i < e2) { /* Right side line length */ sz = c->aTo[i].n; if (sz > sbswidth) sbswidth = etcount(c->aTo[i].z, sz); } } if (i >= c->nFrom && i >= c->nTo) break; /* Account for insertions before adding lines to next chunk. */ start += c->aEdit[j + 2]; } while ((j += 3) < k - 3); *flags |= ((int)(sbswidth & 0xFFF)) << 16; /* Encode max width */ return FSL_RC_OK; } /* * Convert str byte size n to actual column width by expanding tabs and * accounting for unicode continuation bytes. Return the result. */ unsigned short etcount(const char *str, unsigned short n) { unsigned short c = 0; while (str && (str[c] != '\n' && str[c] != '\0')) { /* Expand tabs */ if (str[c] == '\t') n += 8 - (c % 8); if (UTF_CONT(str[c])) --n; ++c; } return n; } /* * Convert mask of public fnc_diff_flag (32-bit) values to the Fossil-internal * 64-bit bitmask used by the DIFF_xxx macros because fossil(1) uses the macro * approach and a low-level encoding of data in the bitmask (e.g., the context * lines count). The public API hides the lower-level flags and allows the * internal API to take care of the encoding. */ uint64_t fnc_diff_flags_convert(int mask) { uint64_t rc = 0U; #define DO(f) if ((mask & f) == f) rc |= (((uint64_t)f) << 24) DO(FNC_DIFF_IGNORE_EOLWS); DO(FNC_DIFF_IGNORE_ALLWS); DO(FSL_DIFF2_LINE_NUMBERS); DO(FNC_DIFF_SIDEBYSIDE); DO(FNC_DIFF_NOTTOOBIG); DO(FNC_DIFF_STRIP_EOLCR); DO(FNC_DIFF_VERBOSE); DO(FNC_DIFF_BRIEF); DO(FNC_DIFF_HTML); DO(FNC_DIFF_NOOPT); DO(FNC_DIFF_INVERT); #undef DO return rc; } static inline int min(int a, int b) { return a < b ? a : b; } /* * Return the number between 0 and 100 that is smaller the closer lline and * rline match. Return 0 for a perfect match. Return 100 if lline and rline * are completely different. The current algorithm is as follows: * * 1. Remove leading and trailing whitespace. * 2. Truncate both strings to at most 250 characters * 3. Find the length of the longest common subsequence * 4. Longer common subsequences yield lower scores. */ int match_dline(fsl_dline *lline, fsl_dline *rline) { const char *l, *r; /* Left and right strings */ int nleft, nright; /* Bytes in l and r */ int avg; /* Average length of l and r */ int i, j, k; /* Loop counters */ int best = 0; /* Current longest match found */ |
︙ | ︙ | |||
561 562 563 564 565 566 567 | * lcs[1] = end of the common segment in left * lcs[2] = start of the common segment in right * lcs[3] = end of the common segment in right * * n.b. This computation is for display purposes only and does not have to be * optimal or exact. */ | | | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | * lcs[1] = end of the common segment in left * lcs[2] = start of the common segment in right * lcs[3] = end of the common segment in right * * n.b. This computation is for display purposes only and does not have to be * optimal or exact. */ bool find_lcs(const char *left, int nleft, const char *right, int nright, int *lcs) { const unsigned char *l, *r; /* Left and right strings */ unsigned int probe; /* Probe to compare target */ unsigned int t[3]; /* 4-byte alignment targets */ int ntargets; /* Number of target points */ |
︙ | ︙ | |||
632 633 634 635 636 637 638 | } return rc; } /* * Send src to o->out(). If n is negative, use strlen() to determine length. */ | | | | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 | } return rc; } /* * Send src to o->out(). If n is negative, use strlen() to determine length. */ int diff_out(struct diff_out_state *const o, void const *src, fsl_int_t n) { return o->rc = n ? o->out(o->state, src, n < 0 ? strlen((char const *)src) : (size_t)n) : 0; } /* * Starting from seek lines, copy n lines from src to dst. The seek starts * from offset bytes into src->mem. This routine does _not_ modify src. */ int buffer_copy_lines_from(fsl_buffer *const dst, const fsl_buffer *const src, fsl_size_t *offset, fsl_size_t seek, fsl_size_t n) { const char *z = (const char *)src->mem; fsl_size_t idx = *offset, ln = 0, start = 0; int rc = FSL_RC_OK; |
︙ | ︙ | |||
674 675 676 677 678 679 680 | if (dst) /* trim trailing '\n' when copying */ rc = fsl_buffer_append(dst, &src->mem[start], idx - start - 1); *offset = start; return rc; } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | | | > | < | < < | > > > > > | > | > > > > > > > > > > > > | < > | < < | < < < < < < < < < < < | | > | | > | | | | | > > | | | > < > | < | | | 582 583 584 585 586 587 588 589 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 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | if (dst) /* trim trailing '\n' when copying */ rc = fsl_buffer_append(dst, &src->mem[start], idx - start - 1); *offset = start; return rc; } /* * Scan the diffed file dst->proto->file from line pos preceding the start of * the current chunk for the enclosing function in which the change resides. * Return first match. */ char * match_chunk_function(struct diff_out_state *const dst, uint32_t pos) { fsl_buffer buf = fsl_buffer_empty; const char *line; char *spec = NULL; fsl_size_t offset; uint32_t last = dst->proto.lastline; dst->proto.lastline = pos; offset = dst->proto.offset; /* Begin seek from last match */ while (pos > 1 && pos > last) { buffer_copy_lines_from(&buf, dst->proto.file, &offset, pos - dst->proto.lastmatch, 1); line = fsl_buffer_cstr(&buf); /* * GNU C and MSVC allow '$' in identifier names. * https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html * https://docs.microsoft.com/en-us/cpp/cpp/identifiers-cpp */ if (line) { if (fsl_isalpha(line[0]) || line[0] == '_' || line[0] == '$'){ if (starts_with(line, "private:")) { if (!spec) spec = " (private)"; } else if (starts_with(line, "protected:")) { if (!spec) spec = " (protected)"; } else if (starts_with(line, "public:")) { if (!spec) spec = " (public)"; } else { /* * Don't exceed 80 cols: chunk header * consumes ~25, so cap signature at 55. */ char *sig = fsl_mprintf("%s%s", line, spec ? spec : ""); fsl_free(dst->proto.signature); dst->proto.signature = fsl_mprintf("%.55s", sig); /* * It's expensive to seek from the start * of the file for each chunk when * diffing large files, so save offset * and line index of this match. */ dst->proto.lastmatch = pos; dst->proto.offset = offset; fsl_free(sig); fsl_buffer_clear(&buf); return dst->proto.signature; } } } /* No match, revert to last offset. */ offset = dst->proto.offset; fsl_buffer_clear(&buf); --pos; } return dst->proto.lastmatch > 0 ? dst->proto.signature : NULL; } /* * Render the diff triples array in cx->aEdit as a side-by-side diff in out. * cx Raw diff data * out Side-by-side diff representation * regex Show changes matching this regex * context Number of context lines * flags Flags controlling the diff */ int sbsdiff(fsl__diff_cx *cx, struct diff_out_state *dst, void *regex, uint16_t context, uint64_t flags) { fsl_dline *l, *r; /* Left and right side of diff */ fsl_buffer sbscols[5] = { fsl_buffer_empty_m, fsl_buffer_empty_m, fsl_buffer_empty_m, fsl_buffer_empty_m, fsl_buffer_empty_m }; struct sbsline s; /* Output line buffer */ static int chunks = 0; /* Number of chunks so far processed */ int li, ri; /* Index of next line in l[] and r[] */ int *c; /* copy/delete/insert triples */ int ci; /* Index into c[] */ int nc; /* number of c[] triples to process */ int max_ci; /* Maximum value for ci */ int nleft, nright; /* Number of l and r lines to output */ int ntotal; /* Total number of lines to output */ |
︙ | ︙ | |||
889 890 891 892 893 894 895 | for (i = 1; i < nc; ++i) { nleft += c[ci + i * 3]; nright += c[ci + i * 3]; } /* Draw separator between blocks except the first. */ if (showsep) { | < | < | | < | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 | for (i = 1; i < nc; ++i) { nleft += c[ci + i * 3]; nright += c[ci + i * 3]; } /* Draw separator between blocks except the first. */ if (showsep) { rc = add_line_type(lines, nlines, LINE_DIFF_SEPARATOR); if (rc) goto end; if (s.esc) { char ln[10]; fsl_snprintf(ln, sizeof(ln), "%d", li + skip + 1); rc = sbsdiff_separator(&s, fsl_strlen(ln), SBS_LLINE); if (rc) |
︙ | ︙ | |||
922 923 924 925 926 927 928 | } else rc = diff_outf(s.output, "%.*c\n", s.width * 2 + 16, '.'); if (rc) goto end; } showsep = true; | | | | | < | < | < < < | > | < | | < | | < | | < | > | | < | 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 | } else rc = diff_outf(s.output, "%.*c\n", s.width * 2 + 16, '.'); if (rc) goto end; } showsep = true; ++chunks; if (s.esc) rc = fsl_buffer_appendf(s.cols[SBS_LLINE], "<span class=\"fsl-diff-chunk-%d\"></span>", chunks); /* Show the initial common area */ li += skip; ri += skip; ntotal = c[ci] - skip; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc) rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); s.idx = s.end = -1; if (!rc) rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (!rc) rc = sbsdiff_marker(&s, " ", ""); if (!rc) rc = sbsdiff_lineno(&s, ri + j, SBS_RLINE); if (!rc) rc = sbsdiff_txt(&s, &r[ri + j], SBS_RTEXT); } if (rc) goto end; li += ntotal; ri += ntotal; /* Show the differences */ for (i = 0; i < nc; i++) { unsigned char *alignment; nleft = c[ci + i * 3 + 1]; /* Lines on left */ |
︙ | ︙ | |||
984 985 986 987 988 989 990 | alignment = sbsdiff_align(&l[li], nleft, &r[ri], nright); if (!alignment) { rc = FSL_RC_OOM; goto end; } | | | < < | | < | 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 | alignment = sbsdiff_align(&l[li], nleft, &r[ri], nright); if (!alignment) { rc = FSL_RC_OOM; goto end; } for (j = 0; !rc && nleft + nright > 0; j++) { char tag[30] = "<span class=\"fsl-diff-"; switch (alignment[j]) { case 1: /* Delete one line from the left */ rc = sbsdiff_lineno(&s, li, SBS_LLINE); if (rc) goto end_align; s.idx = 0; fsl_strlcat(tag, "rm\">", sizeof(tag)); s.tag = tag; s.end = LENGTH(&l[li]); rc = sbsdiff_txt(&s, &l[li], SBS_LTEXT); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_MINUS); if (rc) goto end_align; rc = sbsdiff_marker(&s, " <", "<"); if (rc) goto end_align; rc = sbsdiff_newline(&s); if (rc) goto end_align; assert(nleft > 0); |
︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 | if (rc) goto end_align; s.idx = 0; fsl_strlcat(tag, "add\">", sizeof(tag)); s.tag = tag; s.end = LENGTH(&r[ri]); rc = sbsdiff_txt(&s, &r[ri], SBS_RTEXT); | | < < | | < | | < < | | < > > > | > < | < < | | < | 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | if (rc) goto end_align; s.idx = 0; fsl_strlcat(tag, "add\">", sizeof(tag)); s.tag = tag; s.end = LENGTH(&r[ri]); rc = sbsdiff_txt(&s, &r[ri], SBS_RTEXT); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_PLUS); if (rc) goto end_align; assert(nright > 0); nright--; ri++; break; case 3: /* Left line changed into the right */ rc = sbsdiff_write_change(&s, &l[li], li, &r[ri], ri); if (rc) goto end_align; assert(nleft > 0 && nright > 0); nleft--; nright--; li++; ri++; break; default: { /* Delete left and insert right */ rc = sbsdiff_lineno(&s, li, SBS_LLINE); if (rc) goto end_align; s.idx = 0; fsl_strlcat(tag, "rm\">", sizeof(tag)); s.tag = tag; s.end = LENGTH(&l[li]); rc = sbsdiff_txt(&s, &l[li], SBS_LTEXT); if (rc) goto end_align; rc = sbsdiff_marker(&s, " | ", "|"); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (rc) goto end_align; rc = sbsdiff_lineno(&s, ri, SBS_RLINE); if (rc) goto end_align; s.idx = 0; s.tag = "<span class=\"fsl-diff-add\">"; s.end = LENGTH(&r[ri]); rc = sbsdiff_txt(&s, &r[ri], SBS_RTEXT); if (rc) goto end_align; nleft--; nright--; li++; ri++; break; } } } end_align: fsl_free(alignment); if (rc) goto end; if (i < nc - 1) { ntotal = c[ci + i * 3 + 3]; for (j = 0; !rc && j < ntotal; j++) { rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); s.idx = s.end = -1; if (rc) goto end; rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) goto end; rc = sbsdiff_marker(&s, " ", ""); if (rc) goto end; rc = sbsdiff_lineno(&s, ri + j, SBS_RLINE); if (rc) goto end; |
︙ | ︙ | |||
1137 1138 1139 1140 1141 1142 1143 | } /* Show the final common area */ assert(nc == i); ntotal = c[ci + nc * 3]; if (ntotal > context) ntotal = context; | | < | < | < < < | > | < | | < | | < | | < | < < | | < < | | | | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 | } /* Show the final common area */ assert(nc == i); ntotal = c[ci + nc * 3]; if (ntotal > context) ntotal = context; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc) rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); s.idx = s.end = -1; if (!rc) rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (!rc) rc = sbsdiff_marker(&s, " ", ""); if (!rc) rc = sbsdiff_lineno(&s, ri + j, SBS_RLINE); if (!rc) rc = sbsdiff_txt(&s, &r[ri + j], SBS_RTEXT); if (rc) goto end; } } /* diff triplet loop */ assert(!rc); if (s.esc && (s.cols[SBS_LLINE]->used > 0)) { rc = diff_out(dst, "<table class=\"fsl-sbsdiff-cols\"><tr>\n", -1); for (i = SBS_LLINE; !rc && i <= SBS_RTEXT; i++) rc = sbsdiff_column(dst, s.cols[i], i); if (!rc) rc = diff_out(dst, "</tr></table>\n", -1); } end: for (i = 0; i < (int)nitems(sbscols); ++i) fsl_buffer_clear(&sbscols[i]); return rc; } /* * Render the diff triples array in cx->aEdit as a unified diff in out. * cx Raw diff data * out Side-by-side diff representation * regex Show changes matching this regex * context Number of context lines * flags Flags controlling the diff */ int unidiff(fsl__diff_cx *cx, struct diff_out_state *dst, void *regex, uint16_t context, uint64_t flags) { fsl_dline *l, *r; /* Left and right side of diff */ static int chunks = 0; /* Number of chunks so far processed */ int li, ri; /* Index of next line in l[] and r[] */ int *c; /* copy/delete/insert triples */ int ci; /* Index into c[] */ int nc; /* number of c[] triples to process */ int max_ci; /* Maximum value for ci */ int nleft, nright; /* Number of l and r lines to output */ int ntotal; /* Total number of lines to output */ |
︙ | ︙ | |||
1291 1292 1293 1294 1295 1296 1297 | } /* * Show the header for this block, or if we are doing a modified * unified diff that contains line numbers, show the separator * from the previous block. */ | | | < < | < | | | < < | < < | | | < < < | | | < | < < < | > | | | > | < < | | < < | | | < < | < | < < | | | < < < < | < < < | | < < < | | < | | < | < | < < < < | | < < | > | | < | < | | < < < | | < < | | > | | < | < | | < < < | | < < | | > | | < | < | | < < < | | < < | | > | | < | < | < < < < | | < < | < < > < | | < | | | > | > > | | | | | | | | 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 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 | } /* * Show the header for this block, or if we are doing a modified * unified diff that contains line numbers, show the separator * from the previous block. */ ++chunks; if (showln) { if (!showsep) showsep = 1; /* Don't show a top divider */ else if (html) rc = diff_outf(dst, "<span class=\"fsl-diff-hr\">%.*c</span>\n", 80, '.'); else { rc = add_line_type(lines, nlines, LINE_DIFF_SEPARATOR); if (!rc) rc = diff_outf(dst, "%.95c\n", '.'); } if (!rc && html) rc = diff_outf(dst, "<span class=\"fsl-diff-chunk-%d\"></span>", chunks); } else { char const *ansi1 = ""; char const *ansi2 = ""; char const *ansi3 = ""; if (html) rc = diff_outf(dst, "<span class=\"fsl-diff-lineno\">"); #if 0 /* Turns out this just confuses the output */ else if (dst->ansi) { ansi1 = ANSI_DIFF_RM(0); ansi2 = ANSI_DIFF_ADD(0); ansi3 = ANSI_RESET; } #endif /* * If the patch changes an empty file or results in * an empty file, the block header must use 0,0 as * position indicator and not 1,0. Otherwise, patch * would be confused and may reject the diff. */ if (!rc) { rc = diff_outf(dst, "@@ %s-%d,%d %s+%d,%d%s @@", ansi1, nleft ? li + skip + 1 : 0, nleft, ansi2, nright ? ri + skip + 1 : 0, nright, ansi3); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_CHUNK); } if (!rc) { if (html) rc = diff_outf(dst, "</span>"); if (proto && li + skip > 1) { char *f = match_chunk_function(dst, (li + skip) - 1); if (f != NULL) rc = diff_outf(dst, " %s", f); } if (!rc) rc = diff_out(dst, "\n", 1); } } if (rc) return rc; /* Show the initial common area */ li += skip; ri += skip; ntotal = c[ci] - skip; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc && showln) rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (!rc) rc = unidiff_txt(dst, ' ', &l[li + j], html, 0); } if (rc) return rc; li += ntotal; ri += ntotal; /* Show the differences */ for (i = 0; i < nc; i++) { ntotal = c[ci + i * 3 + 1]; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_MINUS); if (!rc && showln) rc = unidiff_lineno(dst, li + j + 1, 0, html); if (!rc) rc = unidiff_txt(dst, '-', &l[li + j], html, regex); } if (rc) return rc; li += ntotal; ntotal = c[ci + i * 3 + 2]; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_PLUS); if (!rc && showln) rc = unidiff_lineno(dst, 0, ri + j + 1, html); if (!rc) rc = unidiff_txt(dst, '+', &r[ri + j], html, regex); } if (rc) return rc; ri += ntotal; if (i < nc - 1) { ntotal = c[ci + i * 3 + 3]; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc && showln) rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (!rc) rc = unidiff_txt(dst, ' ', &l[li + j], html, 0); } if (rc) return rc; ri += ntotal; li += ntotal; } } /* Show the final common area */ assert(nc==i); ntotal = c[ci + nc * 3]; if (ntotal > context) ntotal = context; for (j = 0; !rc && j < ntotal; j++) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc && showln) rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (!rc) rc = unidiff_txt(dst, ' ', &l[li + j], html, 0); } } /* _big_ for() loop */ fsl_free(dst->proto.signature); return rc; } /* Extract the number of context lines from flags. */ int diff_context_lines(uint64_t flags) { int n = flags & FNC_DIFF_CONTEXT_MASK; if (!n && !FLAG_CHK(flags, FNC_DIFF_CONTEXT_EX)) n = 5; return n; } /* * 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; } /* Append a separator line of length len to column col. */ int sbsdiff_separator(struct sbsline *dst, int len, int col) { char ch = '.'; if (len < 1) { len = 1; ch = ' '; } return fsl_buffer_appendf(dst->cols[col], "<span class=\"fsl-diff-hr\">%.*c</span>\n", len, ch); } /* * fsl_output_f() implementation for use with diff_outf(). State must be a * struct diff_out_state *. */ int fsl_output_f_diff_out(void *state, void const *src, fsl_size_t n) { struct diff_out_state *const dst = (struct diff_out_state *)state; return dst->rc = dst->out(dst->state, src, n); } int diff_outf(struct diff_out_state *dst, char const *fmt, ...) { va_list va; va_start(va,fmt); fsl_appendfv(fsl_output_f_diff_out, dst, fmt, va); va_end(va); return dst->rc; } /* Append a column to the final output blob. */ int sbsdiff_column(struct diff_out_state *dst, fsl_buffer const *content, int col) { return diff_outf(dst, "<td><div class=\"fsl-diff-%s-col\">\n" "<pre>\n" "%b</pre>\n" "</div></td>\n", |
︙ | ︙ | |||
1595 1596 1597 1598 1599 1600 1601 | * Expand tabs to spaces, and add newlines if col is SBS_RTEXT. Translate HTML * characters if esc is true. Pad with spaces to dst->width bytes if col is * SBS_LTEXT and esc is false. * * This comment contains multibyte unicode characters (�, �, �) in order * to test the ability of the diff code to handle such characters. */ | | | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | * Expand tabs to spaces, and add newlines if col is SBS_RTEXT. Translate HTML * characters if esc is true. Pad with spaces to dst->width bytes if col is * SBS_LTEXT and esc is false. * * This comment contains multibyte unicode characters (�, �, �) in order * to test the ability of the diff code to handle such characters. */ int sbsdiff_txt(struct sbsline *dst, fsl_dline *dline, int col) { fsl_buffer *o = dst->cols[col]; const char *str = dline->z; int n = dline->n; int i; /* Number of input characters consumed */ int pos; /* Cursor position */ |
︙ | ︙ | |||
1677 1678 1679 1680 1681 1682 1683 | } /* * Append newlines to columns corresponding to sbs diff format. * html: all columns * text: right column only */ | | | | | | | | 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 | } /* * Append newlines to columns corresponding to sbs diff format. * html: all columns * text: right column only */ int sbsdiff_newline(struct sbsline *dst) { int i, rc = FSL_RC_OK; for (i = dst->esc ? SBS_LLINE : SBS_RTEXT; !rc && i <= SBS_RTEXT; i++) rc = fsl_buffer_append(dst->cols[i], "\n", 1); return rc; } /* Append n spaces to column col in the sbs diff. */ int sbsdiff_space(struct sbsline *dst, int n, int col) { return fsl_buffer_appendf(dst->cols[col], "%*s", n, ""); } /* Append plaintext XOR html marker into the center column of the sbs diff. */ int sbsdiff_marker(struct sbsline *dst, const char *str, const char *html) { return fsl_buffer_append(dst->cols[SBS_MID], dst->esc ? html : str, -1); } /* Append file line number ln to column col in the sbs diff. */ int sbsdiff_lineno(struct sbsline *dst, int ln, int col) { int rc; if (dst->esc) rc = fsl_buffer_appendf(dst->cols[col], "%d", ln + 1); else { char lnno[8]; fsl_snprintf(lnno, 8, "%5d ", ln + 1); rc = fsl_buffer_appendf(dst->cols[col], "%s ", lnno); } return rc; } /* Try to shift dst->idx as far as possible to the left. */ void sbsdiff_shift_left(struct sbsline *dst, const char *z) { int i, j; while ((i = dst->idx) > 0 && z[i - 1] == z[i]) { for (j = i + 1; j < dst->end && z[j - 1] == z[j]; j++) {} if (j < dst->end) break; --dst->idx; --dst->end; } } /* * Simplify line at idx and idx2 in SBS diff output: * - If idx is a null-change then move idx2 into idx * - Make sure any null-changes are in canonical form. * - Make sure all changes are at character boundaries for multibyte chars. */ void sbsdiff_simplify_line(struct sbsline *dst, const char *z) { if (dst->idx2 == dst->end2) dst->idx2 = dst->end2 = 0; else if (dst->idx2) { while (dst->idx2 > 0 && UTF_CONT(z[dst->idx2])) --dst->idx2; |
︙ | ︙ | |||
1775 1776 1777 1778 1779 1780 1781 | } /* * c[] is an array of six integers: two copy/delete/insert triples for a * pair of adjacent differences. Return true if the gap between these two * differences is so small that they should be rendered as a single edit. */ | | | 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 | } /* * c[] is an array of six integers: two copy/delete/insert triples for a * pair of adjacent differences. Return true if the gap between these two * differences is so small that they should be rendered as a single edit. */ int sbsdiff_close_gap(int *c) { return c[3] <= 2 || c[3] <= (c[1] + c[2] + c[4] + c[5]) / 8; } /* * There is a change block in which nleft lines of text on the left are |
︙ | ︙ | |||
1808 1809 1810 1811 1812 1813 1814 | * Insertion and deletion costs are 50. Match costs are between 0 and 100 where * 0 is a perfect match 100 is a complete mismatch. * left lines of text on the left * nleft number of lines on the left * right lines of text on the right * nright number of lines on the right */ | | | 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | * Insertion and deletion costs are 50. Match costs are between 0 and 100 where * 0 is a perfect match 100 is a complete mismatch. * left lines of text on the left * nleft number of lines on the left * right lines of text on the right * nright number of lines on the right */ unsigned char * sbsdiff_align(fsl_dline *left, int nleft, fsl_dline *right, int nright) { int buf[100]; /* left[] stack if nright not too big */ int *row; /* One row of the Wagner matrix */ int *ptr; /* Space that needs to be freed */ int nmatches; /* Number of matches */ int matchscore; /* Match score */ |
︙ | ︙ | |||
1953 1954 1955 1956 1957 1958 1959 | * highlight to cover only those parts of the line that have changed. * dst The SBS output line * left Left line of the change * llnno Line number for the left line * right Right line of the change * rlnno Line number of the right line */ | | | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | * highlight to cover only those parts of the line that have changed. * dst The SBS output line * left Left line of the change * llnno Line number for the left line * right Right line of the change * rlnno Line number of the right line */ int sbsdiff_write_change(struct sbsline *dst, fsl_dline *left, int llnno, fsl_dline *right, int rlnno) { static const char tag_rm[] = "<span class=\"fsl-diff-rm\">"; static const char tag_add[] = "<span class=\"fsl-diff-add\">"; static const char tag_chg[] = "<span class=\"fsl-diff-change\">"; const char *ltxt; /* Text of the left line */ |
︙ | ︙ | |||
2006 2007 2008 2009 2010 2011 2012 | --nsfx; if (nsfx == leftsz || nsfx == rightsz) npfx = 0; } if (npfx + nsfx > shortest) npfx = shortest - nsfx; | | < < | < | < | < < | < < > | < | < | < < | < < > > > > > > > > | < < > | < < < < < | > | < < < | | < < | < | | < | | < | | > > > | | 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | --nsfx; if (nsfx == leftsz || nsfx == rightsz) npfx = 0; } if (npfx + nsfx > shortest) npfx = shortest - nsfx; /* A single chunk of text inserted on the right */ if (npfx + nsfx == leftsz) { rc = sbsdiff_lineno(dst, llnno, SBS_LLINE); if (rc) return rc; dst->idx2 = dst->end2 = 0; dst->idx = dst->end = -1; rc = sbsdiff_txt(dst, left, SBS_LTEXT); if (!rc && leftsz == rightsz && ltxt[leftsz] == rtxt[rightsz]) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (!rc) rc = sbsdiff_marker(dst, " ", ""); } else { rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (!rc) rc = sbsdiff_marker(dst, " | ", "|"); } if (!rc) { rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (!rc) { dst->idx = npfx; dst->end = rightsz - nsfx; dst->tag = tag_add; rc = sbsdiff_txt(dst, right, SBS_RTEXT); } } return rc; } /* A single chunk of text deleted from the left */ if (npfx + nsfx == rightsz) { /* Text deleted from the left */ rc = sbsdiff_lineno(dst, llnno, SBS_LLINE); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (rc) return rc; dst->idx2 = dst->end2 = 0; dst->idx = npfx; dst->end = leftsz - nsfx; dst->tag = tag_rm; rc = sbsdiff_txt(dst, left, SBS_LTEXT); if (!rc) { rc = sbsdiff_marker(dst, " | ", "|"); if (!rc) { rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (!rc) { dst->idx = dst->end = -1; sbsdiff_txt(dst, right, SBS_RTEXT); } } } return rc; } /* * At this point we know that there is a chunk of text that has * changed between the left and the right. Check to see if there * is a large unchanged section in the middle of that changed block. */ nleft = leftsz - nsfx - npfx; nright = rightsz - nsfx - npfx; if (dst->esc && nleft >= 6 && nright >= 6 && find_lcs(<xt[npfx], nleft, &rtxt[npfx], nright, lcs)) { |
︙ | ︙ | |||
2102 2103 2104 2105 2106 2107 2108 | } else dst->tag = tag_chg; dst->idx2 = npfx + lcs[1]; dst->end2 = leftsz - nsfx; dst->tag2 = lcs[3] == nright ? tag_rm : tag_chg; sbsdiff_simplify_line(dst, ltxt + npfx); rc = sbsdiff_txt(dst, left, SBS_LTEXT); | | < < < | < < < | | < | < | < | | | | | | < < < | < < < < | | < | | < | | > > > > | | 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 | } else dst->tag = tag_chg; dst->idx2 = npfx + lcs[1]; dst->end2 = leftsz - nsfx; dst->tag2 = lcs[3] == nright ? tag_rm : tag_chg; sbsdiff_simplify_line(dst, ltxt + npfx); rc = sbsdiff_txt(dst, left, SBS_LTEXT); if (!rc) rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (!rc) rc = sbsdiff_marker(dst, " | ", "|"); if (!rc) rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (rc) return rc; dst->idx = npfx; dst->end = npfx + lcs[2]; if (!lcs[0]) { sbsdiff_shift_left(dst, right->z); dst->tag = tag_add; } else dst->tag = tag_chg; dst->idx2 = npfx + lcs[3]; dst->end2 = rightsz - nsfx; dst->tag2 = lcs[1]==nleft ? tag_add : tag_chg; sbsdiff_simplify_line(dst, rtxt + npfx); rc = sbsdiff_txt(dst, right, SBS_RTEXT); return rc; } /* If all else fails, show a single big change between left and right */ rc = sbsdiff_lineno(dst, llnno, SBS_LLINE); if (!rc) { dst->idx2 = dst->end2 = 0; dst->idx = npfx; dst->end = leftsz - nsfx; dst->tag = tag_chg; rc = sbsdiff_txt(dst, left, SBS_LTEXT); if (!rc) { rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); rc = sbsdiff_marker(dst, " | ", "|"); if (!rc) { rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (!rc) { dst->end = rightsz - nsfx; sbsdiff_txt(dst, right, SBS_RTEXT); } } } } return rc; } /* * Add two line numbers to the beginning of a unified diff output line. * dst Output destination * lln Line number corresponding to the line in the left (old) file * rln Line number corresponding to the line in the right (new) file * html Specify html formatted output * n.b. lln or rln can be zero to leave that number field blank. */ int unidiff_lineno(struct diff_out_state *dst, int lln, int rln, bool html) { int rc = FSL_RC_OK; if (html) { rc = diff_out(dst, "<span class=\"fsl-diff-lineno\">", -1); if (rc) |
︙ | ︙ | |||
2206 2207 2208 2209 2210 2211 2212 | * Append a single line of unified diff text to dst. * dst Destination * sign Either a " " (context), "+" (added), or "-" (removed) line * line The line to be output * html True if generating HTML, false for plain text * regex colourise only if line matches this regex */ | | | 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 | * Append a single line of unified diff text to dst. * dst Destination * sign Either a " " (context), "+" (added), or "-" (removed) line * line The line to be output * html True if generating HTML, false for plain text * regex colourise only if line matches this regex */ int unidiff_txt(struct diff_out_state *const dst, char sign, fsl_dline *line, int html, void *regex) { char const *ansiccode; int rc = FSL_RC_OK; ansiccode = !dst->ansi ? NULL : ((sign == '+') ? |
︙ | ︙ |
Changes to src/fnc.1.
︙ | ︙ | |||
14 15 16 17 18 19 20 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt FNC 1 .Os .Sh NAME .Nm fnc | | | > > > > > > > | | | | | | > | | | < | | < | > > > | | | | | | | | | | | | | | < | | < | < | | < < < | > | | | | | | < | > | | < | | | | < | > | > | | > | | | < < | < | < < | < > > | | | < | | | < | < | < | > | | | | < < | | | | | | | < | | | < | > | < | | | < | < | > > | > | | | | | | | | | | | | | | | | | | | | < | | | | | < | | | | | < | | | < | | | | | | | | | | | | | | < | < < | | < | | | < | | | | < | > | < < < | | | | | | | | | | | | < | | | | | < | < | | | | | | | | | | | | | | | | | > > > > | | | | | | | | < < < < < < < < | | | | | < < | | < | | | | | < | < | | | | | < | < < < | | | | | | < | < < | < | | > | | | | | < | > | | | | < | | | | | | | | | | | < | | | | | | | | | | | | | | < | > | | | | | < | | < | | | | < | > | < < | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | < | | | < | > | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 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 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 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 391 392 393 394 395 396 397 398 399 400 401 402 403 404 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 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 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 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 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 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 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 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt FNC 1 .Os .Sh NAME .Nm fnc .Nd Read-only ncurses-based Fossil repository browser .Sh SYNOPSIS .Nm .Op Ar command .Op Fl h | -help .Nm .Op Fl h | -help .Op Fl v | -version .Nm .Cm stash .Sm off .Oo .Cm get | pop .Li | .Fl ChPx .Sm on .Oc .Nm .Cm config .Op Fl hu .Op Fl -ls .Op Fl R Ar path .Op Ar setting Op Ar value .Nm .Cm timeline .Op Fl Cz .Op Fl b Ar branch .Op Fl c Ar commit .Op Fl f Ar glob .Op Fl n Ar number .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 .Op Fl c Ar commit .Op Fl R Ar path .Op Ar path .Nm .Cm blame .Op Fl C .Op Fl c Ar commit Op Fl r .Op Fl n Ar number .Op Fl R Ar path .Ar path .Nm .Cm branch .Op Fl Ccopr .Op Fl a Ar date | Fl b Ar date .Op Fl R Ar path .Op Fl s Ar order .Op Ar glob .Nm .Op Ar path .Sh DESCRIPTION .Nm is an interactive interface for .Xr fossil 1 repositories, and supports multiple views to display repository data: .Bl -tag -width Ds .It Timeline view Display commits from the repository's history in chronologically descending order. .Br If no .Ar command or .Ar arg are specified, or just a .Ar path is passed, .Nm will default to displaying this view. .It Diff view Display changes introduced in the specified commit, or between two repository artifacts. .It Tree view Display navigable tree reflecting the repository state as at the specified commit. .It Blame view Display and annotate each line in the given file with the hyperlinked historical version that last modified the line. .It Branch view Display navigable list of all repository branches. .El .Pp .Nm provides both global and command-specific options and in-app key bindings. Global options are as follows: .Bl -tag -width 6v .It Fl h , -help Display program help and usage information then exit. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Nm session. See command specific options for details. .It Fl v , -version Display program version then exit. .El .Pp Note that any global options preceding a command name will be interpreted as the command-specific variant if such an option exists. .Pp Global key bindings are as follows: .Bl -tag -width Ds .It Cm H, ?, F1 Display in-app help. .It Cm Tab Switch focus between open views. .It Cm f Toggle the active view between fullscreen and splitscreen mode. Note that .Nm will open nested views in splitscreen mode if the terminal window is equal to or greater than 110 columns wide. .It Cm Q Immediatey quit .Nm . .It Cm q Quit the active view. .El .Pp Commands available to .Nm are as follows: .Bl -tag -width 4v .Tg conf .It Cm config Oo Fl h | -help Oc Oo Fl -ls Oc Oo Fl R | -repo Ar path Oc \ Oo Ar setting Op Ar value | Fl -unset Oc .Dl Pq aliases: Cm conf , Cm cfg , Cm settings , Cm set Retrieve the current, or set a new, .Ar value for .Ar setting in the local repository. When specified, .Ar value will become the new value for .Ar setting , otherwise .Nm will display the current value of .Ar setting . With no arguments, .Cm fnc config will display a list of all configurable settings. Alternatively, see .Sx ENVIRONMENT for a detailed list of available settings used in the display or processing of data. When no value is found for a given setting in the local repository, environment variables will be searched. If still not found, default values will be used. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. Options for .Cm fnc config are as follows: .Bl -tag -width Ds .It Fl h , -help Display config command help and usage information then exit. .It Fl -ls List all currently defined settings. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc config invocation. .It Fl u , -unset Clear the specified .Ar setting. .El .Tg stash .It Cm stash Oo Cm get Ns | Ns Cm pop Ns | Ns Oo Fl C | -no-colour Oc \ Oo Fl h | -help Oc Oo Fl P | -no-prototype Oc Oo Fl x | -context Ar n Oc Oc .Dl Pq aliases: Cm snapshot , Cm snap , Cm save , Cm sta When run with neither the .Cm get nor .Cm pop subcommands, .Nm .Cm stash will present an interactive view of the local changes on disk and iterate each hunk in the diff, prompting the user to either stash or keep the current change in the checkout. Valid answers are as follows: .Bl -column -offset 2s YXZ description .Sy b Ta scroll back Ns \(ha .Sy m Ta show more of the current hunk Ns \(ha .Sy y Ta yes, stash the current hunk .Sy n Ta no, do not stash the current hunk .Sy a Ta yes, stash this hunk and all remaining hunks in the file .Sy k Ta no, do not stash this hunk nor any remaining hunks in the file .Sy A Ta yes, stash this hunk and all remaining hunks in the diff .Sy K Ta no, do not stash this hunk nor any remaining hunks in the diff .Sy ? Ta display help dialog .El .Pp \(haConditionally available when the current hunk occupies the previous and/or following page. .Pp When all hunks have been selected, .Nm will prompt the user to enter a stash message. If not provided, a default message of .Qo fnc stash HASH-PREFIX .Qc , where .Qq HASH-PREFIX is an abbreviated SHA UUID hash of the current checkout, will be used. At any time prior to the final hunk being selected .Po i.e., before the stash message prompt .Pc , the operation can be aborted by opening the help dialog and entering .Qq Q , which will discard all selections and leave the checkout state unchanged. .Pp Available subcommands for .Nm .Cm stash are as follows: .Bl -ohang -width Ds .It Cm get .Dl Pq aliases: Cm apply Retrieve the most recent stash entry and apply it to the current checkout. .It Cm pop Remove the most recent stash entry and apply it to the current checkout. .El .Pp Options only apply to .Nm .Cm stash .Po i.e., neither the .Cm get nor .Cm pop subcommands .Pc and are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. .It Fl h , -help Display .Cm stash command help and usage information then exit. .It Fl P , -no-prototype Disable hunk 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. .It Fl x , -context Ar n Set .Ar n context lines to be shown in the interactive stash diff display such that 0 \*(Le n \*(Le 64. By default, 5 context lines are shown. Illegal values are a no-op. .El .Pp .Tg log .It Cm timeline Oo Fl C | -no-colour Oc Oo Fl T | -tag Ar tag Oc \ Oo Fl b | -branch Ar branch Oc Oo Fl c | -commit Ar commit Oc \ Oo Fl f | -filter Ar glob Oc Oo Fl h | -help Oc Oo Fl n | -limit Ar n Oc \ Oo Fl R | -repo Ar path Oc Oo Fl t | -type Ar type Oc \ Oo Fl u | -username Ar user Oc Oo Fl z | -utc Oc \ Op Ar path .Dl Pq aliases: Cm log , Cm tl , Cm time , Cm ti Display commit history of a repository. If .Ar path is specified, only commits that modified the file(s) at this path will populate the timeline. The .Ar path may be absolute, relative to the current working directory, or relative to the repository root. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp If no command is explicitly specified, this command will be executed by default. .Pp Options for .Cm fnc timeline are as follows: .Bl -tag -width Ds .It Fl b , -branch Ar branch Display commits that are members of the specified .Ar branch . The expected argument is a glob of the symbolic name of a branch, with the most recent branch to match being selected. Pattern matching is case-insensitive unless .Ar branch has at least one uppercase character, in which case the search will be case-sensitive. By default, .Nm will display all commits irrespective of the branch on which they reside. .It Fl C , -no-colour Disable colourised timeline, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c timeline view key binding as documented below. User-defined colours are also supported, see .Sx ENVIRONMENT for details. .It Fl c , -commit Ar commit Open the timeline from the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated prefix of) a valid commit UUID SHA1 or SHA3 hash. When this option is not supplied, .Nm will open the timeline to the latest leaf on the repository tree. For a complete list of valid arguments this option accepts, see .Lk https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki \ "Fossil's Check-in Names". .It Fl f , -filter Ar glob Filter timeline by commits containing .Ar glob in any of the commit comment, user, or branch fields. Pattern matching is case-insensitive unless .Ar glob has at least one uppercase character, in which case the search will be case-sensitive. Filtering can also be performed in-session with the .Sy F timeline view key binding as documented below. .It Fl h , -help Display timeline command help and usage information then exit. .It Fl n , -limit Ar n Limit timeline to the latest .Ar n commits. By default, .Nm will load the entire history of the repository's local checkout. Negative values are a no-op. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc timeline invocation. When this option is used, the checkout-related Fossil special tags .Qq current , .Qq prev , and .Qq next are invalid arguments to the .Sy --commit option. When .Sy --commit is not specified, .Nm will default to populating the timeline from the latest commit. .It Fl T , -tag Ar tag Only display commits with T cards containing .Ar tag . The expected argument is a glob of a commit manifest's T card argument, with the most recent tag to match being selected. Pattern matching is case-insensitive unless .Ar tag has at least one uppercase character, in which case the search will be case-sensitive. By default, .Nm will indiscriminately display all commits irrespective of which T cards are attached to the commit manifest. .It Fl t , -type Ar type Only display .Ar type commits. Valid .Ar type values are as follows: .Bl -column -offset 2s YXZ description .Sy ci Ta check-in .Sy w Ta wiki .Sy t Ta ticket .Sy e Ta technote .Sy f Ta forum post .Sy g Ta tag artifact .El .Pp By default, when this option is not supplied, .Nm will indiscriminately load all commits irrespective of .Ar type . Note that this is a repeatable flag (e.g., .Nm .Cm timeline .Cm -t e -t t Ns ). .It Fl u , -username Ar user Only display commits authored by .Ar user . The search is case-insensitive by default unless .Ar user contains at least one uppercase character, in which case the search will be case-sensitive. .It Fl z , -utc Use Coordinated Universal Time (UTC) rather than local time when displaying commit dates and timestamps. .El .Pp Key bindings for .Cm fnc timeline are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j, >, \&. Move selection cursor down the timeline. .It Cm Arrow-up, k, <, \&, Move selection cursor up the timeline. .It Cm Arrow-right, l Scroll the view two columns to the right in the buffer. The comment field moves left on the screen. .It Cm Arrow-left, h Scroll the view two columns to the left in the buffer. The comment field moves right on the screen. .It Cm $ Scroll the view right to the end of the longest comment summary line on the page. .It Cm 0 Scroll the view left to the beginning of the line. .It Cm C-f, Page-down Scroll timeline view one page downwards in the buffer. .It Cm C-b, Page-up Scroll timeline view one page upwards in the buffer. .It Cm C-d Scroll timeline view half a page downwards in the buffer. .It Cm C-u Scroll timeline view half a page upwards in the buffer. .It Cm G, End Move selection cursor to the last commit on the timeline (i.e., oldest commit in the repository). .It Cm gg, Home Move selection cursor to the first commit on the timeline (i.e., newest commit in the repository). .It Cm Enter Open a .Cm diff view displaying the changeset of the currently selected commit. .It Cm Space (Un)tag the currently selected commit as the base commit for the next tagged commit to be diffed against. If another commit is already tagged, show the changes between it and the selected commit. .It Cm b Open and populate branch view with all repository branches. .It Cm C Diff local changes on disk in the current checkout against the selected commit. .It Cm c Toggle colourised timeline. On supported terminals, .Nm will default to displaying the timeline in colour. .It Cm F Prompt to enter a search term to filter a new timeline view by commits with comment, user, or branch fields that match the entered pattern. If no commits match, a message is displayed on screen. .It Cm t Display the tree of the repository corresponding to the currently selected commit. .It Cm / Prompt to enter a search term to begin searching for commits matching the pattern provided. The search term is an extended regular expression, which is cross-referenced against a commit's comment, the username of its author, branch, and UUID SHA1 or SHA3 hash. See .Xr re_format 7 for regular expression syntax. .It Cm n 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, .Nm will diff the local changes on disk against the version on which the current checkout is based. If only .Ar artifact1 is specified, .Nm will diff the current checkout, including any local changes on disk, against this version. When both arguments are specified, the changes between these two versions will be displayed. If supplied, .Nm will filter diffs between commits by .Ar path so that only changes involving the file(s) identified are displayed. Paths may be absolute, relative to the current working directory, or relative to the repository root. Both .Ar artifact1 and .Ar artifact2 must be of the same type, which is expected to be either a symbolic check-in name, tag, (unique abbreviated prefix of) a commit or blob artifact UUID SHA1 or SHA3 hash, or an ISO 8601 formatted date. Both artifact arguments must be supplied when diffing blobs; any following non-option arguments are invalid and will be ignored. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Options for .Cm fnc diff are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured diff output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c diff view key binding as documented below. User-defined colours are also supported, see .Sx ENVIRONMENT for details. .It Fl h , -help Display diff command help and usage information then exit. .It Fl i , -invert Invert the difference between artifacts when displaying the diff. .It Fl l , -line-numbers Display file line numbers in diff output. As documented below, this option can 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 Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc diff invocation. When this option is used, both .Ar artifact1 and .Ar artifact2 are required, and the checkout-related Fossil special tags .Qq current , .Qq prev , and .Qq next are invalid .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. .It Cm Arrow-up, k Move the selection cursor up one line. .It Cm Arrow-right, l Scroll the view two columns to the right in the buffer. Diff output moves left on the screen. .It Cm Arrow-left, h Scroll the view two columns to the left in the buffer. Diff output moves right on the screen. .It Cm $ Scroll the view right to the end of the longest line in the diff. .It Cm 0 Scroll the view left to the beginning of the line. .It Cm C-e Scroll view one line downwards in the buffer. Diff output moves upwards on the screen. .It Cm C-y Scroll view one line upwards in the buffer. Diff output moves downwards on the screen. .It Cm C-n Navigate to next file in the diff. .It Cm C-p Navigate to previous file in the diff. .It Cm C-f, Page-down, Space Scroll diff view one page downwards in the buffer. .It Cm C-b, Page-up Scroll diff view one page upwards in the buffer. .It Cm C-d Scroll diff view half a page downwards in the buffer. .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 Toggle verbosity of diff output. By default, .Nm will display the entire content of newly added or deleted files. .It Cm w Toggle whether whitespace-only changes are ignored when comparing lines in the diff. .It Cm / Prompt to enter a search term to begin searching the diff output for lines matching the pattern provided. The search term is an extended regular expression, which is documented in .Xr re_format 7 . .It Cm n Find the next line that matches the current search term. .It Cm N Find the previous line that matches the current search term. .El .Tg dir .It Cm tree Oo Fl C | -no-colour Oc Oo Fl c | -commit Ar commit Oc \ Oo Fl h | -help Oc Oo Fl R | -repo Ar path Oc Op Ar path .Dl Pq aliases: Cm dir , Cm tr Display navigable, hierarchical tree of a repository. If a .Ar path is specified, display tree nodes of this path. The .Ar path may be absolute, relative to the current working directory, or relative to the repository root. With no options passed, the tree will reflect the state of the latest commit on trunk. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Tree nodes are lexicographically ordered and may be postfixed with an identifier corresponding to the mode of the file object on disk as returned by .Xr lstat 2 : .Bl -column -offset Ds YXZ description .It / Ta directory .It * Ta executable .It @ Ta symbolic link .El .Pp Nodes representing symbolic links are also annotated with the path of the source file. .Pp Options for .Cm fnc tree are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c tree view key binding as documented below. User-defined colours are also supported, see .Sx ENVIRONMENT for details. .It Fl c , -commit Ar commit The displayed tree will reflect the state of the repository as at the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated prefix of) a valid commit UUID SHA1 or SHA3 hash. For a complete list of valid arguments this option accepts, see .Lk https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki \ "Fossil's Check-in Names". .It Fl h , -help Display tree command help and usage information then exit. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc tree invocation. When this option is used, the checkout-related Fossil special tags .Qq current , .Qq prev , and .Qq next are invalid arguments to the .Sy --commit option. .El .Pp Key bindings for .Cm fnc tree are as follows: .Bl -tag -width Ds .It Cm Enter, Arrow-right, l Enter the currently selected directory, or open a .Cm blame view of the currently selected file. .It Cm Backspace, Arrow-left, h Move up a level to the parent directory. This is a no-op when in the root tree. .It Cm Arrow-down, j Move selection cursor one node down the tree. .It Cm Arrow-up, k Move selection cursor one node up the tree. .It Cm C-f, Page-down Scroll tree view one page downwards in the buffer. .It Cm C-b, Page-up Scroll tree view one page upwards in the buffer. .It Cm C-d Scroll tree view half a page downwards in the buffer. .It Cm C-u Scroll tree view half a page upwards in the buffer. .It Cm Home, gg Move selection cursor to the first node in the tree. .It Cm End, G Move selection cursor to the last node in the tree. .It Cm b Open and populate branch view with all repository branches. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the tree in colour. .It Cm d Toggle ISO8601 modified timestamp display for each tree entry. .It Cm i Toggle SHA hash UUID display for all file nodes displayed in the tree. .It Cm t Open .Cm timeline view for the currently selected tree node. This will display the timeline of all commits that involve the versioned file(s) corresponding to the selected node. .It Cm / Prompt to enter a search term to begin searching the tree for nodes matching the entered pattern. The search term is an extended regular expression, as documented in .Xr re_format 7 , and is matched against the path of each tree node. .It Cm n Find the next tree node that matches the current search pattern. .It Cm N Find the previous tree node that matches the current search pattern. .El .Tg praise .It Cm blame Oo Fl C | -no-colour Oc \ Oo Fl c | -commit Ar commit Oo Fl r | -reverse Oc Oc Oo Fl h | -help Oc \ Oo Fl n | -limit Ar n Oc Oo Fl R | -repo Ar path Oc Ar path .Dl Pq aliases: Cm praise , Cm annotate , Cm bl , Cm pr , Cm an Show commit attribution history for each line of the file at the specified .Ar path , which may be absolute, relative to the current working directory, or relative to the repository root. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Options for .Cm fnc blame are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c blame view key binding as documented below. User-defined colours are also supported, see .Sx ENVIRONMENT for details. .It Fl c , -commit Ar commit Start blame of file at the specified .Ar path from the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated prefix of) a valid commit UUID SHA1 or SHA3 hash. When this option is not supplied, .Nm will blame the version of the file from the current checkout. For a complete list of valid arguments this option accepts, see .Lk https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki \ "Fossil's Check-in Names". .It Fl h , -help Display blame command help and usage information then exit. .It Fl l , -line Ar lineno Open annotated file at .Ar lineno . As documented below, once the file is loaded, the .Sy @ key binding can be used to navigate to an arbitrary line in the file. .It Fl n , -limit Ar n Limit depth of blame history to .Ar n commits or seconds. The latter is denoted by a postfixed 's' (e.g., 30s). With this option, .Nm will traverse either as many commits as specified, or as possible in the specified time limit. By default, .Nm will traverse the entire historical record of the file, which can be expensive for large files that span many commits. Use this option for a faster, more targeted annotation. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc blame invocation. When this option is used, the checkout-related Fossil special tags .Qq current , .Qq prev , and .Qq next are invalid arguments to the .Sy --commit option. When .Sy --commit is not specified, .Nm will default to blaming the version of the file found in the latest commit. .It Fl r , -reverse Reverse annotate the file starting from a historical commit and move forward in time. That is, rather than show the most recent change to each line, show the first time each line was modified by a subsequent commit after the specified .Ar commit . (Requires \fB\-c\fP|\fB\-\-commit\fP.) .El .Pp Key bindings for .Cm fnc blame are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one line. .It Cm Arrow-up, k Move selection cursor up one line. .It Cm Arrow-right, l Scroll the view two columns to the right in the buffer. File output moves left on the screen. .It Cm Arrow-left, h Scroll the view two columns to the left in the buffer. File output moves right on the screen. .It Cm $ Scroll the view right to the end of the longest line in the file. .It Cm 0 Scroll the view left to the beginning of the line. .It Cm C-f, Page-down Scroll blame view one page downwards in the buffer. .It Cm C-b, Page-up Scroll blame view one page upwards in the buffer. .It Cm C-d Scroll blame view half a page downwards in the buffer. .It Cm C-u Scroll blame view half a page upwards in the buffer. .It Cm Home, gg Move selection cursor to the first line in the file. .It Cm End, G Move selection cursor to the last line in the file. .It Cm Enter Display the .Cm diff of the commit corresponding to the currently selected line. .It Cm # Toggle display of file line numbers. .It Cm @ Open prompt to enter line number and navigate to that line in the file. .It Cm B, Backspace Reload the previous blamed version of the file. .It Cm b Blame the version of the file corresponding to the commit in the currently selected line. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the blamed file in colour. .It Cm p Blame the version of the file corresponding to the parent of the commit in the currently selected line. .It Cm T Open and populate branch view with all repository branches. .It Cm / Prompt to enter a search term to begin searching the file for tokens matching the entered pattern. The search term is an extended regular expression, as documented in .Xr re_format 7 . .It Cm N Find the previous token that matches the current search pattern. .It Cm n Find the next token that matches the current search pattern. .El .Tg tag .It Cm branch Oo Fl C | -no-colour Oc Oo Fl -after Ar date | \ Fl -before Ar date Oc Oo Fl h | -help Oc Oo Fl -open | Fl -closed Oc \ Oo Fl p | -no-private Oc Oo Fl R | -repo Ar path Oc Oo Fl r | -reverse Oc \ Oo Fl s | -sort Ar order Oc Op Ar glob .Dl Pq aliases: Cm tag , Cm br Display navigable list of repository branches. If .Ar glob is specified, only display branches matching the pattern provided. Pattern matching is case-insensitive unless .Ar glob contains at least one uppercase character, in which case the search will be case-sensitive. Unless the .Sy --repo option is used, this command must be executed from within the repository hierarchy; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Branches are lexicographically ordered by default, and are prefixed with an identifier corresponding to the branch state (i.e., open/closed). The current and private branches are additionally annotated with a postfixed identifier: .Bl -column -offset Ds ABCDEFGHIJ description .It +dev-foo Ta open .It -rm-bar Ta closed .It +trunk@ Ta current .It +wip-baz* Ta private .El .Pp All branches, irrespective of state or privacy, are displayed by default, but can be filtered based on several characteristics. .Pp Options for .Cm fnc branch are as follows: .Bl -tag -width Ds .It Fl a , -after Ar date Display only those branches with activity after the specified .Ar date , which is expected to be either an ISO8601 .Po e.g., |
︙ | ︙ | |||
1173 1174 1175 1176 1177 1178 1179 | Display only opened branches. .It Fl p , -no-private Do not show private branches, which are included in the list of displayed branches by default. .It Fl R , -repo Ar path Use the .Xr fossil 1 | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | < | | | | | < | | < | | | | 1129 1130 1131 1132 1133 1134 1135 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 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 | Display only opened branches. .It Fl p , -no-private Do not show private branches, which are included in the list of displayed branches by default. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository database at the specified .Ar path for the current .Cm fnc branch invocation. .It Fl r , -reverse Reverse the order in which branches are displayed. .It Fl s , -sort Ar order Sort branches by .Ar order . Valid .Ar order values are as follows: .Bl -column -offset 2s YXZ description .Sy mru Ta most recently used .Sy state Ta open/closed state .El .Pp Branches are sorted in lexicographical order by default. .El .Pp Key bindings for .Cm fnc branch are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one branch. .It Cm Arrow-up, k Move selection cursor up one branch. .It Cm C-f, Page-down Scroll branch view one page downwards in the buffer. .It Cm C-b, Page-up Scroll branch view one page upwards in the buffer. .It Cm C-d Scroll branch view half a page downwards in the buffer. .It Cm C-u Scroll branch view half a page upwards in the buffer. .It Cm Home, gg Move selection cursor to the first branch in the list. .It Cm End, G Move selection cursor to the last branch in the list. .It Cm Enter, Space Display the .Cm timeline of the currently selected branch. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the branch list in colour. .It Cm d Toggle display of the date on which the branch last received changes. .It Cm i Toggle display of the SHA{1,3} hash that identifies branch, which is the hash of the commit on the tip of said branch. .It Cm s Toggle sort order of currently displayed branches. If branches are ordered lexicographically, .Nm will sort branches in most recently used order, otherwise branches will be sorted by their open/closed state. .It Cm t Open the .Cm tree view of the currently selected branch. .It Cm R, C-l Reload the view with all repository branches, irrespective of which options were used in this .Cm fnc branch invocation. .It Cm / Prompt to enter a search term to begin searching the list for branches matching the entered pattern. The search term is an extended regular expression, as documented in .Xr re_format 7 . .It Cm n Find the next branch that matches the current search pattern. .It Cm N Find the previous branch that matches the current search pattern. .El .El .Sh ENVIRONMENT Depending on the available screen estate determined by the LINES and COLUMNS environment variables, .Nm will display child views in either a horizontal or vertical split. By default, if COLUMNS is \(>= 120, a child view will open in a vertical split at least 80 columns wide. Otherwise, the child view will open in a horizontal split that is approximately 60% of the terminal height. This behaviour can be customised by configuring the following options as either exported environment variables or with .Nm Cm config as documented above. .Bl -tag -width FNC_VIEW_SPLIT_HEIGHT .It Ev FNC_VIEW_SPLIT_MODE Open child views in a horizontal or vertical split. Value can be one of .Sy auto , .Sy horizontal , or .Sy vertical . Default: .Qq auto . .It Ev FNC_VIEW_SPLIT_HEIGHT Height of the child view when opening in a horizontal split. Valid numeric values are 1 \(<= .Sy n < LINES. Percentage values denoted with a postfixed .Sq % .Po e.g., .Sy 55% .Pc are also valid. Default: .Qq 60% . .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 |
︙ | ︙ | |||
1317 1318 1319 1320 1321 1322 1323 | 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 | | | < | < | | < | | < | | | | < | > | | | | | < | < | | | < | < | < | | | | | | | | | | | | | | | < | < | | | | < | | < > | | 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | 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 .Nm Cm config as documented above. At startup, .Nm will search for user-defined colours in the following order: .Bl -column " environment variables " description -offset Ds .It 1. repository settings Ta Pa repo.fossil .It 3. environment variables Ta Sy shell .El .Pp If none are found, the default colour scheme will be displayed. This enables setting per-project colours to visually distinguish the current repository being viewed, and globally changing the colour scheme for all repositories with no local settings configured. Except where documented below, colours supported in .Nm are: .Bl -column "black" "yellow" "magenta" "default" -offset indent-two .It Qo black Qc Ta Qo green Qc Ta Qo blue Qc Ta Qo cyan Qc .It Qo red Qc Ta Qo yellow Qc Ta Qo magenta Qc Ta Qo default Qc .El .Pp Where .Qq default is the current foreground (i.e., text) colour in the terminal. User-definable colour objects displayed in various .Nm views are as follows: .Bl -tag -width FNC_COLOUR_BRANCH_PRIVATE .It Ev FNC_COLOUR_COMMIT The commit hash ID field displayed in the timeline, diff, tree, and blame views. If not defined, the default value is .Qq green . .It Ev FNC_COLOUR_USER The username field displayed in the timeline and diff views. If not defined, the default value is .Qq cyan . .It Ev FNC_COLOUR_DATE The date field displayed in the timeline and diff views. If not defined, the default value is .Qq yellow . .It Ev FNC_COLOUR_DIFF_MINUS Removed lines displayed in the diff view. If not defined, the default value is .Qq magenta . .It Ev FNC_COLOUR_DIFF_PLUS Added lines displayed in the diff view. If not defined, the default value is .Qq cyan . .It Ev FNC_COLOUR_DIFF_CHUNK Chunk header lines .Po e.g., .Li @@ -732,34 +747,40 @@ .Pc displayed in the diff view. If not defined, the default value is .Qq yellow . .It Ev FNC_COLOUR_DIFF_META Metadata displayed in the diff view. If not defined, the default value is .Qq green . .It Ev FNC_COLOUR_DIFF_TAGS The tag field displayed in the diff view. If not defined, the default value is .Qq magenta . .It Ev FNC_COLOUR_DIFF_SBS_EDIT Changed .Pq i.e., not added or removed lines in the diff view when displaying side-by-side diffs. If not defined, the default value is .Qq red . .It Ev FNC_COLOUR_TREE_DIR Directory entries displayed in the tree view. If not defined, the default value is .Qq cyan . .It Ev FNC_COLOUR_TREE_EXEC Executable file entries displayed in the tree view. If not defined, the default value is .Qq green . .It Ev FNC_COLOUR_TREE_LINK Symbolic link entries displayed in the tree view. If not defined, the default value is .Qq magenta . .It Ev FNC_COLOUR_BRANCH_OPEN Open branches displayed in the branch view. If not defined, the default value is .Qq cyan . .It Ev FNC_COLOUR_BRANCH_CLOSED Closed branches displayed in the branch view. If not defined, the default value is .Qq magenta . .It Ev FNC_COLOUR_BRANCH_CURRENT The branch corresponding to the current checkout displayed in the branch view. If not defined, the default value is .Qq green . .It Ev FNC_COLOUR_BRANCH_PRIVATE Private branches displayed in the branch view. If not defined, the default value is .Qq yellow . .It Ev FNC_COLOUR_HL_LINE Selected line highlight in the diff view. Value can be one of .Sy auto or .Sy mono . The former will invert the foreground colour of the selected line, while the latter will use a monochromatic highlight. If not defined, the default value is .Qq auto . .It Ev FNC_COLOUR_HL_SEARCH Search term highlight in blame and diff view. If not defined, the default value is .Qq yellow . .El .Pp To clear environment variables, issue .Cm unset Ar ENVIRONMENT_VARIABLE in the shell. .Pp .Nm displays best with UTF-8, and will detect whether UTF-8 is enabled to determine which characters to draw in certain views. If UTF-8 is supported by your terminal but is currently disabled, it can be enabled with .Qq export LC_ALL=en_US.UTF-8 ; If not available, .Nm will revert to ASCII. Relatedly, some fonts may render certain characters poorly in the help screen; .Li Monospace Regular , .Li JetBrains Mono , and .Li Menlo are known to render all characters well. .Sh EXIT STATUS .Ex -std fnc .Sh SEE ALSO .Xr fossil 1 , .Xr re_format 7 .Xr sqlite3 1 .Sh AUTHOR .An Mark Jamsek Aq Mt mark@jamsek.com |
Changes to src/fnc.c.
︙ | ︙ | |||
78 79 80 81 82 83 84 | #include <wchar.h> #include <langinfo.h> #include "libfossil.h" #include "diff.h" #define FNC_VERSION VERSION /* cf. Makefile */ | < < | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include <wchar.h> #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. */ |
︙ | ︙ | |||
107 108 109 110 111 112 113 | #define FLAG_SET(_f, _b) ((_f) |= (_b)) #define FLAG_CHK(_f, _b) ((_f) & (_b)) #define FLAG_TOG(_f, _b) ((_f) ^= (_b)) #define FLAG_CLR(_f, _b) ((_f) &= ~(_b)) /* Application macros. */ #define PRINT_VERSION STRINGIFY(FNC_VERSION) | < < | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #define FLAG_SET(_f, _b) ((_f) |= (_b)) #define FLAG_CHK(_f, _b) ((_f) & (_b)) #define FLAG_TOG(_f, _b) ((_f) ^= (_b)) #define FLAG_CLR(_f, _b) ((_f) &= ~(_b)) /* Application macros. */ #define PRINT_VERSION STRINGIFY(FNC_VERSION) #define DEF_DIFF_CTX 5 /* Default diff context lines. */ #define MAX_DIFF_CTX 64 /* Max diff context lines. */ #define HSPLIT_SCALE 0.4 /* Default horizontal split scale. */ #define SPIN_INTERVAL 200 /* Status line progress indicator. */ #define LINENO_WIDTH 6 /* View lineno max column width. */ #define MAX_PCT_LEN 7 /* Line position upto max len 99.99% */ #define SPINNER "\\|/-\0" |
︙ | ︙ | |||
144 145 146 147 148 149 150 | # define strtonum(s, min, max, o) strtol(s, (char **)o, 10) # define inrange(n, min, max) (((n) >= (min)) && ((n) <= (max))) # endif /* HAVE_STRTONUM */ #else # define inrange(n, min, max) true #endif /* OpenBSD */ | < < < < < < < < < < < < < < < < < < < < < < < | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | # define strtonum(s, min, max, o) strtol(s, (char **)o, 10) # define inrange(n, min, max) (((n) >= (min)) && ((n) <= (max))) # endif /* HAVE_STRTONUM */ #else # define inrange(n, min, max) true #endif /* OpenBSD */ #define PRINTFV(fmt, args) __attribute__((format (printf, fmt, args))) #ifndef __dead #define __dead __attribute__((noreturn)) #endif #ifndef TAILQ_FOREACH_SAFE /* Rewrite of OpenBSD 6.9 sys/queue.h for Linux builds. */ #define TAILQ_FOREACH_SAFE(var, head, field, tmp) \ for ((var) = ((head)->tqh_first); \ (var) != (NULL) && ((tmp) = TAILQ_NEXT(var, field), 1); \ (var) = (tmp)) #endif |
︙ | ︙ | |||
268 269 270 271 272 273 274 | 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. */ | < | | 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 | 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[8]; /* 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. */ fcli_cliflag cliflags_stash[5]; /* Stash options. */ } fnc_init = { NULL, /* cmdarg copy of argv[1] to aid usage/error report. */ |
︙ | ︙ | |||
319 320 321 322 323 324 325 | 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 hunk header defaults to on. */ | < | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | 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 hunk 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. */ |
︙ | ︙ | |||
343 344 345 346 347 348 349 | "Display program help and usage then exit."), FCLI_FLAG_BOOL("v", "version", &fnc_init.vflag, "Display program version number and exit."), fcli_cliflag_empty_m }, { /* cmd_args available app commands. */ | | | | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | "Display program help and usage then exit."), FCLI_FLAG_BOOL("v", "version", &fnc_init.vflag, "Display program version number and exit."), fcli_cliflag_empty_m }, { /* cmd_args available app commands. */ {"timeline", "tl\0time\0ti\0log\0", "Show chronologically descending commit history of the repository.", cmd_timeline, usage_timeline, fnc_init.cliflags_timeline}, {"diff", "di\0", "Show changes to versioned files introduced with a given commit.", cmd_diff, usage_diff, fnc_init.cliflags_diff}, {"tree", "tr\0dir\0", "Show repository tree corresponding to a given commit", cmd_tree, usage_tree, fnc_init.cliflags_tree}, {"blame", "bl\0praise\0pr\0annotate\0an\0", "Show commit attribution history for each line of a file.", cmd_blame, usage_blame, fnc_init.cliflags_blame}, {"branch", "br\0tag\0", "Show navigable list of repository branches.", cmd_branch, usage_branch, fnc_init.cliflags_branch}, {"config", "conf\0cfg\0settings\0set\0", "Configure or view currently available settings.", cmd_config, usage_config, fnc_init.cliflags_config}, {"stash", "snapshot\0snap\0save\0sta\0", "Interactively select hunks to stash from the diff of local " "changes on\n disk.", cmd_stash, usage_stash, fnc_init.cliflags_stash}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel. */ }, { /* cliflags_timeline timeline command related options. */ |
︙ | ︙ | |||
418 419 420 421 422 423 424 | "Only display commits authored by <username>."), FCLI_FLAG_BOOL("z", "utc", &fnc_init.utc, "Use UTC (instead of local) time."), fcli_cliflag_empty_m }, /* End cliflags_timeline. */ { /* cliflags_diff diff command related options. */ | < < < | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | "Only display commits authored by <username>."), FCLI_FLAG_BOOL("z", "utc", &fnc_init.utc, "Use UTC (instead of local) time."), fcli_cliflag_empty_m }, /* End cliflags_timeline. */ { /* cliflags_diff diff command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable coloured diff output, which is enabled by default on\n " "supported terminals. Colour can also be toggled with the 'c' " "\n key binding in diff view when this option is not used."), FCLI_FLAG_BOOL("h", "help", NULL, "Display diff command help and usage."), FCLI_FLAG_BOOL("i", "invert", &fnc_init.invert, |
︙ | ︙ | |||
693 694 695 696 697 698 699 | char *branch; char *type; enum fnc_diff_type diff_type; }; struct fsl_file_artifact { fsl_card_F *fc; | | | 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | char *branch; char *type; enum fnc_diff_type diff_type; }; struct fsl_file_artifact { fsl_card_F *fc; enum fsl_ckout_change_e change; }; TAILQ_HEAD(commit_tailhead, commit_entry); struct commit_entry { TAILQ_ENTRY(commit_entry) entries; struct fnc_commit_artifact *commit; int idx; |
︙ | ︙ | |||
880 881 882 883 884 885 886 887 | uint8_t context; /* MAX_DIFF_CTX lines = 64 */ enum fnc_patch_rc rc; bool report; }; struct stash_cx { struct patch_cx pcx; char patch[2][PATH_MAX]; /* stash & ckout patch filepath */ | > | > | | | | < | 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 | uint8_t context; /* MAX_DIFF_CTX lines = 64 */ enum fnc_patch_rc rc; bool report; }; struct stash_cx { struct patch_cx pcx; struct index hunk; /* line indexes for each hunk in the diff */ char patch[2][PATH_MAX]; /* stash & ckout patch filepath */ unsigned char *stash; /* bit array into this.hunk->lineno */ #define NBITS (sizeof(unsigned char) * 8) #define nbytes(nbits) (((nbits) + 7) >> 3) #define BIT_SET(_B, _i) (_B[(_i / NBITS)] |= (1 << (_i % NBITS))) #define BIT_CLR(_B, _i) (_B[(_i / NBITS)] &= ~(1 << (_i % NBITS))) #define BIT_CHK(_B, _i) (_B[(_i / NBITS)] & (1 << (_i % NBITS))) }; struct fnc_diff_view_state { struct fnc_view *view; struct fnc_view *parent_view; struct fnc_commit_artifact *selected_entry; struct fnc_pathlist_head *paths; struct stash_cx scx; fsl_buffer buf; struct fnc_colours colours; struct index index; FILE *f; fsl_uuid_str id1; fsl_uuid_str id2; int first_line_onscreen; int last_line_onscreen; int diff_flags; int context; |
︙ | ︙ | |||
955 956 957 958 959 960 961 | unsigned int lineno; bool annotated; }; struct fnc_blame_cb_cx { struct fnc_view *view; struct fnc_blame_line *lines; | | > | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 | unsigned int lineno; bool annotated; }; struct fnc_blame_cb_cx { struct fnc_view *view; struct fnc_blame_line *lines; fsl_uuid_str commit_id; fsl_uuid_str root_commit; int nlines; uint32_t maxlen; bool *quit; }; typedef int (*fnc_cancel_cb)(void *); |
︙ | ︙ | |||
997 998 999 1000 1001 1002 1003 | struct fnc_blame_view_state { struct fnc_blame blame; struct fnc_commit_id_queue blamed_commits; struct fnc_commit_qid *blamed_commit; struct fnc_commit_artifact *selected_entry; struct fnc_colours colours; | | | 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 | struct fnc_blame_view_state { struct fnc_blame blame; struct fnc_commit_id_queue blamed_commits; struct fnc_commit_qid *blamed_commit; struct fnc_commit_artifact *selected_entry; struct fnc_colours colours; fsl_uuid_str commit_id; const char *lineno; char *path; int first_line_onscreen; int last_line_onscreen; int selected_line; int matched_line; int spin_idx; |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 | void (*grep_init)(struct fnc_view *); int (*grep)(struct fnc_view *); }; static volatile sig_atomic_t rec_sigwinch; static volatile sig_atomic_t rec_sigpipe; static volatile sig_atomic_t rec_sigcont; | < < | 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | void (*grep_init)(struct fnc_view *); int (*grep)(struct fnc_view *); }; static volatile sig_atomic_t rec_sigwinch; static volatile sig_atomic_t rec_sigpipe; static volatile sig_atomic_t rec_sigcont; static void fnc_show_version(void); static int init_curses(void); static int fnc_set_signals(void); static struct fnc_view *view_open(int, int, int, int, enum fnc_view_id); static int open_timeline_view(struct fnc_view *, fsl_id_t, const char *, const char *); |
︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 | static int make_stash_diff(struct fnc_diff_view_state *, char *); 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 *); static int add_line_offset(off_t **, size_t *, off_t); static int diff_commit(struct fnc_diff_view_state *); | < < | | | | | | < < < < < | < | 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 1195 1196 1197 1198 1199 1200 1201 | static int make_stash_diff(struct fnc_diff_view_state *, char *); 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 *); static int add_line_offset(off_t **, size_t *, off_t); static int diff_commit(struct fnc_diff_view_state *); static int diff_checkout(struct fnc_diff_view_state *); static int write_diff_meta(struct fnc_diff_view_state *, const char *, fsl_uuid_str, const char *, fsl_uuid_str, enum fsl_ckout_change_e); static int diff_file(struct fnc_diff_view_state *, fsl_buffer *, const char *, const char *, fsl_uuid_str, const char *, enum fsl_ckout_change_e); static int diff_non_checkin(struct fnc_diff_view_state *); static int diff_file_artifact(struct fnc_diff_view_state *, fsl_id_t, const fsl_card_F *, const fsl_card_F *, fsl_ckout_change_e); static int show_diff(struct fnc_view *); static int write_diff(struct fnc_view *, char *); static int match_line(const char *, regex_t *, size_t, regmatch_t *); static int draw_matched_line(struct fnc_view *, const char *, int *, int, int, regmatch_t *, attr_t); static void drawborder(struct fnc_view *); static int diff_input_handler(struct fnc_view **, struct fnc_view *, int); static int request_tl_commits(struct fnc_view *); static int reset_diff_view(struct fnc_view *, bool); static int stash_get_rm_cb(fsl_ckout_unmanage_state const *); static int stash_get_add_cb(fsl_ckout_manage_state const *, bool *); static int f__add_files_in_sfile(int *, int); static int f__stash_get(bool); static int fnc_stash(struct fnc_view *); static int select_hunks(struct fnc_view *); static int stash_input_handler(struct fnc_view *, bool *); static void set_choice(struct fnc_diff_view_state *, bool *, struct input *, struct index *, uint32_t *, size_t *, size_t *, bool *, enum stash_opt *); static unsigned char *alloc_bitstring(size_t); static int generate_prompt(char ***, char *, size_t, short); |
︙ | ︙ | |||
1287 1288 1289 1290 1291 1292 1293 | struct commit_entry *); static void diff_grep_init(struct fnc_view *); static int find_next_match(struct fnc_view *); static void grep_set_view(struct fnc_view *, FILE **, off_t **, size_t *, int **, int **, int **, int **, uint8_t *); static int view_close(struct fnc_view *); | | < > | | | | 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 | struct commit_entry *); static void diff_grep_init(struct fnc_view *); static int find_next_match(struct fnc_view *); static void grep_set_view(struct fnc_view *, FILE **, off_t **, size_t *, int **, int **, int **, int **, uint8_t *); static int view_close(struct fnc_view *); static int map_repo_path(char **); static int init_timeline_view(struct fnc_view **, int, int, fsl_id_t, const char *, const char *); static bool path_is_child(const char *, const char *, size_t); static int path_skip_common_ancestor(char **, const char *, size_t, const char *, size_t); static bool fnc_path_is_root_dir(const char *); /* static bool fnc_path_is_cwd(const char *); */ static int fnc_pathlist_insert(struct fnc_pathlist_entry **, struct fnc_pathlist_head *, const char *, void *); static int fnc_path_cmp(const char *, const char *, size_t, size_t); static void fnc_pathlist_free(struct fnc_pathlist_head *); static int browse_commit_tree(struct fnc_view **, int, int, struct commit_entry *, const char *); static int open_tree_view(struct fnc_view *, const char *, fsl_id_t); static int walk_tree_path(struct fnc_tree_view_state *, struct fnc_repository_tree *, struct fnc_tree_object **, const char *); static int create_repository_tree(struct fnc_repository_tree **, fsl_uuid_str *, fsl_id_t); static int tree_builder(struct fnc_repository_tree *, struct fnc_tree_object **, const char *); /* static void delete_tree_node(struct fnc_tree_entry **, */ /* struct fnc_tree_entry *); */ static int link_tree_node(struct fnc_repository_tree *, const char *, const char *, double); static int show_tree_view(struct fnc_view *); static int tree_input_handler(struct fnc_view **, struct fnc_view *, int); static int blame_tree_entry(struct fnc_view **, int, int, struct fnc_tree_entry *, struct fnc_parent_trees *, fsl_uuid_str); static void tree_grep_init(struct fnc_view *); static int tree_search_next(struct fnc_view *); static int tree_entry_path(char **, struct fnc_parent_trees *, struct fnc_tree_entry *); static int draw_tree(struct fnc_view *, const char *); static int blame_selected_file(struct fnc_view **, struct fnc_view *); static int timeline_tree_entry(struct fnc_view **, int, struct fnc_tree_view_state *); static void tree_scroll_up(struct fnc_tree_view_state *, int); static int tree_scroll_down(struct fnc_view *, int); static int visit_subtree(struct fnc_tree_view_state *, struct fnc_tree_object *); static int tree_entry_get_symlink_target(char **, struct fnc_tree_entry *); static int match_tree_entry(struct fnc_tree_entry *, regex_t *); static void fnc_object_tree_close(struct fnc_tree_object *); static void fnc_close_repo_tree(struct fnc_repository_tree *); static int open_blame_view(struct fnc_view *, char *, fsl_uuid_str, fsl_id_t, int, const char *); static int run_blame(struct fnc_view *); static int fnc_dump_buffer_to_file(off_t *, int *, off_t **, FILE *, fsl_buffer *); static int show_blame_view(struct fnc_view *); static void *blame_thread(void *); static int blame_cb(void *, fsl_annotate_opt const * const, fsl_annotate_step const * const); |
︙ | ︙ | |||
1388 1389 1390 1391 1392 1393 1394 | static void fnc_branch_close(struct fnc_branch *); static bool view_is_parent(struct fnc_view *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); static int close_diff_view(struct fnc_view *); | | < < < | 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 | static void fnc_branch_close(struct fnc_branch *); static bool view_is_parent(struct fnc_view *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); static int close_diff_view(struct fnc_view *); static void free_index(struct index *); static void free_tags(struct fnc_tl_view_state *, bool); static int view_resize(struct fnc_view *, bool); static bool screen_is_split(struct fnc_view *); static bool screen_is_shared(struct fnc_view *); static void updatescreen(WINDOW *, bool, bool); static void fnc_resizeterm(void); static int join_tl_thread(struct fnc_tl_view_state *); static void fnc_free_commits(struct commit_queue *); static void fnc_commit_artifact_close(struct fnc_commit_artifact*); static int fsl_file_artifact_free(void *, void *); static void sigwinch_handler(int); static void sigpipe_handler(int); static void sigcont_handler(int); static int draw_lineno(struct fnc_view *, int, int, attr_t); static bool gotoline(struct fnc_view *, int *, int *); static int strtonumcheck(long *, const char *, const int, const int); static int fnc_prompt_input(struct fnc_view *, struct input *); static int fnc_date_to_mtime(double *, const char *, int); static int cook_input(char *, int, WINDOW *); |
︙ | ︙ | |||
1448 1449 1450 1451 1452 1453 1454 | int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); int main(int argc, const char **argv) { | | | | | 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 | int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); int main(int argc, const char **argv) { fcli_command *cmd = NULL; char *path = NULL; int rc = FSL_RC_OK; /* * XXX Guard against misuse. Will have to take another approach once * the test harness is finished as we pipe input for our tests cases. */ if (!isatty(fileno(stdin))) { rc = RC(FSL_RC_MISUSE, "invalid input device"); |
︙ | ︙ | |||
1480 1481 1482 1483 1484 1485 1486 | if (fnc_init.vflag) { fnc_show_version(); goto end; } else if (fnc_init.hflag) { rc = FCLI_RC_HELP; goto end; } | < < < < | | < | | | | > | | | | | | > | > > > | < | > > < | > > > > | | 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | if (fnc_init.vflag) { fnc_show_version(); goto end; } else if (fnc_init.hflag) { rc = FCLI_RC_HELP; goto end; } #ifdef __OpenBSD__ /* * See pledge(2). This is the most restrictive set we can operate under. * Look for any adverse impact & revise when implementing new features. * stdio (close, sigaction); rpath (chdir getcwd lstat); wpath (getcwd); * cpath (symlink); flock (open); tty (TIOCGWINSZ); unveil (unveil). * XXX 'fnc stash' needs more perms, call pledge(2) from cmd_stash(). */ if (!(!fsl_strcmp(fnc_init.cmdarg, "stash") || fcli_cmd_aliascmp(&fnc_init.cmd_args[6], fnc_init.cmdarg)) && pledge("stdio rpath wpath cpath flock tty unveil", NULL) == -1) { rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "pledge"); goto end; } #endif rc = fcli_fingerprint_check(true); if (argc == 1) cmd = &fnc_init.cmd_args[FNC_VIEW_TIMELINE]; else if (!rc) { rc = fcli_dispatch_commands(fnc_init.cmd_args, false); if (rc == FSL_RC_NOT_FOUND && argc == 2) { /* * Check if user entered fnc path/in/repo; if valid path * is found, assume fnc timeline path/in/repo was meant. */ rc = map_repo_path(&path); if (rc == FSL_RC_UNKNOWN_RESOURCE || !path) { rc = RC(FSL_RC_NOT_FOUND, "'%s' is not a valid command or path", argv[1]); } else if (!rc) { cmd = &fnc_init.cmd_args[FNC_VIEW_TIMELINE]; fnc_init.path = path; fcli_err_reset(); /* for fcli_process_flags */ } } } if (rc) goto end; if (!fsl_cx_db_repo(fcli_cx())) { rc = RC(FSL_RC_MISUSE, "repository database required"); goto end; } if (cmd != NULL) rc = cmd->f(cmd); end: fsl_free(path); !isendwin() ? endwin() : 0; /* may have been called in cmd_stash() */ if (rc) { if (rc == FSL_RC_BREAK) { const fsl_cx *const f = fcli_cx(); const char *errstr; fsl_error_get(&f->error, &errstr, NULL); fsl_fprintf(stdout, "%s", errstr); RC_RESET(rc); /* for fcli_end_of_main() */ } else if (rc == FSL_RC_UNKNOWN_RESOURCE) { /* file not found by map_repo_path() */ fcli_err_set(FSL_RC_NOT_FOUND, "%s", fsl_buffer_cstr(&fcli_error()->msg)); } else { fnc_init.err = rc == FCLI_RC_HELP ? FSL_RC_OK : rc; usage(); /* NOT REACHED */ } } putchar('\n'); return fcli_end_of_main(rc); } |
︙ | ︙ | |||
1578 1579 1580 1581 1582 1583 1584 | } if (fnc_init.glob) glob = fsl_strdup(fnc_init.glob); if (fnc_init.path) path = fsl_strdup(fnc_init.path); else { | | | < | < < < | 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 | } if (fnc_init.glob) glob = fsl_strdup(fnc_init.glob); if (fnc_init.path) path = fsl_strdup(fnc_init.path); else { rc = map_repo_path(&path); if (rc) goto end; } rc = init_curses(); if (rc) goto end; rc = init_unveil(((const char *[]){REPODB, CKOUTDIR, P_tmpdir, gettzfile()}), ((const char *[]){"rw", "rwc", "rwc", "r"}), 4, true); |
︙ | ︙ | |||
1620 1621 1622 1623 1624 1625 1626 | if (!rc) rc = open_timeline_view(*view, rid, path, glob); return rc; } /* | | | | | < | | | | > | > > > > > > > > > | < < | | | | | > > > < < | | | | | | | | | > > | | | | | > | | > > > > | < < | | | | | < | | | | | | | | > > > > | | | < | | | | | | > > | < > > > > > > > > | > | | > > > > > > > > > > > > > > > > | | > < | | | | | < < | 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 | if (!rc) rc = open_timeline_view(*view, rid, path, glob); return rc; } /* * Look for an in-repository path in **argv. If found, canonicalise it as an * absolute path relative to the repository root (e.g., /ckoutdir/found/path), * and assign to a dynamically allocated string in *requested_path, which the * caller must dispose of with fsl_free or free(3). */ static int map_repo_path(char **requested_path) { fsl_cx *const f = fcli_cx(); fsl_buffer buf = fsl_buffer_empty; char *canonpath = NULL, *ckoutdir = NULL, *path = NULL; const char *ckoutdir0 = NULL; fsl_size_t len; int rc = 0; bool root; *requested_path = NULL; /* If no path argument is supplied, default to repository root. */ if (!fcli_next_arg(false)) { *requested_path = fsl_strdup("/"); if (*requested_path == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); return rc; } canonpath = fsl_strdup(fcli_next_arg(true)); if (canonpath == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } /* * If no checkout (e.g., 'fnc timeline -R') copy the path verbatim to * check its validity against a deck of F cards in open_timeline_view(). */ ckoutdir0 = fsl_cx_ckout_dir_name(f, &len); if (!ckoutdir0) { path = fsl_strdup(canonpath); goto end; } path = realpath(canonpath, NULL); if (path == NULL && (errno == ENOENT || errno == ENOTDIR)) { /* Path is not on disk, assume it is relative to repo root. */ rc = fsl_file_canonical_name2(ckoutdir0, canonpath, &buf, NULL); if (rc) { rc = RC(rc, "fsl_file_canonical_name2"); goto end; } fsl_free(path); path = realpath(fsl_buffer_cstr(&buf), NULL); if (path) { /* Confirmed path is relative to repository root. */ fsl_free(path); path = fsl_strdup(canonpath); if (path == NULL) rc = RC(FSL_RC_ERROR, "fsl_strdup"); } else rc = RC(FSL_RC_UNKNOWN_RESOURCE, "'%s' not found in tree", canonpath); goto end; } /* * Use the cwd as the virtual root to canonicalise the supplied path if * it is either: (a) relative; or (b) the root of the current checkout. * Otherwise, use the root of the current checkout. */ rc = fsl_cx_getcwd(f, &buf); if (rc) goto end; ckoutdir = fsl_mprintf("%.*s", len - 1, ckoutdir0); root = fsl_strcmp(ckoutdir, fsl_buffer_cstr(&buf)) == 0; fsl_buffer_reuse(&buf); rc = fsl_ckout_filename_check(f, (canonpath[0] == '.' || !root) ? true : false, canonpath, &buf); if (rc) goto end; fsl_free(path); fsl_free(canonpath); canonpath = fsl_strdup(fsl_buffer_str(&buf)); if (canonpath[0] == '\0') { path = fsl_strdup(canonpath); if (path == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else { fsl_buffer_reuse(&buf); rc = fsl_file_canonical_name2(f->ckout.dir, canonpath, &buf, false); if (rc) goto end; path = fsl_strdup(fsl_buffer_str(&buf)); if (path == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } if (access(path, F_OK) != 0) { rc = RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "path does not exist or inaccessible [%s]", path); goto end; } /* * Now we have an absolute path, check again if it's the ckout * dir; if so, clear it to signal an open_timeline_view() check. */ len = fsl_strlen(path); if (!fsl_strcmp(path, f->ckout.dir)) { fsl_free(path); path = fsl_strdup(""); if (path == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else if (len > f->ckout.dirLen && path_is_child(path, f->ckout.dir, f->ckout.dirLen)) { char *child; /* * Matched on-disk path within the repository; strip * common prefix with repository root path. */ rc = path_skip_common_ancestor(&child, f->ckout.dir, f->ckout.dirLen, path, len); if (rc) goto end; fsl_free(path); path = child; } else { /* * Matched on-disk path outside the repository; treat * as relative to repo root. (Though this should fail.) */ fsl_free(path); path = canonpath; canonpath = NULL; } } /* Trim trailing slash if it exists. */ if (path[fsl_strlen(path) - 1] == '/') path[fsl_strlen(path) - 1] = '\0'; end: if (rc) { *requested_path = fsl_strdup(canonpath); fsl_free(path); } else { /* Make path absolute from repository root. */ if (path[0] != '/' && (path[0] != '.' && path[1] != '/')) { char *abspath; if ((abspath = fsl_mprintf("/%s", path)) == NULL) { rc = RC(FSL_RC_ERROR, "fsl_mprintf"); } fsl_free(path); path = abspath; } *requested_path = path; } fsl_buffer_clear(&buf); fsl_free(canonpath); fsl_free(ckoutdir); return rc; } static bool path_is_child(const char *child, const char *parent, size_t parentlen) { if (parentlen == 0 || fnc_path_is_root_dir(parent)) return true; if (fsl_strncmp(parent, child, parentlen) != 0) return false; if (child[parentlen - 1 /* Trailing slash */] != '/') return false; return true; } /* * As a special case, due to fsl_ckout_filename_check() resolving the current * checkout directory to ".", this function returns true for ".". For this * reason, when path is intended to be the current working directory for any * directory other than the repository root, callers must ensure path is either * absolute or relative to the respository root--not ".". */ static bool fnc_path_is_root_dir(const char *path) { while (*path == '/' || *path == '.') ++path; return (*path == '\0'); } static int path_skip_common_ancestor(char **child, const char *parent_abspath, |
︙ | ︙ | |||
1818 1819 1820 1821 1822 1823 1824 | return (path[0] == '.' && path[1] == '\0'); } #endif static int init_curses(void) { | < < < < < < | < < < < < < < < < | 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 | return (path[0] == '.' && path[1] == '\0'); } #endif static int init_curses(void) { initscr(); cbreak(); noecho(); nonl(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); raw(); /* Don't signal control characters, specifically C-y */ curs_set(0); set_escdelay(0); /* ESC should return immediately. */ #ifndef __linux__ typeahead(-1); /* Don't disrupt screen update operations. */ #endif if (!fnc_init.nocolour && has_colors()) { start_color(); use_default_colors(); } return fnc_set_signals(); } static int fnc_set_signals(void) { if (sigaction(SIGPIPE, &(struct sigaction){{sigpipe_handler}}, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "sigaction(SIGPIPE)"); if (sigaction(SIGWINCH, &(struct sigaction){{sigwinch_handler}}, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "sigaction(SIGWINCH)"); if (sigaction(SIGCONT, &(struct sigaction){{sigcont_handler}}, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "sigaction(SIGCONT)"); return FSL_RC_OK; } static struct fnc_view * view_open(int nlines, int ncols, int start_ln, int start_col, enum fnc_view_id vid) |
︙ | ︙ | |||
1947 1948 1949 1950 1951 1952 1953 | if (rid) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); else fsl_ckout_version_info(f, NULL, &s->curr_ckout_uuid); /* | | | | | > | > > > > > > > > > > > > | > > | > | | > | 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 | if (rid) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); else fsl_ckout_version_info(f, NULL, &s->curr_ckout_uuid); /* * In 'fnc timeline -R repo.fossil path' case, check that path is a * valid repository path in the repository tree as at either the * latest check-in or the specified commit. */ if (s->curr_ckout_uuid == NULL && path[1]) { fsl_deck d = fsl_deck_empty; fsl_uuid_str id = NULL; bool ispath = false; if (rid) id = fsl_rid_to_uuid(f, rid); rc = fsl_deck_load_sym(f, &d, fnc_init.sym ? fnc_init.sym : id ? id : "tip", FSL_SATYPE_CHECKIN); fsl_deck_F_rewind(&d); if (fsl_deck_F_search(&d, path + 1 /* Slash */) == NULL) { const fsl_card_F *cf; fsl_deck_F_next(&d, &cf); do { fsl_deck_F_next(&d, &cf); if (cf && !fsl_strncmp(path + 1 /* Slash */, cf->name, fsl_strlen(path) - 1)) { ispath = true; break; } } while (cf); } else ispath = true; fsl_deck_finalize(&d); fsl_free(id); if (!ispath) return RC(FSL_RC_NOT_FOUND, "'%s' invalid path in [%s]", path + 1, fnc_init.sym ? fnc_init.sym : "tip"); } if ((rc = pthread_cond_init(&s->thread_cx.commit_consumer, NULL))) { RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "pthread_cond_init"); goto end; } if ((rc = pthread_cond_init(&s->thread_cx.commit_producer, NULL))) { |
︙ | ︙ | |||
2083 2084 2085 2086 2087 2088 2089 | if (startdate) { fsl_buffer_appendf(&sql, " AND event.mtime <= %s", startdate); fsl_free(startdate); } /* | | | | | | | | > > | 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 | if (startdate) { fsl_buffer_appendf(&sql, " AND event.mtime <= %s", startdate); fsl_free(startdate); } /* * If path is not root ("/"), a versioned path in the repository has * been requested, only retrieve commits involving path. */ if (path[1]) { fsl_buffer_appendf(&sql, " AND EXISTS(SELECT 1 FROM mlink" " WHERE mlink.mid = event.objid" " AND mlink.fnid IN "); if (fsl_cx_is_case_sensitive(f,false)) { fsl_buffer_appendf(&sql, "(SELECT fnid FROM filename" " WHERE name = %Q OR name GLOB '%q/*')", path + 1, path + 1); /* Skip prepended slash. */ } else { fsl_buffer_appendf(&sql, "(SELECT fnid FROM filename" " WHERE name = %Q COLLATE nocase" " OR lower(name) GLOB lower('%q/*'))", path + 1, path + 1); /* Skip prepended slash. */ } fsl_buffer_append(&sql, ")", 1); } fsl_buffer_appendf(&sql, " ORDER BY event.mtime DESC"); if (fnc_init.nrecords.limit > 0) fsl_buffer_appendf(&sql, " LIMIT %d", fnc_init.nrecords.limit); |
︙ | ︙ | |||
2130 2131 2132 2133 2134 2135 2136 | case FSL_RC_STEP_ROW: rc = 0; break; case FSL_RC_STEP_ERROR: rc = RC(rc, "fsl_stmt_step"); goto end; case FSL_RC_STEP_DONE: | < | < < < < < < < < < | 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 | case FSL_RC_STEP_ROW: rc = 0; break; case FSL_RC_STEP_ERROR: rc = RC(rc, "fsl_stmt_step"); goto end; case FSL_RC_STEP_DONE: rc = RC(FSL_RC_BREAK, "no matching records"); goto end; } s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = true; s->thread_cx.rc = 0; s->thread_cx.db = db; |
︙ | ︙ | |||
2199 2200 2201 2202 2203 2204 2205 | TAILQ_INSERT_HEAD(&views, view, entries); view->active = true; rc = view->show(view); if (rc) return rc; | | | 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 | TAILQ_INSERT_HEAD(&views, view, entries); view->active = true; rc = view->show(view); if (rc) return rc; while (!TAILQ_EMPTY(&views) && !done && !rec_sigpipe) { rc = view_input(&new_view, &done, view, &views); if (rc) break; if (view->egress) { struct fnc_view *v, *prev = NULL; if (view_is_parent(view)) |
︙ | ︙ | |||
2337 2338 2339 2340 2341 2342 2343 | int rc; bool done = false; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; | | | 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 | int rc; bool done = false; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; while (!done && !rc && !rec_sigpipe) { switch (rc = build_commits(cx)) { case FSL_RC_STEP_DONE: done = true; /* FALL THROUGH */ case FSL_RC_STEP_ROW: rc = 0; /* FALL THROUGH */ |
︙ | ︙ | |||
2401 2402 2403 2404 2405 2406 2407 | block_main_thread_signals(void) { sigset_t set; if (sigemptyset(&set) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigemptyset"); | | < < < < | 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 | block_main_thread_signals(void) { sigset_t set; if (sigemptyset(&set) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigemptyset"); /* Bespoke signal handlers for SIGWINCH and SIGCONT. */ if (sigaddset(&set, SIGWINCH) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigaddset"); if (sigaddset(&set, SIGCONT) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigaddset"); /* ncurses handles SIGTSTP. */ if (sigaddset(&set, SIGTSTP) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigaddset"); if (pthread_sigmask(SIG_BLOCK, &set, NULL)) |
︙ | ︙ | |||
2675 2676 2677 2678 2679 2680 2681 | struct commit_entry *entry = s->selected_entry; struct fnc_colour *c = NULL; const char *search_str = NULL; char *headln = NULL, *idxstr = NULL; char *branch = NULL, *type = NULL; char *uuid = NULL; wchar_t *wline; | | | 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 | struct commit_entry *entry = s->selected_entry; struct fnc_colour *c = NULL; const char *search_str = NULL; char *headln = NULL, *idxstr = NULL; char *branch = NULL, *type = NULL; char *uuid = NULL; wchar_t *wline; attr_t rx = A_BOLD; int ncommits = 0, rc = FSL_RC_OK, wlen = 0; int ncols_needed, maxlen = -1; if (s->selected_entry && !(view->searching != SEARCH_DONE && view->search_status == SEARCH_WAITING)) { uuid = fsl_strdup(s->selected_entry->commit->uuid); branch = fsl_strdup(s->selected_entry->commit->branch); |
︙ | ︙ | |||
2732 2733 2734 2735 2736 2737 2738 | * for the space separator. Same applies if search_str is being shown. */ ncols_needed = fsl_strlen(type) + fsl_strlen(idxstr) + FSL_STRLEN_K256 + (!search_str && (!fsl_strcmp(type, "wiki") || !fsl_strcmp(type, "tag") || !fsl_strcmp(type, "ticket") || (!branch && !fsl_strcmp(type, "technote"))) ? 0 : 1); /* If a path has been requested, display it in the headline. */ | | | | 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 | * for the space separator. Same applies if search_str is being shown. */ ncols_needed = fsl_strlen(type) + fsl_strlen(idxstr) + FSL_STRLEN_K256 + (!search_str && (!fsl_strcmp(type, "wiki") || !fsl_strcmp(type, "tag") || !fsl_strcmp(type, "ticket") || (!branch && !fsl_strcmp(type, "technote"))) ? 0 : 1); /* If a path has been requested, display it in the headline. */ if (s->path[1]) { if ((headln = fsl_mprintf("%s%c%.*s %s%s", type ? type : "", type ? ' ' : SPINNER[tcx->spin_idx], view->ncols < ncols_needed ? view->ncols - (ncols_needed - FSL_STRLEN_K256) : FSL_STRLEN_K256, uuid ? uuid : "........................................", s->path, idxstr)) == NULL) { rc = RC(FSL_RC_RANGE, "fsl_mprintf"); headln = NULL; |
︙ | ︙ | |||
2761 2762 2763 2764 2765 2766 2767 | rc = formatln(&wline, &wlen, headln, view->ncols, 0, false); if (rc) goto end; werase(view->window); if (screen_is_shared(view) || view->active) | | | 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 | rc = formatln(&wline, &wlen, headln, view->ncols, 0, false); if (rc) goto end; werase(view->window); if (screen_is_shared(view) || view->active) rx |= A_REVERSE; if (s->colour) c = get_colour(&s->colours, FNC_COLOUR_COMMIT); if (c) rx |= COLOR_PAIR(c->scheme); wattron(view->window, rx); waddwstr(view->window, wline); while (wlen < view->ncols) { |
︙ | ︙ | |||
3453 3454 3455 3456 3457 3458 3459 | {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" C-e ", " ❬C-e❭ "}, {" C-y ", " ❬C-y❭ "}, | < | < < | 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 | {""}, {""}, /* 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❭ "}, |
︙ | ︙ | |||
3505 3506 3507 3508 3509 3510 3511 | {""}, {""}, {0} }; static const char *desc[] = { "", "Global", | | | 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 | {""}, {""}, {0} }; static const char *desc[] = { "", "Global", "Open in-app help", "Move selection cursor or page up one line", "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", |
︙ | ︙ | |||
3546 3547 3548 3549 3550 3551 3552 | "", "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", | < | < < | 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 | "", "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 hunk header", "Display side-by-side formatted diff", "Toggle verbosity of diff output", |
︙ | ︙ | |||
3644 3645 3646 3647 3648 3649 3650 | || (stash == 1 && ln == 2) || (stash == 2 && ln == 3)) continue; /* only show available stash keymaps */ if (keys[ln][1]) { width = MAX((fsl_size_t)width, fsl_strlen(keys[ln][cs]) + fsl_strlen(desc[ln])); } fsl_fprintf(txt, "%s%s%c", keys[ln][cs], desc[ln], | | | 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 | || (stash == 1 && ln == 2) || (stash == 2 && ln == 3)) continue; /* only show available stash keymaps */ if (keys[ln][1]) { width = MAX((fsl_size_t)width, fsl_strlen(keys[ln][cs]) + fsl_strlen(desc[ln])); } fsl_fprintf(txt, "%s%s%c", keys[ln][cs], desc[ln], keys[ln + 1] ? '\n' : 0); } ++width; rewind(txt); x0 = 4; /* column number at which to start the help window */ y0 = 2; /* line number at which to start the help window */ cury = curx = 0; |
︙ | ︙ | |||
3874 3875 3876 3877 3878 3879 3880 | 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 | | | 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 | 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, SR_ALL, "-- checkout db busy --"); break; } else if (rc) return RC(rc, "fsl_ckout_changes_scan"); |
︙ | ︙ | |||
4745 4746 4747 4748 4749 4750 4751 | 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; | | < < | 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 | 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->scx.hunk.n = 0; s->paths = paths; s->selected_entry = commit; s->first_line_onscreen = 1; s->last_line_onscreen = view->nlines; s->selected_line = 1; s->f = NULL; s->view = view; s->parent_view = parent_view; s->diff_mode = mode; |
︙ | ︙ | |||
4796 4797 4798 4799 4800 4801 4802 | } /* * 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 | | < | 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 | } /* * 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) |
︙ | ︙ | |||
4842 4843 4844 4845 4846 4847 4848 | opt = fnc_conf_getopt(FNC_DIFF_FLAGS, false); while (opt && (ch = opt[i++])) { switch (ch) { case 'C': s->colour = false; break; | < < < | 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 | 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); |
︙ | ︙ | |||
4903 4904 4905 4906 4907 4908 4909 | char *line, *st0 = NULL, *st = NULL; off_t off = 0; uint32_t idx = 0; int rc = 0; s->maxx = 0; | < | | 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 | char *line, *st0 = NULL, *st = NULL; off_t off = 0; uint32_t idx = 0; int rc = 0; s->maxx = 0; free_index(&s->index); free(s->dlines); s->dlines = fsl_malloc(sizeof(enum line_type)); if (s->dlines == NULL) return RC(FSL_RC_ERROR, "fsl_malloc"); s->ndlines = 0; free(s->line_offsets); s->line_offsets = fsl_malloc(sizeof(off_t)); |
︙ | ︙ | |||
4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 | if (!rc && s->diff_mode == COMMIT_META) rc = write_commit_meta(s); if (!rc && s->selected_entry->diff_type == FNC_DIFF_WIKI) rc = diff_non_checkin(s); if (rc) goto end; if (s->diff_mode != COMMIT_META) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = 0; } /* | > > > > > > > > > > > > > > > > > > > > > > > | 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 | if (!rc && s->diff_mode == COMMIT_META) rc = write_commit_meta(s); if (!rc && s->selected_entry->diff_type == FNC_DIFF_WIKI) rc = diff_non_checkin(s); if (rc) goto end; /* * Delay assigning diff headline labels (i.e., diff id1 id2) till now * because wiki parent commits are obtained in diff_non_checkin(). */ if (s->selected_entry->puuid) { fsl_free(s->id1); s->id1 = fsl_strdup(s->selected_entry->puuid); if (s->id1 == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else s->id1 = NULL; /* Initial commit, tag, technote, etc. */ if (s->selected_entry->uuid) { fsl_free(s->id2); s->id2 = fsl_strdup(s->selected_entry->uuid); if (s->id2 == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } else s->id2 = NULL; /* Local work tree. */ if (s->diff_mode != COMMIT_META) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = 0; } /* |
︙ | ︙ | |||
5011 5012 5013 5014 5015 5016 5017 | lineno = s->nlines + (idx ? 1 : 0); s->index.lineno[idx++] = lineno; } off += n; rc = add_line_offset(&s->line_offsets, &s->nlines, off); if (rc) goto end; | | > | | | | | | | 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 | lineno = s->nlines + (idx ? 1 : 0); s->index.lineno[idx++] = lineno; } off += n; rc = add_line_offset(&s->line_offsets, &s->nlines, off); if (rc) goto end; /* If in stash mode, save line offsets for each hunk. */ if (s->diff_mode == STASH_INTERACTIVE && s->nlines < s->ndlines - 1 && s->dlines[s->nlines] == LINE_DIFF_CHUNK) { s->scx.hunk.lineno = fsl_realloc(s->scx.hunk.lineno, (s->scx.hunk.n + 1) * sizeof(size_t)); if (s->scx.hunk.lineno == NULL) { rc = RC(FSL_RC_ERROR, "realloc"); goto end; } s->scx.hunk.lineno[s->scx.hunk.n++] = s->nlines; } } --s->nlines; /* Don't count EOF '\n' */ end: fsl_free(st0); fsl_buffer_clear(&s->buf); if (s->f && fflush(s->f) != 0 && rc == 0) |
︙ | ︙ | |||
5083 5084 5085 5086 5087 5088 5089 | /* * Write all index headers irrespective of whether the file has * any selected hunks, otherwise we end up with orphaned hunks. */ if (s->dlines[lineno] == LINE_DIFF_INDEX || s->dlines[lineno] == LINE_DIFF_META) drop = false; | | | 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 | /* * Write all index headers irrespective of whether the file has * any selected hunks, otherwise we end up with orphaned hunks. */ if (s->dlines[lineno] == LINE_DIFF_INDEX || s->dlines[lineno] == LINE_DIFF_META) drop = false; if (s->dlines[lineno++] == LINE_DIFF_CHUNK) { /* Stash diff and hunk not marked for stash? Drop it. */ if (s->stash == HUNK_STASH) drop = !BIT_CHK(s->scx.stash, idx) ? true : false; else /* ckout diff & hunk marked for stash? drop it */ drop = BIT_CHK(s->scx.stash, idx) ? true : false; |
︙ | ︙ | |||
5453 5454 5455 5456 5457 5458 5459 | fsl_deck_F_next(&d1, &fc1); } fsl_deck_F_next(&d2, &fc2); while (fc1 || fc2) { const fsl_card_F *a = NULL, *b = NULL; fsl_ckout_change_e change = FSL_CKOUT_CHANGE_NONE; | | | > > | > > > > > > > > > | 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 | fsl_deck_F_next(&d1, &fc1); } fsl_deck_F_next(&d2, &fc2); while (fc1 || fc2) { const fsl_card_F *a = NULL, *b = NULL; fsl_ckout_change_e change = FSL_CKOUT_CHANGE_NONE; bool diff = true; if (s->paths != NULL && !TAILQ_EMPTY(s->paths)) { struct fnc_pathlist_entry *pe; diff = false; TAILQ_FOREACH(pe, s->paths, entry) if (!fsl_strcmp(pe->path, fc1->name) || !fsl_strcmp(pe->path, fc2->name) || !fsl_strncmp(pe->path, fc1->name, pe->pathlen) || !fsl_strncmp(pe->path, fc2->name, pe->pathlen)) { diff = true; break; } } if (!fc1) /* File added. */ different = 1; else if (!fc2) /* File deleted. */ different = -1; else /* Same filename in both versions. */ different = fsl_strcmp(fc1->name, fc2->name); |
︙ | ︙ | |||
5509 5510 5511 5512 5513 5514 5515 | } end: fsl_deck_finalize(&d1); fsl_deck_finalize(&d2); return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 | } end: fsl_deck_finalize(&d1); fsl_deck_finalize(&d2); return rc; } /* * Diff local changes on disk in the current checkout against either a previous * commit or, if no version has been supplied, the current checkout. * buf output buffer in which diff content is appended * vid repository database record id of the version to diff against * diff_flags, context, and sbs are the same parameters as diff_file_artifact() * nb. This routine is only called with 'fnc diff [hash]'; that is, one or |
︙ | ︙ | |||
5607 5608 5609 5610 5611 5612 5613 | rc = fsl_cx_prepare(f, st, "%b", &sql); if (rc) { rc = RC(rc, "fsl_cx_prepare"); goto yield; } while ((rc = fsl_stmt_step(st)) == FSL_RC_STEP_ROW) { | | | | | | 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 | rc = fsl_cx_prepare(f, st, "%b", &sql); if (rc) { rc = RC(rc, "fsl_cx_prepare"); goto yield; } while ((rc = fsl_stmt_step(st)) == FSL_RC_STEP_ROW) { const char *path, *ogpath; int deleted, changed, added, fid, symlink; enum fsl_ckout_change_e change; bool diff = true; path = fsl_stmt_g_text(st, 0, NULL); ogpath = fsl_stmt_g_text(st, 1, NULL); deleted = fsl_stmt_g_int32(st, 2); changed = fsl_stmt_g_int32(st, 3); added = fsl_stmt_g_int32(st, 4); fid = fsl_stmt_g_int32(st, 5); |
︙ | ︙ | |||
5638 5639 5640 5641 5642 5643 5644 | change = FSL_CKOUT_CHANGE_ADDED; } else if (changed == 3) { fid = 0; change = FSL_CKOUT_CHANGE_MERGE_ADD; } else if (changed == 5) { fid = 0; change = FSL_CKOUT_CHANGE_INTEGRATE_ADD; | | | 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 | change = FSL_CKOUT_CHANGE_ADDED; } else if (changed == 3) { fid = 0; change = FSL_CKOUT_CHANGE_MERGE_ADD; } else if (changed == 5) { fid = 0; change = FSL_CKOUT_CHANGE_INTEGRATE_ADD; } else if (fsl_strcmp(ogpath, path)) change = FSL_CKOUT_CHANGE_RENAMED; else change = FSL_CKOUT_CHANGE_MOD; /* * For changed files of which this checkout is already aware, * grab their hash to make comparisons. For removed files, if |
︙ | ︙ | |||
5760 5761 5762 5763 5764 5765 5766 | * zplus file name of the file being diffed * xplus hex hash of the file named zplus * diff_flags bitwise flags to control the diff * change enum denoting the versioning change of the file */ static int write_diff_meta(struct fnc_diff_view_state *s, const char *zminus, | | | | < > > > > > < > > > > | | | | | | | | | | | | < < | | > | > | < | > | < | > > | | | | | 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 | * zplus file name of the file being diffed * xplus hex hash of the file named zplus * diff_flags bitwise flags to control the diff * change enum denoting the versioning change of the file */ static int write_diff_meta(struct fnc_diff_view_state *s, const char *zminus, fsl_uuid_str xminus, const char *zplus, fsl_uuid_str xplus, enum fsl_ckout_change_e change) { const char *index, *plus, *minus; int rc = FSL_RC_OK; index = zplus ? zplus : (zminus ? zminus : NULL_DEVICE); switch (change) { case FSL_CKOUT_CHANGE_MERGE_ADD: /* FALL THROUGH */ case FSL_CKOUT_CHANGE_INTEGRATE_ADD: /* FALL THROUGH */ case FSL_CKOUT_CHANGE_ADDED: minus = NULL_DEVICE; plus = xplus; zminus = NULL_DEVICE; break; case FSL_CKOUT_CHANGE_MISSING: /* FALL THROUGH */ case FSL_CKOUT_CHANGE_REMOVED: minus = xminus; plus = NULL_DEVICE; zplus = NULL_DEVICE; break; case FSL_CKOUT_CHANGE_RENAMED: /* FALL THROUGH */ case FSL_CKOUT_CHANGE_MOD: /* FALL THROUGH */ default: minus = xminus; plus = xplus; break; } zminus = zminus ? zminus : zplus; if FLAG_CHK(s->diff_flags, FNC_DIFF_INVERT) { const char *tmp = minus; minus = plus; plus = tmp; tmp = zminus; zminus = zplus; zplus = tmp; } if (!FLAG_CHK(s->diff_flags, FNC_DIFF_BRIEF)) { int c; enum line_type i; if (s->buf.used) { /* * There're previous files in the diff--I don't like * Git's contiguous lines between files--so add a new * line before this file's 'Index: file/path' line. */ rc = add_line_type(&s->dlines, &s->ndlines, LINE_BLANK); if (!rc) rc = fsl_buffer_append(&s->buf, "\n", 1); } for (c = 0, i = 10; !rc && i < 14; ++c) { rc = add_line_type(&s->dlines, &s->ndlines, i); i += (c > 3 || c % 2) ? 1 : 0; } if (!rc) rc = fsl_buffer_appendf(&s->buf, "Index: %s\n%.71c\n", index, '='); if (!rc) rc = fsl_buffer_appendf(&s->buf, "hash - %s\nhash + %s\n", minus, plus); if (!rc) rc = fsl_buffer_appendf(&s->buf, "--- %s\n+++ %s\n", zminus, zplus); } return rc; } /* * The diff_file_artifact() counterpart that diffs actual files on disk rather * than file artifacts in the Fossil repository's blob table. * buf output buffer in which diff output will be appended * bminus blob containing content of the versioned file being diffed against * zminus filename of bminus * xminus hex UUID containing the SHA{1,3} hash of the file named zminus * abspath absolute path to the file on disk being diffed * change enum denoting the versioning change of the file * diff_flags, context, and sbs are the same parameters as diff_file_artifact() */ static int diff_file(struct fnc_diff_view_state *s, fsl_buffer *bminus, const char *zminus, const char *zplus, fsl_uuid_str xminus, const char *abspath, enum fsl_ckout_change_e change) { fsl_cx *const f = fcli_cx(); fsl_buffer bplus = fsl_buffer_empty; fsl_buffer xplus = fsl_buffer_empty; int rc = 0; /* * If it exists, read content of abspath to diff EXCEPT for the content * of 'fossil rm FILE' files because they will either: (1) have the same * content as the versioned file's blob in bminus or (2) have changes. * As a result, the upcoming call to fsl_diff_text_to_buffer() _will_ * (1) produce an empty diff or (2) show the differences; neither are * expected behaviour because the SCM has been instructed to remove the * file; therefore, the diff should display the versioned file content * as being entirely removed. With this check, fnc now contrasts the * behaviour of fossil(1), which produces the abovementioned unexpected * output described in (1) and (2). */ |
︙ | ︙ | |||
5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 | case NULL_DEVICELEN: switch (fsl_config_get_int32(f, FSL_CONFDB_REPO, FSL_HPOLICY_AUTO, "hash-policy")) { case FSL_HPOLICY_SHA1: rc = fsl_sha1sum_buffer(&bplus, &xplus); break; case FSL_HPOLICY_AUTO: case FSL_HPOLICY_SHA3: case FSL_HPOLICY_SHA3_ONLY: rc = fsl_sha3sum_buffer(&bplus, &xplus); break; } break; default: RC(FSL_RC_SIZE_MISMATCH, "invalid artifact uuid [%s]", xminus); goto end; } if (rc) goto end; | > > < < < < < < < < < < < < | | > > > > | | | 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 | case NULL_DEVICELEN: switch (fsl_config_get_int32(f, FSL_CONFDB_REPO, FSL_HPOLICY_AUTO, "hash-policy")) { case FSL_HPOLICY_SHA1: rc = fsl_sha1sum_buffer(&bplus, &xplus); break; case FSL_HPOLICY_AUTO: /* FALL THROUGH */ case FSL_HPOLICY_SHA3: /* FALL THROUGH */ case FSL_HPOLICY_SHA3_ONLY: rc = fsl_sha3sum_buffer(&bplus, &xplus); break; } break; default: RC(FSL_RC_SIZE_MISMATCH, "invalid artifact uuid [%s]", xminus); goto end; } if (rc) goto end; if (s->buf.used) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = s->buf.used; } rc = write_diff_meta(s, zminus, xminus, zplus, fsl_buffer_str(&xplus), change); if (rc) goto end; if FLAG_CHK(s->diff_flags, FNC_DIFF_BRIEF) { rc = fsl_buffer_compare(bminus, &bplus); if (!rc) rc = fsl_buffer_appendf(&s->buf, "CHANGED -> %s\n", zminus); } else if (FLAG_CHK(s->diff_flags, FNC_DIFF_VERBOSE) || (bminus->used && bplus.used)) rc = fnc_diff_text_to_buffer(bminus, &bplus, &s->buf, &s->dlines, &s->ndlines, s->context, s->sbs, s->diff_flags); end: fsl_buffer_clear(&bplus); fsl_buffer_clear(&xplus); return rc; |
︙ | ︙ | |||
6065 6066 6067 6068 6069 6070 6071 | * change enum denoting the versioning change of the file * diff_flags bitwise flags to control the diff * context the number of context lines to surround changes * sbs number of columns in which to display each side-by-side diff */ static int diff_file_artifact(struct fnc_diff_view_state *s, fsl_id_t vid1, | | | 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 | * change enum denoting the versioning change of the file * diff_flags bitwise flags to control the diff * context the number of context lines to surround changes * sbs number of columns in which to display each side-by-side diff */ static int diff_file_artifact(struct fnc_diff_view_state *s, fsl_id_t vid1, const fsl_card_F *a, const fsl_card_F *b, enum fsl_ckout_change_e change) { fsl_cx *const f = fcli_cx(); fsl_stmt stmt = fsl_stmt_empty; fsl_buffer fbuf1 = fsl_buffer_empty; fsl_buffer fbuf2 = fsl_buffer_empty; char *zminus0 = NULL, *zplus0 = NULL; const char *zplus = NULL, *zminus = NULL; |
︙ | ︙ | |||
6112 6113 6114 6115 6116 6117 6118 | else if (rc) { rc = RC(rc, "fsl_stmt_step"); goto end; } xminus0 = fsl_rid_to_uuid(f, vid1); xminus = xminus0; fsl_stmt_finalize(&stmt); | | < < < < | 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 | else if (rc) { rc = RC(rc, "fsl_stmt_step"); goto end; } xminus0 = fsl_rid_to_uuid(f, vid1); xminus = xminus0; fsl_stmt_finalize(&stmt); fsl_content_get(f, vid1, &fbuf1); } if (b) { rc = fsl_card_F_content(f, b, &fbuf2); if (rc) goto end; zplus = b->name; xplus = b->uuid; |
︙ | ︙ | |||
6146 6147 6148 6149 6150 6151 6152 | else if (rc) { rc = RC(rc, "fsl_stmt_step"); goto end; } xplus0 = fsl_rid_to_uuid(f, vid2); xplus = xplus0; fsl_stmt_finalize(&stmt); | | < < < < < | | 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 6149 | else if (rc) { rc = RC(rc, "fsl_stmt_step"); goto end; } xplus0 = fsl_rid_to_uuid(f, vid2); xplus = xplus0; fsl_stmt_finalize(&stmt); fsl_content_get(f, vid2, &fbuf2); } if (s->buf.used) { s->index.offset = fsl_realloc(s->index.offset, (s->index.n + 1) * sizeof(off_t)); s->index.offset[s->index.n++] = s->buf.used + s->index.offset[0]; } rc = write_diff_meta(s, zminus, xminus, zplus, xplus, change); if (rc) goto end; if (FLAG_CHK(s->diff_flags, FNC_DIFF_VERBOSE) || (a && b)) rc = fnc_diff_text_to_buffer(&fbuf1, &fbuf2, &s->buf, &s->dlines, &s->ndlines, s->context, s->sbs, s->diff_flags); if (rc) RC(rc, "%s: fnc_diff_text_to_buffer\n" " -> %s [%s]\n -> %s [%s]", fsl_rc_cstr(rc), a ? a->name : NULL_DEVICE, a ? a->uuid : NULL_DEVICE, |
︙ | ︙ | |||
6187 6188 6189 6190 6191 6192 6193 | return rc; } static int show_diff(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; | | < | > > > | > > > > > > > | > > > > | > > > | | < | < < > > > > > | | | | | | > | | | < | | < > | | | < | | < | | | < | | | | | < | | | > > | 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 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 | return rc; } static int show_diff(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; char *headln, *id2, *id1 = NULL; /* Some diffs (e.g., technote, tag) have no parent hash to display. */ id1 = fsl_strdup(s->id1 ? s->id1 : "/dev/null"); if (id1 == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); /* * If diffing the work tree, we have no hash to display for it. * XXX Display "work tree" or "checkout" or "/dev/null" for clarity? */ id2 = fsl_strdup(s->id2 ? s->id2 : ""); if (id2 == NULL) { fsl_free(id1); return RC(FSL_RC_ERROR, "fsl_strdup"); } if ((headln = fsl_mprintf("diff %.40s %.40s", id1, id2)) == NULL) { fsl_free(id1); fsl_free(id2); return RC(FSL_RC_RANGE, "fsl_mprintf"); } fsl_free(id1); fsl_free(id2); return write_diff(view, headln); } static int write_diff(struct fnc_view *view, char *headln) { struct fnc_diff_view_state *s = &view->state.diff; regmatch_t *regmatch = &view->regmatch; struct fnc_colour *c = NULL; wchar_t *wcstr; char *line; size_t lidx, linesz = 0; ssize_t linelen; off_t line_offset; attr_t rx = A_BOLD; int col, wstrlen, max_lines = view->nlines; int nlines = s->nlines; int nprinted = 0, rc = FSL_RC_OK; bool selected; s->lineno = s->first_line_onscreen - 1; line_offset = s->line_offsets[s->first_line_onscreen - 1]; if (fseeko(s->f, line_offset, SEEK_SET)) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "fseeko"); werase(view->window); if (headln) { static char pct[MAX_PCT_LEN]; double percent; int ln, pctlen; ln = s->gtl ? s->gtl : s->lineno + s->selected_line; percent = 100.00 * ln / nlines; pctlen = snprintf(pct, MAX_PCT_LEN, "%.*lf%%", percent > 99.99 ? 0 : 2, percent); if (pctlen < 0 || pctlen >= MAX_PCT_LEN) return RC(fsl_errno_to_rc(errno, FSL_RC_RANGE), "snprintf"); line = fsl_mprintf("[%d/%d] %s", ln, nlines, headln); if (line == NULL) return RC(FSL_RC_RANGE, "fsl_mprintf"); rc = formatln(&wcstr, &wstrlen, line, view->ncols, 0, false); fsl_free(line); fsl_free(headln); if (rc) return rc; if (screen_is_shared(view) || view->active) rx |= A_REVERSE; wattron(view->window, rx); waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; col = wstrlen; while (col++ < view->ncols) waddch(view->window, ' '); if (wstrlen < view->ncols - pctlen) mvwaddstr(view->window, 0, view->ncols - pctlen, pct); wattroff(view->window, rx); if (--max_lines < 1) return rc; } s->eof = false; line = NULL; while (max_lines > 0 && nprinted < max_lines) { col = wstrlen = 0; linelen = getline(&line, &linesz, s->f); if (linelen == -1) { if (feof(s->f)) { s->eof = true; break; |
︙ | ︙ | |||
6287 6288 6289 6290 6291 6292 6293 | if (s->gtl) if (!gotoline(view, &s->lineno, &nprinted)) continue; rx = 0; lidx = s->lineno - 1; if ((selected = nprinted == s->selected_line - 1)) | | | | | 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 | if (s->gtl) if (!gotoline(view, &s->lineno, &nprinted)) continue; rx = 0; lidx = s->lineno - 1; if ((selected = nprinted == s->selected_line - 1)) rx = A_BOLD | A_REVERSE; if (s->showln) col = draw_lineno(view, nlines, s->lineno, rx); if (s->diff_mode == STASH_INTERACTIVE && s->scx.hunk.lineno[s->scx.hunk.idx] == lidx) rx = A_REVERSE; /* highlight current hunk to stash */ if (s->colour) c = get_colour(&s->colours, s->dlines[MIN(s->ndlines - 1, lidx)]); if (c && !(selected && s->sline == SLINE_MONO)) rx |= COLOR_PAIR(c->scheme); if (c || selected) wattron(view->window, rx); |
︙ | ︙ | |||
6395 6396 6397 6398 6399 6400 6401 | static int draw_matched_line(struct fnc_view *view, const char *line, int *col, int limit, int offset, regmatch_t *regmatch, attr_t rx) { wchar_t *wstr = NULL; int rme, rms, skip = view->pos.col, wlen; int n, rc = FSL_RC_OK; | | | 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 | static int draw_matched_line(struct fnc_view *view, const char *line, int *col, int limit, int offset, regmatch_t *regmatch, attr_t rx) { wchar_t *wstr = NULL; int rme, rms, skip = view->pos.col, wlen; int n, rc = FSL_RC_OK; attr_t hl = A_BOLD | A_REVERSE; *col = n = 0; rms = regmatch->rm_so; rme = regmatch->rm_eo; rc = formatln(&wstr, &wlen, line, limit + view->pos.col, offset, true); |
︙ | ︙ | |||
6519 6520 6521 6522 6523 6524 6525 | view->pos.col += 2; break; case KEY_LEFT: case 'h': view->pos.col -= MIN(view->pos.col, 2); break; case CTRL('p'): | < | > | > > > | > > > | | < | > | > > > | > | | 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 | view->pos.col += 2; break; case KEY_LEFT: case 'h': view->pos.col -= MIN(view->pos.col, 2); break; case CTRL('p'): if (s->selected_entry->diff_type == FNC_DIFF_WIKI || !s->index.lineno) break; if (!((size_t)s->lineno > s->index.lineno[s->index.n - 1])) { if (s->index.idx == 0) s->index.idx = s->index.n - 1; else --s->index.idx; } else s->index.idx = s->index.n - 1; s->first_line_onscreen = s->index.lineno[s->index.idx]; s->selected_line = 1; break; case CTRL('n'): if (s->selected_entry->diff_type == FNC_DIFF_WIKI || !s->index.lineno) break; if (!((size_t)s->lineno < s->index.lineno[0])) { if (++s->index.idx == s->index.n) s->index.idx = 0; } else s->index.idx = 0; s->first_line_onscreen = s->index.lineno[s->index.idx]; s->selected_line = 1; break; case CTRL('e'): if (!s->eof) { ++s->first_line_onscreen; if (s->lineno == nlines) --s->selected_line; |
︙ | ︙ | |||
6672 6673 6674 6675 6676 6677 6678 | rc = fnc_prompt_input(view, &input); s->gtl = input.ret; break; } case '#': s->showln = !s->showln; break; | | | 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 | rc = fnc_prompt_input(view, &input); s->gtl = input.ret; break; } case '#': s->showln = !s->showln; break; case 'b': { int start_col = 0; if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); branch_view = view_open(view->nlines, view->ncols, view->start_ln, start_col, FNC_VIEW_BRANCH); if (branch_view == NULL) return RC(FSL_RC_ERROR, "view_open"); |
︙ | ︙ | |||
6702 6703 6704 6705 6706 6707 6708 | *new_view = branch_view; break; } case 'P': s->patch = true; rc = create_diff(s); break; | < | < < | 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 | *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') |
︙ | ︙ | |||
6805 6806 6807 6808 6809 6810 6811 | break; } return rc; } static int | < < < < | < < < < < < < | < < | < < < < < | < < < < < | < < | < < < < | < < < < < < < < < < < < < | < < | < < < < | < < < < < < < < < < < < < < < < | | < < < < < < < < < | 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 | break; } return rc; } static int f__stash_get(bool pop) { fsl_cx *const f = fcli_cx(); fsl_db *db = fsl_needs_ckout(f); fsl_stmt q = fsl_stmt_empty; int nadded, stashid, vid, rc0, rc = FSL_RC_OK; uint32_t cc = 0; stashid = fsl_db_g_int32(db, 0, "SELECT max(stashid) FROM stash"); if (!stashid) { f_out(">> empty stash"); return rc; } vid = f->ckout.rid; rc = fsl_db_prepare(db, &q, "SELECT blob.rid, isRemoved, isExec," " isLink, origname, newname, delta FROM stashfile, blob" " WHERE stashid=%d AND blob.uuid=stashfile.hash UNION ALL" " SELECT 0, isRemoved, isExec, isLink, origname, newname, delta" |
︙ | ︙ | |||
7053 7054 7055 7056 7057 7058 7059 | fsl_buffer_clear(&a); fsl_buffer_clear(&b); fsl_buffer_clear(&out); fsl_buffer_clear(&disk); if (rc) goto clear_delta; } | > > | | | < < | < < | > | > | < > < < | | 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 | fsl_buffer_clear(&a); fsl_buffer_clear(&b); fsl_buffer_clear(&out); fsl_buffer_clear(&disk); if (rc) goto clear_delta; } clear_delta: fsl_buffer_clear(&delta); if (!rc && fsl_strcmp(ogname, name)) { fsl_file_unlink(ogpath); if (rc) rc = RC(rc, "fsl_file_unlink(%s)", ogpath); rc = rc ? rc : fsl_db_exec_multi(db, "UPDATE vfile SET pathname='%q', origname='%q'" " WHERE pathname='%q' %s AND vid=%d", name, ogname, ogname, fsl_cx_filename_collation(f), vid); } fsl_free(path); fsl_free(ogpath); if (rc) break; } if (!rc) rc = f__add_files_in_sfile(&nadded, vid); rc0 = fsl_stmt_finalize(&q); rc = rc ? rc : rc0; if (cc) f_out("\n>> merge conflict(s): %u\n", cc); if (pop) { rc = fsl_db_exec_multi(db, "DELETE FROM stash WHERE stashid=%d;" "DELETE FROM stashfile WHERE stashid=%d;", stashid, stashid); } return rc; } static int f__add_files_in_sfile(int *nadded, int vid) { fsl_cx *const f = fcli_cx(); fsl_db *db = fsl_needs_ckout(f); |
︙ | ︙ | |||
7141 7142 7143 7144 7145 7146 7147 | { *include = true; f_out("[+] %s\n", cms->filename); return FSL_RC_OK; } /* | < < | | | < | | < | | | | | | | | | | < < < < < < < < < > > > > < < < < < < | < < | | < < < | > | | < < | < < < > | | < < | | < < | < < | < > | > | 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 | { *include = true; f_out("[+] %s\n", cms->filename); return FSL_RC_OK; } /* * Get hunks selected to stash from the user and make two patch(1) files: * (1) diff of all hunks selected to stash; and (2) diff of all hunks to be * kept in the checkout. Then revert the checkout and use patch files to: * 1. apply patch of hunks selected to stash * 2. stash (and revert) checkout * 3. apply patch of hunks that were not selected to stash * This produces a ckout with only those changes that were not selected * to stash, achieving the same function as 'git add -p'. The user can * now test the code, commit, then run 'fnc stash pop' and repeat. */ static int fnc_stash(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; struct stash_cx *scx = &s->scx; struct input in; char *msg = NULL, *prompt = NULL; int rc = FSL_RC_OK; scx->stash = alloc_bitstring(scx->hunk.n); if (scx->stash == NULL) return RC(FSL_RC_ERROR, "calloc"); rc = select_hunks(view); /* get hunks to stash */ if (rc) goto end; /* Use default stash msg of "fnc stash CKOUT-HASH" if not provided. */ msg = fsl_mprintf("fnc stash %.11s", s->id2); prompt = fsl_mprintf("stash message [%s]: ", msg); in = (struct input){NULL, prompt, INPUT_ALPHA, SR_CLREOL}; rc = fnc_prompt_input(view, &in); if (in.buf[0]) { fsl_free(msg); msg = fsl_mprintf("%s", in.buf); } if (rc) { rc = RC(rc, "fnc_prompt_input"); goto end; } s->stash = HUNK_STASH; /* make patch of hunks selected to stash */ rc = create_diff(s); s->stash = HUNK_CKOUT; /* make patch of hunks to keep in ckout */ if (!rc) rc = create_diff(s); if (rc) goto end; endwin(); /* restore tty so we can report progress to stdout */ /* Clean ckout to apply patches; vfile already scanned in cmd_stash() */ rc = revert_ckout(true, false); if (rc) { rc = RC(rc, "revert_ckout"); goto end; } /* XXX With revert_ckout() finished, we can drop privileges further. */ rc = init_unveil(((const char *[]){"/usr/bin/patch", "fossil"}), ((const char *[]){"rx", "rx"}), 2, true); if (rc) goto end; scx->pcx.context = s->context; scx->pcx.report = true; /* report files with changes stashed */ rc = fnc_patch(&scx->pcx, scx->patch[0]); /* (1) apply stash patch */ scx->pcx.report = false; /* don't report changes kept in ckout */ if (!rc) { /* fnc_execp((const char *const []) */ /* {"fossil", "stash", "save", "-m", msg, (char *)NULL}, 10); */ rc = f__stash_create(msg, fcli_cx()->ckout.rid); /* (2) */ if (!rc) rc = revert_ckout(false, false); } if (!rc) rc = fnc_patch(&scx->pcx, scx->patch[1]); /* (3) ckout patch */ rc = (rc == NO_PATCH ? FSL_RC_OK : rc); end: fsl_free(scx->stash); fsl_free(scx->hunk.lineno); fsl_free(msg); fsl_free(prompt); return rc; } /* * Allocate, zero, and return an unsigned char pointer of enough bytes |
︙ | ︙ | |||
7317 7318 7319 7320 7321 7322 7323 | struct input in; size_t last, nxt = 0, nf = 0; uint32_t *nh; int hl, rc = FSL_RC_OK; enum stash_opt choice = NO_CHOICE; bool lastfile = false; | | | 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 | struct input in; size_t last, nxt = 0, nf = 0; uint32_t *nh; int hl, rc = FSL_RC_OK; enum stash_opt choice = NO_CHOICE; bool lastfile = false; hunks = &s->scx.hunk; nh = &hunks->idx; in = (struct input){NULL, NULL, INPUT_ALPHA, SR_CLREOL, "X"}; /* Iterate hunks and prompt user to stash or keep in ckout. */ while (!rc && *nh < hunks->n) { char **ans = NULL; char prompt[64]; |
︙ | ︙ | |||
9034 9035 9036 9037 9038 9039 9040 | return rc; } static int set_selected_commit(struct fnc_diff_view_state *s, struct commit_entry *entry) { | > | > > > | | 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 | return rc; } static int set_selected_commit(struct fnc_diff_view_state *s, struct commit_entry *entry) { fsl_free(s->id2); s->id2 = fsl_strdup(entry->commit->uuid); if (s->id2 == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); fsl_free(s->id1); s->id1 = entry->commit->puuid ? fsl_strdup(entry->commit->puuid) : NULL; s->selected_entry = entry->commit; return 0; } static void diff_grep_init(struct fnc_view *view) |
︙ | ︙ | |||
9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 | close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "fclose"); fsl_free(s->line_offsets); free_colours(&s->colours); s->line_offsets = NULL; s->nlines = 0; | > > > > | < | < | | 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 | close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "fclose"); fsl_free(s->id1); s->id1 = NULL; fsl_free(s->id2); s->id2 = NULL; fsl_free(s->line_offsets); free_colours(&s->colours); s->line_offsets = NULL; s->nlines = 0; free_index(&s->index); fsl_free(s->dlines); s->dlines = NULL; return rc; } static void free_index(struct index *index) { index->idx = 0; index->n = 0; fsl_free(index->lineno); index->lineno = NULL; fsl_free(index->offset); index->offset = NULL; } |
︙ | ︙ | |||
9336 9337 9338 9339 9340 9341 9342 | fcli_flag_type_arg_cb(fcli_cliflag const *v) { struct artifact_types *ft = &fnc_init.filter_types; const char *t = *((const char **)v->flagValue); /* Valid types: ci, e, f, g, t, w */ if (strlen(t) > 2 || (t[1] && (*t != 'c' || t[1] != 'i')) || (!t[1] && | | | > > > | 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 | fcli_flag_type_arg_cb(fcli_cliflag const *v) { struct artifact_types *ft = &fnc_init.filter_types; const char *t = *((const char **)v->flagValue); /* Valid types: ci, e, f, g, t, w */ if (strlen(t) > 2 || (t[1] && (*t != 'c' || t[1] != 'i')) || (!t[1] && (*t != 'e' && *t != 'f' && *t != 'g' && *t != 't' && *t != 'w'))) { fnc_init.err = RC(FSL_RC_TYPE, "invalid type: %s", t); usage(); /* NOT REACHED */ } ft->values = fsl_realloc(ft->values, (ft->nitems + 1) * sizeof(char *)); ft->values[ft->nitems++] = t; return FCLI_RC_FLAG_AGAIN; } |
︙ | ︙ | |||
9377 9378 9379 9380 9381 9382 9383 | static void sigcont_handler(int sig) { rec_sigcont = 1; } | < < < < < < < < < < < < < < < < < < | 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 | static void sigcont_handler(int sig) { rec_sigcont = 1; } __dead static void usage(void) { /* * It looks like the fsl_cx f member of the ::fcli singleton has * already been cleaned up by the time this wrapper is called from * fcli_help() after hijacking the process whenever the '--help' |
︙ | ︙ | |||
9418 9419 9420 9421 9422 9423 9424 | !isendwin() ? endwin() : 0; /* If a command was passed on the CLI, output its corresponding help. */ if (fnc_init.cmdarg) for (idx = 0; idx < nitems(fnc_init.cmd_args); ++idx) { fcli_command cmd = fnc_init.cmd_args[idx]; if (!fsl_strcmp(fnc_init.cmdarg, cmd.name) || | | | 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 | !isendwin() ? endwin() : 0; /* If a command was passed on the CLI, output its corresponding help. */ if (fnc_init.cmdarg) for (idx = 0; idx < nitems(fnc_init.cmd_args); ++idx) { fcli_command cmd = fnc_init.cmd_args[idx]; if (!fsl_strcmp(fnc_init.cmdarg, cmd.name) || fcli_cmd_aliascmp(&cmd, fnc_init.cmdarg)) { if (!fsl_strcmp(cmd.name, "stash")) { help_stash(&cmd); } else fcli_command_help(&cmd, true, true); exit(fcli_end_of_main(fnc_init.err)); } } |
︙ | ︙ | |||
9451 9452 9453 9454 9455 9456 9457 | fcli_progname(), fcli_progname()); } static void usage_diff(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, | | | | < | 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 | fcli_progname(), fcli_progname()); } 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) { |
︙ | ︙ | |||
9505 9506 9507 9508 9509 9510 9511 | fcli_progname(), fcli_progname()); } static void usage_stash(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | < > | < | > > > | < | | | | | 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 | fcli_progname(), fcli_progname()); } static void usage_stash(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s stash [get|pop | [-C|--no-colour] [-h|--help] " "[-P|--no-prototype] [-x|--context n]]\n" " e.g.: %s stash -x 10\n\n", fcli_progname(), fcli_progname()); } static void help_stash(const fcli_command *cmd) { fcli_command_help(cmd, false, true); f_out("[stash] subcommands:\n\n"); f_out(" get\n " "Apply the most recent stash changeset to the checkout.\n\n"); f_out(" pop\n Remove the most recent stash changeset, " "and apply to the checkout.\n\n"); usage_stash(); } static int cmd_stash(fcli_command const *argv) { fsl_cx *const f = fcli_cx(); struct fnc_view *view = NULL; struct fnc_commit_artifact *commit = NULL; fsl_id_t prid = -1, rid = -1; int rc = FSL_RC_OK; enum fnc_diff_type diff_type = FNC_DIFF_CKOUT; enum fnc_diff_mode diff_mode = STASH_INTERACTIVE; #ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr flock tty unveil", NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "pledge"); #endif rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; if (fcli_next_arg(false)) { const char *cmd; bool pop = false; if (fsl_strcmp((cmd = fcli_next_arg(true)), "get") && fsl_strcmp(cmd, "apply") && !(pop = !fsl_strcmp(cmd, "pop"))) return RC(FSL_RC_NOT_FOUND, "invalid stash subcommand: %s", cmd); return f__stash_get(pop); } rc = fsl_sym_to_rid(f, "current", FSL_SATYPE_CHECKIN, &prid); if (rc || prid < 0) return RC(rc, "fsl_sym_to_rid"); fsl_ckout_version_info(f, &rid, NULL); if ((rc = fsl_ckout_changes_scan(f))) return RC(rc, "fsl_ckout_changes_scan"); if (!fsl_ckout_has_changes(f)) { fsl_fprintf(stdout, "No local changes.\n"); return rc; } commit = calloc(1, sizeof(*commit)); if (commit == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); commit->prid = prid; commit->rid = rid; commit->puuid = fsl_rid_to_uuid(f, prid); commit->uuid = fsl_rid_to_uuid(f, rid); commit->type = fsl_strdup("blob"); commit->diff_type = diff_type; rc = init_curses(); if (rc) goto end; /* * XXX revert_ckout:fsl_ckout_revert()^ walks the tree from / to the |
︙ | ︙ | |||
9648 9649 9650 9651 9652 9653 9654 | view = view_open(0, 0, 0, 0, FNC_VIEW_DIFF); if (view == NULL) { rc = RC(FSL_RC_ERROR, "view_open"); goto end; } rc = open_diff_view(view, commit, NULL, NULL, diff_mode); | | < < | | < | < < < > > > | 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 | view = view_open(0, 0, 0, 0, FNC_VIEW_DIFF); if (view == NULL) { rc = RC(FSL_RC_ERROR, "view_open"); goto end; } rc = open_diff_view(view, commit, NULL, NULL, diff_mode); if (!rc) { rc = show_diff(view); if (!rc) rc = fnc_stash(view); } end: /* * We must check for changes based on file content--not mtime--else * the lib will report files as unchanged in some cases. */ fsl_vfile_changes_scan(f, f->ckout.rid, FSL_VFILE_CKSIG_HASH); if (commit) fnc_commit_artifact_close(commit); if (view) view_close(view); return rc; } static int cmd_diff(fcli_command const *argv) { fsl_cx *const f = fcli_cx(); struct fnc_view *view; struct fnc_commit_artifact *commit = NULL; 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; enum fnc_diff_mode diff_mode = DIFF_PLAIN; rc = fcli_process_flags(argv->flags); |
︙ | ︙ | |||
9716 9717 9718 9719 9720 9721 9722 | if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &rid)) { artifact2 = fcli_next_arg(true); diff_type = FNC_DIFF_COMMIT; if (!fsl_rid_is_a_checkin(f, rid)) ++blob; } | < < < < > | | > > | < > | | | | | | | < | | > | < | | > | | | < | > > > > > > > > > > > | | > > > > | > | > | | | 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 | if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &rid)) { artifact2 = fcli_next_arg(true); diff_type = FNC_DIFF_COMMIT; if (!fsl_rid_is_a_checkin(f, rid)) ++blob; } } if (fcli_error()->code == FSL_RC_NOT_FOUND) RC_RESET(rc); /* If args aren't symbols, treat as paths. */ if (blob == 2) diff_type = FNC_DIFF_BLOB; if (!artifact1 && diff_type != FNC_DIFF_BLOB) { artifact1 = "current"; rc = fsl_sym_to_rid(f, artifact1, FSL_SATYPE_CHECKIN, &prid); if (rc || prid < 0) { rc = RC(rc, "fsl_sym_to_rid"); goto end; } } if (!artifact2 && diff_type != FNC_DIFF_BLOB) { fsl_ckout_version_info(f, &rid, NULL); if ((rc = fsl_ckout_changes_scan(f))) return RC(rc, "fsl_ckout_changes_scan"); if (!fsl_strcmp(artifact1, "current") && !fsl_ckout_has_changes(f)) { fsl_fprintf(stdout, "No local changes.\n"); return rc; } } while (fcli_next_arg(false) && diff_type != FNC_DIFF_BLOB) { struct fnc_pathlist_entry *ins; char *path, *path_to_diff; rc = map_repo_path(&path0); path = path0; if (rc) { if (rc != FSL_RC_UNKNOWN_RESOURCE) { if (!fsl_strcmp(artifact1, "current") && !artifact2) { rc = RC(rc, "invalid artifact hash: %s", path); } goto end; } RC_RESET(rc); /* Path may be valid in tree of specified commit(s). */ const fsl_card_F *cf = NULL; rc = fsl_deck_load_sym(f, &d, artifact1, FSL_SATYPE_CHECKIN); if (rc) goto end; cf = fsl_deck_F_search(&d, path); if (cf == NULL) { if (!artifact2) { rc = RC(FSL_RC_UNKNOWN_RESOURCE, "'%s' not found in tree [%s]", path, artifact1); goto end; } fsl_deck_finalize(&d); rc = fsl_deck_load_sym(f, &d, artifact2, FSL_SATYPE_CHECKIN); if (rc) goto end; cf = fsl_deck_F_search(&d, path); if (cf == NULL) { rc = RC(FSL_RC_NOT_FOUND, "'%s' not found in trees [%s] [%s]", path, artifact1, artifact2); goto end; } } } else while (path[0] == '/') ++path; path_to_diff = fsl_strdup(path); if (path_to_diff == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } rc = fnc_pathlist_insert(&ins, &paths, path_to_diff, NULL); if (rc || ins == NULL /* Duplicate path. */) fsl_free(path_to_diff); if (rc) goto end; } if (diff_type != FNC_DIFF_BLOB && diff_type != FNC_DIFF_CKOUT) { q = fsl_stmt_malloc(); rc = commit_builder(&commit, rid, q); |
︙ | ︙ | |||
9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 | goto end; } rc = open_diff_view(view, commit, &paths, NULL, diff_mode); if (!rc) rc = view_loop(view); end: fsl_deck_finalize(&d); fsl_stmt_finalize(q); if (commit) fnc_commit_artifact_close(commit); TAILQ_FOREACH(pe, &paths, entry) free((char *)pe->path); fnc_pathlist_free(&paths); return rc; } | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | goto end; } rc = open_diff_view(view, commit, &paths, NULL, diff_mode); if (!rc) rc = view_loop(view); end: fsl_free(path0); fsl_deck_finalize(&d); fsl_stmt_finalize(q); if (commit) fnc_commit_artifact_close(commit); TAILQ_FOREACH(pe, &paths, entry) free((char *)pe->path); fnc_pathlist_free(&paths); return rc; } static int browse_commit_tree(struct fnc_view **new_view, int start_col, int start_ln, struct commit_entry *entry, const char *path) { struct fnc_view *tree_view; int rc = 0; |
︙ | ︙ | |||
9911 9912 9913 9914 9915 9916 9917 | fsl_id_t rid; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) goto end; | | < < < < < < < | | < > | 9711 9712 9713 9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 | fsl_id_t rid; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) goto end; rc = map_repo_path(&path); if (rc) goto end; if (fnc_init.sym) rc = fsl_sym_to_rid(f, fnc_init.sym, FSL_SATYPE_ANY, &rid); else fsl_ckout_version_info(f, &rid, NULL); if (rc) { switch (rc) { case FSL_RC_AMBIGUOUS: RC(rc, "prefix too ambiguous [%s]", fnc_init.sym); goto end; case FSL_RC_NOT_A_REPO: RC(rc, "%s tree needs a local checkout", fcli_progname()); goto end; case FSL_RC_NOT_FOUND: RC(rc, "invalid symbolic checkin name [%s]", fnc_init.sym); goto end; case FSL_RC_MISUSE: /* FALL THROUGH */ default: goto end; } } /* In 'fnc tree -R repo.db [path]' case, use the latest checkin. */ if (rid == 0) { |
︙ | ︙ | |||
10007 10008 10009 10010 10011 10012 10013 | if (rc) goto end; /* * Open the initial root level of the repository tree now. Subtrees * opened during traversal are built and destroyed on demand. */ | | | | | | 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809 9810 9811 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 | if (rc) goto end; /* * Open the initial root level of the repository tree now. Subtrees * opened during traversal are built and destroyed on demand. */ rc = tree_builder(s->repo, &s->root, "/"); if (rc) goto end; s->tree = s->root; /* * If user has supplied a path arg (i.e., fnc tree path/in/repo), or * has selected a commit from an 'fnc timeline path/in/repo' command, * walk the path and open corresponding (sub)tree objects now. */ if (!fnc_path_is_root_dir(path)) { rc = walk_tree_path(s, s->repo, &s->root, path); if (rc) goto end; } if ((s->tree_label = fsl_mprintf("checkin %s", s->commit_id)) == NULL) { rc = RC(FSL_RC_RANGE, "fsl_mprintf"); goto end; } s->first_entry_onscreen = &s->tree->entries[0]; s->selected_entry = &s->tree->entries[0]; if (s->colour) { STAILQ_INIT(&s->colours); rc = set_colours(&s->colours, FNC_VIEW_TREE); if (rc) goto end; } |
︙ | ︙ | |||
10052 10053 10054 10055 10056 10057 10058 | close_tree_view(view); return rc; } /* * Decompose the supplied path into its constituent components, then build, * open and visit each subtree segment on the way to the requested entry. | < < | > | 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 | close_tree_view(view); return rc; } /* * Decompose the supplied path into its constituent components, then build, * open and visit each subtree segment on the way to the requested entry. */ static int walk_tree_path(struct fnc_tree_view_state *s, struct fnc_repository_tree *repo, struct fnc_tree_object **root, const char *path) { struct fnc_tree_object *tree = NULL; const char *p; char *slash, *subpath = NULL; int rc = 0; /* Find each slash and open preceding directory segment as a tree. */ |
︙ | ︙ | |||
10091 10092 10093 10094 10095 10096 10097 | rc = RC(FSL_RC_NOT_FOUND, "find_tree_entry(%s)", te_name); fsl_free(te_name); break; } fsl_free(te_name); | < < < < < < < < < < | | | 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 | rc = RC(FSL_RC_NOT_FOUND, "find_tree_entry(%s)", te_name); fsl_free(te_name); break; } fsl_free(te_name); s->first_entry_onscreen = s->selected_entry = te; if (!S_ISDIR(s->selected_entry->mode)) break; /* If a file, jump to this entry. */ slash = strchr(p, '/'); if (slash) subpath = fsl_strndup(path, slash - path); else subpath = fsl_strdup(path); if (subpath == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); break; } rc = tree_builder(repo, &tree, subpath + 1 /* Leading slash */); if (rc) break; rc = visit_subtree(s, tree); if (rc) { fnc_object_tree_close(tree); break; } |
︙ | ︙ | |||
10218 10219 10220 10221 10222 10223 10224 | *tree = fsl_malloc(sizeof(**tree)); if (*tree == NULL) return RC(FSL_RC_ERROR, "fsl_malloc"); memset(*tree, 0, sizeof(**tree)); /* * Count how many elements will comprise the tree to be allocated. | | | | | > | > | | | | 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 | *tree = fsl_malloc(sizeof(**tree)); if (*tree == NULL) return RC(FSL_RC_ERROR, "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)); if ((*tree)->entries == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); /* Construct the tree to be displayed. */ for(tn = repo->head, i = 0; tn; tn = tn->next) { if ((!tn->parent_dir && fsl_strcmp(dir, "/")) || (tn->parent_dir && fsl_strcmp(dir, tn->parent_dir->path))) continue; te = &(*tree)->entries[i]; te->mode = tn->mode; te->mtime = tn->mtime; te->basename = fsl_strdup(tn->basename); if (te->basename == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); te->path = fsl_strdup(tn->path); if (te->path == NULL) return RC(FSL_RC_ERROR, "fsl_strdup"); te->uuid = fsl_strdup(tn->uuid); if (te->uuid == NULL && !S_ISDIR(te->mode)) return RC(FSL_RC_ERROR, "fsl_strdup"); te->idx = i++; } (*tree)->nentries = i; return 0; } #if 0 static void delete_tree_node(struct fnc_tree_entry **head, struct fnc_tree_entry *del) { struct fnc_tree_entry *temp = *head, *prev; |
︙ | ︙ | |||
10427 10428 10429 10430 10431 10432 10433 | * a timeline of all commits modifying path. */ static int tree_entry_path(char **path, struct fnc_parent_trees *parents, struct fnc_tree_entry *te) { struct fnc_parent_tree *pt; | | > | 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 | * a timeline of all commits modifying path. */ static int tree_entry_path(char **path, struct fnc_parent_trees *parents, struct fnc_tree_entry *te) { struct fnc_parent_tree *pt; size_t len = 2; /* Leading slash and NUL. */ int rc = 0; TAILQ_FOREACH(pt, parents, entry) len += strlen(pt->selected_entry->basename) + 1 /* slash */; if (te) len += strlen(te->basename); *path = calloc(1, len); if (path == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); (*path)[0] = '/'; /* Make it absolute from the repository root. */ pt = TAILQ_LAST(parents, fnc_parent_trees); while (pt) { const char *name = pt->selected_entry->basename; if (strlcat(*path, name, len) >= len) { rc = RC(FSL_RC_RANGE, "strlcat(%s, %s, %lu)", *path, name, len); goto end; |
︙ | ︙ | |||
10528 10529 10530 10531 10532 10533 10534 | if (screen_is_shared(view)) wattroff(view->window, A_REVERSE); fsl_free(wcstr); wcstr = NULL; if (--limit <= 0) return rc; | | | | | < < | < < > | > > | 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 | if (screen_is_shared(view)) wattroff(view->window, A_REVERSE); fsl_free(wcstr); wcstr = NULL; if (--limit <= 0) return rc; /* Write this (sub)tree's absolute repository path subheader. */ rc = formatln(&wcstr, &wstrlen, treepath, view->ncols, 0, false); if (rc) return rc; waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; if (wstrlen < view->ncols - 1) waddch(view->window, '\n'); if (--limit <= 0) return rc; waddch(view->window, '\n'); if (--limit <= 0) return rc; /* Write parent dir entry (i.e., "..") if top of the tree is in view. */ if (s->first_entry_onscreen == NULL) { te = &s->tree->entries[0]; if (s->selected == 0) { wattr_on(view->window, A_REVERSE, NULL); s->selected_entry = NULL; } waddstr(view->window, " ..\n"); if (s->selected == 0) wattr_off(view->window, A_REVERSE, NULL); ++s->ndisplayed; if (--limit <= 0) return rc; n = 1; } else { n = 0; te = s->first_entry_onscreen; } nentries = s->tree->nentries; for (idx = 0; idx < nentries; ++idx) /* Find max hash length. */ hashlen = MAX(fsl_strlen(s->tree->entries[idx].uuid), hashlen); /* Iterate and write tree nodes postfixed with path type identifier. */ for (idx = te->idx; idx < nentries; ++idx) { char *line = NULL, *idstr = NULL, *targetlnk = NULL; |
︙ | ︙ | |||
11019 11020 11021 11022 11023 11024 11025 | next = get_tree_entry(s->tree, next->idx + 1); } } return FSL_RC_OK; } | < < < < < < < < < | < < < | | | 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 10840 10841 | next = get_tree_entry(s->tree, next->idx + 1); } } return FSL_RC_OK; } static int visit_subtree(struct fnc_tree_view_state *s, struct fnc_tree_object *subtree) { struct fnc_parent_tree *parent; parent = calloc(1, sizeof(*parent)); if (parent == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); parent->tree = s->tree; parent->first_entry_onscreen = s->first_entry_onscreen; parent->selected_entry = s->selected_entry; parent->selected = s->selected; TAILQ_INSERT_HEAD(&s->parents, parent, entry); s->tree = subtree; s->selected = 0; s->first_entry_onscreen = NULL; return 0; } static int blame_tree_entry(struct fnc_view **new_view, int start_col, int start_ln, struct fnc_tree_entry *te, struct fnc_parent_trees *parents, fsl_uuid_str commit_id) { struct fnc_view *blame_view; char *path; int rc = 0; *new_view = NULL; |
︙ | ︙ | |||
11284 11285 11286 11287 11288 11289 11290 | 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) { | | | > > > | 11056 11057 11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 | 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) { if (fnc_init.unset) { fnc_init.err = RC(FSL_RC_MISSING_INFO, "-u|--unset requires <setting>"); usage(); /* NOT REACHED */ } return fnc_conf_lsopt(fnc_init.lsconf ? false : true); } setid = fnc_conf_str2enum(opt); if (!setid) return RC(FSL_RC_NOT_FOUND, "invalid setting: %s", opt); |
︙ | ︙ | |||
11425 11426 11427 11428 11429 11430 11431 | const int pairs_diff[][2] = { {LINE_DIFF_META, init_colour(FNC_COLOUR_DIFF_META)}, {LINE_DIFF_USER, init_colour(FNC_COLOUR_USER)}, {LINE_DIFF_DATE, init_colour(FNC_COLOUR_DATE)}, {LINE_DIFF_TAGS, init_colour(FNC_COLOUR_DIFF_TAGS)}, {LINE_DIFF_MINUS, init_colour(FNC_COLOUR_DIFF_MINUS)}, {LINE_DIFF_PLUS, init_colour(FNC_COLOUR_DIFF_PLUS)}, | | | 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 11210 11211 11212 11213 11214 | const int pairs_diff[][2] = { {LINE_DIFF_META, init_colour(FNC_COLOUR_DIFF_META)}, {LINE_DIFF_USER, init_colour(FNC_COLOUR_USER)}, {LINE_DIFF_DATE, init_colour(FNC_COLOUR_DATE)}, {LINE_DIFF_TAGS, init_colour(FNC_COLOUR_DIFF_TAGS)}, {LINE_DIFF_MINUS, init_colour(FNC_COLOUR_DIFF_MINUS)}, {LINE_DIFF_PLUS, init_colour(FNC_COLOUR_DIFF_PLUS)}, {LINE_DIFF_CHUNK, init_colour(FNC_COLOUR_DIFF_CHUNK)}, {LINE_DIFF_EDIT, init_colour(FNC_COLOUR_DIFF_SBS_EDIT)} }; rc = set_colour_scheme(s, pairs_diff, regexp_diff, nitems(regexp_diff)); break; } case FNC_VIEW_TREE: { |
︙ | ︙ | |||
11613 11614 11615 11616 11617 11618 11619 | return COLOR_GREEN; case FNC_COLOUR_USER: case FNC_COLOUR_DIFF_PLUS: case FNC_COLOUR_TREE_DIR: case FNC_COLOUR_BRANCH_OPEN: return COLOR_CYAN; case FNC_COLOUR_DATE: | | | 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 | return COLOR_GREEN; case FNC_COLOUR_USER: case FNC_COLOUR_DIFF_PLUS: case FNC_COLOUR_TREE_DIR: case FNC_COLOUR_BRANCH_OPEN: return COLOR_CYAN; case FNC_COLOUR_DATE: case FNC_COLOUR_DIFF_CHUNK: case FNC_COLOUR_BRANCH_PRIVATE: return COLOR_YELLOW; case FNC_COLOUR_DIFF_MINUS: case FNC_COLOUR_DIFF_TAGS: case FNC_COLOUR_TREE_LINK: case FNC_COLOUR_BRANCH_CLOSED: return COLOR_MAGENTA; |
︙ | ︙ | |||
11719 11720 11721 11722 11723 11724 11725 | static int cmd_blame(fcli_command const *argv) { fsl_cx *const f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_uuid_str commit_id = NULL; | | | 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 | static int cmd_blame(fcli_command const *argv) { fsl_cx *const f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_uuid_str commit_id = NULL; fsl_id_t tip = 0, rid = 0; long nlimit = 0; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) goto end; if (!fcli_next_arg(false)) { |
︙ | ︙ | |||
11753 11754 11755 11756 11757 11758 11759 | if (fnc_init.reverse) { if (!fnc_init.sym) { rc = RC(FSL_RC_MISSING_INFO, "%s blame --reverse requires --commit", fcli_progname()); goto end; } | | | | > | < | | | | | | | | | 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 | if (fnc_init.reverse) { if (!fnc_init.sym) { rc = RC(FSL_RC_MISSING_INFO, "%s blame --reverse requires --commit", fcli_progname()); goto end; } rc = fsl_sym_to_rid(f, "tip", FSL_SATYPE_CHECKIN, &tip); if (rc) goto end; } rc = fsl_sym_to_rid(f, fnc_init.sym, FSL_SATYPE_CHECKIN, &rid); if (rc) goto end; } else if (!fnc_init.sym) { fsl_ckout_version_info(f, &rid, NULL); if (!rid) /* -R|--repo option used */ fsl_sym_to_rid(f, "tip", FSL_SATYPE_CHECKIN, &rid); } rc = map_repo_path(&path); if (rc) { if (rc != FSL_RC_UNKNOWN_RESOURCE || !fnc_init.sym) goto end; /* Path may be valid in repository tree of specified commit. */ RC_RESET(rc); } commit_id = fsl_rid_to_uuid(f, rid); if (rc || (path[0] == '/' && path[1] == '\0')) { rc = rc ? rc : RC(FSL_RC_MISSING_INFO, "%s blame requires versioned file path", fcli_progname()); goto end; } rc = init_curses(); if (rc) goto end; rc = init_unveil(((const char *[]){REPODB, CKOUTDIR, P_tmpdir, gettzfile()}), ((const char *[]){"rw", "rwc", "rwc", "r"}), 4, true); if (rc) goto end; view = view_open(0, 0, 0, 0, FNC_VIEW_BLAME); if (view == NULL) { rc = RC(FSL_RC_ERROR, "view_open"); goto end; } rc = open_blame_view(view, path, commit_id, tip, nlimit, fnc_init.lineno); if (rc) goto end; rc = view_loop(view); end: fsl_free(path); fsl_free(commit_id); return rc; } static int open_blame_view(struct fnc_view *view, char *path, fsl_uuid_str commit_id, fsl_id_t tip, int nlimit, const char *lineno) { struct fnc_blame_view_state *s = &view->state.blame; int rc = 0; CONCAT(STAILQ, _INIT)(&s->blamed_commits); s->path = fsl_strdup(path); |
︙ | ︙ | |||
11833 11834 11835 11836 11837 11838 11839 | entry); memset(&s->blame, 0, sizeof(s->blame)); s->first_line_onscreen = 1; s->last_line_onscreen = view->nlines; s->selected_line = 1; s->blame_complete = false; s->commit_id = commit_id; | | | 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 | entry); memset(&s->blame, 0, sizeof(s->blame)); s->first_line_onscreen = 1; s->last_line_onscreen = view->nlines; s->selected_line = 1; s->blame_complete = false; s->commit_id = commit_id; s->blame.origin = tip; s->blame.nlimit = nlimit; s->spin_idx = 0; s->colour = !fnc_init.nocolour && has_colors(); s->lineno = lineno; if (s->colour) { STAILQ_INIT(&s->colours); |
︙ | ︙ | |||
11866 11867 11868 11869 11870 11871 11872 11873 11874 | struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; fsl_deck d = fsl_deck_empty; fsl_buffer buf = fsl_buffer_empty; fsl_annotate_opt *opt = NULL; const fsl_card_F *cf; char *filepath = NULL; int rc = 0; | > > > > > > > > | | | | 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 | struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; fsl_deck d = fsl_deck_empty; fsl_buffer buf = fsl_buffer_empty; fsl_annotate_opt *opt = NULL; const fsl_card_F *cf; char *filepath = NULL; char *master = NULL, *root = NULL; int rc = 0; /* * Trim prefixed '/' if path has been processed by map_repo_path(), * which only occurs when the -c option has not been passed. * XXX This slash trimming is cumbersome; we should not prefix a slash * in map_repo_path() as we only want the slash for displaying an * absolute-repository-relative path, so we should prefix it only then. */ filepath = s->path[0] != '/' ? s->path : s->path + 1; rc = fsl_deck_load_sym(f, &d, s->blamed_commit->id, FSL_SATYPE_CHECKIN); if (rc) goto end; cf = fsl_deck_F_search(&d, filepath); if (cf == NULL) { rc = RC(FSL_RC_NOT_FOUND, "'%s' not found in tree [%s]", filepath, s->blamed_commit->id); goto end; } rc = fsl_card_F_content(f, cf, &buf); if (rc) goto end; if (fsl_looks_like_binary(&buf)) { rc = RC(FSL_RC_DIFF_BINARY, "cannot blame binary file"); |
︙ | ︙ | |||
11905 11906 11907 11908 11909 11910 11911 | opt = &blame->thread_cx.blame_opt; opt->filename = fsl_strdup(filepath); fcli_fax((char *)opt->filename); rc = fsl_sym_to_rid(f, s->blamed_commit->id, FSL_SATYPE_CHECKIN, &opt->versionRid); if (rc) goto end; | | | 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 | opt = &blame->thread_cx.blame_opt; opt->filename = fsl_strdup(filepath); fcli_fax((char *)opt->filename); rc = fsl_sym_to_rid(f, s->blamed_commit->id, FSL_SATYPE_CHECKIN, &opt->versionRid); if (rc) goto end; opt->originRid = blame->origin; /* tip when -r is passed */ if (blame->nlimit < 0) opt->limitMs = abs(blame->nlimit) * 1000; else opt->limitVersions = blame->nlimit; opt->out = blame_cb; opt->outState = &blame->cb_cx; |
︙ | ︙ | |||
11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 | } blame->lines = calloc(blame->nlines, sizeof(*blame->lines)); if (blame->lines == NULL) { rc = RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); goto end; } blame->cb_cx.view = view; blame->cb_cx.lines = blame->lines; blame->cb_cx.nlines = blame->nlines; | > > > > > > > > > > > > > > > > | > > > > > > | 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 11738 11739 11740 11741 11742 11743 11744 11745 11746 11747 11748 11749 11750 11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 | } blame->lines = calloc(blame->nlines, sizeof(*blame->lines)); if (blame->lines == NULL) { rc = RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "calloc"); goto end; } master = fsl_config_get_text(f, FSL_CONFDB_REPO, "main-branch", NULL); if (master == NULL) { master = fsl_strdup("trunk"); if (master == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } } root = fsl_mprintf("root:%s", master); rc = fsl_sym_to_uuid(f, root, FSL_SATYPE_CHECKIN, &blame->cb_cx.root_commit, NULL); if (rc) { rc = RC(rc, "fsl_sym_to_uuid"); goto end; } blame->cb_cx.view = view; blame->cb_cx.lines = blame->lines; blame->cb_cx.nlines = blame->nlines; blame->cb_cx.commit_id = fsl_strdup(s->blamed_commit->id); if (blame->cb_cx.commit_id == NULL) { rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } blame->cb_cx.quit = &s->done; blame->thread_cx.path = s->path; blame->thread_cx.cb_cx = &blame->cb_cx; blame->thread_cx.complete = &s->blame_complete; blame->thread_cx.cancel_cb = cancel_blame; blame->thread_cx.cancel_cx = &s->done; s->blame_complete = false; if (s->first_line_onscreen + view->nlines - 1 > blame->nlines) { s->first_line_onscreen = 1; s->last_line_onscreen = view->nlines; s->selected_line = 1; } s->matched_line = 0; s->maxx = &blame->thread_cx.cb_cx->maxlen; end: fsl_free(master); fsl_free(root); fsl_deck_finalize(&d); fsl_buffer_clear(&buf); if (rc) stop_blame(blame); return rc; } |
︙ | ︙ | |||
12153 12154 12155 12156 12157 12158 12159 | rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } line->annotated = true; } else line->id = NULL; | | | > | > | 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 | rc = RC(FSL_RC_ERROR, "fsl_strdup"); goto end; } line->annotated = true; } else line->id = NULL; /* -r can return lines with no version, so use root check-in. */ if (opt->originRid && !line->id) { line->id = fsl_strdup(cx->root_commit); line->annotated = true; } line->lineno = step->lineNumber; cx->maxlen = MAX(step->lineLength, cx->maxlen); ++cx->nlines; end: rc = pthread_mutex_unlock(&fnc_mutex); if (rc) |
︙ | ︙ | |||
12178 12179 12180 12181 12182 12183 12184 | struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; struct fnc_blame_line *blame_line; regmatch_t *regmatch = &view->regmatch; struct fnc_colour *c = NULL; wchar_t *wcstr; char *line = NULL; | | | 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998 11999 | struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; struct fnc_blame_line *blame_line; regmatch_t *regmatch = &view->regmatch; struct fnc_colour *c = NULL; wchar_t *wcstr; char *line = NULL; fsl_uuid_str prev_id = NULL; ssize_t linelen; size_t linesz = 0; int col, width, lineno = 0, nprinted = 0; int rc = FSL_RC_OK; const int idfield = 11; /* Prefix + space. */ bool selected; |
︙ | ︙ | |||
12219 12220 12221 12222 12223 12224 12225 | if (screen_is_shared(view)) wattroff(view->window, A_REVERSE); fsl_free(wcstr); wcstr = NULL; line = fsl_mprintf("[%d/%d] %s%s %c", s->gtl ? s->gtl : MIN(blame->nlines, s->first_line_onscreen - 1 + s->selected_line), | | | 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 | if (screen_is_shared(view)) wattroff(view->window, A_REVERSE); fsl_free(wcstr); wcstr = NULL; line = fsl_mprintf("[%d/%d] %s%s %c", s->gtl ? s->gtl : MIN(blame->nlines, s->first_line_onscreen - 1 + s->selected_line), blame->nlines, s->blame_complete ? "" : "annotating... ", s->path, s->blame_complete ? ' ' : SPINNER[s->spin_idx]); if (SPINNER[++s->spin_idx] == '\0') s->spin_idx = 0; rc = formatln(&wcstr, &width, line, view->ncols, 0, false); fsl_free(line); line = NULL; if (rc) |
︙ | ︙ | |||
12255 12256 12257 12258 12259 12260 12261 | if (++lineno < s->first_line_onscreen) continue; if (s->gtl) if (!gotoline(view, &lineno, &nprinted)) continue; if ((selected = nprinted == s->selected_line - 1)) { | | | 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 12074 12075 12076 | if (++lineno < s->first_line_onscreen) continue; if (s->gtl) if (!gotoline(view, &lineno, &nprinted)) continue; if ((selected = nprinted == s->selected_line - 1)) { rx = A_BOLD | A_REVERSE; wattron(view->window, rx); } if (blame->nlines > 0) { blame_line = &blame->lines[lineno - 1]; if (blame_line->annotated && prev_id && !fsl_uuidcmp(prev_id, blame_line->id) && |
︙ | ︙ | |||
12536 12537 12538 12539 12540 12541 12542 | fsl_uuid_str pid = fsl_db_g_text(db, NULL, "SELECT uuid FROM plink, blob WHERE plink.cid=%d " "AND blob.rid=plink.pid AND plink.isprim", rid); if (pid == NULL) break; /* Check file exists in parent check-in. */ rc = fsl_deck_load_sym(f, &d, pid, FSL_SATYPE_CHECKIN); | | > > > > | > > > > > | > | < > > > > < | | < | | | > | | | < > > > > | 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 | fsl_uuid_str pid = fsl_db_g_text(db, NULL, "SELECT uuid FROM plink, blob WHERE plink.cid=%d " "AND blob.rid=plink.pid AND plink.isprim", rid); if (pid == NULL) break; /* Check file exists in parent check-in. */ rc = fsl_deck_load_sym(f, &d, pid, FSL_SATYPE_CHECKIN); if (rc) { fsl_deck_finalize(&d); fsl_free(pid); return RC(rc, "fsl_deck_load_sym"); } rc = fsl_deck_F_rewind(&d); if (rc) { fsl_deck_finalize(&d); fsl_free(pid); return RC(rc, "fsl_deck_F_rewind"); } if (fsl_deck_F_search(&d, s->path + (fnc_init.sym ? 0 : 1)) == NULL) { sitrep(view, SR_ALL ^ SR_RESET, "-- %s not in [%.12s] --", s->path + (fnc_init.sym ? 0 : 1), pid); fsl_deck_finalize(&d); fsl_free(pid); break; } rc = fnc_commit_qid_alloc(&s->blamed_commit, pid); if (rc) return rc; } else { if (!fsl_uuidcmp(id, s->blamed_commit->id)) break; rc = fnc_commit_qid_alloc(&s->blamed_commit, id); } if (rc) break; s->done = true; rc = stop_blame(&s->blame); s->done = false; if (rc) break; CONCAT(STAILQ, _INSERT_HEAD)(&s->blamed_commits, s->blamed_commit, entry); rc = run_blame(view); if (rc) break; break; } case KEY_BACKSPACE: case 'B': { struct fnc_commit_qid *first; first = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); if (!fsl_uuidcmp(first->id, s->commit_id)) break; s->done = true; rc = stop_blame(&s->blame); s->done = false; if (rc) break; CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(s->blamed_commit); s->blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); rc = run_blame(view); if (rc) break; break; } case 'T': if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); branch_view = view_open(view->nlines, view->ncols, view->start_ln, start_col, FNC_VIEW_BRANCH); |
︙ | ︙ | |||
12669 12670 12671 12672 12673 12674 12675 | break; } case KEY_RESIZE: if (s->selected_line > view->nlines - 2) { s->selected_line = MIN(s->blame.nlines, view->nlines - 2); } | < > | 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 | break; } case KEY_RESIZE: if (s->selected_line > view->nlines - 2) { s->selected_line = MIN(s->blame.nlines, view->nlines - 2); } break; default: break; } return rc; } static void |
︙ | ︙ | |||
12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 12785 12786 12787 12788 | if (blame->lines) { for (idx = 0; idx < blame->nlines; ++idx) fsl_free(blame->lines[idx].id); fsl_free(blame->lines); blame->lines = NULL; } fsl_free(blame->line_offsets); return rc; } static int cancel_blame(void *state) | > > > > | 12597 12598 12599 12600 12601 12602 12603 12604 12605 12606 12607 12608 12609 12610 12611 12612 12613 12614 | if (blame->lines) { for (idx = 0; idx < blame->nlines; ++idx) fsl_free(blame->lines[idx].id); fsl_free(blame->lines); blame->lines = NULL; } fsl_free(blame->cb_cx.root_commit); blame->cb_cx.root_commit = NULL; fsl_free(blame->cb_cx.commit_id); blame->cb_cx.commit_id = NULL; fsl_free(blame->line_offsets); return rc; } static int cancel_blame(void *state) |
︙ | ︙ | |||
13151 13152 13153 13154 13155 13156 13157 | { 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; | | | | 12977 12978 12979 12980 12981 12982 12983 12984 12985 12986 12987 12988 12989 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 | { 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, "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) { |
︙ | ︙ | |||
13407 13408 13409 13410 13411 13412 13413 | if (rid < 0) return RC(rc, "fsl_uuid_to_rid"); tree_view = view_open(0, 0, 0, start_col, FNC_VIEW_TREE); if (tree_view == NULL) return RC(FSL_RC_ERROR, "view_open"); | | | 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 | if (rid < 0) return RC(rc, "fsl_uuid_to_rid"); tree_view = view_open(0, 0, 0, start_col, FNC_VIEW_TREE); if (tree_view == NULL) return RC(FSL_RC_ERROR, "view_open"); rc = open_tree_view(tree_view, "/", rid); if (!rc) *new_view = tree_view; return rc; } static void branch_scroll_up(struct fnc_branch_view_state *s, int maxscroll) |
︙ | ︙ | |||
13689 13690 13691 13692 13693 13694 13695 | free(pe); } } static void fnc_show_version(void) { | | < | 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 | free(pe); } } static void fnc_show_version(void) { printf("%s %s", fcli_progname(), PRINT_VERSION); } static int strtonumcheck(long *ret, const char *nstr, const int min, const int max) { const char *ptr; long n; |
︙ | ︙ | |||
13767 13768 13769 13770 13771 13772 13773 | return rc == ERR ? RC(FSL_RC_ERROR, "wgetnstr") : FSL_RC_OK; } static int PRINTFV(3, 4) sitrep(struct fnc_view *view, int flags, const char *msg, ...) { | | | | | | | | | | | | | | | | | | | | | 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 | return rc == ERR ? RC(FSL_RC_ERROR, "wgetnstr") : FSL_RC_OK; } static int PRINTFV(3, 4) sitrep(struct fnc_view *view, int flags, const char *msg, ...) { va_list args; va_start(args, msg); /* vw_printw(view->window, msg, args); */ wattr_on(view->window, A_BOLD, NULL); wmove(view->window, view->nlines - 1, 0); vw_printw(view->window, msg, args); if (FLAG_CHK(flags, SR_CLREOL)) wclrtoeol(view->window); wattr_off(view->window, A_BOLD, NULL); va_end(args); if (FLAG_CHK(flags, SR_UPDATE)) { update_panels(); doupdate(); } if (FLAG_CHK(flags, SR_RESET)) fcli_err_reset(); if (FLAG_CHK(flags, SR_SLEEP)) sleep(1); return FSL_RC_OK; } /* * Attempt to parse string d, which must resemble either an ISO8601 formatted * date (e.g., 2021-10-10, 2020-01-01T10:10:10), disgregarding any trailing * garbage or space characters such that "2021-10-10x" or "2020-01-01 10:10:10" * will pass, or an _unambiguous_ DD/MM/YYYY or MM/DD/YYYY formatted date. Upon |
︙ | ︙ | |||
13934 13935 13936 13937 13938 13939 13940 | static int init_unveil(const char **paths, const char **perms, int n, bool disable) { #ifdef __OpenBSD__ int i; for (i = 0; i < n; ++i) { | | | 13759 13760 13761 13762 13763 13764 13765 13766 13767 13768 13769 13770 13771 13772 13773 | static int init_unveil(const char **paths, const char **perms, int n, bool disable) { #ifdef __OpenBSD__ int i; for (i = 0; i < n; ++i) { if (unveil(paths[i], perms[i]) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), "unveil(%s, \"%s\")", paths[i], perms[i]); } if (disable) if (unveil(NULL, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ACCESS), |
︙ | ︙ |