# libpaper 2.1.2 — paper-size configuration library (BRINTOS-326). # # The port ships two runnable artifacts, `paper` and `paperconf`, so every case # here drives the library through one of them rather than through a probe: the # CLIs are thin wrappers whose output IS the library's answer. `paper` is the # modern interface (natural units, long options); `paperconf` is the legacy one # every pre-2.x consumer calls (lowercase names, PostScript points, -w/-h/-s). # Both are covered, because a consumer that finds only one of them has an # incomplete package, not a working port. # # Cases are grouped: presence and shipped artifacts, the paper(1) surface (name # lookup, the three unit conversions, listing, the error paths), the paperconf(1) # surface, and finally the four-tier default-paper resolution chain — $PAPERSIZE, # $XDG_CONFIG_HOME/papersize, the locale's LC_PAPER, $sysconfdir/papersize — which # is the part of libpaper an application actually depends on and the part that # reaches furthest into the platform. # # LC_ALL is pinned to C throughout. That is not merely hygiene: glibc's C locale # defines LC_PAPER as 210x297 mm, so the locale tier resolves to A4 and every # expectation below is stable only while the locale is known. # # Every expectation is the value a native x86_64 build of the identical 2.1.2 # source produces under the same environment, not a value read off the paperspecs # table — so a red here is the platform's, not the test's. That control is also # why `(null)` appears in the paper(1) error cases rather than `paper`: upstream # sets the gnulib program_name only under --enable-relocatable, which neither this # recipe nor meta-oe's enables, and the native build prints `(null)` identically. . "$BST_LIB" require paper libpaper/present bst_ok libpaper/present LC_ALL=C; export LC_ALL unset PAPERSIZE XDG_CONFIG_HOME # --- the package is whole --------------------------------------------------- # paperconf is a `not ok` rather than a `skip` when missing: it ships from the # same package as paper, so its absence is a broken port, not an absent tool. if command -v paperconf >/dev/null 2>&1; then bst_ok libpaper/paperconf-present else bst_notok libpaper/paperconf-present "paperconf missing though paper is installed"; fi # The specs table is the library's data, and every size below comes from it; the # header and the archive are what make the port consumable by anything else. check libpaper/specs-file test -s /etc/paperspecs check libpaper/header test -s /usr/include/paper.h check libpaper/staticlib test -s /usr/lib/libpaper.a # A real ar archive carrying the library's entry points — the -staticdev package # is a link target, so an archive that is present but empty or truncated would # pass the size check above and fail every consumer. check_eq libpaper/staticlib-ar '!' "$(head -c 7 /usr/lib/libpaper.a)" if grep -q defaultpapername /usr/lib/libpaper.a 2>/dev/null; then bst_ok libpaper/staticlib-symbols else bst_notok libpaper/staticlib-symbols "libpaper.a carries no defaultpapername"; fi # --- paper(1): identity and help -------------------------------------------- check_out libpaper/version 'paper 2.1.2' paper --version check_out libpaper/help-usage '[OPTION...] [PAPER...|--all]' paper --help check_out libpaper/help-units 'pt, mm, in; default: natural units' paper --help # --- paper(1): name lookup -------------------------------------------------- # The default with no argument, then the same size named explicitly: they must # agree, which is what says the default tier resolved to a real table entry. check_out libpaper/default 'A4: 210x297 mm' paper check_out libpaper/name-a4 'A4: 210x297 mm' paper a4 check_out libpaper/name-letter 'Letter: 8.5x11 in' paper letter # Lookup is case-insensitive by contract, and returns the table's own casing. check_out libpaper/name-case 'Letter: 8.5x11 in' paper LETTER # Names with a space and names that begin with a digit are both in the table and # both are shapes a naive tokenizer drops. check_out libpaper/name-spaced 'JIS B5: 182x257 mm' paper 'JIS B5' check_out libpaper/name-numeric '11x17: 11x17 in' paper 11x17 # --no-size prints the name alone; asserted by equality because a substring test # would pass on the full "A4: 210x297 mm" line this option exists to suppress. check_eq libpaper/no-size 'A4' "$(paper --no-size a4)" # --- paper(1): unit conversion ---------------------------------------------- # The natural unit of each entry is what the table records; --unit converts. The # in/pt values are the library's own %g-formatted doubles, so these three cases # are as much a float-formatting assertion for this arm as a conversion one. check_out libpaper/unit-mm 'Letter: 215.9x279.4 mm' paper --unit mm letter check_out libpaper/unit-in 'A4: 8.26772x11.6929 in' paper --unit in a4 check_out libpaper/unit-pt 'A4: 595.276x841.89 pt' paper --unit pt a4 # --- paper(1): several papers in one invocation ----------------------------- # Each argument is looked up independently; this catches state left behind # between lookups. multi=$(paper a4 letter 2>&1) case "$multi" in *'A4: 210x297 mm'*) case "$multi" in *'Letter: 8.5x11 in'*) bst_ok libpaper/multi-arg ;; *) bst_notok libpaper/multi-arg "second paper missing: $(_bst_1line "$multi")" ;; esac ;; *) bst_notok libpaper/multi-arg "first paper missing: $(_bst_1line "$multi")" ;; esac # --- paper(1): the whole table ---------------------------------------------- # 82 entries is the shipped paperspecs verbatim, so a short count means the file # was truncated or a line failed to parse. The last entry is asserted by name # because it is the second "Letter" — the mm-denominated one the locale tier # matches — and a table that stopped early would still contain the first. check_eq libpaper/all-count 82 "$(paper --all | wc -l | tr -d ' ')" check_eq libpaper/all-last 'Letter: 216x279 mm' "$(paper --all | tail -1)" check_out libpaper/all-legal 'Legal: 8.5x14 in' paper --all # --- paper(1): the error paths ---------------------------------------------- # Each is checked for its rc AND its message: an rc alone would pass for a tool # that failed for an unrelated reason. bad=$(paper nosuchpaper 2>&1); bad_rc=$? if [ "$bad_rc" -eq 1 ]; then case "$bad" in *"unknown paper 'nosuchpaper'"*) bst_ok libpaper/reject-unknown-paper ;; *) bst_notok libpaper/reject-unknown-paper "rc=1 but wrong message: $(_bst_1line "$bad")" ;; esac else bst_notok libpaper/reject-unknown-paper "rc=$bad_rc want=1 (unknown paper was accepted)" fi opt=$(paper --nosuchopt 2>&1); opt_rc=$? if [ "$opt_rc" -eq 1 ]; then case "$opt" in *"unknown option '--nosuchopt'"*) bst_ok libpaper/reject-unknown-option ;; *) bst_notok libpaper/reject-unknown-option "rc=1 but wrong message: $(_bst_1line "$opt")" ;; esac else bst_notok libpaper/reject-unknown-option "rc=$opt_rc want=1" fi unit=$(paper --unit bogus a4 2>&1); unit_rc=$? if [ "$unit_rc" -eq 1 ]; then case "$unit" in *'bad unit (valid units: pt, mm, in)'*) bst_ok libpaper/reject-bad-unit ;; *) bst_notok libpaper/reject-bad-unit "rc=1 but wrong message: $(_bst_1line "$unit")" ;; esac else bst_notok libpaper/reject-bad-unit "rc=$unit_rc want=1" fi # A missing option argument is distinguished from an unknown option by the # leading ':' in the getopt_long spec, so the two produce different messages. miss=$(paper --unit 2>&1); miss_rc=$? if [ "$miss_rc" -eq 1 ]; then case "$miss" in *"option '--unit' requires an argument"*) bst_ok libpaper/reject-missing-arg ;; *) bst_notok libpaper/reject-missing-arg "rc=1 but wrong message: $(_bst_1line "$miss")" ;; esac else bst_notok libpaper/reject-missing-arg "rc=$miss_rc want=1" fi # --all and an explicit paper are mutually exclusive: rc 1 with the usage text. check_rc libpaper/reject-all-with-paper 1 paper --all a4 # --- paperconf(1): the legacy interface ------------------------------------- # Lowercased names, PostScript points by default, and the unit suffixes are all # ABI for pre-2.x consumers; each is asserted by equality because the whole point # of this interface is its exact output shape. check_eq libpaper/pc-default 'a4' "$(paperconf)" check_eq libpaper/pc-explicit-d 'a4' "$(paperconf -d)" check_eq libpaper/pc-lowercases 'a4' "$(paperconf -p A4)" check_eq libpaper/pc-uppername 'A4' "$(paperconf -N -p a4)" check_eq libpaper/pc-size-pt '595.276 841.89' "$(paperconf -p a4 -s)" check_eq libpaper/pc-width-pt '612' "$(paperconf -p letter -w)" check_eq libpaper/pc-height-pt '792' "$(paperconf -p letter -h)" check_eq libpaper/pc-size-mm '210 mm 297 mm' "$(paperconf -p a4 -s -m)" check_eq libpaper/pc-size-cm '21 cm 29.7 cm' "$(paperconf -p a4 -s -c)" check_eq libpaper/pc-width-in '8.5"' "$(paperconf -p letter -w -i)" check_eq libpaper/pc-name-and-size 'a4 595.276 841.89' "$(paperconf -p a4 -n -s)" check_eq libpaper/pc-all-count 82 "$(paperconf -a | wc -l | tr -d ' ')" # paperconf signals an unknown paper with rc 2, and a misuse of its options with # rc 1 plus the usage line — two distinct contracts a caller scripts against. check_rc libpaper/pc-reject-unknown 2 paperconf -p nosuchpaper pcu=$(paperconf -n -N 2>&1); pcu_rc=$? if [ "$pcu_rc" -eq 1 ]; then case "$pcu" in *'usage: paperconf'*) bst_ok libpaper/pc-reject-misuse ;; *) bst_notok libpaper/pc-reject-misuse "rc=1 but no usage line: $(_bst_1line "$pcu")" ;; esac else bst_notok libpaper/pc-reject-misuse "rc=$pcu_rc want=1" fi # --- the default-paper resolution chain ------------------------------------- # Four tiers, in the order libpaper consults them. Each case below shadows the # tier under it, so the pair of results is what proves the ORDER rather than # merely that some tier answered. tmp=${TMPDIR:-/tmp}/bst-libpaper.$$ rm -rf "$tmp"; mkdir -p "$tmp" # Tier 1 — $PAPERSIZE, which overrides everything. check_out libpaper/tier-env 'Letter: 8.5x11 in' env PAPERSIZE=letter paper # Tier 2 — $XDG_CONFIG_HOME/papersize, a one-line file naming a size. printf 'executive\n' > "$tmp/papersize" check_out libpaper/tier-xdg 'Executive: 7.25x10.5 in' env XDG_CONFIG_HOME="$tmp" paper check_out libpaper/tier-env-beats-xdg 'A3: 297x420 mm' env XDG_CONFIG_HOME="$tmp" PAPERSIZE=a3 paper rm -f "$tmp/papersize" # $XDG_CONFIG_HOME/paperspecs adds sizes to the system table rather than # replacing it — asserted by both halves: the added size resolves with the user # file in scope, and is unknown without it. printf 'Emcraft,100,200,mm\n' > "$tmp/paperspecs" check_out libpaper/user-spec-added 'Emcraft: 100x200 mm' env XDG_CONFIG_HOME="$tmp" paper Emcraft check_out libpaper/user-spec-keeps-system 'A4: 210x297 mm' env XDG_CONFIG_HOME="$tmp" paper a4 check_rc libpaper/user-spec-scoped 1 paper Emcraft rm -rf "$tmp" # Tier 3 — the locale. glibc's C locale sets LC_PAPER to 210x297 mm, which the # table's mm-denominated entries match, so A4 must win over an /etc/papersize # naming something else. That file is tier 4 and is consulted only if the locale # tier declines, which makes this the one case that reaches nl_langinfo's # _NL_PAPER_WIDTH/_NL_PAPER_HEIGHT on this arm: a `Legal` answer here means the # locale layer returned nothing, not that libpaper mis-ordered its tiers. if [ -e /etc/papersize ]; then cp /etc/papersize "$tmp.papersize.bak" 2>/dev/null; fi if printf 'legal\n' > /etc/papersize 2>/dev/null; then locale_out=$(paper 2>&1) rm -f /etc/papersize if [ -f "$tmp.papersize.bak" ]; then mv "$tmp.papersize.bak" /etc/papersize; fi check_eq libpaper/tier-locale 'A4: 210x297 mm' "$locale_out" else bst_skip libpaper/tier-locale "/etc is not writable on this image" fi