├── .gitignore ├── .travis.yml ├── ChangeLog ├── INSTALL ├── LICENSE ├── Makefile.am ├── README.md ├── TODO.md ├── build ├── README.md └── duc.spec ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright ├── duc-nox.examples ├── duc-nox.install ├── duc-nox.links ├── duc-nox.manpages ├── duc.examples ├── duc.install ├── duc.manpages ├── rules ├── source │ └── format └── watch ├── doc ├── Makefile ├── duc.1 ├── duc.1.html ├── duc.md └── manual.txt ├── examples ├── duc.css ├── ducrc ├── footer.htm ├── header.htm └── index.cgi ├── gentoo ├── duc-1.4.1.ebuild ├── duc-9999.ebuild └── metadata.xml ├── img ├── duc.png ├── example.png ├── fuzz-off.png ├── fuzz-on.png ├── palette-greyscale.png ├── palette-monochrome.png ├── palette-rainbow.png ├── palette-size.png └── ui.png ├── src ├── duc │ ├── cmd-cgi.c │ ├── cmd-graph.c │ ├── cmd-gui.c │ ├── cmd-guigl.c │ ├── cmd-histogram.c │ ├── cmd-index.c │ ├── cmd-info.c │ ├── cmd-json.c │ ├── cmd-ls.c │ ├── cmd-topn.c │ ├── cmd-ui.c │ ├── cmd-xml.c │ ├── cmd.h │ ├── ducrc.c │ ├── ducrc.h │ └── main.c ├── glad │ ├── KHR │ │ └── khrplatform.h │ ├── glad.c │ └── glad │ │ └── glad.h ├── libduc-graph │ ├── duc-graph.h │ ├── font.c │ ├── graph-cairo.c │ ├── graph-html.c │ ├── graph-opengl.c │ ├── graph-private.h │ ├── graph-svg.c │ └── graph.c └── libduc │ ├── buffer.c │ ├── buffer.h │ ├── canonicalize.c │ ├── db-kyoto.c │ ├── db-leveldb.c │ ├── db-lmdb.c │ ├── db-sqlite3.c │ ├── db-tkrzw.c │ ├── db-tokyo.c │ ├── db.c │ ├── db.h │ ├── dir.c │ ├── duc.c │ ├── duc.h │ ├── index.c │ ├── list.c │ ├── list.h │ ├── private.h │ ├── uthash.h │ ├── utlist.h │ ├── utstring.h │ ├── varint.c │ └── varint.h ├── test.sh ├── testing └── dbs │ ├── kyotocabinet.db │ ├── lmdb.db │ ├── sqlite3.db │ └── tokyocabinet.db ├── todo └── valgrind-suppressions /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.la 4 | *.lo 5 | *.swp 6 | *.rej 7 | *.orig 8 | /aclocal.m4 9 | /autom4te.cache/ 10 | /config.* 11 | /compile 12 | /config.status 13 | /Makefile.in 14 | /Makefile 15 | /configure 16 | /libtool 17 | /ltmain.sh 18 | /missing 19 | /stamp-h1 20 | /install-sh 21 | /depcomp 22 | /test 23 | /go 24 | .deps/ 25 | .dirstamp 26 | /duc 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | os: 3 | - linux 4 | compiler: 5 | - gcc 6 | - clang 7 | before_install: 8 | - sudo apt-get update -qq 9 | - sudo apt-get install -qq libcairo2-dev libpango1.0-dev libtokyocabinet-dev imagemagick jp2a libgles2-mesa-dev 10 | script: 11 | - autoreconf -i 12 | - ./configure 13 | - make 14 | - sudo make install 15 | - sudo ldconfig 16 | - sudo duc index -x / 17 | - sudo duc ls /usr 18 | - sudo duc graph / 19 | - identify duc.png 20 | - convert duc.png duc.jpg 21 | - jp2a duc.jpg 22 | - ls -l /dev/stdin /dev/stdout /dev/stderr 23 | - ls -l /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 24 | 25 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.5.0-rc1 (2024-09-03) 2 | - new: added support for tkrzw backend DB and made it the default 3 | - this DB is newer and under active support compared to 4 | TokyoCabinet, KyotoCabinet, etc. 5 | - also supports really large filesystems. Big thanks to 6 | stuartthebruce for testing and debugging (Issue #300) 7 | - new: added support to tracking topN largest files in 8 | filesystem. (Issue #284) 9 | - new: added '-T' option to duc index to change maximum default number of 10 | topN files to track. 11 | - new: added '-B' option to duc index to change number of histogram buckets. 12 | - new: added 'topn' command to show topN files stored in 13 | DB. defaults to 10 currently. 14 | - new: added 't' key in duc ui to toggle between regular and topN 15 | display mode. Initial support. 16 | - new: added histogram report of filesizes found during 17 | indexing to 'duc info'. (Issue #284) 18 | - new: added 'H' or '--histogram' option to duc info 19 | - new: added 'duc histogram' command (Issue #284) 20 | - needs work still, especially CGI, UI and GUI output. 21 | - fix: 22 | 23 | 1.4.5 (2022-07-29) 24 | 25 | - new: added '-u' option to duc index to index by username 26 | - new: added `json` subcommand (thanks Nicolas!) 27 | - new: added support for NO_COLOR env var 28 | - cha: duc db location is now XDG compliant 29 | - fix: various bug fixes, check the git log for details 30 | 31 | 1.4.4 (2018-09-28) 32 | 33 | - new: added --directory/-D option to 'duc ls' to allow listing 34 | of only directory itself (#196) 35 | - new: 'duc ls' now handles multiple path arguments 36 | - new: added 'o' key to duc-ui to pass current file to xdg-open 37 | - new: added DPI handing for graph font size (#201) 38 | - new: added '-a/--apparent' option to 'duc xml' (#171) 39 | - fix: improved cairo and SVG graph rendering (#202) 40 | - cha: changed license from GPL to LGPL (#190) 41 | 42 | 1.4.3 (2017-01-02) 43 | 44 | - fix: fixed mouse and tooltip coordinates in scrolled CGI 45 | - fix: fixed lmdb read/write locking 46 | - new: added --dry-run option to 'duc index' for testing purposes 47 | 48 | 1.4.2 (2016-11-20) 49 | 50 | - new: default per-user file path is now ~/.config/duc/ducrc 51 | - new: added 'classic' palette for Philesight look 52 | - new: added -n/--name-sort option to 'duc ui' and 'duc ls' 53 | - new: added --header and --footer options to 'duc cgi' 54 | - cha: warnings in during indexing now show full path 55 | - fix: fixed multiline label printing in CGI graph 56 | - fix: removed PATH_MAX references which broke build on GNU Hurd 57 | - fix: fixed line stroking for HTML drawing backend 58 | 59 | 1.4.1 (2016-03-12) 60 | 61 | - new: added --fs-include and --fs-exclude options 62 | - cha: disabled gui gradients by default. use the --gradient option 63 | for original graph style 64 | - cha: tooltip now shows actual size, apparent size and count 65 | - fix: once more fixed cgi parameter decoding 66 | - fix: fixed handling of filenames with spaces in 'duc cgi' 67 | 68 | 1.4.0 (2016-02-24) 69 | 70 | - new: added --count option 71 | - new: added support for windows 72 | - cha: renamed guigl to gui, only one gui (X11/OpenGL) can now be enabled 73 | - cha: improved font handling on OpenGL backend 74 | - cha: changed CGI environment detection to work around bug in Lighttpd 75 | 76 | 1.3.2 (2015-06-14) 77 | 78 | - fix: added missing file font.c for --enable-opengl 79 | 80 | 1.3.2 (2015-06-13) 81 | 82 | - fix: rewind dir before drawing 'list' in CGI 83 | - fix: fix clicking through CGI graph if tooltip is not enabled 84 | 85 | 1.3 (2015-06-12) 86 | 87 | - new: added SVG and HTML/canvas graph back ends 88 | - new: added OpenGL/GLFW graph back end for better portability 89 | - new: added tooltip to CGI, enable with --tooltip option 90 | - new: duc database is now portable between endianness and 91 | architectures for sqlite3 databases 92 | - new: added --ring-gap option to gui 93 | - new: added --color option to ui 94 | - cha: optimized memory usage 95 | - fix: fixed building on Solaris 96 | - fix: added better error handling in sqlite3 backend 97 | 98 | 1.2 (2015-05-11) 99 | 100 | - new: Added --dark option to duc-gui 101 | - fix: Fixed CGI file system traversing 102 | - fix: Fixed duc-info output to be consistent with other tools 103 | 104 | 1.1 (2015-05-09) 105 | 106 | - new: Added support for leveldb database backend 107 | - new: Some visual changes and performance improvements in duc-gui 108 | - new: Link to ncursesw if available for proper UTF-8 support in 109 | duc-ui 110 | - fix: Fixed HTML and CGI escaping for duc-cgi 111 | - fix: Fixed unit scaling on 'humanized' formatted numbers 112 | - fix: Added UTF-8 charset declaration to duc-cgi 113 | - fix: Fatal errors are still printed in --quiet mode 114 | - fix: Better handling of non-printable characters in file names 115 | 116 | 1.0-rc1 (2015-04-25) 117 | 118 | - First release candidate for 1.0 119 | 120 | 121 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | 2 | Quickstart 3 | ---------- 4 | 5 | To build Duc with its default options, run: 6 | 7 | $ ./configure 8 | $ make 9 | $ sudo make install 10 | 11 | Generate the configure script when it is not available (cloned git repo): 12 | 13 | $ autoreconf -i 14 | 15 | 16 | To get the required dependencies on Debian or Ubuntu, run: 17 | 18 | $ sudo apt-get install libncursesw5-dev libcairo2-dev libpango1.0-dev \ 19 | build-essential libtkrzw-dev 20 | 21 | On Debian 11 (bullseye), you need to have the following line in your 22 | /etc/apt/sources.list file: 23 | 24 | deb http://deb.debian.org/debian bullseye-backports main 25 | 26 | Then you would do: 27 | 28 | $ sudo apt update 29 | 30 | $ sudo apt-get install libncursesw5-dev libcairo2-dev libpango1.0-dev \ 31 | build-essential libtkrzw-dev tkrzw-doc tkrzw-utils 32 | 33 | 34 | On older RHEL or CentOS systems, you need to do: 35 | 36 | $ sudo yum install pango-devel cairo-devel tokyocabinet-devel 37 | 38 | 39 | RHEL 8 & 9 / Rockly Linux 8 & 9 / Alma Linux 8 & 9 40 | 41 | Install epel-release & update 42 | 43 | $ sudo yum install epel-release 44 | $ sudo yum update 45 | 46 | Install tkrzw and other packages: 47 | 48 | $ sudo yum install tkrzw tkrzw-devel tkrzw-doc tkrzw-libs pango-devel cairo-devel tokyocabinet-devel 49 | 50 | 51 | Configuration Options 52 | --------------------- 53 | 54 | Duc comes with support for various user interfaces and a number of 55 | backends for database access and graph drawing. You can choose which 56 | options should be used with the ./configure script to build Duc to fit 57 | best in your environment. 58 | 59 | This document describes the various options which can be passed to the 60 | ./configure script, and the impact these options have on Duc 61 | functionality. But the ./configure --help is the definitive source. 62 | 63 | 64 | User interfaces 65 | --------------- 66 | 67 | Duc comes with the following user interfaces: 68 | 69 | - Command line interface (duc ls): This user interface has no external 70 | dependencies and is always enabled. 71 | 72 | - Ncurses console interface (duc ui): an interactive console interface, which 73 | depends on ncurses or ncursesw. This user interface is enabled by default. If 74 | your system does not provide ncurses, you can disable this with 75 | 76 | --disable-ui 77 | 78 | - X11 GUI (duc gui): This is the default interface for Linux and other Unix 79 | systems. This user interface depends on the cairo library (see below). If 80 | your system has no X11 or cairo available, disable this user interface with: 81 | 82 | --disable-x11 83 | 84 | - OpenGL GUI (duc gui): an OpenGL/GLFW user interface which should be portable 85 | on a large range of operating systems. The OpenGL gui is disabled by default. 86 | If you want to enable OpenGL, run ./configure with: 87 | 88 | --enable-opengl --disable-x11 89 | 90 | 91 | Database backends 92 | ----------------- 93 | 94 | Duc supports various key-value database backends: 95 | - Tokyo Cabinet: tokyocabinet 96 | - LevelDB: leveldb 97 | - Sqlite3: sqlite3 98 | - Lightning Memory-Mapped Database: lmdb 99 | - Kyoto Cabinet: kyotocabinet 100 | - Tkrzw: tkrzw (default as of v1.5.0) 101 | 102 | Duc now uses Tkrzw by default: the performance is acceptable and it 103 | handles extremely large databases of volumes with terabytes of storage 104 | and millions of files. 105 | 106 | --with-db-backend=ARG 107 | 108 | If your system supports none of the above, contact the authors to see 109 | if we can add your favourite backend. 110 | 111 | Please note: Not all database formats can be shared between machines 112 | with different architectures. Notably, Tokyo Cabinet is built with 113 | non-standard options which break compatibility with other linux 114 | distributions, even on the same architecture [1]. If you are planning 115 | to share databases between different platforms (index machine A, 116 | display on machine B) we recommend using the sqlite3 backend. 117 | 118 | Note, Tokyo Cabiner, Kyoto Cabinet, LevelDB and LMDB are all being 119 | deprecated from future versions because the lack of development and 120 | support for these libraries, especially for super large volumes to be 121 | indexed. 122 | 123 | 1. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=667979 124 | 125 | When picking a backend you probably need to choose between speed, size 126 | and robustness. Some (out of date) measurements on a system with a 127 | 372G directory containing 1.6M files: 128 | 129 | ---------------------------------- 130 | Database Run time Db size 131 | (s) (kB) 132 | ---------------------------------- 133 | tokyocabinet 8.4 19.2 134 | leveldb 7.1 31.5 135 | sqlite3 13.5 71.1 136 | lmdb 5.9 78.7 137 | kyotocabinet 8.3 26.7 138 | tkrzw [*] ??? ??? 139 | ---------------------------------- 140 | 141 | 142 | [*] Tkrzw currently is the default used by Duc because of it's current 143 | development, good compression and reasonable performance. 144 | 145 | Tokyo Cabinet is not very stable and can create corrupt databases when 146 | interrupting the indexing. If this is a problem for you, choose a 147 | different db backend. 148 | 149 | 150 | Graphics 151 | -------- 152 | 153 | Duc supports various backends for creating graphics, but some have dependencies 154 | which cannot be met on all systems. Especially on headless server systems or 155 | embedded systems not all graphics libraries are available. 156 | 157 | - SVG: Duc has native SVG support, which can be used by the 'duc graph' command 158 | to create static images of disk usage. The SVG backend has no external 159 | dependencies and is always enabled. 160 | 161 | - HTML canvas: Duc has native support for generating graphs in HTML5 using 162 | JavaScript and the canvas object. The HTML backend has not external 163 | dependencies and is always enabled. 164 | 165 | - Cairo/pango: The cairo/pango backend is required for the X11 user interface, 166 | and also adds an option to the duc-graph command to generate images in .PNG 167 | or .PDF file format. If your systems does not have cairo and pango available, 168 | add the following flag to ./configure: 169 | 170 | --disable-cairo 171 | 172 | - OpenGL: Duc can draw to OpenGL contexts and can be used by the 'duc gui' 173 | command. To enable the OpenGL backend for the 'duc gui' command, run 174 | ./configure with: 175 | 176 | --enable-opengl --disable-x11 177 | 178 | 179 | Testing 180 | ------- 181 | 182 | Duc comes with a rudimentary test harness which can be run at the top 183 | level directory with: 184 | 185 | ./test.sh 186 | 187 | If you have valgrind and you want to run the tests using it do: 188 | 189 | USE_VALGRIND=1 ./test.sh 190 | 191 | It will complain if you try this and valgrind isn't installed. The 192 | test harness still needs work and more tests, but should hopefully 193 | help keep us from re-introducing bugs as they are fixed and checked 194 | for. We would love to see more tests and a better harness, patches 195 | welcome! 196 | 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | bin_PROGRAMS := duc 3 | 4 | duc_SOURCES := \ 5 | src/libduc/buffer.c \ 6 | src/libduc/buffer.h \ 7 | src/libduc/db.c \ 8 | src/libduc/db.h \ 9 | src/libduc/db-tokyo.c \ 10 | src/libduc/db-tkrzw.c \ 11 | src/libduc/db-kyoto.c \ 12 | src/libduc/db-leveldb.c \ 13 | src/libduc/db-sqlite3.c \ 14 | src/libduc/db-lmdb.c \ 15 | src/libduc/dir.c \ 16 | src/libduc/duc.c \ 17 | src/libduc/duc.h \ 18 | src/libduc/index.c \ 19 | src/libduc/private.h \ 20 | src/libduc/canonicalize.c \ 21 | src/libduc/varint.c \ 22 | src/libduc/varint.h \ 23 | src/libduc/uthash.h \ 24 | src/libduc/utlist.h \ 25 | src/libduc/utstring.h 26 | 27 | duc_SOURCES += \ 28 | src/glad/glad.c \ 29 | src/glad/KHR/khrplatform.h \ 30 | src/glad/glad/glad.h 31 | 32 | duc_SOURCES += \ 33 | src/libduc-graph/graph.c \ 34 | src/libduc-graph/graph-cairo.c \ 35 | src/libduc-graph/graph-opengl.c \ 36 | src/libduc-graph/graph-svg.c \ 37 | src/libduc-graph/graph-html.c \ 38 | src/libduc-graph/graph-private.h \ 39 | src/libduc-graph/duc-graph.h 40 | 41 | duc_SOURCES += \ 42 | src/duc/cmd-cgi.c \ 43 | src/duc/cmd-graph.c \ 44 | src/duc/cmd-gui.c \ 45 | src/duc/cmd-guigl.c \ 46 | src/duc/cmd.h \ 47 | src/duc/cmd.h \ 48 | src/duc/cmd-histogram.c \ 49 | src/duc/cmd-index.c \ 50 | src/duc/cmd-info.c \ 51 | src/duc/cmd-ls.c \ 52 | src/duc/cmd-topn.c \ 53 | src/duc/cmd-ui.c \ 54 | src/duc/cmd-xml.c \ 55 | src/duc/cmd-json.c \ 56 | src/duc/ducrc.c \ 57 | src/duc/ducrc.h \ 58 | src/duc/main.c 59 | 60 | 61 | AM_CFLAGS := @CAIRO_CFLAGS@ @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ 62 | AM_CFLAGS += @TC_CFLAGS@ @SQLITE3_CFLAGS@ @GLFW3_CFLAGS@ @LMDB_CFLAGS@ 63 | AM_CFLAGS += @KC_CFLAGS@ @TKRZW_CFLAGS@ 64 | AM_CFLAGS += -Isrc/libduc -Isrc/libduc-graph -Isrc/glad 65 | 66 | duc_LDADD := @CAIRO_LIBS@ @PANGO_LIBS@ @PANGOCAIRO_LIBS@ 67 | duc_LDADD += @TC_LIBS@ @SQLITE3_LIBS@ @GLFW3_LIBS@ @LMDB_LIBS@ @KC_LIBS@ @TKRZW_LIBS@ 68 | 69 | man1_MANS = \ 70 | doc/duc.1 71 | 72 | EXTRA_DIST = \ 73 | doc/duc.1 \ 74 | doc/duc.1.html \ 75 | src/libduc-graph/font.c \ 76 | doc/duc.md \ 77 | LICENSE 78 | 79 | install-exec-hook: 80 | -/sbin/ldconfig || :; 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Build Status](https://travis-ci.org/zevv/duc.svg?branch=master)](https://travis-ci.org/zevv/duc) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/duc.svg)](https://anaconda.org/conda-forge/duc) 3 | 4 | Duc is a collection of tools for indexing, inspecting and visualizing disk 5 | usage. Duc maintains a database of accumulated sizes of directories of the file 6 | system, and allows you to query this database with some tools, or create fancy 7 | graphs showing you where your bytes are. 8 | 9 | Check the [Duc homepage](http://duc.zevv.nl) for more information, documentation and news. 10 | 11 | ![duc gui](/img/palette-rainbow.png) 12 | 13 | 14 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | This is a list of feature requests and enhancements that have come up over the 3 | years. Most of them are not easy to implement in the current architecture, but 4 | I'm still keeping the requests here for future reference or for if I ever get 5 | bored and have a lot of time on my hands. Anybody is free to pick and implement 6 | any of these tasks, of course! 7 | 8 | ### Edit database to remove path(s) from Index and do all cleanup 9 | 10 | This should be a simple change to add, though it does require some hackery to 11 | remove entries from the records[] array in the DB. Needs thought. Currently 12 | only solution would be to index to a totally new DB file with only the path(s) 13 | you want. 14 | 15 | ### Show increase since last index or time period 16 | 17 | https://github.com/zevv/duc/issues/153 18 | 19 | The main reason I started using this tool is actually trying to find users 20 | that increase their home directory rapidly. It was easy in the beginning but 21 | starting to get much harder now that we have more users and I keep forgetting 22 | the initial size. :) I think it would be nice, and certainly possible?, for 23 | DUC to show the increase in % since last index or a configured time period 24 | (say 7 days ago). 25 | 26 | ### Incremental Indexing 27 | 28 | https://github.com/zevv/duc/issues/115 29 | 30 | Just a question, is the indexing process incremental or does it restart the 31 | indexing from scratch each time? 32 | 33 | ### Per user filtering 34 | 35 | https://github.com/zevv/duc/issues/30 36 | 37 | At work I have to monitor files that differ on a per-user basis, so it would 38 | be nice to produce graphs that are limited to a single user and / or use the 39 | data to produce a graph that showed the percentage of the disk space used by 40 | a specific user. This way I could send users graphs of the disk usage limited 41 | to their user. 42 | 43 | ### Directory mtime optimization 44 | 45 | https://github.com/zevv/duc/issues/101 46 | 47 | When running index against an existing database does duc compare the current 48 | mtime of directories in the filesystem versus the database to see whether 49 | readdir() is needed to update the database, or as a performance optimization 50 | whether an update of that directory can be skipped? 51 | 52 | ### overall graph of multiple datasets in a database 53 | 54 | https://github.com/zevv/duc/issues/56 55 | 56 | 57 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | Installation through RPM 2 | ======================== 3 | 4 | If you want you can install duc through a rpm package. Under `build/duc.spec` you find the spec file to build the rpm. 5 | 6 | Tested on `CentOS 6.5`. 7 | 8 | ### Build 9 | 10 | Using `mock` and `rpmdevtools` to create the rpm (`yum install mock rpm-build rpmdevtools` from `EPEL` ). 11 | 12 | ##### User 13 | 14 | Add new mock user: 15 | 16 | ```sh 17 | useradd 18 | passwd 19 | usermod -aG mock 20 | ``` 21 | 22 | Or editing existing users: 23 | 24 | ```sh 25 | usermod -aG mock && newgrp mock 26 | ``` 27 | 28 | ##### Get sources from repository 29 | 30 | ```sh 31 | cd deploy/rpmbuild/ 32 | spectool -g duc.spec 33 | ``` 34 | 35 | ##### Build the sources (src.rpm) 36 | 37 | ```sh 38 | mock --resultdir=. --root epel-6-x86_64 --buildsrpm --spec duc.spec --sources . 39 | ``` 40 | 41 | ##### Build rpm 42 | 43 | ```sh 44 | mock --resultdir=. --root=epel-6-x86_64 --rebuild duc-1.0-1.el6.src.rpm 45 | ``` 46 | 47 | After this operations you have `duc-1.0-1.el6.x86_64.rpm`. To install it simply run: 48 | 49 | ```sh 50 | yum localinstall duc-1.0-1.el6.x86_64.rpm 51 | ``` 52 | -------------------------------------------------------------------------------- /build/duc.spec: -------------------------------------------------------------------------------- 1 | %global name duc 2 | %global version 1.4.4 3 | %global release 1 4 | 5 | Name: %{name} 6 | Version: %{version} 7 | Release: %{release}%{?dist} 8 | Summary: Duc, a library and suite of tools for inspecting disk usage 9 | Requires: pango cairo tokyocabinet ncurses 10 | 11 | License: GNU General Public License 12 | URL: https://github.com/zevv/duc 13 | Source0: https://github.com/zevv/duc/releases/download/%{version}/duc-%{version}.tar.gz 14 | 15 | BuildArch: x86_64 16 | BuildRequires: libtool autoconf 17 | BuildRequires: pango-devel cairo-devel tokyocabinet-devel ncurses-devel 18 | 19 | %description 20 | Duc is a small library and a collection of tools for inspecting and visualizing 21 | disk usage. 22 | Duc maintains a database of accumulated sizes of directories of your file system, 23 | and allows you to query this database with some tools, 24 | or create fancy graphs showing you where your bytes are. 25 | 26 | %prep 27 | %setup 28 | 29 | %build 30 | autoreconf --install 31 | ./configure --libdir=%{_libdir} --bindir=%{_bindir} --mandir=%{_mandir} 32 | make 33 | 34 | %install 35 | make install DESTDIR=$RPM_BUILD_ROOT 36 | 37 | %post 38 | 39 | %clean 40 | rm -rf $RPM_BUILD_ROOT 41 | 42 | %files 43 | %defattr(-,root,root) 44 | %{_bindir}/%{name} 45 | %{_mandir}/man1/%{name}.1.gz 46 | 47 | %changelog 48 | * Mon Oct 14 2019 Peter Cummuskey - 1.4.4 49 | - Update to 1.4.4 50 | - Added runtime requirements 51 | 52 | * Mon Jan 02 2017 Giacomo Sanchietti - 1.4.3 53 | - Update to 1.4.3 54 | 55 | * Fri Dec 09 2016 Giacomo Sanchietti - 1.4.2 56 | - Update to 1.4.2 57 | - Removed devel rpm 58 | - Cleanup spec 59 | 60 | * Mon Jun 29 2015 James Chang - 1.3.3 61 | - added ncurses-devel package requirement 62 | - fixed file not found for libs 63 | 64 | * Wed Feb 11 2015 Edoardo Spadoni - 1.0 65 | - first version 66 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf, et al to produce a configure script. 3 | # 4 | # autoreconf -v -f 5 | # which is just: 6 | # aclocal && autoconf && automake -a -f && ./configure && make 7 | 8 | AC_PREREQ([2.13]) 9 | 10 | AC_INIT([duc], [1.5.0-rc1], [duc@zevv.nl]) 11 | 12 | LIB_CURRENT=1 13 | LIB_REVISION=0 14 | LIB_AGE=0 15 | AC_SUBST(LIB_CURRENT) 16 | AC_SUBST(LIB_REVISION) 17 | AC_SUBST(LIB_AGE) 18 | 19 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 20 | 21 | AC_CONFIG_HEADER([config.h]) 22 | AC_GNU_SOURCE 23 | 24 | AC_SYS_LARGEFILE 25 | AC_SYS_LONG_FILE_NAMES 26 | 27 | AC_PROG_CC 28 | PKG_PROG_PKG_CONFIG 29 | 30 | AC_CHECK_LIB([m], [main]) 31 | AC_CHECK_MEMBERS([struct stat.st_blocks]) 32 | 33 | # 34 | # Check --disable options 35 | # 36 | 37 | AC_ARG_ENABLE( 38 | [cairo], 39 | [AS_HELP_STRING([--disable-cairo], [disable cairo drawing @<:@default=yes@:>@])], , 40 | [enable_cairo="yes"] 41 | ) 42 | 43 | AC_ARG_ENABLE( 44 | [opengl], 45 | [AS_HELP_STRING([--enable-opengl], [enable OpenGL drawing @<:@default=no@:>@])], , 46 | [enable_opengl="no"] 47 | ) 48 | 49 | AC_ARG_ENABLE( 50 | [ui], 51 | [AS_HELP_STRING([--disable-ui], [disable ncurses ui @<:@default=yes@:>@])], , 52 | [enable_ui="yes"] 53 | ) 54 | 55 | AC_ARG_ENABLE( 56 | [x11], 57 | [AS_HELP_STRING([--disable-x11], [disable X11 @<:@default=yes@:>@])], , 58 | [enable_x11="yes"] 59 | ) 60 | 61 | AC_ARG_WITH( 62 | [db-backend], 63 | [AS_HELP_STRING([--with-db-backend], [select database backend (tokyocabinet,leveldb,sqlite3,lmdb,kyotocabinet,tkrzw) @<:@default=tkrzw@:>@])], , 64 | [with_db_backend="tkrzw"] 65 | ) 66 | 67 | AC_MSG_RESULT([Selected backend ${with_db_backend}]) 68 | 69 | # 70 | # Check for available libraries 71 | # 72 | 73 | case "${with_db_backend}" in 74 | tokyocabinet) 75 | PKG_CHECK_MODULES([TC], [tokyocabinet]) 76 | AC_DEFINE([ENABLE_TOKYOCABINET], [1], [Enable tokyocabinet db backend]) 77 | ;; 78 | tkrzw) 79 | LDFLAGS="$outer_LDFLAGS -ltkrzw" 80 | AC_CHECK_LIB(tkrzw, tkrzw_get_last_status, 81 | [ 82 | TKRZW_LIBS="-ltkrzw" 83 | AC_DEFINE([ENABLE_TKRZW], [1], [Enable tkrzw db backend]) 84 | ], [ AC_MSG_ERROR(Unable to find tkrzw) ]) 85 | AC_SUBST([TKRZW_LIBS]) 86 | p AC_SUBST([TKRZW_CFLAGS]) 87 | ;; 88 | leveldb) 89 | AC_CHECK_LIB([leveldb], [leveldb_open]) 90 | AC_DEFINE([ENABLE_LEVELDB], [1], [Enable leveldb db backend]) 91 | ;; 92 | sqlite3) 93 | PKG_CHECK_MODULES([SQLITE3], [sqlite3]) 94 | AC_DEFINE([ENABLE_SQLITE], [1], [Enable sqlite3 db backend]) 95 | ;; 96 | lmdb) 97 | LDFLAGS="$outer_LDFLAGS -llmdb" 98 | AC_CHECK_LIB(lmdb, mdb_env_create, 99 | [ 100 | LMDB_LIBS="-llmdb" 101 | AC_DEFINE([ENABLE_LMDB], [1], [Enable lmdb db backend]) 102 | ], [ AC_MSG_ERROR(Unable to find LMDB) ]) 103 | AC_SUBST([LMDB_LIBS]) 104 | AC_SUBST([LMDB_CFLAGS]) 105 | ;; 106 | kyotocabinet) 107 | PKG_CHECK_MODULES([KC], [kyotocabinet]) 108 | AC_DEFINE([ENABLE_KYOTOCABINET], [1], [Enable kyotocabinet db backend]) 109 | ;; 110 | *) 111 | AC_MSG_ERROR([Unsupported db-backend "${with_db_backend}"]) 112 | esac 113 | 114 | AC_DEFINE_UNQUOTED(DB_BACKEND, ["${with_db_backend}"], [Database backend]) 115 | 116 | if test "${enable_cairo}" = "yes"; then 117 | 118 | PKG_CHECK_MODULES([CAIRO], [cairo],, [AC_MSG_ERROR([ 119 | The cairo library was not found, which is needed for graph support. Either install 120 | the cairo development libraries, or compile without graph support (--disable-cairo) 121 | ])]) 122 | 123 | PKG_CHECK_MODULES([PANGO], [pango],, [AC_MSG_ERROR([ 124 | The pango library was not found, which is needed for graph support. Either install 125 | the pango development libraries, or compile without graph support (--disable-cairo) 126 | ])]) 127 | 128 | PKG_CHECK_MODULES([PANGOCAIRO], [pangocairo],, [AC_MSG_ERROR([ 129 | The pangocairo library was not found, which is needed for graph support. Either install 130 | the pangocairo development libraries, or compile without graph support (--disable-cairo) 131 | ])]) 132 | 133 | AC_DEFINE([ENABLE_CAIRO], [1], [Enable cairo]) 134 | fi 135 | 136 | if test "${enable_opengl}" = "yes"; then 137 | 138 | PKG_CHECK_MODULES([GLFW3], [glfw3],, [AC_MSG_ERROR([ 139 | The glfw3 library was not found, which is needed for opengl support. Either install 140 | the glfw3 development libraries, or compile without opengl support (--disable-opengl) 141 | ])]) 142 | 143 | if test "${enable_x11}" = "yes"; then 144 | AC_MSG_ERROR([ 145 | Only one graphical user interface can be configured, use --disable-x11 when 146 | using --enable-opengl]) 147 | fi 148 | 149 | AC_CHECK_LIB([dl], [dlopen]) 150 | AC_DEFINE([ENABLE_OPENGL], [1], [Enable opengl]) 151 | fi 152 | 153 | 154 | if test "${enable_ui}" = "yes"; then 155 | AC_DEFINE([ENABLE_UI], [1], [Enable ui]) 156 | 157 | AC_CHECK_LIB([ncursesw], [tputs],, [ 158 | AC_CHECK_LIB([ncurses], [tputs],, [ 159 | AC_MSG_ERROR([ 160 | The ncurses library was not found, which is needed for ui support. Either install 161 | the ncurses development libraries, or compile without ui support (--disable-ui) 162 | ]) 163 | ]) 164 | ]) 165 | fi 166 | 167 | 168 | if test "${enable_x11}" = "yes"; then 169 | test "${enable_cairo}" != "yes" && AC_MSG_ERROR([cairo must be enabled for x11]) 170 | 171 | AC_CHECK_LIB([X11], [XOpenDisplay],, [AC_MSG_ERROR([ 172 | The X11 library was not found, which is needed for x11 gui support. 173 | ])]) 174 | AC_DEFINE([ENABLE_X11], [1], [Enable X11]) 175 | fi 176 | 177 | 178 | AC_CHECK_HEADERS([fcntl.h limits.h stdint.h stdlib.h string.h sys/ioctl.h unistd.h fnmatch.h termios.h]) 179 | AC_CHECK_HEADERS([ncurses.h ncurses/ncurses.h ncursesw/ncurses.h]) 180 | 181 | AC_TYPE_MODE_T 182 | AC_TYPE_OFF_T 183 | AC_TYPE_SIZE_T 184 | AC_TYPE_UINT32_T 185 | AC_TYPE_UINT64_T 186 | AC_TYPE_UINT8_T 187 | AC_CHECK_TYPES([dev_t, ino_t]) 188 | 189 | AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK 190 | AC_CHECK_FUNCS([floor memset strchr strdup strerror gettimeofday lstat]) 191 | 192 | AC_CONFIG_FILES([Makefile]) 193 | AC_OUTPUT 194 | 195 | AM_CONDITIONAL([ENABLE_CAIRO], [test "$enable_cairo" = "true"]) 196 | AM_CONDITIONAL([ENABLE_OPENGL], [test "$enable_opengl" = "true"]) 197 | AM_CONDITIONAL([ENABLE_UI], [test "$enable_ui" = "true"]) 198 | AM_CONDITIONAL([ENABLE_X11], [test "$enable_x11" = "true"]) 199 | 200 | AC_MSG_RESULT([ 201 | 202 | configuration summary: 203 | 204 | - Package version: $PACKAGE $VERSION 205 | - Prefix: ${prefix} 206 | - Database backend: ${with_db_backend} 207 | - X11 support: ${enable_x11} 208 | - OpenGL support: ${enable_opengl} 209 | - UI (ncurses) support: ${enable_ui} 210 | - Graph cairo support: ${enable_cairo} 211 | 212 | ]) 213 | 214 | # End 215 | 216 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | duc (1.4.4-1) unstable; urgency=medium 2 | 3 | * New upstream version. 4 | * Bump standards version. 5 | * Move to debhelper 12. 6 | 7 | -- Jonathan Dowland Wed, 06 May 2020 16:20:12 +0100 8 | 9 | duc (1.4.3-6) unstable; urgency=medium 10 | 11 | * Fix FTBFS (dh_installman: Cannot find "debian/build-nox/doc/duc.1"). 12 | (Closes: #924473) 13 | 14 | -- Bruno Kleinert Sun, 17 Mar 2019 18:55:56 +0100 15 | 16 | duc (1.4.3-5) unstable; urgency=medium 17 | 18 | [Ondřej Nový] 19 | * d/copyright: Use https protocol in Format field 20 | * d/rules: Remove trailing whitespaces 21 | 22 | [Helmut Grohne] 23 | * Address FTCBFS: Let dh_auto_configure pass --host to ./configure. 24 | (Closes: #915595) 25 | 26 | -- Helmut Grohne Wed, 05 Dec 2018 06:24:22 +0100 27 | 28 | duc (1.4.3-4) unstable; urgency=medium 29 | 30 | * Update Vcs-* headers to reflect migration from Alioth to Salsa. 31 | * Change B-D on ruby-conn to conn. Thanks Helmut Grohne. Closes: #903067. 32 | * Bump standards version (no other changes) 33 | 34 | -- Jonathan Dowland Thu, 05 Jul 2018 21:58:21 +0100 35 | 36 | duc (1.4.3-3) unstable; urgency=medium 37 | 38 | * Adopt duc. Thank you Herbert Parentes Fortes Neto for all your hard 39 | work and best wishes for the future! Closes: #886013. 40 | * Bump standards version (no further changes needed) 41 | * duc-nox provides /usr/bin/duc and the duc and duc-nox packages now 42 | conflict with each other. Closes: #885404. 43 | * Add a compatibility symlink for duc-nox. 44 | * Remove empty debian/patches/series 45 | * Revert src/libduc-graph/font.c to upstream version (DOS line endings) 46 | to keep dpkg-source happy. 47 | 48 | -- Jonathan Dowland Tue, 02 Jan 2018 16:12:09 +0000 49 | 50 | duc (1.4.3-2) unstable; urgency=medium 51 | 52 | * debian/control: 53 | - Add ruby-ronn to Build-Depends 54 | * debian/duc-nox.manpages: 55 | - Upstream manpage generated at build time. 56 | * debian/man removed. 57 | * debian/rules: 58 | - generate duc-nox manpage 59 | 60 | -- Herbert Parentes Fortes Neto Mon, 30 Jan 2017 10:55:43 -0200 61 | 62 | duc (1.4.3-1) unstable; urgency=medium 63 | 64 | * New upstream release. 65 | * debian/copyright: 66 | - Updated. 67 | * debian/man: 68 | - Refresh duc-nox manpage. 69 | 70 | -- Herbert Parentes Fortes Neto Mon, 30 Jan 2017 10:33:18 -0200 71 | 72 | duc (1.4.2-1) unstable; urgency=medium 73 | 74 | * New upstream release. 75 | * debian/man/duc-nox.1: 76 | - Refresh file. 77 | * debian/patches: 78 | - all patches applied by the upstream. 79 | * debian/rules: 80 | - dh_auto_configure: 81 | - Add '-fPIE' to CFLAGS. 82 | - Add '-fPIE -pie' to LDFLAGS. 83 | 84 | -- Herbert Parentes Fortes Neto Sat, 26 Nov 2016 08:46:56 -0200 85 | 86 | duc (1.4.1-4) unstable; urgency=medium 87 | 88 | * debian/control: 89 | - Update my email. 90 | * debian/copyright: 91 | - Update my email 92 | * debian/man: 93 | - Fix typo in duc-nox.txt and generate a new duc-nox.1 94 | * debian/patches: 95 | - Create fix-typo-manpage.patch. duc.1 96 | - Create fix-typo-cmd-cgi-c.patch. 97 | 98 | -- Herbert Parentes Fortes Neto Sat, 05 Nov 2016 12:24:11 -0200 99 | 100 | duc (1.4.1-3) unstable; urgency=medium 101 | 102 | * debian/control: 103 | - bump Standards-Version from 3.9.7 to 3.9.8 104 | * debian/copyright: 105 | - License other_1 renamed to Expat. 106 | 107 | -- Herbert Parentes Fortes Neto Sun, 19 Jun 2016 15:28:07 -0300 108 | 109 | duc (1.4.1-2) unstable; urgency=low 110 | 111 | * debian/patches: 112 | - ftbfs_hurd-i386.patch file created. 113 | 114 | -- Herbert Parentes Fortes Neto Sat, 02 Apr 2016 17:18:35 -0300 115 | 116 | duc (1.4.1-1) unstable; urgency=low 117 | 118 | * New upstream release. 119 | * debian/man/* updated. 120 | 121 | -- Herbert Parentes Fortes Neto Tue, 22 Mar 2016 13:48:45 -0300 122 | 123 | duc (1.4.0-1) unstable; urgency=low 124 | 125 | * New upstream release. 126 | * debian/control: 127 | - dh-autoreconf added to Build-Depends field. 128 | - Bump Standards-Version to 3.9.7. 129 | - Vcs-* fields using https. 130 | * debian/copyright updated. 131 | * debian/duc*.examples created. 132 | * debian/man/* updated to version 1.4.0. 133 | * debian/patches: 134 | - fix_typo_cmd-index.patch created. 135 | - fix_typo_libduc_duc.patch created. 136 | * debian/rules: 137 | - '--with autoreconf' param added. 138 | - set '--prefix=/usr' as a install dir. 139 | - two lines added to 'cp src/glad' to where the build is done. 140 | * debian/watch: 141 | - version 4. 142 | 143 | -- Herbert Parentes Fortes Neto Wed, 09 Mar 2016 15:06:01 -0300 144 | 145 | duc (1.3.3-1) unstable; urgency=medium 146 | 147 | * Initial release (Closes: #783310) 148 | 149 | -- Herbert Parentes Fortes Neto Sun, 21 Jun 2015 11:06:54 -0300 150 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: duc 2 | Section: utils 3 | Priority: optional 4 | Maintainer: Jonathan Dowland 5 | Build-Depends: debhelper (>= 12), 6 | libcairo2-dev, 7 | libncursesw5-dev, 8 | libpango1.0-dev, 9 | libtokyocabinet-dev, 10 | ronn, 11 | txt2man 12 | Standards-Version: 4.5.0 13 | Homepage: http://duc.zevv.nl/ 14 | Vcs-Git: https://salsa.debian.org/debian/duc.git 15 | Vcs-Browser: https://salsa.debian.org/debian/duc 16 | 17 | Package: duc-nox 18 | Architecture: any 19 | Depends: ${misc:Depends}, ${shlibs:Depends} 20 | Conflicts: duc 21 | Description: high-performance disk usage analyzer (without X support) 22 | Duc maintains a database of accumulated sizes of directories of the file 23 | system, and allows you to query this database with some tools. 24 | . 25 | Duc comes with console utilities, ncursesw and a CGI wrapper for disk 26 | usage querying and visualisation. 27 | . 28 | Duc is designed to scale to huge filesystems: it will index and display 29 | hundreds of millions of files on petabytes of storage without problems. 30 | . 31 | This package provides the console version of the duc. 32 | 33 | Package: duc 34 | Architecture: any 35 | Depends: ${misc:Depends}, ${shlibs:Depends} 36 | Conflicts: duc-nox (>= 1.4.3-3) 37 | Description: high-performance disk usage analyzer 38 | Duc maintains a database of accumulated sizes of directories of the file 39 | system, and allows you to query this database with some tools, or create 40 | fancy graphs showing you where your bytes are. 41 | . 42 | Duc comes with console utilities, ncursesw and X11 user interfaces and a 43 | CGI wrapper for disk usage querying and visualisation. 44 | . 45 | Duc is designed to scale to huge filesystems: it will index and display 46 | hundreds of millions of files on petabytes of storage without problems. 47 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: duc 3 | Source: https://github.com/zevv/duc/releases 4 | 5 | Files: * 6 | Copyright: 2014-2017 Ico Doornekamp 7 | 2014-2017 John Stoffel 8 | License: GPL-2 9 | 10 | Files: src/glad/KHR/khrplatform.h 11 | Copyright: 2008-2009 The Khronos Group Inc. 12 | License: Expat 13 | 14 | Files: src/libduc/utlist.h 15 | src/libduc/uthash.h 16 | src/libduc/utstring.h 17 | Copyright: 2003-2014 Troy D. Hanson 18 | License: BSD-1-Clause 19 | 20 | Files: src/libduc/varint.c 21 | Copyright: 2012 ? 22 | License: other 23 | 24 | Files: debian/* 25 | Copyright: 2015-2017 Herbert Parentes Fortes Neto 26 | License: GPL-2+ 27 | 28 | License: GPL-2 or GPL-2+ 29 | This package is free software; you can redistribute it and/or modify 30 | it under the terms of the GNU General Public License as published by 31 | the Free Software Foundation; either version 2 of the License, or 32 | (at your option) any later version. 33 | . 34 | This package is distributed in the hope that it will be useful, 35 | but WITHOUT ANY WARRANTY; without even the implied warranty of 36 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 37 | GNU General Public License for more details. 38 | . 39 | You should have received a copy of the GNU General Public License 40 | along with this program. If not, see 41 | . 42 | On Debian systems, the complete text of the GNU General 43 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 44 | 45 | License: BSD-1-Clause 46 | Redistribution and use in source and binary forms, with or without 47 | modification, are permitted provided that the following conditions are met: 48 | . 49 | * Redistributions of source code must retain the above copyright 50 | notice, this list of conditions and the following disclaimer. 51 | . 52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 53 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 55 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 56 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 57 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 58 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 59 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 60 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 61 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 62 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 | 64 | License: other 65 | The authors renounce all claim of copyright to this code and dedicate 66 | this code to the public domain. In place of legal notice, here is 67 | a blessing: 68 | . 69 | May you do good and not evil. 70 | May you find forgiveness for yourself and forgive others. 71 | May you share freely, never taking more than you give. 72 | 73 | License: Expat 74 | Permission is hereby granted, free of charge, to any person obtaining a 75 | copy of this software and/or associated documentation files (the 76 | "Materials"), to deal in the Materials without restriction, including 77 | without limitation the rights to use, copy, modify, merge, publish, 78 | distribute, sublicense, and/or sell copies of the Materials, and to 79 | permit persons to whom the Materials are furnished to do so, subject to 80 | the following conditions: 81 | . 82 | The above copyright notice and this permission notice shall be included 83 | in all copies or substantial portions of the Materials. 84 | . 85 | THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 88 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 89 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 90 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 91 | MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 92 | -------------------------------------------------------------------------------- /debian/duc-nox.examples: -------------------------------------------------------------------------------- 1 | examples/ducrc 2 | -------------------------------------------------------------------------------- /debian/duc-nox.install: -------------------------------------------------------------------------------- 1 | debian/build-nox/duc usr/bin 2 | -------------------------------------------------------------------------------- /debian/duc-nox.links: -------------------------------------------------------------------------------- 1 | usr/bin/duc usr/bin/duc-nox 2 | usr/share/man/man1/duc.1.gz usr/share/man/man1/duc-nox.1.gz 3 | -------------------------------------------------------------------------------- /debian/duc-nox.manpages: -------------------------------------------------------------------------------- 1 | debian/build-nox.inst/usr/share/man/man1/duc.1 2 | -------------------------------------------------------------------------------- /debian/duc.examples: -------------------------------------------------------------------------------- 1 | examples/ducrc 2 | -------------------------------------------------------------------------------- /debian/duc.install: -------------------------------------------------------------------------------- 1 | debian/build/duc usr/bin 2 | -------------------------------------------------------------------------------- /debian/duc.manpages: -------------------------------------------------------------------------------- 1 | doc/duc.1 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | #DH_VERBOSE = 1 4 | 5 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 6 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 7 | 8 | FLAGS_duc:=--enable-dependency-tracking 9 | FLAGS_duc-nox:=--enable-dependency-tracking 10 | FLAGS_duc-nox+=--disable-cairo 11 | FLAGS_duc-nox+=--disable-x11 12 | 13 | BUILDDIR := $(CURDIR)/debian/build 14 | BUILDDIR_NOX := $(CURDIR)/debian/build-nox 15 | 16 | %: 17 | dh $@ 18 | 19 | override_dh_auto_configure: 20 | dh_auto_configure --builddirectory=$(BUILDDIR) -- $(FLAGS_duc) CFLAGS="$(CFLAGS) -fPIE" LDFLAGS="$(LDFLAGS) -fPIE -pie" 21 | dh_auto_configure --builddirectory=$(BUILDDIR_NOX) -- $(FLAGS_duc-nox) CFLAGS="$(CFLAGS) -fPIE" LDFLAGS="$(LDFLAGS) -fPIE -pie" 22 | 23 | override_dh_auto_build: 24 | cp -r src/glad/* $(BUILDDIR)/src/glad/ 25 | cp -r src/glad/* $(BUILDDIR_NOX)/src/glad/ 26 | cp src/libduc/*.h $(BUILDDIR)/src/libduc/ 27 | cp src/libduc/*.h $(BUILDDIR_NOX)/src/libduc/ 28 | cp src/libduc-graph/*.h $(BUILDDIR)/src/libduc-graph/ 29 | cp src/libduc-graph/*.h $(BUILDDIR_NOX)/src/libduc-graph/ 30 | dh_auto_build -a -- -C $(BUILDDIR) 31 | dh_auto_build -a -- -C $(BUILDDIR_NOX)/src 32 | 33 | override_dh_auto_install: 34 | $(MAKE) -C $(BUILDDIR) install DESTDIR=$(BUILDDIR).inst 35 | $(MAKE) -C $(BUILDDIR_NOX) install DESTDIR=$(BUILDDIR_NOX).inst 36 | cp -r doc $(BUILDDIR_NOX)/ 37 | rm -f $(BUILDDIR_NOX)/doc/duc.1 38 | $(MAKE) -C $(BUILDDIR_NOX)/doc 39 | 40 | override_dh_auto_clean: 41 | dh_clean 42 | rm -fr $(BUILDDIR) $(BUILDDIR_NOX) 43 | rm -fr debian/*.inst 44 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | https://github.com/zevv/duc/releases .*/archive/v?(\d\S+)\.tar\.(?:gz|bz2|xz) 3 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | duc.1 duc.html: manual.txt ../duc 3 | ../duc manual > options.txt 4 | sed -e "/>>>>/r options.txt" -e "//d" < manual.txt > duc.md 5 | ronn --style=toc,80c duc.md 6 | 7 | clean: 8 | rm -f options.txt 9 | -------------------------------------------------------------------------------- /examples/duc.css: -------------------------------------------------------------------------------- 1 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/ducrc: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # This is an example configuration file for duc. Each section holds the options 4 | # for the duc subcommand with the same name. The [global] section holds options 5 | # which are passed to all subcommands. 6 | # 7 | # This example configuration file does not list all available options, but is 8 | # only meant as a starting point. For a list of available options and their 9 | # meaning, run 'duc help --all'. 10 | # 11 | 12 | [global] 13 | 14 | # 15 | # Enable the line below if you want to ensure duc always looks for duc.db 16 | # someplace globally accessible, for example if you plan to regularly run duc 17 | # index from cron. 18 | # 19 | 20 | # database /var/cache/duc.db 21 | 22 | # 23 | # Enable one of the options below to see more info during duc runs. 24 | # 25 | 26 | # verbose 27 | # debug 28 | 29 | 30 | [ls] 31 | 32 | recursive 33 | classify 34 | 35 | 36 | [index] 37 | 38 | exclude Cache 39 | exclude .ccache 40 | 41 | 42 | [gui] 43 | 44 | fuzz 0.7 45 | palette rainbow 46 | levels 7 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/footer.htm: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/header.htm: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /examples/index.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Simple script to both generate simple CGI scripts for a bunch of duc databases, as well 4 | # as acting as the main index page to browse those DBs. 5 | # 6 | # When called from the CLI with the -g option, it will generate .cgi scripts in the $CWD. 7 | # 8 | # This is totally not secure or hardened against malicious actors! Only use internally in 9 | # a secured network you trust completely. You have been warned! 10 | # 11 | # Only tested with Apache, and you will need to make sure you have the +ExecCGI option setup, 12 | # and this file chmod +x as well. 13 | # 14 | # Author: John Stoffel (john@stoffel.org) 15 | # License: GPLv2 16 | 17 | mkcgi=0 18 | 19 | title="Disk Usage graphs" 20 | dbdir="/path/to/duc/dbs" 21 | wwwdir="/var/www/html" 22 | duc="/usr/local/bin/duc" 23 | 24 | # Sort index by time, or by db file name. It's just the arg to 'ls' 25 | sortby="-t" 26 | 27 | # If these are not empty, use these files in $wwwdir for the .cgi file's --header 28 | # and/or --footer options of 'duc cgi ...'. 29 | footer="" 30 | header="" 31 | 32 | 33 | # Option processing 34 | while getopts "g" opt; do 35 | case $opt in 36 | g) 37 | echo "Generating new cgi scripts in $CWD from DBs in $dbdir/." 38 | mkcgi=1 39 | ;; 40 | \?) 41 | echo "Invalid option: -$OPTARG" >&2 42 | ;; 43 | esac 44 | done 45 | 46 | 47 | # Now do the work, 48 | cd $dbdir 49 | 50 | if [ "$mkcgi" = "0" ]; then 51 | echo "Content-type: text/html" 52 | echo "" 53 | echo "" 54 | echo "" 58 | echo "" 59 | echo "$title" 60 | echo "" 61 | echo "" 62 | echo " " 63 | echo " " 64 | echo " " 65 | echo " " 66 | echo " " 67 | echo " " 68 | echo " " 69 | echo " " 70 | echo "" 71 | fi 72 | 73 | for db in `ls $sortby *.db` 74 | do 75 | base=`echo $db | awk -F. '{print $1}'` 76 | array=(`$duc info -d ${db} | tail -1 `) 77 | 78 | if [ "$mkcgi" = "0" ]; then 79 | echo " " 80 | echo " " 81 | echo " " 82 | echo " " 83 | echo " " 84 | echo " " 85 | echo " " 86 | echo " " 87 | fi 88 | 89 | 90 | if [ "$mkcgi" = "1" ]; then 91 | cgi="${wwwdir}/${base}.cgi" 92 | echo " $cgi" 93 | cat < $cgi 94 | #!/bin/sh 95 | 96 | args="-d /data/sjscratch/is/duc/${db} --palette=rainbow --list --header=header.htm --footer=footer.htm" 97 | 98 | /usr/local/bin/duc cgi \$args 99 | 100 | EOF 101 | 102 | chmod +x $cgi 103 | fi 104 | done 105 | 106 | if [ "$mkcgi" = "0" ]; then 107 | echo "
PathSizeFilesDirsDateTime
${array[5]}${array[4]}${array[2]}${array[3]}${array[0]}${array[1]}
" 108 | echo "" 109 | echo "" 110 | fi 111 | 112 | -------------------------------------------------------------------------------- /gentoo/duc-1.4.1.ebuild: -------------------------------------------------------------------------------- 1 | # Copyright 1999-2016 Gentoo Foundation 2 | # Distributed under the terms of the GNU General Public License v2 3 | # $Id$ 4 | 5 | EAPI=5 6 | 7 | AUTOTOOLS_AUTORECONF="true" 8 | AUTOTOOLS_IN_SOURCE_BUILD="true" 9 | inherit autotools-utils 10 | 11 | if [[ "${PV}" == "9999" ]]; then 12 | inherit git-r3 13 | EGIT_REPO_URI="https://github.com/zevv/duc.git" 14 | KEYWORDS="" 15 | else 16 | KEYWORDS="~amd64 ~x86" 17 | SRC_URI="https://github.com/zevv/${PN}/releases/download/${PV}/${P}.tar.gz" 18 | fi 19 | 20 | DESCRIPTION="A library and suite of tools for inspecting disk usage" 21 | HOMEPAGE="https://github.com/zevv/duc" 22 | 23 | LICENSE="GPL-2" 24 | SLOT="0" 25 | IUSE="cairo gui -leveldb ncurses -sqlite +tokyocabinet X" 26 | 27 | REQUIRED_USE=" 28 | ^^ ( tokyocabinet leveldb sqlite ) 29 | X? ( cairo gui ) 30 | " 31 | 32 | DEPEND=" 33 | cairo? ( x11-libs/cairo x11-libs/pango ) 34 | gui? ( 35 | X? ( 36 | x11-libs/cairo[X] 37 | x11-libs/libX11 38 | x11-libs/pango[X] 39 | ) 40 | !X? ( virtual/opengl ) 41 | ) 42 | leveldb? ( dev-libs/leveldb ) 43 | ncurses? ( sys-libs/ncurses:= ) 44 | sqlite? ( dev-db/sqlite:3 ) 45 | tokyocabinet? ( dev-db/tokyocabinet ) 46 | " 47 | RDEPEND="${DEPEND}" 48 | 49 | src_unpack() { 50 | if [[ "${PV}" == "9999" ]]; then 51 | git-r3_src_unpack 52 | else 53 | unpack ${A} 54 | fi 55 | } 56 | 57 | src_prepare() { 58 | sed -i -e "/ldconfig/d" -e "/install-exec-hook/d" Makefile.am || die 59 | 60 | autotools-utils_src_prepare 61 | } 62 | 63 | src_configure() { 64 | local myconf=( 65 | --disable-static 66 | $(use_with ncurses ui) 67 | ) 68 | 69 | if use tokyocabinet; then 70 | myconf+=( --with-db-backend=tokyocabinet ) 71 | elif use leveldb; then 72 | myconf+=( --with-db-backend=leveldb ) 73 | else 74 | myconf+=( --with-db-backend=sqlite3 ) 75 | fi 76 | 77 | # Necessary logic for cairo 78 | if use gui && use X; then 79 | # X backend GUI 80 | myconf+=( --enable-x11 --disable-opengl --enable-cairo ) 81 | elif use gui; then 82 | # OpenGL backend GUI 83 | myconf+=( --disable-x11 --enable-opengl $(use_enable cairo) ) 84 | else 85 | # No GUI 86 | myconf+=( $(use_enable cairo) ) 87 | fi 88 | 89 | autotools-utils_src_configure 90 | } 91 | -------------------------------------------------------------------------------- /gentoo/duc-9999.ebuild: -------------------------------------------------------------------------------- 1 | # Copyright 1999-2016 Gentoo Foundation 2 | # Distributed under the terms of the GNU General Public License v2 3 | # $Id$ 4 | 5 | EAPI=5 6 | 7 | AUTOTOOLS_AUTORECONF="true" 8 | AUTOTOOLS_IN_SOURCE_BUILD="true" 9 | inherit autotools-utils 10 | 11 | if [[ "${PV}" == "9999" ]]; then 12 | inherit git-r3 13 | EGIT_REPO_URI="https://github.com/zevv/duc.git" 14 | KEYWORDS="" 15 | else 16 | KEYWORDS="~amd64 ~x86" 17 | SRC_URI="https://github.com/zevv/${PN}/releases/download/${PV}/${P}.tar.gz" 18 | fi 19 | 20 | DESCRIPTION="A library and suite of tools for inspecting disk usage" 21 | HOMEPAGE="https://github.com/zevv/duc" 22 | 23 | LICENSE="GPL-2" 24 | SLOT="0" 25 | IUSE="cairo gui -leveldb ncurses -sqlite +tokyocabinet X" 26 | 27 | REQUIRED_USE=" 28 | ^^ ( tokyocabinet leveldb sqlite ) 29 | X? ( cairo gui ) 30 | " 31 | 32 | DEPEND=" 33 | cairo? ( x11-libs/cairo x11-libs/pango ) 34 | gui? ( 35 | X? ( 36 | x11-libs/cairo[X] 37 | x11-libs/libX11 38 | x11-libs/pango[X] 39 | ) 40 | !X? ( virtual/opengl ) 41 | ) 42 | leveldb? ( dev-libs/leveldb ) 43 | ncurses? ( sys-libs/ncurses:= ) 44 | sqlite? ( dev-db/sqlite:3 ) 45 | tokyocabinet? ( dev-db/tokyocabinet ) 46 | " 47 | RDEPEND="${DEPEND}" 48 | 49 | src_unpack() { 50 | if [[ "${PV}" == "9999" ]]; then 51 | git-r3_src_unpack 52 | else 53 | unpack ${A} 54 | fi 55 | } 56 | 57 | src_prepare() { 58 | sed -i -e "/ldconfig/d" -e "/install-exec-hook/d" Makefile.am || die 59 | 60 | autotools-utils_src_prepare 61 | } 62 | 63 | src_configure() { 64 | local myconf=( 65 | --disable-static 66 | $(use_with ncurses ui) 67 | ) 68 | 69 | if use tokyocabinet; then 70 | myconf+=( --with-db-backend=tokyocabinet ) 71 | elif use leveldb; then 72 | myconf+=( --with-db-backend=leveldb ) 73 | else 74 | myconf+=( --with-db-backend=sqlite3 ) 75 | fi 76 | 77 | # Necessary logic for cairo 78 | if use gui && use X; then 79 | # X backend GUI 80 | myconf+=( --enable-x11 --disable-opengl --enable-cairo ) 81 | elif use gui; then 82 | # OpenGL backend GUI 83 | myconf+=( --disable-x11 --enable-opengl $(use_enable cairo) ) 84 | else 85 | # No GUI 86 | myconf+=( $(use_enable cairo) ) 87 | fi 88 | 89 | autotools-utils_src_configure 90 | } 91 | -------------------------------------------------------------------------------- /gentoo/metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NP-Hardass@gentoo.org 6 | NP-Hardass 7 | 8 | 9 | duc@zevv.nl 10 | Ico Doornekamp 11 | 12 | 13 | Build support for exporting to PNG and PDF with x11-libs/cairo 14 | Build support for a graphical client 15 | Use dev-libs/leveldb as the backend 16 | Build support for the ncurses client 17 | Use dev-db/sqlite:3 as the backend 18 | Use dev-db/tokyocabinet as the backend 19 | Use X11 for the GUI instead of OpenGL. This is the recommended option 20 | 21 | 22 | zevv/duc 23 | 24 | 25 | -------------------------------------------------------------------------------- /img/duc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/duc.png -------------------------------------------------------------------------------- /img/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/example.png -------------------------------------------------------------------------------- /img/fuzz-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/fuzz-off.png -------------------------------------------------------------------------------- /img/fuzz-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/fuzz-on.png -------------------------------------------------------------------------------- /img/palette-greyscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/palette-greyscale.png -------------------------------------------------------------------------------- /img/palette-monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/palette-monochrome.png -------------------------------------------------------------------------------- /img/palette-rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/palette-rainbow.png -------------------------------------------------------------------------------- /img/palette-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/palette-size.png -------------------------------------------------------------------------------- /img/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/img/ui.png -------------------------------------------------------------------------------- /src/duc/cmd-graph.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "duc.h" 16 | #include "duc-graph.h" 17 | #include "cmd.h" 18 | 19 | static char *opt_database = NULL; 20 | static bool opt_apparent = false; 21 | static bool opt_count = false; 22 | static int opt_size = 800; 23 | static double opt_fuzz = 0.7; 24 | static int opt_levels = 4; 25 | static char *opt_output = NULL; 26 | static char *opt_palette = NULL; 27 | static enum duc_graph_palette palette = 0; 28 | static int opt_ring_gap = 4; 29 | static bool opt_gradient = false; 30 | static double opt_dpi = 96.0; 31 | 32 | #ifdef ENABLE_CAIRO 33 | static char *opt_format = "png"; 34 | #else 35 | static char *opt_format = "svg"; 36 | #endif 37 | 38 | static int graph_main(duc *duc, int argc, char **argv) 39 | { 40 | char *path_out = opt_output; 41 | char *path_out_default = "duc.png"; 42 | 43 | duc_graph_file_format format = DUC_GRAPH_FORMAT_PNG; 44 | 45 | if(strcasecmp(opt_format, "html") == 0) { 46 | format = DUC_GRAPH_FORMAT_HTML; 47 | path_out_default = "duc.html"; 48 | } 49 | 50 | if(strcasecmp(opt_format, "svg") == 0) { 51 | format = DUC_GRAPH_FORMAT_SVG; 52 | path_out_default = "duc.svg"; 53 | } 54 | 55 | if(strcasecmp(opt_format, "pdf") == 0) { 56 | format = DUC_GRAPH_FORMAT_PDF; 57 | path_out_default = "duc.pdf"; 58 | } 59 | 60 | if(opt_palette) { 61 | char c = tolower(opt_palette[0]); 62 | if(c == 's') palette = DUC_GRAPH_PALETTE_SIZE; 63 | if(c == 'r') palette = DUC_GRAPH_PALETTE_RAINBOW; 64 | if(c == 'g') palette = DUC_GRAPH_PALETTE_GREYSCALE; 65 | if(c == 'm') palette = DUC_GRAPH_PALETTE_MONOCHROME; 66 | if(c == 'c') palette = DUC_GRAPH_PALETTE_CLASSIC; 67 | } 68 | 69 | if(path_out == NULL) path_out = path_out_default; 70 | 71 | char *path = "."; 72 | if(argc > 0) path = argv[0]; 73 | 74 | int r = duc_open(duc, opt_database, DUC_OPEN_RO); 75 | if(r != DUC_OK) { 76 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 77 | return -1; 78 | } 79 | 80 | duc_dir *dir = duc_dir_open(duc, path); 81 | if(dir == NULL) { 82 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 83 | return -1; 84 | } 85 | 86 | FILE *f = NULL; 87 | if(strcmp(path_out, "-") == 0) { 88 | f = stdout; 89 | } else { 90 | f = fopen(path_out, "w"); 91 | } 92 | 93 | if(f == NULL) { 94 | duc_log(duc, DUC_LOG_FTL, "Error opening output file: %s", strerror(errno)); 95 | return -1; 96 | } 97 | 98 | duc_graph *graph; 99 | 100 | switch(format) { 101 | case DUC_GRAPH_FORMAT_SVG: 102 | graph = duc_graph_new_svg(duc, f); 103 | break; 104 | case DUC_GRAPH_FORMAT_HTML: 105 | graph = duc_graph_new_html(duc, f, 1); 106 | break; 107 | #ifdef ENABLE_CAIRO 108 | case DUC_GRAPH_FORMAT_PNG: 109 | case DUC_GRAPH_FORMAT_PDF: 110 | graph = duc_graph_new_cairo_file(duc, format, f); 111 | break; 112 | #endif 113 | default: 114 | duc_log(duc, DUC_LOG_FTL, "Requested image format is not supported"); 115 | exit(1); 116 | break; 117 | } 118 | 119 | duc_size_type st = opt_count ? DUC_SIZE_TYPE_COUNT : 120 | opt_apparent ? DUC_SIZE_TYPE_APPARENT : DUC_SIZE_TYPE_ACTUAL; 121 | 122 | duc_graph_set_size(graph, opt_size, opt_size); 123 | duc_graph_set_dpi(graph, opt_dpi); 124 | duc_graph_set_fuzz(graph, opt_fuzz); 125 | duc_graph_set_max_level(graph, opt_levels); 126 | duc_graph_set_palette(graph, palette); 127 | duc_graph_set_size_type(graph, st); 128 | duc_graph_set_ring_gap(graph, opt_ring_gap); 129 | duc_graph_set_gradient(graph, opt_gradient); 130 | 131 | duc_graph_draw(graph, dir); 132 | 133 | duc_graph_free(graph); 134 | duc_dir_close(dir); 135 | duc_close(duc); 136 | 137 | return 0; 138 | } 139 | 140 | 141 | static struct ducrc_option options[] = { 142 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "Show apparent instead of actual file size" }, 143 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 144 | { &opt_count, "count", 0, DUCRC_TYPE_BOOL, "show number of files instead of file size" }, 145 | { &opt_dpi, "dpi", 0 , DUCRC_TYPE_DOUBLE, "set destination resolution in DPI [96.0]" }, 146 | { &opt_format, "format", 'f', DUCRC_TYPE_STRING, 147 | #ifdef ENABLE_CAIRO 148 | "select output format [png]" }, 149 | #else 150 | "select output format [svg]" }, 151 | #endif 152 | { &opt_fuzz, "fuzz", 0, DUCRC_TYPE_DOUBLE, "use radius fuzz factor when drawing graph [0.7]" }, 153 | { &opt_gradient, "gradient", 0, DUCRC_TYPE_BOOL, "draw graph with color gradient" }, 154 | { &opt_levels, "levels", 'l', DUCRC_TYPE_INT, "draw up to ARG levels deep [4]" }, 155 | { &opt_output, "output", 'o', DUCRC_TYPE_STRING, "output file name [duc.png]" }, 156 | { &opt_palette, "palette", 0, DUCRC_TYPE_STRING, "select palette", 157 | "available palettes are: size, rainbow, greyscale, monochrome, classic" }, 158 | { &opt_ring_gap, "ring-gap", 0, DUCRC_TYPE_INT, "leave a gap of VAL pixels between rings" }, 159 | { &opt_size, "size", 's', DUCRC_TYPE_INT, "image size [800]" }, 160 | { NULL } 161 | }; 162 | 163 | struct cmd cmd_graph = { 164 | .name = "graph", 165 | .descr_short = "Generate a sunburst graph for a given path", 166 | .usage = "[options] [PATH]", 167 | .main = graph_main, 168 | .options = options, 169 | .descr_long = 170 | "The 'graph' subcommand queries the duc database and generates a sunburst graph\n" 171 | "showing the disk usage of the given path. If no path is given a graph is created\n" 172 | "for the current working directory.\n" 173 | "\n" 174 | "By default the graph is written to the file 'duc.png', which can be overridden by\n" 175 | "using the -o/--output option. The output can be sent to stdout by using the special\n" 176 | "file name '-'.\n" 177 | }; 178 | 179 | /* 180 | * End 181 | */ 182 | 183 | -------------------------------------------------------------------------------- /src/duc/cmd-guigl.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifdef ENABLE_OPENGL 4 | 5 | #include "duc.h" 6 | #include "duc-graph.h" 7 | #include "ducrc.h" 8 | #include "cmd.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | static bool opt_bytes = false; 21 | static bool opt_dark = false; 22 | static char *opt_database = NULL; 23 | static char *opt_palette = NULL; 24 | static double opt_fuzz = 0.5; 25 | static int opt_levels = 4; 26 | static bool opt_apparent = false; 27 | static bool opt_count = false; 28 | static int opt_ring_gap = 4; 29 | static bool opt_gradient = false; 30 | 31 | static double tooltip_x = 0; 32 | static double tooltip_y = 0; 33 | static enum duc_graph_palette palette = 0; 34 | static int win_w = 600; 35 | static int win_h = 600; 36 | static duc_dir *dir; 37 | static duc_graph *graph; 38 | static double fuzz; 39 | 40 | 41 | static void sc2fb(GLFWwindow* window, double *x, double *y) 42 | { 43 | int w1, h1, w2, h2; 44 | glfwGetFramebufferSize(window, &w1, &h1); 45 | glfwGetWindowSize(window, &w2, &h2); 46 | if(x) *x *= (double)w1 / (double)w2; 47 | if(y) *y *= (double)h1 / (double)h2; 48 | } 49 | 50 | 51 | void cb_winsize(GLFWwindow* window, int w, int h) 52 | { 53 | win_w = w; 54 | win_h = h; 55 | glViewport(0, 0, win_w, win_h); 56 | } 57 | 58 | 59 | static void draw(GLFWwindow *window) 60 | { 61 | if(opt_levels < 1) opt_levels = 1; 62 | if(opt_levels > 10) opt_levels = 10; 63 | 64 | duc_size_type st = opt_count ? DUC_SIZE_TYPE_COUNT : 65 | opt_apparent ? DUC_SIZE_TYPE_APPARENT : DUC_SIZE_TYPE_ACTUAL; 66 | 67 | duc_graph_set_size(graph, win_w, win_h); 68 | duc_graph_set_max_level(graph, opt_levels); 69 | duc_graph_set_fuzz(graph, fuzz); 70 | duc_graph_set_palette(graph, palette); 71 | duc_graph_set_max_name_len(graph, 30); 72 | duc_graph_set_size_type(graph, st); 73 | duc_graph_set_exact_bytes(graph, opt_bytes); 74 | duc_graph_set_tooltip(graph, tooltip_x, tooltip_y); 75 | duc_graph_set_ring_gap(graph, opt_ring_gap); 76 | duc_graph_set_gradient(graph, opt_gradient); 77 | 78 | if(opt_dark) { 79 | glClearColor(0, 0, 0, 1); 80 | } else { 81 | glClearColor(1, 1, 1, 1); 82 | } 83 | 84 | glClear(GL_COLOR_BUFFER_BIT); 85 | 86 | duc_graph_draw(graph, dir); 87 | 88 | glfwSwapBuffers(window); 89 | } 90 | 91 | 92 | static void cb_keyboard(GLFWwindow* window, int k, int scancode, int action, int mods) 93 | { 94 | if(action != 1) return; 95 | 96 | if(k == '-') opt_levels--; 97 | if(k == '=') opt_levels++; 98 | if(k == '0') opt_levels = 4; 99 | if(k == GLFW_KEY_ESCAPE) exit(0); 100 | if(k == 'Q') exit(0); 101 | if(k == 'A') opt_apparent = !opt_apparent; 102 | if(k == 'C') opt_count = !opt_count; 103 | if(k == 'B') opt_bytes = !opt_bytes; 104 | if(k == 'F') fuzz = (fuzz == 0) ? opt_fuzz : 0; 105 | if(k == 'G') opt_gradient = !opt_gradient; 106 | if(k == ',') if(opt_ring_gap > 0) opt_ring_gap --; 107 | if(k == '.') opt_ring_gap ++; 108 | if(k == 'P') palette = (palette + 1) % 5; 109 | if(k == GLFW_KEY_BACKSPACE) { 110 | duc_dir *dir2 = duc_dir_openat(dir, ".."); 111 | if(dir2) { 112 | duc_dir_close(dir); 113 | dir = dir2; 114 | } 115 | } 116 | } 117 | 118 | 119 | void cb_mouse_button(GLFWwindow* window, int b, int action, int mods) 120 | { 121 | if(action != 1) return; 122 | 123 | double x, y; 124 | glfwGetCursorPos(window, &x, &y); 125 | sc2fb(window, &x, &y); 126 | 127 | if(b == 0) { 128 | duc_dir *dir2 = duc_graph_find_spot(graph, dir, x, y, NULL); 129 | if(dir2) { 130 | duc_dir_close(dir); 131 | dir = dir2; 132 | } 133 | } 134 | if(b == 3) { 135 | duc_dir *dir2 = duc_dir_openat(dir, ".."); 136 | if(dir2) { 137 | duc_dir_close(dir); 138 | dir = dir2; 139 | } 140 | } 141 | } 142 | 143 | void cb_scroll(GLFWwindow* window, double xoffset, double yoffset) 144 | { 145 | static double scroll = 0; 146 | scroll += yoffset; 147 | if(scroll < -1) { opt_levels --; scroll += 1; } 148 | if(scroll > +1) { opt_levels ++; scroll -= 1; } 149 | } 150 | 151 | 152 | void cb_mouse_motion(GLFWwindow* window, double x, double y) 153 | { 154 | sc2fb(window, &x, &y); 155 | tooltip_x = x; 156 | tooltip_y = y; 157 | } 158 | 159 | 160 | int guigl_main(duc *duc, int argc, char *argv[]) 161 | { 162 | char *path = "."; 163 | if(argc > 0) path = argv[0]; 164 | 165 | fuzz = opt_fuzz; 166 | 167 | if(opt_palette) { 168 | char c = tolower(opt_palette[0]); 169 | if(c == 's') palette = DUC_GRAPH_PALETTE_SIZE; 170 | if(c == 'r') palette = DUC_GRAPH_PALETTE_RAINBOW; 171 | if(c == 'g') palette = DUC_GRAPH_PALETTE_GREYSCALE; 172 | if(c == 'm') palette = DUC_GRAPH_PALETTE_MONOCHROME; 173 | if(c == 'c') palette = DUC_GRAPH_PALETTE_CLASSIC; 174 | } 175 | 176 | int r = duc_open(duc, opt_database, DUC_OPEN_RO); 177 | if(r != DUC_OK) { 178 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 179 | return -1; 180 | } 181 | 182 | dir = duc_dir_open(duc, path); 183 | if(dir == NULL) { 184 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 185 | return -1; 186 | } 187 | 188 | if(!glfwInit()) { 189 | duc_log(duc, DUC_LOG_FTL, "Error initializen glfw"); 190 | return -1; 191 | } 192 | 193 | glfwWindowHint(GLFW_SAMPLES, 4); 194 | 195 | GLFWwindow* window = window = glfwCreateWindow(640, 480, "Duc", NULL, NULL);; 196 | if(window == NULL) 197 | { 198 | duc_log(duc, DUC_LOG_FTL, "Error creating glfw window"); 199 | glfwTerminate(); 200 | return -1; 201 | } 202 | 203 | glfwMakeContextCurrent(window); 204 | gladLoadGLES2Loader((GLADloadproc) glfwGetProcAddress); 205 | 206 | glfwGetFramebufferSize(window, &win_w, &win_h); 207 | 208 | double font_scale = 1.0; 209 | sc2fb(window, &font_scale, NULL); 210 | graph = duc_graph_new_opengl(duc, font_scale); 211 | 212 | GLFWmonitor* mon = glfwGetPrimaryMonitor(); 213 | const GLFWvidmode* mode = glfwGetVideoMode(mon); 214 | if(mode) { 215 | int w_mm = 0, h_mm = 0; 216 | glfwGetMonitorPhysicalSize(mon, &w_mm, &h_mm); 217 | if(mode && mode->width && w_mm) { 218 | double dpi = 25.4 * mode->width / w_mm; 219 | printf("dpi %f\n", dpi); 220 | duc_graph_set_dpi(graph, dpi); 221 | } 222 | } 223 | 224 | glfwSetKeyCallback(window, cb_keyboard); 225 | glfwSetFramebufferSizeCallback(window, cb_winsize); 226 | glfwSetMouseButtonCallback(window, cb_mouse_button); 227 | glfwSetCursorPosCallback(window, cb_mouse_motion); 228 | glfwSetScrollCallback(window, cb_scroll); 229 | 230 | while (!glfwWindowShouldClose(window)) { 231 | draw(window); 232 | glfwWaitEvents(); 233 | } 234 | 235 | glfwTerminate(); 236 | 237 | return 0; 238 | } 239 | 240 | static struct ducrc_option options[] = { 241 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "show apparent instead of actual file size" }, 242 | { &opt_bytes, "bytes", 'b', DUCRC_TYPE_BOOL, "show file size in exact number of bytes" }, 243 | { &opt_count, "count", 0, DUCRC_TYPE_BOOL, "show number of files instead of file size" }, 244 | { &opt_dark, "dark", 0, DUCRC_TYPE_BOOL, "use dark background color" }, 245 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 246 | { &opt_fuzz, "fuzz", 0, DUCRC_TYPE_DOUBLE, "use radius fuzz factor when drawing graph" }, 247 | { &opt_gradient, "gradient", 0, DUCRC_TYPE_BOOL, "draw graph with color gradient" }, 248 | { &opt_levels, "levels", 'l', DUCRC_TYPE_INT, "draw up to VAL levels deep [4]" }, 249 | { &opt_palette, "palette", 0, DUCRC_TYPE_STRING, "select palette", 250 | "available palettes are: size, rainbow, greyscale, monochrome, classic" }, 251 | { &opt_ring_gap, "ring-gap", 0, DUCRC_TYPE_INT, "leave a gap of VAL pixels between rings" }, 252 | { NULL } 253 | }; 254 | 255 | 256 | struct cmd cmd_guigl = { 257 | .name = "gui", 258 | .descr_short = "Interactive OpenGL graphical interface", 259 | .usage = "[options] [PATH]", 260 | .main = guigl_main, 261 | .options = options, 262 | .descr_long = 263 | "The 'guigl' subcommand queries the duc database and runs an interactive graphical\n" 264 | "utility for exploring the disk usage of the given path. If no path is given the\n" 265 | "current working directory is explored.\n" 266 | "\n" 267 | "The following keys can be used to navigate and alter the graph:\n" 268 | "\n" 269 | " + increase maximum graph depth\n" 270 | " - decrease maximum graph depth\n" 271 | " 0 Set default graph depth\n" 272 | " a Toggle between apparent and actual disk usage\n" 273 | " b Toggle between exact byte count and abbreviated sizes\n" 274 | " c Toggle between file size and file count\n" 275 | " f toggle graph fuzz\n" 276 | " g toggle graph gradient\n" 277 | " p toggle palettes\n" 278 | " backspace go up one directory\n" 279 | 280 | }; 281 | 282 | #endif 283 | 284 | /* 285 | * End 286 | */ 287 | -------------------------------------------------------------------------------- /src/duc/cmd-histogram.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "cmd.h" 16 | #include "duc.h" 17 | 18 | static bool opt_apparent = false; 19 | static bool opt_base = false; 20 | static bool opt_bytes = false; 21 | static char *opt_database = NULL; 22 | 23 | static int histogram_db(duc *duc, char *file) 24 | { 25 | struct duc_index_report *report; 26 | duc_size_type st = opt_apparent ? DUC_SIZE_TYPE_APPARENT : DUC_SIZE_TYPE_ACTUAL; 27 | int i = 0; 28 | 29 | int r = duc_open(duc, file, DUC_OPEN_RO); 30 | if(r != DUC_OK) { 31 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 32 | return -1; 33 | } 34 | 35 | while(( report = duc_get_report(duc, i)) != NULL) { 36 | 37 | printf("Path: %s\n%3s %10s %10s\n",report->path,"Bkt","Size","Count"); 38 | 39 | size_t count; 40 | size_t bucket_size = 0; 41 | char pretty[32]; 42 | setlocale(LC_NUMERIC, ""); 43 | for (int i=0; i < report->histogram_buckets; i++) { 44 | count = report->histogram[i]; 45 | bucket_size = pow(2, i); 46 | int ret = humanize(bucket_size, 0, 1024, pretty, sizeof pretty); 47 | printf("%3d %10s %'10d\n",i, pretty, count); 48 | } 49 | 50 | duc_index_report_free(report); 51 | printf("\n"); 52 | i++; 53 | } 54 | 55 | duc_close(duc); 56 | 57 | return 0; 58 | } 59 | 60 | 61 | static int histogram_main(duc *duc, int argc, char **argv) 62 | { 63 | return(histogram_db(duc, opt_database)); 64 | } 65 | 66 | 67 | static struct ducrc_option options[] = { 68 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "show apparent instead of actual file size" }, 69 | { &opt_bytes, "bytes", 'b', DUCRC_TYPE_BOOL, "show bucket size in exact number of bytes" }, 70 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 71 | { &opt_base, "base10", 't', DUCRC_TYPE_BOOL, "show histogram in base 10 bucket spacing, default base2 bucket sizes." }, 72 | { NULL } 73 | }; 74 | 75 | 76 | struct cmd cmd_histogram = { 77 | .name = "histogram", 78 | .descr_short = "Dump histogram of file sizes found.", 79 | .usage = "[options]", 80 | .main = histogram_main, 81 | .options = options, 82 | }; 83 | 84 | 85 | /* 86 | * End 87 | */ 88 | 89 | -------------------------------------------------------------------------------- /src/duc/cmd-index.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "cmd.h" 18 | #include "duc.h" 19 | #include "ducrc.h" 20 | 21 | 22 | static bool opt_bytes = false; 23 | static char *opt_database = NULL; 24 | static bool opt_force = false; 25 | static bool opt_check_hard_links = false; 26 | static bool opt_hide_file_names = false; 27 | static char *opt_username = NULL; 28 | static int opt_uid = 0; 29 | static int opt_histogram_buckets = DUC_HISTOGRAM_BUCKETS_DEF; 30 | static int opt_max_depth = 0; 31 | static int opt_topn_min_size = DUC_TOPN_MIN_FILE_SIZE; 32 | static int opt_topn_cnt = DUC_TOPN_CNT; 33 | static int opt_topn_cnt_max = DUC_TOPN_CNT_MAX; 34 | static bool opt_one_file_system = false; 35 | static bool opt_progress = false; 36 | static bool opt_uncompressed = false; 37 | static bool opt_dryrun = false; 38 | static duc_index_req *req; 39 | 40 | 41 | static int index_init(duc *duc, int argc, char **argv) 42 | { 43 | req = duc_index_req_new(duc); 44 | 45 | return 0; 46 | } 47 | 48 | 49 | void progress_cb(struct duc_index_report *rep, void *ptr) 50 | { 51 | static int n = 0; 52 | char meter[] = "--------"; 53 | int i = 7 - abs(n-7); 54 | meter[i] = '#'; 55 | n = (n+1) % 14; 56 | 57 | char siz[32], fs[32], ds[32]; 58 | duc_human_size(&rep->size, DUC_SIZE_TYPE_ACTUAL, opt_bytes, siz, sizeof siz); 59 | duc_human_number(rep->file_count, opt_bytes, fs, sizeof fs); 60 | duc_human_number(rep->dir_count, opt_bytes, ds, sizeof ds); 61 | 62 | printf("\e[K[%s] Indexed %sb in %s files and %s directories\r", meter, siz, fs, ds); 63 | fflush(stdout); 64 | } 65 | 66 | 67 | static void log_callback(duc_log_level level, const char *fmt, va_list va) 68 | { 69 | vfprintf(stderr, fmt, va); 70 | fprintf(stderr, "\e[K\n"); 71 | } 72 | 73 | 74 | static int index_main(duc *duc, int argc, char **argv) 75 | { 76 | duc_index_flags index_flags = 0; 77 | int open_flags = DUC_OPEN_RW | DUC_OPEN_COMPRESS; 78 | 79 | if(opt_force) open_flags |= DUC_OPEN_FORCE; 80 | if(opt_max_depth) duc_index_req_set_maxdepth(req, opt_max_depth); 81 | if(opt_one_file_system) index_flags |= DUC_INDEX_XDEV; 82 | if(opt_hide_file_names) index_flags |= DUC_INDEX_HIDE_FILE_NAMES; 83 | if(opt_check_hard_links) index_flags |= DUC_INDEX_CHECK_HARD_LINKS; 84 | if(opt_uncompressed) open_flags &= ~DUC_OPEN_COMPRESS; 85 | if(opt_dryrun) index_flags |= DUC_INDEX_DRY_RUN; 86 | if(opt_username) duc_index_req_set_username(req, opt_username); 87 | if(opt_uid) duc_index_req_set_uid(req, opt_uid); 88 | if(opt_topn_cnt) { 89 | if (opt_topn_cnt > DUC_TOPN_CNT_MAX) { 90 | duc_log(duc, DUC_LOG_FTL, "Cannot store more than %d topN files", DUC_TOPN_CNT_MAX); 91 | duc_index_req_set_topn(req, DUC_TOPN_CNT_MAX); 92 | } 93 | duc_index_req_set_topn(req, opt_topn_cnt); 94 | // For now always index topN files. 95 | index_flags |= DUC_INDEX_TOPN_FILES; 96 | } 97 | 98 | if(opt_histogram_buckets) { 99 | if (opt_histogram_buckets > DUC_HISTOGRAM_BUCKETS_MAX) { 100 | duc_log(duc, DUC_LOG_FTL, "Cannot use more than %d histogram buckets.", DUC_HISTOGRAM_BUCKETS_MAX); 101 | duc_index_req_set_buckets(req,DUC_HISTOGRAM_BUCKETS_MAX); 102 | } 103 | duc_index_req_set_buckets(req,opt_histogram_buckets); 104 | } 105 | 106 | if(argc < 1) { 107 | duc_log(duc, DUC_LOG_FTL, "Required index PATH missing."); 108 | return -2; 109 | } 110 | 111 | if(opt_progress) { 112 | duc_index_req_set_progress_cb(req, progress_cb, NULL); 113 | duc_set_log_callback(duc, log_callback); 114 | } 115 | 116 | int c; 117 | for (c=0; c 1000000) { 128 | duc_log(duc, DUC_LOG_INF, "Found big filesystem"); 129 | open_flags |= DUC_FS_BIG; 130 | } 131 | if (iused > 10000000) { 132 | duc_log(duc, DUC_LOG_INF, "Found bigger filesystem"); 133 | open_flags |= DUC_FS_BIGGER; 134 | } 135 | if (iused > 100000000) { 136 | duc_log(duc, DUC_LOG_INF, "Found biggest filesystem"); 137 | open_flags |= DUC_FS_BIGGEST; 138 | } 139 | } 140 | 141 | int r = duc_open(duc, opt_database, open_flags); 142 | if(r != DUC_OK) { 143 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 144 | return -1; 145 | } 146 | 147 | /* Index all paths passed on the cmdline */ 148 | 149 | int i; 150 | for(i=0; isize, DUC_SIZE_TYPE_APPARENT, opt_bytes, siz_apparent, sizeof siz_apparent); 161 | duc_human_size(&report->size, DUC_SIZE_TYPE_ACTUAL, opt_bytes, siz_actual, sizeof siz_actual); 162 | 163 | if(r == DUC_OK) { 164 | char dur[64]; 165 | duc_human_duration(report->time_start, report->time_stop, dur, sizeof dur); 166 | duc_log(duc, DUC_LOG_INF, 167 | "Indexed %zu files and %zu directories, (%sB apparent, %sB actual) in %s", 168 | report->file_count, 169 | report->dir_count, 170 | siz_apparent, 171 | siz_actual, 172 | dur); 173 | } else { 174 | duc_log(duc, DUC_LOG_WRN, "An error occurred while indexing: %s", duc_strerror(duc)); 175 | } 176 | 177 | /* Prevent final output of progress_cb() from being overwritten with the shell's prompt */ 178 | if (opt_progress) { 179 | fputc ('\n', stdout); 180 | fflush (stdout); 181 | } 182 | 183 | duc_index_report_free(report); 184 | } 185 | 186 | duc_close(duc); 187 | duc_index_req_free(req); 188 | 189 | return 0; 190 | } 191 | 192 | 193 | static void fn_exclude(const char *val) 194 | { 195 | duc_index_req_add_exclude(req, val); 196 | } 197 | 198 | 199 | static void fn_fs_include(const char *val) 200 | { 201 | duc_index_req_add_fstype_include(req, val); 202 | } 203 | 204 | 205 | static void fn_fs_exclude(const char *val) 206 | { 207 | duc_index_req_add_fstype_exclude(req, val); 208 | } 209 | 210 | 211 | static struct ducrc_option options[] = { 212 | { &opt_bytes, "bytes", 'b', DUCRC_TYPE_BOOL, "show file size in exact number of bytes" }, 213 | { &opt_histogram_buckets, "buckets", 'B', DUCRC_TYPE_INT, "number of buckets in histogram, default XX" }, 214 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "use database file VAL" }, 215 | { fn_exclude, "exclude", 'e', DUCRC_TYPE_FUNC, "exclude files matching VAL" }, 216 | { &opt_check_hard_links,"check-hard-links",'H', DUCRC_TYPE_BOOL, "count hard links only once", 217 | "if two or more hard links point to the same file, only one of the hard links is displayed and counted" }, 218 | { &opt_force, "force", 'f', DUCRC_TYPE_BOOL, "force writing in case of corrupted db" }, 219 | { fn_fs_exclude, "fs-exclude", 0, DUCRC_TYPE_FUNC, "exclude file system type VAL during indexing", 220 | "VAL is a comma separated list of file system types as found in your systems fstab, for example ext3,ext4,dosfs" }, 221 | { fn_fs_include, "fs-include", 0, DUCRC_TYPE_FUNC, "include file system type VAL during indexing", 222 | "VAL is a comma separated list of file system types as found in your systems fstab, for example ext3,ext4,dosfs" }, 223 | { &opt_hide_file_names, "hide-file-names", 0 , DUCRC_TYPE_BOOL, "hide file names in index (privacy)", 224 | "the names of directories will be preserved, but the names of the individual files will be hidden" }, 225 | { &opt_uid, "uid", 'U', DUCRC_TYPE_INT, "limit index to only files/dirs owned by uid" }, 226 | { &opt_topn_cnt, "topn", 'T', DUCRC_TYPE_INT, "Number of topN largest files found to store in index" }, 227 | { &opt_topn_min_size, "topn-min", 'M', DUCRC_TYPE_INT, "Minimum size (in bytes) to make topN list of files by size" }, 228 | { &opt_username, "username", 'u', DUCRC_TYPE_STRING, "limit index to only files/dirs owned by username" }, 229 | { &opt_max_depth, "max-depth", 'm', DUCRC_TYPE_INT, "limit directory names to given depth" , 230 | "when this option is given duc will traverse the complete file system, but will only store the " 231 | "first VAL levels of directories in the database to reduce the size of the index" }, 232 | { &opt_one_file_system, "one-file-system", 'x', DUCRC_TYPE_BOOL, "skip directories on different file systems" }, 233 | { &opt_progress, "progress", 'p', DUCRC_TYPE_BOOL, "show progress during indexing" }, 234 | { &opt_dryrun, "dry-run", 0 , DUCRC_TYPE_BOOL, "do not update database, just crawl" }, 235 | { &opt_uncompressed, "uncompressed", 0 , DUCRC_TYPE_BOOL, "do not use compression for database", 236 | "Duc enables compression if the underlying database supports this. This reduces index size at the cost " 237 | "of slightly longer indexing time" }, 238 | { NULL } 239 | }; 240 | 241 | 242 | struct cmd cmd_index = { 243 | .name = "index", 244 | .descr_short = "Scan the filesystem and generate the Duc index", 245 | .usage = "[options] PATH ...", 246 | .init = index_init, 247 | .main = index_main, 248 | .options = options, 249 | .descr_long = 250 | "The 'index' subcommand performs a recursive scan of the given paths on the\n" 251 | "filesystem and calculates the inclusive size of all directories. The results\n" 252 | "are written to the index, and can later be queried by one of the other duc tools.\n" 253 | }; 254 | 255 | /* 256 | * End 257 | */ 258 | 259 | -------------------------------------------------------------------------------- /src/duc/cmd-info.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "cmd.h" 15 | #include "duc.h" 16 | 17 | static bool opt_apparent = false; 18 | static bool opt_bytes = false; 19 | static char *opt_database = NULL; 20 | static bool opt_histogram = false; 21 | 22 | static int info_db(duc *duc, char *file) 23 | { 24 | struct duc_index_report *report; 25 | duc_size_type st = opt_apparent ? DUC_SIZE_TYPE_APPARENT : DUC_SIZE_TYPE_ACTUAL; 26 | int i = 0; 27 | 28 | int r = duc_open(duc, file, DUC_OPEN_RO); 29 | if(r != DUC_OK) { 30 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 31 | return -1; 32 | } 33 | 34 | printf("Date Time Files Dirs Size Path\n"); 35 | while(( report = duc_get_report(duc, i)) != NULL) { 36 | 37 | char ts[32]; 38 | time_t t = report->time_start.tv_sec; 39 | struct tm *tm = localtime(&t); 40 | strftime(ts, sizeof ts, "%Y-%m-%d %H:%M:%S",tm); 41 | 42 | char siz[32], fs[32], ds[32]; 43 | duc_human_size(&report->size, st, opt_bytes, siz, sizeof siz); 44 | duc_human_number(report->file_count, opt_bytes, fs, sizeof fs); 45 | duc_human_number(report->dir_count, opt_bytes, ds, sizeof ds); 46 | 47 | printf("%s %7s %7s %7s %s\n", ts, fs, ds, siz, report->path); 48 | 49 | if (opt_histogram) { 50 | size_t count; 51 | setlocale(LC_NUMERIC, ""); 52 | printf("\nHistogram:\n----------\n"); 53 | for (int i=0; i < report->histogram_buckets; i++) { 54 | count = report->histogram[i]; 55 | if (count != 0) { 56 | printf("2^%-02d %'d\n",i, count); 57 | } 58 | } 59 | } 60 | 61 | duc_index_report_free(report); 62 | i++; 63 | } 64 | 65 | duc_close(duc); 66 | 67 | return 0; 68 | } 69 | 70 | 71 | static int info_main(duc *duc, int argc, char **argv) 72 | { 73 | return(info_db(duc, opt_database)); 74 | } 75 | 76 | 77 | static struct ducrc_option options[] = { 78 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "show apparent instead of actual file size" }, 79 | { &opt_bytes, "bytes", 'b', DUCRC_TYPE_BOOL, "show file size in exact number of bytes" }, 80 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 81 | { &opt_histogram, "histogram",'H', DUCRC_TYPE_BOOL, "show file size in exact number of bytes" }, 82 | { NULL } 83 | }; 84 | 85 | 86 | struct cmd cmd_info = { 87 | .name = "info", 88 | .descr_short = "Dump database info", 89 | .usage = "[options]", 90 | .main = info_main, 91 | .options = options, 92 | }; 93 | 94 | 95 | /* 96 | * End 97 | */ 98 | 99 | -------------------------------------------------------------------------------- /src/duc/cmd-json.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "cmd.h" 14 | #include "duc.h" 15 | 16 | 17 | static bool opt_apparent = false; 18 | static char *opt_database = NULL; 19 | static double opt_min_size = 0; 20 | static bool opt_exclude_files = false; 21 | 22 | 23 | static void indent(int n) 24 | { 25 | int i; 26 | for(i=0; isize, st); 57 | 58 | if (!first) printf(",\n"); 59 | 60 | if(e->type == DUC_FILE_TYPE_DIR && size>= min_size) { 61 | duc_dir *dir_child = duc_dir_openent(dir, e); 62 | if(dir_child) { 63 | indent(depth); 64 | printf("{\n"); 65 | 66 | indent(depth + 2); 67 | printf("\"name\": \""); 68 | print_escaped(e->name); 69 | printf("\",\n"); 70 | 71 | indent(depth + 2); 72 | printf("\"count\": %jd,\n", e->size.count); 73 | 74 | indent(depth + 2); 75 | printf("\"size_apparent\": %jd,\n", e->size.apparent); 76 | 77 | indent(depth + 2); 78 | printf("\"size_actual\": %jd,\n", e->size.actual); 79 | 80 | indent(depth + 2); 81 | printf("\"children\": [\n"); 82 | 83 | dump(duc, dir_child, depth + 4, min_size, ex_files); 84 | 85 | printf("\n"); 86 | indent(depth + 2); 87 | printf("]\n"); 88 | 89 | indent(depth); 90 | printf("}"); 91 | } 92 | duc_dir_close(dir_child); 93 | } else { 94 | if(!ex_files && size >= min_size) { 95 | indent(depth); 96 | printf("{\n"); 97 | 98 | indent(depth + 2); 99 | printf("\"name\": \""); 100 | print_escaped(e->name); 101 | printf("\",\n"); 102 | 103 | indent(depth + 2); 104 | printf("\"size_apparent\": %jd,\n", e->size.apparent); 105 | 106 | indent(depth + 2); 107 | printf("\"size_actual\": %jd\n", e->size.actual); 108 | 109 | indent(depth); 110 | printf("}"); 111 | } 112 | } 113 | first = false; 114 | } 115 | } 116 | 117 | 118 | static int json_main(duc *duc, int argc, char **argv) 119 | { 120 | char *path = "."; 121 | if(argc > 0) path = argv[0]; 122 | 123 | int r = duc_open(duc, opt_database, DUC_OPEN_RO); 124 | if(r != DUC_OK) { 125 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 126 | return -1; 127 | } 128 | 129 | duc_dir *dir = duc_dir_open(duc, path); 130 | if(dir == NULL) { 131 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 132 | return -1; 133 | } 134 | 135 | struct duc_size size; 136 | duc_dir_get_size(dir, &size); 137 | 138 | printf("{\n"); 139 | 140 | indent(2); 141 | printf("\"name\": \""); 142 | print_escaped(path); 143 | printf("\",\n"); 144 | 145 | indent(2); 146 | printf("\"count\": %jd,\n", size.count); 147 | 148 | indent(2); 149 | printf("\"size_apparent\": %jd,\n", size.apparent); 150 | 151 | indent(2); 152 | printf("\"size_actual\": %jd,\n", size.actual); 153 | 154 | indent(2); 155 | printf("\"children\": [\n"); 156 | 157 | dump(duc, dir, 4, (off_t)opt_min_size, opt_exclude_files); 158 | printf("\n"); 159 | 160 | indent(2); 161 | printf("]\n"); 162 | 163 | printf("}\n"); 164 | 165 | duc_dir_close(dir); 166 | duc_close(duc); 167 | 168 | return 0; 169 | } 170 | 171 | 172 | static struct ducrc_option options[] = { 173 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "interpret min_size/-s value as apparent size" }, 174 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 175 | { &opt_exclude_files, "exclude-files", 'x', DUCRC_TYPE_BOOL, "exclude file from json output, only include directories" }, 176 | { &opt_min_size, "min_size", 's', DUCRC_TYPE_DOUBLE, "specify min size for files or directories" }, 177 | { NULL } 178 | }; 179 | 180 | 181 | struct cmd cmd_json = { 182 | .name = "json", 183 | .descr_short = "Dump JSON output", 184 | .usage = "[options] [PATH]", 185 | .main = json_main, 186 | .options = options, 187 | }; 188 | 189 | 190 | /* 191 | * End 192 | */ 193 | -------------------------------------------------------------------------------- /src/duc/cmd-topn.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "cmd.h" 15 | #include "duc.h" 16 | 17 | static bool opt_apparent = false; 18 | static bool opt_base = false; 19 | static bool opt_bytes = false; 20 | static char *opt_database = NULL; 21 | 22 | static int topn_db(duc *duc, char *file) 23 | { 24 | struct duc_index_report *report; 25 | duc_size_type st = DUC_SIZE_TYPE_ACTUAL; 26 | duc_sort sort = DUC_SORT_SIZE; 27 | 28 | // We can have multiple reports in a single DB... needs more testing. 29 | int rep_idx = 0; 30 | 31 | int r = duc_open(duc, file, DUC_OPEN_RO); 32 | if(r != DUC_OK) { 33 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 34 | return -1; 35 | } 36 | 37 | printf("%*s Filename\n",12,"Size"); 38 | while(( report = duc_get_report(duc, rep_idx)) != NULL) { 39 | 40 | size_t count; 41 | // Get number of topN files actually stored in report. 42 | int topn_cnt = report->topn_cnt; 43 | 44 | setlocale(LC_NUMERIC, ""); 45 | // Counting DOWN from largest to smallest, assumes array already sorted. 46 | for (int idx=topn_cnt-1; idx >= 0; idx--) { 47 | size_t size = report->topn_array[idx]->size; 48 | if ( size != 0) { 49 | // FIXME - replace 32 with correct #define 50 | char siz[32]; 51 | struct duc_size dsize; 52 | dsize.actual = size; 53 | duc_human_size(&dsize, st, opt_bytes, siz, sizeof siz); 54 | // FIXME - replace 12 with correct #define 55 | printf("%*s", 12, siz); 56 | printf(" %s\n", report->topn_array[idx]->name); 57 | } 58 | } 59 | 60 | duc_index_report_free(report); 61 | rep_idx++; 62 | } 63 | 64 | duc_close(duc); 65 | 66 | return 0; 67 | } 68 | 69 | 70 | static int topn_main(duc *duc, int argc, char **argv) 71 | { 72 | return(topn_db(duc, opt_database)); 73 | } 74 | 75 | 76 | static struct ducrc_option options[] = { 77 | { &opt_bytes, "bytes", 'b', DUCRC_TYPE_BOOL, "show file size in exact number of bytes" }, 78 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 79 | { NULL } 80 | }; 81 | 82 | 83 | struct cmd cmd_topn = { 84 | .name = "topn", 85 | .descr_short = "Dump N largest files in DB.", 86 | .usage = "[options]", 87 | .main = topn_main, 88 | .options = options, 89 | }; 90 | 91 | 92 | /* 93 | * End 94 | */ 95 | 96 | -------------------------------------------------------------------------------- /src/duc/cmd-xml.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "cmd.h" 14 | #include "duc.h" 15 | 16 | 17 | static bool opt_apparent = false; 18 | static char *opt_database = NULL; 19 | static double opt_min_size = 0; 20 | static bool opt_exclude_files = false; 21 | 22 | 23 | static void indent(int n) 24 | { 25 | int i; 26 | for(i=0; i': printf(">"); break; 38 | case '&': printf("&"); break; 39 | case '"': printf("""); break; 40 | case '\t': putchar('\t'); break; 41 | case '\n': putchar('\n'); break; 42 | case '\r': putchar('\r'); break; 43 | default: 44 | if(*s >= 0 && *s < 32) { 45 | printf("#x%02x", *(uint8_t *)s); 46 | } else { 47 | putchar(*s); 48 | } 49 | break; 50 | } 51 | s++; 52 | } 53 | } 54 | 55 | 56 | static void dump(duc *duc, duc_dir *dir, int depth, off_t min_size, int ex_files) 57 | { 58 | struct duc_dirent *e; 59 | 60 | duc_size_type st = opt_apparent ? DUC_SIZE_TYPE_APPARENT : DUC_SIZE_TYPE_ACTUAL; 61 | 62 | while( (e = duc_dir_read(dir, DUC_SIZE_TYPE_ACTUAL, DUC_SORT_SIZE)) != NULL) { 63 | 64 | off_t size = duc_get_size(&e->size, st); 65 | 66 | if(e->type == DUC_FILE_TYPE_DIR && size>= min_size) { 67 | duc_dir *dir_child = duc_dir_openent(dir, e); 68 | if(dir_child) { 69 | indent(depth); 70 | printf("name); 72 | printf("\" size_apparent=\"%jd\" size_actual=\"%jd\" count=\"%jd\">\n", (intmax_t)e->size.apparent, (intmax_t)e->size.actual, (intmax_t)e->size.count); 73 | dump(duc, dir_child, depth + 1, min_size, ex_files); 74 | indent(depth); 75 | printf("\n"); 76 | } 77 | duc_dir_close(dir_child); 78 | } else { 79 | if(!ex_files && size >= min_size) { 80 | indent(depth); 81 | printf("name); 83 | printf("\" size_apparent=\"%jd\" size_actual=\"%jd\" />\n", (intmax_t)e->size.apparent, (intmax_t)e->size.actual); 84 | } 85 | } 86 | } 87 | } 88 | 89 | 90 | static int xml_main(duc *duc, int argc, char **argv) 91 | { 92 | char *path = "."; 93 | if(argc > 0) path = argv[0]; 94 | 95 | int r = duc_open(duc, opt_database, DUC_OPEN_RO); 96 | if(r != DUC_OK) { 97 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 98 | return -1; 99 | } 100 | 101 | duc_dir *dir = duc_dir_open(duc, path); 102 | if(dir == NULL) { 103 | duc_log(duc, DUC_LOG_FTL, "%s", duc_strerror(duc)); 104 | return -1; 105 | } 106 | 107 | struct duc_size size; 108 | duc_dir_get_size(dir, &size); 109 | printf("\n"); 110 | printf("\n", 111 | path, (intmax_t)size.apparent, (intmax_t)size.actual, (intmax_t)size.count); 112 | 113 | dump(duc, dir, 1, (off_t)opt_min_size, opt_exclude_files); 114 | 115 | printf("\n"); 116 | 117 | duc_dir_close(dir); 118 | duc_close(duc); 119 | 120 | return 0; 121 | } 122 | 123 | 124 | static struct ducrc_option options[] = { 125 | { &opt_apparent, "apparent", 'a', DUCRC_TYPE_BOOL, "interpret min_size/-s value as apparent size" }, 126 | { &opt_database, "database", 'd', DUCRC_TYPE_STRING, "select database file to use [~/.duc.db]" }, 127 | { &opt_exclude_files, "exclude-files", 'x', DUCRC_TYPE_BOOL, "exclude file from xml output, only include directories" }, 128 | { &opt_min_size, "min_size", 's', DUCRC_TYPE_DOUBLE, "specify min size for files or directories" }, 129 | { NULL } 130 | }; 131 | 132 | 133 | struct cmd cmd_xml = { 134 | .name = "xml", 135 | .descr_short = "Dump XML output", 136 | .usage = "[options] [PATH]", 137 | .main = xml_main, 138 | .options = options, 139 | }; 140 | 141 | 142 | /* 143 | * End 144 | */ 145 | -------------------------------------------------------------------------------- /src/duc/cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef ps_h 2 | #define ps_h 3 | 4 | #include 5 | #include 6 | 7 | #include "duc.h" 8 | #include "ducrc.h" 9 | 10 | struct cmd { 11 | char *name; 12 | int (*init)(duc *duc, int argc, char **argv); 13 | int (*main)(duc *duc, int argc, char **argv); 14 | char *descr_short; 15 | char *descr_long; 16 | char *usage; 17 | struct ducrc_option *options; 18 | int hidden; 19 | }; 20 | 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/duc/ducrc.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "private.h" 10 | #include "duc.h" 11 | #include "ducrc.h" 12 | 13 | #define MAX_OPTIONS 64 14 | 15 | 16 | struct ducrc { 17 | char *section; 18 | int noptions; 19 | struct ducrc_option *option_list[MAX_OPTIONS]; 20 | }; 21 | 22 | 23 | struct ducrc *ducrc_new(const char *section) 24 | { 25 | struct ducrc *ducrc; 26 | 27 | ducrc = duc_malloc(sizeof *ducrc); 28 | ducrc->noptions = 0; 29 | ducrc->section = duc_strdup(section); 30 | 31 | return ducrc; 32 | } 33 | 34 | 35 | void ducrc_free(struct ducrc *ducrc) 36 | { 37 | duc_free(ducrc->section); 38 | duc_free(ducrc); 39 | } 40 | 41 | 42 | void ducrc_add_options(struct ducrc *ducrc, struct ducrc_option *o) 43 | { 44 | while(o && o->longopt) { 45 | if(ducrc->noptions < MAX_OPTIONS) { 46 | ducrc->option_list[ducrc->noptions++] = o; 47 | } 48 | o++; 49 | } 50 | } 51 | 52 | 53 | 54 | /* 55 | * Trim whitespace off string. Modifies the string, and returns pointer 56 | * to first non-whitespace char. 57 | */ 58 | 59 | static char *trim(char *s) 60 | { 61 | char *e; 62 | while(isspace(*s)) s++; 63 | if(*s == 0) return s; 64 | e = s + strlen(s) - 1; 65 | while(e > s && isspace(*e)) e--; 66 | *(e+1) = 0; 67 | return s; 68 | } 69 | 70 | 71 | static void handle_opt(struct ducrc *ducrc, char shortopt, const char *longopt, const char *val) 72 | { 73 | struct ducrc_option **os = ducrc->option_list; 74 | struct ducrc_option *o = NULL; 75 | bool *v_bool; 76 | int *v_int; 77 | double *v_double; 78 | const char **v_string; 79 | int i; 80 | void (*fn)(const char *val); 81 | 82 | /* Find option */ 83 | 84 | for(i=0; inoptions; i++) { 85 | o = *os; 86 | if(shortopt && shortopt == o->shortopt) goto found; 87 | if(longopt && strcmp(longopt, o->longopt) == 0) goto found; 88 | os++; 89 | } 90 | 91 | if(shortopt) { 92 | fprintf(stderr, "Unknown option '%c'\n", shortopt); 93 | } else { 94 | fprintf(stderr, "Unknown option '%s'\n", longopt); 95 | } 96 | 97 | return; 98 | 99 | found: 100 | 101 | /* Handle option */ 102 | 103 | switch(o->type) { 104 | 105 | case DUCRC_TYPE_BOOL: 106 | v_bool = o->ptr; 107 | *v_bool = 1; 108 | break; 109 | 110 | case DUCRC_TYPE_INT: 111 | v_int = o->ptr; 112 | *v_int = atoi(val); 113 | break; 114 | 115 | case DUCRC_TYPE_DOUBLE: 116 | v_double = o->ptr; 117 | *v_double = atof(val); 118 | break; 119 | 120 | case DUCRC_TYPE_STRING: 121 | v_string = o->ptr; 122 | *v_string = strdup(val); 123 | break; 124 | 125 | case DUCRC_TYPE_FUNC: 126 | fn = o->ptr; 127 | fn(val); 128 | break; 129 | } 130 | 131 | } 132 | 133 | 134 | int ducrc_read(struct ducrc *ducrc, const char *path) 135 | { 136 | 137 | FILE *f = fopen(path, "r"); 138 | if(f == NULL) { 139 | //duc_log(NULL, DUC_LOG_DBG, "Not reading configuration from '%s': %s", path, strerror(errno)); 140 | return -1; 141 | } 142 | 143 | char section[256] = ""; 144 | char buf[256]; 145 | 146 | while(fgets(buf, sizeof buf, f) != NULL) { 147 | 148 | char *l = trim(buf); 149 | char *p; 150 | 151 | /* Strip newlines and comments */ 152 | 153 | p = strchr(l, '#'); if(p) *p = '\0'; 154 | p = strchr(l, '\n'); if(p) *p = '\0'; 155 | p = strchr(l, '\r'); if(p) *p = '\0'; 156 | 157 | /* section? */ 158 | 159 | if(l[0] == '[') { 160 | p = strchr(l, ']'); 161 | if(p) { 162 | *p = 0; 163 | strncpy(section, l+1, sizeof(section)); 164 | } 165 | continue; 166 | } 167 | 168 | if(strlen(section) == 0 || 169 | strcmp(section, "global") == 0 || 170 | strcmp(section, ducrc->section) == 0) { 171 | 172 | /* key + value ? */ 173 | 174 | p = strchr(l, ' '); 175 | 176 | if(p) { 177 | *p = '\0'; 178 | char *longopt = trim(l); 179 | char *val = trim(p + 1); 180 | handle_opt(ducrc, 0, longopt, val); 181 | continue; 182 | } 183 | 184 | /* longopt only */ 185 | 186 | char *longopt = trim(l); 187 | if(strlen(longopt) > 0) { 188 | handle_opt(ducrc, 0, longopt, NULL); 189 | } 190 | } 191 | } 192 | 193 | 194 | fclose(f); 195 | 196 | return 0; 197 | } 198 | 199 | 200 | /* 201 | * I tried to implement a standard-style getopt_long function for parsing the 202 | * command line into duc options. This is hard work, and it has been a long 203 | * day. Instead of parsing myself, this function creates a 'struct option' list 204 | * from the duc options and relies on POSIX getopt_long() to do the hard work. 205 | */ 206 | 207 | int ducrc_getopt(struct ducrc *ducrc, int *argc, char **argv[]) 208 | { 209 | struct option longopts[MAX_OPTIONS + 1] = { { NULL } }; 210 | char optstr[MAX_OPTIONS*2] = ""; 211 | int n = 0; 212 | int l = 0; 213 | 214 | /* Prepare a list of options for getopt_long() */ 215 | 216 | struct ducrc_option **os = ducrc->option_list; 217 | 218 | int i; 219 | for(i=0; inoptions; i++) { 220 | struct ducrc_option *o = *os; 221 | 222 | longopts[n].name = o->longopt; 223 | if(o->shortopt) { 224 | l += sprintf(optstr+l, "%c", o->shortopt); 225 | longopts[n].val = o->shortopt; 226 | } 227 | 228 | if(o->type == DUCRC_TYPE_BOOL) { 229 | longopts[n].has_arg = no_argument; 230 | } else { 231 | longopts[n].has_arg = required_argument; 232 | if(o->shortopt) l += sprintf(optstr+l, ":"); 233 | } 234 | 235 | n++; 236 | os++; 237 | } 238 | 239 | /* Rely on libc to do the hard work */ 240 | 241 | int c; 242 | int idx; 243 | 244 | if(*argc > 1) optind = 2; 245 | 246 | while( ( c = getopt_long(*argc, *argv, optstr, longopts, &idx)) != -1) { 247 | if(c == '?') return -1; 248 | handle_opt(ducrc, c, c ? 0 : longopts[idx].name, optarg); 249 | } 250 | 251 | *argc -= optind; 252 | *argv += optind; 253 | 254 | return 0; 255 | } 256 | 257 | 258 | /* 259 | * End 260 | */ 261 | -------------------------------------------------------------------------------- /src/duc/ducrc.h: -------------------------------------------------------------------------------- 1 | #ifndef ducrc_h 2 | #define ducrc_h 3 | 4 | enum ducrc_type { 5 | DUCRC_TYPE_BOOL, 6 | DUCRC_TYPE_INT, 7 | DUCRC_TYPE_DOUBLE, 8 | DUCRC_TYPE_STRING, 9 | DUCRC_TYPE_FUNC, 10 | }; 11 | 12 | struct ducrc_option { 13 | void *ptr; 14 | const char *longopt; 15 | char shortopt; 16 | enum ducrc_type type; 17 | const char *descr_short; 18 | const char *descr_long; 19 | }; 20 | 21 | 22 | struct ducrc; 23 | 24 | struct ducrc *ducrc_new(const char *section); 25 | void ducrc_free(struct ducrc *ducrc); 26 | 27 | void ducrc_add_options(struct ducrc *ducrc, struct ducrc_option *option_list); 28 | 29 | int ducrc_read(struct ducrc *ducrc, const char *path); 30 | int ducrc_getopt(struct ducrc *ducrc, int *argc, char **argv[]); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/libduc-graph/duc-graph.h: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifndef duc_graph_h 4 | #define duc_graph_h 5 | 6 | #include 7 | 8 | /* 9 | * Graph drawing 10 | */ 11 | 12 | typedef struct duc_graph duc_graph; 13 | 14 | enum duc_graph_palette { 15 | DUC_GRAPH_PALETTE_SIZE, 16 | DUC_GRAPH_PALETTE_RAINBOW, 17 | DUC_GRAPH_PALETTE_GREYSCALE, 18 | DUC_GRAPH_PALETTE_MONOCHROME, 19 | DUC_GRAPH_PALETTE_CLASSIC, 20 | }; 21 | 22 | typedef enum { 23 | DUC_GRAPH_FORMAT_PNG, 24 | DUC_GRAPH_FORMAT_SVG, 25 | DUC_GRAPH_FORMAT_PDF, 26 | DUC_GRAPH_FORMAT_HTML, 27 | } duc_graph_file_format; 28 | 29 | #ifdef ENABLE_CAIRO 30 | #include 31 | duc_graph *duc_graph_new_cairo(duc *duc, cairo_t *cr); 32 | duc_graph *duc_graph_new_cairo_file(duc *duc, duc_graph_file_format fmt, FILE *fout); 33 | #endif 34 | #ifdef ENABLE_OPENGL 35 | duc_graph *duc_graph_new_opengl(duc *duc, double font_scale); 36 | #endif 37 | duc_graph *duc_graph_new_svg(duc *duc, FILE *fout); 38 | duc_graph *duc_graph_new_html(duc *duc, FILE *fout, int write_body); 39 | 40 | void duc_graph_free(duc_graph *g); 41 | 42 | void duc_graph_set_max_level(duc_graph *g, int max_level); 43 | void duc_graph_set_size(duc_graph *g, int w, int h); 44 | void duc_graph_set_dpi(duc_graph *g, double dpi); 45 | void duc_graph_set_position(duc_graph *g, double x, double y); 46 | void duc_graph_set_tooltip(duc_graph *g, double x, double y); 47 | void duc_graph_set_palette(duc_graph *g, enum duc_graph_palette p); 48 | void duc_graph_set_fuzz(duc_graph *g, double fuzz); 49 | void duc_graph_set_max_name_len(duc_graph *g, size_t len); 50 | void duc_graph_set_size_type(duc_graph *g, duc_size_type st); 51 | void duc_graph_set_exact_bytes(duc_graph *g, int exact); 52 | void duc_graph_set_ring_gap(duc_graph *g, int gap); 53 | void duc_graph_set_gradient(duc_graph *g, int onoff); 54 | 55 | int duc_graph_draw(duc_graph *g, duc_dir *dir); 56 | duc_dir *duc_graph_find_spot(duc_graph *g, duc_dir *dir, double x, double y, struct duc_dirent **ent); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/libduc-graph/graph-cairo.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_CAIRO 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "private.h" 27 | #include "duc.h" 28 | #include "duc-graph.h" 29 | #include "utlist.h" 30 | #include "graph-private.h" 31 | 32 | struct cairo_backend_data { 33 | cairo_surface_t *cs; 34 | cairo_t *cr; 35 | duc_graph_file_format fmt; 36 | FILE *fout; 37 | }; 38 | 39 | 40 | 41 | static cairo_status_t cairo_writer(void *closure, const unsigned char *data, unsigned int length) 42 | { 43 | FILE *f = closure; 44 | size_t out = fwrite(data, length, 1, f); 45 | if (out == 0 || out < length) { 46 | return CAIRO_STATUS_WRITE_ERROR; 47 | } 48 | return CAIRO_STATUS_SUCCESS; 49 | } 50 | 51 | 52 | 53 | void br_cairo_start(duc_graph *g) 54 | { 55 | struct cairo_backend_data *bd = g->backend_data; 56 | cairo_t *cr = bd->cr; 57 | cairo_save(cr); 58 | cairo_translate(cr, g->pos_x, g->pos_y); 59 | } 60 | 61 | 62 | void br_file_start(duc_graph *g) 63 | { 64 | struct cairo_backend_data *bd = g->backend_data; 65 | 66 | switch(bd->fmt) { 67 | 68 | case DUC_GRAPH_FORMAT_PNG: 69 | bd->cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)g->size, (int)g->size); 70 | break; 71 | case DUC_GRAPH_FORMAT_SVG: 72 | bd->cs = cairo_svg_surface_create_for_stream(cairo_writer, bd->fout, (int)g->size, (int)g->size); 73 | break; 74 | case DUC_GRAPH_FORMAT_PDF: 75 | bd->cs = cairo_pdf_surface_create_for_stream(cairo_writer, bd->fout, (int)g->size, (int)g->size); 76 | break; 77 | default: 78 | duc_log(g->duc, DUC_LOG_FTL, "Image format not handled by cairo backend"); 79 | exit(1); 80 | } 81 | 82 | bd->cr = cairo_create(bd->cs); 83 | cairo_translate(bd->cr, g->pos_x, g->pos_y); 84 | } 85 | 86 | 87 | static void br_cairo_draw_text(duc_graph *g, double x, double y, double size, char *text) 88 | { 89 | struct cairo_backend_data *bd = g->backend_data; 90 | cairo_t *cr = bd->cr; 91 | 92 | char font[32]; 93 | snprintf(font, sizeof font, "Arial, Sans, %.0f", size); 94 | PangoLayout *layout = pango_cairo_create_layout(cr); 95 | PangoFontDescription *desc = pango_font_description_from_string(font); 96 | 97 | pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); 98 | pango_layout_set_text(layout, text, -1); 99 | pango_layout_set_font_description(layout, desc); 100 | pango_font_description_free(desc); 101 | 102 | pango_cairo_update_layout(cr, layout); 103 | 104 | int w,h; 105 | pango_layout_get_size(layout, &w, &h); 106 | 107 | x -= ((double)w / PANGO_SCALE / 2); 108 | y -= ((double)h / PANGO_SCALE / 2); 109 | 110 | cairo_move_to(cr, floor(x+0.5), floor(y+0.5)); 111 | pango_cairo_layout_path(cr, layout); 112 | g_object_unref(layout); 113 | 114 | /* light grey background */ 115 | 116 | cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL); 117 | cairo_set_source_rgba(cr, 1, 1, 1, 0.6); 118 | cairo_set_line_width(cr, 3); 119 | cairo_stroke_preserve(cr); 120 | 121 | /* black text */ 122 | 123 | cairo_set_source_rgb(cr, 0, 0, 0); 124 | cairo_set_line_width(cr, 1); 125 | cairo_fill(cr); 126 | } 127 | 128 | 129 | static void br_cairo_draw_tooltip(duc_graph *g, double x, double y, char *text) 130 | { 131 | struct cairo_backend_data *bd = g->backend_data; 132 | cairo_t *cr = bd->cr; 133 | 134 | char font[32]; 135 | snprintf(font, sizeof font, "Arial, Sans, %d", FONT_SIZE_TOOLTIP); 136 | PangoLayout *layout = pango_cairo_create_layout(cr); 137 | PangoFontDescription *desc = pango_font_description_from_string(font); 138 | 139 | pango_layout_set_text(layout, text, -1); 140 | pango_layout_set_font_description(layout, desc); 141 | pango_font_description_free(desc); 142 | 143 | pango_cairo_update_layout(cr, layout); 144 | 145 | int w,h; 146 | pango_layout_get_size(layout, &w, &h); 147 | 148 | w /= PANGO_SCALE; 149 | h /= PANGO_SCALE; 150 | 151 | /* shadow box */ 152 | 153 | int i; 154 | for(i=1; i<3; i++) { 155 | cairo_rectangle(cr, x - w - 10 + i, y - h - 10 + i, w + 10 + i, h + 10 + i); 156 | cairo_set_source_rgba(cr, 0, 0, 0, 0.25); 157 | cairo_fill(cr); 158 | } 159 | 160 | /* tooltip box */ 161 | 162 | cairo_rectangle(cr, x - w - 10 + 0.5, y - h - 10 + 0.5, w + 10, h + 10); 163 | cairo_set_source_rgba(cr, 1, 1, 1, 1); 164 | cairo_fill_preserve(cr); 165 | cairo_set_source_rgba(cr, 0, 0, 0, 1); 166 | cairo_stroke(cr); 167 | 168 | /* black text */ 169 | 170 | cairo_move_to(cr, x - w - 5, y - h - 5); 171 | pango_cairo_layout_path(cr, layout); 172 | g_object_unref(layout); 173 | 174 | cairo_set_source_rgb(cr, 0, 0, 0); 175 | cairo_set_line_width(cr, 1); 176 | cairo_fill(cr); 177 | } 178 | 179 | 180 | static void br_cairo_draw_section(duc_graph *g, double a1, double a2, double r1, double r2, double R, double G, double B, double L) 181 | { 182 | struct cairo_backend_data *bd = g->backend_data; 183 | cairo_t *cr = bd->cr; 184 | 185 | cairo_new_path(cr); 186 | cairo_arc(cr, g->cx, g->cy, r1, ang(a1), ang(a2)); 187 | cairo_arc_negative(cr, g->cx, g->cy, r2, ang(a2), ang(a1)); 188 | cairo_close_path(cr); 189 | 190 | if(R != 1.0 || G != 1.0 || B != 1.0) { 191 | cairo_pattern_t *pat; 192 | pat = cairo_pattern_create_radial(g->cx, g->cy, 0, g->cx, g->cy, g->cx-50); 193 | double off1 = r2 / g->cx; 194 | double off2 = r1 / g->cx; 195 | cairo_pattern_add_color_stop_rgb(pat, off1, R, G, B); 196 | if(g->gradient) { 197 | cairo_pattern_add_color_stop_rgb(pat, off2, R * 0.6, G * 0.6, B * 0.6); 198 | } 199 | cairo_set_source(cr, pat); 200 | 201 | cairo_fill(cr); 202 | cairo_pattern_destroy(pat); 203 | } 204 | 205 | if(L != 0.0) { 206 | cairo_set_line_width(cr, 0.5); 207 | cairo_set_source_rgba(cr, L, L, L, 0.9); 208 | cairo_stroke(cr); 209 | } 210 | } 211 | 212 | 213 | 214 | void br_cairo_done(duc_graph *g) 215 | { 216 | struct cairo_backend_data *bd = g->backend_data; 217 | cairo_t *cr = bd->cr; 218 | cairo_restore(cr); 219 | } 220 | 221 | 222 | void br_file_done(duc_graph *g) 223 | { 224 | struct cairo_backend_data *bd = g->backend_data; 225 | 226 | switch(bd->fmt) { 227 | case DUC_GRAPH_FORMAT_PNG: 228 | cairo_surface_write_to_png_stream(bd->cs, cairo_writer, bd->fout); 229 | break; 230 | default: 231 | break; 232 | } 233 | 234 | cairo_destroy(bd->cr); 235 | cairo_surface_destroy(bd->cs); 236 | } 237 | 238 | 239 | static void br_cairo_free(duc_graph *g) 240 | { 241 | struct cairo_backend_data *bd = g->backend_data; 242 | free(bd); 243 | } 244 | 245 | 246 | struct duc_graph_backend duc_graph_backend_cairo = { 247 | .start = br_cairo_start, 248 | .draw_text = br_cairo_draw_text, 249 | .draw_tooltip = br_cairo_draw_tooltip, 250 | .draw_section = br_cairo_draw_section, 251 | .done = br_cairo_done, 252 | .free = br_cairo_free, 253 | }; 254 | 255 | 256 | struct duc_graph_backend duc_graph_backend_file = { 257 | .start = br_file_start, 258 | .draw_text = br_cairo_draw_text, 259 | .draw_tooltip = br_cairo_draw_tooltip, 260 | .draw_section = br_cairo_draw_section, 261 | .done = br_file_done, 262 | .free = br_cairo_free, 263 | }; 264 | 265 | 266 | duc_graph *duc_graph_new_cairo(duc *duc, cairo_t *cr) 267 | { 268 | duc_graph *g = duc_graph_new(duc); 269 | g->backend = &duc_graph_backend_cairo; 270 | 271 | struct cairo_backend_data *bd; 272 | bd = duc_malloc(sizeof *bd); 273 | g->backend_data = bd; 274 | 275 | bd->cr = cr; 276 | 277 | return g; 278 | } 279 | 280 | 281 | duc_graph *duc_graph_new_cairo_file(duc *duc, duc_graph_file_format fmt, FILE *fout) 282 | { 283 | duc_graph *g = duc_graph_new(duc); 284 | g->backend = &duc_graph_backend_file; 285 | 286 | struct cairo_backend_data *bd; 287 | bd = duc_malloc(sizeof *bd); 288 | g->backend_data = bd; 289 | 290 | bd->fmt = fmt; 291 | bd->fout = fout; 292 | 293 | return g; 294 | } 295 | 296 | #endif 297 | 298 | /* 299 | * End 300 | */ 301 | 302 | -------------------------------------------------------------------------------- /src/libduc-graph/graph-html.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "private.h" 20 | #include "duc.h" 21 | #include "duc-graph.h" 22 | #include "graph-private.h" 23 | #include "utlist.h" 24 | 25 | struct html_backend_data { 26 | FILE *fout; 27 | int write_body; 28 | }; 29 | 30 | 31 | void br_html_start(duc_graph *g) 32 | { 33 | struct html_backend_data *bd = g->backend_data; 34 | FILE *f = bd->fout; 35 | 36 | if(bd->write_body) { 37 | fprintf(f, "\n"); 38 | fprintf(f, "\n"); 40 | fprintf(f, "\n"); 41 | } 42 | 43 | fprintf(f, "\n", g->width, g->height); 44 | fprintf(f, "Your browser does not support the HTML5 canvas tag.\n"); 45 | fprintf(f, "\n"); 46 | 47 | 48 | fprintf(f, "\n"); 144 | if(bd->write_body) { 145 | fprintf(f, "\n"); 146 | } 147 | } 148 | 149 | 150 | static void br_html_free(duc_graph *g) 151 | { 152 | } 153 | 154 | 155 | 156 | struct duc_graph_backend duc_graph_backend_html = { 157 | .start = br_html_start, 158 | .draw_text = br_html_draw_text, 159 | .draw_tooltip = br_html_draw_tooltip, 160 | .draw_section = br_html_draw_section, 161 | .done = br_html_done, 162 | .free = br_html_free, 163 | }; 164 | 165 | 166 | 167 | duc_graph *duc_graph_new_html(duc *duc, FILE *fout, int write_body) 168 | { 169 | duc_graph *g = duc_graph_new(duc); 170 | g->backend = &duc_graph_backend_html; 171 | 172 | struct html_backend_data *bd; 173 | bd = duc_malloc(sizeof *bd); 174 | g->backend_data = bd; 175 | 176 | bd->fout = fout; 177 | bd->write_body = write_body; 178 | 179 | return g; 180 | } 181 | 182 | /* 183 | * End 184 | */ 185 | 186 | -------------------------------------------------------------------------------- /src/libduc-graph/graph-private.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef graph_private_h 3 | #define graph_private_h 4 | 5 | #include "duc-graph.h" 6 | 7 | #define FONT_SIZE_LABEL 8 8 | #define FONT_SIZE_TOOLTIP 8 9 | #define FONT_SIZE_CENTER 14 10 | 11 | struct label { 12 | double x, y; 13 | char *text; 14 | struct label *next; 15 | }; 16 | 17 | 18 | struct duc_graph_backend { 19 | void (*free)(duc_graph *g); 20 | void (*start)(duc_graph *g); 21 | void (*draw_tooltip)(duc_graph *g, double x, double y, char *text); 22 | void (*draw_section)(duc_graph *g, double a1, double a2, double r1, double r2, double R, double G, double B, double line); 23 | void (*draw_text)(duc_graph *g, double x, double y, double size, char *text); 24 | void (*done)(duc_graph *g); 25 | }; 26 | 27 | 28 | struct duc_graph { 29 | 30 | /* Settings */ 31 | 32 | struct duc *duc; 33 | double size; 34 | double dpi; 35 | double font_scale; 36 | double cx, cy; 37 | double pos_x, pos_y; 38 | double width, height; 39 | double tooltip_a, tooltip_r; 40 | double tooltip_x, tooltip_y; 41 | char tooltip_msg[DUC_PATH_MAX+256]; 42 | double r_start; 43 | double fuzz; 44 | int max_level; 45 | enum duc_graph_palette palette; 46 | size_t max_name_len; 47 | duc_size_type size_type; 48 | int bytes; 49 | int ring_gap; 50 | int gradient; 51 | 52 | /* format */ 53 | 54 | /* Reusable runtime info. Cleared after each graph_draw_* call */ 55 | 56 | struct label *label_list; 57 | double spot_a; 58 | double spot_r; 59 | duc_dir *spot_dir; 60 | struct duc_dirent *spot_ent; 61 | 62 | struct duc_graph_backend *backend; 63 | void *backend_data; 64 | }; 65 | 66 | 67 | duc_graph *duc_graph_new(duc *duc); 68 | double ang(double a); 69 | void pol2car(duc_graph *g, double a, double r, double *x, double *y); 70 | void car2pol(duc_graph *g, double x, double y, double *a, double *r); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/libduc-graph/graph-svg.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "private.h" 20 | #include "duc.h" 21 | #include "duc-graph.h" 22 | #include "graph-private.h" 23 | #include "utlist.h" 24 | 25 | struct svg_backend_data { 26 | FILE *fout; 27 | int gid; 28 | }; 29 | 30 | 31 | void br_svg_start(duc_graph *g) 32 | { 33 | struct svg_backend_data *bd = g->backend_data; 34 | FILE *f = bd->fout; 35 | 36 | fprintf(f, "\n"); 37 | fprintf(f, "\n"); 39 | fprintf(f, "width, g->height); 40 | fprintf(f, " xmlns='http://www.w3.org/2000/svg'\n"); 41 | fprintf(f, " xmlns:xlink='http://www.w3.org/1999/xlink'>\n"); 42 | fprintf(f, " \n"); 53 | 54 | } 55 | 56 | static void draw_text_aux(int x, const char *s, FILE *f) 57 | { 58 | char *p = strdup(s); 59 | assert(p); 60 | 61 | double y = 0; 62 | char *l = strtok(p, "\n"); 63 | 64 | while(l != NULL) { 65 | fprintf(f, " ", x, y); 66 | while(*l) { 67 | switch(*l) { 68 | case '<': fprintf(f, "<"); break; 69 | case '>': fprintf(f, ">"); break; 70 | case '&': fprintf(f, "&"); break; 71 | case '"': fprintf(f, """); break; 72 | default: fputc(*l, f); break; 73 | } 74 | l++; 75 | } 76 | fprintf(f, "\n"); 77 | 78 | y += 1.2; 79 | l = strtok(NULL, "\n"); 80 | } 81 | 82 | free(p); 83 | } 84 | 85 | static void br_svg_draw_text(duc_graph *g, double x, double y, double size, char *text) 86 | { 87 | struct svg_backend_data *bd = g->backend_data; 88 | FILE *f = bd->fout; 89 | 90 | fprintf(f, "\n", x, y, size); 91 | draw_text_aux(x, text, f); 92 | fprintf(f, "\n"); 93 | 94 | fprintf(f, "\n", x, y, size); 95 | draw_text_aux(x, text, f); 96 | fprintf(f, "\n"); 97 | } 98 | 99 | 100 | static void br_svg_draw_tooltip(duc_graph *g, double x, double y, char *text) 101 | { 102 | } 103 | 104 | 105 | static void br_svg_draw_section(duc_graph *g, double a1, double a2, double r1, double r2, double R, double G, double B, double L) 106 | { 107 | struct svg_backend_data *bd = g->backend_data; 108 | FILE *f = bd->fout; 109 | 110 | if(g->gradient) { 111 | fprintf(f, "\n"); 112 | fprintf(f, " \n", bd->gid, g->cx, g->cy, r2); 113 | fprintf(f, " \n", 100*r1/r2, (int)(R*156), (int)(G*156), (int)(B*156)); 114 | fprintf(f, " \n", (int)(R*255), (int)(G*255), (int)(B*255)); 115 | fprintf(f, " \n"); 116 | fprintf(f, "\n"); 117 | fprintf(f, "gid); 118 | bd->gid++; 119 | } else { 120 | fprintf(f, " 0.5; 124 | 125 | fprintf(f, "d='"); 126 | 127 | fprintf(f, "M%.0f,%.0f ", 128 | g->cx + r1 * sin(a1 * M_PI*2), 129 | g->cy - r1 * cos(a1 * M_PI*2)); 130 | 131 | fprintf(f, "L%.0f,%.0f ", 132 | g->cx + r2 * sin(a1 * M_PI*2), 133 | g->cy - r2 * cos(a1 * M_PI*2)); 134 | 135 | fprintf(f, "A %.0f %.0f 0 %d 1 %.0f %.0f ", r2, r2, large, 136 | g->cx + r2 * sin(a2 * M_PI*2), 137 | g->cy - r2 * cos(a2 * M_PI*2)); 138 | 139 | fprintf(f, "L%.0f,%.0f ", 140 | g->cx + r1 * sin(a2 * M_PI*2), 141 | g->cy - r1 * cos(a2 * M_PI*2)); 142 | 143 | fprintf(f, "A %.0f %.0f 0 %d 0 %.0f %.0f ", r1, r1, large, 144 | g->cx + r1 * sin(a1 * M_PI*2), 145 | g->cy - r1 * cos(a1 * M_PI*2)); 146 | 147 | fprintf(f, "'/>\n"); 148 | 149 | } 150 | 151 | 152 | void br_svg_done(duc_graph *g) 153 | { 154 | struct svg_backend_data *bd = g->backend_data; 155 | FILE *f = bd->fout; 156 | fprintf(f, "\n"); 157 | } 158 | 159 | 160 | static void br_svg_free(duc_graph *g) 161 | { 162 | } 163 | 164 | 165 | 166 | struct duc_graph_backend duc_graph_backend_svg = { 167 | .start = br_svg_start, 168 | .draw_text = br_svg_draw_text, 169 | .draw_tooltip = br_svg_draw_tooltip, 170 | .draw_section = br_svg_draw_section, 171 | .done = br_svg_done, 172 | .free = br_svg_free, 173 | }; 174 | 175 | 176 | 177 | duc_graph *duc_graph_new_svg(duc *duc, FILE *fout) 178 | { 179 | duc_graph *g = duc_graph_new(duc); 180 | g->backend = &duc_graph_backend_svg; 181 | 182 | struct svg_backend_data *bd; 183 | bd = duc_malloc(sizeof *bd); 184 | g->backend_data = bd; 185 | 186 | bd->fout = fout; 187 | bd->gid = 0; 188 | 189 | return g; 190 | } 191 | 192 | /* 193 | * End 194 | */ 195 | 196 | -------------------------------------------------------------------------------- /src/libduc/buffer.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "private.h" 11 | #include "buffer.h" 12 | #include "varint.h" 13 | 14 | 15 | /* 16 | * Generic buffer manipulation for serialization 17 | */ 18 | 19 | 20 | struct buffer *buffer_new(void *data, size_t len) 21 | { 22 | struct buffer *b; 23 | 24 | b = duc_malloc(sizeof(struct buffer)); 25 | b->ptr = 0; 26 | 27 | if(data) { 28 | b->max = len; 29 | b->len = len; 30 | b->data = data; 31 | } else { 32 | b->max = 1024; 33 | b->len = 0; 34 | b->data = duc_malloc(b->max); 35 | } 36 | 37 | return b; 38 | } 39 | 40 | 41 | void buffer_free(struct buffer *b) 42 | { 43 | duc_free(b->data); 44 | duc_free(b); 45 | } 46 | 47 | // Add item to buffer, but grow by doubling if needed 48 | static int buffer_put(struct buffer *b, const void *data, size_t len) 49 | { 50 | if(b->ptr + len > b->max) { 51 | while(b->len + len > b->max) { 52 | b->max *= 2; 53 | } 54 | b->data = duc_realloc(b->data, b->max); 55 | } 56 | 57 | memcpy(b->data + b->ptr, data, len); 58 | b->ptr += len; 59 | if(b->ptr > b->len) b->len = b->ptr; 60 | return len; 61 | } 62 | 63 | 64 | static int buffer_get(struct buffer *b, void *data, size_t len) 65 | { 66 | if(b->ptr <= b->len - len) { 67 | memcpy(data, b->data + b->ptr, len); 68 | b->ptr += len; 69 | return len; 70 | } else { 71 | return 0; 72 | } 73 | } 74 | 75 | 76 | static int buffer_put_varint(struct buffer *b, uint64_t v) 77 | { 78 | uint8_t buf[9]; 79 | int l = PutVarint64(buf, v); 80 | buffer_put(b, buf, l); 81 | return l; 82 | } 83 | 84 | 85 | static int buffer_get_varint(struct buffer *b, uint64_t *v) 86 | { 87 | uint8_t buf[9]; 88 | int r = buffer_get(b, buf, 1); 89 | if(r == 0) return 0; 90 | 91 | int n = 0; 92 | if(buf[0] >= 249) { 93 | n = buf[0] - 247; 94 | } else if(buf[0] >= 241) { 95 | n = 1; 96 | } 97 | if(n > 0) buffer_get(b, buf+1, n); 98 | int l = GetVarint64(buf, n+1, v); 99 | return l; 100 | } 101 | 102 | 103 | static void buffer_put_string(struct buffer *b, const char *s) 104 | { 105 | size_t len = strlen(s); 106 | if(len < 256) { 107 | uint8_t l = len; 108 | buffer_put(b, &l, sizeof l); 109 | buffer_put(b, s, l); 110 | } 111 | else { 112 | fprintf(stderr,"cannot buffer_put_string() larger than 255 bytes\n"); 113 | exit(1); 114 | } 115 | } 116 | 117 | // FIXME! This must return something on error. Or crash cleanly? 118 | // Maximum string size of 255 bytes... why? 119 | static void buffer_get_string(struct buffer *b, char **sout) 120 | { 121 | uint8_t len; 122 | buffer_get(b, &len, sizeof(len)); 123 | char *s = duc_malloc(len + 1); 124 | if(s) { 125 | buffer_get(b, s, len); 126 | s[len] = '\0'; 127 | *sout = s; 128 | } 129 | } 130 | 131 | 132 | static void buffer_put_devino(struct buffer *b, const struct duc_devino *devino) 133 | { 134 | buffer_put_varint(b, devino->dev); 135 | buffer_put_varint(b, devino->ino); 136 | } 137 | 138 | 139 | static void buffer_get_devino(struct buffer *b, struct duc_devino *devino) 140 | { 141 | uint64_t v; 142 | buffer_get_varint(b, &v); devino->dev = v; 143 | buffer_get_varint(b, &v); devino->ino = v; 144 | } 145 | 146 | 147 | static void buffer_put_size(struct buffer *b, const struct duc_size *size) 148 | { 149 | buffer_put_varint(b, size->apparent); 150 | buffer_put_varint(b, size->actual); 151 | buffer_put_varint(b, size->count); 152 | } 153 | 154 | 155 | static void buffer_get_size(struct buffer *b, struct duc_size *size) 156 | { 157 | uint64_t v; 158 | buffer_get_varint(b, &v); size->apparent = v; 159 | buffer_get_varint(b, &v); size->actual = v; 160 | buffer_get_varint(b, &v); size->count = v; 161 | } 162 | 163 | 164 | /* 165 | * Serialize data from structs into buffer 166 | */ 167 | 168 | void buffer_put_dir(struct buffer *b, const struct duc_devino *devino, time_t mtime) 169 | { 170 | buffer_put_devino(b, devino); 171 | buffer_put_varint(b, mtime); 172 | } 173 | 174 | 175 | void buffer_get_dir(struct buffer *b, struct duc_devino *devino, time_t *mtime) 176 | { 177 | uint64_t v; 178 | buffer_get_devino(b, devino); 179 | buffer_get_varint(b, &v); *mtime = v; 180 | } 181 | 182 | void buffer_put_dirent(struct buffer *b, const struct duc_dirent *ent) 183 | { 184 | buffer_put_string(b, ent->name); 185 | buffer_put_size(b, &ent->size); 186 | buffer_put_varint(b, ent->type); 187 | 188 | if(ent->type == DUC_FILE_TYPE_DIR) { 189 | buffer_put_devino(b, &ent->devino); 190 | } 191 | } 192 | 193 | void buffer_get_dirent(struct buffer *b, struct duc_dirent *ent) 194 | { 195 | uint64_t v; 196 | 197 | buffer_get_string(b, &ent->name); 198 | buffer_get_size(b, &ent->size); 199 | buffer_get_varint(b, &v); ent->type = v; 200 | 201 | if(ent->type == DUC_FILE_TYPE_DIR) { 202 | buffer_get_devino(b, &ent->devino); 203 | } 204 | } 205 | 206 | 207 | /* make sure these next two are in sync, the format needs to be identical */ 208 | void buffer_put_index_report(struct buffer *b, const struct duc_index_report *report) 209 | { 210 | buffer_put_string(b, report->path); 211 | buffer_put_devino(b, &report->devino); 212 | buffer_put_varint(b, report->time_start.tv_sec); 213 | buffer_put_varint(b, report->time_start.tv_usec); 214 | buffer_put_varint(b, report->time_stop.tv_sec); 215 | buffer_put_varint(b, report->time_stop.tv_usec); 216 | buffer_put_varint(b, report->file_count); 217 | buffer_put_varint(b, report->dir_count); 218 | buffer_put_size(b, &report->size); 219 | /* write topN data */ 220 | buffer_put_varint(b, report->topn_min_size); 221 | buffer_put_varint(b, report->topn_cnt); 222 | buffer_put_varint(b, report->topn_cnt_max); 223 | buffer_put_varint(b, report->histogram_buckets); 224 | 225 | /* Make this dynamic where the last bucket has -1 maybe */ 226 | for(int i = 0; i < report->histogram_buckets; i++) { 227 | buffer_put_varint(b,report->histogram[i]); 228 | } 229 | 230 | /* write topN data, FIXME */ 231 | for(int i = 0; i < report->topn_cnt; i++) { 232 | buffer_put_varint(b, strlen(report->topn_array[i]->name)); 233 | buffer_put_string(b, report->topn_array[i]->name); 234 | buffer_put_varint(b, report->topn_array[i]->size); 235 | } 236 | } 237 | 238 | /* must have identical layout as buffer_put_index_report()! */ 239 | void buffer_get_index_report(struct buffer *b, struct duc_index_report *report) 240 | { 241 | char *vs = NULL; 242 | buffer_get_string(b, &vs); 243 | if(vs == NULL) return; 244 | 245 | snprintf(report->path, sizeof(report->path), "%s", vs); 246 | duc_free(vs); 247 | 248 | uint64_t vi; 249 | buffer_get_devino(b, &report->devino); 250 | buffer_get_varint(b, &vi); report->time_start.tv_sec = vi; 251 | buffer_get_varint(b, &vi); report->time_start.tv_usec = vi; 252 | buffer_get_varint(b, &vi); report->time_stop.tv_sec = vi; 253 | buffer_get_varint(b, &vi); report->time_stop.tv_usec = vi; 254 | buffer_get_varint(b, &vi); report->file_count = vi; 255 | buffer_get_varint(b, &vi); report->dir_count = vi; 256 | buffer_get_size(b, &report->size); 257 | /* read topN data as well, if found */ 258 | buffer_get_varint(b, &vi); report->topn_min_size = vi; 259 | buffer_get_varint(b, &vi); report->topn_cnt = vi; 260 | buffer_get_varint(b, &vi); report->topn_cnt_max = vi; 261 | buffer_get_varint(b, &vi); report->histogram_buckets = vi; 262 | 263 | /* when reading, look for -1 as last bucket, so we can be dynamically sized? */ 264 | for(int i = 0; i < report->histogram_buckets; i++) { 265 | buffer_get_varint(b, &vi); 266 | report->histogram[i] = vi; 267 | } 268 | 269 | for(int i = 0; i < report->topn_cnt; i++) { 270 | uint64_t length; 271 | buffer_get_varint(b, &length); 272 | buffer_get_string(b, &vs); 273 | report->topn_array[i] = duc_malloc0(sizeof(duc_topn_file)); 274 | strncpy(report->topn_array[i]->name, vs, strlen(vs)); 275 | buffer_get_varint(b, &vi); report->topn_array[i]->size = vi; 276 | } 277 | } 278 | 279 | 280 | /* 281 | * End 282 | */ 283 | 284 | -------------------------------------------------------------------------------- /src/libduc/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef buffer_h 2 | #define buffer_h 3 | 4 | struct buffer { 5 | uint8_t *data; 6 | size_t max; 7 | size_t len; 8 | size_t ptr; 9 | }; 10 | 11 | struct buffer *buffer_new(void *data, size_t len); 12 | void buffer_free(struct buffer *b); 13 | 14 | void buffer_put_dir(struct buffer *b, const struct duc_devino *devino, time_t mtime); 15 | void buffer_get_dir(struct buffer *b, struct duc_devino *devino, time_t *mtime); 16 | 17 | void buffer_put_dirent(struct buffer *b, const struct duc_dirent *ent); 18 | void buffer_get_dirent(struct buffer *b, struct duc_dirent *ent); 19 | 20 | void buffer_put_index_report(struct buffer *b, const struct duc_index_report *report); 21 | void buffer_get_index_report(struct buffer *b, struct duc_index_report *report); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/libduc/canonicalize.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * idea stolen from https://raw.githubusercontent.com/duanev/path-canon-c/master/pathcanon.c 4 | */ 5 | 6 | #include "config.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "private.h" 16 | #include "utstring.h" 17 | 18 | 19 | #ifdef TEST 20 | 21 | /* 22 | gcc -Wall -Werror -I. -DTEST -g src/libduc/canonicalize.c && valgrind --quiet --leak-check=full ./a.out 23 | x86_64-w64-mingw32-gcc -Wall -Werror -I. -DTEST -g src/libduc/canonicalize.c && wine64 ./a.exe 24 | */ 25 | 26 | #define duc_malloc malloc 27 | #define duc_malloc0(n) calloc(n, 1) 28 | #define duc_strdup strdup 29 | #define duc_free free 30 | 31 | char *test[][2] = { 32 | 33 | /* absolute unix paths */ 34 | 35 | { "/", "/" }, 36 | { "//", "/" }, 37 | { "/home/ico", "/home/ico" }, 38 | { "/home/ico/", "/home/ico" }, 39 | { "//home/ico/", "/home/ico" }, 40 | { "//home///ico/", "/home/ico" }, 41 | { "/home/ico/..", "/home" }, 42 | { "/home/ico/../ico", "/home/ico" }, 43 | { "/home/ico/../..", "/" }, 44 | { "/home/./ico", "/home/ico" }, 45 | { "/../..", "/" }, 46 | { "//d/./e/.././o/f/g/./h/../../..//./n/././e/./i/..///", "/d/o/n/e" }, 47 | 48 | /* absolute windows paths */ 49 | 50 | { "C:\\Windows\\System32", "C:/Windows/System32" }, 51 | { "c:/", "C:/" }, 52 | { "c:\\..\\..\\..", "C:/" }, 53 | { "c:\\", "C:/" }, 54 | { "c:\\users\\ico", "C:/users/ico" }, 55 | { "c:\\users\\ico\\..", "C:/users" }, 56 | 57 | #ifdef WIN32 58 | 59 | /* relative windows paths */ 60 | 61 | { ".", "Z:/home/ico/sandbox/prjs/duc" }, 62 | { "./..", "Z:/home/ico/sandbox/prjs" }, 63 | { "..", "Z:/home/ico/sandbox/prjs" }, 64 | { "foo/bar", "Z:/home/ico/sandbox/prjs/duc/foo/bar" }, 65 | { "foo/bar/woo/../..", "Z:/home/ico/sandbox/prjs/duc/foo" }, 66 | #else 67 | 68 | /* relative unix paths */ 69 | 70 | { ".", "/home/ico/sandbox/prjs/duc" }, 71 | { "./..", "/home/ico/sandbox/prjs" }, 72 | { "..", "/home/ico/sandbox/prjs" }, 73 | { "foo/bar", "/home/ico/sandbox/prjs/duc/foo/bar" }, 74 | { "foo/bar/woo/../..", "/home/ico/sandbox/prjs/duc/foo" }, 75 | #endif 76 | 77 | { NULL }, 78 | }; 79 | 80 | 81 | int main(void) 82 | { 83 | int i = 0; 84 | while(test[i][0]) { 85 | char *o = duc_canonicalize_path(test[i][0]); 86 | printf("%-40.40s %-40.40s %s\n", o, test[i][1], strcmp(test[i][1], o) ? "ERR" : "ok"); 87 | duc_free(o); 88 | i++; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | #endif 95 | 96 | 97 | 98 | static int is_sep(char c) 99 | { 100 | return c == '/' || c == '\\'; 101 | } 102 | 103 | struct component { 104 | const char *name; 105 | int len; 106 | }; 107 | 108 | 109 | struct splitter { 110 | struct component *cs; 111 | int n; 112 | }; 113 | 114 | 115 | static int count(const char *path) 116 | { 117 | int n = 0; 118 | 119 | while(*path) { 120 | if(is_sep(*path++)) n++; 121 | } 122 | 123 | return n; 124 | } 125 | 126 | 127 | static void split(struct splitter *s, const char *path) 128 | { 129 | const char *p = path; 130 | 131 | s->cs[s->n].name = path; 132 | s->cs[s->n].len = 0; 133 | 134 | while(*p) { 135 | if(is_sep(*p)) { 136 | s->n++; 137 | s->cs[s->n].name = p + 1; 138 | s->cs[s->n].len = 0; 139 | } else { 140 | s->cs[s->n].len++; 141 | } 142 | p++; 143 | } 144 | s->n++; 145 | } 146 | 147 | 148 | char *duc_canonicalize_path(const char *in) 149 | { 150 | char cwd[DUC_PATH_MAX] = ""; 151 | char drive = '\0'; 152 | int i, j; 153 | 154 | /* Check if the given path is relative or absolute */ 155 | 156 | int in_len = strlen(in); 157 | int absolute = 0; 158 | if(in_len >= 1 && is_sep(in[0])) absolute = 1; 159 | if(in_len >= 3 && isalpha(in[0]) && in[1] == ':' && is_sep(in[2])) { 160 | drive = in[0]; 161 | absolute = 1; 162 | } 163 | 164 | if(!absolute) { 165 | if(getcwd(cwd, sizeof(cwd)) == NULL) { 166 | return NULL; 167 | } 168 | #ifdef WIN32 169 | drive = cwd[0]; 170 | #endif 171 | } 172 | 173 | /* Estimate number of parts and alloc space */ 174 | 175 | int parts = count(in) + 2; 176 | if(!absolute) parts += count(cwd); 177 | 178 | struct splitter s; 179 | s.n = 0; 180 | s.cs = duc_malloc0(parts * sizeof(*s.cs)); 181 | 182 | 183 | /* Split parts */ 184 | 185 | if(!absolute) split(&s, cwd); 186 | split(&s, in); 187 | 188 | 189 | /* canonicalize '.' and '..' */ 190 | 191 | for(i=0; ilen == 1 && c->name[0] == '.') { 197 | c->len = 0; 198 | continue; 199 | } 200 | 201 | /* '..' eliminates this and the previous non-empty component */ 202 | 203 | if(c->len == 2 && c->name[0] == '.' && c->name[1] == '.') { 204 | c->len = 0; 205 | for(j=i-1; j>=0; j--) { 206 | if(s.cs[j].len > 0) { 207 | break; 208 | } 209 | } 210 | if(j>=0) s.cs[j].len = 0; 211 | } 212 | } 213 | 214 | UT_string out; 215 | utstring_init(&out); 216 | 217 | /* Calculate total length of all collected parts */ 218 | 219 | if(drive) { 220 | utstring_printf(&out, "%c:", toupper(drive)); 221 | } 222 | 223 | int n = 0; 224 | for(i=drive ? 1 : 0; ilen > 0) { 227 | utstring_printf(&out, "/"); 228 | utstring_bincpy(&out, c->name, c->len); 229 | n++; 230 | } 231 | } 232 | 233 | if(n == 0) utstring_printf(&out, "/"); 234 | 235 | free(s.cs); 236 | return utstring_body(&out); 237 | } 238 | 239 | 240 | /* 241 | * End 242 | */ 243 | 244 | -------------------------------------------------------------------------------- /src/libduc/db-kyoto.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_KYOTOCABINET 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "duc.h" 15 | #include "private.h" 16 | #include "db.h" 17 | 18 | struct db { 19 | KCDB* kdb; 20 | }; 21 | 22 | 23 | duc_errno tcdb_to_errno(KCDB *kdb) 24 | { 25 | return DUC_E_UNKNOWN; 26 | } 27 | 28 | 29 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 30 | { 31 | struct db *db; 32 | int compress = 0; 33 | 34 | uint32_t mode = KCOREADER; 35 | if(flags & DUC_OPEN_RW) mode |= KCOWRITER | KCOCREATE; 36 | if(flags & DUC_OPEN_COMPRESS) compress = 1; 37 | 38 | db = duc_malloc(sizeof *db); 39 | 40 | db->kdb = kcdbnew(); 41 | 42 | db->kdb = kcdbnew(); 43 | if(!db->kdb) { 44 | *e = DUC_E_DB_BACKEND; 45 | goto err1; 46 | } 47 | 48 | char fname[DUC_PATH_MAX]; 49 | snprintf(fname, sizeof(fname), "%s#type=kct#opts=c", path_db); 50 | 51 | int r = kcdbopen(db->kdb, fname, mode); 52 | if(r == 0) { 53 | perror(kcecodename(kcdbecode(db->kdb))); 54 | *e = tcdb_to_errno(db->kdb); 55 | goto err2; 56 | } 57 | 58 | size_t vall; 59 | char *version = db_get(db, "duc_db_version", 14, &vall); 60 | if(version) { 61 | if(strcmp(version, DUC_DB_VERSION) != 0) { 62 | *e = DUC_E_DB_VERSION_MISMATCH; 63 | goto err3; 64 | } 65 | free(version); 66 | } else { 67 | db_put(db, "duc_db_version", 14, DUC_DB_VERSION, strlen(DUC_DB_VERSION)); 68 | } 69 | 70 | return db; 71 | 72 | err3: 73 | kcdbclose(db->kdb); 74 | err2: 75 | kcdbdel(db->kdb); 76 | err1: 77 | free(db); 78 | return NULL; 79 | } 80 | 81 | 82 | void db_close(struct db *db) 83 | { 84 | kcdbclose(db->kdb); 85 | kcdbdel(db->kdb); 86 | free(db); 87 | } 88 | 89 | 90 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 91 | { 92 | int r = kcdbset(db->kdb, key, key_len, val, val_len); 93 | return (r==1) ? DUC_OK : DUC_E_UNKNOWN; 94 | } 95 | 96 | 97 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 98 | { 99 | size_t vall; 100 | void *val = kcdbget(db->kdb, key, key_len, &vall); 101 | *val_len = vall; 102 | return val; 103 | } 104 | 105 | #endif 106 | 107 | /* 108 | * End 109 | */ 110 | -------------------------------------------------------------------------------- /src/libduc/db-leveldb.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_LEVELDB 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "duc.h" 15 | #include "private.h" 16 | #include "db.h" 17 | 18 | struct db { 19 | leveldb_t *db; 20 | leveldb_options_t *options; 21 | leveldb_readoptions_t *roptions; 22 | leveldb_writeoptions_t *woptions; 23 | }; 24 | 25 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 26 | { 27 | struct db *db; 28 | char *err = NULL; 29 | 30 | db = duc_malloc(sizeof *db); 31 | 32 | db->options = leveldb_options_create(); 33 | db->woptions = leveldb_writeoptions_create(); 34 | db->roptions = leveldb_readoptions_create(); 35 | 36 | leveldb_options_set_create_if_missing(db->options, 1); 37 | leveldb_options_set_compression(db->options, leveldb_snappy_compression); 38 | 39 | db->db = leveldb_open(db->options, path_db, &err); 40 | if (err != NULL) { 41 | fprintf(stderr, "%s\n", err); 42 | *e = DUC_E_DB_BACKEND; 43 | return(NULL); 44 | } 45 | 46 | return db; 47 | } 48 | 49 | 50 | void db_close(struct db *db) 51 | { 52 | free(db); 53 | } 54 | 55 | 56 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 57 | { 58 | char *err = NULL; 59 | leveldb_put(db->db, db->woptions, key, key_len, val, val_len, &err); 60 | return err ? DUC_E_UNKNOWN : DUC_OK; 61 | } 62 | 63 | 64 | duc_errno db_putcat(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 65 | { 66 | void *t; 67 | size_t t_len; 68 | 69 | t = db_get(db, key, key_len, &t_len); 70 | if(t) { 71 | t = realloc(t, t_len + val_len); 72 | memcpy(t+t_len, val, t_len + val_len); 73 | db_put(db, key, key_len, t, t_len + val_len); 74 | free(t); 75 | return DUC_OK; 76 | } else { 77 | db_put(db, key, key_len, val, val_len); 78 | return DUC_OK; 79 | } 80 | } 81 | 82 | 83 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 84 | { 85 | char *err = NULL; 86 | char *val = leveldb_get(db->db, db->roptions, key, key_len, val_len, &err); 87 | return val; 88 | } 89 | 90 | #endif 91 | 92 | /* 93 | * End 94 | */ 95 | -------------------------------------------------------------------------------- /src/libduc/db-lmdb.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_LMDB 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "duc.h" 16 | #include "private.h" 17 | #include "db.h" 18 | 19 | struct db { 20 | MDB_env *env; 21 | MDB_dbi dbi; 22 | MDB_txn *txn; 23 | }; 24 | 25 | 26 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 27 | { 28 | struct db *db; 29 | unsigned int env_flags = MDB_NOSUBDIR; 30 | unsigned int open_flags = 0; 31 | unsigned int txn_flags = 0; 32 | 33 | if(flags & DUC_OPEN_RW) { 34 | open_flags |= MDB_CREATE; 35 | } else { 36 | env_flags |= MDB_RDONLY; 37 | txn_flags |= MDB_RDONLY; 38 | } 39 | 40 | /* On 32 bit machines the maximum db size is limited to 1GB, for 64 bit 41 | * machines we increase to 256 GB */ 42 | 43 | size_t map_size = 1024u * 1024u * 1024u; 44 | if(sizeof(size_t) == 8) map_size *= 256u; 45 | 46 | db = duc_malloc(sizeof *db); 47 | 48 | int rc; 49 | 50 | rc = mdb_env_create(&db->env); 51 | if(rc != MDB_SUCCESS) goto out; 52 | 53 | rc = mdb_env_set_mapsize(db->env, map_size); 54 | if(rc != MDB_SUCCESS) goto out; 55 | 56 | rc = mdb_env_open(db->env, path_db, env_flags, 0664); 57 | if(rc != MDB_SUCCESS) goto out; 58 | 59 | rc = mdb_txn_begin(db->env, NULL, txn_flags, &db->txn); 60 | if(rc != MDB_SUCCESS) goto out; 61 | 62 | rc = mdb_open(db->txn, NULL, open_flags, &db->dbi); 63 | if(rc != MDB_SUCCESS) goto out; 64 | 65 | return db; 66 | out: 67 | fprintf(stderr, "%s\n", mdb_strerror(rc)); 68 | *e = DUC_E_DB_NOT_FOUND; 69 | return NULL; 70 | } 71 | 72 | 73 | void db_close(struct db *db) 74 | { 75 | mdb_txn_commit(db->txn); 76 | mdb_dbi_close(db->env, db->dbi); 77 | mdb_env_close(db->env); 78 | free(db); 79 | } 80 | 81 | 82 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 83 | { 84 | MDB_val k, d; 85 | int rc; 86 | 87 | k.mv_size = key_len; 88 | k.mv_data = (void *)key; 89 | d.mv_size = val_len; 90 | d.mv_data = (void *)val; 91 | 92 | rc = mdb_put(db->txn, db->dbi, &k, &d, 0); 93 | if(rc != MDB_SUCCESS) { 94 | fprintf(stderr, "%s\n", mdb_strerror(rc)); 95 | exit(1); 96 | } 97 | 98 | return DUC_OK; 99 | } 100 | 101 | 102 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 103 | { 104 | MDB_val k, d; 105 | int rc; 106 | 107 | k.mv_size = key_len; 108 | k.mv_data = (void *)key; 109 | 110 | rc = mdb_get(db->txn, db->dbi, &k, &d); 111 | 112 | if(rc == MDB_SUCCESS) { 113 | *val_len = d.mv_size; 114 | void *data = malloc(d.mv_size); 115 | memcpy(data, d.mv_data, d.mv_size); 116 | return data; 117 | } else { 118 | *val_len = 0; 119 | return NULL; 120 | } 121 | } 122 | 123 | #endif 124 | 125 | /* 126 | * End 127 | */ 128 | -------------------------------------------------------------------------------- /src/libduc/db-sqlite3.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_SQLITE 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "duc.h" 14 | #include "private.h" 15 | #include "db.h" 16 | 17 | struct db { 18 | sqlite3 *s; 19 | }; 20 | 21 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 22 | { 23 | struct db *db; 24 | int sflags = 0; 25 | 26 | db = duc_malloc(sizeof *db); 27 | 28 | if(flags & DUC_OPEN_RW) 29 | sflags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; 30 | else 31 | sflags |= SQLITE_OPEN_READONLY; 32 | 33 | int r = sqlite3_open_v2(path_db, &db->s, sflags, NULL); 34 | if(r != SQLITE_OK) goto err1; 35 | 36 | /* sqlite3_open() does not always notice corrupt database files. We do a bogus query 37 | * here to catch this error case */ 38 | 39 | r = sqlite3_exec(db->s, "select bogus from bogus", 0, 0, 0); 40 | if(r != 1) goto err1; 41 | 42 | char *q = "create table blobs(key unique primary key, value)"; 43 | sqlite3_exec(db->s, q, 0, 0, 0); 44 | 45 | q = "create index keys on blobs(key)"; 46 | sqlite3_exec(db->s, q, 0, 0, 0); 47 | 48 | sqlite3_exec(db->s, "begin", 0, 0, 0); 49 | 50 | return db; 51 | err1: 52 | free(db); 53 | *e = DUC_E_DB_CORRUPT; 54 | if(r == SQLITE_CANTOPEN) *e = DUC_E_DB_NOT_FOUND; 55 | return NULL; 56 | } 57 | 58 | 59 | void db_close(struct db *db) 60 | { 61 | sqlite3_exec(db->s, "commit", 0, 0, 0); 62 | sqlite3_close(db->s); 63 | free(db); 64 | } 65 | 66 | 67 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 68 | { 69 | sqlite3_stmt *pStmt; 70 | char *q = "insert or replace into blobs(key, value) values(?, ?)"; 71 | 72 | sqlite3_prepare(db->s, q, -1, &pStmt, 0); 73 | sqlite3_bind_text(pStmt, 1, key, key_len, SQLITE_STATIC); 74 | sqlite3_bind_blob(pStmt, 2, val, val_len, SQLITE_STATIC); 75 | sqlite3_step(pStmt); 76 | sqlite3_finalize(pStmt); 77 | return DUC_OK; 78 | } 79 | 80 | 81 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 82 | { 83 | sqlite3_stmt *pStmt; 84 | char *q = "select value from blobs where key = ?"; 85 | char *val = NULL; 86 | 87 | sqlite3_prepare(db->s, q, -1, &pStmt, 0); 88 | sqlite3_bind_text(pStmt, 1, key, key_len, SQLITE_STATIC); 89 | 90 | int r = sqlite3_step(pStmt); 91 | if(r == SQLITE_ROW) { 92 | *val_len = sqlite3_column_bytes(pStmt, 0); 93 | val = duc_malloc(*val_len); 94 | memcpy(val, sqlite3_column_blob(pStmt, 0), *val_len); 95 | } 96 | sqlite3_finalize(pStmt); 97 | 98 | return val; 99 | } 100 | 101 | #endif 102 | 103 | /* 104 | * End 105 | */ 106 | -------------------------------------------------------------------------------- /src/libduc/db-tkrzw.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_TKRZW 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // For sizing the DBs on large large filesystems. 11 | #include 12 | 13 | #include 14 | 15 | #include "duc.h" 16 | #include "private.h" 17 | #include "db.h" 18 | 19 | struct db { 20 | TkrzwDBM* hdb; 21 | }; 22 | 23 | duc_errno tkrzwdb_to_errno(TkrzwDBM *hdb) 24 | { 25 | 26 | TkrzwStatus status = tkrzw_get_last_status(); 27 | switch(status.code) { 28 | case TKRZW_STATUS_SUCCESS: return DUC_OK; 29 | case TKRZW_STATUS_UNKNOWN_ERROR: return DUC_E_UNKNOWN; 30 | case TKRZW_STATUS_SYSTEM_ERROR: return DUC_E_DB_NOT_FOUND; 31 | case TKRZW_STATUS_NOT_IMPLEMENTED_ERROR: return DUC_E_NOT_IMPLEMENTED; 32 | case TKRZW_STATUS_PRECONDITION_ERROR: return DUC_E_NOT_IMPLEMENTED; 33 | case TKRZW_STATUS_INVALID_ARGUMENT_ERROR: return DUC_E_NOT_IMPLEMENTED; 34 | case TKRZW_STATUS_CANCELED_ERROR: return DUC_E_UNKNOWN; 35 | case TKRZW_STATUS_NOT_FOUND_ERROR: return DUC_E_DB_NOT_FOUND; 36 | case TKRZW_STATUS_PERMISSION_ERROR: return DUC_E_PERMISSION_DENIED; 37 | case TKRZW_STATUS_INFEASIBLE_ERROR: return DUC_E_DB_BACKEND; 38 | case TKRZW_STATUS_DUPLICATION_ERROR: return DUC_E_DB_CORRUPT; 39 | case TKRZW_STATUS_BROKEN_DATA_ERROR: return DUC_E_DB_CORRUPT; 40 | case TKRZW_STATUS_NETWORK_ERROR: return DUC_E_UNKNOWN; 41 | case TKRZW_STATUS_APPLICATION_ERROR: return DUC_E_UNKNOWN; 42 | default: return DUC_E_UNKNOWN; 43 | } 44 | } 45 | 46 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 47 | { 48 | struct db *db; 49 | int compress = 0; 50 | int writeable = 0; 51 | char options[256] = "dbm=HashDBM,file=StdFile,offset_width=5"; 52 | 53 | if (flags & DUC_OPEN_FORCE) { 54 | char trunc[] = ",truncate=true"; 55 | strcat(options,trunc); 56 | } 57 | 58 | // Ideally we would know the filesystem here so we can scale things properly, but this is a major re-work of API, so for now just define some new DUC_FS_*" factors... 59 | if (flags & DUC_FS_BIG) { 60 | char big[] = ",num_buckets=100000000"; 61 | strcat(options,big); 62 | } 63 | 64 | if (flags & DUC_FS_BIGGER) { 65 | char bigger[] = ",num_buckets=1000000000"; 66 | strcat(options,bigger); 67 | } 68 | 69 | if (flags & DUC_FS_BIGGEST) { 70 | char biggest[] = ",num_buckets=10000000000"; 71 | strcat(options,biggest); 72 | } 73 | 74 | if (flags & DUC_OPEN_RW) writeable = 1; 75 | if (flags & DUC_OPEN_COMPRESS) { 76 | /* Do no compression for now, need to update configure tests first */ 77 | char comp[] = ",record_comp_mode=RECORD_COMP_LZ4"; 78 | strcat(options,comp); 79 | } 80 | 81 | db = duc_malloc(sizeof *db); 82 | 83 | db->hdb = tkrzw_dbm_open(path_db, writeable, options); 84 | if (!db->hdb) { 85 | *e = tkrzwdb_to_errno(db->hdb); 86 | goto err1; 87 | } 88 | 89 | size_t vall; 90 | char *version = db_get(db, "duc_db_version", 14, &vall); 91 | if (version) { 92 | if(strcmp(version, DUC_DB_VERSION) != 0) { 93 | *e = DUC_E_DB_VERSION_MISMATCH; 94 | goto err2; 95 | } 96 | free(version); 97 | } else { 98 | db_put(db, "duc_db_version", 14, DUC_DB_VERSION, strlen(DUC_DB_VERSION)); 99 | } 100 | 101 | return db; 102 | 103 | err2: 104 | tkrzw_dbm_close(db->hdb); 105 | err1: 106 | free(db); 107 | return NULL; 108 | } 109 | 110 | 111 | void db_close(struct db *db) 112 | { 113 | tkrzw_dbm_close(db->hdb); 114 | free(db); 115 | } 116 | 117 | 118 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 119 | { 120 | int r = tkrzw_dbm_set(db->hdb, key, key_len, val, val_len, 1); 121 | return (r==1) ? DUC_OK : DUC_E_UNKNOWN; 122 | } 123 | 124 | 125 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 126 | { 127 | int vall; 128 | void *val = tkrzw_dbm_get(db->hdb, key, key_len, &vall); 129 | *val_len = vall; 130 | return val; 131 | } 132 | 133 | #endif 134 | 135 | /* 136 | * End 137 | */ 138 | -------------------------------------------------------------------------------- /src/libduc/db-tokyo.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #ifdef ENABLE_TOKYOCABINET 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "duc.h" 16 | #include "private.h" 17 | #include "db.h" 18 | 19 | struct db { 20 | TCBDB* hdb; 21 | }; 22 | 23 | 24 | duc_errno tcdb_to_errno(TCBDB *hdb) 25 | { 26 | int ec = tcbdbecode(hdb); 27 | 28 | switch(ec) { 29 | case TCESUCCESS: return DUC_OK; 30 | case TCENOFILE: return DUC_E_DB_NOT_FOUND; 31 | case TCENOPERM: return DUC_E_PERMISSION_DENIED; 32 | case TCEOPEN: return DUC_E_PERMISSION_DENIED; 33 | case TCEMETA: return DUC_E_DB_CORRUPT; 34 | case TCERHEAD: return DUC_E_DB_CORRUPT; 35 | case TCEREAD: return DUC_E_DB_CORRUPT; 36 | case TCESEEK: return DUC_E_DB_CORRUPT; 37 | default: return DUC_E_UNKNOWN; 38 | } 39 | } 40 | 41 | 42 | struct db *db_open(const char *path_db, int flags, duc_errno *e) 43 | { 44 | struct db *db; 45 | int compress = 0; 46 | 47 | uint32_t mode = HDBONOLCK | HDBOREADER; 48 | if(flags & DUC_OPEN_RW) mode |= HDBOWRITER | HDBOCREAT; 49 | if(flags & DUC_OPEN_COMPRESS) compress = 1; 50 | 51 | /* If we pass in the -f switch, force opening the DB no matter what */ 52 | if(flags & DUC_OPEN_FORCE) { mode |= BDBOTRUNC; } 53 | 54 | db = duc_malloc(sizeof *db); 55 | 56 | db->hdb = tcbdbnew(); 57 | if(!db->hdb) { 58 | *e = DUC_E_DB_BACKEND; 59 | goto err1; 60 | } 61 | 62 | int opts = BDBTLARGE; 63 | if(compress) opts |= BDBTDEFLATE; 64 | int ret = tcbdbtune(db->hdb, 256, 512, 131072, 9, 11, opts); 65 | if(ret == 0) { 66 | *e = tcdb_to_errno(db->hdb); 67 | goto err2; 68 | } 69 | 70 | int r = tcbdbopen(db->hdb, path_db, mode); 71 | if(r == 0) { 72 | *e = tcdb_to_errno(db->hdb); 73 | goto err2; 74 | } 75 | 76 | size_t vall; 77 | char *version = db_get(db, "duc_db_version", 14, &vall); 78 | if(version) { 79 | if(strcmp(version, DUC_DB_VERSION) != 0) { 80 | *e = DUC_E_DB_VERSION_MISMATCH; 81 | goto err3; 82 | } 83 | free(version); 84 | } else { 85 | db_put(db, "duc_db_version", 14, DUC_DB_VERSION, strlen(DUC_DB_VERSION)); 86 | } 87 | 88 | return db; 89 | 90 | err3: 91 | tcbdbclose(db->hdb); 92 | err2: 93 | tcbdbdel(db->hdb); 94 | err1: 95 | free(db); 96 | return NULL; 97 | } 98 | 99 | 100 | void db_close(struct db *db) 101 | { 102 | tcbdbclose(db->hdb); 103 | tcbdbdel(db->hdb); 104 | free(db); 105 | } 106 | 107 | 108 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len) 109 | { 110 | int r = tcbdbput(db->hdb, key, key_len, val, val_len); 111 | return (r==1) ? DUC_OK : DUC_E_UNKNOWN; 112 | } 113 | 114 | 115 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len) 116 | { 117 | int vall; 118 | void *val = tcbdbget(db->hdb, key, key_len, &vall); 119 | *val_len = vall; 120 | return val; 121 | } 122 | 123 | #endif 124 | 125 | /* 126 | * End 127 | */ 128 | -------------------------------------------------------------------------------- /src/libduc/db.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "duc.h" 9 | #include "db.h" 10 | #include "buffer.h" 11 | #include "private.h" 12 | 13 | #define MAGIC_LEN 64 14 | 15 | 16 | 17 | /* 18 | * Store report. Add the report index to the 'duc_index_reports' key if not 19 | * previously indexed 20 | */ 21 | 22 | duc_errno db_write_report(duc *duc, const struct duc_index_report *report) 23 | { 24 | size_t tmpl; 25 | char *tmp = db_get(duc->db, report->path, strlen(report->path), &tmpl); 26 | 27 | //printf("writing report, ->topn_cnt = %d, ->topn_cnt_max = %d\n",report->topn_cnt, report->topn_cnt_max); 28 | if(tmp == NULL) { 29 | char *tmp = db_get(duc->db, "duc_index_reports", 17, &tmpl); 30 | if(tmp) { 31 | tmp = duc_realloc(tmp, tmpl + sizeof(report->path)); 32 | memcpy(tmp + tmpl, report->path, sizeof(report->path)); 33 | db_put(duc->db, "duc_index_reports", 17, tmp, tmpl + sizeof(report->path)); 34 | } else { 35 | db_put(duc->db, "duc_index_reports", 17, report->path, sizeof(report->path)); 36 | } 37 | 38 | /* write histogram */ 39 | tmp = db_get(duc->db, "duc_index_histograms", 20, &tmpl); 40 | if (tmp) { 41 | tmp = duc_realloc(tmp, tmpl + sizeof(report->histogram)); 42 | memcpy(tmp + tmpl, report->histogram, sizeof(report->histogram)); 43 | db_put(duc->db, "duc_index_histograms", 20, tmp, 44 | tmpl + sizeof(report->histogram)); 45 | } else { 46 | db_put(duc->db, "duc_index_histograms", 20, report->histogram, 47 | sizeof(report->histogram)); 48 | } 49 | 50 | /* write topn array, FIXME to really work... */ 51 | char str[] = "duc_index_topn_info"; 52 | int str_len = sizeof(str); 53 | tmp = db_get(duc->db, str, str_len , &tmpl); 54 | if (tmp) { 55 | tmp = duc_realloc(tmp, tmpl + sizeof(report->topn_array)); 56 | memcpy(tmp + tmpl, report->topn_array, sizeof(report->topn_array)); 57 | db_put(duc->db, str, str_len, tmp, 58 | tmpl + sizeof(report->topn_array)); 59 | } else { 60 | db_put(duc->db, str, str_len, report->topn_array, 61 | sizeof(report->topn_array)); 62 | } 63 | 64 | } else { 65 | free(tmp); 66 | } 67 | 68 | struct buffer *b = buffer_new(NULL, 0); 69 | 70 | buffer_put_index_report(b, report); 71 | db_put(duc->db, report->path, strlen(report->path), b->data, b->len); 72 | buffer_free(b); 73 | 74 | return 0; 75 | } 76 | 77 | 78 | struct duc_index_report *db_read_report(duc *duc, const char *path) 79 | { 80 | struct duc_index_report *report; 81 | size_t vall; 82 | 83 | char *val = db_get(duc->db, path, strlen(path), &vall); 84 | if(val == NULL) { 85 | duc->err = DUC_E_PATH_NOT_FOUND; 86 | return NULL; 87 | } 88 | 89 | struct buffer *b = buffer_new(val, vall); 90 | 91 | report = duc_malloc(sizeof *report); 92 | buffer_get_index_report(b, report); 93 | buffer_free(b); 94 | 95 | return report; 96 | } 97 | 98 | /* Return what type of DB we think this is. Note, leveldb is a directory... */ 99 | 100 | char *duc_db_type_check(const char *path_db) 101 | { 102 | struct stat sb; 103 | 104 | stat(path_db,&sb); 105 | 106 | if (S_ISREG(sb.st_mode)) { 107 | 108 | FILE *f = fopen(path_db,"r"); 109 | 110 | if(f == NULL) { 111 | //duc_log(NULL, DUC_LOG_DBG, "Not reading configuration from '%s': %s", path, streo; 112 | return("unknown"); 113 | } 114 | 115 | char buf[MAGIC_LEN]; 116 | 117 | /* read first MAGIC_LEN bytes of file then look for the strings, etc for each type of DB we support. */ 118 | size_t len = fread(buf, 1, sizeof(buf),f); 119 | 120 | if (strncmp(buf,"Kyoto CaBiNeT",13) == 0) { 121 | return("Kyoto Cabinet"); 122 | } 123 | 124 | if (strncmp(buf,"ToKyO CaBiNeT",13) == 0) { 125 | return("Tokyo Cabinet"); 126 | } 127 | 128 | if (strncmp(buf,"TkrzwHDB",8) == 0) { 129 | return("Tkrzw HashDBM"); 130 | } 131 | 132 | if (strncmp(buf,"SQLite format 3",15) == 0) { 133 | return("SQLite3"); 134 | } 135 | 136 | } 137 | 138 | /* Check for DB_PATH that's a directory, and look in there. */ 139 | if (S_ISDIR(sb.st_mode)) { 140 | return("leveldb"); 141 | } 142 | return("unknown"); 143 | } 144 | 145 | /* 146 | * End 147 | */ 148 | 149 | -------------------------------------------------------------------------------- /src/libduc/db.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef db_h 3 | #define db_h 4 | 5 | #include "duc.h" 6 | 7 | struct db; 8 | 9 | struct db *db_open(const char *path_db, int flags, duc_errno *e); 10 | void db_close(struct db *db); 11 | duc_errno db_put(struct db *db, const void *key, size_t key_len, const void *val, size_t val_len); 12 | void *db_get(struct db *db, const void *key, size_t key_len, size_t *val_len); 13 | 14 | 15 | duc_errno db_write_report(duc *duc, const struct duc_index_report *rep); 16 | struct duc_index_report *db_read_report(duc *duc, const char *path); 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /src/libduc/dir.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "duc.h" 12 | #include "db.h" 13 | #include "buffer.h" 14 | #include "private.h" 15 | 16 | 17 | struct duc_dir { 18 | struct duc *duc; 19 | struct duc_devino devino; 20 | struct duc_devino devino_parent; 21 | time_t mtime; 22 | char *path; 23 | struct duc_dirent *ent_list; 24 | struct duc_size size; 25 | size_t ent_cur; 26 | size_t ent_count; 27 | size_t ent_pool; 28 | duc_size_type size_type; 29 | duc_sort sort; 30 | }; 31 | 32 | 33 | struct duc_dir *duc_dir_new(struct duc *duc, const struct duc_devino *devino) 34 | { 35 | size_t vall; 36 | char key[32]; 37 | size_t keyl = snprintf(key, sizeof(key), "%jx/%jx", (uintmax_t)devino->dev, (uintmax_t)devino->ino); 38 | char *val = db_get(duc->db, key, keyl, &vall); 39 | if(val == NULL) { 40 | duc->err = DUC_E_PATH_NOT_FOUND; 41 | return NULL; 42 | } 43 | 44 | struct duc_dir *dir = duc_malloc0(sizeof(struct duc_dir)); 45 | 46 | dir->duc = duc; 47 | dir->devino.dev = devino->dev; 48 | dir->devino.ino = devino->ino; 49 | dir->path = NULL; 50 | dir->ent_pool = 32768; 51 | dir->ent_list = duc_malloc(dir->ent_pool); 52 | dir->size_type = -1; 53 | 54 | struct buffer *b = buffer_new(val, vall); 55 | 56 | /* Read dir header */ 57 | 58 | buffer_get_dir(b, &dir->devino_parent, &dir->mtime); 59 | 60 | /* Read all dirents */ 61 | 62 | while(b->ptr < b->len) { 63 | 64 | if((dir->ent_count+1) * sizeof(struct duc_dirent) > dir->ent_pool) { 65 | dir->ent_pool *= 2; 66 | dir->ent_list = duc_realloc(dir->ent_list, dir->ent_pool); 67 | } 68 | 69 | struct duc_dirent *ent = &dir->ent_list[dir->ent_count]; 70 | buffer_get_dirent(b, ent); 71 | 72 | duc_size_accum(&dir->size, &ent->size); 73 | dir->ent_count ++; 74 | } 75 | 76 | buffer_free(b); 77 | 78 | return dir; 79 | } 80 | 81 | 82 | void duc_dir_get_size(duc_dir *dir, struct duc_size *size) 83 | { 84 | *size = dir->size; 85 | } 86 | 87 | 88 | size_t duc_dir_get_count(duc_dir *dir) 89 | { 90 | return dir->ent_count; 91 | } 92 | 93 | 94 | char *duc_dir_get_path(duc_dir *dir) 95 | { 96 | return strdup(dir->path); 97 | } 98 | 99 | 100 | duc_dir *duc_dir_openent(duc_dir *dir, const struct duc_dirent *e) 101 | { 102 | 103 | duc_dir *dir2 = duc_dir_new(dir->duc, &e->devino); 104 | if(dir2) { 105 | int r = asprintf(&dir2->path, "%s/%s", dir->path, e->name); 106 | if (r < 0) { 107 | dir2->path = '\0'; 108 | } 109 | } 110 | return dir2; 111 | } 112 | 113 | 114 | duc_dir *duc_dir_openat(duc_dir *dir, const char *name) 115 | { 116 | if(strcmp(name, "..") == 0) { 117 | 118 | /* Special case: go up one directory */ 119 | 120 | if(dir->devino_parent.dev && dir->devino_parent.ino) { 121 | duc_dir *pdir = duc_dir_new(dir->duc, &dir->devino_parent); 122 | if(pdir == NULL) return NULL; 123 | pdir->path = duc_strdup(dir->path); 124 | dirname(pdir->path); 125 | return pdir; 126 | } 127 | 128 | } else { 129 | 130 | /* Find given name in dir */ 131 | 132 | size_t i; 133 | struct duc_dirent *e = dir->ent_list; 134 | for(i=0; ient_count; i++) { 135 | if(strcmp(e->name, name) == 0) { 136 | return duc_dir_openent(dir, e); 137 | } 138 | e++; 139 | } 140 | } 141 | 142 | return NULL; 143 | } 144 | 145 | 146 | struct duc_dirent *duc_dir_find_child(duc_dir *dir, const char *name) 147 | { 148 | size_t i; 149 | struct duc_dirent *ent = dir->ent_list; 150 | 151 | for(i=0; ient_count; i++) { 152 | if(strcmp(name, ent->name) == 0) { 153 | return ent; 154 | } 155 | ent++; 156 | } 157 | 158 | dir->duc->err = DUC_E_PATH_NOT_FOUND; 159 | return NULL; 160 | } 161 | 162 | 163 | duc_dir *duc_dir_open(struct duc *duc, const char *path) 164 | { 165 | /* Canonicalized path */ 166 | 167 | char *path_canon = duc_canonicalize_path(path); 168 | if(!path_canon) { 169 | duc->err = DUC_E_PATH_NOT_FOUND; 170 | return NULL; 171 | } 172 | 173 | /* Find top path in database */ 174 | 175 | char *path_try = duc_strdup(path_canon); 176 | int l = strlen(path_try); 177 | struct duc_devino devino = { 0, 0 }; 178 | while(l > 0) { 179 | path_try[l] = '\0'; 180 | struct duc_index_report *report; 181 | report = db_read_report(duc, path_try); 182 | if(report) { 183 | devino = report->devino; 184 | free(report); 185 | break; 186 | } 187 | l--; 188 | } 189 | free(path_try); 190 | 191 | if(l == 0) { 192 | duc_log(duc, DUC_LOG_FTL, "Path %s not found in database", path_canon); 193 | duc->err = DUC_E_PATH_NOT_FOUND; 194 | free(path_canon); 195 | return NULL; 196 | } 197 | 198 | struct duc_dir *dir; 199 | 200 | dir = duc_dir_new(duc, &devino); 201 | 202 | if(dir == NULL) { 203 | duc->err = DUC_E_PATH_NOT_FOUND; 204 | free(path_canon); 205 | return NULL; 206 | } 207 | 208 | char rest[DUC_PATH_MAX]; 209 | strncpy(rest, path_canon+l, sizeof rest); 210 | 211 | char *name = strtok(rest, "/"); 212 | 213 | while(dir && name) { 214 | 215 | struct duc_dirent *ent = duc_dir_find_child(dir, name); 216 | 217 | struct duc_dir *dir_next = NULL; 218 | 219 | if(ent) { 220 | dir_next = duc_dir_openent(dir, ent); 221 | } 222 | 223 | duc_dir_close(dir); 224 | dir = dir_next; 225 | name = strtok(NULL, "/"); 226 | } 227 | 228 | if(dir) { 229 | dir->path = strdup(path_canon); 230 | } 231 | 232 | free(path_canon); 233 | 234 | return dir; 235 | } 236 | 237 | 238 | static int fn_comp_apparent(const void *a, const void *b) 239 | { 240 | const struct duc_dirent *ea = a; 241 | const struct duc_dirent *eb = b; 242 | const struct duc_size *sa = &ea->size; 243 | const struct duc_size *sb = &eb->size; 244 | if(sa->apparent < sb->apparent) return +1; 245 | if(sa->apparent > sb->apparent) return -1; 246 | if(sa->actual < sb->actual) return +1; 247 | if(sa->actual > sb->actual) return -1; 248 | return strcmp(ea->name, eb->name); 249 | } 250 | 251 | 252 | static int fn_comp_actual(const void *a, const void *b) 253 | { 254 | const struct duc_dirent *ea = a; 255 | const struct duc_dirent *eb = b; 256 | const struct duc_size *sa = &ea->size; 257 | const struct duc_size *sb = &eb->size; 258 | if(sa->actual < sb->actual) return +1; 259 | if(sa->actual > sb->actual) return -1; 260 | if(sa->apparent < sb->apparent) return +1; 261 | if(sa->apparent > sb->apparent) return -1; 262 | return strcmp(ea->name, eb->name); 263 | } 264 | 265 | 266 | static int fn_comp_count(const void *a, const void *b) 267 | { 268 | const struct duc_dirent *ea = a; 269 | const struct duc_dirent *eb = b; 270 | const struct duc_size *sa = &ea->size; 271 | const struct duc_size *sb = &eb->size; 272 | if(sa->count < sb->count) return +1; 273 | if(sa->count > sb->count) return -1; 274 | return strcmp(ea->name, eb->name); 275 | } 276 | 277 | 278 | static int fn_comp_name(const void *a, const void *b) 279 | { 280 | const struct duc_dirent *ea = a; 281 | const struct duc_dirent *eb = b; 282 | return strcmp(ea->name, eb->name); 283 | } 284 | 285 | 286 | struct duc_dirent *duc_dir_read(duc_dir *dir, duc_size_type st, duc_sort sort) 287 | { 288 | int (*fn_comp)(const void *, const void *); 289 | 290 | dir->duc->err = 0; 291 | 292 | if(dir->size_type != st || dir->sort != sort) { 293 | switch(sort) { 294 | case DUC_SORT_SIZE: 295 | switch(st) { 296 | case DUC_SIZE_TYPE_APPARENT: 297 | fn_comp = fn_comp_apparent; 298 | break; 299 | case DUC_SIZE_TYPE_ACTUAL: 300 | fn_comp = fn_comp_actual; 301 | break; 302 | case DUC_SIZE_TYPE_COUNT: 303 | fn_comp = fn_comp_count; 304 | break; 305 | } 306 | break; 307 | case DUC_SORT_NAME: 308 | fn_comp = fn_comp_name; 309 | break; 310 | } 311 | qsort(dir->ent_list, dir->ent_count, sizeof(struct duc_dirent), fn_comp); 312 | dir->size_type = st; 313 | dir->sort = sort; 314 | } 315 | 316 | if(dir->ent_cur < dir->ent_count) { 317 | struct duc_dirent *ent = &dir->ent_list[dir->ent_cur]; 318 | dir->ent_cur ++; 319 | return ent; 320 | } else { 321 | return NULL; 322 | } 323 | } 324 | 325 | 326 | int duc_dir_seek(duc_dir *dir, size_t offset) 327 | { 328 | if(offset < dir->ent_count) { 329 | dir->ent_cur = offset; 330 | return 0; 331 | } else { 332 | return -1; 333 | } 334 | } 335 | 336 | 337 | int duc_dir_rewind(duc_dir *dir) 338 | { 339 | return duc_dir_seek(dir, 0); 340 | } 341 | 342 | 343 | int duc_dir_close(duc_dir *dir) 344 | { 345 | if(dir->path) free(dir->path); 346 | size_t i; 347 | for(i=0; ient_count; i++) { 348 | free(dir->ent_list[i].name); 349 | } 350 | free(dir->ent_list); 351 | free(dir); 352 | return 0; 353 | } 354 | 355 | 356 | struct duc_index_report *duc_get_report(duc *duc, size_t id) 357 | { 358 | size_t indexl; 359 | 360 | char *index = db_get(duc->db, "duc_index_reports", 17, &indexl); 361 | if(index == NULL) return NULL; 362 | 363 | size_t report_count = indexl / DUC_PATH_MAX; 364 | if(id >= report_count) return NULL; 365 | 366 | char *path = index + id * DUC_PATH_MAX; 367 | 368 | struct duc_index_report *r = db_read_report(duc, path); 369 | 370 | free(index); 371 | 372 | return r; 373 | } 374 | 375 | 376 | /* 377 | * End 378 | */ 379 | 380 | -------------------------------------------------------------------------------- /src/libduc/duc.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "private.h" 17 | #include "duc.h" 18 | #include "db.h" 19 | 20 | 21 | static void default_log_callback(duc_log_level level, const char *fmt, va_list va) 22 | { 23 | vfprintf(stderr, fmt, va); 24 | fprintf(stderr, "\n"); 25 | } 26 | 27 | 28 | 29 | duc *duc_new(void) 30 | { 31 | duc *duc = duc_malloc(sizeof *duc); 32 | memset(duc, 0, sizeof *duc); 33 | duc->log_level = DUC_LOG_WRN; 34 | duc->log_callback = default_log_callback; 35 | 36 | return duc; 37 | } 38 | 39 | 40 | void duc_del(duc *duc) 41 | { 42 | if(duc->db) duc_close(duc); 43 | free(duc); 44 | } 45 | 46 | 47 | void duc_set_log_level(duc *duc, duc_log_level level) 48 | { 49 | duc->log_level = level; 50 | } 51 | 52 | 53 | void duc_set_log_callback(duc *duc, duc_log_callback cb) 54 | { 55 | duc->log_callback = cb; 56 | } 57 | 58 | // Return 0 ok, -1 for errors. 59 | int duc_open(duc *duc, const char *path_db, duc_open_flags flags) 60 | { 61 | char tmp[DUC_PATH_MAX]; 62 | 63 | /* An empty path means check the ENV path instead */ 64 | if(path_db == NULL) { 65 | path_db = getenv("DUC_DATABASE"); 66 | } 67 | 68 | #ifdef WIN32 69 | /* If the path is still empty, default to %APPDATA%/duc.db on windows */ 70 | if(path_db == NULL) { 71 | char *home = getenv("APPDATA"); 72 | if(home) { 73 | snprintf(tmp, sizeof tmp, "%s/duc.db", home); 74 | path_db = tmp; 75 | } 76 | } 77 | #else 78 | /* Search for ~/.duc.db and use it if it exists for backwards compatibility reasons. */ 79 | if(path_db == NULL) { 80 | char *home = getenv("HOME"); 81 | if(home) { 82 | snprintf(tmp, sizeof tmp, "%s/.duc.db", home); 83 | if(access(tmp, R_OK | W_OK) != -1) { 84 | // file exists 85 | path_db = tmp; 86 | duc_log(duc, DUC_LOG_WRN, "Using old database at \"%s\", please move this to \"$XDG_CACHE_HOME/duc/duc.db\"", path_db); 87 | } 88 | } 89 | } 90 | /* Otherwise, follow the basedir specification and use $XDG_CACHE_HOME/duc/duc.db*/ 91 | if(path_db == NULL) { 92 | char *home = getenv("XDG_CACHE_HOME"); 93 | if(home) { 94 | /* Append parent folder */ 95 | snprintf(tmp, sizeof tmp, "%s/duc", home); 96 | /* Create if needed */ 97 | mkdir(tmp, 0700); 98 | /* Append file to folder*/ 99 | snprintf(tmp, sizeof tmp, "%s/duc/duc.db", home); 100 | path_db = tmp; 101 | } 102 | } 103 | /* Last fallback: ~/.cache/duc/duc.db */ 104 | if(path_db == NULL) { 105 | char *home = getenv("HOME"); 106 | if(home) { 107 | /* Append parent folder */ 108 | snprintf(tmp, sizeof tmp, "%s/.cache/duc", home); 109 | /* Create if needed */ 110 | mkdir(tmp, 0700); 111 | /* Append file to folder*/ 112 | snprintf(tmp, sizeof tmp, "%s/.cache/duc/duc.db", home); 113 | path_db = tmp; 114 | } 115 | } 116 | #endif 117 | 118 | if(path_db == NULL) { 119 | duc->err = DUC_E_DB_NOT_FOUND; 120 | return -1; 121 | } 122 | 123 | duc_log(duc, DUC_LOG_INF, "%s database \"%s\"", 124 | (flags & DUC_OPEN_RO) ? "Reading from" : "Writing to", 125 | path_db); 126 | 127 | duc->db = db_open(path_db, flags, &duc->err); 128 | if(duc->db == NULL) { 129 | duc_log(duc, DUC_LOG_FTL, "Error opening: %s - %s", path_db, duc_strerror(duc)); 130 | return -1; 131 | } 132 | else { 133 | 134 | /* Now we can maybe do some quick checks to see if we 135 | * tried to open a non-supported DB type. */ 136 | 137 | char *db_type = duc_db_type_check(path_db); 138 | if (db_type && (strcmp(db_type,"unknown") == 0)) { 139 | duc_log(duc, DUC_LOG_FTL, "Error opening: %s - unsupported DB type _%s_, duc compiled for %s", path_db, db_type, DB_BACKEND); 140 | return -1; 141 | } 142 | } 143 | return 0; 144 | } 145 | 146 | 147 | int duc_close(struct duc *duc) 148 | { 149 | if(duc->db) { 150 | db_close(duc->db); 151 | duc->db = NULL; 152 | } 153 | return 0; 154 | } 155 | 156 | 157 | void duc_log(struct duc *duc, duc_log_level level, const char *fmt, ...) 158 | { 159 | va_list va; 160 | va_start(va, fmt); 161 | 162 | if(duc) { 163 | if(duc->log_callback && level <= duc->log_level) { 164 | duc->log_callback(level, fmt, va); 165 | } 166 | } else { 167 | default_log_callback(level, fmt, va); 168 | } 169 | 170 | va_end(va); 171 | } 172 | 173 | 174 | duc_errno duc_error(duc *duc) 175 | { 176 | return duc->err; 177 | } 178 | 179 | 180 | const char *duc_strerror(duc *duc) 181 | { 182 | switch(duc->err) { 183 | case DUC_OK: return "No error: success"; 184 | case DUC_E_DB_NOT_FOUND: return "Database not found"; 185 | case DUC_E_DB_CORRUPT: return "Database corrupt and not usable"; 186 | case DUC_E_DB_VERSION_MISMATCH: return "Database version mismatch"; 187 | case DUC_E_DB_TYPE_MISMATCH: return "Database type mismatch"; 188 | case DUC_E_PATH_NOT_FOUND: return "Requested path not found"; 189 | case DUC_E_PERMISSION_DENIED: return "Permission denied"; 190 | case DUC_E_OUT_OF_MEMORY: return "Out of memory"; 191 | case DUC_E_DB_BACKEND: return "An error occurred in the database backend"; 192 | default: return "Unknown error, contact the author"; 193 | } 194 | } 195 | 196 | 197 | int humanize(double v, int exact, double scale, char *buf, size_t maxlen) 198 | { 199 | char prefix[] = { '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; 200 | char *p = prefix; 201 | 202 | if(exact || v < scale) { 203 | return snprintf(buf, maxlen, "%.0f", v); 204 | } else { 205 | while(v >= scale) { 206 | v /= scale; 207 | p ++; 208 | } 209 | return snprintf(buf, maxlen, "%.1f%c", v, *p); 210 | } 211 | } 212 | 213 | 214 | int duc_human_number(double v, int exact, char *buf, size_t maxlen) 215 | { 216 | return humanize(v, exact, 1000, buf, maxlen); 217 | } 218 | 219 | 220 | int duc_human_size(const struct duc_size *size, duc_size_type st, int exact, char *buf, size_t maxlen) 221 | { 222 | double v; 223 | int base; 224 | 225 | if(st == DUC_SIZE_TYPE_COUNT) { 226 | v = size->count; 227 | base = 1000; 228 | } 229 | if(st == DUC_SIZE_TYPE_APPARENT) { 230 | v = size->apparent; 231 | base = 1024; 232 | } 233 | if(st == DUC_SIZE_TYPE_ACTUAL) { 234 | v = size->actual; 235 | base = 1024; 236 | } 237 | return humanize(v, exact, base, buf, maxlen); 238 | } 239 | 240 | 241 | int duc_human_duration(struct timeval start, struct timeval stop, char *buf, size_t maxlen) 242 | { 243 | double start_secs, stop_secs, secs; 244 | double days, hours, mins; 245 | 246 | /* fixme: use timersub here */ 247 | 248 | start_secs = start.tv_sec + (start.tv_usec / 1000000.0); 249 | stop_secs = stop.tv_sec + (stop.tv_usec / 1000000.0); 250 | secs = stop_secs - start_secs; 251 | 252 | days = floor(secs / 86400); 253 | secs = secs - (days * 86400); 254 | hours = floor(secs / 3600); 255 | secs = secs - (hours * 3600); 256 | mins = floor(secs / 60); 257 | secs = secs - (mins * 60); 258 | 259 | int l; 260 | 261 | if (days) { 262 | l = snprintf(buf, maxlen, "%.0fd, %.0f hours, %.0f minutes, and %.2f seconds.", days, hours, mins, secs); 263 | } else if (hours) { 264 | l = snprintf(buf, maxlen, "%.0f hours, %.0f minutes, and %.2f seconds.", hours, mins, secs); 265 | } else if (mins) { 266 | l = snprintf(buf, maxlen, "%.0f minutes, and %.2f seconds.", mins, secs); 267 | } else { 268 | l = snprintf(buf, maxlen, "%.2f secs.", secs); 269 | } 270 | 271 | return l; 272 | } 273 | 274 | 275 | off_t duc_get_size(struct duc_size *size, duc_size_type st) 276 | { 277 | switch(st) { 278 | case DUC_SIZE_TYPE_APPARENT: 279 | return size->apparent; 280 | case DUC_SIZE_TYPE_ACTUAL: 281 | return size->actual; 282 | case DUC_SIZE_TYPE_COUNT: 283 | return size->count; 284 | } 285 | return 0; 286 | } 287 | 288 | 289 | void *duc_malloc(size_t s) 290 | { 291 | void *p = malloc(s); 292 | if(p == NULL) { 293 | duc_log(NULL, DUC_LOG_FTL, "out of memory"); 294 | exit(1); 295 | } 296 | return p; 297 | } 298 | 299 | 300 | void *duc_malloc0(size_t s) 301 | { 302 | void *p = calloc(s, 1); 303 | if(p == NULL) { 304 | duc_log(NULL, DUC_LOG_FTL, "out of memory"); 305 | exit(1); 306 | } 307 | return p; 308 | } 309 | 310 | 311 | void *duc_realloc(void *p, size_t s) 312 | { 313 | void *p2 = realloc(p, s); 314 | if(p2 == NULL) { 315 | duc_log(NULL, DUC_LOG_FTL, "out of memory"); 316 | exit(1); 317 | } 318 | return p2; 319 | } 320 | 321 | 322 | char *duc_strdup(const char *s) 323 | { 324 | char *s2 = strdup(s); 325 | if(s2 == NULL) { 326 | duc_log(NULL, DUC_LOG_FTL, "out of memory"); 327 | exit(1); 328 | } 329 | return s2; 330 | } 331 | 332 | 333 | void duc_free(void *p) 334 | { 335 | free(p); 336 | } 337 | 338 | 339 | static struct { 340 | char c; 341 | char *s; 342 | } duc_file_type_list[] = { 343 | [DUC_FILE_TYPE_BLK] = { '%', "block device" }, 344 | [DUC_FILE_TYPE_CHR] = { '%', "character device" }, 345 | [DUC_FILE_TYPE_DIR] = { '/', "directory" }, 346 | [DUC_FILE_TYPE_FIFO] = { '|', "fifo" }, 347 | [DUC_FILE_TYPE_LNK] = { '>', "soft link" }, 348 | [DUC_FILE_TYPE_REG] = { ' ', "regular file" }, 349 | [DUC_FILE_TYPE_SOCK] = { '@', "socket" }, 350 | [DUC_FILE_TYPE_UNKNOWN] = { '?', "unknown" }, 351 | }; 352 | 353 | 354 | char duc_file_type_char(duc_file_type t) 355 | { 356 | return (t <= DUC_FILE_TYPE_UNKNOWN) ? duc_file_type_list[t].c : ' '; 357 | } 358 | 359 | 360 | char *duc_file_type_name(duc_file_type t) 361 | { 362 | return (t <= DUC_FILE_TYPE_UNKNOWN) ? duc_file_type_list[t].s : " "; 363 | } 364 | 365 | 366 | void duc_size_accum(struct duc_size *s1, const struct duc_size *s2) 367 | { 368 | s1->actual += s2->actual; 369 | s1->apparent += s2->apparent; 370 | s1->count += s2->count; 371 | } 372 | 373 | 374 | 375 | /* 376 | * End 377 | */ 378 | 379 | -------------------------------------------------------------------------------- /src/libduc/duc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef duc_h 3 | #define duc_h 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define DUC_PATH_MAX 16384 16 | 17 | /* Don't expect to find files size 2^48 or larger, so we need a function that uses smaller buckets when we have a larger number of buckets. Still being worked on. */ 18 | #define DUC_HISTOGRAM_BUCKETS_MAX 512 19 | #define DUC_HISTOGRAM_BUCKETS_DEF 48 20 | 21 | /* Number of Largest files to track */ 22 | #define DUC_TOPN_CNT 10 23 | /* Maximum number of topN files we can track, totally arbitrary... */ 24 | #define DUC_TOPN_CNT_MAX 1000 25 | 26 | /* minimum file size to track in topN list: 10 kilobytes */ 27 | #define DUC_TOPN_MIN_FILE_SIZE 10240 28 | 29 | #ifdef WIN32 30 | typedef int64_t duc_dev_t; 31 | typedef int64_t duc_ino_t; 32 | #else 33 | typedef dev_t duc_dev_t; 34 | typedef ino_t duc_ino_t; 35 | #endif 36 | 37 | typedef struct duc duc; 38 | typedef struct duc_dir duc_dir; 39 | typedef struct duc_index_req duc_index_req; 40 | 41 | typedef enum { 42 | DUC_OPEN_RO = 1<<0, /* Open read-only (for querying)*/ 43 | DUC_OPEN_RW = 1<<1, /* Open read-write (for indexing) */ 44 | DUC_OPEN_COMPRESS = 1<<2, /* Create compressed database */ 45 | DUC_OPEN_FORCE = 1<<3, /* Force over-write of database for indexing */ 46 | DUC_FS_BIG = 1<<4, /* Tune for large filesystems to index */ 47 | DUC_FS_BIGGER = 1<<5, /* Tune for large filesystems to index */ 48 | DUC_FS_BIGGEST = 1<<6, /* Tune for large filesystems to index */ 49 | } duc_open_flags; 50 | 51 | 52 | typedef enum { 53 | DUC_INDEX_XDEV = 1<<0, /* Do not cross device boundaries while indexing */ 54 | DUC_INDEX_HIDE_FILE_NAMES = 1<<1, /* Hide file names */ 55 | DUC_INDEX_CHECK_HARD_LINKS = 1<<2, /* Count hard links only once during indexing */ 56 | DUC_INDEX_DRY_RUN = 1<<3, /* Do not touch the database */ 57 | DUC_INDEX_TOPN_FILES = 1<<4, /* Keep side DB of top N largest files */ 58 | } duc_index_flags; 59 | 60 | typedef enum { 61 | DUC_SIZE_TYPE_APPARENT, 62 | DUC_SIZE_TYPE_ACTUAL, 63 | DUC_SIZE_TYPE_COUNT, 64 | } duc_size_type; 65 | 66 | typedef enum { 67 | DUC_SORT_SIZE = 1, 68 | DUC_SORT_NAME = 2, 69 | } duc_sort; 70 | 71 | typedef enum { 72 | DUC_OK, /* No error, success */ 73 | DUC_E_DB_NOT_FOUND, /* Database not found */ 74 | DUC_E_DB_CORRUPT, /* Database corrupt and unusable */ 75 | DUC_E_DB_VERSION_MISMATCH, /* Database version mismatch */ 76 | DUC_E_DB_TYPE_MISMATCH, /* Database compiled in type mismatch */ 77 | DUC_E_PATH_NOT_FOUND, /* Requested path not found */ 78 | DUC_E_PERMISSION_DENIED, /* Permission denied */ 79 | DUC_E_OUT_OF_MEMORY, /* Out of memory */ 80 | DUC_E_DB_BACKEND, /* Unable to initialize database backend */ 81 | DUC_E_NOT_IMPLEMENTED, /* Some feature request is not supported */ 82 | DUC_E_UNKNOWN, /* Unknown error, contact the author */ 83 | } duc_errno; 84 | 85 | 86 | typedef enum { 87 | DUC_LOG_FTL, 88 | DUC_LOG_WRN, 89 | DUC_LOG_INF, 90 | DUC_LOG_DBG, 91 | DUC_LOG_DMP 92 | } duc_log_level; 93 | 94 | typedef enum { 95 | DUC_FILE_TYPE_BLK, 96 | DUC_FILE_TYPE_CHR, 97 | DUC_FILE_TYPE_DIR, 98 | DUC_FILE_TYPE_FIFO, 99 | DUC_FILE_TYPE_LNK, 100 | DUC_FILE_TYPE_REG, 101 | DUC_FILE_TYPE_SOCK, 102 | DUC_FILE_TYPE_UNKNOWN, 103 | } duc_file_type; 104 | 105 | struct duc_devino { 106 | duc_dev_t dev; 107 | duc_ino_t ino; 108 | }; 109 | 110 | struct duc_size { 111 | off_t actual; 112 | off_t apparent; 113 | off_t count; 114 | }; 115 | 116 | /* Track largest files found */ 117 | 118 | typedef struct duc_topn_file { 119 | // Should be dynamically allocated... 120 | char name[DUC_PATH_MAX]; 121 | size_t size; 122 | } duc_topn_file; 123 | 124 | struct duc_index_report { 125 | char path[DUC_PATH_MAX]; /* Indexed path */ 126 | struct duc_devino devino; /* Index top device id and inode number */ 127 | struct timeval time_start; /* Index start time */ 128 | struct timeval time_stop; /* Index finished time */ 129 | size_t file_count; /* Total number of files indexed */ 130 | size_t dir_count; /* Total number of directories indexed */ 131 | struct duc_size size; /* Total size */ 132 | size_t topn_min_size; /* minimum size in bytes to get added to topN list of files */ 133 | int topn_cnt; /* Max topN number of files to report on*/ 134 | int topn_cnt_max; /* Maximum number of topN files to track */ 135 | int histogram_buckets; /* Number of buckets in histogram */ 136 | size_t histogram[DUC_HISTOGRAM_BUCKETS_MAX]; /* histogram of file sizes, log(size)/log(2) */ 137 | duc_topn_file* topn_array[DUC_TOPN_CNT_MAX]; /* pointer to array of structs, stores each topN filename and size */ 138 | }; 139 | 140 | struct duc_dirent { 141 | char *name; /* File name */ 142 | duc_file_type type; /* File type */ 143 | struct duc_size size; /* File size */ 144 | struct duc_devino devino; /* Device id and inode number */ 145 | }; 146 | 147 | 148 | /* 149 | * Duc context, logging and error reporting 150 | */ 151 | 152 | typedef void (*duc_log_callback)(duc_log_level level, const char *fmt, va_list va); 153 | 154 | duc *duc_new(void); 155 | void duc_del(duc *duc); 156 | 157 | void duc_set_log_level(duc *duc, duc_log_level level); 158 | void duc_set_log_callback(duc *duc, duc_log_callback cb); 159 | 160 | duc_errno duc_error(duc *duc); 161 | const char *duc_strerror(duc *duc); 162 | 163 | 164 | /* 165 | * Open and close database 166 | */ 167 | 168 | int duc_open(duc *duc, const char *path_db, duc_open_flags flags); 169 | int duc_close(duc *duc); 170 | 171 | 172 | /* 173 | * Index file systems 174 | */ 175 | 176 | typedef void (*duc_index_progress_cb)(struct duc_index_report *report, void *ptr); 177 | 178 | duc_index_req *duc_index_req_new(duc *duc); 179 | int duc_index_req_set_username(duc_index_req *req, const char *username); 180 | int duc_index_req_set_uid(duc_index_req *req, int uid); 181 | int duc_index_req_add_exclude(duc_index_req *req, const char *pattern); 182 | int duc_index_req_add_fstype_include(duc_index_req *req, const char *types); 183 | int duc_index_req_add_fstype_exclude(duc_index_req *req, const char *types); 184 | int duc_index_req_set_maxdepth(duc_index_req *req, int maxdepth); 185 | int duc_index_req_set_progress_cb(duc_index_req *req, duc_index_progress_cb fn, void *ptr); 186 | int duc_index_req_set_topn(duc_index_req *req, int topn); 187 | int duc_index_req_set_buckets(duc_index_req *req, int topn); 188 | struct duc_index_report *duc_index(duc_index_req *req, const char *path, duc_index_flags flags); 189 | int duc_index_req_free(duc_index_req *req); 190 | int duc_index_report_free(struct duc_index_report *rep); 191 | 192 | 193 | /* 194 | * Querying the duc database 195 | */ 196 | 197 | struct duc_index_report *duc_get_report(duc *duc, size_t id); 198 | 199 | duc_dir *duc_dir_open(duc *duc, const char *path); 200 | duc_dir *duc_dir_openat(duc_dir *dir, const char *name); 201 | duc_dir *duc_dir_openent(duc_dir *dir, const struct duc_dirent *e); 202 | struct duc_dirent *duc_dir_read(duc_dir *dir, duc_size_type st, duc_sort sort); 203 | char *duc_dir_get_path(duc_dir *dir); 204 | void duc_dir_get_size(duc_dir *dir, struct duc_size *size); 205 | size_t duc_dir_get_count(duc_dir *dir); 206 | struct duc_dirent *duc_dir_find_child(duc_dir *dir, const char *name); 207 | int duc_dir_seek(duc_dir *dir, size_t offset); 208 | int duc_dir_rewind(duc_dir *dir); 209 | int duc_dir_close(duc_dir *dir); 210 | 211 | /* 212 | * Helper functions 213 | */ 214 | 215 | off_t duc_get_size(struct duc_size *size, duc_size_type st); 216 | int humanize(double v, int exact, double scale, char *buf, size_t maxlen); 217 | int duc_human_number(double v, int exact, char *buf, size_t maxlen); 218 | int duc_human_size(const struct duc_size *size, duc_size_type st, int exact, char *buf, size_t maxlen); 219 | int duc_human_duration(struct timeval start, struct timeval end, char *buf, size_t maxlen); 220 | void duc_log(struct duc *duc, duc_log_level lvl, const char *fmt, ...); 221 | char duc_file_type_char(duc_file_type t); 222 | char *duc_file_type_name(duc_file_type t); 223 | 224 | char *duc_db_type_check(const char *path_db); 225 | #endif /* ifndef duc_h */ 226 | -------------------------------------------------------------------------------- /src/libduc/list.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "list.h" 8 | 9 | 10 | void list_push(struct list **list, void *data) 11 | { 12 | struct list *ln = malloc(sizeof(struct list)); 13 | assert(ln); 14 | ln->data = data; 15 | ln->next = *list; 16 | *list = ln; 17 | } 18 | 19 | 20 | void *list_pop(struct list **list) 21 | { 22 | struct list *tmp = *list; 23 | if(tmp) { 24 | void *data = tmp->data; 25 | *list = tmp->next; 26 | free(tmp); 27 | return data; 28 | } 29 | return NULL; 30 | } 31 | 32 | 33 | void list_free(struct list *list, void(*fn)(void *data)) 34 | { 35 | while(list) { 36 | if(fn) fn(list->data); 37 | struct list *tmp = list; 38 | free(list); 39 | list = tmp; 40 | } 41 | } 42 | 43 | 44 | /* 45 | * End 46 | */ 47 | -------------------------------------------------------------------------------- /src/libduc/list.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef list_h 3 | #define list_h 4 | 5 | struct list { 6 | void *data; 7 | struct list *next; 8 | }; 9 | 10 | void list_push(struct list **list, void *data); 11 | void *list_pop(struct list **list); 12 | void list_free(struct list *list, void(*fn)(void *data)); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/libduc/private.h: -------------------------------------------------------------------------------- 1 | #ifndef duc_internal_h 2 | #define duc_internal_h 3 | 4 | #include "duc.h" 5 | 6 | #define DUC_DB_VERSION "17" 7 | 8 | #ifndef S_ISLNK 9 | #define S_ISLNK(v) 0 10 | #endif 11 | 12 | #ifndef S_ISSOCK 13 | #define S_ISSOCK(v) 0 14 | #endif 15 | 16 | #ifndef HAVE_LSTAT 17 | #define lstat stat 18 | #endif 19 | 20 | #ifndef timeradd 21 | # define timeradd(a, b, result) \ 22 | do { \ 23 | (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ 24 | (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ 25 | if ((result)->tv_usec >= 1000000) \ 26 | { \ 27 | ++(result)->tv_sec; \ 28 | (result)->tv_usec -= 1000000; \ 29 | } \ 30 | } while (0) 31 | #endif 32 | 33 | struct duc { 34 | struct db *db; 35 | duc_errno err; 36 | duc_log_level log_level; 37 | duc_log_callback log_callback; 38 | }; 39 | 40 | void *duc_malloc(size_t s); 41 | void *duc_malloc0(size_t s); 42 | void *duc_realloc(void *p, size_t s); 43 | char *duc_strdup(const char *s); 44 | void duc_free(void *p); 45 | 46 | 47 | void duc_size_accum(struct duc_size *s1, const struct duc_size *s2); 48 | char *duc_canonicalize_path(const char *dir); 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /src/libduc/varint.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | /* 5 | ** 2012 January 17 6 | ** 7 | ** The authors renounce all claim of copyright to this code and dedicate 8 | ** this code to the public domain. In place of legal notice, here is 9 | ** a blessing: 10 | ** 11 | ** May you do good and not evil. 12 | ** May you find forgiveness for yourself and forgive others. 13 | ** May you share freely, never taking more than you give. 14 | ** 15 | ************************************************************************* 16 | ** 17 | ** This file contains routines used to encode or decode variable-length 18 | ** integers. 19 | ** 20 | ** A variable length integer is an encoding of 64-bit unsigned integers 21 | ** into between 1 and 9 bytes. The encoding is designed so that small 22 | ** (and common) values take much less space that larger values. Additional 23 | ** properties: 24 | ** 25 | ** * The length of the varint can be determined after examining just 26 | ** the first byte of the encoding. 27 | ** 28 | ** * Varints compare in numerical order using memcmp(). 29 | ** 30 | ************************************************************************** 31 | ** 32 | ** Treat each byte of the encoding as an unsigned integer between 0 and 255. 33 | ** Let the bytes of the encoding be called A0, A1, A2, ..., A8. 34 | ** 35 | ** DECODE 36 | ** 37 | ** If A0 is between 0 and 240 inclusive, then the result is the value of A0. 38 | ** 39 | ** If A0 is between 241 and 248 inclusive, then the result is 40 | ** 240+256*(A0-241)+A1. 41 | ** 42 | ** If A0 is 249 then the result is 2288+256*A1+A2. 43 | ** 44 | ** If A0 is 250 then the result is A1..A3 as a 3-byte big-ending integer. 45 | ** 46 | ** If A0 is 251 then the result is A1..A4 as a 4-byte big-ending integer. 47 | ** 48 | ** If A0 is 252 then the result is A1..A5 as a 5-byte big-ending integer. 49 | ** 50 | ** If A0 is 253 then the result is A1..A6 as a 6-byte big-ending integer. 51 | ** 52 | ** If A0 is 254 then the result is A1..A7 as a 7-byte big-ending integer. 53 | ** 54 | ** If A0 is 255 then the result is A1..A8 as an 8-byte big-ending integer. 55 | ** 56 | ** ENCODE 57 | ** 58 | ** Let the input value be V. 59 | ** 60 | ** If V<=240 then output a single by A0 equal to V. 61 | ** 62 | ** If V<=2287 then output A0 as (V-240)/256 + 241 and A1 as (V-240)%256. 63 | ** 64 | ** If V<=67823 then output A0 as 249, A1 as (V-2288)/256, and A2 65 | ** as (V-2288)%256. 66 | ** 67 | ** If V<=16777215 then output A0 as 250 and A1 through A3 as a big-endian 68 | ** 3-byte integer. 69 | ** 70 | ** If V<=4294967295 then output A0 as 251 and A1..A4 as a big-ending 71 | ** 4-byte integer. 72 | ** 73 | ** If V<=1099511627775 then output A0 as 252 and A1..A5 as a big-ending 74 | ** 5-byte integer. 75 | ** 76 | ** If V<=281474976710655 then output A0 as 253 and A1..A6 as a big-ending 77 | ** 6-byte integer. 78 | ** 79 | ** If V<=72057594037927935 then output A0 as 254 and A1..A7 as a 80 | ** big-ending 7-byte integer. 81 | ** 82 | ** Otherwise then output A0 as 255 and A1..A8 as a big-ending 8-byte integer. 83 | ** 84 | ** SUMMARY 85 | ** 86 | ** Bytes Max Value Digits 87 | ** ------- --------- --------- 88 | ** 1 240 2.3 89 | ** 2 2287 3.3 90 | ** 3 67823 4.8 91 | ** 4 2**24-1 7.2 92 | ** 5 2**32-1 9.6 93 | ** 6 2**40-1 12.0 94 | ** 7 2**48-1 14.4 95 | ** 8 2**56-1 16.8 96 | ** 9 2**64-1 19.2 97 | ** 98 | */ 99 | 100 | /* 101 | ** Decode the varint in the first n bytes z[]. Write the integer value 102 | ** into *pResult and return the number of bytes in the varint. 103 | ** 104 | ** If the decode fails because there are not enough bytes in z[] then 105 | ** return 0; 106 | */ 107 | 108 | #include 109 | 110 | #include "varint.h" 111 | 112 | int GetVarint64( 113 | const uint8_t *z, 114 | int n, 115 | uint64_t *pResult 116 | ){ 117 | unsigned int x; 118 | if( n<1 ) return 0; 119 | if( z[0]<=240 ){ 120 | *pResult = z[0]; 121 | return 1; 122 | } 123 | if( z[0]<=248 ){ 124 | if( n<2 ) return 0; 125 | *pResult = (z[0]-241)*256 + z[1] + 240; 126 | return 2; 127 | } 128 | if( n>24); 164 | z[1] = (uint8_t)(y>>16); 165 | z[2] = (uint8_t)(y>>8); 166 | z[3] = (uint8_t)(y); 167 | } 168 | 169 | /* 170 | ** Write a varint into z[]. The buffer z[] must be at least 9 characters 171 | ** long to accommodate the largest possible varint. Return the number of 172 | ** bytes of z[] used. 173 | */ 174 | int PutVarint64(uint8_t *z, uint64_t x){ 175 | unsigned int w, y; 176 | if( x<=240 ){ 177 | z[0] = (uint8_t)x; 178 | return 1; 179 | } 180 | if( x<=2287 ){ 181 | y = (unsigned int)(x - 240); 182 | z[0] = (uint8_t)(y/256 + 241); 183 | z[1] = (uint8_t)(y%256); 184 | return 2; 185 | } 186 | if( x<=67823 ){ 187 | y = (unsigned int)(x - 2288); 188 | z[0] = 249; 189 | z[1] = (uint8_t)(y/256); 190 | z[2] = (uint8_t)(y%256); 191 | return 3; 192 | } 193 | y = (unsigned int)x; 194 | w = (unsigned int)(x>>32); 195 | if( w==0 ){ 196 | if( y<=16777215 ){ 197 | z[0] = 250; 198 | z[1] = (uint8_t)(y>>16); 199 | z[2] = (uint8_t)(y>>8); 200 | z[3] = (uint8_t)(y); 201 | return 4; 202 | } 203 | z[0] = 251; 204 | varintWrite32(z+1, y); 205 | return 5; 206 | } 207 | if( w<=255 ){ 208 | z[0] = 252; 209 | z[1] = (uint8_t)w; 210 | varintWrite32(z+2, y); 211 | return 6; 212 | } 213 | if( w<=65535 ){ 214 | z[0] = 253; 215 | z[1] = (uint8_t)(w>>8); 216 | z[2] = (uint8_t)w; 217 | varintWrite32(z+3, y); 218 | return 7; 219 | } 220 | if( w<=16777215 ){ 221 | z[0] = 254; 222 | z[1] = (uint8_t)(w>>16); 223 | z[2] = (uint8_t)(w>>8); 224 | z[3] = (uint8_t)w; 225 | varintWrite32(z+4, y); 226 | return 8; 227 | } 228 | z[0] = 255; 229 | varintWrite32(z+1, w); 230 | varintWrite32(z+5, y); 231 | return 9; 232 | } 233 | 234 | /* 235 | ** Return the number of bytes required to encode value v as a varint. 236 | */ 237 | int VarintLen(uint64_t v){ 238 | uint8_t aDummy[9]; 239 | return PutVarint64(aDummy, v); 240 | } 241 | 242 | /* 243 | ** Read a varint from buffer z and set *pResult to the value read. 244 | ** Return the number of bytes read from the buffer. 245 | */ 246 | int GetVarint32(const uint8_t *z, uint32_t *pResult){ 247 | uint64_t iRes; 248 | int ret; 249 | ret = GetVarint64(z, 9, &iRes); 250 | *pResult = (uint32_t)iRes; 251 | return ret; 252 | } 253 | 254 | /* 255 | ** Encode v as a varint and write the result to buffer p. Return the 256 | ** number of bytes written. 257 | */ 258 | int PutVarint32(uint8_t *p, uint32_t v){ 259 | return PutVarint64(p, v); 260 | } 261 | 262 | /* 263 | * End 264 | */ 265 | -------------------------------------------------------------------------------- /src/libduc/varint.h: -------------------------------------------------------------------------------- 1 | #ifndef varint_h 2 | #define varint_h 3 | 4 | int GetVarint64(const uint8_t *z, int n, uint64_t *pResult); 5 | int PutVarint64(uint8_t *z, uint64_t x); 6 | int GetVarint32(const uint8_t *z, uint32_t *pResult); 7 | int PutVarint32(uint8_t *p, uint32_t v); 8 | int VarintLen(uint64_t v); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export DUC_DATABASE=/tmp/duc-test.db 4 | DUC_TEST_DIR=/tmp/duc-test 5 | 6 | # Set to 0 to allow valgrind use 7 | DEF_USE_VALGRIND=0 8 | 9 | # Run with USE_VALGRIND=1 ./test.sh 10 | DO_VALGRIND=${USE_VALGRIND:=$DEF_USE_VALGRIND} 11 | 12 | valargs="--quiet \ 13 | --suppressions=valgrind-suppressions \ 14 | --leak-check=full \ 15 | --leak-check=full \ 16 | --show-leak-kinds=all \ 17 | --num-callers=30 \ 18 | --error-exitcode=1 \ 19 | " 20 | # Check for valgrind only if asked to use it. 21 | valgrind="" 22 | if [ "${DO_VALGRIND}" = "1" ]; then 23 | hash valgrind 2>/dev/null 24 | 25 | if [ $? -eq 1 ]; then 26 | echo "Error! Valgrind not installed, can't run with \$DO_VALGRIND set." 27 | exit 1 28 | else 29 | echo "Using valgrind with : $valargs" 30 | echo "" 31 | valgrind="valgrind $valargs" 32 | fi 33 | fi 34 | 35 | mkfile() 36 | { 37 | mkdir -p "`dirname \"$1\"`" 38 | dd if=/dev/zero of="$1" bs=1 count=$2 2> /dev/null 39 | } 40 | 41 | rm -rf $DUC_DATABASE $DUC_TEST_DIR 42 | mkdir $DUC_TEST_DIR 43 | 44 | # Regular files 45 | 46 | mkfile ${DUC_TEST_DIR}/tree/one 100 47 | mkfile ${DUC_TEST_DIR}/tree/two 100 48 | mkfile ${DUC_TEST_DIR}/tree/three 100 49 | mkfile ${DUC_TEST_DIR}/tree/four 100 50 | mkfile ${DUC_TEST_DIR}/tree/sub1/alpha 100 51 | mkfile ${DUC_TEST_DIR}/tree/sub1/bravo 1000 52 | mkfile ${DUC_TEST_DIR}/tree/sub1/charlie 1000 53 | mkfile ${DUC_TEST_DIR}/tree/sub1/delta 10000 54 | mkfile ${DUC_TEST_DIR}/tree/sub2/echo 100 55 | mkfile ${DUC_TEST_DIR}/tree/sub2/foxtrot 1000 56 | mkfile ${DUC_TEST_DIR}/tree/sub2/golf 1000 57 | mkfile ${DUC_TEST_DIR}/tree/sub2/hotel 10000 58 | mkfile ${DUC_TEST_DIR}/tree/sub3/india 5000 59 | mkfile ${DUC_TEST_DIR}/tree/sub3/juliet 4000 60 | mkfile ${DUC_TEST_DIR}/tree/sub4/kilo 1000 61 | mkfile ${DUC_TEST_DIR}/tree/sub4/lima 2000 62 | mkfile ${DUC_TEST_DIR}/tree/sub4/mike 3000 63 | mkfile ${DUC_TEST_DIR}/tree/sub4/november 5000 64 | 65 | # Hard link 66 | 67 | mkdir ${DUC_TEST_DIR}/hard-link 68 | mkfile ${DUC_TEST_DIR}/hard-link/one 10000 69 | ln ${DUC_TEST_DIR}/hard-link/one ${DUC_TEST_DIR}/hard-link/two 70 | ln ${DUC_TEST_DIR}/hard-link/one ${DUC_TEST_DIR}/hard-link/three 71 | 72 | # Sparse file 73 | 74 | mkdir ${DUC_TEST_DIR}/sparse 75 | dd if=/dev/zero of=${DUC_TEST_DIR}/sparse/first bs=1 count=1 seek=32K 2> /dev/null 76 | 77 | # Potentional problematic characters 78 | 79 | mkfile "${DUC_TEST_DIR}/strange/cgi-space-%20-dir/file" 100 80 | mkfile "${DUC_TEST_DIR}/strange/newline- -dir/file" 100 81 | mkfile "${DUC_TEST_DIR}/strange/tab- -dir/file" 100 82 | mkfile "${DUC_TEST_DIR}/strange/space- -dir/file" 100 83 | mkfile "${DUC_TEST_DIR}/strange/carriage-return- 84 | -dir/file" 100 85 | mkfile "${DUC_TEST_DIR}/strange/question-mark-?-dir/file" 100 86 | mkfile "${DUC_TEST_DIR}/strange/control-a--dir/file" 100 87 | mkfile "${DUC_TEST_DIR}/strange/escape--dir/file" 100 88 | mkfile "${DUC_TEST_DIR}/strange/less-then-<-dir/file" 100 89 | mkfile "${DUC_TEST_DIR}/strange/more-then->-dir/file" 100 90 | mkfile "${DUC_TEST_DIR}/strange/ampersand-&-dir/file" 100 91 | mkfile "${DUC_TEST_DIR}/strange/single-quote-'-dir/file" 100 92 | mkfile "${DUC_TEST_DIR}/strange/double-quote-\"-dir/file" 100 93 | mkfile "${DUC_TEST_DIR}/strange/backslash-\\-dir/file" 100 94 | 95 | # UTF-8 characters 96 | 97 | mkfile ${DUC_TEST_DIR}/utf-8/оживлённым/foo 100 98 | mkfile ${DUC_TEST_DIR}/utf-8/有朋自遠方來/foo 100 99 | mkfile ${DUC_TEST_DIR}/utf-8/♜♞♝♛♚♝♞♜/foo 100 100 | 101 | # Deep and long named directories 102 | x=1 103 | long="some_stupidly_long_directory_name_I_just_made_up" 104 | current=`pwd` 105 | cd $DUC_TEST_DIR 106 | while [ $x -le 20 ]; do 107 | #echo mkdir ${long}${x} 108 | mkdir ${long}${x} 109 | #echo cd ${long}${x} 110 | cd ${long}${x} 111 | dd if=/dev/zero of=longone bs=1 count=40 2> /dev/null 112 | dd if=/dev/zero of=longtwo bs=1 count=100 2> /dev/null 113 | x=`expr $x + 1` 114 | done 115 | cd $current 116 | 117 | 118 | # Create index 119 | 120 | $valgrind ./duc index --debug --check-hard-links --bytes --verbose ${DUC_TEST_DIR} > ${DUC_TEST_DIR}.out 2>&1 121 | 122 | if [ "$?" != "0" ]; then 123 | echo "valgrind error" 124 | cat ${DUC_TEST_DIR}.out 125 | exit 1 126 | fi 127 | 128 | 129 | #--------------------------------------------------------------------------------- 130 | # Actual tests are below. If you add test cases above, these need to be tweaked. 131 | #--------------------------------------------------------------------------------- 132 | 133 | # An exact match is expected on the apparent size; the actual size may vary. 134 | cat ${DUC_TEST_DIR}.out | grep -q "Indexed 77 files and 47 directories, (91869B apparent, [0-9]*B actual)" 135 | 136 | if [ "$?" = "0" ]; then 137 | echo "report: ok" 138 | else 139 | echo "-----------------------------" 140 | echo "report failed" 141 | echo "-----------------------------" 142 | echo "" 143 | cat ${DUC_TEST_DIR}.out 144 | echo "" 145 | exit 1 146 | fi 147 | 148 | $valgrind ./duc ls -aR ${DUC_TEST_DIR} > ${DUC_TEST_DIR}.out 2>&1 149 | 150 | if [ "$?" != "0" ]; then 151 | echo "valgrind error" 152 | cat ${DUC_TEST_DIR}.out 153 | exit 1 154 | fi 155 | 156 | # When two or more hard links point to the same file and when running duc with 157 | # the `--check-hard-links' option, only one of the hard links will be 158 | # counted. However, duc may pick up and display a different hard link depending 159 | # on the system it is being run on. Since our tests include three hard links to 160 | # the same file, we should be expecting three possible outcomes, all equally 161 | # valid, each corresponding to one of the following MD5 checksums. 162 | testsum0="78dbf880ef6917ea665fddb5ebb44428" 163 | testsum1="38ab7b7d1ec6ac57d672c5618371386d" 164 | testsum2="33e2be27a9e70e81d4006a2d7b555948" 165 | md5sum ${DUC_TEST_DIR}.out > /tmp/.duc.md5sum 166 | grep -q "$testsum0\|$testsum1\|$testsum2" /tmp/.duc.md5sum 167 | 168 | if [ "$?" = "0" ]; then 169 | echo "md5sum: ok" 170 | else 171 | echo "md5sum: failed" 172 | echo "expected one of: " 173 | echo "$testsum0 ${DUC_TEST_DIR}.out" 174 | echo "$testsum1 ${DUC_TEST_DIR}.out" 175 | echo "$testsum2 ${DUC_TEST_DIR}.out" 176 | echo "got: " 177 | cat /tmp/.duc.md5sum 178 | exit 1 179 | fi 180 | 181 | 182 | # Test backend checking. 183 | ductype=`./duc --version | tail -1 | awk '{print $NF}'` 184 | typemax=5 185 | typecount=0 186 | for type in leveldb lmdb tokyocabinet kyotocabinet sqlite; do 187 | if [ "$ductype" != "$type" ]; then 188 | #echo "./duc info -d test/dbs/${type}.db" 189 | ./duc info -d test/dbs/${type}.db > ${DUC_TEST_DIR}.out 2>&1 190 | if [ "$?" = "1" ]; then 191 | typecount=`expr $typecount + 1` 192 | fi 193 | else 194 | echo "skipping DB type $ductype, we are compiled with that type." > ${DUC_TEST_DIR}.out 2>&1 195 | fi 196 | done 197 | 198 | if [ "$typecount" = "4" ]; then 199 | echo "DB Types: ok" 200 | else 201 | echo "Type checks: failed - got $typecount failures instead of expected 4 out of $typemax." 202 | fi 203 | 204 | # end 205 | 206 | -------------------------------------------------------------------------------- /testing/dbs/kyotocabinet.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/testing/dbs/kyotocabinet.db -------------------------------------------------------------------------------- /testing/dbs/lmdb.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/testing/dbs/lmdb.db -------------------------------------------------------------------------------- /testing/dbs/sqlite3.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/testing/dbs/sqlite3.db -------------------------------------------------------------------------------- /testing/dbs/tokyocabinet.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zevv/duc/a58fa4e681c82d59f03cb4fc2d4b450aa62ec142/testing/dbs/tokyocabinet.db -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | 2 | - try to open with O_NOATIME if proper capabilities available 3 | - parallel indexing 4 | - add import feature for loading output of gnu find 5 | 6 | -------------------------------------------------------------------------------- /valgrind-suppressions: -------------------------------------------------------------------------------- 1 | { 2 | all dl_init 3 | Memcheck:Leak 4 | ... 5 | fun:_dl_init 6 | obj:* 7 | } 8 | --------------------------------------------------------------------------------