├── .astylerc ├── .editorconfig ├── .gitignore ├── COPYING ├── Makefile ├── README.pod ├── doc ├── .gitignore ├── Makefile ├── paccapability.pod ├── paccheck.pod ├── pacconf.pod ├── pacfile.pod ├── pacinfo.pod ├── pacini.pod ├── paclock.pod ├── paclog.pod ├── pacrepairdb.pod ├── pacrepairfile.pod ├── pacreport.pod ├── pacsift.pod ├── pacsync.pod ├── pactrans.pod ├── pacutils-mtree.pod ├── pacutils-sysroot.pod └── pacutils-uix.pod ├── ext ├── globdir.c │ ├── .gitignore │ ├── COPYING │ ├── Makefile │ ├── README.pod │ ├── ext │ │ └── tap.c │ │ │ ├── .gitignore │ │ │ ├── README.pod │ │ │ ├── t │ │ │ ├── .gitignore │ │ │ ├── 01-sanity.c │ │ │ ├── 10-basic.c │ │ │ ├── Makefile │ │ │ └── expected │ │ │ │ └── 10-basic.t.out │ │ │ └── tap.c │ ├── globdir.c │ ├── globdir.h │ └── t │ │ ├── .gitignore │ │ ├── .proverc │ │ ├── 01-sanity.c │ │ ├── 10-escape_pattern.c │ │ ├── 10-is_pattern.c │ │ ├── 10-split_pattern.c │ │ ├── 20-globat.c │ │ ├── 30-globdir.c │ │ ├── 40-glob_append.c │ │ ├── 40-glob_dooffs.c │ │ ├── 40-glob_err.c │ │ ├── 40-glob_mark.c │ │ ├── 40-glob_nocheck.c │ │ ├── 40-glob_noescape.c │ │ ├── 40-glob_nosort.c │ │ ├── Makefile │ │ ├── globdir_test.h │ │ └── runtest.sh ├── mini.c │ ├── .gitignore │ ├── .gitmodules │ ├── README.pod │ ├── mini.c │ ├── mini.h │ └── t │ │ ├── .gitignore │ │ ├── 01-sanity.c │ │ ├── 10-basic.c │ │ ├── 10-lookup.c │ │ ├── 90-smoke.c │ │ └── Makefile ├── tap.c │ ├── .gitignore │ ├── README.pod │ ├── t │ │ ├── .gitignore │ │ ├── 01-sanity.c │ │ ├── 10-basic.c │ │ ├── Makefile │ │ └── expected │ │ │ └── 10-basic.t.out │ └── tap.c └── update ├── lib ├── Makefile ├── pacutils.c ├── pacutils.h └── pacutils │ ├── config-defaults.h │ ├── config.c │ ├── config.h │ ├── depends.c │ ├── depends.h │ ├── log.c │ ├── log.h │ ├── mtree.c │ ├── mtree.h │ ├── ui.c │ ├── ui.h │ ├── uix.c │ ├── uix.h │ ├── util.c │ └── util.h ├── src ├── .gitignore ├── Makefile ├── config-defaults.h ├── paccapability.c ├── paccheck.c ├── pacconf.c ├── pacfile.c ├── pacinfo.c ├── pacini.c ├── paclock.c ├── paclog.c ├── pacrepairdb.c ├── pacrepairfile.c ├── pacreport.c ├── pacsift.c ├── pacsync.c └── pactrans.c └── t ├── .gitignore ├── .proverc ├── 10-basename.c ├── 10-config-basic.c ├── 10-filelist_contains_path.c ├── 10-log-action-parse.c ├── 10-log-reader-basic.c ├── 10-log-transaction-parse.c ├── 10-mtree-basic.c ├── 10-parse-datetime.c ├── 10-pathcmp.c ├── 10-strreplace.c ├── 10-util-read-list.c ├── 20-config-includes.c ├── 20-config-root-inheritance.c ├── 30-config-sysroot.c ├── 40-ui-cb-download-progress.c ├── 99-pu_list_shift.c ├── Makefile ├── pacutils_test.h └── runtest.sh /.astylerc: -------------------------------------------------------------------------------- 1 | style=attach 2 | indent=spaces=2 3 | indent-switches 4 | indent-after-parens 5 | indent-continuation=2 6 | indent-preproc-define 7 | pad-oper 8 | pad-comma 9 | align-pointer=name 10 | add-one-line-braces 11 | attach-return-type 12 | keep-one-line-blocks 13 | convert-tabs 14 | max-code-length=80 15 | mode=c 16 | pad-oper 17 | pad-comma 18 | pad-header 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{c,h}] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | indent_size = 2 9 | indent_style = space 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | 19 | # Editor Files 20 | *~ 21 | *.swp 22 | *.orig 23 | tags 24 | 25 | # site-specific configuration 26 | configure.mk 27 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2012-2015 Andrew Gregory 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: lib src 2 | 3 | lib: 4 | $(MAKE) -C $@ all 5 | 6 | src: lib 7 | $(MAKE) -C $@ all 8 | 9 | check: lib src 10 | $(MAKE) -C t/ check 11 | 12 | doc: 13 | $(MAKE) -C doc/ $@ 14 | 15 | tidy: 16 | astyle --options=.astylerc --exclude=ext --recursive "*.c,*.h" --suffix=none 17 | 18 | install: lib 19 | $(MAKE) -C doc/ $@ 20 | $(MAKE) -C lib/ $@ 21 | $(MAKE) -C src/ $@ 22 | 23 | clean: 24 | $(MAKE) -C doc/ $@ 25 | $(MAKE) -C lib/ $@ 26 | $(MAKE) -C src/ $@ 27 | $(MAKE) -C t/ $@ 28 | 29 | .PHONY: all check clean doc install lib src tidy 30 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *.1 2 | *.3 3 | *.8 4 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | -include ../configure.mk 2 | -include configure.mk 3 | 4 | PREFIX ?= /usr/local 5 | DATAROOTDIR ?= $(PREFIX)/share 6 | MANDIR ?= $(DATAROOTDIR)/man 7 | MAN1DIR ?= $(MANDIR)/man1 8 | MAN1EXT ?= .1 9 | MAN3DIR ?= $(MANDIR)/man3 10 | MAN3EXT ?= .3 11 | MAN7DIR ?= $(MANDIR)/man7 12 | MAN7EXT ?= .7 13 | 14 | MAN1PAGES = \ 15 | paccapability$(MAN1EXT) \ 16 | paccheck$(MAN1EXT) \ 17 | pacconf$(MAN1EXT) \ 18 | pacfile$(MAN1EXT) \ 19 | pacinfo$(MAN1EXT) \ 20 | pacini$(MAN1EXT) \ 21 | paclock$(MAN1EXT) \ 22 | paclog$(MAN1EXT) \ 23 | pacrepairdb$(MAN1EXT) \ 24 | pacrepairfile$(MAN1EXT) \ 25 | pacreport$(MAN1EXT) \ 26 | pacsift$(MAN1EXT) \ 27 | pacsync$(MAN1EXT) \ 28 | pactrans$(MAN1EXT) 29 | 30 | MAN3PAGES = \ 31 | pacutils-mtree$(MAN3EXT) 32 | 33 | MAN7PAGES = \ 34 | pacutils-sysroot$(MAN7EXT) 35 | 36 | all: doc 37 | 38 | %$(MAN1EXT): %.pod 39 | pod2man --center $* --release pacutils --section 1 $< $@ 40 | 41 | %$(MAN3EXT): %.pod 42 | pod2man --center $* --release pacutils --section 3 $< $@ 43 | 44 | %$(MAN7EXT): %.pod 45 | pod2man --center $* --release pacutils --section 7 $< $@ 46 | 47 | pacinstall$(MAN1EXT): | pactrans$(MAN1EXT) 48 | ln -fs $| $@ 49 | 50 | pacremove$(MAN1EXT): | pactrans$(MAN1EXT) 51 | ln -fs $| $@ 52 | 53 | doc: $(MAN1PAGES) $(MAN3PAGES) $(MAN7PAGES) pacinstall$(MAN1EXT) pacremove$(MAN1EXT) 54 | 55 | install: doc 56 | install -d "${DESTDIR}${MAN1DIR}" 57 | install -d "${DESTDIR}${MAN3DIR}" 58 | install -d "${DESTDIR}${MAN7DIR}" 59 | install -m 644 $(MAN1PAGES) "${DESTDIR}${MAN1DIR}" 60 | install -m 644 $(MAN3PAGES) "${DESTDIR}${MAN3DIR}" 61 | install -m 644 $(MAN7PAGES) "${DESTDIR}${MAN7DIR}" 62 | cp -d pacinstall$(MAN1EXT) "${DESTDIR}${MAN1DIR}" 63 | cp -d pacremove$(MAN1EXT) "${DESTDIR}${MAN1DIR}" 64 | 65 | clean: 66 | $(RM) $(MAN1PAGES) $(MAN3PAGES) $(MAN7PAGES) pacinstall$(MAN1EXT) pacremove$(MAN1EXT) 67 | 68 | .PHONY: all clean doc install 69 | -------------------------------------------------------------------------------- /doc/paccapability.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | paccapability - query libalpm capabilities 4 | 5 | =head1 SYNOPSIS 6 | 7 | paccapability [options] [...] 8 | paccapability (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | B provides a way to query which features libalpm was built with. 13 | Recognized capabilities are: 14 | 15 | =over 16 | 17 | =item * ALPM_CAPABILITY_NLS 18 | 19 | =item * ALPM_CAPABILITY_DOWNLOADER 20 | 21 | =item * ALPM_CAPABILITY_SIGNATURES 22 | 23 | =back 24 | 25 | Capabilities are case-insensitive and may omit the leading C. 26 | If no capabilities are provided, all enabled capabilities will be printed, one 27 | per line. Otherwise, each provided capability will printed, one per line, 28 | followed by a one or zero to indicate that the feature is enabled or disabled, 29 | respectively. If any given capabilities are disabled B will 30 | exit with a non-zero value. 31 | 32 | =head1 OPTIONS 33 | 34 | =over 35 | 36 | =item B<--help> 37 | 38 | Display usage information and exit. 39 | 40 | =item B<--version> 41 | 42 | Display version information and exit. 43 | 44 | =back 45 | 46 | =head1 EXAMPLES 47 | 48 | =over 49 | 50 | =item Check if libalpm was built with signature checking: 51 | 52 | paccapability signatures >/dev/null && ... 53 | 54 | =back 55 | -------------------------------------------------------------------------------- /doc/paccheck.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | paccheck - check installed packages 4 | 5 | =head1 SYNOPSIS 6 | 7 | paccheck [options] []... 8 | paccheck (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Check installed packages. Additional packages may be specified on F. 13 | If no package are provided, all installed packages will be checked. By default 14 | only package dependencies and basic file information will checked. 15 | 16 | =head1 OPTIONS 17 | 18 | =over 19 | 20 | =item B<--config>=F 21 | 22 | Set an alternate configuration file path. 23 | 24 | =item B<--dbpath>=F 25 | 26 | Set an alternate database path. 27 | 28 | =item B<--root>=F 29 | 30 | Set an alternate installation root. 31 | 32 | =item B<--sysroot>=F 33 | 34 | Set an alternate system root. See L. 35 | 36 | =item B<--null>[=I] 37 | 38 | Set an alternate separator for values parsed from F. By default 39 | a newline C<\n> is used as the separator. If B<--null> is used without 40 | specifying I C will be used. 41 | 42 | =item B<--list-broken> 43 | 44 | Only print the names of packages that fail the selected checks. 45 | 46 | =item B<--quiet> 47 | 48 | Only display messages if a problem is found. 49 | 50 | =item B<--recursive> 51 | 52 | Recursively perform checks on packages' dependencies as well. 53 | 54 | =item B<--depends> 55 | 56 | Check that all package dependencies are satisfied. 57 | 58 | =item B<--opt-depends> 59 | 60 | Check that all package optional dependencies are satisfied. 61 | 62 | =item B<--files> 63 | 64 | Check package files against the local database. 65 | 66 | =item B<--file-properties> 67 | 68 | Check package files against MTREE data. 69 | 70 | =item B<--md5sum> 71 | 72 | Check file md5sums against MTREE data. 73 | 74 | =item B<--sha256sum> 75 | 76 | Check file sha256sums against MTREE data. 77 | 78 | =item B<--require-mtree> 79 | 80 | Treat missing MTREE data as an error for B<--db-files> and/or 81 | B<--file-properties>. 82 | 83 | =item B<--db-files> 84 | 85 | Include database files in B<--files> and B<--file-properties> checks. 86 | B<--files> will test for the existence of F, F, and F (with 87 | B<--require-mtree>) files in the package database entry. B<--file-properties> 88 | will check F and F files in the package database where 89 | applicable. 90 | 91 | =item B<--backup> 92 | 93 | Include backup files in file modification checks. 94 | 95 | =item B<--noextract> 96 | 97 | Include NoExtract files in file modification checks. 98 | 99 | =item B<--noupgrade> 100 | 101 | Include NoUpgrade files in file modification checks. 102 | 103 | =item B<--help> 104 | 105 | Display usage information and exit. 106 | 107 | =item B<--version> 108 | 109 | Display version information and exit. 110 | 111 | =back 112 | 113 | =head1 CAVEATS 114 | 115 | B determines whether or not to read packages from F based on 116 | a naive check using L. If B is called in an environment, 117 | such as a shell function or script being used in a pipe, where F is not 118 | connected to a terminal but does not contain packages to check, B 119 | should be called with F closed. For POSIX-compatible shells, this can 120 | be done with C<< <&- >>. 121 | -------------------------------------------------------------------------------- /doc/pacconf.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacconf - query pacman's configuration file 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacconf [options] [...] 8 | pacconf (--repo-list|--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Query configuration options from pacman's configuration file. If no 13 | Is are provided, the entire configuration will be printed in 14 | a normalized format. By default, if only a single I is provided, 15 | only its value will be printed without the option name. For directives without 16 | a value, the directive name will be used as the value. 17 | 18 | The values displayed are the final values as would be parsed by pacman itself. 19 | All default values are set, C directives are processed, C<$arch> and 20 | C<$repo> variables in repository servers are replaced, and C will 21 | be replaced if set to C. 22 | 23 | =head1 OPTIONS 24 | 25 | =over 26 | 27 | =item B<--config>=F 28 | 29 | Set an alternate configuration file path. 30 | 31 | =item B<--root>=F 32 | 33 | Set an alternate installation root. 34 | 35 | =item B<--sysroot>=F 36 | 37 | Set an alternate system root. See L. 38 | 39 | =item B<--arch>=I 40 | 41 | Set an alternate architecture. 42 | 43 | =item B<--null>[=I] 44 | 45 | Set an alternate separator for values parsed from F. By default 46 | a newline C<\n> is used as the separator. If B<--null> is used without 47 | specifying I C will be used. 48 | 49 | =item B<--options> 50 | 51 | Only display global settings from the C<[options]> section. The C<[options]> 52 | header itself is not printed. 53 | 54 | =item B<--raw> 55 | 56 | Display unmodified values. C directives will be processed, but 57 | defaults will not be set, C<$arch> and C<$repo> variables in repository servers 58 | will not be replaced, and C will not be replaced if set to 59 | C. 60 | 61 | =item B<--repo>=I 62 | 63 | Only display directives for repository I. The repository header is not 64 | printed. 65 | 66 | =item B<--repo-list> 67 | 68 | List configured repositories. 69 | 70 | =item B<--single> 71 | 72 | Display only first value of multi-value options. 73 | 74 | =item B<--verbose> 75 | 76 | Always show directive names even if only one I is provided. 77 | 78 | =item B<--help> 79 | 80 | Display usage information and exit. 81 | 82 | =item B<--version> 83 | 84 | Display version information and exit. 85 | 86 | =back 87 | 88 | =head1 EXAMPLES 89 | 90 | =over 91 | 92 | =item Check if color is enabled: 93 | 94 | color=$(pacconf color) 95 | [[ -n $color ]] && ... # print something in color 96 | 97 | =back 98 | -------------------------------------------------------------------------------- /doc/pacfile.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacfile - display information about package files 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacfile [options] ... 8 | pacfile (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Display file information from the local package database. 13 | 14 | =head1 OPTIONS 15 | 16 | =over 17 | 18 | =item B<--config>=F 19 | 20 | Set an alternate configuration file path. 21 | 22 | =item B<--dbpath>=F 23 | 24 | Set an alternate database path. 25 | 26 | =item B<--root>=F 27 | 28 | Set an alternate installation root. 29 | 30 | =item B<--sysroot>=F 31 | 32 | Set an alternate system root. See L. 33 | 34 | =item B<--package>=I 35 | 36 | Limit information to the specified package. May be specified multiple times. 37 | 38 | =item B<--check> 39 | 40 | Compare database values to the file system. 41 | 42 | =item B<--help> 43 | 44 | Display usage information and exit. 45 | 46 | =item B<--version> 47 | 48 | Display version information and exit. 49 | 50 | =back 51 | -------------------------------------------------------------------------------- /doc/pacinfo.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacinfo - display package information 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacinfo [options] (|)... 8 | pacinfo (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | If a I is given, information for all packages matching that name in 13 | all databases will be displayed. 14 | 15 | If F is not connected to a terminal, packages will be read from 16 | F. 17 | 18 | =head1 OPTIONS 19 | 20 | =over 21 | 22 | =item B<--config>=F 23 | 24 | Set an alternate configuration file path. 25 | 26 | =item B<--dbext>=I 27 | 28 | Set an alternate sync database extension. 29 | 30 | =item B<--dbpath>=F 31 | 32 | Set an alternate database path. 33 | 34 | =item B<--root>=F 35 | 36 | Set an alternate installation root. 37 | 38 | =item B<--sysroot>=F 39 | 40 | Set an alternate system root. See L. 41 | 42 | =item B<--null>[=I] 43 | 44 | Set an alternate separator for values parsed from F. By default 45 | a newline C<\n> is used as the separator. If B<--null> is used without 46 | specifying I C will be used. 47 | 48 | =item B<--no-timeout> 49 | 50 | Disable low-speed timeouts for downloads. 51 | 52 | =item B<--short> 53 | 54 | Display brief information. Suitable for printing a potentially large number of 55 | packages: 56 | 57 | pacsift --name libreoffice | pacinfo --short 58 | 59 | =item B<--verbose> 60 | 61 | Display additional package information: C, C, and 62 | C. 63 | 64 | =item B<--raw> 65 | 66 | Display raw numeric size and date values. 67 | 68 | =item B<--removable-size> 69 | 70 | Include the size of any removable dependencies in installed size. 71 | 72 | =item B<--help> 73 | 74 | Display usage information and exit. 75 | 76 | =item B<--version> 77 | 78 | Display version information and exit. 79 | 80 | =back 81 | 82 | =head1 CAVEATS 83 | 84 | B determines whether or not to read packages from F based on 85 | a naive check using L. If B is called in an environment, 86 | such as a shell function or script being used in a pipe, where F is not 87 | connected to a terminal but does not contain packages to print, B 88 | should be called with F closed. For POSIX-compatible shells, this can 89 | be done with C<< <&- >>. 90 | -------------------------------------------------------------------------------- /doc/pacini.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacini - query pacman-style configuration files 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacini [options] [ [...]] 8 | pacini (--section-list|--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Query configuration options from pacman-style INI files. If no Is 13 | are provided, I will be printed in a normalized format. By default, if 14 | only a single I is provided, only its value will be printed without 15 | the option name. For directives without a value, the directive name will be 16 | used as the value. If I is omitted or C<-> the configuration will be 17 | read from F. 18 | 19 | =head1 OPTIONS 20 | 21 | =over 22 | 23 | =item B<--section>=I 24 | 25 | Only display directives listed in section I. 26 | 27 | =item B<--section-list> 28 | 29 | List configured sections. 30 | 31 | =item B<--verbose> 32 | 33 | Always show directive names even if only one I is provided. 34 | 35 | =item B<--help> 36 | 37 | Display usage information and exit. 38 | 39 | =item B<--version> 40 | 41 | Display version information and exit. 42 | 43 | =back 44 | -------------------------------------------------------------------------------- /doc/paclock.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | paclock - lock/unlock the alpm database 4 | 5 | =head1 SYNOPSIS 6 | 7 | paclock [options] 8 | paclock (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Safe locking and unlocking of the ALPM database using identity keys for 13 | scripts. 14 | 15 | =head1 OPTIONS 16 | 17 | =over 18 | 19 | =item B<--config>=F 20 | 21 | Set an alternate configuration file path. 22 | 23 | =item B<--lockfile>=F 24 | 25 | Set an alternate lock file path. 26 | 27 | =item B<--root>=F 28 | 29 | Set an alternate installation root. 30 | 31 | =item B<--sysroot>=F 32 | 33 | Set an alternate system root. See L. 34 | 35 | =item B<--lock> 36 | 37 | Lock the database (default). 38 | 39 | =item B<--unlock> 40 | 41 | Unlock the database. 42 | 43 | =item B<--run> 44 | 45 | Treat C as a command to run with the database locked. If the command 46 | returns non-zero, the lock file will be left in place to indicate an error 47 | unless B<--fail-ok> is used. 48 | 49 | =item B<--fail-ok> 50 | 51 | Unlock the database after running a command with B<--run> even if the command 52 | returns non-zero. Generally, the database should be left locked if it may be 53 | in an inconsistent to indicate that it may need to be repaired. This flag is 54 | intended primarily for read-only operations. 55 | 56 | =item B<--print> 57 | 58 | Write the lock file path to F and exit without modifying the lock file. 59 | 60 | =item B<--no-check-keys> 61 | 62 | Skip checking for an identity key match before unlocking. 63 | 64 | =item B<--enoent-ok> 65 | 66 | Do not treat a non-existent lock file as an error when unlocking. 67 | 68 | =item B<--key>=I 69 | 70 | An identifier to write to the lock file. By default, B<--unlock> will only 71 | operate if the lock file was previously locked with the same key. Defaults to 72 | the hostname followed by a colon and the process id of the caller (for example: 73 | C). 74 | 75 | =item B<--help> 76 | 77 | Display usage information and exit. 78 | 79 | =item B<--version> 80 | 81 | Display version information and exit. 82 | 83 | =back 84 | 85 | =head1 EXAMPLE 86 | 87 | The system configuration that B relies on to construct the default 88 | lock file path and identity key may change between invocations. To ensure 89 | consistency, scripts should explicitly provide the path and key to use each 90 | time B is called. 91 | 92 | #!/bin/bash 93 | lock_file="$(paclock --print)" 94 | lock_key="myapp:$(hostname):$$" 95 | paclock --lockfile="$lock_file" --key="$lock_key" || exit 1 96 | echo "do stuff here" 97 | paclock --lockfile="$lock_file" --key="$lock_key" --unlock || exit 1 98 | 99 | Run a command lacking native lock support: 100 | 101 | paclock --run -- paccache --dryrun --verbose 102 | 103 | =head1 SEE ALSO 104 | 105 | pacconf(1) 106 | -------------------------------------------------------------------------------- /doc/paclog.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | paclog - filter pacman log entries 4 | 5 | =head1 SYNOPSIS 6 | 7 | paclog [options] [filters]... 8 | paclog [options] --pkglist 9 | paclog (--help|--version) 10 | 11 | =head1 DESCRIPTION 12 | 13 | If input is provided on F it will be parsed instead of B<--logfile>. 14 | Log entries will be displayed if they match I of the provided filters. To 15 | display the intersection of multiple filters they can be connected by a pipe: 16 | 17 | paclog --after=2015-01-01 | paclog --warnings 18 | 19 | =head1 OPTIONS 20 | 21 | =over 22 | 23 | =item B<--config>=F 24 | 25 | Set an alternate configuration file path. 26 | 27 | =item B<--logfile>=F 28 | 29 | Set an alternate log file path. 30 | 31 | =item B<--root>=F 32 | 33 | Set an alternate installation root. 34 | 35 | =item B<--sysroot>=F 36 | 37 | Set an alternate system root. See L. 38 | 39 | =item B<--[no-]color> 40 | 41 | Colorize output. By default output will be colorized if F is 42 | a terminal. 43 | 44 | =item B<--pkglist> 45 | 46 | Print the list of installed packages according to the log. 47 | 48 | =item B<--help> 49 | 50 | Display usage information and exit. 51 | 52 | =item B<--version> 53 | 54 | Display version information and exit. 55 | 56 | =back 57 | 58 | =head2 Filters 59 | 60 | =over 61 | 62 | =item B<--action>=I 63 | 64 | Display package operations. I must be one of C, C, 65 | C, C, C, or C. 66 | 67 | =item B<--after>=I, B<--before>=I 68 | 69 | Display entries after/before I. If seconds or timezone information is 70 | included it will be silently ignored, allowing output from C to be 71 | used: 72 | 73 | paclog --after "$(date -Iminutes --date '3 days ago')" 74 | 75 | =item B<--caller>=I 76 | 77 | Display log entries from I. May be specified multiple times. 78 | Case-sensitive. 79 | 80 | =item B<--commandline> 81 | 82 | Display pacman-style logged commandline entries. 83 | 84 | =item B<--grep>=I 85 | 86 | Display log entries whose message matches I. 87 | 88 | =item B<--package>=I 89 | 90 | Display logged actions affecting I. May be specified multiple times. 91 | 92 | =item B<--warnings> 93 | 94 | Display errors, warnings, and notes. 95 | 96 | =back 97 | 98 | =head1 CAVEATS 99 | 100 | B determines whether or not to read the log file from F based on 101 | a naive check using L. If B is called in an environment, 102 | such as a shell function or script being used in a pipe, where F is not 103 | connected to a terminal but does not a log file to parse, B should be 104 | called with F closed. For POSIX-compatible shells, this can be done 105 | with C<< <&- >>. 106 | -------------------------------------------------------------------------------- /doc/pacrepairdb.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacrepairdb - fix corrupted database entries 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacrepairdb [options] ... 8 | pacrepairdb (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Attempt to repair broken package entries in libalpm's database. Any missing 13 | essential files will be created and the packages will be reinstalled from the 14 | cache. 15 | 16 | If F is not connected to a terminal, package names will be read from 17 | F. 18 | 19 | =head1 OPTIONS 20 | 21 | =over 22 | 23 | =item B<--cachedir>=F 24 | 25 | Set an alternate cache directory path. 26 | 27 | =item B<--config>=F 28 | 29 | Set an alternate configuration file path. 30 | 31 | =item B<--dbonly> 32 | 33 | Make the changes to the database without actually extracting or removing any 34 | packages. 35 | 36 | =item B<--dbpath>=F 37 | 38 | Set an alternate database path. 39 | 40 | =item B<--debug> 41 | 42 | Display additional debugging information. 43 | 44 | =item B<--hookdir> 45 | 46 | Add additional user hook directories. 47 | 48 | =item B<--logfile>=F 49 | 50 | Set an alternate log file path. 51 | 52 | =item B<--no-confirm> 53 | 54 | Assume default responses to all prompts. 55 | 56 | =item B<--no-scriptlet> 57 | 58 | Do not run package install scripts. 59 | 60 | =item B<--no-hooks> 61 | 62 | Do not run transaction hooks. 63 | 64 | =item B<--no-timeout> 65 | 66 | Disable low-speed timeouts for downloads. 67 | 68 | =item B<--print-only> 69 | 70 | Display the packages to be repaired and the cache packages to be used and exit. 71 | 72 | =item B<--root>=F 73 | 74 | Set an alternate installation root. 75 | 76 | =item B<--sysroot>=F 77 | 78 | Set an alternate system root. See L. 79 | 80 | =item B<--verbose> 81 | 82 | Display additional progress information. 83 | 84 | =item B<--help> 85 | 86 | Display usage information and exit. 87 | 88 | =item B<--version> 89 | 90 | Display version information and exit. 91 | 92 | =back 93 | 94 | =head1 EXAMPLES 95 | 96 | Find and reinstall broken packages: 97 | 98 | paccheck --list-broken --files --file-properties --db-files --require-mtree | pacrepairdb 99 | 100 | =head1 CAVEATS 101 | 102 | B expects all of the packages being repaired to be cached. Cached 103 | packages that match the name and version of an installed package are assumed to 104 | be the same package. No attempt is made to validate cached packages. If 105 | a cache contains a package that does not match the installed package, but 106 | nonetheless has the same name and version, B will blindly install 107 | it, worsening any database problems. 108 | 109 | B does not check for leftover orphaned files. It is the user's 110 | responsibility locate and handle orphaned files. 111 | 112 | B determines whether or not to read package names from F 113 | based on a naive check using L. If B is called in an 114 | environment, such as a shell function or script being used in a pipe, where 115 | F is not connected to a terminal but does not contain package names to 116 | repair, B should be called with F closed. For 117 | POSIX-compatible shells, this can be done with C<< <&- >>. 118 | -------------------------------------------------------------------------------- /doc/pacrepairfile.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacrepairfile - reset properties on alpm-managed files 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacrepairfile [options] (--gid|--mode|--mtime|--uid)... ... 8 | pacrepairfile (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Resets file properties for alpm-managed files based on MTREE data. 13 | 14 | If F is not connected to a terminal, files will be read from F. 15 | 16 | =head1 OPTIONS 17 | 18 | =over 19 | 20 | =item B<--config>=F 21 | 22 | Set an alternate configuration file path. 23 | 24 | =item B<--dbpath>=F 25 | 26 | Set an alternate database path. 27 | 28 | =item B<--root>=F 29 | 30 | Set an alternate installation root. 31 | 32 | =item B<--sysroot>=F 33 | 34 | Set an alternate system root. See L. 35 | 36 | =item B<--quiet> 37 | 38 | Do not display progress information. 39 | 40 | =item B<--package>=I 41 | 42 | Search I for file properties. May be specified multiple times. If 43 | B<--package> is not specified, all installed packages will be searched. 44 | 45 | =item B<--help> 46 | 47 | Display usage information and exit. 48 | 49 | =item B<--version> 50 | 51 | Display version information and exit. 52 | 53 | =back 54 | 55 | =head2 Fields 56 | 57 | =over 58 | 59 | =item B<--gid> 60 | 61 | Reset file owner group id. 62 | 63 | =item B<--mode> 64 | 65 | Reset file permissions. 66 | 67 | =item B<--mtime> 68 | 69 | Reset file modification time. 70 | 71 | =item B<--uid> 72 | 73 | Reset file owner user id. 74 | 75 | =back 76 | 77 | =head1 CAVEATS 78 | 79 | B determines whether or not to read files from F based on 80 | a naive check using L. If B is called in an 81 | environment, such as a shell function or script being used in a pipe, where 82 | F is not connected to a terminal but does not contain files to reset, 83 | B should be called with F closed. For POSIX-compatible 84 | shells, this can be done with C<< <&- >>. 85 | 86 | In order for B to reset a file's properties, the package which 87 | owns the file must have MTREE data. 88 | -------------------------------------------------------------------------------- /doc/pacreport.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacreport - display a summary of installed packages 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacreport [options] 8 | pacreport (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Generate a report of installed packages including: 13 | 14 | =over 15 | 16 | =item unneeded packages installed explicitly 17 | 18 | =item unneeded packages installed as dependencies 19 | 20 | =item unneeded packages in a dependency cycle 21 | 22 | =item installed packages not in a repository 23 | 24 | =item packages missing from specified groups 25 | 26 | =item missing package files 27 | 28 | =item unmerged backup files 29 | 30 | =item unowned files 31 | 32 | =item cache directory sizes 33 | 34 | =back 35 | 36 | Package sizes include dependencies not needed by other packages. 37 | 38 | Packages prefixed by an asterisk (C<*>) are optional dependencies for another 39 | package. 40 | 41 | =head1 OPTIONS 42 | 43 | =over 44 | 45 | =item B<--config>=F 46 | 47 | Set an alternate pacman configuration file path. 48 | 49 | =item B<--dbext>=I 50 | 51 | Set an alternate sync database extension. 52 | 53 | =item B<--dbpath>=F 54 | 55 | Set an alternate database path. 56 | 57 | =item B<--root>=F 58 | 59 | Set an alternate installation root. 60 | 61 | =item B<--sysroot>=F 62 | 63 | Set an alternate system root. See L. 64 | 65 | =item B<--cachedir>=F 66 | 67 | Set an alternate cache directory path. 68 | 69 | =item B<--backups> 70 | 71 | Search for F<.pac{save,orig,new}> files. By default F is searched and 72 | all known config files are checked; pass twice to search outside F. 73 | 74 | =item B<--group>=I 75 | 76 | Display any packages in group I that are not currently installed. May be specified multiple times. 77 | 78 | =item B<--missing-files> 79 | 80 | Check for missing package files. 81 | 82 | =item B<--unowned-files> 83 | 84 | Check for unowned files. See F under L for more 85 | information. 86 | 87 | =item B<--optional-for> 88 | 89 | Show which packages optionally depend on listed packages. 90 | 91 | =item B<--optional-deps> 92 | 93 | Take optional dependencies into account when listing unneeded packages and 94 | dependency loops. 95 | 96 | =item B<--help> 97 | 98 | Display usage information and exit. 99 | 100 | =item B<--version> 101 | 102 | Display version information and exit. 103 | 104 | =back 105 | 106 | =head1 FILES 107 | 108 | =over 109 | 110 | =item F 111 | 112 | INI-style configuration file listing paths to ignore when run with 113 | B<--unowned-files>. Paths which should always be ignored may be specified with 114 | C in the C section. Paths which should only be ignored 115 | if a particular package is installed can be listed under the section 116 | C using the package name as the option name and the path to 117 | be ignored as the value. All options can be specified multiple times. Paths 118 | may include shell-style globs. The installation root should not be included in 119 | paths. 120 | 121 | [Options] 122 | IgnoreUnowned = home/* 123 | IgnoreUnowned = proc/* 124 | IgnoreUnowned = mnt/* 125 | 126 | [PkgIgnoreUnowned] 127 | linux = boot/initramfs-linux.img 128 | linux = boot/initramfs-linux-fallback.img 129 | 130 | =back 131 | -------------------------------------------------------------------------------- /doc/pacsift.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacsift - query and filter packages 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacsift [options] ( )... 8 | pacsift (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | By default the intersection of matched packages are returned. If a field is 13 | provided multiple times the union of matches will be used for that field. 14 | 15 | If F is not connected to a terminal, packages to filter will be read 16 | from F. 17 | 18 | Combine with B to display brief package information similar to 19 | C and C: 20 | 21 | pacsift --name pacman | pacinfo --short 22 | 23 | =head1 OPTIONS 24 | 25 | =over 26 | 27 | =item B<--config>=F 28 | 29 | Set an alternate configuration file path. 30 | 31 | =item B<--dbext>=I 32 | 33 | Set an alternate sync database extension. 34 | 35 | =item B<--dbpath>=F 36 | 37 | Set an alternate database path. 38 | 39 | =item B<--root>=F 40 | 41 | Set an alternate installation root. 42 | 43 | =item B<--sysroot>=F 44 | 45 | Set an alternate system root. See L. 46 | 47 | =item B<--null>[=I] 48 | 49 | Set an alternate separator for values parsed from F. By default 50 | a newline C<\n> is used as the separator. If B<--null> is used without 51 | specifying I C will be used. 52 | 53 | =item B<--exists> 54 | 55 | Exit with a non-zero value if no matches are found. 56 | 57 | =item B<--not-exists> 58 | 59 | Exit with a non-zero value if matches are found. 60 | 61 | =item B<--invert> 62 | 63 | Return packages that B match the provided search terms. 64 | 65 | =item B<--any> 66 | 67 | Return the union of matched packages rather than the intersection. 68 | 69 | =item B<--exact> 70 | 71 | Match values exactly. 72 | 73 | =item B<--regex> 74 | 75 | Treat string values as extended case-insensitive regular expressions. 76 | 77 | =item B<--help> 78 | 79 | Display usage information and exit. 80 | 81 | =item B<--version> 82 | 83 | Display version information and exit. 84 | 85 | =back 86 | 87 | =head2 Filters 88 | 89 | Filters limit the initial set of packages to be searched. They may not be used 90 | if packages are provided on F and they are not affected by B<--invert> 91 | or B<--any>. 92 | 93 | =over 94 | 95 | =item B<--local> 96 | 97 | Search locally installed packages. Similar to: 98 | pacsift --repo=local | pacsift ... 99 | 100 | =item B<--sync> 101 | 102 | Search packages in sync databases. Similar to: 103 | pacsift --invert --repo=local | pacsift ... 104 | 105 | =item B<--cache> (B) 106 | 107 | Search packages in cache directories. 108 | 109 | =back 110 | 111 | =head2 String Fields 112 | 113 | By default string fields will use a case-insensitive substring search. If 114 | B<--exact> is used string fields must match I exactly. If C<--regex> is 115 | used I will be used as an extended case-insensitive regular expression. 116 | 117 | =over 118 | 119 | =item B<--repo>=I 120 | 121 | =item B<--name>=I 122 | 123 | =item B<--base>=I 124 | 125 | =item B<--description>=I 126 | 127 | =item B<--packager>=I 128 | 129 | =item B<--group>=I 130 | 131 | =item B<--owns-file>=I 132 | 133 | File paths must match the package database; no attempt is made to resolve the 134 | provided path. B when used with B<--exact> the installation root will 135 | be removed from I if present, otherwise I will be used exactly as 136 | provided. May be used with B or B to more easily search for 137 | the owner of installed files: 138 | 139 | pacsift --local --exact --owns-file="$(which pacsift)" 140 | 141 | =item B<--license>=I 142 | 143 | =item B<--url>=I 144 | 145 | =back 146 | 147 | =head2 Dependency Fields 148 | 149 | Dependencies may be specified in the format C<< [>. 150 | C may be any of the following: C<=>, C<< < >>, C<< <= >>, C<< > >>, 151 | C<< >= >>. If C is provided the package dependency must be an exact 152 | match to the search term. If C is omitted only C will be 153 | compared unless B<--exact> is used. If C<--exact> is used and C is 154 | not provided only dependencies with no version specified will be matched. 155 | 156 | Dependency fields are not affected by B<--regex>. 157 | 158 | =over 159 | 160 | =item B<--provides> 161 | 162 | =item B<--conflicts> 163 | 164 | =item B<--replaces> 165 | 166 | =item B<--depends> 167 | 168 | =item B<--optdepends> 169 | 170 | =back 171 | 172 | =head2 Size Fields 173 | 174 | Size fields may be prefixed with any of the following comparisons: C<=>, C, 175 | C<< < >>, C<< <= >>, C<< > >>, C<< >= >>. If no comparison is provided C<=> 176 | will be used. The size may be followed by abbreviated unit. Units are 177 | case-sensitive. 178 | 179 | =over 180 | 181 | =item B<--size> 182 | 183 | =item B<--installed-size> 184 | 185 | =item B<--download-size> 186 | 187 | =back 188 | 189 | =head2 Date Fields 190 | 191 | Date fields may be prefixed with any of the following comparisons: C<=>, C, 192 | C<< < >>, C<< <= >>, C<< > >>, C<< >= >>. If no comparison is provided C<=> 193 | will be used. Dates may be provided as seconds since the epoch or any of the 194 | following C formats: C<%Y-%m-%d>, C<%Y-%m-%d %H:%M>, 195 | C<%Y-%m-%d %H:%M:%S>. 196 | 197 | =over 198 | 199 | =item B<--install-date> 200 | 201 | =item B<--build-date> 202 | 203 | =back 204 | 205 | =head2 Other 206 | 207 | =over 208 | 209 | =item B<--satisfies>=I 210 | 211 | Search for packages that satisfy I. 212 | 213 | =back 214 | 215 | =head1 EXAMPLES 216 | 217 | =over 218 | 219 | =item Find broken packages: 220 | 221 | pacsift --packager allan 222 | 223 | =item Find large installed packages: 224 | 225 | pacsift --local --size '>1.5GB' 226 | 227 | =item Find packages with either C or C in their description: 228 | 229 | pacsift --description pacman --description alpm 230 | 231 | =item Find packages with both C and C in their description: 232 | 233 | pacsift --description pacman | pacsift --description alpm 234 | 235 | =item Check if a package is installed: 236 | 237 | pacsift --local --exists --satisfies pacman && echo "pacman is installed" 238 | 239 | =back 240 | 241 | =head1 CAVEATS 242 | 243 | B determines whether or not to read packages from F based on 244 | a naive check using L. If B is called in an environment, 245 | such as a shell function or script being used in a pipe, where F is not 246 | connected to a terminal but does not contain packages to filter, B 247 | should be called with F closed. For POSIX-compatible shells, this can 248 | be done with C<< <&- >>. 249 | -------------------------------------------------------------------------------- /doc/pacsync.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacsync - update sync databases 4 | 5 | =head1 SYNOPSIS 6 | 7 | pacsync [options] []... 8 | pacsync (--help|--version) 9 | 10 | =head1 DESCRIPTION 11 | 12 | Update sync databases. If no I names are provided all databases will 13 | by updated. 14 | 15 | =head1 OPTIONS 16 | 17 | =over 18 | 19 | =item B<--config>=F 20 | 21 | Set an alternate configuration file path. 22 | 23 | =item B<--dbext>=I 24 | 25 | Set an alternate sync database extension. 26 | 27 | =item B<--dbpath>=F 28 | 29 | Set an alternate database path. 30 | 31 | =item B<--logfile>=F 32 | 33 | Set an alternate log file path. 34 | 35 | =item B<--no-timeout> 36 | 37 | Disable low-speed timeouts for downloads. 38 | 39 | =item B<--root>=F 40 | 41 | Set an alternate installation root. 42 | 43 | =item B<--sysroot>=F 44 | 45 | Set an alternate system root. See L. 46 | 47 | =item B<--debug> 48 | 49 | Display additional debugging information. 50 | 51 | =item B<--updated> 52 | 53 | Return true only if a database was actually updated. 54 | 55 | =item B<--force> 56 | 57 | Update databases even if already up-to-date. 58 | 59 | =item B<--help> 60 | 61 | Display usage information and exit. 62 | 63 | =item B<--version> 64 | 65 | Display version information and exit. 66 | 67 | =back 68 | -------------------------------------------------------------------------------- /doc/pacutils-mtree.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacutils-mtree - read mtree data for installed packages 4 | 5 | =head1 SYNOPSIS 6 | 7 | #include 8 | 9 | typedef struct pu_mtree_t { 10 | char *path; 11 | char *type; 12 | uid_t uid; 13 | gid_t gid; 14 | mode_t mode; 15 | off_t size; 16 | char *md5digest; 17 | char *sha256digest; 18 | } pu_mtree_t; 19 | 20 | typedef struct { 21 | FILE *stream; 22 | int eof; 23 | pu_mtree_t defaults; 24 | } pu_mtree_reader_t; 25 | 26 | pu_mtree_reader_t *pu_mtree_reader_open_stream(FILE *stream); 27 | pu_mtree_reader_t *pu_mtree_reader_open_package(alpm_handle_t *h, alpm_pkg_t *p); 28 | pu_mtree_t *pu_mtree_reader_next(pu_mtree_reader_t *reader, pu_mtree_t *dest); 29 | void pu_mtree_reader_free(pu_mtree_reader_t *reader); 30 | void pu_mtree_free(pu_mtree_t *mtree); 31 | 32 | /* deprecated */ 33 | alpm_list_t *pu_mtree_load_pkg_mtree(alpm_handle_t *handle, alpm_pkg_t *pkg); 34 | 35 | =head1 DESCRIPTION 36 | 37 | Package mtree reading functions are provided to fill gaps in libarchive's mtree 38 | reader, notably C and C which are currently not read 39 | by libarchive. Support is incomplete and should only be used to supplement 40 | libalpm's native mtree reader. 41 | 42 | =over 43 | 44 | =item pu_mtree_reader_t *pu_mtree_reader_open_stream(FILE *stream); 45 | 46 | Open a file stream for parsing. 47 | 48 | =item pu_mtree_reader_t *pu_mtree_reader_open_package(alpm_handle_t *h, alpm_pkg_t *p); 49 | 50 | Open an installed package's mtree file for parsing. Results are undefined if 51 | C

is not a locally installed package. 52 | 53 | =item pu_mtree_t *pu_mtree_reader_next(pu_mtree_reader_t *reader, pu_mtree_t *dest); 54 | 55 | Read and return the next entry in the mtree file. If C is C a new 56 | C object will be allocated that should be freed by the caller. 57 | Otherwise, it will be filled with the parsed data. Internally allocated memory 58 | will automatically be freed as needed. 59 | 60 | =item void pu_mtree_free(pu_mtree_t *mtree); 61 | 62 | Free a C struct. 63 | 64 | =item void pu_mtree_reader_free(pu_mtree_reader_t *reader); 65 | 66 | Free a C object. 67 | 68 | =item alpm_list_t *pu_mtree_load_pkg_mtree(alpm_handle_t *handle, alpm_pkg_t *pkg); 69 | 70 | Returns a list of mtree entries for C. B: use 71 | C instead. 72 | 73 | =back 74 | 75 | =head1 EXAMPLES 76 | 77 | =over 78 | 79 | =item Print file md5sums for a package: 80 | 81 | pu_mtree_t *m; 82 | pu_mtree_reader_t *r; 83 | 84 | if((r = pu_mtree_reader_open_package(handle, pkg)) == NULL) { 85 | fprintf(stderr, "error: unable to load mtree data for '%s'\n", 86 | alpm_pkg_get_name(pkg)); 87 | return; 88 | } 89 | 90 | while((m = pu_mtree_reader_next(r, NULL))) { 91 | const char *md5 = m->md5digest; 92 | printf("%s: %s\n", m->path, 93 | md5 && md5[0] != '\0' ? md5 : "(no md5sum provided)"); 94 | pu_mtree_free(m); 95 | } 96 | if(!reader->eof) { 97 | fprintf(stderr, "error: unable to read mtree data for '%s'\n", 98 | alpm_pkg_get_name(pkg)); 99 | } 100 | 101 | pu_mtree_reader_free(r); 102 | 103 | =back 104 | 105 | =head1 SEE ALSO 106 | 107 | =over 108 | 109 | =item alpm_pkg_mtree_open(3), alpm_pkg_mtree_next(3), alpm_pkg_mtree_close(3) 110 | 111 | =item mtree(5) - description of C members 112 | 113 | =back 114 | -------------------------------------------------------------------------------- /doc/pacutils-sysroot.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacutils-sysroot - managing a mounted guest system 4 | 5 | =head1 DESCRIPTION 6 | 7 | =head2 Managing Guests with --root 8 | 9 | When a libalpm-based installation becomes broken to the point that the package 10 | manager itself can no longer be used, it must be fixed by loading a working 11 | environment and mounting the installation in need of repair as a guest. 12 | Typically this is done by setting the installation root to the guest with the 13 | B<--root> option. Libalpm only uses the installation root to determine where 14 | to install and remove files during package transactions. It is completely 15 | independent of all other configuration options. This makes using B<--root> 16 | unreliable for managing a mounted guest. 17 | 18 | The first issue is that when changing the installation root, the host system's 19 | configuration is still used. Because the installation root is a configuration 20 | option (C) it cannot be used to load an alternate configuration file. 21 | Even if B<--config> is added to load the guest configuration, any C'd 22 | paths will still be resolved relative to the host's root. The only way to 23 | actually use the guest's configuration without using C is to use 24 | B<--config> and manually add the mount path to all C paths. 25 | 26 | Now the guest configuration files are being used, but all configured paths will 27 | still refer to paths under to the host's root, not the guest's. If an 28 | installation root is explicitly provided, pacman and pacutils will set defaults 29 | for some, but not all, configuration paths to be underneath it. This typically 30 | works, because those paths are rarely explicitly set, so the defaults generally 31 | do the correct thing. If they have been set, however, the configured values 32 | will be used without modification. In order to reliably operate on the guest, 33 | all configuration paths must be set relative to the installation root (except 34 | for the database and log file paths which will default to paths inside the 35 | installation root if unset). 36 | 37 | =head2 Introducing the Sysroot 38 | 39 | This is a significant amount of work just to run operate on a mounted system. 40 | In order to allow reliable operating on a mounted guest, pacman and pacutils 41 | have added the concept of a "sysroot". The sysroot is what is commonly 42 | intended when using the B<--root> option; the program will operate as if the 43 | sysroot were actually the filesystem root. This is similar to using C 44 | to enter the mounted system before running the program, but still runs the 45 | host's copy. There are two ways to do this: chroot into the sysroot shortly 46 | after startup prior to reading any configuration or prepend the sysroot to all 47 | paths. 48 | 49 | Using C directly can cause problems with libraries that use delayed 50 | loading of configuration files or shared objects. Notably, glibc delays 51 | loading certain objects until their functionality is actually used. This can 52 | cause a mismatch between components loaded prior to the C and those 53 | loaded after. To avoid this problem, libalpm should generally be configured to 54 | use paths under the sysroot without actually calling C. 55 | 56 | Pacutils provides sysroot-aware versions of its configuration parsing functions 57 | to simplify the process. Note that the sysroot is prepended to all paths 58 | during config resolution. Programs using the sysroot configuration parsing 59 | routines should B prepend the sysroot to configuration paths provided on 60 | the command line. 61 | 62 | =head1 EXAMPLES 63 | 64 | #include 65 | #include 66 | #include 67 | 68 | enum { 69 | FLAG_CONFIG = 1, 70 | FLAG_SYSROOT, 71 | }; 72 | 73 | int main(int argc, char *argv[]) { 74 | const char *config_file = "/etc/pacman.conf"; 75 | const char *sysroot = NULL; 76 | alpm_handle_t *handle = NULL; 77 | pu_config_t *config = NULL; 78 | 79 | struct option long_opts[] = { 80 | { "config" , required_argument , NULL , FLAG_CONFIG } , 81 | { "sysroot" , required_argument , NULL , FLAG_SYSROOT } , 82 | { 0, 0, 0, 0 }, 83 | }; 84 | while((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { 85 | switch(c) { 86 | case FLAG_CONFIG: config_file = optarg; break; 87 | case FLAG_SYSROOT: sysroot = optarg; break; 88 | case '?': return 1; /* getopt_long already printed an error message */ 89 | } 90 | } 91 | if((config = pu_ui_config_load_sysroot(NULL, config_file, sysroot)) == NULL) 92 | { return 1; } /* load_sysroot already printed an error message */ 93 | 94 | if(!(handle = pu_initialize_handle_from_config(config))) { 95 | fprintf(stderr, "error: failed to initialize alpm.\n"); 96 | return 1; 97 | } 98 | 99 | puts(alpm_option_get_root(handle)); 100 | 101 | pu_config_free(config); 102 | alpm_release(handle); 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /doc/pacutils-uix.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pacutils-uix - low-level utility wrappers with error checking 4 | 5 | =head1 SYNOPSIS 6 | 7 | #include 8 | 9 | =head1 DESCRIPTION 10 | 11 | Wrappers for low-level utility functions that provide basic error messages and 12 | immediately exit the program on failure. Primarily intended for front-end use 13 | for setup where failure is non-recoverable. C or C can be used 14 | to perform cleanup if needed. 15 | 16 | =over 17 | 18 | =item char *pu_uix_strdup(const char *string); 19 | 20 | =item void *pu_uix_calloc(size_t nmemb, size_t size) 21 | 22 | =item void *pu_uix_malloc(size_t size) 23 | 24 | =item void *pu_uix_realloc(void *ptr, size_t size) 25 | 26 | =item alpm_list_t *pu_uix_list_append(alpm_list_t **list, void *data); 27 | 28 | =item alpm_list_t *pu_uix_list_append_strdup(alpm_list_t **list, char *data); 29 | 30 | =item void pu_uix_read_list_from_fdstr(const char *fdstr, int sep, alpm_list_t **dest); 31 | 32 | =item void pu_uix_read_list_from_path(const char *file, int sep, alpm_list_t **dest); 33 | 34 | =item void pu_uix_read_list_from_stream(FILE *stream, int sep, alpm_list_t **dest, const char *label); 35 | 36 | =item void pu_uix_process_std_arg(const char *arg, int sep, alpm_list_t **dest); 37 | 38 | =back 39 | 40 | =head1 EXAMPLES 41 | 42 | #include 43 | 44 | int main(int argc, char *argv[]) { 45 | alpm_list_t *args = NULL; 46 | for(int i = 1; i < argc; i++) { 47 | pu_uix_list_append_strdup(&args, argv[i]); 48 | } 49 | 50 | // is guaranteed to be a valid list with copies of program arguments 51 | for(alpm_list_t *i = args; i; i = i->next) { 52 | ... 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /ext/globdir.c/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /ext/globdir.c/COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2017-2020 Andrew Gregory 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ext/globdir.c/Makefile: -------------------------------------------------------------------------------- 1 | check: ## build and run test suite 2 | $(MAKE) -C t/ check 3 | 4 | clean: ## clean all built artifacts 5 | $(MAKE) -C t/ $@ 6 | 7 | update-ext: ## pull and commit updates to external modules 8 | git subtree pull --squash --prefix=ext/tap.c https://github.com/andrewgregory/tap.c.git master 9 | 10 | help: ## show this help 11 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 12 | 13 | .PHONY: check clean update-ext help 14 | -------------------------------------------------------------------------------- /ext/globdir.c/README.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | globdir.c - expand glob patterns relative to a specific directory 4 | 5 | =head1 SYNOPSIS 6 | 7 | glob(3) only expands glob patterns relative to the current working directory. 8 | In order to expand glob patterns relative to a different directory the caller 9 | must either chdir(2) into the directory or alter the glob to refer to that 10 | directory including accounting for any glob-characters in the path. C 11 | provides a neater and more direct way to expand patterns outside the current 12 | directory. 13 | 14 | =head1 DESCRIPTION 15 | 16 | =over 17 | 18 | =item typedef glob_t globdir_t; 19 | 20 | Structure used to store the results of the glob expansion. Includes the same 21 | members POSIX requires for C. Currently C is just an alias 22 | for C but this may change in a future revision. 23 | 24 | =item void globdirfree(globdir_t *g); 25 | 26 | Free the memory associated with a C structure but not the structure 27 | itself. This should be used in place of C for the results of 28 | C and C. 29 | 30 | =item int globdir(const char *dir, const char *pattern, int flags, 31 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 32 | 33 | Expand a glob pattern relative to a specific directory C

. All other 34 | arguments are identical to their C counterparts. C may include 35 | all flags specified by POSIX and C if defined in C. 36 | 37 | =item int globat(int fd, const char *pattern, int flags, 38 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 39 | 40 | Identical to C except the base directory is specified as an open 41 | directory file descriptor C rather than a string path. 42 | 43 | =item int globdir_glob(const char *pattern, int flags, 44 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 45 | 46 | Direct C replacement except that C is a C instead of 47 | C and C are limited to those specified by POSIX and 48 | C (if defined on the user's system). Equivalent to: 49 | 50 | globat(AT_FDCWD, pattern, flags, errfunc, pglob); 51 | 52 | =item int globdir_str_is_pattern(const char *str, int noescape); 53 | 54 | Check if C is a POSIX glob pattern. Backslash escaping will not be 55 | performed if C is true. 56 | 57 | =item char *globdir_escape_pattern(const char *pattern); 58 | 59 | Return a copy of C with all special characters escaped. The returned 60 | string needs to be freed by the caller. This can be used to accomplish 61 | relative globing with glob(3) as long as C or other 62 | syntax-altering flags (such as glibc's C) are not used: 63 | 64 | int globdir(const char *dir, const char *pattern, int flags, 65 | int(*errfunc), (const char *epath, int eerrno), glob_t *pglob) { 66 | int ret; 67 | char *escaped_dir, relpattern[PATH_MAX]; 68 | if( flags & GLOB_NOESCAPE ) { errno = EINVAL; return GLOB_ABORTED; } 69 | if( (escaped_dir = globdir_escape_pattern(dir)) == NULL) { return GLOB_NOSPACE; } 70 | if( snprintf(relpattern, PATH_MAX, "%s/%s", escaped_dir, pattern) >= PATH_MAX) { return GLOB_NOSPACE; } 71 | ret = glob(relpattern, flags, errfunc, &pglob); 72 | free(escaped_pattern); 73 | return ret; 74 | } 75 | 76 | =back 77 | 78 | =head1 EXAMPLES 79 | 80 | #include 81 | 82 | #include "globdir.c" 83 | 84 | int main(int argc, char *argv[]) { 85 | const char *base_dir, *glob_str; 86 | globdir_t globbuf; 87 | size_t i; 88 | 89 | if(argc != 3) { 90 | fputs("error: wrong number of arguments (expected 2)\n", stderr); 91 | return 1; 92 | } 93 | 94 | base_dir = argv[1]; 95 | glob_str = argv[2]; 96 | 97 | switch(globdir(base_dir, glob_str, GLOB_ERR, NULL, &globbuf)) { 98 | case GLOB_ABORTED: 99 | fputs("error: directory read error\n", stderr); 100 | return 1; 101 | case GLOB_NOSPACE: 102 | fputs("error: memory exhausted\n", stderr); 103 | return 1; 104 | case GLOB_NOMATCH: 105 | fputs("error: no matches\n", stderr); 106 | return 1; 107 | default: 108 | for(i = 0; i < globbuf.gl_pathc; i++) { 109 | puts(globbuf.gl_pathv[i]); 110 | } 111 | globdirfree(&globbuf); 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | =head1 COPYRIGHT AND LICENSE 118 | 119 | Copyright 2017-2020 Andrew Gregory 120 | 121 | Permission is hereby granted, free of charge, to any person obtaining a copy 122 | of this software and associated documentation files (the "Software"), to 123 | deal in the Software without restriction, including without limitation the 124 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 125 | sell copies of the Software, and to permit persons to whom the Software is 126 | furnished to do so, subject to the following conditions: 127 | 128 | The above copyright notice and this permission notice shall be included in 129 | all copies or substantial portions of the Software. 130 | 131 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 132 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 133 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 134 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 135 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 136 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 137 | IN THE SOFTWARE. 138 | -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewgregory/pacutils/d3998fef76cb930195664db45dcc38ac8a72e410/ext/globdir.c/ext/tap.c/.gitignore -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/t/.gitignore: -------------------------------------------------------------------------------- 1 | *.gcda 2 | *.gcno 3 | *.gcov 4 | *.t 5 | tmp_* 6 | -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/t/01-sanity.c: -------------------------------------------------------------------------------- 1 | #include "../tap.c" 2 | 3 | /* just verify the library compiles */ 4 | 5 | int main(void) { 6 | tap_plan(1); 7 | tap_ok(1, "compilation successful"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/t/10-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../tap.c" 7 | 8 | static char outfile[] = "tmp_10-basic_XXXXXX"; 9 | static char expfile[] = "expected/10-basic.t.out"; 10 | 11 | static void test(void) { 12 | tap_plan(0); 13 | tap_plan(1); 14 | tap_plan(2); 15 | 16 | tap_skip_all(NULL); 17 | tap_skip_all("foo %s", "bar"); 18 | 19 | tap_skip(0, NULL); 20 | tap_skip(1, "foo %s", "bar"); 21 | tap_skip(2, NULL); 22 | 23 | tap_diag("foo"); 24 | tap_diag("foo %s", "bar"); 25 | 26 | tap_bail(NULL); 27 | tap_bail("foo %s", "bar"); 28 | 29 | tap_ok(0, NULL); 30 | tap_ok(1, NULL); 31 | tap_ok(0, "%s", "foo"); 32 | tap_ok(1, "%s", "foo"); 33 | 34 | tap_is_str("foo", "foo", NULL); 35 | tap_is_str("foo", "bar", NULL); 36 | tap_is_str("foo", "foo", "foo %s", "bar"); 37 | tap_is_str("foo", "bar", "foo %s", "bar"); 38 | tap_is_str(NULL, NULL, NULL); 39 | tap_is_str(NULL, "bar", NULL); 40 | 41 | tap_is_int(1, 1, NULL); 42 | tap_is_int(1, 0, NULL); 43 | tap_is_int(1, 1, "foo %s", "bar"); 44 | tap_is_int(1, 0, "foo %s", "bar"); 45 | 46 | tap_is_float(1.0, 1.0, 0.1, NULL); 47 | tap_is_float(1.0, 2.0, 0.1, NULL); 48 | tap_is_float(1.0, 1.0, 0.1, "foo %s", "bar"); 49 | tap_is_float(1.0, 2.0, 0.1, "foo %s", "bar"); 50 | 51 | tap_done_testing(); 52 | } 53 | 54 | static void run(void) { 55 | test(); 56 | tap_todo("FOO TODO"); 57 | test(); 58 | } 59 | 60 | int main(void) { 61 | int sfd, efd, ofd, ret = 0; 62 | FILE *efile, *ofile; 63 | char ebuf[50], obuf[50]; 64 | int line = 0; 65 | 66 | /* redirect output to temp file */ 67 | ofd = mkstemp(outfile); 68 | sfd = dup(STDOUT_FILENO); 69 | efd = dup(STDERR_FILENO); 70 | dup2(ofd, STDOUT_FILENO); 71 | dup2(ofd, STDERR_FILENO); 72 | close(ofd); 73 | 74 | run(); 75 | 76 | /* flush and restore output */ 77 | fflush(stdout); 78 | fflush(stderr); 79 | dup2(sfd, STDOUT_FILENO); 80 | dup2(efd, STDERR_FILENO); 81 | close(sfd); 82 | close(efd); 83 | 84 | efile = fopen(expfile, "r"); 85 | ofile = fopen(outfile, "r"); 86 | 87 | puts("1..1"); 88 | while(1) { 89 | char *eres = fgets(ebuf, 50, efile); 90 | char *ores = fgets(obuf, 50, ofile); 91 | ++line; 92 | if(ores == NULL && eres == NULL) { 93 | puts("ok 1 - output matches expected"); 94 | break; 95 | } else if(ores && eres && strcmp(ebuf, obuf) == 0) { 96 | /* match */ 97 | } else { 98 | fprintf(stdout, "not ok 1 - mismatch beginning on line %d\n", line); 99 | fprintf(stderr, " # got: '%s'\n", obuf); 100 | fprintf(stderr, " # expected: '%s'\n", ebuf); 101 | ret = 1; 102 | break; 103 | } 104 | } 105 | 106 | fclose(efile); 107 | fclose(ofile); 108 | 109 | if(getenv("TAP_SAVE")) { 110 | fprintf(stderr, "%s output saved to %s\n", __FILE__, outfile); 111 | } else { 112 | unlink(outfile); 113 | } 114 | 115 | return ret; 116 | } 117 | -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/t/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -Wextra -Wpedantic -Werror -g 2 | 3 | override CPPFLAGS += -I.. 4 | 5 | TESTS = 01-sanity.t 10-basic.t 6 | 7 | 01-sanity.t: CFLAGS += -std=c99 -pedantic -Werror 8 | 9 | %.t: %.c ../tap.c 10 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ 11 | 12 | check: tests 13 | prove . 14 | 15 | tests: $(TESTS) 16 | 17 | all: tests 18 | 19 | Weverything: CC = clang 20 | Weverything: CFLAGS += -Weverything 21 | Weverything: check 22 | 23 | gcov: CC = gcc 24 | gcov: CFLAGS += -fprofile-arcs -ftest-coverage 25 | gcov: check 26 | gcov $(TESTS) 27 | 28 | clean: 29 | $(RM) $(TESTS) 30 | $(RM) tmp_* 31 | $(RM) *.gcov *.gcda *.gcno 32 | 33 | .PHONY: all clean check gcov tests Weverything 34 | -------------------------------------------------------------------------------- /ext/globdir.c/ext/tap.c/t/expected/10-basic.t.out: -------------------------------------------------------------------------------- 1 | 1..0 2 | 1..1 3 | 1..2 4 | 1..0 # SKIP 5 | 1..0 # SKIP foo bar 6 | ok 1 # SKIP foo bar 7 | ok 2 # SKIP 8 | ok 3 # SKIP 9 | # foo 10 | # foo bar 11 | Bail out! 12 | Bail out! foo bar 13 | not ok 4 14 | # Failed test at 10-basic.c line 29. 15 | ok 5 16 | not ok 6 - foo 17 | # Failed test at 10-basic.c line 31. 18 | ok 7 - foo 19 | ok 8 20 | not ok 9 21 | # Failed test at 10-basic.c line 35. 22 | # got: 'foo' 23 | # expected: 'bar' 24 | ok 10 - foo bar 25 | not ok 11 - foo bar 26 | # Failed test at 10-basic.c line 37. 27 | # got: 'foo' 28 | # expected: 'bar' 29 | ok 12 30 | not ok 13 31 | # Failed test at 10-basic.c line 39. 32 | # got: '(null)' 33 | # expected: 'bar' 34 | ok 14 35 | not ok 15 36 | # Failed test at 10-basic.c line 42. 37 | # got: '1' 38 | # expected: '0' 39 | ok 16 - foo bar 40 | not ok 17 - foo bar 41 | # Failed test at 10-basic.c line 44. 42 | # got: '1' 43 | # expected: '0' 44 | ok 18 45 | not ok 19 46 | # Failed test at 10-basic.c line 47. 47 | # got: '1.000000' 48 | # expected: '2.000000' 49 | # delta: '1.000000' 50 | # allowed: '0.100000' 51 | ok 20 - foo bar 52 | not ok 21 - foo bar 53 | # Failed test at 10-basic.c line 49. 54 | # got: '1.000000' 55 | # expected: '2.000000' 56 | # delta: '1.000000' 57 | # allowed: '0.100000' 58 | 1..21 59 | 1..0 60 | 1..1 61 | 1..2 62 | 1..0 # SKIP 63 | 1..0 # SKIP foo bar 64 | ok 22 # SKIP foo bar 65 | ok 23 # SKIP 66 | ok 24 # SKIP 67 | # foo 68 | # foo bar 69 | Bail out! 70 | Bail out! foo bar 71 | not ok 25 # TODO FOO TODO 72 | # Failed (TODO) test at 10-basic.c line 29. 73 | ok 26 # TODO FOO TODO 74 | not ok 27 - foo # TODO FOO TODO 75 | # Failed (TODO) test at 10-basic.c line 31. 76 | ok 28 - foo # TODO FOO TODO 77 | ok 29 # TODO FOO TODO 78 | not ok 30 # TODO FOO TODO 79 | # Failed (TODO) test at 10-basic.c line 35. 80 | # got: 'foo' 81 | # expected: 'bar' 82 | ok 31 - foo bar # TODO FOO TODO 83 | not ok 32 - foo bar # TODO FOO TODO 84 | # Failed (TODO) test at 10-basic.c line 37. 85 | # got: 'foo' 86 | # expected: 'bar' 87 | ok 33 # TODO FOO TODO 88 | not ok 34 # TODO FOO TODO 89 | # Failed (TODO) test at 10-basic.c line 39. 90 | # got: '(null)' 91 | # expected: 'bar' 92 | ok 35 # TODO FOO TODO 93 | not ok 36 # TODO FOO TODO 94 | # Failed (TODO) test at 10-basic.c line 42. 95 | # got: '1' 96 | # expected: '0' 97 | ok 37 - foo bar # TODO FOO TODO 98 | not ok 38 - foo bar # TODO FOO TODO 99 | # Failed (TODO) test at 10-basic.c line 44. 100 | # got: '1' 101 | # expected: '0' 102 | ok 39 # TODO FOO TODO 103 | not ok 40 # TODO FOO TODO 104 | # Failed (TODO) test at 10-basic.c line 47. 105 | # got: '1.000000' 106 | # expected: '2.000000' 107 | # delta: '1.000000' 108 | # allowed: '0.100000' 109 | ok 41 - foo bar # TODO FOO TODO 110 | not ok 42 - foo bar # TODO FOO TODO 111 | # Failed (TODO) test at 10-basic.c line 49. 112 | # got: '1.000000' 113 | # expected: '2.000000' 114 | # delta: '1.000000' 115 | # allowed: '0.100000' 116 | 1..42 117 | -------------------------------------------------------------------------------- /ext/globdir.c/globdir.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef GLOBDIR_H 24 | #define GLOBDIR_H 25 | 26 | #include 27 | 28 | typedef glob_t globdir_t; 29 | 30 | void globdirfree(globdir_t *g); 31 | 32 | int globat(int fd, const char *pattern, int flags, 33 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 34 | 35 | int globdir(const char *dir, const char *pattern, int flags, 36 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 37 | 38 | int globdir_glob(const char *pattern, int flags, 39 | int (*errfunc) (const char *epath, int eerrno), globdir_t *pglob); 40 | 41 | int globdir_str_is_pattern(const char *string, int noescape); 42 | 43 | char *globdir_escape_pattern(const char *pattern); 44 | 45 | #endif /* GLOBDIR_H */ 46 | -------------------------------------------------------------------------------- /ext/globdir.c/t/.gitignore: -------------------------------------------------------------------------------- 1 | *.gcda 2 | *.gcno 3 | *.gcov 4 | *.t 5 | gmon.out 6 | -------------------------------------------------------------------------------- /ext/globdir.c/t/.proverc: -------------------------------------------------------------------------------- 1 | --exec=./runtest.sh 2 | -------------------------------------------------------------------------------- /ext/globdir.c/t/01-sanity.c: -------------------------------------------------------------------------------- 1 | #include "../ext/tap.c/tap.c" 2 | 3 | #include "../globdir.c" 4 | 5 | /* just verify the library compiles */ 6 | 7 | int main(void) { 8 | tap_plan(1); 9 | tap_ok(1, "compilation successful"); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /ext/globdir.c/t/10-escape_pattern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../ext/tap.c/tap.c" 5 | 6 | #include "../globdir.c" 7 | 8 | #define IS(p, e) { \ 9 | char *escaped = globdir_escape_pattern(p); \ 10 | tap_is_str(escaped, e, "%s -> %s", p ? p : "NULL", e ? e : "NULL"); \ 11 | free(escaped); \ 12 | } 13 | 14 | int main(void) { 15 | tap_plan(4); 16 | 17 | IS( "foo/bar", "foo/bar" ); 18 | IS( "fo*/bar", "fo\\*/bar" ); 19 | IS( "\\?*[", "\\\\\\?\\*\\[" ); 20 | IS( NULL, NULL ); 21 | 22 | return tap_finish(); 23 | } 24 | -------------------------------------------------------------------------------- /ext/globdir.c/t/10-is_pattern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../ext/tap.c/tap.c" 5 | 6 | #include "../globdir.c" 7 | 8 | int main(void) { 9 | tap_plan(8); 10 | 11 | tap_ok( globdir_str_is_pattern("foo*", 0), "foo*" ); 12 | tap_ok( globdir_str_is_pattern("foo?", 0), "foo?" ); 13 | tap_ok( globdir_str_is_pattern("fo[o]", 0), "fo[o]" ); 14 | tap_ok( globdir_str_is_pattern("foo\\*", 1), "foo\\* (NOESCAPE)" ); 15 | 16 | tap_ok( !globdir_str_is_pattern("foo", 0), "foo" ); 17 | tap_ok( !globdir_str_is_pattern("foo\\*", 0), "foo\\*" ); 18 | tap_ok( !globdir_str_is_pattern("foo\\", 0), "foo\\" ); 19 | tap_ok( !globdir_str_is_pattern("foo\\", 1), "foo\\ (NOESCAPE)" ); 20 | 21 | return tap_finish(); 22 | } 23 | -------------------------------------------------------------------------------- /ext/globdir.c/t/10-split_pattern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../ext/tap.c/tap.c" 5 | 6 | #include "../globdir.c" 7 | 8 | #define IS(g, ...) is(g, #g, __LINE__, __VA_ARGS__) 9 | 10 | void is(char **got, char *msg, int line, ...) { 11 | char *e, **g = got; 12 | va_list ap, a; 13 | va_start(ap, line); 14 | 15 | va_copy(a, ap); 16 | 17 | e = va_arg(a, char*); 18 | while(*g && e) { 19 | if(strcmp(*g, e) != 0) { break; } 20 | g++; 21 | e = va_arg(a, char*); 22 | } 23 | va_end(a); 24 | 25 | if(*g != NULL || e != NULL) { 26 | g = got; 27 | _tap_ok(__FILE__, line, 0, msg); 28 | tap_diag(" got:"); 29 | while(*g) { tap_diag(" : '%s'", *g++); } 30 | tap_diag(" expected:"); 31 | while((e = va_arg(ap, char*))) { tap_diag(" : '%s'", e); } 32 | } else { 33 | _tap_ok(__FILE__, line, 1, msg); 34 | } 35 | _globdir_freepattern(got); 36 | } 37 | 38 | int main(void) { 39 | tap_plan(4); 40 | 41 | IS(_globdir_split_pattern("foo/bar"), "foo", "bar", NULL); 42 | IS(_globdir_split_pattern("foo/bar/"), "foo", "bar", "/", NULL); 43 | IS(_globdir_split_pattern("/foo/bar/"), "/", "foo", "bar", "/", NULL); 44 | IS(_globdir_split_pattern("//foo//bar//"), "/", "foo", "bar", "/", NULL); 45 | 46 | return tap_finish(); 47 | } 48 | -------------------------------------------------------------------------------- /ext/globdir.c/t/20-globat.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/20-globat.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | int fd; 13 | globdir_t globbuf; 14 | 15 | ASSERT(atexit(cleanup) == 0); 16 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 17 | ASSERT(tmpdir = mkdtemp(template)); 18 | ASSERT(chdir(tmpdir) == 0); 19 | ASSERT(mkdir("foo", 0755) == 0); 20 | ASSERT(mkdir("foo/bar", 0755) == 0); 21 | ASSERT(touch("foo/bar/quux", 0755) == 0); 22 | ASSERT(touch("foo/baz", 0755) == 0); 23 | ASSERT((fd = open("foo", O_DIRECTORY)) != -1); 24 | 25 | tap_plan(12); 26 | 27 | tap_is_int(globat(fd, "ba*", 0, NULL, &globbuf), 0, "globat return"); 28 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 29 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 30 | tap_is_str(globbuf.gl_pathv[1], "baz", "glob path 2"); 31 | globdirfree(&globbuf); 32 | 33 | tap_is_int(globat(fd, "ba*/", 0, NULL, &globbuf), 0, "globat return"); 34 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 35 | tap_is_str(globbuf.gl_pathv[0], "bar/", "glob path 1"); 36 | globdirfree(&globbuf); 37 | 38 | tap_is_int(globat(fd, "bar/quux", 0, NULL, &globbuf), 0, "globat return"); 39 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 40 | tap_is_str(globbuf.gl_pathv[0], "bar/quux", "glob path 1"); 41 | globdirfree(&globbuf); 42 | 43 | tap_is_int(globat(fd, "quux", 0, NULL, &globbuf), GLOB_NOMATCH, "globat return"); 44 | tap_is_int(globbuf.gl_pathc, 0, "glob count"); 45 | globdirfree(&globbuf); 46 | 47 | return tap_finish(); 48 | } 49 | -------------------------------------------------------------------------------- /ext/globdir.c/t/30-globdir.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/30-globdir.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | int cwd; 13 | globdir_t globbuf; 14 | 15 | ASSERT(atexit(cleanup) == 0); 16 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 17 | ASSERT(tmpdir = mkdtemp(template)); 18 | ASSERT(chdir(tmpdir) == 0); 19 | ASSERT(mkdir("foo", 0755) == 0); 20 | ASSERT(mkdir("foo/bar", 0755) == 0); 21 | 22 | tap_plan(3); 23 | 24 | tap_is_int(globdir("foo", "ba*", 0, NULL, &globbuf), 0, "globdir return"); 25 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 26 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 27 | globdirfree(&globbuf); 28 | 29 | return tap_finish(); 30 | } 31 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_append.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/40-glob_append.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | globdir_t globbuf; 13 | 14 | ASSERT(atexit(cleanup) == 0); 15 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 16 | ASSERT(tmpdir = mkdtemp(template)); 17 | ASSERT(chdir(tmpdir) == 0); 18 | ASSERT(mkdir("foo", 0755) == 0); 19 | ASSERT(touch("foo/bar", 0755) == 0); 20 | 21 | tap_plan(10); 22 | 23 | tap_is_int(globdir("foo", "ba*", 0, NULL, &globbuf), 0, "globdir return"); 24 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 25 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 26 | 27 | tap_is_int(globdir("foo", "ba*", GLOB_APPEND, NULL, &globbuf), 0, "globdir return"); 28 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 29 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 30 | tap_is_str(globbuf.gl_pathv[1], "bar", "glob path 2"); 31 | globfree(&globbuf); 32 | 33 | globbuf.gl_pathc = 2; 34 | tap_is_int(globdir("foo", "ba*", 0, NULL, &globbuf), 0, "globdir return"); 35 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 36 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 37 | globfree(&globbuf); 38 | 39 | return tap_finish(); 40 | } 41 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_dooffs.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/40-glob_dooffs.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | globdir_t globbuf; 13 | 14 | ASSERT(atexit(cleanup) == 0); 15 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 16 | ASSERT(tmpdir = mkdtemp(template)); 17 | ASSERT(chdir(tmpdir) == 0); 18 | ASSERT(mkdir("foo", 0755) == 0); 19 | ASSERT(mkdir("foo/bar", 0755) == 0); 20 | ASSERT(touch("foo/baz", 0755) == 0); 21 | 22 | tap_plan(10); 23 | 24 | globbuf.gl_offs = 2; 25 | tap_is_int(globdir("foo", "ba*", 0, NULL, &globbuf), 0, "globdir return"); 26 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 27 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 28 | tap_is_str(globbuf.gl_pathv[1], "baz", "glob path 2"); 29 | globfree(&globbuf); 30 | 31 | globbuf.gl_offs = 2; 32 | tap_is_int(globdir("foo", "ba*", GLOB_DOOFFS, NULL, &globbuf), 0, "globdir return"); 33 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 34 | tap_is_str(globbuf.gl_pathv[0], NULL, "glob path 1"); 35 | tap_is_str(globbuf.gl_pathv[1], NULL, "glob path 2"); 36 | tap_is_str(globbuf.gl_pathv[2], "bar", "glob path 3"); 37 | tap_is_str(globbuf.gl_pathv[3], "baz", "glob path 4"); 38 | globfree(&globbuf); 39 | 40 | return tap_finish(); 41 | } 42 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_err.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int main(void) { 4 | tap_skip_all("need to find a way to force readdir to return an error"); 5 | return tap_finish(); 6 | } 7 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_mark.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/40-glob_mark.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | globdir_t globbuf; 13 | 14 | ASSERT(atexit(cleanup) == 0); 15 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 16 | ASSERT(tmpdir = mkdtemp(template)); 17 | ASSERT(chdir(tmpdir) == 0); 18 | ASSERT(mkdir("foo", 0755) == 0); 19 | ASSERT(mkdir("foo/bar", 0755) == 0); 20 | ASSERT(touch("foo/baz", 0755) == 0); 21 | 22 | tap_plan(8); 23 | 24 | tap_is_int(globdir("foo", "ba*", 0, NULL, &globbuf), 0, "globdir return"); 25 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 26 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 27 | tap_is_str(globbuf.gl_pathv[1], "baz", "glob path 2"); 28 | globfree(&globbuf); 29 | 30 | tap_is_int(globdir("foo", "ba*", GLOB_MARK, NULL, &globbuf), 0, "globdir return"); 31 | tap_is_int(globbuf.gl_pathc, 2, "glob count"); 32 | tap_is_str(globbuf.gl_pathv[0], "bar/", "glob path 1"); 33 | tap_is_str(globbuf.gl_pathv[1], "baz", "glob path 2"); 34 | globfree(&globbuf); 35 | 36 | return tap_finish(); 37 | } 38 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_nocheck.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/40-glob_nocheck.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | globdir_t globbuf; 13 | 14 | ASSERT(atexit(cleanup) == 0); 15 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 16 | ASSERT(tmpdir = mkdtemp(template)); 17 | ASSERT(chdir(tmpdir) == 0); 18 | ASSERT(mkdir("foo", 0755) == 0); 19 | ASSERT(touch("foo/bar", 0755) == 0); 20 | 21 | tap_plan(8); 22 | 23 | tap_is_int(globdir("foo", "q*ux", 0, NULL, &globbuf), GLOB_NOMATCH, "globdir return"); 24 | tap_is_int(globbuf.gl_pathc, 0, "glob count"); 25 | globfree(&globbuf); 26 | 27 | tap_is_int(globdir("foo", "q*ux", GLOB_NOCHECK, NULL, &globbuf), 0, "globdir return"); 28 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 29 | tap_is_str(globbuf.gl_pathv[0], "q*ux", "glob path 1"); 30 | globfree(&globbuf); 31 | 32 | tap_is_int(globdir("foo", "b*r", GLOB_NOCHECK, NULL, &globbuf), 0, "globdir return"); 33 | tap_is_int(globbuf.gl_pathc, 1, "glob count"); 34 | tap_is_str(globbuf.gl_pathv[0], "bar", "glob path 1"); 35 | globfree(&globbuf); 36 | 37 | return tap_finish(); 38 | } 39 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_noescape.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int cwd = -1; 4 | char *tmpdir = NULL, template[] = "/tmp/40-glob_noescape.t-XXXXXX"; 5 | 6 | void cleanup(void) { 7 | if(cwd != -1) { fchdir(cwd); close(cwd); } 8 | if(tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 9 | } 10 | 11 | int main(void) { 12 | globdir_t globbuf; 13 | 14 | ASSERT(atexit(cleanup) == 0); 15 | ASSERT((cwd = open(".", O_DIRECTORY)) != -1); 16 | ASSERT(tmpdir = mkdtemp(template)); 17 | ASSERT(chdir(tmpdir) == 0); 18 | ASSERT(mkdir("foo", 0755) == 0); 19 | ASSERT(touch("foo/b\\ar", 0755) == 0); 20 | 21 | tap_plan(5); 22 | 23 | /* try to match literal "b*ar" */ 24 | tap_is_int(globdir("foo", "b\\*ar", 0, NULL, &globbuf), GLOB_NOMATCH, "globdir return"); 25 | tap_is_int(globbuf.gl_pathc, 0, "globdir count"); 26 | globfree(&globbuf); 27 | 28 | /* try to match literal '\', '*' retains special meaning */ 29 | tap_is_int(globdir("foo", "b\\*ar", GLOB_NOESCAPE, NULL, &globbuf), 0, "globdir return"); 30 | tap_is_int(globbuf.gl_pathc, 1, "globdir count"); 31 | tap_is_str(globbuf.gl_pathv[0], "b\\ar", "glob path 1"); 32 | globfree(&globbuf); 33 | 34 | return tap_finish(); 35 | } 36 | -------------------------------------------------------------------------------- /ext/globdir.c/t/40-glob_nosort.c: -------------------------------------------------------------------------------- 1 | #include "globdir_test.h" 2 | 3 | int main(void) { 4 | tap_skip_all("need to find a way to force readdir to return entries in unsorted order"); 5 | return tap_finish(); 6 | } 7 | -------------------------------------------------------------------------------- /ext/globdir.c/t/Makefile: -------------------------------------------------------------------------------- 1 | PROVE ?= prove 2 | 3 | GIT ?= git 4 | 5 | CFLAGS += -Wall -Wextra -Wpedantic -g 6 | 7 | override CPPFLAGS += -I.. 8 | 9 | TESTS = \ 10 | 01-sanity.t \ 11 | 10-escape_pattern.t \ 12 | 10-is_pattern.t \ 13 | 10-split_pattern.t \ 14 | 20-globat.t \ 15 | 30-globdir.t \ 16 | 40-glob_append.t \ 17 | 40-glob_dooffs.t \ 18 | 40-glob_err.t \ 19 | 40-glob_mark.t \ 20 | 40-glob_nocheck.t \ 21 | 40-glob_noescape.t \ 22 | 40-glob_nosort.t 23 | 24 | 01-sanity.t: CFLAGS += -pedantic -Werror 25 | 26 | %.t: %.c ../globdir.c ../globdir.h globdir_test.h ../ext/tap.c/tap.c Makefile 27 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ 28 | 29 | check: tests 30 | $(PROVE) --exec="./runtest.sh" $(TESTS) 31 | 32 | valgrind: tests 33 | $(PROVE) --exec="./runtest.sh -v" $(TESTS) 34 | 35 | Weverything: CC = clang 36 | Weverything: CFLAGS += -Weverything -Wno-padded 37 | Weverything: check 38 | 39 | gcov: CC = gcc 40 | gcov: CFLAGS += -fprofile-arcs -ftest-coverage 41 | gcov: check 42 | gcov $(TESTS) 43 | 44 | gprof: CC = gcc 45 | gprof: CFLAGS += -pg 46 | gprof: check 47 | 48 | tests: $(TESTS) 49 | 50 | all: tests 51 | 52 | clean: 53 | $(RM) $(TESTS) 54 | $(RM) *.gcov *.gcda *.gcno gmon.out 55 | 56 | .PHONY: all clean check gcov gprof tests Weverything 57 | -------------------------------------------------------------------------------- /ext/globdir.c/t/globdir_test.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBDIR_TEST_H 2 | #define GLOBDIR_TEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../ext/tap.c/tap.c" 14 | 15 | #include "../globdir.c" 16 | 17 | #define ASSERT(x) if(!(x)) { tap_bail("ASSERT FAILED (%s)", #x); exit(1); } 18 | 19 | int rmrfat(int dd, const char *path) { 20 | struct stat sbuf; 21 | if(fstatat(dd, path, &sbuf, AT_SYMLINK_NOFOLLOW) != 0) { 22 | return errno == ENOENT ? 0 : -1; 23 | } else if(S_ISDIR(sbuf.st_mode)) { 24 | struct dirent *ent; 25 | int fd; 26 | DIR *d; 27 | 28 | if((fd = openat(dd, path, O_DIRECTORY)) < 0) { return -1; } 29 | if((d = fdopendir(fd)) == NULL) { close(fd); return -1; } 30 | 31 | while(errno = 0, ent = readdir(d)) { 32 | if(strcmp(ent->d_name, ".") == 0) { continue; } 33 | if(strcmp(ent->d_name, "..") == 0) { continue; } 34 | if(rmrfat(fd, ent->d_name) != 0) { break; } 35 | } 36 | closedir(d); 37 | return errno == 0 ? unlinkat(dd, path, AT_REMOVEDIR) : -1; 38 | } else { 39 | return unlinkat(dd, path, 0); 40 | } 41 | } 42 | 43 | int touch(const char *path, mode_t mode) { 44 | int fd = open(path, O_RDONLY | O_CREAT, mode); 45 | if(fd == -1) { return -1; } 46 | close(fd); 47 | return 0; 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /ext/globdir.c/t/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gdb=0 4 | valgrind=0 5 | prefix=() 6 | 7 | extend() { 8 | if which "$1" &>/dev/null; then 9 | prefix+=("$@") 10 | else 11 | # bailing out would be counted as a failure by test harnesses, 12 | # ignore missing programs so that tests can be gracefully skipped 13 | printf "warning: command '$1' not found\n" >&2 14 | fi 15 | } 16 | 17 | usage() { 18 | printf "runtest.sh - run an executable test\n" 19 | printf "usage: runtest.sh [options] [test-options]\n" 20 | printf "\n" 21 | printf "Options:\n" 22 | printf " -g gdb\n" 23 | printf " -h display help\n" 24 | printf " -v valgrind\n" 25 | } 26 | 27 | while getopts cdghlrsv name; do 28 | case $name in 29 | g) gdb=1;; 30 | h) usage; exit;; 31 | v) valgrind=1;; 32 | esac 33 | done 34 | 35 | [ $gdb -eq 1 ] && extend gdb 36 | [ $valgrind -eq 1 ] && extend valgrind --quiet --leak-check=full \ 37 | --gen-suppressions=no --error-exitcode=123 38 | 39 | shift $(($OPTIND - 1)) # remove our options from the stack 40 | 41 | prog="$(realpath "$1")"; shift 42 | "${prefix[@]}" "$prog" "$@" 43 | -------------------------------------------------------------------------------- /ext/mini.c/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.swp 4 | -------------------------------------------------------------------------------- /ext/mini.c/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "t/tap.c"] 2 | path = t/tap.c 3 | url = git@github.com:andrewgregory/tap.c.git 4 | -------------------------------------------------------------------------------- /ext/mini.c/mini.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | * 22 | * Project URL: http://github.com/andrewgregory/mINI.c 23 | */ 24 | 25 | #ifndef MINI_C 26 | #define MINI_C 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "mini.h" 34 | 35 | mini_t *mini_finit(FILE *stream) { 36 | mini_t *mini; 37 | 38 | if((mini = calloc(1, sizeof(mini_t))) == NULL) { return NULL; } 39 | 40 | mini->_buf_size = MINI_BUFFER_SIZE; 41 | mini->_buf = malloc(mini->_buf_size); 42 | if(!mini->_buf) { mini_free(mini); return NULL; } 43 | 44 | mini->stream = stream; 45 | 46 | return mini; 47 | } 48 | 49 | mini_t *mini_init(const char *path) { 50 | FILE *stream; 51 | mini_t *m; 52 | if(!(stream = fopen(path, "r"))) { return NULL; } 53 | if(!(m = mini_finit(stream))) { fclose(stream); return NULL; }; 54 | m->_free_stream = 1; 55 | return m; 56 | } 57 | 58 | void mini_free(mini_t *mini) { 59 | if(mini == NULL) { return; } 60 | free(mini->_buf); 61 | free(mini->section); 62 | if(mini->stream && mini->_free_stream) { fclose(mini->stream); } 63 | free(mini); 64 | } 65 | 66 | static inline int _mini_isspace(int c) { 67 | return c == ' ' || c == '\n' || c == '\t' 68 | || c == '\r' || c == '\f' || c == '\v'; 69 | } 70 | 71 | static size_t _mini_strtrim(char *str, size_t len) { 72 | char *start = str, *end = str + len - 1; 73 | size_t newlen; 74 | 75 | if(!(str && *str)) { return 0; } 76 | 77 | while(*start && _mini_isspace((int) *start)) { start++; } 78 | while(end > start && _mini_isspace((int) *end)) { end--; } 79 | 80 | *(++end) = '\0'; 81 | newlen = (size_t)(end - start); 82 | memmove(str, start, newlen + 1); 83 | 84 | return newlen; 85 | } 86 | 87 | /* fgetc wrapper that retries on EINTR */ 88 | static int _mini_fgetc(FILE *stream) { 89 | int c, errno_orig = errno; 90 | do { errno = 0; c = fgetc(stream); } while(c == EOF && errno == EINTR); 91 | if(c != EOF || feof(stream) ) { errno = errno_orig; } 92 | return c; 93 | } 94 | 95 | /* fgets replacement to handle signal interrupts */ 96 | static char *_mini_fgets(char *buf, size_t size, FILE *stream) { 97 | char *s = buf; 98 | int c; 99 | 100 | if(size == 1) { 101 | *s = '\0'; 102 | return buf; 103 | } else if(size < 1) { 104 | return NULL; 105 | } 106 | 107 | do { 108 | c = _mini_fgetc(stream); 109 | if(c != EOF) { *(s++) = (char) c; } 110 | } while(--size > 1 && c != EOF && c != '\n'); 111 | 112 | if(s == buf || ( c == EOF && !feof(stream) )) { 113 | return NULL; 114 | } else { 115 | *s = '\0'; 116 | return buf; 117 | } 118 | } 119 | 120 | mini_t *mini_next(mini_t *mini) { 121 | char *c = NULL; 122 | size_t buflen = 0; 123 | 124 | mini->key = NULL; 125 | mini->value = NULL; 126 | 127 | if(_mini_fgets(mini->_buf, mini->_buf_size, mini->stream) == NULL) { 128 | mini->eof = feof(mini->stream); 129 | return NULL; 130 | } 131 | 132 | mini->lineno++; 133 | 134 | buflen = strlen(mini->_buf); 135 | if(mini->_buf[buflen - 1] != '\n' && !feof(mini->stream)) { 136 | errno = EOVERFLOW; 137 | return NULL; 138 | } 139 | 140 | if((c = strchr(mini->_buf, '#'))) { 141 | *c = '\0'; 142 | buflen = (size_t)(c - mini->_buf); 143 | } 144 | 145 | buflen = _mini_strtrim(mini->_buf, buflen); 146 | 147 | if(buflen == 0) { return mini_next(mini); } 148 | 149 | if(mini->_buf[0] == '[' && mini->_buf[buflen - 1] == ']') { 150 | free(mini->section); 151 | mini->_buf[buflen - 1] = '\0'; 152 | if((mini->section = malloc(buflen - 1)) == NULL) { return NULL; } 153 | memcpy(mini->section, mini->_buf + 1, buflen - 1); 154 | } else { 155 | mini->key = mini->_buf; 156 | if((c = strchr(mini->_buf, '='))) { 157 | *c = '\0'; 158 | mini->value = c + 1; 159 | _mini_strtrim(mini->key, (size_t)(c - mini->_buf)); 160 | _mini_strtrim(mini->value, buflen - (size_t)(mini->value - mini->_buf)); 161 | } 162 | } 163 | 164 | return mini; 165 | } 166 | 167 | mini_t *mini_lookup_key(mini_t *mini, const char *section, const char *key) { 168 | int in_section = (section == NULL); 169 | 170 | rewind(mini->stream); 171 | free(mini->section); 172 | mini->section = NULL; 173 | mini->key = NULL; 174 | mini->value = NULL; 175 | mini->lineno = 0; 176 | mini->eof = 0; 177 | 178 | if(!key) { return NULL; } 179 | 180 | while(mini_next(mini)) { 181 | if(!mini->key) { 182 | /* starting a new section */ 183 | in_section = (strcmp(mini->section, section) == 0); 184 | } else if(in_section && strcmp(mini->key, key) == 0) { 185 | return mini; 186 | } 187 | } 188 | 189 | return NULL; 190 | } 191 | 192 | int mini_fparse_cb(FILE *stream, mini_cb_t cb, void *data) { 193 | mini_t *mini = mini_finit(stream); 194 | int ret = 0; 195 | 196 | if(!mini) { 197 | return cb(0, NULL, NULL, NULL, data); 198 | } 199 | 200 | while(ret == 0 && mini_next(mini)) { 201 | ret = cb(mini->lineno, mini->section, mini->key, mini->value, data); 202 | } 203 | if(ret == 0 && !mini->eof) { 204 | ret = cb(0, NULL, NULL, NULL, data); 205 | } 206 | 207 | mini_free(mini); 208 | return ret; 209 | } 210 | 211 | int mini_parse_cb(const char *path, mini_cb_t cb, void *data) { 212 | FILE *stream = fopen(path, "r"); 213 | int ret; 214 | if(stream) { 215 | ret = mini_fparse_cb(stream, cb, data); 216 | fclose(stream); 217 | } else { 218 | ret = cb(0, NULL, NULL, NULL, data); 219 | } 220 | return ret; 221 | } 222 | 223 | #endif /* MINI_C */ 224 | -------------------------------------------------------------------------------- /ext/mini.c/mini.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | * 22 | * Project URL: http://github.com/andrewgregory/mINI.c 23 | */ 24 | 25 | #ifndef MINI_H 26 | #define MINI_H 27 | 28 | #include 29 | #include 30 | 31 | #if !defined(MINI_BUFFER_SIZE) || (MINI_BUFFER_SIZE < 2) 32 | #ifdef LINE_MAX 33 | #define MINI_BUFFER_SIZE LINE_MAX 34 | #else 35 | #define MINI_BUFFER_SIZE 2048 36 | #endif 37 | #endif 38 | 39 | typedef struct { 40 | FILE *stream; 41 | unsigned int lineno; 42 | char *section, *key, *value; 43 | int eof; 44 | /* private */ 45 | char *_buf; 46 | size_t _buf_size; 47 | int _free_stream; 48 | } mini_t; 49 | 50 | typedef int (*mini_cb_t)(unsigned int line, char *section, 51 | char *key, char *value, void *data); 52 | 53 | mini_t *mini_finit(FILE *stream); 54 | mini_t *mini_init(const char *path); 55 | mini_t *mini_next(mini_t *ctx); 56 | void mini_free(mini_t *ctx); 57 | 58 | mini_t *mini_lookup_key(mini_t *ctx, const char *section, const char *key); 59 | 60 | int mini_fparse_cb(FILE *stream, mini_cb_t cb, void *data); 61 | int mini_parse_cb(const char *path, mini_cb_t cb, void *data); 62 | 63 | #endif /* MINI_H */ 64 | -------------------------------------------------------------------------------- /ext/mini.c/t/.gitignore: -------------------------------------------------------------------------------- 1 | *.gcda 2 | *.gcno 3 | *.gcov 4 | *.t 5 | gmon.out 6 | -------------------------------------------------------------------------------- /ext/mini.c/t/01-sanity.c: -------------------------------------------------------------------------------- 1 | #include "tap.c/tap.c" 2 | 3 | #include "mini.c" 4 | 5 | /* just verify the library compiles */ 6 | 7 | int main(void) { 8 | tap_plan(1); 9 | tap_ok(1, "compilation successful"); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /ext/mini.c/t/10-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tap.c/tap.c" 7 | 8 | #include "mini.c" 9 | 10 | int main(void) 11 | { 12 | char buf[] = 13 | "\n" 14 | " key outside section\n" 15 | " [ section with spaces ] \n" 16 | " [key \n" 17 | " key = value \n" 18 | " \n" 19 | " # comment-only line \n" 20 | " key with spaces \n" 21 | "key=value\n" 22 | "key#this is a comment\n" 23 | " [] # empty section name\n" 24 | "emptysection\n"; 25 | FILE *stream; 26 | mini_t *ini; 27 | 28 | tap_plan(59); 29 | 30 | if((stream = fmemopen(buf, strlen(buf), "r")) == NULL) { 31 | tap_bail("error: could not open memory stream (%s)\n", strerror(errno)); 32 | return 1; 33 | } 34 | if((ini = mini_finit(stream)) == NULL) { 35 | tap_bail("error: could not init mini parser (%s)\n", strerror(errno)); 36 | return 1; 37 | } 38 | 39 | #define CHECKLN(ln, s, k, v) { \ 40 | tap_ok(mini_next(ini) != NULL, "line %d next != NULL", ln); \ 41 | tap_is_int(ini->lineno, ln, "line %d lineno", ln); \ 42 | tap_is_str(ini->section, s, "line %d section", ln); \ 43 | tap_is_str(ini->key, k, "line %d key", ln); \ 44 | tap_is_str(ini->value, v, "line %d value", ln); \ 45 | tap_is_int(ini->eof, 0, "line %d eof", ln); \ 46 | } 47 | 48 | tap_ok(ini != NULL, "mini_init"); 49 | 50 | CHECKLN(2, NULL, "key outside section", NULL); 51 | CHECKLN(3, " section with spaces ", NULL, NULL); 52 | CHECKLN(4, " section with spaces ", "[key", NULL); 53 | CHECKLN(5, " section with spaces ", "key", "value"); 54 | CHECKLN(8, " section with spaces ", "key with spaces", NULL); 55 | CHECKLN(9, " section with spaces ", "key", "value"); 56 | CHECKLN(10, " section with spaces ", "key", NULL); 57 | CHECKLN(11, "", NULL, NULL); 58 | CHECKLN(12, "", "emptysection", NULL); 59 | 60 | tap_ok(mini_next(ini) == NULL, "next(eof) == NULL"); 61 | tap_is_int(ini->lineno, 12, "final line count"); 62 | tap_ok(feof(ini->stream), "feof(ini->stream == 1"); 63 | tap_ok(ini->eof, "ini->eof == 1"); 64 | 65 | #undef CHECKLN 66 | 67 | mini_free(ini); 68 | fclose(stream); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /ext/mini.c/t/10-lookup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tap.c/tap.c" 7 | 8 | #include "mini.c" 9 | 10 | int main(void) 11 | { 12 | char buf[] = 13 | "\n" 14 | " key outside section\n" 15 | " [ section with spaces ] \n" 16 | " [key \n" 17 | " key = value \n" 18 | " \n" 19 | " # comment-only line \n" 20 | " key with spaces \n" 21 | "key=value\n" 22 | "key#this is a comment\n" 23 | " [] # empty section name\n" 24 | "emptysection\n"; 25 | FILE *stream; 26 | mini_t *ini; 27 | 28 | tap_plan(34); 29 | 30 | if((stream = fmemopen(buf, strlen(buf), "r")) == NULL) { 31 | tap_bail("error: could not open memory stream (%s)\n", strerror(errno)); 32 | return 1; 33 | } 34 | if((ini = mini_finit(stream)) == NULL) { 35 | tap_bail("error: could not init mini parser (%s)\n", strerror(errno)); 36 | return 1; 37 | } 38 | 39 | #define CHECKLN(ln, s, k, v) { \ 40 | tap_ok(mini_lookup_key(ini, s, k) != NULL, "lookup(%s, %s)", #s, #k); \ 41 | tap_is_int(ini->lineno, ln, "line %d lineno", ln); \ 42 | tap_is_str(ini->section, s, "line %d section", ln); \ 43 | tap_is_str(ini->key, k, "line %d key", ln); \ 44 | tap_is_str(ini->value, v, "line %d value", ln); \ 45 | tap_is_int(ini->eof, 0, "line %d eof", ln); \ 46 | } 47 | 48 | tap_ok(ini != NULL, "mini_init"); 49 | 50 | CHECKLN(12, "", "emptysection", NULL); 51 | CHECKLN(8, " section with spaces ", "key with spaces", NULL); 52 | CHECKLN(5, " section with spaces ", "key", "value"); 53 | CHECKLN(4, " section with spaces ", "[key", NULL); 54 | CHECKLN(2, NULL, "key outside section", NULL); 55 | 56 | tap_ok(mini_lookup_key(ini, "non-existent", "non-existent") == NULL, NULL); 57 | tap_is_int(ini->lineno, 12, NULL); 58 | tap_ok(ini->eof, NULL); 59 | 60 | #undef CHECKLN 61 | 62 | mini_free(ini); 63 | fclose(stream); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /ext/mini.c/t/90-smoke.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tap.c/tap.c" 7 | 8 | #include "mini.c" 9 | 10 | #define SECTIONS 10000 11 | 12 | int main(void) 13 | { 14 | char *buf; 15 | size_t i, buf_siz; 16 | mini_t *ini; 17 | 18 | FILE *stream = open_memstream(&buf, &buf_siz); 19 | if(stream == NULL) { 20 | tap_bail("error: could not open memory stream (%s)\n", strerror(errno)); 21 | return 1; 22 | } 23 | for(i = 1; i <= SECTIONS; i++) { 24 | fprintf(stream, " [ section %zu ] \n", i); 25 | fprintf(stream, " # comment \n"); 26 | fprintf(stream, " key%zu = value%zu #inline comment \n", i, i); 27 | fprintf(stream, " # comment \n"); 28 | fprintf(stream, " key%zu = value%zu #inline comment \n", i, i); 29 | fprintf(stream, " # comment \n"); 30 | fprintf(stream, " key%zu = value%zu #inline comment \n", i, i); 31 | fprintf(stream, " # comment \n"); 32 | } 33 | fprintf(stream, "[target section]\n"); 34 | fprintf(stream, "target key = target value\n"); 35 | fflush(stream); 36 | 37 | if((ini = mini_finit(stream)) == NULL) { 38 | tap_bail("error: could not init mini parser (%s)\n", strerror(errno)); 39 | return 1; 40 | } 41 | 42 | tap_plan(3); 43 | tap_ok(mini_lookup_key(ini, "target section", "target key") != NULL, "lookup"); 44 | tap_is_str(ini->value, "target value", "value"); 45 | tap_is_int(ini->lineno, SECTIONS * 8 + 2, "lineno"); 46 | 47 | mini_free(ini); 48 | fclose(stream); 49 | free(buf); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /ext/mini.c/t/Makefile: -------------------------------------------------------------------------------- 1 | # the tap.c submodule must be checked out to build and run tests 2 | 3 | CFLAGS += -Wall -Wextra -Wpedantic -g 4 | 5 | override CPPFLAGS += -I.. 6 | 7 | TESTS = 01-sanity.t 10-basic.t 10-lookup.t 90-smoke.t 8 | 9 | 01-sanity.t: CFLAGS += -std=c99 -pedantic -Werror 10 | 11 | %.t: %.c ../mini.c ../mini.h tap.c/tap.c 12 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ 13 | 14 | check: tests 15 | prove . 16 | 17 | Weverything: CC = clang 18 | Weverything: CFLAGS += -Weverything -Wno-padded 19 | Weverything: check 20 | 21 | gcov: CC = gcc 22 | gcov: CFLAGS += -fprofile-arcs -ftest-coverage 23 | gcov: check 24 | gcov $(TESTS) 25 | 26 | gprof: CC = gcc 27 | gprof: CFLAGS += -pg 28 | gprof: check 29 | 30 | tests: $(TESTS) 31 | 32 | all: tests 33 | 34 | clean: 35 | $(RM) $(TESTS) 36 | $(RM) *.gcov *.gcda *.gcno gmon.out 37 | 38 | .PHONY: all clean check gcov gprof tests Weverything 39 | -------------------------------------------------------------------------------- /ext/tap.c/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewgregory/pacutils/d3998fef76cb930195664db45dcc38ac8a72e410/ext/tap.c/.gitignore -------------------------------------------------------------------------------- /ext/tap.c/t/.gitignore: -------------------------------------------------------------------------------- 1 | *.gcda 2 | *.gcno 3 | *.gcov 4 | *.t 5 | tmp_* 6 | -------------------------------------------------------------------------------- /ext/tap.c/t/01-sanity.c: -------------------------------------------------------------------------------- 1 | #include "../tap.c" 2 | 3 | /* just verify the library compiles */ 4 | 5 | int main(void) { 6 | tap_plan(1); 7 | tap_ok(1, "compilation successful"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ext/tap.c/t/10-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../tap.c" 7 | 8 | static char outfile[] = "tmp_10-basic_XXXXXX"; 9 | static char expfile[] = "expected/10-basic.t.out"; 10 | 11 | static void test(void) { 12 | tap_plan(0); 13 | tap_plan(1); 14 | tap_plan(2); 15 | 16 | tap_skip_all(NULL); 17 | tap_skip_all("foo %s", "bar"); 18 | 19 | tap_skip(0, NULL); 20 | tap_skip(1, "foo %s", "bar"); 21 | tap_skip(2, NULL); 22 | 23 | tap_diag("foo"); 24 | tap_diag("foo %s", "bar"); 25 | 26 | tap_bail(NULL); 27 | tap_bail("foo %s", "bar"); 28 | 29 | tap_ok(0, NULL); 30 | tap_ok(1, NULL); 31 | tap_ok(0, "%s", "foo"); 32 | tap_ok(1, "%s", "foo"); 33 | 34 | tap_is_str("foo", "foo", NULL); 35 | tap_is_str("foo", "bar", NULL); 36 | tap_is_str("foo", "foo", "foo %s", "bar"); 37 | tap_is_str("foo", "bar", "foo %s", "bar"); 38 | tap_is_str(NULL, NULL, NULL); 39 | tap_is_str(NULL, "bar", NULL); 40 | 41 | tap_is_int(1, 1, NULL); 42 | tap_is_int(1, 0, NULL); 43 | tap_is_int(1, 1, "foo %s", "bar"); 44 | tap_is_int(1, 0, "foo %s", "bar"); 45 | 46 | tap_is_float(1.0, 1.0, 0.1, NULL); 47 | tap_is_float(1.0, 2.0, 0.1, NULL); 48 | tap_is_float(1.0, 1.0, 0.1, "foo %s", "bar"); 49 | tap_is_float(1.0, 2.0, 0.1, "foo %s", "bar"); 50 | 51 | tap_done_testing(); 52 | } 53 | 54 | static void run(void) { 55 | test(); 56 | tap_todo("FOO TODO"); 57 | test(); 58 | } 59 | 60 | int main(void) { 61 | int sfd, efd, ofd, ret = 0; 62 | FILE *efile, *ofile; 63 | char ebuf[50], obuf[50]; 64 | int line = 0; 65 | 66 | /* redirect output to temp file */ 67 | ofd = mkstemp(outfile); 68 | sfd = dup(STDOUT_FILENO); 69 | efd = dup(STDERR_FILENO); 70 | dup2(ofd, STDOUT_FILENO); 71 | dup2(ofd, STDERR_FILENO); 72 | close(ofd); 73 | 74 | run(); 75 | 76 | /* flush and restore output */ 77 | fflush(stdout); 78 | fflush(stderr); 79 | dup2(sfd, STDOUT_FILENO); 80 | dup2(efd, STDERR_FILENO); 81 | close(sfd); 82 | close(efd); 83 | 84 | efile = fopen(expfile, "r"); 85 | ofile = fopen(outfile, "r"); 86 | 87 | puts("1..1"); 88 | while(1) { 89 | char *eres = fgets(ebuf, 50, efile); 90 | char *ores = fgets(obuf, 50, ofile); 91 | ++line; 92 | if(ores == NULL && eres == NULL) { 93 | puts("ok 1 - output matches expected"); 94 | break; 95 | } else if(ores && eres && strcmp(ebuf, obuf) == 0) { 96 | /* match */ 97 | } else { 98 | fprintf(stdout, "not ok 1 - mismatch beginning on line %d\n", line); 99 | fprintf(stderr, " # got: '%s'\n", obuf); 100 | fprintf(stderr, " # expected: '%s'\n", ebuf); 101 | ret = 1; 102 | break; 103 | } 104 | } 105 | 106 | fclose(efile); 107 | fclose(ofile); 108 | 109 | if(getenv("TAP_SAVE")) { 110 | fprintf(stderr, "%s output saved to %s\n", __FILE__, outfile); 111 | } else { 112 | unlink(outfile); 113 | } 114 | 115 | return ret; 116 | } 117 | -------------------------------------------------------------------------------- /ext/tap.c/t/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -Wextra -Wpedantic -Werror -g 2 | 3 | override CPPFLAGS += -I.. 4 | 5 | TESTS = 01-sanity.t 10-basic.t 6 | 7 | 01-sanity.t: CFLAGS += -std=c99 -pedantic -Werror 8 | 9 | %.t: %.c ../tap.c 10 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ 11 | 12 | check: tests 13 | prove . 14 | 15 | tests: $(TESTS) 16 | 17 | all: tests 18 | 19 | Weverything: CC = clang 20 | Weverything: CFLAGS += -Weverything 21 | Weverything: check 22 | 23 | gcov: CC = gcc 24 | gcov: CFLAGS += -fprofile-arcs -ftest-coverage 25 | gcov: check 26 | gcov $(TESTS) 27 | 28 | clean: 29 | $(RM) $(TESTS) 30 | $(RM) tmp_* 31 | $(RM) *.gcov *.gcda *.gcno 32 | 33 | .PHONY: all clean check gcov tests Weverything 34 | -------------------------------------------------------------------------------- /ext/tap.c/t/expected/10-basic.t.out: -------------------------------------------------------------------------------- 1 | 1..0 2 | 1..1 3 | 1..2 4 | 1..0 # SKIP 5 | 1..0 # SKIP foo bar 6 | ok 1 # SKIP foo bar 7 | ok 2 # SKIP 8 | ok 3 # SKIP 9 | # foo 10 | # foo bar 11 | Bail out! 12 | Bail out! foo bar 13 | not ok 4 14 | # Failed test at 10-basic.c line 29. 15 | ok 5 16 | not ok 6 - foo 17 | # Failed test at 10-basic.c line 31. 18 | ok 7 - foo 19 | ok 8 20 | not ok 9 21 | # Failed test at 10-basic.c line 35. 22 | # got: 'foo' 23 | # expected: 'bar' 24 | ok 10 - foo bar 25 | not ok 11 - foo bar 26 | # Failed test at 10-basic.c line 37. 27 | # got: 'foo' 28 | # expected: 'bar' 29 | ok 12 30 | not ok 13 31 | # Failed test at 10-basic.c line 39. 32 | # got: '(null)' 33 | # expected: 'bar' 34 | ok 14 35 | not ok 15 36 | # Failed test at 10-basic.c line 42. 37 | # got: '1' 38 | # expected: '0' 39 | ok 16 - foo bar 40 | not ok 17 - foo bar 41 | # Failed test at 10-basic.c line 44. 42 | # got: '1' 43 | # expected: '0' 44 | ok 18 45 | not ok 19 46 | # Failed test at 10-basic.c line 47. 47 | # got: '1.000000' 48 | # expected: '2.000000' 49 | # delta: '1.000000' 50 | # allowed: '0.100000' 51 | ok 20 - foo bar 52 | not ok 21 - foo bar 53 | # Failed test at 10-basic.c line 49. 54 | # got: '1.000000' 55 | # expected: '2.000000' 56 | # delta: '1.000000' 57 | # allowed: '0.100000' 58 | 1..21 59 | 1..0 60 | 1..1 61 | 1..2 62 | 1..0 # SKIP 63 | 1..0 # SKIP foo bar 64 | ok 22 # SKIP foo bar 65 | ok 23 # SKIP 66 | ok 24 # SKIP 67 | # foo 68 | # foo bar 69 | Bail out! 70 | Bail out! foo bar 71 | not ok 25 # TODO FOO TODO 72 | # Failed (TODO) test at 10-basic.c line 29. 73 | ok 26 # TODO FOO TODO 74 | not ok 27 - foo # TODO FOO TODO 75 | # Failed (TODO) test at 10-basic.c line 31. 76 | ok 28 - foo # TODO FOO TODO 77 | ok 29 # TODO FOO TODO 78 | not ok 30 # TODO FOO TODO 79 | # Failed (TODO) test at 10-basic.c line 35. 80 | # got: 'foo' 81 | # expected: 'bar' 82 | ok 31 - foo bar # TODO FOO TODO 83 | not ok 32 - foo bar # TODO FOO TODO 84 | # Failed (TODO) test at 10-basic.c line 37. 85 | # got: 'foo' 86 | # expected: 'bar' 87 | ok 33 # TODO FOO TODO 88 | not ok 34 # TODO FOO TODO 89 | # Failed (TODO) test at 10-basic.c line 39. 90 | # got: '(null)' 91 | # expected: 'bar' 92 | ok 35 # TODO FOO TODO 93 | not ok 36 # TODO FOO TODO 94 | # Failed (TODO) test at 10-basic.c line 42. 95 | # got: '1' 96 | # expected: '0' 97 | ok 37 - foo bar # TODO FOO TODO 98 | not ok 38 - foo bar # TODO FOO TODO 99 | # Failed (TODO) test at 10-basic.c line 44. 100 | # got: '1' 101 | # expected: '0' 102 | ok 39 # TODO FOO TODO 103 | not ok 40 # TODO FOO TODO 104 | # Failed (TODO) test at 10-basic.c line 47. 105 | # got: '1.000000' 106 | # expected: '2.000000' 107 | # delta: '1.000000' 108 | # allowed: '0.100000' 109 | ok 41 - foo bar # TODO FOO TODO 110 | not ok 42 - foo bar # TODO FOO TODO 111 | # Failed (TODO) test at 10-basic.c line 49. 112 | # got: '1.000000' 113 | # expected: '2.000000' 114 | # delta: '1.000000' 115 | # allowed: '0.100000' 116 | 1..42 117 | -------------------------------------------------------------------------------- /ext/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | 3 | r="$(git rev-parse --show-toplevel 2>/dev/null)" 4 | if [ -d "$r" ] && cd "$r"; then 5 | git subtree pull --squash --prefix=ext/globdir.c https://github.com/andrewgregory/globdir.c.git master 6 | git subtree pull --squash --prefix=ext/mini.c https://github.com/andrewgregory/mini.c.git master 7 | git subtree pull --squash --prefix=ext/tap.c https://github.com/andrewgregory/tap.c.git master 8 | else 9 | printf "unable to chdir to repository root '%s'\n" "$r" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | -include ../configure.mk 2 | -include configure.mk 3 | 4 | ALPM_CFLAGS ?= $(shell pkg-config libalpm --cflags) 5 | 6 | CFLAGS ?= -Wall -Wextra -Wpedantic -Werror -g 7 | 8 | override CFLAGS += $(ALPM_CFLAGS) 9 | override LDLIBS += -lalpm 10 | 11 | PREFIX ?= /usr/local 12 | EXEC_PREFIX ?= ${PREFIX} 13 | BINDIR ?= ${EXEC_PREFIX}/bin 14 | INCLUDEDIR ?= ${PREFIX}/include 15 | LIBDIR ?= ${EXEC_PREFIX}/lib 16 | LOCALSTATEDIR ?= ${PREFIX}/var 17 | SYSCONFDIR ?= ${PREFIX}/etc 18 | 19 | CACHEDIR ?= ${LOCALSTATEDIR}/cache/pacman/pkg 20 | DBEXT ?= .db 21 | DBPATH ?= ${LOCALSTATEDIR}/lib/pacman 22 | GPGDIR ?= ${SYSCONFDIR}/pacman.d/gnupg 23 | HOOKDIR ?= ${SYSCONFDIR}/pacman.d/hooks 24 | LOGFILE ?= ${LOCALSTATEDIR}/log/pacman.log 25 | ROOTDIR ?= / 26 | 27 | override CPPFLAGS += \ 28 | -DCACHEDIR=\"$(CACHEDIR)\" \ 29 | -DDBEXT=\"$(DBEXT)\" \ 30 | -DDBPATH=\"$(DBPATH)\" \ 31 | -DGPGDIR=\"$(GPGDIR)\" \ 32 | -DHOOKDIR=\"$(HOOKDIR)\" \ 33 | -DLOCALSTATEDIR=\"$(LOCALSTATEDIR)\" \ 34 | -DLOGFILE=\"$(LOGFILE)\" \ 35 | -DROOTDIR=\"$(ROOTDIR)\" \ 36 | -DSYSCONFDIR=\"$(SYSCONFDIR)\" 37 | 38 | GITTAG = $(shell git describe --abbrev=0 2>/dev/null) 39 | ifneq ($(GITTAG),) 40 | GITVER = $(subst $(GITTAG)-,,$(shell git describe --dirty)) 41 | ifneq ($(GITTAG),$(GITVER)) 42 | override CPPFLAGS += -DGITVER=\"$(GITVER)\" 43 | endif 44 | endif 45 | 46 | HEADERS = \ 47 | pacutils.h \ 48 | pacutils/config.h \ 49 | pacutils/depends.h \ 50 | pacutils/log.h \ 51 | pacutils/mtree.h \ 52 | pacutils/ui.h \ 53 | pacutils/uix.h \ 54 | pacutils/util.h 55 | 56 | SOURCES = \ 57 | ../ext/globdir.c/globdir.c \ 58 | ../ext/mini.c/mini.c \ 59 | pacutils.c \ 60 | pacutils/config.c \ 61 | pacutils/depends.c \ 62 | pacutils/log.c \ 63 | pacutils/mtree.c \ 64 | pacutils/ui.c \ 65 | pacutils/uix.c \ 66 | pacutils/util.c 67 | 68 | all: libpacutils.so 69 | 70 | libpacutils.so: ${SOURCES} 71 | $(CC) $(CPPFLAGS) -shared -fPIC $(CFLAGS) -o $@ $^ $(LDLIBS) $(LDFLAGS) 72 | 73 | install: libpacutils.so 74 | install -d "${DESTDIR}${INCLUDEDIR}/pacutils" 75 | for h in ${HEADERS}; do install -m 644 $$h "${DESTDIR}${INCLUDEDIR}/$$h"; done 76 | install -d "${DESTDIR}${LIBDIR}" 77 | install -m 644 libpacutils.so "${DESTDIR}${LIBDIR}/libpacutils.so" 78 | 79 | clean: 80 | $(RM) *.o *.so* 81 | 82 | .PHONY: all clean install 83 | -------------------------------------------------------------------------------- /lib/pacutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "pacutils.h" 28 | #include "pacutils/config-defaults.h" 29 | 30 | char *pu_version(void) { 31 | return BUILDVER; 32 | } 33 | 34 | void pu_print_version(const char *progname, const char *progver) { 35 | printf("%s v%s - libalpm v%s - pacutils v%s\n", progname, progver, 36 | alpm_version(), pu_version()); 37 | } 38 | 39 | int pu_pathcmp(const char *p1, const char *p2) { 40 | while (*p1 && *p1 == *p2) { 41 | /* skip repeated '/' */ 42 | if (*p1 == '/') { 43 | while (*(++p1) == '/'); 44 | while (*(++p2) == '/'); 45 | } else { 46 | p1++; 47 | p2++; 48 | } 49 | } 50 | 51 | /* skip trailing '/' */ 52 | if (*p1 == '\0') { 53 | while (*p2 == '/') { p2++; } 54 | } else if (*p2 == '\0') { 55 | while (*p1 == '/') { p1++; } 56 | } 57 | 58 | return *p1 - *p2; 59 | } 60 | 61 | static int _pu_filelist_path_cmp(const char *needle, alpm_file_t *file) { 62 | return pu_pathcmp(needle, file->name); 63 | } 64 | 65 | alpm_file_t *pu_filelist_contains_path(alpm_filelist_t *files, 66 | const char *path) { 67 | if (files == NULL) { return NULL; } 68 | 69 | return bsearch(path, files->files, files->count, sizeof(alpm_file_t), 70 | (int(*)(const void *, const void *)) _pu_filelist_path_cmp); 71 | } 72 | 73 | static char *pu_fetch_pkgurl(alpm_handle_t *handle, const char *url) { 74 | alpm_list_t l = { .data = (void *)url }, *result = NULL; 75 | if (alpm_fetch_pkgurl(handle, &l, &result) == 0 && result) { 76 | char *path = result->data; 77 | alpm_list_free(result); 78 | return path; 79 | } else { 80 | return NULL; 81 | } 82 | } 83 | 84 | alpm_pkg_t *pu_find_pkgspec(alpm_handle_t *handle, const char *pkgspec) { 85 | char *c; 86 | 87 | if (strstr(pkgspec, "://")) { 88 | alpm_pkg_t *pkg; 89 | alpm_siglevel_t sl 90 | = strncmp(pkgspec, "file://", 7) == 0 91 | ? alpm_option_get_local_file_siglevel(handle) 92 | : alpm_option_get_remote_file_siglevel(handle); 93 | char *path = pu_fetch_pkgurl(handle, pkgspec); 94 | int err = alpm_pkg_load(handle, path ? path : pkgspec, 1, sl, &pkg); 95 | free(path); 96 | if (!err) { 97 | return pkg; 98 | } 99 | } else if ((c = strchr(pkgspec, '/'))) { 100 | alpm_db_t *db = NULL; 101 | size_t dblen = c - pkgspec; 102 | 103 | if (dblen == strlen("local") && memcmp(pkgspec, "local", dblen) == 0) { 104 | db = alpm_get_localdb(handle); 105 | } else { 106 | alpm_list_t *i; 107 | for (i = alpm_get_syncdbs(handle); i; i = i->next) { 108 | const char *dbname = alpm_db_get_name(i->data); 109 | if (dblen == strlen(dbname) && strncmp(pkgspec, dbname, dblen) == 0) { 110 | db = i->data; 111 | break; 112 | } 113 | } 114 | } 115 | 116 | if (!db) { 117 | return NULL; 118 | } else { 119 | return alpm_db_get_pkg(db, c + 1); 120 | } 121 | } 122 | 123 | return NULL; 124 | } 125 | 126 | int pu_fprint_pkgspec(FILE *stream, alpm_pkg_t *pkg) { 127 | const char *c; 128 | switch (alpm_pkg_get_origin(pkg)) { 129 | case ALPM_PKG_FROM_FILE: 130 | c = alpm_pkg_get_filename(pkg); 131 | if (strstr(c, "://")) { 132 | return fprintf(stream, "%s", c); 133 | } else { 134 | char *real = realpath(c, NULL); 135 | int ret = fprintf(stream, "file://%s", real); 136 | free(real); 137 | return ret; 138 | } 139 | case ALPM_PKG_FROM_LOCALDB: 140 | return fprintf(stream, "local/%s", alpm_pkg_get_name(pkg)); 141 | case ALPM_PKG_FROM_SYNCDB: 142 | return fprintf(stream, "%s/%s", 143 | alpm_db_get_name(alpm_pkg_get_db(pkg)), alpm_pkg_get_name(pkg)); 144 | default: 145 | /* no idea where this package came from, fall back to its name */ 146 | return fprintf(stream, "%s", alpm_pkg_get_name(pkg)); 147 | } 148 | } 149 | 150 | char *pu_pkgspec(alpm_pkg_t *pkg) { 151 | const char *c; 152 | switch (alpm_pkg_get_origin(pkg)) { 153 | case ALPM_PKG_FROM_FILE: 154 | c = alpm_pkg_get_filename(pkg); 155 | if (strstr(c, "://")) { 156 | return pu_asprintf("%s", c); 157 | } else { 158 | char *real = realpath(c, NULL); 159 | char *str = pu_asprintf("file://%s", real); 160 | free(real); 161 | return str; 162 | } 163 | case ALPM_PKG_FROM_LOCALDB: 164 | return pu_asprintf("local/%s", alpm_pkg_get_name(pkg)); 165 | case ALPM_PKG_FROM_SYNCDB: 166 | return pu_asprintf("%s/%s", 167 | alpm_db_get_name(alpm_pkg_get_db(pkg)), alpm_pkg_get_name(pkg)); 168 | default: 169 | /* no idea where this package came from, fall back to its name */ 170 | return strdup(alpm_pkg_get_name(pkg)); 171 | } 172 | } 173 | 174 | int pu_log_command(alpm_handle_t *handle, const char *caller, int argc, 175 | char **argv) { 176 | int i; 177 | char *cmd, *c; 178 | size_t cmdlen = 0; 179 | 180 | for (i = 0; i < argc; ++i) { 181 | cmdlen += strlen(argv[i]) + 1; 182 | } 183 | 184 | cmd = c = malloc(cmdlen + 1); 185 | if (!cmd) { 186 | return -1; 187 | } 188 | 189 | for (i = 0; i < argc; ++i) { 190 | c = stpcpy(c, " "); 191 | c = stpcpy(c, argv[i]); 192 | } 193 | 194 | alpm_logaction(handle, caller, "Running%s\n", cmd); 195 | 196 | free(cmd); 197 | 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /lib/pacutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PACUTILS_H 24 | #define PACUTILS_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include "pacutils/config.h" 35 | #include "pacutils/depends.h" 36 | #include "pacutils/log.h" 37 | #include "pacutils/mtree.h" 38 | #include "pacutils/ui.h" 39 | #include "pacutils/uix.h" 40 | #include "pacutils/util.h" 41 | 42 | char *pu_version(void); 43 | void pu_print_version(const char *progname, const char *progver); 44 | 45 | int pu_pathcmp(const char *p1, const char *p2); 46 | alpm_file_t *pu_filelist_contains_path(alpm_filelist_t *files, 47 | const char *path); 48 | 49 | alpm_pkg_t *pu_find_pkgspec(alpm_handle_t *handle, const char *pkgspec); 50 | int pu_fprint_pkgspec(FILE *stream, alpm_pkg_t *pkg); 51 | char *pu_pkgspec(alpm_pkg_t *pkg); 52 | 53 | int pu_log_command(alpm_handle_t *handle, const char *caller, int argc, 54 | char **argv); 55 | 56 | #endif /* PACUTILS_H */ 57 | -------------------------------------------------------------------------------- /lib/pacutils/config-defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PACUTILS_CONFIG_DEFAULTS_H 24 | #define PACUTILS_CONFIG_DEFAULTS_H 25 | 26 | #define BASEVER "0.15.0" 27 | 28 | #ifdef GITVER 29 | #define BUILDVER BASEVER "+" GITVER 30 | #else 31 | #define BUILDVER BASEVER 32 | #endif 33 | 34 | /* duplicates default configuration settings from lib/Makefile in order to 35 | * allow compilation without make and to provide defaults for analyzers */ 36 | 37 | #ifndef PREFIX 38 | #define PREFIX "/usr/local" 39 | #endif 40 | 41 | #ifndef LOCALSTATEDIR 42 | #define LOCALSTATEDIR PREFIX "/var" 43 | #endif 44 | 45 | #ifndef SYSCONFDIR 46 | #define SYSCONFDIR PREFIX "/etc" 47 | #endif 48 | 49 | #ifndef CACHEDIR 50 | #define CACHEDIR LOCALSTATEDIR "/cache/pacman/pkg" 51 | #endif 52 | 53 | #ifndef DBEXT 54 | #define DBEXT ".db" 55 | #endif 56 | 57 | #ifndef DBPATH 58 | #define DBPATH LOCALSTATEDIR "/lib/pacman" 59 | #endif 60 | 61 | #ifndef GPGDIR 62 | #define GPGDIR SYSCONFDIR "/pacman.d/gnupg" 63 | #endif 64 | 65 | #ifndef HOOKDIR 66 | #define HOOKDIR SYSCONFDIR "/pacman.d/hooks" 67 | #endif 68 | 69 | #ifndef LOGFILE 70 | #define LOGFILE LOCALSTATEDIR "/log/pacman.log" 71 | #endif 72 | 73 | #ifndef ROOTDIR 74 | #define ROOTDIR "/" 75 | #endif 76 | 77 | #endif /* PACUTILS_CONFIG_DEFAULTS_H */ 78 | -------------------------------------------------------------------------------- /lib/pacutils/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #ifndef PACUTILS_CONFIG_H 26 | #define PACUTILS_CONFIG_H 27 | 28 | typedef enum pu_config_option_t { 29 | PU_CONFIG_OPTION_ROOTDIR, 30 | PU_CONFIG_OPTION_DBPATH, 31 | PU_CONFIG_OPTION_GPGDIR, 32 | PU_CONFIG_OPTION_LOGFILE, 33 | PU_CONFIG_OPTION_ARCHITECTURE, 34 | PU_CONFIG_OPTION_XFERCOMMAND, 35 | 36 | PU_CONFIG_OPTION_CLEANMETHOD, 37 | PU_CONFIG_OPTION_COLOR, 38 | PU_CONFIG_OPTION_NOPROGRESSBAR, 39 | PU_CONFIG_OPTION_USESYSLOG, 40 | PU_CONFIG_OPTION_CHECKSPACE, 41 | PU_CONFIG_OPTION_VERBOSEPKGLISTS, 42 | PU_CONFIG_OPTION_ILOVECANDY, 43 | PU_CONFIG_OPTION_DISABLEDOWNLOADTIMEOUT, 44 | PU_CONFIG_OPTION_PARALLELDOWNLOADS, 45 | 46 | PU_CONFIG_OPTION_SIGLEVEL, 47 | PU_CONFIG_OPTION_LOCAL_SIGLEVEL, 48 | PU_CONFIG_OPTION_REMOTE_SIGLEVEL, 49 | 50 | PU_CONFIG_OPTION_HOOKDIRS, 51 | PU_CONFIG_OPTION_HOLDPKGS, 52 | PU_CONFIG_OPTION_IGNOREPKGS, 53 | PU_CONFIG_OPTION_IGNOREGROUPS, 54 | PU_CONFIG_OPTION_NOUPGRADE, 55 | PU_CONFIG_OPTION_NOEXTRACT, 56 | PU_CONFIG_OPTION_REPOS, 57 | PU_CONFIG_OPTION_CACHEDIRS, 58 | 59 | PU_CONFIG_OPTION_SERVER, 60 | PU_CONFIG_OPTION_CACHESERVER, 61 | 62 | PU_CONFIG_OPTION_USAGE, 63 | 64 | PU_CONFIG_OPTION_DOWNLOADUSER, 65 | PU_CONFIG_OPTION_DISABLESANDBOX, 66 | 67 | PU_CONFIG_OPTION_INCLUDE 68 | } pu_config_option_t; 69 | 70 | /* config */ 71 | typedef enum pu_config_cleanmethod_t { 72 | PU_CONFIG_CLEANMETHOD_KEEP_INSTALLED = (1 << 0), 73 | PU_CONFIG_CLEANMETHOD_KEEP_CURRENT = (1 << 1), 74 | } pu_config_cleanmethod_t; 75 | 76 | typedef enum pu_config_bool_t { 77 | PU_CONFIG_BOOL_UNSET = -1, 78 | PU_CONFIG_BOOL_FALSE = 0, 79 | PU_CONFIG_BOOL_TRUE = 1, 80 | } pu_config_bool_t; 81 | 82 | typedef struct pu_config_t { 83 | char *rootdir; 84 | char *dbpath; 85 | char *gpgdir; 86 | char *logfile; 87 | char *xfercommand; 88 | char *downloaduser; 89 | 90 | int paralleldownloads; 91 | 92 | pu_config_bool_t checkspace; 93 | pu_config_bool_t color; 94 | pu_config_bool_t noprogressbar; 95 | pu_config_bool_t ilovecandy; 96 | pu_config_bool_t usesyslog; 97 | pu_config_bool_t verbosepkglists; 98 | pu_config_bool_t disabledownloadtimeout; 99 | pu_config_bool_t disablesandbox; 100 | 101 | int siglevel; 102 | int localfilesiglevel; 103 | int remotefilesiglevel; 104 | 105 | int siglevel_mask; 106 | int localfilesiglevel_mask; 107 | int remotefilesiglevel_mask; 108 | 109 | alpm_list_t *architectures; 110 | alpm_list_t *cachedirs; 111 | alpm_list_t *holdpkgs; 112 | alpm_list_t *hookdirs; 113 | alpm_list_t *ignoregroups; 114 | alpm_list_t *ignorepkgs; 115 | alpm_list_t *noextract; 116 | alpm_list_t *noupgrade; 117 | 118 | int cleanmethod; 119 | 120 | alpm_list_t *repos; 121 | } pu_config_t; 122 | 123 | /* sync repos */ 124 | typedef struct pu_repo_t { 125 | char *name; 126 | alpm_list_t *servers; 127 | alpm_list_t *cacheservers; 128 | int usage; 129 | int siglevel; 130 | int siglevel_mask; 131 | } pu_repo_t; 132 | 133 | typedef enum pu_config_reader_status_t { 134 | PU_CONFIG_READER_STATUS_OK, 135 | PU_CONFIG_READER_STATUS_ERROR, 136 | PU_CONFIG_READER_STATUS_INVALID_VALUE, 137 | PU_CONFIG_READER_STATUS_UNKNOWN_OPTION, 138 | } pu_config_reader_status_t; 139 | 140 | typedef struct pu_config_reader_t { 141 | int eof, line, error; 142 | char *sysroot, *file, *section, *key, *value; 143 | pu_config_t *config; 144 | pu_repo_t *repo; 145 | pu_config_reader_status_t status; 146 | 147 | void *_mini; 148 | struct pu_config_reader_t *_parent; 149 | alpm_list_t *_includes; 150 | int _sysroot_fd; 151 | } pu_config_reader_t; 152 | 153 | pu_repo_t *pu_repo_new(void); 154 | void pu_repo_free(pu_repo_t *repo); 155 | alpm_db_t *pu_register_syncdb(alpm_handle_t *handle, pu_repo_t *repo); 156 | alpm_list_t *pu_register_syncdbs(alpm_handle_t *handle, alpm_list_t *repos); 157 | 158 | pu_config_t *pu_config_new(void); 159 | void pu_config_merge(pu_config_t *dest, pu_config_t *src); 160 | int pu_config_resolve(pu_config_t *config); 161 | int pu_config_resolve_sysroot(pu_config_t *config, const char *sysroot); 162 | void pu_config_free(pu_config_t *config); 163 | 164 | alpm_handle_t *pu_initialize_handle_from_config(pu_config_t *config); 165 | 166 | pu_config_reader_t *pu_config_reader_new_sysroot(pu_config_t *config, 167 | const char *file, const char *sysroot); 168 | pu_config_reader_t *pu_config_reader_new(pu_config_t *config, const char *file); 169 | pu_config_reader_t *pu_config_reader_finit(pu_config_t *config, FILE *stream); 170 | int pu_config_reader_next(pu_config_reader_t *reader); 171 | void pu_config_reader_free(pu_config_reader_t *reader); 172 | 173 | #endif /* PACUTILS_CONFIG_H */ 174 | -------------------------------------------------------------------------------- /lib/pacutils/depends.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2020 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include "depends.h" 26 | 27 | typedef enum pu_deptype_t { 28 | PU_DEPTYPE_DEPEND = (1 << 0), 29 | PU_DEPTYPE_OPTIONAL = (1 << 1), 30 | PU_DEPTYPE_MAKE = (1 << 2), 31 | PU_DEPTYPE_CHECK = (1 << 3), 32 | } pu_deptype_t; 33 | 34 | static int pu_pkgver_satisfies_dep(const char *ver, alpm_depend_t *dep) { 35 | switch (dep->mod) { 36 | case ALPM_DEP_MOD_ANY: 37 | return 1; 38 | case ALPM_DEP_MOD_EQ: 39 | return alpm_pkg_vercmp(ver, dep->version) == 0; 40 | case ALPM_DEP_MOD_LE: 41 | return alpm_pkg_vercmp(ver, dep->version) <= 0; 42 | case ALPM_DEP_MOD_GE: 43 | return alpm_pkg_vercmp(ver, dep->version) >= 0; 44 | case ALPM_DEP_MOD_LT: 45 | return alpm_pkg_vercmp(ver, dep->version) < 0; 46 | case ALPM_DEP_MOD_GT: 47 | return alpm_pkg_vercmp(ver, dep->version) > 0; 48 | } 49 | return 0; /* should never reach this */ 50 | } 51 | 52 | int pu_provision_satisfies_dep(alpm_depend_t *provision, alpm_depend_t *dep) { 53 | /* alpm exposes the alpm_depend_t structure, but does not provide a method 54 | * for filling in the name_hash, only rely on it if both arguments have it 55 | * filled in */ 56 | return (provision->name_hash == 0 || dep->name_hash == 0 57 | || provision->name_hash == dep->name_hash) 58 | && strcmp(provision->name, dep->name) == 0 59 | && pu_pkgver_satisfies_dep(provision->version, dep); 60 | } 61 | 62 | int pu_pkg_satisfies_dep(alpm_pkg_t *pkg, alpm_depend_t *dep) { 63 | alpm_list_t *p; 64 | if (strcmp(alpm_pkg_get_name(pkg), dep->name) == 0 && 65 | pu_pkgver_satisfies_dep(alpm_pkg_get_version(pkg), dep)) { 66 | return 1; 67 | } 68 | for (p = alpm_pkg_get_provides(pkg); p; p = p->next) { 69 | if (pu_provision_satisfies_dep(p->data, dep)) { 70 | return 1; 71 | } 72 | } 73 | return 0; 74 | } 75 | 76 | static int _pu_pkg_satisfies_deplist(alpm_pkg_t *pkg, alpm_list_t *deps) { 77 | while (deps) { 78 | if (pu_pkg_satisfies_dep(pkg, deps->data)) { return 1; } 79 | deps = deps->next; 80 | } 81 | return 0; 82 | } 83 | 84 | int pu_pkg_depends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg) { 85 | return _pu_pkg_satisfies_deplist(dpkg, alpm_pkg_get_depends(pkg)); 86 | } 87 | 88 | int pu_pkg_optdepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg) { 89 | return _pu_pkg_satisfies_deplist(dpkg, alpm_pkg_get_optdepends(pkg)); 90 | } 91 | 92 | int pu_pkg_checkdepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg) { 93 | return _pu_pkg_satisfies_deplist(dpkg, alpm_pkg_get_checkdepends(pkg)); 94 | } 95 | 96 | int pu_pkg_makedepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg) { 97 | return _pu_pkg_satisfies_deplist(dpkg, alpm_pkg_get_makedepends(pkg)); 98 | } 99 | 100 | static int pu_pkg_find_reversedeps(alpm_pkg_t *pkg, int type, 101 | alpm_list_t *pkgs, alpm_list_t **ret) { 102 | alpm_list_t *h; 103 | for (h = pkgs; h; h = h->next) { 104 | if ( (type & PU_DEPTYPE_DEPEND && pu_pkg_depends_on(h->data, pkg)) 105 | || (type & PU_DEPTYPE_OPTIONAL && pu_pkg_optdepends_on(h->data, pkg)) 106 | || (type & PU_DEPTYPE_CHECK && pu_pkg_checkdepends_on(h->data, pkg)) 107 | || (type & PU_DEPTYPE_MAKE && pu_pkg_makedepends_on(h->data, pkg)) ) { 108 | if (alpm_list_append(ret, h->data) == NULL) { 109 | return -1; 110 | } 111 | } 112 | } 113 | return 0; 114 | } 115 | 116 | int pu_pkg_find_requiredby(alpm_pkg_t *pkg, alpm_list_t *pkgs, 117 | alpm_list_t **ret) { 118 | return pu_pkg_find_reversedeps(pkg, PU_DEPTYPE_DEPEND, pkgs, ret); 119 | } 120 | 121 | int pu_pkg_find_optionalfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 122 | alpm_list_t **ret) { 123 | return pu_pkg_find_reversedeps(pkg, PU_DEPTYPE_OPTIONAL, pkgs, ret); 124 | } 125 | 126 | int pu_pkg_find_makedepfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 127 | alpm_list_t **ret) { 128 | return pu_pkg_find_reversedeps(pkg, PU_DEPTYPE_MAKE, pkgs, ret); 129 | } 130 | 131 | int pu_pkg_find_checkdepfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 132 | alpm_list_t **ret) { 133 | return pu_pkg_find_reversedeps(pkg, PU_DEPTYPE_CHECK, pkgs, ret); 134 | } 135 | 136 | alpm_pkg_t *pu_pkglist_find_dep_satisfier(alpm_list_t *pkgs, 137 | alpm_depend_t *dep) { 138 | alpm_list_t *p; 139 | for (p = pkgs; p; p = p->next) { 140 | if (pu_pkg_satisfies_dep(p->data, dep)) { return p->data; } 141 | } 142 | return NULL; 143 | } 144 | 145 | alpm_pkg_t *pu_db_find_dep_satisfier(alpm_db_t *db, alpm_depend_t *dep) { 146 | alpm_pkg_t *pkg = alpm_db_get_pkg(db, dep->name); 147 | if (pkg && pu_pkg_satisfies_dep(pkg, dep)) { return pkg; } 148 | return pu_pkglist_find_dep_satisfier(alpm_db_get_pkgcache(db), dep); 149 | } 150 | 151 | alpm_pkg_t *pu_dblist_find_dep_satisfier(alpm_list_t *dbs, alpm_depend_t *dep) { 152 | alpm_list_t *d; 153 | for (d = dbs; d; d = d->next) { 154 | alpm_pkg_t *pkg = alpm_db_get_pkg(d->data, dep->name); 155 | if (pkg && pu_pkg_satisfies_dep(pkg, dep)) { return pkg; } 156 | } 157 | for (d = dbs; d; d = d->next) { 158 | alpm_pkg_t *pkg = pu_pkglist_find_dep_satisfier(alpm_db_get_pkgcache(d->data), 159 | dep); 160 | if (pkg) { return pkg; } 161 | } 162 | return NULL; 163 | } 164 | -------------------------------------------------------------------------------- /lib/pacutils/depends.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2020 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PACUTILS_DEPENDS_H 24 | #define PACUTILS_DEPENDS_H 25 | 26 | #include 27 | 28 | int pu_provision_satisfies_dep(alpm_depend_t *provision, alpm_depend_t *dep); 29 | int pu_pkg_satisfies_dep(alpm_pkg_t *pkg, alpm_depend_t *dep); 30 | int pu_pkg_depends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg); 31 | int pu_pkg_optdepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg); 32 | int pu_pkg_checkdepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg); 33 | int pu_pkg_makedepends_on(alpm_pkg_t *pkg, alpm_pkg_t *dpkg); 34 | 35 | int pu_pkg_find_requiredby(alpm_pkg_t *pkg, alpm_list_t *pkgs, 36 | alpm_list_t **ret); 37 | int pu_pkg_find_optionalfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 38 | alpm_list_t **ret); 39 | int pu_pkg_find_makedepfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 40 | alpm_list_t **ret); 41 | int pu_pkg_find_checkdepfor(alpm_pkg_t *pkg, alpm_list_t *pkgs, 42 | alpm_list_t **ret); 43 | 44 | alpm_pkg_t *pu_pkglist_find_dep_satisfier(alpm_list_t *pkgs, 45 | alpm_depend_t *dep); 46 | alpm_pkg_t *pu_db_find_dep_satisfier(alpm_db_t *db, alpm_depend_t *dep); 47 | alpm_pkg_t *pu_dblist_find_dep_satisfier(alpm_list_t *dbs, alpm_depend_t *dep); 48 | 49 | #endif /* PACUTILS_DEPENDS_H */ 50 | -------------------------------------------------------------------------------- /lib/pacutils/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PACUTILS_LOG_H 24 | #define PACUTILS_LOG_H 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | typedef enum { 32 | PU_LOG_OPERATION_INSTALL, 33 | PU_LOG_OPERATION_REINSTALL, 34 | PU_LOG_OPERATION_UPGRADE, 35 | PU_LOG_OPERATION_DOWNGRADE, 36 | PU_LOG_OPERATION_REMOVE, 37 | } pu_log_operation_t; 38 | 39 | typedef struct { 40 | pu_log_operation_t operation; 41 | char *target; 42 | char *old_version; 43 | char *new_version; 44 | } pu_log_action_t; 45 | 46 | typedef struct { 47 | struct tm tm; 48 | int gmtoff; 49 | unsigned int has_seconds: 1; 50 | unsigned int has_gmtoff: 1; 51 | } pu_log_timestamp_t; 52 | 53 | typedef struct { 54 | pu_log_timestamp_t timestamp; 55 | char *caller; 56 | char *message; 57 | } pu_log_entry_t; 58 | 59 | typedef enum { 60 | PU_LOG_TRANSACTION_STARTED = 1, 61 | PU_LOG_TRANSACTION_COMPLETED, 62 | PU_LOG_TRANSACTION_INTERRUPTED, 63 | PU_LOG_TRANSACTION_FAILED, 64 | } pu_log_transaction_status_t; 65 | 66 | typedef struct { 67 | pu_log_transaction_status_t status; 68 | alpm_list_t *start, *end; 69 | } pu_log_transaction_t; 70 | 71 | typedef struct { 72 | FILE *stream; 73 | int eof; 74 | 75 | char _buf[256]; /* read buffer */ 76 | char *_next; /* next line indicator */ 77 | int _close_stream; /* close stream on free */ 78 | pu_log_timestamp_t _next_ts; 79 | } pu_log_reader_t; 80 | 81 | pu_log_transaction_status_t pu_log_transaction_parse(const char *message); 82 | 83 | int pu_log_fprint_entry(FILE *stream, pu_log_entry_t *entry); 84 | pu_log_entry_t *pu_log_reader_next(pu_log_reader_t *reader); 85 | pu_log_reader_t *pu_log_reader_open_stream(FILE *stream); 86 | pu_log_reader_t *pu_log_reader_open_file(const char *path); 87 | void pu_log_reader_free(pu_log_reader_t *p); 88 | alpm_list_t *pu_log_parse_file(FILE *stream); 89 | void pu_log_entry_free(pu_log_entry_t *entry); 90 | 91 | pu_log_action_t *pu_log_action_parse(const char *message); 92 | void pu_log_action_free(pu_log_action_t *action); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /lib/pacutils/mtree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #ifndef PACUTILS_MTREE_H 26 | #define PACUTILS_MTREE_H 27 | 28 | typedef struct pu_mtree_t { 29 | char *path; 30 | char type[16]; 31 | uid_t uid; 32 | gid_t gid; 33 | mode_t mode; 34 | off_t size; 35 | char md5digest[33]; 36 | char sha256digest[65]; 37 | } pu_mtree_t; 38 | 39 | typedef struct { 40 | FILE *stream; 41 | int eof; 42 | pu_mtree_t defaults; 43 | 44 | char *_buf; /* line buffer */ 45 | size_t _buflen; /* line buffer length */ 46 | char *_stream_buf; /* buffer for in-memory streams */ 47 | int _close_stream; /* close stream on free */ 48 | } pu_mtree_reader_t; 49 | 50 | __attribute__((__deprecated__)) 51 | alpm_list_t *pu_mtree_load_pkg_mtree(alpm_handle_t *handle, alpm_pkg_t *pkg); 52 | 53 | pu_mtree_reader_t *pu_mtree_reader_open_stream(FILE *stream); 54 | pu_mtree_reader_t *pu_mtree_reader_open_file(const char *path); 55 | pu_mtree_reader_t *pu_mtree_reader_open_package(alpm_handle_t *h, 56 | alpm_pkg_t *p); 57 | pu_mtree_t *pu_mtree_reader_next(pu_mtree_reader_t *reader, pu_mtree_t *dest); 58 | pu_mtree_t *pu_mtree_new(void); 59 | void pu_mtree_reader_free(pu_mtree_reader_t *reader); 60 | void pu_mtree_free(pu_mtree_t *mtree); 61 | 62 | #endif /* PACUTILS_MTREE_H */ 63 | -------------------------------------------------------------------------------- /lib/pacutils/ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include "config.h" 26 | 27 | #ifndef PACUTILS_UI_H 28 | #define PACUTILS_UI_H 29 | 30 | typedef struct pu_ui_ctx_download_t { 31 | /* settings */ 32 | 33 | /* FILE to write to */ 34 | FILE *out; 35 | /* how frequently to update the current download (ms), 36 | * setting too low a value can cause flickering */ 37 | uint64_t update_interval_same; 38 | /* how frequently to advance to the next download (ms), 39 | * setting too low a value can cause flickering */ 40 | uint64_t update_interval_next; 41 | 42 | /* context */ 43 | uint64_t last_update; 44 | uint64_t last_advance; 45 | alpm_list_t *active_downloads; 46 | int index; 47 | } pu_ui_ctx_download_t; 48 | 49 | void pu_ui_error(const char *fmt, ...); 50 | void pu_ui_warn(const char *fmt, ...); 51 | void pu_ui_notice(const char *fmt, ...); 52 | 53 | void pu_ui_verror(const char *fmt, va_list args); 54 | void pu_ui_vwarn(const char *fmt, va_list args); 55 | void pu_ui_vnotice(const char *fmt, va_list args); 56 | 57 | int pu_ui_confirm(int def, const char *prompt, ...); 58 | long pu_ui_select_index(long def, long min, long max, const char *prompt, ...); 59 | 60 | const char *pu_ui_msg_progress(alpm_progress_t event); 61 | 62 | void pu_ui_display_transaction(alpm_handle_t *handle); 63 | 64 | void pu_ui_cb_download(void *ctx, const char *filename, 65 | alpm_download_event_type_t event, void *data); 66 | void pu_ui_cb_progress(void *ctx, alpm_progress_t event, const char *pkgname, 67 | int percent, size_t total, size_t current); 68 | void pu_ui_cb_question(void *ctx, alpm_question_t *question); 69 | void pu_ui_cb_event(void *ctx, alpm_event_t *event); 70 | 71 | pu_config_t *pu_ui_config_parse(pu_config_t *dest, const char *file); 72 | pu_config_t *pu_ui_config_load(pu_config_t *dest, const char *file); 73 | 74 | pu_config_t *pu_ui_config_parse_sysroot(pu_config_t *dest, const char *file, 75 | const char *root); 76 | pu_config_t *pu_ui_config_load_sysroot(pu_config_t *dest, const char *file, 77 | const char *root); 78 | 79 | int pu_ui_read_list_from_fd(int fd, int sep, alpm_list_t **dest); 80 | int pu_ui_read_list_from_fdstr(const char *fdstr, int sep, alpm_list_t **dest); 81 | int pu_ui_read_list_from_path(const char *file, int sep, alpm_list_t **dest); 82 | int pu_ui_read_list_from_stream(FILE *file, int sep, alpm_list_t **dest, const char *name); 83 | 84 | int pu_ui_process_std_arg(const char *arg, int sep, alpm_list_t **dest); 85 | 86 | #endif /* PACUTILS_UI_H */ 87 | -------------------------------------------------------------------------------- /lib/pacutils/uix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "ui.h" 27 | #include "uix.h" 28 | 29 | static void pu_uix_assert(int success) { 30 | if(!success) { 31 | pu_ui_error("fatal error (%s)", strerror(errno)); 32 | exit(EXIT_FAILURE); 33 | } 34 | } 35 | 36 | static void *pu_uix_assertp(void *success) { 37 | pu_uix_assert(success != NULL); 38 | return success; 39 | } 40 | 41 | char *pu_uix_strdup(const char *string) { 42 | return pu_uix_assertp(strdup(string)); 43 | } 44 | 45 | void *pu_uix_malloc(size_t size) { 46 | return pu_uix_assertp(malloc(size)); 47 | } 48 | void *pu_uix_calloc(size_t nelem, size_t elsize) { 49 | return pu_uix_assertp(calloc(nelem, elsize)); 50 | } 51 | void *pu_uix_realloc(void *ptr, size_t size) { 52 | return pu_uix_assertp(realloc(ptr, size)); 53 | } 54 | 55 | alpm_list_t *pu_uix_list_append(alpm_list_t **list, void *data) { 56 | return pu_uix_assertp(alpm_list_append(list, data)); 57 | } 58 | 59 | alpm_list_t *pu_uix_list_append_strdup(alpm_list_t **list, const char *data) { 60 | return pu_uix_assertp(alpm_list_append_strdup(list, data)); 61 | } 62 | 63 | void pu_uix_read_list_from_fdstr(const char *fdstr, int sep, alpm_list_t **dest) { 64 | pu_uix_assert(pu_ui_read_list_from_fdstr(fdstr, sep, dest) == 0); 65 | } 66 | void pu_uix_read_list_from_path(const char *file, int sep, alpm_list_t **dest) { 67 | pu_uix_assert(pu_ui_read_list_from_path(file, sep, dest) == 0); 68 | } 69 | void pu_uix_read_list_from_stream(FILE *stream, int sep, alpm_list_t **dest, const char *label) { 70 | pu_uix_assert(pu_ui_read_list_from_stream(stream, sep, dest, label) == 0); 71 | } 72 | 73 | void pu_uix_process_std_arg(const char *arg, int sep, alpm_list_t **dest) { 74 | pu_uix_assert(pu_ui_process_std_arg(arg, sep, dest) == 0); 75 | } 76 | -------------------------------------------------------------------------------- /lib/pacutils/uix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #ifndef PACUTILS_UIX_H 27 | #define PACUTILS_UIX_H 28 | 29 | /****************************************************************************** 30 | * Basic wrappers with error messages that exit the program on failure 31 | *****************************************************************************/ 32 | 33 | char *pu_uix_strdup(const char *string); 34 | 35 | void *pu_uix_malloc(size_t size); 36 | void *pu_uix_calloc(size_t nelem, size_t elsize); 37 | void *pu_uix_realloc(void *ptr, size_t size); 38 | 39 | alpm_list_t *pu_uix_list_append(alpm_list_t **list, void *data); 40 | alpm_list_t *pu_uix_list_append_strdup(alpm_list_t **list, const char *data); 41 | 42 | void pu_uix_read_list_from_fd_string(const char *fdstr, int sep, alpm_list_t **dest); 43 | void pu_uix_read_list_from_path(const char *file, int sep, alpm_list_t **dest); 44 | void pu_uix_read_list_from_stream(FILE *stream, int sep, alpm_list_t **dest, const char *label); 45 | 46 | void pu_uix_process_std_arg(const char *arg, int sep, alpm_list_t **dest); 47 | 48 | #endif /* PACUTILS_UIX_H */ 49 | -------------------------------------------------------------------------------- /lib/pacutils/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #define _XOPEN_SOURCE 700 /* strptime */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "util.h" 35 | 36 | int pu_iscspace(int c) { 37 | return c == ' ' || c == '\f' || c == '\n' 38 | || c == '\r' || c == '\t' || c == '\v'; 39 | } 40 | 41 | char *pu_basename(char *path) { 42 | char *c; 43 | if (!path) { return NULL; } 44 | for (c = path + strlen(path); c > path && *(c - 1) != '/'; --c); 45 | return c; 46 | } 47 | 48 | char *pu_hr_size(off_t bytes, char *dest) { 49 | static char *suff[] = {"B", "K", "M", "G", "T", "P", "E", NULL}; 50 | float hrsize; 51 | int s = 0; 52 | while ((bytes >= 1000000 || bytes <= -1000000) && suff[s + 1]) { 53 | bytes /= 1024; 54 | ++s; 55 | } 56 | hrsize = bytes; 57 | if ((hrsize >= 1000 || hrsize <= -1000) && suff[s + 1]) { 58 | hrsize /= 1024; 59 | ++s; 60 | } 61 | sprintf(dest, "%.2f %s", hrsize, suff[s]); 62 | return dest; 63 | } 64 | 65 | void *_pu_list_shift(alpm_list_t **list) { 66 | alpm_list_t *l = *list; 67 | void *data; 68 | if (l == NULL) { return NULL; } 69 | data = l->data; 70 | if (l->next) { l->next->prev = l->prev; } 71 | *list = l->next; 72 | free(l); 73 | return data; 74 | } 75 | 76 | struct tm *pu_parse_datetime(const char *string, struct tm *stm) { 77 | const char *c, *end; 78 | memset(stm, 0, sizeof(struct tm)); 79 | stm->tm_isdst = -1; 80 | stm->tm_mday = 1; 81 | 82 | /* locate the end of the usable date */ 83 | if ((c = strpbrk(string, " T")) && (c = strpbrk(c, ",.Z-+"))) { 84 | /* ignore trailing timezone and/or fractional elements */ 85 | end = c; 86 | } else { 87 | end = string + strlen(string); 88 | } 89 | 90 | c = string; 91 | #define pu_parse_bit(s, f, t) \ 92 | do { \ 93 | if(!(s = strptime(s, f, t))) { \ 94 | return NULL; \ 95 | } \ 96 | if(s == end) { \ 97 | return stm; \ 98 | } else if(s > end) { \ 99 | /* no idea how we got here, but better safe than sorry */ \ 100 | return NULL; \ 101 | } \ 102 | } while(0) 103 | pu_parse_bit(c, "%Y", stm); 104 | pu_parse_bit(c, "-%m", stm); 105 | pu_parse_bit(c, "-%d", stm); 106 | if (c[0] == ' ' || c[0] == 'T') { c++; } 107 | pu_parse_bit(c, "%H", stm); 108 | pu_parse_bit(c, ":%M", stm); 109 | pu_parse_bit(c, ":%S", stm); 110 | #undef pu_parse_bit 111 | 112 | return NULL; 113 | } 114 | 115 | alpm_list_t *pu_list_append_str(alpm_list_t **list, const char *str) { 116 | alpm_list_t *ret; 117 | char *dup; 118 | if ((dup = strdup(str)) && (ret = alpm_list_append(list, dup))) { 119 | return ret; 120 | } else { 121 | free(dup); 122 | return NULL; 123 | } 124 | } 125 | 126 | char *pu_vasprintf(const char *fmt, va_list args) { 127 | va_list args_copy; 128 | char *p; 129 | int len; 130 | 131 | va_copy(args_copy, args); 132 | len = vsnprintf(NULL, 0, fmt, args_copy); 133 | va_end(args_copy); 134 | 135 | #if SIZE_MAX <= INT_MAX 136 | if (len >= SIZE_MAX) { 137 | errno = EOVERFLOW; 138 | return NULL; 139 | } 140 | #endif 141 | 142 | if (len < 0) { 143 | return NULL; 144 | } 145 | 146 | if ((p = malloc((size_t)len + 1)) == NULL) { 147 | return NULL; 148 | } 149 | 150 | vsprintf(p, fmt, args); 151 | 152 | return p; 153 | } 154 | 155 | char *pu_asprintf(const char *fmt, ...) { 156 | char *p; 157 | va_list args; 158 | 159 | va_start(args, fmt); 160 | p = pu_vasprintf(fmt, args); 161 | va_end(args); 162 | 163 | return p; 164 | } 165 | 166 | char *pu_prepend_dir(const char *dir, const char *path) { 167 | const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/"; 168 | while (path[0] == '/') { path++; } 169 | return pu_asprintf("%s%s%s", dir, sep, path); 170 | } 171 | 172 | int pu_prepend_dir_list(const char *dir, alpm_list_t *paths) { 173 | while (paths) { 174 | char *newval = pu_prepend_dir(dir, paths->data); 175 | if (newval == NULL) { return -1; } 176 | free(paths->data); 177 | paths->data = newval; 178 | paths = paths->next; 179 | } 180 | return 0; 181 | } 182 | 183 | FILE *pu_fopenat(int dirfd, const char *path, const char *mode) { 184 | int fd, flags = 0, rwflag = 0; 185 | FILE *stream; 186 | const char *m = mode; 187 | switch (*(m++)) { 188 | case 'r': 189 | rwflag = O_RDONLY; 190 | break; 191 | case 'w': 192 | rwflag = O_WRONLY; 193 | flags |= O_CREAT | O_TRUNC; 194 | break; 195 | case 'a': 196 | rwflag = O_WRONLY; 197 | flags |= O_CREAT | O_APPEND; 198 | break; 199 | default: 200 | errno = EINVAL; 201 | return NULL; 202 | } 203 | if (m[1] == 'b') { m++; } 204 | if (m[1] == '+') { m++; rwflag = O_RDWR; } 205 | while (*m) { 206 | switch (*(m++)) { 207 | case 'e': 208 | flags |= O_CLOEXEC; 209 | break; 210 | case 'x': 211 | flags |= O_EXCL; 212 | break; 213 | } 214 | } 215 | if ((fd = openat(dirfd, path, flags | rwflag, 0666)) < 0) { return NULL; } 216 | if ((stream = fdopen(fd, mode)) == NULL) { close(fd); return NULL; } 217 | return stream; 218 | } 219 | 220 | int pu_read_list_from_stream(FILE *f, int sep, alpm_list_t **dest) { 221 | char *buf = NULL; 222 | size_t len = 0; 223 | ssize_t read; 224 | while ((read = getdelim(&buf, &len, sep, f)) != -1) { 225 | if (buf[read - 1] == sep) { buf[read - 1] = '\0'; } 226 | if (alpm_list_append_strdup(dest, buf) == NULL) { 227 | return -1; 228 | } 229 | } 230 | free(buf); 231 | return feof(f) ? 0 : -1; 232 | } 233 | 234 | int _pu_read_list_internal(FILE *f, int sep, alpm_list_t **dest) { 235 | if (f) { 236 | int ret = pu_read_list_from_stream(f, sep, dest); 237 | fclose(f); 238 | return ret; 239 | } else { 240 | return -1; 241 | } 242 | } 243 | 244 | int pu_read_list_from_fd(int fd, int sep, alpm_list_t **dest) { 245 | return _pu_read_list_internal(fdopen(fd, "r"), sep, dest); 246 | } 247 | 248 | int pu_read_list_from_path(const char *path, int sep, alpm_list_t **dest) { 249 | return _pu_read_list_internal(fopen(path, "r"), sep, dest); 250 | } 251 | -------------------------------------------------------------------------------- /lib/pacutils/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PACUTILS_UTIL_H 24 | #define PACUTILS_UTIL_H 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | int pu_iscspace(int c); 32 | 33 | char *pu_basename(char *path); 34 | char *pu_hr_size(off_t bytes, char *dest); 35 | struct tm *pu_parse_datetime(const char *string, struct tm *stm); 36 | 37 | void *_pu_list_shift(alpm_list_t **list); 38 | alpm_list_t *pu_list_append_str(alpm_list_t **list, const char *str); 39 | 40 | char *pu_vasprintf(const char *fmt, va_list args); 41 | char *pu_asprintf(const char *fmt, ...); 42 | 43 | char *pu_prepend_dir(const char *dir, const char *path); 44 | int pu_prepend_dir_list(const char *dir, alpm_list_t *paths); 45 | 46 | FILE *pu_fopenat(int dirfd, const char *path, const char *mode); 47 | 48 | int pu_read_list_from_stream(FILE *f, int sep, alpm_list_t **dest); 49 | int pu_read_list_from_fd(int fd, int sep, alpm_list_t **dest); 50 | int pu_read_list_from_path(const char *path, int sep, alpm_list_t **dest); 51 | 52 | #endif /* PACUTILS_UTIL_H */ 53 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | paccapability 2 | paccheck 3 | pacconf 4 | pacfile 5 | pacinfo 6 | pacini 7 | pacinstall 8 | paclock 9 | paclog 10 | pacremove 11 | pacrepairdb 12 | pacrepairfile 13 | pacreport 14 | pacsift 15 | pacsync 16 | pactrans 17 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | -include ../configure.mk 2 | -include configure.mk 3 | 4 | ALPM_CFLAGS ?= $(shell pkg-config libalpm --cflags) 5 | 6 | CFLAGS ?= -Wall -Wextra -Wpedantic -Werror -g 7 | 8 | override CFLAGS += $(ALPM_CFLAGS) 9 | override CPPFLAGS += -I../lib 10 | override LDFLAGS += -L../lib 11 | override LDLIBS += -lpacutils -lalpm -larchive 12 | 13 | PREFIX ?= /usr/local 14 | EXEC_PREFIX ?= ${PREFIX} 15 | BINDIR ?= ${EXEC_PREFIX}/bin 16 | INCLUDEDIR ?= ${PREFIX}/include 17 | LIBDIR ?= ${EXEC_PREFIX}/lib 18 | SYSCONFDIR ?= ${PREFIX}/etc 19 | 20 | FILESDBEXT ?= .files 21 | PACMANCONF ?= ${SYSCONFDIR}/pacman.conf 22 | 23 | override CPPFLAGS += \ 24 | -DFILESDBEXT=\"$(FILESDBEXT)\" \ 25 | -DPACMANCONF=\"$(PACMANCONF)\" \ 26 | -DSYSCONFDIR=\"$(SYSCONFDIR)\" 27 | 28 | GITTAG = $(shell git describe --abbrev=0 2>/dev/null) 29 | ifneq ($(GITTAG),) 30 | GITVER = $(subst $(GITTAG)-,,$(shell git describe --dirty)) 31 | ifneq ($(GITTAG),$(GITVER)) 32 | override CPPFLAGS += -DGITVER=\"$(GITVER)\" 33 | endif 34 | endif 35 | 36 | OBJECTS = \ 37 | paccapability \ 38 | paccheck \ 39 | pacconf \ 40 | pacfile \ 41 | pacinfo \ 42 | pacini \ 43 | paclock \ 44 | paclog \ 45 | pacrepairdb \ 46 | pacrepairfile \ 47 | pacreport \ 48 | pacsift \ 49 | pacsync \ 50 | pactrans 51 | 52 | all: $(OBJECTS) pacinstall pacremove 53 | 54 | pacsift: LDLIBS += -lm 55 | 56 | pacremove: | pactrans 57 | ln -fs $| $@ 58 | 59 | pacinstall: | pactrans 60 | ln -fs $| $@ 61 | 62 | install: all 63 | install -d "${DESTDIR}${BINDIR}" 64 | install -m 755 $(OBJECTS) "${DESTDIR}${BINDIR}/" 65 | cp -d pacinstall "${DESTDIR}${BINDIR}/pacinstall" 66 | cp -d pacremove "${DESTDIR}${BINDIR}/pacremove" 67 | 68 | clean: 69 | $(RM) $(OBJECTS) pacinstall pacremove 70 | 71 | .PHONY: all clean install 72 | -------------------------------------------------------------------------------- /src/config-defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef CONFIG_DEFAULTS_H 24 | #define CONFIG_DEFAULTS_H 25 | 26 | #define BASEVER "0.15.0" 27 | 28 | #ifdef GITVER 29 | #define BUILDVER BASEVER "+" GITVER 30 | #else 31 | #define BUILDVER BASEVER 32 | #endif 33 | 34 | /* duplicates default configuration settings from lib/Makefile in order to 35 | * allow compilation without make and to provide defaults for analyzers */ 36 | 37 | #ifndef FILESDBEXT 38 | #define FILESDBEXT ".files" 39 | #endif 40 | 41 | #ifndef PREFIX 42 | #define PREFIX "/usr/local" 43 | #endif 44 | 45 | #ifndef SYSCONFDIR 46 | #define SYSCONFDIR PREFIX "/etc" 47 | #endif 48 | 49 | #ifndef PACMANCONF 50 | #define PACMANCONF SYSCONFDIR "/etc/pacman.conf" 51 | #endif 52 | 53 | #endif /* CONFIG_DEFAULTS_H */ 54 | -------------------------------------------------------------------------------- /src/paccapability.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include "config-defaults.h" 28 | 29 | const char *myname = "paccapability", *myver = BUILDVER; 30 | 31 | enum longopt_flags { 32 | FLAG_HELP = 1000, 33 | FLAG_VERSION, 34 | }; 35 | 36 | const char *cap_prefix = "ALPM_CAPABILITY_"; 37 | 38 | struct cap { 39 | const char *name; 40 | enum alpm_caps value; 41 | } cap_map[] = { 42 | { "NLS", ALPM_CAPABILITY_NLS }, 43 | { "DOWNLOADER", ALPM_CAPABILITY_DOWNLOADER }, 44 | { "SIGNATURES", ALPM_CAPABILITY_SIGNATURES }, 45 | }; 46 | 47 | void usage(int ret) { 48 | FILE *stream = (ret ? stderr : stdout); 49 | #define hputs(x) fputs(x"\n", stream) 50 | hputs("paccapability - query alpm capabilities"); 51 | hputs("usage: paccapability [options] [...]"); 52 | hputs(" paccapability (--help|--version)"); 53 | hputs("options:"); 54 | hputs(" --help display this help information"); 55 | hputs(" --version display version information"); 56 | #undef hputs 57 | exit(ret); 58 | } 59 | 60 | struct cap *get_cap(const char *name) { 61 | size_t i; 62 | 63 | if (strncasecmp(name, cap_prefix, strlen(cap_prefix)) == 0) { 64 | name += strlen(cap_prefix); 65 | } 66 | 67 | for (i = 0; i < sizeof(cap_map) / sizeof(struct cap); i++) { 68 | if (strcasecmp(name, cap_map[i].name) == 0) { return &cap_map[i]; } 69 | } 70 | 71 | return NULL; 72 | } 73 | 74 | void parse_opts(int argc, char **argv) { 75 | int c; 76 | 77 | char *short_opts = ""; 78 | struct option long_opts[] = { 79 | { "help", no_argument, NULL, FLAG_HELP }, 80 | { "version", no_argument, NULL, FLAG_VERSION }, 81 | { 0, 0, 0, 0 }, 82 | }; 83 | 84 | while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { 85 | switch (c) { 86 | case FLAG_HELP: 87 | usage(0); 88 | break; 89 | case FLAG_VERSION: 90 | pu_print_version(myname, myver); 91 | exit(0); 92 | break; 93 | case '?': 94 | default: 95 | usage(1); 96 | break; 97 | } 98 | } 99 | } 100 | 101 | int main(int argc, char **argv) { 102 | int ret = 0; 103 | 104 | parse_opts(argc, argv); 105 | 106 | if (optind < argc) { 107 | for (; optind < argc; optind++) { 108 | struct cap *c = get_cap(argv[optind]); 109 | if (c) { 110 | int hascap = (alpm_capabilities() & c->value) ? 1 : 0; 111 | printf("%s%s: %d\n", cap_prefix, c->name, hascap); 112 | if (!hascap) { ret = 1; } 113 | } else { 114 | pu_ui_warn("unknown capability '%s'", argv[optind]); 115 | ret = 1; 116 | } 117 | } 118 | } else { 119 | size_t i; 120 | for (i = 0; i < sizeof(cap_map) / sizeof(struct cap); i++) { 121 | if (alpm_capabilities() & cap_map[i].value) { 122 | printf("%s%s\n", cap_prefix, cap_map[i].name); 123 | } 124 | } 125 | } 126 | 127 | return ret; 128 | } 129 | -------------------------------------------------------------------------------- /src/pacini.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2016 Andrew Gregory 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 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell 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 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include "config-defaults.h" 31 | 32 | #include "../ext/mini.c/mini.c" 33 | 34 | const char *myname = "pacini", *myver = BUILDVER; 35 | 36 | mini_t *ini = NULL; 37 | alpm_list_t *directives = NULL; 38 | char sep = '\n', *section_name = NULL, *input_file = NULL; 39 | int section_list = 0, verbose = 0; 40 | 41 | void cleanup(void) { 42 | free(section_name); 43 | alpm_list_free(directives); 44 | } 45 | 46 | void usage(int ret) { 47 | FILE *stream = (ret ? stderr : stdout); 48 | #define hputs(x) fputs(x"\n", stream) 49 | hputs("pacini - query pacman-style configuration file"); 50 | hputs("usage: pacini [options] [ [...]]"); 51 | hputs(" pacini (--section-list|--help|--version)"); 52 | hputs("options:"); 53 | hputs(" --section= query options for a specific section"); 54 | hputs(" --verbose always show directive names"); 55 | hputs(" --section-list list configured sections"); 56 | hputs(" --help display this help information"); 57 | hputs(" --version display version information"); 58 | #undef hputs 59 | cleanup(); 60 | exit(ret); 61 | } 62 | 63 | void parse_opts(int argc, char **argv) { 64 | int c; 65 | 66 | char *short_opts = ""; 67 | struct option long_opts[] = { 68 | { "section", required_argument, NULL, 's' }, 69 | { "section-list", no_argument, NULL, 'l' }, 70 | { "verbose", no_argument, NULL, 'v' }, 71 | { "help", no_argument, NULL, 'h' }, 72 | { "version", no_argument, NULL, 'V' }, 73 | { 0, 0, 0, 0 }, 74 | }; 75 | 76 | while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { 77 | switch (c) { 78 | case 'l': 79 | section_list = 1; 80 | break; 81 | case 's': 82 | free(section_name); 83 | section_name = strdup(optarg); 84 | break; 85 | case 'v': 86 | verbose = 1; 87 | break; 88 | case 'h': 89 | usage(0); 90 | break; 91 | case 'V': 92 | pu_print_version(myname, myver); 93 | cleanup(); 94 | exit(0); 95 | break; 96 | case '?': 97 | usage(1); 98 | break; 99 | } 100 | } 101 | } 102 | 103 | alpm_list_t *find_str(alpm_list_t *haystack, const char *needle) { 104 | for (; haystack; haystack = alpm_list_next(haystack)) { 105 | if (strcasecmp(haystack->data, needle) == 0) { 106 | return haystack; 107 | } 108 | } 109 | return NULL; 110 | } 111 | 112 | void print_section(mini_t *ini) { 113 | if (directives) { return; } 114 | printf("[%s]", ini->section); 115 | fputc(sep, stdout); 116 | } 117 | 118 | void print_option(mini_t *ini) { 119 | if (directives && !find_str(directives, ini->key)) { return; } 120 | if (verbose || !ini->value) { fputs(ini->key, stdout); } 121 | if (ini->value) { 122 | if (verbose) { fputs(" = ", stdout); } 123 | fputs(ini->value, stdout); 124 | } 125 | fputc(sep, stdout); 126 | } 127 | 128 | void list_sections(void) { 129 | while (mini_next(ini)) { 130 | if (!ini->key) { 131 | fputs(ini->section, stdout); 132 | fputc(sep, stdout); 133 | } 134 | } 135 | } 136 | 137 | void show_section(void) { 138 | int in_section = (section_name[0] == '\0'); 139 | while (mini_next(ini)) { 140 | if (!ini->key) { 141 | const char *section = ini->section ? ini->section : ""; 142 | in_section = (strcmp(section, section_name) == 0); 143 | } else if (in_section) { 144 | print_option(ini); 145 | } 146 | } 147 | } 148 | 149 | void show_directives(void) { 150 | while (mini_next(ini)) { 151 | if (!ini->key) { print_section(ini); } 152 | else { print_option(ini); } 153 | } 154 | } 155 | 156 | int main(int argc, char **argv) { 157 | int ret = 0; 158 | 159 | parse_opts(argc, argv); 160 | 161 | input_file = argv[optind++]; 162 | 163 | if (input_file == NULL || strcmp(input_file, "-") == 0) { 164 | ini = mini_finit(stdin); 165 | input_file = ""; 166 | } else { 167 | ini = mini_init(input_file); 168 | } 169 | 170 | if (!ini) { 171 | ret = 1; 172 | pu_ui_error("unable to open '%s' for reading (%s)", 173 | input_file, strerror(errno)); 174 | goto cleanup; 175 | } 176 | 177 | for (; optind < argc; optind++) { 178 | directives = alpm_list_add(directives, argv[optind]); 179 | } 180 | 181 | if (alpm_list_count(directives) != 1) { 182 | verbose = 1; 183 | } 184 | 185 | if (section_list) { 186 | list_sections(); 187 | } else if (section_name) { 188 | show_section(); 189 | } else { 190 | show_directives(); 191 | } 192 | if (!ini->eof) { 193 | fprintf(stderr, "error reading '%s' (%s)\n", argv[1], strerror(errno)); 194 | return 1; 195 | } 196 | 197 | cleanup: 198 | cleanup(); 199 | 200 | return ret; 201 | } 202 | -------------------------------------------------------------------------------- /t/.gitignore: -------------------------------------------------------------------------------- 1 | *.gcda 2 | *.gcno 3 | *.gcov 4 | *.t 5 | -------------------------------------------------------------------------------- /t/.proverc: -------------------------------------------------------------------------------- 1 | --exec=./runtest.sh 2 | -------------------------------------------------------------------------------- /t/10-basename.c: -------------------------------------------------------------------------------- 1 | #include "pacutils/util.h" 2 | 3 | #include "pacutils_test.h" 4 | 5 | int main(void) { 6 | tap_plan(5); 7 | #define CHECK(in, exp) tap_is_str(pu_basename(in), exp, #in); 8 | CHECK("foo/bar", "bar"); 9 | CHECK("foo/bar/", ""); 10 | CHECK("/foo/bar", "bar"); 11 | CHECK("/foo/bar/", ""); 12 | CHECK(NULL, NULL); 13 | #undef CHECK 14 | return tap_finish(); 15 | } 16 | -------------------------------------------------------------------------------- /t/10-config-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pacutils.h" 6 | 7 | #include "pacutils_test.h" 8 | 9 | FILE *f = NULL; 10 | pu_config_t *config = NULL; 11 | pu_config_reader_t *reader = NULL; 12 | 13 | void cleanup(void) { 14 | fclose(f); 15 | pu_config_free(config); 16 | pu_config_reader_free(reader); 17 | } 18 | 19 | char buf[] = 20 | "\n" 21 | "[options]\n" 22 | "#RootDir = /root1\n" 23 | "RootDir = /root2\n" 24 | "RootDir = /root3\n" 25 | "DBPath = /dbpath1/ \n" 26 | "DBPath = /dbpath2/ \n" 27 | "CacheDir=/cachedir\n" 28 | "HookDir=/hookdir1\n" 29 | "HookDir=/hookdir2\n" 30 | "GPGDir =gpgdir\n" 31 | "GPGDir =gpgdir2\n" 32 | "LogFile= /logfile #this is a comment\n" 33 | "LogFile= /logfile2\n" 34 | " HoldPkg = holdpkga holdpkgb \n" 35 | "IgnorePkg = ignorepkga\n" 36 | "IgnorePkg = ignorepkgb\n" 37 | " IgnoreGroup = ignoregroupa ignoregroupb \n" 38 | "Architecture = i686\n" 39 | "Architecture = x86_64\n" 40 | "ParallelDownloads = 3\n" 41 | "XferCommand = xcommand\n" 42 | "XferCommand = xcommand2\n" 43 | "NoUpgrade = /tmp/noupgrade*\n" 44 | "NoExtract = /tmp/noextract*\n" 45 | "CleanMethod = KeepInstalled KeepCurrent\n" 46 | "UseSyslog\n" 47 | "Color\n" 48 | "NoProgressBar\n" 49 | "CheckSpace\n" 50 | "VerbosePkgLists\n" 51 | "ILoveCandy\n" 52 | "SigLevel = Never\n" 53 | "\n" 54 | "[core]\n" 55 | "Server = $repo:$arch\n" 56 | "CacheServer = $repo:$arch\n" 57 | ""; 58 | 59 | #define is_list_exhausted(l, name) do { \ 60 | tap_ok(l == NULL, name " exhausted"); \ 61 | if(l) { \ 62 | tap_diag("remaining elements:"); \ 63 | while(l) { \ 64 | tap_diag("%s", (char*) l->data); \ 65 | l = alpm_list_next(l); \ 66 | } \ 67 | } \ 68 | } while(0) 69 | 70 | #define is_str_list(l, str, desc) do { \ 71 | if(l) { \ 72 | tap_is_str(l->data, str, desc); \ 73 | l = alpm_list_next(l); \ 74 | } else { \ 75 | tap_ok(l != NULL, desc); \ 76 | } \ 77 | } while(0) 78 | 79 | #define sig_diag(sl, s) if(sl & s) { tap_diag(" %s", #s); } 80 | #define sig_dump(sl) \ 81 | do { \ 82 | sig_diag(sl, ALPM_SIG_USE_DEFAULT); \ 83 | sig_diag(sl, ALPM_SIG_PACKAGE); \ 84 | sig_diag(sl, ALPM_SIG_PACKAGE_OPTIONAL); \ 85 | sig_diag(sl, ALPM_SIG_PACKAGE_UNKNOWN_OK); \ 86 | sig_diag(sl, ALPM_SIG_PACKAGE_MARGINAL_OK); \ 87 | sig_diag(sl, ALPM_SIG_DATABASE); \ 88 | sig_diag(sl, ALPM_SIG_DATABASE_OPTIONAL); \ 89 | sig_diag(sl, ALPM_SIG_DATABASE_UNKNOWN_OK); \ 90 | sig_diag(sl, ALPM_SIG_DATABASE_MARGINAL_OK); \ 91 | } while(0) 92 | 93 | #define is_siglevel(g, e, ...) \ 94 | do { \ 95 | if(!tap_ok(g == e, __VA_ARGS__)) { \ 96 | tap_diag(" expected:"); \ 97 | sig_dump(e); \ 98 | tap_diag(" got:"); \ 99 | sig_dump(g); \ 100 | } \ 101 | } while(0) 102 | 103 | int main(void) { 104 | alpm_list_t *i; 105 | pu_repo_t *repo; 106 | 107 | ASSERT(atexit(cleanup) == 0); 108 | ASSERT(f = fmemopen(buf, strlen(buf), "r")); 109 | ASSERT(config = pu_config_new()); 110 | ASSERT(reader = pu_config_reader_finit(config, f)); 111 | 112 | while (pu_config_reader_next(reader) != -1); 113 | 114 | tap_plan(42); 115 | 116 | tap_ok(reader->eof, "eof reached"); 117 | tap_ok(!reader->error, "no error"); 118 | tap_ok(pu_config_resolve(config) == 0, "finalize config"); 119 | 120 | tap_is_str(config->rootdir, "/root2", "RootDir"); 121 | tap_is_str(config->dbpath, "/dbpath1/", "DBPath"); 122 | tap_is_str(config->gpgdir, "gpgdir", "GPGDir"); 123 | tap_is_str(config->logfile, "/logfile", "LogFile"); 124 | tap_is_str(config->xfercommand, "xcommand2", "XferCommand"); 125 | 126 | tap_is_int(config->paralleldownloads, 3, "ParallelDownloads"); 127 | 128 | tap_ok(config->usesyslog, "UseSyslog"); 129 | tap_ok(config->color, "Color"); 130 | tap_ok(config->noprogressbar, "NoProgressBar"); 131 | tap_ok(config->checkspace, "CheckSpace"); 132 | tap_ok(config->verbosepkglists, "VerbosePkgLists"); 133 | tap_ok(config->ilovecandy, "ILoveCandy"); 134 | 135 | is_siglevel(config->siglevel, 0, "SigLevel"); 136 | 137 | i = config->architectures; 138 | is_str_list(i, "i686", "Arch i686"); 139 | is_str_list(i, "x86_64", "Arch x86_64"); 140 | is_list_exhausted(i, "architectures"); 141 | 142 | i = config->ignorepkgs; 143 | is_str_list(i, "ignorepkga", "IgnorePkg a"); 144 | is_str_list(i, "ignorepkgb", "IgnorePkg b"); 145 | is_list_exhausted(i, "ignorepkg"); 146 | 147 | i = config->ignoregroups; 148 | is_str_list(i, "ignoregroupa", "IgnoreGroup a"); 149 | is_str_list(i, "ignoregroupb", "IgnoreGroup b"); 150 | is_list_exhausted(i, "ignoregroup"); 151 | 152 | i = config->noupgrade; 153 | is_str_list(i, "/tmp/noupgrade*", "NoUpgrade"); 154 | is_list_exhausted(i, "noupgrade"); 155 | 156 | i = config->noextract; 157 | is_str_list(i, "/tmp/noextract*", "NoExtract"); 158 | is_list_exhausted(i, "noextract"); 159 | 160 | i = config->cachedirs; 161 | is_str_list(i, "/cachedir", "CacheDir"); 162 | is_list_exhausted(i, "cachedir"); 163 | 164 | i = config->hookdirs; 165 | is_str_list(i, "/hookdir1", "HookDir"); 166 | is_str_list(i, "/hookdir2", "HookDir"); 167 | is_list_exhausted(i, "hookdir"); 168 | 169 | i = config->holdpkgs; 170 | is_str_list(i, "holdpkga", "HoldPkg a"); 171 | is_str_list(i, "holdpkgb", "HoldPkg b"); 172 | is_list_exhausted(i, "holdpkgs"); 173 | 174 | tap_ok(config->repos != NULL, "repo list"); 175 | 176 | repo = config->repos->data; 177 | tap_ok(repo != NULL, "core"); 178 | tap_is_str(repo->name, "core", "repo->name == 'core'"); 179 | tap_is_str(repo->servers->data, "core:i686", "[core] server"); 180 | tap_is_str(repo->cacheservers->data, "core:i686", "[core] cache server"); 181 | 182 | return tap_finish(); 183 | } 184 | -------------------------------------------------------------------------------- /t/10-filelist_contains_path.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pacutils.h" 4 | 5 | #include "pacutils_test.h" 6 | 7 | int main(void) { 8 | alpm_file_t files[] = {{.name = "foo/bar"}, {.name = "foo/baz/"}}; 9 | alpm_filelist_t filelist; 10 | filelist.files = files; 11 | filelist.count = sizeof(files) / sizeof(*files); 12 | 13 | tap_plan(4); 14 | #define CHECK(in, exp) do { \ 15 | alpm_file_t *f = pu_filelist_contains_path(&filelist, in); \ 16 | tap_is_str(f ? f->name : NULL, exp, in); \ 17 | } while(0); 18 | CHECK("foo/bar", "foo/bar"); 19 | CHECK("foo/bar/", "foo/bar"); 20 | CHECK("foo//baz/", "foo/baz/"); 21 | CHECK("foo/baz", "foo/baz/"); 22 | #undef CHECK 23 | return tap_finish(); 24 | } 25 | -------------------------------------------------------------------------------- /t/10-log-action-parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pacutils/log.h" 4 | 5 | #include "pacutils_test.h" 6 | 7 | int main(void) { 8 | pu_log_action_t *a; 9 | 10 | tap_plan(31); 11 | 12 | tap_ok((a = pu_log_action_parse("installed pacutils (1.0.0)")) != NULL, 13 | "install"); 14 | tap_is_int(a->operation, PU_LOG_OPERATION_INSTALL, "operation"); 15 | tap_is_str(a->target, "pacutils", "package"); 16 | tap_is_str(a->new_version, "1.0.0", "new_version"); 17 | tap_is_str(a->old_version, NULL, "old_version"); 18 | pu_log_action_free(a); 19 | 20 | tap_ok((a = pu_log_action_parse("upgraded pacutils (1.0.0 -> 2.0.0)")) != NULL, 21 | "upgrade"); 22 | tap_is_int(a->operation, PU_LOG_OPERATION_UPGRADE, "operation"); 23 | tap_is_str(a->target, "pacutils", "package"); 24 | tap_is_str(a->new_version, "2.0.0", "new_version"); 25 | tap_is_str(a->old_version, "1.0.0", "old_version"); 26 | pu_log_action_free(a); 27 | 28 | tap_ok((a = pu_log_action_parse("downgraded pacutils (2.0.0 -> 1.0.0)")) != 29 | NULL, "downgrade"); 30 | tap_is_int(a->operation, PU_LOG_OPERATION_DOWNGRADE, "operation"); 31 | tap_is_str(a->target, "pacutils", "package"); 32 | tap_is_str(a->new_version, "1.0.0", "new_version"); 33 | tap_is_str(a->old_version, "2.0.0", "old_version"); 34 | pu_log_action_free(a); 35 | 36 | tap_ok((a = pu_log_action_parse("reinstalled pacutils (1.0.0)")) != NULL, 37 | "reinstall"); 38 | tap_is_int(a->operation, PU_LOG_OPERATION_REINSTALL, "operation"); 39 | tap_is_str(a->target, "pacutils", "package"); 40 | tap_is_str(a->new_version, "1.0.0", "new_version"); 41 | tap_is_str(a->old_version, "1.0.0", "old_version"); 42 | pu_log_action_free(a); 43 | 44 | tap_ok((a = pu_log_action_parse("removed pacutils (1.0.0)")) != NULL, 45 | "removed"); 46 | tap_is_int(a->operation, PU_LOG_OPERATION_REMOVE, "operation"); 47 | tap_is_str(a->target, "pacutils", "package"); 48 | tap_is_str(a->new_version, NULL, "new_version"); 49 | tap_is_str(a->old_version, "1.0.0", "old_version"); 50 | pu_log_action_free(a); 51 | 52 | tap_ok(pu_log_action_parse("") == NULL, "empty input"); 53 | tap_is_int(errno, EINVAL, "errno"); 54 | 55 | tap_ok(pu_log_action_parse(NULL) == NULL, "NULL input"); 56 | tap_is_int(errno, EINVAL, "errno"); 57 | 58 | tap_ok(pu_log_action_parse("not a valid action") == NULL, "invalid input"); 59 | tap_is_int(errno, EINVAL, "errno"); 60 | 61 | return tap_finish(); 62 | } 63 | -------------------------------------------------------------------------------- /t/10-log-reader-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pacutils/log.h" 6 | 7 | #include "pacutils_test.h" 8 | 9 | FILE *stream = NULL; 10 | pu_log_reader_t *reader = NULL; 11 | 12 | void cleanup(void) { 13 | pu_log_reader_free(reader); 14 | fclose(stream); 15 | } 16 | 17 | char buf[] = 18 | "[2016-10-23 11:12] old-style message with no caller\n" 19 | "[2016-10-23 09:00] old-style multi-line message\n" 20 | "continued on line 2...\n" 21 | "and line3\n" 22 | "[2016-10-23 09:00] [mycaller] new-style message with caller\n" 23 | "[2016-10-23 09:00] [mycaller] new-style multi-line message\n" 24 | "continued on line 2...\n" 25 | "and line3\n" 26 | "[2016-10-24T11:23:45-0100] [mycaller] new timestamp negative offset\n" 27 | "[2016-10-24T11:23:45+0100] [mycaller] new timestamp positive offset\n" 28 | ""; 29 | 30 | int main(void) { 31 | pu_log_entry_t *e; 32 | 33 | ASSERT(atexit(cleanup) == 0); 34 | ASSERT(stream = fmemopen(buf, strlen(buf), "r")); 35 | ASSERT(reader = pu_log_reader_open_stream(stream)); 36 | 37 | tap_plan(56); 38 | 39 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 40 | tap_is_str(e->caller, NULL, "caller"); 41 | tap_is_str(e->message, "old-style message with no caller\n", "message"); 42 | tap_is_int(e->timestamp.tm.tm_year + 1900, 2016, "timestamp year"); 43 | tap_is_int(e->timestamp.tm.tm_mon, 9, "timestamp month"); 44 | tap_is_int(e->timestamp.tm.tm_mday, 23, "timestamp day"); 45 | tap_is_int(e->timestamp.tm.tm_hour, 11, "timestamp hour"); 46 | tap_is_int(e->timestamp.tm.tm_min, 12, "timestamp minute"); 47 | tap_is_int(e->timestamp.tm.tm_sec, 0, "timestamp second"); 48 | tap_is_int(e->timestamp.tm.tm_isdst, -1, "timestamp second"); 49 | tap_is_int(e->timestamp.gmtoff, 0, "timestamp gmt offset"); 50 | tap_ok(!e->timestamp.has_seconds, "timestamp has seconds"); 51 | tap_ok(!e->timestamp.has_gmtoff, "timestamp has gmt offset"); 52 | tap_is_int(reader->eof, 0, "eof"); 53 | pu_log_entry_free(e); 54 | 55 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 56 | tap_is_str(e->caller, NULL, "caller"); 57 | tap_is_str(e->message, 58 | "old-style multi-line message\ncontinued on line 2...\nand line3\n", "message"); 59 | tap_is_int(reader->eof, 0, "eof"); 60 | pu_log_entry_free(e); 61 | 62 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 63 | tap_is_str(e->caller, "mycaller", "caller"); 64 | tap_is_str(e->message, "new-style message with caller\n", "message"); 65 | tap_is_int(reader->eof, 0, "eof"); 66 | pu_log_entry_free(e); 67 | 68 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 69 | tap_is_str(e->caller, "mycaller", "caller"); 70 | tap_is_str(e->message, 71 | "new-style multi-line message\ncontinued on line 2...\nand line3\n", "message"); 72 | tap_is_int(reader->eof, 0, "eof"); 73 | pu_log_entry_free(e); 74 | 75 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 76 | tap_is_str(e->caller, "mycaller", "caller"); 77 | tap_is_str(e->message, "new timestamp negative offset\n", "message"); 78 | tap_is_int(e->timestamp.tm.tm_year + 1900, 2016, "timestamp year"); 79 | tap_is_int(e->timestamp.tm.tm_mon, 9, "timestamp month"); 80 | tap_is_int(e->timestamp.tm.tm_mday, 24, "timestamp day"); 81 | tap_is_int(e->timestamp.tm.tm_hour, 11, "timestamp hour"); 82 | tap_is_int(e->timestamp.tm.tm_min, 23, "timestamp minute"); 83 | tap_is_int(e->timestamp.tm.tm_sec, 45, "timestamp second"); 84 | tap_is_int(e->timestamp.tm.tm_isdst, -1, "timestamp second"); 85 | tap_is_int(e->timestamp.gmtoff, -100, "timestamp gmt offset"); 86 | tap_ok(e->timestamp.has_seconds, "timestamp has seconds"); 87 | tap_ok(e->timestamp.has_gmtoff, "timestamp has gmt offset"); 88 | tap_is_int(reader->eof, 0, "eof"); 89 | pu_log_entry_free(e); 90 | 91 | tap_ok((e = pu_log_reader_next(reader)) != NULL, "next"); 92 | tap_is_str(e->caller, "mycaller", "caller"); 93 | tap_is_str(e->message, "new timestamp positive offset\n", "message"); 94 | tap_is_int(e->timestamp.tm.tm_year + 1900, 2016, "timestamp year"); 95 | tap_is_int(e->timestamp.tm.tm_mon, 9, "timestamp month"); 96 | tap_is_int(e->timestamp.tm.tm_mday, 24, "timestamp day"); 97 | tap_is_int(e->timestamp.tm.tm_hour, 11, "timestamp hour"); 98 | tap_is_int(e->timestamp.tm.tm_min, 23, "timestamp minute"); 99 | tap_is_int(e->timestamp.tm.tm_sec, 45, "timestamp second"); 100 | tap_is_int(e->timestamp.tm.tm_isdst, -1, "timestamp second"); 101 | tap_is_int(e->timestamp.gmtoff, 100, "timestamp gmt offset"); 102 | tap_ok(e->timestamp.has_seconds, "timestamp has seconds"); 103 | tap_ok(e->timestamp.has_gmtoff, "timestamp has gmt offset"); 104 | tap_is_int(reader->eof, 0, "eof"); 105 | pu_log_entry_free(e); 106 | 107 | tap_ok(pu_log_reader_next(reader) == NULL, "next"); 108 | tap_ok(reader->eof, "eof"); 109 | 110 | return tap_finish(); 111 | } 112 | -------------------------------------------------------------------------------- /t/10-log-transaction-parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pacutils/log.h" 4 | 5 | #include "pacutils_test.h" 6 | 7 | int main(void) { 8 | tap_plan(10); 9 | 10 | tap_is_int(pu_log_transaction_parse("transaction started\n"), 11 | PU_LOG_TRANSACTION_STARTED, "started"); 12 | tap_is_int(pu_log_transaction_parse("transaction completed\n"), 13 | PU_LOG_TRANSACTION_COMPLETED, "completed"); 14 | tap_is_int(pu_log_transaction_parse("transaction interrupted\n"), 15 | PU_LOG_TRANSACTION_INTERRUPTED, "interrupted"); 16 | tap_is_int(pu_log_transaction_parse("transaction failed\n"), 17 | PU_LOG_TRANSACTION_FAILED, "failed"); 18 | 19 | tap_is_int(pu_log_transaction_parse("invalid"), 0, "invalid input"); 20 | tap_is_int(errno, EINVAL, "errno"); 21 | 22 | tap_is_int(pu_log_transaction_parse(""), 0, "empty input"); 23 | tap_is_int(errno, EINVAL, "errno"); 24 | 25 | tap_is_int(pu_log_transaction_parse(NULL), 0, "NULL input"); 26 | tap_is_int(errno, EINVAL, "errno"); 27 | 28 | return tap_finish(); 29 | } 30 | -------------------------------------------------------------------------------- /t/10-mtree-basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pacutils.h" 6 | 7 | #include "pacutils_test.h" 8 | 9 | FILE *stream = NULL; 10 | pu_mtree_reader_t *reader = NULL; 11 | 12 | void cleanup(void) { 13 | pu_mtree_reader_free(reader); 14 | fclose(stream); 15 | } 16 | 17 | char buf[] = 18 | "#mtree\n" 19 | "/set type=file uid=0 gid=0 mode=644\n" 20 | "./.BUILDINFO time=1453283269.954514837 size=25444 md5digest=1b30bf27a1f20eef4402ecc95134844a sha256digest=e21cb92b5239f423ce578a45575b425ea0583aa9245f501c8e54d19b8bfe16b1\n" 21 | "./.PKGINFO time=1453283269.864514835 size=410 md5digest=b90ee962592f6c66c2ccbfa3718ebdce sha256digest=863dfa105c333a4e91d70b7332931e99fc5561a35685039e098e4b261466eae0\n" 22 | "/set mode=755\n" 23 | "./usr time=1453283269.234514817 type=dir\n" 24 | "./usr/bin time=1453283269.447848157 type=dir\n" 25 | "./usr/bin/paccheck time=1453283269.447848157 size=23712 md5digest=adeb5af3c33e76f0e663394c88272c14 sha256digest=0669f596e333e053f61ee9a2c6b443a9a3ef2c2640fe6bf67acc502d67d9b51b\n" 26 | ""; 27 | 28 | int main(void) { 29 | pu_mtree_t *e; 30 | 31 | ASSERT(stream = fmemopen(buf, strlen(buf), "r")); 32 | ASSERT(reader = pu_mtree_reader_open_stream(stream)); 33 | 34 | tap_plan(47); 35 | 36 | tap_ok((e = pu_mtree_reader_next(reader, NULL)) != NULL, "next"); 37 | tap_is_str(e->path, ".BUILDINFO", "path"); 38 | tap_is_str(e->type, "file", "type"); 39 | tap_is_int(e->uid, 0, "uid"); 40 | tap_is_int(e->gid, 0, "gid"); 41 | tap_is_int(e->mode, 0644, "mode"); 42 | tap_is_int(e->size, 25444, "time"); 43 | tap_is_str(e->md5digest, "1b30bf27a1f20eef4402ecc95134844a", "md5"); 44 | tap_ok(!reader->eof, "eof"); 45 | pu_mtree_free(e); 46 | 47 | tap_ok((e = pu_mtree_reader_next(reader, NULL)) != NULL, "next"); 48 | tap_is_str(e->path, ".PKGINFO", "path"); 49 | tap_is_str(e->type, "file", "type"); 50 | tap_is_int(e->uid, 0, "uid"); 51 | tap_is_int(e->gid, 0, "gid"); 52 | tap_is_int(e->mode, 0644, "mode"); 53 | tap_is_int(e->size, 410, "time"); 54 | tap_is_str(e->md5digest, "b90ee962592f6c66c2ccbfa3718ebdce", "md5"); 55 | tap_ok(!reader->eof, "eof"); 56 | pu_mtree_free(e); 57 | 58 | tap_ok((e = pu_mtree_reader_next(reader, NULL)) != NULL, "next"); 59 | tap_is_str(e->path, "usr", "path"); 60 | tap_is_str(e->type, "dir", "type"); 61 | tap_is_int(e->uid, 0, "uid"); 62 | tap_is_int(e->gid, 0, "gid"); 63 | tap_is_int(e->mode, 0755, "mode"); 64 | tap_is_int(e->size, 0, "time"); 65 | tap_is_str(e->md5digest, "", "md5"); 66 | tap_ok(!reader->eof, "eof"); 67 | pu_mtree_free(e); 68 | 69 | tap_ok((e = pu_mtree_reader_next(reader, NULL)) != NULL, "next"); 70 | tap_is_str(e->path, "usr/bin", "path"); 71 | tap_is_str(e->type, "dir", "type"); 72 | tap_is_int(e->uid, 0, "uid"); 73 | tap_is_int(e->gid, 0, "gid"); 74 | tap_is_int(e->mode, 0755, "mode"); 75 | tap_is_int(e->size, 0, "time"); 76 | tap_is_str(e->md5digest, "", "md5"); 77 | tap_ok(!reader->eof, "eof"); 78 | pu_mtree_free(e); 79 | 80 | tap_ok((e = pu_mtree_reader_next(reader, NULL)) != NULL, "next"); 81 | tap_is_str(e->path, "usr/bin/paccheck", "path"); 82 | tap_is_str(e->type, "file", "type"); 83 | tap_is_int(e->uid, 0, "uid"); 84 | tap_is_int(e->gid, 0, "gid"); 85 | tap_is_int(e->mode, 0755, "mode"); 86 | tap_is_int(e->size, 23712, "time"); 87 | tap_is_str(e->md5digest, "adeb5af3c33e76f0e663394c88272c14", "md5"); 88 | tap_ok(!reader->eof, "eof"); 89 | pu_mtree_free(e); 90 | 91 | tap_ok(pu_mtree_reader_next(reader, NULL) == NULL, "next"); 92 | tap_ok(reader->eof, "eof"); 93 | 94 | return tap_finish(); 95 | } 96 | -------------------------------------------------------------------------------- /t/10-parse-datetime.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pacutils/util.h" 4 | 5 | #include "pacutils_test.h" 6 | 7 | #define IS(str, yr, mo, dy, h, m, s) \ 8 | _tap_ok(__FILE__, __LINE__, pu_parse_datetime(str, &t) == &t, "%s - parse", str); \ 9 | _tap_is_int(__FILE__, __LINE__, t.tm_year + 1900, yr, "%s - year", str); \ 10 | _tap_is_int(__FILE__, __LINE__, t.tm_mon + 1, mo, "%s - month", str); \ 11 | _tap_is_int(__FILE__, __LINE__, t.tm_mday, dy, "%s - day", str); \ 12 | _tap_is_int(__FILE__, __LINE__, t.tm_hour, h, "%s - hour", str); \ 13 | _tap_is_int(__FILE__, __LINE__, t.tm_min, m, "%s - minute", str); \ 14 | _tap_is_int(__FILE__, __LINE__, t.tm_sec, s, "%s - second", str); \ 15 | _tap_is_int(__FILE__, __LINE__, t.tm_isdst, -1, "%s - dst", str); \ 16 | 17 | int main(void) { 18 | struct tm t; 19 | 20 | tap_plan(88); 21 | 22 | IS("2016-12-31T23:59:59.99-04:00", 2016, 12, 31, 23, 59, 59); 23 | IS("2016-12-31T23:59:59-04:00", 2016, 12, 31, 23, 59, 59); 24 | IS("2016-12-31T23:59:59Z", 2016, 12, 31, 23, 59, 59); 25 | IS("2016-12-31T23:59:59", 2016, 12, 31, 23, 59, 59); 26 | IS("2016-12-31 23:59:59", 2016, 12, 31, 23, 59, 59); 27 | IS("2015-11-30 22:58", 2015, 11, 30, 22, 58, 0); 28 | IS("2014-10-29 21", 2014, 10, 29, 21, 0, 0); 29 | IS("2013-09-28", 2013, 9, 28, 0, 0, 0); 30 | IS("2012-09", 2012, 9, 1, 0, 0, 0); 31 | IS("2011", 2011, 1, 1, 0, 0, 0); 32 | 33 | /* timezones ahead of UTC (GH#20) */ 34 | IS("2016-12-31T23:59:59+04:00", 2016, 12, 31, 23, 59, 59); 35 | 36 | return tap_finish(); 37 | } 38 | -------------------------------------------------------------------------------- /t/10-pathcmp.c: -------------------------------------------------------------------------------- 1 | #include "pacutils.h" 2 | 3 | #include "pacutils_test.h" 4 | 5 | #define CHECK(p1, p2, exp) do { \ 6 | int match = pu_pathcmp(p1, p2) ? 1 : 0; \ 7 | tap_ok(match == exp, exp ? p1 " != " p2 : p1 " == " p2); \ 8 | } while(0) 9 | 10 | int main(void) { 11 | tap_plan(6); 12 | CHECK("/foo/bar", "/foo/bar", 0); 13 | CHECK("//foo/bar", "/foo/bar", 0); 14 | CHECK("/foo/bar//", "/foo/bar", 0); 15 | CHECK("/foo//bar", "/foo/bar", 0); 16 | CHECK("foo/bar", "/foo/bar", 1); 17 | CHECK("/foo/bar", "/bar/foo", 1); 18 | return tap_finish(); 19 | } 20 | -------------------------------------------------------------------------------- /t/10-strreplace.c: -------------------------------------------------------------------------------- 1 | #include "pacutils/config.c" 2 | 3 | #include "pacutils_test.h" 4 | 5 | #define CHECK(in, tgt, repl, exp, desc) do { \ 6 | char *got = _pu_strreplace(in, tgt, repl); \ 7 | tap_is_str(got, exp, desc); \ 8 | free(got); \ 9 | } while(0); 10 | 11 | int main(void) { 12 | tap_plan(8); 13 | CHECK("foobarbaz", "bar", "qux", "fooquxbaz", "equal length"); 14 | CHECK("foobarbaz", "bar", "quux", "fooquuxbaz", "longer replacement"); 15 | CHECK("foobarbaz", "bar", "qx", "fooqxbaz", "shorter replacement"); 16 | CHECK("foobarbaz", "foo", "qux", "quxbarbaz", "startswith target"); 17 | CHECK("foobarbaz", "baz", "qux", "foobarqux", "endswith target"); 18 | CHECK("foobarbaz", "a", "x", "foobxrbxz", "multiple instances"); 19 | CHECK("barbarbar", "barbar", "foo", "foobar", "overlapping instances"); 20 | CHECK("foo", "foo", "bar", "bar", "equals target"); 21 | return tap_finish(); 22 | } 23 | -------------------------------------------------------------------------------- /t/10-util-read-list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "pacutils.h" 7 | #include "pacutils_test.h" 8 | 9 | #define is_str_list(l, str, desc) do { \ 10 | if(l) { \ 11 | tap_is_str(l->data, str, desc); \ 12 | l = alpm_list_next(l); \ 13 | } else { \ 14 | tap_ok(l != NULL, desc); \ 15 | } \ 16 | } while(0) 17 | 18 | #define is_list_exhausted(l, name) do { \ 19 | tap_ok(l == NULL, name " exhausted"); \ 20 | if(l) { \ 21 | tap_diag("remaining elements:"); \ 22 | while(l) { \ 23 | tap_diag("%s", (char*) l->data); \ 24 | l = alpm_list_next(l); \ 25 | } \ 26 | } \ 27 | } while(0) 28 | 29 | FILE *stream = NULL; 30 | 31 | void cleanup(void) { 32 | fclose(stream); 33 | } 34 | 35 | char buf[] = 36 | "foo\n" 37 | "bar\n" 38 | "baz\n" 39 | ""; 40 | 41 | int main(void) { 42 | alpm_list_t *dest = NULL; 43 | 44 | ASSERT(atexit(cleanup) == 0); 45 | ASSERT(stream = fmemopen(buf, strlen(buf), "r")); 46 | 47 | tap_plan(5); 48 | 49 | tap_is_int(pu_read_list_from_stream(stream, '\n', &dest), 0, "read successful"); 50 | is_str_list(dest, "foo", "foo"); 51 | is_str_list(dest, "bar", "bar"); 52 | is_str_list(dest, "baz", "baz"); 53 | is_list_exhausted(dest, "dest"); 54 | 55 | return tap_finish(); 56 | } 57 | 58 | /* vim: set ts=2 sw=2 et: */ 59 | -------------------------------------------------------------------------------- /t/20-config-includes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pacutils_test.h" 6 | 7 | #include "pacutils.h" 8 | 9 | char *tmpdir = NULL, template[] = "/tmp/20-config-includes.c-XXXXXX"; 10 | int tmpfd = -1; 11 | char *conf_path = NULL, *include_path = NULL; 12 | pu_config_t *config = NULL; 13 | pu_config_reader_t *reader = NULL; 14 | 15 | char pacman_conf[] = 16 | "\n" 17 | "[options]\n" 18 | "Include = %s\n" 19 | "Server = included_server2\n" 20 | "[core]\n" 21 | "Server = core_server\n"; 22 | 23 | char include_conf[] = 24 | "XferCommand = included_xfercommand\n" 25 | "DBPath = included_dbpath\n" 26 | "[included repo]\n" 27 | "Server = included_server1\n"; 28 | 29 | void cleanup(void) { 30 | pu_config_free(config); 31 | pu_config_reader_free(reader); 32 | free(conf_path); 33 | free(include_path); 34 | 35 | if (tmpfd != -1) { close(tmpfd); } 36 | if (tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 37 | } 38 | 39 | int main(void) { 40 | pu_repo_t *repo; 41 | 42 | ASSERT(atexit(cleanup) == 0); 43 | ASSERT(tmpdir = mkdtemp(template)); 44 | ASSERT((tmpfd = open(tmpdir, O_DIRECTORY)) != -1); 45 | ASSERT(conf_path = pu_asprintf("%s/%s", tmpdir, "pacman.conf")); 46 | ASSERT(include_path = pu_asprintf("%s/%s", tmpdir, "include.conf")); 47 | ASSERT(spew(tmpfd, "pacman.conf", pacman_conf, include_path) == 0); 48 | ASSERT(spew(tmpfd, "include.conf", include_conf) == 0); 49 | 50 | ASSERT(config = pu_config_new()); 51 | ASSERT(reader = pu_config_reader_new(config, conf_path)); 52 | 53 | while (pu_config_reader_next(reader) != -1); 54 | 55 | tap_plan(13); 56 | 57 | tap_ok(reader->eof, "eof reached"); 58 | tap_ok(!reader->error, "no error"); 59 | tap_ok(pu_config_resolve(config) == 0, "finalize config"); 60 | 61 | tap_is_str(config->xfercommand, "included_xfercommand", "Include XferCommand"); 62 | tap_is_str(config->dbpath, "included_dbpath", "Include DBPath"); 63 | 64 | tap_ok(config->repos != NULL, "repo list"); 65 | 66 | repo = config->repos->data; 67 | tap_ok(repo != NULL, "included repo"); 68 | tap_is_str(repo->name, "included repo", "repo->name == 'included repo'"); 69 | tap_is_str(repo->servers->data, "included_server1", "[included] server1"); 70 | tap_is_str(repo->servers->next->data, "included_server2", "[included] server2"); 71 | 72 | repo = config->repos->next->data; 73 | tap_ok(repo != NULL, "core"); 74 | tap_is_str(repo->name, "core", "repo->name == 'core'"); 75 | tap_is_str(repo->servers->data, "core_server", "[core] server"); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /t/20-config-root-inheritance.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pacutils.h" 5 | 6 | #include "pacutils_test.h" 7 | 8 | pu_config_t *defaults = NULL; 9 | 10 | void cleanup(void) { 11 | pu_config_free(defaults); 12 | } 13 | 14 | #define CHECK(r, d, l, g, config_text) do { \ 15 | pu_config_t *config = pu_config_new(); \ 16 | FILE *f = fmemopen(config_text, strlen(config_text), "r"); \ 17 | pu_config_reader_t *reader = pu_config_reader_finit(config, f); \ 18 | if(config == NULL || reader == NULL) { \ 19 | tap_bail("error initializing reader (%s)", strerror(errno)); \ 20 | return 1; \ 21 | } \ 22 | while(pu_config_reader_next(reader) != -1); \ 23 | tap_ok(reader->eof, "eof reached"); \ 24 | tap_ok(!reader->error, "no error"); \ 25 | tap_ok(pu_config_resolve(config) == 0, "finalize config"); \ 26 | tap_is_str(config->rootdir, r, "RootDir == %s", r); \ 27 | tap_is_str(config->dbpath, d, "DBPath == %s", d); \ 28 | tap_is_str(config->logfile, l, "LogFile == %s", l); \ 29 | tap_is_str(config->gpgdir, g, "GPGDir == %s", g); \ 30 | pu_config_reader_free(reader); \ 31 | pu_config_free(config); \ 32 | fclose(f); \ 33 | } while(0) 34 | 35 | int main(void) { 36 | /* if RootDir is set and DBPath or LogFile are not 37 | * they should be relative to RootDir, GPGDir should not */ 38 | char dbpath[PATH_MAX], logfile[PATH_MAX]; 39 | 40 | ASSERT(atexit(cleanup) == 0); 41 | ASSERT(defaults = pu_config_new()); 42 | ASSERT(pu_config_resolve(defaults) == 0); 43 | 44 | tap_plan(7 * 4); 45 | CHECK("/root", "/dbpath/", "/logfile", "/gpgdir", 46 | "[options]\n" 47 | "RootDir = /root\n" 48 | "DBPath = /dbpath/\n" 49 | "LogFile = /logfile\n" 50 | "GPGDir = /gpgdir\n" 51 | ); 52 | snprintf(dbpath, PATH_MAX, "%s/%s", "/root", defaults->dbpath); 53 | snprintf(logfile, PATH_MAX, "%s/%s", "/root", defaults->logfile); 54 | CHECK("/root", dbpath, logfile, defaults->gpgdir, 55 | "[options]\n" 56 | "RootDir = /root\n" 57 | "#DBPath = /dbpath/\n" 58 | "#LogFile = /logfile\n" 59 | "#GPGDir = /gpgdir\n" 60 | ); 61 | CHECK("/", "/dbpath/", "/logfile", "/gpgdir", 62 | "[options]\n" 63 | "#RootDir = /root\n" 64 | "DBPath = /dbpath/\n" 65 | "LogFile = /logfile\n" 66 | "GPGDir = /gpgdir\n" 67 | ); 68 | CHECK(defaults->rootdir, defaults->dbpath, defaults->logfile, defaults->gpgdir, 69 | "[options]\n" 70 | "#RootDir = /root\n" 71 | "#DBPath = /dbpath/\n" 72 | "#LogFile = /logfile\n" 73 | "#GPGDir = /gpgdir\n" 74 | ); 75 | 76 | return tap_finish(); 77 | } 78 | -------------------------------------------------------------------------------- /t/30-config-sysroot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pacutils_test.h" 5 | 6 | #include "pacutils.h" 7 | 8 | char *tmpdir = NULL, template[] = "/tmp/30-config-sysroot.c-XXXXXX"; 9 | int tmpfd = -1; 10 | char *rootdir = NULL, *dbpath = NULL, *server = NULL; 11 | pu_repo_t *r = NULL; 12 | pu_config_t *config = NULL; 13 | pu_config_reader_t *reader = NULL; 14 | 15 | char pacman_conf[] = 16 | "[options]\n" 17 | "RootDir = 30-config-sysroot-RootDir\n" 18 | "Include = /etc/include.conf\n"; 19 | 20 | char include_conf[] = 21 | "DBPath = 30-config-sysroot-DBPath\n" 22 | "[repo]\n" 23 | "Server = http://somehost/path\n" 24 | "Server = file:///30-config-sysroot-Server/\n"; 25 | 26 | void cleanup(void) { 27 | pu_config_free(config); 28 | pu_config_reader_free(reader); 29 | 30 | free(rootdir); 31 | free(dbpath); 32 | 33 | if (tmpfd != -1) { close(tmpfd); } 34 | if (tmpdir) { rmrfat(AT_FDCWD, tmpdir); } 35 | } 36 | 37 | int main(void) { 38 | ASSERT(atexit(cleanup) == 0); 39 | ASSERT(tmpdir = mkdtemp(template)); 40 | ASSERT((tmpfd = open(tmpdir, O_DIRECTORY)) != -1); 41 | ASSERT(mkdirat(tmpfd, "etc", 0777) == 0); 42 | ASSERT(spew(tmpfd, "etc/pacman.conf", pacman_conf) == 0); 43 | ASSERT(spew(tmpfd, "etc/include.conf", include_conf) == 0); 44 | ASSERT(rootdir = pu_asprintf("%s/%s", template, "30-config-sysroot-RootDir")); 45 | ASSERT(dbpath = pu_asprintf("%s/%s", template, "30-config-sysroot-DBPath")); 46 | ASSERT(server = pu_asprintf("file://%s/%s", template, 47 | "30-config-sysroot-Server/")); 48 | 49 | ASSERT(config = pu_config_new()); 50 | ASSERT(reader = pu_config_reader_new_sysroot(config, "/etc/pacman.conf", 51 | tmpdir)); 52 | 53 | while (pu_config_reader_next(reader) != -1); 54 | r = config->repos->data; 55 | 56 | tap_plan(12); 57 | 58 | tap_is_int(reader->status, PU_CONFIG_READER_STATUS_OK, "reader status"); 59 | tap_ok(reader->eof, "reader eof"); 60 | tap_ok(!reader->error, "reader error"); 61 | 62 | /* raw values */ 63 | tap_is_str(config->rootdir, "30-config-sysroot-RootDir", "rootdir"); 64 | tap_is_str(config->dbpath, "30-config-sysroot-DBPath", "dbpath"); 65 | tap_is_str(r->servers->data, "http://somehost/path", "http:// server"); 66 | tap_is_str(r->servers->next->data, "file:///30-config-sysroot-Server/", 67 | "file:// server"); 68 | 69 | /* resolved values */ 70 | tap_is_int(pu_config_resolve_sysroot(config, tmpdir), 0, "resolve_sysroot"); 71 | tap_is_str(config->rootdir, rootdir, "resolved rootdir"); 72 | tap_is_str(config->dbpath, dbpath, "resolved dbpath"); 73 | 74 | tap_is_str(r->servers->data, "http://somehost/path", "resolved http:// server"); 75 | tap_is_str(r->servers->next->data, server, "resolved file:// server"); 76 | 77 | return tap_finish(); 78 | } 79 | -------------------------------------------------------------------------------- /t/40-ui-cb-download-progress.c: -------------------------------------------------------------------------------- 1 | #include "pacutils_test.h" 2 | #include "pacutils/ui.h" 3 | 4 | pu_ui_ctx_download_t ctx = { 5 | .update_interval_same = 0, 6 | .update_interval_next = 0, 7 | }; 8 | 9 | #define IS(file, event, data, expected, ...) { \ 10 | char *out = NULL; \ 11 | size_t outlen = 0; \ 12 | ctx.out = open_memstream(&out, &outlen); \ 13 | pu_ui_cb_download(&ctx, file, event, data); \ 14 | fclose(ctx.out); \ 15 | _tap_is_str(__FILE__, __LINE__, out, expected, __VA_ARGS__); \ 16 | free(out); \ 17 | } 18 | 19 | #define LINE(str) "\x1B[K" str 20 | 21 | int main(void) { 22 | tap_plan(11); 23 | 24 | IS("qux", 25 | ALPM_DOWNLOAD_INIT, 26 | &(alpm_download_event_init_t) {0}, 27 | LINE("(1/1) qux (0)\r"), 28 | "initialize first download"); 29 | 30 | IS("bar", 31 | ALPM_DOWNLOAD_INIT, 32 | &(alpm_download_event_init_t) {0}, 33 | LINE("(2/2) bar (0)\r"), 34 | "initialize second download"); 35 | 36 | IS("qux", 37 | ALPM_DOWNLOAD_PROGRESS, 38 | (&(alpm_download_event_progress_t) { .downloaded = 5, .total = 10 }), 39 | LINE("(1/2) qux (5/10) 50%\r"), 40 | "first download progress"); 41 | 42 | IS("foo", 43 | ALPM_DOWNLOAD_INIT, 44 | (&(alpm_download_event_init_t) {0}), 45 | LINE("(2/3) bar (0)\r"), 46 | "initialize third download"); 47 | 48 | IS("foo", 49 | ALPM_DOWNLOAD_PROGRESS, 50 | (&(alpm_download_event_progress_t) { .downloaded = 3 }), 51 | LINE("(3/3) foo (3)\r"), 52 | "third download progress"); 53 | 54 | IS("qux", 55 | ALPM_DOWNLOAD_COMPLETED, 56 | (&(alpm_download_event_completed_t) { .total = 8 }), 57 | LINE("qux (8/8) 100%\n") LINE("(1/2) bar (0)\r"), 58 | "initialize first download"); 59 | 60 | IS("bar", 61 | ALPM_DOWNLOAD_PROGRESS, 62 | (&(alpm_download_event_progress_t) { .downloaded = 3 }), 63 | LINE("(2/2) foo (3)\r"), 64 | "second download progress"); 65 | 66 | IS("bar", 67 | ALPM_DOWNLOAD_RETRY, 68 | (&(alpm_download_event_retry_t) { .resume = 1 }), 69 | LINE("(1/2) bar (3)\r"), 70 | "second download retry with resume"); 71 | 72 | IS("foo", 73 | ALPM_DOWNLOAD_COMPLETED, 74 | (&(alpm_download_event_completed_t) { .total = 3, .result = 1 }), 75 | LINE("foo is up to date\n") LINE("(1/1) bar (3)\r"), 76 | "third download complete (up to date)"); 77 | 78 | IS("bar", 79 | ALPM_DOWNLOAD_RETRY, 80 | (&(alpm_download_event_retry_t) { .resume = 0 }), 81 | LINE("(1/1) bar (0)\r"), 82 | "second download retry without resume"); 83 | 84 | IS("bar", 85 | ALPM_DOWNLOAD_COMPLETED, 86 | (&(alpm_download_event_completed_t) { .result = -1 }), 87 | LINE("bar failed to download\n"), 88 | "second download complete (failed to download)"); 89 | 90 | return tap_finish(); 91 | } 92 | -------------------------------------------------------------------------------- /t/99-pu_list_shift.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pacutils.h" 4 | 5 | #include "pacutils_test.h" 6 | 7 | int main(void) { 8 | alpm_list_t *l = NULL; 9 | char *c, *data1 = "foo", *data2 = "bar", *data3 = "baz"; 10 | 11 | alpm_list_append(&l, data1); 12 | alpm_list_append(&l, data2); 13 | alpm_list_append(&l, data3); 14 | 15 | tap_plan(4); 16 | 17 | c = _pu_list_shift(&l); 18 | tap_ok(c == data1, NULL); 19 | 20 | c = _pu_list_shift(&l); 21 | tap_ok(c == data2, NULL); 22 | 23 | c = _pu_list_shift(&l); 24 | tap_ok(c == data3, NULL); 25 | 26 | c = _pu_list_shift(&l); 27 | tap_ok(c == NULL, NULL); 28 | 29 | return tap_finish(); 30 | } 31 | -------------------------------------------------------------------------------- /t/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -g -Wextra -I../lib 2 | LDFLAGS += -L../lib 3 | LDLIBS += -lpacutils -lalpm -larchive 4 | 5 | ALPM_CFLAGS ?= $(shell pkg-config libalpm --cflags) 6 | override CFLAGS += $(ALPM_CFLAGS) 7 | 8 | PROVE ?= prove 9 | 10 | GIT ?= git 11 | 12 | TESTS += \ 13 | 10-basename.t \ 14 | 10-config-basic.t \ 15 | 10-filelist_contains_path.t \ 16 | 10-log-action-parse.t \ 17 | 10-log-transaction-parse.t \ 18 | 10-log-reader-basic.t \ 19 | 10-mtree-basic.t \ 20 | 10-parse-datetime.t \ 21 | 10-pathcmp.t \ 22 | 10-strreplace.t \ 23 | 10-util-read-list.t \ 24 | 20-config-includes.t \ 25 | 20-config-root-inheritance.t \ 26 | 30-config-sysroot.t \ 27 | 40-ui-cb-download-progress.t \ 28 | 99-pu_list_shift.t 29 | 30 | %.t: %.c ../lib/libpacutils.so ../ext/tap.c/tap.c Makefile 31 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ 32 | 33 | check: tests 34 | LD_LIBRARY_PATH=../lib $(PROVE) $(TESTS) 35 | 36 | tests: $(TESTS) 37 | 38 | all: tests 39 | 40 | valgrind: tests 41 | LD_LIBRARY_PATH=../lib $(PROVE) --exec="./runtest.sh -v" $(TESTS) 42 | 43 | gcov: CC = gcc 44 | gcov: CFLAGS += -fprofile-arcs -ftest-coverage 45 | gcov: check 46 | gcov $(TESTS) 47 | 48 | clean: 49 | $(RM) $(TESTS) 50 | $(RM) *.gcda *.gcno *.gcov 51 | 52 | .PHONY: all clean check tests 53 | -------------------------------------------------------------------------------- /t/pacutils_test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../ext/tap.c/tap.c" 10 | 11 | #ifndef PACUTILS_TEST_H 12 | #define PACUTILS_TEST_H 13 | 14 | #define ASSERT(x) if(!(x)) { tap_bail("ASSERT FAILED: %s - %s", __FILE__, #x); exit(1); } 15 | 16 | int rmrfat(int dd, const char *path) { 17 | if (!unlinkat(dd, path, 0)) { 18 | return 0; 19 | } else { 20 | struct dirent *de; 21 | DIR *d; 22 | int fd; 23 | 24 | switch (errno) { 25 | case ENOENT: 26 | return 0; 27 | case EPERM: 28 | case EISDIR: 29 | break; 30 | default: 31 | /* not a directory */ 32 | return 0; 33 | } 34 | 35 | fd = openat(dd, path, O_DIRECTORY); 36 | d = fdopendir(fd); 37 | if (!d) { return 0; } 38 | for (de = readdir(d); de != NULL; de = readdir(d)) { 39 | if (strcmp(de->d_name, "..") != 0 && strcmp(de->d_name, ".") != 0) { 40 | char name[PATH_MAX]; 41 | snprintf(name, PATH_MAX, "%s/%s", path, de->d_name); 42 | rmrfat(dd, name); 43 | } 44 | } 45 | closedir(d); 46 | unlinkat(dd, path, AT_REMOVEDIR); 47 | } 48 | return 0; 49 | } 50 | 51 | int vspew(int dd, const char *path, const char *format, va_list vargs) { 52 | int fd = openat(dd, path, O_WRONLY | O_CREAT, 0777); 53 | if (fd == -1) { return -1; } 54 | if (vdprintf(fd, format, vargs) < 0) { close(fd); return -1; } 55 | close(fd); 56 | return 0; 57 | } 58 | 59 | int spew(int dd, const char *path, const char *format, ...) { 60 | int ret; 61 | va_list args; 62 | 63 | va_start(args, format); 64 | ret = vspew(dd, path, format, args); 65 | va_end(args); 66 | 67 | return ret; 68 | } 69 | 70 | #endif /* PACUTILS_TEST_H */ 71 | -------------------------------------------------------------------------------- /t/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gdb=0 4 | valgrind=0 5 | prefix=() 6 | 7 | extend() { 8 | if which "$1" &>/dev/null; then 9 | prefix+=("$@") 10 | else 11 | # bailing out would be counted as a failure by test harnesses, 12 | # ignore missing programs so that tests can be gracefully skipped 13 | printf "warning: command '$1' not found\n" >&2 14 | fi 15 | } 16 | 17 | usage() { 18 | printf "runtest.sh - run an pacman test\n" 19 | printf "usage: runtest.sh [options] [test-options]\n" 20 | printf "\n" 21 | printf "Options:\n" 22 | printf " -g gdb\n" 23 | printf " -h display help\n" 24 | printf " -v valgrind\n" 25 | } 26 | 27 | while getopts cdghlrsv name; do 28 | case $name in 29 | g) gdb=1;; 30 | h) usage; exit;; 31 | v) valgrind=1;; 32 | esac 33 | done 34 | 35 | [ $gdb -eq 1 ] && extend gdb 36 | [ $valgrind -eq 1 ] && extend valgrind --quiet --leak-check=full \ 37 | --gen-suppressions=no --error-exitcode=123 38 | 39 | shift $(($OPTIND - 1)) # remove our options from the stack 40 | 41 | prog="$(realpath "$1")"; shift 42 | "${prefix[@]}" "$prog" "$@" 43 | --------------------------------------------------------------------------------