├── .gitignore ├── LICENSE.md ├── README.md ├── deinstall.sh ├── install.inc ├── install.sh ├── list.sh ├── man ├── bprintf.1 ├── buildflags.awk.1 ├── buildflags.conf.5 ├── buildflags.mk.1 ├── distviper.8 ├── loaderupdate.8 ├── makeplist.8 ├── pkg_libchk.8 ├── pkg_trim.8 ├── pkg_validate.8 └── pkg_version.8 ├── ref ├── bsda_err.md ├── bsda_obj.md ├── lst.md └── type.md ├── src ├── bprintf ├── bsda_async.sh ├── bsda_bsdmake.sh ├── bsda_container.sh ├── bsda_dialog.sh ├── bsda_elf.sh ├── bsda_err.sh ├── bsda_fifo.sh ├── bsda_fmt.sh ├── bsda_obj.sh ├── bsda_opts.sh ├── bsda_test.sh ├── bsda_tty.sh ├── bsda_tty_test.sh ├── bsda_util.sh ├── buildflags.awk ├── buildflags.conf.sample ├── buildflags.mk ├── compat.sh ├── distviper ├── distviper.sh ├── interrupt.mk ├── ldd_filter.awk ├── loaderupdate ├── loaderupdate.sh ├── lst.sh ├── makeplist ├── makeplist.sh ├── makeplist_filter.awk ├── makeplist_keywords.awk ├── options.mk ├── pkg_info.sh ├── pkg_libchk ├── pkg_libchk.sh ├── pkg_options.sh ├── pkg_query.sh ├── pkg_trim ├── pkg_trim.sh ├── pkg_validate ├── pkg_validate.sh ├── pkg_version ├── pkg_version.sh ├── testify.awk └── type.sh ├── test └── tests ├── bsda_async.sh ├── bsda_container.sh ├── bsda_elf.sh ├── bsda_err.sh ├── bsda_fifo.sh ├── bsda_fmt.sh ├── bsda_obj.sh ├── bsda_opts.sh ├── bsda_util.sh ├── compat.sh ├── lst.sh └── type.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2006 - 2024 Dominic Fandrey 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /deinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . install.inc 4 | 5 | dirs= 6 | for file in $files; { 7 | test -z "$file" && continue 8 | source="${file%%,*}" 9 | file="${file##*,}" 10 | target="${destdir}${file#${destdir:+/}}" 11 | if [ -n "${source}" -o -f "${target}" ]; then 12 | echo "deleting: $target" 13 | rm "$target" 14 | dirs="${dirs}${target%/*}"$'\n' 15 | fi 16 | } 17 | dirs="$(echo "${dirs}" | sort -ur)" 18 | for dir in $dirs; do 19 | echo "remove: $dir" 20 | rmdir "${dir}" 21 | done 22 | -------------------------------------------------------------------------------- /install.inc: -------------------------------------------------------------------------------- 1 | set -ef 2 | 3 | # sort -t/ -k2 4 | files=" 5 | src/bprintf,0755,%%PREFIX%%/bin/bprintf 6 | man/bprintf.1,0644,%%MAN%%/man1/bprintf.1.gz 7 | src/bsda_async.sh,0644,%%DATADIR%%/bsda_async.sh 8 | src/bsda_bsdmake.sh,0644,%%DATADIR%%/bsda_bsdmake.sh 9 | src/bsda_container.sh,0644,%%DATADIR%%/bsda_container.sh 10 | src/bsda_dialog.sh,0644,%%DATADIR%%/bsda_dialog.sh 11 | src/bsda_elf.sh,0644,%%DATADIR%%/bsda_elf.sh 12 | ref/bsda_err.md,0644,%%DOCSDIR%%/ref/bsda_err.md 13 | src/bsda_err.sh,0644,%%DATADIR%%/bsda_err.sh 14 | src/bsda_fifo.sh,0644,%%DATADIR%%/bsda_fifo.sh 15 | src/bsda_fmt.sh,0644,%%DATADIR%%/bsda_fmt.sh 16 | ref/bsda_obj.md,0644,%%DOCSDIR%%/ref/bsda_obj.md 17 | src/bsda_obj.sh,0644,%%DATADIR%%/bsda_obj.sh 18 | src/bsda_opts.sh,0644,%%DATADIR%%/bsda_opts.sh 19 | src/bsda_tty.sh,0644,%%DATADIR%%/bsda_tty.sh 20 | src/bsda_util.sh,0644,%%DATADIR%%/bsda_util.sh 21 | src/buildflags.awk,0755,%%DATADIR%%/buildflags.awk 22 | man/buildflags.awk.1,0644,%%MAN%%/man1/buildflags.awk.1.gz 23 | man/buildflags.conf.5,0644,%%MAN%%/man5/buildflags.conf.5.gz 24 | src/buildflags.conf.sample,0644,%%PREFIX%%/etc/buildflags.conf.sample 25 | src/buildflags.mk,0644,%%DATADIR%%/buildflags.mk 26 | man/buildflags.mk.1,0644,%%MAN%%/man1/buildflags.mk.1.gz 27 | src/compat.sh,0644,%%DATADIR%%/compat.sh 28 | src/distviper,0755,%%PREFIX%%/sbin/distviper 29 | man/distviper.8,0644,%%MAN%%/man8/distviper.8.gz 30 | src/distviper.sh,0644,%%DATADIR%%/distviper.sh 31 | src/interrupt.mk,0644,%%DATADIR%%/interrupt.mk 32 | src/ldd_filter.awk,0755,%%DATADIR%%/ldd_filter.awk 33 | src/loaderupdate,0755,%%PREFIX%%/sbin/loaderupdate 34 | man/loaderupdate.8,0644,%%MAN%%/man8/loaderupdate.8.gz 35 | src/loaderupdate.sh,0644,%%DATADIR%%/loaderupdate.sh 36 | ref/lst.md,0644,%%DOCSDIR%%/ref/lst.md 37 | src/lst.sh,0644,%%DATADIR%%/lst.sh 38 | src/makeplist_filter.awk,0755,%%DATADIR%%/makeplist_filter.awk 39 | src/makeplist_keywords.awk,0755,%%DATADIR%%/makeplist_keywords.awk 40 | src/makeplist,0755,%%PREFIX%%/sbin/makeplist 41 | man/makeplist.8,0644,%%MAN%%/man8/makeplist.8.gz 42 | src/makeplist.sh,0644,%%DATADIR%%/makeplist.sh 43 | src/options.mk,0644,%%DATADIR%%/options.mk 44 | src/pkg_info.sh,0644,%%DATADIR%%/pkg_info.sh 45 | src/pkg_libchk,0755,%%PREFIX%%/sbin/pkg_libchk 46 | man/pkg_libchk.8,0644,%%MAN%%/man8/pkg_libchk.8.gz 47 | src/pkg_libchk.sh,0644,%%DATADIR%%/pkg_libchk.sh 48 | src/pkg_options.sh,0644,%%DATADIR%%/pkg_options.sh 49 | src/pkg_query.sh,0644,%%DATADIR%%/pkg_query.sh 50 | src/pkg_trim,0755,%%PREFIX%%/sbin/pkg_trim 51 | man/pkg_trim.8,0644,%%MAN%%/man8/pkg_trim.8.gz 52 | src/pkg_trim.sh,0644,%%DATADIR%%/pkg_trim.sh 53 | src/pkg_validate,0755,%%PREFIX%%/sbin/pkg_validate 54 | man/pkg_validate.8,0644,%%MAN%%/man8/pkg_validate.8.gz 55 | src/pkg_validate.sh,0644,%%DATADIR%%/pkg_validate.sh 56 | src/pkg_version,0755,%%PREFIX%%/sbin/pkg_version 57 | man/pkg_version.8,0644,%%MAN%%/man8/pkg_version.8.gz 58 | src/pkg_version.sh,0644,%%DATADIR%%/pkg_version.sh 59 | ref/type.md,0644,%%DOCSDIR%%/ref/type.md 60 | src/type.sh,0644,%%DATADIR%%/type.sh 61 | LICENSE.md,0644,%%DOCSDIR%%/LICENSE.md 62 | README.md,0644,%%DOCSDIR%%/README.md 63 | " 64 | 65 | # moved/deleted files 66 | files="${files} 67 | ,,%%MAN%%/man1/pkg_libchk.1.gz 68 | ,,%%MAN%%/man1/pkg_validate.1.gz 69 | ,,%%MAN%%/man1/buildflags.conf.1.gz 70 | " 71 | 72 | IFS=$'\n' 73 | 74 | for parameter in "$@"; { 75 | case "${parameter%%=*}" in 76 | -destdir | -prefix | -datadir | -docsdir | -tmp | -ports) 77 | value="${parameter#*=}" 78 | parameter="${parameter%%=*}" 79 | parameter="${parameter#-}" 80 | eval "$parameter='$value'" 81 | ;; 82 | -nodoc) 83 | files="$(echo "$files" | grep -vF "%%MAN%%${IFS}%%DOCSDIR%%")" 84 | ;; 85 | *) 86 | echo "Unknown parameter '$parameter'." 1>&2 87 | return 1 88 | ;; 89 | esac 90 | } 91 | 92 | : ${destdir=} 93 | : ${prefix=/usr/local} 94 | : ${datadir=$prefix/share/bsda2} 95 | : ${docsdir=$prefix/share/doc/bsda2} 96 | : ${tmp=/tmp} 97 | : ${ports=/usr/ports} 98 | 99 | # If set, destdir should end with a / 100 | destdir=${destdir:+${destdir%/}/} 101 | 102 | replace=" 103 | %%PREFIX%%,$prefix 104 | %%DATADIR%%,$datadir 105 | %%DOCSDIR%%,$docsdir 106 | %%MAN%%,$prefix/man 107 | %%TMP%%,$tmp 108 | %%PORTS%%,$ports 109 | " 110 | 111 | replace_cmd="sed -e '/#HACK/,/#hack/d'" 112 | 113 | for substitution in $replace; { 114 | test -z "$substitution" && continue 115 | replace_cmd="$replace_cmd -e 's,$substitution,g'" 116 | } 117 | 118 | files="$(echo "$files" | eval "$replace_cmd")" 119 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . install.inc 4 | 5 | for file in $files; { 6 | source="${file%%,*}" 7 | test -z "${source}" && continue 8 | mode="${file%,*}" 9 | mode="${mode#*,}" 10 | file="${file##*,}" 11 | target="${destdir}${file#${destdir:+/}}" 12 | echo "installing: $target" 13 | mkdir -p "${target%/*}" 14 | eval "$replace_cmd '$source'" > "${target%.gz}" 15 | test "${target%.gz}" != "$target" && gzip -f9 "${target%.gz}" 16 | chmod "$mode" "$target" 17 | } 18 | -------------------------------------------------------------------------------- /list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -fCe 3 | 4 | . install.inc 5 | 6 | for file in $files; { 7 | source="${file%%,*}" 8 | test -z "${source}" && continue 9 | file="${file##*,}" 10 | target="${destdir}${file#${destdir:+/}}" 11 | echo "$target" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /man/bprintf.1: -------------------------------------------------------------------------------- 1 | .Dd 19 January, 2021 2 | .Dt BPRINTF 1 3 | .Os 4 | .Sh NAME 5 | .Nm bprintf 6 | .Nd formatted output with named arguments 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Ar format 10 | .Op Ar field Ns Cm = Ns Ar value ... 11 | .Sh DESCRIPTION 12 | The 13 | .Nm 14 | command is an advanced string formatting utility. It is a semantic 15 | wrapper for 16 | .Xr printf 1 . 17 | .Nm 18 | replaces argument order with named arguments and can perform arithmetic 19 | within format specifications. 20 | .Pp 21 | A 22 | .Ar format 23 | string can contain a set of substitutions. A substitution consists 24 | of a 25 | .Ar field 26 | name and an optional format specification. The format specification 27 | determines how the field 28 | .Ar value 29 | is interpreted and displayed. A single 30 | .Ar field 31 | may be used within multiple substitutions, each with its own formatting. 32 | .Ss Format Syntax 33 | The 34 | .Ar format 35 | string is reproduced on 36 | .Pa stdout 37 | after performing substitutions. 38 | .Pp 39 | The backslash notation character escape sequences supported by 40 | .Xr printf 1 41 | can be used. The 42 | .Sq % 43 | character has no special meaning. 44 | .Pp 45 | A substitution is enclosed in curly braces. To print a literal opening 46 | or closing curly brace escape it with a backslash, i.e. 47 | .Ql \e{ 48 | and 49 | .Ql \e} 50 | respectively. 51 | .Pp 52 | A substitution consists of the field name and an optional format 53 | specification: 54 | .Dl Brq Ar field Ns Op Cm : Ns Ar specification 55 | .Pp 56 | A substitution without a 57 | .Ar specification 58 | behaves like a 59 | .Sq %s 60 | field in 61 | .Xr printf 1 . 62 | .Pp 63 | The format specification consists of the same flags, Field Width, 64 | Precision and Format characters described by 65 | .Xr printf 1 . 66 | If the format character is not supplied, 67 | .Sq s 68 | is implied. An arithmetic expression can be inserted into the format 69 | by enclosing it in parenthesis. 70 | .Ss Arithmetic Expressions 71 | Arithmetic expressions support the same Values, Constants and Variables 72 | as well as the same Unary, Binary and Conditional operators as described 73 | in the 74 | .Sx Arithmetic Expansion 75 | section of 76 | .Xr sh 1 . 77 | .Pp 78 | Note, this list does not include Assignment operators. 79 | .Pp 80 | All variables referred to within an expression must have a corresponding 81 | field with an integral value assigned. Variables can be assigned 82 | values with the same syntax as constants, i.e. an optional sign followed 83 | by either a decimal value, hexadecimal value 84 | starting with 85 | .Sq 0x 86 | or an octal value starting with 87 | .Sq 0 . 88 | .Ss Field Assignments 89 | The 90 | .Ar format 91 | string may use all and any fields supplied by the subsequent 92 | .Ar field Ns Cm = Ns Ar value 93 | arguments. Fields not occurring in the format string are discarded 94 | from the output. Values are arbitrary unless used within arithmetic 95 | expressions. 96 | .Pp 97 | Field names must be made up of the pattern 98 | .Ql [a-zA-Z][a-zA-Z0-9_]* . 99 | .Sh EXIT STATUS 100 | The following is a list of all anticipated exit codes: 101 | .Bl -tag -with indent 102 | .It Er EOK=0 103 | Command completed successfully. 104 | .It Er ESIGNAL=1 105 | Interrupted by signal. 106 | .It Er EFAIL=2 107 | Generic application logic error. 108 | .It Er ENOARGS=3 109 | No format string was supplied. 110 | .It Er ESUB=4 111 | A substitution field has no corresponding field assignment argument. 112 | .It Er EID=5 113 | A substitution field name is illegal. 114 | .It Er EFORMAT=6 115 | A substitution format specification is ill-formed. 116 | .It Er EEXPR=7 117 | An arithmetic expression is ill-formed. 118 | .It Er EASSIGN=8 119 | An arithmetic expression contains an assignment. 120 | .It Er EARG=9 121 | An argument uses an illegal field name. 122 | .It Er ETYPE=10 123 | A field value used in an arithmetic expression is not an integer. 124 | .El 125 | .Sh EXAMPLES 126 | Simple substitution: 127 | .Bd -literal -offset indent 128 | $ bprintf 'My name is {name}.\en' name=Johnny 129 | My name is Johnny. 130 | .Ed 131 | .Pp 132 | Using a field multiple times: 133 | .Bd -literal -offset indent 134 | $ bprintf '{str:.5}\en{str:.10}\en{str}\en' str=SugarHoneyMilk 135 | Sugar 136 | SugarHoney 137 | SugarHoneyMilk 138 | .Ed 139 | .Pp 140 | Use different field types: 141 | .Bd -literal -offset indent 142 | $ bprintf '{var:16} = {value:g} {unit}\en' var=g value=9.81 unit=mps^2 143 | g = 9.81 mps^2 144 | .Ed 145 | .Pp 146 | Compute column width using arithmetic expressions: 147 | .Bd -literal -offset indent 148 | $ bprintf '| {name:-16} | {val:(cols-36)} {unit:-12} |\en' cols=64 name=Name val=Value unit=Unit 149 | | Name | Value Unit | 150 | $ bprintf '| {name:-16} | {val:(cols-36).3f} {unit:-12} |\en' cols=64 name=g val=9.81 unit=mps^2 151 | | g | 9.810 mps^2 | 152 | .Ed 153 | .Sh SEE ALSO 154 | .Xr printf 1 , 155 | .Xr sh 1 156 | .Sh HISTORY 157 | The 158 | .Nm 159 | command was added with the 160 | .Sy bsda2-0.4.0 161 | release. 162 | .Sh AUTHORS 163 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 164 | -------------------------------------------------------------------------------- /man/buildflags.awk.1: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt BUILDFLAGS.AWK 1 3 | .Os 4 | .Sh NAME 5 | .Nm buildflags.awk 6 | .Nd convert 7 | .Xr buildflags.conf 5 8 | files into make syntax 9 | .Sh SYNOPSIS 10 | .Nm 11 | file 12 | .Sh DESCRIPTION 13 | The 14 | .Nm 15 | script converts a 16 | .Xr buildflags.conf 5 17 | file into valid make syntax. The script can 18 | normally be found under '%%DATADIR%%/buildflags.awk'. 19 | .Pp 20 | This is not end user documentation, if you just want to use buildflags.conf 21 | files please refer to the 22 | .Xr buildflags.conf 5 23 | manual page. 24 | .Sh SYNTAX 25 | While the 26 | .Xr buildflags.conf 5 27 | man page describes how to use the buildflags.conf syntax, this page 28 | describes the resulting make syntax. Syntax examples will always be followed 29 | by the resulting make code. The term space actually refers to all whitespace 30 | characters (including tabs). 31 | .Ss Comments 32 | Unless they're enclosed by '"' comments have the highest priority in the 33 | buildflags.conf syntax. 34 | Comments that are found behind valid code will end up one line before it. 35 | .Pp 36 | EXAMPLE 37 | .Bd -literal -offset indent 38 | %%PORTS%%/audio/arts {IGNORE} # I do not want this, ever! 39 | .Ed 40 | .Pp 41 | RESULT 42 | .Bd -literal -offset indent 43 | # I do not want this, ever! 44 | \&.if ${.CURDIR:M%%PORTS%%/audio/arts} 45 | IGNORE= yes 46 | \&.endif # %%PORTS%%/audio/arts 47 | .Ed 48 | .Ss Directives 49 | Apart from being put behind trailing comments native 50 | .Xr make 1 51 | directives remain 52 | entirely unchanged. Native directives are everything that begins with a '.'. 53 | .Pp 54 | EXAMPLE 55 | .Bd -literal -offset indent 56 | %%PORTS%%/* { 57 | .if defined(WANT_I386) 58 | CFLAGS+= -m32 59 | LDCONFIG+= -32 60 | .endif 61 | } 62 | .Ed 63 | .Pp 64 | RESULT 65 | .Bd -literal -offset indent 66 | \&.if ${CURDIR:M%%PORTS%%/*} 67 | \&.if defined(WANT_I386) 68 | CFLAGS+= -m32 69 | LDCONFIG+= -32 70 | \&.endif 71 | \&.endif # %%PORTS%%/* 72 | .Ed 73 | .Ss Quotes 74 | Unless part of a comment quotes always have to follow a variable assignment. 75 | Whatever lies within them will remain untouched, but there are no escape 76 | sequences, thus there is no way to enclose a '"' within quotes. Only double 77 | quotes have meaning, single quotes do not have a special function. 78 | .Pp 79 | EXAMPLE 80 | .Bd -literal -offset indent 81 | # " in a comment does not matter. 82 | BUT= " in an 83 | assignment 84 | does" 85 | CFLAGS="-O2 -pipe" # We want optimized binaries! 86 | .Ed 87 | .Pp 88 | RESULT 89 | .Bd -literal -offset indent 90 | # " in a comment does not matter. 91 | BUT= " in an 92 | assignment 93 | does" 94 | # We want optimized binaries! 95 | CFLAGS="-O2 -pipe" 96 | .Ed 97 | .Ss Locations 98 | Locations are paths that are used to define where a variable assignment is 99 | valid, this is achieved by make. This script will simply convert such location 100 | blocks to a make '.if' statement. If possible symlinked paths will be 101 | substituted with their physical paths. A '!' at the beginning of a path means 102 | that is should not be matched. Several paths can be appended with '&' 103 | (logical and) and '|' (logical or). 104 | .Pp 105 | After the location a block is opened by the character '{' and closed by 106 | the character '}'. 107 | .Pp 108 | EXAMPLE 109 | .Bd -literal -offset indent 110 | %%PORTS%%/* & !*/work/*{ 111 | */x11* {IGNORE} 112 | } 113 | .Ed 114 | .Pp 115 | RESULT 116 | .Bd -literal -offset indent 117 | \&.if ${.CURDIR:M%%PORTS%%/*} && !${.CURDIR:M*/work/*} 118 | \&.if ${.CURDIR:M*/x11*} 119 | IGNORE= yes 120 | \&.endif # */x11* 121 | \&.endif # %%PORTS%%/* & !*/work/* 122 | .Ed 123 | .Ss Variables 124 | For 125 | .Nm 126 | there are two kinds of variable assignments. Compact variable assignments 127 | and long variable assignments. Variable assignments within quotes are 128 | directly dealt with by the quoting code. 129 | .Pp 130 | Compact variable assignments are directly followed by their value, without 131 | any spaces behind the '=' and their value ends with the first space or line 132 | break. This makes it possible to have several such assignments in a single 133 | line. Any such assignment will be parsed into its own line, though. 134 | .Pp 135 | Long variable assignments are followed by spaces and the only way to end 136 | them without a line break is a '}'. 137 | .Pp 138 | EXAMPLE 139 | .Bd -literal -offset indent 140 | THREADS=4 141 | CPUTYPE?=p3 CFLAGS= -O2 -pipe 142 | /usr/src{CPUTYPE=i686 CFLAGS= -O -pipe} 143 | .Ed 144 | .Pp 145 | RESULT 146 | .Bd -literal -offset indent 147 | THREADS=4 148 | CPUTYPE?=p3 149 | CFLAGS= -O2 -pipe 150 | \&.if ${.CURDIR:M/usr/src} 151 | CPUTYPE=i686 152 | CFLAGS= -O -pipe 153 | \&.endif # /usr/src 154 | .Ed 155 | .Ss Flags 156 | There are two kinds of flags, negated flags and regular flags. 157 | .Pp 158 | Regular flags are variable assignments assuming that the mostly used assignment 159 | simply is 'yes'. To define a flag it is enough to put the flag name in an 160 | appropriate place. 161 | .Pp 162 | Negated flags are a way to undefine variables. To do so simply precede a 163 | flag name with '!'. 164 | .Pp 165 | EXAMPLE 166 | .Bd -literal -offset indent 167 | !THREADS WITHOUT_BDB 168 | .Ed 169 | .Pp 170 | RESULT 171 | .Bd -literal -offset indent 172 | \&.undef THREADS 173 | WITHOUT_BDB= yes 174 | .Ed 175 | .Sh SEE ALSO 176 | .Xr buildflags.conf 5 , 177 | .Xr buildflags.mk 1 178 | .Sh HISTORY 179 | The 180 | .Nm 181 | script first appeared in the bsdadminscripts-2.1 collection. 182 | .Sh AUTHORS 183 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 184 | -------------------------------------------------------------------------------- /man/buildflags.conf.5: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt BUILDFLAGS.CONF 5 3 | .Os 4 | .Sh NAME 5 | .Nm buildflags.conf 6 | .Nd set build options for 7 | .Xr ports 7 , 8 | world and everything 9 | .Sh DESCRIPTION 10 | The 11 | .Nm 12 | file is used by 13 | .Xr buildflags.mk 1 14 | to configure 15 | .Xr make 1 16 | jobs. 17 | .Pp 18 | The intended way to use it is to add the following lines to 19 | .Xr make.conf 5 . 20 | .Bd -literal -offset indent 21 | \&.sinclude "%%DATADIR%%/buildflags.mk" 22 | .Ed 23 | .Sh SYNTAX 24 | A 25 | .Nm 26 | file gets parsed into a make file. If you want to make sure that your 27 | syntax is right you can do so by running: 28 | .Pp 29 | %%DATADIR%%/buildflags.awk buildflags.conf | less 30 | .Pp 31 | The 32 | .Nm 33 | syntax only knows four kinds of data: 34 | .Bd -literal -offset indent 35 | comments, locations, knobs and variables 36 | .Ed 37 | .Ss Comments 38 | Comments are everything behind a '#'. The only exception is a '#' within 39 | a '"' block. 40 | .Bd -literal -offset indent 41 | # This is a comment. 42 | .Ed 43 | .Ss Locations 44 | A location is a folder in which a make job runs. 45 | Locations make it possible to limit assignments only to certain ports or 46 | other build systems. 47 | .Pp 48 | A location consists of a path and/or a pattern. The difference between a 49 | path and a pattern is that the pattern will remain untouched and a path will 50 | be resolved as far as possible. Resolving a path means that a path will be 51 | substituted with its physical path. 52 | .Pp 53 | Several locations can be connected with '&' (logical and) and '|' 54 | (logical or). 55 | .Pp 56 | If e.g. '%%PORTS%%' is a link to '/mnt/vault/ports' a location 57 | like '%%PORTS%%/x11*' will be resolved into '/mnt/vault/ports/x11*'. 58 | In this case '%%PORTS%%' is the path and '/x11*' the pattern. 59 | .Pp 60 | Every location is followed by a block that can contain comments, knobs, 61 | variables and other locations. A block is opened with the character '{' 62 | and closed with '}'. Here is an example: 63 | .Bd -literal -offset indent 64 | %%PORTS%%/* & !*/work/*{ 65 | WITH_IPV6 66 | */x11/toolkits-gtk20 {WITHOUT_DEBUG} 67 | } 68 | .Ed 69 | .Ss Knobs 70 | A knob really is a variable with a default assignment: 71 | .Bd -literal -offset indent 72 | WITH_DEBUG 73 | !CPUTYPE 74 | .Ed 75 | .Pp 76 | This example would set 'WITH_DEBUG=yes' and undefine 'CPUTYPE'. You can have 77 | several knobs in a single line: 78 | .Bd -literal -offset indent 79 | WITH_DEBUG !CPUTYPE 80 | .Ed 81 | .Ss Variables 82 | Assigning variables is simply done with an '=' other ways of assignment 83 | known from 84 | .Xr make 1 85 | like '?=', ':=' or '!=' can also be used. There may not be spaces in front of 86 | a '='. 87 | .Pp 88 | If the '=' is followed by spaces. The whole trailing line will be assigned 89 | as the value, unless the first character is a '"'. 90 | Values may not contain a '"'. If the value is right behind the '=' only one 91 | word will be assigned, the next word will be treated as a knob or variable. 92 | .Pp 93 | Here are some examples of valid assignments: 94 | .Bd -literal -offset indent 95 | CPUTYPE?="pentium-m" CFLAGS="-O2 -pipe" THREADS=5 96 | SUPFLAGS= -E -g -L 2 -z 97 | .Ed 98 | .Ss Directives 99 | Native 100 | .Xr make 1 101 | directives can be included. Apart from putting trailing comments in front of 102 | them and removing preceding spaces they are not changed. Make directives begin 103 | with a '.', among them are '.if', '.include' and '.for'. 104 | .Pp 105 | The following is a valid example: 106 | .Bd -literal -offset indent 107 | %%PORTS%%/* { 108 | .if defined(WANT_I386) 109 | CFLAGS+= -m32 110 | LDCONFIG+= -32 111 | .endif 112 | } 113 | .Ed 114 | .Sh WARNINGS 115 | There are things that should not be done. 116 | .Ss WRKDIRPREFIX 117 | Do not set the WRKDIRPREFIX directive in 118 | .Nm . 119 | Doing so would break updating of 120 | .Xr bsdadminscripts 1 121 | in the FreeBSD 122 | .Xr ports 7 123 | collection. 124 | .Sh SEE ALSO 125 | .Xr buildflags.awk 1 , 126 | .Xr buildflags.mk 1 127 | .Xr make 1 128 | .Sh FILES 129 | .Bl -tag -width indent 130 | .It Pa ${HOME}/.buildflags.conf 131 | User specific build settings. 132 | .It Pa %%PREFIX%%/etc/buildflags.conf 133 | Default location of the 134 | .Nm . 135 | .It Pa %%PREFIX%%/etc/buildflags.conf.sample 136 | An example configuration file. 137 | .El 138 | .Sh HISTORY 139 | The 140 | .Nm 141 | file first appeared in the bsdadminscripts-2.0 collection. 142 | .Sh AUTHORS 143 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 144 | -------------------------------------------------------------------------------- /man/buildflags.mk.1: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt BUILDFLAGS.MK 1 3 | .Os 4 | .Sh NAME 5 | .Nm buildflags.mk 6 | .Nd import 7 | .Xr buildflags.conf 5 8 | .Sh SYNOPSIS 9 | .Nm 10 | .Sh DESCRIPTION 11 | The primary function of the 12 | .Nm 13 | script is to use the 14 | .Xr buildflags.conf 5 15 | file, but it also offers knobs to easily use tools such as distcc and ccache. 16 | .Pp 17 | The intended way to use it is to add the following lines to 18 | .Xr make.conf 5 . 19 | .Bd -literal -offset indent 20 | \&.sinclude "%%DATADIR%%/buildflags.mk" 21 | .Ed 22 | .Pp 23 | .Ss Options 24 | The following options to control the internals of 25 | .Nm 26 | are available: 27 | .Bl -tag -width indent 28 | .It BUILDFLAGS_CCACHE 29 | The ccache binary. 30 | .Pp 31 | Default: 32 | .Pa %%PREFIX%%/bin/ccache 33 | .It BUILDFLAGS_CONF 34 | The configuration file. 35 | .Pp 36 | Default: 37 | .Pa %%PREFIX%%/etc/buildflags.conf 38 | .It BUILDFLAGS_DISTCC 39 | The distcc binary. 40 | .Pp 41 | Default: 42 | .Pa %%PREFIX%%/bin/distcc 43 | .It BUILDFLAGS_PARSER 44 | The program that parses the configuration file. 45 | .Pp 46 | Default: 47 | .Pa %%DATADIR%%/buildflags.awk 48 | .It BUILDFLAGS_TMP 49 | A temporary file required to include the parsed configuration. 50 | .Pp 51 | Default: 52 | .Pa %%TMP%%/buildflags.tmp.mk.${USER} 53 | .El 54 | .Pp 55 | The following additional knobs are offered to manipulate make jobs: 56 | .Bl -tag -width indent 57 | .It SUBTHREADS 58 | WARNING: This setting is deprecated in favour of the ports native settings 59 | .Dv FORCE_MAKE_JOBS 60 | and 61 | .Dv MAKE_JOBS_NUMBER. 62 | .Pp 63 | This knob does for ports what 64 | .Dv THREADS 65 | does everywhere else. It does not 66 | have an effect if the target install is specified. Thus it is recommended to 67 | run 68 | .Ic make build 69 | and 70 | .Ic make install 71 | seperately. 72 | .It THREADS 73 | This knob causes make to create parallel jobs. A sensible value is 74 | twice the number of CPU cores available. This increases the 75 | amount of memory used. If the system starts to swap often, the number should 76 | be decreased to the number of available CPU cores. 77 | Do not use 78 | .Dv THREADS 79 | with the ports system. 80 | .It USE_DISTCC 81 | Prepends the distcc binary to 82 | .Dv CC , CXX 83 | and 84 | .Dv CPP . 85 | .It USE_CCACHE 86 | Prepends the ccache binary to 87 | .Dv CC , CXX 88 | and 89 | .Dv CPP . 90 | .It WITH_GCC 91 | Triggers the use of the alternative gcc defined with 92 | .Dv BUILDFLAGS_GCC . 93 | If 94 | .Dv BUILDFLAGS_GCC 95 | has not been definied it will be set to whatever 96 | .Dv WITH_GCC 97 | was set. 98 | .El 99 | .Sh SEE ALSO 100 | .Xr buildflags.awk 1 , 101 | .Xr buildflags.conf 5 102 | .Sh HISTORY 103 | The 104 | .Nm 105 | script first appeared in the bsdadminscripts-2.0 collection. 106 | .Sh AUTHORS 107 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 108 | -------------------------------------------------------------------------------- /man/distviper.8: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt DISTVIPER 8 3 | .Os 4 | .Sh NAME 5 | .Nm distviper 6 | .Nd wipe outdated files from the FreeBSD ports distdir 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl dinqv 10 | .Op Ar keep 11 | .Nm 12 | .Fl h 13 | .Sh DESCRIPTION 14 | The 15 | .Nm 16 | script removes outdated files within 17 | .Li DISTDIR 18 | of the 19 | .Xr ports 7 . 20 | To achieve this it reads distinfo files from the 21 | .Li PORTSDIR 22 | and creates a list of files to keep if they should be encountered. 23 | .Ss Arguments 24 | The following argument types can be given. 25 | .Bl -tag -width indent 26 | .It Ar keep 27 | Chooses which kind of files are kept: 28 | .Bl -tag -nested -width indent -compact 29 | .It Li all , Li thorough 30 | Keep files required for any port in the ports tree. The 31 | .Ar thorough 32 | alias exists for compatibility with the original bsdadminscripts. 33 | .It Li installed 34 | Keep files required for any currently installed port. 35 | .It Li fast 36 | This is the same as calling 37 | .Nm 38 | with the 39 | .Fl n 40 | flag and the 41 | .Ar installed 42 | argument. It exists for compatibility with the original bsdadminscripts. 43 | .El 44 | .El 45 | .Ss Options 46 | The following options are supported: 47 | .Bl -tag -width indent 48 | .It Fl d , -demo 49 | The program is run in demo mode. The program acts as if it would operate 50 | normally, but does not remove any files. 51 | .It Fl h , -help 52 | Displays the available options. 53 | .It Fl i , -interactive 54 | This option prompts for confirmation before deleting a file. 55 | It is ignored in demo mode. 56 | .It Fl n , -no-checksum 57 | Turn off checksum validation for files to keep. Much faster, but also 58 | risks leaving invalid files in 59 | .Li DISTDIR . 60 | .It Fl q , -quiet 61 | Supresses all output except for interactive prompts. 62 | .It Fl v , -verbose 63 | Makes the script talkative about what is currently going on. 64 | .El 65 | .Sh ENVIRONMENT 66 | The following environment variables are supported: 67 | .Bl -tag -width indent 68 | .It Ev PORTSDIR 69 | The location of the ports tree, e.g. 70 | .Pa /usr/ports . 71 | .It Ev DISTDIR 72 | The location of the distribution files, e.g. 73 | .Pa /usr/ports/distfiles . 74 | .El 75 | .Pp 76 | The variables are collected running: 77 | .Bd -literal -offset indent 78 | make -f/usr/share/mk/bsd.port.mk -VPORTSDIR -VDISTDIR 79 | .Ed 80 | .Sh EXAMPLES 81 | Delete any distfile not referenced by the current ports tree: 82 | .Bd -literal -offset indent 83 | distviper 84 | .Ed 85 | .Pp 86 | The demo mode can be used to check which files would be removed: 87 | .Bd -literal -offset indent 88 | distviper -d 89 | .Ed 90 | .Pp 91 | Avoid expensive operations, i.e. only keep files for installed ports 92 | and do not perform checksum validation: 93 | .Bd -literal -offset indent 94 | distviper fast 95 | .Ed 96 | .Pp 97 | Only keep files for installed ports, and ask for confirmation: 98 | .Bd -literal -offset indent 99 | distviper -i installed 100 | .Ed 101 | .Pp 102 | .Sh EXIT CODES 103 | .Bl -tag -width indent 104 | .It 1 105 | An unknown parameter has been supplied. 106 | .It 2 107 | An unknown mode command has been supplied. 108 | .It 3 109 | More than one mode commands have been supplied. 110 | .El 111 | .Sh SEE ALSO 112 | .Xr ports 7 113 | .Sh HISTORY 114 | The 115 | .Nm 116 | script first appeared in the bsdadminscripts-5.0 collection. It was rewritten 117 | for the bsda2 collection. 118 | .Sh AUTHORS 119 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 120 | -------------------------------------------------------------------------------- /man/loaderupdate.8: -------------------------------------------------------------------------------- 1 | .Dd 18 December, 2023 2 | .Dt LOADERUPDATE 8 3 | .Os 4 | .Sh NAME 5 | .Nm loaderupdate 6 | .Nd update boot loaders 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl d Ar destdir 10 | .Op Fl L Ar efilabel 11 | .Op Fl b Ar bootloader 12 | .Op Fl e Ar efiloader 13 | .Op Fl o Ar efifile 14 | .Op Fl p Ar pmbr 15 | .Op Fl cDn 16 | .Ar device ... 17 | .Nm 18 | .Op Fl d Ar destdir 19 | .Op Fl L Ar efilabel 20 | .Op Fl b Ar bootloader 21 | .Op Fl e Ar efiloader 22 | .Op Fl o Ar efifile 23 | .Op Fl p Ar pmbr 24 | .Op Fl cDn 25 | .Fl a 26 | .Nm 27 | .Op Fl d Ar destdir 28 | .Op Fl L Ar efilabel 29 | .Op Fl b Ar bootloader 30 | .Op Fl e Ar efiloader 31 | .Op Fl o Ar efifile 32 | .Op Fl p Ar pmbr 33 | .Op Fl cn 34 | .Fl P 35 | .Op Fl a | Ar device ... 36 | .Nm 37 | .Fl h 38 | .Sh DESCRIPTION 39 | The 40 | .Nm 41 | script updates the boot loaders of a given set of bootable devices 42 | according to a given boot environment. 43 | .Pp 44 | The boot environment must contain a system kernel under 45 | .Pa /boot/kernel/kernel 46 | as well as the required loader images. The defaults of which can 47 | be overridden by the 48 | .Fl b , e 49 | and 50 | .Fl p 51 | parameters. Which boot loaders are used by default also depends on 52 | the file system of the boot environment. 53 | .Pp 54 | The default boot environment 55 | .Pa / 56 | can be overridden by providing the path to the current mountpoint 57 | of a different boot environment via the 58 | .Ev DESTDIR 59 | environment variable, or the 60 | .Fl d 61 | parameter. 62 | .Pp 63 | The 64 | .Sx BOOTSTRAPPING 65 | section of 66 | .Xr gpart 8 67 | provides a good overview of the boot process. 68 | .Ss Arguments 69 | The following argument types can be given: 70 | .Bl -tag -with indent 71 | .It Ar destdir 72 | The path of the mountpoint for the boot environment containing the 73 | kernel and boot loader images. The default boot environment is 74 | .Pa / . 75 | .It Ar efifile 76 | Formatting string for the 77 | .Nm EFI 78 | file destination, defaults to: 79 | .Dl Dq /efi/{ostype}/boot{arch}.efi 80 | .Pp 81 | The following substitutions are available: 82 | .Bl -tag -offset indent -width 12m -compact 83 | .It Brq Ar arch 84 | The kernel architecture 85 | .It Brq Ar efiarch 86 | The architecture as used by 87 | .Nm EFI 88 | default paths (e.g. X64) 89 | .It Brq Ar ostype 90 | The OS type (FreeBSD or derived) 91 | .El 92 | .Pp 93 | Supports the formatting style described by 94 | .Xr bprintf 1 . 95 | .It Ar efilabel 96 | Formatting string for 97 | .Nm EFI 98 | boot manager entry labels, defaults to: 99 | .Dl Dq {version} {arch} [{pdev}] 100 | .Pp 101 | The following substitutions are available: 102 | .Bl -tag -offset indent -width 12m -compact 103 | .It Brq Ar dev 104 | The device containing the partition 105 | .It Brq Ar pdev 106 | The device of the efi partition 107 | .It Brq Ar index 108 | The index of the efi partition 109 | .It Brq Ar version 110 | The kernel version 111 | .It Brq Ar arch 112 | The kernel architecture 113 | .It Brq Ar efiarch 114 | The architecture as used by 115 | .Nm EFI 116 | default paths (e.g. X64) 117 | .It Brq Ar ostype 118 | The OS type (FreeBSD or derived) 119 | .El 120 | .Pp 121 | Supports the formatting style described by 122 | .Xr bprintf 1 . 123 | .It Ar bootloader 124 | The boot loader image to use for 125 | .Nm freebsd-boot 126 | partitions. The default is either 127 | .Pa /boot/gptboot 128 | or 129 | .Pa /boot/gptzfsboot , 130 | depending on the file system containing the boot environment. 131 | .It Ar efiloader 132 | The boot loader image to use for 133 | .Nm EFI 134 | partitions, the default is 135 | .Pa /boot/loader.efi , 136 | the 137 | .Nm EFI 138 | variant of 139 | .Xr loader 8 . 140 | .It Ar pmbr 141 | The protective MBR image installed in front of the GUID Partition 142 | Table. The protective MBR chain-boots the 143 | .Nm freebsd-boot 144 | partition on systems booting in legacy BIOS mode. This is only installed 145 | if a 146 | .Nm freebsd-boot 147 | partition is present. The default protective MBR image is 148 | .Pa /boot/pmbr . 149 | .It Ar device 150 | The hard disk device to update. 151 | .El 152 | .Ss Options 153 | The following options are available: 154 | .Bl -tag -width indent 155 | .It Fl a , -all 156 | Selects all available devices. 157 | .It Fl b Ar bootloader , Fl -bootloader Ar bootloader 158 | Override the default 159 | .Nm freebsd-boot 160 | loader image. 161 | .It Fl c , -compat 162 | Override the 163 | .Nm EFI 164 | loader destination inside the 165 | .Nm EFI 166 | partition with the 167 | .Xr uefi 8 168 | default location 169 | .Pa /EFI/BOOT/BOOT{efiarch}.EFI . 170 | .Pp 171 | This provides compatibility with systems that ignore or forget boot 172 | entries made with 173 | .Xr efibootmgr 8 , 174 | such as virtual machines that do not persist the 175 | .Nm EFI 176 | boot configuration. 177 | .Pp 178 | .Sy Warning , 179 | not all 180 | .Xr uefi 8 181 | implementations support these default locations, use 182 | .Fl o 183 | to supply a proprietary location. 184 | .It Fl D , -dry-run 185 | Instead of updating the boot loaders just show the commands that 186 | would be run. In combination with 187 | .Fl q 188 | this does not print anything apart from errors, which is useful to 189 | run all checks performed by 190 | .Nm 191 | without committing any changes. 192 | .It Fl d Ar destdir , Fl -destdir Ar destdir 193 | Override the default boot environment. 194 | .It Fl e Ar efiloader , Fl -efiloader Ar efiloader 195 | Override the default 196 | .Nm EFI 197 | loader image. 198 | .It Fl L Ar efilabel , Fl -label Ar efilabel 199 | Set a custom label for 200 | .Nm EFI 201 | boot manager entries. 202 | .It Fl n , -noefi 203 | Do not create 204 | .Nm EFI 205 | boot manager entries via the 206 | .Xr efibootmgr 8 207 | command. This is useful when preparing a disk for another system. 208 | .It Fl o Ar efifile , Fl -efifile Ar efifile 209 | Override the 210 | .Nm EFI 211 | loader destination inside the 212 | .Nm EFI 213 | partition with 214 | .Ar efifile . 215 | .Pp 216 | This can be used on systems that neither persist the 217 | .Nm EFI 218 | boot configuration nor support the 219 | .Xr uefi 8 220 | default locations available via 221 | .Fl c . 222 | .It Fl P , -dump 223 | Instead of performing boot loader updates print a summary of the boot 224 | environment and selected devices. 225 | .It Fl p Ar pmbr , Fl -pmbr Ar pmbr 226 | Override the default protective MBR image. 227 | .It Fl q , -quiet 228 | Do not print the commands that are run. This flag does not suppress 229 | the output from those commands. 230 | .El 231 | .Sh ENVIRONMENT 232 | .Bl -tag -with indent 233 | .It Ev DESTDIR 234 | Overrides the default boot environment. 235 | .El 236 | .Sh FILES 237 | .Bl -tag -with indent 238 | .It Pa /boot/kernel/kernel 239 | Used to determine the version, architecture and OS to boot by extracting 240 | the 241 | .Va version , machine 242 | and 243 | .Va ostype 244 | symbols from the kernel binary. 245 | .It Pa /boot/gptboot 246 | The 247 | .Nm freebsd-boot 248 | partition image for booting from 249 | .Nm UFS , 250 | see 251 | .Xr gptboot 8 . 252 | .It Pa /boot/gptzfsboot 253 | The 254 | .Nm freebsd-boot 255 | partition image for booting from 256 | .Nm ZFS , 257 | see 258 | .Xr gptboot 8 . 259 | .It Pa /boot/loader.efi 260 | The default 261 | .Nm EFI 262 | variant of 263 | .Xr loader 8 . 264 | Capable of booting from 265 | .Nm UFS 266 | and 267 | .Nm ZFS , 268 | alternatives include 269 | .Pa /boot/loader_4th.efi , /boot/loader_lua.efi 270 | and 271 | .Pa /boot/loader_simp.efi . 272 | .It Pa /boot/pmbr 273 | The default protective MBR image. 274 | .El 275 | .Sh EXIT STATUS 276 | The following is a list of all anticipated exit codes: 277 | .Bl -tag -with indent 278 | .It Er EOK=0 279 | Command completed successfully. 280 | .It Er ESIGNAL=1 281 | Interrupted by signal. 282 | .It Er EFAIL=2 283 | Generic application logic error. 284 | .It Er EPARAM=3 285 | Invalid or conflicting arguments were supplied. 286 | .It Er ENODEVICE=4 287 | No or inaccessible devices selected. 288 | .It Er EDESTDIR=5 289 | The 290 | .Ar destdir 291 | is not a directory. 292 | .It Er ENOKERNEL=6 293 | Cannot access kernel in 294 | .Ar destdir . 295 | .It Er EEFILABEL=7 296 | Corrupt label formatting, see 297 | .Ar efilabel 298 | in the 299 | .Sx Arguments 300 | section. 301 | .It Er ESCHEME=8 302 | None or unsupported partitioning scheme detected in device. 303 | .It Er ENOPARTS=9 304 | Neither a 305 | .Nm freebsd-boot 306 | nor an 307 | .Nm EFI 308 | boot partition was found on a selected device. 309 | .It Er EEFIBOOTMGR=10 310 | Failed to run 311 | .Xr efibootmgr 8 . 312 | .It Er ELOADER=11 313 | Cannot read a required loader image. 314 | .It Er EMOUNT=12 315 | Failed to mount the 316 | .Nm EFI 317 | boot partition. 318 | .It Er ECMD=13 319 | Failed to execute a command during the update procedure. 320 | .It Er EEFIFILE=13 321 | Invalid or conflicting 322 | .Nm EFI 323 | file destinations. 324 | .El 325 | .Sh EXAMPLES 326 | Run 327 | .Xr gpart 8 328 | for a list of devices: 329 | .Dl gpart show 330 | .Pp 331 | Inspect the boot environment and the desired device: 332 | .Dl loaderupdate -P nvd0 333 | .Pp 334 | Review the commands to execute: 335 | .Dl loaderupdate -D nvd0 336 | .Pp 337 | Finally update the loaders for the device: 338 | .Dl loaderupdate nvd0 339 | .Sh SEE ALSO 340 | .Xr bprintf 1 , 341 | .Xr efibootmgr 8 , 342 | .Xr gpart 8 , 343 | .Xr gptboot 8 , 344 | .Xr gptzfsboot 8 , 345 | .Xr loader 8 , 346 | .Xr uefi 8 347 | .Sh HISTORY 348 | The 349 | .Nm 350 | command was added with the 351 | .Sy bsda2-0.4.0 352 | release. 353 | .Sh AUTHORS 354 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 355 | .Sh CAVEATS 356 | Only supports GUID Partition Table formatted devices. 357 | -------------------------------------------------------------------------------- /man/makeplist.8: -------------------------------------------------------------------------------- 1 | .Dd 23 February, 2021 2 | .Dt MAKEPLIST 8 3 | .Os 4 | .Sh NAME 5 | .Nm makeplist 6 | .Nd generate a 7 | .Li pkg-plist 8 | file for 9 | .Xr ports 7 10 | .Sh SYNOPSIS 11 | .Nm 12 | .Op Fl lOq 13 | .Op Fl I Ar make-vars 14 | .Op Fl o Ar plist-file 15 | .Op Ar port-name 16 | .Nm 17 | .Fl h 18 | .Sh DESCRIPTION 19 | The 20 | .Nm 21 | script is a tool for 22 | .Xr ports 7 23 | maintainers and committers, to generate a 24 | .Pa pkg-plist 25 | file. 26 | .Ss Arguments 27 | The following arguments are accepted: 28 | .Bl -tag -width indent 29 | .It Ar make-vars 30 | A comma separated list of make variables. The following variables 31 | may be specified: 32 | .Pp 33 | .Bl -bullet -compact 34 | .It 35 | .Li DESKTOP_ENTRIES 36 | .It 37 | .Li PLIST_FILES 38 | .It 39 | .Li PLIST_SUB 40 | .It 41 | .Li PORTDATA 42 | .It 43 | .Li PORTDOCS 44 | .It 45 | .Li PORTEXAMPLES 46 | .It 47 | .Li USE_RC_SUBR 48 | .El 49 | .It Ar plist-file 50 | The output file for the generated plist. Defaults to the ports 51 | .Pa pkg-plist 52 | with a 53 | .Pa .makeplist 54 | suffix appended. 55 | .It Ar port-name 56 | The name or origin of the port to generate a pkg-plist for. This may 57 | also be the directory the port can be found in. 58 | .Pp 59 | If omitted the current directory is used. 60 | .El 61 | .Ss Options 62 | The following options are support: 63 | .Bl -tag -width indent 64 | .It Fl h , -help 65 | Displays the available options. 66 | .It Fl I Ar make-vars , Fl -ignore-vars Ar make-vars 67 | Set the make variables to ignore when postprocessing the generated plist. 68 | .It Fl l , -licenses 69 | Usually 70 | .Nm 71 | runs with with the 72 | .Xr ports 7 73 | license auditing framework disabled. This option turns it on. 74 | .Pp 75 | This flag cannot be combined with 76 | .Fl q . 77 | .It Fl O , -orig 78 | Includes files with the .orig suffix in the plist. 79 | .It Fl o Ar plist-file , Fl -outfile Ar plist-file 80 | Sets the output file for the generated plist. 81 | .It Fl q , -quiet 82 | Suppress build/stage output. 83 | .Pp 84 | This flag cannot be combined with 85 | .Fl l . 86 | .El 87 | .Sh IMPLEMENTATION NOTES 88 | In order to make effective use of 89 | .Nm 90 | some knowledge about it is required. 91 | .Ss Limitations 92 | The exponential growth of possible combinations of options puts a 93 | hard limit on automated plist generation. The 94 | .Nm 95 | script does not support cases where files are only installed if a 96 | combination of options is given. 97 | .Pp 98 | The tool is built around the assumption that options do not affect 99 | each other. The following subsections describe what exactly is supported 100 | and workarounds for common cases where this is not the case. 101 | .Ss Configurations 102 | The core idea of 103 | .Nm 104 | is to run 105 | .Dq Li make stage 106 | and create a list of the files in the staging area. This functionality 107 | is provided by the 108 | .Xr ports 7 109 | when running 110 | .Dq Li make makeplist . 111 | .Pp 112 | In order to identify all the files installed by each option staging 113 | is performed for every option. The trivial approach of staging with 114 | one option at a time is not possible when 115 | .Li OPTIONS_SINGLE 116 | or 117 | .Li OPTIONS_MULTI 118 | groups are defined, because every such group must be represented in 119 | all configurations. Even so under the assumption that options do not 120 | affect each other n + 1 stage cycles suffice to identify all files 121 | provided by all options (n being the number of options). 122 | .Ss Staging 123 | Usually 124 | .Nm 125 | calls 126 | .Dq Li make clean stage 127 | for each configuration. One exception to this rule is when ports define 128 | .Li NO_BUILD 129 | in which case 130 | .Dq Li make restage 131 | is called. This means that the 132 | .Li extract 133 | target is only performed during the first stage cycle. And it breaks 134 | if extraction is affected by options. 135 | .Ss Build Failure 136 | In case one or more configurations fail an error message with the 137 | build options and the name of the log file will be printed after 138 | the plist is created. Only logs of failed builds are kept. The logs 139 | are created under 140 | .Pa /tmp 141 | and compressed using 142 | .Xr gzip 1 . 143 | They can be viewed with the command 144 | .Dq Li gunzip -c Ar logfile Li | less -R . 145 | .Ss Plist Accumulation 146 | After every stage cycle all files installed into the 147 | .Li STAGEDIR 148 | are collected for later assembly of the plist. 149 | .Pp 150 | Certain files are not included, these files are selected using the 151 | following 152 | .Xr ports 7 153 | variables: 154 | .Bl -tag -width indent 155 | .It Li DESKTOP_ENTRIES 156 | Macro for creating/installing desktop icons. 157 | .It Li USE_RC_SUBR 158 | Macro for creating/installing 159 | .Xr rc 8 160 | scripts. 161 | .It Li PLIST_FILES 162 | A list of files automatically added to the plist. 163 | .It Li PORTDOCS , PORTEXAMPLES , PORTDATA 164 | Lists of files / glob patterns in 165 | .Li DOCSDIR , EXAMPLESDIR 166 | and 167 | .Li DATADIR . 168 | .El 169 | .Pp 170 | This can be used to deal with cases that violate the independent 171 | option principle. E.g. if there is a number of options to switch 172 | certain modules on/off and a 173 | .Li DOCS 174 | option which causes each of these modules to generate some documentation, 175 | setting 176 | .Li PORTDOCS=* 177 | keeps everything in 178 | .Li DOCSDIR 179 | out of the plist. 180 | .Ss Plist Creation 181 | Files in the generated plist are sorted alphabetically by 182 | .Xr sort 1 Fl n . 183 | .Pp 184 | Common files installed independent of the given options are listed 185 | first, followed by the option specific files. Option specific files 186 | appear in alphabetical order of the options. The options 187 | .Li DOCS 188 | and 189 | .Li EXAMPLES 190 | are implicitly replaced with 191 | .Li PORTDOCS 192 | and 193 | .Li PORTEXAMPLES . 194 | Files with a 195 | .Pa .sample 196 | suffix are automatically prefixed with 197 | .Dq Li @sample\ . 198 | .Pp 199 | In the next stage of plist creation the reverse of the substitutions 200 | defined in 201 | .Li PLIST_SUB 202 | is applied. The substitutions are sorted by size and applied largest 203 | first. Empty substitutions, 204 | .Li PREFIX 205 | substitutions and substitutions starting with an 206 | .Li @ 207 | character are discarded. Lines starting with 208 | .Li %%\&DOCSDIR%% 209 | are prefixed with 210 | .Li %%\&PORTDOCS%% 211 | and 212 | .Li %%\&EXAMPLESDIR%% 213 | with 214 | .Li %%\&PORTEXAMPLES%% . 215 | .Pp 216 | In the final stage of plist creation 217 | .Nm 218 | attempts to transplant the 219 | .Li @ 220 | keywords (see 221 | .Xr pkg-create 8 ) 222 | from the old plist to the new one. The script makes an effort to 223 | place them in the same context. The prefix 224 | .Dq Li @sample\ 225 | is stripped from all files that appear in the old plist without it. 226 | .Sh EXAMPLES 227 | The current directory is a port to create a pkg-plist for: 228 | .Pp 229 | .Dl % makeplist 230 | .Pp 231 | The quiet mode suppresses the build output: 232 | .Pp 233 | .Dl % makeplist -q 234 | .Pp 235 | Instead of creating the file 236 | .Pa pkg-plist.makeplist 237 | overwrite the 238 | .Pa pkg-plist 239 | file: 240 | .Pp 241 | .Dl % makeplist -o pkg-plist 242 | .Pp 243 | To create a pkg-plist as an unprivileged user run: 244 | .Pp 245 | .Dl % env WRKDIRPREFIX='/var/tmp/obj.${USER}' makeplist -o ~/myplist 246 | .Pp 247 | The 248 | .Li WRKDIRPREFIX 249 | can be set in the 250 | .Xr make.conf 5 file: 251 | .Pp 252 | .Dl WRKDIRPREFIX?=/var/tmp/obj.${USER} 253 | .Pp 254 | There is no need to 255 | .Li cd 256 | into a ports directory: 257 | .Pp 258 | .Dl % makeplist games/ioquake3-devel 259 | .Pp 260 | The ports tree does not have to be in 261 | .Pa /usr/ports : 262 | .Pp 263 | .Dl % env PORTSDIR=$HOME/ports.svn makeplist games/ioquake3-devel 264 | .Pp 265 | Or just use 266 | .Li cd : 267 | .Pp 268 | .Dl % cd ~/ports.svn/games/ioquake3-devel && makeplist 269 | .Pp 270 | A port listing all its files in 271 | .Li PLIST_FILES 272 | results in an empty plist. 273 | .Li PLIST_FILES 274 | can be ignored: 275 | .Pp 276 | .Dl % makeplist -I PLIST_FILES 277 | .Sh SEE ALSO 278 | .Xr ports 7 , Xr make 1 , Xr pkg-greate 8 279 | .Sh HISTORY 280 | The 281 | .Nm 282 | script first appeared in the bsda2-0.2.0 release. 283 | .Sh AUTHORS 284 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 285 | -------------------------------------------------------------------------------- /man/pkg_libchk.8: -------------------------------------------------------------------------------- 1 | .Dd 29 July, 2024 2 | .Dt PKG_LIBCHK 8 3 | .Os 4 | .Sh NAME 5 | .Nm pkg_libchk 6 | .Nd check packages for links against missing libraries 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl cmnoqv 10 | .Op Fl j Ar jobs 11 | .Op Fl a 12 | .Nm 13 | .Op Fl CcdgimnOoqrvx 14 | .Op Fl j Ar jobs 15 | .Ar pkg-name ... 16 | .Nm 17 | .Fl h 18 | .Sh DESCRIPTION 19 | The 20 | .Nm 21 | script uses 22 | .Xr pkg-info 8 , 23 | .Xr ldd 1 24 | and 25 | .Xr readelf 1 26 | to check whether a package is linked against missing libraries or 27 | using compatibility libraries 28 | .Pq matching Pa */lib*/compat/* . 29 | .Ss Arguments 30 | The following arguments are accepted: 31 | .Bl -tag -width indent 32 | .It Ar jobs 33 | The number of parallel process to perform library checks. 34 | The default is number of detected cores plus one. 35 | .It Ar pkg-name 36 | Packages are package names or shell glob patterns matching these. Whatever 37 | works with 38 | .Xr pkg-info 8 39 | is an acceptable package. 40 | .El 41 | .Ss Options 42 | The following options are available: 43 | .Bl -tag -width indent 44 | .It Fl a , -all 45 | Check all packages. This is the default action in case no 46 | .Ar pkg-name 47 | has been specified. 48 | .It Fl C , -case-sensitive 49 | See 50 | .Xr pkg-info 8 . 51 | .It Fl c , -clean 52 | Create clean output without status messages. 53 | .It Fl d , -dependencies 54 | Also operate on the dependencies of the provided 55 | .Ar pkg-name . 56 | .It Fl g , -glob 57 | See 58 | .Xr pkg-info 8 . 59 | .It Fl h , -help 60 | Displays the available options. 61 | .It Fl i , -case-insensitive 62 | See 63 | .Xr pkg-info 8 . 64 | .It Fl j Ar jobs , Fl -jobs Ar jobs 65 | Set the number of parallel processes to perform library checks. 66 | .It Fl m , -no-filter 67 | In this mode all the checks to detect false positives produced by 68 | .Xr ldd 1 69 | are deactivated. 70 | .Pp 71 | .Bl -bullet -compact 72 | .It 73 | Indirect dependencies are no longer recognised as such. 74 | .It 75 | Dependencies that are part of a package even though 76 | .Xr ldd 1 77 | does not find them are no longer discarded - finding these dependencies 78 | usually just requires some runtime information ldd is not privy to. 79 | .It 80 | Dependencies of unbranded 81 | .Xr elf 3 82 | binaries are no longer distinguished from dependencies of native binaries. 83 | .El 84 | .It Fl n , -no-compat 85 | This deactivates detecting compatibility libraries. 86 | .It Fl O , -by-origin 87 | See 88 | .Xr pkg-info 8 . 89 | .It Fl o , -origin 90 | Instead of the package name and version the package origin and flavour 91 | is printed. 92 | .It Fl q , -quiet 93 | Only print the names of affected packages. Do not print any details. This 94 | option is meant for machine readability. 95 | .Pp 96 | This option cannot be combined with verbose output. 97 | .It Fl r , -required-by 98 | Also check packages that depend on the provided 99 | .Ar packages . 100 | This is a good idea when checking for the effects of a library update. 101 | .It Fl v , -verbose 102 | Be verbose about missing dependencies. Instead of rejecting indirect 103 | dependencies print them. 104 | .Pp 105 | This option cannot be combined with quiet output. 106 | .It Fl x , -regex 107 | See 108 | .Xr pkg-info 8 . 109 | .El 110 | .Sh EXAMPLES 111 | To check all your packages run: 112 | .Bd -literal -offset indent 113 | pkg_libchk 114 | .Ed 115 | .Pp 116 | To create plain output for everything connected to gtk. 117 | .Bd -literal -offset indent 118 | pkg_libchk -q \\*gtk\\* 119 | .Ed 120 | .Pp 121 | After upgrading a library, in this case icu, you can check all depending 122 | packages: 123 | .Bd -literal -offset indent 124 | pkg_libchk -r icu 125 | .Ed 126 | .Sh EXIT CODES 127 | .Bl -tag -width indent 128 | .It 1 129 | The script has terminated because it received SIGHUP, SIGINT or SIGTERM. 130 | .It 2 131 | An unknown parameter has been supplied. 132 | .It 3 133 | The incompatible parameters 134 | .Fl v 135 | and 136 | .Fl q 137 | have been supplied. 138 | .It 4 139 | The parameter 140 | .Fl j 141 | has been supplied without an acceptable number. 142 | .El 143 | .Sh SEE ALSO 144 | .Xr pkg-info 8 , 145 | .Xr ldd 1 , 146 | .Xr readelf 1 , 147 | .Xr hier 7 148 | .Sh HISTORY 149 | The 150 | .Nm 151 | script first appeared in the 152 | .Sy bsdadminscripts-4.0 153 | collection. It was rewritten 154 | for the 155 | .Sy bsda2 156 | collection. 157 | .Sh AUTHORS 158 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 159 | -------------------------------------------------------------------------------- /man/pkg_trim.8: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt PKG_TRIM 8 3 | .Os 4 | .Sh NAME 5 | .Nm pkg_trim 6 | .Nd select and remove unwanted leaf packages 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl oy 10 | .Nm 11 | .Fl h 12 | .Sh DESCRIPTION 13 | The 14 | .Nm 15 | script performs dialog driven selection of leaf packages. After completing 16 | selection, the user is asked to either update the autoremove flags 17 | of the selected packages or delete them immediately. 18 | .Ss Options 19 | The following options are supported: 20 | .Bl -tag -width indent 21 | .It Fl h , -help 22 | Displays the available options. 23 | .It Fl o , -origin 24 | List package origins instead of names. 25 | .It Fl y , -yes 26 | Assume yes when calling 27 | .Xr pkg-set 8 28 | or 29 | .Xr pkg-delete 8 . 30 | .El 31 | .Sh DIAGNOSTICS 32 | During package selection the script may exit with the exit status 33 | returned by 34 | .Xr dialog 1 . 35 | During package processing the script may exit with the exit status 36 | returned by 37 | .Xr pkg-set 8 38 | or 39 | .Xr pkg-delete 8 . 40 | .Sh SEE ALSO 41 | .Xr dialog 1 , Xr pkg-query 8 , Xr pkg-set 8 , Xr pkg-delete 8 42 | .Sh HISTORY 43 | The 44 | .Nm 45 | script first appeared in bsda2-0.1. 46 | .Sh AUTHORS 47 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 48 | -------------------------------------------------------------------------------- /man/pkg_validate.8: -------------------------------------------------------------------------------- 1 | .Dd 15 November, 2023 2 | .Dt PKG_VALIDATE 8 3 | .Os 4 | .Sh NAME 5 | .Nm pkg_validate 6 | .Nd list mismatched and missing files of installed packages 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl cDmoqv 10 | .Op Fl b Ar batchsize 11 | .Op Fl j Ar jobs 12 | .Op Fl a 13 | .Nm 14 | .Op Fl CcdDgimOoqrvx 15 | .Op Fl b Ar batchsize 16 | .Op Fl j Ar jobs 17 | .Ar pkg-name ... 18 | .Nm 19 | .Fl h 20 | .Sh DESCRIPTION 21 | The 22 | .Nm 23 | script provides the same functionality as running 24 | .Ql Cm pkg check Fl s . 25 | It can be run by an unprivileged user and its use of multiple processes 26 | can make it significantly faster when files are cached in memory or 27 | read from a fast solid state drive. 28 | .Ss Arguments 29 | The following arguments are accepted: 30 | .Bl -tag -width indent 31 | .It Ar batchsize 32 | The number of files to verify in a single chunk. This controls the 33 | granularity of parallel job execution, the default is 1024. 34 | .It Ar jobs 35 | The number of parallel processes to perform file validation. 36 | The default is number of detected cores plus one. 37 | .It Ar pkg-name 38 | Packages are package names or shell glob patterns matching these. Whatever 39 | works with 40 | .Xr pkg-info 8 41 | is an acceptable package. 42 | .El 43 | .Ss Options 44 | The following options are available: 45 | .Bl -tag -width indent 46 | .It Fl a , -all 47 | Check all packages. This is the default action in case no 48 | .Ar pkg-name 49 | has been specified. 50 | .It Fl b Ar batchsize , Fl -batch Ar batchsize 51 | Set the number of files to verify in a single chunk. 52 | .It Fl C , -case-sensitive 53 | See 54 | .Xr pkg-info 8 . 55 | .It Fl c , -clean 56 | Create clean output without status messages. 57 | .It Fl d , -dependencies 58 | Also operate on the dependencies of the provided 59 | .Ar pkg-name . 60 | .It Fl D , -developer 61 | Produce additional output relevant to 62 | .Xr ports 7 and Xr pkg 8 developers. 63 | .Pp 64 | Currently it provides information about dead symlinks. 65 | .It Fl g , -glob 66 | See 67 | .Xr pkg-info 8 . 68 | .It Fl h , -help 69 | Displays the available options. 70 | .It Fl i , -case-insensitive 71 | See 72 | .Xr pkg-info 8 . 73 | .It Fl j Ar jobs , Fl -jobs Ar jobs 74 | Specify the amount of parallel 75 | .Ar jobs 76 | the script will attempt run. 77 | .It Fl m , -no-filter 78 | In this mode files that cannot be checked due to user permissions 79 | are listed along with checksum mismatches and missing files. 80 | .It Fl O , -by-origin 81 | See 82 | .Xr pkg-info 8 . 83 | .It Fl o , -origin 84 | Instead of the package name the package origin is printed. 85 | .It Fl q , -quiet 86 | Only print the names of affected packages. Do not print any details. This 87 | option is meant for machine readability. 88 | .Pp 89 | This option cannot be combined with verbose output. 90 | .It Fl r , -required-by 91 | Also check packages that depend on the provided 92 | .Ar packages . 93 | This is a good idea when checking for the effects of a library update. 94 | .It Fl v , -verbose 95 | List the selected packages before checking them. 96 | .It Fl x , -regex 97 | See 98 | .Xr pkg-info 8 . 99 | .El 100 | .Sh EXAMPLES 101 | To check all your packages run: 102 | .Bd -literal -offset indent 103 | pkg_validate 104 | .Ed 105 | .Pp 106 | List files that cannot be checked due to missing user privileges: 107 | .Bd -literal -offset indent 108 | pkg_validate -m 109 | .Ed 110 | .Pp 111 | Produce output suitable as input to other 112 | .Xr pkg 8 113 | tools: 114 | .Bd -literal -offset indent 115 | pkg_validate -q 116 | .Ed 117 | .Pp 118 | For operating 119 | .Xr poudriere 8 120 | or 121 | .Xr portmaster 8 122 | it can be more useful to output package origins instead of names: 123 | .Bd -literal -offset indent 124 | pkg_validate -o 125 | .Ed 126 | .Pp 127 | Flags can be combined: 128 | .Bd -literal -offset indent 129 | pkg_validate -qo 130 | .Ed 131 | .Sh ENVIRONMENT 132 | The following environment variables affect the operation of 133 | .Nm . 134 | .Bl -tag -width indent 135 | .It Ev DEVELOPER 136 | Activates additional diagnostics, see the 137 | .Fl -developer 138 | option. 139 | .Pp 140 | Set to 141 | .Dv Sq yes 142 | or 143 | .Dv 1 144 | to enable. This is equivalent to the 145 | .Ev DEVELOPER 146 | macro of 147 | .In bsd.port.mk . 148 | .El 149 | .Sh EXIT STATUS 150 | .Bl -tag -width indent 151 | .It 1 152 | The script has terminated because it received SIGHUP, SIGINT or SIGTERM. 153 | .It 2 154 | An unknown parameter has been supplied. 155 | .It 3 156 | The incompatible parameters 157 | .Fl v 158 | and 159 | .Fl q 160 | have been supplied. 161 | .It 4 162 | The parameter 163 | .Fl j 164 | has been supplied without an acceptable number. 165 | .El 166 | .Sh SEE ALSO 167 | .Xr pkg-check 8 , 168 | .Xr pkg-query 8 , 169 | .Xr pkg-info 8 , 170 | .Xr sha256 1 , 171 | .Xr readlink 1 172 | .Sh HISTORY 173 | A 174 | .Nm 175 | script first appeared in the 176 | .Sy bsdadminscripts-3.0 177 | collection. It was 178 | deemed obsolete with the inception of 179 | .Sy bsda2 180 | and rewritten for 181 | .Sy bsda2-0.3.0 . 182 | .Sh AUTHORS 183 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 184 | -------------------------------------------------------------------------------- /man/pkg_version.8: -------------------------------------------------------------------------------- 1 | .Dd 18 November, 2023 2 | .Dt PKG_VERSION 8 3 | .Os 4 | .Sh NAME 5 | .Nm pkg_version 6 | .Nd summarise installed versions of packages 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl coqUv 10 | .Op Fl j Ar jobs 11 | .Op Fl l Ar limchar 12 | .Op Fl L Ar limchar 13 | .Op Fl a | Oo Fl CdgiOrx Oc Ar pkg-name ... 14 | .Nm 15 | .Fl P 16 | .Op Fl coqv 17 | .Op Fl j Ar jobs 18 | .Op Fl l Ar limchar 19 | .Op Fl L Ar limchar 20 | .Op Fl a | Oo Fl CdgiOrx Oc Ar pkg-name ... 21 | .Nm 22 | .Fl I Op Ar index 23 | .Op Fl coqv 24 | .Op Fl j Ar jobs 25 | .Op Fl l Ar limchar 26 | .Op Fl L Ar limchar 27 | .Op Fl a | Oo Fl CdgiOrx Oc Ar pkg-name ... 28 | .Nm 29 | .Fl R 30 | .Op Fl coqUv 31 | .Op Fl j Ar jobs 32 | .Op Fl l Ar limchar 33 | .Op Fl L Ar limchar 34 | .Op Fl a | Oo Fl CdgiOrx Oc Ar pkg-name ... 35 | .Nm 36 | .Fl h 37 | .Sh Description 38 | The 39 | .Nm 40 | script is a 41 | .Xr pkg-version 8 42 | wrapper capable of greatly improving the performance of using the 43 | .Xr ports 7 44 | tree as the version source by querying version numbers in parallel. 45 | .Pp 46 | For most of the behaviour 47 | .Xr pkg-version 8 48 | can be consulted. Noteworthy differences are: 49 | .Pp 50 | .Bl -bullet -compact 51 | .It 52 | Multiple package arguments can be provided 53 | .It 54 | Origins 55 | .Po if 56 | .Fl o 57 | is used 58 | .Pc 59 | contain the flavour of the installed package 60 | .It 61 | If 62 | .Fl l 63 | .Ar limchar 64 | and 65 | .Fl q 66 | are given, only package identifiers are output 67 | .It 68 | The 69 | .Fl t 70 | and 71 | .Fl T 72 | arguments are not supported 73 | .El 74 | .Ss Arguments 75 | The following arguments are accepted: 76 | .Bl -tag -width indent 77 | .It Ar index 78 | The index file to compare installed package versions to. Defaults to 79 | .Pa /usr/ports/INDEX-N 80 | with N being the major 81 | .Fx 82 | version number. See 83 | .Xr pkg-version 8 . 84 | .It Ar jobs 85 | The number of processes to use in parallel to run 86 | .Xr pkg-version 8 87 | queries. 88 | .It Ar limchar 89 | A single character constraining which packages are output. 90 | .It Ar pkg-name 91 | Packages are package names or shell glob patterns matching these. Whatever 92 | works with 93 | .Xr pkg-info 8 94 | is an acceptable package. 95 | .El 96 | .Ss Options 97 | The following options are available: 98 | .Bl -tag -width indent 99 | .It Fl a , -all 100 | Check all packages. This is the default action in case no 101 | .Ar pkg-name 102 | has been specified. 103 | .It Fl C , -case-sensitive 104 | See 105 | .Xr pkg-info 8 . 106 | .It Fl c , -clean 107 | Create clean output without status messages. 108 | .It Fl d , -dependencies 109 | Also operate on the dependencies of the provided 110 | .Ar pkg-name . 111 | .It Fl g , -glob 112 | See 113 | .Xr pkg-info 8 . 114 | .It Fl h , -help 115 | Displays the available options. 116 | .It Fl i , -case-insensitive 117 | See 118 | .Xr pkg-info 8 . 119 | .It Fl I Oo Ar index Oc , Fl -index Op Ar index 120 | Use index file to determine if a package is out of date. See 121 | .Xr pkg-version 8 . 122 | .It Fl j Ar jobs , Fl -jobs Ar jobs 123 | Specify the amount of parallel 124 | .Ar jobs 125 | the script will attempt run. The default is number of detected cores. 126 | .It Fl l Ar limchar , Fl --like Ar limchar 127 | Display only packages with the given version status, see 128 | .Xr pkg-version 8 . 129 | .It Fl L Ar limchar , Fl --not-like Ar limchar 130 | Display only packages unlike the given version status, see 131 | .Xr pkg-version 8 . 132 | .It Fl O , -by-origin 133 | See 134 | .Xr pkg-info 8 . 135 | .It Fl o , -origin 136 | Instead of the package name and version the package origin and flavour 137 | is printed. 138 | .It Fl P , -ports 139 | Use 140 | .Xr ports 7 141 | to determine if a package is out of date. See 142 | .Xr pkg-version 8 . 143 | .It Fl q , -quiet 144 | Produce less output, see 145 | .Xr pkg-version 8 . 146 | In combination with 147 | .Fl l 148 | it suppresses printing 149 | .Ar limchar 150 | behind matched packages. 151 | .It Fl R , -remote 152 | Use repository catalogue to determine if a package is out of date. 153 | See 154 | .Xr pkg-version 8 . 155 | .It Fl r , -required-by 156 | Also check packages that depend on the provided packages. 157 | .It Fl U , -no-repo-update 158 | Disable automatic update of the repository catalogue. See 159 | .Xr pkg-version 8 . 160 | .It Fl v , -verbose 161 | Be verbose, see 162 | .Xr pkg-version 8 . 163 | Use twice to be extra verbose. 164 | .It Fl x , -regex 165 | See 166 | .Xr pkg-info 8 . 167 | .El 168 | .Sh EXAMPLES 169 | Check all packages whether they are out of date: 170 | .Bd -literal -offset indent 171 | pkg_version 172 | .Ed 173 | .Pp 174 | List all out of date GPU firmware packages, without any visual clutter: 175 | .Bd -literal -offset indent 176 | pkg_version -ql\\< gpu-firmware-\\* 177 | .Ed 178 | .Sh SEE ALSO 179 | .Xr pkg-info 8 , 180 | .Xr pkg-version 8 , 181 | .Xr ports 7 182 | .Sh HISTORY 183 | The 184 | .Nm 185 | script first appeared in the 186 | .Sy bsda2-0.5.0 187 | collection. 188 | .Sh AUTHORS 189 | .An Dominic Fandrey Aq Mt freebsd@k4m1.org 190 | -------------------------------------------------------------------------------- /ref/bsda_err.md: -------------------------------------------------------------------------------- 1 | BSDA:ERR - Error Handling for BSDA:OBJ 2 | ====================================== 3 | 4 | `bsda:err` is an error handling library written for use with the 5 | [bsda:obj](bsda_obj.md) framework. 6 | 7 | The workflow of `bsda:err` is very similar to the try/throw/catch 8 | exceptions workflow known from other languages. However, some fundamental 9 | differences exist, thus all facilities have been named differently 10 | in order to curb expectations of conforming to the familiar model. 11 | 12 | The following table lists the well known language mechanisms and 13 | their library counterparts: 14 | 15 | | Mechanism | Substitution | 16 | |-----------|---------------------------------------| 17 | | `try` | `bsda:err:collect` | 18 | | `throw` | `bsda:err:raise` / `bsda:err:forward` | 19 | | `catch` | `bsda:err:get` | 20 | 21 | Issues 22 | ------ 23 | 24 | This error handling framework is based around the concept of issues. 25 | An issue is a pair consisting of an error/exit code and a message. 26 | The error/exit code may be an integral value, but it is usually 27 | a predefined symbol. The following error/exit codes are predefined: 28 | 29 | | Symbol | Value | Meaning | 30 | |----------|-------|----------------------------------------| 31 | | E_OK | 0 | No error | 32 | | E_WARN | 0 | Warning, non-fatal even when unhandled | 33 | | E_SIGNAL | 1 | Interrupted by signal | 34 | | E_FAIL | 2 | Application logic failure | 35 | 36 | The bsda:err:createECs() function can be used to create additional 37 | application specific error/exit codes. 38 | 39 | Reporting and Handling Errors/Warnings 40 | -------------------------------------- 41 | 42 | An issue can be created using the bsda:err:raise() function. The 43 | default behaviour is to print the given message and exit with the 44 | given error/exit code if it is non-zero. 45 | 46 | The caller can override this behaviour by calling the bsda:err:collect() 47 | function. This is the expected use case, the default behaviour is 48 | a fallback for the case that issues are not handled by the application. 49 | 50 | To force exit on an issue, even when the caller handles issues, 51 | the bsda:err:panic() function can be used. This is only appropriate 52 | for errors that indicate a flaw in the programming logic. 53 | 54 | If the caller opts in to collecting issues these must be handled 55 | before the caller scope ends. Otherwise all issue messages and a 56 | couple of hints and warnings are printed on stderr. 57 | 58 | Handling issues is done by calling bsda:err:get() until all issues 59 | have been handled: 60 | 61 | ```sh 62 | local e msg 63 | bsda:err:collect 64 | … code potentially raising issues … 65 | while bsda:err:get e msg; do 66 | … handle errors/warnings … 67 | done 68 | ``` 69 | 70 | Errors usually should be matched using the error/exit code: 71 | 72 | ```sh 73 | while bsda:err:get e msg; do 74 | case "$e" in 75 | E_APP_ERROR) 76 | … handle … 77 | ;; 78 | E_FAIL) 79 | # unrecoverable error 80 | bsda:err:panic "$e" "$msg" 81 | ;; 82 | *) 83 | # forward to caller 84 | bsda:err:forward "$e" "$msg" 85 | ;; 86 | esac 87 | done 88 | ``` 89 | 90 | The bsda:err:forward() function acts like bsda:err:raise(), except 91 | that it raises the issue in the parent context, passing on the 92 | responsibility of handling the issue. 93 | -------------------------------------------------------------------------------- /ref/lst.md: -------------------------------------------------------------------------------- 1 | __ __ __ 2 | / /\ ____ / /_ ____ / /\ 3 | / / / / __/\/ __/\ / __/\/ \/ 4 | / /_/ /_ /\/ /___/_ /_ /\/ / /\ 5 | \__/\/___/ /\__/\/_/\/___/ /_/_/ / 6 | \__/\____/ \__/ \_/\____/\____/ 7 | 8 | [ STANDALONE LIBRARY est. 2021 ] 9 | 10 | LST.SH - Portable Array Library for Shell-Scripts 11 | ================================================= 12 | 13 | LST.sh is a simple portable shell array library. It provides a way 14 | of treating a string as an array by splitting it using a Record Separator 15 | character. 16 | 17 | Arrays can be accessed by index: 18 | 19 | ```sh 20 | . ./lst.sh 21 | 22 | rec a= 'First entry' $'Second\nentry' 'Third entry$' 23 | 24 | rec a[1] # prints: First entry 25 | rec a[-1] # prints: Third entry$ 26 | rec a[2] entry 27 | test "${entry}" = $'Second\nentry' # succeeds 28 | ``` 29 | 30 | Entries can be appended or prepended: 31 | 32 | ```sh 33 | rec a.push_back 'Final entry' 34 | rec a.push_front 'Preliminary entry' 35 | ORS=\| rec a # prints: 36 | # Preliminary entry|First entry|Second 37 | # entry|Third entry$|Final entry 38 | ``` 39 | 40 | Entries can be iterated over by index: 41 | 42 | ```sh 43 | i=0 44 | while rec a[i+=1] entry; do 45 | printf '%2d: <%s>\n' $i "${entry}" 46 | done # prints: 47 | # 1: 48 | # 2: 49 | # 3: 51 | # 4: 52 | # 5: 53 | ``` 54 | 55 | Or by regular variable expansion: 56 | 57 | ```sh 58 | rec a.set_ifs 59 | for entry in $a; do 60 | echo "<${entry}>" 61 | done # prints: 62 | # 63 | # 64 | # 66 | # 67 | # 68 | ``` 69 | 70 | 71 | The array functionality is accessed by using the function `lst()` 72 | with the variable `RS` set to the value of the separator character. 73 | The first argument to `lst()` is always the name of an array followed 74 | by an operator. 75 | 76 | Providing the Record Separator `RS` to `lst()` 77 | ---------------------------------------------- 78 | 79 | There are three ways providing the Record Separator to the `lst()` 80 | function: 81 | 82 | 1. Setting it globally: 83 | `RS='|'` 84 | 2. Providing it constrained to a single invocation: 85 | `RS='|' lst ...` 86 | 3. Or defining a wrapper function that calls `lst()`: 87 | `array() { RS='|' lst "$@"; }` 88 | 89 | The first way has most merit when a single Record Separator is the 90 | correct choice for an entire shell script. The third method provides 91 | an opportunity to give more semantic meaning to calls of `lst()`. 92 | Three `lst()` wrappers are provided: 93 | 94 | - `log()` 95 | Uses the Line Feed character as an entry separator, this is subject 96 | to limitations, detailed in the next section. 97 | - `uni()` 98 | Uses the ASCII Unit Separator character (US) as a Record Separator 99 | (see `ascii(7)`). Other good candidates from this character group 100 | are the Record Separator (RS), Group Separator (GS) and 101 | the File Separator (FS). 102 | - `rec()` 103 | Uses the ASCII Record Separator character (RS) as a Record Separator. 104 | - `grp()` 105 | Uses the ASCII Group Separator character (GS) as a Record Separator. 106 | - `fil()` 107 | Uses the ASCII File Separator character (FS) as a Record Separator. 108 | - `csv()` 109 | Uses the comma character `,` to separate array entries. This is 110 | akin to using other characters like slashes, colons, semicolons 111 | or pipes when they happen to not be used as characters within the 112 | array entries. 113 | 114 | The `rec()` function is a sane default providing full functionality 115 | using a character that is unlikely to occur within non-binary data. 116 | 117 | ### White Space Record Separators 118 | 119 | Using White Space characters such as Space, the Line Feed or Horizontal 120 | Tab characters, affects the functionality of `lst()` in conjunction with 121 | empty array entries. 122 | This is due to some methods internally using Field Splitting, which 123 | is subject to special rules when the Input Field Separator is set 124 | to White Space character. See the White Space Splitting (Field Splitting) 125 | section of `sh(1)`. 126 | 127 | The Method/Function table column WS RS in the Operators and Methods 128 | section documents which methods/functions are affected by this. 129 | The source code documentation of the affected methods/functions provides 130 | details on the specific effects. 131 | 132 | If the use case does not make use of empty array entries, all functions 133 | and methods can be used without limitations. 134 | 135 | ### Array Conversions 136 | 137 | There are two functions to convert arrays from one Record Separator 138 | to another. The difference is in the delivery of the Input and Output 139 | Record Separators. 140 | 141 | The `lst:convert()` function allows providing arbitrary Separators: 142 | 143 | ```sh 144 | # converts `log src` to `rec dst` 145 | IRS=$'\n' ORS=$'\036' lst:convert src dst 146 | 147 | # converts `log a` to `rec a` 148 | IRS=$'\n' ORS=$'\036' lst:convert a a 149 | ``` 150 | 151 | The `lst:cast()` function uses `lst()` wrapper functions to determine 152 | the Record Separators: 153 | 154 | ```sh 155 | # converts `log src` to `rec dst` 156 | lst:cast log:src rec:dst 157 | 158 | # converts `log a` to `rec a` 159 | lst:cast log:a rec:a 160 | ``` 161 | 162 | Operators and Methods 163 | --------------------- 164 | 165 | The following operators are available (`a` is the name of an array, 166 | `i` is an index value / arithmetic expression, `m` is the name of 167 | a method): 168 | 169 | | Operator | Action | 170 | |----------|-----------------------------------------------------------------| 171 | | `a[i].m` | Call array subscript method | 172 | | `a[i]=` | Array subscript assign (equivalent to `a[i].set`) | 173 | | `a[i]` | Array subscript access (equivalent to `a[i].get`) | 174 | | `a.m` | Call array method | 175 | | `a=cat` | Create by concatenating arrays (equivalent to `lst:cat a`) | 176 | | `a=` | Array create/reset and assign values | 177 | | `a` | Print array (equivalent to `a.print`) | 178 | 179 | Supported methods and functions are listed in the table below. 180 | 181 | The Complexity column is based on the number of shell operations, 182 | so even operations like field splitting that clearly have a size 183 | dependent cost are considered constant size. The variable `n` refers 184 | to the size of the array, the variable `#` to the number of arguments 185 | provided to the function/method call. 186 | 187 | The WS RS column lists which functions/methods are subject to limitations 188 | if the Record Separator is a White Space character. 189 | 190 | | Method/Function | Description | Complexity | WS RS | 191 | |------------------|---------------------------------------------------------|------------|---------| 192 | | `a[i].get` | Read a single indexed entry | O(1) | Limited | 193 | | `a[i].set` | Overwrite a single indexed entry | O(n) | Limited | 194 | | `a[i].rm` | Remove an indexed array entry | O(n) | Limited | 195 | | `a.resize` | Change the size of the array | O(n) | Limited | 196 | | `a.push_front` | Prepend values | O(#) | | 197 | | `a.push_back` | Append values | O(#) | | 198 | | `a.peek_front` | Read first value | O(1) | | 199 | | `a.peek_back` | Read last value | O(1) | | 200 | | `a.pop_front` | Read first value and remove it | O(#) | | 201 | | `a.pop_back` | Read last value and remove it | O(#) | | 202 | | `a.rm_first` | Remove first match with the given value | O(#) | | 203 | | `a.rm_last` | Remove last match with the given value | O(#) | | 204 | | `a.map_front` | Map array entries to variables from front to back | O(#) | Limited | 205 | | `a.map_back` | Map array entries to variables from back to front | O(#) | Limited | 206 | | `a.count` | Provide the number of entries | O(1) | Limited | 207 | | `a.contains` | Return whether the given value is in the array | O(1) | | 208 | | `a.contains_all` | Return whether all the given values are in the array | O(#) | | 209 | | `a.contains_any` | Return whether any of the given values are in the array | O(#) | | 210 | | `a.is_defined` | Check whether the array is defined | O(1) | | 211 | | `a.is_undefined` | Check whether the array is undefined | O(1) | | 212 | | `a.is_empty` | Check whether the array is empty | O(1) | | 213 | | `a.is_not_empty` | Check whether the array is not empty | O(1) | | 214 | | `a.print` | Print array `ORS` separated | O(1) | Limited | 215 | | `a.printf` | Print array with custom formatting | O(1) | Limited | 216 | | `a.append` | Append the given array(s) | O(1) | | 217 | | `a.set_irs` | Set the Input Record Separator (`IRS`) to `RS` | O(1) | | 218 | | `a.set_ors` | Set the Output Record Separator (`ORS`) to `RS` | O(1) | | 219 | | `a.set_ifs` | Set the shell Input Field Separator (`IFS`) to `RS` | O(1) | | 220 | | `lst:cat` | Concatenate arrays | O(1) | | 221 | | `lst:convert` | Convert array Record Separator `IRS` to `ORS` | O(1) | Limited | 222 | | `lst:cast` | Convert array Record Separator based on lst() wrappers | O(1) | Limited | 223 | -------------------------------------------------------------------------------- /ref/type.md: -------------------------------------------------------------------------------- 1 | __ __ 2 | / /_______ ___ ____/ / 3 | / __/ / / \/ _ \ / __/ \ 4 | / /_/ / / / / ___/__/_ / / / 5 | \__/\ / __/\__/ /_/___/_/_/ 6 | ____/ /_/ 7 | \____/ STANDALONE LIBRARY est. 2020 8 | 9 | TYPE.SH - Simple Type Match/Cast for Shell-Scripts 10 | ================================================== 11 | 12 | This standalone library provides rudimentary type checking/casting 13 | functionality, entirely relying on shell builtins. 14 | 15 | Types 16 | ----- 17 | 18 | As far as the shell is concerned the only type is the character string. 19 | It can handle integer arithmetic and comparison, so there is a notion 20 | of integers. However even integers are stored in strings. 21 | 22 | The library adds a set of types with more constraints: 23 | 24 | | Type | Description | Matches (regex, case insensitive) | 25 | |----------|------------------|--------------------------------------------| 26 | | uint | unsigned integer | `^(0x[0-9a-f]+|[1-9][0-9]*|0[0-7]*)$` | 27 | | int | integer | `^[-+]?(0x[0-9a-f]+|[1-9][0-9]*|0[0-7]*)$` | 28 | | bool | boolean | `^(0|1|yes|no|true|false)` | 29 | | empty | empty string | `^$` | 30 | | argname | argument number | `^[0-9]$` | 31 | | varname | variable name | `^([a-z]|[a-z_][a-z0-9_]+)$` | 32 | | funcname | function name | `^[0-9]*[][a-z_.:][][a-z0-9_.:]*$` | 33 | 34 | Conformity to a type can be checked using the match functions: 35 | 36 | | Function | Cost | Complexity | 37 | |-------------------------|------|------------------------------| 38 | | `type:match:empty()` | O(1) | single `test` call | 39 | | `type:match:bool()` | O(1) | single glob pattern match | 40 | | `type:match:uint()` | O(n) | recursive glob pattern match | 41 | | `type:match:int()` | O(n) | recursive glob pattern match | 42 | | `type:match:argname()` | O(1) | single glob pattern match | 43 | | `type:match:varname()` | O(n) | recursive glob pattern match | 44 | | `type:match:funcname()` | O(n) | recursive glob pattern match | 45 | 46 | The `type:match()` function allows checking whether a value matches 47 | one of a set of types: 48 | 49 | ```sh 50 | if ! type:match empty,bool "$1"; then 51 | … 52 | fi 53 | ``` 54 | 55 | Type Detection 56 | -------------- 57 | 58 | The `type:which()` function provides provides the name of the first 59 | matched type from a list of given types. 60 | 61 | ```sh 62 | if ! type:which type empty,bool,uint,int,varname "$1"; then 63 | echo "invalid value: $1" >&2 64 | return 1 65 | fi 66 | case "$type" in 67 | empty) 68 | echo "empty value" >&2 69 | return 1 70 | ;; 71 | bool) 72 | … 73 | ;; 74 | … 75 | esac 76 | ``` 77 | 78 | Type Casting 79 | ------------ 80 | 81 | The `type:cast[int]()` function assigns matching inputs as decimal 82 | integer values: 83 | 84 | ```sh 85 | type:cast[int] x empty,bool 86 | echo "$x" # 0 87 | type:cast[int] x empty,bool YES 88 | echo "$x" # 1 89 | type:cast[int] x empty,bool no 90 | echo "$x" # 0 91 | type:cast[int] x empty,bool True 92 | echo "$x" # 1 93 | ``` 94 | 95 | Only the following types can be cast to `int`: 96 | 97 | - `empty` 98 | - `bool` 99 | - `uint` 100 | - `int` 101 | 102 | The conversion is done for the first matching type. Independent of 103 | the matched type, for a given value the assigned value is always the 104 | same. E.g. 1 is a valid bool, int and uint and the assigned value for all 105 | three is 1. Thus the order of the given types does not affect the 106 | outcome of the type conversion. 107 | 108 | The return value of type:cast[int] functions is the same as that 109 | of type:match functions, so the same uses for control flow are possible: 110 | 111 | ```sh 112 | if ! type:cast[int] verbose bool,int "$VERBOSITY"; then 113 | echo "Invalid VERBOSITY level, assume 0!" >&2 114 | verbose=0 115 | fi 116 | ``` 117 | 118 | Individual cast functions can be called directly: 119 | 120 | ```sh 121 | x=1 122 | type:cast[int]:empty x || type:cast[int]:bool x 123 | echo "$x" # 0 124 | type:cast[int]:empty x YES || type:cast[int]:bool x YES 125 | echo "$x" # 1 126 | type:cast[int]:empty x no || type:cast[int]:bool x no 127 | echo "$x" # 0 128 | type:cast[int]:empty x True || type:cast[int]:bool x True 129 | echo "$x" # 1 130 | ``` 131 | 132 | If no type matches, the destination variable remains unchanged: 133 | 134 | ```sh 135 | x=nope 136 | type:cast[int] x bool,int NaN 137 | echo "$x" # nope 138 | ``` 139 | -------------------------------------------------------------------------------- /src/bprintf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -f 3 | bsda_dir="%%DATADIR%%" 4 | #HACK 5 | bsda_dir="${0%${0##*/}}";bsda_dir="${bsda_dir%/}" 6 | test -n "${bsda_dir##/*}" && bsda_dir="$PWD/$bsda_dir" 7 | #hack 8 | readonly bsda_dir 9 | 10 | . ${bsda_dir:-.}/bsda_err.sh 11 | 12 | bsda:err:createECs ENOARGS 13 | 14 | . ${bsda_dir:-.}/bsda_fmt.sh 15 | 16 | if [ -z "${*}" ]; then 17 | bsda:err:raise ENOARGS "usage: ${0##*/} format [field=value ...]" 18 | fi 19 | 20 | bsda:fmt:printf "$@" 21 | -------------------------------------------------------------------------------- /src/bsda_async.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_async_" && return 0 2 | readonly _bsda_async_=1 3 | 4 | . ${bsda_dir:-.}/bsda_fifo.sh 5 | 6 | # 7 | # Creates an asynchronous dispatch variant of an existing class. 8 | # 9 | # Each instance lives in a separate process, connected via bsda:fifo:Fifo. 10 | # 11 | # Method calls are serialised and sent through the FIFO along with 12 | # $IFS. A 0xff byte is used to separate arguments, it is neither a 13 | # valid ascii nor utf-8 code. 14 | # 15 | # @param 1 16 | # The name of the asynchronous class to create 17 | # @param 2 18 | # The name of the class to manage in the asynchronous context 19 | # @retval 0 20 | # The new class was created successfully 21 | # @retval 1 22 | # The name of the destination class is missing 23 | # @retval 2 24 | # The name of the source class is missing 25 | # @retval 3 26 | # Error accessing the source class traits 27 | # 28 | bsda:async:createClass() { 29 | local IFS dstClass srcClass methods 30 | IFS=$'\n' 31 | dstClass="$1" 32 | srcClass="$2" 33 | readonly dstClass srcClass 34 | if [ -z "$dstClass" ]; then 35 | echo "bsda:async: ERROR: No destination class name supplied!" >&2 36 | return 1 37 | fi 38 | if [ -z "$srcClass" ]; then 39 | echo "bsda:async: ERROR: No source class name supplied!" >&2 40 | return 2 41 | fi 42 | if ! $srcClass.getMethods methods 2>&-; then 43 | echo "bsda:async: ERROR: Invalid source class ${srcClass}!" >&2 44 | return 3 45 | fi 46 | local method forward 47 | # Generate forwarding functions 48 | forward= 49 | for method in $methods; do 50 | case "$method" in 51 | public:copy | public:delete | public:dump) 52 | # ignore 53 | ;; 54 | public:*) 55 | forward="${forward}x:${method}${IFS}" 56 | eval "$dstClass.${method#*:}() { 57 | eval \"\$this.${method#*:}() { 58 | bsda:async:call \$(\$this.bsda_async_Fifo) \$(\$this.getBsda_async_obj).${method#*:} \\\"\\\$@\\\" 59 | }\" 60 | \$this.${method#*:} \"\$@\" 61 | }" 62 | ;; 63 | esac 64 | done 65 | bsda:obj:createClass $dstClass \ 66 | a:private:bsda_async_Fifo=bsda:fifo:Fifo \ 67 | r:private:bsda_async_pid \ 68 | r:private:bsda_async_obj \ 69 | i:private:bsda_async_init \ 70 | c:private:bsda_async_free \ 71 | $forward 72 | eval " 73 | $dstClass.bsda_async_init() { 74 | bsda:async:init $srcClass \"\$@\" 75 | } 76 | $dstClass.bsda_async_free() { 77 | bsda:async:free \"\$@\" 78 | } 79 | " 80 | } 81 | 82 | # 83 | # Serialise and dispatch a call. 84 | # 85 | # @param 1 86 | # The bsda:fifo:Fifo instance to dispatch through 87 | # @param 2 88 | # The call to execute in the asynchronous context 89 | # @param @ 90 | # Call arguments 91 | # 92 | bsda:async:call() { 93 | $1.send "IFS=\$'\377'; set -- \$(printf '$(shift; IFS=$'\377'; echo -n "$*" | bsda:obj:escape)'); IFS=\"\$(printf '$(echo -n "$IFS." | bsda:obj:escape)')\"; IFS=\"\${IFS%.}\"; \"\$@\"" 94 | } 95 | 96 | # 97 | # Construct an async class instance. 98 | # 99 | # @param @ 100 | # Arguments are forwarded to the constructor of the managed class 101 | # @retval 1 102 | # Construction failed 103 | # @retval 0 104 | # The asynchronous context was successfully set up 105 | # 106 | bsda:async:init() { 107 | local fifo obj 108 | bsda:fifo:Fifo ${this}bsda_async_Fifo || return 1 109 | bsda:async:daemon "$@" & 110 | setvar ${this}bsda_async_pid $! 111 | $this.bsda_async_Fifo fifo 112 | $fifo.recv ${this}bsda_async_obj 113 | $this.getBsda_async_obj obj 114 | test -n "$obj" 115 | } 116 | 117 | # 118 | # Harvest the asynchronous context. 119 | # 120 | bsda:async:free() { 121 | local pid fifo 122 | $this.getBsda_async_pid pid 123 | if [ -n "$pid" ]; then 124 | $this.bsda_async_Fifo fifo 125 | $fifo.send 'exit 0' 126 | wait "$pid" 127 | fi 128 | } 129 | 130 | # 131 | # Setup an asynchronous context creating an instance of the managed 132 | # class. 133 | # 134 | # This must be run in a separate context. It calls bsda:obj:fork(). 135 | # 136 | # @param 1 137 | # The name of the managed class 138 | # @param @ 139 | # Constructor arguments for the managed class instance 140 | # 141 | bsda:async:daemon() { 142 | bsda:obj:fork 143 | local fifo objClass obj cmd retval 144 | trap '' HUP INT TERM 145 | $this.bsda_async_Fifo fifo 146 | objClass="$1" 147 | shift 148 | $objClass obj "$@" 149 | retval=$? 150 | if [ $retval -ne 0 ]; then 151 | echo "bsda:async:daemon: ERROR: $class returned: $retval" >&2 152 | exit $retval 153 | fi 154 | 155 | # Send the object ID to the host process, there is a race 156 | # condition where the daemon receives the object ID instead 157 | # of the host process, this will simply resend in this case. 158 | eval "$obj() { $fifo.send $obj; }" 159 | $obj 160 | 161 | while true; do 162 | cmd= 163 | $fifo.recv cmd 164 | retval=$? 165 | if [ $retval -gt 128 ]; then 166 | # Retry read if interrupted by a signal 167 | continue 168 | elif [ $retval -ne 0 ]; then 169 | echo "bsda:async:daemon: ERROR: Read from pipe returned: $retval" >&2 170 | exit $retval 171 | fi 172 | eval "$cmd" 173 | done 174 | } 175 | -------------------------------------------------------------------------------- /src/bsda_bsdmake.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_bsdmake_" && return 0 2 | readonly _bsda_bsdmake_=1 3 | 4 | # 5 | # The BSD make binary, this can be overridden. 6 | # 7 | : ${bsda_bsdmake:=/usr/bin/make} 8 | 9 | # 10 | # Alias for the make executable. 11 | # 12 | # @param @ 13 | # Arguments to make 14 | # @return 15 | # The return value of make 16 | # 17 | bsda:bsdmake() { 18 | "$bsda_bsdmake" "$@" 19 | } 20 | 21 | # 22 | # Instances retrieve variable values from make. 23 | # 24 | # The added benefit is that this can be fed a list of variables that 25 | # should be ignored. 26 | # 27 | bsda:obj:createClass bsda:bsdmake:Vars \ 28 | r:private:ignore "The list of make variables to ignore" \ 29 | x:public:ignore "Add to the list of make variables to ignore" \ 30 | x:public:get "Retrieve make variables" 31 | 32 | # 33 | # Adds all arguments to the list of make variables to ignore. 34 | # 35 | # @param * 36 | # The make variables to ignore 37 | # 38 | bsda:bsdmake:Vars.ignore() { 39 | local IFS ignore 40 | IFS=$'\n' 41 | $this.getIgnore ignore 42 | setvar ${this}ignore "$ignore$*$IFS" 43 | } 44 | 45 | # 46 | # Retrieve the requested make variables not in the ignore list. 47 | # 48 | # The retrieval definition contains of a list of variables to retrieve 49 | # and optionally a list of vanilla arguments to make. 50 | # 51 | # All arguments following the first -- are passed on to make as provided. 52 | # 53 | # All other arguments are treated as make variables. Every argument 54 | # must start with the name of a make variable, but may make use of 55 | # the : modifiers. Please note that every variable definition is 56 | # quoted with ', so the use of ' inside of modifiers should be avoided 57 | # or escaped very carefully. 58 | # 59 | # @param &1 60 | # The destination variable for the make output 61 | # @param @ 62 | # The retrieval definition 63 | # 64 | bsda:bsdmake:Vars.get() { 65 | $caller.setvar "$1" "$( 66 | shift 67 | $this.getIgnore ignore 68 | args= 69 | for arg in "$@"; do shift; case "$arg" in 70 | --) 71 | break 72 | ;; 73 | *) 74 | if echo "$ignore" | /usr/bin/grep -qFx "${arg%%:*}"; then 75 | continue 76 | fi 77 | args="$args -V'\${$arg}'" 78 | ;; 79 | esac; done 80 | eval "bsda:bsdmake $args \"\$@\"" 81 | )" 82 | } 83 | -------------------------------------------------------------------------------- /src/bsda_dialog.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_dialog_" && return 0 2 | readonly _bsda_dialog_=1 3 | 4 | . ${bsda_dir:-.}/bsda_obj.sh 5 | 6 | # 7 | # Provides a slim wrapper around dialog(1). 8 | # 9 | 10 | # 11 | # A slim wrapper around dialog(1). 12 | # 13 | bsda:obj:createClass bsda:dialog:Dialog \ 14 | r:private:desc "A file descriptor used when calling dialog(1)" \ 15 | r:private:args "Additional arguments to dialog(1)" \ 16 | i:private:init "The constructor" \ 17 | c:private:clean "The destructor" \ 18 | x:private:call "Perform a call to dialog(1)" \ 19 | x:public:setArgs "Set additional arguments to dialog(1)" \ 20 | x:public:checklist "Call dialog --checklist" \ 21 | x:public:menu "Call dialog --menu" \ 22 | x:public:msgbox "Call dialog --msgbox" 23 | 24 | # 25 | # The constructor. 26 | # 27 | # Sets up a file descriptor to route dialog's use of stdout around 28 | # the variable assignment that catches the output of dialog. 29 | # 30 | # Construction fails if no more file descriptors are available. 31 | # 32 | # @param @ 33 | # Additional arguments to dialog, see dialog(1) 34 | # 35 | bsda:dialog:Dialog.init() { 36 | local desc 37 | bsda:obj:getDesc desc || return $? 38 | setvar ${this}desc "$desc" 39 | eval "exec $desc>&1" 40 | $this.setArgs "$@" 41 | } 42 | 43 | # 44 | # The destructor. 45 | # 46 | # Closes and releases the file descriptor. 47 | # 48 | bsda:dialog:Dialog.clean() { 49 | local desc 50 | $this.getDesc desc 51 | eval "exec $desc>&-" 52 | bsda:obj:releaseDesc "$desc" 53 | } 54 | 55 | # 56 | # Perform a call to dialog(1) catching and returning its output. 57 | # 58 | # @param &1 59 | # The variable to return the output to 60 | # @param @ 61 | # The arguments to the dialog call 62 | # @return 63 | # See dialog(1) 64 | # 65 | bsda:dialog:Dialog.call() { 66 | local ret result 67 | result="$( 68 | $this.getArgs args 69 | $this.getDesc desc 70 | shift 71 | (/usr/bin/dialog $args --backtitle "${0##*/}" "$@" \ 72 | >&$desc ) 2>&1)" 73 | ret=$? 74 | $caller.setvar "$1" "$result" 75 | return $ret 76 | } 77 | 78 | # 79 | # Set additional arguments to dialog(1). 80 | # 81 | # @param @ 82 | # Arguments to use for following dialog(1) calls 83 | # 84 | bsda:dialog:Dialog.setArgs() { 85 | local IFS 86 | IFS=$'\n' 87 | setvar ${this}args "$*" 88 | } 89 | 90 | # 91 | # Creates a checklist. 92 | # 93 | # @param &1 94 | # The variable to return dialog's output to 95 | # @param 2 96 | # The checklist label 97 | # @param @ 98 | # [ tag item status ] … 99 | # @return 100 | # See dialog(1) 101 | # 102 | bsda:dialog:Dialog.checklist() { 103 | local retvar text result 104 | retvar="$1" 105 | text="$2" 106 | shift 2 107 | $this.call result --separate-output --checklist "$text" 0 0 0 "$@" 108 | ret=$? 109 | $caller.setvar "$retvar" "$result" 110 | return $ret 111 | } 112 | 113 | # 114 | # Creates a menu. 115 | # 116 | # @param &1 117 | # The variable to return dialog's output to 118 | # @param 2 119 | # The menu label 120 | # @param @ 121 | # [ tag item ] … 122 | # @return 123 | # See dialog(1) 124 | # 125 | bsda:dialog:Dialog.menu() { 126 | local retvar text result 127 | retvar="$1" 128 | text="$2" 129 | shift 2 130 | $this.call result --menu "$text" 0 0 0 "$@" 131 | ret=$? 132 | $caller.setvar "$retvar" "$result" 133 | return $ret 134 | } 135 | 136 | # 137 | # Creates a msgbox. 138 | # 139 | # @param &1 140 | # The variable to return dialog's output to 141 | # @param 2 142 | # The text 143 | # 144 | bsda:dialog:Dialog.msgbox() { 145 | $class.call "$1" --msgbox "$2" 0 0 146 | } 147 | -------------------------------------------------------------------------------- /src/bsda_elf.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_elf_" && return 0 2 | readonly _bsda_elf_=1 3 | 4 | . ${bsda_dir:-.}/bsda_err.sh 5 | 6 | # 7 | # The bsda:elf package provides access to the symbols of ELF executable 8 | # files. 9 | # 10 | 11 | # 12 | # Error/exit codes for error reporting. 13 | # 14 | # | Code | Severity | Meaning | 15 | # |------------------|----------|----------------------------| 16 | # | E_BSDA_ELF_NOENT | error | Cannot read the given file | 17 | # 18 | bsda:err:createECs E_BSDA_ELF_NOENT 19 | 20 | # 21 | # Grants access to the symbol values in a binary file. 22 | # 23 | bsda:obj:createClass bsda:elf:File \ 24 | r:private:filename "The name of the file" \ 25 | r:private:virtual "The virtual address offset" \ 26 | r:private:symbols "A list of all symbols with address and size" \ 27 | i:private:init "Initialise the list of symbols" \ 28 | x:private:getTuple "Retrieve parameters of a single symbol" \ 29 | x:public:fetchEnc "Fetch an encoded value of a symbol" \ 30 | x:public:fetch "Fetch a printable string symbol value" 31 | 32 | # 33 | # Initialise symbol list for a given file. 34 | # 35 | # Determines the virtual address offset and creates a list of symbols. 36 | # 37 | # @param 1 38 | # The file name to read symbols from 39 | # @retval 0 40 | # Reading the file succeeded 41 | # @retval 1 42 | # An error occurred 43 | # @throws E_BSDA_ELF_NOENT 44 | # Cannot read the given file 45 | # 46 | bsda:elf:File.init() { 47 | if ! [ -r "$1" ]; then 48 | bsda:err:raise E_BSDA_ELF_NOENT "ERROR: Cannot read file: ${1}" 49 | return 1 50 | fi 51 | setvar ${this}filename "$1" 52 | setvar ${this}virtual "$( 53 | /usr/bin/readelf -Wl "$1" 2>&1 | while read -r type offs virt tail; do 54 | if [ "${type}" = "LOAD" ] && [ $((offs)) -eq 0 ]; then 55 | echo "${virt}" 56 | return 57 | fi 58 | done 59 | )" 60 | setvar ${this}symbols "$( 61 | /usr/bin/nm --demangle --print-size "$1" 2>&1 \ 62 | | /usr/bin/awk " 63 | \$4{ 64 | sub(/^/, \"addr=0x\") 65 | sub(/ /, \";size=0x\") 66 | sub(/ /, \";type=\") 67 | sub(/ /, \";name='\") 68 | sub(/$/, \"'\") 69 | print 70 | } 71 | " 72 | )" 73 | } 74 | 75 | # 76 | # Retrieve the type, address and size of a symbol value. 77 | # 78 | # The _OUTPUT FORMAT_ section of nm(1) documents the symbol types. 79 | # 80 | # @param &1 81 | # Symbol type destination variable 82 | # @param &2 83 | # Absolute symbol address destination variable 84 | # @param &3 85 | # Symbol size destination variable 86 | # @param 4 87 | # The name of the symbol to access 88 | # 89 | bsda:elf:File.getTuple() { 90 | local name type addr size offset 91 | eval "$($this.getSymbols | /usr/bin/grep -F "name='${4}'")" 92 | $this.getVirtual offset 93 | $caller.setvar "${1}" "${type}" 94 | $caller.setvar "${2}" "$((addr - offset))" 95 | $caller.setvar "${3}" "$((size))" 96 | } 97 | 98 | # 99 | # Extracts a symbol from the binary and runs it through an encoder. 100 | # 101 | # | Tag | Encoder | Description | 102 | # |-----|--------------|----------------------------------| 103 | # | vis | vis(1) | Escapes non-printable characters | 104 | # | b64 | b64encode(1) | Base64 encoder | 105 | # | uue | uuencode(1) | Binary file encoder | 106 | # | hex | hexdump(1) | Formattable hex and octal output | 107 | # 108 | # @param &1 109 | # Destination variable for the encoded value 110 | # @param 2 111 | # The symbol name to fetch 112 | # @param 3 113 | # The encoder tag 114 | # @param @ 115 | # Remaining arguments are forwarded to the encoder 116 | # 117 | bsda:elf:File.fetchEnc() { 118 | local dst sym mode value type addr size filename 119 | dst="$1" 120 | sym="$2" 121 | mode="$3" 122 | shift;shift;shift 123 | value= 124 | $this.getTuple type addr size "${sym}" 125 | if [ -n "${addr}" ]; then 126 | $this.getFilename filename 127 | value="$( 128 | /bin/dd if="${filename}" bs=1 skip="${addr}" count="${size}" 2>&- \ 129 | | case "${mode}" in 130 | vis) /usr/bin/vis "$@";; 131 | b64) /usr/bin/b64encode "$@" -;; 132 | uue) /usr/bin/uuencode "$@" -;; 133 | hex) /usr/bin/hexdump "$@";; 134 | esac 135 | )" 136 | fi 137 | $caller.setvar "${dst}" "${value}" 138 | } 139 | 140 | # 141 | # Extracts a printable string value from the binary. 142 | # 143 | # @param &1 144 | # Destination variable for the encoded value 145 | # @param 2 146 | # The symbol name to fetch 147 | # 148 | bsda:elf:File.fetch() { 149 | local value type addr size filename 150 | value= 151 | $this.getTuple type addr size "${2}" 152 | if [ -n "${addr}" ]; then 153 | $this.getFilename filename 154 | value="$(/bin/dd if="${filename}" bs=1 skip="${addr}" count="${size}" conv=sparse 2>&-)" 155 | fi 156 | $caller.setvar "${1}" "${value}" 157 | } 158 | -------------------------------------------------------------------------------- /src/bsda_err.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_err_" && return 0 2 | readonly _bsda_err_=1 3 | 4 | . ${bsda_dir:-.}/bsda_obj.sh 5 | 6 | # 7 | # Error handling facilities. 8 | # 9 | # @file 10 | # @see bsda_err.md 11 | # 12 | 13 | # 14 | # The greatest error/exit code value defined so far. 15 | # 16 | bsda_err_ecMax=-1 17 | 18 | # 19 | # A comma separated list of the defined error/exit codes and aliases. 20 | # 21 | bsda_err_ecs= 22 | 23 | # 24 | # Define error/exit codes. 25 | # 26 | # Takes a list of error/exit code names and aliases. Arguments may 27 | # have the following form: 28 | # 29 | # - `` 30 | # A symbolic error/exit code name is assigned a sequential number 31 | # - `=` 32 | # An alias may reference a previously defined error/exit code 33 | # - `=` 34 | # An alias may be assigned a fixed integral value 35 | # 36 | # The following error conditions exist: 37 | # 38 | # - Redefining a symbol or alias is a fatal error (exit code 2) 39 | # - Assigning a non-integral value is a fatal error (exit code 2) 40 | # 41 | # @param @ 42 | # A list of symbolic error/exit code names and aliases 43 | # @param bsda_err_ecMax 44 | # The greatest assigned sequential number 45 | # @param bsda_err_ecs 46 | # A comma separated record of error/exit code names/aliases 47 | # 48 | bsda:err:createECs() { 49 | local arg 50 | for arg in "$@"; do 51 | case "$arg" in 52 | *=*) 53 | # Create an alias 54 | if ! setvar ${arg%%=*} $((${arg#*=})); then 55 | echo "bsda:err:createECs: ERROR: Cannot assign alias $arg!" >&2 56 | exit 2 57 | fi 58 | ;; 59 | *) 60 | # Create a new exit number 61 | if ! setvar $arg $((bsda_err_ecMax += 1)); then 62 | echo "bsda:err:createECs: ERROR: Cannot assign $arg!" >&2 63 | exit 2 64 | fi 65 | ;; 66 | esac 67 | readonly ${arg%%=*} 68 | bsda_err_ecs="${bsda_err_ecs}${arg%%=*}," 69 | done 70 | } 71 | 72 | # 73 | # Setup a basic set of error/exit codes. 74 | # 75 | bsda:err:createECs E_OK E_WARN=E_OK E_SIGNAL E_FAIL 76 | 77 | # 78 | # Provide the newline separated list of error/exit codes. 79 | # 80 | # Each code is given in the format `=`. 81 | # 82 | # @param &1 83 | # Reference to the destination variable, the list is output 84 | # on stdout if this is not provided 85 | # 86 | bsda:err:ecs() { 87 | if [ -n "$1" ]; then 88 | setvar "$1" "$(bsda:err:_ecs)" 89 | else 90 | bsda:err:_ecs 91 | fi 92 | } 93 | 94 | # 95 | # Output a newline separated list of error/exit codes on stdout. 96 | # 97 | # This is a helper function for bsda:err:ecs(). 98 | # 99 | bsda:err:_ecs() { 100 | local IFS ec 101 | IFS=, 102 | for ec in $bsda_err_ecs; do 103 | echo "${ec}=$((${ec}))" 104 | done 105 | } 106 | 107 | # 108 | # The current error collecting context. 109 | # 110 | # Should point to a bsda:err:Context instance if set. 111 | # 112 | bsda_err_context= 113 | 114 | # 115 | # Prints a message on stderr and exits if the given code is non-zero. 116 | # 117 | # @param 1 118 | # An error/exit code 119 | # @param @ 120 | # An optional message printed on stderr, regardless of the error/exit code 121 | # @warning 122 | # Terminates the program if the error/exit code is non-zero 123 | # 124 | bsda:err:panic() { 125 | if [ -n "$2" ]; then 126 | (shift; IFS=$'\n'; echo "$*") >&2 127 | fi 128 | if ! ( : $(($1)) ) 2>&-; then 129 | echo "bsda:err:panic: Not a valid exit code: $1" >&2 130 | bsda_err_context= 131 | exit $((E_FAIL)) 132 | fi 133 | if [ $(($1)) -ne 0 ]; then 134 | bsda_err_context= 135 | exit $(($1)) 136 | fi 137 | } 138 | 139 | # 140 | # Create a new issue handling context. 141 | # 142 | # An issue handling context is a FIFO of issues created with 143 | # bsda:err:raise(), retrieval is done with bsda:err:get(). 144 | # 145 | bsda:err:collect() { 146 | bsda:err:Context bsda_err_context 147 | $caller.delete "$bsda_err_context" 148 | } 149 | 150 | # 151 | # Raise an issue in the current issue handling context. 152 | # 153 | # Issues can be retrieved calling bsda:err:get(). 154 | # 155 | # Defers to bsda:err:panic() if no context exists. 156 | # 157 | # @param 1 158 | # An error/exit code 159 | # @param @ 160 | # An error/warning message 161 | # @warning 162 | # May terminate the program if the error/exit code is non-zero 163 | # 164 | bsda:err:raise() { 165 | if [ -z "$bsda_err_context" ]; then 166 | bsda:err:panic "$@" 167 | else 168 | $bsda_err_context.raise "$@" 169 | fi 170 | } 171 | 172 | # 173 | # Retrieve the next issue from the current context. 174 | # 175 | # @param &1 176 | # The destination variable for the error/exit code 177 | # @param &2 178 | # The destination variable for the message 179 | # @retval 0 180 | # An issue was successfully retrieved 181 | # @retval 1 182 | # No issues remain in context 183 | # @warning 184 | # Panics with E_FAIL if no context exists 185 | # 186 | bsda:err:get() { 187 | if [ -z "$bsda_err_context" ]; then 188 | bsda:err:panic E_FAIL "bsda:err: ERROR: Cannot bsda:err:get without bsda:err:collect!" 189 | fi 190 | $bsda_err_context.get "$@" 191 | } 192 | 193 | # 194 | # Raise an issue in the parent issue handling context. 195 | # 196 | # This is intended to be used if bsda:err:get() returned an issue 197 | # that cannot be handled in the current context. 198 | # 199 | # Defers to bsda:err:panic() if there is no current context or if 200 | # there is no parent context. 201 | # 202 | # @param 1 203 | # An error/exit code 204 | # @param @ 205 | # An error/warning message 206 | # @warning 207 | # May terminate the program if the error/exit code is non-zero 208 | # @warning 209 | # Panics with E_FAIL if no context exists 210 | # 211 | bsda:err:forward() { 212 | if [ -z "$bsda_err_context" ]; then 213 | bsda:err:panic E_FAIL "bsda:err: ERROR: Cannot bsda:err:forward without bsda:err:get!" 214 | fi 215 | local context 216 | $bsda_err_context.getPrevious context 217 | if [ -n "$context" ]; then 218 | $context.raise "$@" 219 | else 220 | bsda:err:panic "$@" 221 | fi 222 | } 223 | 224 | # 225 | # Instances represent an issue consisting of an error/exit code, 226 | # message pair. 227 | # 228 | bsda:obj:createClass bsda:err:Issue \ 229 | r:private:e "The error/exit code" \ 230 | r:private:msg "The error/warning message" \ 231 | i:private:init "Initialise the issue" \ 232 | x:public:get "Retrieve the error/exit code and message" 233 | 234 | # 235 | # Initialise the issue. 236 | # 237 | # @param 1 238 | # An error/exit code 239 | # @param @ 240 | # An error/warning message 241 | # 242 | bsda:err:Issue.init() { 243 | local IFS=$'\n' 244 | setvar ${this}e "$1" 245 | shift 246 | setvar ${this}msg "$*" 247 | } 248 | 249 | # 250 | # Retrieve the error/exit code and error/warning message. 251 | # 252 | # @param &1 253 | # The destination variable for the error/exit code 254 | # @param &2 255 | # The destination variable for the message 256 | # 257 | bsda:err:Issue.get() { 258 | local e msg 259 | $this.getE e 260 | $this.getMsg msg 261 | $caller.setvar "$1" "$e" 262 | $caller.setvar "$2" "$msg" 263 | } 264 | 265 | # 266 | # Instances represent an error handling context. 267 | # 268 | bsda:obj:createClass bsda:err:Context \ 269 | r:public:previous "The previous (parent) context" \ 270 | r:private:issues "A list of active issues" \ 271 | i:private:init "Record the parent context" \ 272 | c:private:clean "Finalise this context" \ 273 | x:public:raise "Add an issue to the context" \ 274 | x:public:get "Pop an issue off the beginning of the list" 275 | 276 | # 277 | # Remember the parent context to restore when this context goes out 278 | # of scope. 279 | # 280 | # @param bsda_err_context 281 | # Set to this context 282 | # 283 | bsda:err:Context.init() { 284 | setvar ${this}previous "$bsda_err_context" 285 | rec ${this}issues= 286 | } 287 | 288 | # 289 | # Perform sanity checks, complain and recover as far as possible. 290 | # 291 | # - If this context is still active 292 | # - Restore parent context 293 | # - Print hints on processing issues 294 | # - If issues remain in this context 295 | # - Attempt to forward them to the parent context 296 | # - Complain about deferring unhandled issues 297 | # - Otherwise print issue messages 298 | # - Complain about dropping unhandled issues 299 | # 300 | # @param bsda_err_context 301 | # Restore to previous context if this context is still active 302 | # 303 | bsda:err:Context.clean() { 304 | local previous 305 | if [ "$bsda_err_context" = $this ]; then 306 | echo "bsda:err: HINT: Use bsda:err:get to handle issues" 307 | echo "bsda:err: HINT: Use bsda:err:panic to terminate on an issue" 308 | echo "bsda:err: HINT: Use bsda:err:forward to defer handling an issue" 309 | $this.getPrevious bsda_err_context 310 | fi >&2 311 | if rec ${this}issues.is_not_empty; then 312 | $this.getPrevious previous 313 | if [ -n "$previous" ]; then 314 | rec ${previous}issues.append ${this}issues 315 | echo "bsda:err: WARNING: Unhandled issue(s) deferred to parent context!" 316 | return 0 317 | fi 318 | local issue e msg 319 | while rec ${this}issues.pop_front issue; do 320 | $issue.get e msg 321 | $issue.delete 322 | echo "$msg" >&2 323 | done 324 | echo "bsda:err: WARNING: Unhandled issue(s) dropped!" 325 | fi >&2 326 | return 0 327 | } 328 | 329 | # 330 | # Append an issue to the list of issues. 331 | # 332 | # @param 1 333 | # An error/exit code 334 | # @param @ 335 | # An error/warning message 336 | # 337 | bsda:err:Context.raise() { 338 | local issue 339 | bsda:err:Issue issue "$@" 340 | rec ${this}issues.push_back "${issue}" 341 | } 342 | 343 | # 344 | # Pop the first issue off the list of issues. 345 | # 346 | # @param &1 347 | # The destination variable for the error/exit code 348 | # @param &2 349 | # The destination variable for the message 350 | # @param bsda_err_context 351 | # Restored to the previous context if no issue is available 352 | # @retval 0 353 | # An issue was retrieved 354 | # @retval 1 355 | # No issues are available from the context 356 | # 357 | bsda:err:Context.get() { 358 | local issue e msg 359 | if rec ${this}issues.pop_front issue; then 360 | $issue.get e msg 361 | $caller.setvar "$1" "$e" 362 | $caller.setvar "$2" "$msg" 363 | $issue.delete 1 364 | return 0 365 | fi 366 | $this.getPrevious bsda_err_context 367 | return 1 368 | } 369 | -------------------------------------------------------------------------------- /src/bsda_fifo.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_fifo_" && return 0 2 | readonly _bsda_fifo_=1 3 | 4 | . ${bsda_dir:-.}/bsda_obj.sh 5 | 6 | # 7 | # Offers a light weight mkfifo(1) wrapper to create and use named pipes. 8 | # 9 | 10 | # 11 | # Provides a light weight wrapper around mkfifo(1). 12 | # 13 | # Creating this prior to forking opens a two-way communication pipe 14 | # between processes. 15 | # 16 | # The sink() and source() methods are used like eval. The given command's 17 | # output (sink) or input (source) is redirected through the named pipe: 18 | # 19 | # bsda:fifo:Fifo fifo 20 | # $fifo.sink echo "This is my line" & 21 | # $fifo.source read -r line 22 | # echo "$line" 23 | # 24 | # The send() and recv() methods can be used to provide string argument 25 | # access: 26 | # 27 | # bsda:fifo:Fifo fifo 28 | # $fifo.send "This is my line" & 29 | # $fifo.recv line 30 | # echo "$line" 31 | # 32 | # The recv() function provides line wise access, multiple arguments 33 | # can be provided to split a line into columns divided by the characters 34 | # in IFS (see `read -r`). 35 | # 36 | # The send() function merges multiple arguments using `"$*"`, i.e. 37 | # arguments are concatenated using the first character in IFS. 38 | # 39 | # Internally a set of named pipes is created one for I/O (fifo.pipe) 40 | # and three for locking (send.lock, recv.lock and wait.lock). 41 | # 42 | # All locks initially block on read, locks can be freed by writing 43 | # a newline character into the respective named pipe, allowing a single 44 | # process' read from the lock to complete. 45 | # The send.lock and recv.lock locks are initially freed by a guard 46 | # processes. The guard process in turn waits for wait.lock to be 47 | # freed. This is performed by the host process when the bsda:fifo:Fifo 48 | # instance is deleted, causing the guard process to destroy the named 49 | # pipe set and terminate. 50 | # 51 | bsda:obj:createClass bsda:fifo:Fifo \ 52 | r:private:loc "The named pipe file system location" \ 53 | i:private:init "Sets up the named pipe" \ 54 | c:private:clean "Releases the file descriptor" \ 55 | x:public:send "Send a string" \ 56 | x:public:recv "Receive a string" \ 57 | x:public:sink "Use like eval to send" \ 58 | x:public:source "Use like eval to read" 59 | 60 | # 61 | # The constructor sets up two-way communication. 62 | # 63 | # It creates a named pipe, opens a file descriptor, unlinks the named 64 | # pipe and creates low overhead source() and sink() methods. 65 | # 66 | bsda:fifo:Fifo.init() { 67 | local loc 68 | # Create a named pipe location 69 | setvar ${this}loc "$(/usr/bin/mktemp -dt bsda:fifo)" || return $? 70 | $this.getLoc loc 71 | 72 | # Create the named pipes for I/O, and read/write locking 73 | /usr/bin/mkfifo -m 0600 "${loc}/fifo.pipe" \ 74 | "${loc}/recv.lock" \ 75 | "${loc}/send.lock" \ 76 | "${loc}/wait.lock" || return $? 77 | 78 | # Open the pipes in a subprocess to make them non-blocking 79 | ( 80 | # Ignore signals 81 | trap '' INT HUP TERM; 82 | # Make the pipes non-blocking 83 | exec 3<> "${loc}/fifo.pipe" 84 | exec 4<> "${loc}/recv.lock" 85 | exec 5<> "${loc}/send.lock" 86 | # Release the read/write locks 87 | echo >&4 88 | echo >&5 89 | # Acquire the wait lock to die 90 | read -r tmp < "${loc}/wait.lock" 91 | /bin/rm -rf "$loc" 92 | ) & 93 | 94 | $this.send 95 | $this.recv 96 | $this.sink 97 | $this.source 98 | } 99 | 100 | # 101 | # Write the given arguments to the FIFO. 102 | # 103 | # @param * 104 | # All arguments are written to the FIFO 105 | # @param IFS 106 | # The first character of IFS is used to concatenate arguments 107 | # @note 108 | # Usable after the first call, which is performed by the initialiser 109 | # 110 | bsda:fifo:Fifo.send() { 111 | local loc 112 | $this.getLoc loc 113 | eval "$this.send() { 114 | local bsda_fifo_Fifo_lock 115 | read -r bsda_fifo_Fifo_lock < ${loc}/send.lock 116 | echo \"\$*\" >> ${loc}/fifo.pipe 117 | echo >> ${loc}/send.lock 118 | }" 119 | } 120 | 121 | # 122 | # Read one line from the FIFO. 123 | # 124 | # @param &@ 125 | # The line is split into fields (see read -r) 126 | # @param IFS 127 | # List of field separators 128 | # @note 129 | # Usable after the first call, which is performed by the initialiser 130 | # 131 | bsda:fifo:Fifo.recv() { 132 | local loc 133 | $this.getLoc loc 134 | eval "$this.recv() { 135 | local bsda_fifo_Fifo_lock 136 | read -r bsda_fifo_Fifo_lock < ${loc}/recv.lock 137 | read -r \"\$@\" < ${loc}/fifo.pipe 138 | echo >> ${loc}/recv.lock 139 | }" 140 | } 141 | 142 | # 143 | # Write the output of the given command to the FIFO. 144 | # 145 | # A call to this method is equivalent to using `eval` with the 146 | # output redirected to the FIFO. 147 | # 148 | # @param @ 149 | # The `eval` arguments 150 | # @return 151 | # The return value of the executed command 152 | # @note 153 | # Usable after the first call, which is performed by the initialiser 154 | # 155 | bsda:fifo:Fifo.sink() { 156 | local loc 157 | $this.getLoc loc 158 | eval "$this.sink() { 159 | local bsda_fifo_Fifo_lock bsda_fifo_Fifo_ret 160 | read -r bsda_fifo_Fifo_lock < ${loc}/send.lock 161 | eval \"\$@\" >> ${loc}/fifo.pipe 162 | bsda_fifo_Fifo_ret=\$? 163 | echo >> ${loc}/send.lock 164 | return \$bsda_fifo_Fifo_ret 165 | }" 166 | } 167 | 168 | # 169 | # Read input from the FIFO. 170 | # 171 | # A call to this method is equivalent to using `eval` with the input 172 | # connected to the FIFO. 173 | # 174 | # @param @ 175 | # The `eval` arguments 176 | # @return 177 | # The return value of the executed command 178 | # @note 179 | # Usable after the first call, which is performed by the initialiser 180 | # 181 | bsda:fifo:Fifo.source() { 182 | local loc 183 | $this.getLoc loc 184 | eval "$this.source() { 185 | local bsda_fifo_Fifo_lock bsda_fifo_Fifo_ret 186 | read -r bsda_fifo_Fifo_lock < ${loc}/recv.lock 187 | eval \"\$@\" < ${loc}/fifo.pipe 188 | bsda_fifo_Fifo_ret=\$? 189 | echo >> ${loc}/recv.lock 190 | return \$bsda_fifo_Fifo_ret 191 | }" 192 | } 193 | 194 | # 195 | # Clean up behind the fifo. 196 | # 197 | # The destructor closes the file descriptors and releases them back 198 | # into the pool of available descriptors. 199 | # 200 | bsda:fifo:Fifo.clean() { 201 | local loc die 202 | $this.getLoc loc 203 | # Release the wait lock 204 | echo >> "${loc}/wait.lock" 205 | /bin/rm -rf "$loc" 206 | } 207 | -------------------------------------------------------------------------------- /src/bsda_test.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_test_" && return 0 2 | readonly _bsda_test_=1 3 | 4 | # 5 | # Print failure and exit. 6 | # 7 | # @param 1 8 | # The file name 9 | # @param 2 10 | # This should be $LINENO 11 | # @param 3 12 | # The error number 13 | # 14 | bsda:test:err() { 15 | echo "$1 ERROR: $3" >&2 16 | echo "$1:$2: $(/usr/bin/head -n$2 "$1" | /usr/bin/tail -n1)" >&2 17 | exit $3 18 | } 19 | 20 | # 21 | # Check a string against a glob pattern. 22 | # 23 | # @param 1 24 | # The glob pattern to match 25 | # @param 2 26 | # The string to check 27 | # @retval 0 28 | # The string matches the pattern 29 | # @retval 1 30 | # The string does not match the pattern 31 | # 32 | bsda:test:gmatch() { 33 | case "$2" in 34 | $1) 35 | return 0 36 | ;; 37 | esac 38 | return 1 39 | } 40 | 41 | # 42 | # Each line is matched against a set of glob patterns. 43 | # 44 | # The return value depends on a relationship criteria that specifies 45 | # how lines from the string should relate to glob patterns. The relationship 46 | # is a tuple of two variables: `:` 47 | # 48 | # | Relationship | Meaning | 49 | # |--------------|------------------------------------------------------| 50 | # | all:* | All the lines must be matched by a pattern | 51 | # | any:* | At least one line must be matched by a pattern | 52 | # | *:all | All the patterns must match at least one line | 53 | # | *:any | Any of the patterns can be matched | 54 | # | *:once | All the patterns must be matched by exactly one line | 55 | # 56 | # @param 1 57 | # The string to match against the patterns 58 | # @param 2 59 | # The relationship between string lines and patterns 60 | # @param @ 61 | # The glob patterns to match against 62 | # @retval 0 63 | # The relationship is satisfied 64 | # @retval 1 65 | # The relationship is not satisfied 66 | # @retval 13 67 | # The string line part of the relationship is unknown 68 | # @retval 23 69 | # The pattern part of the relationship is unknown 70 | # 71 | bsda:test:xmatch() { 72 | local IFS rel str i 73 | IFS=$'\n' 74 | str="$1" 75 | rel="$2" 76 | shift 2 77 | i=0 78 | while [ $i -lt $# ]; do 79 | local count_$i 80 | i=$((i + 1)) 81 | done 82 | case "$rel" in 83 | all:any) 84 | for str in $str; do 85 | bsda:test:xmatch_any "$str" "$@" || return $? 86 | done 87 | return 0 88 | ;; 89 | any:any) 90 | for str in $str; do 91 | bsda:test:xmatch_any "$str" "$@" && return 0 92 | done 93 | return 1 94 | ;; 95 | all:*) 96 | for str in $str; do 97 | bsda:test:xmatch_count "$str" "$@" || return $? 98 | done 99 | ;; 100 | any:*) 101 | for str in $str; do 102 | bsda:test:xmatch_count "$str" "$@" 103 | done 104 | ;; 105 | *) 106 | # Unsupported relation 107 | return 13 108 | ;; 109 | esac 110 | # Check counts 111 | case "$rel" in 112 | *:all) 113 | i=0 114 | while [ $i -lt $# ]; do 115 | # Bail out if a pattern was not matched 116 | if [ $((count_$i)) -eq 0 ]; then 117 | return 1 118 | fi 119 | i=$((i + 1)) 120 | done 121 | return 0 122 | ;; 123 | *:once) 124 | i=0 125 | while [ $i -lt $# ]; do 126 | # Bail out if a pattern was not matched once 127 | if [ $((count_$i)) -ne 1 ]; then 128 | return 1 129 | fi 130 | i=$((i + 1)) 131 | done 132 | return 0 133 | ;; 134 | esac 135 | # Unsupported relation 136 | return 23 137 | } 138 | 139 | # 140 | # Helper function to bsda:test:xmatch(). 141 | # 142 | # Checks the given line against the given patterns. 143 | # 144 | # @param 1 145 | # The string line to match against patterns 146 | # @param @ 147 | # The patterns to match against 148 | # @retval 0 149 | # A pattern match was encountered 150 | # @retval 1 151 | # None of the patterns are a match 152 | # 153 | bsda:test:xmatch_any() { 154 | local line pattern 155 | line="$1" 156 | pattern="$2" 157 | # Terminate recursion, when running out of patterns to mach 158 | if ! shift 2; then 159 | return 1 160 | fi 161 | # Try the current pattern 162 | case "$line" in 163 | $pattern) 164 | return 0 165 | ;; 166 | esac 167 | # Try next pattern 168 | bsda:test:xmatch_any "$line" "$@" 169 | } 170 | 171 | # 172 | # Helper function to bsda:test:xmatch(). 173 | # 174 | # Counts the matches of each pattern. 175 | # 176 | # @param [count_0..count_$#) 177 | # Store the number of matches for each pattern 178 | # @param 1 179 | # The string line to match against patterns 180 | # @param @ 181 | # The patterns to match against 182 | # @retval 0 183 | # At least one pattern match was encountered 184 | # @retval 1 185 | # None of the patterns are a match 186 | # 187 | bsda:test:xmatch_count() { 188 | local line pattern ret 189 | line="$1" 190 | pattern="$2" 191 | # Terminate recursion, when running out of patterns to mach 192 | if ! shift 2; then 193 | return 1 194 | fi 195 | # Recurse to next pattern 196 | bsda:test:xmatch_count "$line" "$@" 197 | ret=$? 198 | # Try the current pattern 199 | case "$line" in 200 | $pattern) 201 | : $((count_$# += 1)) 202 | return 0 203 | ;; 204 | esac 205 | return $ret 206 | } 207 | 208 | # 209 | # Return the function type of the given function. 210 | # 211 | # | Type | Description | 212 | # |------------|---------------------------------------------------| 213 | # | alias | The given function is a shell alias | 214 | # | builtin | The given function is a shell builtin | 215 | # | function | The given function is a shell function | 216 | # | executable | The given function names a file system executable | 217 | # | none | The given function cannot be found | 218 | # | nil | The `type` builtin returned an unsupported string | 219 | # 220 | # Note that `bash` only supports aliases in interactive mode. 221 | # 222 | # @param &1 223 | # The variable to receive the function type 224 | # @param 2 225 | # The function to determine the type of 226 | # 227 | bsda:test:type() { 228 | setvar "$1" "$( 229 | case "$(type "$2" 2>&1 )" in 230 | "$2 is an alias "*|"$2 is aliased "*) 231 | echo alias 232 | ;; 233 | "$2 is a shell builtin") 234 | echo builtin 235 | ;; 236 | "$2 is a shell function"|"$2 is a function"*) 237 | echo function 238 | ;; 239 | "$2 is /"*) 240 | echo executable 241 | ;; 242 | *"$2: not found") 243 | echo none 244 | ;; 245 | *) 246 | echo nil 247 | ;; 248 | esac 249 | )" 250 | } 251 | 252 | # 253 | # Check if the given function is an alias. 254 | # 255 | # @param 1 256 | # The name of the function 257 | # @return 258 | # Returns 0 for yes and 1 for no 259 | # 260 | bsda:test:isAlias() { 261 | local type 262 | bsda:test:type type "$1" 263 | test "$type" = alias 264 | } 265 | 266 | # 267 | # Check if the given function is a builtin. 268 | # 269 | # @param 1 270 | # The name of the function 271 | # @return 272 | # Returns 0 for yes and 1 for no 273 | # 274 | bsda:test:isBuiltin() { 275 | local type 276 | bsda:test:type type "$1" 277 | test "$type" = builtin 278 | } 279 | 280 | # 281 | # Check if the given function is a shell function. 282 | # 283 | # @param 1 284 | # The name of the function 285 | # @return 286 | # Returns 0 for yes and 1 for no 287 | # 288 | bsda:test:isFunction() { 289 | local type 290 | bsda:test:type type "$1" 291 | test "$type" = function 292 | } 293 | 294 | # 295 | # Check if the given function is a file system executable. 296 | # 297 | # @param 1 298 | # The name of the function 299 | # @return 300 | # Returns 0 for yes and 1 for no 301 | # 302 | bsda:test:isExecutable() { 303 | local type 304 | bsda:test:type type "$1" 305 | test "$type" = executable 306 | } 307 | 308 | # 309 | # Check if the given function cannot be found. 310 | # 311 | # @param 1 312 | # The name of the function 313 | # @return 314 | # Returns 0 for yes and 1 for no 315 | # 316 | bsda:test:isNone() { 317 | local type 318 | bsda:test:type type "$1" 319 | test "$type" = none 320 | } 321 | 322 | # 323 | # Check if the given variable exists. 324 | # 325 | # Note that this returns true for all defined variables, even empty 326 | # ones. 327 | # 328 | # @param 1 329 | # The name of the variable 330 | # @retval 0 331 | # The variable has been defined 332 | # @retval 1 333 | # The variable does not exist 334 | # 335 | bsda:test:isSet() { 336 | eval "test -n \"\${$1+x}\"" 337 | } 338 | -------------------------------------------------------------------------------- /src/bsda_tty.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_tty_" && return 0 2 | readonly _bsda_tty_=1 3 | 4 | . ${bsda_dir:-.}/bsda_obj.sh 5 | 6 | # 7 | # A package for controlling the terminal and mixing status output on 8 | # /dev/tty with regular output on /dev/stdout and /dev/stderr. 9 | # 10 | # Output duplication has been removed in favour of tee(1). 11 | # 12 | # Tested on: 13 | # 14 | # | Terminal | TERM | 15 | # |--------------|--------| 16 | # | xterm | xterm | 17 | # | console | xterm | 18 | # | rxvt-unicode | rxvt | 19 | # | tmux | screen | 20 | # 21 | 22 | # 23 | # A list of useful termcap(5) capabilities, used with tput(1): 24 | # 25 | # | Command | Short | Escape | Problems | 26 | # |--------------------|----------|---------------|-----------------------| 27 | # | save_cursor | sc | \e7 | | 28 | # | restore_cursor | rc | \e8 | | 29 | # | cursor_address | cm #1 #2 | \e[#2;#1H | Escape counts from 1 | 30 | # | cursor_home | ho | \e[H | | 31 | # | columns | co => # | | | 32 | # | lines | li => # | | | 33 | # | clr_eol | ce | \e[K | | 34 | # | clr_eos | cd | \e[J | | 35 | # | delete_line | dl | \e[M | | 36 | # | parm_insert_line | AL #1 | \e[#1L | | 37 | # | insert_line | al | \e[L | | 38 | # | cursor_invisible | vi | \e[?25l | | 39 | # | cursor_normal | ve | \e[34h\e[?25h | | 40 | # | cursor_visible | vs | \e[34l | | 41 | # | parm_down_cursor | DO #1 | \e[#1B | DO 0 glitches in tmux | 42 | # | parm_up_cursor | UP #1 | \e[#1A | UP 0 glitches in tmux | 43 | # | carriage_return | cr | \r | | 44 | # | newline | nw | \eE | | 45 | # | cursor_down | do | \n | | 46 | # | cursor_up | up | \eM | | 47 | # | eat_newline_glitch | xn | | | 48 | # | init_tabs | it => # | | | 49 | # | enter_am_mode | SA | \e[?7h | tput SA does not work | 50 | # | exit_am_mode | RA | \e[?7l | tput RA does not work | 51 | # | enter_ca_mode | ti | \e[?1049h | | 52 | # | exit_ca_mode | te | \e[?1049l | | 53 | # 54 | 55 | # 56 | # Attach a filter to a file descriptor. 57 | # 58 | bsda:obj:createClass bsda:tty:Filter \ 59 | r:private:desc "The filter descriptor" \ 60 | r:private:fifo "The named pipe to hook redirects into" \ 61 | i:private:init "Attach the filter" \ 62 | c:private:free "Terminate the filter" 63 | 64 | # 65 | # Dispatch the filter and redirect the requested output into it. 66 | # 67 | # @param 1 68 | # The file descriptor to attach to the filter 69 | # @param @ 70 | # The filter command (see eval) 71 | # 72 | bsda:tty:Filter.init() { 73 | local fifo 74 | # Setup a named pipe 75 | setvar ${this}fifo "$(/usr/bin/mktemp -ut bsda:tty:Filter)" || return $? 76 | $this.getFifo fifo 77 | /usr/bin/mkfifo -m 0600 "${fifo}" || return $? 78 | # Dispatch the filter 79 | (shift; eval "$@") < "$fifo" & 80 | # Redirect the requested file descriptor into the filter 81 | setvar ${this}desc $(($1)) 82 | eval "exec $(($1))>> ${fifo}" 83 | # Clean up the named pipe 84 | /bin/rm -f "$fifo" 85 | unset ${this}fifo 86 | } 87 | 88 | # 89 | # Release the named pipe and kill the filter process. 90 | # 91 | bsda:tty:Filter.free() { 92 | local fifo desc 93 | # Cleanup fifo in case of incomplete initialisation 94 | $this.getFifo fifo 95 | if [ -n "$fifo" ]; then 96 | /bin/rm -f "$fifo" 97 | fi 98 | # Kill filter process 99 | $this.getDesc desc 100 | if [ -n "$desc" ]; then 101 | eval "exec $((desc))>&-" 102 | fi 103 | } 104 | 105 | # 106 | # Provides terminal output. 107 | # 108 | # This provides n status lines directly on the terminal, i.e. they 109 | # are not affected by redirecting stdout or stderr. It also provides 110 | # output on stdout and stderr without messing up the status lines. 111 | # 112 | bsda:obj:createClass bsda:tty:Terminal \ 113 | a:private:Filter1=bsda:tty:Filter \ 114 | a:private:Filter2=bsda:tty:Filter \ 115 | r:private:stLines "The number of status lines" \ 116 | r:private:drLines "The number of status lines to draw" \ 117 | x:public:winch "Update window signal handler" \ 118 | i:private:init "Take over the terminal output" \ 119 | x:public:use "Set number of status lines" \ 120 | x:public:line "Set a status line" \ 121 | c:public:deactivate "Deactivate status lines" \ 122 | x:public:stdout "Print to stdout" \ 123 | x:public:stderr "Print to stderr" \ 124 | x:public:filter "Install an optional output filter" \ 125 | x:private:refresh "Redraw status lines" 126 | 127 | # 128 | # SIGWINCH handler. 129 | # 130 | # Caps the number of status lines to draw to half of the terminal height 131 | # and calls refresh(). 132 | # 133 | bsda:tty:Terminal.winch() { 134 | local teLines 135 | trap '' WINCH 136 | teLines=$(($(/usr/bin/tput li 2> /dev/tty || echo 24))) 137 | # Use at most half of the available terminal space 138 | setvar ${this}drLines $((${this}stLines < (teLines / 2) ? ${this}stLines : (teLines / 2))) 139 | $this.refresh 140 | trap "$this.winch" WINCH 141 | } 142 | 143 | # 144 | # Setup terminal. 145 | # 146 | bsda:tty:Terminal.init() { 147 | if [ -w /dev/tty ]; then 148 | $this.use 0 149 | else 150 | $this.deactivate 151 | fi 152 | } 153 | 154 | # 155 | # Changes the number of status lines. 156 | # 157 | # Updates the number of desired status lines, sets the number of 158 | # actual status lines to draw and redraws the status. 159 | # 160 | # @param 1 161 | # The requested number of status lines 162 | # 163 | bsda:tty:Terminal.use() { 164 | local lines 165 | $this.getStLines lines 166 | # delete no longer needed status lines 167 | while [ $((lines)) -gt $(($1)) ]; do 168 | unset ${this}line$((lines -= 1)) 169 | done 170 | setvar ${this}stLines $(($1)) 171 | $this.winch 172 | } 173 | 174 | # 175 | # Prints a string a given number of times. 176 | # 177 | # This can be used to generate repeated function arguments. 178 | # 179 | # @param 1 180 | # Number of repetitions 181 | # @param 2 182 | # String to repeat 183 | # 184 | bsda:tty:Terminal:repeat() { 185 | if [ $1 -le 0 ]; then 186 | return 187 | fi 188 | echo -n "$2" 189 | bsda:tty:Terminal:repeat $(($1 - 1)) "$2" 190 | } 191 | 192 | # 193 | # Draw the given status line. 194 | # 195 | # This jumps to the given line and draws it. 196 | # 197 | # @param 1 198 | # The status line number to draw on 199 | # @param * 200 | # The status line contents to draw 201 | # 202 | bsda:tty:Terminal.line() { 203 | if [ $(($1)) -ge $((${this}drLines)) ] || [ $(($1)) -lt 0 ]; then 204 | return 205 | fi 206 | local lineno line 207 | lineno=$(($1)) 208 | line=${this}line$(($1)) 209 | shift 210 | setvar $line "$*" 211 | ( 212 | # tput vi....... 213 | printf '%b' '\033[?25l\r' $($class:repeat $lineno '\n') 214 | # tput RA...... ce.... SA...... 215 | printf '\033[?7l%s\033[K\r\033[?7h' "$*" 216 | # tput up... ve............... 217 | printf '%b' $($class:repeat $lineno '\033M') '\033[34h\033[?25h' 218 | ) > /dev/tty 219 | } 220 | 221 | # 222 | # Clear status lines, turn cursor visible and replace methods with 223 | # dummies. 224 | # 225 | # Note that stdout() and stderr() still perform output. 226 | # 227 | bsda:tty:Terminal.deactivate() { 228 | local stLines 229 | # delete stored status lines 230 | $this.getStLines stLines 231 | if [ $((stLines)) -gt 0 ]; then 232 | $this.use 0 233 | # tput cd..ve........... 234 | echo -n $'\r\e[J\e[34h\e[?25h' > /dev/tty 235 | fi 236 | # restore signal default 237 | trap - WINCH 238 | # replace public methods with dummies 239 | eval "$this.winch() { :; }" 240 | eval "$this.use() { :; }" 241 | eval "$this.line() { :; }" 242 | eval "$this.stdout() { echo \"\$*\"; }" 243 | eval "$this.stderr() { echo \"\$*\" >&2; }" 244 | } 245 | 246 | # 247 | # Print on stdout. 248 | # 249 | # Clears the status lines, prints the requested output and redraws the 250 | # status lines. 251 | # 252 | # @param IFS 253 | # The first character in IFS is used to join multiple arguments, 254 | # if unset a single space is used 255 | # @param * 256 | # The strings to output 257 | # 258 | bsda:tty:Terminal.stdout() { 259 | echo -n $'\e[J' > /dev/tty 260 | echo "$*" 261 | $this.refresh 262 | } 263 | 264 | # 265 | # Print on stderr. 266 | # 267 | # Clears the status lines, prints the requested output and redraws the 268 | # status lines. 269 | # 270 | # @param IFS 271 | # The first character in IFS is used to join multiple arguments, 272 | # if unset a single space is used 273 | # @param * 274 | # The strings to output 275 | # 276 | bsda:tty:Terminal.stderr() { 277 | $class.stdout "$@" >&2 278 | } 279 | 280 | # 281 | # Install an output filter on the requested output. 282 | # 283 | # Filters are permanent, if the Terminal instance is deleted the 284 | # outputs are closed and remain unusable until redirected. 285 | # 286 | # @param 1 287 | # The file descriptor to filter, 1 (stdout) or 2 (stderr) 288 | # @param @ 289 | # The filter command (see eval) 290 | # @retval 0 291 | # Attaching the filter was successful 292 | # @retval 1 293 | # Invalid file descriptor (must be 1 or 2) 294 | # @retval 2 295 | # A filter was already attached to the output 296 | # @retval 3 297 | # Filter setup failed 298 | # 299 | bsda:tty:Terminal.filter() { 300 | local filter 301 | case "$1" in 302 | 1 | 2) 303 | $this.Filter$1 filter 304 | test -z "$filter" || return 2 305 | bsda:tty:Filter ${this}Filter$1 "$@" || return 3 306 | ;; 307 | *) 308 | return 1 309 | ;; 310 | esac 311 | } 312 | 313 | # 314 | # Draw all the status lines. 315 | # 316 | bsda:tty:Terminal.refresh() ( 317 | if [ $((${this}drLines)) -le 0 ]; then 318 | return 0 319 | fi 320 | exec > /dev/tty 321 | i=$((${this}drLines - 1)) 322 | # tput vi....... RA...... 323 | printf '%b' '\033[?25l\r' $($class:repeat $i '\n') '\033[?7l' 324 | while [ $i -gt 0 ]; do 325 | # tput ce.... up... 326 | eval "printf '%s\033[K\r\033M' \"\$${this}line$i\"" 327 | i=$((i - 1)) 328 | done 329 | # tput ce.... SA......ve............... 330 | eval "printf '%s\033[K\r\033[?7h\033[34h\033[?25h' \"\$${this}line0\"" 331 | ) 332 | -------------------------------------------------------------------------------- /src/bsda_tty_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | bsda_dir="${0%${0##*/}}" 3 | . ${bsda_dir:-.}/bsda_tty.sh 4 | bsda:tty:Terminal term 5 | $term.use 6 6 | $term.line 0 "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" 7 | index=0 8 | set +f 9 | width=$(($(tput co || echo 80) - 6)) 10 | for file in *; do 11 | $term.stdout "$file" 12 | status="$(printf "%03d: %${width}s" $index "$file")" 13 | $term.line $((index % 5 + 1)) "$status" 14 | index=$((index + 1)) 15 | done 16 | sleep 1 17 | $term.stdout "$(ls -f)" 18 | sleep 1 19 | # Sleep to see whether status lines were preserved. 20 | $term.delete 21 | -------------------------------------------------------------------------------- /src/bsda_util.sh: -------------------------------------------------------------------------------- 1 | test -n "$_bsda_util_" && return 0 2 | readonly _bsda_util_=1 3 | 4 | . ${bsda_dir:-.}/compat.sh 5 | 6 | # 7 | # A collection of utility functions. 8 | # 9 | 10 | # 11 | # Splits a string and maps the fields to a list of variables. 12 | # 13 | # The string is split by applying sh(1) Field Splitting using the 14 | # Input Field Separator. The values of unassigned fields are discarded. 15 | # 16 | # @param 1 17 | # The string to map 18 | # @param &@ 19 | # The variables to map fields of the string onto 20 | # @param IFS 21 | # The separator 22 | # 23 | bsda:util:map() { 24 | eval "bsda:util:_map() { 25 | $(i=0; shift && for arg in "$@"; do 26 | echo "${arg}=\"\${$((i += 1))}\"" 27 | done) 28 | }" 29 | bsda:util:_map $1 30 | unset -f bsda:util:_map 31 | } 32 | 33 | # 34 | # Generates a field splitting function. 35 | # 36 | # The function assigns fields to the given set of variables. All 37 | # arguments to the function are subjected to field splitting. 38 | # 39 | # The string is split by applying sh(1) Field Splitting using the 40 | # Input Field Separator. The values of unassigned fields are discarded. 41 | # 42 | # This requires the vis(1) command to safely store the IFS value. 43 | # 44 | # @param 1 45 | # Name of the generated function 46 | # @param &@ 47 | # The variables to map fields of the string onto 48 | # @param IFS 49 | # The separator 50 | # 51 | bsda:util:mapfun() { 52 | eval "$1() { 53 | local IFS 54 | IFS=\$'$(echo -n "${IFS}" | /usr/bin/vis -owe\')' 55 | set -- \$@ 56 | $(i=0; shift && for arg in "$@"; do 57 | echo " ${arg}=\"\${$((i += 1))}\"" 58 | done) 59 | }" 60 | } 61 | 62 | # 63 | # Join all arguments. 64 | # 65 | # @param &1 66 | # The destination variable for the joined string 67 | # @param 2 68 | # The separator character 69 | # @param * 70 | # The arguments to join 71 | # 72 | bsda:util:join() { 73 | setvar "$1" "$(IFS="$2"; shift 2; echo "$*")" 74 | } 75 | 76 | # 77 | # Split string by separator into space delimited list. 78 | # 79 | # @param &1 80 | # The source and destination variable 81 | # @param 2 82 | # The separator character 83 | # 84 | bsda:util:split() { 85 | setvar "$1" "$(IFS="$2"; eval "echo \$$1")" 86 | } 87 | 88 | # 89 | # Count the number of arguments. 90 | # 91 | # The destination variable is not counted. 92 | # 93 | # @param &1 94 | # The destination variable for the argument count 95 | # @param @ 96 | # The arguments to count 97 | # 98 | bsda:util:count() { 99 | setvar "$1" $(($# - 1)) 100 | } 101 | 102 | # 103 | # Test whether the first argument occurs in the following argument list. 104 | # 105 | # @param 1 106 | # The needle 107 | # @param @ 108 | # The haystack 109 | # @retval 0 110 | # The needle is in the haystack 111 | # @retval 1 112 | # No needle in the haystack 113 | # 114 | bsda:util:in() { 115 | local needle x 116 | needle="$1" 117 | shift 118 | for x in "$@"; do 119 | test "$x" = "$needle" && return 0 120 | done 121 | return 1 122 | } 123 | -------------------------------------------------------------------------------- /src/buildflags.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # This script parses a configuration file and returns it as make syntax. 4 | # 5 | 6 | 7 | # 8 | # Replaces something in a string, returns the new string. 9 | # 10 | # @param regexp 11 | # The regular expression to replace. 12 | # @param replace 13 | # What to replace it with. 14 | # @param haystack 15 | # The string to work on. 16 | # @return 17 | # The processed string. 18 | # 19 | function substitute(regexp, replace, haystack) { 20 | sub(regexp, replace, haystack); 21 | return haystack; 22 | } 23 | 24 | ## 25 | # This selects the first string from haystack that matches regexp. 26 | # 27 | # @param regexp 28 | # The regular expression to match. 29 | # @param haystack 30 | # The string to work on. 31 | # @return 32 | # The string matching regexp. 33 | # 34 | function grep(regexp, haystack) { 35 | sub(regexp, "<>&<>", haystack); 36 | sub(".*<>", "", haystack); 37 | sub("<>.*", "", haystack); 38 | return haystack; 39 | } 40 | 41 | ## 42 | # This function checks whether a string contains useable data. 43 | # 44 | # @param haystack 45 | # The string to check. 46 | # @return 47 | # 1 if the string contains useable data. 48 | # 49 | function isset(haystack) { 50 | if (haystack ~ "[^[:space:]]") 51 | return 1; 52 | } 53 | 54 | ## 55 | # Resolves paths at the beginning of a location pattern as 56 | # far as possible and prints the result. The string can 57 | # contain several paths separated by '|' and '&'. 58 | # 59 | # @param path 60 | # The location pattern to resolve. 61 | # 62 | function resolvePath(path, 63 | pattern) { 64 | # Find previous paths. 65 | # Take care of logical or connections. 66 | if (path ~ "\\|") { 67 | # Select all paths before this one. 68 | pattern = substitute("\\|[^\\|]*$", "", path); 69 | # Process the previous patterns. 70 | resolvePath(pattern); 71 | 72 | # Process the last one. 73 | path = grep("[^\\|]*$", path); 74 | printf("%s", " || "); 75 | } 76 | 77 | # Take care of logical and connections. 78 | if (path ~ "\\&") { 79 | # Select all paths before this one. 80 | pattern = substitute("\\&[^\\&]*$", "", path); 81 | # Process the previous patterns. 82 | resolvePath(pattern); 83 | 84 | # Process the last one. 85 | path = grep("[^\\&]*$", path); 86 | printf("%s", " && "); 87 | } 88 | 89 | # Trim the location pattern. 90 | sub("^[[:space:]]+", "", path); 91 | sub("[[:space:]]+$", "", path); 92 | 93 | # Check whether this is a pattern that should NOT be matched. 94 | if (path ~ "^!.*") { 95 | sub("^!", "", path); 96 | printf("%s", "!${.CURDIR:M"); 97 | } else 98 | printf("%s", "${.CURDIR:M"); 99 | 100 | # Paths that do not begin with '/' are never resolvable. 101 | if (path ~ "^[^/]") { 102 | printf("%s", path "}"); 103 | return; 104 | } 105 | 106 | pattern = path; 107 | # Select the resolvable part of the given path. 108 | sub("[/]*[^/]*[?*].*$", "", path); 109 | # Remember the part that cannot be resolved. 110 | sub("^" path, "", pattern); 111 | 112 | # If there is a resolvable part resolve it. 113 | if (path) { 114 | # If path exists, resolve and print it, else print the 115 | # original string. 116 | print("printf `[ -d \"" path "\" ] && (cd \"" path "\";pwd -P) || printf \"" path "\"`") | "sh"; 117 | close("sh"); 118 | } 119 | 120 | # Append the unresolvable part. 121 | printf("%s", pattern "}"); 122 | } 123 | 124 | ## 125 | # Prints the beginning of a new block. 126 | # 127 | # @param pattern 128 | # The location pattern for this block. 129 | # 130 | function beginBlock(pattern) { 131 | nesting[nesting_len++] = pattern; 132 | # Print the if. 133 | printf("%s", ".if "); 134 | 135 | # Print the if statement. 136 | resolvePath(pattern); 137 | 138 | # Append a newline. 139 | print(""); 140 | } 141 | 142 | ## 143 | # Parses lines in quoted mode, dealing with things like ending quotes 144 | # and single line quotes. 145 | # 146 | # @param line The line to parse into a quoted string. 147 | # 148 | function parseQuoted(line) { 149 | # If the quote ends here, take care of it. 150 | if (line ~ "\"") { 151 | # Print the part that is still within the quote. 152 | print(substitute("\".*", "\"", line)); 153 | 154 | # If present parse what is left of the line. 155 | sub("[^\"]*\"", "", line); 156 | if (isset(line)) 157 | return parse(line); 158 | 159 | # Or end here, if there is nothing left to parse. 160 | return; 161 | } 162 | 163 | # Lines that are in a quoted block do not get parsed. 164 | print(line); 165 | return 1; 166 | } 167 | 168 | ## 169 | # Parses a line of buildflags.conf file. 170 | # 171 | # @param line The line to parse. 172 | # @param quoted Set to true if the line is part of a 173 | # quoted string. 174 | # @return Whether the next line should be parsed in 175 | # quoted mode. 176 | # 177 | function parse(line, quoted, 178 | block) { 179 | # Lines in a quoted block only get parsed for the end of the block. 180 | if (quoted) 181 | return parseQuoted(line); 182 | 183 | # Spaces at the beginning are never required. 184 | sub("^[[:space:]]+", "", line); 185 | 186 | # Deal with comments. Comments behind parsable data will end up 187 | # in the line in front of that data. 188 | if (line ~ "^[^\"]*#") { 189 | # Print the comment. 190 | print(grep("#.*", line)); 191 | 192 | # Parse whatever was before the comment. 193 | sub("#.*", "", line); 194 | if (isset(line)) 195 | parse(line); 196 | 197 | return; 198 | } 199 | 200 | # Deal with make native directives. 201 | if (line ~ "^\\.") { 202 | print(line); 203 | return; 204 | } 205 | 206 | # Deal with quotes. 207 | if (line ~ "\"") { 208 | # Check whether the assignment is properly formatted. 209 | if (line ~ "[^[:space:]{}]+=[[:space:]]*\"") { 210 | # Take whatever is in front of the assignment 211 | # and parse it. 212 | block = substitute("[^[:space:]{}]+=[[:space:]]*\".*", "", line); 213 | if (isset(block)) 214 | parse(block); 215 | } else 216 | printf("%s", ".error "); 217 | 218 | # Print the variable assignment in front of the quoted string. 219 | block = grep("[^[:space:]{}]+=[[:space:]]*\"", line); 220 | printf("%s", block); 221 | 222 | # Parse the quoted string. 223 | block = substitute("[^\"]*\"", "", line); 224 | return parseQuoted(block); 225 | } 226 | 227 | # Close a block. 228 | if (line ~ "}") { 229 | # Parse whatever is before the end of the block. 230 | block = substitute("}.*", "", line); 231 | if (isset(block)) 232 | parse(block); 233 | 234 | # End the block. 235 | print(".endif # " nesting[--nesting_len]); 236 | 237 | # Parse whatever is behind the end of the block. 238 | sub("[^}]*}", "", line); 239 | if (line ~ "[^[:space:]]") 240 | parse(line); 241 | 242 | return; 243 | } 244 | 245 | # Begin a new block. 246 | if (line ~ "{") { 247 | # Open the new block. 248 | beginBlock(substitute("{.*", "", line)); 249 | 250 | # Parse whatever is inside. 251 | sub("[^{]*{", "", line); 252 | if (isset(line)) 253 | parse(line); 254 | 255 | return; 256 | } 257 | 258 | # Deal with compact variable assignments. 259 | if (line ~ "^[^[:space:]=]+=[^[:space:]]+") { 260 | # Print the assignment. 261 | print(grep("^[^[:space:]=]+=[^[:space:]]+", line)); 262 | 263 | # Parse what follows. 264 | block = substitute("^[^[:space:]=]+=[^[:space:]]+", "", line); 265 | if (isset(block)) 266 | parse(block); 267 | 268 | return; 269 | } 270 | 271 | # Deal with long variable assignments. 272 | if (line ~ "^[^[:space:]]+=") { 273 | # Print the whole line. 274 | print(line); 275 | return; 276 | } 277 | 278 | # Deal with negated flags. 279 | if (line ~ "^![^[:space:]=]+") { 280 | # Remove the negation symbol. 281 | line = substitute("^!", "", line); 282 | # Print the undefined command. 283 | print(".undef " grep("^[^[:space:]=]+", line)); 284 | 285 | # Parse what follows. 286 | block = substitute("^[^[:space:]=]+", "", line); 287 | if (isset(block)) 288 | parse(block); 289 | 290 | return; 291 | } 292 | 293 | # Deal with flags. 294 | if (line ~ "^[^[:space:]=]+") { 295 | # Print the flag. 296 | printf("%-23s %s", grep("^[^[:space:]=]+", line) "=", "yes\n"); 297 | 298 | # Parse what follows. 299 | block = substitute("^[^[:space:]=]+", "", line); 300 | if (isset(block)) 301 | parse(block); 302 | 303 | return; 304 | } 305 | 306 | # Print empty lines. 307 | if (!isset(line)) 308 | print(""); 309 | } 310 | 311 | { 312 | # Parse the file by line. 313 | quoted = parse($0, quoted); 314 | } 315 | -------------------------------------------------------------------------------- /src/buildflags.conf.sample: -------------------------------------------------------------------------------- 1 | # 2 | # Dear bsda2 user, beware for this example is a copy of my very own 3 | # buildflags.conf and I'm of a rather experimental nature when it 4 | # comes to building software. There are things done here that are 5 | # definitely not supported or recommended. 6 | # 7 | # If some of the variables used here don't make sense to you, search for them 8 | # in the buildflags.mk(1), ports(7), make.conf(5), src.conf(5) or in the file 9 | # %%PORTS%%/Mk/bsd.port.mk. 10 | # 11 | # Apart from that, I hope the syntax is easy enough to understand with 12 | # the help of the buildflags.conf(1) manual page. 13 | # 14 | # Note that buildflags need to be enabled manually by making a make.conf 15 | # entry (see buildflags.mk(1)). 16 | # 17 | # - Dominic Fandrey 18 | # 19 | 20 | # ---< configure buildworld/buildkernel >-------------------------------------- 21 | /usr/src | /usr/src/*{ 22 | WITH_CCACHE_BUILD 23 | THREADS=4 24 | 25 | # Don't clean 26 | NO_CLEAN 27 | } 28 | # ----------------------------------------------------------------------------- 29 | 30 | # ---< configure ports >------------------------------------------------------- 31 | %%PORTS%%/*{ 32 | # Clustering 33 | USE_DISTCC 34 | USE_CCACHE 35 | 36 | # Common settings that are applied to all ports in hope to do some good 37 | DEFAULT_VERSIONS+= ssl=libressl linux=c6 38 | PAPERSIZE=a4 39 | 40 | # Problems with ccache/distcc 41 | */audio/cmus {!USE_CCACHE !USE_DISTCC} 42 | */archivers/lzip {!USE_CCACHE !USE_DISTCC} 43 | */archivers/unrar {!USE_CCACHE !USE_DISTCC} 44 | */devel/boost* {!USE_CCACHE !USE_DISTCC} 45 | */devel/llvm39 {!USE_CCACHE !USE_DISTCC} 46 | */devel/valgrind {!USE_CCACHE !USE_DISTCC} 47 | */editors/vim* {!USE_CCACHE !USE_DISTCC} 48 | */emulators/virtualbox-* {!USE_CCACHE !USE_DISTCC} 49 | */print/freetype {!USE_CCACHE !USE_DISTCC} 50 | */textproc/xmlto {!USE_CCACHE !USE_DISTCC} 51 | */security/libgpg-error {!USE_CCACHE !USE_DISTCC} 52 | */sysutils/fusefs-davfs2 {!USE_CCACHE !USE_DISTCC} 53 | */www/node {!USE_CCACHE !USE_DISTCC} 54 | */www/nspluginwrapper {!USE_CCACHE !USE_DISTCC} 55 | */www/webkit2-gtk3 {!USE_CCACHE !USE_DISTCC} 56 | */x11/dgs {!USE_CCACHE !USE_DISTCC} 57 | */x11/gnome-menus {!USE_CCACHE !USE_DISTCC} 58 | */x11/xbrightness {!USE_CCACHE !USE_DISTCC} 59 | 60 | # Compiler troubles 61 | */devel/py-orbit {USE_GCC=any} 62 | */devel/sdcc {USE_GCC=any} 63 | */graphics/gimp-app {USE_GCC} 64 | 65 | # Bug workarounds 66 | */print/linux-c6-cups-libs{USE_LINUX_RPM=yes} 67 | } 68 | # ----------------------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /src/buildflags.mk: -------------------------------------------------------------------------------- 1 | # Default locations 2 | BUILDFLAGS_PARSER?= %%DATADIR%%/buildflags.awk 3 | BUILDFLAGS_CONF?= %%PREFIX%%/etc/buildflags.conf 4 | BUILDFLAGS_USER?= ${HOME}/.buildflags.conf 5 | BUILDFLAGS_TMP?= %%TMP%%/buildflags.tmp.mk.${USER} 6 | 7 | BUILDFLAGS_DISTCC?= %%PREFIX%%/bin/distcc 8 | BUILDFLAGS_CCACHE?= %%PREFIX%%/bin/ccache 9 | 10 | # Parse configurations 11 | .if exists(${BUILDFLAGS_CONF}) && exists(${BUILDFLAGS_USER}) 12 | BUILDFLAGS!= test "${BUILDFLAGS_TMP}" -nt "${BUILDFLAGS_CONF}" \ 13 | -a "${BUILDFLAGS_TMP}" -nt "${BUILDFLAGS_USER}" \ 14 | || "${BUILDFLAGS_PARSER}" "${BUILDFLAGS_USER}" \ 15 | "${BUILDFLAGS_CONF}" \ 16 | > "${BUILDFLAGS_TMP}"; echo 17 | .elif exists(${BUILDFLAGS_CONF}) 18 | BUILDFLAGS!= test "${BUILDFLAGS_TMP}" -nt "${BUILDFLAGS_CONF}" \ 19 | || "${BUILDFLAGS_PARSER}" "${BUILDFLAGS_CONF}" \ 20 | > "${BUILDFLAGS_TMP}"; echo 21 | .elif exists(${BUILDFLAGS_USER}) 22 | BUILDFLAGS!= test "${BUILDFLAGS_TMP}" -nt "${BUILDFLAGS_USER}" \ 23 | || "${BUILDFLAGS_PARSER}" "${BUILDFLAGS_USER}" \ 24 | > "${BUILDFLAGS_TMP}"; echo 25 | .endif 26 | 27 | # Include generated make file 28 | .sinclude "${BUILDFLAGS_TMP}" 29 | 30 | # Deprecated, because USE_ variables are not supposed to be user-defined 31 | .if defined(USE_CCACHE) 32 | .warning "buildflags.mk: USE_CCACHE is deperecated in favour of WITH_CCACHE_BUILD" 33 | .endif 34 | .if defined(USE_DISTCC) 35 | .warning "buildflags.mk: USE_DISTCC is deperecated" 36 | .endif 37 | 38 | # Use ccache and distcc. 39 | .if defined(USE_CCACHE) && !${CC:M*ccache*} && exists(${BUILDFLAGS_CCACHE}) && defined(USE_DISTCC) && !${CC:M*distcc*} && exists(${BUILDFLAGS_DISTCC}) 40 | CC:= env CCACHE_PREFIX=${BUILDFLAGS_DISTCC} ${BUILDFLAGS_CCACHE} ${CC} 41 | CPP:= env CCACHE_PREFIX=${BUILDFLAGS_DISTCC} ${BUILDFLAGS_CCACHE} ${CPP} 42 | CXX:= env CCACHE_PREFIX=${BUILDFLAGS_DISTCC} ${BUILDFLAGS_CCACHE} ${CXX} 43 | # Use distcc. 44 | .elif defined(USE_DISTCC) && !${CC:M*distcc*} && exists(${BUILDFLAGS_DISTCC}) && !${CC:M*ccache*} 45 | CC:= ${BUILDFLAGS_DISTCC} ${CC} 46 | CPP:= ${BUILDFLAGS_DISTCC} ${CPP} 47 | CXX:= ${BUILDFLAGS_DISTCC} ${CXX} 48 | # Use ccache. 49 | .elif defined(USE_CCACHE) && !${CC:M*ccache*} && exists(${BUILDFLAGS_CCACHE}) && !${CC:M*distcc*} 50 | CC:= ${BUILDFLAGS_CCACHE} ${CC} 51 | CPP:= ${BUILDFLAGS_CCACHE} ${CPP} 52 | CXX:= ${BUILDFLAGS_CCACHE} ${CXX} 53 | .endif 54 | 55 | # Activate normal parallel builds. 56 | .if defined(THREADS) 57 | .MAKEFLAGS: -j${THREADS} 58 | .endif 59 | -------------------------------------------------------------------------------- /src/compat.sh: -------------------------------------------------------------------------------- 1 | test -n "$_compat_" && return 0 2 | readonly _compat_=1 3 | 4 | # 5 | # Compatibility hacks. 6 | # 7 | 8 | # Emulate setvar for shells that don't have it, i.e. bash. 9 | if ! setvar 2>&-; then 10 | setvar() { 11 | eval "${1:+$1="\$2"}" 12 | } 13 | fi 14 | 15 | # Setup getvar for symmetry with setvar 16 | if ! getvar 2>&-; then 17 | # 18 | # Returns a variable from a given reference. 19 | # 20 | # The variable is either written to a named variable, or in 21 | # absence of one, output to stdout. 22 | # 23 | # @param &1 24 | # The name of the variable to write to 25 | # @param 2 26 | # The reference to the variable to return 27 | # 28 | getvar() { 29 | eval "${1:-${2:+echo \"\$$2\"} #}=\"\${$2}\"" 30 | } 31 | fi 32 | -------------------------------------------------------------------------------- /src/distviper: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/distviper.sh: -------------------------------------------------------------------------------- 1 | test -n "$_distviper_" && return 0 2 | readonly _distviper_=1 3 | 4 | . ${bsda_dir:-.}/bsda_tty.sh 5 | . ${bsda_dir:-.}/bsda_opts.sh 6 | . ${bsda_dir:-.}/pkg_info.sh 7 | . ${bsda_dir:-.}/bsda_bsdmake.sh 8 | . ${bsda_dir:-.}/bsda_fmt.sh 9 | 10 | # 11 | # The session class for distviper. 12 | # 13 | bsda:obj:createClass distviper:Session \ 14 | a:private:Flags=bsda:opts:Flags \ 15 | a:private:Term=bsda:tty:Terminal \ 16 | r:private:keep "The kind of files to keep" \ 17 | r:private:portsdir "The PORTSDIR" \ 18 | r:private:distdir "The DISTDIR" \ 19 | i:private:init "The constructor" \ 20 | x:private:params "Process command line arguments" \ 21 | x:private:help "Print usage message" \ 22 | x:private:error "Print error message" \ 23 | x:private:status "Print status" \ 24 | x:private:getMakeVar "Retrieve variables from make" \ 25 | x:private:run "Select files and delete them" 26 | 27 | # 28 | # Set up the session and run it. 29 | # 30 | # @param @ 31 | # Command line arguments 32 | # 33 | distviper:Session.init() { 34 | # Setup terminal manager 35 | bsda:tty:Terminal ${this}Term 36 | 37 | # Set default mode 38 | setvar ${this}keep all 39 | 40 | # Read command line arguments 41 | bsda:opts:Flags ${this}Flags 42 | $this.params "$@" || return $? 43 | 44 | # Perform 45 | $this.run 46 | } 47 | 48 | # 49 | # Parse command line arguments. 50 | # 51 | # @param @ 52 | # The command line arguments 53 | # 54 | distviper:Session.params() { 55 | local options flags option term 56 | 57 | $this.Term term 58 | 59 | bsda:opts:Options options \ 60 | DEMO -d --demo 'Just print what would have been done' \ 61 | HELP -h --help 'Display the list of command arguments' \ 62 | INTERACTIVE -i --interactive 'Ask for confirmation before deleting' \ 63 | NOCHKSUM -n --no-checksum 'Skip checksum checks' \ 64 | QUIET -q --quiet 'Do not print anything' \ 65 | VERBOSE -v --verbose 'Verbose output' 66 | $caller.delete $options 67 | 68 | $this.Flags flags 69 | while [ $# -gt 0 ]; do 70 | $options.getopt option "$1" 71 | case "$option" in 72 | HELP) 73 | $this.help "$options" 74 | ;; 75 | OPT_UNKNOWN) 76 | $this.error "Unknown parameter \"$1\"." 77 | return 1 78 | ;; 79 | OPT_SPLIT) 80 | eval "$bsda_opts_split" 81 | continue 82 | ;; 83 | OPT_NOOPT) 84 | # Assume this is a mode 85 | $flags.add KEEP 86 | setvar ${this}keep "$1" 87 | if $flags.check KEEP -gt 1; then 88 | $this.error "Too many arguments: ... $@" 89 | return 3 90 | fi 91 | ;; 92 | *) 93 | # Catch all the flags 94 | $flags.add "$option" 95 | ;; 96 | esac 97 | shift 98 | done 99 | 100 | if $flags.check VERBOSE && $flags.check QUIET; then 101 | $this.error "Conflicting options -v and -q supplied." 102 | return 1 103 | fi 104 | 105 | if $flags.check QUIET -eq 0; then 106 | $term.use 1 107 | fi 108 | 109 | $this.getMakeVar ${this}portsdir PORTSDIR || return $? 110 | $this.getMakeVar ${this}distdir DISTDIR || return $? 111 | 112 | local keep 113 | $this.getKeep keep 114 | case "$keep" in 115 | all | installed) 116 | # Usual modes 117 | ;; 118 | fast) 119 | setvar ${this}keep installed 120 | $flags.add NOCHKSUM 121 | ;; 122 | thorough) 123 | setvar ${this}keep all 124 | ;; 125 | *) 126 | $this.error "Unknown category of files to keep: $keep" 127 | return 2 128 | ;; 129 | esac 130 | } 131 | 132 | # 133 | # Print usage message. 134 | # 135 | # @param &1 136 | # A reference to a bsda:opts:Options instance 137 | # 138 | distviper:Session.help() { 139 | local usage 140 | $1.usage usage "\t%.2s, %-13s %s\n" 141 | $($this.Term).stdout "usage: distviper [-dhinqv] [keep] 142 | $(echo -n "$usage" | /usr/bin/sort -f)" 143 | exit 0 144 | } 145 | 146 | # 147 | # Print error message. 148 | # 149 | # @param * 150 | # The message to print 151 | # 152 | distviper:Session.error() { 153 | $($this.Term).stderr "${0##*/}: $*" 154 | } 155 | 156 | # 157 | # Puts a message on the terminal. 158 | # 159 | # In verbose mode it is printed on stdout, otherwise the status line 160 | # is updated. 161 | # 162 | # @param 1 163 | # The status message 164 | # 165 | distviper:Session.status() { 166 | local term 167 | $this.Term term 168 | if $($this.Flags).check VERBOSE; then 169 | $term.stdout "$1" 170 | else 171 | $term.line 0 "$1" 172 | fi 173 | } 174 | 175 | # 176 | # Pull a variable from make. 177 | # 178 | # Return a variable from /usr/share/mk/bsd.port.mk. 179 | # 180 | # @param &1 181 | # The variable to return the value to 182 | # @param 2 183 | # The name of the make variable to get 184 | # @retval 0 185 | # The variable was successfully acquired 186 | # @retval 1 187 | # The variable is not a directory 188 | # 189 | distviper:Session.getMakeVar() { 190 | local term value 191 | $this.Term term 192 | if ! value="$(bsda:bsdmake -f/usr/share/mk/bsd.port.mk \ 193 | -V"$2" 2>&1)"; then 194 | $this.error "$bsda_bsdmake -V$2 failed:" 195 | $term.stderr "$value" 196 | return 1 197 | fi 198 | if [ ! -d "$value" ]; then 199 | $this.error "The $2 '$value' is not a directory." 200 | return 1 201 | fi 202 | $caller.setvar $1 "$value" 203 | } 204 | 205 | # 206 | # A static method retrieve file|chksum pairs. 207 | # 208 | # @param @ 209 | # The folders to search for distinfo files 210 | # 211 | distviper:Session.run_find() { 212 | /usr/bin/find "$@" -type f -name distinfo -not -exec /usr/bin/awk ' 213 | /^SHA/ { 214 | sub(/^\(/, "", $2) 215 | sub(/\)$/, "", $2) 216 | pair = $2 "|" $4 217 | if (!(pair in pairs)) { 218 | pairs[pair] 219 | print pair 220 | } 221 | }' '{}' + 2>&1 | /usr/bin/sort 222 | } 223 | 224 | # 225 | # Static method outputting file|chksum pairs for all ports. 226 | # 227 | # @param portsdir 228 | # The PORTSDIR 229 | # 230 | distviper:Session.run_find_all() { 231 | $class.run_find "$portsdir" 232 | } 233 | 234 | # 235 | # Static method outputting file|chksum pairs for installed ports. 236 | # 237 | # @param portsdir 238 | # The PORTSDIR 239 | # 240 | distviper:Session.run_find_installed() { 241 | cd "$portsdir" 242 | $class.run_find $(pkg:info:origins) 243 | } 244 | 245 | # 246 | # Static method outputting the present distfiles. 247 | # 248 | # @param distdir 249 | # The DISTDIR 250 | # 251 | distviper:Session.run_find_present() { 252 | /usr/bin/find -s "$distdir" -type f | /usr/bin/sed "s|^$distdir/||" 253 | } 254 | 255 | # 256 | # Select and delete obsolete distfiles. 257 | # 258 | distviper:Session.run() { 259 | local IFS term flags keep portsdir distdir keepSums keepFiles 260 | IFS=$'\n' 261 | $this.Term term 262 | $this.Flags flags 263 | $this.getKeep keep 264 | $this.getPortsdir portsdir 265 | $this.getDistdir distdir 266 | 267 | $this.status "Creating a list of $keep distfiles" 268 | keepSums="$($class.run_find_$keep)" 269 | keepFiles="$(echo "$keepSums" | /usr/bin/sed 's/|.*//')" 270 | 271 | if echo "$keepSums" | /usr/bin/grep -qv '|[0-9a-f]*$'; then 272 | $this.error "Access to $portsdir failed" 273 | $term.stderr "$(echo "$keepSums" | /usr/bin/grep -v '|[0-9a-f]*$')" 274 | exit 1 275 | fi 276 | 277 | $this.status "Creating a list of distfiles to delete" 278 | local verify files file obsoleteFiles mismatchFiles chksum fcount i fmt 279 | local fsize osize msize ocount mcount 280 | osize=0 281 | msize=0 282 | verify= 283 | $flags.check NOCHKSUM -eq 0 && verify=1 284 | log obsoleteFiles= 285 | log mismatchFiles= 286 | files="$($class.run_find_present)" 287 | fcount=$(($(echo "$files" | /usr/bin/wc -w))) 288 | fmt="Checking file %${#fcount}d of $fcount: %s" 289 | i=0 290 | for file in $files; do 291 | i=$((i + 1)) 292 | $term.line 0 "$(printf "$fmt" $i "$file")" 293 | if ! echo "$keepFiles" | /usr/bin/grep -qFx "$file"; then 294 | log obsoleteFiles.push_back "$file" 295 | fsize=$(/usr/bin/stat -f %z "$distdir/$file") 296 | osize=$((osize + fsize)) 297 | continue 298 | fi 299 | test -z "$verify" && continue 300 | 301 | chksum="$(/sbin/sha256 < "$distdir/$file")" 302 | if ! echo "$keepSums" | /usr/bin/grep -qFx "$file|$chksum"; then 303 | log mismatchFiles.push_back "$file" 304 | fsize=$(/usr/bin/stat -f %z "$distdir/$file") 305 | msize=$((msize + fsize)) 306 | continue 307 | fi 308 | done 309 | $term.deactivate 310 | 311 | # Perform deletions 312 | local rmcmd rmflags 313 | rmcmd=/bin/rm 314 | log rmflags= -f 315 | if $flags.check INTERACTIVE -eq 0; then 316 | # !interactive && !quiet => -v 317 | $flags.check QUIET -eq 0 && log rmflags.push_back -v 318 | else 319 | # interactive => -i 320 | log rmflags.push_back -i 321 | fi 322 | 323 | if $flags.check DEMO; then 324 | rmcmd=echo 325 | log rmflags= 326 | fi 327 | 328 | local s p 329 | bsda:fmt:iec s p "$osize" 330 | : ${p:=B} 331 | $this.status "Remove obsolete files ($s${p%i}):" 332 | for file in $obsoleteFiles; do 333 | $rmcmd $rmflags "$distdir/$file" 334 | done 335 | bsda:fmt:iec s p "$msize" 336 | : ${p:=B} 337 | $this.status "Remove files with checksum mismatches ($s${p%i}):" 338 | for file in $mismatchFiles; do 339 | $rmcmd $rmflags "$distdir/$file" 340 | done 341 | if $flags.check QUIET -eq 0; then 342 | bsda:fmt:iec s p $((osize + msize)) 343 | : ${p:=B} 344 | log obsoleteFiles.count ocount 345 | log mismatchFiles.count mcount 346 | $term.stdout "Removed $((ocount + mcount)) file(s) ($s${p%i})." 347 | fi 348 | 349 | return 0 350 | } 351 | -------------------------------------------------------------------------------- /src/interrupt.mk: -------------------------------------------------------------------------------- 1 | # Forward interruptions to a calling shell script 2 | .INTERRUPT: 3 | @kill -INT ${BSDA_PID} 4 | 5 | # Proceed with the Makefile 6 | .include "Makefile" 7 | -------------------------------------------------------------------------------- /src/ldd_filter.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # Compile a set of dynamic linker errors. 4 | # 5 | # Expects the output of ldd(1) on input and collects a set of errors 6 | # and missing dpendencies for each binary given to ldd. 7 | # The script discards irrelevant errors (such as the information 8 | # that the given file is not an ELF executable/library). 9 | # When necessary the binary is fed into readelf(1) to obtain more 10 | # specific information. 11 | # 12 | # Each error is output as a set: 13 | # 14 | # 1. binary name 15 | # 2. missing library or other info 16 | # 3. tags, a comma separated list of tags consisting of a single 17 | # primary tag followed by optional secondary tags 18 | # 19 | # The following primary tags exist: 20 | # 21 | # | Tag | Binary | Info | Description | 22 | # |---------|--------|---------------------|--------------------------------| 23 | # | compat | yes | dependency path | Path matches `*/lib*/compat/*` | 24 | # | miss | yes | dependency filename | Dependency was not found | 25 | # | verbose | yes | error message | Binary specific ldd(1) error | 26 | # | invalid | - | whole input line | Unknown ldd(1) output | 27 | # 28 | # The following secondary tags can be appended to primary tags with a comma 29 | # separator: 30 | # 31 | # | Tag | Description | 32 | # |----------|-----------------------------------------------------------------| 33 | # | direct | The missing dependency is a direct dependency | 34 | # | indirect | The missing dependency is an indirect dependency | 35 | # | os/abi | The given binary is an unbranded ELF binary, i.e. OS/ABi = NONE | 36 | # 37 | # The direct and indirect tags are mutually exclusive. 38 | # 39 | # Originally the ldd(1) filter was introduced as an inline awk command 40 | # to work around a bug in formatted ldd output: 41 | # [see](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=259069). 42 | # 43 | # Over time more and more functionality was introduced into the filter 44 | # until it became its own entity. 45 | # 46 | # @param OFS 47 | # Set to control the tuple item separator 48 | # @param COMPAT 49 | # Set to 1 to flag binaries linking compatibility libraries, 50 | # set to 0 otherwise 51 | # @param VERBOSE 52 | # Set to 1 to output interesting information that does not 53 | # indicate a missing dependency 54 | # @param FILTER 55 | # Set to 1 to determine secondary tags, if 0 no information 56 | # whether a dependency is direct or indirect or if the binary 57 | # is unbranded (i.e. the os/abi tag), will be produced 58 | # 59 | 60 | # 61 | # Output each row only once, at least on FreeBSD 62 | # stable/13-n247530-3637d2a1835e ldd(1) prints missing dependencies 63 | # many times. 64 | # 65 | # This output filter changed the runtime for `pkg_libck samba413` after 66 | # a libicu update from >120s to ~4s, supposedly because every reported 67 | # missing dependency corresponds to a readelf(1) call and this package 68 | # produces 23601 lines of output without the filter. 69 | # 70 | # @param bin,info,tag 71 | # A tuple consisting of a binary name, the issue information 72 | # and a set of tags 73 | # 74 | function printrow(bin, info, tags) { 75 | # print only if this binary/info combination has not been printed yet 76 | if (!ROW[bin, info]++) { 77 | print(bin, info, tags) 78 | } 79 | } 80 | 81 | # 82 | # Call readelf on the given binary and add secondory tags. 83 | # 84 | # - `os/abi` for unbranded ELF binaries 85 | # - `direct` where the given library is a direct dependency of the 86 | # given binary 87 | # - `indirect` where the given library is an indirect dependency of 88 | # the given binary 89 | # 90 | # @param bin,lib,tags 91 | # A tuble consisting of a binary name, the missed library and 92 | # a tag, either miss or compat 93 | # 94 | function readelf_tag(bin, lib, tags, _cmd, _bin, _direct, _osabi) { 95 | if (FILTER && !READELF[bin, lib]++) { 96 | # just escape every character in the file name, this 97 | # should at least cover the easy stuff like whitespace 98 | _bin = bin 99 | gsub(/./, "\\\\&", _bin) 100 | _cmd = "/usr/bin/readelf -hd " _bin 101 | _direct = _osabi = 0 102 | while ((_cmd | getline) > 0) { 103 | _direct += (0 < index($0, "Shared library: [" lib "]")) 104 | _osabi += (/^ *OS\/ABI: *NONE$/) 105 | } 106 | close(_cmd) 107 | # bail if nothing should be printed, but we must not 108 | # bail before calling close(_cmd) 109 | if (!VERBOSE && (_osabi || !_direct)) { return } 110 | # ^~~~~~~~ ^~~~~~ ^~~~~~~~ 111 | # | | | bail on indirect dependency 112 | # | | bail if OS/ABI is not set (binary is not branded) 113 | # | never bail when verbose 114 | 115 | # append tags 116 | tags = tags (_osabi ? ",os/abi" : "") \ 117 | (_direct ? ",direct" : ",indirect") 118 | } 119 | printrow(bin, lib, tags) 120 | } 121 | 122 | # update binary name 123 | /^[^\t].*:$/ { 124 | sub(/:$/, "") 125 | BIN=$0 126 | next 127 | } 128 | 129 | # compat library 130 | COMPAT && /^\t.* => .*\/lib[^\/]*\/compat\/.* \(0x[0-9a-f]+\)$/ { 131 | sub(/^\t.* => /, "") 132 | sub(/ \(0x[0-9a-f]+\)$/, "") 133 | readelf_tag(BIN, $0, "compat") 134 | next 135 | } 136 | 137 | # missing library 138 | /\(0\)$/ || /^\t.* => not found \(0x[0-9a-f]+\)$/ { 139 | sub(/^\t/, "") 140 | sub(/ => .*/, "") 141 | readelf_tag(BIN, $0, "miss") 142 | next 143 | } 144 | 145 | # ignore 146 | /^\t.* \(0x[0-9a-f]+\)$/ || # library was found 147 | /^ldd: .*: not a dynamic ELF executable$/ || # non-executable 148 | /^ldd: .*: not a .* ELF shared object$/ || # non-executable 149 | /^ldd: .*: Invalid argument$/ || # non-executable 150 | /^ldd: .*: unsupported machine$/ || # cross-platform executable 151 | /^\[preloaded\]$/ || # start of preloaded section 152 | /^.*: exit status 1$/ { # redundant message 153 | next 154 | } 155 | 156 | # verbose error 157 | VERBOSE && /ldd: .*: .*/ { 158 | sub(/ldd: /, "") 159 | file=$0 160 | sub(/: [^:]*$/, "", file) 161 | sub(/.*: /, "") 162 | printrow(file, $0, "verbose") 163 | next 164 | } 165 | 166 | # unknown/invalid ldd output 167 | VERBOSE { 168 | printrow("", $0, "invalid") 169 | } 170 | 171 | -------------------------------------------------------------------------------- /src/loaderupdate: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/makeplist: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/makeplist_filter.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # Combine multiple pkg-plist files into a single pkg-plist. 4 | # 5 | # This takes a list of all options as command line arguments. The 6 | # order of the options determines the order option specific plist 7 | # entries appear in the combined output. 8 | # 9 | # The plists must be fed into stdin and preceded by a line starting 10 | # with `OPTIONS:` followed by a white space separated list of the 11 | # active options. 12 | # 13 | # Files that appear with all options are listed first, followed by 14 | # the option specific output. 15 | # 16 | # @param ARGV 17 | # A sorted list of options 18 | # 19 | 20 | # Get the order of options 21 | BEGIN { 22 | OPTION_STR["DOCS"] = "%%PORTDOCS%%" 23 | OPTION_STR["EXAMPLES"] = "%%PORTEXAMPLES%%" 24 | for (i = 1; i < ARGC; ++i) { 25 | OPTIONS_ORDERD[i] = ARGV[i] 26 | CNT_OPT_FILES[ARGV[i]] = 0 27 | if (!(ARGV[i] in OPTION_STR)) { 28 | OPTION_STR[ARGV[i]] = "%%" ARGV[i] "%%" 29 | } 30 | if (!("NO_" ARGV[i] in OPTION_STR)) { 31 | OPTION_STR["NO_" ARGV[i]] = "%%NO_" ARGV[i] "%%" 32 | } 33 | delete ARGV[i] 34 | } 35 | CNT_FILES = 0 36 | } 37 | 38 | # Get the options the following files were staged with 39 | /^OPTIONS:/ { 40 | delete aoptions 41 | sub(/^OPTIONS: */, "") 42 | cnt_aoptions = split($0, aoptions) 43 | for (i = 1; i <= cnt_aoptions; ++i) { 44 | ++OPTIONS[aoptions[i]] 45 | } 46 | ++CONFIGS 47 | next 48 | } 49 | 50 | # Collect files 51 | { 52 | # Record order of file 53 | if (!($0 in FILES)) { 54 | FILES_ORDERED[++CNT_FILES] = $0 55 | } 56 | # Count occurence of file 57 | ++FILES[$0] 58 | # The same book keepin per option 59 | for (i = 1; i <= cnt_aoptions; ++i) { 60 | option = aoptions[i] 61 | # Record order of file for option 62 | if (!OPT_FILES[option, $0]) { 63 | OPT_FILES_ORDERED[option, ++CNT_OPT_FILES[option]] = $0 64 | } 65 | # Count occurence of file by option 66 | ++OPT_FILES[option, $0] 67 | } 68 | } 69 | 70 | # Print files 71 | END { 72 | # Print files common to all configurations 73 | for (i = 1; i <= CNT_FILES; ++i) { 74 | file = FILES_ORDERED[i] 75 | if (FILES[file] == CONFIGS) { 76 | print file 77 | delete FILES[file] 78 | } 79 | } 80 | # Print option specific files 81 | for (i = 1; OPTIONS_ORDERD[i]; ++i) { 82 | option = OPTIONS_ORDERD[i] 83 | for (p = 1; p <= CNT_OPT_FILES[option]; ++p) { 84 | file = OPT_FILES_ORDERED[option, p] 85 | # Skip files that have already been printed 86 | if (!(file in FILES)) { continue } 87 | # Print file if it only occurs for the current 88 | # option 89 | if (OPT_FILES[option, file] == OPTIONS[option] && 90 | OPT_FILES[option, file] == FILES[file]) { 91 | print OPTION_STR[option] file 92 | delete FILES[file] 93 | } 94 | } 95 | for (p = 1; p <= CNT_FILES; ++p) { 96 | file = FILES_ORDERED[p] 97 | if (!(file in FILES)) { continue } 98 | # Print file if it occurs everywhere but with 99 | # this option 100 | if (!OPT_FILES[option, file] && 101 | FILES[file] + OPTIONS[option] == CONFIGS) { 102 | print OPTION_STR["NO_" option] file 103 | delete FILES[file] 104 | } 105 | } 106 | } 107 | # Print all files that have not been printed 108 | for (i = 1; i <= CNT_FILES; ++i) { 109 | file = FILES_ORDERED[i] 110 | if (!(file in FILES)) { continue } 111 | print "@fail " file " could not be mapped to an option!" 112 | msg = "@fail Candidates:" 113 | for (p = 1; OPTIONS_ORDERD[p]; ++p) { 114 | if (OPT_FILES[OPTIONS_ORDERD[p], file]) { 115 | msg = msg " " OPTIONS_ORDERD[p] 116 | } 117 | } 118 | print msg 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/makeplist_keywords.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # Transplant special keywords from one plist to the next, seeking 4 | # to put them into the same context. 5 | # 6 | # @param ARGV[1] 7 | # The new plist file name 8 | # @param ARGV[2] 9 | # The old plist file name 10 | # 11 | 12 | BEGIN { 13 | # List of auto-prefixes 14 | AUTO["@sample "] 15 | } 16 | 17 | # Index the new plist file 18 | NR == FNR { 19 | PLIST[NR] = $0 20 | PLIST_IND[$0] = NR 21 | PLIST_CNT = NR 22 | next 23 | } 24 | 25 | # Index the old plist file 26 | { 27 | OLD_PLIST[NR - PLIST_CNT] = $0 28 | OLD_PLIST_CNT = NR - PLIST_CNT 29 | # Strip auto-prefixes from the new plist if the file 30 | # is listed but does not have the prefix. 31 | for (auto in AUTO) { 32 | if (auto $0 in PLIST_IND) { 33 | PLIST[PLIST_IND[auto $0]] = $0 34 | } 35 | } 36 | } 37 | 38 | # Print plist 39 | END { 40 | PLIST_PRINT = 1 41 | # Find keyword in old plist 42 | for (NR = 1; NR <= OLD_PLIST_CNT; ++NR) { 43 | # Accumulate files that come before the next 44 | # keyword. 45 | PREVIOUS[OLD_PLIST[NR]] 46 | 47 | # Check for keyword 48 | if (OLD_PLIST[NR] ~ /(^|%%)@[[:alnum:]]+ /) { 49 | # Skip if already in new plist 50 | if (OLD_PLIST[NR] in PLIST_IND) { 51 | continue 52 | } 53 | # Read the new plist file from the 54 | # back until a line from the PREVIOUS 55 | # list is encountered. 56 | for (i = PLIST_CNT; i >= PLIST_PRINT; --i) { 57 | if (PLIST[i] in PREVIOUS) { 58 | break 59 | } 60 | } 61 | # Print everything before and including 62 | # the match. 63 | for (; PLIST_PRINT <= i; ++PLIST_PRINT) { 64 | print PLIST[PLIST_PRINT] 65 | } 66 | # Print the current (keyword) line 67 | print OLD_PLIST[NR] 68 | } 69 | } 70 | 71 | # Print remaining plist 72 | for (; PLIST_PRINT <= PLIST_CNT; ++PLIST_PRINT) { 73 | print PLIST[PLIST_PRINT] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/options.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Creates a list of all options for the port in the current location. 3 | # 4 | # The BSDA_GROUPS format is as follows: 5 | # ${group}|${options} 6 | # 7 | # The options are a comma seperated list. 8 | # 9 | # The BSDA_OPTIONS format is as follows: 10 | # ${group}|${opt}|${${opt}_IMPLIES}|${${opt}_PREVENTS} 11 | # 12 | # ${opt}_IMPLIES and ${opt}_PREVENTS are comma seperated lists of options. 13 | # 14 | 15 | .include "Makefile" 16 | 17 | .for _grp in OPTIONS_DEFINE \ 18 | ${OPTIONS_SINGLE:S,^,OPTIONS_SINGLE_,} \ 19 | ${OPTIONS_RADIO:S,^,OPTIONS_RADIO_,} \ 20 | ${OPTIONS_MULTI:S,^,OPTIONS_MULTI_,} \ 21 | ${OPTIONS_GROUP:S,^,OPTIONS_GROUP_,} 22 | .if ! empty(${_grp}) 23 | BSDA_GROUPS+= ${_grp}|${${_grp}:ts,} 24 | .endif 25 | .for _opt in ${${_grp}} 26 | BSDA_OPTIONS+= ${_grp}|${_opt}|${${_opt}_IMPLIES:ts,}|${${_opt}_PREVENTS:ts,} 27 | .endfor 28 | .endfor 29 | -------------------------------------------------------------------------------- /src/pkg_info.sh: -------------------------------------------------------------------------------- 1 | test -n "$_pkg_info_" && return 0 2 | readonly _pkg_info_=1 3 | 4 | . ${bsda_dir:-.}/bsda_obj.sh 5 | . ${bsda_dir:-.}/pkg_query.sh 6 | 7 | # 8 | # This package collects ways to query pkg-info(8). 9 | # 10 | # The pkg:info:Env class can be used to make complex queries. 11 | # 12 | # Simple wrappers are provided as static functions: 13 | # 14 | # - pkg:info:files() 15 | # 16 | 17 | # 18 | # An environment to make pkg-info(8) queries. 19 | # 20 | # An instance requires a bsda:opts:Flags instance, which can be used 21 | # to configure the behaviour of the queried information. 22 | # 23 | # The flags used are defined in pkg_options.sh. 24 | # 25 | bsda:obj:createClass pkg:info:Env \ 26 | r:private:flags "A reference to a bsda:opts:Flag instance" \ 27 | r:public:errmsg "Holds an error message in case of failure" \ 28 | r:public:errnum "The error number returned by pkg-info" \ 29 | r:public:warn "Warning messages from the last pkg-info call" \ 30 | i:private:init "The constructor" \ 31 | x:public:match "For the given query return a list of packages" \ 32 | x:public:files "For the given packages list all installed files" 33 | 34 | # 35 | # The constructor initialises the reference to the bsda:opts:Flags instance. 36 | # 37 | # @param 1 38 | # A pointer to a bsda:opts:Flags instances 39 | # 40 | pkg:info:Env.init() { 41 | setvar ${this}flags "$1" 42 | } 43 | 44 | # 45 | # Take the list of requested packages and turn it into a list of package 46 | # names. 47 | # 48 | # This method is affected by the given flags as described in 49 | # pkg:options:append(). The PKG_ALL flag is set if no arguments have 50 | # been given. 51 | # 52 | # The PKG_DEPENDENCIES and PKG_REQUIRED_BY flags select the given packages 53 | # and their dependencies/requirements instead of just selecting the 54 | # dependencies/requirements of the packages. 55 | # 56 | # @param &1 57 | # The variable to return the list of packages to 58 | # @param @ 59 | # The package queries 60 | # 61 | pkg:info:Env.match() { 62 | local IFS retvar flags args pkgs ret dep req 63 | IFS=$'\n' 64 | 65 | retvar="$1" 66 | shift 67 | $this.getFlags flags 68 | 69 | # Fall back to all packages if no queries are given 70 | if [ $# -eq 0 ] && $flags.check PKG_ALL -eq 0; then 71 | $flags.add PKG_ALL 72 | fi 73 | 74 | # Select command line arguments 75 | log args= -E 76 | if $flags.check PKG_ALL; then 77 | log args.push_back -aq 78 | fi 79 | if $flags.check PKG_CASE_SENSITIVE; then 80 | log args.push_back -C 81 | fi 82 | if $flags.check PKG_GLOB; then 83 | log args.push_back -g 84 | fi 85 | if $flags.check PKG_CASE_INSENSITIVE; then 86 | log args.push_back -i 87 | fi 88 | if $flags.check PKG_REGEX; then 89 | log args.push_back -x 90 | fi 91 | if $flags.check PKG_BY_ORIGIN; then 92 | log args.push_back -Oq 93 | fi 94 | 95 | # Get requested packages 96 | pkgs="$(/usr/sbin/pkg info $args "$@" 2>&1)" 97 | ret=$? 98 | # Bail on error. 99 | if [ $ret -ne 0 ]; then 100 | setvar ${this}errmsg "$pkgs" 101 | setvar ${this}errnum $ret 102 | return $ret 103 | fi 104 | # Remove and record warnings. 105 | if [ -n "$pkgs" ] && [ -z "${pkgs##*pkg: Warning:*}" ]; then 106 | setvar ${this}warn "$(echo "$pkgs" | /usr/bin/grep '^pkg: Warning:')" 107 | pkgs="$(echo "$pkgs" | /usr/bin/grep -v '^pkg: Warning:')" 108 | fi 109 | # Get related packages, unless all packages are selected any way 110 | if $flags.check PKG_ALL -eq 0; then 111 | # Get dependencies if requested 112 | if $flags.check PKG_DEPENDENCIES; then 113 | dep="$(/usr/sbin/pkg info -qd $pkgs 2>&-)" 114 | pkgs="$pkgs${dep:+$IFS}$dep" 115 | pkgs="$(echo "$pkgs" | /usr/bin/awk '!a[$0]++')" 116 | fi 117 | # Get required by packages if requested 118 | if $flags.check PKG_REQUIRED_BY; then 119 | req="$(/usr/sbin/pkg info -qr $pkgs 2>&-)" 120 | pkgs="$pkgs${req:+$IFS}$req" 121 | pkgs="$(echo "$pkgs" | /usr/bin/awk '!a[$0]++')" 122 | fi 123 | fi 124 | 125 | # Sort packages by number of files in descending order, so 126 | # the longest running jobs are processed first. Number of 127 | # files is a better predictor of job execution time than package 128 | # size. 129 | pkgs="$(pkg:query:sort -rn '%#F' $pkgs)" 130 | 131 | # Origins are equally valid unique identifiers, so they can be 132 | # used internally as well, so we do not have to convert for 133 | # display. 134 | if $flags.check PKG_ORIGIN; then 135 | pkgs="$(pkg:query:origin $pkgs 2>&-)" 136 | fi 137 | 138 | # Return the collected packages 139 | $caller.setvar "$retvar" "$pkgs" 140 | return 0 141 | } 142 | 143 | # 144 | # Puts the list of all files installed by the given packages on stdout. 145 | # 146 | # @param @ 147 | # A list of packages 148 | # 149 | pkg:info:files() { 150 | /usr/sbin/pkg info -ql "$@" 2>&1 151 | } 152 | 153 | # 154 | # Outputs the origins of the given packages. 155 | # 156 | # @param @ 157 | # A list of packages 158 | # 159 | pkg:info:origins() { 160 | /usr/sbin/pkg info -qo "$@" 161 | } 162 | -------------------------------------------------------------------------------- /src/pkg_libchk: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -f 3 | prefix="${0##*/}" 4 | namespace="$(echo "$prefix" | /usr/bin/tr '_' ':')" 5 | 6 | bsda_dir="%%DATADIR%%" 7 | #HACK 8 | bsda_dir="${0%${0##*/}}";bsda_dir="${bsda_dir%/}" 9 | test -n "${bsda_dir##/*}" && bsda_dir="$PWD/$bsda_dir" 10 | #hack 11 | readonly bsda_dir 12 | 13 | . ${bsda_dir:-.}/${prefix}.sh 14 | ${namespace}:Session session "$@" && $session.delete 15 | -------------------------------------------------------------------------------- /src/pkg_options.sh: -------------------------------------------------------------------------------- 1 | test -n "$_pkg_options_" && return 0 2 | readonly _pkg_options_=1 3 | 4 | . ${bsda_dir:-.}/bsda_opts.sh 5 | 6 | # 7 | # Appends pkg-info(8) like options to a bsda:opts:Options instance. 8 | # 9 | # @param 1 10 | # The options to append to 11 | # 12 | pkg:options:append() { $1.append \ 13 | PKG_ALL -a --all 'Select all packages' \ 14 | PKG_CASE_SENSITIVE -C --case-sensitive 'Make pkg-name matching case sensitive' \ 15 | PKG_GLOB -g --glob 'Treat pkg-name as a shell glob pattern' \ 16 | PKG_CASE_INSENSITIVE -i --case-insensitive 'Make pkg-name matching case insensitive' \ 17 | PKG_REGEX -x --regex 'Treat pkg-name as a regular expression' \ 18 | PKG_DEPENDENCIES -d --dependencies 'Select the packages on which pkg-name depends' \ 19 | PKG_REQUIRED_BY -r --required-by 'Select the packages which require pkg-name' \ 20 | PKG_BY_ORIGIN -O --by-origin 'Select by pkg-name origin' \ 21 | PKG_QUIET -q --quiet 'Print only the requested information' \ 22 | PKG_ORIGIN -o --origin 'Display package origins instead of names' 23 | } 24 | -------------------------------------------------------------------------------- /src/pkg_query.sh: -------------------------------------------------------------------------------- 1 | test -n "$_pkg_query_" && return 0 2 | readonly _pkg_query_=1 3 | 4 | # 5 | # Provides wrappers around pkg-query(8). 6 | # 7 | 8 | # 9 | # Return info for the given packages. 10 | # 11 | # @param 1 12 | # The format string 13 | # @param @ 14 | # The packages to select 15 | # 16 | pkg:query:select() { 17 | /usr/sbin/pkg query "$@" 18 | } 19 | 20 | # 21 | # List all leaves. 22 | # 23 | # @param 1 24 | # The format string 25 | # 26 | pkg:query:leaves() { 27 | /usr/sbin/pkg query -e '%?r=0' "$1" 28 | } 29 | 30 | # 31 | # List all autoremove packages. 32 | # 33 | # @param 1 34 | # The format string 35 | # 36 | pkg:query:auto() { 37 | /usr/sbin/pkg query -e '%a=1' "$1" 38 | } 39 | 40 | # 41 | # List dependencies of the given packages. 42 | # 43 | # @param 1 44 | # The format string 45 | # @param @ 46 | # The packages to get dependencies of 47 | # 48 | pkg:query:depends() { 49 | local fmt 50 | fmt="$(echo "$1" | /usr/bin/sed 's/%/%d/g')" 51 | shift 52 | /usr/sbin/pkg query "$fmt" "$@" | /usr/bin/sort -u 53 | } 54 | 55 | # 56 | # List all packages which are required only by the given packages. 57 | # 58 | # @param 1 59 | # The format string for input and output 60 | # @param @ 61 | # The packages to filter by 62 | # 63 | pkg:query:required_only_by() { 64 | local fmt 65 | fmt="$1" 66 | shift 67 | /usr/bin/awk -vFMT="$fmt" ' 68 | BEGIN { 69 | QUERY = "/usr/sbin/pkg query" 70 | DFMT = FMT 71 | gsub(/%/, "%d", DFMT) 72 | RFMT = FMT 73 | gsub(/%/, "%r", RFMT) 74 | 75 | # Get all the packages to check 76 | delete aleaves 77 | leaves = "" 78 | for (i = 1; i < ARGC; ++i) { 79 | if (!(ARGV[i] in aleaves)) { 80 | aleaves[ARGV[i]] 81 | leaves = leaves " " ARGV[i] 82 | } 83 | delete ARGV[i] 84 | } 85 | 86 | # Get all the dependencies of leaves 87 | delete adepends 88 | depends = "" 89 | cmd = QUERY " \"" DFMT "\" " leaves 90 | while (0 < (cmd | getline)) { 91 | if (!($0 in adepends) && !($0 ~ /\(null\)/)) { 92 | adepends[$0] 93 | depends = depends " " $0 94 | } 95 | } 96 | close(cmd) 97 | 98 | # Get all the dependencies required only by leaves 99 | depend = "" 100 | required_by_others = 1 101 | cmd = QUERY " \"" FMT " " RFMT "\" " depends 102 | while (0 < (cmd | getline)) { 103 | if (depend != $1) { 104 | if (!required_by_others) { 105 | print depend 106 | } 107 | required_by_others = 0 108 | depend = $1 109 | } 110 | if (!($2 in aleaves)) { 111 | required_by_others = 1 112 | } 113 | } 114 | close(cmd) 115 | if (!required_by_others) { 116 | print depend 117 | } 118 | }' "$@" | /usr/bin/sort -u 119 | } 120 | 121 | # 122 | # Produce a unique identifier for the given packages. 123 | # 124 | # @param 1 125 | # The formatting style either '%n-%v' or '%o' for origin@flavor 126 | # style identifiers 127 | # @param @ 128 | # The packages to produce an ID for, or all packages if left 129 | # empty 130 | # 131 | pkg:query:id() { 132 | case "$1" in 133 | %n-%v) shift && /usr/sbin/pkg query %n-%v "$@";; 134 | %o) shift && pkg:query:origin "$@";; 135 | *) return 1;; 136 | esac 137 | } 138 | 139 | # 140 | # List all package origins as `origin` or `origin@flavor`. 141 | # 142 | # @param @ 143 | # The set of packages to provide the origin for 144 | # 145 | pkg:query:origin() { 146 | { 147 | /usr/sbin/pkg query '%n-%v %o' "$@" 148 | /usr/sbin/pkg query '%n-%v %o %At %Av' "$@" 149 | } | /usr/bin/awk ' 150 | !a[$1] { 151 | a[$1] = NR 152 | o[NR] = $2 153 | } 154 | $3 == "flavor" { 155 | o[a[$1]] = $2 "@" $4 156 | } 157 | END { 158 | for (i = 1; o[i]; ++i) { 159 | print o[i] 160 | } 161 | } 162 | ' 163 | } 164 | 165 | # 166 | # Sort the given list of packages. 167 | # 168 | # - All leading arguments starting with a `-` are forwarded to sort(1) 169 | # - The first argument not starting with - is assumed to be a pkg-query 170 | # format string denoting the data to sort by, the format has to 171 | # produce a single line output, the field separator charcter FS aka 172 | # \034 may not be used 173 | # - The remaining arguments are assumed to be the set of packages 174 | # to use 175 | # 176 | # @param @ 177 | # A set of sort(1) arguments, the formatting string and a package 178 | # list 179 | # 180 | pkg:query:sort() { 181 | local IFS pkgs sortargs fmt 182 | IFS=$'\034' 183 | pkgs="${IFS}${*}" 184 | sortargs="${pkgs%%"${IFS}"[^-]*}" 185 | pkgs="${pkgs#"${sortargs}"}" 186 | sortargs="${sortargs#"${IFS}"}" 187 | pkgs="${pkgs#"${IFS}"}" 188 | fmt="${pkgs%%"${IFS}"*}${IFS}%n-%v" 189 | pkgs="${pkgs#*"${IFS}"}" 190 | /usr/sbin/pkg query "${fmt}" ${pkgs} \ 191 | | /usr/bin/sort ${sortargs} \ 192 | | /usr/bin/awk -vFS="${IFS}" '$0=$2' 193 | } 194 | -------------------------------------------------------------------------------- /src/pkg_trim: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/pkg_validate: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/pkg_version: -------------------------------------------------------------------------------- 1 | pkg_libchk -------------------------------------------------------------------------------- /src/testify.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # 3 | # Append "|| bsda:test:err …" to every command in a shell script. 4 | # 5 | 6 | BEGIN { 7 | # Define states 8 | ST_REG = 0 # Regular state 9 | ST_SQUOT = 1 # Inside single quotes 10 | ST_DQUOT = 2 # Inside double quotes 11 | ST_DSQUOT = 3 # Inside dollar single quotes 12 | ST_CASE_BLK = 4 # Inside case/esac 13 | ST_CASE_REG = 5 # Inside case match 14 | 15 | TOP = 0 16 | STATE[TOP] = ST_REG 17 | REG_STATES[ST_REG] 18 | REG_STATES[ST_CASE_REG] 19 | } 20 | 21 | # 22 | # Manage single and double quotes. 23 | # 24 | # This maintains a state stack, representing the nesting of states. 25 | # 26 | # The current line is parsed character by character, the characters 27 | # are interpreted according to the top of the state stack. Depending 28 | # on the characters encountered a new state is pushed on top of the 29 | # stack or the top is pointed back down to the previous state. 30 | # 31 | # If by the end of the line the stack top is not at the bottom the 32 | # line is printed as is and the next line is pulled in to finish 33 | # parsing. 34 | # 35 | { 36 | do { 37 | for (i = 1; i <= length; ++i) { 38 | ch = substr($0, i, 1) 39 | front = substr($0, i) 40 | # Single quotes are simple 41 | if (STATE[TOP] == ST_SQUOT) { 42 | if (ch == "'") { 43 | --TOP 44 | } 45 | continue 46 | } 47 | # All the following states support \ escapes 48 | if (ch == "\\") { 49 | ++i 50 | continue 51 | } 52 | # Double quotes 53 | if (STATE[TOP] == ST_DQUOT) { 54 | if (ch == "\"") { 55 | --TOP 56 | } else if (ch == "$") { 57 | # Peek ahead 58 | ch = substr($0, i, 2) 59 | if (ch == "$(") { 60 | ++i 61 | STATE[++TOP] = ST_REG 62 | } 63 | if (ch == "$'") { 64 | ++i 65 | STATE[++TOP] = ST_DSQUOT 66 | } 67 | } 68 | continue 69 | } 70 | # Dollar single quotes 71 | if (STATE[TOP] == ST_DSQUOT) { 72 | if (ch == "'") { 73 | --TOP 74 | } 75 | continue 76 | } 77 | # Inside case match 78 | if (STATE[TOP] == ST_CASE_REG) { 79 | if (front ~ /^;;.*/) { 80 | ++i 81 | --TOP 82 | continue 83 | } 84 | } 85 | # Inside case block 86 | if (STATE[TOP] == ST_CASE_BLK) { 87 | if (ch == ")") { 88 | STATE[++TOP] = ST_CASE_REG 89 | # Output the parsed section so 90 | # a check is only appended if the 91 | # closing parentheses are followed 92 | # by code 93 | printf("%s", substr($0, 1, i)) 94 | $0 = substr($0, i + 1) 95 | continue 96 | } 97 | if (front ~ /^esac([ \t#].*)?/) { 98 | --TOP 99 | i += 3 100 | continue 101 | } 102 | } 103 | # Regular state, or command substitution 104 | if (ch == "'") { 105 | STATE[++TOP] = ST_SQUOT 106 | } else if (ch == "\"") { 107 | STATE[++TOP] = ST_DQUOT 108 | } else if (ch == "$") { 109 | # Peek ahead 110 | ch = substr($0, i, 2) 111 | if (ch == "$'") { 112 | ++i 113 | STATE[++TOP] = ST_DSQUOT 114 | } 115 | } else if (ch == "(") { 116 | STATE[++TOP] = ST_REG 117 | } else if (ch == ")") { 118 | --TOP 119 | } else if (front ~ /^case[ \t].+[ \t]in([ \t#].*)?/) { 120 | STATE[++TOP] = ST_CASE_BLK 121 | } 122 | } 123 | # About to get the next line, print this one 124 | if (!(STATE[TOP] in REG_STATES)) { 125 | print 126 | } 127 | } while (!(STATE[TOP] in REG_STATES) && getline > 0) 128 | } 129 | 130 | # Do not append error checks to lines not ending in a command. 131 | /((^|then|else|do|[({&;])|[ \t]*\\)[ \t]*$/ { 132 | print 133 | next 134 | } 135 | 136 | # Append the error check. 137 | { 138 | print $0 " || bsda:test:err \"" FILENAME "\" " NR " $?" 139 | } 140 | -------------------------------------------------------------------------------- /src/type.sh: -------------------------------------------------------------------------------- 1 | test -n "$_type_sh_" && return 0 2 | readonly _type_sh_=1 3 | 4 | # 5 | # Simple type checking and casting standalone library. 6 | # 7 | # @file 8 | # @see type.md 9 | # 10 | 11 | # 12 | # The given value matches at least one of the given types. 13 | # 14 | # @param 1 15 | # A comma separated list of types 16 | # @param 2 17 | # The value to match 18 | # @retval 0 19 | # At least one of the given types matches 20 | # @retval 1 21 | # None of the given types fit the given value 22 | # 23 | type:match() { 24 | # Last type to check, terminate recursion 25 | if [ "${1#*,}" = "$1" ]; then 26 | type:match:$1 "$2" 27 | return $? 28 | fi 29 | 30 | # Recursively look for matching type 31 | type:match:${1%%,*} "$2" || type:match "${1#*,}" "$2" 32 | } 33 | 34 | # 35 | # Provide the name of the first matched type. 36 | # 37 | # @param &1 38 | # The destination variable, only assigned in case of success 39 | # @param 2 40 | # A comma separated list of matchable types 41 | # @param 3 42 | # The value to match 43 | # @retval 0 44 | # Value matched successfully 45 | # @retval 1 46 | # None of the given types fit the given value 47 | # 48 | type:which() { 49 | # Last type to check, terminate recursion 50 | if [ "${2#*,}" = "$2" ]; then 51 | type:match:$2 "$3" || return $? 52 | eval "$1=$2" 53 | return 0 54 | fi 55 | 56 | # Recursively look for matching type 57 | if type:match:${2%%,*} "$3"; then 58 | eval "$1=${2%%,*}" 59 | return 0 60 | fi 61 | type:which "$1" "${2#*,}" "$3" 62 | } 63 | 64 | # 65 | # Cast to integer from a set of types. 66 | # 67 | # @param &1 68 | # The destination variable, only assigned in case of success 69 | # @param 2 70 | # A comma separated list of matchable types 71 | # @param 3 72 | # The value to assign 73 | # @retval 0 74 | # Value successfully assigned 75 | # @retval 1 76 | # None of the given types fit the given value 77 | # 78 | type:cast[int]() { 79 | # Last type to check, terminate recursion 80 | if [ "${2#*,}" = "$2" ]; then 81 | type:cast[int]:$2 "$1" "$3" 82 | return $? 83 | fi 84 | 85 | # Recursively look for matching type 86 | type:cast[int]:${2%%,*} "$1" "$3" || type:cast[int] "$1" "${2#*,}" "$3" 87 | } 88 | 89 | # 90 | # Accept empty inputs. 91 | # 92 | # @param 1 93 | # The input string 94 | # @return 95 | # Whether the input string is empty 96 | # 97 | type:match:empty() { 98 | test -z "$1" 99 | } 100 | 101 | # 102 | # Accept boolean values. 103 | # 104 | # Accepts 0, 1, yes, no, true, false. 105 | # 106 | # @param 1 107 | # The input string 108 | # @return 109 | # Whether the input string is a boolean value 110 | # 111 | type:match:bool() { 112 | case "$1" in 113 | 0|1|[Yy][Ee][Ss]|[Nn][Oo]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]) 114 | return 0 115 | ;; 116 | esac 117 | return 1 118 | } 119 | 120 | # 121 | # Accept unsigned integer values. 122 | # 123 | # Accepts hex, decimal and octal integer values without a sign. 124 | # 125 | # @param 1 126 | # The input string 127 | # @return 128 | # Whether the input string is an unsigned integer 129 | # 130 | type:match:uint() { 131 | case "$1" in 132 | 0[Xx]*) 133 | type:match:uint:base16 "${1#??}" 134 | return $? 135 | ;; 136 | [1-9]*) 137 | type:match:uint:base10 "$1" 138 | return $? 139 | ;; 140 | 0*) 141 | type:match:uint:base8 "$1" 142 | return $? 143 | ;; 144 | esac 145 | return 1 146 | } 147 | 148 | # 149 | # Accept a string of hex digits. 150 | # 151 | # @param 1 152 | # The input string 153 | # @return 154 | # Whether the input string consists of hex digits 155 | # 156 | type:match:uint:base16() { 157 | case "$1" in 158 | [0-9A-Fa-f]) 159 | return 0 160 | ;; 161 | [0-9A-Fa-f]*) 162 | type:match:uint:base16 "${1#?}" 163 | return $? 164 | ;; 165 | esac 166 | return 1 167 | } 168 | 169 | # 170 | # Accept a string of decimal digits. 171 | # 172 | # @param 1 173 | # The input string 174 | # @return 175 | # Whether the input string consists of decimal digits 176 | # 177 | type:match:uint:base10() { 178 | case "$1" in 179 | [0-9]) 180 | return 0 181 | ;; 182 | [0-9]*) 183 | type:match:uint:base10 "${1#?}" 184 | return $? 185 | ;; 186 | esac 187 | return 1 188 | } 189 | 190 | # 191 | # Accept a string of octal digits. 192 | # 193 | # @param 1 194 | # The input string 195 | # @return 196 | # Whether the input string consists of octal digits 197 | # 198 | type:match:uint:base8() { 199 | case "$1" in 200 | [0-7]) 201 | return 0 202 | ;; 203 | [0-7]*) 204 | type:match:uint:base8 "${1#?}" 205 | return $? 206 | ;; 207 | esac 208 | return 1 209 | } 210 | 211 | # 212 | # Accept integer values. 213 | # 214 | # Accepts hex, decimal and octal integer values with an optional sign. 215 | # 216 | # @param 1 217 | # The input string 218 | # @return 219 | # Whether the input string is an integer 220 | # 221 | type:match:int() { 222 | type:match:uint "${1#[-+]}" 223 | } 224 | 225 | # 226 | # Accept the digits [0-9]. 227 | # 228 | # @param 1 229 | # The input string 230 | # @return 231 | # Whether the input string is a positional argument number 232 | # 233 | type:match:argname() { 234 | case "$1" in 235 | [0-9]) 236 | return 0 237 | ;; 238 | esac 239 | return 1 240 | } 241 | 242 | # 243 | # Accepts assignable variable names. 244 | # 245 | # A single _ is not accepted, because it has weird behaviour in BASH. 246 | # 247 | # @param 1 248 | # The input string 249 | # @return 250 | # Whether the input string is a valid variable name 251 | # 252 | type:match:varname() { 253 | case "$1" in 254 | [a-zA-Z]) 255 | return 0 256 | ;; 257 | [a-zA-Z_]*) 258 | type:match:varname:tail "${1#?}" 259 | return $? 260 | ;; 261 | esac 262 | return 1 263 | } 264 | 265 | # 266 | # Accepts the tail of assignable variable names. 267 | # 268 | # @param 1 269 | # The input string tail 270 | # @return 271 | # Whether the input string is a valid variable name 272 | # 273 | type:match:varname:tail() { 274 | case "$1" in 275 | [a-zA-Z0-9_]) 276 | return 0 277 | ;; 278 | [a-zA-Z0-9_]*) 279 | type:match:varname:tail "${1#?}" 280 | return $? 281 | ;; 282 | esac 283 | return 1 284 | } 285 | 286 | # 287 | # Accepts valid shell function names. 288 | # 289 | # This follows the more restrictive rules of BASH over ASH, where 290 | # names may not be made up entirely of numerals. 291 | # 292 | # @param 1 293 | # The input string 294 | # @return 295 | # Whether the input string is a valid shell function name 296 | # 297 | type:match:funcname() { 298 | case "$1" in 299 | [0-9]) 300 | return 1 301 | ;; 302 | [0-9]*) 303 | type:match:funcname "${1#?}" 304 | return $? 305 | ;; 306 | [][a-zA-Z_.:]) 307 | return 0 308 | ;; 309 | [][a-zA-Z_.:]*) 310 | type:match:funcname:tail "${1#?}" 311 | return $? 312 | ;; 313 | esac 314 | return 1 315 | } 316 | 317 | # 318 | # Accepts the tail of valid shell function names. 319 | # 320 | # This validates the section of the name following the first non-numeral 321 | # character. 322 | # 323 | # @param 1 324 | # The input string tail 325 | # @return 326 | # Whether the input string is a valid shell function name 327 | # 328 | type:match:funcname:tail() { 329 | case "$1" in 330 | [][a-zA-Z0-9_.:]) 331 | return 0 332 | ;; 333 | [][a-zA-Z0-9_.:]*) 334 | type:match:funcname:tail "${1#?}" 335 | return $? 336 | ;; 337 | esac 338 | return 1 339 | } 340 | 341 | # 342 | # Assign 0 on empty input. 343 | # 344 | # @param &1 345 | # The variable to assign to 346 | # @param 2 347 | # The input string 348 | # @return 349 | # Whether the input string is empty 350 | # 351 | type:cast[int]:empty() { 352 | if [ -z "$2" ]; then 353 | eval "$1=0" 354 | return 0 355 | fi 356 | return 1 357 | } 358 | 359 | # 360 | # Assign 0 or 1 on boolean input. 361 | # 362 | # Accepts 0, 1, yes, no, true, false. 363 | # 364 | # @param &1 365 | # The variable to assign to 366 | # @param 2 367 | # The input string 368 | # @return 369 | # Whether the input string is a boolean value 370 | # 371 | type:cast[int]:bool() { 372 | case "$2" in 373 | 0|[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]) 374 | eval "$1=0" 375 | return 0 376 | ;; 377 | 1|[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) 378 | eval "$1=1" 379 | return 0 380 | ;; 381 | esac 382 | return 1 383 | } 384 | 385 | # 386 | # Assign unsigned decimal integer on unsigned integer input. 387 | # 388 | # Accepts hex, decimal and octal integer values without a sign. 389 | # 390 | # @param &1 391 | # The variable to assign to 392 | # @param 2 393 | # The input string 394 | # @return 395 | # Whether the input string is an unsigned integer 396 | # 397 | type:cast[int]:uint() { 398 | if type:match:uint "$2"; then 399 | eval "$1=\$(($2))" 400 | return 0 401 | fi 402 | return 1 403 | } 404 | 405 | # 406 | # Assign decimal integer on integer input. 407 | # 408 | # Accepts hex, decimal and octal integer values with an optional sign. 409 | # 410 | # @param &1 411 | # The variable to assign to 412 | # @param 2 413 | # The input string 414 | # @return 415 | # Whether the input string is an integer 416 | # 417 | type:cast[int]:int() { 418 | if type:match:int "$2"; then 419 | eval "$1=\$(($2))" 420 | return 0 421 | fi 422 | return 1 423 | } 424 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Executes the given tests. 5 | # 6 | # Glob patterns are matched against the tests in `tests/`. 7 | # 8 | # Execute without arguments to run all tests or list specific tests. 9 | # 10 | # Returns the number of failed tests. 11 | # 12 | 13 | set -e 14 | 15 | dir="${0%${0##*/}}" 16 | dir="${dir%/}" 17 | cd "${dir:-.}/tests" || return 1 18 | 19 | # Match all tests when no arguments are given 20 | if [ $# -eq 0 ]; then 21 | set -- * 22 | fi 23 | 24 | fails=0 25 | for test in $@; do 26 | # Remove folder component 27 | test="${test##*/}" 28 | 29 | # Skip hidden files, `.` and `..` 30 | if [ -z "${test##.*}" ]; then 31 | echo "${test} failed: skipping" 32 | fails=$((fails + 1)) 33 | continue 34 | fi 35 | 36 | # Need to exist 37 | if [ ! -e "$test" ]; then 38 | echo "${test} failed: missing" 39 | fails=$((fails + 1)) 40 | continue 41 | fi 42 | 43 | # Need read access 44 | if [ ! -r "$test" ]; then 45 | echo "${test} failed: cannot access" 46 | fails=$((fails + 1)) 47 | continue 48 | fi 49 | 50 | # Inject bsda:test and add `|| bsda:test:err $LINENO` to every line 51 | # The true call is to ensure that bash has called a non-builtin 52 | # function, before executing a test, because that causes it 53 | # to initialise some variables, which is undesirable to happen 54 | # during the test run. 55 | code="/usr/bin/true;bsda_dir='../src';readonly bsda_dir;. ../src/bsda_test.sh;$( 56 | awk -f../src/testify.awk "$test")" || return 1 57 | for shell in sh bash; do 58 | which $shell >&- || continue 59 | echo "${test} running: ${shell} ${test}" 60 | if $shell -c "$code"; then 61 | if [ -t 1 ]; then 62 | tput up 63 | fi 64 | echo "${test} passed: ${shell} ${test}" 65 | else 66 | echo "${test} failed: ${shell} ${test}" 67 | fails=$((fails + 1)) 68 | fi 69 | done 70 | done 71 | if [ $fails -ne 0 ]; then 72 | echo "Test runs failed: $fails" 73 | fi 74 | exit $fails 75 | -------------------------------------------------------------------------------- /tests/bsda_async.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_async.sh 2 | . ../src/bsda_fifo.sh 3 | . ../src/bsda_test.sh 4 | 5 | # Create an asynchronous runner that outputs into a FIFO 6 | bsda:obj:createClass Runner x:public:eval 7 | bsda:async:createClass AsyncRunner Runner 8 | bsda:fifo:Fifo fifo 9 | eval "Runner.eval() { 10 | $fifo.sink \"\$@\" 11 | }" 12 | 13 | # 14 | # Test both a synchronous and an asynchronous runner to verify the 15 | # procedure for both cases. 16 | # 17 | prestate="$(x=0; set | grep -v _nextId=)" 18 | Runner sync 19 | AsyncRunner async 20 | 21 | for run in $sync $async; do 22 | # Check dump of object 23 | $run.eval '$this.dump' 24 | $fifo.recv dump 25 | bsda:test:gmatch "Runner@${bsda_obj_frameworkPrefix}*_ {}" "$dump" 26 | 27 | # Check whether the object exist in this process 28 | $run.eval 'echo $this' 29 | $fifo.recv obj 30 | test -n "$obj" 31 | if [ $run = $sync ]; then 32 | type="$(type $obj.dump 2>&1)" 33 | fi 34 | if [ $run = $async ]; then 35 | ! type="$(type $obj.dump 2>&1)" 36 | fi 37 | # Remove facility 38 | $run.delete 39 | done 40 | 41 | # Check for complete cleanup 42 | unset sync async run dump obj type 43 | poststate="$(unset prestate; x=0; set | grep -v _nextId=)" 44 | test "$prestate" = "$poststate" 45 | -------------------------------------------------------------------------------- /tests/bsda_container.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_container.sh 2 | . ../src/bsda_test.sh 3 | . ../src/bsda_util.sh 4 | 5 | NL=$'\n' 6 | # Comma separated fields 7 | IFS="," 8 | 9 | # Lambda, produce comma separated list of key→value pairs 10 | lambda() { 11 | kvs="${kvs:+$kvs,}$(printf "%s→%s" "$@")" 12 | } 13 | 14 | # Create a memory dump 15 | memdump0="$(true;set)" 16 | 17 | # 18 | # Check bsda:container:Array 19 | # 20 | 21 | # Create array 22 | bsda:container:Array arr a b c d e 23 | kvs= 24 | $arr.foreach lambda 25 | test "0→a,1→b,2→c,3→d,4→e" = "$kvs" 26 | 27 | # Check item count 28 | $arr.getCount val && test "$val" = 5 29 | # Value retrieval 30 | $arr.[ 3 ] val && test "$val" = d 31 | # Value assignment 32 | $arr.[ 3 ]= D 33 | $arr.[ 3 ] val && test "$val" = D 34 | # Pop/Push 35 | $arr.pop val && test "$val" = e 36 | $arr.getCount val && test "$val" = 4 37 | $arr.push E 38 | $arr.getCount val && test "$val" = 5 39 | # Verify 40 | kvs= 41 | $arr.foreach lambda 42 | test "0→a,1→b,2→c,3→D,4→E" = "$kvs" 43 | 44 | # Copy 45 | $arr.copy arr1 46 | kvs= 47 | $arr1.foreach lambda 48 | test "0→a,1→b,2→c,3→D,4→E" = "$kvs" 49 | # Check serialise/deserialise 50 | $arr1.serialise arr1s 51 | # Restore modified object from serialised string 52 | $arr1.[ 9 ]= X 53 | $arr1.getCount val && test "$val" = 10 54 | bsda:obj:deserialise arr1 "$arr1s" 55 | $arr1.getCount val && test "$val" = 5 56 | kvs= 57 | $arr1.foreach lambda 58 | test "0→a,1→b,2→c,3→D,4→E" = "$kvs" 59 | # Delete and restore 60 | $arr1.delete 61 | unset arr1 62 | bsda:obj:deserialise arr1 "$arr1s" 63 | kvs= 64 | $arr1.foreach lambda 65 | test "0→a,1→b,2→c,3→D,4→E" = "$kvs" 66 | # Clean up 67 | $arr1.delete 68 | $arr.delete 69 | unset arr1 arr1s arr kvs val 70 | 71 | # Create a new memory dump 72 | memdump1="$(unset memdump0;true;set)" 73 | 74 | # Compare before and after memory dumps, the only thing allowed to have 75 | # changed are object ID counters. 76 | diff01="$(echo "$memdump1" | grep -vFx "$memdump0")" 77 | bsda:test:xmatch "$diff01" all:any "$bsda_obj_frameworkPrefix*_nextId=[0-9]*" 78 | 79 | # 80 | # Check bsda:container:Map 81 | # 82 | 83 | # Create map 84 | bsda:container:Map map A a B b C c D d E e 85 | kvs= 86 | $map.foreach lambda 87 | bsda:util:join kvs "$NL" $kvs 88 | bsda:test:xmatch "$kvs" all:once A→a B→b C→c D→d E→e 89 | 90 | # Check item count 91 | $map.getCount val && test "$val" = 5 92 | # Value retrieval 93 | $map.[ D ] val && test "$val" = d 94 | # Value assignment 95 | $map.[ D ]= D 96 | $map.[ D ] val && test "$val" = D 97 | # Value removal 98 | $map.[ E ]x 99 | $map.getCount val && test "$val" = 4 100 | $map.[ E ] val && test -z "$val" 101 | # Special character assignment 102 | $map.[ '%$foo&&{' ]= '$bar}' 103 | $map.getCount val && test "$val" = 5 104 | $map.[ '%$foo&&{' ] val && test "$val" = '$bar}' 105 | # Verify 106 | kvs= 107 | $map.foreach lambda 108 | bsda:util:join kvs "$NL" $kvs 109 | bsda:test:xmatch "$kvs" all:once A→a B→b C→c D→D '%$foo&&{→$bar}' 110 | 111 | # Copy 112 | $map.copy map1 113 | kvs= 114 | $map1.foreach lambda 115 | bsda:util:join kvs "$NL" $kvs 116 | bsda:test:xmatch "$kvs" all:once A→a B→b C→c D→D '%$foo&&{→$bar}' 117 | # Check serialise/deserialise 118 | $map1.serialise map1s 119 | # Restore modified object from serialised string 120 | $map1.[ Z ]= X 121 | $map1.getCount val && test "$val" = 6 122 | bsda:obj:deserialise map1 "$map1s" 123 | $map1.getCount val && test "$val" = 5 124 | kvs= 125 | $map1.foreach lambda 126 | bsda:util:join kvs "$NL" $kvs 127 | bsda:test:xmatch "$kvs" all:once A→a B→b C→c D→D '%$foo&&{→$bar}' 128 | # Delete and restore 129 | $map1.delete 130 | unset map1 131 | bsda:obj:deserialise map1 "$map1s" 132 | kvs= 133 | $map1.foreach lambda 134 | bsda:util:join kvs "$NL" $kvs 135 | bsda:test:xmatch "$kvs" all:once A→a B→b C→c D→D '%$foo&&{→$bar}' 136 | # Clean up 137 | $map1.delete 138 | $map.delete 139 | unset map1 map1s map kvs val 140 | 141 | # Create a new memory dump 142 | memdump2="$(unset diff01 memdump0 memdump1;true;set)" 143 | 144 | # Compare before and after memory dumps, the only thing allowed to have 145 | # changed are object ID counters. 146 | diff12="$(echo "$memdump2" | grep -vFx "$memdump1")" 147 | bsda:test:xmatch "$diff12" all:any "$bsda_obj_frameworkPrefix*_nextId=[0-9]*" 148 | -------------------------------------------------------------------------------- /tests/bsda_elf.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_elf.sh 2 | 3 | prestate="$(x=0; set | grep -v _nextId=)" 4 | 5 | ! (bsda:elf:File file /dev/null/fail 2>&-) 6 | 7 | bsda:elf:File file /boot/kernel/kernel 8 | $file.fetch value ostype 9 | ostype="$(sysctl -n kern.ostype)" 10 | test "${value}" = "${ostype}" 11 | $file.fetchEnc value ostype vis 12 | test "${value}" = "${ostype}\000" 13 | $file.delete 14 | 15 | # Check for complete cleanup 16 | unset file value ostype 17 | poststate="$(unset prestate; x=0; set | grep -v _nextId=)" 18 | test "$prestate" = "$poststate" 19 | -------------------------------------------------------------------------------- /tests/bsda_fifo.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_fifo.sh 2 | . ../src/bsda_test.sh 3 | . ../src/bsda_util.sh 4 | 5 | NL=$'\n' 6 | 7 | # Record number of file descriptors 8 | IFS=, 9 | bsda:util:count fdcount0 $bsda_obj_desc 10 | # Create a Fifo instance 11 | bsda:fifo:Fifo fifo 12 | # Check if file descriptors were consumed 13 | bsda:util:count fdcount1 $bsda_obj_desc 14 | test $fdcount1 -le $fdcount0 15 | # Fifos are not serialisable or copyable 16 | bsda:test:isNone $fifo.serialise 17 | bsda:test:isNone $fifo.copy 18 | # Send something through the fifo 19 | $fifo.sink echo foobar 20 | $fifo.source read -r str 21 | test "$str" = foobar 22 | # Send a big thing many times in parallel 23 | doc="$(cat ../ref/bsda_obj.md)$NL" 24 | IFS="$NL" 25 | threads=8 26 | pids= 27 | for i in $(jot $threads); do 28 | $fifo.sink 'echo -n "$doc"' & 29 | pids="${pids}$!${NL}" 30 | done 31 | for i in $(jot $threads); do 32 | recv= 33 | while [ "$recv" != "$doc" ]; do 34 | $fifo.source read -rt5 line 35 | recv="$recv$line$NL" 36 | done 37 | done 38 | wait $pids 39 | # Clean up 40 | $fifo.delete 41 | IFS=, 42 | bsda:util:count fdcount2 $bsda_obj_desc 43 | test $fdcount2 -eq $fdcount0 44 | 45 | # Creating fifos must eventually fail if file descriptors are consumed 46 | if [ $((fdcount0)) -lt $((fdcount1)) ]; then 47 | fdcount_last=$fdcount0 48 | while bsda:fifo:Fifo fifo; do 49 | bsda:util:count fdcount $bsda_obj_desc 50 | test $fdcount -lt $fdcount_last 51 | fdcount_last=$fdcount 52 | done 53 | fi 54 | -------------------------------------------------------------------------------- /tests/bsda_obj.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_test.sh 2 | . ../src/bsda_obj.sh 3 | 4 | # Evil IFS, to make sure everything is IFS safe. Using _ in IFS means 5 | # every object reference needs to be quoted, which is annoying, but 6 | # at least the backend should support this. 7 | IFS=':._' 8 | 9 | # Calling bsda:obj:createClass() without arguments 10 | error=$(bsda:obj:createClass 2>&1; test $? -eq 5) 11 | bsda:test:gmatch "bsda:obj:createClass: ERROR: *" "$error" 12 | 13 | # Checking class creation 14 | bsda:obj:createClass Foo 15 | Foo.getMethods | grep -qFx public:delete 16 | Foo.getMethods | grep -qFx public:dump 17 | Foo.getMethods | grep -qFx public:copy 18 | Foo.getMethods | grep -qFx public:serialise 19 | 20 | # Checking implicit removal of copy and serialise for classes with 21 | # a cleanup method 22 | bsda:obj:createClass Bar c:clean 23 | Bar.getMethods | grep -qFx public:delete 24 | Bar.getMethods | grep -qFx public:dump 25 | ! Bar.getMethods | grep -qFx public:copy 26 | ! Bar.getMethods | grep -qFx public:serialise 27 | 28 | # Create an instance 29 | Foo foo 30 | 31 | # Test empty obj dump format 32 | dump=$("$foo".dump) 33 | bsda:test:gmatch 'Foo@* {}' "$dump" 34 | 35 | # Test deleting 36 | "$foo".delete 37 | ! error=$("$foo".delete 2>&1) 38 | bsda:test:gmatch "* not found" "$error" 39 | 40 | # Test setters'n'getters 41 | bsda:obj:createClass Baz w:value 42 | Baz.getMethods | grep -qFx public:getValue 43 | Baz.getMethods | grep -qFx public:setValue 44 | Baz baz 45 | "$baz".getValue x && test -z "$x" 46 | "$baz".setValue 13 47 | "$baz".getValue x && test "$x" = 13 48 | "$baz".delete 49 | # Test private/public 50 | bsda:obj:createClass Baz w:private:value 51 | Baz.getMethods | grep -qFx private:getValue 52 | Baz.getMethods | grep -qFx private:setValue 53 | # Use x: redeclaration 54 | bsda:obj:createClass Baz \ 55 | w:private:value x:public:getValue 56 | Baz.getMethods | grep -qFx public:getValue 57 | Baz.getMethods | grep -qFx private:setValue 58 | bsda:obj:createClass Baz \ 59 | x:public:getValue w:private:value 60 | Baz.getMethods | grep -qFx public:getValue 61 | Baz.getMethods | grep -qFx private:setValue 62 | bsda:obj:createClass Baz \ 63 | w:private:value x:getValue 64 | Baz.getMethods | grep -qFx public:getValue 65 | Baz.getMethods | grep -qFx private:setValue 66 | # Use r: redecleration 67 | bsda:obj:createClass Baz \ 68 | w:private:value r:public:value 69 | Baz.getMethods | grep -qFx public:getValue 70 | Baz.getMethods | grep -qFx private:setValue 71 | bsda:obj:createClass Baz \ 72 | r:public:value w:private:value 73 | Baz.getMethods | grep -qFx public:getValue 74 | Baz.getMethods | grep -qFx private:setValue 75 | bsda:obj:createClass Baz \ 76 | w:private:value r:value 77 | Baz.getMethods | grep -qFx public:getValue 78 | Baz.getMethods | grep -qFx private:setValue 79 | 80 | # Test private 81 | Baz baz 82 | ! error=$("$baz".setValue 42 2>&1 ) 83 | bsda:test:gmatch "Baz.setValue:*access*private*" "$error" 84 | # Fake private 85 | class=Baz 86 | "$baz".setValue 42 87 | unset class 88 | "$baz".getValue x && test "$x" = 42 89 | 90 | # Fail invalid scope 91 | error="$(bsda:obj:createClass X x:protected:foobar 2>&1; test $? -eq 3)" 92 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*scope*protected*" "$error" 93 | error="$(bsda:obj:createClass X r:protected:foobar 2>&1; test $? -eq 3)" 94 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*scope*protected*" "$error" 95 | error="$(bsda:obj:createClass X w:protected:foobar 2>&1; test $? -eq 3)" 96 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*scope*protected*" "$error" 97 | error="$(bsda:obj:createClass X a:protected:foobar 2>&1; test $? -eq 3)" 98 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*scope*protected*" "$error" 99 | error="$(bsda:obj:createClass X a:protected:foobar=Baz 2>&1; test $? -eq 3)" 100 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*scope*protected*" "$error" 101 | error="$(bsda:obj:createClass X -:protected:foobar 2>&1; test $? -eq 6)" 102 | bsda:test:gmatch "bsda:obj:createClass: ERROR:*characters*" "$error" 103 | 104 | # Test copy 105 | "$baz".copy baz1 106 | "$baz1".getValue x && test "$x" = 42 107 | "$baz1".delete 108 | # Test serialise 109 | "$baz".serialise serialised 110 | x=0 111 | getvar x "${baz}"value && test "$x" = 42 112 | "$baz".delete 113 | getvar x "${baz}"value && test -z "$x" 114 | ! error=$($baz.getValue x 2>&1 ) 115 | bsda:test:gmatch "* not found" "$error" 116 | bsda:obj:deserialise baz "$serialised" 117 | x=0 118 | "$baz".getValue x && test "$x" = 42 119 | # Test dump format 120 | "$baz".dump dump 121 | bsda:test:gmatch $'Baz@* {\n value=\'42\'\n}' "$dump" 122 | 123 | # Test methods 124 | bsda:obj:createClass Boom x:foo 125 | Boom.foo() { call="Boom.foo"; } 126 | Boom boom 127 | call= 128 | "$boom".foo && test "$call" = "Boom.foo" 129 | # Test $caller.setvar 130 | Boom.foo() { "$caller".setvar "$1" "Boom.foo"; } 131 | call= 132 | "$boom".foo call && test "$call" = "Boom.foo" 133 | call=$("$boom".foo) && test "$call" = "Boom.foo" 134 | # Test recursion using fibonacci function 135 | Boom.foo() { 136 | if [ $2 -le 2 ]; then 137 | "$caller".setvar "$1" 1 138 | return 139 | fi 140 | local a b 141 | "$this".foo a $(($2 - 1)) 142 | "$this".foo b $(($2 - 2)) 143 | "$caller".setvar "$1" $((a + b)) 144 | } 145 | "$boom".foo x 7 146 | test "$x" = 13 147 | 148 | # Test initialiser/finaliser 149 | bsda:obj:createClass Black i:private:init c:private:clean 150 | Black.init() { call="Black.init"; } 151 | Black.clean() { call="Black.clean"; } 152 | call= 153 | Black black 154 | test "$call" = "Black.init" 155 | "$black".delete 156 | test "$call" = "Black.clean" 157 | 158 | # Test $caller.delete 159 | bsda:obj:createClass White x:run 160 | call= 161 | White.run() { 162 | local black 163 | Black black 164 | "$caller".delete "$black" 165 | test "$call" = "Black.init" 166 | } 167 | White white 168 | "$white".run 169 | test "$call" = "Black.clean" 170 | 171 | # Test double initialiser function 172 | error=$(bsda:obj:createClass Bam i:foo i:bar 2>&1; test $? -eq 1) 173 | bsda:test:gmatch "bsda:obj:createClass: ERROR: *init*" "$error" 174 | # Test double cleanup function 175 | error=$(bsda:obj:createClass Bam c:foo c:bar 2>&1; test $? -eq 2) 176 | bsda:test:gmatch "bsda:obj:createClass: ERROR: *cleanup*" "$error" 177 | 178 | # Test delete thoroughly 179 | bsda:obj:createClass Bob \ 180 | r:private:rval \ 181 | w:private:wval \ 182 | x:private:func \ 183 | i:private:init 184 | Bob.init() { 185 | setvar "${this}"rval _rval_ 186 | setvar "${this}"wval _wval_ 187 | } 188 | # Test if everything got setup properly 189 | Bob bob 190 | bsda:test:isFunction "$bob".getRval 191 | bsda:test:isFunction "$bob".setWval 192 | bsda:test:isFunction "$bob".getWval 193 | bsda:test:isFunction "$bob".func 194 | bsda:test:isFunction "$bob".init 195 | bsda:test:isFunction "$bob".copy 196 | bsda:test:isFunction "$bob".dump 197 | bsda:test:isFunction "$bob".serialise 198 | bsda:test:isFunction "$bob".delete 199 | getvar val "${bob}"rval && test "$val" = _rval_ 200 | getvar val "${bob}"wval && test "$val" = _wval_ 201 | # Test if everything gets removed properly 202 | "$bob".delete 203 | bsda:test:isNone "$bob".getRval 204 | bsda:test:isNone "$bob".setWval 205 | bsda:test:isNone "$bob".getWval 206 | bsda:test:isNone "$bob".func 207 | bsda:test:isNone "$bob".init 208 | bsda:test:isNone "$bob".copy 209 | bsda:test:isNone "$bob".dump 210 | bsda:test:isNone "$bob".serialise 211 | bsda:test:isNone "$bob".delete 212 | ! bsda:test:isSet "${bob}"rval 213 | ! bsda:test:isSet "${bob}"wval 214 | 215 | # Aggregation with undefined class should fail 216 | error="$(bsda:obj:createClass X a:y=Y 2>&1; test $? -eq 4)" 217 | bsda:test:gmatch "bsda:obj:createClass: ERROR: *" "$error" 218 | bsda:obj:createClass X a:y=Y 2> /dev/null || test $? -eq 4 219 | 220 | # Test aggregation 221 | bsda:obj:createClass A 222 | bsda:obj:createClass B a:a=A i:private:init 223 | bsda:obj:createClass C a:b=B a:a=A i:private:init 224 | B.init() { 225 | A "${this}"a 226 | } 227 | C.init() { 228 | B "${this}"b 229 | A "${this}"a 230 | } 231 | # Create aggregation 232 | C c 233 | # Check structure 234 | "$c".dump dump 235 | bsda:test:gmatch $'C@*_0_ {\n b=B@*_0_ {\n a=A@*_0_ {}\n }\n a=A@*_1_ {}\n}' "$dump" 236 | bsda:test:isFunction "$c".serialise 237 | bsda:test:isFunction "$c".copy 238 | # Copy and check copy 239 | "$c".copy c1 240 | "$c1".dump dump1 241 | bsda:test:gmatch $'C@*_1_ {\n b=B@*_1_ {\n a=A@*_2_ {}\n }\n a=A@*_3_ {}\n}' "$dump1" 242 | "$c1".delete 243 | # Check recursive delete 244 | "$c".b cb 245 | "$c".a ca 246 | "$cb".a cba 247 | "$c".delete 248 | ! bsda:test:isSet "${c}"b 249 | ! bsda:test:isSet "${c}"a 250 | ! bsda:test:isSet "${cb}"a 251 | bsda:test:isNone "$c".b 252 | bsda:test:isNone "$c".a 253 | bsda:test:isNone "$c".delete 254 | bsda:test:isNone "$cb".a 255 | bsda:test:isNone "$cb".delete 256 | bsda:test:isNone "$cba".delete 257 | # Check aggregation without type 258 | bsda:obj:createClass D a:foo 259 | D d 260 | bsda:test:isNone "$d".serialise 261 | bsda:test:isNone "$d".copy 262 | "$d".delete 263 | # Check aggregation with non copyable type 264 | bsda:obj:createClass E c:private:clean 265 | bsda:obj:createClass F a:a=A a:e=E 266 | F f 267 | bsda:test:isNone "$f".copy 268 | bsda:test:isNone "$f".serialise 269 | "$f".delete 270 | 271 | # Test stack unwinding 272 | bsda:obj:createClass SUa c:private:clean 273 | SUa.clean() { echo A; } 274 | bsda:obj:createClass SUb c:private:clean 275 | SUb.clean() { echo B; } 276 | bsda:obj:createClass SUc c:private:clean 277 | SUc.clean() { echo C; } 278 | bsda:obj:createClass SU \ 279 | i:private:init \ 280 | x:private:run 281 | SU.init() { 282 | local obj 283 | SUa obj 284 | "$caller".delete "$obj" 285 | "$this".run 286 | } 287 | SU.run() { 288 | local obj 289 | SUb obj 290 | "$caller".delete "$obj" 291 | SUc obj 292 | "$caller".delete "$obj" 293 | } 294 | unwind="$(SU obj)" 295 | test $'C\nB\nA' = "$unwind" 296 | 297 | # Test terminal stack unwinding 298 | SU.run() { 299 | local obj 300 | SUb obj 301 | "$caller".delete "$obj" 302 | SUc obj 303 | "$caller".delete "$obj" 304 | exit 305 | } 306 | unwind="$(bsda:obj:fork; SU obj)" 307 | test $'C\nB\nA' = "$unwind" 308 | 309 | # Test GC 310 | bsda:obj:createClass GCa c:private:clean 311 | GCa.clean() { echo GCa; } 312 | bsda:obj:createClass GC \ 313 | a:private:a=GCa \ 314 | i:private:init \ 315 | c:private:clean 316 | GC.init() { 317 | local obj 318 | GCa ${this}a 319 | SU obj 320 | $caller.delete $obj 321 | } 322 | GC.clean() { echo GC; } 323 | harvest="$(bsda:obj:fork; GC obj)" 324 | test $'C\nB\nA\nGC\nGCa' = "$harvest" 325 | 326 | # Test GC with unimplemented cleanup method 327 | warn=$( (bsda:obj:fork;Bar bar) 2>&1 ) 328 | bsda:test:gmatch $'* not found\nbsda:obj:exit: WARNING: *' "$warn" 329 | -------------------------------------------------------------------------------- /tests/bsda_opts.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_opts.sh 2 | . ../src/bsda_test.sh 3 | 4 | # Create empty object 5 | bsda:opts:Options opts 6 | $opts.getopt; test $? -eq 2 7 | $opts.usage; test $? -eq 2 8 | $opts.delete 9 | 10 | # Fail with incomplete tuples 11 | ! bsda:opts:Options opts TEST 12 | ! bsda:opts:Options opts TEST -t 13 | ! bsda:opts:Options opts TEST -t --test 14 | # Create object 15 | bsda:opts:Options opts TEST -t --test "Test desc" 16 | $opt.delete 17 | # Fail with incomplete tuples 18 | ! bsda:opts:Options opts \ 19 | TEST -t --test "Test desc" \ 20 | NEXT 21 | ! bsda:opts:Options opts \ 22 | TEST -t --test "Test desc" \ 23 | NEXT -n 24 | ! bsda:opts:Options opts \ 25 | TEST -t --test "Test desc" \ 26 | NEXT -n --next 27 | # Create a two options list 28 | bsda:opts:Options opts \ 29 | TEST -t --test "Test desc" \ 30 | NEXT -n --next "Next desc" 31 | 32 | # Test if all the information is contained 33 | $opts.usage str "%s,%s,%s\n" 34 | test $'-t,--test,Test desc\n-n,--next,Next desc\n' = "$str" 35 | 36 | # Test appending incomplete tuples 37 | ! $opts.append FOO 38 | ! $opts.append FOO -f 39 | ! $opts.append FOO -f --foo 40 | # Append complete tuple 41 | $opts.append FOO -f --foo "Foo desc" 42 | 43 | # Check object completeness 44 | $opts.usage str "%s,%s,%s\n" 45 | test $'-t,--test,Test desc\n-n,--next,Next desc\n-f,--foo,Foo desc\n' = "$str" 46 | 47 | # Fail with more incomplete tuples 48 | ! $opts.append BAR -b --bar "Bar desc" XXX 49 | ! $opts.append BAR -b --bar "Bar desc" XXX -x 50 | ! $opts.append BAR -b --bar "Bar desc" XXX -x --xxx 51 | # Append two options 52 | $opts.append BAR -b --bar "Bar desc" XXX -x --xxx "Xxx desc" 53 | 54 | # Check object completeness 55 | $opts.usage str "%s,%s,%s\n" 56 | test $'-t,--test,Test desc\n-n,--next,Next desc\n-f,--foo,Foo desc\n-b,--bar,Bar desc\n-x,--xxx,Xxx desc\n' = "$str" 57 | 58 | # Append an option with an argument 59 | $opts.append ARG -a* --arg "Arg desc" 60 | 61 | # Check object completeness 62 | $opts.usage str "%s,%s,%s\n" 63 | test $'-t,--test,Test desc\n-n,--next,Next desc\n-f,--foo,Foo desc\n-b,--bar,Bar desc\n-x,--xxx,Xxx desc\n-a*,--arg,Arg desc\n' = "$str" 64 | 65 | # Check matching stuff 66 | $opts.getopt opt -x 67 | test "$opt" = XXX 68 | $opts.getopt opt --test 69 | test "$opt" = TEST 70 | $opts.getopt opt -nt 71 | test "$opt" = OPT_SPLIT 72 | $opts.getopt opt -ntf 73 | test "$opt" = OPT_SPLIT 74 | $opts.getopt opt -a 75 | test "$opt" = ARG 76 | $opts.getopt opt -afoobar 77 | test "$opt" = ARG 78 | $opts.getopt opt -y 79 | test "$opt" = OPT_UNKNOWN 80 | $opts.getopt opt --yes 81 | test "$opt" = OPT_UNKNOWN 82 | ! $opts.getopt opt ./test 83 | test "$opt" = OPT_NOOPT 84 | 85 | # Clear up 86 | $opts.delete 87 | 88 | # Check OPT_SPLIT expression 89 | bsda:test:isSet bsda_opts_split 90 | do_split() { 91 | eval "$bsda_opts_split" 92 | echo "$*" 93 | } 94 | opts="$(IFS=, do_split -nt)" 95 | test "$opts" = "-n,-t" 96 | opts="$(IFS=, do_split -ntf)" 97 | test "$opts" = "-n,-tf" 98 | 99 | # Create a Flags instance 100 | bsda:opts:Flags flags 101 | $flags.add FOO 102 | $flags.add BAR 103 | $flags.add FOO 104 | $flags.add BAZ 105 | 106 | # Check flags 107 | $flags.check FOO -ne 0 108 | $flags.check BAR -ne 0 109 | $flags.check BAZ -ne 0 110 | ! $flags.check XXX -ne 0 111 | $flags.check FOO 112 | $flags.check BAR 113 | $flags.check BAZ 114 | ! $flags.check XXX 115 | ! $flags.check FOO -eq 0 116 | ! $flags.check BAR -eq 0 117 | ! $flags.check BAZ -eq 0 118 | $flags.check XXX -eq 0 119 | ! $flags.check FOO -eq 1 120 | $flags.check BAR -eq 1 121 | $flags.check BAZ -eq 1 122 | ! $flags.check XXX -eq 1 123 | $flags.check FOO -eq 2 124 | ! $flags.check BAR -eq 2 125 | ! $flags.check BAZ -eq 2 126 | ! $flags.check XXX -eq 2 127 | 128 | # Clear up 129 | $flags.delete 130 | 131 | # 132 | # Collect errors and warnings, check at the end. 133 | # 134 | bsda:err:collect 135 | 136 | # Check Flags initialisation from environment 137 | unset FOO BAR BAZ 138 | FOO=2 139 | BAR=1 140 | BAZ=1 141 | 142 | # Do not use environment 143 | bsda:opts:Flags flags 144 | $flags.check FOO -eq 0 145 | $flags.check BAR -eq 0 146 | $flags.check BAZ -eq 0 147 | $flags.delete 148 | 149 | # Use environment, but variables are not available 150 | bsda:opts:Flags flags FOO BAR BAZ 151 | $flags.check FOO -eq 0 152 | $flags.check BAR -eq 0 153 | $flags.check BAZ -eq 0 154 | $flags.delete 155 | 156 | # Add variables to environment 157 | export FOO BAR BAZ 158 | 159 | # Do not use environment 160 | bsda:opts:Flags flags 161 | $flags.check FOO -eq 0 162 | $flags.check BAR -eq 0 163 | $flags.check BAZ -eq 0 164 | $flags.delete 165 | 166 | # Use environment 167 | bsda:opts:Flags flags FOO BAR BAZ 168 | $flags.check FOO -eq 2 169 | $flags.check BAR -eq 1 170 | $flags.check BAZ -eq 1 171 | $flags.delete 172 | 173 | # Ignore invalid values 174 | BAR=-1 175 | BAZ=firecracker 176 | bsda:opts:Flags flags FOO BAR BAZ 177 | $flags.check FOO -eq 2 178 | $flags.check BAR -eq 0 179 | $flags.check BAZ -eq 0 180 | $flags.delete 181 | 182 | # Assign regardless of env 183 | bsda:opts:Flags flags FOO=3 BAR=4 BAZ=5 184 | $flags.check FOO -eq 3 185 | $flags.check BAR -eq 4 186 | $flags.check BAZ -eq 5 187 | $flags.delete 188 | 189 | # Assign regardless of env, ignoring invalid values 190 | BAR=1 191 | BAZ=1 192 | bsda:opts:Flags flags FOO=3 BAR=-1 BAZ=firecracker 193 | $flags.check FOO -eq 3 194 | $flags.check BAR -eq 0 195 | $flags.check BAZ -eq 0 196 | $flags.delete 197 | 198 | # Assign default values, env has higher priority 199 | bsda:opts:Flags flags FOO?=3 BAR?=4 BAZ?=5 200 | $flags.check FOO -eq 2 201 | $flags.check BAR -eq 1 202 | $flags.check BAZ -eq 1 203 | $flags.delete 204 | 205 | # Assign default values, env has higher priority 206 | unset BAR # not in env, select default 207 | BAZ= # empty string > default 208 | bsda:opts:Flags flags FOO?=3 BAR?=4 BAZ?=5 209 | $flags.check FOO -eq 2 210 | $flags.check BAR -eq 4 211 | $flags.check BAZ -eq 0 212 | $flags.delete 213 | 214 | # Test boolean values 215 | unset A B C D E F G H I J K L M N O 216 | bsda:opts:Flags flags A= B=0 C=no D=NO E=No F=false G=FALSE H=False \ 217 | I=1 J=yes K=YES L=Yes M=true N=TRUE O=True 218 | $flags.check A -eq 0 219 | $flags.check B -eq 0 220 | $flags.check C -eq 0 221 | $flags.check D -eq 0 222 | $flags.check E -eq 0 223 | $flags.check F -eq 0 224 | $flags.check G -eq 0 225 | $flags.check H -eq 0 226 | $flags.check I -eq 1 227 | $flags.check J -eq 1 228 | $flags.check K -eq 1 229 | $flags.check L -eq 1 230 | $flags.check M -eq 1 231 | $flags.check N -eq 1 232 | $flags.check O -eq 1 233 | $flags.delete 234 | 235 | # Test boolean default values 236 | bsda:opts:Flags flags A?= B?=0 C?=no D?=NO E?=No F?=false G?=FALSE H?=False \ 237 | I?=1 J?=yes K?=YES L?=Yes M?=true N?=TRUE O?=True 238 | $flags.check A -eq 0 239 | $flags.check B -eq 0 240 | $flags.check C -eq 0 241 | $flags.check D -eq 0 242 | $flags.check E -eq 0 243 | $flags.check F -eq 0 244 | $flags.check G -eq 0 245 | $flags.check H -eq 0 246 | $flags.check I -eq 1 247 | $flags.check J -eq 1 248 | $flags.check K -eq 1 249 | $flags.check L -eq 1 250 | $flags.check M -eq 1 251 | $flags.check N -eq 1 252 | $flags.check O -eq 1 253 | $flags.delete 254 | 255 | # Set boolean values from environment 256 | export A=1 B=yes C=YES D=Yes E=true F=TRUE G=True \ 257 | H= I=0 J=no K=NO L=No M=false N=FALSE O=False 258 | bsda:opts:Flags flags A B C D E F G H I J K L M N O 259 | $flags.check A -eq 1 260 | $flags.check B -eq 1 261 | $flags.check C -eq 1 262 | $flags.check D -eq 1 263 | $flags.check E -eq 1 264 | $flags.check F -eq 1 265 | $flags.check G -eq 1 266 | $flags.check H -eq 0 267 | $flags.check I -eq 0 268 | $flags.check J -eq 0 269 | $flags.check K -eq 0 270 | $flags.check L -eq 0 271 | $flags.check M -eq 0 272 | $flags.check N -eq 0 273 | $flags.check O -eq 0 274 | $flags.delete 275 | 276 | # Override boolean defaults from environment 277 | bsda:opts:Flags flags A?= B?=0 C?=no D?=NO E?=No F?=false G?=FALSE H?=False \ 278 | I?=1 J?=yes K?=YES L?=Yes M?=true N?=TRUE O?=True 279 | $flags.check A -eq 1 280 | $flags.check B -eq 1 281 | $flags.check C -eq 1 282 | $flags.check D -eq 1 283 | $flags.check E -eq 1 284 | $flags.check F -eq 1 285 | $flags.check G -eq 1 286 | $flags.check H -eq 0 287 | $flags.check I -eq 0 288 | $flags.check J -eq 0 289 | $flags.check K -eq 0 290 | $flags.check L -eq 0 291 | $flags.check M -eq 0 292 | $flags.check N -eq 0 293 | $flags.check O -eq 0 294 | $flags.delete 295 | 296 | # Set boolean values regardless of environment 297 | bsda:opts:Flags flags A= B=0 C=no D=NO E=No F=false G=FALSE H=False \ 298 | I=1 J=yes K=YES L=Yes M=true N=TRUE O=True 299 | $flags.check A -eq 0 300 | $flags.check B -eq 0 301 | $flags.check C -eq 0 302 | $flags.check D -eq 0 303 | $flags.check E -eq 0 304 | $flags.check F -eq 0 305 | $flags.check G -eq 0 306 | $flags.check H -eq 0 307 | $flags.check I -eq 1 308 | $flags.check J -eq 1 309 | $flags.check K -eq 1 310 | $flags.check L -eq 1 311 | $flags.check M -eq 1 312 | $flags.check N -eq 1 313 | $flags.check O -eq 1 314 | $flags.delete 315 | 316 | # Check issue sequence 317 | bsda:err:get e msg 318 | test $(($e)) -eq 0 319 | test "$e" = E_BSDA_OPTS_ENV 320 | test -n "$msg" -a -z "${msg##WARNING:*BAR=-1*}" 321 | bsda:err:get e msg 322 | test $(($e)) -eq 0 323 | test "$e" = E_BSDA_OPTS_ENV 324 | test -n "$msg" -a -z "${msg##WARNING:*BAZ=firecracker*}" 325 | bsda:err:get e msg 326 | test $(($e)) -ne 0 327 | test "$e" = E_BSDA_OPTS_ASSIGN 328 | test -n "$msg" -a -z "${msg##ERROR:*BAR=-1*}" 329 | bsda:err:get e msg 330 | test $(($e)) -ne 0 331 | test "$e" = E_BSDA_OPTS_ASSIGN 332 | test -n "$msg" -a -z "${msg##ERROR:*BAZ=firecracker*}" 333 | ! bsda:err:get e msg 334 | -------------------------------------------------------------------------------- /tests/bsda_util.sh: -------------------------------------------------------------------------------- 1 | . ../src/bsda_util.sh 2 | 3 | f=x 4 | bsda:util:map 'x y z 123.456 foo' a b c d e f 5 | test "$a" = "x" 6 | test "$b" = "y" 7 | test "$c" = "z" 8 | test "$d" = "123.456" 9 | test -z "$f" 10 | 11 | IFS=, bsda:util:map '1,2,3,4,5' a b c 12 | test "$a" = "1" 13 | test "$b" = "2" 14 | test "$c" = "3" 15 | test "$d" = "123.456" 16 | test -z "$f" 17 | 18 | IFS=: bsda:util:mapfun colonmap foo bar baz bam boom bang 19 | colonmap a:b c:d:e f 20 | test "$foo" = "a" 21 | test "$bar" = "b" 22 | test "$baz" = "c" 23 | test "$bam" = "d" 24 | test "$boom" = "e" 25 | test "$bang" = "f" 26 | 27 | colonmap yes no:maybe 28 | test "$foo" = "yes" 29 | test "$bar" = "no" 30 | test "$baz" = "maybe" 31 | test -z "$bam" 32 | test -z "$boom" 33 | test -z "$bang" 34 | 35 | unset -f colonmap 36 | 37 | bsda:util:join list , mondays are the worst 38 | test "$list" = "mondays,are,the,worst" 39 | 40 | bsda:util:split list , 41 | test "$list" = "mondays are the worst" 42 | 43 | bsda:util:count cnt $list 44 | test "$cnt" -eq 4 45 | 46 | bsda:util:in foo a b c bam boom foo bar 47 | bsda:util:in bam a b c bam boom foo bar 48 | bsda:util:in foo foo a b c bam boom bar 49 | bsda:util:in foo a b c bam boom bar foo 50 | bsda:util:in foo foo a b c bam boom bar foo 51 | ! bsda:util:in fox a b c bam boom foo bar 52 | -------------------------------------------------------------------------------- /tests/compat.sh: -------------------------------------------------------------------------------- 1 | . ../src/compat.sh 2 | 3 | # Try setvar/getvar 4 | setvar 5 | setvar x 1337 && test "$x" = 1337 6 | getvar 7 | getvar y x && test "$y" = "$x" 8 | test "$(getvar '' x)" = 1337 9 | --------------------------------------------------------------------------------