Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.12 To 0.13
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 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 | **fnc 0.13** 2022-11-26 [[history][0.13a] / [diff][0.13b]] - improve reporting of invalid diff command arguments (reported by Dan Shearer) - accept libfossil global `-V|--verbose` option for all commands - plug memleak when using the `p` keymap in blame view - fix `fnc blame -r` and make it behave like `fossil blame -o` for familiarity - improve `fnc timeline <path>` lookup of repository paths not in the work tree - make tree navigation more intuitive when opening repository subtrees - implement `[` & `]` keymaps to navigate to the previous/next hunk in the diff - allow `fnc stash get` to take an optional `<stash_id>` argument - document fnc(1) missing `W` diff view keymap to ignore end-of-line whitespace - document optional `<stash_id>` argument of `fnc stash pop [stash_id]` - fix typo producing gcc 12.1.0 compiler warning (comparison always true) - improve scope matching for function prototype in diff hunk headers - ifdef out `A_BOLD` attr on OpenBSD to fix diff view line highlight in xterm(1) - display checkout hash and build date with `fnc -v` like fossil(1) - miscellaneous diff API implementation improvements - protect against out-of-bound reads now line_type array is optionally populated - add `-b|--brief` option to `fnc diff` and `b` diff view keymap - general fnc(1) manual page markup, syntax, and documentation improvements - rename FNC_DIFF_COLOUR_CHUNK to FNC_DIFF_COLOUR_HUNK to match internal naming (*breaking change: existing FNC_DIFF_COLOUR_CHUNK will be silently ignored*) - ensure diff parser does not internally mark modified files as renamed - improve const correctness with fsl_uuid_cstr where possible - fix contrived empty diff case when work tree root is passed to `fnc diff` - improve path handling of files on disk vs. files in the repository database - produce less verbose output on error by not presumptuously dumping help - zap unnecessary allocation and minor refactor in the path parser - don't display empty diff in contrived modified checkout edge case - catch missed fsl_content_get() fsl_unlink_file() & fsl_mprintf() return codes - miscellaneous style(9) fixes - fix missed format specifier argument in blame path error message - ensure `fnc stash (get|pop)` doesn't report updated files as renames - use "(checkout)" label in diff header when diffing or stashing the work tree - internal stash refactor in preparation to move code into libfossil - catched missed SIGINT and SIGTERM signals - use CHAR_BIT for bitstring manipulation macros - fix assumption of invalid command argument as a path to pass to `fnc timeline` - merge upstream libfossil with various changes **fnc 0.12** 2022-05-09 [[history][0.12a] / [diff][0.12b]] - 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 [[history][0.11a] / [diff][0.11b]] - 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) |
︙ | ︙ | |||
26 27 28 29 30 31 32 | - 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() | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | - 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 [[history][0.10a] / [diff][0.10b]] - 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 |
︙ | ︙ | |||
51 52 53 54 55 56 57 | - 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 | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | - 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 [[history][0.9a] / [diff][0.9b]] - 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 |
︙ | ︙ | |||
75 76 77 78 79 80 81 | - 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 | | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | - 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 [[history][0.8a] / [diff][0.8b]] - 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 [[history][0.7a] / [diff][0.7b]] - 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 |
︙ | ︙ | |||
123 124 125 126 127 128 129 | - 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 | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | - 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 [[history][0.6a] / [diff][0.6b]] - 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 |
︙ | ︙ | |||
148 149 150 151 152 153 154 | - 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 | | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | - 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 [[history][0.5a] / [diff][0.5b]] - 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 [[history][0.4a] / [diff][0.4b]] - 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 |
︙ | ︙ | |||
187 188 189 190 191 192 193 | - 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) | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | - 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 [[history][0.3a] / [diff][0.3b]] - 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' |
︙ | ︙ | |||
211 212 213 214 215 216 217 | - 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 | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | - 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 [[history][0.2a] / [diff][0.2b]] - 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 |
︙ | ︙ | |||
233 234 235 236 237 238 239 | - 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 | | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | - 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 [[history][0.1a] / [diff][0.1b]] - 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 [0.13a]: https://fnc.bsdbox.org/timeline?p=0.13&bt=0.12 [0.13b]: https://fnc.bsdbox.org/vdiff?from=0.12&to=0.13 [0.12a]: https://fnc.bsdbox.org/timeline?p=0.12&bt=0.11 [0.12b]: https://fnc.bsdbox.org/vdiff?from=0.11&to=0.12 [0.11a]: https://fnc.bsdbox.org/timeline?p=0.11&bt=0.10 [0.11b]: https://fnc.bsdbox.org/vdiff?from=0.10&to=0.11 [0.10a]: https://fnc.bsdbox.org/timeline?p=0.10&bt=0.9 [0.10b]: https://fnc.bsdbox.org/vdiff?from=0.9&to=0.10 [0.9a]: https://fnc.bsdbox.org/timeline?p=0.9&bt=0.8 [0.9b]: https://fnc.bsdbox.org/vdiff?from=0.8&to=0.9 [0.8a]: https://fnc.bsdbox.org/timeline?p=0.8&bt=0.7 [0.8b]: https://fnc.bsdbox.org/vdiff?from=0.7&to=0.8 [0.7a]: https://fnc.bsdbox.org/timeline?p=0.7&bt=0.6 [0.7b]: https://fnc.bsdbox.org/vdiff?from=0.6&to=0.7 [0.6a]: https://fnc.bsdbox.org/timeline?p=0.6&bt=0.5 [0.6b]: https://fnc.bsdbox.org/vdiff?from=0.5&to=0.6 [0.5a]: https://fnc.bsdbox.org/timeline?p=0.5&bt=0.4 [0.5b]: https://fnc.bsdbox.org/vdiff?from=0.4&to=0.5 [0.4a]: https://fnc.bsdbox.org/timeline?p=0.4&bt=0.3 [0.4b]: https://fnc.bsdbox.org/vdiff?from=0.3&to=0.4 [0.3a]: https://fnc.bsdbox.org/timeline?p=0.3&bt=0.2 [0.3b]: https://fnc.bsdbox.org/vdiff?from=689182448e&to=0.3 [0.2a]: https://fnc.bsdbox.org/timeline?p=0.2&bt=96e142ed8e [0.2b]: https://fnc.bsdbox.org/vdiff?from=96e142ed8e&to=689182448e [0.1a]: https://fnc.bsdbox.org/timeline?p=689182448e&bt=6eaea2465f [0.1b]: https://fnc.bsdbox.org/vdiff?from=6eaea2465f&to=96e142ed8e |
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 142 143 144 145 146 147 148 149 | # README # fnc: an interactive text-based user interface for [Fossil] `fnc` uses [ncurses] and [libfossil] to create a [`fossil ui`][fui] experience in the terminal, and parse local changes at the hunk level to prepare atomic commits. Tested and confirmed to run on the following amd64 systems (additional platforms noted inline): 1. OpenBSD 6.8-, 6.9-, 7.0-, 7.1-, and 7.2-{current,release} 2. macOS Catalina 10.15.7, Big Sur 11.5.2, and Ventura 13.0.1 3. Linux Mint 20.2 (32- and 64-bit ARM) 4. Ubuntu 18.04, 21.04, 21.10, and 22.04 running Linux 5.1{1,3} (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`, `branch`, and `stash` 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` * **NixOS** - `nix-env -iA nixos.fnc` * **Alpine Linux** - `apk add fnc` n.b. Ensure _testing_ repository is enabled (see <https://wiki.alpinelinux.org/wiki/Repositories#Testing>) Check [repology] to find if a package is provided for your operating system. If no package exists, [download] 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]. # Doc Commands available in **fnc**: 1. [**stash**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash) - interactively select hunks to stash from the diff of local changes on disk 2. [**timeline**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#timeline) - hyperlinked chronological commit history of the repository 3. [**diff**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#diff) - diff of all changes between commits or blobs 4. [**blame**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#blame) - annotated file displaying commit attribution history of each line 5. [**tree**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#tree) - navigable file hierarchy of the repository tree 6. [**branch**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#branch) - hyperlinked list of all public and private branches 7. [**config**](https://fnc.bsdbox.org/uv/doc/fnc.1.html#config) - configure or view fnc settings See `fnc --help` for a quick reference, and the [fnc(1)] manual page for more comprehensive documentation. Runtime 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][demoimg]][demo] # Why `fnc` is heavily inspired by [`tog`], which I missed when I left [Got] 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], the [forum], or by creating a new [ticket]. 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]. While `diff -up` patches (or `fnc` diffs saved with the [`P` keymap]) 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)]. 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             # Trivia **fnc** [fɪŋk] *noun* (n.) 1. an interactive ncurses browser for [Fossil] repositories *verb* (v.) 2. to inform etymology From the German word *Fink*, meaning "finch", a type of bird. [demoimg]: https://fnc.bsdbox.org/uv/img/fnc-timeline-fullscreen.png [demo]: https://itac.bsdbox.org/fnc-demo.mp4 [download]: https://fnc.bsdbox.org/uv/download.html [email]: mailto:fnc@bsdbox.org [fnc(1)]: https://fnc.bsdbox.org/uv/doc/fnc.1.html [forum]: https://fnc.bsdbox.org/forum [Fossil]: https://fossil-scm.org [fui]: https://fossil-scm.org/home/help?cmd=ui [Got]: https://gameoftrees.org [libfossil]: https://fossil.wanderinghorse.net/r/libfossil [mailing list]: https://itac.bsdbox.org/listinfo/fnc [ncurses]: https://invisible-island.net/ncurses/ncurses.html [repology]: https://repology.org/project/fnc/versions [style(9)]: https://man.openbsd.org/style.9 [ticket]: https://fnc.bsdbox.org/ticket [`tog`]: https://gameoftrees.org/tog.1.html [`P` keymap]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#P~3 |
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 17 18 | # # FNC Common Build # # CONFIGURATION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man VERSION ?= 0.13 HASH != cut -f 1 manifest.uuid DATE != sed '2q;d' manifest | cut -d ' ' -f 2 | tr T ' ' # FLAGS NEEDED TO BUILD SQLITE3 SQLITE_CFLAGS = ${CFLAGS} -Wall -Werror -Wno-sign-compare -pedantic -std=c99 \ -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ |
︙ | ︙ | |||
38 39 40 41 42 43 44 | 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 \ | | > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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} -DHASH=${HASH} \ -DDATE="${DATE}" FNC_LDFLAGS = ${LDFLAGS} -lm -lutil -lz -lpthread -fPIC all: bin bin: lib/sqlite3.o lib/libfossil.o src/fnc.o src/fnc |
︙ | ︙ | |||
74 75 76 77 78 79 80 | uninstall: rm -f ${PREFIX}/bin/fnc ${PREFIX}${MANDIR}/man1/fnc.1 clean: rm -f lib/*.o src/*.o src/fnc release: clean | > > | > | 77 78 79 80 81 82 83 84 85 86 87 88 89 | uninstall: rm -f ${PREFIX}/bin/fnc ${PREFIX}${MANDIR}/man1/fnc.1 clean: rm -f lib/*.o src/*.o src/fnc release: clean mkdir /tmp/fnc-${VERSION} pax -rw * /tmp/fnc-${VERSION} tar czvf ../fnc-${VERSION}.tgz -C /tmp fnc-${VERSION} rm -rf /tmp/fnc-${VERSION} .PHONY: clean release |
Changes to include/diff.h.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 | * 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, | > > > | | | | | | | | | | | | < | > | < < < < < | < > | | | < < < < | < > > | < < < < < < < > | < < < > > > > > > > > | < > | | | > > | > | < | | < < < > | | | > > | < < < < < < < | < > | | | | | | | < > | | | < < < < < < < < < | | | 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 | * 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" /* * Flags set by callers of the below diff APIs to determine diff output. */ enum fnc_diff_flag { FNC_DIFF_IGNORE_EOLWS = 0x01, FNC_DIFF_IGNORE_ALLWS = 0x03, FNC_DIFF_SIDEBYSIDE = 1 << 2, /* output side-by-side diff */ FNC_DIFF_VERBOSE = 1 << 3, /* show added/rm'd file content */ FNC_DIFF_BRIEF = 1 << 4, FNC_DIFF_HTML = 1 << 5, FNC_DIFF_LINENO = 1 << 6, /* output diff with line numbers */ 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 /* show func sig in hunk header */ #define FNC_DIFF_CONTEXT_EX (((uint64_t)0x04) << 32) /* Allow 0 context */ #define FNC_DIFF_CONTEXT_MASK ((uint64_t)0x0000ffff) /* Default context */ #define FNC_DIFF_WIDTH_MASK ((uint64_t)0x00ff0000) /* SBS column width */ }; /* * Compute the diff of changes to convert the file in fsl_buffer parameter 1 * to the file in fsl_buffer parameter 2 and save the result to the provided * output fsl_buffer in parameter 3. * * A unified diff is output by default. This, along with other diff options * (detailed in the above fnc_diff_flag enum), can be changed by setting the * corresponding flags passed in int parameter 8. * * If a unified diff, parameter 7 is ignored, and the number of context lines * is specified in short parameter 6. Negative values fallback to default. If * a side-by-side diff, parameter 6 is ignored, and the column width of each * side is specified in short parameter 7; only values larger than the longest * line are honoured, otherwise the column width of each side will * automatically grow to accommodate the longest line in the diff. * If not NULL, the enum array pointer in paramater 4 and uint32_t pointer in * parameter 5 will be populated with each line_type and the total number of * lines in the diff, respectively. Both pointers and the output buffer can * be prepopulated and must be disposed of by the caller. */ int fnc_diff_text_to_buffer(const fsl_buffer *, const fsl_buffer *, fsl_buffer *, enum line_type **, uint32_t *, short, short, int); /* * Compute the diff of changes to convert the file in fsl_buffer parameter 1 * to the file in fsl_buffer parameter 2 and invoke the fsl_output_f callback * in parameter 3 for each computed line. The callback receives the provided * void parameter 4 as its output state and a char pointer of the diffed line * or diff metadata (e.g., hunk header, index). Remaining parameters are the * same as the above fnc_diff_text_to_buffer() routine. */ int fnc_diff_text(const fsl_buffer *, const fsl_buffer *, fsl_output_f, void *, enum line_type **, uint32_t *, short, short, int); /* * Compute and save to the int array pointer in parameter 4 the array of * copy/delete/insert triples that describes the sequence of changes to convert * the file in fsl_buffer parameter 1 to the file in fsl_buffer parameter 2. * Diff format and related options are set via flags passed in int parameter 3. */ int fnc_diff_text_raw(const fsl_buffer *, const fsl_buffer *, int, int **); /* * Return the number of columns required to draw to the screen the char pointer * in parameter 1 by expanding any tabs and accounting for unicode continuation * bytes. Short parameter 2 may either be the byte size of the string or * a negative value. */ unsigned short etcount(const char *, unsigned short); /* * Save the line_type specified in parameter 3 to the nth index denoted by the * uint32_t pointer in parameter 2 of the line_type array in parameter 1. The * uint32_t value pointed to by the 2nd parameter will be incremented. */ 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_HUNK), \ _(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_HUNK), \ _(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 "46008704a620cc770bce96cc4313b0bf96498390" #define FSL_LIB_VERSION_TIMESTAMP "2022-11-24 13:51:38.504 UTC" #define FSL_LIB_CONFIG_TIME "2022-11-24 14:03 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 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | #include "libfossil.h" /* start of file ./src/xdirent.h */ /** Origin: https://gist.github.com/isakbosman/758eb668938806aabb04830736f4ac41 Modified only very slightly for use in the libfossil project: a couple of #if's were added to allow us to include this file without having to check which platform we're building on. In non-Windows builds it uses the corresponding POSIX APIs. */ /* * dirent.h - dirent API for Microsoft Visual Studio * * Copyright (C) 2006-2012 Toni Ronkko * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * ``Software''), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * $Id: dirent.h,v 1.20 2014/03/19 17:52:23 tronkko Exp $ */ #ifndef DIRENT_H #define DIRENT_H #if FSL_PLATFORM_IS_WINDOWS /* * Define architecture flags so we don't need to include windows.h. * Avoiding windows.h makes it simpler to use windows sockets in conjunction * with dirent.h. */ #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) # define _X86_ #endif #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_AMD64) #define _AMD64_ #endif #include <stdio.h> #include <stdarg.h> #include <windef.h> #include <winbase.h> #include <wchar.h> #include <string.h> #include <stdlib.h> #include <malloc.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat() */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT /* File type mask */ #endif #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR /* Directory */ #endif #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR /* Character device */ #endif #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO /* Pipe */ #endif #if !defined(S_IFREG) # define S_IFREG _S_IFREG /* Regular file */ #endif #if !defined(S_IREAD) # define S_IREAD _S_IREAD /* Read permission */ #endif #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE /* Write permission */ #endif #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC /* Execute permission */ #endif #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO /* Pipe */ #endif #if !defined(S_IFBLK) # define S_IFBLK 0 /* Block device */ #endif #if !defined(S_IFLNK) # define S_IFLNK 0 /* Link */ #endif #if !defined(S_IFSOCK) # define S_IFSOCK 0 /* Socket */ #endif #if defined(_MSC_VER) # define S_IRUSR S_IREAD /* Read user */ # define S_IWUSR S_IWRITE /* Write user */ # define S_IXUSR 0 /* Execute user */ # define S_IRGRP 0 /* Read group */ # define S_IWGRP 0 /* Write group */ # define S_IXGRP 0 /* Execute group */ # define S_IROTH 0 /* Read others */ # define S_IWOTH 0 /* Write others */ # define S_IXOTH 0 /* Execute others */ #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK #define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ wchar_t d_name[PATH_MAX]; /* File name */ }; typedef struct _wdirent _wdirent; struct _WDIR { struct _wdirent ent; /* Current directory entry */ WIN32_FIND_DATAW data; /* Private file data */ int cached; /* True if data is valid */ HANDLE handle; /* Win32 search handle */ wchar_t *patt; /* Initial directory name */ }; typedef struct _WDIR _WDIR; static _WDIR *_wopendir (const wchar_t *dirname); static struct _wdirent *_wreaddir (_WDIR *dirp); static int _wclosedir (_WDIR *dirp); static void _wrewinddir (_WDIR* dirp); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Multi-byte character versions */ struct dirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ char d_name[PATH_MAX]; /* File name */ }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; static DIR *opendir (const char *dirname); static struct dirent *readdir (DIR *dirp); static int closedir (DIR *dirp); static void rewinddir (DIR* dirp); /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno (int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator */ n = GetFullPathNameW (dirname, 0, NULL, NULL); /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). */ n = GetFullPathNameW (dirname, n, dirp->patt, NULL); if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/; break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first (dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno (ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno (ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. The directory entry is returned in dirent * structure in the d_name field. Individual directory entries returned by * this function include regular files, sub-directories, pseudo-directories * "." and ".." as well as volume labels, hidden files and system files. */ static struct _wdirent* _wreaddir( _WDIR *dirp) { WIN32_FIND_DATAW *datap; struct _wdirent *entp; /* Read next directory entry */ datap = dirent_next (dirp); if (datap) { size_t n; DWORD attr; /* Pointer to directory entry to return */ entp = &dirp->ent; /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } dirp->ent.d_name[n] = 0; /* Length of file name excluding zero terminator */ entp->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct _wdirent); } else { /* Last directory entry read */ entp = NULL; } return entp; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir( _WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free (dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free (dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir( _WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } /* Open new search handle */ dirent_first (dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileW (dirp->patt, &dirp->data); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* Get next directory entry (internal) */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occured */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir( const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * When working with text consoles, please note that file names returned by * readdir() are represented in the default ANSI code page while any output to * console is typically formatted on another code page. Thus, non-ASCII * characters in file names will not usually display correctly on console. The * problem can be fixed in two ways: (1) change the character set of console * to 1252 using chcp utility and use Lucida Console font, or (2) use * _cprintf function when writing to console. The _cprinf() will re-encode * ANSI strings to the console code page so many non-ASCII characters will * display correcly. */ static struct dirent* readdir( DIR *dirp) { WIN32_FIND_DATAW *datap; struct dirent *entp; /* Read next directory entry */ datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cAlternateFileName, PATH_MAX); } if (!error) { DWORD attr; /* Initialize directory entry for return */ entp = &dirp->ent; /* Length of file name excluding zero terminator */ entp->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an errornous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entp = &dirp->ent; entp->d_name[0] = '?'; entp->d_name[1] = '\0'; entp->d_namlen = 1; entp->d_type = DT_UNKNOWN; entp->d_ino = 0; entp->d_reclen = 0; } } else { /* No more directory entries */ entp = NULL; } return entp; } /* * Close directory stream. */ static int closedir( DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir (dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free (dirp); } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir (dirp->wdirp); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to wide-character string (or count characters) */ n = mbstowcs (wcstr, mbstr, sizeInWords); if (!wcstr || n < count) { /* Zero-terminate output buffer */ if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } wcstr[n] = 0; } /* Length of resuting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Could not convert string */ error = 1; } #endif return error; } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, /* max size of mbstr */ const wchar_t *wcstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to multi-byte string (or count the number of bytes needed) */ n = wcstombs (mbstr, wcstr, sizeInBytes); if (!mbstr || n < count) { /* Zero-terminate output buffer */ if (mbstr && sizeInBytes) { if (n >= sizeInBytes) { n = sizeInBytes - 1; } mbstr[n] = '\0'; } /* Lenght of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Cannot convert string */ error = 1; } #endif return error; } /* Set errno variable */ static void dirent_set_errno( int error) { #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 and later */ _set_errno (error); #else /* Non-Microsoft compiler or older Microsoft compiler */ errno = error; #endif } #ifdef __cplusplus } #endif #else /* !FSL_PLATFORM_IS_WINDOWS */ #include <sys/types.h> #include <dirent.h> #endif /* !FSL_PLATFORM_IS_WINDOWS */ #endif /*DIRENT_H*/ /* end of file ./src/xdirent.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 |
︙ | ︙ | |||
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; | > > | 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 | 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_rename_opt fsl_ckout_rename_opt_empty = fsl_ckout_rename_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; |
︙ | ︙ | |||
129 130 131 132 133 134 135 | }else{ /* realloc() */ return FLCA.f(FLCA.state, mem, n); } #undef FLCA } | | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | }else{ /* realloc() */ return FLCA.f(FLCA.state, mem, n); } #undef FLCA } void * fsl_realloc_f_stdalloc(void * state __unused, void * mem, fsl_size_t n){ if(!mem){ return malloc(n); }else if(!n){ free(mem); return NULL; }else{ return realloc(mem, n); |
︙ | ︙ | |||
166 167 168 169 170 171 172 | 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; | | < | | | < < < < < < < < | > | > > > > | 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 | 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; fsl_buffer_reuse(&err->msg); } int fsl_error_copy( fsl_error const * const src, fsl_error * const dest ){ if(src==dest) return FSL_RC_MISUSE; else { int rc = 0; fsl_buffer_reuse(&dest->msg); 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 ){ fsl_buffer_reuse(&err->msg); if(code){ int rc = 0; err->code = code; if(FSL_RC_OOM!=code){ if(fmt) rc = fsl_buffer_appendfv(&err->msg, fmt, args); else rc = fsl_buffer_appendf(&err->msg, "fsl_rc_e #%d: %s", code, fsl_rc_cstr(code)); if(rc) err->code = rc; } return rc ? rc : code; }else{ /* clear error state */ err->code = 0; return 0; } } int fsl_error_set( fsl_error * const err, int code, char const * fmt, ... ){ int rc; va_list args; va_start(args,fmt); |
︙ | ︙ | |||
236 237 238 239 240 241 242 | : NULL; if(len) *len = err->msg.used; return err->code; } char const * fsl_rc_cstr(int rc){ | | | | < < > > | 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 | : NULL; if(len) *len = err->msg.used; return err->code; } char const * fsl_rc_cstr(int rc){ switch((fsl_rc_e)rc){ /* we cast ^^^^ so that gcc will warn if the switch() below is missing any fsl_rc_e entries. */; #define STR(T) case FSL_RC_##T: return "FSL_RC_" #T STR(ACCESS); STR(ALREADY_EXISTS); STR(AMBIGUOUS); STR(BREAK); STR(SYNTAX); STR(CANNOT_HAPPEN); 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(LOCKED); STR(MISSING_INFO); STR(MISUSE); STR(NOOP); STR(NOT_A_CKOUT); STR(NOT_A_REPO); STR(NOT_FOUND); STR(NYI); |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 | STR(SIZE_MISMATCH); STR(STEP_DONE); STR(STEP_ERROR); STR(STEP_ROW); STR(TYPE); STR(UNKNOWN_RESOURCE); STR(UNSUPPORTED); #undef STR } | > | | 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | STR(SIZE_MISMATCH); STR(STEP_DONE); STR(STEP_ERROR); STR(STEP_ROW); STR(TYPE); STR(UNKNOWN_RESOURCE); STR(UNSUPPORTED); STR(WOULD_FORK); #undef STR } return NULL; } char const * fsl_library_version(){ return FSL_LIBRARY_VERSION; } bool fsl_library_version_matches(char const * yourLibVersion){ |
︙ | ︙ | |||
399 400 401 402 403 404 405 | 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; | | < < | | | | | | < > > > | 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 | 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){ while((*(dst+offset) = *(src+offset))!='\0'){ if(++offset == dstsz){ --offset; break; } } } *(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); if( offset>=dstsz-1 ) return dstlen+srclen; while((*(dst+offset++) = *(src+idx++))!='\0'){ if(offset==dstsz-1){ break; } } *(dst+offset)='\0'; |
︙ | ︙ | |||
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 */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1876 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 | } } } void fsl_randomness(unsigned int n, void *tgt){ sqlite3_randomness((int)n, tgt); } int fsl_system(const char *zOrigCmd){ int rc; /* The following was ported over from fossil(1). As of this writing, the Windows version is completely untested even for compilability. */ #if defined(_WIN32) /* On windows, we have to put double-quotes around the entire command. ** Who knows why - this is just the way windows works. */ char *zNewCmd = fsl_mprintf("\"%s\"", zOrigCmd); if(!zNewCmd){FSL__WARN_OOM; return FSL_RC_OOM;} wchar_t *zUnicode = (wchar_t *)fsl_utf8_to_unicode(zNewCmd); if(!zUnicode){ fsl_free(zNewCmd); FSL__WARN_OOM; return FSL_RC_OOM; } //fossil_assert_safe_command_string(zOrigCmd); rc = _wsystem(zUnicode); fsl_unicode_free(zUnicode); free(zNewCmd); #else /* On unix, evaluate the command directly. */ //fossil_assert_safe_command_string(zOrigCmd); /* The regular system() call works to get a shell on unix */ rc = system(zOrigCmd); if(rc) { if(-1==rc) rc = errno; else if(rc>0){ rc = FSL_RC_ERROR; } } #endif return rc ? fsl_errno_to_rc(rc, FSL_RC_ERROR) : 0; } #undef MARKER #if defined(_WIN32) || defined(WIN32) #undef isatty #endif /* end of file ./src/fsl.c */ /* start of file ./src/annotate.c */ |
︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 | 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; | > | > > | | 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 | 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; if(!opt->out){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "fsl_annotate_opt is missing its output function."); } 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; |
︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 | 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 | | > > > > > > > > | 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 | 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. */ /* 2022-05-17: apparently VLAs were made OPTIONAL in C11 and MSVC decided not to support them. So we'll go ahead and remove the VLA usage altogether. */ #define FSLPRINTF_HAVE_VARARRAY 0 #if 0 #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 #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 */ |
︙ | ︙ | |||
1989 1990 1991 1992 1993 1994 1995 | 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, | | | 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 | 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 __unused, 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; |
︙ | ︙ | |||
2499 2500 2501 2502 2503 2504 2505 | # 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); \ | | | | | 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 | # 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(FSL_RC_OOM);} \ } # 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(RC) if( zExtra ) fsl_free(zExtra); return RC #define FSLPRINTF_CHECKERR if( 0!=pfrc ) { FSLPRINTF_RETURN(pfrc); } (void)0 #define FSLPRINTF_SPACES(N) \ { \ FSLPRINTF_CHARARRAY(zSpaces,N); \ memset( zSpaces,' ',N); \ pfrc = pfAppend(pfAppendArg, zSpaces, N); \ FSLPRINTF_CHARARRAY_FREE(zSpaces); \ FSLPRINTF_CHECKERR; \ |
︙ | ︙ | |||
2620 2621 2622 2623 2624 2625 2626 | : 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) ){ | | | 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 | : 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(FSL_RC_RANGE); } /* 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; } |
︙ | ︙ | |||
2653 2654 2655 2656 2657 2658 2659 | 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); | | | 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 | 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); FSL_SWITCH_FALL_THROUGH; 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); |
︙ | ︙ | |||
2915 2916 2917 2918 2919 2920 2921 | /* 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); | | | | 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 | /* 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 FSL_RC_OOM; 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. */; FSL_SWITCH_FALL_THROUGH; } case etSTRING: { bufpt = va_arg(ap,char*); length = bufpt ? StrNLen32(bufpt, (precision>0 && flag_alternateform) ? precision*4/*max bytes per char*/ |
︙ | ︙ | |||
2993 2994 2995 2996 2997 2998 2999 | 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); | | | 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 | 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(check); } zExtra = bufpt = (char*)fb.mem /*transfer ownership*/; length = (int)fb.used; if( precision>=0 && precision<length ) length = precision; break; } |
︙ | ︙ | |||
3107 3108 3109 3110 3111 3112 3113 | } } if( zExtra ){ fsl_free(zExtra); zExtra = 0; } }/* End for loop over the format string */ | | | 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 | } } if( zExtra ){ fsl_free(zExtra); zExtra = 0; } }/* End for loop over the format string */ FSLPRINTF_RETURN(0); } /* End of function */ #undef FSLPRINTF_CHARARRAY_STACK #undef FSLPRINTF_CHARARRAY #undef FSLPRINTF_CHARARRAY_FREE #undef FSLPRINTF_SPACES |
︙ | ︙ | |||
3136 3137 3138 3139 3140 3141 3142 | 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 ) | | | 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 | 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 ) : FSL_RC_MISUSE; } 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 ); |
︙ | ︙ | |||
3190 3191 3192 3193 3194 3195 3196 | 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; | < | 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); |
︙ | ︙ | |||
3578 3579 3580 3581 3582 3583 3584 | #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) | | > | > | > > > > > > > > > | | > | > < | | > > | | | > | | | | 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 | #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) #define buf__is_external(b) (b->mem && 0==b->capacity) #define buff__errcheck(B) if((B)->errCode) return (B)->errCode /** 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(buf__is_external(b)); buff__errcheck(b); fsl_size_t const n = b->used + extra + 1; unsigned char * x = (unsigned char *)fsl_malloc(n); if(!x) return b->errCode = FSL_RC_OOM; memcpy(x, b->mem, b->used); b->capacity = n; x[b->used] = 0; b->mem = x; return 0; } int fsl_buffer_err( fsl_buffer const * b ){ return b->errCode; } void fsl_buffer_err_clear(fsl_buffer * const b){ b->errCode = 0; } int fsl_buffer_materialize( fsl_buffer * const b ){ buff__errcheck(b); return buf__is_external(b) ? fsl__buffer_materialize(b, 0) : 0; } #define buf__materialize(B,N) (buf__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->errCode = 0; b->mem = (unsigned char *)mem; b->capacity = 0; } fsl_buffer * fsl_buffer_reuse( fsl_buffer * const b ){ if(buf__is_external(b)){ *b = fsl_buffer_empty; }else{ if(b->capacity){ assert(b->mem); b->mem[0] = 0; b->used = 0; } b->cursor = 0; b->errCode = 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( 0 == n ){ if(!buf__is_external(buf)){ fsl_free(buf->mem); }/* else if it has memory, it's owned elsewhere */ *buf = fsl_buffer_empty; return 0; } else buff__errcheck(buf); else if( !buf__is_external(buf) && buf->capacity >= n ){ assert(buf->mem); return 0; }else{ unsigned char * x; bool const isExt = buf__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 buf->errCode = 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 ){ buff__errcheck(b); else if(buf__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 b->errCode = 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 b->errCode = 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; |
︙ | ︙ | |||
3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 | if(0 == rc){ 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){ | > > > > > > > > | 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 | if(0 == rc){ rc = (szL==szR) ? 0 : ((szL<szR) ? -1 : 1); } return rc; } bool fsl_buffer_eq(fsl_buffer const * const b, char const * str, fsl_int_t nStr){ if(nStr<0) nStr = (fsl_int_t)fsl_strlen(str); fsl_buffer rhs = fsl_buffer_empty; fsl_buffer_external(&rhs, str, nStr); return 0==fsl_buffer_compare(b, &rhs); } /* 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){ |
︙ | ︙ | |||
3742 3743 3744 3745 3746 3747 3748 | return rc; } int fsl_buffer_append( fsl_buffer * const b, void const * const data, fsl_int_t len ){ | > | | | | | | | | | | | | | > | | < | | | | | | 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 | return rc; } int fsl_buffer_append( fsl_buffer * const b, void const * const data, fsl_int_t len ){ if(0==b->errCode){ fsl_size_t sz = b->used; if(len<0) len = (fsl_int_t)fsl_strlen((char const *)data); if(buf__materialize(b, (fsl_size_t)len + 1)) return b->errCode; assert(b->capacity ? !!b->mem : !b->mem); assert(b->used <= b->capacity); sz += len + 1/*NUL*/; if(b->capacity<sz) fsl_buffer_reserve( b, sz ); if(!b->errCode){ 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 b->errCode; } 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, ... ){ buff__errcheck(b); else{ va_list args; va_start(args,fmt); fsl_buffer_appendfv( b, fmt, args ); va_end(args); return b->errCode; } } char const * fsl_buffer_cstr(fsl_buffer const * const b){ return b->errCode ? NULL : (char const *)b->mem; } char const * fsl_buffer_cstr2(fsl_buffer const * const b, fsl_size_t * const len){ char const * rc = NULL; if(0==b->errCode){ rc = (char const *)b->mem; if(len) *len = b->used; } return rc; } char * fsl_buffer_str(fsl_buffer const * const b){ return b->errCode ? NULL : (char *)b->mem; } #if 0 fsl_size_t fsl_buffer_size(fsl_buffer const * const b){ return b->used; } |
︙ | ︙ | |||
3894 3895 3896 3897 3898 3899 3900 | : -1; } fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b){ return fsl_data_uncompressed_size(b->mem, b->used); } | | > > | 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 | : -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 * const pOut){ buff__errcheck(pIn); else buff__errcheck(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; |
︙ | ︙ | |||
3930 3931 3932 3933 3934 3935 3936 | assert(outSize==pOut->used); } return rc; } } int fsl_buffer_compress2(fsl_buffer const *pIn1, | | > > > | 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 | assert(outSize==pOut->used); } return rc; } } int fsl_buffer_compress2(fsl_buffer const *pIn1, fsl_buffer const *pIn2, fsl_buffer * const pOut){ buff__errcheck(pIn1); else buff__errcheck(pIn2); else buff__errcheck(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{ |
︙ | ︙ | |||
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); | > > > | 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 | fsl_buffer_reserve(&temp, 0); } return rc; } } int fsl_buffer_uncompress(fsl_buffer const * const pIn, fsl_buffer * const pOut){ buff__errcheck(pIn); else buff__errcheck(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); } assert(pOut->errCode == rc); 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); |
︙ | ︙ | |||
4007 4008 4009 4010 4011 4012 4013 | */ fsl_buffer_external(&temp, pOut->mem, pOut->capacity); #else fsl_buffer_swap(&temp, pOut); #endif }else{ rc = fsl_buffer_reserve(&temp, nOut+1); | | | 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 | */ 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 pOut->errCode = 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 |
︙ | ︙ | |||
4052 4053 4054 4055 4056 4057 4058 | 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); | | | < > < | 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 | 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 pOut->errCode = rc; } int fsl_buffer_fill_from( fsl_buffer * const dest, fsl_input_f src, void * const state ){ buff__errcheck(dest); int rc; enum { BufSize = 512 * 8 }; char rbuf[BufSize]; fsl_size_t total = 0; fsl_size_t rlen = 0; fsl_buffer_reuse(dest); while(1){ rlen = BufSize; rc = src( state, rbuf, &rlen ); if( rc ) break; total += rlen; if(total<rlen){ |
︙ | ︙ | |||
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 | 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; } | > | | > > > > | | | | < | > | 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 | 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 ){ buff__errcheck(dest); 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 * const left, fsl_buffer * const right ){ fsl_buffer const tmp = *left; *left = *right; *right = tmp; } void fsl_buffer_swap_free( fsl_buffer * const left, fsl_buffer * const 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){ buff__errcheck(orig); else buff__errcheck(pDelta); else buff__errcheck(pTarget); 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; } assert(n>0); rc = fsl_buffer_resize( &out, n ); if(0==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 ){ buff__errcheck(b); 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) { |
︙ | ︙ | |||
4210 4211 4212 4213 4214 4215 4216 | delta->mem, &delta->used ); } return rc; } int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n ){ | < < | | > | 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 | delta->mem, &delta->used ); } return rc; } int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n ){ return fsl_buffer_append((fsl_buffer*)state, src, n); } int fsl_finalizer_f_buffer( void * state __unused, 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){ buff__errcheck(pFrom); 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 ){ |
︙ | ︙ | |||
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; | > | 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 | } int fsl_buffer_copy_lines(fsl_buffer * const pTo, fsl_buffer * const pFrom, fsl_size_t N){ #if 1 if(pTo) buff__errcheck(pTo); 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; |
︙ | ︙ | |||
4295 4296 4297 4298 4299 4300 4301 | pFrom->cursor = i; } return rc; #endif } int fsl_input_f_buffer( void * state, void * dest, fsl_size_t * n ){ | | > | 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 | pFrom->cursor = i; } return rc; #endif } int fsl_input_f_buffer( void * state, void * dest, fsl_size_t * n ){ fsl_buffer * const b = (fsl_buffer*)state; buff__errcheck(b); fsl_size_t const from = b->cursor; fsl_size_t to; fsl_size_t c; if(from >= b->used){ *n = 0; return 0; } |
︙ | ︙ | |||
4351 4352 4353 4354 4355 4356 4357 | #endif return rc; } } char * fsl_buffer_take(fsl_buffer * const b){ char * z = NULL; | | > | 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 | #endif return rc; } } char * fsl_buffer_take(fsl_buffer * const b){ char * z = NULL; if(0==buf__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; __attribute__ ((fallthrough)); 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 |
︙ | ︙ | |||
4395 4396 4397 4398 4399 4400 4401 | return b->cursor; } void fsl_buffer_rewind(fsl_buffer * const b){ b->cursor = 0; } | | | | | | | | > | | | | | | | > | 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 | 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 * const b, char const * separator){ int i = 0; fsl_int_t const sepLen = (fsl_id_t)fsl_strlen(separator); fsl_buffer_reserve(b, b->used + (bag->entryCount * 7) + (bag->entryCount * sepLen)); for(fsl_id_t e = fsl_id_bag_first(bag); !b->errCode && e; e = fsl_id_bag_next(bag, e)){ if(i++) fsl_buffer_append(b, separator, sepLen); fsl_buffer_appendf(b, "%" FSL_ID_T_PFMT, e); } return b->errCode; } int fsl_buffer_append_tcl_literal(fsl_buffer * const b, bool escapeSquigglies, char const * z, fsl_int_t n){ buff__errcheck(b); int rc; if(n<0) n = fsl_strlen(z); fsl_buffer_append(b, "\"", 1); for(fsl_int_t i=0; 0==b->errCode && 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: fsl_buffer_append(b, &c, 1); } } fsl_buffer_append(b, "\"", 1); return b->errCode; } #undef MARKER #undef buf__is_external #undef buf__errcheck #undef buf__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 |
︙ | ︙ | |||
4721 4722 4723 4724 4725 4726 4727 | 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])){ | | | | | | | | | < | | | | < < | < | > > | 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 | 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])){ 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{ fsl_buffer_appendf(sql, "SELECT id FROM vfile WHERE vid=%" FSL_ID_T_PFMT, vid); } if(changedOnly){ fsl_buffer_append(sql, " AND (chnged OR deleted OR rid=0 " "OR (origname IS NOT NULL AND " " origname<>pathname))", -1); } rc = fsl_buffer_appendf(sql, " /* %s() */", __func__); if(0==rc) 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; 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 * const f, fsl_id_t vid, char const * zName, fsl_id_t * const 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, |
︙ | ︙ | |||
8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 | z[sz] = 0; 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() */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 9709 9710 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 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 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 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 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 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 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 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 | z[sz] = 0; 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; } /** Internal helper for fsl_ckout_rename(). Performs the vfile update for renaming zFrom to zTo, taking into account certain vfile-semantics error conditions. */ static int fsl__mv_one_file(fsl_cx * const f, char const * zFrom, char const * zTo, bool dryRun){ fsl_db * const db = fsl_cx_db_ckout(f); int deleted = fsl_db_g_int32(db, -1, "SELECT deleted FROM vfile WHERE vid=%"FSL_ID_T_PFMT " AND pathname=%Q %s", f->ckout.rid, zTo, fsl_cx_filename_collation(f)); if(deleted>=0){ if(0==deleted){ if( !fsl_cx_is_case_sensitive(f,false) && 0==fsl_stricmp(zFrom, zTo) ){ /* Case change only */ }else{ return fsl_cx_err_set(f, FSL_RC_ALREADY_EXISTS, "Cannot rename '%s' to '%s' because " "another file named '%s' is already " "under management.", zFrom, zTo, zTo); } }else{ return fsl_cx_err_set(f, FSL_RC_CONSISTENCY, "Cannot rename '%s' to '%s' because " "a pending deletion of '%s' has not " "yet been checked in.", zFrom, zTo, zTo); } } int rc = 0; if( !dryRun ){ rc = fsl_cx_exec(f, "UPDATE vfile SET pathname=%Q WHERE " "pathname=%Q %s AND vid=%"FSL_ID_T_PFMT, zTo, zFrom, fsl_cx_filename_collation(f), f->ckout.rid); } return rc; } /** Internal helper for fsl_ckout_rename(). Performs the filesystem-level moving of all files in the TEMP.ckout_mv table. All fs-level errors _are ignored_. */ static int fsl__rename_process_fmove(fsl_cx * const f){ int rc = 0; fsl_stmt q = fsl_stmt_empty; bool const allowSymlinks = fsl_cx_allows_symlinks(f, false); rc = fsl_cx_prepare(f, &q, "SELECT fsl_ckout_dir()||f, " "fsl_ckout_dir()||t " "FROM ckout_mv ORDER BY 1"); while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&q)){ char const * zFrom = fsl_stmt_g_text(&q, 0, NULL); char const * zTo = fsl_stmt_g_text(&q, 1, NULL); if(!zFrom || !zTo){FSL__WARN_OOM; rc = FSL_RC_OOM; break;} //MARKER(("MOVING: %s ==> %s\n", zFrom, zTo)); int const fromDirCheck = fsl_dir_check(zFrom); int fsrc; if(fromDirCheck>0){ /* This case is "impossible." Unless... perhaps... a user somehow moves things around in the filesystem during the fsl_ckout_rename(), such that a to-be-renamed entry which was formerly a file is not a directory. */ #if 0 assert(!"This case cannot possibly happen."); fsl__fatal(FSL_RC_CANNOT_HAPPEN, "Input name for a file-rename is a directory: %s", zFrom)/*does not return*/; #endif int const toDirCheck = fsl_dir_check(zTo); if(0==toDirCheck){ fsl_file_rename(zFrom, zTo); } }else if(fromDirCheck<0){ if(fsl_is_symlink(zFrom)){ fsrc = fsl_symlink_copy(zFrom, zTo, allowSymlinks); }else{ fsrc = fsl_file_copy(zFrom, zTo); } if(0==fsrc){ /* fossil(1) unconditionally unlinks zFrom if zFrom is not a directory. Maybe we should too? */ fsl_file_unlink(zFrom); } } } fsl_stmt_finalize(&q); return rc; } int fsl_ckout_rename(fsl_cx * const f, fsl_ckout_rename_opt const * opt){ int rc = 0; bool inTrans = false; fsl_buffer * const bDest = fsl__cx_scratchpad(f) /* Destination directory */; fsl_buffer * const bSrc = fsl__cx_scratchpad(f) /* One source file/dir */; fsl_stmt qName = fsl_stmt_empty; fsl_stmt qMv = fsl_stmt_empty; int origType = 0 /* -1 == multiple input files, 0 == one file, 1 == directory */; int destType = 0 /* >0==directory, 0==does not exist, <0==non-dir */; uint32_t srcCount = 0; if(!opt->src->used){ rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Expecting 1 or more source files/directories."); goto end; } rc = fsl_cx_transaction_begin(f); if(rc) goto end; inTrans = true; rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->dest, bDest); if(rc) goto end; fsl_buffer_strip_slashes(bDest); rc = fsl_cx_exec_multi(f, "DROP TABLE IF EXISTS TEMP.ckout_mv; " "CREATE TEMP TABLE ckout_mv(" "f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT)"); if(rc) goto end; rc = fsl_cx_exec(f, "UPDATE vfile SET origname=pathname " "WHERE origname IS NULL"); if(rc) goto end; if(opt->src->used > 1){ origType = -1; }else{ /* Make opt->src->list[0] absolute and see if it resolves to an existing dir. */ char const * zSrc= (char const *)opt->src->list[0]; rc = fsl_ckout_filename_check(f, opt->relativeToCwd, zSrc, bSrc); if(rc) goto end; fsl_buffer * const bCheck = fsl__cx_scratchpad(f); int oCheck = 0; rc = fsl_buffer_append(bCheck, f->ckout.dir, f->ckout.dirLen); if(0==rc) rc = fsl_buffer_append(bCheck, bSrc->mem, bSrc->used); if(0==rc) oCheck = fsl_dir_check(fsl_buffer_cstr(bCheck)); fsl__cx_scratchpad_yield(f, bCheck); if(rc){FSL__WARN_OOM; goto end;} if(oCheck>0) origType = 1; else if(oCheck<0) origType = 0; } { /* Make bDest absolute and see if it resolves to an existing dir. */ fsl_buffer * const bCheck = fsl__cx_scratchpad(f); rc = fsl_buffer_append(bCheck, f->ckout.dir, f->ckout.dirLen); if(0==rc) rc = fsl_buffer_append(bCheck, bDest->mem, bDest->used); if(0==rc) destType = fsl_dir_check(fsl_buffer_cstr(bCheck)); fsl__cx_scratchpad_yield(f, bCheck); if(rc){FSL__WARN_OOM; goto end;} } if(-1==origType && destType<=0){ rc = fsl_cx_err_set(f, FSL_RC_MISUSE, "Multiple source files provided, so " "destination must be an existing directory."); goto end; }else if(1==origType && destType<0){ rc = fsl_cx_err_set(f, FSL_RC_TYPE, "Cannot rename '%s' to '%s' " "because a non-directory named '%s' already exists.", (char const *)opt->src->list[0], opt->dest, opt->dest); goto end; }else if( 0==origType && destType<=0 ){ /* Move single file to dest. */ fsl_id_t vfidCheck = 0; rc = fsl_filename_to_vfile_id(f, 0, fsl_buffer_cstr(bSrc), &vfidCheck); if(rc) goto end; else if(!vfidCheck){ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "File not under SCM management: %B", bSrc); goto end; } rc = fsl_cx_exec(f, "INSERT INTO ckout_mv(f,t) VALUES(%B,%B)", bSrc, bDest); if(rc) goto end; else ++srcCount; } else { if(fsl_buffer_eq(bDest, ".", 1)){ fsl_buffer_reuse(bDest); }else{ rc = fsl_buffer_append(bDest, "/", 1); if(rc){FSL__WARN_OOM; goto end;} } rc = fsl_cx_prepare(f, &qName, "SELECT pathname FROM vfile" " WHERE vid=%"FSL_ID_T_PFMT " AND fsl_match_vfile_or_dir(pathname,?1)" " ORDER BY 1", f->ckout.rid); if(rc) goto end; rc = fsl_cx_prepare(f, &qMv, "INSERT INTO ckout_mv(f,t) VALUES(" "?1, ?2||?3)"); if(rc) goto end; for(fsl_size_t i = 0; i < opt->src->used; ++i){ uint32_t nFound = 0; char const * zSrc = (char const *)opt->src->list[i]; fsl_buffer_reuse(bSrc); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, zSrc, bSrc); if(rc) goto end; fsl_size_t nOrig = 0; char const * const zOrig = fsl_buffer_cstr2(bSrc, &nOrig); rc = fsl_stmt_bind_text(&qName, 1, zOrig, (fsl_int_t)nOrig, false); if(rc) goto end; while(FSL_RC_STEP_ROW==fsl_stmt_step(&qName)){ fsl_size_t nPath = 0; char const * zPath = NULL; ++nFound; rc = fsl_stmt_get_text(&qName, 0, &zPath, &nPath); if(rc){fsl_cx_uplift_db_error(f, qName.db); goto end;} else if(!zPath){FSL__WARN_OOM; rc = FSL_RC_OOM; goto end;} char const * zTail; if(nPath==nOrig){ zTail = fsl_file_tail(zPath); }else if(origType!=0 && destType>0 ){ zTail = &zPath[nOrig-fsl_strlen(fsl_file_tail(zOrig))]; }else{ zTail = &zPath[nOrig+1]; } rc = fsl_stmt_bind_step(&qMv, "sbs", zPath, bDest, zTail); if(0!=rc){ fsl_cx_uplift_db_error(f, qMv.db); goto end; } } srcCount += nFound; fsl_stmt_reset(&qName); if(!nFound){ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Name does not resolve to any " "SCM-managed files: %B", bSrc); goto end; } }/*for each opt->src*/ } assert(0==rc); fsl_stmt_finalize(&qName); fsl_stmt_finalize(&qMv); if(0==srcCount){ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Source name(s) do not resolve to " "any managed files."); goto end; } rc = fsl_cx_prepare(f, &qName, "SELECT f, t FROM ckout_mv ORDER BY f"); if(rc) goto end; //rc = fsl_cx_prepare(f, &qMv, "INSERT while(FSL_RC_STEP_ROW==fsl_stmt_step(&qName)){ char const * zFrom = fsl_stmt_g_text(&qName, 0, NULL); char const * zTo = fsl_stmt_g_text(&qName, 1, NULL); rc = fsl__mv_one_file(f, zFrom, zTo, opt->dryRun); if(rc) goto end; if(opt->callback){ rc = opt->callback(f, opt, zFrom, zTo); if(rc) goto end; } } end: fsl_stmt_finalize(&qName); fsl_stmt_finalize(&qMv); fsl__cx_scratchpad_yield(f, bDest); fsl__cx_scratchpad_yield(f, bSrc); if(0==rc){ assert(inTrans); if(!opt->dryRun && opt->doFsMv){ rc = fsl__rename_process_fmove(f); } fsl_cx_exec(f, "DROP TABLE TEMP.ckout_mv"); } if(inTrans){ if(rc) fsl_cx_transaction_end(f, true); else rc = fsl_cx_transaction_end(f, false); } return rc; } int fsl_ckout_rename_revert(fsl_cx * const f, char const *zNewName, bool relativeToCwd, bool doFsMv, bool *didSomething){ fsl_buffer * bufFName = fsl__cx_scratchpad(f); int rc = 0; bool inTrans = false; fsl_db * const dbC = fsl_needs_ckout(f); fsl_stmt q = fsl_stmt_empty; if(!dbC) return FSL_RC_NOT_A_CKOUT; rc = fsl_ckout_filename_check(f, relativeToCwd, zNewName, bufFName); if(rc) goto end; rc = fsl_cx_transaction_begin(f); if(rc) goto end; inTrans = true; rc = fsl_cx_prepare(f, &q, "SELECT id FROM vfile " "WHERE pathname=%Q AND origname<>pathname " "and origname IS NOT NULL %s", fsl_buffer_cstr(bufFName), fsl_cx_filename_collation(f)); if(rc) goto end; switch(fsl_stmt_step(&q)){ case FSL_RC_STEP_ROW: { char const * zNameP = NULL; char const * zNameO = NULL; fsl_id_t const vfid = fsl_stmt_g_id(&q, 0); assert(vfid>0); fsl_stmt_finalize(&q); if(doFsMv){ rc = fsl_cx_prepare(f, &q, "SELECT fsl_ckout_dir()||pathname, " "fsl_ckout_dir()||origname FROM vfile " "WHERE id=%"FSL_ID_T_PFMT, vfid); if(rc) goto end; rc = fsl_stmt_step(&q); assert(FSL_RC_STEP_ROW==rc && "We _just_ confirmed that these are there."); zNameP = fsl_stmt_g_text(&q, 0, NULL); zNameO = fsl_stmt_g_text(&q, 1, NULL); if(!zNameO || !zNameO) {FSL__WARN_OOM; rc = FSL_RC_OOM; goto end;} } rc = fsl_cx_exec(f, "UPDATE vfile SET pathname=origname, origname=NULL " "WHERE id=%"FSL_ID_T_PFMT, vfid); if(rc) goto end; if(didSomething) *didSomething = true; if(doFsMv && fsl_is_file(zNameP)){ assert(zNameO && zNameP); fsl_file_unlink(zNameO); rc = fsl_file_rename(zNameP, zNameO); if(rc){ rc = fsl_cx_err_set(f, rc, "File rename failed with code %s: " "'%s' => '%s'", fsl_rc_cstr(rc), zNameO + f->ckout.dirLen, zNameP + f->ckout.dirLen); } } break; } case FSL_RC_STEP_DONE: if(didSomething) *didSomething = false; goto end; default: rc = fsl_cx_uplift_db_error(f, dbC); goto end; } end: fsl_stmt_finalize(&q); fsl__cx_scratchpad_yield(f, bufFName); if(inTrans){ if(0==rc) rc = fsl_cx_transaction_end(f, false); else fsl_cx_transaction_end(f, true); } return rc; } #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() */ #include <errno.h> #if !defined(FSL_AMALGAMATION_BUILD) /* When not in the amalgamation build, force assert() to always work... */ # if defined(NDEBUG) # undef NDEBUG # undef DEBUG # define DEBUG 1 # endif #endif #include <assert.h> /* for the benefit of test apps */ /* Only for debugging */ #include <stdio.h> |
︙ | ︙ | |||
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; | > | 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 | 0/*helpRequested*/, \ false/*versionRequested*/\ }, \ {/*config*/ \ -1/*traceSql*/, \ fsl_outputer_empty_m \ }, \ {/*paths*/fsl_pathfinder_empty_m/*bins*/}, \ 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; |
︙ | ︙ | |||
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); } | > | 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 | 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*/; fsl_pathfinder_clear(&fcli.paths.bins); 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); } |
︙ | ︙ | |||
9002 9003 9004 9005 9006 9007 9008 | } 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, | | | 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 | } 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 libfossil 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."), |
︙ | ︙ | |||
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.*/ | > | 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 | f-ci -m="message" ... simply doesn't fit the age-old muscle memory of: svn ci -m ... cvs ci -m ... fossil ci -m ... f-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.*/ |
︙ | ︙ | |||
9188 9189 9190 9191 9192 9193 9194 | rc = fcli__error->code; break; } //MARKER(("Got?=%d flag: %s/%s %s\n",gotIt, f->flagShort, f->flagLong, v ? v : "")); if(!gotIt){ continue; } | < | 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; |
︙ | ︙ | |||
9279 9280 9281 9282 9283 9284 9285 | ++fcli.transient.helpRequested; continue; } FLAG("V") { fcli.clientFlags.verbose += 1; continue; } | < < < < < < < < | 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 | ++fcli.transient.helpRequested; continue; } FLAG("V") { fcli.clientFlags.verbose += 1; continue; } FLAG("verbose") { fcli.clientFlags.verbose += 1; continue; } } #undef FLAG /* else fall through */ |
︙ | ︙ | |||
9418 9419 9420 9421 9422 9423 9424 | We copy fsl_lib_configurable.allocator as a base allocator. */ static fsl_allocator fslAllocOrig; /** Proxies fslAllocOrig.f() and abort()s on OOM conditions. */ | | | 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 | 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 __unused, 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; } |
︙ | ︙ | |||
9741 9742 9743 9744 9745 9746 9747 | /* 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 | | | 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 | /* 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) || 0==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){ |
︙ | ︙ | |||
9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 | } 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{ | > | | | | | 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095 11096 11097 11098 11099 11100 11101 11102 11103 11104 11105 11106 11107 11108 11109 11110 | } break; } } if(helpState){ f_out("\n"); fcli_command_help(helpPos, true, helpState>1); fcli.transient.helpRequested++; }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 = FCLI_RC_NO_CMD; 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; } int 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 0; } alias = strchr(alias, 0) + 1; } return 1; } 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){ |
︙ | ︙ | |||
10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 | f_out("fsl_cx::cache::blobContent hits = %u misses = %u. " "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 */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 11448 11449 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 | f_out("fsl_cx::cache::blobContent hits = %u misses = %u. " "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); } char const * fcli_fossil_binary(bool errIfNotFound, int reportPolicy){ static bool once = false; if(!once){ int rc = 0; char const * path = getenv("PATH"); if(path && *path){ fsl_path_splitter pt = fsl_path_splitter_empty; fsl_size_t tLen = 0; char const * t = 0; fsl_path_splitter_init(&pt, path, -1); while(0==rc && 0==fsl_path_splitter_next(&pt, &t, &tLen)){ rc = fsl_pathfinder_dir_add2(&fcli.paths.bins, t, (fsl_int_t)tLen); } } if(0==rc){ fsl_pathfinder_ext_add2(&fcli.paths.bins,".exe", 4); } once = true; } char const * z = NULL; fsl_pathfinder_search(&fcli.paths.bins, "fossil", &z, NULL); if(!z && errIfNotFound){ fcli_err_set(FSL_RC_NOT_FOUND, "Fossil binary not found in $PATH."); if(reportPolicy){ fcli_err_report(reportPolicy>0); } } return z; } static int fcli__transaction_check(void){ if(fsl_cx_transaction_level(fcli.f)){ return fcli_err_set(FSL_RC_LOCKED, "Sync cannot succeed if a transaction " "is opened. Close all transactions before " "calling %s().", __func__); } return 0; } static bool fcli__autosync_setting(void){ return fsl_configs_get_bool(fcli.f, "crg", fsl_configs_get_bool(fcli.f, "crg", false, "autosync"), "fcli.autosync"); } int fcli_sync( int ops ){ int rc = 0; if((rc = fcli__transaction_check())) return rc; int doPush = -1; int doPull = -1; char const * zSuppressOut = ""; fsl_db * const dbR = fsl_needs_repo(fcli.f); if(!dbR){ return FSL_RC_NOT_A_REPO; }else if(!fsl_db_exists(dbR, "select 1 from config " "where name like 'syncwith:%%'")){ /* No remote, so nothing to do (and any attempt would fail). */ return 0; } if(FCLI_SYNC_PULL & ops){ doPull = 1; } if(FCLI_SYNC_PUSH & ops){ doPush = 1; } #if !FSL_PLATFORM_IS_WINDOWS if(FCLI_SYNC_NO_OUTPUT & ops){ zSuppressOut = " >/dev/null 2>&1"; }else if(FCLI_SYNC_NO_STDOUT & ops){ zSuppressOut = " >/dev/null"; } #endif bool const autosync = fcli__autosync_setting(); if(!autosync && (FCLI_SYNC_AUTO & ops)){ return 0; } if(doPull<=0 && doPush<=0){ return 0; } char const * zCmd; char const * fslBin; if(doPull>0 && doPush>0) zCmd = "sync"; else if(doPull>0) zCmd = "pull"; else{ assert(doPush>0); zCmd = "push"; } fslBin = fcli_fossil_binary(true, 0); if(!fslBin){ assert(fcli__error->code); return fcli__error->code; } ; char * cmd = fsl_mprintf("%s %s%s", fslBin, zCmd, zSuppressOut); rc = fsl_system(cmd); if(rc){ fsl_cx_caches_reset(fcli.f); rc = fcli_err_set(rc, "Command exited with non-0 result: %s", cmd); } fsl_free(cmd); return rc; } #undef FCLI_V3 #undef fcli_empty_m #undef fcli__error #undef MARKER #undef FCLI_USE_SIGACTION /* end of file ./src/cli.c */ |
︙ | ︙ | |||
11651 11652 11653 11654 11655 11656 11657 | assert(!"Invalid fsl_confdb_e value"); return NULL; } } int fsl_config_versionable_filename(fsl_cx *f, char const * key, fsl_buffer *b){ | | | | | | 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 | assert(!"Invalid fsl_confdb_e value"); return NULL; } } int fsl_config_versionable_filename(fsl_cx *f, char const * key, fsl_buffer *b){ if(!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 * const 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 * const 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 * 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); |
︙ | ︙ | |||
11712 11713 11714 11715 11716 11717 11718 | } 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:{ | | | | | 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 | } 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 * const 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 * const db = fsl_config_for_role(f, mode); char const * 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); |
︙ | ︙ | |||
11754 11755 11756 11757 11758 11759 11760 | } 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:{ | | | | | 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 | } 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 * const val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = strtod(val, NULL); fsl_free(val); } break; } default: { fsl_db * const db = fsl_config_for_role(f, mode); if(!db) break/*e.g. global config is not opened*/; fsl_stmt * st = NULL; char const * 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)){ |
︙ | ︙ | |||
11808 11809 11810 11811 11812 11813 11814 | fsl_buffer_reuse(b); switch(mode){ case FSL_CONFDB_VERSIONABLE:{ if(!fsl_needs_ckout(f)){ rc = FSL_RC_NOT_A_CKOUT; break; } | | | | | | > | | < | | 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 13251 13252 13253 13254 13255 13256 13257 13258 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293 | fsl_buffer_reuse(b); switch(mode){ case FSL_CONFDB_VERSIONABLE:{ if(!fsl_needs_ckout(f)){ rc = FSL_RC_NOT_A_CKOUT; break; } fsl_buffer * const fname = fsl__cx_scratchpad(f); rc = fsl_config_versionable_filename(f, key, fname); if(!rc){ char const * 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 * 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 * 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((key && *key) ? mode : 999){ case 999: break; case FSL_CONFDB_VERSIONABLE:{ char * const 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 * const table = fsl_config_table_for_role(mode); fsl_db * db; 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 * const col = fsl_stmt_g_text(st, 0, NULL); rv = col ? fsl_str_bool(col) : dflt /* 0? */; } fsl_stmt_cached_yield(st); } break; } } |
︙ | ︙ | |||
11900 11901 11902 11903 11904 11905 11906 | 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); | | < | 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 | 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 * const 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. */ |
︙ | ︙ | |||
12896 12897 12898 12899 12900 12901 12902 | /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; } | | > > > > > > > | 14296 14297 14298 14299 14300 14301 14302 14303 14304 14305 14306 14307 14308 14309 14310 14311 14312 14313 14314 14315 14316 14317 14318 14319 14320 14321 14322 14323 14324 | /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_caches_reset(fsl_cx * const f){ fsl__bccache_reset(&f->cache.blobContent); fsl__cx_mcache_clear(f); fsl__cx_clear_mf_seen(f, false); f->cache.allowSymlinks = f->cache.caseInsensitive = f->cache.seenDeltaManifest = f->cache.manifestSetting = -1; if(fsl_cx_db_ckout(f)){ fsl__ckout_version_fetch(f) /* FIXME: this "really should" be fsl__cx_ckout_clear(), but that data is not fetched on demand (maybe it should be?). */; }else{ fsl__cx_ckout_clear(f); } } 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); |
︙ | ︙ | |||
12952 12953 12954 12955 12956 12957 12958 | 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; } | | | 14359 14360 14361 14362 14363 14364 14365 14366 14367 14368 14369 14370 14371 14372 14373 | 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_caches_reset(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); |
︙ | ︙ | |||
13020 13021 13022 13023 13024 13025 13026 | 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 ){ | < | < | < < < | | | | | | < | 14427 14428 14429 14430 14431 14432 14433 14434 14435 14436 14437 14438 14439 14440 14441 14442 14443 14444 14445 14446 14447 14448 14449 14450 14451 14452 14453 14454 14455 14456 14457 14458 14459 14460 14461 14462 | 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(!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 fsl_error_setv( &f->error, code, fmt, args ); } int fsl_cx_err_set( fsl_cx * const f, int code, char const * fmt, ... ){ 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: |
︙ | ︙ | |||
13208 13209 13210 13211 13212 13213 13214 | "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: | < < | | 14609 14610 14611 14612 14613 14614 14615 14616 14617 14618 14619 14620 14621 14622 14623 14624 | "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: fsl_cx_caches_reset(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 |
︙ | ︙ | |||
13515 13516 13517 13518 13519 13520 13521 | 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){ | | > | < | > > > > > > > > > > > > > > > > > > > > | > > | < | > > | 14914 14915 14916 14917 14918 14919 14920 14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931 14932 14933 14934 14935 14936 14937 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 | 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 /* from fsl_getenv(). Note the special-case free() semantics!!! */; char * zRc = 0 /* `*zOut` result, from fsl_mprintf() */; int rc = 0; #if FSL_PLATFORM_IS_WINDOWS zEnv = fsl_getenv("FOSSIL_HOME"); if( zEnv==0 ){ zEnv = fsl_getenv("LOCALAPPDATA"); if( zEnv==0 ){ zEnv = fsl_getenv("APPDATA"); if( zEnv==0 ){ zEnv = fsl_getenv("USERPROFILE"); if( zEnv==0 ){ char * const zDrive = fsl_getenv("HOMEDRIVE"); char * const zPath = fsl_getenv("HOMEPATH"); if( zDrive && zPath ){ zRc = fsl_mprintf("%s%//_fossil", zDrive, zPath); } if(zDrive) fsl_filename_free(zDrive); if(zPath) fsl_filename_free(zPath); } } } } if(!zRc){ if(!zEnv) rc = FSL_RC_NOT_FOUND; else{ zRc = fsl_mprintf("%//_fossil", zEnv); if(!zRc) rc = FSL_RC_OOM; } } #else fsl_buffer buf = fsl_buffer_empty; /* 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; } |
︙ | ︙ | |||
13566 13567 13568 13569 13570 13571 13572 | 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); | < > > < | > < | 14988 14989 14990 14991 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 | 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: fsl_buffer_clear(&buf); #endif if(zEnv) fsl_filename_free(zEnv); if(!rc){ if(zRc) *zOut = zRc; else rc = FSL_RC_OOM; } return rc; } int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; |
︙ | ︙ | |||
13867 13868 13869 13870 13871 13872 13873 | } 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; | < | | < < | 15289 15290 15291 15292 15293 15294 15295 15296 15297 15298 15299 15300 15301 15302 15303 15304 15305 | } 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); if(!dbC) return 0; else if(!dbR) return FSL_RC_NOT_A_REPO; 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){ |
︙ | ︙ | |||
14958 14959 14960 14961 14962 14963 14964 | 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: | | | 16377 16378 16379 16380 16381 16382 16383 16384 16385 16386 16387 16388 16389 16390 16391 | 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: rc = FSL_RC_LOCKED; break; 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)); |
︙ | ︙ | |||
15689 15690 15691 15692 15693 15694 15695 | 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, | | > | 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 | 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: %s", sqlite3_errmsg(st->db->dbh)); break; } } return rc; } int fsl_stmt_bind_step( fsl_stmt * st, char const * fmt, ... ){ |
︙ | ︙ | |||
16308 16309 16310 16311 16312 16313 16314 | 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. */ | < < | < < < < < < | < < < < < < < < | < < < < < < | < | < | < < < < | < | | 17728 17729 17730 17731 17732 17733 17734 17735 17736 17737 17738 17739 17740 17741 17742 17743 17744 17745 17746 17747 17748 17749 17750 17751 17752 17753 | 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. */ fsl_cx_caches_reset(db->f); } fsl_db_cleanup_beforeCommit(db); fsl_db_reset_change_count(db); rc = fsl_db_exec(db, db->doRollback ? "ROLLBACK" : "COMMIT"); if(db->doRollback && db->f && changeCount>0 && db->f->ckout.rid>0){ int const rc2 = fsl__ckout_version_fetch(db->f) /*Else it might be out of sync, leading to chaos.*/; if(0==rc && rc2!=0) rc = rc2; } db->doRollback = 0; return rc; } 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 |
︙ | ︙ | |||
16651 16652 16653 16654 16655 16656 16657 | 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){ | | < | 18042 18043 18044 18045 18046 18047 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 18064 18065 18066 18067 18068 18069 18070 18071 18072 | 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(!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; rc = fsl_buffer_append( b, str, len ); } break; } case FSL_RC_STEP_DONE: rc = 0; break; |
︙ | ︙ | |||
16797 16798 16799 16800 16801 16802 16803 | 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; | | | 18187 18188 18189 18190 18191 18192 18193 18194 18195 18196 18197 18198 18199 18200 18201 | 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(0==fsl_db_preparev(db, &st, sql, args)){ rv = FSL_RC_STEP_ROW==fsl_stmt_step(&st) ? true : false; } fsl_stmt_finalize(&st); return rv; } } |
︙ | ︙ | |||
17325 17326 17327 17328 17329 17330 17331 | } } /** fsl_list_visitor_f() impl which requires that obj be-a (fsl_card_T*), which this function passes to fsl_card_T_free(). */ | | | | | 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 | } } /** 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 __unused){ if(obj) fsl_card_T_free( (fsl_card_T*)obj ); return 0; } static int fsl_list_v_card_Q_free(void * obj, void * visitorState __unused ){ if(obj) fsl_card_Q_free( (fsl_card_Q*)obj ); return 0; } static int fsl_list_v_card_J_free(void * obj, void * visitorState __unused){ 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){ |
︙ | ︙ | |||
19697 19698 19699 19700 19701 19702 19703 19704 19705 19706 19707 | 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 ){ | > | | | | | | | 21087 21088 21089 21090 21091 21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 21119 21120 21121 21122 | 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); } bool const caseSensitive = fsl_cx_is_case_sensitive(d->f, false); #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 && caseSensitive) ? fsl_strcmp(FCARD(d->F.cursor+1)->name, zName) : fsl_stricmp(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 && caseSensitive) ? fsl_strcmp(FCARD(i)->name, zName) : fsl_stricmp(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; |
︙ | ︙ | |||
20609 20610 20611 20612 20613 20614 20615 | return rc; } /** Overrideable crosslink listener which updates the timeline for attachment records. */ | | | 22000 22001 22002 22003 22004 22005 22006 22007 22008 22009 22010 22011 22012 22013 22014 | 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 __unused){ 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 |
︙ | ︙ | |||
20687 20688 20689 20690 20691 20692 20693 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ | | | 22078 22079 22080 22081 22082 22083 22084 22085 22086 22087 22088 22089 22090 22091 22092 | 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 __unused){ 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," |
︙ | ︙ | |||
20735 20736 20737 20738 20739 20740 20741 | (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); } | | | 22126 22127 22128 22129 22130 22131 22132 22133 22134 22135 22136 22137 22138 22139 22140 | (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 __unused){ 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 |
︙ | ︙ | |||
20882 20883 20884 20885 20886 20887 20888 | end: fsl__cx_scratchpad_yield(d->f, comment); return rc; } | | | 22273 22274 22275 22276 22277 22278 22279 22280 22281 22282 22283 22284 22285 22286 22287 | end: fsl__cx_scratchpad_yield(d->f, comment); return rc; } static int fsl_deck_xlink_f_forum(fsl_deck * const d, void * state __unused){ 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; |
︙ | ︙ | |||
20968 20969 20970 20971 20972 20973 20974 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } | | | 22359 22360 22361 22362 22363 22364 22365 22366 22367 22368 22369 22370 22371 22372 22373 | 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 __unused){ 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); |
︙ | ︙ | |||
21017 21018 21019 21020 21021 21022 21023 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } | | | 22408 22409 22410 22411 22412 22413 22414 22415 22416 22417 22418 22419 22420 22421 22422 | 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 __unused){ 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; |
︙ | ︙ | |||
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, | > > > | 24807 24808 24809 24810 24811 24812 24813 24814 24815 24816 24817 24818 24819 24820 24821 24822 24823 24824 24825 | sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); __attribute__ ((fallthrough)); case 2: sum3 += (z[1] << 16); __attribute__ ((fallthrough)); case 1: sum3 += (z[0] << 24); __attribute__ ((fallthrough)); default: ; } return sum3; } int fsl_delta_create2( unsigned char const *zSrc, fsl_size_t lenSrc, unsigned char const *zOut, fsl_size_t lenOut, |
︙ | ︙ | |||
23646 23647 23648 23649 23650 23651 23652 | } 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; | < | | 25040 25041 25042 25043 25044 25045 25046 25047 25048 25049 25050 25051 25052 25053 25054 25055 25056 25057 25058 25059 25060 | } 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; *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 |
︙ | ︙ | |||
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 } | > > > > > | 25401 25402 25403 25404 25405 25406 25407 25408 25409 25410 25411 25412 25413 25414 25415 25416 25417 25418 25419 | uint32_t lnnoRHS, uint32_t linesRHS ){ #if 1 if(1==b->passNumber){ DICOSTATE(sst); ++sst->displayLines; return 0; } if(b->lnLHS+1==lnnoLHS && b->lnRHS+1==lnnoRHS){ fdb__outf(b, "<<<Unfortunate chunk separator." "Ticket 746ebbe86c20b5c0f96cdadd19abd8284770de16.>>>\n"); } //fdb__outf(b, "lnLHS=%d, lnRHS=%d\n", (int)b->lnLHS, (int)b->lnRHS); return fdb__outf(b, "@@ -%" PRIu32 ",%" PRIu32 " +%" PRIu32 ",%" PRIu32 " @@\n", lnnoLHS, linesLHS, lnnoRHS, linesRHS); #else return 0; #endif } |
︙ | ︙ | |||
24427 24428 24429 24430 24431 24432 24433 | return rc; } static int fdb__utxt_chunkHeader(fsl_dibu* const b, uint32_t lnnoLHS, uint32_t linesLHS, uint32_t lnnoRHS, uint32_t linesRHS ){ /* | > > | | | | 25825 25826 25827 25828 25829 25830 25831 25832 25833 25834 25835 25836 25837 25838 25839 25840 25841 25842 25843 25844 25845 | return rc; } static int fdb__utxt_chunkHeader(fsl_dibu* const b, uint32_t lnnoLHS, uint32_t linesLHS, uint32_t lnnoRHS, uint32_t linesRHS ){ /* Ticket 746ebbe86c20b5c0f96cdadd19abd8284770de16: Annoying cosmetic bug: the libf impl of this diff will sometimes render two directly-adjecent chunks with a separator, e.g.: */ // $ f-vdiff --format 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; |
︙ | ︙ | |||
24461 24462 24463 24464 24465 24466 24467 | /* 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 | | | 25861 25862 25863 25864 25865 25866 25867 25868 25869 25870 25871 25872 25873 25874 25875 | /* 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 destinations). 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); |
︙ | ︙ | |||
24760 24761 24762 24763 24764 24765 24766 | BR_CLOSE; RC; rc = fdb__out(b, "\n", 1); end: #undef RC return rc; } | | | 26160 26161 26162 26163 26164 26165 26166 26167 26168 26169 26170 26171 26172 26173 26174 | BR_CLOSE; RC; rc = fdb__out(b, "\n", 1); end: #undef RC return rc; } static int fdb__tcl_finish(fsl_dibu * const b __unused){ int rc = 0; #if 0 BR_CLOSE; if(0==rc && FSL_DIBU_TCL_BRACES & b->implFlags){ rc = fdb__out(b, "\n", 1); } #endif |
︙ | ︙ | |||
25388 25389 25390 25391 25392 25393 25394 | ** 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 ** | | > > > > | | | > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > | > > > > > > > > | > > > > > > > > > > > > > > > | > > | | 26788 26789 26790 26791 26792 26793 26794 26795 26796 26797 26798 26799 26800 26801 26802 26803 26804 26805 26806 26807 26808 26809 26810 26811 26812 26813 26814 26815 26816 26817 26818 26819 26820 26821 26822 26823 26824 26825 26826 26827 26828 26829 26830 26831 26832 26833 26834 26835 26836 26837 26838 26839 26840 26841 26842 26843 26844 26845 26846 26847 26848 26849 26850 26851 26852 26853 26854 26855 26856 26857 26858 26859 26860 26861 26862 26863 26864 26865 26866 26867 26868 26869 26870 26871 26872 26873 26874 26875 26876 26877 26878 26879 26880 26881 26882 26883 | ** 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 no lines of the subsequence contains more than one ** non-whitespace character, --> return 0 ** ** 3. If any line of the subsequence contains more than one non-whitespace ** character and is unique across the entire sequence after ignoring ** leading and trailing whitespace --> return 1 ** ** 4. 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, n; /* Rule (1) */ if( (iEX-iSX)*7 >= (iE1-iS1) ) return 1; /* Compute fsl_dline.indent and fsl_dline.nw for all lines of the subsequence. ** If no lines contain more than one non-whitespace character return ** 0 because the subsequence could be due to an indentation change. ** Rule (2). */ n = 0; for(i=iSX; i<iEX; i++){ fsl_dline *pA = &p->aFrom[i]; if( pA->nw==0 && pA->n ){ const char *zA = pA->z; const int nn = pA->n; int ii, jj; for(ii=0; ii<nn && diff_isspace(zA[ii]); ii++){} pA->indent = ii; for(jj=nn-1; jj>ii && diff_isspace(zA[jj]); jj--){} pA->nw = jj - ii + 1; } if( pA->nw>1 ) n++; } if( n==0 ) return 0; /* Compute fsl_dline.indent and fsl_dline.nw for the entire sequence */ for(i=iS1; i<iE1; i++){ fsl_dline *pA; if( i==iSX ){ i = iEX; if( i>=iE1 ) break; } pA = &p->aFrom[i]; if( pA->nw==0 && pA->n ){ const char *zA = pA->z; const int nn = pA->n; int ii, jj; for(ii=0; ii<nn && diff_isspace(zA[ii]); ii++){} pA->indent = ii; for(jj=nn-1; jj>ii && diff_isspace(zA[jj]); jj--){} pA->nw = jj - ii + 1; } } /* Check to see if any subsequence line that has more than one ** non-whitespace character is unique across the entire sequence. ** Rule (3) */ for(i=iSX; i<iEX; i++){ const char *z = p->aFrom[i].z + p->aFrom[i].indent; const int nw = p->aFrom[i].nw; if( nw<=1 ) continue; for(j=iS1; j<iSX; j++){ if( p->aFrom[j].nw!=nw ) continue; if( memcmp(p->aFrom[j].z+p->aFrom[j].indent,z,nw)==0 ) break; } if( j<iSX ) continue; for(j=iEX; j<iE1; j++){ if( p->aFrom[j].nw!=nw ) continue; if( memcmp(p->aFrom[j].z+p->aFrom[j].indent,z,nw)==0 ) break; } if( j>=iE1 ) break; } return i<iEX; } /** 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. |
︙ | ︙ | |||
26270 26271 26272 26273 26274 26275 26276 | fsl_dline * const aRight, int nRight, fsl_dibu_opt const * pOpt, unsigned char **pResult, unsigned *pNResult ); /* | | | 27729 27730 27731 27732 27733 27734 27735 27736 27737 27738 27739 27740 27741 27742 27743 | fsl_dline * const aRight, int nRight, fsl_dibu_opt const * pOpt, unsigned char **pResult, unsigned *pNResult ); /* ** Make a copy of a list of nLine fsl_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 ){ |
︙ | ︙ | |||
27348 27349 27350 27351 27352 27353 27354 | */ /************************************************************************ This file implements technote (formerly known as event)-related parts of the library. */ #include <assert.h> | | > > > > > > | > > > | < < < > > > > | | < | < | | | | > | | | | | | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 28807 28808 28809 28810 28811 28812 28813 28814 28815 28816 28817 28818 28819 28820 28821 28822 28823 28824 28825 28826 28827 28828 28829 28830 28831 28832 28833 28834 28835 28836 28837 28838 28839 28840 28841 28842 28843 28844 28845 28846 28847 28848 28849 28850 28851 28852 28853 28854 28855 28856 28857 28858 28859 28860 28861 28862 28863 28864 28865 28866 28867 28868 28869 28870 28871 28872 28873 28874 28875 28876 28877 28878 28879 28880 28881 28882 28883 28884 28885 28886 28887 28888 28889 28890 28891 28892 28893 28894 28895 28896 28897 28898 28899 28900 28901 28902 28903 28904 28905 28906 28907 28908 28909 28910 28911 28912 28913 28914 28915 28916 28917 28918 28919 28920 28921 28922 28923 28924 28925 28926 28927 28928 28929 28930 28931 28932 28933 28934 28935 28936 28937 28938 28939 28940 28941 28942 28943 28944 28945 28946 28947 28948 28949 28950 28951 28952 28953 28954 28955 28956 28957 28958 28959 28960 28961 28962 28963 28964 28965 28966 28967 28968 28969 28970 28971 28972 28973 28974 28975 28976 28977 28978 28979 28980 28981 28982 28983 28984 28985 28986 28987 28988 28989 28990 28991 28992 28993 28994 28995 28996 28997 28998 28999 29000 29001 29002 29003 29004 29005 29006 29007 29008 29009 29010 29011 29012 29013 29014 29015 29016 29017 29018 29019 29020 29021 29022 29023 29024 29025 29026 29027 29028 29029 29030 29031 29032 29033 29034 29035 29036 29037 29038 29039 29040 29041 29042 29043 29044 29045 29046 29047 29048 29049 29050 29051 29052 29053 29054 29055 29056 29057 29058 29059 29060 29061 29062 29063 29064 29065 29066 29067 29068 29069 29070 29071 29072 29073 29074 29075 29076 29077 29078 29079 29080 29081 29082 29083 29084 29085 29086 29087 29088 29089 29090 29091 29092 29093 29094 29095 29096 29097 29098 29099 29100 29101 29102 29103 29104 29105 29106 29107 29108 29109 29110 29111 29112 29113 29114 29115 29116 29117 29118 29119 29120 29121 29122 29123 29124 29125 29126 29127 29128 29129 29130 29131 29132 29133 29134 29135 29136 29137 29138 29139 29140 29141 29142 29143 29144 29145 29146 29147 29148 29149 29150 29151 29152 29153 29154 29155 29156 29157 29158 29159 29160 29161 29162 29163 29164 29165 29166 29167 29168 29169 29170 29171 29172 29173 29174 29175 29176 29177 29178 | */ /************************************************************************ This file implements technote (formerly known as event)-related parts of the library. */ #include <assert.h> /** Fetches all "technote" (formerly "event") IDs from the repository and appends each one to the given list in the form of a (`char*`). This function relies on the `event-` tag prefix being reserved for technotes and that the technote IDS are all exactly 40 bytes long. Returns 0 on success, FSL_RC_NOT_A_REPO if f has no repository db opened, FSL_RC_OOM if allocation of a new list entry fails, or propagates db-related code on any other error. Results are undefined if either argument is NULL. TODO? Reformulate this to be like fsl_tkt_id_to_rids(), returning the list as RIDs? */ /*FSL_EXPORT*/ int fsl_technote_ids_get(fsl_cx * const f, fsl_list * const tgt ); int fsl_technote_ids_get( fsl_cx * const f, fsl_list * const tgt ){ fsl_db * const db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; int rc = fsl_db_select_slist( db, tgt, "SELECT substr(tagname,7) AS n " "FROM tag " "WHERE tagname GLOB 'event-*' " "AND length(tagname)=46 " "ORDER BY n"); if(rc && db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } /* end of file ./src/event.c */ /* start of file ./src/foci.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* * Copyright 2022 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). */ /* * This file implements the files-of-checkin (foci) API used to construct a * SQLite3 virtual table via a table-valued function to aggregate all files * pertaining to a specific check-in. This table is used in repository * queries such as listing all files belonging to a specific version. * * Usage (from fossil(1) /src/foci.c:24): * * SELECT * FROM fsl_foci('trunk'); * * temp.foci table schema: * * CREATE TABLE fsl_foci( * checkinID INTEGER, -- RID for the check-in manifest * filename TEXT, -- Name of a file * uuid TEXT, -- hash of the file * previousName TEXT, -- Name of the file in previous check-in * perm TEXT, -- Permissions on the file * symname TEXT HIDDEN -- Symbolic name of the check-in. * ); * * The hidden symname column is (optionally) used as a query parameter to * identify the particular check-in to parse. The checkinID parameter * (such is a unique numeric RID rather than symbolic name) can also be used * to identify the check-in. Example: * * SELECT * FROM fsl_foci * WHERE checkinID=fsl_sym2rid('trunk'); * */ #include <string.h>/*memset()*/ #include <assert.h> /* Only for debugging */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) enum { FOCI_CHECKINID = 0, FOCI_FILENAME = 1, FOCI_UUID = 2, FOCI_PREVNAME = 3, FOCI_PERM = 4, FOCI_SYMNAME = 5 }; typedef struct FociCursor FociCursor; struct FociCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ fsl_deck d; /* Current manifest */ const fsl_card_F *cf; /* Current file */ int idx; /* File index */ }; typedef struct FociTable FociTable; struct FociTable { sqlite3_vtab base; /* Base class - must be first */ fsl_cx * f; /* libfossil context */ }; /* * The schema for the virtual table: */ static const char zFociSchema[] = " CREATE TABLE fsl_foci(" " checkinID INTEGER, -- RID for the check-in manifest\n" " filename TEXT, -- Name of a file\n" " uuid TEXT, -- hash of the file\n" " previousName TEXT, -- Name of the file in previous check-in\n" " perm TEXT, -- Permissions on the file\n" " symname TEXT HIDDEN -- Symbolic name of the check-in\n" " );"; /* * Connect to or create a foci virtual table. */ static int fociConnect( sqlite3 *db, void *pAux /*a (fsl_cx*) */, int argc __unused, const char * const * argv __unused, sqlite3_vtab **ppVtab, char **pzErr __unused ){ FociTable *pTab; int rc = SQLITE_OK; pTab = (FociTable *)sqlite3_malloc(sizeof(FociTable)); if( !pTab ){ return SQLITE_NOMEM; } memset(pTab, 0, sizeof(FociTable)); rc = sqlite3_declare_vtab(db, zFociSchema); if( rc==SQLITE_OK ){ pTab->f = (fsl_cx*)pAux; *ppVtab = &pTab->base; } return rc; } /* * Disconnect from or destroy a focivfs virtual table. */ static int fociDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* * Available scan methods: * * (0) A full scan. Visit every manifest in the repo. (Slow) * (1) checkinID=?. visit only the single manifest specified. * (2) symName=? visit only the single manifest specified. */ static int fociBestIndex(sqlite3_vtab *tab __unused, sqlite3_index_info *pIdxInfo){ int i; pIdxInfo->estimatedCost = 1000000000.0; for( i=0; i<pIdxInfo->nConstraint; i++ ){ if( !pIdxInfo->aConstraint[i].usable ) continue; if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ && (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID || pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME) ){ if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){ pIdxInfo->idxNum = 1; }else{ pIdxInfo->idxNum = 2; } pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; break; } } return SQLITE_OK; } /* * Open a new focivfs cursor. */ static int fociOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ FociCursor *pCsr; pCsr = (FociCursor *)sqlite3_malloc(sizeof(FociCursor)); if( !pCsr ){ return SQLITE_NOMEM; } memset(pCsr, 0, sizeof(FociCursor)); pCsr->d = fsl_deck_empty; pCsr->base.pVtab = pVTab; *ppCursor = (sqlite3_vtab_cursor *)pCsr; return SQLITE_OK; } /* * Close a focivfs cursor. */ static int fociClose(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; fsl_deck_finalize(&pCsr->d); sqlite3_free(pCsr); return SQLITE_OK; } /* * Move a focivfs cursor to the next F card entry in the deck. If this fails, * pass the vtab cursor to fociClose and return the failing result code. */ static int fociNext(sqlite3_vtab_cursor *pCursor){ int rc = SQLITE_OK; FociCursor *pCsr = (FociCursor *)pCursor; rc = fsl_deck_F_next(&pCsr->d, &pCsr->cf); if( !rc ){ pCsr->idx++; }else{ fociClose(pCursor); } return rc; } static int fociEof(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; return pCsr->cf==0; } static int fociFilter( sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr __unused, int argc __unused, sqlite3_value **argv ){ int rc = SQLITE_OK; FociCursor *const pCur = (FociCursor *)pCursor; fsl_cx * const f = ((FociTable*)pCur->base.pVtab)->f; fsl_deck_finalize(&pCur->d); if( idxNum ){ fsl_id_t rid; if( idxNum==1 ){ rid = sqlite3_value_int(argv[0]); }else{ rc = fsl_sym_to_rid(f, (const char *)sqlite3_value_text(argv[0]), FSL_SATYPE_CHECKIN, &rid); if( rc ){ goto end; } } rc = fsl_deck_load_rid(f, &pCur->d, rid, FSL_SATYPE_CHECKIN); if( rc ){ goto end; } if( pCur->d.rid ){ rc = fsl_deck_F_rewind(&pCur->d); if( !rc ){ rc = fsl_deck_F_next(&pCur->d, &pCur->cf); } if( rc ){ goto end; } } } pCur->idx = 0; end: if( rc ){ fsl_deck_finalize(&pCur->d); } return rc; } static int fociColumn( sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i ){ FociCursor *pCsr = (FociCursor *)pCursor; switch( i ){ case FOCI_CHECKINID: sqlite3_result_int(ctx, pCsr->d.rid); break; case FOCI_FILENAME: sqlite3_result_text(ctx, pCsr->cf->name, -1, SQLITE_TRANSIENT); break; case FOCI_UUID: sqlite3_result_text(ctx, pCsr->cf->uuid, -1, SQLITE_TRANSIENT); break; case FOCI_PREVNAME: sqlite3_result_text(ctx, pCsr->cf->priorName, -1, SQLITE_TRANSIENT); break; case FOCI_PERM: { char *perm[3] = {"l", "w", "x"}; int i = 1; switch( pCsr->cf->perm ){ case FSL_FILE_PERM_LINK: i = 0; break; case FSL_FILE_PERM_EXE: i = 2; break; default: break; } sqlite3_result_text(ctx, perm[i], 1, SQLITE_TRANSIENT); break; } case FOCI_SYMNAME: break; } return SQLITE_OK; } static int fociRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ FociCursor *pCsr = (FociCursor *)pCursor; *pRowid = pCsr->idx; return SQLITE_OK; } int fsl__foci_register(fsl_db * const db){ static sqlite3_module foci_module = { 0, /* iVersion */ fociConnect, /* xCreate */ fociConnect, /* xConnect */ fociBestIndex, /* xBestIndex */ fociDisconnect, /* xDisconnect */ fociDisconnect, /* xDestroy */ fociOpen, /* xOpen - open a cursor */ fociClose, /* xClose - close a cursor */ fociFilter, /* xFilter - configure scan constraints */ fociNext, /* xNext - advance a cursor */ fociEof, /* xEof - check for end of scan */ fociColumn, /* xColumn - read data */ fociRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0 /* xShadowName */ }; assert(db->f); int rc = sqlite3_create_module(db->dbh, "fsl_foci", &foci_module, db->f); return fsl__db_errcode(db, rc); } #undef MARKER /* end of file ./src/foci.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 |
︙ | ︙ | |||
27398 27399 27400 27401 27402 27403 27404 | #endif #include <assert.h> #include <string.h> /* strlen() */ #include <stddef.h> /* NULL on linux */ #include <ctype.h> #include <errno.h> | < < < < < < < < < > > < | 29187 29188 29189 29190 29191 29192 29193 29194 29195 29196 29197 29198 29199 29200 29201 29202 29203 29204 29205 29206 29207 29208 29209 29210 29211 29212 29213 29214 29215 29216 29217 29218 29219 | #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> const fsl_path_splitter fsl_path_splitter_empty = fsl_path_splitter_empty_m; /* 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 |
︙ | ︙ | |||
27900 27901 27902 27903 27904 27905 27906 | 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); } } | | | > > > > > > | > > > > | | | 29681 29682 29683 29684 29685 29686 29687 29688 29689 29690 29691 29692 29693 29694 29695 29696 29697 29698 29699 29700 29701 29702 29703 29704 29705 29706 29707 29708 29709 29710 29711 | 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); } } } const char *fsl_file_tail(const char *z){ const char *zTail = z; if( !zTail ) return 0; while( z[0] ){ if( '/'==z[0] || '\\'==z[0] ) zTail = &z[1]; z++; } return zTail; } int fsl_find_home_dir( fsl_buffer * const tgt, bool requireWriteAccess ){ char * zHome = NULL; int rc = 0; fsl_buffer_reuse(tgt); #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"); |
︙ | ︙ | |||
28316 28317 28318 28319 28320 28321 28322 | fsl_fstat fst; return ( 0 != fsl_stat(zFilename, &fst, 0) ) ? -1 : fst.size; } #endif | < < < | 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]; |
︙ | ︙ | |||
28358 28359 28360 28361 28362 28363 28364 | fsl_list_visit_free(&pf->ext, 1); fsl_list_visit_free(&pf->dirs, 1); fsl_buffer_clear(&pf->buf); *pf = fsl_pathfinder_empty; } } | | > | | | > > > > > > > | > > > > > > > | | > | | < | 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 | 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_impl(fsl_list * const li, char const * str, fsl_int_t strLen){ char * cp = fsl_strndup(str, strLen); 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 dir ? fsl__pathfinder_add_impl(&pf->dirs, dir, -1) : FSL_RC_MISUSE; } int fsl_pathfinder_dir_add2(fsl_pathfinder * const pf, char const * const dir, fsl_int_t strLen){ return dir ? fsl__pathfinder_add_impl(&pf->dirs, dir, strLen) : FSL_RC_MISUSE; } int fsl_pathfinder_ext_add(fsl_pathfinder * const pf, char const * const ext){ return (pf && ext) ? fsl__pathfinder_add_impl(&pf->ext, ext, -1) : FSL_RC_MISUSE; } int fsl_pathfinder_ext_add2(fsl_pathfinder * const pf, char const * const ext, fsl_int_t strLen){ return (pf && ext) ? fsl__pathfinder_add_impl(&pf->ext, ext, strLen) : 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 * const buf = &pf->buf; 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) '\\' /* TODO: confirm whether we can always use '/', and do so if we can. */ #else '/' #endif ; if(!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. */ if(pOut) *pOut = base; if(outLen) *outLen = fsl_strlen(base); return 0; } baseLen = fsl_strlen(base); ext = &pf->ext; dirs = &pf->dirs; |
︙ | ︙ | |||
28447 28448 28449 28450 28451 28452 28453 | assert(buf->used < buf->capacity); buf->mem[buf->used] = 0; if(0==fsl_file_access( (char const *)buf->mem, 0 )){ goto gotone; } } } | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30250 30251 30252 30253 30254 30255 30256 30257 30258 30259 30260 30261 30262 30263 30264 30265 30266 30267 30268 30269 30270 30271 30272 30273 30274 30275 30276 30277 30278 30279 30280 30281 30282 30283 30284 30285 30286 30287 30288 30289 30290 30291 30292 30293 30294 30295 30296 30297 30298 30299 30300 30301 30302 30303 30304 30305 30306 30307 30308 30309 30310 30311 30312 30313 30314 30315 30316 30317 30318 30319 30320 30321 30322 30323 30324 30325 30326 30327 30328 30329 30330 30331 | 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; } void fsl_path_splitter_init( fsl_path_splitter * pt, char const * path, fsl_int_t len ){ *pt = fsl_path_splitter_empty; pt->pos = pt->begin = path; pt->end = pt->begin + ((len>=0) ? (fsl_size_t)len : fsl_strlen(path)); } int fsl_path_splitter_next( fsl_path_splitter * const pt, char const ** token, fsl_size_t * const len ){ if(!pt->pos || pt->pos>=pt->end) return FSL_RC_RANGE; else if(!pt->separators || !*pt->separators) return FSL_RC_MISUSE; else{ char const * pos = pt->pos; char const * t; char const * sep; for( sep = pt->separators; *sep; ++sep){ if(*sep & 0x80) return FSL_RC_MISUSE; /* non-ASCII */ } for( ; pos<pt->end; ){ /*skip leading separators*/ for( sep = pt->separators; *sep && *pos!=*sep; ++sep ){ } if(*pos == *sep) ++pos; else break; } t = pos; for( ; pos<pt->end; ){ /*skip until the next separator*/ for( sep = pt->separators; *sep && *pos!=*sep; ++sep ){ } if(*pos == *sep) break; else ++pos; } pt->pos = pos; if(pos>t){ *token = t; *len = (fsl_size_t)(pos - t); return 0; } return FSL_RC_NOT_FOUND; } } int fsl_pathfinder_split( fsl_pathfinder * const tgt, bool isDirs, char const * path, fsl_int_t pathLen ){ int rc = 0; char const * t = 0; fsl_size_t tLen = 0; fsl_path_splitter pt = fsl_path_splitter_empty; fsl_path_splitter_init(&pt, path, pathLen); while(0==rc && 0==fsl_path_splitter_next(&pt, &t, &tLen)){ rc = isDirs ? fsl_pathfinder_dir_add2(tgt, t, (fsl_int_t)tLen) : fsl_pathfinder_ext_add2(tgt, t, (fsl_int_t)tLen); } return rc; } char * fsl__file_without_drive_letter(char * zIn){ #ifdef _WIN32 if( zIn && fsl_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; #endif return zIn; } |
︙ | ︙ | |||
28618 28619 28620 28621 28622 28623 28624 | *z = 0; ++rc; } } return rc; } | | | 30481 30482 30483 30484 30485 30486 30487 30488 30489 30490 30491 30492 30493 30494 30495 | *z = 0; ++rc; } } return rc; } void fsl_buffer_strip_slashes(fsl_buffer * const 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) |
︙ | ︙ | |||
28843 28844 28845 28846 28847 28848 28849 | to overwrite the symlinked-to file */; rc = fsl_buffer_to_filename(&content, zLinkFile); } } return rc; } | | > > > > > > > > > > > > | 30706 30707 30708 30709 30710 30711 30712 30713 30714 30715 30716 30717 30718 30719 30720 30721 30722 30723 30724 30725 30726 30727 30728 30729 30730 30731 30732 30733 30734 30735 30736 30737 30738 30739 30740 30741 | 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; } char const * fsl_last_path_sep(char const * str, fsl_int_t slen ){ if(slen<0) slen = (fsl_int_t)fsl_strlen(str); unsigned char const * pos = (unsigned char const *)str + slen; while( --pos >= (unsigned char const *)str ){ if('/'==*pos || '\\'==*pos){ return (char const *)pos; } } return NULL; } #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; |
︙ | ︙ | |||
28911 28912 28913 28914 28915 28916 28917 | 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; } | < | 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; } |
︙ | ︙ | |||
29176 29177 29178 29179 29180 29181 29182 | FILE * f = (FILE*) state; *n = (fsl_size_t)fread( dest, 1, *n, f ); return *n ? 0 : (feof(f) ? 0 : FSL_RC_IO); } | | | 31050 31051 31052 31053 31054 31055 31056 31057 31058 31059 31060 31061 31062 31063 31064 | 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 __unused, void * mem ){ if(mem){ fsl_fclose((FILE*)mem); } } int fsl_stream( fsl_input_f inF, void * inState, fsl_output_f outF, void * outState ){ |
︙ | ︙ | |||
29660 29661 29662 29663 29664 29665 29666 | if(rc) return rc; } self->list[self->used++] = cp; if(self->used<self->capacity) self->list[self->used]=NULL; return 0; } | | | 31534 31535 31536 31537 31538 31539 31540 31541 31542 31543 31544 31545 31546 31547 31548 | 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 __unused){ if(obj) fsl_free( obj ); return 0; } int fsl_list_clear( fsl_list * const self, fsl_list_visitor_f childFinalizer, void * finalizerState ){ /* |
︙ | ︙ | |||
30991 30992 30993 30994 30995 30996 30997 | ); } #define fsl_merge_state_empty_m { \ NULL/*f*/, \ NULL/*opt*/, \ NULL/*filename*/, \ | | | > | 32865 32866 32867 32868 32869 32870 32871 32872 32873 32874 32875 32876 32877 32878 32879 32880 32881 | ); } #define fsl_merge_state_empty_m { \ NULL/*f*/, \ NULL/*opt*/, \ NULL/*filename*/, \ NULL/*priorName*/, \ FSL_MERGE_FCHANGE_NONE/*fileChangeType*/,\ FSL_CKUP_RM_NOT/* fileRmInfo */ \ } /** 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; |
︙ | ︙ | |||
31485 31486 31487 31488 31489 31490 31491 | 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) | | | | 33360 33361 33362 33363 33364 33365 33366 33367 33368 33369 33370 33371 33372 33373 33374 33375 33376 33377 33378 33379 33380 33381 33382 33383 | 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: |
︙ | ︙ | |||
32581 32582 32583 32584 32585 32586 32587 | 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. */ | | | 34456 34457 34458 34459 34460 34461 34462 34463 34464 34465 34466 34467 34468 34469 34470 | 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'" |
︙ | ︙ | |||
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, | > > > | 34488 34489 34490 34491 34492 34493 34494 34495 34496 34497 34498 34499 34500 34501 34502 34503 34504 | ") LIMIT 1;", zType, zTag, zTag, zType ); } /** Modes for fsl_start_of_branch(). These values are hard-coded and must retain these values, else queries will break. */ enum fsl_stobr_type { /** The check-in of the parent branch off of which the branch containing RID originally diverged. */ FSL_STOBR_ORIGIN = 0, |
︙ | ︙ | |||
32636 32637 32638 32639 32640 32641 32642 | }; /* ** 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. */ | | < | | > | < | | | > > > > > > | | > > > | < < < < | < | < | < < | | | | | 34514 34515 34516 34517 34518 34519 34520 34521 34522 34523 34524 34525 34526 34527 34528 34529 34530 34531 34532 34533 34534 34535 34536 34537 34538 34539 34540 34541 34542 34543 34544 34545 34546 34547 34548 34549 34550 34551 34552 34553 34554 34555 34556 34557 34558 34559 34560 34561 34562 34563 34564 34565 34566 34567 34568 34569 34570 34571 34572 34573 34574 34575 34576 34577 34578 34579 34580 34581 34582 34583 34584 34585 34586 34587 34588 34589 34590 34591 34592 34593 34594 34595 | }; /* ** 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 * const f, fsl_id_t rid, enum fsl_stobr_type eType){ 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 -1; rc = fsl_cx_prepare(f, &q, "WITH RECURSIVE" " par(pid, ex, cnt) as (" " SELECT pid, EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid), 1" " FROM plink WHERE cid=%"FSL_ID_T_PFMT" AND isprim" " UNION ALL " " SELECT plink.pid, EXISTS(SELECT 1 FROM tagxref " " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid)," " 1+par.cnt" " FROM plink, par" " WHERE cid=par.pid AND isprim AND par.ex " " LIMIT 100000 " " )" " SELECT pid FROM par WHERE ex>=%d ORDER BY cnt DESC LIMIT 1", FSL_TAGID_BRANCH, zBr, ans, FSL_TAGID_BRANCH, zBr, eType%2 ); fsl_free(zBr); zBr = 0; if(rc){ ans = -2; MARKER(("Internal error: fsl_db_prepare() says: %s\n", fsl_rc_cstr(rc))); goto end; } if( FSL_RC_STEP_ROW == fsl_stmt_step(&q) ) { ans = fsl_stmt_g_id(&q, 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 * const rv ){ fsl_id_t rid = 0; fsl_id_t vid; fsl_size_t symLen; /* fsl_int_t i; */ fsl_db * const dbR = fsl_cx_db_repo(f); fsl_db * const dbC = fsl_cx_db_ckout(f); bool startOfBranch = 0; int rc = 0; if(!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 |
︙ | ︙ | |||
32779 32780 32781 32782 32783 32784 32785 | if(rid>0) goto gotit; } /* Deprecated time formats elided: local:..., utc:... */ /* "tag:" + symbolic-name */ if( memcmp(sym, "tag:", 4)==0 ){ | | > > > > > > > > > | 34657 34658 34659 34660 34661 34662 34663 34664 34665 34666 34667 34668 34669 34670 34671 34672 34673 34674 34675 34676 34677 34678 34679 34680 34681 34682 34683 34684 34685 34686 34687 34688 34689 34690 34691 34692 34693 34694 | 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; } /* start:TAG -> The first check-in on branch named TAG */ if( strncmp(sym, "start:", 6)==0 ){ rc = fsl_sym_to_rid(f, sym+6, type, &rid); if(!rc && rid>0){ rid = fsl_start_of_branch(f, rid, FSL_STOBR_FIRST_CI); } 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); } |
︙ | ︙ | |||
32815 32816 32817 32818 32819 32820 32821 | && 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; | | < < | 34702 34703 34704 34705 34706 34707 34708 34709 34710 34711 34712 34713 34714 34715 34716 | && 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. */ 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" |
︙ | ︙ | |||
32861 32862 32863 32864 32865 32866 32867 | " 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{ | | | | 34746 34747 34748 34749 34750 34751 34752 34753 34754 34755 34756 34757 34758 34759 34760 34761 34762 34763 34764 34765 34766 34767 34768 34769 34770 | " 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:### ==> validate that ### is a known 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 ){ |
︙ | ︙ | |||
32933 32934 32935 32936 32937 32938 32939 | assert(f->error.code); rv = -3; } return rv; } } | | | | 34818 34819 34820 34821 34822 34823 34824 34825 34826 34827 34828 34829 34830 34831 34832 34833 | assert(f->error.code); rv = -3; } return rv; } } int fsl_sym_to_uuid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_uuid_str * const rv, fsl_id_t * const 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){ |
︙ | ︙ | |||
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; } | > | 35022 35023 35024 35025 35026 35027 35028 35029 35030 35031 35032 35033 35034 35035 35036 | case FSL_RC_STEP_ROW: rc = 0; *rv = fsl_stmt_g_id(q, 0); break; case 0: rc = 0; *rv = 0; __attribute__ ((fallthrough)); default: fsl_cx_uplift_db_error(f, q->db); break; } fsl_stmt_reset(q); return rc; } |
︙ | ︙ | |||
33425 33426 33427 33428 33429 33430 33431 | 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); | | | 35311 35312 35313 35314 35315 35316 35317 35318 35319 35320 35321 35322 35323 35324 35325 | 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' |
︙ | ︙ | |||
34120 34121 34122 34123 34124 34125 34126 | * 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); | | | | | | | | | < | 36006 36007 36008 36009 36010 36011 36012 36013 36014 36015 36016 36017 36018 36019 36020 36021 36022 36023 36024 36025 36026 36027 36028 36029 36030 | * 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); fsl_buffer_append(sql, "SELECT rcvid, quote(uid), quote(mtime), " "quote(nonce), quote(ipaddr) " "FROM rcvfrom ", -1); }else{ assert(1==version); fsl_buffer_append(sql, "SELECT rcvid, quote(uid), datetime(mtime), " "quote(nonce), quote(ipaddr) " "FROM rcvfrom ", -1); } 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); |
︙ | ︙ | |||
34215 34216 34217 34218 34219 34220 34221 | rc = fsl_rid_to_uuid2(f, manifestRid, bHash); if(rc) goto end; ridHash = (char *)bHash->mem; }else{ ridHash = f->ckout.uuid; } assert(ridHash); | | | < | 36100 36101 36102 36103 36104 36105 36106 36107 36108 36109 36110 36111 36112 36113 36114 36115 | rc = fsl_rid_to_uuid2(f, manifestRid, bHash); if(rc) goto end; ridHash = (char *)bHash->mem; }else{ ridHash = f->ckout.uuid; } assert(ridHash); fsl_buffer_append(pHash, ridHash, -1); rc = fsl_buffer_append(pHash, "\n", 1); } 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 |
︙ | ︙ | |||
34239 34240 34241 34242 34243 34244 34245 | " 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__); | < | < < | < | 36123 36124 36125 36126 36127 36128 36129 36130 36131 36132 36133 36134 36135 36136 36137 36138 36139 36140 36141 36142 36143 36144 | " 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__); while( 0==rc && 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); } fsl_stmt_finalize(&q); } end: if(bHash) fsl__cx_scratchpad_yield(f, bHash); return rc; } /** Internal state for the rebuild process. */ struct FslRebuildState { |
︙ | ︙ | |||
34637 34638 34639 34640 34641 34642 34643 | 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 | | > > > > > > > | 36517 36518 36519 36520 36521 36522 36523 36524 36525 36526 36527 36528 36529 36530 36531 36532 36533 36534 36535 36536 36537 36538 | 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... 2022-07-31: we might want to stop doing this because: if fossil adds new tables, there may be a lag in getting them into libfossil and we don't necessarily want to nuke those. OTOH, all such tables would be transient/rebuildable state, so if we nuke them then a rebuild from fossil(1) would correct it. */ 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'," |
︙ | ︙ | |||
39438 39439 39440 39441 39442 39443 39444 | /** 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, | | | | | | 41325 41326 41327 41328 41329 41330 41331 41332 41333 41334 41335 41336 41337 41338 41339 41340 41341 41342 41343 41344 41345 41346 41347 41348 41349 41350 41351 | /** 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 __unused, sqlite3_value **argv __unused ){ 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 __unused, sqlite3_value **argv __unused ){ 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 |
︙ | ︙ | |||
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); | > | 41384 41385 41386 41387 41388 41389 41390 41391 41392 41393 41394 41395 41396 41397 41398 | 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); assert(2<=argc); 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); |
︙ | ︙ | |||
39662 39663 39664 39665 39666 39667 39668 | /* 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, | | | | 41550 41551 41552 41553 41554 41555 41556 41557 41558 41559 41560 41561 41562 41563 41564 41565 | /* 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 __unused, sqlite3_value **argv __unused ){ 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); |
︙ | ︙ | |||
39759 39760 39761 39762 39763 39764 39765 | 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 *) = | | | 41647 41648 41649 41650 41651 41652 41653 41654 41655 41656 41657 41658 41659 41660 41661 | 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 *) = fsl_cx_is_case_sensitive(f, false) ? 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; |
︙ | ︙ | |||
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); | > | 41692 41693 41694 41695 41696 41697 41698 41699 41700 41701 41702 41703 41704 41705 41706 | 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; assert(2<=argc); 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); |
︙ | ︙ | |||
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. */; | > | 41794 41795 41796 41797 41798 41799 41800 41801 41802 41803 41804 41805 41806 41807 41808 | 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 ); rc = fsl__foci_register(db); #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. */; |
︙ | ︙ | |||
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); | > | 41937 41938 41939 41940 41941 41942 41943 41944 41945 41946 41947 41948 41949 41950 41951 | void fsl_filename_free(void *pOld){ #if defined(_WIN32) fsl_free(pOld); #elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) fsl_free(pOld); #else (void)pOld; /* 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); |
︙ | ︙ | |||
40123 40124 40125 40126 40127 40128 40129 | 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 | | | | 42014 42015 42016 42017 42018 42019 42020 42021 42022 42023 42024 42025 42026 42027 42028 42029 | 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 (literally) 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. */ |
︙ | ︙ | |||
40281 40282 40283 40284 40285 40286 40287 | 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); | | | 42172 42173 42174 42175 42176 42177 42178 42179 42180 42181 42182 42183 42184 42185 42186 | 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_cx_transaction_begin(f); 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 |
︙ | ︙ | |||
40371 40372 40373 40374 40375 40376 40377 | when changing the checkout. */ if(!rc && vid>0){ if(!alreadyHad){ assert(d.rid>0); } } fsl_deck_finalize(&d); | | | | 42262 42263 42264 42265 42266 42267 42268 42269 42270 42271 42272 42273 42274 42275 42276 42277 | when changing the checkout. */ if(!rc && vid>0){ if(!alreadyHad){ assert(d.rid>0); } } fsl_deck_finalize(&d); if(rc) fsl_cx_transaction_end(f, true); else rc = fsl_cx_transaction_end(f, false); 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; } |
︙ | ︙ | |||
40673 40674 40675 40676 40677 40678 40679 | } fsl_stmt_cached_yield(stUpdate); fsl_stmt_finalize(&q); return rc; } int fsl__vfile_to_ckout(fsl_cx * const f, fsl_id_t vfileId, | | | 42564 42565 42566 42567 42568 42569 42570 42571 42572 42573 42574 42575 42576 42577 42578 | } 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; |
︙ | ︙ | |||
40811 40812 40813 40814 40815 40816 40817 40818 40819 40820 40821 40822 40823 40824 | default: break; } 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: */ /* | > > > > > > > > > > > > > | 42702 42703 42704 42705 42706 42707 42708 42709 42710 42711 42712 42713 42714 42715 42716 42717 42718 42719 42720 42721 42722 42723 42724 42725 42726 42727 42728 | default: break; } end: fsl_buffer_clear(&content); fsl_stmt_finalize(&q); return rc; } int fsl_vfile_pathname(fsl_cx * const f, fsl_id_t vfid, bool absolute, char **zOut){ assert(f->ckout.dir); fsl_db * const db = fsl_cx_db_ckout(f); assert(db); int const rc = fsl_db_get_text(db, zOut, NULL, "SELECT %Q || pathname FROM vfile " "WHERE id=%"FSL_ID_T_PFMT, absolute ? f->ckout.dir : "", vfid); if(rc) fsl_cx_uplift_db_error(f, db); 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: */ /* |
︙ | ︙ | |||
41485 41486 41487 41488 41489 41490 41491 | 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() */ | < < < < < < | | | | | | < | | | | | | | | | | < < | < | | | | | | | | | | | | | < > | | < | 43389 43390 43391 43392 43393 43394 43395 43396 43397 43398 43399 43400 43401 43402 43403 43404 43405 43406 43407 43408 43409 43410 43411 43412 43413 43414 43415 43416 43417 43418 43419 43420 43421 43422 43423 43424 43425 43426 43427 43428 43429 43430 43431 43432 43433 43434 43435 43436 43437 43438 43439 43440 43441 43442 43443 43444 43445 43446 43447 43448 43449 43450 43451 43452 43453 43454 43455 43456 43457 43458 43459 43460 43461 43462 43463 43464 43465 43466 43467 43468 43469 43470 43471 43472 43473 43474 43475 43476 43477 43478 43479 43480 43481 43482 43483 43484 43485 43486 43487 43488 43489 43490 43491 43492 43493 43494 43495 43496 43497 43498 43499 43500 43501 43502 43503 43504 43505 43506 43507 43508 43509 43510 43511 43512 43513 43514 43515 43516 43517 43518 43519 43520 43521 43522 43523 43524 43525 43526 43527 43528 43529 43530 43531 43532 43533 43534 43535 43536 43537 43538 43539 43540 43541 43542 43543 43544 | 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. */ 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->body; } 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; unsigned 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 * 32]; /* 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){ fsl_buffer_reuse(&zw->scratch); rc = fsl_buffer_appendf(&zw->scratch, "%s%s", zw->rootDir, zName); if(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(zw->body.errCode) return zw->body.errCode; else 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; |
︙ | ︙ | |||
41660 41661 41662 41663 41664 41665 41666 | } 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); | | > | | | | | | | < | | | | | | | | | | | | | | | | | | < | | | | | 43553 43554 43555 43556 43557 43558 43559 43560 43561 43562 43563 43564 43565 43566 43567 43568 43569 43570 43571 43572 43573 43574 43575 43576 43577 43578 43579 43580 43581 43582 43583 43584 43585 43586 43587 43588 43589 43590 43591 43592 43593 43594 43595 43596 43597 43598 43599 43600 43601 43602 43603 43604 43605 43606 43607 43608 43609 43610 43611 43612 43613 43614 43615 43616 43617 43618 43619 43620 43621 43622 43623 43624 43625 43626 43627 43628 43629 43630 43631 43632 43633 43634 43635 43636 43637 43638 | } 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 && 0==zw->body.errCode ); if(zw->body.errCode) return zw->body.errCode; 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; 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 zw->toc.errCode; } 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{ |
︙ | ︙ | |||
41774 41775 41776 41777 41778 41779 41780 | 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 | | | | < | | | | | | | | | | 43666 43667 43668 43669 43670 43671 43672 43673 43674 43675 43676 43677 43678 43679 43680 43681 43682 43683 43684 43685 43686 43687 43688 43689 43690 43691 43692 43693 43694 43695 43696 43697 43698 43699 43700 43701 43702 43703 43704 43705 43706 43707 43708 43709 43710 43711 43712 43713 43714 43715 43716 43717 43718 43719 43720 43721 43722 43723 43724 43725 43726 43727 43728 43729 43730 43731 43732 43733 43734 43735 43736 | 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(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(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(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{ |
︙ | ︙ | |||
41870 41871 41872 41873 41874 41875 41876 | } } fsl_zip_finalize( z ); return rc; } } | < < < | | 43761 43762 43763 43764 43765 43766 43767 43768 43769 43770 43771 43772 43773 43774 43775 43776 43777 43778 43779 43780 43781 43782 43783 43784 43785 43786 43787 43788 43789 43790 43791 43792 43793 43794 43795 | } } 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; fsl_buffer cbuf; }; typedef struct ZipState ZipState; static const ZipState ZipState_empty = { NULL, 0, NULL, NULL, fsl_zip_writer_empty_m, fsl_buffer_empty_m }; 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(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."); } |
︙ | ︙ | |||
41916 41917 41918 41919 41920 41921 41922 | 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, | | < | 43804 43805 43806 43807 43808 43809 43810 43811 43812 43813 43814 43815 43816 43817 43818 43819 43820 43821 43822 43823 43824 43825 43826 43827 43828 43829 | 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, fc->perm); 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; |
︙ | ︙ | |||
42012 42013 42014 42015 42016 42017 42018 | rc, fsl_rc_cstr(rc)); } fsl_buffer_clear(&zs.cbuf); fsl_zip_finalize(&zs.z); fsl_deck_clean(&mf); return 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. */ |
︙ | ︙ |
Changes to lib/libfossil.h.
︙ | ︙ | |||
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: */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /** If true, the fsl_timer_xxx() family of functions might do something useful, otherwise they do not. */ #define FSL_CONFIG_ENABLE_TIMER 1 #if !defined(FSL_SWITCH_FALL_THROUGH) # if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 7) /* #define FSL_USING_GCC gcc v7+ treats implicit 'switch' fallthrough as a warning (i.e. error because we always build with -Wall -Werror -Wextra -pedantic). Because now it's apparently considered modern to warn for using perfectly valid features of the language. Holy cow, guys, what the hell were you thinking!?!?!? Similarly braindead, clang #defines __GNUC__. _Sigh_. */ # define FSL_SWITCH_FALL_THROUGH __attribute__ ((fallthrough)) # else # define FSL_SWITCH_FALL_THROUGH # endif #endif /* /FSL_SWITCH_FALL_THROUGH TODO: add support for the C++ attributes for doing this. */ #if !defined(__unused) #define __unused #endif #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: */ |
︙ | ︙ | |||
736 737 738 739 740 741 742 | 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. | > > > | | > | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | 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. If fsl_buffer_err() is true for the state argument, that code is returned without other side effects. Returns 0 on success and has no error conditions except for invalid arguments, which result in undefined beavhiour, or fsl_buffer_err() being true for the state argument. 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: ``` |
︙ | ︙ | |||
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` | > > | 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | @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() @see fsl_buffer_err() @see fsl_buffer_err_clear() */ 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` |
︙ | ︙ | |||
934 935 936 937 938 939 940 941 | 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. | > > > > > > > | > > > > | < < > | < > | | | 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 | 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. */ fsl_size_t cursor; /** When any buffer-related routines encounter an error, they set this flag so that the error can be propagated. Many APIs also become no-ops when this is set, the intention being to simplify many common uses of this class, e.g.: ``` fsl_buffer_append(b, ... ); fsl_buffer_append(b, ... ); fsl_buffer_append(b, ... ); if(fsl_buffer_err(b)) ... ``` The alternative (without this member) being to check for an OOM error after every append operation. */ int errCode; }; /** Empty-initialized fsl_buffer instance, intended for const-copy initialization. */ #define fsl_buffer_empty_m {NULL,0U,0U,0U,0} /** 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 |
︙ | ︙ | |||
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 | 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 | > > > > > > > > > > > > > > > > | > | | > | 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 | 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); /** Returns the current error code of the given buffer. Many buffer APIs become noops when this function returns non-0. */ FSL_EXPORT int fsl_buffer_err(fsl_buffer const * b); /** Resets the error code of the given buffer to 0. */ FSL_EXPORT void fsl_buffer_err_clear(fsl_buffer * const b); /** 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 fsl_buffer_err() is true and n is not 0 then this function returns its value without other side effects, with one exception: if n is 0 then the error state is ignored. 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) - sets b->capacity, buf->used, and b->cursor to 0 - sets b->errCode 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 ); |
︙ | ︙ | |||
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 | 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 | > > > > > > > | | | | 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 | 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. This function resets b->errCode. @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. This function resets b->errCode. 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... - If fsl_buffer_err() is true, that result is returned with no other side effects. 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. |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 | @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 ); /** | | | | 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 | @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 entire state 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 * const left, fsl_buffer * const 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 |
︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 | ``` fsl_buffer_swap_free( &b1, &b2, 1 ); ``` Swaps the contents of b1 and b2, then frees the contents of the right-side buffer (b2). */ | | > > > > | 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 | ``` 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 * const left, fsl_buffer * const 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 fsl_buffer_err() is true, its result is returned without further side effects. 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 |
︙ | ︙ | |||
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 | void const * src, fsl_int_t n ); /** 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. | > > > > > > > | | > > | > > > > | > > > > | | > | 1435 1436 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 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 | void const * src, fsl_int_t n ); /** 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. If fsl_buffer_err() is true, its result is returned without further side effects. @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. If fsl_buffer_err() is true for either buffer, its result is returned without further side effects. The buffers are checked in the order of their parameter declaration. 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. If this function returns non-0, it does not update pOut's error state because the error will have happened on a temporary buffer. 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 * const 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. If fsl_buffer_err() is true for any buffer, its result is returned without further side effects. The buffers are checked in the order of their parameter declaration. 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 * const 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). If fsl_buffer_err() is true for either buffer, its result is returned without further side effects. The buffers are checked in the order of their parameter declaration. 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. On error, pOut->errCode is updated. 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. |
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 | /** The fsl_buffer counterpart of fsl_data_uncompressed_size(). */ FSL_EXPORT fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b); /** | | > | | > > | | > | > > > | | < | 1570 1571 1572 1573 1574 1575 1576 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 | /** 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) except that if b->errCode is non-0, this returns NULL. 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. Results are undefined if b is NULL. @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 and b->errCode is non-0, that memory is returned. If len is not NULL then *len is set to b->used. If b has no memory or b->errCode is non-0 then NULL is returned and *len (if len is not NULL) is set to 0. Results are undefined if b is NULL. @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) except that if b->errCode is non-0 then NULL is returned. 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 |
︙ | ︙ | |||
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. | > > > > > > > > > | 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 | 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); /** Compares b and the first nStr bytes of the given string. If nStr is negative, fsl_strlen() is used to calculate it. Returns true if the buffer and the string match, as per the rules of fsl_buffer_compare(), else returns false. */ FSL_EXPORT bool fsl_buffer_eq(fsl_buffer const * const b, char const * str, fsl_int_t nStr); /** 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. |
︙ | ︙ | |||
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 | 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. | > > > > > > > > > > > | < < | > | > | < | 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 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 | 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. If fsl_buffer_err() is true for any buffer, its result is returned without further side effects. The buffers are checked in the order of their parameter declaration. 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. If fsl_buffer_err() is true for any buffer, its result is returned without further side effects. The buffers are checked in the order of their parameter declaration. @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. If fsl_buffer_err() is true then this function returns its value without other side effects. 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 be partially populated. Errors include: - Allocation error (FSL_RC_OOM), in which case dest->errCode is updated. - src() returns an error code, in which case dest-errCode is _not_ modified. 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, |
︙ | ︙ | |||
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 | */ 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); | > > > > > > > > > > > > | 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 1869 1870 1871 1872 1873 1874 1875 1876 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 | */ 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. If fsl_buffer_err() is true then this function returns its value without other side effects. 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. If fsl_buffer_err() is true, that result is returned with no other side effects. 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 fsl_buffer_err() is true for either buffer (checked in parameter order), that result is returned with no other side effects. 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. If fsl_buffer_err() is true, that result is returned with no other side effects. @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); |
︙ | ︙ | |||
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, | > > > > > > > | 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 | 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); /** Return the tail of a NUL-terminated file pathname. The tail is the last component of the path. For example, the tail of "/a/b/c.d" is "c.d". If the name ends in a slash, a pointer to its NUL terminator is returned. */ FSL_EXPORT const char *fsl_file_tail(const char *z); /** 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, |
︙ | ︙ | |||
2759 2760 2761 2762 2763 2764 2765 | The returned type is (wchar_t*) on Windows and (char*) everywhere else. */ FSL_EXPORT void *fsl_utf8_to_filename(const char *zUtf8); /** | | | | | 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 | 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 be NULL or 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 |
︙ | ︙ | |||
3090 3091 3092 3093 3094 3095 3096 | 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. | | | | > > > > > > > > > > > > > | 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 | 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 with FSL_RC_TYPE 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); /** Reads symlink zFrom, as per fsl_symlink_read(), then creates a copy named zTo, as per fsl_symlink_create(). The first argument for the latter call is the contents of the result of fsl_symlink_read(). The 2nd and 3rd arguments to fsl_symlink_create() are the 2nd and 3rd arguments to this function. Returns 0 on success, FSL_RC_OOM on OOM, or any of various filesystem-related non-0 codes if reading or saving the link fails. */ FSL_EXPORT int fsl_symlink_copy(char const *zFrom, char const *zTo, 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 |
︙ | ︙ | |||
3162 3163 3164 3165 3166 3167 3168 | 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. */ | | | 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 | 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 * const 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, |
︙ | ︙ | |||
3288 3289 3290 3291 3292 3293 3294 | 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. | < | > > | 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 | 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_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). Results are undefined if any pointer is NULL. 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 |
︙ | ︙ | |||
3389 3390 3391 3392 3393 3394 3395 | /** 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). | | > | > > | | | | | 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 | /** 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 any of the buffers have the same address, FSL_RC_MISUSE is returned. If fsl_buffer_err() is true for the 3rd argument, that value is returned without side effects. If allocation of memory for the delta fails, FSL_RC_OOM is returned and delta's errCode is updated with that value. Results are undefined 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 |
︙ | ︙ | |||
3941 3942 3943 3944 3945 3946 3947 | /** Convenience typedef. */ typedef struct fsl_dline fsl_dline; /** Initialized-with-defaults fsl_dline structure, intended for const-copy initialization. */ | | | 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 | /** 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,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. */ |
︙ | ︙ | |||
4354 4355 4356 4357 4358 4359 4360 | 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*/, \ | | | 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 | 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*/, \ {/*metrics*/0,0,0,0} \ } /** Initialized-with-defaults fsl_dibu structure, intended for non-const copy initialization. */ extern const fsl_dibu fsl_dibu_empty; /** |
︙ | ︙ | |||
4807 4808 4809 4810 4811 4812 4813 | /** 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); /** | | < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 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 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 | /** 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); /** Equivalent to fsl_pathfinder_dir_add2() with -1 as a final argument. @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 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. If strLen is negative, fsl_strlen() is used to calculate the length of the string. @see fsl_pathfinder_ext_add() @see fsl_pathfinder_search() */ FSL_EXPORT int fsl_pathfinder_dir_add2(fsl_pathfinder * const pf, char const * const dir, fsl_int_t strLen); /** 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_add2(fsl_pathfinder * const pf, char const * const ext, fsl_int_t strLen); /** Equivalent to fsl_pathfinder_ext_add2() with -1 as a final argument. @see fsl_pathfinder_dir_add() @see fsl_pathfinder_search() */ FSL_EXPORT int fsl_pathfinder_ext_add(fsl_pathfinder * const pf, char const * const ext); /** Splits a conventional path-separator-delimited string into tokens and adds each as either a directory (if isDirs is true) or an extension in the given fsl_pathfinder object. Returns 0 on success, FSL_RC_OOM on allocation error. pathLen is the length of the path string. If it's negative, fsl_strlen() is used to calculate it. See fsl_path_splitter for the semantics of the splitting. To change the delimiter characters, set tgt->separators before calling this. */ FSL_EXPORT int fsl_pathfinder_split( fsl_pathfinder * const tgt, bool isDirs, char const * path, fsl_int_t pathLen ); /** 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: |
︙ | ︙ | |||
4856 4857 4858 4859 4860 4861 4862 | extensions, so "~" and "-foo" are legal extensions). Check for a match. On success (a readable filesystem entry is found): - It returns 0. | | | | | | | | > | | | | | | | | | < | 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 | 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). As a special case, if `base` is found as-is, without a lookup, `*pOut` is set to `base`, so has its lifetime. - 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 surpass 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 |
︙ | ︙ | |||
5153 5154 5155 5156 5157 5158 5159 | (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 ); /** | | | | 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 | (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() |
︙ | ︙ | |||
5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 | fsl_buffer * const pOut, 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. */ | > > > | 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 | fsl_buffer * const pOut, 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 fsl_buffer_err() is true for the given buffer, that code is returned without other side effects. 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. */ |
︙ | ︙ | |||
5936 5937 5938 5939 5940 5941 5942 | /** 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. */ | | > > > | > | 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 | /** 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 * const b); /** Appends each ID from the given bag to the given buffer using the given separator string. Returns FSL_RC_OOM on allocation error. If fsl_buffer_err() is true for the given buffer, that code is returned without other side effects. */ FSL_EXPORT int fsl_id_bag_to_buffer(fsl_id_bag const * bag, fsl_buffer * const b, char const * separator); /** Flags for use with the fsl_looks family of functions. */ enum fsl_lookslike_e { /* Nothing special was found. */ |
︙ | ︙ | |||
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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 | 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); /** Given a filename and its length, this function returns a pointer to the last instance of a path separator character in that string, checking for both `/` and `\\`. If the length is negative, fsl_strlen() is used to calculate it. If no separator is found, NULL is returned. If it returns non-NULL, the pointer is guaranteed to live somewhere between the half-open range [str,str+slen). */ FSL_EXPORT char const * fsl_last_path_sep(char const * str, fsl_int_t slen ); /** A helper type for tokenizing conventional PATH-style strings. Initialize them with fsl_path_splitter_init() and iterate over them with fsl_path_splitter_next(). */ struct fsl_path_splitter { /** Begining of the input range. */ char const * begin; /** One-after-the-end of the input range. */ char const * end; /** Position for the next token lookup. */ char const * pos; /** List of token separator characters (ASCII only). */ char const * separators; }; typedef struct fsl_path_splitter fsl_path_splitter; /** @def fsl_path_splitter_empty_m Default-initialized fsl_path_splitter instance, intended for const-copy initialization. On Windows builds its separators member is set to ";" and on other platforms it's set to ":;". */ #if FSL_PLATFORM_IS_WINDOWS # define fsl_path_splitter_empty_m {NULL,NULL,NULL,";"} #else # define fsl_path_splitter_empty_m {NULL,NULL,NULL,":;"} #endif /** Default-initialized fsl_path_splitter instance, intended for copy initialization. @see fsl_path_splitter_empty_m */ FSL_EXPORT const fsl_path_splitter fsl_path_splitter_empty; /** Wipes out pt's current state by copying fsl_path_splitter_empty over it and initializes pt to use the given path as its input. If len is 0 or more then it must be the length of the string, in bytes. If len is less than 0, fsl_strlen() is used to determine the path's length. (When dealing with inputs which are not NUL-terminated, it's critical that the user pass the correct non-negative length.) If the client wants to modify pt->separators, it must be done so *after* calling this. Use fsl_path_splitter_next() to iterate over the path entries. */ void fsl_path_splitter_init( fsl_path_splitter * const pt, char const * path, fsl_int_t len ); /** Given a fsl_path_splitter which was formerly initialized using fsl_path_splitter_init(), this iterates over the next-available path component in the input, skipping over empty entries (leading, consecutive, or trailing separator characters). The separator characters are specified by pt->separators, which must be a NUL-terminated string of 1 or more characters. If a non-empty entry is found then: - *token is set to the first byte of the entry. - *len is assigned to the byte length of the entry. If no entry is found then: - *token, and *len are not modified. - FSL_RC_NOT_FOUND is returned if the end of the path was found while tokenizing. - FSL_RC_MISUSE is returned if pt->separators is NULL or empty or contains any non-ASCII characters. - FSL_RC_RANGE is returned if called after the previous case, or if the input object's path has a length of 0. In any non-0-return case, it's not a fatal error, it's simply information about why tokenization cannot continue, and can normally be ignored. After non-0 is returned, the tokenizer must be re-initialized if it is to be used again. Example: @code char const * t = 0; fsl_size_t tLen = 0; fsl_path_splitter pt = fsl_path_splitter_empty; fsl_path_splitter_init(&pt, path, pathLen); while(0==fsl_path_splitter_next(&pt, &t, &tLen)){ // The next element is the tLen bytes of memory starting at t: printf("Path element: %.*s\n", (int)tLen, t); } @endcode */ int fsl_path_splitter_next( fsl_path_splitter * const pt, char const ** token, fsl_size_t * const len ); /** Implements a cross-platform system(3) interface. It passes its argument, which must not be NULL or empty to the equivalent of system(). Returns 0 if that call fails, else it will return a FSL_RC code approximating the lower-level error code (noting that this system's man pages are a bit vague on the exact return semantics of system(3)). */ int fsl_system(const char *zOrigCmd); #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 |
︙ | ︙ | |||
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). | > > > > > > > > > | 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 | /** The fsl namespace is reserved for an eventual C++ wrapper for the API. */ namespace fsl {} extern "C" { #endif /** @internal An internal helper macro to help localize OOM error reports (which are most often side effects of other issues, rather than being real OOM cases). */ #define FSL__WARN_OOM \ fprintf(stderr,"OOM @ %s:%d\n", __FILE__, __LINE__) /** @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). |
︙ | ︙ | |||
6362 6363 6364 6365 6366 6367 6368 | 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 { | | | | | | | > | | 6693 6694 6695 6696 6697 6698 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 | 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, making it "impossible" to get SHA1-hashed content into the repository (for a given value of "impossible"). */ 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 |
︙ | ︙ | |||
6410 6411 6412 6413 6414 6415 6416 | */ FSL_RC_NYI = 101, /** Out of memory. Indicates that a resource allocation request failed. */ FSL_RC_OOM = 102, | | | 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 | */ 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, |
︙ | ︙ | |||
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, /** | > > > > > > | 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 | 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 a resource-locking error of some sort, normally a db lock. */ FSL_RC_LOCKED, /** Indicates that a merge conflict, or some other context-dependent type of conflict, was detected. */ FSL_RC_CONFLICT, /** |
︙ | ︙ | |||
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 }; | > > > > > > > > > > > > > > | 6971 6972 6973 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 | /** Intended to be used with fsl_cx_interrupt() by signal handlers and UI threads. */ FSL_RC_INTERRUPTED, /** Intended to be used by operations which would cause what is presumably an unintended fork. Fossil does not have any issues with forking, but practice suggests that most forks (that is, checking in to a non-leaf version) are unintentional. */ FSL_RC_WOULD_FORK, /** This is intended only for internal use with fsl__fatal(), to report conditions which "cannot possibly happen." */ FSL_RC_CANNOT_HAPPEN, /** 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 }; |
︙ | ︙ | |||
6669 6670 6671 6672 6673 6674 6675 | 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 | | > | | 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 | 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. If passed a code which is not in the fsl_rc_e enum, it returns NULL. */ FSL_EXPORT char const * fsl_rc_cstr(int rc); /** 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 |
︙ | ︙ | |||
6729 6730 6731 6732 6733 6734 6735 | 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. | | | 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 | 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 or a fsl_outputer. */ 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; |
︙ | ︙ | |||
6853 6854 6855 6856 6857 6858 6859 | @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 | | | > > | 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 | @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). If it was allocated using fsl_cx_malloc(), this function frees f, else the memory is owned by someone else and it is not freed. 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. */ |
︙ | ︙ | |||
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: | > > > > > > > > > > > > > > > > | 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 | 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); /** Clears various internal caches and resets various internally-cached values related to repository and checkout dbs. 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 (and it is internally used so - _do not_ call this after initiating a rollback!). It can also be used when shelling out to an external app like fossil(1) which might update the db or checkout. This "really should not" be called while a transaction is underway. */ FSL_EXPORT void fsl_cx_caches_reset(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: |
︙ | ︙ | |||
9396 9397 9398 9399 9400 9401 9402 | 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 | | < > > > | 9767 9768 9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 9789 9790 | 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, appending its results to the given buffer. 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. Results are undefined if any pointer argument is NULL. Returns FSL_RC_MISUSE if the SQL is an empty string. */ 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(). |
︙ | ︙ | |||
13458 13459 13460 13461 13462 13463 13464 | 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. | | | < | | > > > > > | | | < < | | > | | | | 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 13843 13844 13845 13846 13847 13848 13849 13850 13851 13852 13853 13854 13855 13856 13857 13858 13859 13860 13861 13862 13863 13864 13865 13866 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 13886 13887 13888 13889 13890 13891 13892 13893 13894 13895 13896 13897 13898 13899 13900 13901 13902 13903 13904 13905 13906 13907 | 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 !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, e.g. branch name - "tag:" + symbolic name - Date or date-time - "date:" + Date or date-time - symbolic-name ":" date-time - "tip" means the most recent checkin, regardless of its branch - "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. - "start:" prefix resolves to the first checkin of the branch to which the given checkin belongs. This differs from "root:" by a single checkin: the "root:" point is the parent checkin of the "start:" point. - "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 * const 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 one of any number of possible result codes on error, most notably FSL_RC_NOT_FOUND if no match is found. */ FSL_EXPORT int fsl_sym_to_uuid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_uuid_str * const rv, fsl_id_t * const 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. |
︙ | ︙ | |||
14184 14185 14186 14187 14188 14189 14190 | 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. */ | | | 14560 14561 14562 14563 14564 14565 14566 14567 14568 14569 14570 14571 14572 14573 14574 | 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,FSL_SATYPE_INVALID} /** 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 |
︙ | ︙ | |||
14351 14352 14353 14354 14355 14356 14357 | }; /** Convenience typedef. */ typedef struct fsl_cidiff_opt fsl_cidiff_opt; /** Initialized-with-defaults fsl_cidiff_opt structure, intended for const-copy initialization. */ | | | 14727 14728 14729 14730 14731 14732 14733 14734 14735 14736 14737 14738 14739 14740 14741 | }; /** 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,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 |
︙ | ︙ | |||
17005 17006 17007 17008 17009 17010 17011 | 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. */ | | | | 17381 17382 17383 17384 17385 17386 17387 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 | 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 * const f, fsl_id_t vid, char const * zName, fsl_id_t * const 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 |
︙ | ︙ | |||
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 */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 18017 18018 18019 18020 18021 18022 18023 18024 18025 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 18064 18065 18066 18067 18068 18069 18070 18071 18072 18073 18074 18075 18076 18077 18078 18079 18080 18081 18082 18083 18084 18085 18086 18087 18088 18089 18090 18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 18103 18104 18105 18106 18107 18108 18109 18110 18111 18112 18113 18114 18115 18116 18117 18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133 18134 18135 18136 18137 18138 18139 18140 18141 18142 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158 18159 18160 18161 18162 18163 18164 18165 18166 18167 18168 18169 18170 18171 18172 18173 18174 18175 18176 18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 18188 18189 18190 18191 18192 18193 18194 18195 18196 18197 18198 18199 18200 18201 18202 18203 18204 18205 18206 18207 18208 18209 18210 18211 18212 18213 18214 18215 18216 18217 18218 18219 18220 18221 18222 18223 18224 18225 18226 18227 18228 18229 18230 18231 18232 18233 18234 18235 18236 18237 18238 18239 18240 18241 18242 18243 18244 18245 18246 18247 | 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); /** Convenience typedef. */ typedef struct fsl_ckout_rename_opt fsl_ckout_rename_opt; /** Callback type for use with fsl_ckout_rename(). It gets called once per iteration of a rename operation, along with the original options object for the rename and: - zSrcName is the checkout-relative original name of the file. - zDestname is the checkout-relative new name of the file. Both strings are owned by the calling operation and will be freed soon after this call returns. If the client needs them, they must make copies. It must returns 0 on success. If it returns non-0 then the current renaming op is cancelled (rolled back) and the result code is propagated back to the caller of fsl_ckout_rename(). This gets called immediately after the associated db record is modified and before the filesystem move (if any) is performed. Design note: ideally the callback would be called after the filesystem move, but the way the moves are currently processed (as a batch after the db updates, noting that those updates may trigger variou error conditions) precludes that. Alternately, we could delay the callback until the file-move phase (noting that the file-move step is optional), and that might be a sensible change to make. */ typedef int (*fsl_ckout_rename_f)(fsl_cx *, fsl_ckout_rename_opt const *, char const * zSrcName, char const *zDestName); /** Options object for use with fsl_ckout_rename(). */ struct fsl_ckout_rename_opt { /** The source filename(s) or directory name(s) to rename. The contents of this list _must not_ be modified while fsl_ckout_rename() is running (e.g. via a callback function). */ fsl_list const * src; /** The target filename or directory name to rename to. */ char const * dest; /** If true, src and dest are resolved/normalized based on the current working directory, else they must be relative to the top of the checkout. */ bool relativeToCwd; /** If true fsl_ckout_rename() will attempt to move files within the filesystem. If false, it will only do the db-side renaming. */ bool doFsMv; /** If true, fsl_ckout_rename() will not perform any lasting operations but will "go through the motions" with the exception of actually attempting to move files on disk (if doFsMv is true). It will still report errors in this mode, with the exception that filesystem-level moving is not attempted so potential failures there cannot be detected. Achtung: in dry-run mode it will trigger a rollback of any pending transaction which is opened before fsl_ckout_rename() is called. */ bool dryRun; /** Optional callback. May be NULL. */ fsl_ckout_rename_f callback; /** Optional state for the callback. */ void * callbackState; }; /** Initialized-with-defaults fsl_ckout_rename_opt structure, intended for const-copy initialization. */ #define fsl_ckout_rename_opt_empty_m {NULL,NULL,true,false,false,NULL,NULL} /** Initialized-with-defaults fsl_ckout_rename_opt structure, intended for non-const copy initialization. */ extern const fsl_ckout_rename_opt fsl_ckout_rename_opt_empty; /** This routine renames one or more SCM-managed files matching all entries in opt->src to opt->dest within the current checkout. This updates the current checkout's vfile table with the changes, and optionally attempts to move the files within the filesystem, but does not commit anything, so the change may still be reverted later on. If an entry in opt->src matches an existing directory name within the SCM-managed file list, this function assumes that all files under that directory are to be moved. If opt->relativeToCwd is true then the destination name and all opt->src names are canonicalized based on the current working directory (see fsl_getcwd()), otherwise they are assumed to be relative to f's current checkout directory. If opt->dest refers to an _existing_ directory, when an opt->src entry refers to a directory then that whole directory, including the directory name part, is renamed. If opt->dest does not exist in the filesystem and if opt->src resolves to multiple inputs, opt->dest is assumed to be a new target directory but the renaming behavior of the opt->src files differs. Examples: - Case 1: opt->dest refers to an existing directory: moving directory foo/bar to baz will result in baz/bar. - Case 2: opt->dest does not refer to an existing directory: moving directory foo/bar to baz results in baz with the previous contents of foo/bar. Why? Because that's how fossil(1) does it. This function matches opt->src entries only against `vfile.pathname`, not `vfile.origname`. If opt->doFsMv is true then the final operation this function attempts is to rename source files to their new destination. It will only attempt this in cases where (A) the original file exists and (B) the destination file does _not_ exist. Any errors generated during this filesystem-level rename/move step _are ignored_ for two reasons: 1) fossil(1) does it that way and (2) after moving any files, trying to roll back and undo the filesystem-level operations would add a significant amount of complexity and new errror cases (what happens when a try-to-undo-rename fails?). Returns 0 on succcess. Potential errors include: - FSL_RC_OOM on allocation error. - FSL_RC_NOT_A_CKOUT if f has no opened checkout. - FSL_RC_MISUSE: opt->src contains multiple entries but the destination is not an _existing_ directory. - FSL_RC_ALREADY_EXISTS: cannot rename because the destination name is already in use by another record. - FSL_RC_TYPE: opt->dest refers to a non-directory and an opt->src entry refers to a directory. - Any potential DB-related errors. BUGS/Shortcomings: - If it fails moving files in the filesystem, it might have successfully already moved one or more source files, and those moves are not undone. - Depending on the case-sensitivity of the repo and the filesystem, it might (UNTESTED) fail to rename a file if the new name differs only in its case (e.g. `README.TXT` to `README.txt`). Those relavant code renames such vfile entries but leaves the filesystem entries intact because fossil(1) does it that way. - If it moves a whole directory full of files it leaves any original directories intact. Improving this is on the TODO list. Often, such directories have contents (e.g. compiled/generated/temp files) and cannot be removed. */ FSL_EXPORT int fsl_ckout_rename(fsl_cx * const f, fsl_ckout_rename_opt const * opt); /** Undoes a rename scheduled by fsl_ckout_rename(). zNewName is the current (renamed) name of the file. If relativeToCwd is true then zNewName is canonicalized based on the current working directory (see fsl_getcwd()), otherwise it is assumed to be relative to f's current checkout directory. On success, or if no such pending rename is found, it returns 0. If it actually performs a db-level rename then it sets `*didSomething` to true if didSomething is not NULL. It should arguably return FSL_RC_NOT_FOUND (or some such) if no matching rename is pending, but that currently feels overly-pedantic. If doFsMv is true and a db entry is renamed and a file with the new name exists in the filesystem then this function will _delete_ any file which has the original name and rename the zNewName file to its reverted name. If that fails, the operation as a whole will fail and the db changes will be rolled back. Note, however, that a file deleted by this operation cannot be recovered and that the deletion must be done before the rename is attempted. On error, returns any of a wide array of non-0 result codes, only two of which can come directly from this function: FSL_RC_OOM or FSL_RC_NOT_A_CKOUT. Any other non-zero result codes are propagated errors from lower-level code. */ FSL_EXPORT int fsl_ckout_rename_revert(fsl_cx * const f, char const * zNewName, bool relativeToCwd, bool doFsMv, bool *didSomething); /** Fetches the vfile.pathname value for the given vfile.id entry. Results are undefined if f does not have an opened checkout. On success returns 0 and sets `*zOut` to the name, transfering ownership to the caller (who must eventually pass it to fsl_free()). If absolute is true then the returned name has the checkout dir prepended to it, else it is relative to the top of the repo. Potential TODO: this routine is in no way optimized for heavy use within an app, expecting to be only rarely used, e.g. in conjunction with fsl_ckout_rename()-like ops. If it sees any significant use it should be refactored to use a cached statement. */ FSL_EXPORT int fsl_vfile_pathname(fsl_cx * const f, fsl_id_t vfid, bool absolute, char **zOut); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_CHECKOUT_H_INCLUDED */ /* end of file ./include/fossil-scm/checkout.h */ |
︙ | ︙ | |||
18026 18027 18028 18029 18030 18031 18032 18033 18034 18035 18036 18037 18038 18039 18040 18041 18042 18043 18044 18045 18046 18047 18048 18049 | /** 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). | > > > > > > | | > > > > > > | > > > | < > > | 18619 18620 18621 18622 18623 18624 18625 18626 18627 18628 18629 18630 18631 18632 18633 18634 18635 18636 18637 18638 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 18654 18655 18656 18657 18658 18659 18660 18661 18662 18663 18664 18665 18666 18667 18668 18669 18670 18671 18672 | /** 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(). Errors include: - FSL_RC_OOM on allocation error - FSL_RC_NOT_FOUND (Windows only) if no relevant environment variables are set from which to form the db path. 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). Windows: 1) If $FOSSIL_HOME is set, use that directory. 2) If $LOCALAPPDATA is set, use that directory. 3) If $APPDATA is set, use that directory. 4) If $USERPROFILE is set, use that directory. 5) If $HOMEDRIVE and $HOMEPATH are set, use that directory. If none of the above are set, FSL_RC_NOT_FOUND is returned. The name `_fossil` is appended to the first of the above-listed locations which is set in the environment. All platforms: Except where listed above, this function does not check whether the file already exists or is a database. */ 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. |
︙ | ︙ | |||
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 | > > > > > > > > > > > > > > > > > > > > > > > > > > | 19007 19008 19009 19010 19011 19012 19013 19014 19015 19016 19017 19018 19019 19020 19021 19022 19023 19024 19025 19026 19027 19028 19029 19030 19031 19032 19033 19034 19035 19036 19037 19038 19039 19040 19041 19042 19043 19044 19045 19046 | */ #if defined(__cplusplus) extern "C" { #endif /** @internal An internal helper macro to help localize OOM error reports (which are most often side effects of other issues, rather than being real OOM cases). It's intended to be used like: ``` void * someAllocedMemory = fsl_malloc(...); // any allocating routine if(!someAllocedMemory){ FSL__WARN_OOM; rc = FSL_RC_OOM; goto end; } ``` or: ``` if(FSL_RC_OOM==fs){ FSL__WARN_OOM; } ``` Note that this macro _may_ expand to encompass multiple commands so must never be used as the RHS of an `if` without squiggly braces surrounding it. */ #define FSL__WARN_OOM \ fprintf(stderr,"OOM @ %s:%d\n", __FILE__, __LINE__) 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 |
︙ | ︙ | |||
20722 20723 20724 20725 20726 20727 20728 | 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 | < < < < < < < < < < < | 21357 21358 21359 21360 21361 21362 21363 21364 21365 21366 21367 21368 21369 21370 | 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 preconditions which are assert()ed. |
︙ | ︙ | |||
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 */ | > > > > > > > > > > > > | 21405 21406 21407 21408 21409 21410 21411 21412 21413 21414 21415 21416 21417 21418 21419 21420 21421 21422 21423 21424 21425 21426 21427 21428 21429 21430 21431 21432 21433 21434 21435 21436 21437 21438 21439 21440 21441 21442 21443 | /** @internal Frees/clears the non-db state of f->ckout. */ void fsl__cx_ckout_clear(fsl_cx * const f); /** @internal Register the "files of checkin" (fsl_foci) SQLite3 virtual table. */ int fsl__foci_register(fsl_db * const db); /** @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) /** @internal Internal impl of fsl_buffer_err(), implemented as a macro for efficiency's sake. */ #define fsl__buffer_err(B) (B)->errCode #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 */ |
︙ | ︙ | |||
21795 21796 21797 21798 21799 21800 21801 | */ #define FCLI_V(pfexp) FCLI_VN(1,pfexp) #if defined(__cplusplus) extern "C" { #endif | < > > > > > | 22431 22432 22433 22434 22435 22436 22437 22438 22439 22440 22441 22442 22443 22444 22445 22446 22447 22448 22449 22450 22451 22452 22453 22454 22455 22456 22457 22458 | */ #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_dispatch_commands() when the string returned from fcli_next_arg() does not match any fcli_command in the list. */ FCLI_RC_NO_CMD, /** 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 }; |
︙ | ︙ | |||
22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 22216 22217 22218 | This output channel is used when initializing this->f. The 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; }; | > > > > > > > | 22845 22846 22847 22848 22849 22850 22851 22852 22853 22854 22855 22856 22857 22858 22859 22860 22861 22862 22863 22864 22865 | This output channel is used when initializing this->f. The default implementation uses fsl_outputer_FILE to output to stdout. */ fsl_outputer outputer; } config; /** Search paths. */ struct { /** Search object for binaries. */ fsl_pathfinder bins; } paths; /** 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; }; |
︙ | ︙ | |||
22462 22463 22464 22465 22466 22467 22468 | /** Convenience macro for using fcli_err_report2(). */ #define fcli_err_report(CLEAR) fcli_err_report2((CLEAR), __FILE__, __LINE__) /** | | | > > | 23109 23110 23111 23112 23113 23114 23115 23116 23117 23118 23119 23120 23121 23122 23123 23124 23125 23126 23127 | /** 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. Returns NULL if the args list is empty. */ 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 |
︙ | ︙ | |||
22527 22528 22529 22530 22531 22532 22533 | }; /** 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 | | > > > > | | | | 23176 23177 23178 23179 23180 23181 23182 23183 23184 23185 23186 23187 23188 23189 23190 23191 23192 23193 23194 23195 23196 23197 23198 23199 23200 23201 23202 23203 23204 23205 23206 23207 23208 23209 23210 23211 23212 23213 23214 23215 23216 23217 | }; /** 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 FCLI_RC_NO_CMD 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. The distinct FCLI return code aims to help clients distinguish between fcli_dispatch_commands() failure and failures propagated by successfully dispatched fcli_command fcli_command_f callbacks. 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 0 if a match is found, else 1. Note that cmd->aliases _must_ be double-NUL terminated. */ FSL_EXPORT int 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 |
︙ | ︙ | |||
22779 22780 22781 22782 22783 22784 22785 | /** 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. */ | | > > > > > > > > > > > > > > > > > > > | 23432 23433 23434 23435 23436 23437 23438 23439 23440 23441 23442 23443 23444 23445 23446 23447 23448 23449 23450 23451 23452 23453 23454 23455 23456 23457 23458 23459 23460 23461 23462 23463 23464 23465 | /** 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(void); /** Searches the `$PATH` for a binary named fossil or fossil.exe. If found, that string is returned. The bytes are guaranteed to be alive until either the app ends or someone modifies fcli.path.bins. If not found, it returns NULL. If errIfNotFound is true and no binary is found, it will update fcli's error state with a description of the problem. If errIfNotFound is false, reportPolicy is ignored, else it is interpreted as: - 0 = do not fcli_err_report() any errors and retain error state. - <0 = report and retain the error state. - >0 = report and clear error state. */ FSL_EXPORT char const * fcli_fossil_binary(bool errIfNotFound, int reportPolicy); /** 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 |
︙ | ︙ | |||
22804 22805 22806 22807 22808 22809 22810 22811 22812 22813 22814 22815 22816 22817 | /** 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 23476 23477 23478 23479 23480 23481 23482 23483 23484 23485 23486 23487 23488 23489 23490 23491 23492 23493 23494 23495 23496 23497 23498 23499 23500 23501 23502 23503 23504 23505 23506 23507 23508 23509 23510 23511 23512 23513 23514 23515 23516 23517 23518 23519 23520 23521 23522 23523 23524 23525 23526 23527 23528 23529 23530 23531 23532 23533 23534 23535 23536 23537 23538 23539 23540 23541 23542 23543 23544 23545 23546 23547 23548 23549 23550 | /** 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); /** Flags for use with fcli_sync(). */ enum fcli_sync_e { /** Only perform one of the request operations if the fossil config setting "fcli.autosync" or "autosync" is set, preferring the former. "autosync" is fossil(1) standard flag for this, but clients may use "fcli.autosync" to trump that without changing fossil's behavior. */ FCLI_SYNC_AUTO = 0x01, /** Perform a pull (read-only) sync. */ FCLI_SYNC_PULL = 0x02, /** Perform a push (write-only) sync. */ FCLI_SYNC_PUSH = 0x04, /** Perform a full sync. */ FCLI_SYNC_FULL = FCLI_SYNC_PULL | FCLI_SYNC_PUSH, /** Perform a full sync, but only if autosync is enabled. */ FCLI_SYNC_FULLAUTO = FCLI_SYNC_AUTO | FCLI_SYNC_FULL, /** Redirect all stdout output from the fsl_system() to /dev/null. This only works on non-Windows platforms. stderr output is retained. */ FCLI_SYNC_NO_STDOUT = 0x10, /** Works like FCLI_SYNC_NO_STDOUT, but also suppresses stderr. */ FCLI_SYNC_NO_OUTPUT = 0x20 }; /** Uses fcli_fossil_binary() to find the fossil(1) binary and run a sync operation, optionally either or both of push and pull operations. Its argument may be a mask of any values from the fcli_sync_e enum. Returns 0 if it does nothing or if fossil exits with a zero code. Returns FSL_RC_NOT_A_REPO if no repo is opened. If the repo is opened but has no "syncwith:" entries in its config table, no sync is attempted and 0 is returned. If fossil cannot be found, returns FSL_RC_NOT_FOUND, else returns as documented for fsl_system(). This function will fail with FSL_RC_LOCKED if a transaction is currently open. Synching cannot succeed if a transaction is opened because the repo is write-locked during that time. Though this routine does not generate any console output, the proxied binary will, which may corrupt screen state of any hypothetical curses-style libfossil clients. */ //FSL_EXPORT int fcli_sync( bool push, bool pull ); FSL_EXPORT int fcli_sync( int ops ); /** @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 |
︙ | ︙ | |||
22826 22827 22828 22829 22830 22831 22832 | 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); | < < | 23559 23560 23561 23562 23563 23564 23565 23566 23567 23568 23569 23570 23571 23572 23573 23574 23575 | 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: */ |
︙ | ︙ |
Deleted signify/fnc-11-release.pub.
|
| < < |
Added signify/fnc-13-release.pub.
> > | 1 2 | untrusted comment: fnc 0.13 public key RWRBkQNnoj1IBcgbH7YSJa2155xwSKh7+TGI3jW8KwraJ/GGgXtJPp0I |
Changes to src/diff.c.
︙ | ︙ | |||
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") | > > > > > > > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | */ #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 */ /* Return values for validate_scope_line() */ #define VSL_FALSE 0 #define VSL_TRUE 1 #define VSL_PRIVATE 2 #define VSL_PROTECT 3 #define VSL_PUBLIC 4 /* * 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") |
︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 91 92 | #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) { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | < | | | 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 | #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" */ 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 *sig; /* Matching function */ char *spec; /* C++ specifier */ uint32_t lastmatch; /* Match line index */ uint32_t lastline; /* Last line scanned */ fsl_size_t offset; /* Match byte offset */ } proto; }; static const struct diff_out_state diff_out_state_ctor = { NULL, NULL, NULL, 0U, 0, '\0', { NULL, NULL, NULL, 0U, 0U, 0U } }; 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 */ }; static int diff_blobs(fsl_buffer const *, fsl_buffer const *, fsl_output_f, void *, enum line_type **, uint32_t *, /* void *regex, */ uint16_t, short, int, int **); static int fnc_output_f_diff_out(void *, void const *, fsl_size_t); static int diff_outf(struct diff_out_state *, char const *, ... ); static int diff_out(struct diff_out_state * const, void const *, fsl_int_t); static int validate_scope_line(const char *); static int match_hunk_function(struct diff_out_state *const, uint32_t); static int buffer_copy_lines_from(fsl_buffer *const, const fsl_buffer *const, fsl_size_t *, fsl_size_t, fsl_size_t); /* static uint64_t fnc_diff_flags_convert(int); */ /* static int diff_context_lines(uint64_t); */ static int match_dline(fsl_dline *, fsl_dline *); static bool find_lcs(const char *z, int, const char *, int, int *); static int unidiff(fsl__diff_cx *, struct diff_out_state *, void *, uint16_t, uint64_t); static int unidiff_lineno(struct diff_out_state *, int, int, bool); static int unidiff_txt( struct diff_out_state *const, char, fsl_dline *, int, void *); static int sbsdiff(fsl__diff_cx *, struct diff_out_state *, void *, uint16_t, uint64_t); static int max_sbs_width(fsl__diff_cx *, int *, unsigned short, uint16_t); static int sbsdiff_width(uint64_t); static int sbsdiff_separator(struct sbsline *, int, int); static int sbsdiff_lineno(struct sbsline *, int, int); static void sbsdiff_shift_left(struct sbsline *, const char *); static void sbsdiff_simplify_line(struct sbsline *, const char *); static int sbsdiff_column(struct diff_out_state *, fsl_buffer const *, int); static int sbsdiff_txt(struct sbsline *, fsl_dline *, int); static int sbsdiff_newline(struct sbsline *); static int sbsdiff_space(struct sbsline *, int, int); static int sbsdiff_marker(struct sbsline *, const char *, const char *); static int sbsdiff_close_gap(int *); static unsigned char *sbsdiff_align(fsl_dline *, int, fsl_dline *, int); static int sbsdiff_write_change(struct sbsline *, fsl_dline *, int, fsl_dline *, int); int fnc_diff_text_raw(fsl_buffer const *blob1, fsl_buffer const *blob2, int flags, int **out) { return 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) ? 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, enum line_type **lines, uint32_t *nlines, short context, short sbswidth, int flags) { return diff_blobs(blob1, blob2, out, state, lines, nlines, 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. */ static int 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; |
︙ | ︙ | |||
190 191 192 193 194 195 196 | } } /* 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 */ | < | < < < | | | < < < | > | | > > > > > > | | > > | > | | | | | > > | | | | | | | | | | | | | | | > > > | > | > | | 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 | } } /* 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 */ if (FLAG_CHK(flags, FNC_DIFF_SIDEBYSIDE)) { rc = max_sbs_width(&c, &flags, sbswidth, context); if (rc) goto end; } if (out) { /* * Compute a unified or side-by-side diff. * XXX Missing regex support. */ struct diff_out_state dos = diff_out_state_ctor; dos.out = out; dos.state = state; if (lines && nlines) { dos.lines = *lines; dos.nlines = *nlines; } dos.ansi = !!(FLAG_CHK(flags, FNC_DIFF_ANSI_COLOR)); if (FLAG_CHK(flags, FNC_DIFF_PROTOTYPE)) dos.proto.file = blob1; if (FLAG_CHK(flags, FNC_DIFF_SIDEBYSIDE)) rc = sbsdiff(&c, &dos, NULL /*regex*/, context, flags); else rc = unidiff(&c, &dos, NULL /*regex*/, context, flags); if (lines && nlines) { *lines = dos.lines; /* realloc'd */ *nlines = dos.nlines; } fsl_free(dos.proto.sig); } 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, if longer than the * currently set sbs width, encode the length in *flags. */ static int max_sbs_width(fsl__diff_cx *c, int *flags, unsigned short width, uint16_t ctxt) { int i, j, k; int endl, endr, start; /* Start and end of l+r hunks */ unsigned short sz; /* Size of each line */ j = endl = endr = start = 0; k = c->nEdit; if (width < 0) width = 80; /* 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 hunk. * Both hunks will have the _same_ start line, but may have * distinct end lines. */ start += c->aEdit[j]; /* Hunk start line */ endl = endr = start; endl += MAX(c->aEdit[j + 1], 0); /* End of left side hunk */ endr += MAX(c->aEdit[j + 2], 0); /* End of right side hunk */ 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; i < e || i < e2; ++i) { if (i < e) { /* Left side line length */ sz = etcount(c->aFrom[i].z, c->aFrom[i].n); if (sz > width) width = sz; } if (i < e2) { /* Right side line length */ sz = etcount(c->aTo[i].z, c->aTo[i].n); if (sz > width) width = sz; } } if (i >= c->nFrom && i >= c->nTo) break; /* Account for insertions before adding lines to next hunk. */ start += c->aEdit[j + 2]; } while ((j += 3) < k); *flags |= ((int)(width & 0xFF)) << 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; if (n < 0) n = fsl_strlen(str); while (str && (str[c] != '\n' && str[c] != '\0')) { /* Expand tabs */ if (str[c] == '\t') n += (8 - ((c - (c ? 1 : 0)) % 8)) - 1; 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. */ #if 0 static 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; } #endif 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. */ static 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 */ |
︙ | ︙ | |||
469 470 471 472 473 474 475 | * 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. */ | | | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | * 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. */ static 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 */ |
︙ | ︙ | |||
540 541 542 543 544 545 546 | } return rc; } /* * Send src to o->out(). If n is negative, use strlen() to determine length. */ | | | | 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 | } return rc; } /* * Send src to o->out(). If n is negative, use strlen() to determine length. */ static 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. */ static int buffer_copy_lines_from(fsl_buffer *const dst, const fsl_buffer *const src, fsl_size_t *offset, fsl_size_t seek, fsl_size_t n) { const char *z = (const char *)src->mem; fsl_size_t idx = *offset, ln = 0, start = 0; int rc = FSL_RC_OK; |
︙ | ︙ | |||
582 583 584 585 586 587 588 | if (dst) /* trim trailing '\n' when copying */ rc = fsl_buffer_append(dst, &src->mem[start], idx - start - 1); *offset = start; return rc; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | < | > | > > | < < < < < | < | < < < < < < < < < < < < | < | > > > | > > > > > > > > > > > | | < | | < | | | | | < < | | | < > < | > | | | 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 | if (dst) /* trim trailing '\n' when copying */ rc = fsl_buffer_append(dst, &src->mem[start], idx - start - 1); *offset = start; return rc; } /* * If str is a valid scope line (i.e., function prototype; class, namespace, * enum, struct, or union declaration; or C++ class specifier) return the * corresponding int value. GNU C and MSVC allow '$' in identifier names: * https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html * https://docs.microsoft.com/en-us/cpp/cpp/identifiers-cpp */ static int validate_scope_line(const char *str) { if (fsl_isalpha(*str) || *str == '_' || *str == '$') { if (strchr(str, '(') || strchr(str, '{') || strstr(str, "struct ") || strstr(str, "union ") || strstr(str, "enum ") || strstr(str, "class ") || strstr(str, "namespace ")) return VSL_TRUE; if (starts_with(str, "private")) return VSL_PRIVATE; if (starts_with(str, "protected")) return VSL_PROTECT; if (starts_with(str, "public")) return VSL_PUBLIC; } return VSL_FALSE; } /* * Back scan the diffed file dst->proto.file from line pos, which is the first * changed line of the current hunk, for the enclosing function in which the * hunk resides. Point dst->proto.sig to the heap-allocated matching line, * which the caller must eventually free. Return FSL_RC_OK on success. */ static int match_hunk_function(struct diff_out_state *const dst, uint32_t pos) { fsl_buffer buf = fsl_buffer_empty; char *line = NULL; fsl_size_t offset; uint32_t last = dst->proto.lastline; int rc = FSL_RC_OK; dst->proto.lastline = pos; offset = dst->proto.offset; /* Begin seek from last match */ while (pos > 1 && pos > last) { rc = buffer_copy_lines_from(&buf, dst->proto.file, &offset, pos - dst->proto.lastmatch, 1); if (rc) return rc; line = fsl_buffer_take(&buf); if (line == NULL) continue; switch (validate_scope_line(line)) { case VSL_FALSE: break; case VSL_PRIVATE: if (!dst->proto.spec) dst->proto.spec = " (private)"; break; case VSL_PROTECT: if (!dst->proto.spec) dst->proto.spec = " (protected)"; break; case VSL_PUBLIC: if (!dst->proto.spec) dst->proto.spec = " (public)"; break; case VSL_TRUE: fsl_free(dst->proto.sig); dst->proto.sig = line; /* * It's expensive to seek from the start of the * file for each hunk when diffing large files, * so save offset and line index of this match. */ dst->proto.lastmatch = pos; dst->proto.offset = offset; return FSL_RC_OK; } /* No match, revert to last offset. */ fsl_free(line); offset = dst->proto.offset; --pos; } return FSL_RC_OK; } /* * 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 */ static 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 hunks = 0; /* Number of hunks 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 */ |
︙ | ︙ | |||
776 777 778 779 780 781 782 | 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) { | > | > | | > | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | 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) { if (lines && nlines) { 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) |
︙ | ︙ | |||
806 807 808 809 810 811 812 | } else rc = diff_outf(s.output, "%.*c\n", s.width * 2 + 16, '.'); if (rc) goto end; } showsep = true; | | | | | > | > | < > > > | > | | > | | > | | > | < | | > | 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 | } else rc = diff_outf(s.output, "%.*c\n", s.width * 2 + 16, '.'); if (rc) goto end; } showsep = true; ++hunks; if (s.esc) rc = fsl_buffer_appendf(s.cols[SBS_LLINE], "<span class=\"fsl-diff-hunk-%d\"></span>", hunks); /* Show the initial common area */ li += skip; ri += skip; ntotal = c[ci] - skip; for (j = 0; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) goto end; } s.idx = s.end = -1; rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); if (rc) goto end; rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (rc) goto end; rc = sbsdiff_marker(&s, " ", ""); if (rc) goto end; rc = sbsdiff_lineno(&s, ri + j, SBS_RLINE); if (rc) goto end; 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 */ |
︙ | ︙ | |||
860 861 862 863 864 865 866 | alignment = sbsdiff_align(&l[li], nleft, &r[ri], nright); if (!alignment) { rc = FSL_RC_OOM; goto end; } | | | > > | | > | 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 | alignment = sbsdiff_align(&l[li], nleft, &r[ri], nright); if (!alignment) { rc = FSL_RC_OOM; goto end; } for (j = 0; 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) goto end_align; if (lines && nlines) { 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); |
︙ | ︙ | |||
907 908 909 910 911 912 913 | 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); | | > > | | > | | > > | | > | < < < | < > | > > | | > | 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 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 | 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) goto end_align; if (lines && nlines) { 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) goto end_align; if (lines && nlines) { 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 (i < nc - 1) { ntotal = c[ci + i * 3 + 3]; for (j = 0; j < ntotal; j++) { rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); if (rc) goto end; s.idx = s.end = -1; rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (rc) goto end; if (lines && nlines) { 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; |
︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 | } /* Show the final common area */ assert(nc == i); ntotal = c[ci + nc * 3]; if (ntotal > context) ntotal = context; | | > | > | < > > > | > | | > | | > | | > | > > | | > > | | | | 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 | } /* Show the final common area */ assert(nc == i); ntotal = c[ci + nc * 3]; if (ntotal > context) ntotal = context; for (j = 0; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) goto end; } s.idx = s.end = -1; rc = sbsdiff_lineno(&s, li + j, SBS_LLINE); if (rc) goto end; rc = sbsdiff_txt(&s, &l[li + j], SBS_LTEXT); if (rc) goto end; rc = sbsdiff_marker(&s, " ", ""); if (rc) goto end; rc = sbsdiff_lineno(&s, ri + j, SBS_RLINE); if (rc) goto end; 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); if (rc) goto end; for (i = SBS_LLINE; i <= SBS_RTEXT; i++) { rc = sbsdiff_column(dst, s.cols[i], i); if (rc) goto end; } 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 */ static 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 hunks = 0; /* Number of hunks 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 */ |
︙ | ︙ | |||
1146 1147 1148 1149 1150 1151 1152 | } /* * 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. */ | | | > > | > | | | > > | > > | | | > > > | | | > | > > > | < | | | < | > > | | > > | | | > > | > | > > | | | > > > > | > > > | | > > > | | > | | > | > | > > > > | | < > | > | | > | > | | > > > | | > > | | < | | > | > | | > > > | | > > | | < | | > | > | | > > > | | > > | | < | | > | > | > > > > | | > > | > > < > | | > | | | < | < < | | | | | | | | 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 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 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 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 | } /* * 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. */ ++hunks; 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, '.'); if (rc) return rc; } else { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_SEPARATOR); if (rc) return rc; } rc = diff_outf(dst, "%.95c\n", '.'); if (rc) return rc; } if (html) { rc = diff_outf(dst, "<span class=\"fsl-diff-hunk-%d\"></span>", hunks); if (rc) return rc; } } else { const char *ansi1 = ""; const char *ansi2 = ""; const char *ansi3 = ""; if (html) { rc = diff_outf(dst, "<span class=\"fsl-diff-lineno\">"); if (rc) return rc; } #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(1) * would be confused and may reject the diff. */ 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) return rc; if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_HUNK); if (rc) return rc; } if (html) { rc = diff_outf(dst, "</span>"); if (rc) return rc; } if (proto && li + skip > 1) { int n = 55; rc = match_hunk_function(dst, li + skip + context); if (rc) return rc; n -= fsl_strlen(dst->proto.spec); if (dst->proto.sig) { rc = diff_outf(dst, " %.*s", n, dst->proto.sig); if (rc) return rc; } if (dst->proto.spec) { rc = diff_outf(dst, " %s", dst->proto.spec); if (rc) return 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; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) return rc; } if (showln) { rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (rc) return 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; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_MINUS); if (rc) return rc; } if (showln) { rc = unidiff_lineno(dst, li + j + 1, 0, html); if (rc) return 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; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_PLUS); if (rc) return rc; } if (showln) { rc = unidiff_lineno(dst, 0, ri + j + 1, html); if (rc) return 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; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) return rc; } if (showln) { rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (rc) return 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; j < ntotal; j++) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) return rc; } if (showln) { rc = unidiff_lineno(dst, li + j + 1, ri + j + 1, html); if (rc) return rc; } rc = unidiff_txt(dst, ' ', &l[li + j], html, 0); if (rc) return rc; } } /* _big_ for() loop */ return rc; } /* Extract the number of context lines from flags. */ #if 0 static int diff_context_lines(uint64_t flags) { int n = flags >> 32; if (!n && !FLAG_CHK(flags, FNC_DIFF_CONTEXT_EX)) n = 5; return n; } #endif /* * Extract column width from flags for side-by-side diff. * Return appropriate default if no width is specified. */ static int sbsdiff_width(uint64_t flags) { int w = (flags & FNC_DIFF_WIDTH_MASK) / (FNC_DIFF_CONTEXT_MASK + 1); return w ? w : 80; } /* Append a separator line of length len to column col. */ static 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 *. */ static int fnc_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); } static int diff_outf(struct diff_out_state *dst, char const *fmt, ...) { va_list va; va_start(va,fmt); fsl_appendfv(fnc_output_f_diff_out, dst, fmt, va); va_end(va); return dst->rc; } /* Append a column to the final output blob. */ static 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", |
︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 | * 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. */ | | | 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 | * 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. */ static 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 */ |
︙ | ︙ | |||
1467 1468 1469 1470 1471 1472 1473 | } /* * Append newlines to columns corresponding to sbs diff format. * html: all columns * text: right column only */ | | | | | | | | 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 | } /* * Append newlines to columns corresponding to sbs diff format. * html: all columns * text: right column only */ static 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. */ static 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. */ static 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. */ static 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. */ static 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. */ static 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; |
︙ | ︙ | |||
1565 1566 1567 1568 1569 1570 1571 | } /* * 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. */ | | | 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 | } /* * 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. */ static 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 |
︙ | ︙ | |||
1598 1599 1600 1601 1602 1603 1604 | * 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 */ | | | 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 | * 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 */ static 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 */ |
︙ | ︙ | |||
1743 1744 1745 1746 1747 1748 1749 | * 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 */ | | | 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 | * 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 */ static 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 */ |
︙ | ︙ | |||
1796 1797 1798 1799 1800 1801 1802 | --nsfx; if (nsfx == leftsz || nsfx == rightsz) npfx = 0; } if (npfx + nsfx > shortest) npfx = shortest - nsfx; | | > > | > | > | < > | > > > | > | > | > > | > > < < < < < < < < | < > > | > > > > > | < | > > > | | > > | > | | > | | > | | < < < | | 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 | --nsfx; if (nsfx == leftsz || nsfx == rightsz) npfx = 0; } if (npfx + nsfx > shortest) npfx = shortest - nsfx; /* A single hunk 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) return rc; if (leftsz == rightsz && ltxt[leftsz] == rtxt[rightsz]) { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_CONTEXT); if (rc) return rc; } rc = sbsdiff_marker(dst, " ", ""); if (rc) return rc; } else { if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (rc) return rc; } rc = sbsdiff_marker(dst, " | ", "|"); if (rc) return rc; } rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (rc) return rc; dst->idx = npfx; dst->end = rightsz - nsfx; dst->tag = tag_add; rc = sbsdiff_txt(dst, right, SBS_RTEXT); return rc; } /* A single hunk of text deleted from the left */ if (npfx + nsfx == rightsz) { rc = sbsdiff_lineno(dst, llnno, SBS_LLINE); if (rc) return rc; if (lines && nlines) { 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) return rc; rc = sbsdiff_marker(dst, " | ", "|"); if (rc) return rc; rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (rc) return rc; dst->idx = dst->end = -1; rc = sbsdiff_txt(dst, right, SBS_RTEXT); return rc; } /* * At this point we know that there is a hunk 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)) { |
︙ | ︙ | |||
1877 1878 1879 1880 1881 1882 1883 | } 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); | | > > > | > > > | | > | > | > | | | | | | > > > | > > > > | | > | | > | | < < < < | | 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 | } 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) return rc; if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (rc) return rc; } rc = sbsdiff_marker(dst, " | ", "|"); if (rc) return 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) return 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) return rc; if (lines && nlines) { rc = add_line_type(lines, nlines, LINE_DIFF_EDIT); if (rc) return rc; } rc = sbsdiff_marker(dst, " | ", "|"); if (rc) return rc; rc = sbsdiff_lineno(dst, rlnno, SBS_RLINE); if (rc) return rc; dst->end = rightsz - nsfx; rc = 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. */ static 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) |
︙ | ︙ | |||
1967 1968 1969 1970 1971 1972 1973 | * 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 */ | | | 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 | * 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 */ static 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 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt FNC 1 .Os .Sh NAME .Nm fnc .Nd An interactive text-based user interface for Fossil .Sh SYNOPSIS .Nm .Op Ar command .Op Fl h | -help .Nm .Op Fl h | -help .Op Fl v | -version .Nm .Cm stash Op Po Cm get Ns | Ns Cm pop Pc Oo Ar id Oc | Fl ChPx .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 Chz .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 bChilPqsWw .Op Fl R Ar path .Op Fl x Ar number .Op Ar artifact1 Op Ar artifact2 .Op Ar path ... .Nm .Cm tree .Op Fl Ch .Op Fl c Ar commit .Op Fl R Ar path .Op Ar path .Nm .Cm blame .Op Fl Ch .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 Cchopr .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 text-based user 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. 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 commit 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 runtime 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 at the specified .Ar path for the current .Nm invocation. See command specific options for details. .It Fl v , -version Display program version then exit. .El .Pp Global key bindings are as follows: .Bl -tag -width Ds .It Ic H, \&?, F1 Display runtime help. .It Ic Tab Switch focus between open views. .It Ic f Toggle the active view between fullscreen and splitscreen mode. By default, .Nm will open nested views in a splitscreen if the terminal window is equal to or greater than 110 columns wide. .It Ic Q Quit .Nm . .It Ic 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 Oo Ar value | Fl -unset Oc Oc .Dl Pq aliases: Cm conf , 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, .Nm Cm 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 defined for a given setting in the local repository, environment variables will be searched. If still not found, .Nm will fallback to default values. Unless the .Sy --repo option is used, this command must be invoked from within a work tree; that is, .Nm assumes a local checkout is open in or above the current working directory. Options for .Nm Cm config are as follows: .Bl -tag -width Ds .It Fl h , -help Display config command help and usage information then exit. .It Fl -ls List all currently defined settings. .It Fl R , -repo Ar path Use the .Xr fossil 1 repository at the specified .Ar path for the current .Nm Cm config invocation. .It Fl u , -unset Clear the specified .Ar setting . .El .Tg stash .It Cm stash Oo Po Cm get Ns | Ns Cm pop Pc Oo Ar id Oc | \ 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 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 hash of the current checkout, will be used. At any time prior to the final hunk being selected .Pq i.e., before the stash message prompt , the operation can be aborted by opening the help dialog and entering .Qq Q . This will discard all selections and leave the checkout state unchanged. .Pp Available subcommands for .Nm Cm stash are as follows: .Bl -tag -width 00 .It Cm get Op Ar id .Dl Pq aliases: Cm apply Retrieve stash .Ar id , or if not provided the most recent stash entry, and apply it to the current checkout. .It Cm pop Op Ar id Remove stash .Ar id , or if not provided 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 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 invoked from within a work tree; 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 .Nm Cm 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 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 at runtime 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 at the specified .Ar path for the current .Nm Cm 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 . This is a repeatable flag .Pq e.g., Nm Cm timeline Cm -t e -t t . .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 .Nm Cm timeline are as follows: .Bl -tag -width Ds .It Ic Arrow-down, j, >, \&. Move selection cursor down the timeline. .It Ic Arrow-up, k, <, \&, Move selection cursor up the timeline. .It Ic Arrow-right, l Scroll the view two columns to the right in the buffer. The comment field moves left on the screen. .It Ic Arrow-left, h Scroll the view two columns to the left in the buffer. The comment field moves right on the screen. .It Ic $ Scroll the view right to the end of the longest comment summary line on the page. .It Ic 0 Scroll the view left to the beginning of the line. .It Ic C-f, Page-down Scroll timeline view one page downwards in the buffer. .It Ic C-b, Page-up Scroll timeline view one page upwards in the buffer. .It Ic C-d Scroll timeline view half a page downwards in the buffer. .It Ic C-u Scroll timeline view half a page upwards in the buffer. .It Ic G, End Move selection cursor to the last commit on the timeline (i.e., oldest commit in the repository). .It Ic gg, Home Move selection cursor to the first commit on the timeline (i.e., newest commit in the repository). .It Ic Enter Open a .Cm diff view displaying the changeset of the currently selected commit. .It Ic 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 Ic b Open and populate branch view with all repository branches. .It Ic C Diff local changes on disk in the current checkout against the selected commit. .It Ic c Toggle colourised timeline. On supported terminals, .Nm will default to displaying the timeline in colour. .It Ic 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 Ic t Display the tree of the repository corresponding to the currently selected commit. .It Ic / 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 SHA1 or SHA3 hash. See .Xr re_format 7 for regular expression syntax. .It Ic 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 Ic 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 Ic 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 b | -brief Oc 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 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 invoked from within a work tree; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Options for .Nm Cm diff are as follows: .Bl -tag -width Ds .It Fl b , -brief Display file index and hash lines only. .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 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. Function prototype cannot be displayed in the hunk header with either .Fl l|-line-numbers or .Fl s|-sbs formatted diffs. This option can be toggled at runtime 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 at the specified .Ar path for the current .Nm Cm 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 at runtime 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 .Nm Cm diff are as follows: .Bl -tag -width Ds .It Ic Arrow-down, j Move the selection cursor down one line. .It Ic Arrow-up, k Move the selection cursor up one line. .It Ic Arrow-right, l Scroll the view two columns to the right in the buffer. Diff output moves left on the screen. .It Ic Arrow-left, h Scroll the view two columns to the left in the buffer. Diff output moves right on the screen. .It Ic $ Scroll the view right to the end of the longest line in the diff. .It Ic 0 Scroll the view left to the beginning of the line. .It Ic C-e Scroll view one line downwards in the buffer. Diff output moves upwards on the screen. .It Ic C-y Scroll view one line upwards in the buffer. Diff output moves downwards on the screen. .It Ic C-f, Page-down, Space Scroll diff view one page downwards in the buffer. .It Ic C-b, Page-up Scroll diff view one page upwards in the buffer. .It Ic C-d Scroll diff view half a page downwards in the buffer. .It Ic C-u Scroll diff view half a page upwards in the buffer. .It Ic G, End Scroll to the end of the view (i.e., last line of diff output). .It Ic gg, Home Scroll to the top of the view (i.e., first line of diff output). .It Ic 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 Ic 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 Ic C-p Navigate to previous file in the diff. .It Ic C-n Navigate to next file in the diff. .It Ic \&[ Navigate to previous hunk in the diff. .It Ic \&] Navigate to next hunk in the diff. .It Ic \&-, \&_ Decrease the number of context lines shown in diff output. .It Ic \&=, \&+ Increase the number of context lines shown in diff output. .It Ic # Toggle display of diff view line numbers. .It Ic @ Open prompt to enter line number and navigate to that line in the view. .It Ic b Toggle brief diff mode by only displaying file index and hash lines. .It Ic B Open and populate branch view with all repository branches. .It Ic c Toggle coloured diff output. On supported terminals, .Nm will default to displaying changes and diff metadata in colour. .It Ic F Open prompt to enter file number and navigate to that file in the diff. .It Ic i Toggle inversion of diff output. .It Ic L Toggle display of file line numbers in the diff. .It Ic 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 .Pq e.g., Qq Pa 2870235eef.patch . .It Ic p In the diff hunk header, toggle display of which function each change is in; for example: .Sy @@ -2360,10 +2361,11 @@ draw_commits(struct fnc_view *view) .It Ic S Toggle display of a side-by-side formatted diff. .It Ic v Toggle verbosity of diff output. By default, .Nm will display the entire content of newly added or deleted files. .It Ic W Toggle whether end-of-line whitespace changes are ignored when comparing lines in the diff. .It Ic w Toggle whether whitespace-only changes are ignored when comparing lines in the diff. .It Ic / 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 Ic n Find the next line that matches the current search term. .It Ic 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 invoked from within a work tree; 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 .Nm Cm 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 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 at the specified .Ar path for the current .Nm Cm 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 .Nm Cm tree are as follows: .Bl -tag -width Ds .It Ic Enter, Arrow-right, l Enter the currently selected directory, or open a .Cm blame view of the currently selected file. .It Ic Backspace, Arrow-left, h Move up a level to the parent directory. This is a no-op when in the root tree. .It Ic Arrow-down, j Move selection cursor one node down the tree. .It Ic Arrow-up, k Move selection cursor one node up the tree. .It Ic C-f, Page-down Scroll tree view one page downwards in the buffer. .It Ic C-b, Page-up Scroll tree view one page upwards in the buffer. .It Ic C-d Scroll tree view half a page downwards in the buffer. .It Ic C-u Scroll tree view half a page upwards in the buffer. .It Ic Home, gg Move selection cursor to the first node in the tree. .It Ic End, G Move selection cursor to the last node in the tree. .It Ic b Open and populate branch view with all repository branches. .It Ic c Toggle coloured output. On supported terminals, .Nm will default to displaying the tree in colour. .It Ic d Toggle ISO8601 modified timestamp display for each tree entry. .It Ic i Toggle SHA hash display for all file nodes displayed in the tree. .It Ic 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 Ic / 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 Ic n Find the next tree node that matches the current search pattern. .It Ic 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 bl 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 invoked from within a work tree; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Options for .Nm Cm 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 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 at the specified .Ar path for the current .Nm Cm 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 .Nm Cm blame are as follows: .Bl -tag -width Ds .It Ic Arrow-down, j Move selection cursor down one line. .It Ic Arrow-up, k Move selection cursor up one line. .It Ic Arrow-right, l Scroll the view two columns to the right in the buffer. File output moves left on the screen. .It Ic Arrow-left, h Scroll the view two columns to the left in the buffer. File output moves right on the screen. .It Ic $ Scroll the view right to the end of the longest line in the file. .It Ic 0 Scroll the view left to the beginning of the line. .It Ic C-f, Page-down Scroll blame view one page downwards in the buffer. .It Ic C-b, Page-up Scroll blame view one page upwards in the buffer. .It Ic C-d Scroll blame view half a page downwards in the buffer. .It Ic C-u Scroll blame view half a page upwards in the buffer. .It Ic Home, gg Move selection cursor to the first line in the file. .It Ic End, G Move selection cursor to the last line in the file. .It Ic Enter Display the .Cm diff of the commit corresponding to the currently selected line. .It Ic # Toggle display of file line numbers. .It Ic @ Open prompt to enter line number and navigate to that line in the file. .It Ic B, Backspace Reload the previous blamed version of the file. .It Ic b Blame the version of the file corresponding to the commit in the currently selected line. .It Ic c Toggle coloured output. On supported terminals, .Nm will default to displaying the blamed file in colour. .It Ic p Blame the version of the file corresponding to the parent of the commit in the currently selected line. .It Ic T Open and populate branch view with all repository branches. .It Ic / 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 Ic N Find the previous token that matches the current search pattern. .It Ic 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 invoked from within a work tree; 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 .Nm Cm 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., |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | 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 | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | > | | | | | > | | > | | | | 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 | 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 at the specified .Ar path for the current .Nm Cm 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 .Nm Cm branch are as follows: .Bl -tag -width Ds .It Ic Arrow-down, j Move selection cursor down one branch. .It Ic Arrow-up, k Move selection cursor up one branch. .It Ic C-f, Page-down Scroll branch view one page downwards in the buffer. .It Ic C-b, Page-up Scroll branch view one page upwards in the buffer. .It Ic C-d Scroll branch view half a page downwards in the buffer. .It Ic C-u Scroll branch view half a page upwards in the buffer. .It Ic Home, gg Move selection cursor to the first branch in the list. .It Ic End, G Move selection cursor to the last branch in the list. .It Ic Enter, Space Display the .Cm timeline of the currently selected branch. .It Ic c Toggle coloured output. On supported terminals, .Nm will default to displaying the branch list in colour. .It Ic d Toggle display of the date on which the branch last received changes. .It Ic 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 Ic 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 Ic t Open the .Cm tree view of the currently selected branch. .It Ic R, C-l Reload the view with all repository branches, irrespective of which options were used in this .Nm Cm branch invocation. .It Ic / 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 Ic n Find the next branch that matches the current search pattern. .It Ic 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 |
︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 | 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 | | | > | > | | > | | > | | | | > | < | | | | | > | > | | | > | > | > | | | | | | | | | | | | | | | > | > | | | | > | | > < | | 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 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 | 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., bCilPqsWw .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 Ev 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 2. 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_HUNK Hunk 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 sqlite3 1 , .Xr re_format 7 .Sh AUTHORS .An Mark Jamsek Aq Mt mark@jamsek.com |
Changes to src/fnc.c.
︙ | ︙ | |||
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. */ | > > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #include <wchar.h> #include <langinfo.h> #include "libfossil.h" #include "diff.h" #define FNC_VERSION VERSION /* cf. Makefile */ #define FNC_HASH HASH #define FNC_DATE DATE /* 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. */ |
︙ | ︙ | |||
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" | > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | #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 PRINT_HASH STRINGIFY(FNC_HASH) #define PRINT_DATE STRINGIFY(FNC_DATE) #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" |
︙ | ︙ | |||
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 | > > > > > > > > > > > > > > > > > > > > > > > | 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 | # 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 */ /* * Bold intersects the colour space, which makes colours that use the intensity * bit (e.g., yellow) unavailable in VGA text mode; observed in xterm(1). */ #ifdef __OpenBSD__ # define FNC_HIGHLIGHT A_REVERSE #else # define FNC_HIGHLIGHT A_BOLD | A_REVERSE #endif #define PRINTFV(fmt, args) __attribute__((format (printf, fmt, args))) #ifndef __dead #define __dead __attribute__((noreturn)) #endif #ifndef __predict_true # ifdef __has_builtin # if __has_builtin(__builtin_expect) # define __predict_true(_e) __builtin_expect(((_e) != 0), 1) # define __predict_false(_e) __builtin_expect(((_e) != 0), 0) # endif /* __builtin_expect */ # endif /* __has_builtin */ #endif /* __predict_true */ #ifndef __predict_true # define __predict_true(_e) ((_e) != 0) # define __predict_false(_e) ((_e) != 0) #endif /* __predict_true */ #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 |
︙ | ︙ | |||
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 | 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. */ | > | | 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 | 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. */ bool brief; /* Only display file index lines. */ /* 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[13]; /* 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. */ |
︙ | ︙ | |||
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. */ | > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | 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. */ false, /* brief diff defaults to off. */ NULL, /* before defaults to any time. */ NULL, /* after defaults to any time. */ NULL, /* sort by MRU or open/closed (dflt: lexicographical) */ false, /* closed only branches is off (defaults to all). */ false, /* open only branches is off by (defaults to all). */ false, /* noprivate is off (default to show private branch). */ false, /* do not list all defined settings by default. */ |
︙ | ︙ | |||
314 315 316 317 318 319 320 | "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. */ | | | | | | 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 | "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\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\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\0set\0", "Configure or view currently available settings.", cmd_config, usage_config, fnc_init.cliflags_config}, {"stash", "save\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. */ |
︙ | ︙ | |||
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, | > > > | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | "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("b", "brief", &fnc_init.brief, "Display file index and hash lines only. Toggle with the 'b' key\n" " binding in diff view."), 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, |
︙ | ︙ | |||
661 662 663 664 665 666 667 | char *branch; char *type; enum fnc_diff_type diff_type; }; struct fsl_file_artifact { fsl_card_F *fc; | | | 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | char *branch; char *type; enum fnc_diff_type diff_type; }; struct fsl_file_artifact { fsl_card_F *fc; 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; |
︙ | ︙ | |||
848 849 850 851 852 853 854 | uint8_t context; /* MAX_DIFF_CTX lines = 64 */ enum fnc_patch_rc rc; bool report; }; struct stash_cx { struct patch_cx pcx; | < | < | | | | > | 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 | 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 */ unsigned char *stash; /* fnc_diff_view_state.hundex.lineno bitarray */ #define nbytes(nbits) (((nbits) + 7) >> 3) #define BIT_SET(_B, _i) (_B[(_i / CHAR_BIT)] |= (1 << (_i % CHAR_BIT))) #define BIT_CLR(_B, _i) (_B[(_i / CHAR_BIT)] &= ~(1 << (_i % CHAR_BIT))) #define BIT_CHK(_B, _i) (_B[(_i / CHAR_BIT)] & (1 << (_i % CHAR_BIT))) }; 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; /* line indexes for files */ struct index hundex; /* line indexes for hunks */ FILE *f; fsl_uuid_str id1; fsl_uuid_str id2; int first_line_onscreen; int last_line_onscreen; int diff_flags; int context; |
︙ | ︙ | |||
924 925 926 927 928 929 930 | unsigned int lineno; bool annotated; }; struct fnc_blame_cb_cx { struct fnc_view *view; struct fnc_blame_line *lines; | | < | 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 | unsigned int lineno; bool annotated; }; struct fnc_blame_cb_cx { struct fnc_view *view; struct fnc_blame_line *lines; fsl_uuid_cstr commit_id; int nlines; uint32_t maxlen; bool *quit; }; typedef int (*fnc_cancel_cb)(void *); |
︙ | ︙ | |||
967 968 969 970 971 972 973 | 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; | | | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 | 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_cstr commit_id; const char *lineno; char *path; int first_line_onscreen; int last_line_onscreen; int selected_line; int matched_line; int spin_idx; |
︙ | ︙ | |||
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 *); | > > | 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 | 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 volatile sig_atomic_t rec_sigint; static volatile sig_atomic_t rec_sigterm; 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 *); |
︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 1167 1168 | 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 *, | > > | | | | | | > > > > > | > | 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 | 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 bool path_to_diff(const struct fnc_pathlist_head *, const fsl_card_F *, const fsl_card_F *); static int diff_checkout(struct fnc_diff_view_state *); static int write_diff_meta(struct fnc_diff_view_state *, const char *, fsl_uuid_cstr, const char *, fsl_uuid_cstr, const fsl_ckout_change_e); static int diff_file(struct fnc_diff_view_state *, fsl_buffer *, const char *, const char *, fsl_uuid_cstr, const char *, const fsl_ckout_change_e); static int diff_non_checkin(struct fnc_diff_view_state *); static int diff_file_artifact(struct fnc_diff_view_state *, fsl_id_t, const fsl_card_F *, const fsl_card_F *, const fsl_ckout_change_e); static int show_diff(struct fnc_view *); static int write_diff(struct fnc_view *, const char *, const 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 prev_file(struct fnc_diff_view_state *); static int next_file(struct fnc_diff_view_state *); static int prev_hunk(struct fnc_diff_view_state *); static int next_hunk(struct fnc_diff_view_state *); 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(uint32_t, bool); static int fnc_stash(struct fnc_view *); static int fnc_stash_get(bool); static int select_hunks(struct fnc_view *); static int stash_input_handler(struct fnc_view *, bool *); static void set_choice(struct fnc_diff_view_state *, bool *, struct input *, struct index *, uint32_t *, size_t *, size_t *, bool *, enum stash_opt *); static unsigned char *alloc_bitstring(size_t); static int generate_prompt(char ***, char *, size_t, short); |
︙ | ︙ | |||
1247 1248 1249 1250 1251 1252 1253 | 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 *); | | > < | | | | 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 | 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_ckout_path(char **); static int valid_path(const char *, const 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 *, const char *, uint16_t); 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_cstr); 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_cstr, 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); |
︙ | ︙ | |||
1348 1349 1350 1351 1352 1353 1354 | 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 *); | | > > > | 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 | 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 *, bool); 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 void sigint_handler(int); static void sigterm_handler(int); static bool fatal_signal(void); 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 *); |
︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 | int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); int main(int argc, const char **argv) { | | | | | 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 | int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); int main(int argc, const char **argv) { const 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"); |
︙ | ︙ | |||
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 | 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") || | > > > > | | > | | | | < | | | | | | < | | > | < < | < < < > | < < < < | | 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 1545 1546 1547 1548 1549 1550 1551 1552 | if (fnc_init.vflag) { fnc_show_version(); goto end; } else if (fnc_init.hflag) { rc = FCLI_RC_HELP; goto end; } if (!fsl_cx_db_repo(fcli_cx())) { rc = RC(FSL_RC_MISUSE, "repository database required"); 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 (rc) goto end; if (fcli.argc) { rc = fcli_dispatch_commands(fnc_init.cmd_args, false); if (rc == FCLI_RC_NO_CMD && fcli.argc == 1) { /* * Check if user entered fnc path/in/repo; if valid path * is found, assume fnc timeline path/in/repo was meant. */ rc = map_ckout_path(&path); if (rc && rc != FSL_RC_NOT_FOUND) goto end; /* Path may have existed in the repo at some point. */ RC_RESET(rc); /* for fcli_process_flags */ cmd = &fnc_init.cmd_args[FNC_VIEW_TIMELINE]; fnc_init.path = path; } } else cmd = &fnc_init.cmd_args[FNC_VIEW_TIMELINE]; if (fcli.transient.helpRequested) 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 == FCLI_RC_HELP) { fnc_init.err = FCLI_RC_HELP; usage(); /* NOT REACHED */ } } putchar('\n'); return fcli_end_of_main(rc); } |
︙ | ︙ | |||
1539 1540 1541 1542 1543 1544 1545 | } if (fnc_init.glob) glob = fsl_strdup(fnc_init.glob); if (fnc_init.path) path = fsl_strdup(fnc_init.path); else { | | | > | > > > | 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 | } if (fnc_init.glob) glob = fsl_strdup(fnc_init.glob); if (fnc_init.path) path = fsl_strdup(fnc_init.path); else { rc = map_ckout_path(&path); if (rc) { if (rc != FSL_RC_NOT_FOUND && !fnc_init.sym) goto end; /* Path may have existed in the repo at some point. */ RC_RESET(rc); } } 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); |
︙ | ︙ | |||
1577 1578 1579 1580 1581 1582 1583 | if (!rc) rc = open_timeline_view(*view, rid, path, glob); return rc; } /* | | | | | > | | | | < | < < < < < < < < < | > > | | | | | < < < > > | | | | | | | | | < < | | | | | < | | < < < < | > > | | | | | > | | | | | | | | < < < < | | | > | | | | | | < < | > < < < < < < < < | < | | < < < < < < < < < < < < < < < < | | < > | | | | | > > | 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 1782 1783 1784 1785 | if (!rc) rc = open_timeline_view(*view, rid, path, glob); return rc; } /* * Look for a work tree path in **argv. If found, canonicalise path as * relative to the root of the tree (e.g., ckoutdir/found/path), and assign to * a dynamically allocated string in *requested_path, which the caller must * free. As a special case, if path is the root of the tree, *requested_path * will be NULL. If path is not found, return FSL_RC_NOT_FOUND. */ static int map_ckout_path(char **requested_path) { fsl_cx *const f = fcli_cx(); fsl_buffer buf = fsl_buffer_empty; char *ckoutdir = NULL, *path = NULL; const char *canonpath = NULL, *ckoutdir0 = NULL; fsl_size_t len; int rc = 0; bool root; *requested_path = NULL; /* If no path argument is supplied, default to repository root. */ canonpath = fcli_next_arg(true); if (canonpath == NULL) return rc; /* * 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; } path = realpath(fsl_buffer_cstr(&buf), NULL); if (!path) rc = RC(FSL_RC_NOT_FOUND, "path not found in checkout: %s", canonpath); /* Confirmed path is relative to repository root. */ fsl_free(path); path = fsl_strdup(canonpath); if (!rc && path == NULL) rc = RC(FSL_RC_ERROR, "fsl_strdup"); 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)); fsl_buffer_reuse(&buf); rc = fsl_ckout_filename_check(f, (*canonpath == '.' || !root) ? true : false, path, &buf); if (rc) goto end; fsl_free(path); path = fsl_buffer_take(&buf); if (*path == '\0' || (*path == '.' && !path[1])) { fsl_free(path); path = NULL; goto end; } fsl_buffer_reuse(&buf); rc = fsl_file_canonical_name2(f->ckout.dir, path, &buf, false); if (rc) goto end; fsl_free(path); path = fsl_buffer_take(&buf); 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 = NULL; } 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); fsl_free(path); if (!rc) path = child; } end: /* Trim trailing slash if it exists. */ if (path && path[fsl_strlen(path) - 1] == '/') path[fsl_strlen(path) - 1] = '\0'; *requested_path = path; fsl_buffer_clear(&buf); 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; } /* * NULL, "/", and "." all resolve to the repository root. The latter being a * special case, due to fsl_ckout_filename_check() resolving the current * checkout root directory to ".". 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) { if (!path) return true; while (*path == '/' || *path == '.') ++path; return (*path == '\0'); } static int path_skip_common_ancestor(char **child, const char *parent_abspath, |
︙ | ︙ | |||
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 | 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(); } | > > > > > > | > > > > > > > > > | 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 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 | return (path[0] == '.' && path[1] == '\0'); } #endif static int init_curses(void) { int rc; rc = fnc_set_signals(); if (rc) return rc; 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 FSL_RC_OK; } 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)"); if (sigaction(SIGINT, &(struct sigaction){{sigint_handler}}, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "sigaction(SIGINT)"); if (sigaction(SIGTERM, &(struct sigaction){{sigterm_handler}}, NULL) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "sigaction(SIGTERM)"); 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) |
︙ | ︙ | |||
1928 1929 1930 1931 1932 1933 1934 | if (rid) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); else fsl_ckout_version_info(f, NULL, &s->curr_ckout_uuid); /* | | | | | < | < < < < < < < < < < < < | < < | < | | < | 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 1973 1974 | 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 tl -R <repo> <path>' or 'fnc tl -c <sym> <path>' case, * check that path is a valid path in the tree as at either the * latest checkin or the specified commit. */ if (s->curr_ckout_uuid == NULL && path) { fsl_uuid_str id = NULL; if (rid) id = fsl_rid_to_uuid(f, rid); rc = valid_path(path, id ? id : "tip"); fsl_free(id); if (rc) return rc; } 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))) { |
︙ | ︙ | |||
2081 2082 2083 2084 2085 2086 2087 | if (startdate) { fsl_buffer_appendf(&sql, " AND event.mtime <= %s", startdate); fsl_free(startdate); } /* | | | | | | | | < < | 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 (NULL, /, .), a versioned path in the repository * has been requested, only retrieve commits involving path. */ if (path && 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, path); else fsl_buffer_appendf(&sql, "(SELECT fnid FROM filename" " WHERE name = %Q COLLATE nocase" " OR lower(name) GLOB lower('%q/*'))", path, path); 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 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 | 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: if (path == NULL) rc = RC(FSL_RC_NOT_FOUND, "no matching records"); else { fcli_command *c = &fnc_init.cmd_args[FNC_VIEW_TIMELINE]; const char *say = "invalid command or path: "; if (!fsl_strcmp(c->name, fnc_init.cmdarg) || !fcli_cmd_aliascmp(c, fnc_init.cmdarg)) say = "path not found in repository: "; rc = RC(FSL_RC_NOT_FOUND, "%s%s", say, path); } goto end; } s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = true; s->thread_cx.rc = 0; s->thread_cx.db = db; |
︙ | ︙ | |||
2189 2190 2191 2192 2193 2194 2195 | TAILQ_INSERT_HEAD(&views, view, entries); view->active = true; rc = view->show(view); if (rc) return rc; | | | 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 | TAILQ_INSERT_HEAD(&views, view, entries); view->active = true; rc = view->show(view); if (rc) return rc; while (!TAILQ_EMPTY(&views) && !done && !fatal_signal()) { 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)) |
︙ | ︙ | |||
2327 2328 2329 2330 2331 2332 2333 | int rc; bool done = false; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; | | | 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 | int rc; bool done = false; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; while (!done && !rc && !fatal_signal()) { switch (rc = build_commits(cx)) { case FSL_RC_STEP_DONE: done = true; /* FALL THROUGH */ case FSL_RC_STEP_ROW: rc = 0; /* FALL THROUGH */ |
︙ | ︙ | |||
2391 2392 2393 2394 2395 2396 2397 | block_main_thread_signals(void) { sigset_t set; if (sigemptyset(&set) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigemptyset"); | | > > > > | 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 | block_main_thread_signals(void) { sigset_t set; if (sigemptyset(&set) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigemptyset"); /* Bespoke handlers for SIGWINCH, SIGCONT, SIGINT, and SIGTERM. */ 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"); if (sigaddset(&set, SIGINT) == -1) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), "sigaddset"); if (sigaddset(&set, SIGTERM) == -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)) return RC(fsl_errno_to_rc(errno, FSL_RC_MISUSE), |
︙ | ︙ | |||
2661 2662 2663 2664 2665 2666 2667 | 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; | | | 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 | 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 = 0; 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); |
︙ | ︙ | |||
2718 2719 2720 2721 2722 2723 2724 | * 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. */ | | | | 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 | * 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 && 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; |
︙ | ︙ | |||
2747 2748 2749 2750 2751 2752 2753 | rc = formatln(&wline, &wlen, headln, view->ncols, 0, false); if (rc) goto end; werase(view->window); if (screen_is_shared(view) || view->active) | | | 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 | rc = formatln(&wline, &wlen, headln, view->ncols, 0, false); if (rc) goto end; werase(view->window); if (screen_is_shared(view) || view->active) rx = FNC_HIGHLIGHT; 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) { |
︙ | ︙ | |||
3439 3440 3441 3442 3443 3444 3445 3446 | {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, {" # ", " ❬#❭ "}, {" @ ", " ❬@❭ "}, {" C-e ", " ❬C-e❭ "}, {" C-y ", " ❬C-y❭ "}, {" C-n ", " ❬C-n❭ "}, |