1#! /usr/bin/env modernish
2#! use safe -k
3#! use var/loop
4
5# Test and demonstration program for shellquote().
6# See README.md under "Low-level shell utilities" -> "shellquote" for more info.
7#
8# This program includes an -N option for trying the current shell's builtin
9# quoting algorithm. Its output is not always portable, and it's generally
10# *much* worse at minimising exponential growth when quoting multiple times.
11
12PATH=$DEFPATH   # use standard utilities, esp. 'tput'
13
14showusage() {
15	putln "usage: ${ME##*/} [ -n DEPTHLEVEL ] [ -f ] [ -P | -N ] [ STRING ]"
16}
17
18# Parse options.
19force='' method='' level=6
20while getopts n:fPN opt; do
21	case $opt in
22	( n )	level=$OPTARG ;;	# Number of times to quote
23	( f )	force='-f' ;;		# Force quoting shell-safe strings
24	( P )	method='-P' ;;		# Portable POSIX quoting (no $CC*)
25	( N )	method=native ;;	# Use shell's native algorithm
26	( * )	exit -u 1 ;;		# 'exit -u' calls showusage()
27	esac
28done
29shift $((OPTIND-1))
30str isint $level || exit -u 1 "bad number: $level"
31
32# Set the string to quote.
33if let "$#"; then
34	# "$*" separates all arguments with the first character of IFS.
35	# But in the safe mode, IFS is emptied to disable global field
36	# splitting, so there would be no separator.
37	push IFS; IFS=' '; quotestring="$*"; pop IFS
38else
39	quotestring='Let`s \see hôw modernish shellquote()
40		"handles" '\''quoting'\'' $of '${CCv}'`weird` multi#line \$strings\\.
41		(To try another string, specify one on the command line.)\'
42fi
43quotestring_orig=$quotestring
44
45# Quoted strings can get large, so make them easier to tell apart.
46if is onterminal 1; then
47	emphasis=$(tput md; tput setaf 1 2>/dev/null || tput rev) # bold & either red or reverse
48	reset=$(tput sgr0)
49else
50	emphasis='' reset=''
51fi
52
53# Quote the specified number of levels with specified options.
54LOOP for i=1 to level; DO
55	case $method in
56	( native )
57		# Set a dummy alias in a subshell and ask the shell to print
58		# it, quoting the value with its internal algorithm.
59		quotestring=$(alias Q=$quotestring; alias Q)
60		quotestring=${quotestring#*Q=} ;;
61	( * )
62		# Modernish shellquoting.
63		#    (Due to the shell's empty removal mechanism, $force
64		#    and $method below are skipped entirely if empty.)
65		shellquote $force $method quotestring ;;
66	esac
67	putln "${emphasis}q$i: [${#quotestring}]$reset $quotestring" ""
68DONE
69
70# Roll back the quoting and verify the results.
71LOOP for i=level-1 to 0; DO
72	eval quotestring=$quotestring || exit
73	putln "${emphasis}u$i: [${#quotestring}]$reset $quotestring" ""
74DONE
75str eq $quotestring $quotestring_orig && putln ok
76