1#! /module/for/moderni/sh
2\command unalias replacein 2>/dev/null
3
4# var/string/replacein
5#
6# replacein: Replace the leading or (-t)railing occurrence or (-a)ll
7# occurrences of a string by another string in a variable.
8#
9# Usage: replacein [ -t | -a ] <varname> <oldstring> <newstring>
10#
11# TODO: support glob
12# TODO: reconsider option letters
13#
14# --- begin license ---
15# Copyright (c) 2019 Martijn Dekker <martijn@inlv.org>, Groningen, Netherlands
16#
17# Permission to use, copy, modify, and/or distribute this software for any
18# purpose with or without fee is hereby granted, provided that the above
19# copyright notice and this permission notice appear in all copies.
20#
21# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
22# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
23# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
24# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
27# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28# --- end license ---
29
30if thisshellhas PSREPLACE; then
31	# bash, *ksh, zsh, yash: we can use ${var/"x"/"y"} and ${var//"x"/"y"}
32	replacein() {
33		case ${#},${1-},${2-} in
34		( 3,,"${2-}" | 3,[0123456789]*,"${2-}" | 3,*[!"$ASCIIALNUM"_]*,"${2-}" )
35			die "replacein: invalid variable name: $1" ;;
36		( 4,-[ta], | 4,-[ta],[0123456789]* | 4,-[ta],*[!"$ASCIIALNUM"_]* )
37			die "replacein: invalid variable name: $2" ;;
38		( 3,* )	eval "$1=\${$1/\"\$2\"/\"\$3\"}" ;;
39		( 4,-t,* )
40			eval "if str in \"\$$2\" \"\$3\"; then
41				$2=\${$2%\"\$3\"*}\$4\${$2##*\"\$3\"}
42			fi" ;;
43		( 4,-a,* )
44			eval "$2=\${$2//\"\$3\"/\"\$4\"}" ;;
45		( * )	die "replacein: invalid arguments" ;;
46		esac
47	}
48else
49	# POSIX:
50	replacein() {
51		case ${#},${1-},${2-} in
52		( 3,,"${2-}" | 3,[0123456789]*,"${2-}" | 3,*[!"$ASCIIALNUM"_]*,"${2-}" )
53			die "replacein: invalid variable name: $1" ;;
54		( 4,-[ta], | 4,-[ta],[0123456789]* | 4,-[ta],*[!"$ASCIIALNUM"_]* )
55			die "replacein: invalid variable name: $2" ;;
56		( 3,* )	eval "if str in \"\$$1\" \"\$2\"; then
57				$1=\${$1%%\"\$2\"*}\$3\${$1#*\"\$2\"}
58			fi" ;;
59		( 4,-t,* )
60			eval "if str in \"\$$2\" \"\$3\"; then
61				$2=\${$2%\"\$3\"*}\$4\${$2##*\"\$3\"}
62			fi" ;;
63		( 4,-a,* )
64			if str in "$4" "$3"; then
65				# use a temporary variable to avoid an infinite loop when
66				# replacing all of one character by one or more of itself
67				# (e.g. "replacein -a somevariable / //")
68				eval "_Msh_rAi=\$$2
69				$2=
70				while str in \"\${_Msh_rAi}\" \"\$3\"; do
71					$2=\$$2\${_Msh_rAi%%\"\$3\"*}\$4
72					_Msh_rAi=\${_Msh_rAi#*\"\$3\"}
73				done
74				$2=\$$2\${_Msh_rAi}"
75				unset -v _Msh_rAi
76			else
77				# use faster algorithm without extra variable
78				eval "while str in \"\$$2\" \"\$3\"; do
79					$2=\${$2%%\"\$3\"*}\$4\${$2#*\"\$3\"}
80				done"
81			fi ;;
82		( * )	die "replacein: invalid arguments" ;;
83		esac
84	}
85fi
86
87if thisshellhas ROFUNC; then
88	readonly -f replacein
89fi
90