├── .codedocs ├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── SECURITY.md └── workflows │ ├── build.yml │ ├── coverity.yml │ └── release.yml ├── .gitignore ├── ChangeLog.md ├── LICENSE ├── Makefile.am ├── README.md ├── TODO.org ├── autogen.sh ├── configure.ac ├── debian ├── .gitignore ├── changelog ├── clean ├── compat ├── control ├── copyright ├── docs ├── libite-dev.dirs ├── libite-dev.install ├── libite-dev.links ├── libite5.dirs ├── libite5.install ├── libite5.symbols ├── rules └── source │ ├── format │ └── options ├── doc ├── .gitignore ├── API.md ├── Doxyfile.in ├── HACKING.md ├── Makefile.am └── TODO.md ├── m4 ├── .gitignore └── ax_prog_doxygen.m4 ├── src ├── .gitignore ├── Makefile.am ├── chomp.c ├── conio.c ├── conio.h ├── copyfile.c ├── dir.c ├── erasef.c ├── fexist.c ├── fisdir.c ├── fopenf.c ├── fparseln.c ├── fremove.c ├── fsendfile.c ├── ifconfig.c ├── lfile.c ├── libite.pc.in ├── lite.h ├── makepath.c ├── pidfile.c ├── pidfilefn.c ├── popenf.c ├── procval.c ├── progress.c ├── queue.h ├── reallocarray.c ├── rsync.c ├── runbg.c ├── strdupa.h ├── strlcat.c ├── strlcpy.c ├── strlite.h ├── strmatch.c ├── strndupa.h ├── strnlen.h ├── strtonum.c ├── strtrim.c ├── systemf.c ├── telnet.c ├── tempfile.c ├── touchf.c ├── tree.h ├── truncatef.c ├── which.c └── yorn.c └── test ├── .gitignore ├── Makefile.am ├── check.h ├── chomp.c ├── copyfile.c ├── data ├── fexist │ ├── broken-link │ ├── dir │ │ └── .placeholder │ ├── executable │ ├── link │ └── regular ├── lfile │ ├── fstab │ ├── group │ ├── passwd │ ├── protocols │ └── services └── which │ ├── executable │ ├── executable-link │ └── regular ├── dir.c ├── fexist.c ├── fisdir.c ├── fopenf.c ├── fsendfile.c ├── lfile.c ├── makepath.c ├── pidfile.c ├── printhdr.c ├── progress.c ├── rsync.c ├── runbg.c ├── str.c ├── strmatch.c ├── systemf.c ├── tempfile.c ├── touch.c ├── which.c └── yorn.c /.codedocs: -------------------------------------------------------------------------------- 1 | # CodeDocs.xyz Configuration File 2 | # 3 | # Rename this example to '.codedocs' and put it in the root directory of your 4 | # repository. This file is optional, documentation will still be generated 5 | # without it using sensible defaults. 6 | 7 | #--------------------------------------------------------------------------- 8 | # CodeDocs Configuration 9 | #--------------------------------------------------------------------------- 10 | 11 | # Include the Doxygen configuration from another file. 12 | # The file must be a relative path with respect to the root of the repository. 13 | # If any of the options in this doxyfile include a path (ie, INPUT), these 14 | # paths will be considered relative to the root of the repository, not the 15 | # location of the DOXYFILE. 16 | 17 | DOXYFILE = doc/Doxyfile 18 | 19 | # Specify external repository to link documentation with. 20 | # This is similar to Doxygen's TAGFILES option, but will automatically link to 21 | # tags of other repositories already using CodeDocs. List each repository to 22 | # link with by giving its location in the form of owner/repository. 23 | # For example: 24 | # TAGLINKS = doxygen/doxygen CodeDocs/osg 25 | # Note: these repositories must already be built on CodeDocs. 26 | 27 | TAGLINKS = 28 | 29 | #--------------------------------------------------------------------------- 30 | # Doxygen Configuration 31 | #--------------------------------------------------------------------------- 32 | 33 | # Doxygen configuration may also be placed in this file. 34 | # Currently, the following Doxygen configuration options are available. Refer 35 | # to http://doxygen.org/manual/config.html for detailed explanation of the 36 | # options. To request support for more options, contact support@codedocs.xyz. 37 | # 38 | # ABBREVIATE_BRIEF = 39 | # ALIASES = 40 | # ALLEXTERNALS = 41 | # ALLOW_UNICODE_NAMES = 42 | # ALPHABETICAL_INDEX = 43 | # ALWAYS_DETAILED_SEC = 44 | # AUTOLINK_SUPPORT = 45 | # BRIEF_MEMBER_DESC = 46 | # BUILTIN_STL_SUPPORT = 47 | # CALLER_GRAPH = 48 | # CALL_GRAPH = 49 | # CASE_SENSE_NAMES = 50 | # CITE_BIB_FILES = 51 | # CLASS_DIAGRAMS = 52 | # CLASS_GRAPH = 53 | # COLLABORATION_GRAPH = 54 | # COLS_IN_ALPHA_INDEX = 55 | # CPP_CLI_SUPPORT = 56 | # DIAFILE_DIRS = 57 | # DIRECTORY_GRAPH = 58 | # DISABLE_INDEX = 59 | # DISTRIBUTE_GROUP_DOC = 60 | # DOTFILE_DIRS = 61 | # DOT_FONTNAME = 62 | # DOT_FONTSIZE = 63 | # DOT_GRAPH_MAX_NODES = 64 | # DOT_IMAGE_FORMAT = 65 | # DOT_TRANSPARENT = 66 | # DOXYFILE_ENCODING = 67 | # ENABLED_SECTIONS = 68 | # ENABLE_PREPROCESSING = 69 | # ENUM_VALUES_PER_LINE = 70 | # EXAMPLE_PATH = 71 | # EXAMPLE_PATTERNS = 72 | # EXAMPLE_RECURSIVE = 73 | EXCLUDE = src/README.md 74 | # EXCLUDE_PATTERNS = 75 | # EXCLUDE_SYMBOLS = 76 | # EXPAND_AS_DEFINED = 77 | # EXPAND_ONLY_PREDEF = 78 | # EXTENSION_MAPPING = 79 | # EXTERNAL_GROUPS = 80 | # EXTERNAL_PAGES = 81 | # EXTRACT_ALL = 82 | # EXTRACT_ANON_NSPACES = 83 | # EXTRACT_LOCAL_CLASSES = 84 | # EXTRACT_LOCAL_METHODS = 85 | # EXTRACT_PACKAGE = 86 | # EXTRACT_PRIVATE = 87 | # EXTRACT_STATIC = 88 | # EXT_LINKS_IN_WINDOW = 89 | # FILE_PATTERNS = 90 | # FORCE_LOCAL_INCLUDES = 91 | # FORMULA_FONTSIZE = 92 | # FORMULA_TRANSPARENT = 93 | # FULL_PATH_NAMES = 94 | # GENERATE_BUGLIST = 95 | # GENERATE_DEPRECATEDLIST = 96 | # GENERATE_LEGEND = 97 | # GENERATE_TESTLIST = 98 | # GENERATE_TODOLIST = 99 | # GENERATE_TREEVIEW = 100 | # GRAPHICAL_HIERARCHY = 101 | # GROUP_GRAPHS = 102 | # GROUP_NESTED_COMPOUNDS = 103 | # HIDE_COMPOUND_REFERENCE= = 104 | # HIDE_FRIEND_COMPOUNDS = 105 | # HIDE_IN_BODY_DOCS = 106 | # HIDE_SCOPE_NAMES = 107 | # HIDE_UNDOC_CLASSES = 108 | # HIDE_UNDOC_MEMBERS = 109 | # HIDE_UNDOC_RELATIONS = 110 | # HTML_COLORSTYLE_GAMMA = 111 | # HTML_COLORSTYLE_HUE = 112 | # HTML_COLORSTYLE_SAT = 113 | # HTML_DYNAMIC_SECTIONS = 114 | # HTML_EXTRA_FILES = 115 | # HTML_EXTRA_STYLESHEET = 116 | # HTML_FOOTER = 117 | # HTML_HEADER = 118 | # HTML_INDEX_NUM_ENTRIES = 119 | # HTML_STYLESHEET = 120 | # HTML_TIMESTAMP = 121 | # IDL_PROPERTY_SUPPORT = 122 | # IGNORE_PREFIX = 123 | # IMAGE_PATH = 124 | # INCLUDED_BY_GRAPH = 125 | # INCLUDE_FILE_PATTERNS = 126 | # INCLUDE_GRAPH = 127 | # INCLUDE_PATH = 128 | # INHERIT_DOCS = 129 | # INLINE_GROUPED_CLASSES = 130 | # INLINE_INFO = 131 | # INLINE_INHERITED_MEMB = 132 | # INLINE_SIMPLE_STRUCTS = 133 | # INLINE_SOURCES = 134 | INPUT = src/ README.md doc/API.md 135 | # INPUT_ENCODING = 136 | # INTERACTIVE_SVG = 137 | # INTERNAL_DOCS = 138 | # JAVADOC_AUTOBRIEF = 139 | # LAYOUT_FILE = 140 | # MACRO_EXPANSION = 141 | # MARKDOWN_SUPPORT = 142 | # MAX_DOT_GRAPH_DEPTH = 143 | # MSCFILE_DIRS = 144 | # MULTILINE_CPP_IS_BRIEF = 145 | # OPTIMIZE_FOR_FORTRAN = 146 | # OPTIMIZE_OUTPUT_FOR_C = 147 | # OPTIMIZE_OUTPUT_JAVA = 148 | # OPTIMIZE_OUTPUT_VHDL = 149 | # OUTPUT_LANGUAGE = 150 | # PLANTUML_JAR_PATH = 151 | # PREDEFINED = 152 | # PROJECT_BRIEF = 153 | # PROJECT_LOGO = 154 | # PROJECT_NAME = 155 | # PROJECT_NUMBER = 156 | # QT_AUTOBRIEF = 157 | # RECURSIVE = 158 | # REFERENCED_BY_RELATION = 159 | # REFERENCES_LINK_SOURCE = 160 | # REFERENCES_RELATION = 161 | # REPEAT_BRIEF = 162 | # SEARCHENGINE = 163 | # SEARCH_INCLUDES = 164 | # SEPARATE_MEMBER_PAGES = 165 | # SHORT_NAMES = 166 | # SHOW_FILES = 167 | # SHOW_GROUPED_MEMB_INC = 168 | # SHOW_INCLUDE_FILES = 169 | # SHOW_NAMESPACES = 170 | # SHOW_USED_FILES = 171 | # SIP_SUPPORT = 172 | # SKIP_FUNCTION_MACROS = 173 | # SORT_BRIEF_DOCS = 174 | # SORT_BY_SCOPE_NAME = 175 | # SORT_GROUP_NAMES = 176 | # SORT_MEMBERS_CTORS_1ST = 177 | # SORT_MEMBER_DOCS = 178 | # SOURCE_BROWSER = 179 | # SOURCE_TOOLTIPS = 180 | # STRICT_PROTO_MATCHING = 181 | # STRIP_CODE_COMMENTS = 182 | # STRIP_FROM_INC_PATH = 183 | # STRIP_FROM_PATH = 184 | # SUBGROUPING = 185 | # TAB_SIZE = 186 | # TEMPLATE_RELATIONS = 187 | # TREEVIEW_WIDTH = 188 | # TYPEDEF_HIDES_STRUCT = 189 | # UML_LIMIT_NUM_FIELDS = 190 | # UML_LOOK = 191 | USE_MDFILE_AS_MAINPAGE = README.md 192 | # VERBATIM_HEADERS = 193 | # 194 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to the Project 2 | =========================== 3 | 4 | Thank you for considering contributing back to [Free Software][1]! 5 | 6 | There are a few things we would like you to consider when filing an 7 | issue or pull request with this project: 8 | 9 | 1. If you are filing a bug report or feature request 10 | 11 | Please take the time to check if an issue already has been filed 12 | matching your problem 13 | 14 | 2. What version are you running, have you tried the latest release? 15 | 16 | UNIX distributions often package and test software for their 17 | particular brand. If you are using a pre-packaged version, 18 | then please file a bug with that distribution instead. 19 | 20 | 3. Coding Style 21 | 22 | Lines are allowed to be longer than 72 characters these days, there 23 | is no enforced max. length. 24 | 25 | > **Tip:** Always submit code that follows the style of surrounding code! 26 | 27 | The coding style itself is strictly Linux [KNF][], like GIT it is 28 | becoming a de facto standard for C programming 29 | 30 | https://www.kernel.org/doc/Documentation/CodingStyle 31 | 32 | 4. Logical Change Sets 33 | 34 | Changes should be broken down into logical units that add a feature 35 | or fix a bug. Keep changes separate from each other and do not mix a 36 | bug fix with a whitespace cleanup or a new feature addition. 37 | 38 | This is important not only for readability, or for the possibility of 39 | maintainers to revert changes, but does also increase your chances of 40 | having a change accepted. 41 | 42 | 5. Commit messages 43 | 44 | Commit messages exist to track *why* a change was made. Try to be as 45 | clear and concise as possible in your commit messages, and always, be 46 | proud of your work and set up a proper GIT identity for your commits: 47 | 48 | git config --global user.name "J. Random Hacker" 49 | git config --global user.email random.j.hacker@example.com 50 | 51 | See this helpful guide for how to write simple, readable commit 52 | messages, or have at least a look at the below example. 53 | 54 | http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 55 | 56 | 57 | Example 58 | ------- 59 | 60 | Example commit message from the [Pro Git][gitbook] online book, notice 61 | how `git commit -s` is used to automatically add a `Signed-off-by`: 62 | 63 | Capitalized, short (50 chars or less) summary 64 | 65 | More detailed explanatory text, if necessary. Wrap it to about 72 66 | characters or so. In some contexts, the first line is treated as the 67 | subject of an email and the rest of the text as the body. The blank 68 | line separating the summary from the body is critical (unless you omit 69 | the body entirely); tools like rebase can get confused if you run the 70 | two together. 71 | 72 | Write your commit message in the imperative: "Fix bug" and not "Fixed bug" 73 | or "Fixes bug." This convention matches up with commit messages generated 74 | by commands like git merge and git revert. 75 | 76 | Further paragraphs come after blank lines. 77 | 78 | - Bullet points are okay, too 79 | 80 | - Typically a hyphen or asterisk is used for the bullet, followed by a 81 | single space, with blank lines in between, but conventions vary here 82 | 83 | - Use a hanging indent 84 | 85 | Signed-off-by: J. Random Hacker 86 | 87 | 88 | [1]: http://www.gnu.org/philosophy/free-sw.en.html 89 | [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form 90 | [gitbook]: https://git-scm.com/book/ch5-2.html 91 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [troglobit] 4 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | libite (-lite) is a small project, as such we have no possibility to support older versions. 6 | The only supported version is the latest released on GitHub: 7 | 8 | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | Contact the project's main author and owner to report and discuss vulnerabilities. 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Bob the Builder 2 | 3 | # Run on all branches, including all pull requests, except the 'dev' 4 | # branch since that's where we run Coverity Scan (limited tokens/day) 5 | on: 6 | push: 7 | branches: 8 | - '**' 9 | - '!dev' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | # Verify we can build on latest Ubuntu with both gcc and clang 17 | name: ${{ matrix.compiler }} 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | compiler: [gcc, clang] 22 | fail-fast: false 23 | env: 24 | MAKEFLAGS: -j3 25 | CC: ${{ matrix.compiler }} 26 | steps: 27 | - name: Install dependencies 28 | run: | 29 | sudo apt-get -y update 30 | sudo apt-get -y install tree doxygen 31 | - uses: actions/checkout@v4 32 | - name: Configure 33 | run: | 34 | ./autogen.sh 35 | ./configure --prefix= --disable-silent-rules 36 | - name: Build 37 | run: | 38 | make 39 | - name: Install to ~/tmp and Inspect 40 | run: | 41 | DESTDIR=~/tmp make install-strip 42 | tree ~/tmp 43 | - name: Run Unit Tests 44 | run: | 45 | make check || (cat test/test-suite.log; false) 46 | - name: Upload Test Results 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: libite-test-${{ matrix.compiler }} 50 | path: test/* 51 | debian: 52 | name: Verify Debian Package 53 | runs-on: ubuntu-latest 54 | container: debian:stable 55 | env: 56 | MAKEFLAGS: -j3 57 | steps: 58 | - uses: actions/checkout@v4 59 | - name: Installing dependencies 60 | run: | 61 | apt-get update 62 | apt-get install -y build-essential autoconf automake pkg-config doxygen \ 63 | dpkg-dev debhelper devscripts 64 | - name: Building Debian package 65 | run: | 66 | ./autogen.sh 67 | ./configure 68 | make check || (cat test/test-suite.log; false) 69 | make distcheck 70 | make package 71 | cat ../*.changes 72 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'dev' 7 | 8 | env: 9 | PROJECT_NAME: libite 10 | CONTACT_EMAIL: troglobit@gmail.com 11 | COVERITY_NAME: troglobit-libite 12 | COVERITY_PROJ: troglobit%2Flibite 13 | 14 | jobs: 15 | coverity: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Fetch latest Coverity Scan MD5 20 | id: var 21 | env: 22 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 23 | run: | 24 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 25 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ 26 | -O coverity-latest.tar.gz.md5 27 | echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT 28 | - uses: actions/cache@v4 29 | id: cache 30 | with: 31 | path: coverity-latest.tar.gz 32 | key: ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 33 | restore-keys: | 34 | ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 35 | ${{ runner.os }}-coverity- 36 | ${{ runner.os }}-coverity 37 | - name: Download Coverity Scan 38 | env: 39 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 40 | run: | 41 | if [ ! -f coverity-latest.tar.gz ]; then 42 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 43 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}" \ 44 | -O coverity-latest.tar.gz 45 | else 46 | echo "Latest Coverity Scan available from cache :-)" 47 | md5sum coverity-latest.tar.gz 48 | fi 49 | mkdir coverity 50 | tar xzf coverity-latest.tar.gz --strip 1 -C coverity 51 | - name: Install dependencies 52 | run: | 53 | sudo apt-get -y update 54 | sudo apt-get -y install pkg-config 55 | - name: Configure 56 | run: | 57 | ./autogen.sh 58 | ./configure --prefix= --disable-silent-rules 59 | - name: Build 60 | run: | 61 | export PATH=`pwd`/coverity/bin:$PATH 62 | cov-build --dir cov-int make 63 | - name: Submit results to Coverity Scan 64 | env: 65 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 66 | run: | 67 | tar czvf ${PROJECT_NAME}.tgz cov-int 68 | curl \ 69 | --form project=${COVERITY_NAME} \ 70 | --form token=$TOKEN \ 71 | --form email=${CONTACT_EMAIL} \ 72 | --form file=@${PROJECT_NAME}.tgz \ 73 | --form version=trunk \ 74 | --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ 75 | https://scan.coverity.com/builds?project=${COVERITY_PROJ} 76 | - name: Upload build.log 77 | uses: actions/upload-artifact@v4 78 | with: 79 | name: coverity-build.log 80 | path: cov-int/build-log.txt 81 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release General 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | name: Build and upload release tarball 11 | if: startsWith(github.ref, 'refs/tags/') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Installing dependencies ... 16 | run: | 17 | sudo apt-get -y update 18 | sudo apt-get -y install tree doxygen 19 | - name: Creating Makefiles ... 20 | run: | 21 | ./autogen.sh 22 | ./configure --prefix= 23 | - name: Build release ... 24 | run: | 25 | make release 26 | mkdir -p artifacts/ 27 | mv ../*.tar.* artifacts/ 28 | - name: Extract ChangeLog entry ... 29 | run: | 30 | awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ 31 | |head -n -1 > release.md 32 | cat release.md 33 | - uses: ncipollo/release-action@v1 34 | with: 35 | name: Libite (-lite) ${{ github.ref_name }} 36 | bodyFile: "release.md" 37 | artifacts: "artifacts/*" 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | ID 3 | GPATH 4 | GRTAGS 5 | GTAGS 6 | compile 7 | config.* 8 | configure 9 | depcomp 10 | install-sh 11 | libtool 12 | ltmain.sh 13 | missing 14 | stamp-h1 15 | Makefile 16 | Makefile.in 17 | aclocal.m4 18 | ar-lib 19 | autom4te.cache 20 | test-driver 21 | libite.pc 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2010 Claudio Matsuoka 2 | Copyright (c) 2008-2023 Joachim Wiberg 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | SUBDIRS = src test 3 | doc_DATA = README.md LICENSE ChangeLog.md 4 | EXTRA_DIST = README.md LICENSE ChangeLog.md 5 | 6 | if ENABLE_DOXYGEN 7 | SUBDIRS += doc 8 | .PHONY: doc 9 | doc: 10 | $(MAKE) -C @top_builddir@/doc $@ 11 | 12 | ## The distribution should include man pages, which are generated 13 | dist-hook: doc 14 | else 15 | doc: 16 | @echo "Doxygen documentation (html + man) disabled, skipping ..." 17 | endif 18 | 19 | ## Check if tagged in git 20 | release-hook: 21 | @if [ ! `git tag -l v$(PACKAGE_VERSION) | grep $(PACKAGE_VERSION)` ]; then \ 22 | echo; \ 23 | printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ 24 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 25 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 26 | printf "OK, aborting release.\n"; \ 27 | exit 1; \ 28 | fi; \ 29 | echo; \ 30 | else \ 31 | echo; \ 32 | printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ 33 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 34 | echo; \ 35 | fi 36 | 37 | ## Generate .deb package 38 | package: 39 | @debuild -uc -us -B --lintian-opts --profile debian -i -I --show-overrides 40 | 41 | ## Target to run when building a release 42 | release: release-hook distcheck 43 | @for file in $(DIST_ARCHIVES); do \ 44 | md5sum $$file > ../$$file.md5; \ 45 | sha256sum $$file > ../$$file.sha256; \ 46 | done 47 | @mv $(DIST_ARCHIVES) ../ 48 | @echo 49 | @echo "Resulting release files:" 50 | @echo "=================================================================" 51 | @for file in $(DIST_ARCHIVES); do \ 52 | printf "%-32s Distribution tarball\n" $$file; \ 53 | printf "%-32s " $$file.md5; cat ../$$file.md5 | cut -f1 -d' '; \ 54 | printf "%-32s " $$file.sha256; cat ../$$file.sha256 | cut -f1 -d' '; \ 55 | done 56 | @for file in `cd ..; ls $(PACKAGE)*_$(VERSION)*`; do \ 57 | printf "%-32s Debian/Ubuntu package\n" $$file; \ 58 | done 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | -lite | Frog DNA, basically 2 | =========================== 3 | [![GitHub Status][]][GitHub] [![codedocs status][]][codedocs] [![Coverity Status][]][Coverity Scan] 4 | 5 | Table of Contents 6 | ----------------- 7 | 8 | * [Introduction](#introduction) 9 | * [Using -lite](#using--lite) 10 | * [API](doc/API.md#overview) 11 | * [Important Node](doc/API.md#important-note) 12 | * [Helper Macros](doc/API.md#helper-macros) 13 | * [Generic Functions](doc/API.md#generic-functions) 14 | * [GNU Functions](doc/API.md#gnu-functions) 15 | * [OpenBSD/NetBSD/FreeBSD Functions](doc/API.md#openbsd-netbsd-freebsd-functions) 16 | * [Build & Install](#build--install) 17 | * [Origin & References](#origin--references) 18 | 19 | 20 | > **NOTE:** Incompatible changes in v2.0 compared to v1.x! 21 | 22 | Introduction 23 | ------------ 24 | 25 | Libite is a lightweight library of *frog DNA* that can be used to fill 26 | the gaps in any dinosaur project. It holds useful functions and macros 27 | developed by both [Finit][1] and the [OpenBSD][2] project. Most notably 28 | the string functions: [strlcpy(3)][3], [strlcat(3)][3] and the highly 29 | useful *BSD [sys/queue.h][4] and [sys/tree.h][7] API's. 30 | 31 | Libite holds many of the missing pieces in GNU libc, although -lite does 32 | not aim to become another [GLIB][5]. One noticeable gap in GLIBC is the 33 | `_SAFE` macros available in the BSD `sys/queue.h` API — highly 34 | recommended when traversing lists to delete/free nodes. 35 | 36 | The code is open sourced under a mix of permissive Open Source licenses: 37 | [MIT/X11 license][MIT], [ISC license][ISC], and [BSD licenses][BSD]. 38 | 39 | > **Tip:** have a look at my blog post about other useful UNIX API's! 40 | > https://troglobit.com/post/2020-02-22-useful-unix-apis/ 41 | 42 | 43 | Using -lite 44 | ----------- 45 | 46 | Libite is by default installed as a library and a set of include files. 47 | To prevent clashing with include files of the same name `-lite` employs 48 | an include file namespace `libite/`, which is strongly recommended to 49 | use in your applications: 50 | 51 | ```C 52 | #include 53 | #include 54 | #include 55 | #include 56 | ``` 57 | 58 | > **Note:** prior to v2.5.0, the `lite/` namespace was used for headers, 59 | > which is still available in the default install. This clashed with 60 | > the headers of the LiTE library from the DirectFB project. 61 | 62 | The output from the `pkg-config` tool holds no surprises: 63 | 64 | ```bash 65 | $ pkg-config --libs --static --cflags libite 66 | -I/usr/local/include -D_LIBITE_LITE -L/usr/local/lib -lite 67 | ``` 68 | 69 | > **Note:** `_LIBITE_LITE` is defined since v2.5.0, useful for software 70 | > that want to be able to build against headers from an older libite: 71 | > 72 | > ```C 73 | > #ifdef _LIBITE_LITE 74 | > # include 75 | > #else 76 | > # include 77 | > #endif 78 | > ``` 79 | 80 | The prefix path `/usr/local/` shown here is only the default. Use the 81 | `configure` script to select a different prefix when installing libite. 82 | 83 | For GNU autotools based projects, use the following in `configure.ac`: 84 | 85 | ```m4 86 | # Check for required libraries 87 | PKG_CHECK_MODULES([lite], [libite >= 1.5.0]) 88 | ``` 89 | 90 | and in your `Makefile.am`: 91 | 92 | ```Makefile 93 | proggy_CFLAGS = $(lite_CFLAGS) 94 | proggy_LDADD = $(lite_LIBS) 95 | ``` 96 | 97 | > API Documentaion: https://codedocs.xyz/troglobit/libite/ 98 | 99 | 100 | Build & Install 101 | --------------- 102 | 103 | This library was initially built for and developed on GNU/Linux systems 104 | as a light weight utility library, these days NetBSD should also work. 105 | 106 | ```bash 107 | $ ./configure 108 | $ make -j5 109 | $ sudo make install-strip 110 | $ sudo ldconfig 111 | ``` 112 | 113 | Use ./configure --without-symlink to prevent the install step 114 | from creating the `lite -> libite/` compatibility symlink for the header 115 | files, required for systems with DirectFB LiTE. The default, however, 116 | is to install the symlink to ensure compatibility with existing software 117 | that depends on the `-lite` library headers in their previous namespace. 118 | 119 | **Note:** When checking out code from GIT, use ./autogen.sh 120 | to generate a `configure` script. It is a generated file and otherwise 121 | only included in released tarballs. 122 | 123 | 124 | Origin & References 125 | ------------------- 126 | 127 | Much of the code in libite (-lite) is written by [Claudio Matsuoka][] 128 | for the original [Finit][original finit] and released under the MIT/X11 129 | license. Joachim Wiberg later improved on the [Finit][1] code base and 130 | included pieces of software released under the ISC and BSD licenses. 131 | See each respective file for license details. 132 | 133 | [1]: https://github.com/troglobit/finit 134 | [2]: http://www.openbsd.org/ 135 | [3]: http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy 136 | [4]: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/LIST_EMPTY.3 137 | [5]: https://developer.gnome.org/glib/ 138 | [7]: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/SPLAY_FOREACH.3 139 | [MIT]: https://en.wikipedia.org/wiki/MIT_License 140 | [ISC]: https://en.wikipedia.org/wiki/ISC_license 141 | [BSD]: https://en.wikipedia.org/wiki/BSD_licenses 142 | [GitHub]: https://github.com/troglobit/libite/actions/workflows/build.yml/ 143 | [GitHub Status]: https://github.com/troglobit/libite/actions/workflows/build.yml/badge.svg 144 | [Coverity Scan]: https://scan.coverity.com/projects/20602 145 | [Coverity Status]: https://img.shields.io/coverity/scan/20602.svg 146 | [codedocs]: https://codedocs.xyz/troglobit/libite.log 147 | [codedocs status]: https://codedocs.xyz/troglobit/libite.svg 148 | [Claudio Matsuoka]: https://github.com/cmatsuoka 149 | [original finit]: http://helllabs.org/finit/ 150 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | * TODO Write tests for new APIs 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(libite, 2.6.1, https://github.com/troglobit/libite/issues) 2 | AC_CONFIG_AUX_DIR(aux) 3 | AM_INIT_AUTOMAKE([1.11 foreign dist-xz]) 4 | AM_SILENT_RULES([yes]) 5 | 6 | AC_CONFIG_SRCDIR(src/lite.h) 7 | AC_CONFIG_HEADERS(config.h) 8 | AC_CONFIG_FILES([Makefile doc/Makefile doc/Doxyfile src/Makefile src/libite.pc test/Makefile]) 9 | AC_CONFIG_MACRO_DIR([m4]) 10 | 11 | AC_PROG_CC 12 | AC_PROG_INSTALL 13 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 14 | LT_INIT 15 | 16 | # Check for Doxygen and enable its features. 17 | # For details, see m4/ax_prog_doxygen.m4 and 18 | # http://www.bioinf.uni-freiburg.de/~mmann/HowTo/automake.html#doxygenSupport 19 | DX_DOXYGEN_FEATURE(ON) 20 | DX_DOT_FEATURE(OFF) 21 | DX_CHI_FEATURE(OFF) 22 | DX_RTF_FEATURE(OFF) 23 | DX_XML_FEATURE(OFF) 24 | DX_PDF_FEATURE(OFF) 25 | DX_PS_FEATURE(OFF) 26 | DX_CHM_FEATURE(OFF) 27 | DX_HTML_FEATURE(ON) 28 | DX_MAN_FEATURE(OFF) 29 | DX_INIT_DOXYGEN(${PACKAGE_NAME}, [${top_builddir}/doc/Doxyfile], [${top_builddir}/doc]) 30 | 31 | AC_ARG_WITH([symlink], 32 | AS_HELP_STRING([--without-symlink], [Disable compat /usr/include/lite -> libite symlink]),, 33 | [with_symlink=yes]) 34 | 35 | AM_CONDITIONAL([ENABLE_DOXYGEN], [test "x${DX_FLAG_doc}" = x1]) 36 | AM_CONDITIONAL([ENABLE_HTML], [test "x${DX_FLAG_html}" = x1]) 37 | AM_CONDITIONAL([COMPAT_SYMLINK], [test "x$with_symlink" != "xno"]) 38 | 39 | AC_OUTPUT 40 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.substvars 3 | .debhelper/* 4 | tmp/* 5 | libite/* 6 | libite5/* 7 | libite-dev/* 8 | autoreconf* 9 | debhelper* 10 | files 11 | !*.dirs 12 | !*.install 13 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libite (2.6.0) stable; urgency=medium 2 | 3 | * New APIs from Infix Project: 4 | - fexistf() 5 | - vfopenf() 6 | - popenf() 7 | - vreadsnf(), readsnf(), writesf() 8 | - vreadllf(), readllf(), readdf() 9 | - writellf(), writedf() 10 | * Spellchecking of API docs 11 | 12 | -- Joachim Wiberg Sun, 17 Sep 2023 12:16:47 +0200 13 | 14 | libite (2.5.3) stable; urgency=medium 15 | 16 | * rsync() does not copy single files correctly 17 | * copyfile() does not handle empty source files correctly (error) 18 | * copyfile() does not properly create destination dir 19 | * Add printf format attribute hint to all vararg functions 20 | * Fix fisdir() segfault on GNU/Hurd 21 | * Fix touch() on Debian/kFreeBSD, use creat() instad of mknod() 22 | 23 | -- Joachim Wiberg Sat, 08 Apr 2023 07:04:00 +0200 24 | 25 | libite (2.5.1) stable; urgency=medium 26 | 27 | * Use dh_link(1) to install `/usr/include/lite -> libite/`. 28 | 29 | -- Joachim Wiberg Thu, 02 Dec 2021 00:08:41 +0100 30 | 31 | libite (2.5.0) stable; urgency=medium 32 | 33 | * Header files have moved from `lite/*.h` to `libite/*.h` to prevent 34 | clashing with DirectFB LiTE library. For a transitional period, 35 | however, a compatibility symlink is installed; `lite -> libite/`. 36 | 37 | -- Joachim Wiberg Tue, 30 Nov 2021 06:57:17 +0100 38 | 39 | libite (2.4.1) stable; urgency=medium 40 | 41 | * Add doxygen documentation, https://codedocs.xyz/troglobit/libite/ 42 | * Fix issue with tempfile() returning EOPNOTSUP on certain file systems, 43 | added fallback that uses mkstemp() with O_CLOEXEC flag set 44 | 45 | -- Joachim Wiberg Sat, 09 Oct 2021 08:56:43 +0200 46 | 47 | libite (2.4.0) stable; urgency=medium 48 | 49 | * New functions: 50 | - touchf(), formatted string support for touch() 51 | - erasef(), formatted string support for erase() 52 | - strtrim(), trims leading and trailing white-space from a string 53 | * Fix systemf() improper handling of system() return value. Now checks 54 | if exited OK, then returning the exit status of the command, or if not 55 | exited OK, then checks if the command was signaled, and returns -1 with 56 | errno set. This also allows for returning 127, like system(), if the 57 | shell, or the program itself, did not exist. 58 | 59 | -- Joachim Wiberg Sat, 27 Mar 2021 09:12:32 +0100 60 | 61 | libite (2.3.1) stable; urgency=medium 62 | 63 | * Minor packaging fixes. 64 | * Copyright updates, including LICENSE file, year + author last name 65 | 66 | -- Joachim Wiberg Fri, 12 Feb 2021 15:10:42 +0100 67 | 68 | libite (2.3.0) stable; urgency=medium 69 | 70 | * New functions: 71 | - Add support for fremove(), formatted remove() replacement 72 | - Add support for truncatef(), formatted truncate() replacement 73 | * Fixes 74 | - Handle ETIMEDOUT error for connect() call in telnet_open() 75 | 76 | -- Joachim Wiberg Fri, 12 Feb 2021 08:03:47 +0100 77 | 78 | libite (2.2.0) stable; urgency=medium 79 | 80 | * New functions: 81 | - systemf(), formatted string system() replacement 82 | - fmkpath(), formatted string mkpath() replacement 83 | - fopenf(), formatted string fopen() replacement 84 | * Fixes 85 | - possible memory leak in rsync() 86 | - Check return value of fstat() to prevent bogus destination file 87 | times when using copyfile() 88 | 89 | -- Joachim Nilsson Mon, 16 Mar 2020 15:42:02 +0100 90 | 91 | libite (2.1.2) stable; urgency=high 92 | 93 | * Fix upgrade of base package, new library name to match SONAME 94 | breaks upgrade from libite_2.0.x.deb to libite5_2.1.x.deb 95 | 96 | -- Joachim Nilsson Sat, 22 Feb 2020 15:04:27 +0100 97 | 98 | libite (2.1.1) stable; urgency=medium 99 | 100 | * Fix to chomp(), ensure stopping before beginning of string 101 | * Added example of printhdr() 102 | * Update copyright years 103 | * Add missing libite5.symbols file, found by lintian 104 | * Fix package short description, found by linitian 105 | 106 | -- Joachim Nilsson Sat, 22 Feb 2020 13:52:24 +0100 107 | 108 | libite (2.1.0) unstable; urgency=medium 109 | 110 | * New functions; strmatch(), strnmatch(), yorn(), telnet expect APIs 111 | * Fixes to pidfile_signal(), strtonum() and C++ linking 112 | 113 | -- Joachim Nilsson Mon, 26 Aug 2019 20:21:46 +0200 114 | 115 | libite (2.0.2) unstable; urgency=medium 116 | 117 | * Fix issues with using `strlite.h` standalone 118 | * Fix build issue with `strtonum.c` older GLIBC 119 | 120 | -- Joachim Nilsson Thu, 10 May 2018 19:12:57 +0200 121 | 122 | libite (2.0.1-1) unstable; urgency=medium 123 | 124 | * New upstream release, v2.0.1, fixes to: 125 | - touch(), cannot handle relative paths when updating mtime 126 | - unit test for which() 127 | - add missing initscr() declaration in conio.h 128 | - ifdef guards around min()/max() macros 129 | 130 | -- Joachim Nilsson Fri, 22 Dec 2017 12:11:41 +0100 131 | 132 | libite (2.0.0-1) unstable; urgency=low 133 | 134 | * New upstream release, v2.0.0, fixes to: 135 | - makepath(), mkpath() 136 | * Changes to 137 | - rsync(), API changes 138 | - copyfile(), API changes 139 | - tree(), removed from library 140 | - fmode(), removed from library 141 | 142 | -- Joachim Nilsson Sun, 19 Nov 2017 22:42:51 +0100 143 | 144 | libite (1.9.3-1) unstable; urgency=low 145 | 146 | * New upstream release, v1.9.3, fixes to conio.h, initscr() and tree(). 147 | 148 | -- Joachim Nilsson Tue, 24 Oct 2017 08:49:54 +0200 149 | 150 | libite (1.9.2-1) unstable; urgency=low 151 | 152 | * New upstream reelase, v1.9.2, see upstream ChangeLog for details. 153 | 154 | -- Joachim Nilsson Tue, 15 Aug 2017 08:15:14 +0200 155 | 156 | libite (1.9.1-1) unstable; urgency=low 157 | 158 | * New upstream reelase, v1.9.1. 159 | 160 | -- Joachim Nilsson Sun, 02 Jul 2017 19:23:00 +0100 161 | 162 | libite (1.9.0-1) unstable; urgency=low 163 | 164 | * New upstream reelase. 165 | 166 | -- Joachim Nilsson Sun, 02 Jul 2017 19:23:00 +0100 167 | 168 | libite (1.8.3-1) unstable; urgency=low 169 | 170 | * New upstream reelase. 171 | 172 | -- Joachim Nilsson Sun, 08 Jan 2017 15:22:26 +0100 173 | 174 | libite (1.8.2-1) unstable; urgency=low 175 | 176 | * Initial release. 177 | 178 | -- Joachim Nilsson Tue, 29 Nov 2016 08:23:35 +0100 179 | -------------------------------------------------------------------------------- /debian/clean: -------------------------------------------------------------------------------- 1 | config.log 2 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libite 2 | Section: libs 3 | Priority: optional 4 | Maintainer: Joachim Wiberg 5 | Build-Depends: debhelper (>= 10) 6 | Build-Depends-Indep: doxygen 7 | Standards-Version: 4.3.0 8 | Homepage: https://github.com/troglobit/libite/ 9 | Vcs-Browser: https://github.com/troglobit/libite/ 10 | Vcs-Git: https://github.com/troglobit/libite.git 11 | 12 | Package: libite-dev 13 | Priority: optional 14 | Section: libdevel 15 | Architecture: any 16 | Depends: ${misc:Depends}, libite5 (= ${binary:Version}) 17 | Description: static library, header files, and docs for libite 18 | Static library, header files, and documentation for libite. 19 | . 20 | Libite is a lightweight library of frog DNA. It can be used to fill 21 | the gaps in any dinosaur project. It holds useful functions and macros 22 | developed by both Finit and the OpenBSD project. Most notably the 23 | string functions: strlcpy(3), strlcat(3) and the highly useful *BSD 24 | sys/queue.h and sys/tree.h API's. 25 | . 26 | Libite is the frog DNA missing in GNU libc. However, -lite does not 27 | aim to become another GLIB! One noticeable gap in GLIBC is the missing 28 | _SAFE macros in the BSD sys/queue.h API — highly recommended when 29 | traversing lists to delete/free nodes. 30 | . 31 | The code is open sourced under a mix of the MIT/X11 license, the ISC 32 | license used by OpenBSD, and BSD licenses, all of them are extremely 33 | liberal and can be used freely in proprietary software if needed. 34 | 35 | Package: libite5 36 | Replaces: libite 37 | Conflicts: libite 38 | Provides: libite 39 | Architecture: any 40 | Depends: ${misc:Depends}, ${shlibs:Depends} 41 | Description: That missing frog DNA you've been looking for 42 | Libite, or -lite, is a lightweight library of frog DNA. It can be used 43 | to fill the gaps in any dinosaur project. It holds useful functions 44 | and macros developed by both Finit and the OpenBSD project. Most 45 | notably the string functions: strlcpy(3), strlcat(3) and the highly 46 | useful *BSD sys/queue.h and sys/tree.h API's. 47 | . 48 | Libite is the frog DNA missing in GNU libc. However, -lite does not 49 | aim to become another GLIB! One noticeable gap in GLIBC is the missing 50 | _SAFE macros in the BSD sys/queue.h API — highly recommended when 51 | traversing lists to delete/free nodes. 52 | . 53 | The code is open source software under a mix of the MIT/X11 license, 54 | the ISC license used by OpenBSD, and BSD licenses, all of them are 55 | extremely liberal and can be used freely in proprietary software if 56 | needed. 57 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: https.//github.com/troglobit/libite/ 3 | 4 | Files: * 5 | Copyright: 6 | 2008-2010 Claudio Matsuoka 7 | 2008-2021 Joachim Wiberg 8 | License: MIT 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | Files: chomp.c 28 | Copyright: 2014-2021 Joachim Wiberg 29 | License: ISC 30 | 31 | Files: conio.? 32 | Copyright: 2009-2021 Joachim Wiberg 33 | License: ISC 34 | 35 | Files: dir.c 36 | Copyright: 2008-2021 Joachim Wiberg 37 | License: ISC 38 | 39 | Files: fopenf.c fremove.c systemf.c truncatef.c 40 | Copyright: 2021 Joachim Wiberg 41 | License: ISC 42 | 43 | Files: fparseln.c 44 | Copyright: 1997 Christos Zoulas 45 | License: BSD-2-clause 46 | 47 | Files: fsendfile.c 48 | Copyright: 2013 Tobias Waldekranz 49 | License: MIT 50 | 51 | Files: lfile.c 52 | Copyright: 2015-2021 Joachim Wiberg 53 | License: ISC 54 | 55 | Files: makepath.c 56 | Copyright: 2016-2021 Joachim Wiberg 57 | License: ISC 58 | 59 | Files: pidfile.c 60 | Copyright: 1999 The NetBSD Foundation, Inc. 61 | License: BSD-2-clause 62 | 63 | Files: pidfilefn.c 64 | Copyright: 2009-2021 Joachim Wiberg 65 | License: ISC 66 | 67 | Files: progress.c 68 | Copyright: 2012-2021 Joachim Wiberg 69 | License: ISC 70 | 71 | Files: queue.h 72 | Copyright: 1991, 1993 The Regents of the University of California. 73 | License: BSD-3-clause 74 | 75 | Files: reallocarray.c 76 | Copyright: 2008 Otto Moerbeek 77 | License: ISC 78 | 79 | Files: rsync.c 80 | Copyright: 2011-2021 Joachim Wiberg 81 | License: ISC 82 | 83 | Files: strdupa.h strndupa.h 84 | Copyright: 2009 William Ahern 85 | License: MIT 86 | 87 | Files: strlcat.c strlcpy.c 88 | Copyright: 1998, 2015 Todd C. Miller 89 | License: MIT 90 | 91 | Files: strlite.h 92 | Copyright: 2008-2021 Joachim Wiberg 93 | License: MIT 94 | 95 | Files: strmatch.c 96 | Copyright: 2009-2021 Joachim Wiberg 97 | License: ISC 98 | 99 | Files: strnlen.c 100 | Copyright: 2016-2021 Joachim Wiberg 101 | License: ISC 102 | 103 | Files: strtonum.c 104 | Copyright: 2004 Ted Unangst and Todd Miller 105 | License: ISC 106 | 107 | Files: telnet.c 108 | Copyright: 2010-2021 Joachim Wiberg 109 | License: ISC 110 | 111 | Files: tempfile.c 112 | Copyright: 2015-2021 Joachim Wiberg 113 | License: ISC 114 | 115 | Files: tree.h 116 | Copyright: 2002 Niels Provos 117 | License: BSD-2-clause 118 | 119 | Files: which.c 120 | Copyright: 2017-2021 Joachim Wiberg 121 | License: ISC 122 | 123 | Files: yorn.c 124 | Copyright: 2009-2021 Joachim Wiberg 125 | License: ISC 126 | 127 | Files: test/*.c 128 | License: public-domain 129 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/libite-dev.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | usr/include 3 | -------------------------------------------------------------------------------- /debian/libite-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/libite/*.h 2 | usr/lib/*/lib*.a 3 | usr/lib/*/lib*.so 4 | usr/lib/*/pkgconfig/lib*.pc 5 | -------------------------------------------------------------------------------- /debian/libite-dev.links: -------------------------------------------------------------------------------- 1 | /usr/include/libite /usr/include/lite 2 | -------------------------------------------------------------------------------- /debian/libite5.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | -------------------------------------------------------------------------------- /debian/libite5.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/lib*.so.* 2 | -------------------------------------------------------------------------------- /debian/libite5.symbols: -------------------------------------------------------------------------------- 1 | libite.so.5 libite5 #MINVER# 2 | * Build-Depends-Package: libite-dev 3 | __pidfile_name@Base 2.0.0 4 | __pidfile_path@Base 2.0.0 5 | chomp@Base 2.0.0 6 | copyfile@Base 2.0.0 7 | dir@Base 2.0.0 8 | erasef@Base 2.4.0 9 | fcopyfile@Base 2.0.0 10 | fexist@Base 2.0.0 11 | fgetint@Base 2.0.0 12 | fisdir@Base 2.0.0 13 | fmkpath@Base 2.2.0 14 | fopenf@Base 2.2.0 15 | fparseln@Base 2.0.0 16 | fremove@Base 2.3.0 17 | fsendfile@Base 2.0.0 18 | ifconfig@Base 2.0.0 19 | initscr@Base 2.0.0 20 | lfclose@Base 2.0.0 21 | lfgetint@Base 2.0.0 22 | lfgetkey@Base 2.0.0 23 | lfopen@Base 2.0.0 24 | lftok@Base 2.0.0 25 | makepath@Base 2.0.0 26 | mkpath@Base 2.0.0 27 | movefile@Base 2.0.0 28 | pidfile@Base 2.0.0 29 | pidfile_poll@Base 2.0.0 30 | pidfile_read@Base 2.0.0 31 | pidfile_signal@Base 2.1.0 32 | progress@Base 2.0.0 33 | progress_simple@Base 2.0.0 34 | reallocarray@Base 2.0.0 35 | rsync@Base 2.0.0 36 | strlcat@Base 2.0.0 37 | strlcpy@Base 2.0.0 38 | strmatch@Base 2.1.0 39 | strnmatch@Base 2.1.0 40 | strtonum@Base 2.1.0 41 | strtrim@Base 2.4.0 42 | systemf@Base 2.2.0 43 | telnet_close@Base 2.1.0 44 | telnet_expect@Base 2.1.0 45 | telnet_open@Base 2.1.0 46 | telnet_session@Base 2.1.0 47 | tempfile@Base 2.0.0 48 | touchf@Base 2.4.0 49 | truncatef@Base 2.3.0 50 | which@Base 2.0.0 51 | whichp@Base 2.0.0 52 | yorn@Base 2.1.0 53 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Uncomment this to turn on verbose mode. 3 | #export DH_VERBOSE=1 4 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 5 | 6 | %: 7 | dh $@ --with autoreconf 8 | 9 | override_dh_auto_configure: 10 | dh_auto_configure -- --without-symlink 11 | 12 | override_dh_installchangelogs: 13 | dh_installchangelogs ChangeLog.md 14 | 15 | override_dh_auto_install: 16 | dh_auto_install 17 | find debian -name LICENSE -delete 18 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | compression=xz 2 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | html/* 2 | libite.tag 3 | Doxyfile 4 | -------------------------------------------------------------------------------- /doc/HACKING.md: -------------------------------------------------------------------------------- 1 | Maintenance and Release Checklist 2 | ================================= 3 | 4 | Maintenance 5 | ----------- 6 | 7 | * Encourage contributors to write tests, in particular for new features 8 | * Run tests regularly, use Travis-CI to do this automatically 9 | * Leverage GitHub issues for milestone planning 10 | * Reference issues from GitHub pull requests to alert issue subscribers 11 | * Bump library ABI version just before release! 12 | 13 | 14 | Release Checklist 15 | ----------------- 16 | 17 | * Update ChangeLog, follow http://keepachangelog.com/ loosely 18 | - Inform users in a plain language of changes and bug fixes 19 | - Do *not* copy-paste GIT commit logs! 20 | - Order entrys according to importance, most relevant first 21 | * Run unit tests: `make check` 22 | * Do we need to bump the ABI version? (Probably, see below) 23 | * Tag 24 | * Push last commit(s) *and* tags to GitHub 25 | * Make release 26 | 27 | make distclean 28 | ./autogen.sh 29 | ./configure 30 | make release 31 | 32 | * Create new release in GitHub releases page 33 | * Copy and paste ChangeLog entry, check any stale links! 34 | * Upload release tarball and MD5 files 35 | 36 | 37 | Library Versioning 38 | ------------------ 39 | 40 | Libite (-lite) relies on GNU Libtool for building the library. For a 41 | user of the library it is important to maintain a clear ABI versioning 42 | scheme. This is not the same as the library version, but rather the 43 | library "compatibility level". 44 | 45 | The ABI version is specified in `src/Makefile.am` and looks like this: 46 | 47 | libite_la_LDFLAGS = -version-info 0:0:0 48 | \ \ `-- age 49 | \ `--- revision 50 | `---- current 51 | 52 | It must be updated according to the [GNU Libtool recommendations][1]: 53 | 54 | 1. Start with version information of `0:0:0` for each libtool library. 55 | 2. Update the version information only immediately before a public 56 | release of your software. More frequent updates are unnecessary, and 57 | only guarantee that the current interface number gets larger faster. 58 | 3. If the library *source code has changed at all* since the last update, 59 | then increment revision (`c:r:a` becomes `c:r+1:a`). 60 | 4. If any *interfaces have been added, removed, or changed* since the 61 | last update, increment current, and set revision to 0. 62 | 5. If any *interfaces have been added* since the last public release, 63 | then increment age. 64 | 6. If any *interfaces have been removed or changed* since the last 65 | public release, then set age to 0. 66 | 67 | The libtool ABI versioning logic is very confusing but works if you just 68 | disable your brain and follow the rules, one by one. 69 | 70 | **Example #1:** a new function has been added, none of the existing ones 71 | have changed. The initial version is 1:0:0, we follow the rules above to 72 | the letter: increase revision, increase current and set revision to zero, 73 | and finally increase age. This, rather confusingly, gives us 2:0:1 which 74 | libtool then translates to `libite.so.1.1.0`. 75 | 76 | **Example #2:** some existing functions are changed, they now return an 77 | `int` instead of `void`. The initial version is 0:0:0, and we follow the 78 | rules again: increment revision, increment current and set revision to 79 | zero, set age to zero. This gives us 1:0:0, which is then translated to 80 | `libite.so.1.0.0`. 81 | 82 | ### Note 83 | 84 | Usually, non-developers have no interest in running development versions 85 | (releases are frequent enough), and developers are expected to know how 86 | to juggle versions. In such an ideal world, it is good enough to bump 87 | the library version just prior to a release, point 2. 88 | 89 | However, if releases are few and far between, distributors may start to 90 | use snapshots. When a distributor uses a snapshot, the distributor has 91 | to handle the library version manually. Things can get ugly when the 92 | distributor has released an intermediate version with a bumped library 93 | version, and when the official release is bumped to that version, the 94 | distributor will then have to bump the library version for the official 95 | release, and it can be confusing if someone reports bugs on versions 96 | that you didn't even know existed. 97 | 98 | The problem with bumping the version with every change is that if your 99 | interface is not finished, the version number might run away, and it 100 | looks pretty bad if a library is at version 262. It kind of tells the 101 | user that the library interface is volatile, which is not good for 102 | business. 103 | 104 | [1]: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 105 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_doc_DATA = API.md 2 | EXTRA_DIST = API.md Doxyfile.in 3 | DISTCLEANFILES = libite.tag 4 | 5 | if ENABLE_HTML 6 | pkghtmldir = $(docdir)/html 7 | pkghtml_DATA = html/* 8 | 9 | # 10 | # Doxygen rules from m4/ax_prog_doxygen.m4 11 | # 12 | @DX_RULES@ 13 | 14 | $(pkghtml_DATA): doxygen-doc 15 | 16 | all-local: doc 17 | 18 | clean-local: 19 | -rm -rf html libite.tag 20 | 21 | doc: doxygen-doc 22 | endif 23 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | TODO ... in no particular order 2 | =============================== 3 | 4 | * Improve documentation, possibly use kdoc or gdoc2 to generate docs from API 5 | * Continuously, update OpenBSD functions/macros 6 | * Investigate adding FreeBSD pidfile_open() et al 7 | * Investigate FreeBSD libutil library, could be useful for porting between unices 8 | 9 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | *.m4 2 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | .libs/* 3 | libite_la* -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libite.la 2 | 3 | libite_la_CPPFLAGS = -D_GNU_SOURCE 4 | libite_la_CFLAGS = -W -Wall -Wextra 5 | libite_la_LDFLAGS = $(AM_LDFLAGS) -version-info 9:0:4 6 | libite_la_SOURCES = chomp.c copyfile.c \ 7 | conio.c conio.h dir.c \ 8 | erasef.c fopenf.c fremove.c \ 9 | fexist.c fisdir.c \ 10 | fparseln.c fsendfile.c \ 11 | ifconfig.c lfile.c \ 12 | makepath.c procval.c progress.c \ 13 | pidfile.c pidfilefn.c popenf.c \ 14 | reallocarray.c rsync.c runbg.c \ 15 | strlcpy.c strlcat.c strtonum.c \ 16 | strdupa.h strndupa.h strnlen.h \ 17 | strmatch.c systemf.c strtrim.c \ 18 | telnet.c tempfile.c truncatef.c \ 19 | touchf.c yorn.c which.c \ 20 | lite.h strlite.h \ 21 | queue.h tree.h 22 | 23 | 24 | ## Distribute these header files in the namespace 25 | pkgincludedir = $(includedir)/libite 26 | pkginclude_HEADERS = lite.h queue.h conio.h tree.h strlite.h strdupa.h \ 27 | strndupa.h strnlen.h 28 | 29 | pkgconfigdir = $(libdir)/pkgconfig 30 | pkgconfig_DATA = libite.pc 31 | 32 | ## For compatibility with previous namespace 33 | if COMPAT_SYMLINK 34 | install-data-hook: 35 | link=$(DESTDIR)$(includedir)/lite; \ 36 | if [ ! -f $$link ]; then \ 37 | $(LN_S) libite $$link; \ 38 | fi 39 | 40 | uninstall-hook: 41 | link=$(shell readlink $(DESTDIR)$(includedir)/lite); \ 42 | if [ "$$link" = "libite" ]; then \ 43 | $(RM) $(DESTDIR)$(includedir)/lite; \ 44 | fi 45 | endif 46 | -------------------------------------------------------------------------------- /src/chomp.c: -------------------------------------------------------------------------------- 1 | /* Perl inspired chomp() implementation. 2 | * 3 | * Copyright (c) 2014-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file chomp.c 20 | * @author Joachim Wiberg 21 | * @date 2014-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | /** 29 | * Perl like chomp function, chop off last char(s) if newline. 30 | * @param str String to chomp 31 | * 32 | * This function is like Perl chomp, but it's set to chop of all 33 | * trailing newlines. Useful in combination with fgets(). 34 | * 35 | * @returns @a str, or @c NULL with @a errno set, if @a str is not a valid pointer. 36 | * @exception EINVAL if the input argument is not a valid pointer. 37 | */ 38 | char *chomp(char *str) 39 | { 40 | char *p; 41 | 42 | if (!str || strlen(str) < 1) { 43 | errno = EINVAL; 44 | return NULL; 45 | } 46 | 47 | p = str + strlen(str) - 1; 48 | while (p >= str && *p == '\n') 49 | *p-- = 0; 50 | 51 | return str; 52 | } 53 | 54 | /** 55 | * Local Variables: 56 | * indent-tabs-mode: t 57 | * c-file-style: "linux" 58 | * End: 59 | */ 60 | -------------------------------------------------------------------------------- /src/conio.c: -------------------------------------------------------------------------------- 1 | /* A conio.h like implementation for VTANSI displays. 2 | * 3 | * Copyright (c) 2009-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file conio.c 20 | * @author Joachim Wiberg 21 | * @date 2009-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * Probe terminal size 32 | * @param row pointer to integer to store number of rows 33 | * @param col pointer to integer to store number of columns 34 | * 35 | * This function checks if stdin and stdout isatty() and then sets the 36 | * TTY in raw mode to silently ask the size using ANSI escape sequences. 37 | * This is achieved by trying to go to corner 999,999 followed by 38 | * querying the cursor position. Afterwards the TTY is returned to the 39 | * state if was before, e.g. cooked. The number of rows and columns is 40 | * returned in the input arguments to this function. 41 | * 42 | * If stdio is @a not a TTY, then a default 24x80 is returned. 43 | */ 44 | void initscr(int *row, int *col) 45 | { 46 | if (!row || !col) 47 | return; 48 | 49 | #if defined(TCSANOW) && defined(POLLIN) 50 | struct termios tc, saved; 51 | struct pollfd fd = { STDIN_FILENO, POLLIN, 0 }; 52 | 53 | if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { 54 | *row = 24; 55 | *col = 80; 56 | return; 57 | } 58 | 59 | /* Disable echo to terminal while probing */ 60 | tcgetattr(STDOUT_FILENO, &tc); 61 | saved = tc; 62 | tc.c_cflag |= (CLOCAL | CREAD); 63 | tc.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 64 | tcsetattr(STDOUT_FILENO, TCSANOW, &tc); 65 | 66 | /* 67 | * Save cursor pos+attr 68 | * Diable top+bottom margins 69 | * Set cursor at the far bottom,right pos 70 | * Query term for resulting pos 71 | */ 72 | fprintf(stdout, "\e7" "\e[r" "\e[999;999H" "\e[6n"); 73 | fflush(stdout); 74 | 75 | /* 76 | * Wait here for terminal to echo back \e[row,lineR ... 77 | */ 78 | if (poll(&fd, 1, 300) <= 0 || scanf("\e[%d;%dR", row, col) != 2) { 79 | *row = 24; 80 | *col = 80; 81 | } 82 | 83 | /* 84 | * Restore above saved cursor pos+attr 85 | */ 86 | fprintf(stdout, "\e8"); 87 | fflush(stdout); 88 | 89 | /* Restore terminal */ 90 | tcsetattr(STDOUT_FILENO, TCSANOW, &saved); 91 | 92 | return; 93 | #else 94 | *row = 24; 95 | *col = 80; 96 | #endif 97 | } 98 | 99 | /** 100 | * Local Variables: 101 | * indent-tabs-mode: t 102 | * c-file-style: "linux" 103 | * End: 104 | */ 105 | -------------------------------------------------------------------------------- /src/conio.h: -------------------------------------------------------------------------------- 1 | /* A conio.h like implementation for VTANSI displays. 2 | * 3 | * Copyright (c) 2009-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file conio.h 20 | * @author Joachim Wiberg 21 | * @date 2009-2021 22 | * @copyright ISC License 23 | * 24 | * Helper macros and functions for interacting with TTY terminals. 25 | */ 26 | 27 | #ifdef __cplusplus 28 | extern "C" 29 | { 30 | #endif 31 | 32 | #ifndef LIBITE_CONIO_H_ 33 | #define LIBITE_CONIO_H_ 34 | 35 | #include 36 | 37 | /** Attributes */ 38 | #define RESETATTR 0 39 | #define BRIGHT 1 40 | #define DIM 2 41 | #define UNDERSCORE 4 42 | #define BLINK 5 /* May not work on all displays. */ 43 | #define REVERSE 7 44 | #define HIDDEN 8 45 | 46 | /** Colors for text and background */ 47 | #define BLACK 0x0 48 | #define RED 0x1 49 | #define GREEN 0x2 50 | #define BROWN 0x3 51 | #define BLUE 0x4 52 | #define MAGENTA 0x5 53 | #define CYAN 0x6 54 | #define LIGHTGREY 0x7 55 | 56 | #define DARKGREY 0x10 57 | #define LIGHTRED 0x11 58 | #define LIGHTGREEN 0x12 59 | #define YELLOW 0x13 60 | #define LIGHTBLUE 0x14 61 | #define LIGHTMAGENTA 0x15 62 | #define LIGHTCYAN 0x16 63 | #define WHITE 0x17 64 | 65 | #ifndef SCREEN_WIDTH 66 | #define SCREEN_WIDTH 80 /**< Fallback screen width, possible to override. */ 67 | #endif 68 | 69 | /** Clear screen and move cursor to 1,1 (upper left) pos. */ 70 | #define clrscr() fputs("\033[2J\033[1;1H", stdout) 71 | /** Erases from the current cursor position to the end of the current line. */ 72 | #define clreol() fputs("\033[K", stdout) 73 | /** Erases the entire current line. */ 74 | #define delline() fputs("\033[2K", stdout) 75 | /** Moves the cursor to the specified position (coordinates) */ 76 | #define gotoxy(x,y) fprintf(stdout, "\033[%d;%dH", y, x) 77 | /** Hide Cursor */ 78 | #define hidecursor() fputs("\033[?25l", stdout) 79 | /** Show Cursor */ 80 | #define showcursor() fputs("\033[?25h", stdout) 81 | 82 | /** Set Graphics Mode (attr, color, val) */ 83 | #define __set_gm(a,c,v) \ 84 | if (!c) \ 85 | fprintf(stdout, "\033[%dm", a); \ 86 | else \ 87 | fprintf(stdout, "\033[%d;%dm", c & 0x10 ? 1 : 0, (c & 0xF) + v) 88 | 89 | /** Set text attribute */ 90 | #define textattr(attr) __set_gm(attr, 0, 0) 91 | /** Set text color */ 92 | #define textcolor(color) __set_gm(RESETATTR, color, 30) 93 | /** Set text background */ 94 | #define textbackground(color) __set_gm(RESETATTR, color, 40) 95 | 96 | void initscr(int *row, int *col); 97 | 98 | /** 99 | * Print table heading, with optional leading newline. 100 | * @param fp output stream 101 | * @param line string to print 102 | * @param nl leading newline or not 103 | * @param attr attribute 104 | * 105 | * @verbatim 106 | * _________________ <-- Empty line with UNDERSCORE to frame first heading 107 | * First heading <-- In normal/RESETATTR 108 | * SUBHEADING <-- In REVERSE and with capital letters like 'top' 109 | * @endverbatim 110 | */ 111 | static inline void printhdr(FILE *fp, const char *line, int nl, int attr) 112 | { 113 | if (attr < 0 || attr > 8) 114 | attr = REVERSE; 115 | 116 | fprintf(fp ?: stdout, 117 | "%s\033[%dm%s%*s\033[0m\n", 118 | nl ? "\n" : "", attr, line, 119 | SCREEN_WIDTH - (int)strlen(line), ""); 120 | } 121 | 122 | /** 123 | * Print reverse mode table heading, e.g. black text on white background. 124 | * @param fp output stream 125 | * @param line string to print 126 | * @param nl leading newline or not 127 | */ 128 | static inline void printheader(FILE *fp, const char *line, int nl) 129 | { 130 | printhdr(fp, line, nl, REVERSE); 131 | } 132 | 133 | #endif /* LIBITE_CONIO_H_ */ 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | /** 140 | * Local Variables: 141 | * indent-tabs-mode: t 142 | * c-file-style: "linux" 143 | * End: 144 | */ 145 | -------------------------------------------------------------------------------- /src/dir.c: -------------------------------------------------------------------------------- 1 | /* Functions for operating on files in directories. 2 | * 3 | * Copyright (c) 2008-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file dir.c 20 | * @author Joachim Wiberg 21 | * @date 2008-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static const char *matcher_type = NULL; 31 | static int (*matcher_filter) (const char *file) = NULL; 32 | 33 | static int matcher(const struct dirent *entry) 34 | { 35 | char *pos = strrchr(entry->d_name, '.'); 36 | 37 | if (matcher_filter && !matcher_filter(entry->d_name)) 38 | /* User matcher overrides the rest. */ 39 | return 0; 40 | 41 | /* Skip current dir "." from list of files. */ 42 | if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') || 43 | (2 == strlen(entry->d_name) && !strcmp(entry->d_name, ".."))) 44 | return 0; 45 | 46 | /* filetype == "" */ 47 | if (matcher_type[0] == 0) 48 | return 1; 49 | 50 | /* Entry has no "." */ 51 | if (!pos) 52 | return 0; 53 | 54 | return !strcmp(pos, matcher_type); 55 | } 56 | 57 | /** 58 | * List all files of a certain type in the given directory. 59 | * @param dir Base directory for dir operation. 60 | * @param type File type suffix, e.g. ".cfg". 61 | * @param filter Optional file name filter. 62 | * @param list Pointer to an array of file names. 63 | * @param strip Flag, if set dir() strips the file type. 64 | * 65 | * This function returns a @a list of files, matching the @a type 66 | * suffix, in the given directory @a dir. 67 | * 68 | * The @a list argument is a pointer to where to store the dynamically 69 | * allocated list of file names. This list should be free'd by first 70 | * calling free() on each file name and then on the list itself. 71 | * 72 | * If @a filter is not @c NULL it will be called for each file found. 73 | * If @a filter returns non-zero the @a file argument is included in the 74 | * resulting @a list. If @a filter returns zero for given @a file it is 75 | * discarded. If the @a strip flag is set the resulting @a list of 76 | * files has their file type stripped, including the dot. So a match 77 | * "config0.cfg" would be returned as "config0". 78 | * 79 | * @returns The number of files in @a list, zero if no matching files of 80 | * @a type, or non-zero on error with @a errno set. 81 | */ 82 | int dir(const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip) 83 | { 84 | int i, n, num = 0; 85 | char **files; 86 | struct dirent **namelist; 87 | 88 | if (!list) { 89 | errno = EINVAL; 90 | return -1; 91 | } 92 | 93 | if (!dir) 94 | /* Assuming current directory */ 95 | dir = "."; 96 | if (!type) 97 | /* Assuming all files. */ 98 | type = ""; 99 | 100 | matcher_type = type; 101 | matcher_filter = filter; 102 | n = scandir(dir, &namelist, matcher, alphasort); 103 | if (n < 0) 104 | return -1; 105 | 106 | if (n > 0) { 107 | files = (char **)malloc(n * sizeof(char *)); 108 | for (i = 0; i < n; i++) { 109 | if (files) { 110 | char *name = namelist[i]->d_name; 111 | char *type = strrchr(name, '.'); 112 | 113 | if (type && strip) 114 | *type = 0; 115 | 116 | files[i] = strdup(name); 117 | num++; 118 | } 119 | free(namelist[i]); 120 | } 121 | if (num) 122 | *list = files; 123 | } 124 | 125 | if (namelist) 126 | free(namelist); 127 | 128 | return num; 129 | } 130 | 131 | /** 132 | * Local Variables: 133 | * indent-tabs-mode: t 134 | * c-file-style: "linux" 135 | * End: 136 | */ 137 | -------------------------------------------------------------------------------- /src/erasef.c: -------------------------------------------------------------------------------- 1 | /* Formatted erase() 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file erasef.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "lite.h" 32 | 33 | /** 34 | * Like erase() but with formatted string support. 35 | * @param fmt Formatted string to be composed into a pathname 36 | * 37 | * This is a wrapper for the erase() function in lite.h, lessening the 38 | * burden of having to compose the filename from parts in a seprate 39 | * buffer. 40 | * 41 | * @returns Upon successful completion erasef() returns POSIX OK(0), 42 | * otherwise, -1 is returned and @a errno is set to indicate the error. 43 | */ 44 | int erasef(const char *fmt, ...) 45 | { 46 | va_list ap; 47 | char *file; 48 | int len; 49 | 50 | va_start(ap, fmt); 51 | len = vsnprintf(NULL, 0, fmt, ap); 52 | va_end(ap); 53 | 54 | file = alloca(len + 1); 55 | if (!file) { 56 | errno = ENOMEM; 57 | return -1; 58 | } 59 | 60 | va_start(ap, fmt); 61 | vsnprintf(file, len + 1, fmt, ap); 62 | va_end(ap); 63 | 64 | return erase(file); 65 | } 66 | 67 | /** 68 | * Local Variables: 69 | * indent-tabs-mode: t 70 | * c-file-style: "linux" 71 | * End: 72 | */ 73 | -------------------------------------------------------------------------------- /src/fexist.c: -------------------------------------------------------------------------------- 1 | /* Check if file exists 2 | * 3 | * Copyright (c) 2008 Claudio Matsuoka 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file fexist.c 26 | * @author Claudio Matsuoka 27 | * @date 2008 28 | * @copyright MIT License 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "lite.h" 38 | 39 | /** 40 | * Check if a file exists in the file system. 41 | * @param file File to look for, with full path. 42 | * 43 | * @returns @c TRUE(1) if the @a file exists, otherwise @c FALSE(0). 44 | */ 45 | int fexist(const char *file) 46 | { 47 | if (!file) { 48 | errno = EINVAL; 49 | return 0; /* Doesn't exist ... */ 50 | } 51 | 52 | if (-1 == access(file, F_OK)) 53 | return 0; 54 | 55 | return 1; 56 | } 57 | 58 | /** 59 | * Like fexist() but with formatted string support 60 | * @param fmt Formatted string to be composed into the file to look for. 61 | * 62 | * This is a wrapper for the fexist() function in lite.h, lessening the 63 | * burden of having to compose the filename from parts in a seprate 64 | * buffer. 65 | * 66 | * @returns @c TRUE(1) if the @a file exists, otherwise @c FALSE(0). 67 | */ 68 | int fexistf(const char *fmt, ...) 69 | { 70 | va_list ap; 71 | char *file; 72 | int len; 73 | 74 | va_start(ap, fmt); 75 | len = vsnprintf(NULL, 0, fmt, ap); 76 | va_end(ap); 77 | 78 | file = alloca(len + 1); 79 | if (!file) { 80 | errno = ENOMEM; 81 | return -1; 82 | } 83 | 84 | va_start(ap, fmt); 85 | vsnprintf(file, len + 1, fmt, ap); 86 | va_end(ap); 87 | 88 | return fexist(file); 89 | } 90 | 91 | /** 92 | * Local Variables: 93 | * indent-tabs-mode: t 94 | * c-file-style: "linux" 95 | * End: 96 | */ 97 | -------------------------------------------------------------------------------- /src/fisdir.c: -------------------------------------------------------------------------------- 1 | /* Check if directory exists 2 | * 3 | * Copyright (c) 2008 Claudio Matsuoka 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file fisdir.c 26 | * @author Claudio Matsuoka 27 | * @date 2008 28 | * @copyright MIT License 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | /** 35 | * Check if a path exists and is a directory. 36 | * @param path Path to file or directory 37 | * 38 | * @returns @c TRUE(1) if @p path exists and is a directory, otherwise @c FALSE(0). 39 | */ 40 | int fisdir(const char *path) 41 | { 42 | struct stat sb; 43 | 44 | if (path && !stat(path, &sb) && S_ISDIR(sb.st_mode)) 45 | return 1; 46 | 47 | return 0; 48 | } 49 | 50 | /** 51 | * Local Variables: 52 | * indent-tabs-mode: t 53 | * c-file-style: "linux" 54 | * End: 55 | */ 56 | -------------------------------------------------------------------------------- /src/fopenf.c: -------------------------------------------------------------------------------- 1 | /* Formatted fopen() 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file fopenf.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * Similar to fopenf() except it takes a @a va_list argument 32 | * @param mode An fopen() mode string, e.g. "w+". 33 | * @param fmt Formatted string to be composed into a pathname. 34 | * @param ap List of variable arguemnts from va_start(). 35 | * 36 | * See fopenf() for details. 37 | * 38 | * @returns a FILE pointer, or @c NULL on error. 39 | */ 40 | FILE *vfopenf(const char *mode, const char *fmt, va_list ap) 41 | { 42 | va_list apc; 43 | char *file; 44 | int len; 45 | 46 | va_copy(apc, ap); 47 | len = vsnprintf(NULL, 0, fmt, apc); 48 | va_end(apc); 49 | 50 | file = alloca(len + 1); 51 | if (!file) { 52 | errno = ENOMEM; 53 | return NULL; 54 | } 55 | 56 | va_copy(apc, ap); 57 | vsnprintf(file, len + 1, fmt, apc); 58 | va_end(apc); 59 | 60 | return fopen(file, mode); 61 | } 62 | 63 | /** 64 | * Open a file based on the formatted string and optional arguments 65 | * @param mode An fopen() mode string, e.g. "w+". 66 | * @param fmt Formatted string to be composed into a pathname. 67 | * 68 | * This function is an extension to the fopen() family, lessening the burden 69 | * of first having to compose the filename from parts in a seprate buffer. 70 | * 71 | * @returns Upon successful completion, fopenf() returns a FILE pointer. 72 | * Otherwise, @c NULL is returned and @a errno is set to indicate the 73 | * error. 74 | */ 75 | FILE *fopenf(const char *mode, const char *fmt, ...) 76 | { 77 | va_list ap; 78 | FILE *fp; 79 | 80 | va_start(ap, fmt); 81 | fp = vfopenf(mode, fmt, ap); 82 | va_end(ap); 83 | if (!fp) 84 | return NULL; 85 | 86 | return fp; 87 | } 88 | 89 | /** 90 | * Local Variables: 91 | * indent-tabs-mode: t 92 | * c-file-style: "linux" 93 | * End: 94 | */ 95 | -------------------------------------------------------------------------------- /src/fparseln.c: -------------------------------------------------------------------------------- 1 | /* $NetBSD: fparseln.c,v 1.10 2009/10/21 01:07:45 snj Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1997 Christos Zoulas. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /** 28 | * @file fparseln.c 29 | * @author Christos Zoulas 30 | * @date 1997 31 | * @copyright 2-clause BSD License 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "lite.h" 40 | 41 | #ifndef _DIAGASSERT 42 | #define _DIAGASSERT(t) 43 | #endif 44 | 45 | static int isescaped(const char *, const char *, int); 46 | 47 | /* isescaped(): 48 | * Return true if the character in *p that belongs to a string 49 | * that starts in *sp, is escaped by the escape character esc. 50 | */ 51 | static int 52 | isescaped(const char *sp, const char *p, int esc) 53 | { 54 | const char *cp; 55 | size_t ne; 56 | 57 | _DIAGASSERT(sp != NULL); 58 | _DIAGASSERT(p != NULL); 59 | 60 | /* No escape character */ 61 | if (esc == '\0') 62 | return 0; 63 | 64 | /* Count the number of escape characters that precede ours */ 65 | for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) 66 | continue; 67 | 68 | /* Return true if odd number of escape characters */ 69 | return (ne & 1) != 0; 70 | } 71 | 72 | 73 | /** 74 | * Read a line from a file parsing continuations and trailing newlines 75 | * @param fp FILE pointer to read from 76 | * @param size The resulting length of the string, unused if @c NULL 77 | * @param lineno Incremented with number of lines read, unused if @c NULL 78 | * @param str Characters to look for, escape character, continuation, and comment 79 | * @param flags ::FPARSELN_UNESCCOMM, ::FPARSELN_UNESCCONT, ::FPARSELN_UNESCESC, ::FPARSELN_UNESCREST, ::FPARSELN_UNESCALL 80 | * 81 | * This function reads a line from a file, parsing continuations ending 82 | * in '\' and eliminating trailing newlines, or comments starting with 83 | * the comment char '#'. 84 | * 85 | * If @a size is not @c NULL, the resulting length of the returned string 86 | * is stored in @a size. 87 | * 88 | * If @a lineno is not @c NULL, it is incremented for each actual line 89 | * read from @a fp. 90 | * 91 | * @returns the line read from @a fp, or @c NULL on EOF or error. 92 | */ 93 | char * 94 | fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags) 95 | { 96 | static const char dstr[3] = { '\\', '\\', '#' }; 97 | 98 | ssize_t s; 99 | size_t len, ptrlen; 100 | char *buf; 101 | char *ptr, *cp; 102 | int cnt; 103 | char esc, con, nl, com; 104 | 105 | _DIAGASSERT(fp != NULL); 106 | 107 | len = 0; 108 | buf = NULL; 109 | ptrlen = 0; 110 | ptr = NULL; 111 | cnt = 1; 112 | 113 | if (str == NULL) 114 | str = dstr; 115 | 116 | esc = str[0]; 117 | con = str[1]; 118 | com = str[2]; 119 | /* 120 | * XXX: it would be cool to be able to specify the newline character, 121 | * getdelim(3) does let us, but supporting it would diverge from BSDs. 122 | */ 123 | nl = '\n'; 124 | 125 | flockfile(fp); 126 | 127 | while (cnt) { 128 | cnt = 0; 129 | 130 | if (lineno) 131 | (*lineno)++; 132 | 133 | s = getline(&ptr, &ptrlen, fp); 134 | if (s < 0) 135 | break; 136 | 137 | if (s && com) { /* Check and eliminate comments */ 138 | for (cp = ptr; cp < ptr + s; cp++) 139 | if (*cp == com && !isescaped(ptr, cp, esc)) { 140 | s = cp - ptr; 141 | cnt = s == 0 && buf == NULL; 142 | break; 143 | } 144 | } 145 | 146 | if (s && nl) { /* Check and eliminate newlines */ 147 | cp = &ptr[s - 1]; 148 | 149 | if (*cp == nl) 150 | s--; /* forget newline */ 151 | } 152 | 153 | if (s && con) { /* Check and eliminate continuations */ 154 | cp = &ptr[s - 1]; 155 | 156 | if (*cp == con && !isescaped(ptr, cp, esc)) { 157 | s--; /* forget continuation char */ 158 | cnt = 1; 159 | } 160 | } 161 | 162 | if (s == 0) { 163 | /* 164 | * nothing to add, skip realloc except in case 165 | * we need a minimal buf to return an empty line 166 | */ 167 | if (cnt || buf != NULL) 168 | continue; 169 | } 170 | 171 | if ((cp = realloc(buf, len + s + 1)) == NULL) { 172 | funlockfile(fp); 173 | free(buf); 174 | free(ptr); 175 | return NULL; 176 | } 177 | buf = cp; 178 | 179 | (void) memcpy(buf + len, ptr, s); 180 | len += s; 181 | buf[len] = '\0'; 182 | } 183 | 184 | funlockfile(fp); 185 | free(ptr); 186 | 187 | if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && 188 | strchr(buf, esc) != NULL) { 189 | ptr = cp = buf; 190 | while (cp[0] != '\0') { 191 | int skipesc; 192 | 193 | while (cp[0] != '\0' && cp[0] != esc) 194 | *ptr++ = *cp++; 195 | if (cp[0] == '\0' || cp[1] == '\0') 196 | break; 197 | 198 | skipesc = 0; 199 | if (cp[1] == com) 200 | skipesc += (flags & FPARSELN_UNESCCOMM); 201 | if (cp[1] == con) 202 | skipesc += (flags & FPARSELN_UNESCCONT); 203 | if (cp[1] == esc) 204 | skipesc += (flags & FPARSELN_UNESCESC); 205 | if (cp[1] != com && cp[1] != con && cp[1] != esc) 206 | skipesc = (flags & FPARSELN_UNESCREST); 207 | 208 | if (skipesc) 209 | cp++; 210 | else 211 | *ptr++ = *cp++; 212 | *ptr++ = *cp++; 213 | } 214 | *ptr = '\0'; 215 | len = strlen(buf); 216 | } 217 | 218 | if (size) 219 | *size = len; 220 | return buf; 221 | } 222 | -------------------------------------------------------------------------------- /src/fremove.c: -------------------------------------------------------------------------------- 1 | /* Formatted fremove() 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file fremove.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * Remove a file based on the formatted string and optional arguments 32 | * @param fmt Formatted string to be composed into a pathname 33 | * 34 | * This function is an extension to remove(), lessening the burden of 35 | * first having to compose the filename from parts in a seprate buffer. 36 | * 37 | * @returns same as remove(3), with an extra @a errno, @c ENOBUFS if 38 | * alloca() fails to get a temporary buffer for composing the file name. 39 | */ 40 | int fremove(const char *fmt, ...) 41 | { 42 | va_list ap; 43 | char *file; 44 | int len; 45 | 46 | va_start(ap, fmt); 47 | len = vsnprintf(NULL, 0, fmt, ap); 48 | va_end(ap); 49 | 50 | file = alloca(len + 1); 51 | if (!file) { 52 | errno = ENOBUFS; 53 | return -1; 54 | } 55 | 56 | va_start(ap, fmt); 57 | vsnprintf(file, len + 1, fmt, ap); 58 | va_end(ap); 59 | 60 | return remove(file); 61 | } 62 | 63 | /** 64 | * Local Variables: 65 | * indent-tabs-mode: t 66 | * c-file-style: "linux" 67 | * End: 68 | */ 69 | -------------------------------------------------------------------------------- /src/fsendfile.c: -------------------------------------------------------------------------------- 1 | /* Copy data between file streams 2 | * 3 | * Copyright (c) 2013 Tobias Waldekranz 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file fsendfile.c 26 | * @author Tobias Waldekranz 27 | * @date 2013 28 | * @copyright MIT License 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | /** 37 | * Copy data between file streams. 38 | * @param src Source stream 39 | * @param dst Destination stream 40 | * @param len Number of bytes to copy 41 | * 42 | * The @p dst argument may be @c NULL, in which case @a len bytes are 43 | * read and discarded from @a src. This can be useful for streams where 44 | * seeking is not permitted. Additionally, @a len may be the special 45 | * value zero (0), in which case fsendfile() copies until @c EOF is seen 46 | * on @a src. 47 | * 48 | * @returns The number of bytes copied. If an error is detected -1 is 49 | * returned and @a errno will be set accordingly. 50 | */ 51 | ssize_t fsendfile(FILE *src, FILE *dst, size_t len) 52 | { 53 | char *buf; 54 | size_t tot = 0; 55 | ssize_t blk = BUFSIZ, num = 0; 56 | 57 | if (!src) { 58 | errno = EINVAL; 59 | return -1; 60 | } 61 | 62 | buf = malloc(BUFSIZ); 63 | if (!buf) 64 | return -1; 65 | 66 | while (!len || tot < len) { 67 | if (len && ((len - tot) < BUFSIZ)) 68 | blk = len - tot; 69 | 70 | num = fread(buf, 1, blk, src); 71 | if (num == 0) 72 | break; 73 | 74 | if (dst && (fwrite(buf, num, 1, dst) != 1)) { 75 | num = -1; 76 | break; 77 | } 78 | 79 | tot += num; 80 | } 81 | 82 | free(buf); 83 | 84 | return (num == -1) ? -1 : (ssize_t)tot; 85 | } 86 | 87 | /** 88 | * Local Variables: 89 | * indent-tabs-mode: t 90 | * c-file-style: "linux" 91 | * End: 92 | */ 93 | -------------------------------------------------------------------------------- /src/ifconfig.c: -------------------------------------------------------------------------------- 1 | /* Fastinit (finit) ifconfig() implementation. 2 | * 3 | * Copyright (c) 2008-2010 Claudio Matsuoka 4 | * Copyright (C) 2009-2021 Joachim Wiberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file ifconfig.c 27 | * @author Claudio Matsuoka 28 | * @author Joachim Wiberg 29 | * @date 2008-2021 30 | * @copyright MIT License 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | extern size_t strlcpy(char *dst, const char *src, size_t siz); 43 | 44 | /** 45 | * Basic ifconfig like operations on an interface 46 | * @param ifname Name of interface to operate on 47 | * @param addr If @p up then set this optional IPv4 address 48 | * @param mask If @p up and @p addr, and @p addr is not @c INADDR_ANY, then set netmask 49 | * @param up Control @c IFF_UP flag on interface 50 | * 51 | * @returns POSIX OK(0) on success, or non-zero on error. 52 | */ 53 | int ifconfig(const char *ifname, const char *addr, const char *mask, int up) 54 | { 55 | int sd, ret = -1; 56 | struct ifreq ifr; 57 | struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 58 | 59 | if ((sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) 60 | return -1; 61 | 62 | memset(&ifr, 0, sizeof (ifr)); 63 | strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); 64 | ifr.ifr_addr.sa_family = AF_INET; 65 | 66 | if (up) { 67 | if (addr) { 68 | if (inet_pton(AF_INET, addr, &sin->sin_addr) == 1) 69 | ret = ioctl(sd, SIOCSIFADDR, &ifr); 70 | } 71 | 72 | /* Non-zero IP address */ 73 | if (mask && addr && strcmp(addr, "0.0.0.0")) { 74 | if (inet_pton(AF_INET, mask, &sin->sin_addr) == -1) 75 | ret = ioctl(sd, SIOCSIFNETMASK, &ifr); 76 | } 77 | } 78 | 79 | if (!ioctl(sd, SIOCGIFFLAGS, &ifr)) { 80 | if (up) 81 | ifr.ifr_flags |= IFF_UP; 82 | else 83 | ifr.ifr_flags &= ~IFF_UP; 84 | 85 | ret = ioctl(sd, SIOCSIFFLAGS, &ifr); 86 | } 87 | 88 | close(sd); 89 | 90 | return ret; 91 | } 92 | 93 | /** 94 | * Local Variables: 95 | * indent-tabs-mode: t 96 | * c-file-style: "linux" 97 | * End: 98 | */ 99 | -------------------------------------------------------------------------------- /src/lfile.c: -------------------------------------------------------------------------------- 1 | /* Parse UNIX /etc configuration files like /etc/protocols and /etc/services 2 | * 3 | * Copyright (c) 2015-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file lfile.c 20 | * @author Joachim Wiberg 21 | * @date 2015-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include /* FILE */ 27 | #include /* atoi() */ 28 | #include /* strlen(), strncmp(), strtok_r() */ 29 | #include /* MAX() */ 30 | 31 | /** @private for internal use only */ 32 | typedef struct lfile { 33 | FILE *fp; /**< FILE pointer to current stream */ 34 | char buf[256]; /**< Internal buffer, limted to 256 bytes per line */ 35 | char *save; /**< Internal save pointer */ 36 | const char *sep; /**< Record separator, from lfopen() */ 37 | } lfile_t; 38 | 39 | /** 40 | * Open file and return parsing context. 41 | * @param file File to parse 42 | * @param sep Separator(s) to use in lftok() 43 | * 44 | * @returns Pointer to an @a lfile_t parser context, or @c NULL on error. 45 | */ 46 | lfile_t *lfopen(const char *file, const char *sep) 47 | { 48 | lfile_t *lf; 49 | 50 | if (!file || !sep) { 51 | errno = EINVAL; 52 | return NULL; 53 | } 54 | 55 | /* Use calloc for clearing buf on behalf of lftok() */ 56 | lf = calloc(sizeof(*lf), sizeof(char)); 57 | if (lf) { 58 | lf->fp = fopen(file, "r"); 59 | lf->sep = sep; 60 | lf->save = lf->buf; 61 | 62 | if (!lf->fp) { 63 | free(lf); 64 | return NULL; 65 | } 66 | } 67 | 68 | return lf; 69 | } 70 | 71 | /** 72 | * Close a parser context. 73 | * @param lf: Pointer to @a lfile_t parser context from lfopen() 74 | */ 75 | void lfclose(lfile_t *lf) 76 | { 77 | if (!lf) 78 | return; 79 | 80 | if (lf->fp) 81 | fclose(lf->fp); 82 | free(lf); 83 | } 84 | 85 | /** 86 | * Get next token in file 87 | * @param lf: Pointer to @a lfile_t parser context from lfopen() 88 | * 89 | * @returns Next token, read from file previously opened with lfopen(), 90 | * or @c NULL if EOF. 91 | */ 92 | char *lftok(lfile_t *lf) 93 | { 94 | char *token; 95 | 96 | if (!lf || !lf->fp || !lf->sep) { 97 | errno = EINVAL; 98 | return NULL; 99 | } 100 | 101 | token = strtok_r(NULL, lf->sep, &lf->save); 102 | if (token) 103 | return token; 104 | 105 | while (fgets(lf->buf, sizeof(lf->buf), lf->fp)) { 106 | if (lf->buf[0] == '#') 107 | continue; 108 | 109 | token = strtok_r(lf->buf, lf->sep, &lf->save); 110 | if (token) 111 | return token; 112 | } 113 | 114 | errno = ENOENT; 115 | return NULL; 116 | } 117 | 118 | /** 119 | * Find key in file 120 | * @param lf Pointer to @a lfile_t parser context from lfopen() 121 | * @param key Key to look for 122 | * 123 | * Locate @a key from the current position in the file parser context 124 | * returned from lfopen(). Please note, the search for @a key does not 125 | * start from the beginning of the file, it searches from the current 126 | * position. To restart search from the beginning use rewind() on the 127 | * lf->fp. 128 | * 129 | * @returns The value to @a key, or @c NULL if not found. 130 | */ 131 | char *lfgetkey(lfile_t *lf, const char *key) 132 | { 133 | char *token; 134 | 135 | while ((token = lftok(lf))) { 136 | if (token[0] == '#') 137 | continue; 138 | 139 | if (!strncmp(token, key, MAX(strlen(token), strlen(key)))) 140 | return lftok(lf); 141 | } 142 | 143 | return NULL; 144 | } 145 | 146 | /** 147 | * Same as lfgetkey() but returns an integer. 148 | * @param lf Pointer to @a lfile_t parser context from lfopen() 149 | * @param key Key to look for 150 | * 151 | * This function is the same as lfgetkey() but returns the positive 152 | * integer value for the matching @a key, if found. 153 | * 154 | * @returns The positive integer value for @a key, or -1 if not found. 155 | */ 156 | int lfgetint(lfile_t *lf, const char *key) 157 | { 158 | char *token = lfgetkey(lf, key); 159 | 160 | if (token) 161 | return atoi(token); 162 | 163 | return -1; 164 | } 165 | 166 | /** 167 | * Find the integer value for key in a file. 168 | * @param file File to search for @a key 169 | * @param sep Separator for tokens in @a file 170 | * @param key Key to look for in @a file 171 | * 172 | * This is a convenience wrapper for lfopen(), lfgetint(), and 173 | * lfclose(). 174 | * 175 | * @returns The positive integer value for @a key, or -1 if not found. 176 | */ 177 | int fgetint(const char *file, const char *sep, const char *key) 178 | { 179 | int val = -1; 180 | lfile_t *lf; 181 | 182 | lf = lfopen(file, sep); 183 | if (lf) { 184 | val = lfgetint(lf, key); 185 | lfclose(lf); 186 | } 187 | 188 | return val; 189 | } 190 | 191 | /** 192 | * Local Variables: 193 | * indent-tabs-mode: t 194 | * c-file-style: "linux" 195 | * End: 196 | */ 197 | -------------------------------------------------------------------------------- /src/libite.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE@ 7 | Description: Frog DNA, basically 8 | Version: @VERSION@ 9 | Requires: 10 | Libs: -L${libdir} -lite 11 | Cflags: -I${includedir} -D_LIBITE_LITE 12 | -------------------------------------------------------------------------------- /src/makepath.c: -------------------------------------------------------------------------------- 1 | /* mkpath() -- Create all components leading up to a given directory 2 | * 3 | * Copyright (c) 2013-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file makepath.c 20 | * @author Joachim Wiberg 21 | * @date 2013-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "lite.h" 32 | 33 | /** 34 | * makepath() but takes a mode_t argument. 35 | * @param dir Directory to created, relative or absolute 36 | * @param mode A &mode_t mode to create @a dir with 37 | * 38 | * @returns POSIX OK(0) on success, otherwise -1 with @a errno set. 39 | */ 40 | int mkpath(const char *dir, mode_t mode) 41 | { 42 | struct stat sb; 43 | 44 | if (!dir) { 45 | errno = EINVAL; 46 | return -1; 47 | } 48 | 49 | if (!stat(dir, &sb)) 50 | return 0; 51 | 52 | mkpath(dirname(strdupa(dir)), mode); 53 | 54 | return mkdir(dir, mode); 55 | } 56 | 57 | /** 58 | * Formatted version of mkpath(). 59 | * @param mode A mode_t mode to create directories with 60 | * @param fmt Formatted string to be composed into a pathname 61 | * 62 | * @note Notice the swapped arguments, compared to mkpath()! 63 | * 64 | * @returns POSIX OK(0) on success, otherwise -1 with @a errno set. 65 | */ 66 | int fmkpath(mode_t mode, const char *fmt, ...) 67 | { 68 | va_list ap; 69 | char *path; 70 | int len; 71 | 72 | va_start(ap, fmt); 73 | len = vsnprintf(NULL, 0, fmt, ap); 74 | va_end(ap); 75 | 76 | path = alloca(len + 1); 77 | if (!path) { 78 | errno = ENOMEM; 79 | return -1; 80 | } 81 | 82 | va_start(ap, fmt); 83 | len = vsnprintf(path, len + 1, fmt, ap); 84 | va_end(ap); 85 | 86 | return mkpath(path, mode); 87 | } 88 | 89 | /** 90 | * Create all components of the specified directory. 91 | * @param dir Directory to create. 92 | * 93 | * @note It is strongly recommended to use mkpath() over this function 94 | * since it has the @a mode argument while this function default to 95 | * 0777, which in most cases is insecure. 96 | * 97 | * @returns POSIX OK (0) on success, otherwise -1 and @a errno set 98 | * appropriately. 99 | * 100 | * @exception EINVAL on bad argument, or 101 | * @exception ENOMEM when it fails allocating temporary memory. 102 | * 103 | * For other error codes see the mkdir(2) syscall description. 104 | */ 105 | int makepath(const char *dir) 106 | { 107 | return mkpath(dir, 0777); 108 | } 109 | 110 | /** 111 | * Local Variables: 112 | * indent-tabs-mode: t 113 | * c-file-style: "linux" 114 | * End: 115 | */ 116 | -------------------------------------------------------------------------------- /src/pidfile.c: -------------------------------------------------------------------------------- 1 | /* Updated by troglobit for libite/finit/uftpd projects 2016/07/04 */ 2 | /* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */ 3 | /* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ 4 | 5 | /*- 6 | * Copyright (c) 1999 The NetBSD Foundation, Inc. 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Jason R. Thorpe. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions 14 | * are met: 15 | * 1. Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @file pidfile.c 36 | * @author NetBSD Foundation Inc. 37 | * @date 1999 38 | * @copyright 2-clause BSD License 39 | */ 40 | 41 | #include /* utimensat() */ 42 | #include /* utimensat() on *BSD */ 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #ifndef pidfile 52 | static char *pidfile_path = NULL; 53 | static pid_t pidfile_pid = 0; 54 | 55 | static void pidfile_cleanup(void); 56 | 57 | const char *__pidfile_path = _PATH_VARRUN; /* Note: includes trailing slash '/' */ 58 | const char *__pidfile_name = NULL; 59 | extern char *__progname; 60 | 61 | /** 62 | * Create or update mtime of process PID file. 63 | * @param basename Program name, or @c NULL, may start with '/' 64 | * 65 | * This function is intended to be used by UNIX daemons to save the PID of the main process 66 | * responsible for handling signals. If @p basename is @c NULL the implicit @a __progname 67 | * variable from the C-library is used. The @p basename may also start with '/', in which 68 | * case it is interpreted as the absolute path to the PID file. 69 | * 70 | * @returns POSIX OK(0) on success, otherwise non-zero on error. 71 | */ 72 | int pidfile(const char *basename) 73 | { 74 | int save_errno; 75 | int atexit_already; 76 | pid_t pid; 77 | FILE *f; 78 | 79 | if (basename == NULL) 80 | basename = __progname; 81 | 82 | pid = getpid(); 83 | atexit_already = 0; 84 | 85 | if (pidfile_path != NULL) { 86 | if (!access(pidfile_path, R_OK) && pid == pidfile_pid) { 87 | utimensat(0, pidfile_path, NULL, 0); 88 | return (0); 89 | } 90 | free(pidfile_path); 91 | pidfile_path = NULL; 92 | __pidfile_name = NULL; 93 | atexit_already = 1; 94 | } 95 | 96 | if (basename[0] != '/') { 97 | size_t len = strlen(__pidfile_path); 98 | int slash = __pidfile_path[len > 0 ? len - 1 : 0] != '/'; 99 | 100 | if (asprintf(&pidfile_path, "%s%s%s.pid", __pidfile_path, slash ? "/" : "", basename) == -1) 101 | return (-1); 102 | } else { 103 | if (asprintf(&pidfile_path, "%s", basename) == -1) 104 | return (-1); 105 | } 106 | 107 | if ((f = fopen(pidfile_path, "w")) == NULL) { 108 | save_errno = errno; 109 | free(pidfile_path); 110 | pidfile_path = NULL; 111 | errno = save_errno; 112 | return (-1); 113 | } 114 | 115 | if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) { 116 | save_errno = errno; 117 | (void) fclose(f); 118 | (void) unlink(pidfile_path); 119 | free(pidfile_path); 120 | pidfile_path = NULL; 121 | errno = save_errno; 122 | return (-1); 123 | } 124 | (void) fclose(f); 125 | __pidfile_name = pidfile_path; 126 | 127 | /* 128 | * LITE extension, no need to set up another atexit() handler 129 | * if user only called us to update the mtime of the PID file 130 | */ 131 | if (atexit_already) 132 | return (0); 133 | 134 | pidfile_pid = pid; 135 | if (atexit(pidfile_cleanup) < 0) { 136 | save_errno = errno; 137 | (void) unlink(pidfile_path); 138 | free(pidfile_path); 139 | pidfile_path = NULL; 140 | pidfile_pid = 0; 141 | errno = save_errno; 142 | return (-1); 143 | } 144 | 145 | return (0); 146 | } 147 | 148 | static void 149 | pidfile_cleanup(void) 150 | { 151 | if (pidfile_path != NULL && pidfile_pid == getpid()) { 152 | (void) unlink(pidfile_path); 153 | free(pidfile_path); 154 | pidfile_path = NULL; 155 | } 156 | } 157 | #endif 158 | -------------------------------------------------------------------------------- /src/pidfilefn.c: -------------------------------------------------------------------------------- 1 | /* Functions for dealing with PID files (client side) 2 | * 3 | * Copyright (c) 2009-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file pidfilefn.c 20 | * @author Joachim Wiberg 21 | * @date 2009-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | extern char *chomp(char *str); 32 | 33 | /** 34 | * Reads a PID value from a pidfile. 35 | * @param pidfile File containing PID, usually in @c /var/run/PROCNAME.pid 36 | * 37 | * This function takes a @p pidfile and returns the PID found therein. 38 | * 39 | * @returns On invalid @p pidfile, -1 with @a errno set. If the @p 40 | * pidfile is empty, or when its contents cannot be translated, this 41 | * function returns zero (0), on success this function returns a PID 42 | * value greater than one. 43 | * 44 | * @note PID 1 is reserved for the system init process. 45 | * 46 | * @exception EINVAL on invalid @p pidfile, or 47 | * @exception ENOENT when @p pidfile does not exist. 48 | */ 49 | pid_t pidfile_read(const char *pidfile) 50 | { 51 | int pid = 0; 52 | char buf[16]; 53 | FILE *fp; 54 | 55 | if (!pidfile) { 56 | errno = EINVAL; 57 | return -1; 58 | } 59 | 60 | fp = fopen(pidfile, "r"); 61 | if (!fp) 62 | return -1; 63 | 64 | if (fgets(buf, sizeof(buf), fp)) { 65 | char *ptr = chomp(buf); 66 | 67 | if (ptr) { 68 | errno = 0; 69 | pid = strtoul(ptr, NULL, 0); 70 | if (errno) 71 | pid = 0; /* Failed conversion. */ 72 | } 73 | } 74 | fclose(fp); 75 | 76 | return pid; 77 | } 78 | 79 | /** 80 | * Poll for the existence of a pidfile and return PID. 81 | * @param pidfile Path to pidfile to poll for 82 | * 83 | * This function polls for the pidfile at @p pidfile for at most 5 84 | * seconds before timing out. If the file is created within that time 85 | * span the file is read and its PID contents returned. 86 | * 87 | * @returns The PID read from @p pidfile, or zero on timeout. 88 | */ 89 | pid_t pidfile_poll(const char *pidfile) 90 | { 91 | pid_t pid = 0; 92 | int tries = 0; 93 | 94 | /* Timeout = 100 * 50ms = 5s */ 95 | while ((pid = pidfile_read(pidfile)) <= 0 && tries++ < 100) 96 | usleep(50000); /* Wait 50ms between retries */ 97 | 98 | if (pid < 0) 99 | pid = 0; 100 | 101 | return pid; 102 | } 103 | 104 | /** 105 | * Send signal to a PID and cleanup pidfile afterwards. 106 | * @param pidfile File containing PID, usually in @c /var/run/PROCNAME.pid 107 | * @param signal Signal to send to PID found in @p pidfile. 108 | * 109 | * If @p signal is any of @c SIGTERM or @c SIGKILL, or if kill(2) 110 | * returns -1, the @p pidfile is removed. 111 | * 112 | * @returns POSIX OK(0) on success, non-zero otherwise. 113 | */ 114 | int pidfile_signal(const char *pidfile, int signal) 115 | { 116 | int pid = -1, ret = -1; 117 | 118 | pid = pidfile_read(pidfile); 119 | if (pid <= 0) 120 | return 1; 121 | 122 | ret = kill(pid, signal); 123 | if (!ret && signal == SIGKILL) 124 | ret = remove(pidfile); 125 | 126 | return ret; 127 | } 128 | 129 | /** 130 | * Local Variables: 131 | * compile-command: "make V=1 -f pidfilefn.mk" 132 | * indent-tabs-mode: t 133 | * c-file-style: "linux" 134 | * End: 135 | */ 136 | -------------------------------------------------------------------------------- /src/popenf.c: -------------------------------------------------------------------------------- 1 | /* Formatted popen() 2 | * 3 | * Copyright (c) 2023 Tobias Waldekranz 4 | * Copyright (c) 2023 Joachim Wiberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * Formatted pipe stream to or from a process 32 | * @param type A popen() type, "r" or "w", can also include "e" for @c O_CLOEXEC. 33 | * @param fmt Formatted string to be composed into a shell command line. 34 | * 35 | * This function is an extension to popen(), lessening the burden of 36 | * first having to compose the filename from parts in a seprate buffer. 37 | * 38 | * @returns Upon successful completion, popenf() returns a FILE pointer. 39 | * Otherwise, @c NULL is returned and @a errno is set to indicate the 40 | * error. 41 | */ 42 | FILE *popenf(const char *type, const char *fmt, ...) 43 | { 44 | va_list ap; 45 | char *cmd; 46 | int len; 47 | 48 | va_start(ap, fmt); 49 | len = vsnprintf(NULL, 0, fmt, ap); 50 | va_end(ap); 51 | 52 | cmd = alloca(len + 1); 53 | if (!cmd) { 54 | errno = ENOMEM; 55 | return NULL; 56 | } 57 | 58 | va_start(ap, fmt); 59 | vsnprintf(cmd, len + 1, fmt, ap); 60 | va_end(ap); 61 | 62 | return popen(cmd, type); 63 | } 64 | -------------------------------------------------------------------------------- /src/procval.c: -------------------------------------------------------------------------------- 1 | /* Reading and writing values/strings to/from /proc and /sys style files 2 | * 3 | * Copyright (c) 2023 Tobias Waldekranz 4 | * Copyright (c) 2023 Joachim Wiberg 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /** 20 | * @file procval.c 21 | * @author Joachim Wiberg 22 | * @date 2023 23 | * @copyright ISC License 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "lite.h" 32 | 33 | /** 34 | * Similar to readsnf() except it takes a @a va_list argument 35 | * @param line Pointer to line buffer. 36 | * @param len Size of line buffer. 37 | * @param fmt Formatted string to be composed into a pathname. 38 | * @param ap List of variable arguemnts from va_start(). 39 | * 40 | * For details, see readsnf(). 41 | * 42 | * @returns same as readsnf(). 43 | */ 44 | char *vreadsnf(char *line, size_t len, const char *fmt, va_list ap) 45 | { 46 | va_list apc; 47 | FILE *fp; 48 | 49 | va_copy(apc, ap); 50 | fp = vfopenf("r", fmt, apc); 51 | va_end(apc); 52 | if (!fp) 53 | return NULL; 54 | 55 | if (!fgets(line, len, fp)) { 56 | fclose(fp); 57 | return NULL; 58 | } 59 | 60 | fclose(fp); 61 | return chomp(line); 62 | } 63 | 64 | 65 | /** 66 | * Read first line from a file composed from fmt and optional args. 67 | * @param line Pointer to line buffer. 68 | * @param len Size of line buffer. 69 | * @param fmt Formatted string to be composed into a pathname. 70 | * 71 | * @returns On success, this function returns the @p line read from the 72 | * file, with any trailing '\n' chomp()ed out. On error, @c NULL. 73 | */ 74 | char *readsnf(char *line, size_t len, const char *fmt, ...) 75 | { 76 | va_list ap; 77 | char *ln; 78 | 79 | va_start(ap, fmt); 80 | ln = vreadsnf(line, len, fmt, ap); 81 | va_end(ap); 82 | 83 | return ln; 84 | } 85 | 86 | /** 87 | * Write a string buffer to a file composed from fmt and optional args. 88 | * @param str Pointer to string buffer, may be multiple lines. 89 | * @param mode An fopen() mode string, e.g. "w+". 90 | * @param fmt Formatted string to be composed into a pathname. 91 | * 92 | * @returns result of operation, with @a errno set on error. 93 | */ 94 | int writesf(const char *str, const char *mode, const char *fmt, ...) 95 | { 96 | va_list ap; 97 | FILE *fp; 98 | 99 | va_start(ap, fmt); 100 | fp = vfopenf(mode, fmt, ap); 101 | va_end(ap); 102 | if (!fp) 103 | return -1; 104 | 105 | fprintf(fp, "%s\n", str); 106 | return fclose(fp); 107 | } 108 | 109 | /** 110 | * Same as readllf() except it takes a @a va_list argument. 111 | */ 112 | int vreadllf(long long *value, const char *fmt, va_list ap) 113 | { 114 | char line[0x100]; 115 | 116 | if (!vreadsnf(line, sizeof(line), fmt, ap)) 117 | return -1; 118 | 119 | errno = 0; 120 | *value = strtoll(line, NULL, 0); 121 | 122 | return errno ? -1 : 0; 123 | } 124 | 125 | /** 126 | * Read 64-bit integer value from a file composed from fmt and optional args. 127 | * @param value Pointer to where to store read 64-bit integer value. 128 | * @param mode An fopen() mode string, e.g. "w+". 129 | * @param fmt Formatted string to be composed into a pathname. 130 | * 131 | * @returns result of operation, with @a errno set on error. 132 | */ 133 | int readllf(long long *value, const char *fmt, ...) 134 | { 135 | va_list ap; 136 | int rc; 137 | 138 | va_start(ap, fmt); 139 | rc = vreadllf(value, fmt, ap); 140 | va_end(ap); 141 | 142 | return rc; 143 | } 144 | 145 | 146 | /** 147 | * Read integer value from a file composed from fmt and optional args. 148 | * @param value Pointer to where to store read integer value. 149 | * @param mode An fopen() mode string, e.g. "w+". 150 | * @param fmt Formatted string to be composed into a pathname. 151 | * 152 | * @returns result of operation, with @a errno set on error. 153 | */ 154 | int readdf(int *value, const char *fmt, ...) 155 | { 156 | long long tmp; 157 | va_list ap; 158 | int rc; 159 | 160 | va_start(ap, fmt); 161 | rc = vreadllf(&tmp, fmt, ap); 162 | va_end(ap); 163 | 164 | if (rc) 165 | return rc; 166 | 167 | if (tmp < INT_MIN || tmp > INT_MAX) { 168 | errno = ERANGE; 169 | return -1; 170 | } 171 | 172 | *value = tmp; 173 | return 0; 174 | } 175 | 176 | /** 177 | * Write 64-bit integer value to a file composed from fmt and optional args. 178 | * @param value 64-bit integer value to write. 179 | * @param mode An fopen() mode string, e.g. "w+". 180 | * @param fmt Formatted string to be composed into a pathname. 181 | * 182 | * @returns result of operation, with @a errno set on error. 183 | */ 184 | int writellf(long long value, const char *mode, const char *fmt, ...) 185 | { 186 | va_list ap; 187 | FILE *fp; 188 | 189 | va_start(ap, fmt); 190 | fp = vfopenf(mode, fmt, ap); 191 | va_end(ap); 192 | if (!fp) 193 | return -1; 194 | 195 | fprintf(fp, "%lld\n", value); 196 | return fclose(fp); 197 | } 198 | 199 | 200 | /** 201 | * Write integer value to a file composed from fmt and optional args. 202 | * @param value Integer value to write. 203 | * @param mode An fopen() mode string, e.g. "w+". 204 | * @param fmt Formatted string to be composed into a pathname. 205 | * 206 | * @returns result of operation, with @a errno set on error. 207 | */ 208 | int writedf(int value, const char *mode, const char *fmt, ...) 209 | { 210 | va_list ap; 211 | FILE *fp; 212 | 213 | va_start(ap, fmt); 214 | fp = vfopenf(mode, fmt, ap); 215 | va_end(ap); 216 | if (!fp) 217 | return -1; 218 | 219 | fprintf(fp, "%d\n", value); 220 | return fclose(fp); 221 | } 222 | 223 | 224 | /** 225 | * Local Variables: 226 | * indent-tabs-mode: t 227 | * c-file-style: "linux" 228 | * End: 229 | */ 230 | -------------------------------------------------------------------------------- /src/progress.c: -------------------------------------------------------------------------------- 1 | /* Simple termios based progress bar 2 | * 3 | * Copyright (c) 2012-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file progress.c 20 | * @author Joachim Wiberg 21 | * @date 2012-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include /* INT_MAX */ 26 | #include 27 | #include 28 | 29 | #include "conio.h" 30 | 31 | #define SPINNER_THROB ".oOo" 32 | #define SPINNER_PULSAR ".oO°Oo." 33 | #define SPINNER_ARROW "v<^>" 34 | #define SPINNER_STAR ".oO@*" 35 | #define SPINNER_DEFAULT "|/-\\" 36 | 37 | static char spinner(char *style) 38 | { 39 | size_t num; 40 | static unsigned int i = 0; 41 | 42 | if (!style) 43 | style = SPINNER_DEFAULT; 44 | num = strlen(style); 45 | 46 | return style[i++ % num]; /* % Number of states in style */ 47 | } 48 | 49 | /** 50 | * Advanced ASCII progress bar with spinner 51 | * @param percent Start first call with this set to 0, end with 100 52 | * @param max_width Max width of progress bar, in total characters. 53 | * 54 | * This function draws an advanced ASCII progressbar at the current 55 | * line. It always start from the first column. 56 | * 57 | * The progress bar will hide the cursor if started with @p percent 0 58 | * and show it again at the end, when called with @p percent 100. 59 | * 60 | * While being called with the same percentage the spinner will spin, 61 | * to show the user the process hasn't frozen. 62 | * 63 | * If the output TTY cannot interpret control characters, like `\r`, it 64 | * is advised to instead used the progress_simple() function. 65 | */ 66 | void progress(int percent, int max_width) 67 | { 68 | int i, bar; 69 | 70 | /* Adjust for progress bar overhead */ 71 | max_width -= 10; 72 | 73 | if (0 == percent) 74 | hidecursor(); 75 | 76 | fprintf(stderr, "\r%3d%% %c [", percent, spinner(NULL)); 77 | 78 | bar = percent * max_width / 100; 79 | for (i = 0; i < max_width; i++) { 80 | if (i > bar) 81 | fputc(' ', stderr); 82 | else if (i == bar) 83 | fputc('>', stderr); 84 | else 85 | fputc('=', stderr); 86 | } 87 | 88 | fprintf(stderr, "]"); 89 | if (100 == percent) { 90 | showcursor(); 91 | fputc('\n', stderr); 92 | } 93 | } 94 | 95 | /** 96 | * Alternative progress bar on systems where progress() doesn't work 97 | * @param percent Start first call with this set to 0, end with 100 98 | */ 99 | void progress_simple(int percent) 100 | { 101 | static int last = 1; 102 | int ratio, numout; 103 | 104 | if (!percent && last) { 105 | last = 0; 106 | fputs("0% 25% 50% 75% 100%\n" 107 | "|---------+---------+---------+---------|\n" 108 | "|", stderr); 109 | return; 110 | } 111 | 112 | ratio = 40 * percent / 100; 113 | numout = ratio - last; 114 | 115 | if (ratio <= last) 116 | return; 117 | 118 | last = ratio; 119 | 120 | while (numout--) { 121 | if (ratio != 40 || numout) 122 | putc('=', stderr); 123 | else 124 | putc('|', stderr); 125 | } 126 | } 127 | 128 | /** 129 | * Local Variables: 130 | * indent-tabs-mode: t 131 | * c-file-style: "linux" 132 | * End: 133 | */ 134 | -------------------------------------------------------------------------------- /src/reallocarray.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ 2 | /* 3 | * Copyright (c) 2008 Otto Moerbeek 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file reallocarray.c 20 | * @author Otto Moerbeek 21 | * @date 2008 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* 31 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 32 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 33 | */ 34 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 35 | 36 | /** 37 | * Similar to realloc() but for an array of items, like calloc() 38 | * @param optr Pointer to old (current) array 39 | * @param nmemb Number of elements 40 | * @param size Size of each element, in bytes 41 | * 42 | * @returns A pointer to the new array, or @c NULL on error. 43 | */ 44 | void *reallocarray(void *optr, size_t nmemb, size_t size) 45 | { 46 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 47 | nmemb > 0 && SIZE_MAX / nmemb < size) { 48 | errno = ENOMEM; 49 | return NULL; 50 | } 51 | return realloc(optr, size * nmemb); 52 | } 53 | -------------------------------------------------------------------------------- /src/rsync.c: -------------------------------------------------------------------------------- 1 | /* Micro "rsync" implementation. 2 | * 3 | * Copyright (c) 2011-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file rsync.c 20 | * @author Joachim Wiberg 21 | * @date 2011-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include /* AT_* macros */ 27 | #include /* NULL, free() */ 28 | #include /* strlen() */ 29 | #include /* rindex() */ 30 | #include 31 | #include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ 32 | #include 33 | #include 34 | 35 | #include "lite.h" 36 | 37 | static int copy(char *src, char *dst, int opt); 38 | static int mdir(char *buf, size_t buf_len, char *dir, char *name, struct stat *st); 39 | static int prune(char *dst, char **new_files, int new_num); 40 | static int set_mtime(char *fn, struct stat *st); 41 | 42 | 43 | /** 44 | * Synchronize contents and optionally remove non-existing backups 45 | * @param src Source directory 46 | * @param dst Destination directory 47 | * @param opt An option mask of ::LITE_FOPT_RSYNC_DELETE, ::LITE_FOPT_KEEP_MTIME 48 | * @param filter Optional filtering function for source directory. 49 | * 50 | * This is a miniature implementation of the famous rsync for local use 51 | * only. In fact, it is not even a true rsync since it copies all files 52 | * from @p src to @p dst. The ::LITE_FOPT_RSYNC_DELETE @p opt flag is 53 | * useful for creating backups, when set all files removed from src 54 | * since last backup are pruned from the destination (backup) directory. 55 | * 56 | * The @p opt parameter to rsync() is an option mask for the most common 57 | * rsync(1) options. Previously this argument was called @p delete and 58 | * to maintain backwards compatibility the value 1 is reserved: 59 | * 60 | * %LITE_FOPT_RSYNC_DELETE: Prune files from @p dst that no longer exist in @p src. 61 | * %LITE_FOPT_KEEP_MTIME: Preserve modification time 62 | * 63 | * The filter callback, @p filter, if provided, is used to determine 64 | * what files to include from the source directory when backing up. If 65 | * a file is to be skipped the callback should simply return zero. 66 | * 67 | * Returns: 68 | * POSIX OK(0), or non-zero with @a errno set on error. 69 | */ 70 | int rsync(char *src, char *dst, int opt, int (*filter)(const char *file)) 71 | { 72 | char source[256]; 73 | char dest[256]; 74 | int delete = (opt & LITE_FOPT_RSYNC_DELETE) != 0; 75 | int keep_mtim = (opt & LITE_FOPT_KEEP_MTIME); 76 | int i = 0, num = 0, result = 0, do_mtim = 0; 77 | char **files; /* Array of file names. */ 78 | struct stat st; 79 | 80 | errno = 0; 81 | 82 | if (stat(dst, &st) && fisslashdir(dst)) 83 | makedir(dst, 0755); 84 | 85 | if (!fisdir(src)) { 86 | if (!fexist(src)) 87 | return 1; 88 | 89 | if (copy(src, dst, keep_mtim)) 90 | return 1; 91 | 92 | return 0; 93 | } 94 | 95 | /* Copy dir as well? */ 96 | if (!fisslashdir(src)) { 97 | char *ptr = rindex(src, '/'); 98 | 99 | if (!ptr) 100 | ptr = src; 101 | else 102 | ptr++; 103 | 104 | if (stat(src, &st)) 105 | return 1; 106 | 107 | if (mdir(dest, sizeof(dest), dst, ptr, &st)) 108 | return 1; 109 | dst = dest; 110 | do_mtim = keep_mtim; 111 | } 112 | 113 | num = dir(src, "", filter, &files, 0); 114 | for (i = 0; i < num; i++) { 115 | /* Recursively copy sub-directries */ 116 | snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]); 117 | if (fisdir(source)) { 118 | char dst2[256]; 119 | struct stat sb; 120 | 121 | strcat(source, "/"); 122 | if (stat(source, &sb)) { 123 | result++; 124 | continue; 125 | } 126 | 127 | if (mdir(dst2, sizeof(dst2), dst, files[i], &sb)) { 128 | result++; 129 | continue; 130 | } 131 | 132 | rsync(source, dst2, opt, filter); 133 | if (keep_mtim) 134 | set_mtime(dst2, &sb); 135 | 136 | continue; /* Next file/dir in @src to copy... */ 137 | } 138 | 139 | if (copy(source, dst, keep_mtim)) 140 | result++; 141 | } 142 | 143 | if (do_mtim) 144 | set_mtime(dest, &st); 145 | 146 | /* We ignore any errors from the pruning, that phase albeit useful is only 147 | * cosmetic. --Jocke 2011-03-24 */ 148 | if (delete) 149 | prune(dst, files, num); 150 | 151 | if (num) { 152 | for (i = 0; i < num; i++) 153 | free(files[i]); 154 | free(files); 155 | } 156 | 157 | return result; 158 | } 159 | 160 | static int copy(char *src, char *dst, int opt) 161 | { 162 | copyfile(src, dst, 0, opt | LITE_FOPT_COPYFILE_SYM); 163 | if (errno) { 164 | if (errno != EEXIST) 165 | return 1; 166 | 167 | errno = 0; 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | /* Creates dir/name @mode ... skipping / if dir already ends so. */ 174 | static int mdir(char *buf, size_t buf_len, char *dir, char *name, struct stat *st) 175 | { 176 | snprintf(buf, buf_len, "%s%s%s", dir, fisslashdir(dir) ? "" : "/", name); 177 | if (mkdir(buf, st->st_mode)) { 178 | if (EEXIST != errno) 179 | return 1; 180 | 181 | errno = 0; 182 | } 183 | 184 | return 0; 185 | } 186 | 187 | 188 | static int find(char *file, char **files, int num) 189 | { 190 | int n; 191 | 192 | for (n = 0; n < num; n++) 193 | if (!strncmp (files[n], file, MAX(strlen(files[n]), strlen(file)))) 194 | return 1; 195 | 196 | return 0; 197 | } 198 | 199 | 200 | /* Prune old files, no longer existing on source, from destination directory. */ 201 | static int prune(char *dst, char **new_files, int new_num) 202 | { 203 | int num, result = 0; 204 | char **files; 205 | 206 | num = dir(dst, "", NULL, &files, 0); 207 | if (num) { 208 | int i; 209 | 210 | for (i = 0; i < num; i++) { 211 | if (!find(files[i], new_files, new_num)) { 212 | char *name; 213 | size_t len = strlen(files[i]) + 2 + strlen(dst); 214 | 215 | name = malloc(len); 216 | if (name) { 217 | snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]); 218 | if (remove(name)) 219 | result++; 220 | free(name); 221 | } 222 | } 223 | free(files[i]); 224 | } 225 | free(files); 226 | } 227 | 228 | return result; 229 | } 230 | 231 | static int set_mtime(char *fn, struct stat *st) 232 | { 233 | struct timespec tv[2]; 234 | 235 | tv[0] = st->st_atim; 236 | tv[1] = st->st_mtim; 237 | 238 | return utimensat(AT_FDCWD, fn, tv, AT_SYMLINK_NOFOLLOW); 239 | } 240 | 241 | /** 242 | * Local Variables: 243 | * indent-tabs-mode: t 244 | * c-file-style: "linux" 245 | * End: 246 | */ 247 | -------------------------------------------------------------------------------- /src/runbg.c: -------------------------------------------------------------------------------- 1 | /* Run a command in the background: fork() + usleep() + execvp() 2 | * 3 | * Copyright (c) 2023 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file runbg.c 20 | * @author Joachim Wiberg 21 | * @date 2023 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * Run command in the background 32 | * @param cmd NULL terminated list of command and optional arguments 33 | * @param delay Microseconds after fork() to wait before calling cmd 34 | * 35 | * This function forks to run the given command @p cmd in the background. 36 | * To ensure there are no lingering zombies the function actuall forks 37 | * twice, and also calls setsid(), before it calls usleep() with the @p 38 | * delay argument. The command @p cmd is handed over to execvp(). 39 | * 40 | * Since it runs in the background it is not possible to get the return 41 | * code of the command. 42 | * 43 | * @returns on successful (first) fork(), this function returns POSIX 44 | * OK(0), otherwise -1 and @a errno is set to indicate the error. 45 | */ 46 | int runbg(char *const cmd[], int delay) 47 | { 48 | int pid = fork(); 49 | int rc; 50 | 51 | if (!pid) { 52 | int fd, maxfd; 53 | 54 | if (setsid() == -1) 55 | _exit(errno); 56 | 57 | /* reparent to init */ 58 | pid = fork(); 59 | if (pid == -1) 60 | _exit(errno); 61 | if (pid > 0) 62 | _exit(0); 63 | 64 | maxfd = sysconf(_SC_OPEN_MAX); 65 | if (maxfd == -1) 66 | maxfd = 8192; 67 | for (fd = 0; fd < maxfd; fd++) 68 | close(fd); 69 | 70 | usleep(delay); 71 | _exit(execvp(cmd[0], cmd)); 72 | } 73 | 74 | if (pid == -1) 75 | return -1; 76 | 77 | if (waitpid(pid, &rc, 0) == -1) 78 | return -1; 79 | 80 | if (WIFEXITED(rc)) { 81 | errno = WEXITSTATUS(rc); 82 | if (errno) 83 | rc = -1; 84 | else 85 | rc = 0; 86 | } else if (WIFSIGNALED(rc)) { 87 | errno = EINTR; 88 | rc = -1; 89 | } else 90 | rc = -1; 91 | 92 | return rc; 93 | } 94 | -------------------------------------------------------------------------------- /src/strdupa.h: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * strdupa.h - Re-implementation of glibc strdupa. 3 | * -------------------------------------------------------------------------- 4 | * Copyright (c) 2009 William Ahern 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the 12 | * following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * ========================================================================== 25 | */ 26 | 27 | /** 28 | * @file strdupa.h 29 | * @author William Ahern 30 | * @date 2009 31 | * @copyright MIT License 32 | */ 33 | 34 | #ifdef __cplusplus 35 | extern "C" 36 | { 37 | #endif 38 | 39 | #ifndef LIBITE_STRDUPA_H 40 | #define LIBITE_STRDUPA_H 41 | 42 | #if !defined(HAVE_STRDUPA) 43 | #if defined(strdupa) 44 | #define HAVE_STRDUPA 1 45 | #endif 46 | #endif 47 | 48 | #if !HAVE_STRDUPA 49 | #if defined(__GNUC__) 50 | #include /* size_t */ 51 | #include /* memcpy(3) strlen(3) */ 52 | 53 | /** 54 | * Duplicate string on stack. 55 | * @param src string to duplicate 56 | * @returns the result of memcpy(3) 57 | */ 58 | #define strdupa(src) (__extension__ ({ \ 59 | size_t len_ = strlen(src); \ 60 | char *dst_ = __builtin_alloca(len_ + 1); \ 61 | dst_[len_] = '\0'; \ 62 | (char *)memcpy(dst_, src, len_); \ 63 | })) 64 | 65 | #else /* If not GCC, e.g. Clang */ 66 | #error strdupa() may use an unsupported GNU C API, please forward any fix to maintainer, cheers! 67 | #endif /* __GNUC__ */ 68 | #endif /* !HAVE_STRDUPA */ 69 | 70 | #endif /* LIBITE_STRDUPA_H */ 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | -------------------------------------------------------------------------------- /src/strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /** 20 | * @file strlcat.c 21 | * @author Todd C. Miller 22 | * @date 1998, 2015 23 | * @copyright ISC License 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #ifndef strlcat 30 | /** 31 | * Safe version of strncat() from OpenBSD 32 | * @param dst Destination string 33 | * @param src Source string 34 | * @param dsize Total maximum size of @p dst 35 | * 36 | * Appends @p src to string @p dst of size @p dsize (unlike strncat(), 37 | * @p dsize is the full size of @p dst, not space left). At most 38 | * dsize-1 characters will be copied. Always NUL terminates (unless 39 | * dsize <= strlen(dst)). 40 | * 41 | * @returns strlen(src) + MIN(dsize, strlen(initial dst)). 42 | * If retval >= dsize, truncation occurred. 43 | */ 44 | size_t 45 | strlcat(char *dst, const char *src, size_t dsize) 46 | { 47 | const char *odst = dst; 48 | const char *osrc = src; 49 | size_t n = dsize; 50 | size_t dlen; 51 | 52 | /* Find the end of dst and adjust bytes left but don't go past end. */ 53 | while (n-- != 0 && *dst != '\0') 54 | dst++; 55 | dlen = dst - odst; 56 | n = dsize - dlen; 57 | 58 | if (n-- == 0) 59 | return(dlen + strlen(src)); 60 | while (*src != '\0') { 61 | if (n != 0) { 62 | *dst++ = *src; 63 | n--; 64 | } 65 | src++; 66 | } 67 | *dst = '\0'; 68 | 69 | return(dlen + (src - osrc)); /* count does not include NUL */ 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /src/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /** 20 | * @file strlcpy.c 21 | * @author Todd C. Miller 22 | * @date 1998, 2015 23 | * @copyright ISC License 24 | */ 25 | 26 | 27 | #include 28 | #include 29 | 30 | #ifndef strlcpy 31 | /** 32 | * Safe version of strncpy() from OpenBSD 33 | * @param dst Destination string 34 | * @param src Source string 35 | * @param dsize Total maximum size of @p dst 36 | * 37 | * This function copies string @p src to buffer @p dst of size @p dsize 38 | * bytes. At most dsize-1 chars will be copied. Always NUL terminates 39 | * (unless dsize==0). 40 | * 41 | * @returns strlen(src); if retval >= dsize, truncation occurred. 42 | */ 43 | size_t 44 | strlcpy(char *dst, const char *src, size_t dsize) 45 | { 46 | const char *osrc = src; 47 | size_t nleft = dsize; 48 | 49 | /* Copy as many bytes as will fit. */ 50 | if (nleft != 0) { 51 | while (--nleft != 0) { 52 | if ((*dst++ = *src++) == '\0') 53 | break; 54 | } 55 | } 56 | 57 | /* Not enough room in dst, add NUL and traverse rest of src. */ 58 | if (nleft == 0) { 59 | if (dsize != 0) 60 | *dst = '\0'; /* NUL-terminate dst */ 61 | while (*src++) 62 | ; 63 | } 64 | 65 | return(src - osrc - 1); /* count does not include NUL */ 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /src/strlite.h: -------------------------------------------------------------------------------- 1 | /* Collection of frog DNA 2 | * 3 | * Copyright (c) 2008-2021 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * @file strlite.h 26 | * @author Joachim Wiberg 27 | * @date 2008-2021 28 | * @copyright MIT License 29 | */ 30 | 31 | #ifdef __cplusplus 32 | extern "C" 33 | { 34 | #endif 35 | 36 | #ifndef LIBITE_STRING_H_ 37 | #define LIBITE_STRING_H_ 38 | 39 | #include /* uint8_t, uint16_t, uint32_t, INT32_MAX, etc. */ 40 | #include 41 | #include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ 42 | 43 | #include "strdupa.h" 44 | #include "strndupa.h" 45 | #include "strnlen.h" 46 | 47 | #ifndef min 48 | /** Geneirc min() macro, if a < b => a, else b */ 49 | #define min(a,b) \ 50 | ({ \ 51 | __typeof__ (a) _a = (a); \ 52 | __typeof__ (b) _b = (b); \ 53 | _a < _b ? _a : _b; \ 54 | }) 55 | #endif 56 | #ifndef max 57 | /** Geneirc max() macro, if a > b => a, else b */ 58 | #define max(a,b) \ 59 | ({ \ 60 | __typeof__ (a) _a = (a); \ 61 | __typeof__ (b) _b = (b); \ 62 | _a > _b ? _a : _b; \ 63 | }) 64 | #endif 65 | 66 | int strnmatch (const char *str, const char **list, size_t num); 67 | int strmatch (const char *str, const char **list); 68 | 69 | #ifndef strlcpy 70 | size_t strlcpy (char *dst, const char *src, size_t siz); 71 | #endif 72 | #ifndef strlcat 73 | size_t strlcat (char *dst, const char *src, size_t siz); 74 | #endif 75 | #ifndef strtonum 76 | long long strtonum (const char *numstr, long long minval, long long maxval, const char **errstrp); 77 | #endif 78 | 79 | char *strtrim (char *str); 80 | 81 | /** 82 | * Convert string to natural number (0-2147483647) 83 | * @param str string to convert to number. 84 | * @returns -1 on error. 85 | */ 86 | static inline int atonum(const char *str) 87 | { 88 | int val = -1; 89 | const char *errstr; 90 | 91 | if (str) { 92 | val = strtonum(str, 0, INT32_MAX, &errstr); 93 | if (errstr) 94 | return -1; 95 | } 96 | 97 | return val; 98 | } 99 | 100 | /** 101 | * Validate string, non NULL and not zero length 102 | * @param str string to validate 103 | * @returns @c TRUE(1) or @c FALSE(0). 104 | */ 105 | static inline int string_valid(const char *str) 106 | { 107 | return str && strlen(str); 108 | } 109 | 110 | /** 111 | * Relaxed comparison, e.g., sys_string_match("small", "smaller") => TRUE 112 | * @param a first string 113 | * @param b second string 114 | * @returns @c TRUE(1) or @c FALSE(0). 115 | */ 116 | static inline int string_match(const char *a, const char *b) 117 | { 118 | size_t min = MIN(strlen(a), strlen(b)); 119 | 120 | return !strncasecmp(a, b, min); 121 | } 122 | 123 | /** 124 | * Strict comparison, e.g., sys_string_match("small", "smaller") => FALSE 125 | * @param a first string 126 | * @param b second string 127 | * @returns @c TRUE(1) or @c FALSE(0). 128 | */ 129 | static inline int string_compare(const char *a, const char *b) 130 | { 131 | return strlen(a) == strlen(b) && !strcmp(a, b); 132 | } 133 | 134 | /** 135 | * Strict comparison, like sys_string_compare(), but case insensitive, 136 | * e.g., sys_string_match("small", "SmAlL") => TRUE 137 | * @param a first string 138 | * @param b second string 139 | * @returns @c TRUE(1) or @c FALSE(0). 140 | */ 141 | static inline int string_case_compare(const char *a, const char *b) 142 | { 143 | return strlen(a) == strlen(b) && !strcasecmp(a, b); 144 | } 145 | 146 | #endif /* LIBITE_STRING_H_ */ 147 | 148 | #ifdef __cplusplus 149 | } 150 | #endif 151 | -------------------------------------------------------------------------------- /src/strmatch.c: -------------------------------------------------------------------------------- 1 | /* Simple string matcher function, finds partial matches. 2 | * 3 | * Copyright (c) 2009-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file strmatch.c 20 | * @author Joachim Wiberg 21 | * @date 2009-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | 29 | /** 30 | * Finds matching strings from a finite list 31 | * @param str String to look for 32 | * @param list List of strings to search. 33 | * @param num Number of entries in @p list. 34 | * 35 | * This function searches the @p list of strings for @p str. If a 36 | * (partial) match is found it returns the index in the @p list. 37 | * 38 | * Very similar in function to strmatch(), but works for sets of strings 39 | * that are not @c NUL terminated. 40 | * 41 | * @returns -1 on error, otherwise the index to the matching string. 42 | */ 43 | int strnmatch(const char *str, const char **list, size_t num) 44 | { 45 | size_t i; 46 | 47 | if (!str || !list) { 48 | errno = EINVAL; 49 | return -1; 50 | } 51 | 52 | for (i = 0; i < num; i++) { 53 | if (!strncasecmp (str, list[i], strlen (str))) 54 | return i; 55 | } 56 | 57 | errno = ENOENT; 58 | return -1; 59 | } 60 | 61 | /** 62 | * Finds matching strings from a list 63 | * @param str String to look for. 64 | * @param list NUL terminated list of strings to search. 65 | * 66 | * This function searches the @p list of strings for @p str. If a 67 | * (partial) match is found it returns the index in the @p list. 68 | * 69 | * Please note, the @p list MUST be terminated by a NUL element. If 70 | * that is not possible, we recommend using strnmatch() instead. 71 | * 72 | * @returns -1 on error, otherwise the index to the matching string. 73 | */ 74 | int strmatch(const char *str, const char **list) 75 | { 76 | size_t i; 77 | 78 | if (!list) { 79 | errno = EINVAL; 80 | return -1; 81 | } 82 | 83 | for (i = 0; list[i]; i++) 84 | ; 85 | 86 | return strnmatch(str, list, i); 87 | } 88 | 89 | /** 90 | * Local Variables: 91 | * indent-tabs-mode: t 92 | * c-file-style: "linux" 93 | * End: 94 | */ 95 | -------------------------------------------------------------------------------- /src/strndupa.h: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * strndupa.h - Re-implementation of glibc strndupa. 3 | * -------------------------------------------------------------------------- 4 | * Copyright (c) 2009 William Ahern 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the 12 | * following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * ========================================================================== 25 | */ 26 | 27 | /** 28 | * @file strndupa.h 29 | * @author William Ahern 30 | * @date 2009 31 | * @copyright MIT License 32 | */ 33 | 34 | #ifdef __cplusplus 35 | extern "C" 36 | { 37 | #endif 38 | 39 | #ifndef LIBITE_STRNDUPA_H_ 40 | #define LIBITE_STRNDUPA_H_ 41 | 42 | #if !defined(HAVE_STRNDUPA) 43 | #if defined(strndupa) 44 | #define HAVE_STRNDUPA 1 45 | #endif 46 | #endif 47 | 48 | #if !HAVE_STRNDUPA 49 | #if defined(__GNUC__) 50 | #include /* size_t */ 51 | #include /* memcpy(3) */ 52 | #include "strnlen.h" 53 | 54 | /** 55 | * Duplicate part of string on stack 56 | * @param src string to duplicate 57 | * @param lim number of bytes to dupliate 58 | * @returns the result of memcpy(3) 59 | */ 60 | #define strndupa(src, lim) (__extension__ ({ \ 61 | size_t len_ = strnlen(src, lim); \ 62 | char *dst_ = __builtin_alloca(len_ + 1); \ 63 | dst_[len_] = '\0'; \ 64 | (char *)memcpy(dst_, src, len_); \ 65 | })) 66 | 67 | #else /* If not GCC, e.g. Clang */ 68 | +#error strndupa() may use an unsupported GNU C API, please forward any fix to maintainer, cheers! 69 | #endif /* __GNUC__ */ 70 | #endif /* !HAVE_STRNDUPA */ 71 | 72 | #endif /* LIBITE_STRNDUPA_H */ 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /src/strnlen.h: -------------------------------------------------------------------------------- 1 | /* strnlen.h - Re-implementation of GLIBC strnlen() 2 | * 3 | * Copyright (c) 2016-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file strnlen.h 20 | * @author Joachim Wiberg 21 | * @date 2016-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" 27 | { 28 | #endif 29 | 30 | #ifndef LIBITE_STRNLEN_H_ 31 | #define LIBITE_STRNLEN_H_ 32 | 33 | #if !defined(HAVE_STRNLEN) 34 | #if defined(strnlen) 35 | #define HAVE_STRNLEN 1 36 | #endif 37 | #endif 38 | 39 | #if !HAVE_STRNLEN 40 | #include /* size_t */ 41 | 42 | #define strnlen(str, lim) xstrnlen(str, lim) /**< Wrapper for xstrnlen() */ 43 | 44 | /** 45 | * Reimplementation of GLIBC strnlen() 46 | * @param str string to return length of 47 | * @param lim max number of bytes to read 48 | * @returns length of @p str in bytes, but at most @lim bytes. 49 | */ 50 | static inline size_t xstrnlen(const char *str, size_t lim) 51 | { 52 | size_t i = 0; 53 | 54 | while (i < lim && str[i]) 55 | i++; 56 | 57 | return i; 58 | } 59 | #endif 60 | 61 | #endif /* LIBITE_STRNLEN_H_ */ 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /src/strtonum.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2004 Ted Unangst and Todd Miller 5 | * All rights reserved. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | /** 21 | * @file strtonum.c 22 | * @author Ted Unangst 23 | * @author Todd Miller 24 | * @date 2004 25 | * @copyright ISC License 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef strtonum 33 | #define INVALID 1 /**< internal */ 34 | #define TOOSMALL 2 /**< internal */ 35 | #define TOOLARGE 3 /**< internal */ 36 | 37 | #ifndef LLONG_MAX 38 | # define LLONG_MAX 0x7fffffffffffffffLL /**< internal */ 39 | #endif 40 | 41 | #ifndef LLONG_MIN 42 | # define LLONG_MIN (-0x7fffffffffffffffLL - 1) /**< internal */ 43 | #endif 44 | 45 | /** 46 | * Reliably convert string value to an integer 47 | * @param numstr String to convert to a number 48 | * @param minval Lower bound to check number against 49 | * @param maxval Upper bound to check number against 50 | * @param errstrp Pointer to error string 51 | * 52 | * This function converts the string in @p numstr to a long long value. 53 | * The function was designed to facilitate safe, robust programming and 54 | * overcome the shortcomings of the atoi(3) and strtol(3) family of 55 | * interfaces. 56 | * 57 | * The string may begin with an arbitrary amount of whitespace (as 58 | * determined by isspace(3)) followed by a single optional ‘+’ or ‘-’ 59 | * sign. 60 | * 61 | * The remainder of the string is converted to a long long value 62 | * according to base 10. 63 | * 64 | * The value obtained is then checked against the provided @p minval and 65 | * @p maxval bounds. If @p errstrp is non-NULL, strtonum() stores an 66 | * error string in @p *errstrp indicating the failure. 67 | * 68 | * @returns The result of the conversion, unless the value would exceed 69 | * the provided bounds or is invalid. On error, 0 is returned, @a errno 70 | * is set, and @p errstrp points to an error message. @p *errstr* is set 71 | * to @c NULL on success; this fact can be used to differentiate a 72 | * successful return of 0 from an error. 73 | */ 74 | long long 75 | strtonum(const char *numstr, long long minval, long long maxval, 76 | const char **errstrp) 77 | { 78 | long long ll = 0; 79 | int error = 0; 80 | char *ep; 81 | struct errval { 82 | const char *errstr; 83 | int err; 84 | } ev[4] = { 85 | { NULL, 0 }, 86 | { "invalid", EINVAL }, 87 | { "too small", ERANGE }, 88 | { "too large", ERANGE }, 89 | }; 90 | 91 | ev[0].err = errno; 92 | errno = 0; 93 | if (minval > maxval) { 94 | error = INVALID; 95 | } else { 96 | ll = strtoll(numstr, &ep, 10); 97 | if (errno == EINVAL || numstr == ep || *ep != '\0') 98 | error = INVALID; 99 | else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 100 | error = TOOSMALL; 101 | else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 102 | error = TOOLARGE; 103 | } 104 | if (errstrp != NULL) 105 | *errstrp = ev[error].errstr; 106 | errno = ev[error].err; 107 | if (error) 108 | ll = 0; 109 | 110 | return (ll); 111 | } 112 | #endif 113 | -------------------------------------------------------------------------------- /src/strtrim.c: -------------------------------------------------------------------------------- 1 | /* Trim a string from whitespace 2 | * 3 | * Copyright (c) 2014 Mattias Walström 4 | * Copyright (c) 2021 Joachim Wiberg 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /** 20 | * @file strtrim.c 21 | * @author Mattias Walström 22 | * @author Joachim Wiberg 23 | * @date 2014,2021 24 | * @copyright ISC License 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "lite.h" 32 | 33 | /** 34 | * Strip leading and trailing whitespace from a string 35 | * @param str The string to trim 36 | * 37 | * Trims a string from any leading and trailing white-space, returns the 38 | * trimmed result in the same buffer. 39 | * 40 | * @returns If @p str is a valid, non-NULL string this function returns 41 | * the same string stripped from whitespace. This function only returns 42 | * @c NULL if @p str itself is @c NULL. 43 | */ 44 | char *strtrim(char *str) 45 | { 46 | char *start, *end; 47 | 48 | if (!str) { 49 | errno = EINVAL; 50 | return NULL; 51 | } 52 | 53 | start = str; 54 | while (isspace(*start)) 55 | start++; 56 | 57 | if (*start == 0) { 58 | str[0] = 0; 59 | return str; 60 | } 61 | 62 | end = start + strlen(start) - 1; 63 | while (end > start && isspace(*end)) 64 | end--; 65 | *(++end) = 0; 66 | 67 | memmove(str, start, end - start + 1); 68 | 69 | return str; 70 | } 71 | 72 | /** 73 | * Local Variables: 74 | * indent-tabs-mode: t 75 | * c-file-style: "linux" 76 | * End: 77 | */ 78 | -------------------------------------------------------------------------------- /src/systemf.c: -------------------------------------------------------------------------------- 1 | /* Formatted system() which returns actual return status of command 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file systemf.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /** 32 | * Like system(), but takes a formatted string as argument. 33 | * @param fmt printf style format list to command to run 34 | * 35 | * This system() wrapper greatly simplifies operations that usually 36 | * consist of composing a command from parts into a dynamic buffer 37 | * before calling it. The return value from system() is also parsed, 38 | * checking for proper exit and signals. 39 | * 40 | * @returns If the command exits normally, the return code of the command 41 | * is returned. Otherwise, if the command is signalled, the return code 42 | * is -1 and @a errno is set to @c EINTR. 43 | */ 44 | int systemf(const char *fmt, ...) 45 | { 46 | va_list ap; 47 | char *cmd; 48 | int len; 49 | int rc; 50 | 51 | va_start(ap, fmt); 52 | len = vsnprintf(NULL, 0, fmt, ap); 53 | va_end(ap); 54 | 55 | cmd = alloca(++len); 56 | if (!cmd) { 57 | errno = ENOMEM; 58 | return -1; 59 | } 60 | 61 | va_start(ap, fmt); 62 | vsnprintf(cmd, len, fmt, ap); 63 | va_end(ap); 64 | 65 | rc = system(cmd); 66 | if (rc == -1) 67 | return -1; 68 | 69 | if (WIFEXITED(rc)) { 70 | errno = 0; 71 | rc = WEXITSTATUS(rc); 72 | } else if (WIFSIGNALED(rc)) { 73 | errno = EINTR; 74 | rc = -1; 75 | } 76 | 77 | return rc; 78 | } 79 | 80 | /** 81 | * Local Variables: 82 | * indent-tabs-mode: t 83 | * c-file-style: "linux" 84 | * End: 85 | */ 86 | -------------------------------------------------------------------------------- /src/tempfile.c: -------------------------------------------------------------------------------- 1 | /* A secure tmpfile() replacement. 2 | * 3 | * Copyright (c) 2015-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file tempfile.c 20 | * @author Joachim Wiberg 21 | * @date 2015-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include /* O_TMPFILE requires -D_GNU_SOURCE */ 28 | #include /* fdopen() */ 29 | #include /* mkostemp() */ 30 | #include /* umask() */ 31 | 32 | static FILE *fallback(void) 33 | { 34 | char nm[15] = _PATH_TMP "XXXXXXXX"; 35 | int fd; 36 | 37 | fd = mkostemp(nm, O_CLOEXEC); 38 | if (-1 == fd) 39 | return NULL; 40 | 41 | return fdopen(fd, "w+"); 42 | } 43 | 44 | /** 45 | * A secure tmpfile() replacement 46 | * 47 | * This is the secure replacement for tmpfile() that does not exist in 48 | * GLIBC. It uses the Linux specific @c O_TMPFILE and @c O_EXCL to hide 49 | * the filename. When the @c FILE is fclose()'ed, the file contents is 50 | * lost. The file is hidden in the @c _PATH_TMP ("/tmp") directory in 51 | * the system. 52 | * 53 | * This function requires Linux 3.11, or later, due to @c O_TMPFILE. 54 | * Not all file systems support hidden inodes, in which case this 55 | * function defaults to call tmpfile() as a fallback. 56 | * 57 | * @returns An open @c FILE pointer, or @c NULL on error. 58 | */ 59 | FILE *tempfile(void) 60 | { 61 | #ifdef O_TMPFILE /* Only on Linux, with fairly recent (G)LIBC */ 62 | int fd; 63 | mode_t oldmask; 64 | 65 | oldmask = umask(0077); 66 | fd = open(_PATH_TMP, O_TMPFILE | O_RDWR | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR); 67 | umask(oldmask); 68 | if (-1 == fd) { 69 | if (errno == EOPNOTSUPP) 70 | return fallback(); 71 | 72 | return NULL; 73 | } 74 | 75 | return fdopen(fd, "w+"); 76 | #else 77 | return fallback(); 78 | #endif 79 | } 80 | 81 | /** 82 | * Local Variables: 83 | * indent-tabs-mode: t 84 | * c-file-style: "linux" 85 | * End: 86 | */ 87 | -------------------------------------------------------------------------------- /src/touchf.c: -------------------------------------------------------------------------------- 1 | /* Formatted touch() 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file touchf.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "lite.h" 32 | 33 | /** 34 | * Like touch() but with formatted string support 35 | * @param fmt Formatted string to be composed into a pathname 36 | * 37 | * This is a wrapper for the touch() function in lite.h, lessening the 38 | * burden of having to compose the filename from parts in a seprate 39 | * buffer. 40 | * 41 | * @returns Upon successful completion touchf() returns POSIX OK(0), 42 | * otherwise, -1 is returned and @a errno is set to indicate error. 43 | */ 44 | int touchf(const char *fmt, ...) 45 | { 46 | va_list ap; 47 | char *file; 48 | int len; 49 | 50 | va_start(ap, fmt); 51 | len = vsnprintf(NULL, 0, fmt, ap); 52 | va_end(ap); 53 | 54 | file = alloca(len + 1); 55 | if (!file) { 56 | errno = ENOMEM; 57 | return -1; 58 | } 59 | 60 | va_start(ap, fmt); 61 | vsnprintf(file, len + 1, fmt, ap); 62 | va_end(ap); 63 | 64 | return touch(file); 65 | } 66 | 67 | /** 68 | * Local Variables: 69 | * indent-tabs-mode: t 70 | * c-file-style: "linux" 71 | * End: 72 | */ 73 | -------------------------------------------------------------------------------- /src/truncatef.c: -------------------------------------------------------------------------------- 1 | /* Formatted truncate() 2 | * 3 | * Copyright (c) 2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file truncatef.c 20 | * @author Joachim Wiberg 21 | * @date 2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /** 32 | * Truncate a file based on the formatted string 33 | * @param length Size in bytes to truncate file to, zero to empty it 34 | * @param fmt Formatted string to be composed into a pathname 35 | * 36 | * This is an extension to the truncate() family, lessening the burden 37 | * of having to compose the filename from parts in a seprate buffer. 38 | * 39 | * @returns Upon successful completion truncate() returns POSIX OK(0), 40 | * otherwise, -1 is returned and @a errno is set to indicate error. 41 | */ 42 | int truncatef(off_t length, const char *fmt, ...) 43 | { 44 | va_list ap; 45 | char *file; 46 | int len; 47 | 48 | va_start(ap, fmt); 49 | len = vsnprintf(NULL, 0, fmt, ap); 50 | va_end(ap); 51 | 52 | file = alloca(len + 1); 53 | if (!file) { 54 | errno = ENOMEM; 55 | return -1; 56 | } 57 | 58 | va_start(ap, fmt); 59 | vsnprintf(file, len + 1, fmt, ap); 60 | va_end(ap); 61 | 62 | return truncate(file, length); 63 | } 64 | 65 | /** 66 | * Local Variables: 67 | * indent-tabs-mode: t 68 | * c-file-style: "linux" 69 | * End: 70 | */ 71 | -------------------------------------------------------------------------------- /src/which.c: -------------------------------------------------------------------------------- 1 | /* C implementation of UNIX which(1) 2 | * 3 | * Copyright (c) 2017-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file which.c 20 | * @author Joachim Wiberg 21 | * @date 2017-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include 27 | #include /* snprintf() */ 28 | #include /* realloc(), free() */ 29 | #include /* strtok_r() */ 30 | #include /* access() */ 31 | 32 | /* Strip any arguments in "/path/to/bin --some args", modifies input string */ 33 | static char *strip_args(char *path) 34 | { 35 | size_t i = 0; 36 | 37 | if (!path) 38 | return NULL; 39 | 40 | /* Find first whitespace (space/tab/etc.) */ 41 | while (path[i] && !isspace(path[i])) 42 | i++; 43 | path[i] = 0; 44 | 45 | return path; 46 | } 47 | 48 | /** 49 | * Like which(1), or `command -v foo` 50 | * @param cmd Command to look for in $PATH 51 | * 52 | * @returns A malloc()'ed path to @a cmd on success, or @c NULL. 53 | */ 54 | char *which(const char *cmd) 55 | { 56 | size_t pathlen = 0; 57 | char *ptr, *tok, *env, *path = NULL; 58 | 59 | if (!cmd) { 60 | errno = EINVAL; 61 | return NULL; 62 | } 63 | 64 | if (cmd[0] == '/') { 65 | path = strip_args(strdup(cmd)); 66 | if (!path) 67 | return NULL; 68 | 69 | if (!access(path, X_OK)) 70 | return path; 71 | 72 | if (path) 73 | free(path); 74 | 75 | return NULL; 76 | } 77 | 78 | ptr = getenv("PATH"); 79 | if (!ptr) 80 | return NULL; 81 | 82 | env = strdup(ptr); 83 | if (!env) 84 | return NULL; 85 | 86 | tok = strtok_r(env, ":", &ptr); 87 | while (tok) { 88 | size_t len = strlen(tok) + strlen(cmd) + 2; 89 | 90 | if (pathlen < len) { 91 | path = realloc(path, len); 92 | if (!path) { 93 | free(env); 94 | return NULL; 95 | } 96 | pathlen = len; 97 | } 98 | 99 | snprintf(path, pathlen, "%s/%s", tok, cmd); 100 | path = strip_args(path); 101 | if (!path || !access(path, X_OK)) { 102 | free(env); 103 | return path; 104 | } 105 | 106 | tok = strtok_r(NULL, ":", &ptr); 107 | } 108 | 109 | if (path) 110 | free(path); 111 | free(env); 112 | 113 | return NULL; 114 | } 115 | 116 | /** 117 | * Predicate variant of which() 118 | * @param cmd Command to look for in $PATH 119 | * 120 | * @returns @c TRUE(1) or @c FALSE(0) if @p cmd exists in $PATH. 121 | */ 122 | int whichp(const char *cmd) 123 | { 124 | char *path; 125 | 126 | path = which(cmd); 127 | if (path) { 128 | free(path); 129 | return 1; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | /** 136 | * Local Variables: 137 | * indent-tabs-mode: t 138 | * c-file-style: "linux" 139 | * End: 140 | */ 141 | -------------------------------------------------------------------------------- /src/yorn.c: -------------------------------------------------------------------------------- 1 | /* Safe yes-or-no with prompt 2 | * 3 | * Copyright (c) 2009-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /** 19 | * @file yorn.c 20 | * @author Joachim Wiberg 21 | * @date 2009-2021 22 | * @copyright ISC License 23 | */ 24 | 25 | #include 26 | #include /* __fpurge() */ 27 | #include 28 | #include 29 | #include 30 | 31 | static char rawgetch(void) 32 | { 33 | struct termios savemodes, modmodes; 34 | char val; 35 | 36 | if (!isatty(STDIN_FILENO)) 37 | return getchar(); 38 | 39 | /* Backup terminal settings. */ 40 | if (tcgetattr(STDIN_FILENO, &savemodes) < 0) { 41 | return -1; 42 | } 43 | 44 | /* "stty cbreak -echo" */ 45 | modmodes = savemodes; 46 | modmodes.c_lflag &= ~ICANON; 47 | modmodes.c_lflag &= ~ECHO; 48 | modmodes.c_cc[VMIN] = 1; 49 | modmodes.c_cc[VTIME] = 0; 50 | 51 | /* Set terminal in raw mode. */ 52 | if (tcsetattr(STDIN_FILENO, TCSANOW, &modmodes) < 0) { 53 | tcsetattr(STDIN_FILENO, TCSANOW, &savemodes); 54 | return -1; 55 | } 56 | 57 | val = getchar(); 58 | 59 | /* Restore terminal to previous state. */ 60 | tcsetattr(STDIN_FILENO, TCSANOW, &savemodes); 61 | 62 | return val; 63 | } 64 | 65 | /** 66 | * Pose a a Yes or No question and return answer 67 | * @param fmt Standard printf() style argument(s). 68 | * 69 | * This function prints the given question on screen, waits for user 70 | * input in the form of yes or no. 71 | * 72 | * @returns TRUE(1) or FALSE(0). True if the answer is yes. 73 | */ 74 | int yorn(const char *fmt, ...) 75 | { 76 | va_list ap; 77 | char yorn; 78 | 79 | va_start(ap, fmt); 80 | vfprintf(stderr, fmt, ap); 81 | va_end(ap); 82 | 83 | __fpurge(stdin); 84 | yorn = rawgetch(); 85 | printf("%c\n", yorn); 86 | fflush(stdout); 87 | if (yorn != 'y' && yorn != 'Y') 88 | return 0; 89 | 90 | return 1; 91 | } 92 | 93 | /** 94 | * Local Variables: 95 | * indent-tabs-mode: t 96 | * c-file-style: "linux" 97 | * End: 98 | */ 99 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | *.trs 4 | .deps/ 5 | /chomp 6 | /copyfile 7 | /dir 8 | /fexist 9 | /fisdir 10 | /fopenf 11 | /fsendfile 12 | /lfile 13 | /makepath 14 | /pidfile 15 | /printhdr 16 | /progress 17 | /rsync 18 | /runbg 19 | /str 20 | /strmatch 21 | /systemf 22 | /tempfile 23 | /touch 24 | /tree 25 | /which 26 | /yorn 27 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | # Ignore warnings about unused result, in e.g. write() 2 | AM_CFLAGS = -W -Wall -Wextra -Wno-unused-result 3 | AM_CPPFLAGS = -D_GNU_SOURCE 4 | AM_LDFLAGS = -L../src/ ../src/libite.la -static 5 | 6 | EXTRA_DIST = check.h data/fexist/dir/.placeholder data/fexist/executable 7 | EXTRA_DIST += data/fexist/link data/fexist/regular data/lfile/fstab 8 | EXTRA_DIST += data/lfile/group data/lfile/passwd data/lfile/protocols 9 | EXTRA_DIST += data/lfile/services data/which/executable 10 | EXTRA_DIST += data/which/executable-link data/which/regular 11 | 12 | CLEANFILES = *~ *.trs *.log 13 | 14 | TESTS_ENVIRONMENT = ([ -d data ] || ln -sf $(srcdir)/data) && 15 | 16 | clean-local: 17 | -find -type l -name data -exec rm {} \; 18 | 19 | TESTS = 20 | TESTS += chomp 21 | TESTS += copyfile 22 | TESTS += dir 23 | TESTS += fexist 24 | TESTS += fisdir 25 | TESTS += fopenf 26 | TESTS += fsendfile 27 | TESTS += lfile 28 | TESTS += makepath 29 | TESTS += pidfile 30 | TESTS += printhdr 31 | TESTS += progress 32 | TESTS += rsync 33 | TESTS += runbg 34 | TESTS += str 35 | TESTS += strmatch 36 | TESTS += systemf 37 | TESTS += touch 38 | TESTS += tempfile 39 | TESTS += which 40 | TESTS += yorn 41 | 42 | check_PROGRAMS = $(TESTS) 43 | -------------------------------------------------------------------------------- /test/check.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBLITE_TESTS_CHECK_H_ 2 | #define LIBLITE_TESTS_CHECK_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../src/conio.h" 10 | #include "../src/lite.h" 11 | 12 | #define PRINT(args...) if (verbose) printf(args) 13 | 14 | #define fail_unless(test) \ 15 | do { \ 16 | if (!(test)) { \ 17 | fprintf(stderr, \ 18 | "----------------------------------------------\n" \ 19 | "%s:%d: test FAILED:\nFailed test: %s\n" \ 20 | "----------------------------------------------\n", \ 21 | __FILE__, __LINE__, #test); \ 22 | exit(1); \ 23 | } \ 24 | } while (0) 25 | 26 | static inline int test(int result, const char *fmt, ...) 27 | { 28 | char buf[80]; 29 | size_t len; 30 | va_list ap; 31 | const char success[] = " \033[1m[ OK ]\033[0m\n"; 32 | const char failure[] = " \033[7m[FAIL]\033[0m\n"; 33 | const char dots[] = " ....................................................................."; 34 | 35 | va_start(ap, fmt); 36 | len = vsnprintf(buf, sizeof(buf), fmt, ap); 37 | va_end(ap); 38 | 39 | write(STDERR_FILENO, buf, len); 40 | write(STDERR_FILENO, dots, 60 - len); /* pad with dots. */ 41 | 42 | if (!result) 43 | write(STDERR_FILENO, success, strlen(success)); 44 | else 45 | write(STDERR_FILENO, failure, strlen(failure)); 46 | 47 | return result; 48 | } 49 | 50 | static inline int timespec_newer(const struct timespec *a, const struct timespec *b) 51 | { 52 | if (a->tv_sec != b->tv_sec) 53 | return a->tv_sec > b->tv_sec; 54 | 55 | return a->tv_nsec > b->tv_nsec; 56 | } 57 | 58 | static inline char *timespec2str(struct timespec *ts, char *buf, size_t len) 59 | { 60 | size_t ret, pos; 61 | struct tm t; 62 | 63 | memset(buf, 0, len); 64 | 65 | tzset(); 66 | if (localtime_r(&(ts->tv_sec), &t) == NULL) 67 | return buf; 68 | 69 | ret = strftime(buf, len, "%F %T", &t); 70 | if (ret == 0) 71 | return buf; 72 | len -= ret - 1; 73 | 74 | pos = strlen(buf); 75 | len -= pos; 76 | snprintf(&buf[pos], len, ".%09ld", ts->tv_nsec); 77 | 78 | return buf; 79 | } 80 | 81 | #endif /* LIBLITE_TESTS_CHECK_H_ */ 82 | -------------------------------------------------------------------------------- /test/chomp.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | int main(void) 4 | { 5 | size_t i; 6 | char t[][16] = { 7 | "hej\ndej", 8 | "Slime\n\n\\n", 9 | "Tripple\n\n\n" 10 | }; 11 | 12 | for (i = 0; i < NELEMS(t); i++) { 13 | char *str = t[i]; 14 | size_t len; 15 | 16 | chomp(str); 17 | len = strlen(str); 18 | printf("[%02zd]: '%s'\n", i, str); 19 | 20 | fail_unless(str[len] != '\n'); 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/copyfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "check.h" 4 | 5 | #define ERR(fmt, args...) { printf(fmt ": %s\n", ##args, strerror(errno)); rc = 1; } 6 | #define ERRX(fmt, args...) { printf(fmt "\n", ##args); rc = 1; } 7 | 8 | static char *files[] = { 9 | "/etc/passwd", "/tmp/mypwd", "/tmp/mypwd", 10 | "/etc/passwd", "/tmp/", "/tmp/passwd", 11 | "/etc/passwd", "/tmp", "/tmp/passwd", 12 | NULL 13 | }; 14 | 15 | static int one_sz(const char *src, const char *dst, int sz) 16 | { 17 | struct stat st; 18 | ssize_t len; 19 | int rc = 0; 20 | 21 | len = copyfile(src, dst, sz, 0); 22 | if (!len) { 23 | ERR("failed copying any bytes from %s", src); 24 | } else { 25 | if (stat(dst, &st)) 26 | ERR("copied but cannot find destination %s", dst); 27 | 28 | if (st.st_size != sz) 29 | ERR("file only %ld bytes, expected %d", st.st_size, sz); 30 | 31 | if (len != st.st_size) 32 | ERRX("copied expected data %d, but copyfile() says %ld", sz, len); 33 | } 34 | 35 | return rc; 36 | } 37 | 38 | static int _sz() 39 | { 40 | const int sz = 32768; 41 | 42 | return test(one_sz("/dev/zero", "/tmp/zeroes", sz), "copyfile(/dev/zero, %d)", sz); 43 | } 44 | 45 | static int fcopyfile_one(const char *src, const char *dst, const char *expected) 46 | { 47 | FILE *from, *to; 48 | int rc; 49 | 50 | from = fopen(src, "r"); 51 | to = fopen(dst, "w"); 52 | 53 | rc = fcopyfile(from, to); 54 | 55 | if (from) 56 | fclose(from); 57 | if (to) 58 | fclose(to); 59 | 60 | if (rc) { 61 | if (!src || !dst) 62 | rc = 0; /* expected */ 63 | else if (fisdir(dst)) 64 | rc = 0; /* expected */ 65 | } else { 66 | if (fexist(expected)) { 67 | if (systemf("diff -q %s %s", src, expected)) 68 | rc = 1; 69 | } else 70 | ERR("cannot find %s", expected); 71 | } 72 | 73 | erase(expected); 74 | 75 | return rc; 76 | } 77 | 78 | static int _fcopyfile() 79 | { 80 | int i, rc = 0; 81 | 82 | for (i = 0; files[i]; i += 3) { 83 | char *src = files[i]; 84 | char *dst = files[i + 1]; 85 | char *res = files[i + 2]; 86 | 87 | rc += test(fcopyfile_one(src, dst, res), "fcopyfile(%s, %s)", src, dst); 88 | } 89 | 90 | return rc; 91 | } 92 | 93 | /* only checks success, correct file size verified by _sz() */ 94 | static int copyfile_one(const char *src, const char *dst, const char *expected) 95 | { 96 | int rc; 97 | 98 | rc = copyfile(src, dst, 0, 0); 99 | if (!rc) 100 | rc = 1; 101 | else if (!fexist(expected)) 102 | rc = 1; 103 | else 104 | rc = 0; 105 | 106 | erase(expected); 107 | 108 | return rc; 109 | } 110 | 111 | static int _copyfile() 112 | { 113 | int i, rc = 0; 114 | 115 | for (i = 0; files[i]; i += 3) { 116 | char *src = files[i]; 117 | char *dst = files[i + 1]; 118 | char *res = files[i + 2]; 119 | 120 | rc += test(copyfile_one(src, dst, res), "copyfile(%s, %s)", src, dst); 121 | } 122 | 123 | return rc; 124 | } 125 | 126 | int main(void) 127 | { 128 | return _fcopyfile() || 129 | _copyfile() || 130 | _sz(); 131 | } 132 | -------------------------------------------------------------------------------- /test/data/fexist/broken-link: -------------------------------------------------------------------------------- 1 | nonexistence -------------------------------------------------------------------------------- /test/data/fexist/dir/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/libite/f0cb0b7bf2d6f6992485e828e8ac6cffa8596164/test/data/fexist/dir/.placeholder -------------------------------------------------------------------------------- /test/data/fexist/executable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/libite/f0cb0b7bf2d6f6992485e828e8ac6cffa8596164/test/data/fexist/executable -------------------------------------------------------------------------------- /test/data/fexist/link: -------------------------------------------------------------------------------- 1 | regular -------------------------------------------------------------------------------- /test/data/fexist/regular: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/libite/f0cb0b7bf2d6f6992485e828e8ac6cffa8596164/test/data/fexist/regular -------------------------------------------------------------------------------- /test/data/lfile/fstab: -------------------------------------------------------------------------------- 1 | UUID=xxxxxxxxxx / xfs defaults 0 1 2 | /dev/sda1 /boot/efi vfat umask=0077 0 1 3 | /dev/sr0 /media/cdrom0 udf,iso9660 user,noauto 0 0 4 | -------------------------------------------------------------------------------- /test/data/lfile/group: -------------------------------------------------------------------------------- 1 | root:x:0: 2 | daemon:x:1: 3 | bin:x:2: 4 | sys:x:3: 5 | adm:x:4: 6 | tty:x:5: 7 | disk:x:6: 8 | lp:x:7: 9 | mail:x:8: 10 | news:x:9: 11 | uucp:x:10: 12 | man:x:12: 13 | proxy:x:13: 14 | kmem:x:15: 15 | dialout:x:20: 16 | fax:x:21: 17 | voice:x:22: 18 | cdrom:x:24: 19 | floppy:x:25: 20 | tape:x:26: 21 | sudo:x:27: 22 | audio:x:29: 23 | dip:x:30: 24 | www-data:x:33: 25 | backup:x:34: 26 | operator:x:37: 27 | list:x:38: 28 | irc:x:39: 29 | src:x:40: 30 | gnats:x:41: 31 | shadow:x:42: 32 | utmp:x:43: 33 | video:x:44: 34 | sasl:x:45: 35 | plugdev:x:46: 36 | staff:x:50: 37 | games:x:60: 38 | users:x:100: 39 | nogroup:x:65534: 40 | -------------------------------------------------------------------------------- /test/data/lfile/passwd: -------------------------------------------------------------------------------- 1 | root:x:0:0:root:/root:/bin/bash 2 | daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 3 | bin:x:2:2:bin:/bin:/usr/sbin/nologin 4 | sys:x:3:3:sys:/dev:/usr/sbin/nologin 5 | sync:x:4:65534:sync:/bin:/bin/sync 6 | games:x:5:60:games:/usr/games:/usr/sbin/nologin 7 | man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 8 | lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 9 | mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 10 | news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 11 | uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 12 | proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 13 | www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 14 | backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 15 | list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 16 | irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 17 | gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 18 | nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 19 | -------------------------------------------------------------------------------- /test/data/lfile/protocols: -------------------------------------------------------------------------------- 1 | # Internet (IP) protocols 2 | # 3 | # Updated from http://www.iana.org/assignments/protocol-numbers and other 4 | # sources. 5 | # New protocols will be added on request if they have been officially 6 | # assigned by IANA and are not historical. 7 | # If you need a huge list of used numbers please install the nmap package. 8 | 9 | ip 0 IP # internet protocol, pseudo protocol number 10 | hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883] 11 | icmp 1 ICMP # internet control message protocol 12 | igmp 2 IGMP # Internet Group Management 13 | ggp 3 GGP # gateway-gateway protocol 14 | ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'') 15 | st 5 ST # ST datagram mode 16 | tcp 6 TCP # transmission control protocol 17 | egp 8 EGP # exterior gateway protocol 18 | igp 9 IGP # any private interior gateway (Cisco) 19 | pup 12 PUP # PARC universal packet protocol 20 | udp 17 UDP # user datagram protocol 21 | hmp 20 HMP # host monitoring protocol 22 | xns-idp 22 XNS-IDP # Xerox NS IDP 23 | rdp 27 RDP # "reliable datagram" protocol 24 | iso-tp4 29 ISO-TP4 # ISO Transport Protocol class 4 [RFC905] 25 | dccp 33 DCCP # Datagram Congestion Control Prot. [RFC4340] 26 | xtp 36 XTP # Xpress Transfer Protocol 27 | ddp 37 DDP # Datagram Delivery Protocol 28 | idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport 29 | ipv6 41 IPv6 # Internet Protocol, version 6 30 | ipv6-route 43 IPv6-Route # Routing Header for IPv6 31 | ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6 32 | idrp 45 IDRP # Inter-Domain Routing Protocol 33 | rsvp 46 RSVP # Reservation Protocol 34 | gre 47 GRE # General Routing Encapsulation 35 | esp 50 IPSEC-ESP # Encap Security Payload [RFC2406] 36 | ah 51 IPSEC-AH # Authentication Header [RFC2402] 37 | skip 57 SKIP # SKIP 38 | ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6 39 | ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6 40 | ipv6-opts 60 IPv6-Opts # Destination Options for IPv6 41 | rspf 73 RSPF CPHB # Radio Shortest Path First (officially CPHB) 42 | vmtp 81 VMTP # Versatile Message Transport 43 | eigrp 88 EIGRP # Enhanced Interior Routing Protocol (Cisco) 44 | ospf 89 OSPFIGP # Open Shortest Path First IGP 45 | ax.25 93 AX.25 # AX.25 frames 46 | ipip 94 IPIP # IP-within-IP Encapsulation Protocol 47 | etherip 97 ETHERIP # Ethernet-within-IP Encapsulation [RFC3378] 48 | encap 98 ENCAP # Yet Another IP encapsulation [RFC1241] 49 | # 99 # any private encryption scheme 50 | pim 103 PIM # Protocol Independent Multicast 51 | ipcomp 108 IPCOMP # IP Payload Compression Protocol 52 | vrrp 112 VRRP # Virtual Router Redundancy Protocol [RFC5798] 53 | l2tp 115 L2TP # Layer Two Tunneling Protocol [RFC2661] 54 | isis 124 ISIS # IS-IS over IPv4 55 | sctp 132 SCTP # Stream Control Transmission Protocol 56 | fc 133 FC # Fibre Channel 57 | mobility-header 135 Mobility-Header # Mobility Support for IPv6 [RFC3775] 58 | udplite 136 UDPLite # UDP-Lite [RFC3828] 59 | mpls-in-ip 137 MPLS-in-IP # MPLS-in-IP [RFC4023] 60 | manet 138 # MANET Protocols [RFC5498] 61 | hip 139 HIP # Host Identity Protocol 62 | shim6 140 Shim6 # Shim6 Protocol [RFC5533] 63 | wesp 141 WESP # Wrapped Encapsulating Security Payload 64 | rohc 142 ROHC # Robust Header Compression 65 | -------------------------------------------------------------------------------- /test/data/which/executable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/libite/f0cb0b7bf2d6f6992485e828e8ac6cffa8596164/test/data/which/executable -------------------------------------------------------------------------------- /test/data/which/executable-link: -------------------------------------------------------------------------------- 1 | executable -------------------------------------------------------------------------------- /test/data/which/regular: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/libite/f0cb0b7bf2d6f6992485e828e8ac6cffa8596164/test/data/which/regular -------------------------------------------------------------------------------- /test/dir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "check.h" 6 | 7 | #define DIR_TYPE_IMAGE ".img" 8 | #define DIR_TYPE_SYSLOG "" 9 | #define DIR_TYPE_CONFIG ".cfg" 10 | #define STARTUP_CONFIG "startup-config.cfg" 11 | 12 | int simulate_files(int creat) 13 | { 14 | size_t i; 15 | char *files[] = 16 | { "config0.cfg", "config1.cfg", "config2.cfg", "config3.cfg", 17 | "rr109.img", "rx100.img", "rx107.img", "rm957.img", "messages" 18 | }; 19 | 20 | for (i = 0; i < NELEMS(files); i++) { 21 | if (creat) 22 | touch(files[i]); 23 | else 24 | erase(files[i]); 25 | } 26 | 27 | if (creat) 28 | symlink("config2.cfg", STARTUP_CONFIG); 29 | else 30 | erase(STARTUP_CONFIG); 31 | 32 | return 0; 33 | } 34 | 35 | static int cfg_dir_filter(const char *file) 36 | { 37 | /* Skip the STARTUP_CONFIG file, it is a symbolic link to the 38 | * current startup configuration. */ 39 | return ! !strcmp(file, STARTUP_CONFIG); 40 | } 41 | 42 | static int is_startup_config(const char *entry) 43 | { 44 | static int once = 1; 45 | static char file[80]; 46 | 47 | if (once) { 48 | int len = readlink(STARTUP_CONFIG, file, sizeof(file)); 49 | 50 | if (len == -1) 51 | return 0; 52 | 53 | file[len] = 0; 54 | once = 0; /* Only once per call to dir() */ 55 | } 56 | //printf ("Comparing link %s with entry %s\n", file, entry); 57 | return !strcmp(file, entry); 58 | } 59 | 60 | int main(int argc, char *argv[]) 61 | { 62 | int i, num; 63 | char *type = DIR_TYPE_CONFIG; 64 | char **files; 65 | 66 | simulate_files(1); 67 | 68 | if (argc >= 2) { 69 | if (!strcasecmp("CONFIG", argv[1])) { 70 | type = DIR_TYPE_CONFIG; 71 | system("ls -l *" DIR_TYPE_CONFIG); 72 | } 73 | if (!strcasecmp("IMAGE", argv[1])) { 74 | type = DIR_TYPE_IMAGE; 75 | system("ls -l *" DIR_TYPE_IMAGE); 76 | } 77 | if (!strcasecmp("SYSLOG", argv[1])) { 78 | type = DIR_TYPE_SYSLOG; 79 | system("ls -l *"); 80 | } 81 | } 82 | 83 | num = dir(NULL, type, cfg_dir_filter, &files, 0); 84 | if (num) { 85 | for (i = 0; i < num; i++) { 86 | printf("%s", files[i]); 87 | if (is_startup_config(files[i])) 88 | printf(" --> startup-config"); 89 | printf("\n"); 90 | 91 | free(files[i]); 92 | } 93 | free(files); 94 | } 95 | 96 | simulate_files(0); 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /test/fexist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "check.h" 3 | 4 | int main(void) 5 | { 6 | size_t i = 0; 7 | struct { char *file; int exist; } arr[] = { 8 | { "data/fexist/regular", 1 }, 9 | { "data/fexist/executable", 1 }, 10 | { "data/fexist/link", 1 }, 11 | { "data/fexist/nonexistence", 0 }, 12 | { "data/fexist/broken-link", 0 }, 13 | { "data/fexist/dir", 1 }, 14 | { "/dev/null", 1 }, 15 | { NULL, 0 }, 16 | }; 17 | 18 | for (i = 0; i < NELEMS(arr); i++) { 19 | if (fexist(arr[i].file) != arr[i].exist) 20 | err(1, "Failed fexist(%s)", arr[i].file ?: "NULL"); 21 | else 22 | printf("File %-11s %-14s => OK!\n", arr[i].file ?: "NULL", 23 | arr[i].exist ? "does exist" : "does not exist"); 24 | } 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /test/fisdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "check.h" 3 | 4 | int main(void) 5 | { 6 | size_t i = 0; 7 | struct { char *file; int exist; } arr[] = { 8 | { "/etc/passwd", 0 }, 9 | { "/etc/", 1 }, 10 | { "/sbin/init", 0 }, 11 | { "/dev/null", 0 }, 12 | { "/dev/", 1 }, 13 | { NULL, 0 }, 14 | }; 15 | 16 | for (i = 0; i < NELEMS(arr); i++) { 17 | if (fisdir(arr[i].file) != arr[i].exist) 18 | err(1, "Failed fisdir(%s)", arr[i].file ?: "NULL"); 19 | else 20 | printf("fisdir() %-11s %-18s => OK!\n", arr[i].file ?: "NULL", 21 | arr[i].exist ? "is a directory" : "is NOT a directory"); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /test/fopenf.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | int main(void) 4 | { 5 | FILE *fp; 6 | char *dir = "/tmp"; 7 | char *file = "__foo__"; 8 | 9 | printf("Pass 1/2: fopenf() ===========================\n"); 10 | fp = fopenf("w", "%s/%s", dir, file); 11 | if (fp) { 12 | fputs("foo", fp); 13 | fclose(fp); 14 | } 15 | 16 | fail_unless(fexist("/tmp/__foo__")); 17 | 18 | printf("Pass 2/2: fremove() ==========================\n"); 19 | fremove("%s/%s", dir, file); 20 | fail_unless(!fexist("/tmp/__foo__")); 21 | 22 | printf("DONE =========================================\n"); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /test/fsendfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "check.h" 4 | 5 | int main(void) 6 | { 7 | int i = 0; 8 | char *files[] = { 9 | "/etc/passwd", "/tmp/tok", 10 | "/etc/passwd", "/tmp/passwd", 11 | "/etc/passwd", "/tmp/passwd", 12 | NULL 13 | }; 14 | FILE *src, *dst; 15 | 16 | while (files[i]) { 17 | src = fopen(files[i], "r"); 18 | dst = fopen(files[i + 1], "w"); 19 | printf("fsendfile(%s, %s, 512)\t", files[i], files[i + 1]); 20 | if (-1 == fsendfile(src, dst, 512)) 21 | err(1, "Failed fsendfile(%s, %s)", files[i], files[i + 1]); 22 | 23 | if (!access(files[i + 1], F_OK)) 24 | printf("OK => %s\n", files[i + 1]); 25 | 26 | remove(files[i + 1]); 27 | i += 2; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /test/lfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "check.h" 3 | 4 | #define FSTAB "data/lfile/fstab" 5 | 6 | static int fstab(void) 7 | { 8 | int field = 1; 9 | char *token; 10 | lfile_t *lf; 11 | 12 | lf = lfopen(FSTAB, " \t\n"); 13 | if (!lf) { 14 | perror("Failed opening " FSTAB); 15 | return 1; 16 | } 17 | 18 | printheader(NULL, "FILE SYSTEM MOUNT OPTIONS D P", 0); 19 | while ((token = lftok(lf))) { 20 | if (field == 1) 21 | printf("%-41s ", token); 22 | if (field == 2) 23 | printf("%-9s ", token); 24 | if (field == 4) 25 | printf("%-17s ", token); 26 | if (field == 5) 27 | printf("%d ", atoi(token)); 28 | if (field == 6) { 29 | field = 0; 30 | printf("%d\n", atoi(token)); 31 | } 32 | 33 | field++; 34 | } 35 | 36 | lfclose(lf); 37 | 38 | return 0; 39 | } 40 | 41 | int main(void) 42 | { 43 | int val; 44 | 45 | val = fgetint("data/lfile/protocols", " \n\t", "udp"); 46 | if (val == -1) { 47 | perror("Failed locating 'udp' protocol"); 48 | return 1; 49 | } 50 | printf("udp has proto %d\n", val); 51 | 52 | val = fgetint("data/lfile/services", " /\n\t", "ftp"); 53 | if (val == -1) { 54 | perror("Failed locating 'ftp' service"); 55 | return 1; 56 | } 57 | printf("ftp is inet port %d\n", val); 58 | 59 | val = fgetint("data/lfile/group", "x:\n", "utmp"); 60 | if (val == -1) { 61 | perror("Failed locating group 'utmp'"); 62 | return 1; 63 | } 64 | printf("utmp is gid %d\n", val); 65 | 66 | val = fgetint("data/lfile/passwd", "x:\n", "nobody"); 67 | if (val == -1) { 68 | perror("Failed locating user 'nobody'"); 69 | return 1; 70 | } 71 | printf("nobody is uid %d\n", val); 72 | 73 | return fstab(); 74 | } 75 | -------------------------------------------------------------------------------- /test/makepath.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "check.h" 6 | 7 | #define BASEDIR "/tmp/libite-test" 8 | 9 | int checkpath(const char *dir) 10 | { 11 | char tmp[256]; 12 | struct stat sb; 13 | 14 | snprintf(tmp, sizeof(tmp), "ls -ld %s", dir); 15 | if (system(tmp)) 16 | perror("system"); 17 | 18 | if (!stat(dir, &sb) && S_ISDIR(sb.st_mode)) 19 | return 0; 20 | 21 | errno = ENOTDIR; 22 | return 1; 23 | } 24 | 25 | int test_makepath(const char *dir) 26 | { 27 | int ret = makepath(dir); 28 | 29 | if (!ret) 30 | ret = checkpath(dir); 31 | if (ret) 32 | perror("Failed"); 33 | 34 | return ret; 35 | } 36 | 37 | int test_fmkpath(const char *subdir) 38 | { 39 | int ret; 40 | 41 | ret = fmkpath(0755, "%s/%s", BASEDIR, subdir); 42 | if (!ret) { 43 | char tmp[256]; 44 | 45 | snprintf(tmp, sizeof(tmp), "%s/%s", BASEDIR, subdir); 46 | ret = checkpath(tmp); 47 | } else 48 | perror("Failed"); 49 | 50 | return ret; 51 | } 52 | 53 | int main(void) 54 | { 55 | int i, ret = 0; 56 | char *list[] = { 57 | BASEDIR "/tok/", 58 | BASEDIR "/tok2", 59 | BASEDIR "/ab", 60 | BASEDIR "/b", 61 | BASEDIR "/a/", 62 | BASEDIR "/a/b", 63 | BASEDIR "/a/c/", 64 | NULL 65 | }; 66 | 67 | mkdir(BASEDIR, 0755); 68 | 69 | printf("Pass 1/2: makepath() =========================\n"); 70 | for (i = 0; list[i] && !ret; i++) 71 | ret |= test_makepath(list[i]); 72 | 73 | printf("Pass 2/2: fmkpath() ==========================\n"); 74 | for (i = 0; list[i] && !ret; i++) 75 | ret |= test_fmkpath(list[i] + strlen(BASEDIR) + 1); 76 | 77 | printf("Done: Cleaning up ============================\n"); 78 | system("rm -rf " BASEDIR); 79 | 80 | return ret; 81 | } 82 | 83 | /** 84 | * Local Variables: 85 | * indent-tabs-mode: t 86 | * c-file-style: "linux" 87 | * compile-command: "make makepath && ./makepath" 88 | * End: 89 | */ 90 | -------------------------------------------------------------------------------- /test/pidfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "check.h" 8 | 9 | extern char *__pidfile_name; 10 | extern char *__pidfile_path; 11 | 12 | static char *ident; 13 | static char *pidfile_arg; 14 | static char PIDFILE[42]; 15 | static int verbose = 0; 16 | static struct stat before, after; 17 | 18 | static void sigterm_handler(int signo) 19 | { 20 | (void)signo; 21 | 22 | PRINT("Exiting ...\n"); 23 | remove(PIDFILE); 24 | } 25 | 26 | static void sigalrm_handler(int signo) 27 | { 28 | (void)signo; 29 | 30 | PRINT("Calling pidfile(%s)\n", pidfile_arg ?: "NULL"); 31 | if (pidfile(pidfile_arg)) 32 | err(1, "failed creating1 %s", pidfile_arg); 33 | if (!fexist(PIDFILE)) 34 | err(1, "failed creating2 %s, created %s", PIDFILE, __pidfile_name); 35 | stat(PIDFILE, &before); 36 | } 37 | 38 | static int mtime() 39 | { 40 | int ret; 41 | char buf[80]; 42 | 43 | /* Must sleep a while here otherwise we execute too fast => no mtime change :-( */ 44 | sleep(2); 45 | 46 | PRINT("Calling pidfile() again to update mtime ...\n"); 47 | if (pidfile(pidfile_arg)) 48 | err(1, "failed creating3 %s", pidfile_arg); 49 | stat(PIDFILE, &after); 50 | 51 | ret = timespec_newer(&after.st_mtim, &before.st_mtim); 52 | PRINT("Before: %s\n", timespec2str(&before.st_mtim, buf, sizeof(buf))); 53 | PRINT("After : %s\n", timespec2str(&after.st_mtim, buf, sizeof(buf))); 54 | 55 | return !ret; 56 | } 57 | 58 | int test_pidfile(char *arg0) 59 | { 60 | /* pidfile() argument */ 61 | pidfile_arg = arg0; 62 | 63 | /* Expected PID file name */ 64 | if (!arg0) 65 | snprintf(PIDFILE, sizeof(PIDFILE), "%s%s.pid", __pidfile_path, ident); 66 | else if (arg0[0] != '/') 67 | snprintf(PIDFILE, sizeof(PIDFILE), "%s%s.pid", __pidfile_path, arg0); 68 | else 69 | snprintf(PIDFILE, sizeof(PIDFILE), "%s", arg0); 70 | 71 | PRINT("Verifying pidfile(%s) ... PID: %d\n", PIDFILE, getpid()); 72 | 73 | /* Remove any lingering files from previous test runs */ 74 | remove(PIDFILE); 75 | 76 | signal(SIGTERM, sigterm_handler); 77 | signal(SIGALRM, sigalrm_handler); 78 | 79 | /* For signalling test */ 80 | alarm(1); 81 | 82 | return test(pidfile_poll(PIDFILE) != getpid(), "Testing pidfile_poll()") 83 | || test(strcmp(__pidfile_name, PIDFILE), "Testing __pidfile_name vs guessed PID filename") 84 | || test(mtime(), "Testing mtime update of followup pidfile() call") 85 | || test(pidfile_signal(PIDFILE, SIGTERM), "Testing signalling ourselves") 86 | ; 87 | } 88 | 89 | static char *progname(char *arg0) 90 | { 91 | char *nm; 92 | 93 | nm = strrchr(arg0, '/'); 94 | if (nm) 95 | nm++; 96 | else 97 | nm = arg0; 98 | 99 | return nm; 100 | } 101 | 102 | int main(int argc, char *argv[]) 103 | { 104 | int ret = 0; 105 | size_t i; 106 | char *pidfiles[] = { 107 | NULL, 108 | "pidfile_test1", 109 | "/var/tmp/pidfile_test2.pid", 110 | }; 111 | 112 | if (argc > 1 && !strcmp(argv[1], "-v")) 113 | verbose = 1; 114 | 115 | /* Override default _PATH_VARRUN in pidfile() */ 116 | __pidfile_path = _PATH_TMP; 117 | 118 | /* Find __progname, not all C libraries support this */ 119 | ident = progname(argv[0]); 120 | 121 | for (i = 0; i < NELEMS(pidfiles); i++) 122 | ret += test_pidfile(pidfiles[i]); 123 | 124 | return ret; 125 | } 126 | 127 | /** 128 | * Local Variables: 129 | * indent-tabs-mode: t 130 | * c-file-style: "linux" 131 | * compile-command: "make pidfile && ./pidfile -v" 132 | * End: 133 | */ 134 | -------------------------------------------------------------------------------- /test/printhdr.c: -------------------------------------------------------------------------------- 1 | #include /* atexit() */ 2 | #include /* usleep() */ 3 | 4 | #include "check.h" 5 | 6 | #define SCREEN_WIDTH 80 7 | 8 | int main(void) 9 | { 10 | FILE *fp = stderr; 11 | char buf[SCREEN_WIDTH]; 12 | 13 | printhdr(fp, "", 0, UNDERSCORE); 14 | printhdr(fp, "Neighbor Table", 0, 0); 15 | snprintf(buf, sizeof(buf), "%-15s %-15s %7s %-5s%10s %6s", 16 | "Neighbor", "Interface", "Version", "Flags", "Uptime", "Expire"); 17 | printhdr(fp, buf, 0, REVERSE); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/progress.c: -------------------------------------------------------------------------------- 1 | #include /* atexit() */ 2 | #include /* usleep() */ 3 | 4 | #include "check.h" 5 | 6 | #define MAX_WIDTH 80 7 | #define msleep(msec) usleep(msec * 1000) 8 | 9 | static void bye(void) 10 | { 11 | showcursor(); 12 | } 13 | 14 | static void testit(int fn, int percent) 15 | { 16 | if (fn) 17 | progress(percent, MAX_WIDTH); 18 | else 19 | progress_simple(percent); 20 | } 21 | 22 | static void run_test(int fn) 23 | { 24 | int i, percent, block = 0, num = 85; 25 | 26 | while (block < num) { 27 | percent = block * 100 / num; 28 | for (i = 0; i < 10; i++) { 29 | testit(fn, percent); 30 | msleep(1); 31 | } 32 | block++; 33 | msleep(1); 34 | } 35 | testit(fn, 100); 36 | } 37 | 38 | int main(void) 39 | { 40 | atexit(bye); 41 | hidecursor(); 42 | 43 | printf("\nAdvanced:\n"); 44 | run_test(1); 45 | printf("\nSimple:\n"); 46 | run_test(0); 47 | printf("\n"); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /test/rsync.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "check.h" 3 | 4 | #define BASE "/tmp/.unittest/" 5 | #define SRC BASE "src/" 6 | #define DST BASE "dst/" 7 | 8 | static int verbose = 0; 9 | static char *files[] = { 10 | SRC "sub1/1.tst", 11 | SRC "sub1/2.tst", 12 | SRC "sub1/3.tst", 13 | SRC "sub2/4.tst", 14 | SRC "sub2/5.tst", 15 | SRC "sub2/6.tst", 16 | SRC "sub3/7.tst", 17 | SRC "sub3/8.tst", 18 | SRC "sub3/9.tst", 19 | NULL 20 | }; 21 | 22 | void cleanup_test(void) 23 | { 24 | system("rm -rf " BASE); 25 | } 26 | 27 | void setup_test(void) 28 | { 29 | int i; 30 | char cmd[256]; 31 | mode_t dir_modes[] = { 755, 700 }; 32 | mode_t file_modes[] = { 644, 600 }; 33 | 34 | cleanup_test(); 35 | 36 | mkdir(BASE, 0755); 37 | mkdir(SRC, 0755); 38 | mkdir(DST, 0755); 39 | 40 | for (i = 0; files[i]; i++) { 41 | snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`", 42 | dir_modes[i % 2], files[i]); 43 | system(cmd); 44 | 45 | snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i], 46 | file_modes[i % 2], files[i]); 47 | system(cmd); 48 | } 49 | } 50 | 51 | int check_dir_to_dir(void) 52 | { 53 | int rc = 0; 54 | 55 | rc += rsync(SRC, DST, LITE_FOPT_KEEP_MTIME, NULL); 56 | rc += systemf("diff -rq %s %s", SRC, DST); 57 | systemf("rm -rf %s/*", DST); 58 | 59 | return rc; 60 | } 61 | 62 | int check_file_to_file(void) 63 | { 64 | int rc = 0; 65 | 66 | touch(SRC "foo.txt"); 67 | PRINT("SRC ==========================\n"); 68 | if (verbose) system("ls -l " SRC); 69 | rc += rsync(SRC "foo.txt", DST "foo.txt", LITE_FOPT_KEEP_MTIME, NULL); 70 | if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt")) { 71 | PRINT("No dst file or it's a directory\n"); 72 | rc++; 73 | } 74 | 75 | PRINT("DST ==========================\n"); 76 | if (verbose) system("ls -l " DST); 77 | systemf("rm -rf %s %s/*", SRC "foo.txt" , DST); 78 | 79 | return rc; 80 | } 81 | 82 | int check_file_to_dir(void) 83 | { 84 | int rc = 0; 85 | 86 | touch(SRC "foo.txt"); 87 | PRINT("SRC ==========================\n"); 88 | if (verbose) system("ls -l " SRC); 89 | rc += rsync(SRC "foo.txt", DST, LITE_FOPT_KEEP_MTIME, NULL); 90 | if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt")) 91 | rc++; 92 | PRINT("DST ==========================\n"); 93 | if (verbose) system("ls -l " DST); 94 | systemf("rm -rf %s %s/*", SRC "foo.txt" , DST); 95 | 96 | return rc; 97 | } 98 | 99 | int check_file_to_nodir(void) 100 | { 101 | int rc = 0; 102 | 103 | touch(SRC "foo.txt"); 104 | PRINT("SRC ==========================\n"); 105 | if (verbose) system("ls -l " SRC); 106 | system("rm -rf " DST); 107 | rc += rsync(SRC "foo.txt", DST, LITE_FOPT_KEEP_MTIME, NULL); 108 | if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt")) 109 | rc++; 110 | PRINT("DST ==========================\n"); 111 | if (verbose) system("ls -l " DST); 112 | systemf("rm -rf %s %s/*", SRC "foo.txt" , DST); 113 | 114 | return rc; 115 | } 116 | 117 | int run_test(void) 118 | { 119 | int result = 0; 120 | 121 | setup_test(); 122 | 123 | result += test(check_dir_to_dir(), "Verifying src/ -> dst/"); 124 | result += test(check_file_to_file(), "Verifying src/foo.txt -> dst/foo.txt"); 125 | result += test(check_file_to_dir(), "Verifying src/foo.txt -> dst/"); 126 | result += test(check_file_to_nodir(),"Verifying src/foo.txt -> dst/ (non-existing target dir)"); 127 | 128 | return result; 129 | } 130 | 131 | int main(int argc, char *argv[]) 132 | { 133 | if (argc > 1) 134 | verbose = !strncmp("-v", argv[1], 2); 135 | 136 | atexit(cleanup_test); 137 | 138 | return run_test(); 139 | } 140 | -------------------------------------------------------------------------------- /test/runbg.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | #define file "/tmp/runbg.txt" 4 | 5 | int main(void) 6 | { 7 | char *cmd[] = { 8 | "sh", "-c", "echo 'hello world' > " file, NULL 9 | }; 10 | 11 | erase(file); 12 | test(runbg(cmd, 100000), "Calling runbg"); 13 | 14 | if (test(fexist(file), "Verifying %s does not yet exist", file)) 15 | return 1; 16 | 17 | test(sleep(1), "Waiting for runbg"); 18 | 19 | if (test(!fexist(file), "Verifying %s has been created", file)) 20 | return 1; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/str.c: -------------------------------------------------------------------------------- 1 | #include "../src/strlite.h" 2 | 3 | int main(void) 4 | { 5 | char buf[10]; 6 | int val; 7 | 8 | val = atonum("42"); 9 | if (val != 42) 10 | return 1; 11 | 12 | if (!string_match("eth1", "eth1234")) 13 | return 1; 14 | 15 | if (string_compare("eth1", "eth1234")) 16 | return 1; 17 | 18 | if (!string_case_compare("somestring", "SoMeSTRING")) 19 | return 1; 20 | 21 | strlcpy(buf, "tooloongstring", sizeof(buf)); 22 | if (buf[9] == 'r') 23 | return 1; 24 | 25 | return 0; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/strmatch.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | #define SYSLOG_NAMES 3 | #include 4 | 5 | #define SEVERITY_LEVEL { \ 6 | CHOOSE(SEVERITY_NONE, "none"), \ 7 | CHOOSE(SEVERITY_DEBUG, "debug"), \ 8 | CHOOSE(SEVERITY_INFO, "info"), \ 9 | CHOOSE(SEVERITY_NOTICE, "notice"), \ 10 | CHOOSE(SEVERITY_WARNING, "warning"), \ 11 | CHOOSE(SEVERITY_ERR, "err"), \ 12 | CHOOSE(SEVERITY_CRIT, "crit"), \ 13 | CHOOSE(SEVERITY_ALERT, "alert"), \ 14 | CHOOSE(SEVERITY_EMERG, "emerg") \ 15 | } 16 | 17 | #define CHOOSE(a, b) b 18 | const char *levels[] = SEVERITY_LEVEL; 19 | 20 | #undef CHOOSE 21 | #define CHOOSE(a, b) a 22 | typedef enum SEVERITY_LEVEL severity_level_t; 23 | 24 | int main(void) 25 | { 26 | size_t i; 27 | int num; 28 | struct { 29 | char *str; 30 | int val; 31 | } t[] = { 32 | { "warn", SEVERITY_WARNING }, 33 | { "alert", SEVERITY_ALERT }, 34 | { NULL, 0 } 35 | }; 36 | 37 | for (i = 0; t[i].str; i++) { 38 | num = strmatch(t[i].str, levels); 39 | fail_unless(num >= 0); 40 | fail_unless(num == t[i].val); 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /test/systemf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "check.h" 3 | 4 | /* 5 | * Depending on the system and default shell, `kill -QUIT $$` (below) 6 | * may leave a core file, so we need to clean that up here to prevent 7 | * the distcleancheck target from failing. 8 | */ 9 | static void cleanup(void) 10 | { 11 | remove("core"); 12 | } 13 | 14 | int main(void) 15 | { 16 | struct { char *cmd; int rc; } list[] = { 17 | { "/app/enoent", 127 }, 18 | { "false", 1 }, 19 | { "true", 0 }, 20 | { "kill -9 $$", -1 }, 21 | { "kill -QUIT $$", -1 }, 22 | { "kill -INT $$", -1 } 23 | }; 24 | 25 | atexit(cleanup); 26 | 27 | for (size_t i = 0; i < NELEMS(list); i++) { 28 | int rc; 29 | 30 | rc = systemf("%s", list[i].cmd); 31 | if (rc != list[i].rc) { 32 | if (rc == -1) 33 | err(rc, "Failed %s", list[i].cmd); 34 | else 35 | errx(rc, "Failed %s, rc %d vs %d", list[i].cmd, rc, list[i].rc); 36 | } 37 | } 38 | 39 | return 0; 40 | } 41 | 42 | /** 43 | * Local Variables: 44 | * indent-tabs-mode: t 45 | * c-file-style: "linux" 46 | * End: 47 | */ 48 | 49 | -------------------------------------------------------------------------------- /test/tempfile.c: -------------------------------------------------------------------------------- 1 | #include /* O_TMPFILE requires -D_GNU_SOURCE */ 2 | #include 3 | #include 4 | #include 5 | #include "check.h" 6 | 7 | #ifndef O_TMPFILE 8 | #warning O_TMPFILE is missing, either too old GLIBC, Linux, or non-Linux system 9 | #endif 10 | 11 | #define READBACK "Reality is frequently inaccurate.\n" 12 | 13 | 14 | int main(void) 15 | { 16 | char buf[80] = ""; 17 | FILE *fp; 18 | 19 | printf("Before tempfile():\n"); 20 | system("ls -lrt " _PATH_TMP " | tail -3"); 21 | fflush(stdout); 22 | 23 | fp = tempfile(); 24 | if (fp == NULL) 25 | fprintf(stderr, "Failed tempfile(), errno %d: %s", errno, strerror(errno)); 26 | fail_unless(fp != NULL); 27 | 28 | fputs(READBACK, fp); 29 | rewind(fp); 30 | if (fgets(buf, sizeof(buf), fp)) 31 | fprintf(stderr, "\nRead-back from tempfile: %s", buf); 32 | fail_unless(!strcmp(buf, READBACK)); 33 | 34 | printf("\nAfter tempfile(), should be same list:\n"); 35 | system("ls -lrt " _PATH_TMP " | tail -3"); 36 | fflush(stdout); 37 | 38 | return fclose(fp); 39 | } 40 | -------------------------------------------------------------------------------- /test/touch.c: -------------------------------------------------------------------------------- 1 | /* test of touch(), touchf(), erase(), and erasef() */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "check.h" 9 | 10 | #define FMT "%s-bar" 11 | 12 | int verbose = 0; 13 | 14 | 15 | static int mtime(char *file) 16 | { 17 | int ret; 18 | char buf[80]; 19 | struct stat before, after; 20 | 21 | PRINT("Calling touch() to create file ...\n"); 22 | touch(file); 23 | stat(file, &before); 24 | 25 | /* Must sleep a while here otherwise we execute too fast => no mtime change :-( */ 26 | sleep(2); 27 | 28 | PRINT("Calling touch() again to update mtime ...\n"); 29 | if (touch(file)) { 30 | erase(file); 31 | err(1, "Failed creating %s", file); 32 | } 33 | stat(file, &after); 34 | 35 | ret = timespec_newer(&after.st_mtim, &before.st_mtim); 36 | PRINT("Before: %s\n", timespec2str(&before.st_mtim, buf, sizeof(buf))); 37 | PRINT("After : %s\n", timespec2str(&after.st_mtim, buf, sizeof(buf))); 38 | erase(file); 39 | 40 | return !ret; 41 | } 42 | 43 | static int formatted(char *file) 44 | { 45 | char vrfy[80]; 46 | 47 | if (touchf(FMT, file)) 48 | err(1, "Failed creating " FMT, file); 49 | 50 | PRINT("Created " FMT "\n", file); 51 | 52 | snprintf(vrfy, sizeof(vrfy), FMT, file); 53 | if (!fexist(vrfy)) 54 | errx(1, "touchf() does not detect failure to create file " FMT, file); 55 | 56 | PRINT("File " FMT " really does exist!\n", file); 57 | 58 | erasef(FMT, file); 59 | if (fexist(vrfy)) { 60 | int saved = errno; 61 | 62 | erase(vrfy); 63 | errno = saved; 64 | errx(1, "erasef() failed removing the created file " FMT, file); 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | int main(int argc, char *argv[]) 71 | { 72 | int fd; 73 | char file[42] = "/tmp/touch_test.XXXXXX"; 74 | 75 | if (argc > 1 && !strcmp(argv[1], "-v")) 76 | verbose = 1; 77 | 78 | fd = mkstemp(file); 79 | if (fd < 0) 80 | err(1, "Failed creating tempfile, %s", file); 81 | close(fd); 82 | 83 | return mtime(file) || formatted(file); 84 | } 85 | 86 | /** 87 | * Local Variables: 88 | * indent-tabs-mode: t 89 | * c-file-style: "linux" 90 | * compile-command: "make touch && ./touch -v" 91 | * End: 92 | */ 93 | -------------------------------------------------------------------------------- /test/which.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | struct tc { 4 | char *cmd; 5 | int exp; 6 | int res; 7 | }; 8 | 9 | int main(void) 10 | { 11 | int result = 0; 12 | size_t i; 13 | struct tc test[] = { 14 | { "executable", 1, 0 }, 15 | { "executable -h", 1, 0 }, 16 | { "executable-link", 1, 0 }, 17 | { "regular", 0, 0 }, 18 | { "ls", 1, 0 }, 19 | { "/usr/bin/which", 1, 0 }, 20 | { NULL, 0, 0 } 21 | }; 22 | 23 | setenv("PATH", "data/which:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin", 1); 24 | for (i = 0; test[i].cmd; i++) { 25 | char *path; 26 | 27 | path = which(test[i].cmd); 28 | if (!path) { 29 | test[i].res = 0; 30 | fprintf(stderr, "%20s : Not hot dog\n", test[i].cmd); 31 | } else { 32 | test[i].res = 1; 33 | fprintf(stderr, "%20s : hot dog --> %s\n", test[i].cmd, path); 34 | free(path); 35 | } 36 | } 37 | 38 | for (i = 0; test[i].cmd; i++) 39 | result += test[i].exp != test[i].res; 40 | 41 | return result; 42 | } 43 | 44 | /** 45 | * Local Variables: 46 | * indent-tabs-mode: t 47 | * c-file-style: "linux" 48 | * End: 49 | */ 50 | -------------------------------------------------------------------------------- /test/yorn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "check.h" 6 | 7 | int main(void) 8 | { 9 | int chan[2]; 10 | int status; 11 | 12 | pipe(chan); 13 | 14 | if (fork() > 0) { 15 | dup2(chan[0], STDIN_FILENO); 16 | if (yorn("Do you really wanna do this (y/N)? ")) { 17 | printf("Affirmative!\n"); 18 | _exit(0); 19 | } 20 | 21 | printf("OK, aborting!\n"); 22 | _exit(1); 23 | } 24 | 25 | sleep(1); 26 | if (write(chan[1], "Y", 1) != 1) 27 | return 1; 28 | 29 | wait(&status); 30 | 31 | return WEXITSTATUS(status); 32 | } 33 | --------------------------------------------------------------------------------