Compare commits

...
Author SHA1 Message Date
Antonio SJ Musumeci 11a5706dc3 config.cpp 2025-10-02 20:24:41 -05:00
Antonio SJ Musumeci fe6518c4a6 config.cpp 2025-10-02 20:22:46 -05:00
Antonio SJ Musumeci 64d37be555 option_parser.cpp 2025-10-02 20:20:40 -05:00
Antonio SJ Musumeci e06fc110ad option_parser.cpp 2025-10-02 20:20:24 -05:00
Antonio SJ Musumeci d9ea3fff4b option_parser.cpp 2025-10-02 20:20:11 -05:00
Antonio SJ Musumeci 02544b649f from_string.cpp 2025-10-02 20:19:15 -05:00
Antonio SJ Musumeci efa630db5c from_string.cpp 2025-10-02 20:18:57 -05:00
Antonio SJ Musumeci 4820724f9d from_string.hpp 2025-10-02 20:18:04 -05:00
Antonio SJ Musumeci 7f215c5ffd to_string.cpp 2025-10-02 20:17:30 -05:00
Antonio SJ Musumeci 587d2dbd43 to_string.hpp 2025-10-02 20:17:00 -05:00
Antonio SJ Musumeci e95b409276 config.hpp 2025-10-02 20:16:41 -05:00
Antonio SJ Musumeci 7f9f7c5278 config.hpp 2025-10-02 20:16:22 -05:00
Antonio SJ Musumeci f92daf0a9e config.hpp 2025-10-02 20:15:55 -05:00
Antonio SJ Musumeci d86224571b config.hpp 2025-10-02 20:15:48 -05:00
Antonio SJ Musumeci f400eec2ad config.hpp 2025-10-02 20:15:14 -05:00
Antonio SJ Musumeci 7566c0485a config.hpp 2025-10-02 20:14:45 -05:00
Antonio SJ Musumeci f748d63dd0 config.hpp 2025-10-02 20:10:14 -05:00
Antonio SJ Musumeci 915b60a957 option_parser.cpp 2025-10-02 20:09:04 -05:00
Antonio SJ Musumeci 1d0f5b1180 option_parser.cpp 2025-10-02 20:08:59 -05:00
Antonio SJ Musumeci 1e8aa240e2 option_parser.cpp 2025-10-02 20:08:08 -05:00
Antonio SJ Musumeci e5a4793332 option_parser.cpp 2025-10-02 20:02:28 -05:00
Antonio SJ Musumeci bb7daa6173 option_parser.cpp 2025-10-02 19:40:58 -05:00
Antonio SJ Musumeci 02b87df975 option_parser.cpp 2025-10-02 19:40:18 -05:00
Antonio SJ Musumeci 133539e4b6 option_parser.cpp 2025-10-02 19:35:58 -05:00
Antonio SJ Musumeci 77805d4742 option_parser.cpp 2025-10-02 19:35:45 -05:00
Antonio SJ Musumeci f48648b056 option_parser.cpp 2025-10-02 19:35:36 -05:00
Antonio SJ Musumeci 798222f37f option_parser.cpp 2025-10-02 19:35:30 -05:00
Antonio SJ Musumeci 641bf41023 option_parser.cpp 2025-10-02 19:35:18 -05:00
Antonio SJ Musumeci b3f9c20279 option_parser.cpp 2025-10-02 19:35:08 -05:00
Antonio SJ Musumeci a8357acbc6 option_parser.cpp 2025-10-02 19:35:00 -05:00
Antonio SJ Musumeci 539c90b029 option_parser.cpp 2025-10-02 19:34:48 -05:00
Antonio SJ Musumeci c531289541 option_parser.cpp 2025-10-02 19:34:43 -05:00
Antonio SJ Musumeci 8f5ef638ec option_parser.cpp 2025-10-02 19:34:38 -05:00
Antonio SJ Musumeci 8efdb641e7 option_parser.cpp 2025-10-02 19:34:34 -05:00
Antonio SJ Musumeci abc05c4c0f option_parser.cpp 2025-10-02 19:34:28 -05:00
Antonio SJ Musumeci 435632b737 option_parser.cpp 2025-10-02 19:34:19 -05:00
Antonio SJ Musumeci d3c1ebf19f option_parser.cpp 2025-10-02 19:34:11 -05:00
Antonio SJ Musumeci 82c72cf6a0 option_parser.cpp 2025-10-02 19:34:03 -05:00
Antonio SJ Musumeci 1d6fe14c5d option_parser.cpp 2025-10-02 19:33:59 -05:00
Antonio SJ Musumeci 2d9300aadc option_parser.cpp 2025-10-02 19:33:48 -05:00
Antonio SJ Musumeci 01c4e72ff4 option_parser.cpp 2025-10-02 19:33:46 -05:00
Antonio SJ Musumeci 6390c34585 option_parser.cpp 2025-10-02 19:33:34 -05:00
Antonio SJ Musumeci 2de22c8893 option_parser.cpp 2025-10-02 19:32:38 -05:00
Antonio SJ Musumeci 517ca11335 option_parser.cpp 2025-10-02 19:32:08 -05:00
Antonio SJ Musumeci 921a21f5a0 option_parser.cpp 2025-10-02 19:22:39 -05:00
Antonio SJ Musumeci 1287daf86e option_parser.cpp 2025-10-02 19:22:18 -05:00
Antonio SJ Musumeci 90c511fbde option_parser.cpp 2025-10-02 19:22:03 -05:00
Antonio SJ Musumeci b4ab6daf88 option_parser.cpp 2025-10-02 19:21:38 -05:00
Antonio SJ Musumeci 6578b589c6 option_parser.cpp 2025-10-02 19:17:34 -05:00
Antonio SJ Musumeci e5bf3e265f option_parser.cpp 2025-10-02 19:17:33 -05:00
Antonio SJ Musumeci b9d2895429 option_parser.cpp 2025-10-02 19:16:53 -05:00
Antonio SJ Musumeci 2d8aad7c77 option_parser.cpp 2025-10-02 19:16:05 -05:00
Antonio SJ Musumeci f398ea2393 option_parser.cpp 2025-10-02 19:15:53 -05:00
Antonio SJ Musumeci d4c39b46d6 option_parser.cpp 2025-10-02 19:11:42 -05:00
Antonio SJ Musumeci 94bf1ef7dc option_parser.cpp 2025-10-02 19:10:58 -05:00
Antonio SJ Musumeci 14ef951f87 option_parser.cpp 2025-10-02 19:07:43 -05:00
Antonio SJ Musumeci 9def5089d3 option_parser.cpp 2025-10-02 19:07:30 -05:00
Antonio SJ Musumeci 45bb98343f option_parser.cpp 2025-10-02 19:05:59 -05:00
Antonio SJ Musumeci 988ec2f7aa option_parser.cpp 2025-10-02 19:05:55 -05:00
Antonio SJ Musumeci 72dd01fd46 option_parser.cpp 2025-10-02 19:05:38 -05:00
Antonio SJ Musumeci 8f45dd01c1 option_parser.cpp 2025-10-02 19:02:49 -05:00
Antonio SJ Musumeci 8106ec2664 option_parser.cpp 2025-10-02 19:00:31 -05:00
Antonio SJ Musumeci 87b4971c26 option_parser.cpp 2025-10-02 18:59:52 -05:00
Antonio SJ Musumeci 73bd437213 option_parser.cpp 2025-10-02 18:59:28 -05:00
Antonio SJ Musumeci d8b27f1a89 option_parser.cpp 2025-10-02 18:56:45 -05:00
Antonio SJ Musumeci aecc6f3d84 option_parser.cpp 2025-10-02 18:56:38 -05:00
Antonio SJ Musumeci 59560b5e2b option_parser.cpp 2025-10-02 18:56:28 -05:00
Antonio SJ Musumeci 7429c3a779 option_parser.cpp 2025-10-02 18:56:17 -05:00
Antonio SJ Musumeci 5d3206f255 option_parser.cpp 2025-10-02 18:55:04 -05:00
Antonio SJ Musumeci 12e8345183 option_parser.cpp 2025-10-02 18:54:47 -05:00
Antonio SJ Musumeci 22c58fce90 option_parser.cpp 2025-10-02 18:53:10 -05:00
Antonio SJ Musumeci f23ede2ccb option_parser.cpp 2025-10-02 18:52:14 -05:00
Antonio SJ Musumeci df7b9fd538 option_parser.cpp 2025-10-02 18:51:58 -05:00
Antonio SJ Musumeci 9bbd5397f6 option_parser.cpp 2025-10-02 18:51:46 -05:00
Antonio SJ Musumeci d0080985ac option_parser.cpp 2025-10-02 18:51:22 -05:00
Antonio SJ Musumeci 2bfa459323 option_parser.cpp 2025-10-02 18:51:18 -05:00
Antonio SJ Musumeci cf07e9cb89 mergerfs.cpp 2025-10-02 18:40:48 -05:00
Antonio SJ Musumeci 075cbfc23f mergerfs.cpp 2025-10-02 18:37:34 -05:00
Antonio SJ Musumeci 9826e97693 mergerfs.cpp 2025-10-02 18:36:32 -05:00
Antonio SJ Musumeci 9843f48627 mergerfs.cpp 2025-10-02 18:35:45 -05:00
Antonio SJ Musumeci 21c41c1e85 mergerfs.cpp 2025-10-02 18:35:17 -05:00
Antonio SJ Musumeci d26d7c439f mergerfs.cpp 2025-10-02 18:35:07 -05:00
Antonio SJ Musumeci 5e607d5dc1 mergerfs.cpp 2025-10-02 18:34:01 -05:00
Antonio SJ Musumeci c39ddb09d1 mergerfs.cpp 2025-10-02 18:33:54 -05:00
Antonio SJ Musumeci addcdd6246 mergerfs.cpp 2025-10-02 18:33:35 -05:00
Antonio SJ Musumeci 9f43af18cc mergerfs.cpp 2025-10-02 18:21:58 -05:00
Antonio SJ Musumeci 8399ca0694 mergerfs.cpp 2025-10-02 18:20:55 -05:00
Antonio SJ Musumeci 4c358f9a3e mergerfs.cpp 2025-10-02 18:19:00 -05:00
Antonio SJ Musumeci 9640e1bf4d mergerfs.cpp 2025-10-02 18:16:44 -05:00
Antonio SJ Musumeci 79eafe549d mergerfs.cpp 2025-10-02 18:11:35 -05:00
Antonio SJ Musumeci 3ca304aad6 mergerfs.cpp 2025-10-02 18:11:26 -05:00
Antonio SJ Musumeci f81058ddc8 mergerfs.cpp 2025-10-02 17:56:22 -05:00
Antonio SJ Musumeci 335f4cf62b mergerfs.cpp 2025-10-02 17:55:59 -05:00
Antonio SJ Musumeci d8750e4343 mergerfs.cpp 2025-10-02 17:55:53 -05:00
Antonio SJ Musumeci 3742a3a937 mergerfs.cpp 2025-10-02 17:46:48 -05:00
Antonio SJ Musumeci a507019415 mergerfs.cpp 2025-10-02 17:46:46 -05:00
Antonio SJ Musumeci 9486554e4e mergerfs.cpp 2025-10-02 17:45:26 -05:00
Antonio SJ Musumeci 9a2a4076ba mergerfs.cpp 2025-10-02 17:45:06 -05:00
Antonio SJ Musumeci 9de89cd29b mergerfs.cpp 2025-10-02 17:43:56 -05:00
Antonio SJ Musumeci c0635b4240 mergerfs.cpp 2025-10-02 17:43:02 -05:00
Antonio SJ Musumeci 857cde4634 mergerfs.cpp 2025-10-02 17:42:43 -05:00
Antonio SJ Musumeci 353032a9ad mergerfs.cpp 2025-10-02 17:38:03 -05:00
Antonio SJ Musumeci 7dae8d6746 mergerfs.cpp 2025-10-02 17:38:00 -05:00
Antonio SJ Musumeci f97b780546 mergerfs.cpp 2025-10-02 17:37:48 -05:00
Antonio SJ Musumeci c2bf982be6 mergerfs.cpp 2025-10-02 17:37:25 -05:00
Antonio SJ Musumeci 2f75003e0e mergerfs.cpp 2025-10-02 17:37:16 -05:00
Antonio SJ Musumeci 70411b35a6 mergerfs.cpp 2025-10-02 17:34:00 -05:00
Antonio SJ Musumeci 34386ea0fc mergerfs.cpp 2025-10-02 17:33:58 -05:00
Antonio SJ Musumeci 3c7adb71e5 mergerfs.cpp 2025-10-02 17:33:03 -05:00
Antonio SJ Musumeci e116f195c4 mergerfs.cpp 2025-10-02 17:33:02 -05:00
Antonio SJ Musumeci aa762bbcf8 mergerfs.cpp 2025-10-02 17:32:04 -05:00
Antonio SJ Musumeci d58b3e5e26 mergerfs.cpp 2025-10-02 17:31:42 -05:00
Antonio SJ Musumeci 39479f747f mergerfs.cpp 2025-10-02 17:31:14 -05:00
Antonio SJ Musumeci 969037a293 mergerfs.cpp 2025-10-02 17:30:25 -05:00
Antonio SJ Musumeci 22f6277deb mergerfs.cpp 2025-10-02 17:30:03 -05:00
Antonio SJ Musumeci 864526c668 mergerfs.cpp 2025-10-02 17:29:41 -05:00
Antonio SJ Musumeci 04e2342848 mergerfs.cpp 2025-10-02 17:29:11 -05:00
Antonio SJ Musumeci 20e9053715 mergerfs.cpp 2025-10-02 17:26:26 -05:00
Antonio SJ Musumeci d8399f375a mergerfs.cpp 2025-10-02 17:26:23 -05:00
Antonio SJ Musumeci a8c38a6f86 mergerfs.cpp 2025-10-02 17:26:20 -05:00
Antonio SJ Musumeci 4b2c92db90 mergerfs.cpp 2025-10-02 17:25:50 -05:00
Antonio SJ Musumeci 6c1190566b mergerfs.cpp 2025-10-02 17:24:45 -05:00
Antonio SJ Musumeci ce4a88b27c mergerfs.cpp 2025-10-02 17:24:43 -05:00
Antonio SJ Musumeci 8f9da0d5b1 mergerfs.cpp 2025-10-02 17:24:13 -05:00
Antonio SJ Musumeci ea15534e52 mergerfs.cpp 2025-10-02 17:22:38 -05:00
Antonio SJ Musumeci e81dbc2adc mergerfs.cpp 2025-10-02 17:21:33 -05:00
Antonio SJ Musumeci 1df1a94231 mergerfs.cpp 2025-10-02 17:20:33 -05:00
Antonio SJ Musumeci 4e68e1eed9 mergerfs.cpp 2025-10-02 17:20:31 -05:00
Antonio SJ Musumeci 26b5585911 mergerfs.cpp 2025-10-02 17:20:16 -05:00
Antonio SJ Musumeci 12cbd2f88a mergerfs.cpp 2025-10-02 17:20:07 -05:00
Antonio SJ Musumeci 73db2ca9b7 mergerfs.cpp 2025-10-02 17:19:53 -05:00
Antonio SJ Musumeci c56b0e644f mergerfs.cpp 2025-10-02 17:18:36 -05:00
Antonio SJ Musumeci b72f8587c3 mergerfs.cpp 2025-10-02 17:18:15 -05:00
Antonio SJ Musumeci c25340aba5 mergerfs.cpp 2025-10-02 17:15:58 -05:00
Antonio SJ Musumeci a7de2c5212 mergerfs.cpp 2025-10-02 17:15:51 -05:00
Antonio SJ Musumeci 851b982518 mergerfs.cpp 2025-10-02 17:15:43 -05:00
Antonio SJ Musumeci f30ad6d45b mergerfs.cpp 2025-10-02 17:15:19 -05:00
Antonio SJ Musumeci 16110672fc mergerfs.cpp 2025-10-02 17:15:15 -05:00
Antonio SJ Musumeci 750866d842 mergerfs.cpp 2025-10-02 17:15:13 -05:00
Antonio SJ Musumeci 7f039a5e7e mergerfs.cpp 2025-10-02 17:13:22 -05:00
Antonio SJ Musumeci 52fe5a5e40 mergerfs.cpp 2025-10-02 17:12:44 -05:00
Antonio SJ Musumeci af5928cbe2 mergerfs.cpp 2025-10-02 17:12:29 -05:00
Antonio SJ Musumeci 6c45d23ecb mergerfs.cpp 2025-10-02 17:10:08 -05:00
Antonio SJ Musumeci 2b2877b8f3 mergerfs.cpp 2025-10-02 17:08:44 -05:00
Antonio SJ Musumeci 882ad29b87 mergerfs.cpp 2025-10-02 17:08:03 -05:00
Antonio SJ Musumeci 8340bc9370 mergerfs.cpp 2025-10-02 17:04:29 -05:00
Antonio SJ Musumeci 92e89a9e99 mergerfs.cpp 2025-10-02 17:02:49 -05:00
Antonio SJ Musumeci 5da92025e0 fuse.cpp 2025-10-01 17:29:57 -05:00
Antonio SJ Musumeci ad8c19118e option_parser.cpp 2025-10-01 17:15:51 -05:00
Antonio SJ Musumeci d4d3aee354 option_parser.cpp 2025-10-01 17:13:43 -05:00
Antonio SJ Musumeci 4b580522c1 Allow setting of passthrough max_stack_depth 2025-10-01 16:32:14 -05:00
trapexit 25b0bc72df Fix dirent64::namelen calculation (#1538)
Worked fine on my machine where padding seemed to be all zeroed out
but that doesn't appear to be the case elsewhere.

Could try
https://codeberg.org/jbruchon/libjodycode/src/branch/master/dir.c#L92-L110
but needs to be tested.
2025-09-30 11:25:19 -05:00
trapexit e6fcebe4f3 Misc build fixes (#1537) 2025-09-29 14:05:07 -05:00
trapexit 46f85c0f6d Misc fixes, mostly for FreeBSD (#1536) 2025-09-28 23:17:51 -05:00
trapexit 7a9c3b7eb2 Remove need for and use of ioprio header (#1535) 2025-09-28 00:01:26 -05:00
trapexit 569a151da2 Add ability to proxy ioprio of client apps (#1534) 2025-09-26 11:36:56 -05:00
trapexit 2f3e807e9d Add getdents based readdir functions for Linux (#1533) 2025-09-23 23:51:21 -05:00
trapexit 95ab37fdd2 Fix typo in visualization and add new vid to docs (#1532) 2025-09-22 16:23:50 -05:00
trapexit 1707dc27e3 Misc doc updates (#1531) 2025-09-21 14:25:24 -05:00
trapexit c4126a0796 Add script to generate docs from scratch and push to github (#1530) 2025-09-21 13:04:55 -05:00
pjv 6acef3a638 Fix typo in moveonenospc.md (#1527)
typo
2025-09-21 11:42:15 -05:00
trapexit b3656a0243 Fix broken doc links (#1525) 2025-09-19 19:25:31 -05:00
trapexit 41b40b94fa Misc docs updates (#1524) 2025-09-19 19:20:28 -05:00
trapexit ca59ae53a5 idmap mount support (#1523)
* Add idmap mount support

* Further replacement of std::filesystem::path to fs::path

* Update docs regarding idmap support
2025-09-18 00:07:28 -05:00
trapexit 8c649d33ee Start using std::filesystem::path for fusepath (#1522) 2025-09-17 22:51:16 -05:00
trapexit f64ff9ca1e Revert removal of readdir init setup, simplify cfg usage (#1520) 2025-09-06 11:03:51 -05:00
trapexit 99c28ad3bc Add Ubuntu 25.04 builds (#1519) 2025-09-04 19:29:47 -05:00
trapexit 5becb334cd Misc updates of docs (#1518) 2025-09-01 20:58:58 -05:00
trapexit 071af2e516 More build process updates (#1516) 2025-08-29 07:51:05 -05:00
trapexit 90b3585347 Update readme to match index of mkdocs (#1515) 2025-08-22 20:53:22 -05:00
trapexit d7549f8d53 Fix string manipulation functions after refactor (#1514) 2025-08-22 19:42:31 -05:00
trapexit 6ac7f389c5 Misc changes (#1513)
* Start moving to string_view where possible
* Rework readdir object to use atomic load/store
2025-08-21 22:42:58 -05:00
trapexit 2101ef5bcc Updates to docs, add nonraid, more intro to fs details (#1512) 2025-08-21 15:09:04 -05:00
Max.-F. HelmandMax-F-Helm dd1bea52cd Allow empty lines in config ini files (#1509)
* Allow empty lines in config files

* Add example comment to ini example in docs/quickstart.md

---------

Co-authored-by: Max-F-Helm <m7t24zklr@relay.firefox.com>
2025-08-13 20:00:05 -05:00
trapexit df19f99976 Fix constructing string from attrval setxattr config (#1508) 2025-08-11 19:50:12 -05:00
trapexit 24e778b95a Rework calculation of branch for release make targets (#1507) 2025-08-10 18:39:18 -05:00
trapexit a8a7b34cf3 Add Debian 13 build targets (#1506) 2025-08-10 17:50:06 -05:00
trapexit 6ae9641bcc Misc documentation updates (#1505) 2025-08-10 17:23:55 -05:00
trapexit 4439c2a030 Add setting of oom_score_adj (#1504)
To limit risk of killing by OOM Killer.
2025-08-07 23:58:06 -05:00
trapexit 9872710720 More makefile tweaks, build log, smaller git clones (#1503) 2025-08-06 12:24:25 -05:00
trapexit b18aba86b6 Build improvements (#1501)
* Remove python dependency
* Remove ecfd
* Improve makefiles
* Make preload.so more 32bit compatible
2025-08-04 23:39:52 -05:00
trapexit 33fc26f81f Cleanup copy functions (#1500) 2025-07-27 16:23:45 -05:00
trapexit e8cd74cab0 Move everything to negative errno return types (#1499) 2025-07-25 17:53:48 -05:00
trapexit 2b500f194a Rework of runtime interface (#1498)
Also have special error handling for when branches are invalid and
ENOENT would be returned for getattr and readdir so users understand
what is going on and the runtime interface can still be used to fix
the problem.
2025-07-21 18:18:59 -05:00
trapexit e87867cd2a Clear O_NOFOLLOW when opening a relative fd (#1494) 2025-07-08 18:25:00 -05:00
trapexit fa851eda3a Ensure passthrough and keep_cache are mutually exclusive (#1493) 2025-07-06 18:31:47 -05:00
trapexit f852474073 Update the secondary group cache (#1492)
* Use concurrent_flat_map to build a global cache rather than per
thread.
* Create global maintanence thread manager for clearing unused cache
entries.
2025-07-05 17:05:53 -05:00
trapexit 9e626228be Add mergerfs.collect-info (#1491)
A tool to collect info for support requests.
2025-07-03 20:18:05 -05:00
trapexit 71ddae8713 Misc documentation changes (#1489) 2025-06-29 19:08:57 -05:00
trapexit e216a81a27 Rework movefile and cow break to use copyfile (#1488) 2025-06-28 17:27:40 -05:00
trapexit 264c5600f8 Misc cleanup (#1486)
* Skip non-directories when loading branches
* Warn if branch doesn't exist
* Add braces to globbing
2025-06-25 22:43:49 -05:00
trapexit be49dbe1bd Remove linux only getdents in favor of readdir for FreeBSD sake (#1485) 2025-06-24 19:31:40 -05:00
trapexit eb124316c9 Set default read thread count to 0 (#1484) 2025-06-23 22:39:42 -05:00
trapexit 77ef88b04c Add fsck.mergerfs tool (#1483) 2025-06-23 21:57:55 -05:00
trapexit 34f07e57f5 Add RHEL/CentOS 10 build support (#1479) 2025-06-09 14:33:55 -05:00
trapexit 095fcf82a3 Misc doc updates 2025-06-08 23:59:17 -05:00
trapexit 1b167d34d8 Spawn "mount" on branches when waiting for mounts enabled 2025-06-08 15:09:47 -05:00
trapexit 23824eaeb4 Misc cleanup 2025-06-06 19:10:19 -05:00
trapexit e310d24615 Add support for FUSE passthrough 2025-06-05 23:47:42 -05:00
trapexit 92312c8507 Add debugging of mutexes (#1470)
When DEBUG defined it will used timed locks which will log when held
too long along with where.
2025-05-26 00:44:39 -05:00
trapexit 4b751c2d37 Fix incorrect dyn cast (#1467) 2025-05-19 22:46:55 -05:00
trapexit 86b50d2d4e Rework policies to return branches rather than path strings 2025-05-17 16:35:18 -05:00
trapexit 7e084ea7db Update README.md to fix link (#1464) 2025-05-12 09:48:19 -05:00
trapexit 6eca711f58 Update mkdocs build action (#1458) 2025-05-10 12:56:10 -05:00
trapexit 923d9608e8 Add details to docs about FreeBSD limitations 2025-05-09 21:18:20 -05:00
trapexit 3a034712f6 Add versioning to docs using 'mike' 2025-05-08 19:53:54 -05:00
Zack Guo 5db04b4b5f Update installation.md for SUSE 2025-05-08 07:44:57 -05:00
trapexit fa3266ca7f Add podman release build tooling + misc build fixes (#1455) 2025-05-08 00:00:53 -05:00
trapexit 344014aa81 Rework thread pool for increased stability + config and doc updates (#1453)
Also updates fmt to 11.2.0 and replaced syslog usage with a wrapper
leveraging fmt.
2025-05-05 18:28:49 -05:00
SolipsistmonkeyandJesse Harwin 455a81290b Fix build instructions typo
the location of install-build-pkgs was in buildtools and not tools
---------

Co-authored-by: Jesse Harwin <jesse@jharwin.com>
2025-04-28 14:00:20 -05:00
trapexit deb54c58d8 Merge pull request #1448 from trapexit/defaultperms
Add config option to control default_permissions
2025-04-25 17:34:16 -05:00
Antonio SJ Musumeci 2b04a791b7 Add config option to control default_permissions
Defaults true but there might be some situations where it is useful to
disable.
2025-04-25 16:34:39 -05:00
trapexit 8ee998e8aa Merge pull request #1447 from trapexit/branchcfg
Improve mount waiting + misc doc improvements
2025-04-23 16:40:26 -05:00
Antonio SJ Musumeci de2a82800a Improve mount waiting + misc doc improvements
Adds user.mergerfs.branch and user.mergerfs.branch_mounts_here
Adds .mergerfs.branch and .mergerfs.branch_mounts_here files.
2025-04-23 15:58:36 -05:00
trapexit 212681ddc5 Merge pull request #1445 from trapexit/docs
Misc doc updates + add logo
2025-04-19 22:45:27 -05:00
Antonio SJ Musumeci 9dac54f223 Misc doc updates + add logo 2025-04-19 22:44:08 -05:00
trapexit 78fa34098e Merge pull request #1444 from trapexit/docs
Add FAQ entry regarding OS support
2025-04-15 23:41:45 -05:00
Antonio SJ Musumeci 54681e885a Add FAQ entry regarding OS support 2025-04-15 23:40:42 -05:00
trapexit de5a871326 Merge pull request #1443 from trapexit/inode
Change the "devino" inode calculation
2025-04-15 19:29:43 -05:00
Antonio SJ Musumeci 77bf16e9ae Change the "devino" inode calculation
Use hashing of branch path rather than use st_dev.
2025-04-15 18:54:51 -05:00
trapexit ad54c8be19 Merge pull request #1441 from trapexit/docs
Add doc pages for minfreespace and moveonenospc + other misc updates
2025-04-10 23:00:43 -05:00
Antonio SJ Musumeci c769e959e8 Add doc pages for minfreespace and moveonenospc + other misc updates 2025-04-10 22:59:49 -05:00
trapexit 1f3ebcf418 Merge pull request #1439 from trapexit/statx
Add statx support
2025-04-04 22:25:30 -05:00
Antonio SJ Musumeci 80085c9844 Add statx support 2025-04-04 22:23:16 -05:00
trapexit b85cc8c370 Merge pull request #1438 from trapexit/docs
Update docs regarding error handling
2025-04-04 18:42:37 -05:00
Antonio SJ Musumeci 95732222d9 Update docs regarding error handling 2025-04-04 18:41:58 -05:00
trapexit 17a551d0a4 Merge pull request #1435 from trapexit/rapidhash
Replace usage of wyhash with rapidhash
2025-03-30 18:03:35 -05:00
Antonio SJ Musumeci f74dc36e92 Replace usage of wyhash with rapidhash
https://github.com/Nicoshev/rapidhash
2025-03-30 17:57:20 -05:00
trapexit 764bd8f7be Merge pull request #1434 from trapexit/change-defaults
Change misc defaults
2025-03-30 17:25:18 -05:00
Antonio SJ Musumeci b7f7f7fa97 Change misc defaults
* category.create=pfrd
* moveonenospc=pfrd
* cache.files=off
* parallel-direct-writes=true

These changes were made to match the recommended settings for most
users to reduce confusion.
2025-03-30 17:22:38 -05:00
trapexit 7f410073d4 Merge pull request #1433 from trapexit/maxpages
Support Linux v6.13 FUSE max_page_limit
2025-03-30 00:09:03 -05:00
Antonio SJ Musumeci 92ae63e842 Support Linux v6.13 FUSE max_page_limit 2025-03-30 00:07:08 -05:00
trapexit d31e97e64c Merge pull request #1431 from trapexit/docs
Add links to options docs page to individual pages
2025-03-27 19:55:19 -05:00
Antonio SJ Musumeci 579690f411 Add links to options docs page to individual pages 2025-03-27 19:47:37 -05:00
trapexit ca449a73be Merge pull request #1429 from trapexit/docs
Update docs on default create policy
2025-03-26 14:34:28 -05:00
Antonio SJ Musumeci 2d86ed946a Update docs on default create policy 2025-03-26 14:33:11 -05:00
trapexit 3e454f528c Merge pull request #1426 from trapexit/docs
Misc doc updates
2025-03-12 18:30:21 -05:00
Antonio SJ Musumeci a4fdbae1ca Misc doc updates 2025-03-12 18:29:38 -05:00
trapexit aff66812c0 Merge pull request #1425 from trapexit/docs-tips
Misc doc updates
2025-03-08 22:03:14 -06:00
Antonio SJ Musumeci 6472ac51e9 Misc doc updates 2025-03-08 22:02:34 -06:00
trapexit d3770802b1 Merge pull request #1423 from trapexit/docs-inotify
Add FAQ entry regarding inotify
2025-03-05 16:59:45 -06:00
Antonio SJ Musumeci bcbff5c82e Add FAQ entry regarding inotify 2025-03-05 16:58:27 -06:00
trapexit 09743ee187 Merge pull request #1416 from trapexit/docs
Add FAQ section on common perceived problems
2025-02-22 22:30:28 -06:00
Antonio SJ Musumeci 3a33a6db2b Add FAQ section on common perceived problems 2025-02-22 22:25:39 -06:00
trapexit c0d5f4795a Merge pull request #1415 from trapexit/docs
Update docs on out-of-band usage
2025-02-19 14:13:32 -06:00
Antonio SJ Musumeci ef9074ab4a Update docs on out-of-band usage 2025-02-19 14:13:06 -06:00
trapexit 2d03b89c2d Merge pull request #1414 from theHenMan/patch-2
Update terminology.md
2025-02-19 00:09:55 -06:00
trapexit 64cc63b2bc Merge pull request #1413 from theHenMan/patch-1
Update quickstart.md
2025-02-18 23:04:59 -06:00
Hennie Botha 514c478a39 Update terminology.md
Fixed typo
2025-02-19 07:04:11 +02:00
Hennie Botha 8179f91c48 Update quickstart.md
Fixed a typo
2025-02-19 07:02:22 +02:00
trapexit 371764d800 Merge pull request #1412 from trapexit/docs
Rework questions on what settings and policies to use
2025-02-11 19:47:18 -06:00
Antonio SJ Musumeci 1c44ef7967 Rework questions on what settings and policies to use 2025-02-11 19:46:33 -06:00
trapexit aa97bac533 Merge pull request #1409 from trapexit/docs
Add details about logging to syslog
2025-01-25 20:58:57 -06:00
Antonio SJ Musumeci 12d6302457 Add details about logging to syslog 2025-01-25 20:56:28 -06:00
trapexit 740fc3f84a Merge pull request #1408 from trapexit/docs
Add details about user namespacing and root squashing
2025-01-25 13:54:43 -06:00
Antonio SJ Musumeci f18719ced2 Add details about user namespacing and root squashing 2025-01-25 13:54:06 -06:00
trapexit 366f5fcc4c Merge pull request #1407 from trapexit/docs
Misc doc updates
2025-01-25 13:02:36 -06:00
Antonio SJ Musumeci 1729835b8c Misc doc updates 2025-01-25 13:00:00 -06:00
trapexit 1180fd6dd3 Merge pull request #1402 from trapexit/docs
Doc and mover script updates
2025-01-20 01:45:51 -06:00
Antonio SJ Musumeci 19509e33d2 Doc and mover script updates 2025-01-20 01:43:58 -06:00
trapexit 8f355d7dd5 Merge pull request #1399 from oregonpillow/master
Readability changes to mkdocs installation
2025-01-18 15:21:20 -06:00
debian 2aa9697c87 re-order OS, shorten Debian based OS instructions 2025-01-18 01:14:50 +01:00
trapexit 978d067921 Merge pull request #1398 from trapexit/freebsd
Improve FreeBSD compatibility
2025-01-15 22:16:13 -06:00
Antonio SJ Musumeci b83bd5fd14 Improve FreeBSD compatibility
First draft. Need to investigate why upstream patches include certain
function that appear to exist in FreeBSD 14.2.
2025-01-15 22:10:02 -06:00
trapexit dd906c8a4c Merge pull request #1395 from oregonpillow/latest-release
fix edit uri
2025-01-11 18:07:15 -06:00
debian 6fb9111c03 fix edit uri 2025-01-12 00:56:39 +01:00
trapexit bc382e0fd2 Merge pull request #1392 from trapexit/usr1
Send invalidate node request outside lock
2025-01-10 00:35:59 -05:00
trapexit 0197984db3 Send invalidate node request outside lock 2025-01-09 23:22:07 -06:00
trapexit 248b1aca1c Merge pull request #1391 from trapexit/cpp
Move fuse.c and fuse_lowlevel.c to C++
2025-01-09 20:10:35 -05:00
Antonio SJ Musumeci 9056898bbf Move fuse.c and fuse_lowlevel.c to C++ 2025-01-09 19:05:32 -06:00
trapexit 5e584f246a Merge pull request #1390 from trapexit/tiered
Add tiered cache details to docs
2025-01-08 00:56:48 -05:00
Antonio SJ Musumeci 7e964283b9 Add tiered cache details to docs 2025-01-07 23:55:13 -06:00
trapexit 03dc17fff2 Merge pull request #1389 from trapexit/misc
Update funding and cirrus builds
2025-01-07 00:18:32 -05:00
Antonio SJ Musumeci 8cb68dce4a Update funding and cirrus builds 2025-01-06 23:07:33 -06:00
trapexit 28260c434a Merge pull request #1388 from trapexit/mkdocs
Update README to point to docs, update project comparisons
2025-01-05 23:40:54 -06:00
Antonio SJ Musumeci fae9b993fe Update README to point to docs, update project comparisons 2025-01-05 23:39:48 -06:00
trapexit 171eb9c7c0 Merge pull request #1387 from trapexit/workflows
Fix pip install in mkdocs workflow
2025-01-03 12:41:47 -06:00
Antonio SJ Musumeci 174e703d2e Fix pip install in mkdocs workflow 2025-01-03 12:40:50 -06:00
trapexit efd0dfaa2f Merge pull request #1386 from trapexit/docs
Rework mkdocs based documentation
2025-01-03 12:33:54 -06:00
Antonio SJ Musumeci 75d7a93724 Rework mkdocs based documentation
Lots of restructoring and extra information.
2025-01-03 12:17:26 -06:00
Timothy Pillow 50fa7ac694 add mkdocs
Initial commit of a mkdocs based docs setup
2024-12-21 09:36:07 -06:00
trapexit 7d64a47b88 Merge pull request #1377 from trapexit/movecopy-readme
Add FAQ entry on 'move' and 'copy'
2024-11-30 00:23:39 -06:00
Antonio SJ Musumeci 5fba42f076 Add FAQ entry on 'move' and 'copy' 2024-11-30 00:12:44 -06:00
trapexit b0bb7ef38c Merge pull request #1346 from grunthos503/master
Add missing --relative flag to rsync in percent-full mover script
2024-07-18 15:28:42 -05:00
Alan Millar 85ce7d6178 Add missing --relative flag to rsync in percent-full mover script 2024-07-18 12:14:02 -07:00
trapexit 40359df453 Merge pull request #1342 from techie2000/patch-2
Update README.md
2024-07-18 13:50:29 -05:00
techie2000 9b2a5a75ee Update README.md
Correcting a couple of typos.
Enhancing readability.
2024-06-28 10:15:02 +01:00
trapexit b21172c609 Merge pull request #1321 from trapexit/allowmmap
Add support for 'direct-io-allow-mmap' if supported by kernel
2024-03-21 21:54:04 -04:00
Antonio SJ Musumeci f0444a1ca9 Add support for 'direct-io-allow-mmap' if supported by kernel 2024-03-21 20:48:42 -05:00
trapexit 977d04229f Merge pull request #1320 from trapexit/readme
Suggest cache.files=auto-full rather than partial
2024-03-21 21:36:27 -04:00
Antonio SJ Musumeci 757ae66327 Suggest cache.files=auto-full rather than partial 2024-03-17 00:47:33 -05:00
trapexit 42d0b5734c Merge pull request #1312 from trapexit/preload-tmpfile
Special case O_TMPFILE in preload
2024-02-29 15:41:22 -05:00
Antonio SJ Musumeci 78050ed6b0 Special case O_TMPFILE in preload 2024-02-29 14:33:27 -06:00
trapexit 19ee44afe7 Merge pull request #1309 from trapexit/root-nodeid
Root nodeid
2024-02-25 18:29:58 -05:00
Antonio SJ Musumeci a234e9446c Update README to remove unnecessary references to export-support for NFS 2024-02-25 17:27:08 -06:00
Antonio SJ Musumeci 951eb9540d Ensure lookups of FUSE_ROOT_ID always return generation=0 2024-02-25 17:14:15 -06:00
trapexit 85a936c95e Merge pull request #1308 from trapexit/readme
Change mount type from fuse.mergerfs to mergerfs in README
2024-02-24 02:09:40 -05:00
Antonio SJ Musumeci 8210e63a66 Change mount type from fuse.mergerfs to mergerfs in README 2024-02-24 01:08:30 -06:00
trapexit facdb66746 Merge pull request #1307 from trapexit/readme
Add NFS options to remote filesystems section of readme
2024-02-19 19:20:41 -06:00
Antonio SJ Musumeci 1916e54a24 Add NFS options to remote filesystems section of readme 2024-02-19 19:19:00 -06:00
trapexit 7fabe515f4 Merge pull request #1306 from trapexit/export-support
Add export-support option as possible workaround for NFS EIO issues
2024-02-19 19:00:54 -06:00
Antonio SJ Musumeci 0ca5d6aad0 Add export-support option as possible workaround for NFS EIO issues 2024-02-19 18:58:50 -06:00
trapexit 0c1aeb411d Merge pull request #1305 from trapexit/lib-updates
Library updates
2024-02-18 17:56:39 -06:00
Antonio SJ Musumeci 17fdbf7f42 Update fuse_kernel.h 2024-02-18 17:50:34 -06:00
Antonio SJ Musumeci 27349e41ef Update fmt 2024-02-18 17:24:18 -06:00
Antonio SJ Musumeci b3ce95c422 Update nonstd::optional 2024-02-18 17:24:18 -06:00
Antonio SJ Musumeci cedc9f0f25 Update ghc::filesystem 2024-02-18 17:24:18 -06:00
trapexit d68ad9ac01 Expand the preload docs (#1299) 2024-01-30 00:03:39 -06:00
trapexit ae6c4f7c25 Rework mergerfs vs X section of readme (#1298) 2024-01-28 18:12:29 -06:00
trapexit 87475098a1 Add credit to nohajc for preload idea (#1295) 2024-01-22 12:52:51 -06:00
trapexit d4dc0701d6 Create ld_preload to reopen files on underlying filesystem (#1294) 2024-01-22 12:42:55 -06:00
0x6d61726b c1c2f075ee Update README.md (#1283)
* Update README.md

Information added that mergerfs v2.35.0 and newer sets `allow_other` (but not 2.33.5, that is used by Debian 12.2)
2023-12-01 15:45:13 -06:00
trapexit 96d34f1eab Merge pull request #1280 from trapexit/getpid
Fix user.mergerfs.pid
2023-11-11 19:48:28 -06:00
Antonio SJ Musumeci f66fb7e2d4 Fix user.mergerfs.pid 2023-11-11 19:03:32 -06:00
trapexit edb3368078 Merge pull request #1277 from trapexit/flushdefault
Set flush-on-close default to opened-for-write
2023-10-31 23:26:08 -05:00
Antonio SJ Musumeci bd70928268 Set flush-on-close default to opened-for-write 2023-10-31 09:23:52 -05:00
trapexit 337aade02d Merge pull request #1276 from trapexit/allowother
Only set allow_other when running as root
2023-10-29 02:54:51 -05:00
Antonio SJ Musumeci d6a2f06797 Only set allow_other when running as root 2023-10-29 01:44:18 -05:00
trapexit 7890a4996c Merge pull request #1275 from trapexit/noflush
Add flushonclose feature
2023-10-28 19:49:17 -05:00
Antonio SJ Musumeci 6aa6452d3e Add flushonclose feature 2023-10-28 19:47:23 -05:00
trapexit 7d6c8e9333 Merge pull request #1271 from trapexit/warn-root
Warn if not running as root
2023-10-22 00:45:41 -05:00
Antonio SJ Musumeci a7069153bd Warn if not running as root 2023-10-22 00:43:19 -05:00
trapexit 5c71a75fdd Merge pull request #1269 from trapexit/wyhash
Update wyhash to 4.2
2023-10-21 18:04:14 -05:00
Antonio SJ Musumeci fd33df04a3 Update wyhash to 4.2 2023-10-21 18:03:05 -05:00
trapexit a9ae36a3e4 Merge pull request #1263 from trapexit/pthread_getname_np
Remove use of pthread_getname_np to work with older musl versions
2023-10-14 01:20:00 -05:00
Antonio SJ Musumeci 569537df9e Remove use of pthread_getname_np to work with older musl versions 2023-10-14 01:15:29 -05:00
trapexit 8534ee7ceb Merge pull request #1262 from trapexit/readdir-errs
Rework thread pool queue depth impl
2023-10-14 01:13:48 -05:00
Antonio SJ Musumeci 620cab2948 Rework thread pool queue depth impl 2023-10-14 00:58:10 -05:00
trapexit 8635456818 Merge pull request #1254 from trapexit/typo
Fix readdir doc typos
2023-09-23 14:54:25 -05:00
Antonio SJ Musumeci 0888f372aa Fix readdir doc typos 2023-09-23 14:53:41 -05:00
trapexit e09b10c55b Merge pull request #1249 from trapexit/tpfix
Fix thread pool destruction where threads don't explicitly exit themselves
2023-09-09 17:07:51 -05:00
Antonio SJ Musumeci 766b923116 Fix thread pool destruction where threads don't explicitly exit themselves 2023-09-09 16:48:47 -05:00
trapexit dd7e9e2aa7 Merge pull request #1244 from trapexit/docs
Add some new questions to FAQ
2023-09-05 22:56:50 -05:00
Antonio SJ Musumeci 3fc0aa3809 Add some new questions to FAQ 2023-09-05 22:56:08 -05:00
trapexit b1dcc94e98 Merge pull request #1242 from trapexit/debug-removal
Remove unneeded debug logging from thread pool
2023-09-04 18:40:38 -05:00
Antonio SJ Musumeci 6d2c99e487 Remove unneeded debug logging from thread pool 2023-09-04 18:39:43 -05:00
trapexit 9d9eacfe7b Merge pull request #1241 from trapexit/version
Add website and license to version option
2023-09-04 16:02:24 -05:00
Antonio SJ Musumeci b91cc1c191 Add website and license to version option 2023-09-04 15:57:47 -05:00
trapexit 6d2f26c60e Merge pull request #1240 from trapexit/dynamic-threadpool
Rework thread pool, add ability to add/remove threads at runtime
2023-09-03 23:37:29 -05:00
Antonio SJ Musumeci 0a94bd7cfc Rework thread pool, add ability to add/remove threads at runtime 2023-09-03 23:36:03 -05:00
trapexit 211e6c7115 Merge pull request #1239 from trapexit/validation
Add proper input validation for readdir policy
2023-09-02 15:48:43 -05:00
Antonio SJ Musumeci 335ca55a6e Add proper input validation for readdir policy 2023-09-02 15:47:49 -05:00
trapexit 69de4de53f Merge pull request #1238 from trapexit/boundedqueue
Change threadpool to use concurrentqueue
2023-09-02 00:47:26 -05:00
Antonio SJ Musumeci 76c8d48dbd Change threadpool to use concurrentqueue 2023-09-02 00:45:47 -05:00
trapexit a927a15e9c Merge pull request #1237 from trapexit/threadfix
Initialize readdir threadpool after daemonizing
2023-08-28 22:48:59 -05:00
Antonio SJ Musumeci 0c555e71a0 Initialize readdir threadpool after daemonizing 2023-08-28 22:47:04 -05:00
trapexit 82781b6ff8 Merge pull request #1235 from DavidFair/readme_direct_parallel
Docs: clarify parallel-direct-writes and cache.files
2023-08-26 18:52:57 -05:00
David Fairbrother b545352712 Nit: cache.files=per-process grammar
Change which 'comm' name -> whose 'comm' name.
Whose implies possession, and the process possess the name we're
matching
2023-08-26 21:27:16 +01:00
David Fairbrother 3d05999db0 State cache.files per-process behaviour w/out match
This makes it clear, along with the previous commit, that per-process will allow
us to use parallel direct writes as they will mount with the equivalent
of cache.files=off (i.e. direct_io)
2023-08-26 21:24:34 +01:00
David Fairbrother f4a8105a42 Clarify parallel direct writes conditions
Clarify that paralell direct can only be enabled where the kernel
version supports it, which is 6.2+.

State the two cache.files modes which can support parallel direct writes
in the description.
2023-08-26 21:22:37 +01:00
trapexit 004b616115 Merge pull request #1234 from trapexit/fixes
Misc fixes for older platforms
2023-08-22 08:13:56 -05:00
Antonio SJ Musumeci 8d7017273f Misc fixes for older platforms 2023-08-22 08:10:55 -05:00
trapexit 51b65cd126 Merge pull request #1233 from trapexit/atomic
Use relaxed memory order for atomic counters
2023-08-21 17:19:09 -05:00
Antonio SJ Musumeci 0ed03a1535 Use relaxed memory order for atomic counters 2023-08-21 15:43:03 -05:00
trapexit b6d3c34d3f Merge pull request #1232 from trapexit/wyhash
Update wyhash to final4
2023-08-21 00:20:04 -05:00
Antonio SJ Musumeci fdecabfaab Update wyhash to final4 2023-08-21 00:18:41 -05:00
trapexit 3a1d8e03a1 Merge pull request #1231 from trapexit/readdir
Fix setting of ugids for concurrent readdir
2023-08-20 23:12:56 -05:00
Antonio SJ Musumeci e586d2fd1e Fix setting of ugids for concurrent readdir 2023-08-20 23:06:05 -05:00
trapexit e0087cd8a7 Merge pull request #1228 from trapexit/threading
Add thread names for easier debugging
2023-08-14 17:52:31 -05:00
Antonio SJ Musumeci 2377cb05b6 Add thread names for easier debugging 2023-08-14 17:51:41 -05:00
trapexit a8ffbc84f6 Merge pull request #1227 from trapexit/gidcache
Add ability to invalidate gid cache on demand
2023-08-13 23:32:33 -05:00
Antonio SJ Musumeci 0aafdefc18 Add ability to invalidate gid cache on demand
SIGUSR2 or ioctl
2023-08-13 23:30:52 -05:00
trapexit 7221fded71 Merge pull request #1226 from feltcat/feltcat-patch-1
Fix a typo in README.md
2023-08-13 23:13:30 -05:00
feltcat 7a09f560f7 Fix a typo in README.md 2023-08-14 03:45:01 +00:00
trapexit 8b769dff7f Merge pull request #1225 from trapexit/new-funcs
Add callbacks for newer functions
2023-08-13 17:46:09 -05:00
Antonio SJ Musumeci 6dcf6111af Add callbacks for newer functions
* setupmapping
* removemapping
* syncfs
* tmpfile
2023-08-13 17:38:13 -05:00
trapexit 9849bcd3cf Merge pull request #1224 from trapexit/readdir
Add readdir policies
2023-08-11 00:05:28 -05:00
Antonio SJ Musumeci c92a10021e Add readdir policies 2023-08-11 00:05:14 -05:00
trapexit d817fa4e97 Merge pull request #1220 from trapexit/fix-logging
Fix logging pin-threads details
2023-07-30 23:14:16 -05:00
Antonio SJ Musumeci eb141c4c4e Fix logging pin-threads details 2023-07-30 23:13:14 -05:00
trapexit 282ce08ddb Merge pull request #1219 from trapexit/fuse-config
Move fuse thread args out of fuse session object
2023-07-30 22:56:27 -05:00
Antonio SJ Musumeci 51d97bb444 Move fuse thread args out of fuse session object 2023-07-30 22:52:43 -05:00
trapexit 539f2221a3 Merge pull request #1213 from trapexit/lib-updates
Lib updates
2023-07-15 18:51:43 -04:00
Antonio SJ Musumeci 2b1804d364 Add a DEPENDENCIES file to list dependencies
Mostly to remind myself which libs to update on occasion
2023-07-15 17:49:57 -05:00
Antonio SJ Musumeci 556c6522c0 Update fmt to v10.0.0 2023-07-15 17:47:36 -05:00
Antonio SJ Musumeci 543603bdfc Update ghc::filesystem to v1.5.14 2023-07-15 14:47:44 -05:00
trapexit 7a86ed6508 Merge pull request #1212 from trapexit/erofs
Create functions can set branches RO on EROFS
2023-07-13 22:34:29 -04:00
Antonio SJ Musumeci 707d298d7c Create functions can set branches RO on EROFS 2023-07-13 21:19:13 -05:00
trapexit 36a4b7a23a Merge pull request #1209 from trapexit/o_direct
Align msg buffer memory to allow O_DIRECT to work
2023-07-09 19:39:52 -04:00
Antonio SJ Musumeci 22833bdfe2 Align msg buffer memory to allow O_DIRECT to work 2023-07-09 15:32:11 -05:00
trapexit 775c3cad90 Merge pull request #1208 from trapexit/syslog
Simplify syslog wrapper a bit
2023-07-06 00:15:57 -05:00
Antonio SJ Musumeci fa537961c5 Simplify syslog wrapper a bit 2023-07-06 00:13:42 -05:00
trapexit ee13810f3b Merge pull request #1207 from trapexit/atomic
Fix build on older distros due to std::atomic definitions
2023-07-02 23:57:45 -05:00
Antonio SJ Musumeci 5d6c9cc56a Fix build on older distros due to std::atomic definitions
std::atomic_uint64_t missing in some older distros
2023-07-02 23:46:46 -05:00
trapexit 832903378e Merge pull request #1206 from trapexit/write-errs
Fix error handling for cached writes
2023-06-29 22:51:34 -05:00
Antonio SJ Musumeci 2722f4f64d Fix error handling for cached writes 2023-06-29 22:50:24 -05:00
trapexit 7cdd12bda4 Merge pull request #1204 from trapexit/gc
Add manual GC triggering + configurable process queue depth
2023-06-26 23:26:47 -05:00
Antonio SJ Musumeci 5ab0fbcaee Add manual GC triggering + configurable process queue depth
Yes, these are unrelated changes but somehow ended up being
prototyped together and I'm too lazy to separate them.
2023-06-26 22:44:23 -05:00
trapexit 42836e0961 Merge pull request #1203 from trapexit/append-move
Fix move when in append mode + fix read/write direct_io vs cached behavior
2023-06-24 23:13:57 -05:00
Antonio SJ Musumeci 6a14a10e6c Fix read/write behavior and return value depending on direct_io
Also add parallel direct write option for 6.2+ kernels.
2023-06-24 23:06:14 -05:00
Antonio SJ Musumeci 82430e1079 Fix moveonenospc when file opened with append mode 2023-06-12 23:40:08 -05:00
trapexit 701f88de43 Merge pull request #1171 from trapexit/srcmounts
Make srcmounts readonly
2023-04-05 22:31:29 -04:00
Antonio SJ Musumeci c72997f298 Make srcmounts readonly
All tools only read from srcmounts and the current setup had a bug
which caused branch mode and minfreespace to be stripped when using
config file.
2023-04-05 22:24:24 -04:00
trapexit c681d6c2fe Merge pull request #1170 from trapexit/fuse_destroy
Fix crash when exiting with open deleted files
2023-04-05 00:35:04 -04:00
Antonio SJ Musumeci 6bc3d77992 Fix crash when exiting with open deleted files 2023-04-05 00:32:30 -04:00
trapexit c266bae96d Merge pull request #1166 from trapexit/docs
Remove duplicate entries in docs
2023-04-01 14:41:33 -04:00
Antonio SJ Musumeci cdcfef2ceb Remove duplicate entries in docs 2023-04-01 14:40:49 -04:00
trapexit 7a62a629b9 Merge pull request #1165 from trapexit/thread-defaults
Change default read-thread-count from -1 to 0 as documented
2023-03-31 13:38:08 -04:00
Antonio SJ Musumeci 0a64d74b4b Change default read-thread-count from -1 to 0 as documented 2023-03-31 12:54:57 -04:00
trapexit 26c8b5c642 Merge pull request #1161 from trapexit/fix
Workaround older gcc bug with namespacing std::hash
2023-03-28 21:57:51 -04:00
Antonio SJ Musumeci 26fd11cb4d Workaround older gcc bug with namespacing std::hash 2023-03-28 21:53:38 -04:00
trapexit cdceade1ae Merge pull request #1159 from trapexit/wait
More actively report to syslog when waiting for branches to mount
2023-03-28 19:03:50 -04:00
Antonio SJ Musumeci eebd6e9eb4 Actively report to syslog when waiting for mounts 2023-03-28 19:01:21 -04:00
trapexit 9744eab819 Merge pull request #1156 from trapexit/options
Fix loading readonly / write once options from file
2023-03-19 23:41:15 -04:00
Antonio SJ Musumeci 1c5b0f2f5f Fix loading readonly / write once options from file 2023-03-19 23:40:32 -04:00
trapexit d9033b7713 Merge pull request #1155 from trapexit/mover-scripts
Fix mover script link typo
2023-03-19 22:08:31 -04:00
Antonio SJ Musumeci b1d11779f0 Fix mover script link typo 2023-03-19 22:07:03 -04:00
trapexit be19b9ce71 Merge pull request #1154 from trapexit/mover-scripts
Move tiered cache mover scripts to files in tools dir
2023-03-19 22:03:31 -04:00
Antonio SJ Musumeci 9fcaf6a674 Move tiered cache mover scripts to files in tools dir 2023-03-19 01:23:19 -04:00
trapexit 2dd4b788ce Merge pull request #1153 from trapexit/readme
Misc README updates
2023-03-18 21:48:16 -04:00
Antonio SJ Musumeci 5152c63480 Misc README updates 2023-03-18 21:47:37 -04:00
trapexit bd02bfd54c Merge pull request #1151 from kephir4eg/master
Fix config file parsing
2023-03-11 00:45:24 -05:00
Ilya Taranov d04e04e89a Fix config file parsing 2023-03-10 20:55:20 -08:00
trapexit 10a9969a64 Merge pull request #1149 from trapexit/readahead
Add ability to set readahead of mergerfs and branches
2023-03-09 00:13:49 -05:00
Antonio SJ Musumeci 486c5d3734 Add ability to set readahead of mergerfs and branches 2023-03-09 00:12:57 -05:00
trapexit 55bd0ca25a Merge pull request #1147 from trapexit/readme
Update README regarding why epmfs is default
2023-03-06 23:26:00 -05:00
Antonio SJ Musumeci ee2855bb38 Update README regarding why epmfs is default 2023-03-06 23:25:42 -05:00
trapexit 65c004a309 Merge pull request #1146 from jonaswinkler/master
Fix typo in basic setup options
2023-03-06 09:26:21 -05:00
Jonas Winkler a598f63235 Update README.md 2023-03-06 15:17:00 +01:00
trapexit 6e845b337a Merge pull request #1145 from trapexit/readme
Lots of misc README updates
2023-03-05 01:43:08 -05:00
Antonio SJ Musumeci e25fa1954e Lots of misc README updates 2023-03-05 01:42:42 -05:00
trapexit b48d368130 Merge pull request #1143 from trapexit/rework-read
Rework fuse read to use same buffers as messages
2023-02-26 23:37:14 -05:00
Antonio SJ Musumeci 373d331f39 Rework fuse read to use same buffers as messages 2023-02-26 23:31:44 -05:00
trapexit 4a647beec1 Merge pull request #1142 from trapexit/remove-splice
Remove splicing features
2023-02-26 01:45:29 -05:00
Antonio SJ Musumeci 84592a9f13 Remove splicing features
After numerous tests it was found the splice features were at best
the same performance as standard IO and at worse actually slower.
To simplify the code all splice features are removed.
2023-02-26 01:42:58 -05:00
trapexit 09fbb80f8c Merge pull request #1140 from trapexit/pinthreads
Add ability to pin read and processing threads
2023-02-20 22:45:54 -05:00
Antonio SJ Musumeci 34310170ac Add ability to pin read and processing threads 2023-02-19 17:52:29 -05:00
trapexit e39ace0eba Merge pull request #1139 from trapexit/per-process-page-cache
Add "per-process" file caching feature
2023-02-18 01:34:37 -05:00
Antonio SJ Musumeci 9effeb6869 Add "per-process" file caching feature
Allows chosing page caching based on process name.
2023-02-18 01:16:36 -05:00
trapexit b51a5970b2 Merge pull request #1137 from trapexit/btrfs-ioctl
Return NOTTY for all btrfs ioctl calls
2023-02-11 10:25:09 -05:00
Antonio SJ Musumeci 2d9c915641 Return NOTTY for all btrfs ioctl calls
mergerfs tries to pass through ioctl calls but due to the complexity
of btrfs calls their is risk of segfaults. In the future it may be
required to whitelist all supported ioctl calls.
2023-02-11 10:07:55 -05:00
trapexit 737e59d232 Merge pull request #1133 from trapexit/lazy-umount
Add option to lazy umount target mount point
2023-02-02 22:12:17 -05:00
Antonio SJ Musumeci 9adddfc66d Add option to lazy umount target mount point 2023-02-02 21:20:23 -05:00
trapexit 9a6b7835f4 Merge pull request #1131 from trapexit/docs
Add more apps to section on sqlite3 mmap problems
2023-01-29 23:57:46 -05:00
Antonio SJ Musumeci ac4e097511 Add more apps to section on sqlite3 mmap problems 2023-01-29 23:56:50 -05:00
trapexit 3d73c6a596 Merge pull request #1130 from trapexit/docs
Update README regarding requesting support and policy descriptions
2023-01-29 23:24:33 -05:00
Antonio SJ Musumeci 9670fb9704 Update README regarding requesting support and policy descriptions 2023-01-29 23:24:09 -05:00
trapexit e2ece936e6 Merge pull request #1129 from trapexit/fuse-version
Remove FUSE_USE_VERSION, no longer used
2023-01-29 18:27:26 -05:00
Antonio SJ Musumeci a4491169b0 Remove FUSE_USE_VERSION, no longer used 2023-01-29 17:04:08 -05:00
trapexit 0d566befba Merge pull request #1128 from trapexit/checkbranches
Forbid adding mountpoint to branch list
2023-01-29 15:50:25 -05:00
Antonio SJ Musumeci 3e313cd605 Forbid adding mountpoint to branch list 2023-01-29 11:51:56 -05:00
trapexit 5bf2a2be59 Merge pull request #1127 from trapexit/bugfixes
Backport bug fixes from libfuse3
2023-01-28 01:17:50 -05:00
Antonio SJ Musumeci 7ed91c1668 Backport bug fixes from libfuse3 2023-01-28 01:17:07 -05:00
trapexit 041e3f50d5 Merge pull request #1126 from trapexit/removeallowother
Hard code setting of allow_other, ignore when set
2023-01-26 23:40:12 -05:00
Antonio SJ Musumeci 92ccfd3c89 Hard code setting of allow_other, ignore when set 2023-01-26 23:37:37 -05:00
trapexit 24f2d3c94d Merge pull request #1125 from trapexit/scheduling-priority
Allow setting of scheduling priority
2023-01-25 23:42:20 -05:00
Antonio SJ Musumeci a7113bb163 Allow setting of scheduling priority 2023-01-25 23:38:31 -05:00
trapexit ec5ac0c517 Merge pull request #1124 from trapexit/builds
Update CirrusCI setup
2023-01-25 20:24:52 -05:00
Antonio SJ Musumeci fdec3355a1 Update CirrusCI setup 2023-01-25 20:12:53 -05:00
trapexit 16e9ba2f19 Merge pull request #1123 from trapexit/wait
Add option to wait for branches to become new mounts
2023-01-25 19:51:58 -05:00
Antonio SJ Musumeci 939eb3996a Add option to wait for branches to become new mounts
branches-mount-timeout=UINT64 in seconds (default: 0)
2023-01-25 11:59:14 -05:00
trapexit 2d8d0f7ad6 Merge pull request #1120 from trapexit/nonempty
Remove 'nonempty' argument
2023-01-16 23:41:51 -05:00
Antonio SJ Musumeci 3ee93d4c56 Remove 'nonempty' argument
This makes it like all other filesystems and brings it into alignment
with libfuse3's behavior.
2023-01-16 23:33:36 -05:00
trapexit 0b2afda30e Merge pull request #1119 from trapexit/useino
Remove use_ino option and make behavior same as if set
2023-01-16 16:53:30 -05:00
Antonio SJ Musumeci d11807ef7c Remove use_ino option and make behavior same as if set 2023-01-16 16:21:44 -05:00
trapexit 3e725a66e3 Merge pull request #1118 from trapexit/msghandling
Add async message processing
2023-01-16 13:41:00 -05:00
Antonio SJ Musumeci 24423b8d2a Add async message processing 2023-01-16 13:15:53 -05:00
trapexit c89fbde48d Merge pull request #1116 from davidkaufman/patch-1
small grammar correction to README file
2023-01-13 13:47:07 -05:00
David Kaufman b65e52d56b small grammar correction to README file
sorry, i'm a grammar nerd with ocd :-)
2023-01-03 00:58:08 -05:00
trapexit 6c2db76b92 Merge pull request #1109 from trapexit/fixes
Fix regression testing for implemented functions
2022-12-11 14:44:54 -05:00
Antonio SJ Musumeci 094ad862e7 Fix regression testing for implemented functions 2022-12-11 14:44:19 -05:00
trapexit e81b1a6b9d Merge pull request #1107 from trapexit/32bitfixes
Fix printf for 32bit systems
2022-12-08 22:06:15 -05:00
Antonio SJ Musumeci a6ca96fa08 Fix printf for 32bit systems 2022-12-08 22:04:15 -05:00
trapexit a449829d5f Merge pull request #1106 from trapexit/xattrfix
Fix reading of setxattr name
2022-12-08 18:40:57 -05:00
Antonio SJ Musumeci d14427ad9e Fix reading of setxattr name 2022-12-08 18:40:08 -05:00
trapexit ac1f4b4d52 Merge pull request #1105 from trapexit/ll
Misc cleanup
2022-11-27 23:29:53 -05:00
Antonio SJ Musumeci ddf5e53aa4 Misc cleanup 2022-11-27 22:23:38 -06:00
trapexit a2a12afb34 Merge pull request #1104 from trapexit/ll
Remove libfuse abstraction in prep for adding request data
2022-11-27 00:39:05 -05:00
Antonio SJ Musumeci 7606430188 Remove libfuse abstraction in prep for adding request data 2022-11-26 23:30:27 -06:00
trapexit 629806e896 Merge pull request #1093 from lgtm-migrator/codeql
Add CodeQL workflow for GitHub code scanning
2022-11-11 09:53:27 -05:00
LGTM Migrator 50fbf14e1e Add CodeQL workflow for GitHub code scanning 2022-11-11 07:59:54 +00:00
trapexit 4a67bbdc71 Merge pull request #1092 from trapexit/cleanup
Remove write_buf, simplify FUSE msg dispatching
2022-11-10 23:24:25 -05:00
Antonio SJ Musumeci dba9c4451a Remove write_buf, simplify FUSE msg dispatching 2022-11-10 22:43:02 -05:00
trapexit fea499e220 Merge pull request #1091 from trapexit/cleanup
Remove unnecessary libfuse abstractions
2022-11-07 23:35:18 -05:00
Antonio SJ Musumeci 9d056d609f Remove unnecessary libfuse abstractions 2022-11-07 23:09:25 -05:00
trapexit 9a839dd8ba Merge pull request #1089 from trapexit/32bit
Tweaks for 32bit systems
2022-11-05 23:37:19 -04:00
Antonio SJ Musumeci 32e2c9c48e Tweaks for 32bit systems 2022-11-05 23:28:10 -04:00
trapexit 83d7886a0a Merge pull request #1087 from trapexit/kernelh
Update fuse_kernel.h
2022-11-03 23:29:22 -04:00
Antonio SJ Musumeci 316abba0ac Update fuse_kernel.h 2022-11-03 23:28:44 -04:00
trapexit 8001c58c36 Merge pull request #1085 from trapexit/symlinkify
Fix setting of stat vars for symlinkify
2022-10-31 19:47:36 -04:00
Antonio SJ Musumeci 3b0d7fb26e Fix setting of stat vars for symlinkify 2022-10-30 22:27:33 -04:00
trapexit 5d3c5002e0 Merge pull request #1058 from trapexit/docs
Support doc update
2022-08-08 14:09:22 -04:00
Antonio SJ Musumeci 9082285cc8 Support doc update 2022-08-08 13:40:43 -04:00
trapexit d1762b2bac Merge pull request #1057 from trapexit/cfgerrs
Error when given invalid policy names
2022-08-06 23:08:10 -04:00
Antonio SJ Musumeci 46ce28b149 Error when given invalid policy names 2022-08-06 23:01:23 -04:00
trapexit faadeb75e0 Merge pull request #1045 from Gelma/master
Fix typos
2022-06-22 14:27:13 -04:00
Andrea Gelmini 338e559e53 Fix typos 2022-06-22 11:51:23 +02:00
trapexit 3db673410f Merge pull request #1039 from trapexit/docs
Add details about usage of FUSE to docs
2022-05-23 12:49:06 -04:00
Antonio SJ Musumeci 77f098614f Add details about usage of FUSE to docs 2022-05-23 12:40:00 -04:00
trapexit 9589846e9a Merge pull request #1032 from trapexit/support
Rework support section in readme
2022-04-25 17:44:16 -04:00
Antonio SJ Musumeci e2dda3c1b1 Rework support section in readme 2022-04-25 17:42:31 -04:00
trapexit d9ab18282f Merge pull request #1031 from trapexit/root-parent
Return ENOENT when dotdot for root node requested
2022-04-12 23:18:57 -04:00
Antonio SJ Musumeci 14c2ff9ab8 Return ENOENT when dotdot for root node requested 2022-04-12 23:17:39 -04:00
trapexit 77e6f400b5 Merge pull request #1026 from trapexit/loglock
Lock less often during logging
2022-03-20 22:45:07 -04:00
Antonio SJ Musumeci b95ff8ba3a Lock less often during logging 2022-03-20 22:34:55 -04:00
trapexit 4bf8738e5c Merge pull request #1025 from trapexit/estale
Change ENOENT to ESTALE for looking up paths to handle rename race conditions
2022-03-20 12:19:00 -04:00
Antonio SJ Musumeci 3f060f4512 Change ENOENT to ESTALE for looking up paths to handle rename race conditions
As done in https://github.com/libfuse/libfuse/pull/636
2022-03-20 12:09:22 -04:00
trapexit c4c35a9ad0 Merge pull request #1023 from trapexit/follow-symlink-fix
Fix query of attr during symlink
2022-03-19 17:01:11 -04:00
Antonio SJ Musumeci c9a935835f Fix query of attr during symlink
Using getattr when follow-symlink is enabled causes invalid type to the kernel
if symlink pointed to non-symlink.
2022-03-19 16:18:46 -04:00
trapexit 015002ebd9 Merge pull request #1020 from trapexit/slab-maintenance
Rework node slab garbage collection to limit blocking work threads
2022-03-19 15:54:29 -04:00
Antonio SJ Musumeci 9ca10b2413 Rework node slab garbage collection to limit blocking work threads
Also remove debug mode from forcing foreground mode
2022-03-19 13:33:14 -04:00
trapexit 2d5c2c2b6e Merge pull request #1007 from trapexit/trapexit-patch-1
Update bug_report.md
2022-01-18 21:16:22 -05:00
trapexit e3ab739a56 Update bug_report.md 2022-01-18 21:15:59 -05:00
trapexit c784d06029 Merge pull request #1005 from trapexit/readme-readahead
Update README.md regarding readahead
2022-01-15 17:39:31 -05:00
trapexit 58803f4845 Update README.md 2022-01-15 17:33:07 -05:00
trapexit f159fe61f9 Merge pull request #997 from trapexit/link-getattr
Call FUSE getattr rather than syscall for extra logic
2021-12-18 17:21:19 -05:00
Antonio SJ Musumeci c43b464d6c Call FUSE getattr rather than syscall for extra logic
While more efficient calling regular getattr (stat) leads to incorrect
information being returned and would not work correctly when using
symlinkify.
2021-12-18 17:07:23 -05:00
trapexit 34c684a4f5 Merge pull request #991 from trapexit/fix/symlink
Get attrs for link, not target
2021-12-16 19:35:18 -05:00
Antonio SJ Musumeci e256c88e4c Get attrs for link, not target 2021-12-16 19:11:29 -05:00
trapexit 518b4a6b85 Merge pull request #984 from trapexit/disable-locks
Ensure server handling of locks is disabled
2021-11-27 00:02:55 -05:00
Antonio SJ Musumeci ee8532c70d Ensure server handling of locks is disabled 2021-11-27 00:01:12 -05:00
trapexit c58da1ab9c Merge pull request #979 from trapexit/node-size
Remove embedded name in node struct
2021-11-01 22:46:09 -04:00
Antonio SJ Musumeci 68b3026264 Remove embedded name in node struct
The 32 byte array is below the average size so those 32 bytes are wasted a
large portion of the time.
2021-11-01 21:08:58 -04:00
trapexit 810c5a40f7 Merge pull request #978 from trapexit/debug2
Add new debug printing routines, fix ioctl cmd type
2021-10-27 22:31:58 -04:00
Antonio SJ Musumeci 18dead4d86 Add new debug printing routines 2021-10-27 22:12:10 -04:00
trapexit 2082357d3a Merge pull request #977 from trapexit/readme
Readme
2021-10-25 21:58:01 -04:00
Antonio SJ Musumeci 68719da828 Remove ioctl runtime section, use getfattr instead of xattr 2021-10-25 21:57:30 -04:00
trapexit 31fd1ec82e Merge pull request #975 from trapexit/sponsorship
Remove subscribestar from sponsor platforms
2021-10-15 23:30:33 -04:00
trapexit c3845097ee Remove subscribestar from sponsor platforms 2021-10-15 23:30:22 -04:00
trapexit d76934ee9d Merge pull request #972 from trapexit/log-node-info
Add option to log node memory usage metrics
2021-10-11 12:33:11 -04:00
Antonio SJ Musumeci 5f737cb7bf Add option to log node memory usage metrics 2021-10-11 11:57:16 -04:00
trapexit 50ede1ba97 Merge pull request #969 from trapexit/node-size
Reduce struct node size
2021-10-03 22:44:03 -04:00
Antonio SJ Musumeci 930dad31de Reduce struct node size
* Replace the stat values with a crc32b of them instead (for auto-cache)
* Replace char flag with bitfield
* Remove node generation. nodeid is an unsigned 64bit int. Would take 500K+ years
  to rollover at 1M nodes per second.
2021-10-03 22:15:34 -04:00
trapexit 88b91161e0 Merge pull request #968 from trapexit/fix
Fix regression from remember_node refactor
2021-09-30 22:14:51 -04:00
Antonio SJ Musumeci 8150957a01 Fix regression from remember_node refactor 2021-09-30 22:13:29 -04:00
trapexit fee97efafb Merge pull request #967 from trapexit/malloc-trim
Add malloc_trim configure test
2021-09-30 20:25:42 -04:00
trapexit 80856cf7ac Merge pull request #966 from trapexit/libupdates
Update ghc::filesystem and nonstd::optional
2021-09-30 20:05:40 -04:00
Antonio SJ Musumeci c929781d08 Add malloc_trim configure test 2021-09-30 20:00:40 -04:00
Antonio SJ Musumeci 313aa2a1d3 Update ghc::filesystem and nonstd::optional 2021-09-30 19:43:05 -04:00
trapexit 021ce3d97c Merge pull request #964 from trapexit/fmp
Rework of allocation in libfuse
2021-09-19 17:31:39 -04:00
Antonio SJ Musumeci 4ea0de3ef2 Rework dirents buffer management 2021-09-19 16:48:03 -04:00
Antonio SJ Musumeci 6b5c484fbf Major rework of memory allocation using fixed mem pools 2021-09-19 16:48:03 -04:00
trapexit b165332486 Merge pull request #959 from trapexit/readme
Add details on dropping caches before benchmarks
2021-09-03 07:22:21 -04:00
Antonio SJ Musumeci f598d3bc38 Add details on dropping caches before benchmarks 2021-09-03 07:21:16 -04:00
trapexit 4daa9e4eb9 Merge pull request #957 from trapexit/cirrus-ci
Add new cirrus-ci targets
2021-08-29 14:25:17 -04:00
Antonio SJ Musumeci 122f9c1d5d Add new cirrus-ci targets 2021-08-29 13:56:56 -04:00
trapexit 2da5cda4b3 Merge pull request #956 from trapexit/mount-bsd
Remove usage printing regression in mount_bsd
2021-08-29 13:20:07 -04:00
Antonio SJ Musumeci 5263a65dcf Remove usage printing regression in mount_bsd 2021-08-29 13:09:38 -04:00
trapexit 1255d3d1b2 Merge pull request #955 from trapexit/mfs
Fix infinite loop in mfs action policy
2021-08-29 13:07:40 -04:00
Antonio SJ Musumeci 8def16a811 Fix infinite loop in mfs action policy 2021-08-29 12:33:52 -04:00
trapexit 7d1683b607 Merge pull request #954 from trapexit/libfuse-cleanup
Major cleanup of libfuse to remove unneeded features
2021-08-29 12:15:05 -04:00
Antonio SJ Musumeci 43a6d66e3c Major cleanup of libfuse to remove unneeded features
* Remove request interrupt code. Required tracking of all requests unnecesssarily.
* Remove all debugging printing. Have plans to do full replacement.
* Remove deprecated functions.
* Remove unneeded error checking.
* Remove "userdata" which was unused.
* Remove allow_root feature.
2021-08-29 00:05:30 -04:00
trapexit 8b33f9a766 Merge pull request #953 from trapexit/readme-chia
Update docs with Chia wallet address
2021-08-26 12:57:16 -04:00
Antonio SJ Musumeci df721eb253 Update docs with Chia wallet address 2021-08-26 12:56:33 -04:00
trapexit 0547517a99 Merge pull request #951 from trapexit/readme
Add more clarity in what to provide when submitting bug reports
2021-08-22 13:12:03 -04:00
Antonio SJ Musumeci 54e85007a2 Add more clarity in what to provide when submitting bug reports or questions 2021-08-22 13:10:54 -04:00
trapexit 6e53f3b698 Merge pull request #945 from trapexit/readme
Update 'features' section of readme
2021-08-10 21:49:32 -04:00
Antonio SJ Musumeci 1aaf742c13 Update 'features' section of readme 2021-08-10 21:48:51 -04:00
trapexit 3754e20049 Merge pull request #944 from trapexit/trapexit-patch-1
tweak bug report template language
2021-08-09 21:10:16 -04:00
trapexit b4c031d1f0 tweak bug report template language 2021-08-09 21:09:54 -04:00
trapexit 3b8d495c43 Merge pull request #940 from trapexit/ioctl-runtime-removal
Remove config ioctl calls
2021-08-03 17:02:01 -04:00
Antonio SJ Musumeci 61c2187c14 Remove config ioctl calls
While they were going to be the new way of interacting with the runtime config
I've decided to use toml based config for mergerfs v3. In that case all updates
will happen through the config file and ioctl will only be used to load/reload
the config rather than a key:value like API.
2021-08-02 16:56:20 -04:00
trapexit 192e4a4af8 Merge pull request #939 from trapexit/update-nonstd-optional
Update nonstd::optional to v3.4.0
2021-08-02 16:29:40 -04:00
trapexit 749f0290e9 Merge pull request #938 from trapexit/update-ghc-filesystem
Update ghc::filesystem to v1.5.8
2021-08-02 16:24:22 -04:00
Antonio SJ Musumeci 7013ff9aeb Update nonstd::optional to v3.4.0 2021-08-02 16:10:56 -04:00
Antonio SJ Musumeci 3ce0dc56e4 Update ghc::filesystem to v1.5.8 2021-08-02 16:06:43 -04:00
trapexit 8e47bf4946 Merge pull request #926 from trapexit/modzero
pfrd: fix mod by zero error when all branches are filtered
2021-06-25 09:45:01 -04:00
Antonio SJ Musumeci 098f353bf1 pfrd: fix mod by zero error when all branches are filtered 2021-06-25 09:17:16 -04:00
trapexit 859b4da3dc Merge pull request #924 from trapexit/varinit-master
properly initialize fuse_buf in worker loop
2021-06-20 17:40:12 -04:00
Antonio SJ Musumeci 80f30999fa properly initialize fuse_buf in worker loop 2021-06-20 17:38:17 -04:00
trapexit 3fdd322a5c Merge pull request #914 from trapexit/readme
mention bind-propagation and add link to hotio
2021-05-29 13:27:19 -04:00
trapexit c40f42e57e mention bind-propagation and add link to hotio 2021-05-29 13:26:54 -04:00
trapexit 18e87980a4 Merge pull request #913 from trapexit/readme
Update README.md
2021-05-29 13:07:54 -04:00
trapexit 44efbaabe6 Update README.md
Add FAQ entry regarding container runtimes
2021-05-29 13:07:09 -04:00
trapexit a02f3a6856 Merge pull request #891 from trapexit/trapexit-patch-1
Update bug_report.md
2021-03-15 07:58:22 -04:00
trapexit 9573a053d6 Update bug_report.md
update the strace commands and make clear to use a recent release
2021-03-15 07:58:02 -04:00
trapexit 9a5d691619 Merge pull request #889 from trapexit/readme
README: update strace args and add question on snapraid
2021-03-11 15:37:37 -05:00
Antonio SJ Musumeci 495afeffb1 README: update strace args and add question on snapraid 2021-03-11 15:36:50 -05:00
trapexit 4acc1defe2 Merge pull request #888 from Gelma/typo
Fix typo
2021-03-06 17:49:40 -05:00
Andrea Gelmini 58b4eb9089 Fix typo 2021-03-06 23:38:33 +01:00
trapexit ca4b4c6d35 Merge pull request #884 from trapexit/travis
remove travis-ci
2021-02-25 15:40:32 -05:00
Antonio SJ Musumeci 9a90609428 remove travis-ci 2021-02-25 15:40:00 -05:00
trapexit 3e691149f5 Merge pull request #883 from trapexit/link-symlink
new features: follow-symlinks, rename-exdev, link-exdev
2021-02-25 15:33:26 -05:00
Antonio SJ Musumeci 8adebc9489 new features: follow-symlinks, rename-exdev, link-exdev
* follow-symlinks: allows mergerfs to transparently follow symlinks
* link-exdev: in the event a link returns EXDEV create a symlink instead
* rename-exdev: in the event a rename returns EXDEV move the oldpath and
  create a symlink for the newpath
2021-02-11 23:24:08 -05:00
trapexit 50daf84ec0 Merge pull request #880 from trapexit/branch-parse-fix
fix parsing of relative branch paths
2021-02-11 19:37:38 -05:00
Antonio SJ Musumeci d337574665 fix parsing of relative branch paths 2021-02-11 19:22:17 -05:00
trapexit 39d98f1eac Merge pull request #878 from trapexit/readdir_rewind_fix
reset dentry buffer when rewind'ed
2021-02-09 20:21:59 -05:00
trapexit cbea2ba91b Merge branch 'master' into readdir_rewind_fix 2021-02-09 18:13:08 -05:00
Antonio SJ Musumeci f6e37672b8 reset dentry buffer when rewind'ed 2021-02-09 18:09:28 -05:00
trapexit 55bd9a06b1 Merge pull request #876 from trapexit/readme
readme: update support section
2021-02-08 18:15:45 -05:00
Antonio SJ Musumeci 6a9f7e6afc readme: update support section 2021-02-08 17:55:49 -05:00
trapexit f2158d3e66 Merge pull request #871 from trapexit/support
README: update support section
2021-01-23 18:54:42 -05:00
Antonio SJ Musumeci 3bc189cfc8 README: update support section 2021-01-23 18:54:06 -05:00
trapexit d0c5ff7f87 Merge pull request #870 from trapexit/readme
README: add warning/clearity about caching script and hardlinks and cow
2021-01-23 17:19:59 -05:00
Antonio SJ Musumeci dfb544fed8 README: add warning/clearity about caching script and hardlinks and cow 2021-01-23 17:18:07 -05:00
trapexit ae893ca051 Merge pull request #865 from trapexit/config
config: rework global config, remove rwlock, make branches RCU like
2021-01-14 22:10:20 -05:00
Antonio SJ Musumeci 538467b86d config: rework global config, remove rwlock, make branches RCU like
Also added unit tests. Should have done separately but found a number of bugs.
2021-01-14 21:02:10 -05:00
trapexit 46de43d4ad Merge pull request #860 from trapexit/funding
Update FUNDING.yml
2021-01-01 15:52:41 -05:00
trapexit 3808e2683e Update FUNDING.yml 2021-01-01 15:52:15 -05:00
trapexit 52342a76c1 Merge pull request #858 from trapexit/docs
README: misc updates and tweaks
2020-12-29 19:36:30 -05:00
Antonio SJ Musumeci e9e17baf5e README: misc updates and tweaks 2020-12-29 19:34:13 -05:00
trapexit 2e4c6c5fd1 Merge pull request #856 from trapexit/dirname
dirname should not return an empty string
2020-12-18 17:03:43 -05:00
Antonio SJ Musumeci f50812a9ce dirname should not return an empty string 2020-12-18 16:04:17 -05:00
trapexit 861edff49a Merge pull request #855 from trapexit/builds
update cirrus-ci builds
2020-12-17 20:18:16 -05:00
Antonio SJ Musumeci 5e08357f0e update cirrus-ci builds 2020-12-17 20:17:16 -05:00
trapexit 411ee36c90 Merge pull request #854 from trapexit/mspfix
msp policies stopped before root path
2020-12-17 20:14:54 -05:00
Antonio SJ Musumeci 44a98910ba msp policies stopped before root path 2020-12-17 19:55:49 -05:00
trapexit 8a335d8a6e Merge pull request #851 from trapexit/fix
fix segv: zero out data structures
2020-12-13 16:56:55 -05:00
Antonio SJ Musumeci 3900543970 fix segv: zero out data structures 2020-12-13 13:40:04 -05:00
trapexit 2e34c7598a Merge pull request #845 from trapexit/configfile
config: fix invalid error check when parsing config file
2020-11-28 21:49:51 -05:00
Antonio SJ Musumeci 7edd3c6cf3 config: fix invalid error check when parsing config file 2020-11-28 20:26:18 -05:00
trapexit ddf0075938 Merge pull request #844 from trapexit/cleanup
general cleanup, slight memory reduction
2020-11-19 23:30:48 -05:00
Antonio SJ Musumeci 1b26f4908e general cleanup, slight memory reduction 2020-11-19 22:58:52 -05:00
trapexit 2d8fab534a Merge pull request #843 from trapexit/policyfixes
msp policies: used wrong path to check existance
2020-11-19 20:34:51 -05:00
Antonio SJ Musumeci c3fffefbc1 msp policies: used wrong path to check existance 2020-11-19 18:34:28 -05:00
trapexit eaa2593a4b Merge pull request #837 from renkoh/rsync-script-readme
Update percentage full expiring script
2020-10-03 01:05:54 -04:00
Ren Koh 7e583e3f48 use relative option for rsync 2020-10-03 14:45:51 +10:00
trapexit 2ad4891617 Merge pull request #835 from trapexit/readme
README.md: add details on per branch minfreespace
2020-09-29 18:51:30 -04:00
Antonio SJ Musumeci 8990e24c0f README.md: add details on per branch minfreespace 2020-09-29 18:50:25 -04:00
trapexit 88b80234c7 Merge pull request #834 from trapexit/readme
README.md: add details about error handling
2020-09-27 00:30:05 -04:00
Antonio SJ Musumeci f6e37c57fe README.md: add details about error handling 2020-09-27 00:28:00 -04:00
trapexit 562295be90 Merge pull request #833 from trapexit/wyhash-fix
wyhash: use safety mode 1
2020-09-24 09:03:10 -04:00
Antonio SJ Musumeci 04844420dc wyhash: use safety mode 1
mode 0 is not intended for unaligned buffers
2020-09-24 08:45:34 -04:00
trapexit 9c352ac555 Merge pull request #832 from trapexit/rework-errors
rework some function error handling
2020-09-23 00:26:03 -04:00
Antonio SJ Musumeci 5a3184359d rework some function error handling
chmod, chown, removexattr, setxattr, truncate, utimens:
* if no errors: return 0
* if no successes: return first error
* if file acted on was the same as related search function: return its value
* return 0

rmdir, unlink:
* if no errors: return 0
* return first error
2020-09-23 00:01:25 -04:00
trapexit 24f03b04eb Merge pull request #831 from trapexit/minfreespace-fix
properly return const ref from tofrom string wrapper
2020-09-20 11:18:24 -04:00
Antonio SJ Musumeci 610c75fa90 properly return const ref from tofrom string wrapper 2020-09-19 18:57:13 -04:00
trapexit dbc4774e8e Merge pull request #828 from trapexit/remove-old-issue-template
Delete issue_template.md
2020-09-13 10:30:02 -04:00
trapexit 73e8ed72cd Delete issue_template.md 2020-09-13 10:29:45 -04:00
trapexit e694585561 Merge pull request #827 from trapexit/bug_report
Update bug_report.md
2020-09-13 10:28:34 -04:00
trapexit 0b1af2b8ef Update bug_report.md 2020-09-13 10:28:20 -04:00
trapexit 668ac4000a Merge pull request #825 from trapexit/rv
option_parser: return 0 when requesting help or version
2020-09-10 22:16:03 -04:00
Antonio SJ Musumeci 767039c492 option_parser: return 0 when requesting help or version 2020-09-10 21:58:23 -04:00
trapexit b1c6d4864f Merge pull request #821 from trapexit/minfreespace
branches: add per branch minfreespace w/ original value as default
2020-09-04 21:26:38 -04:00
Antonio SJ Musumeci fc3453932a branches: add per branch minfreespace w/ original value as default
example: /mnt/disk0=RW,1G:/mnt/disk1=RW,2G
2020-09-04 21:04:45 -04:00
trapexit 98c49b8001 Merge pull request #819 from trapexit/readme
README.md: fix typos
2020-08-30 22:24:39 -04:00
Antonio SJ Musumeci 27bd39f5ac README.md: fix typos 2020-08-30 22:23:16 -04:00
trapexit 9f84f29e12 Merge pull request #818 from trapexit/lchmod
optionally use lchmod depending on if on Linux or not (BSD)
2020-08-26 15:42:42 -04:00
Antonio SJ Musumeci 6311df774d optionally use lchmod depending on if on Linux or not (BSD) 2020-08-26 12:44:32 -04:00
trapexit 9424784fad Merge pull request #817 from trapexit/readme
README.md: add note indicating only tagged releases are supported
2020-08-26 11:03:50 -04:00
Antonio SJ Musumeci 15fb751f65 README.md: add note indicating only tagged releases are supported 2020-08-26 11:02:56 -04:00
trapexit 3534fb1128 Merge pull request #815 from trapexit/policy-pfrd
add {,ep,msp}pfrd policies
2020-08-25 22:58:44 -04:00
Antonio SJ Musumeci 046844083f add {,ep,msp}pfrd policies
Percentage Free Random Distribution

Chooses a random branch based on the available space percentage free.

IE: if branch A has 1G free and branch B has 2G then B should be chosen
twice as often.
2020-08-25 22:19:01 -04:00
trapexit 7ff995631e Merge pull request #814 from trapexit/cleanup
general cleanup
2020-08-18 19:42:49 -04:00
Antonio SJ Musumeci 8675fce04a add fedora builds to cirrus-ci 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 7fd629ebf3 add #warning to make it more obvious what versions of functions are used 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 2fe20b888e clean up and separate out fs_* files 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 2696079601 break fs.hpp up into separate files 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci ec15872a1f cleanup function signatures and definitions 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 6cc6524997 change category to enum class 2020-08-18 19:28:36 -04:00
1031 changed files with 123120 additions and 29310 deletions
+65 -106
View File
@@ -1,69 +1,58 @@
freebsd_task:
name: "freebsd:12.1"
freebsd_instance:
image_family: freebsd-12-1
env:
ASSUME_ALWAYS_YES: yes
script:
- tools/install-build-pkgs
- gmake
linux_task:
name: "alpine:3.11"
name: "alpine:latest"
container:
image: alpine:3.11
image: alpine:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "centos:6"
container:
image: centos:6
name: "alpine-arm:latest"
arm_container:
image: alpine:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "fedora:41"
container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "centos:7"
container:
image: centos:7
name: "fedora-arm:41"
arm_container:
image: fedora:41
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "centos:8"
name: "ubuntu:22.04"
container:
image: centos:8
image: ubuntu:22.04
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "ubuntu:20.04"
container:
image: ubuntu:20.04
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -71,14 +60,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "ubuntu:19.10"
name: "ubuntu:24.04"
container:
image: ubuntu:19.10
image: ubuntu:24.04
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -86,14 +75,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "ubuntu:18.04"
name: "ubuntu:latest"
container:
image: ubuntu:18.04
image: ubuntu:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -101,14 +90,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "ubuntu:16.04"
container:
image: ubuntu:16.04
name: "ubuntu-arm:latest"
arm_container:
image: ubuntu:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -116,14 +105,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "ubuntu:14.04"
name: "debian:12"
container:
image: ubuntu:14.04
image: debian:12
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -131,14 +120,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "debian:10"
name: "debian:latest"
container:
image: debian:10
image: debian:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
@@ -146,44 +135,14 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "debian:9"
container:
image: debian:9
name: "debian-arm:latest"
arm_container:
image: debian:latest
cpu: 4
memory: 2G
timeout_in: 10m
memory: 4G
timeout_in: 15m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:8"
container:
image: debian:8
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:7"
container:
image: debian:8
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
+1 -6
View File
@@ -1,7 +1,2 @@
# These are supported funding model platforms
github: [trapexit]
patreon: trapexit
open_collective: trapexit
ko_fi: trapexit
custom: ['https://paypal.me/trapexit','https://www.subscribestar.com/trapexit']
custom: ['https://github.com/trapexit/support']
+27 -6
View File
@@ -8,21 +8,42 @@ assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Please do not file bugs for unreleased versions. The master branch is **not** to be considered in a production state.
A clear and concise description of the unexpected behavior.
**Please be sure to use latest release of mergerfs to ensure the issue still exists. Not your distro's latest but the latest official release.**
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
**To Reproduce**
Steps to reproduce the behavior. List **all** steps to reproduce. All settings.
Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
Please simplify the reproduction as much as possible.
- Unless it is dependenat on multiple branches, use a single branch
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
**Expected behavior**
A clear and concise description of what you expected to happen.
A clear and concise description of the expected behavior.
**System information:**
- OS, kernel version: `uname -a`
- mergerfs version: `mergerfs -V`
- mergerfs settings
- List of drives, filesystems, & sizes: `df -h`
- A strace of the application having a problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
- strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
- List of drives, filesystems, & sizes:
- `df -h`
- `lsblk -f`
- A strace of the application having a problem:
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
- strace of mergerfs while app tried to do it's thing:
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
**Additional context**
Add any other context about the problem here.
+2 -2
View File
@@ -1,10 +1,10 @@
---
name: Question
about: When you're looking for information
about: When you're looking for information or general support
title: ''
labels: investigating, question
assignees: ''
---
You can also ask questions on Discord for realtime help: https://discord.gg/MpAr69V
For general questions and support please use [GitHub Discussions](https://github.com/trapexit/mergerfs/discussions) or [Discord](https://discord.gg/MpAr69V).
-34
View File
@@ -1,34 +0,0 @@
General description
===================
Expected behavior
=================
Actual behavior
===============
**Precise** steps to reproduce the behavior
===========================================
> Explicitly list **all** steps to reproduce. Preferably create a minimal example of the problem using standard command line tools. The more variables (apps, settings, etc.) that are involved the more difficult it is to debug. Also, **please** be sure to have read all of the README. It contains a lot of information regarding known system and user issues.
System information
==================
Please provide as much of the following information as possible:
* [ ] mergerfs version: `mergerfs -V`
* [ ] mergerfs settings: `cat /etc/fstab` or the command line arguments
* [ ] Linux version: `uname -a`
* [ ] Versions of any additional software being used
* [ ] List of drives, filesystems, & sizes: `df -h`
* [ ] strace of application having problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
* [ ] strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
+42
View File
@@ -0,0 +1,42 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "7 3 * * 6"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
if: ${{ matrix.language == 'python' || matrix.language == 'cpp' }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
+49
View File
@@ -0,0 +1,49 @@
name: "mkdocs"
on:
push:
branches:
- master
tags:
- '*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Configure Git Credentials
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Install dependencies
working-directory: ./mkdocs
run: |
pip install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike
- name: Deploy docs
working-directory: ./mkdocs
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$REF_TYPE" = "tag" ]; then
mike deploy "$REF_NAME" "latest" --allow-empty --ignore-remote-status --update-aliases
mike set-default "latest"
#git checkout gh-pages
#git push -f origin gh-pages
elif [ "$REF_TYPE" = "branch" ] && [ "$REF_NAME" = "master" ]; then
mike deploy "master" --ignore-remote-status --allow-empty
#git checkout gh-pages
#git push -f origin gh-pages
fi
+9 -1
View File
@@ -14,12 +14,20 @@
# build artifacts
mergerfs
obj/
build/
src/version.hpp
VERSION
# Debian files
debian/files
debian/changelog
debian/debhelper-build-stamp
debian/mergerfs.debhelper.log
debian/mergerfs.substvars
# RPM files
rpmbuild/
# mkdocs files
mkdocs/venv
__pycache__/
-44
View File
@@ -1,44 +0,0 @@
language: cpp
matrix:
include:
- os: linux
dist: precise
compiler: gcc
sudo: required
- os: linux
dist: precise
compiler: clang
sudo: required
- os: linux
dist: trusty
compiler: gcc
sudo: required
- os: linux
dist: trusty
compiler: clang
sudo: required
- os: linux
dist: xenial
compiler: gcc
sudo: required
- os: linux
dist: xenial
compiler: clang
sudo: required
- os: linux
dist: bionic
compiler: gcc
sudo: required
- os: linux
dist: bionic
compiler: clang
sudo: required
script:
- sudo -E apt-get install fakeroot
- sudo -E make install-build-pkgs
- make -j$(($(nproc) + 1))
- make deb
- sudo -E dpkg -i ../*.deb
- mergerfs -v || true
+16
View File
@@ -0,0 +1,16 @@
## Libraries
### included in repo
* libfuse: https://github.com/libfuse/libfuse (heavily modified fork of v2.x)
* rapidhash: https://github.com/Nicoshev/rapidhash
* fmt: https://github.com/fmtlib/fmt
* concurrentqueue: https://github.com/cameron314/concurrentqueue
* scope_guard: https://github.com/Neargye/scope_guard
* subprocess: https://github.com/arun11299/cpp-subprocess
* CLI11: https://github.com/CLIUtils/CLI11
* boost
* download boost
* ./bootstrap.sh
* ./b2 tools/bcp
* ./dist/bin/bcp unordered/concurrent_flat_map.hpp <target_dir>
+1 -1
View File
@@ -1,7 +1,7 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
+228 -108
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
# Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -12,153 +12,238 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
GIT = git
TAR = tar
MKDIR = mkdir
TOUCH = touch
CP = cp
RM = rm
LN = ln
FIND = find
INSTALL = install
MKTEMP = mktemp
STRIP = strip
PANDOC = pandoc
SED = sed
RPMBUILD = rpmbuild
GIT2DEBCL = ./tools/git2debcl
PKGCONFIG = pkg-config
ifeq ($(shell id -u),0)
FAKEROOT ?=
endif
GIT_REPO = 0
CP ?= cp
FAKEROOT ?= fakeroot
FIND ?= find
GIT ?= git
GIT2DEBCL ?= buildtools/git2debcl
INSTALL ?= install
LN ?= ln
MKDIR ?= mkdir
MKTEMP ?= mktemp
MV ?= mv
RM ?= rm
SED ?= sed
STRIP ?= strip
TAR ?= tar
TOUCH ?= touch
BUILDDIR := build
GITHUB_REPO ?= "https://github.com/trapexit/mergerfs"
ifndef GIT_REPO
ifneq ($(shell $(GIT) --version 2> /dev/null),)
ifeq ($(shell test -e .git; echo $$?),0)
GIT_REPO = 1
endif
endif
USE_XATTR = 1
UGID_USE_RWLOCK = 0
ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g
else
OPT_FLAGS := -O2
endif
ifeq ($(STATIC),1)
USE_XATTR ?= 1
UGID_USE_RWLOCK ?= 0
ifdef NDEBUG
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -fno-omit-frame-pointer -DDEBUG
endif
ifdef STATIC
STATIC_FLAGS := -static
else
STATIC_FLAGS :=
endif
ifeq ($(LTO),1)
ifdef LTO
LTO_FLAGS := -flto
else
LTO_FLAGS :=
endif
SRC = $(wildcard src/*.cpp)
OBJS = $(SRC:src/%.cpp=build/%.o)
DEPS = $(SRC:src/%.cpp=build/%.d)
MANPAGE = mergerfs.1
CXXFLAGS ?= ${OPT_FLAGS}
CXXFLAGS := \
${CXXFLAGS} \
-std=c++0x \
$(STATIC_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-Wno-unused-result \
-MMD
FUSE_FLAGS = \
-Ilibfuse/include \
-D_FILE_OFFSET_BITS=64 \
-DFUSE_USE_VERSION=29
MFS_FLAGS = \
-DUSE_XATTR=$(USE_XATTR) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
SRC := $(wildcard src/*.cpp)
OBJS := $(SRC:src/%.cpp=build/.objs/%.cpp.o)
DEPS := $(SRC:src/%.cpp=build/.objs/%.cpp.d)
LDFLAGS := \
${LDFLAGS} \
-pthread \
-lrt
TESTS := $(wildcard tests/*.cpp)
TESTS_OBJS := $(filter-out build/.objs/mergerfs.o,$(OBJS))
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.o)
TESTS_DEPS := $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.d)
TESTS_DEPS += $(DEPS)
DESTDIR =
PREFIX = /usr/local
EXEC_PREFIX = $(PREFIX)
DATAROOTDIR = $(PREFIX)/share
DATADIR = $(DATAROOTDIR)
BINDIR = $(EXEC_PREFIX)/bin
SBINDIR = $(EXEC_PREFIX)/sbin
MANDIR = $(DATAROOTDIR)/man
MAN1DIR = $(MANDIR)/man1
MANPAGE := mergerfs.1
CPPFLAGS ?=
override CPPFLAGS += \
-D_FILE_OFFSET_BITS=64
CFLAGS ?= \
$(OPT_FLAGS) \
-Wall \
-Wno-unused-result \
-pipe
override CFLAGS += \
-MMD \
-MP
CXXFLAGS ?= \
$(OPT_FLAGS) \
$(STATIC_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-Wpessimizing-move \
-Werror=pessimizing-move \
-Wredundant-move \
-Werror=redundant-move \
-Wno-unused-result \
-pipe
override CXXFLAGS += \
-std=c++17 \
-MMD \
-MP
override INC_FLAGS := \
-Isrc \
-Ilibfuse/include
override MFS_FLAGS := \
-DUSE_XATTR=$(USE_XATTR) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
override TESTS_FLAGS := \
-Isrc \
-DTESTS
LIBFUSE := libfuse/$(BUILDDIR)/libfuse.a
LDFLAGS ?=
LDLIBS := \
-lrt \
-pthread
override LDLIBS += \
$(LIBFUSE)
ifeq ($(CXX),g++)
GCC_VERSION := $(shell $(CXX) -dumpversion | cut -f1 -d.)
ifeq ($(shell test $(GCC_VERSION) -lt 9; echo $$?),0)
override LDLIBS += -lstdc++fs
endif
endif
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR ?=
PREFIX ?= /usr/local
EXEC_PREFIX ?= $(PREFIX)
DATAROOTDIR ?= $(PREFIX)/share
DATADIR ?= $(DATAROOTDIR)
BINDIR ?= $(EXEC_PREFIX)/bin
SBINDIR ?= $(EXEC_PREFIX)/sbin
LIBDIR ?= $(EXEC_PREFIX)/lib
MANDIR ?= $(DATAROOTDIR)/man
MAN1DIR ?= $(MANDIR)/man1
INSTALLBINDIR ?= $(DESTDIR)$(BINDIR)
INSTALLSBINDIR ?= $(DESTDIR)$(SBINDIR)
INSTALLLIBDIR ?= $(DESTDIR)$(LIBDIR)/mergerfs
INSTALLMAN1DIR ?= $(DESTDIR)$(MAN1DIR)
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
.PHONY: all
all: mergerfs
all: libfuse $(BUILDDIR)/mergerfs $(BUILDDIR)/fsck.mergerfs $(BUILDDIR)/mergerfs.collect-info
.PHONY: help
help:
@echo "usage: make\n"
@echo "make USE_XATTR=0 - build program without xattrs functionality"
@echo "make STATIC=1 - build static binary"
@echo "make LTO=1 - build with link time optimization"
@echo "usage: make ARG\n"
@echo "USE_XATTR=0 - build program without xattrs functionality"
@echo "STATIC=1 - build static binary"
@echo "LTO=1 - build with link time optimization"
@echo "CLEANUP=1 - cleanup images between release build process"
@echo "PKGDIR=/dir/ - location for release build pkgs"
@echo "GITREF=gitref - gitref to use for release builds"
objects: version build/stamp
$(MAKE) $(OBJS)
build/mergerfs: libfuse objects
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
$(BUILDDIR)/mergerfs: $(LIBFUSE) src/version.hpp $(OBJS)
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
mergerfs: build/mergerfs
$(BUILDDIR)/fsck.mergerfs:
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/mergerfs.collect-info:
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/tests: $(BUILDDIR)/mergerfs $(TESTS_OBJS)
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
.PHONY: libfuse
$(LIBFUSE):
libfuse:
$(MAKE) NDEBUG=$(NDEBUG) -C libfuse
tests: $(BUILDDIR)/tests
.PHONY:
changelog:
ifeq ($(GIT_REPO),1)
ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > ChangeLog
else
@echo "WARNING: need git repo to generate ChangeLog"
endif
.PHONY: version
version:
tools/update-version
version: src/version.hpp
build/stamp:
$(MKDIR) -p build
src/version.hpp:
./buildtools/update-version
$(BUILDDIR)/stamp:
$(MKDIR) -p $(BUILDDIR)/.objs
$(MKDIR) -p $(BUILDDIR)/.test_objs
$(TOUCH) $@
build/%.o: src/%.cpp
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/.objs/%.cpp.o: src/%.cpp $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/.test_objs/%.cpp.o: tests/%.cpp
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/preload.so: $(BUILDDIR)/stamp tools/preload.c
$(CC) -shared -fPIC $(CFLAGS) -o $@ tools/preload.c
preload: $(BUILDDIR)/preload.so
.PHONY: clean
clean: rpm-clean
$(RM) -rf build
$(RM) -rf $(BUILDDIR)
$(FIND) . -name "*~" -delete
$(MAKE) -C libfuse clean
.PHONY: distclean
distclean: clean
ifeq ($(GIT_REPO),1)
ifdef GIT_REPO
$(GIT) clean -xfd
endif
.PHONY: install
install: install-base install-mount-tools install-man
install: install-base install-mount-tools install-preload install-man
install-base: build/mergerfs
.PHONY: install-base
install-base: all
$(MKDIR) -p "$(INSTALLBINDIR)"
$(INSTALL) -v -m 0755 build/mergerfs "$(INSTALLBINDIR)/mergerfs"
$(INSTALL) -v -m 0755 "$(BUILDDIR)/mergerfs" "$(INSTALLBINDIR)/mergerfs"
$(LN) -fs "mergerfs" "${INSTALLBINDIR}/fsck.mergerfs"
$(LN) -fs "mergerfs" "${INSTALLBINDIR}/mergerfs.collect-info"
.PHONY: install-mount-tools
install-mount-tools: install-base
$(MKDIR) -p "$(INSTALLBINDIR)"
$(MAKE) -C libfuse install
install-man: $(MANPAGE)
.PHONY: install-man
install-man: man/$(MANPAGE)
$(MKDIR) -p "$(INSTALLMAN1DIR)"
$(INSTALL) -v -m 0644 "man/$(MANPAGE)" "$(INSTALLMAN1DIR)/$(MANPAGE)"
.PHONY: install-preload
install-preload: preload
$(MKDIR) -p "$(INSTALLLIBDIR)"
$(INSTALL) -v -m 444 "$(BUILDDIR)/preload.so" "$(INSTALLLIBDIR)/preload.so"
.PHONY: install-strip
install-strip: install-base
$(STRIP) "$(INSTALLBINDIR)/mergerfs"
@@ -174,17 +259,8 @@ uninstall-mount.mergerfs:
uninstall-man:
$(RM) -f "$(INSTALLMAN1DIR)/$(MANPAGE)"
$(MANPAGE): README.md
ifneq ($(shell $(PANDOC) --version 2> /dev/null),)
$(PANDOC) -s -t man -o "man/$(MANPAGE)" README.md
else
$(warning "pandoc does not appear available: unable to build manpage")
endif
man: $(MANPAGE)
.PHONY: tarball
tarball: man changelog version
tarball: changelog version
$(eval VERSION := $(shell cat VERSION))
$(eval VERSION := $(subst -,_,$(VERSION)))
$(eval FILENAME := mergerfs-$(VERSION))
@@ -194,29 +270,34 @@ tarball: man changelog version
$(TAR) --exclude=.git -cz -C $(TMPDIR) -f $(FILENAME).tar.gz $(FILENAME)
$(RM) -rf $(TMPDIR)
.PHONY: debian-changelog
debian-changelog:
ifeq ($(GIT_REPO),1)
ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > debian/changelog
else
cp ChangeLog debian/changelog
$(CP) -v ChangeLog debian/changelog
endif
.PHONY: signed-deb
signed-deb:
$(MAKE) distclean
$(MAKE) debian-changelog
dpkg-source -b .
dpkg-buildpackage -nc
$(FAKEROOT) dpkg-buildpackage -nc
.PHONY: deb
deb:
$(MAKE) distclean
$(MAKE) debian-changelog
dpkg-source -b .
dpkg-buildpackage -nc -uc -us
$(MAKE) version
$(FAKEROOT) dpkg-buildpackage -nc -uc -us
$(MKDIR) -p ./$(BUILDDIR)/pkgs/
$(MV) -v ../mergerfs*deb ./$(BUILDDIR)/pkgs/
.PHONY: rpm-clean
rpm-clean:
$(RM) -rf rpmbuild
.PHONY: rpm
rpm: tarball
$(eval VERSION := $(shell cat VERSION))
$(eval VERSION := $(subst -,_,$(VERSION)))
@@ -224,15 +305,54 @@ rpm: tarball
$(SED) 's/__VERSION__/$(VERSION)/g' mergerfs.spec > \
rpmbuild/SOURCES/mergerfs.spec
cp -ar mergerfs-$(VERSION).tar.gz rpmbuild/SOURCES
$(RPMBUILD) -ba rpmbuild/SOURCES/mergerfs.spec \
rpmbuild -ba rpmbuild/SOURCES/mergerfs.spec \
--define "_topdir $(CURDIR)/rpmbuild"
.PHONY: install-build-pkgs
install-build-pkgs:
tools/install-build-pkgs
./buildtools/install-build-pkgs
.PHONY: install-build-tools
install-build-tools:
./bulidtools/install-build-tools
define build_release
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
./buildtools/build-release \
--target=$(1) \
$(if $(CLEANUP),--cleanup) \
$(if $(PKGDIR),--pkgdirpath=$(PKGDIR)) \
--git-repo=$(if $(REMOTE_REPO),$(GITHUB_REPO),".") \
--branch=$(GITREF)
endef
.PHONY: release
.PHONY: release-amd64 release-arm64 release-armhf release-riscv64
.PHONY: release-sample release-static release-tarball
release:
$(call build_release,"all")
release-sample:
$(call build_release,"debian:13.amd64")
release-amd64:
$(call build_release,"amd64")
release-arm64:
$(call build_release,"arm64")
release-armhf:
$(call build_release,"armhf")
release-riscv64:
$(call build_release,"risc64")
release-static:
$(call build_release,"static")
release-tarball:
$(call build_release,"tarball")
.PHONY: tags
tags:
rm -fv TAGS
find . -name "*.c" -print | etags --append -
find . -name "*.h" -print | etags --append -
find . -name "*.cpp" -print | etags --append -
find . -name "*.hpp" -print | etags --append -
.PHONY: libfuse
libfuse:
$(MAKE) DEBUG=$(DEBUG) -C libfuse
-include $(DEPS)
+93 -1214
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
#!/bin/sh
set -e
if [ $# -ne 2 ]; then
echo "usage: $0 <branch> <git_repo>"
echo "argv: $@"
echo "argc: $#"
exit 1
fi
GIT_REPO="${1}"
BRANCH="${2}"
BUILDDIR="/tmp/build"
echo "Clone source from ${GIT_REPO} to ${BUILDDIR}"
git \
clone \
--single-branch \
--branch="${BRANCH}" \
"${GIT_REPO}" \
"${BUILDDIR}"
cd "${BUILDDIR}"
git log HEAD^1..
mkdir "/build"
if [ -e /usr/bin/apt-get ]; then
make deb
elif [ -e /usr/bin/dnf ]; then
make rpm
elif [ -e /usr/bin/yum ]; then
. /opt/rh/devtoolset-9/enable
make rpm
elif [ -e /sbin/apk ]; then
echo "NOT YET SUPPORTED"
exit 1
elif [ -e /usr/sbin/pkg ]; then
echo "NOT YET SUPPORTED"
exit 1
else
echo "NOT YET SUPPORTED"
exit 1
fi
echo "Copy packages to host..."
find "${BUILDDIR}" \
-type f \
\( -name "*.deb" -or -name "*.rpm" \) \
-not -name "*dbgsym*" \
-not -name "*sym*" \
-not -name "*src.rpm" \
-exec cp -v {} /build/ \;
exit 0
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
import subprocess
import sys
import os
import argparse
import time
def build(git_repo,
branch,
containerfile,
pkgdirpath):
timestamp = time.time_ns()
args = ['podman',
'build',
'--pull=always',
'--force-rm']
if os.path.exists(git_repo):
git_repo = os.path.realpath(git_repo)
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro']
args += ['--build-arg=GIT_REPO=/tmp/mergerfs.git']
else:
args += [f'--build-arg=GIT_REPO={git_repo}']
args += ['-o',pkgdirpath,
'-f',containerfile,
f'--build-arg=BRANCH={branch}',
f'--build-arg=BUILD_TIMESTAMP={timestamp}',
'buildtools/']
# TODO: Capture output and write to log
print(args)
rv = subprocess.run(args)
os.makedirs(pkgdirpath,exist_ok=True)
report_filepath = os.path.join(pkgdirpath,"build-report.txt")
with open(report_filepath,"a+") as f:
build = os.path.basename(containerfile)
f.write(build + ": ")
f.write(f"branch={branch}; ")
f.write(f"timestamp={timestamp}; ")
f.write("rv=")
if rv.returncode == 0:
f.write("success;")
else:
f.write("fail;")
f.write("\n")
def setup():
args = ['sudo',
'apt-get',
'install',
'-fy',
'qemu-user-static',
'qemu-user-binfmt']
print(args)
subprocess.run(args)
def podman_cleanup():
args = ['podman',
'system',
'prune',
'-af']
print(args)
subprocess.run(args)
def parse_args():
p = argparse.ArgumentParser()
p.add_argument('--target',
default='debian:13.amd64')
p.add_argument('--pkgdirpath',
default='build/pkgs/')
p.add_argument('--branch',
default='master')
p.add_argument('--setup',
required=False,
action='store_true')
p.add_argument('--cleanup',
required=False,
action='store_true')
p.add_argument('--git-repo',
required=True,
type=str)
return p.parse_args()
def should_skip(filename):
if filename.endswith('~'):
return True
return False
def main():
args = parse_args()
print(args)
if args.setup:
setup()
sys.exit(0)
containerfiles = []
basepath = 'buildtools/containerfiles'
if os.path.exists(f'{basepath}/{args.target}'):
containerfiles.append(f'{basepath}/{args.target}')
elif args.target == 'all':
for root,dirnames,filenames in os.walk(basepath):
filenames.sort()
for filename in filenames:
if should_skip(filename):
continue
containerfile = f'buildtools/containerfiles/{filename}'
containerfiles.append(containerfile)
else:
for root,dirnames,filenames in os.walk(basepath):
filenames.sort()
for filename in filenames:
if args.target not in filename:
continue
if should_skip(filename):
continue
containerfile = f'buildtools/containerfiles/{filename}'
containerfiles.append(containerfile)
for containerfile in containerfiles:
if args.cleanup:
podman_cleanup()
build(git_repo=args.git_repo,
branch=args.branch,
containerfile=containerfile,
pkgdirpath=args.pkgdirpath)
sys.exit(0)
if __name__ == '__main__':
main()
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:40 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:40 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:41 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:41 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:42 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:42 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:43 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:43 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:8 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:8 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:9 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:9 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_amd64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/arm64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_arm64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/armhf alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_armhf.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/i386 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_i386.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 ubuntu:latest as build
RUN apt-get update && apt-get install -y makeself
RUN mkdir -p /tmp/mergerfs/
RUN --mount=type=bind,src=pkgs,dst=/pkgs ls -lhd /pkgs
RUN --mount=type=bind,src=pkgs,dst=/pkgs cp -v /pkg/*static*.gz /tmp/mergerfs/
RUN makeself /tmp/mergerfs/ /build/mergerfs-installer.run "mergerfs" echo "foo"
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/riscv64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM alpine:latest as tarball
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
RUN cd /tmp/mergerfs && make version tarball
RUN mkdir /build
RUN cp -v /tmp/mergerfs/*.tar.gz /build/
FROM scratch
COPY --from=tarball /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -x
BASEPATH="/tmp"
for x in $(seq -w 2)
do
FILEPATH="${BASEPATH}/mergerfs-${x}.img"
MOUNTPOINT="${BASEPATH}/mergerfs-${x}"
truncate -s 1G "${FILEPATH}"
mkdir -p "${MOUNTPOINT}"
mkfs.ext4 -m0 -L "mergerfs${x}" "${FILEPATH}"
sudo mount "${FILEPATH}" "${MOUNTPOINT}"
done
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
input_file="${1}"
while IFS= read -r line || [ -n "$line" ]; do
# Skip empty lines
if [ -z "$line" ]; then
continue
fi
image=$(echo "$line" | cut -d' ' -f1)
platform=$(echo "$line" | cut -d' ' -f2-)
echo "${image} ${platform}"
cat <<EOF > "containerfiles/${image##*/}.${platform}"
FROM --platform=${platform} ${image} as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs \$GIT_REPO \$BRANCH
FROM scratch
COPY --from=build /build/ /
EOF
done < "$input_file"
+202
View File
@@ -0,0 +1,202 @@
#!/bin/sh
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# Default values
NAME=""
VERSION="::guess::"
DISTRO="::guess::"
CODENAME="::guess::"
URGENCY="medium"
# Function to display usage
usage() {
cat << EOF
Usage: $0 --name NAME [OPTIONS]
Generated debian/changelog from git log
Options:
--name NAME Name of package (required)
--version VERSION Place in git history to include upto (default: ::guess::)
--distro DISTRO Distribution name (default: ::guess::)
--codename NAME Distribution codename (default: ::guess::)
--urgency URGENCY Urgency (default: medium)
-h, --help Show this help message
EOF
exit 1
}
# Parse command line arguments
while [ $# -gt 0 ]; do
case "$1" in
--name)
NAME="$2"
shift 2
;;
--version)
VERSION="$2"
shift 2
;;
--distro)
DISTRO="$2"
shift 2
;;
--codename)
CODENAME="$2"
shift 2
;;
--urgency)
URGENCY="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
# Check required arguments
if [ -z "$NAME" ]; then
echo "Error: --name is required" >&2
usage
fi
# Function to get git tags sorted in reverse order
git_tags() {
git tag -l | sort -rV
}
# Function to get git log between two tags
git_log() {
local fromtag="$1"
local totag="$2"
git log --no-merges --oneline "${fromtag}...${totag}" 2>/dev/null
}
# Function to get author and time for a tag
git_author_and_time() {
local tag="$1"
git log -1 --format="-- %an <%ae> %cD" "$tag"
}
# Function to get git version
git_version() {
git describe --always --tags --dirty 2>/dev/null || echo "unknown"
}
# Function to guess distribution
guess_distro() {
if command -v lsb_release >/dev/null 2>&1; then
lsb_release -i -s 2>/dev/null | tr '[:upper:]' '[:lower:]'
else
echo "unknown"
fi
}
# Function to guess codename
guess_codename() {
if command -v lsb_release >/dev/null 2>&1; then
lsb_release -c -s 2>/dev/null | tr '[:upper:]' '[:lower:]'
else
echo "unknown"
fi
}
# Handle guessing of values
if [ "$DISTRO" = "::guess::" ]; then
DISTRO=$(guess_distro)
fi
if [ "$CODENAME" = "::guess::" ]; then
CODENAME=$(guess_codename)
fi
if [ "$VERSION" = "::guess::" ]; then
VERSION=$(git_version)
fi
# Version suffix
VERSUFFIX="~${DISTRO}-${CODENAME}"
# Get all tags
TAGS=$(git_tags)
# Create temporary file for tag pairs
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
# Check if version is in tags
if echo "$TAGS" | grep -q "^${VERSION}$"; then
# If version is a tag, start from that tag
echo "$TAGS" | awk -v ver="$VERSION" 'BEGIN{found=0} {if($0==ver)found=1; if(found)print}' > "$TMPFILE"
else
# If version is not a tag, use HEAD as starting point
echo "$VERSION HEAD" > "$TMPFILE"
echo "$TAGS" >> "$TMPFILE"
fi
# Process tags and generate changelog
PREV_TAG=""
PREV_REF=""
FIRST=1
while IFS= read -r line; do
if [ -z "$line" ]; then
continue
fi
# Handle the special case of VERSION HEAD
if [ "$line" = "$VERSION HEAD" ]; then
TAG="$VERSION"
REF="HEAD"
else
TAG="$line"
REF="$line"
fi
if [ -n "$PREV_TAG" ]; then
# Get log entries between current and previous tag
LOG_ENTRIES=$(git_log "$REF" "$PREV_REF")
# Skip if no changes
if [ -z "$LOG_ENTRIES" ] || [ "$LOG_ENTRIES" = "" ]; then
PREV_TAG="$TAG"
PREV_REF="$REF"
continue
fi
# Print package header
printf "%s (%s%s) %s; urgency=%s\n\n" "$NAME" "$PREV_TAG" "$VERSUFFIX" "$CODENAME" "$URGENCY"
# Print each log entry
echo "$LOG_ENTRIES" | while IFS= read -r entry; do
if [ -n "$entry" ]; then
printf " * %s\n" "$entry"
fi
done
# Print author and time
AUTHOR_TIME=$(git_author_and_time "$PREV_REF")
printf "\n %s\n\n" "$AUTHOR_TIME"
fi
PREV_TAG="$TAG"
PREV_REF="$REF"
done < "$TMPFILE"
+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
if [ -e /usr/bin/apt-get ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get -qy update
apt-get -qy --force-yes --no-install-suggests --no-install-recommends \
install \
ca-certificates \
build-essential \
dpkg-dev \
git \
g++ \
debhelper \
lsb-release \
fakeroot
elif [ -e /usr/bin/dnf ]; then
dnf -y update
dnf -y install \
git rpm-build gcc-c++ make which libatomic
elif [ -e /usr/bin/yum ]; then
yum -y update
yum -y install \
git rpm-build gcc-c++ make which
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc\*
elif [ -e /usr/bin/zypper ]; then
zypper update -y
zypper install -y \
git rpm-build gcc-c++ make which
elif [ -e /sbin/apk ]; then
apk add \
abuild git gcc g++ make \
linux-headers
elif [ -e /usr/sbin/pkg ]; then
pkg install \
git gmake gcc
fi
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
if [ -e /usr/bin/apt-get ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get -qy update
apt-get -qy --force-yes --no-install-suggests --no-install-recommends install \
qemu-user-static \
podman
elif [ -e /usr/bin/dnf ]; then
dnf -y update
dnf -y install \
qemu-user-static \
podman
elif [ -e /sbin/apk ]; then
apk add \
python3 \
qemu-user-static \
podman
fi
+39
View File
@@ -0,0 +1,39 @@
debian:11 amd64
debian:11 arm64
debian:11 armhf
debian:11 i386
debian:12 amd64
debian:12 arm64
debian:12 armhf
debian:12 i386
debian:13 amd64
debian:13 arm64
debian:13 armhf
debian:13 i386
debian:13 riscv64
fedora:40 amd64
fedora:40 arm64
fedora:41 amd64
fedora:41 arm64
fedora:42 amd64
fedora:42 arm64
fedora:43 amd64
fedora:43 arm64
quay.io/rockylinux/rockylinux:10 amd64
quay.io/rockylinux/rockylinux:10 arm64
quay.io/rockylinux/rockylinux:8 amd64
quay.io/rockylinux/rockylinux:8 arm64
quay.io/rockylinux/rockylinux:9 amd64
quay.io/rockylinux/rockylinux:9 arm64
ubuntu:22.04 amd64
ubuntu:22.04 arm64
ubuntu:22.04 armhf
ubuntu:22.04 riscv64
ubuntu:24.04 amd64
ubuntu:24.04 arm64
ubuntu:24.04 armhf
ubuntu:24.04 riscv64
ubuntu:25.04 amd64
ubuntu:25.04 arm64
ubuntu:25.04 armhf
ubuntu:25.04 riscv64
@@ -9,10 +9,12 @@ if [ "${VERSION}" = "" ]; then
VERSION="unknown"
fi
echo "version = ${VERSION}"
echo -n "${VERSION}" > VERSION
grep -q \"${VERSION}\" src/version.hpp
grep -sq \"${VERSION}\" src/version.hpp
RV=$?
if [ $RV -ne 0 ]; then
echo "Writing 'src/version.hpp'"
echo "#pragma once" > src/version.hpp
echo "static const char MERGERFS_VERSION[] = \"${VERSION}\";" >> src/version.hpp
fi
-1
View File
@@ -1 +0,0 @@
9
+1 -1
View File
@@ -2,7 +2,7 @@ Source: mergerfs
Section: utils
Priority: optional
Maintainer: Antonio SJ Musumeci <trapexit@spawn.link>
Build-Depends: debhelper (>= 8.0.0)
Build-Depends: debhelper-compat (= 10)
Standards-Version: 3.9.4
Homepage: http://github.com/trapexit/mergerfs
+5 -3
View File
@@ -1,11 +1,13 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
%:
dh $@ --parallel
dh $@
override_dh_auto_install:
$(MAKE) DESTDIR=$(CURDIR)/debian/mergerfs PREFIX=/usr install
$(MAKE) \
DESTDIR=$(CURDIR)/debian/mergerfs \
PREFIX=/usr \
install
-3547
View File
File diff suppressed because it is too large Load Diff
+105 -65
View File
@@ -1,4 +1,25 @@
VERSION = 2.9.7-mergerfs_2.30.0
# Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
AR ?= ar
CHMOD ?= chmod
CHOWN ?= chown
INSTALL ?= install
MKDIR ?= mkdir
RM ?= rm
STRIP ?= strip
TOUCH ?= touch
OS := $(shell uname -s)
ifeq ($(OS),Linux)
@@ -9,108 +30,127 @@ UTILS :=
INSTALLUTILS :=
endif
ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g
ifeq ($(NDEBUG),1)
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O2
OPT_FLAGS := -O0 -g -DDEBUG -fno-omit-frame-pointer
endif
DESTDIR =
PREFIX = /usr/local
EXEC_PREFIX = $(PREFIX)
DATAROOTDIR = $(PREFIX)/share
DATADIR = $(DATAROOTDIR)
BINDIR = $(EXEC_PREFIX)/bin
SBINDIR = /sbin
MANDIR = $(DATAROOTDIR)/man
MAN1DIR = $(MANDIR)/man1
ifeq ($(LTO),1)
LTO_FLAGS := -flto
else
LTO_FLAGS :=
endif
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR ?=
PREFIX ?= /usr/local
EXEC_PREFIX ?= $(PREFIX)
DATAROOTDIR ?= $(PREFIX)/share
DATADIR ?= $(DATAROOTDIR)
BINDIR ?= $(EXEC_PREFIX)/bin
SBINDIR ?= /sbin
MANDIR ?= $(DATAROOTDIR)/man
MAN1DIR ?= $(MANDIR)/man1
AR ?= ar
INSTALLBINDIR ?= $(DESTDIR)$(BINDIR)
INSTALLSBINDIR ?= $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR ?= $(DESTDIR)$(MAN1DIR)
SRC = \
BUILDDIR := build
SRC_C = \
lib/buffer.c \
lib/fuse_dirents.c \
lib/fuse.c \
lib/fuse_kern_chan.c \
lib/fuse_loop_mt.c \
lib/fuse_lowlevel.c \
lib/fuse_mt.c \
lib/crc32b.c \
lib/fuse_opt.c \
lib/fuse_session.c \
lib/fuse_signals.c \
lib/helper.c \
lib/mount.c
OBJS = $(SRC:lib/%.c=build/%.o)
DEPS = $(SRC:lib/%.c=build/%.d)
SRC_CXX = $(wildcard lib/*.cpp)
OBJS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.o)
OBJS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.o)
DEPS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.d)
DEPS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.d)
CPPFLAGS ?=
CFLAGS ?= \
$(OPT_FLAGS)
CFLAGS := \
${CFLAGS} \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-pipe \
-MMD
FUSERMOUNT_DIR = $(BINDIR)
FUSE_FLAGS = \
-pipe
override CFLAGS += \
-std=gnu99 \
-MMD \
-MP
CXXFLAGS ?= \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-pipe
override CXXFLAGS += \
-std=c++17 \
-MMD \
-MP
FUSERMOUNT_DIR := $(BINDIR)
FUSE_FLAGS := \
-Iinclude \
-Ibuild \
-I$(BUILDDIR) \
-D_REENTRANT \
-D_FILE_OFFSET_BITS=64 \
-DFUSE_USE_VERSION=29 \
-DPACKAGE_VERSION=\"$(VERSION)\" \
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
LDFLAGS := \
${LDFLAGS} \
LDFLAGS ?=
LDLIBS := \
-lrt \
-latomic \
-pthread
all: build/libfuse.a $(UTILS)
all: $(BUILDDIR)/libfuse.a $(UTILS)
build/config.h: build/stamp
ecfd/build | tee build/config.h
$(BUILDDIR)/stamp:
$(MKDIR) -p $(BUILDDIR)/.objs/
$(TOUCH) $@
build/stamp:
mkdir -p build
touch $@
objects: build/config.h
$(MAKE) $(OBJS)
build/libfuse.a: objects
${AR} rcs build/libfuse.a $(OBJS)
$(BUILDDIR)/libfuse.a: $(OBJS)
$(AR) rcs $(BUILDDIR)/libfuse.a $(OBJS)
.PHONY: utils
utils: mergerfs-fusermount mount.mergerfs
build/mergerfs-fusermount: build/config.h util/fusermount.c lib/mount_util.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -Ilib -o build/mergerfs-fusermount util/fusermount.c lib/mount_util.c
$(BUILDDIR)/mergerfs-fusermount: util/fusermount.c lib/mount_util.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -Ilib -o $(BUILDDIR)/mergerfs-fusermount util/fusermount.c lib/mount_util.c
mergerfs-fusermount: build/mergerfs-fusermount
mergerfs-fusermount: $(BUILDDIR)/mergerfs-fusermount
build/mount.mergerfs: build/libfuse.a util/mount.mergerfs.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o build/mount.mergerfs util/mount.mergerfs.c build/libfuse.a $(LDFLAGS)
$(BUILDDIR)/mount.mergerfs: $(BUILDDIR)/libfuse.a util/mount.mergerfs.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o $(BUILDDIR)/mount.mergerfs util/mount.mergerfs.c $(BUILDDIR)/libfuse.a $(LDFLAGS)
mount.mergerfs: build/mount.mergerfs
mount.mergerfs: $(BUILDDIR)/mount.mergerfs
build/%.o: lib/%.c
$(BUILDDIR)/.objs/%.c.o: lib/%.c | $(BUILDDIR)/stamp
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
clean:
rm -rf build
$(BUILDDIR)/.objs/%.cpp.o: lib/%.cpp | $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
.PHONY: clean
clean:
$(RM) -rf $(BUILDDIR)
.PHONY: distclean
distclean: clean
.PHONY: strip
strip:
strip --strip-all build/mount.mergerfs
strip --strip-all build/mergerfs-fusermount
$(STRIP) --strip-all $(BUILDDIR)/mount.mergerfs
$(STRIP) --strip-all $(BUILDDIR)/mergerfs-fusermount
install-utils: mergerfs-fusermount mount.mergerfs strip
install -D build/mergerfs-fusermount "$(INSTALLBINDIR)/mergerfs-fusermount"
install -D build/mount.mergerfs "$(INSTALLSBINDIR)/mount.mergerfs"
chown root "$(INSTALLBINDIR)/mergerfs-fusermount"
chmod u+s "$(INSTALLBINDIR)/mergerfs-fusermount"
$(INSTALL) -D $(BUILDDIR)/mergerfs-fusermount "$(INSTALLBINDIR)/mergerfs-fusermount"
$(INSTALL) -D $(BUILDDIR)/mount.mergerfs "$(INSTALLSBINDIR)/mount.mergerfs"
$(CHOWN) root:root "$(INSTALLBINDIR)/mergerfs-fusermount"
$(CHMOD) u+s "$(INSTALLBINDIR)/mergerfs-fusermount"
install: $(INSTALLUTILS)
-16
View File
@@ -1,16 +0,0 @@
# ecfd: embeddable C feature discovery
This is a simple script and set of tests to help with what would traditionally be the `configure` step of most C/C++. It is intended to be simple to be embedded into a project and only depends on the C compiler and /bin/sh.
## USAGE
First copy `ecfd` into your project. Next simply call the build script which will run the tests and output a `config.` style file to `stdout`. Compile errors will output to `stderr` which can be used to understand why they aren't working.
```
$ ./ecfd/build > include/config.h
```
## TESTS
Each test is in a single module. The build script will try to compile each module and on success will execute the program. If anything is printed to `stdout` it will be appended to the `#define` printed to the build script's `stdout`. If the compilation fails nothing will be printed. Look at the tests directory for examples.
-32
View File
@@ -1,32 +0,0 @@
#!/bin/sh
PWD=$(pwd)
BASEPATH="${PWD}/${0%/*}"
CC=${CC:-cc}
OUTPUT="/dev/null"
echo "#ifndef CONFIG_H_INCLUDED"
echo "#define CONFIG_H_INCLUDED"
echo
IFS=
for file in "${BASEPATH}/tests/"*.c
do
binary="${file%.c}"
basename=$(basename ${binary})
${CC} -o "${binary}" "${file}" 1>&2
if [ $? -eq 0 ]; then
STDOUT=$(${binary})
if [ "${STDOUT}" != "" ]; then
echo "#define ${basename} ${STDOUT}"
else
echo "#define ${basename}"
fi
rm -f "${binary}"
fi
done
echo
echo "#endif"
-10
View File
@@ -1,10 +0,0 @@
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
(void)fork;
return 0;
}
-11
View File
@@ -1,11 +0,0 @@
#define _GNU_SOURCE
#include <fcntl.h>
int
main(int argc,
char *argv[])
{
(void)splice;
return 0;
}
@@ -1,14 +0,0 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int
main(int argc,
char *argv[])
{
struct stat st;
(void)st.st_atim;
return 0;
}
-11
View File
@@ -1,11 +0,0 @@
#include <fcntl.h>
#include <sys/stat.h>
int
main(int argc,
char *argv[])
{
(void)utimensat;
return 0;
}
-12
View File
@@ -1,12 +0,0 @@
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/uio.h>
int
main(int argc,
char *argv[])
{
(void)vmsplice;
return 0;
}
-4
View File
@@ -1,4 +0,0 @@
#pragma once
#define STATIC_ASSERT(condition) \
((void)sizeof(char[1 - 2*!(condition)]))
+220
View File
@@ -0,0 +1,220 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
* A dynamic list of formatting arguments with storage.
*
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, char_type>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_t = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty())
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.emplace_back(detail::unwrap(arg.value));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0] = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
operator basic_format_args<Context>() const {
return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
!named_info_.empty());
}
/**
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*
* Note that custom types and string types (but not string views) are copied
* into the store dynamically allocating memory if necessary.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
* Adds a reference to the argument into the dynamic store for later passing
* to a formatting function.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* char band[] = "Rolling Stones";
* store.push_back(std::cref(band));
* band[9] = 'c'; // Changing str affects the output.
* std::string result = fmt::vformat("{}", store);
* // result == "Rolling Scones"
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
/**
* Adds named argument into the dynamic store for later passing to a
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/// Erase all elements from the store.
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = {};
}
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+637
View File
@@ -0,0 +1,637 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
faint = 1 << 1,
italic = 1 << 2,
underline = 1 << 3,
blink = 1 << 4,
reverse = 1 << 5,
conceal = 1 << 6,
strikethrough = 1 << 7,
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
constexpr rgb() : r(0), g(0), b(0) {}
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
constexpr rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
constexpr rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace detail {
// A bit-packed variant of an RGB color, a terminal color, or unset color.
// see text_style for the bit-packing scheme.
struct color_type {
constexpr color_type() noexcept = default;
constexpr color_type(color rgb_color) noexcept
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
constexpr color_type(rgb rgb_color) noexcept
: color_type(static_cast<color>(
(static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
constexpr color_type(terminal_color term_color) noexcept
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
constexpr auto is_terminal_color() const noexcept -> bool {
return (value_ & (1 << 25)) != 0;
}
constexpr auto value() const noexcept -> uint32_t {
return value_ & 0xFFFFFF;
}
constexpr color_type(uint32_t value) noexcept : value_(value) {}
uint32_t value_ = 0;
};
} // namespace detail
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
// The information is packed as follows:
// ┌──┐
// │ 0│─┐
// │..│ ├── foreground color value
// │23│─┘
// ├──┤
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
// ├──┤
// │26│──── overflow bit, always zero (see below)
// ├──┤
// │27│─┐
// │..│ │
// │50│ │
// ├──┤ │
// │51│ ├── background color (same format as the foreground color)
// │52│ │
// ├──┤ │
// │53│─┘
// ├──┤
// │54│─┐
// │..│ ├── emphases
// │61│─┘
// ├──┤
// │62│─┬── unused
// │63│─┘
// └──┘
// The overflow bits are there to make operator|= efficient.
// When ORing, we must throw if, for either the foreground or background,
// one style specifies a terminal color and the other specifies any color
// (terminal or RGB); in other words, if one discriminator is 11 and the
// other is 11 or 01.
//
// We do that check by adding the styles. Consider what adding does to each
// possible pair of discriminators:
// 00 + 00 = 000
// 01 + 00 = 001
// 11 + 00 = 011
// 01 + 01 = 010
// 11 + 01 = 100 (!!)
// 11 + 11 = 110 (!!)
// In the last two cases, the ones we want to catch, the third bit——the
// overflow bit——is set. Bingo.
//
// We must take into account the possible carry bit from the bits
// before the discriminator. The only potentially problematic case is
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
// bit is impossible in that case, because 00 (unset color) means the
// 24 bits that precede the discriminator are all zero.
//
// This test can be applied to both colors simultaneously.
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: style_(static_cast<uint64_t>(em) << 54) {}
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
report_error("can't OR a terminal color");
style_ |= rhs.style_;
return *this;
}
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
-> text_style {
return lhs |= rhs;
}
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
return style_ == rhs.style_;
}
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
return !(*this == rhs);
}
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return (style_ & (1 << 24)) != 0;
}
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return (style_ & (1ULL << 51)) != 0;
}
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return (style_ >> 54) != 0;
}
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return style_ & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return (style_ >> 27) & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return static_cast<emphasis>(style_ >> 54);
}
private:
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
uint64_t style_ = 0;
};
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return foreground.value_;
}
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return static_cast<uint64_t>(background.value_) << 27;
}
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs;
}
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(color_type text_color,
const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code
// sequence.
if (text_color.is_terminal_color()) {
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value();
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value());
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) {
if (ts.has_emphasis()) {
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, fmt, args);
if (ts != text_style()) reset_color<Char>(buf);
}
} // namespace detail
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
* Formats a string and prints it to the specified file stream using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* Formats a string and prints it to stdout using ANSI escape sequences to
* specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
/**
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename... T>
inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
}
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
-> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
}
/**
* Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
}
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = detail::copy<Char>(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(arg.value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
* Returns an argument that will be formatted using ANSI escape sequences,
* to be used in a formatting function.
*
* **Example**:
*
* fmt::print("Elapsed time: {0:.2f} seconds",
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
* fmt::bg(fmt::color::blue)));
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
+539
View File
@@ -0,0 +1,539 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
// A compile-time string which is compiled into fast formatting code.
FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
* `constexpr if` compiler support.
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return detail::get<N - 1>(rest...);
}
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (is_static_named_arg<T>()) {
if (name == T::name) return N;
}
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter.
return -1;
}
# endif
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
# endif
(void)name;
return -1;
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type =
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
*out++ = value;
return out;
}
};
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
return write<Char>(out, arg);
}
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
}
};
template <typename Char> struct parse_arg_id_result {
arg_id_kind kind;
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
if constexpr (result.end >= str.size() || str[result.end] != '}') {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
}
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail
FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
remove_cvref_t<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
return buf.count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_
+5
View File
@@ -0,0 +1,5 @@
// This file is only provided for compatibility and may be removed in future
// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
// otherwise.
#include "format.h"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+427
View File
@@ -0,0 +1,427 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
# endif
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else
# define FMT_USE_FCNTL 0
# endif
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
/**
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
}
FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
format_args args);
/**
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... T>
auto windows_error(int error_code, string_view message, const T&... args)
-> std::system_error {
return vwindows_error(error_code, message, vargs<T...>{{args...}});
}
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
template <typename... T>
inline void print(string_view fmt, const T&... args) {
fmt::vargs<T...> vargs = {{args...}};
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class FMT_API file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
};
// Constructs a file object which doesn't represent any file.
inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
~file() noexcept;
// Returns the file descriptor.
inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
auto getpagesize() -> long;
namespace detail {
struct buffer_size {
constexpr buffer_size() = default;
size_t value = 0;
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
oflag = new_oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
// Intel has a bug that results in failure to deduce a constructor
// for empty parameter packs.
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
ostream_params(int new_oflag) : oflag(new_oflag) {}
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
# endif
};
} // namespace detail
FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
/// A fast buffered output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream : private detail::buffer<char> {
private:
file file_;
ostream(cstring_view path, const detail::ostream_params& params);
static void grow(buffer<char>& buf, size_t);
public:
ostream(ostream&& other) noexcept;
~ostream();
operator writer() {
detail::buffer<char>& buf = *this;
return buf;
}
inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
inline void close() {
flush();
file_.close();
}
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
}
};
/**
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_OS_H_
+167
View File
@@ -0,0 +1,167 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
#ifdef _MSVC_STL_UPDATE
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
# define FMT_MSVC_STL_UPDATE 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = make_unsigned_t<std::streamsize>;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete;
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
auto&& output = std::basic_ostream<Char>(&formatbuf);
output.imbue(std::locale::classic()); // The default is always unlocalized.
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
};
using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
/**
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
inline void vprint(std::ostream& os, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
FILE* f = nullptr;
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
}
}
#endif
detail::ignore_unused(f);
detail::write_buffer(os, buffer);
}
/**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::vargs<T...> vargs = {{args...}};
if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt.str, vargs);
detail::write_buffer(os, buffer);
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, FMT_STRING("{}\n"),
fmt::format(fmt, std::forward<T>(args)...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_
+633
View File
@@ -0,0 +1,633 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
};
namespace detail {
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
template <>
inline auto find<false, char>(const char* first, const char* last, char value,
const char*& out) -> bool {
out =
static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
return out != nullptr;
}
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
inline static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int {
report_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
if (is_signed)
arg_ = static_cast<int>(static_cast<target_type>(value));
else
arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
} else {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
if (is_signed)
arg_ = static_cast<long long>(value);
else
arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = static_cast<typename Context::char_type>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
auto operator()(const Char* s) -> const Char* { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class printf_width_handler {
private:
format_specs& specs_;
public:
inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.set_align(align::left);
width = 0 - width;
}
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned {
report_error("width is not integer");
return 0;
}
};
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
template <typename T> void write(T value) {
detail::write<Char>(this->out, value, this->specs, this->locale);
}
public:
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { write(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (!std::is_same<T, Char>::value) {
write(value);
return;
}
format_specs s = this->specs;
if (s.type() != presentation_type::none &&
s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.set_sign(sign::none);
s.clear_alt();
s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align() == align::none || s.align() == align::numeric)
s.set_align(align::right);
detail::write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
write(value);
}
void operator()(const char* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(const wchar_t* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { write(value); }
void operator()(const void* value) {
if (value)
write(value);
else
write_null_pointer();
}
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
template <typename Char>
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-': specs.set_align(align::left); break;
case '+': specs.set_sign(sign::plus); break;
case '0': specs.set_fill('0'); break;
case ' ':
if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break;
case '#': specs.set_alt(); break;
default: return;
}
}
}
template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
int value = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.set_fill('0');
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd': return in(t, integral_set) ? pt::dec : pt::none;
case 'o': return in(t, integral_set) ? pt::oct : pt::none;
case 'X': upper = true; FMT_FALLTHROUGH;
case 'x': return in(t, integral_set) ? pt::hex : pt::none;
case 'E': upper = true; FMT_FALLTHROUGH;
case 'e': return in(t, float_set) ? pt::exp : pt::none;
case 'F': upper = true; FMT_FALLTHROUGH;
case 'f': return in(t, float_set) ? pt::fixed : pt::none;
case 'G': upper = true; FMT_FALLTHROUGH;
case 'g': return in(t, float_set) ? pt::general : pt::none;
case 'A': upper = true; FMT_FALLTHROUGH;
case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c': return in(t, integral_set) ? pt::chr : pt::none;
case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default: return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!find<false, Char>(it, end, '%', it)) {
it = end; // find leaves it == nullptr if it doesn't find '%'.
break;
}
Char c = *it++;
if (it != end && *it == c) {
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs();
specs.set_align(align::right);
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
}
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
specs.set_fill(' ');
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = sv;
}
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
if (specs.fill_unit<Char>() == '0') {
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
specs.set_align(align::numeric);
} else {
// Ignore '0' flag for non-numeric types or if '-' flag is also present.
specs.set_fill(' ');
}
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j': convert_arg<intmax_t>(arg, t); break;
case 'z': convert_arg<size_t>(arg, t); break;
case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default: --it; convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (is_integral_type(arg.type())) {
// Normalize type.
switch (type) {
case 'i':
case 'u': type = 'd'; break;
case 'c':
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
bool upper = false;
specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
if (upper) specs.set_upper();
start = it;
// Format argument.
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
} // namespace detail
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename Char>
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return {buf.data(), buf.size()};
}
/**
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_
+850
View File
@@ -0,0 +1,850 @@
// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
// ADL overloads. Only participate in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(*detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U, typename V = typename std::remove_cv<U>::type>
static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto all_true(...) -> std::false_type;
template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
template <typename Tuple, typename F, size_t... Is>
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get;
// Using a free function get<Is>(Tuple) now.
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
ignore_unused(unused);
}
template <typename Tuple, typename F>
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
std::forward<Tuple>(t), std::forward<F>(f));
}
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
}
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range>
using range_reference_type =
decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Formatter>
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
-> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
}
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11.
template <typename Char> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
parse_context<Char>& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx));
++i;
}
int i;
FormatContext& ctx;
basic_string_view<char_type> separator;
};
} // namespace detail
template <typename T> struct is_tuple_like {
static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<Tuple, Char>::value>> {
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{};
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && detail::to_ascii(*it) == 'n') {
++it;
set_brackets({}, {});
set_separator({});
}
if (it != end && *it != '}') report_error("invalid format specifier");
ctx.advance_to(it);
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy<Char>(closing_bracket_, ctx.out());
}
};
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
namespace detail {
template <typename Char, typename Element>
using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
template <typename T, typename Char>
struct range_formatter<
T, Char,
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
is_formattable<T, Char>>::value>> {
private:
detail::range_formatter_type<Char, T> underlying_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public:
FMT_CONSTEXPR range_formatter() {}
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
return underlying_;
}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {});
++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it;
}
ctx.advance_to(it);
return underlying_.parse(ctx);
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it; // Need an lvalue
out = underlying_.format(item, ctx);
++i;
}
out = detail::copy<Char>(closing_bracket_, out);
return out;
}
};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
is_range<T, Char>::value, detail::range_format_kind_<T>,
std::integral_constant<range_format, range_format::disabled>> {};
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
};
// A map formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<range_format_kind<R, Char>::value == range_format::map>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
-> const Char* {
return ctx.begin();
}
template <size_t N>
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>)
-> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
report_error("incompatible format specs for tuple elements");
}
#endif
return end;
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& v) -> all {
return {v.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(value), ctx);
}
};
FMT_BEGIN_EXPORT
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{1, 2, 3};
* fmt::print("{}", fmt::join(v, ", "));
* // Output: 1, 2, 3
*
* `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
* auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep};
}
/**
* Returns an object that formats `std::initializer_list` with elements
* separated by `sep`.
*
* **Example**:
*
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
+728
View File
@@ -0,0 +1,728 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>) && \
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
Char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return this->write_padded(ctx, writer{bs});
}
};
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
is_formattable<T, Char>::value) &&
is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
if constexpr (!std::is_void<T>::value)
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
template <typename Char> struct formatter<std::monostate, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
bool debug_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
char c = *it;
if (it != end && ((c >= '0' && c <= '9') || c == '{'))
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && *it == 's') {
specs_.set_type(presentation_type::string);
++it;
}
return it;
}
template <typename FormatContext>
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto buf = memory_buffer();
if (specs_.type() == presentation_type::string) {
buf.append(ec.message());
} else {
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
}
auto quoted = memory_buffer();
auto str = string_view(buf.data(), buf.size());
if (debug_) {
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
str = string_view(quoted.data(), quoted.size());
}
return detail::write<char>(ctx.out(), str, specs);
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.set_sign(sign::plus);
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.dynamic()) {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.copy_fill_from(specs);
outer_specs.set_align(specs.align());
specs.width = 0;
specs.set_fill({});
specs.set_align(align::none);
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_
+369
View File
@@ -0,0 +1,369 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include "color.h"
#include "format.h"
#include "ostream.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if FMT_USE_LOCALE
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<
S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#if FMT_USE_LOCALE
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = parse_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
template <typename Char, typename... T> struct basic_fstring {
private:
basic_string_view<Char> str_;
static constexpr int num_static_named_args =
detail::count_static_named_args<T...>();
using checker = detail::format_string_checker<
Char, static_cast<int>(sizeof...(T)), num_static_named_args,
num_static_named_args != detail::count_named_args<T...>()>;
using arg_pack = detail::arg_pack<T...>;
public:
using t = basic_fstring;
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<Char>(s, checker(s, arg_pack()));
}
template <typename S,
FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
std::is_same<typename S::char_type, Char>::value)>
FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
FMT_CONSTEXPR int ignore =
(parse_format_string(sv, checker(sv, arg_pack())), 0);
detail::ignore_unused(ignore);
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
template <typename Char, typename... T>
using basic_format_string = basic_fstring<Char, T...>;
template <typename... T>
using wformat_string = typename basic_format_string<wchar_t, T...>::t;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
}
#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
return {s};
}
} // namespace literals
#endif
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, fmt, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
auto buf = wmemory_buffer();
detail::vformat_to(buf, fmt, args);
buf.push_back(L'\0');
if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);
detail::write_buffer(os, buffer);
}
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>
void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace fs
{
struct dirent64
{
public:
uint64_t ino;
int64_t off;
uint16_t reclen;
uint8_t type;
char name[];
public:
size_t
namelen() const
{
return ::strlen(name);
}
};
}
+104 -240
View File
@@ -9,23 +9,9 @@
#ifndef _FUSE_H_
#define _FUSE_H_
/** @file
*
* This file defines the library interface of FUSE
*
* IMPORTANT: you should define FUSE_USE_VERSION before including this
* header. To use the newest API define it to 26 (recommended for any
* new application), to use the old API define it to 21 (default) 22
* or 25, to use the even older 1.X API define it to 11.
*/
#ifndef FUSE_USE_VERSION
#define FUSE_USE_VERSION 21
#endif
#include "extern_c.h"
#include "fuse_common.h"
#include "fuse_dirents.h"
#include "fuse_kernel.h"
#include <fcntl.h>
#include <time.h>
@@ -34,6 +20,7 @@
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>
#include <stdint.h>
EXTERN_C_BEGIN
@@ -47,6 +34,9 @@ struct fuse;
/** Structure containing a raw command */
struct fuse_cmd;
struct fuse_dirents_t;
typedef struct fuse_dirents_t fuse_dirents_t;
/**
* The file system operations:
*
@@ -105,15 +95,6 @@ struct fuse_operations
* */
int (*mkdir) (const char *, mode_t);
/** Hide files unlinked / renamed over
*
* Allows storing of a file handle when a file is unlinked
* while open. Helps manage the fact the kernel usually does
* not send fh with getattr requests.
*/
int (*prepare_hide)(const char *name_, uint64_t *fh_);
int (*free_hide)(const uint64_t fh_);
/** Remove a file */
int (*unlink) (const char *);
@@ -121,31 +102,25 @@ struct fuse_operations
int (*rmdir) (const char *);
/** Create a symbolic link */
int (*symlink) (const char *, const char *);
int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Rename a file */
int (*rename) (const char *, const char *);
/** Create a hard link to a file */
int (*link) (const char *, const char *);
int (*link) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Change the permission bits of a file */
int (*chmod) (const char *, mode_t);
int (*fchmod)(const struct fuse_file_info *, const mode_t);
int (*fchmod)(const uint64_t, const mode_t);
/** Change the owner and group of a file */
int (*chown) (const char *, uid_t, gid_t);
int (*fchown)(const struct fuse_file_info *, const uid_t, const gid_t);
int (*fchown)(const uint64_t, const uid_t, const gid_t);
/** Change the size of a file */
int (*truncate) (const char *, off_t);
/** Change the access and/or modification times of a file
*
* Deprecated, use utimens() instead.
*/
int (*utime) (const char *, struct utimbuf *);
/** File open operation
*
* No creation (O_CREAT, O_EXCL) and by default also no
@@ -163,32 +138,7 @@ struct fuse_operations
*
* Changed in version 2.2
*/
int (*open) (const char *, struct fuse_file_info *);
/** Read data from an open file
*
* Read should return exactly the number of bytes requested except
* on EOF or error, otherwise the rest of the data will be
* substituted with zeroes. An exception to this is when the
* 'direct_io' mount option is specified, in which case the return
* value of the read system call will reflect the return value of
* this operation.
*
* Changed in version 2.2
*/
int (*read) (char *, size_t, off_t,
struct fuse_file_info *);
/** Write data to an open file
*
* Write should return exactly the number of bytes requested
* except on error. An exception to this is when the 'direct_io'
* mount option is specified (see read operation).
*
* Changed in version 2.2
*/
int (*write) (const char *, size_t, off_t,
struct fuse_file_info *);
int (*open) (const char *, fuse_file_info_t *);
/** Get file system statistics
*
@@ -222,7 +172,7 @@ struct fuse_operations
*
* Changed in version 2.2
*/
int (*flush) (struct fuse_file_info *);
int (*flush) (const fuse_file_info_t *);
/** Release an open file
*
@@ -238,7 +188,7 @@ struct fuse_operations
*
* Changed in version 2.2
*/
int (*release) (struct fuse_file_info *);
int (*release) (const fuse_file_info_t *);
/** Synchronize file contents
*
@@ -247,7 +197,7 @@ struct fuse_operations
*
* Changed in version 2.2
*/
int (*fsync) (int, struct fuse_file_info *);
int (*fsync) (const uint64_t, int);
/** Set extended attributes */
int (*setxattr) (const char *, const char *, const char *, size_t, int);
@@ -271,7 +221,8 @@ struct fuse_operations
*
* Introduced in version 2.3
*/
int (*opendir) (const char *, struct fuse_file_info *);
int (*opendir) (const char *,
fuse_file_info_t *);
/** Read directory
*
@@ -294,10 +245,10 @@ struct fuse_operations
*
* Introduced in version 2.3
*/
int (*readdir)(struct fuse_file_info *,
int (*readdir)(const fuse_file_info_t *,
fuse_dirents_t *);
int (*readdir_plus)(struct fuse_file_info *,
int (*readdir_plus)(const fuse_file_info_t *,
fuse_dirents_t *);
@@ -305,7 +256,7 @@ struct fuse_operations
*
* Introduced in version 2.3
*/
int (*releasedir) (struct fuse_file_info *);
int (*releasedir) (const fuse_file_info_t *);
/** Synchronize directory contents
*
@@ -314,7 +265,7 @@ struct fuse_operations
*
* Introduced in version 2.3
*/
int (*fsyncdir) (int, struct fuse_file_info *);
int (*fsyncdir) (const fuse_file_info_t *, int);
/**
* Initialize filesystem
@@ -335,7 +286,7 @@ struct fuse_operations
*
* Introduced in version 2.3
*/
void (*destroy) (void *);
void (*destroy) (void);
/**
* Check file access permissions
@@ -362,7 +313,7 @@ struct fuse_operations
*
* Introduced in version 2.5
*/
int (*create) (const char *, mode_t, struct fuse_file_info *);
int (*create) (const char *, mode_t, fuse_file_info_t *);
/**
* Change the size of an open file
@@ -376,7 +327,7 @@ struct fuse_operations
*
* Introduced in version 2.5
*/
int (*ftruncate) (off_t, struct fuse_file_info *);
int (*ftruncate) (const uint64_t, off_t);
/**
* Get attributes from an open file
@@ -390,7 +341,7 @@ struct fuse_operations
*
* Introduced in version 2.5
*/
int (*fgetattr) (struct stat *, struct fuse_file_info *, fuse_timeouts_t *);
int (*fgetattr) (const uint64_t, struct stat *, fuse_timeouts_t *);
/**
* Perform POSIX file locking operation
@@ -424,7 +375,8 @@ struct fuse_operations
*
* Introduced in version 2.6
*/
int (*lock) (struct fuse_file_info *, int cmd,
int (*lock) (const fuse_file_info_t *,
int cmd,
struct flock *);
/**
@@ -439,7 +391,7 @@ struct fuse_operations
* Introduced in version 2.6
*/
int (*utimens)(const char *, const struct timespec tv[2]);
int (*futimens)(const struct fuse_file_info *ffi_, const struct timespec tv_[2]);
int (*futimens)(const uint64_t fh, const struct timespec tv[2]);
/**
* Map block index within file to block index within device
@@ -466,12 +418,12 @@ struct fuse_operations
*
* Introduced in version 2.8
*/
int (*ioctl) (unsigned long cmd,
void *arg,
struct fuse_file_info *ffi,
unsigned int flags,
void *data,
uint32_t *out_bufsz);
int (*ioctl) (const fuse_file_info_t *ffi,
unsigned long cmd,
void *arg,
unsigned int flags,
void *data,
uint32_t *out_bufsz);
/**
* Poll for IO readiness events
@@ -490,8 +442,9 @@ struct fuse_operations
*
* Introduced in version 2.8
*/
int (*poll) (struct fuse_file_info *,
struct fuse_pollhandle *ph, unsigned *reventsp);
int (*poll) (const fuse_file_info_t *ffi,
fuse_pollhandle_t *ph,
unsigned *reventsp);
/** Write contents of buffer to an open file
*
@@ -501,8 +454,10 @@ struct fuse_operations
*
* Introduced in version 2.9
*/
int (*write_buf) (struct fuse_bufvec *buf, off_t off,
struct fuse_file_info *);
int (*write) (const fuse_file_info_t *ffi,
const char *data,
size_t size,
off_t off);
/** Store data from an open file in a buffer
*
@@ -520,8 +475,10 @@ struct fuse_operations
*
* Introduced in version 2.9
*/
int (*read_buf) (struct fuse_bufvec **bufp,
size_t size, off_t off, struct fuse_file_info *);
int (*read)(const fuse_file_info_t *ffi,
char *buf,
size_t size,
off_t off);
/**
* Perform BSD file locking operation
*
@@ -542,7 +499,7 @@ struct fuse_operations
*
* Introduced in version 2.9
*/
int (*flock) (struct fuse_file_info *, int op);
int (*flock) (const fuse_file_info_t *, int op);
/**
* Allocates space for an open file
@@ -554,7 +511,7 @@ struct fuse_operations
*
* Introduced in version 2.9.1
*/
int (*fallocate) (int, off_t, off_t,struct fuse_file_info *);
int (*fallocate) (const uint64_t, int, off_t, off_t);
/**
* Copy a range of data from one file to another
@@ -569,12 +526,32 @@ struct fuse_operations
* destination. Effectively doing an inefficient copy of the
* data.
*/
ssize_t (*copy_file_range)(struct fuse_file_info *fi_in,
off_t offset_in,
struct fuse_file_info *fi_out,
off_t offset_out,
size_t size,
int flags);
ssize_t (*copy_file_range)(const fuse_file_info_t *src_fi,
off_t src_off,
const fuse_file_info_t *dst_fi,
off_t dst_off,
const size_t size,
const unsigned int flags);
ssize_t (*setupmapping)(uint64_t *fh_,
uint64_t foffset_,
uint64_t len_,
uint64_t flags_,
uint64_t moffset_);
int (*removemapping)();
int (*syncfs)();
int (*tmpfile)(const char *, mode_t, fuse_file_info_t *);
int (*statx)(const char *fusepath,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
int (*statx_fh)(const uint64_t fh,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
};
/** Extra context that may be needed by some filesystems
@@ -584,23 +561,14 @@ struct fuse_operations
*/
struct fuse_context
{
/** Pointer to the fuse object */
uint64_t unique;
uint64_t nodeid;
uint32_t opcode;
uid_t uid;
gid_t gid;
pid_t pid;
mode_t umask;
struct fuse *fuse;
/** User ID of the calling process */
uid_t uid;
/** Group ID of the calling process */
gid_t gid;
/** Thread ID of the calling process */
pid_t pid;
/** Private filesystem data */
void *private_data;
/** Umask of the calling process (introduced in version 2.8) */
mode_t umask;
};
/**
@@ -623,15 +591,13 @@ struct fuse_context
* @param argc the argument counter passed to the main() function
* @param argv the argument vector passed to the main() function
* @param op the file system operation
* @param user_data user data supplied in the context during the init() method
* @return 0 on success, nonzero on failure
*/
/*
int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
void *user_data);
int fuse_main(int argc, char *argv[], const struct fuse_operations *op);
*/
#define fuse_main(argc, argv, op, user_data) \
fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
#define fuse_main(argc, argv, op) \
fuse_main_real(argc, argv, op, sizeof(*(op)))
/* ----------------------------------------------------------- *
* More detailed API *
@@ -644,12 +610,10 @@ struct fuse_context
* @param args argument vector
* @param op the filesystem operations
* @param op_size the size of the fuse_operations structure
* @param user_data user data supplied in the context during the init() method
* @return the created FUSE handle
*/
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op, size_t op_size,
void *user_data);
const struct fuse_operations *op, size_t op_size);
/**
* Destroy the FUSE handle.
@@ -670,7 +634,10 @@ void fuse_destroy(struct fuse *f);
*/
void fuse_exit(struct fuse *f);
int fuse_config_num_threads(const struct fuse *fuse_);
int fuse_config_read_thread_count(const struct fuse *f);
int fuse_config_process_thread_count(const struct fuse *f);
int fuse_config_process_thread_queue_depth(const struct fuse *f);
const char* fuse_config_pin_threads(const struct fuse *f);
/**
* FUSE event loop with multiple threads
@@ -719,25 +686,13 @@ int fuse_is_lib_option(const char *opt);
*
* Do not call this directly, use fuse_main()
*/
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
size_t op_size, void *user_data);
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size);
/**
* Start the cleanup thread when using option "remember".
*
* This is done automatically by fuse_loop_mt()
* @param fuse struct fuse pointer for fuse instance
* @return 0 on success and -1 on error
*/
int fuse_start_cleanup_thread(struct fuse *fuse);
void fuse_populate_maintenance_thread(struct fuse *fuse);
int fuse_log_metrics_get(void);
void fuse_log_metrics_set(int enabled);
/**
* Stop the cleanup thread when using option "remember".
*
* This is done automatically by fuse_loop_mt()
* @param fuse struct fuse pointer for fuse instance
*/
void fuse_stop_cleanup_thread(struct fuse *fuse);
/**
* Iterate over cache removing stale entries
@@ -770,99 +725,7 @@ struct fuse_fs;
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
*/
int fuse_fs_getattr(struct fuse_fs *fs,
const char *path,
struct stat *buf,
fuse_timeouts_t *timeout);
int fuse_fs_fgetattr(struct fuse_fs *fs,
struct stat *buf,
struct fuse_file_info *fi,
fuse_timeouts_t *timeout);
int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
const char *newpath);
int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
const char *path);
int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
int fuse_fs_release(struct fuse_fs *fs,
struct fuse_file_info *fi);
int fuse_fs_open(struct fuse_fs *fs, const char *path,
struct fuse_file_info *fi);
int fuse_fs_read(struct fuse_fs *fs, char *buf, size_t size,
off_t off, struct fuse_file_info *fi);
int fuse_fs_read_buf(struct fuse_fs *fs,
struct fuse_bufvec **bufp, size_t size, off_t off,
struct fuse_file_info *fi);
int fuse_fs_write(struct fuse_fs *fs, const char *buf,
size_t size, off_t off, struct fuse_file_info *fi);
int fuse_fs_write_buf(struct fuse_fs *fs,
struct fuse_bufvec *buf, off_t off,
struct fuse_file_info *fi);
int fuse_fs_fsync(struct fuse_fs *fs, int datasync,
struct fuse_file_info *fi);
int fuse_fs_flush(struct fuse_fs *fs,
struct fuse_file_info *fi);
int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
struct fuse_file_info *fi);
int fuse_fs_readdir(struct fuse_fs *fs,
struct fuse_file_info *fi,
fuse_dirents_t *buf);
int fuse_fs_fsyncdir(struct fuse_fs *fs, int datasync,
struct fuse_file_info *fi);
int fuse_fs_releasedir(struct fuse_fs *fs,
struct fuse_file_info *fi);
int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
struct fuse_file_info *fi);
int fuse_fs_lock(struct fuse_fs *fs,
struct fuse_file_info *fi, int cmd, struct flock *lock);
int fuse_fs_flock(struct fuse_fs *fs,
struct fuse_file_info *fi, int op);
int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode);
int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size);
int fuse_fs_ftruncate(struct fuse_fs *fs, off_t size,
struct fuse_file_info *fi);
int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
const struct timespec tv[2]);
int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
size_t len);
int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
dev_t rdev);
int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
const char *value, size_t size, int flags);
int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
char *value, size_t size);
int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
size_t size);
int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
const char *name);
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
uint64_t *idx);
int fuse_fs_ioctl(struct fuse_fs *fs, unsigned long cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
void *data, uint32_t *out_bufsz);
int fuse_fs_poll(struct fuse_fs *fs,
struct fuse_file_info *fi, struct fuse_pollhandle *ph,
unsigned *reventsp);
int fuse_fs_fallocate(struct fuse_fs *fs, int mode,
off_t offset, off_t length, struct fuse_file_info *fi);
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
void fuse_fs_destroy(struct fuse_fs *fs);
int fuse_fs_prepare_hide(struct fuse_fs *fs, const char *path, uint64_t *fh);
int fuse_fs_free_hide(struct fuse_fs *fs, uint64_t fh);
ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs,
struct fuse_file_info *fi_in, off_t off_in,
struct fuse_file_info *fi_out, off_t off_out,
size_t len, int flags);
int fuse_notify_poll(struct fuse_pollhandle *ph);
int fuse_notify_poll(fuse_pollhandle_t *ph);
/**
* Create a new fuse filesystem object
@@ -872,11 +735,9 @@ int fuse_notify_poll(struct fuse_pollhandle *ph);
*
* @param op the filesystem operations
* @param op_size the size of the fuse_operations structure
* @param user_data user data supplied in the context during the init() method
* @return a new filesystem object
*/
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
void *user_data);
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size);
/* ----------------------------------------------------------- *
* Advanced API for event handling, don't worry about this... *
@@ -891,18 +752,11 @@ typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
/** This is the part of fuse_main() before the event loop */
struct fuse *fuse_setup(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
char **mountpoint,
void *user_data);
char **mountpoint);
/** This is the part of fuse_main() after the event loop */
void fuse_teardown(struct fuse *fuse, char *mountpoint);
/** Read a single command. If none are read, return NULL */
struct fuse_cmd *fuse_read_cmd(struct fuse *f);
/** Process a single command */
void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
/** Multi threaded event loop, which calls the custom command
processor function */
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
@@ -917,6 +771,16 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
/** Get session from fuse object */
struct fuse_session *fuse_get_session(struct fuse *f);
void fuse_gc1();
void fuse_gc();
void fuse_invalidate_all_nodes();
int fuse_get_dev_fuse_fd(const struct fuse_context *fc);
int fuse_passthrough_open(const struct fuse_context *fc,
const int fd);
int fuse_passthrough_close(const struct fuse_context *fc,
const int backing_id);
EXTERN_C_END
#endif /* _FUSE_H_ */
+40 -70
View File
@@ -37,7 +37,7 @@
#endif
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
#define FUSE_MAX_MAX_PAGES 256
#define FUSE_DEFAULT_MAX_MAX_PAGES 256
EXTERN_C_BEGIN
@@ -46,8 +46,8 @@ EXTERN_C_BEGIN
*
* Changed in version 2.5
*/
struct
fuse_file_info
typedef struct fuse_file_info_t fuse_file_info_t;
struct fuse_file_info_t
{
/** Open flags. Available in open() and release() */
int flags;
@@ -84,10 +84,17 @@ fuse_file_info
uint32_t auto_cache : 1;
uint32_t parallel_direct_writes:1;
uint32_t noflush:1;
uint32_t passthrough:1;
/** File handle. May be filled in by filesystem in open().
Available in all other file operations */
uint64_t fh;
int32_t backing_id;
/** Lock owner id. Available in locking operations and flush */
uint64_t lock_owner;
};
@@ -101,31 +108,32 @@ fuse_file_info
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
* FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
* FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
* FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
*/
#define FUSE_CAP_ASYNC_READ (1 << 0)
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
#define FUSE_CAP_BIG_WRITES (1 << 5)
#define FUSE_CAP_DONT_MASK (1 << 6)
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
#define FUSE_CAP_SPLICE_READ (1 << 9)
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
#define FUSE_CAP_IOCTL_DIR (1 << 11)
#define FUSE_CAP_READDIR_PLUS (1 << 13)
#define FUSE_CAP_READDIR_PLUS_AUTO (1 << 14)
#define FUSE_CAP_ASYNC_DIO (1 << 15)
#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
#define FUSE_CAP_POSIX_ACL (1 << 19)
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
#define FUSE_CAP_MAX_PAGES (1 << 21)
#define FUSE_CAP_ASYNC_READ (1ULL << 0)
#define FUSE_CAP_POSIX_LOCKS (1ULL << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1ULL << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1ULL << 4)
#define FUSE_CAP_BIG_WRITES (1ULL << 5)
#define FUSE_CAP_DONT_MASK (1ULL << 6)
#define FUSE_CAP_FLOCK_LOCKS (1ULL << 10)
#define FUSE_CAP_IOCTL_DIR (1ULL << 11)
#define FUSE_CAP_READDIR_PLUS (1ULL << 13)
#define FUSE_CAP_READDIR_PLUS_AUTO (1ULL << 14)
#define FUSE_CAP_ASYNC_DIO (1ULL << 15)
#define FUSE_CAP_WRITEBACK_CACHE (1ULL << 16)
#define FUSE_CAP_PARALLEL_DIROPS (1ULL << 18)
#define FUSE_CAP_POSIX_ACL (1ULL << 19)
#define FUSE_CAP_CACHE_SYMLINKS (1ULL << 20)
#define FUSE_CAP_MAX_PAGES (1ULL << 21)
#define FUSE_CAP_SETXATTR_EXT (1ULL << 22)
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1ULL << 23)
#define FUSE_CAP_CREATE_SUPP_GROUP (1ULL << 24)
#define FUSE_CAP_PASSTHROUGH (1ULL << 25)
#define FUSE_CAP_HANDLE_KILLPRIV (1ULL << 26)
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1ULL << 27)
#define FUSE_CAP_ALLOW_IDMAP (1ULL << 28)
/**
* Ioctl flags
@@ -151,61 +159,23 @@ fuse_file_info
* indicate the value requested by the filesystem. The requested
* value must usually be smaller than the indicated value.
*/
struct fuse_conn_info {
/**
* Major version of the protocol (read-only)
*/
struct fuse_conn_info
{
unsigned proto_major;
/**
* Minor version of the protocol (read-only)
*/
unsigned proto_minor;
/**
* Maximum size of the write buffer
*/
unsigned max_write;
/**
* Maximum readahead
*/
unsigned max_readahead;
/**
* Capability flags, that the kernel supports
*/
unsigned capable;
/**
* Capability flags, that the filesystem wants to enable
*/
unsigned want;
/**
* Maximum number of backgrounded requests
*/
uint64_t capable;
uint64_t want;
unsigned max_background;
/**
* Kernel congestion threshold parameter
*/
unsigned congestion_threshold;
/**
* Max pages
*/
uint16_t max_pages;
/**
* For future use.
*/
unsigned reserved[22];
};
struct fuse_session;
struct fuse_chan;
struct fuse_pollhandle;
struct fuse_pollhandle_t;
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
/**
* Create a FUSE mountpoint
@@ -271,7 +241,7 @@ int fuse_version(void);
*
* @param ph the poll handle
*/
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
/* ----------------------------------------------------------- *
* Data buffer *
+31
View File
@@ -0,0 +1,31 @@
/*
ISC License
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
int fuse_config_get_read_thread_count();
int fuse_config_get_process_thread_count();
int fuse_config_get_process_thread_queue_depth();
std::string fuse_config_get_pin_threads();
void fuse_config_set_read_thread_count(int const);
void fuse_config_set_process_thread_count(int const);
void fuse_config_set_process_thread_queue_depth(int const);
void fuse_config_set_pin_threads(std::string const);
-82
View File
@@ -1,82 +0,0 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "extern_c.h"
EXTERN_C_BEGIN
#include "kvec.h"
#include "fuse_dirent.h"
#include "fuse_direntplus.h"
#include "fuse_entry.h"
#include "linux_dirent64.h"
#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
enum fuse_dirents_type_e
{
UNSET = 0,
NORMAL,
PLUS
};
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
typedef struct fuse_dirents_s fuse_dirents_t;
struct fuse_dirents_s
{
char *buf;
uint64_t buf_len;
uint64_t data_len;
kvec_t(uint32_t) offs;
fuse_dirents_type_t type;
};
int fuse_dirents_init(fuse_dirents_t *d);
void fuse_dirents_free(fuse_dirents_t *d);
void fuse_dirents_reset(fuse_dirents_t *d);
int fuse_dirents_add(fuse_dirents_t *d,
const struct dirent *de,
const uint64_t namelen);
int fuse_dirents_add_plus(fuse_dirents_t *d,
const struct dirent *de,
const uint64_t namelen,
const fuse_entry_t *entry,
const struct stat *st);
int fuse_dirents_add_linux(fuse_dirents_t *d,
const struct linux_dirent64 *de,
const uint64_t namelen);
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
const struct linux_dirent64 *de,
const uint64_t namelen,
const fuse_entry_t *entry,
const struct stat *st);
void *fuse_dirents_find(fuse_dirents_t *d,
const uint64_t ino);
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
EXTERN_C_END

Some files were not shown because too many files have changed in this diff Show More