xref: /freebsd-src/contrib/less/evar.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1*c77c4889SXin LI /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  Mark Nudelman
3*c77c4889SXin LI  *
4*c77c4889SXin LI  * You may distribute under the terms of either the GNU General Public
5*c77c4889SXin LI  * License or the Less License, as specified in the README file.
6*c77c4889SXin LI  *
7*c77c4889SXin LI  * For more information, see the README file.
8*c77c4889SXin LI  */
9*c77c4889SXin LI 
10*c77c4889SXin LI /*
11*c77c4889SXin LI  * Code to support expanding environment variables in text.
12*c77c4889SXin LI  */
13*c77c4889SXin LI 
14*c77c4889SXin LI #include "less.h"
15*c77c4889SXin LI #include "xbuf.h"
16*c77c4889SXin LI 
17*c77c4889SXin LI struct replace {
18*c77c4889SXin LI 	struct replace *r_next;
19*c77c4889SXin LI 	char *r_fm;
20*c77c4889SXin LI 	char *r_to;
21*c77c4889SXin LI };
22*c77c4889SXin LI 
23*c77c4889SXin LI /*
24*c77c4889SXin LI  * Skip to the next unescaped slash or right curly bracket in a string.
25*c77c4889SXin LI  */
26*c77c4889SXin LI static size_t skipsl(constant char *buf, size_t len, size_t e)
27*c77c4889SXin LI {
28*c77c4889SXin LI 	lbool esc = FALSE;
29*c77c4889SXin LI 	while (e < len && buf[e] != '\0' && (esc || (buf[e] != '/' && buf[e] != '}')))
30*c77c4889SXin LI 	{
31*c77c4889SXin LI 		esc = (!esc && buf[e] == '\\');
32*c77c4889SXin LI 		++e;
33*c77c4889SXin LI 	}
34*c77c4889SXin LI 	return e;
35*c77c4889SXin LI }
36*c77c4889SXin LI 
37*c77c4889SXin LI /*
38*c77c4889SXin LI  * Parse a replacement string: one or more instances of
39*c77c4889SXin LI  * (slash, pattern, slash, replacement), followed by right curly bracket.
40*c77c4889SXin LI  * Replacement may be empty in which case the second slash is optional.
41*c77c4889SXin LI  */
42*c77c4889SXin LI static struct replace * make_replaces(mutable char *buf, size_t len, size_t *pe, char term)
43*c77c4889SXin LI {
44*c77c4889SXin LI 	size_t e = *pe;
45*c77c4889SXin LI 	struct replace *replaces = NULL;
46*c77c4889SXin LI 
47*c77c4889SXin LI 	while (term == '/')
48*c77c4889SXin LI 	{
49*c77c4889SXin LI 		struct replace *repl;
50*c77c4889SXin LI 		size_t to;
51*c77c4889SXin LI 		size_t fm = e;
52*c77c4889SXin LI 		e = skipsl(buf, len, e);
53*c77c4889SXin LI 		if (e >= len) break;
54*c77c4889SXin LI 		if (e == fm) /* missing fm string; we're done */
55*c77c4889SXin LI 		{
56*c77c4889SXin LI 			while (e < len)
57*c77c4889SXin LI 				if (buf[e++] == '}') break;
58*c77c4889SXin LI 			break;
59*c77c4889SXin LI 		}
60*c77c4889SXin LI 		term = buf[e];
61*c77c4889SXin LI 		buf[e++] = '\0'; /* terminate the fm string */
62*c77c4889SXin LI 		if (term != '/') /* missing to string */
63*c77c4889SXin LI 		{
64*c77c4889SXin LI 			to = e-1;
65*c77c4889SXin LI 		} else
66*c77c4889SXin LI 		{
67*c77c4889SXin LI 			to = e;
68*c77c4889SXin LI 			e = skipsl(buf, len, e);
69*c77c4889SXin LI 			if (e >= len) break;
70*c77c4889SXin LI 			term = buf[e];
71*c77c4889SXin LI 			buf[e++] = '\0'; /* terminate the to string */
72*c77c4889SXin LI 		}
73*c77c4889SXin LI 		repl = ecalloc(1, sizeof(struct replace));
74*c77c4889SXin LI 		repl->r_fm = &buf[fm];
75*c77c4889SXin LI 		repl->r_to = &buf[to];
76*c77c4889SXin LI 		repl->r_next = replaces;
77*c77c4889SXin LI 		replaces = repl;
78*c77c4889SXin LI 	}
79*c77c4889SXin LI 	*pe = e;
80*c77c4889SXin LI 	return replaces;
81*c77c4889SXin LI }
82*c77c4889SXin LI 
83*c77c4889SXin LI /*
84*c77c4889SXin LI  * Free a list of replace structs.
85*c77c4889SXin LI  */
86*c77c4889SXin LI static void free_replaces(struct replace *replaces)
87*c77c4889SXin LI {
88*c77c4889SXin LI 	while (replaces != NULL)
89*c77c4889SXin LI 	{
90*c77c4889SXin LI 		struct replace *r = replaces;
91*c77c4889SXin LI 		replaces = r->r_next;
92*c77c4889SXin LI 		free(r);
93*c77c4889SXin LI 	}
94*c77c4889SXin LI }
95*c77c4889SXin LI 
96*c77c4889SXin LI /*
97*c77c4889SXin LI  * See if the initial substring of a string matches a pattern.
98*c77c4889SXin LI  * Backslash escapes in the pattern are ignored.
99*c77c4889SXin LI  * Return the length of the matched substring, or 0 if no match.
100*c77c4889SXin LI  */
101*c77c4889SXin LI static size_t evar_match(constant char *str, constant char *pat)
102*c77c4889SXin LI {
103*c77c4889SXin LI 	size_t len = 0;
104*c77c4889SXin LI 	while (*pat != '\0')
105*c77c4889SXin LI 	{
106*c77c4889SXin LI 		if (*pat == '\\') ++pat;
107*c77c4889SXin LI 		if (*str++ != *pat++) return 0;
108*c77c4889SXin LI 		++len;
109*c77c4889SXin LI 	}
110*c77c4889SXin LI 	return len;
111*c77c4889SXin LI }
112*c77c4889SXin LI 
113*c77c4889SXin LI /*
114*c77c4889SXin LI  * Find the replacement for a string (&evar[*pv]),
115*c77c4889SXin LI  * given a list of replace structs.
116*c77c4889SXin LI  */
117*c77c4889SXin LI static constant char * find_replace(constant struct replace *repl, constant char *evar, size_t *pv)
118*c77c4889SXin LI {
119*c77c4889SXin LI 	for (;  repl != NULL;  repl = repl->r_next)
120*c77c4889SXin LI 	{
121*c77c4889SXin LI 		size_t len = evar_match(&evar[*pv], repl->r_fm);
122*c77c4889SXin LI 		if (len > 0)
123*c77c4889SXin LI 		{
124*c77c4889SXin LI 			*pv += len;
125*c77c4889SXin LI 			return repl->r_to;
126*c77c4889SXin LI 		}
127*c77c4889SXin LI 	}
128*c77c4889SXin LI 	return NULL;
129*c77c4889SXin LI }
130*c77c4889SXin LI 
131*c77c4889SXin LI /*
132*c77c4889SXin LI  * With buf[e] positioned just after NAME in "${NAME" and
133*c77c4889SXin LI  * term containing the character at that point, parse the rest
134*c77c4889SXin LI  * of the environment var string (including the final right curly bracket).
135*c77c4889SXin LI  * Write evar to xbuf, performing any specified text replacements.
136*c77c4889SXin LI  * Return the new value of e to point just after the final right curly bracket.
137*c77c4889SXin LI  */
138*c77c4889SXin LI static size_t add_evar(struct xbuffer *xbuf, mutable char *buf, size_t len, size_t e, constant char *evar, char term)
139*c77c4889SXin LI {
140*c77c4889SXin LI 	struct replace *replaces = make_replaces(buf, len, &e, term);
141*c77c4889SXin LI 	size_t v;
142*c77c4889SXin LI 
143*c77c4889SXin LI 	for (v = 0;  evar[v] != '\0'; )
144*c77c4889SXin LI 	{
145*c77c4889SXin LI 		constant char *repl = find_replace(replaces, evar, &v);
146*c77c4889SXin LI 		if (repl == NULL)
147*c77c4889SXin LI 			xbuf_add_char(xbuf, evar[v++]);
148*c77c4889SXin LI 		else
149*c77c4889SXin LI 		{
150*c77c4889SXin LI 			size_t r;
151*c77c4889SXin LI 			for (r = 0;  repl[r] != '\0';  r++)
152*c77c4889SXin LI 			{
153*c77c4889SXin LI 				if (repl[r] == '\\') ++r;
154*c77c4889SXin LI 				xbuf_add_char(xbuf, repl[r]);
155*c77c4889SXin LI 			}
156*c77c4889SXin LI 		}
157*c77c4889SXin LI 	}
158*c77c4889SXin LI 	free_replaces(replaces);
159*c77c4889SXin LI 	return e;
160*c77c4889SXin LI }
161*c77c4889SXin LI 
162*c77c4889SXin LI /*
163*c77c4889SXin LI  * Expand env variables in a string.
164*c77c4889SXin LI  * Writes expanded output to xbuf. Corrupts buf.
165*c77c4889SXin LI  */
166*c77c4889SXin LI public void expand_evars(mutable char *buf, size_t len, struct xbuffer *xbuf)
167*c77c4889SXin LI {
168*c77c4889SXin LI 	size_t i;
169*c77c4889SXin LI 	for (i = 0;  i < len; )
170*c77c4889SXin LI 	{
171*c77c4889SXin LI 		if (i+1 < len && buf[i] == '$' && buf[i+1] == '{')
172*c77c4889SXin LI 		{
173*c77c4889SXin LI 			constant char *evar;
174*c77c4889SXin LI 			char term;
175*c77c4889SXin LI 			size_t e;
176*c77c4889SXin LI 			i += 2; /* skip "${" */
177*c77c4889SXin LI 			for (e = i;  e < len;  e++)
178*c77c4889SXin LI 				if (buf[e] == '\0' || buf[e] == '}' || buf[e] == '/')
179*c77c4889SXin LI 					break;
180*c77c4889SXin LI 			if (e >= len || buf[e] == '\0')
181*c77c4889SXin LI 				break; /* missing right curly bracket; ignore var */
182*c77c4889SXin LI 			term = buf[e];
183*c77c4889SXin LI 			buf[e++] = '\0';
184*c77c4889SXin LI 			evar = lgetenv_ext(&buf[i], xbuf->data, xbuf->end);
185*c77c4889SXin LI 			if (evar == NULL) evar = "";
186*c77c4889SXin LI 			i = add_evar(xbuf, buf, len, e, evar, term);
187*c77c4889SXin LI 		} else
188*c77c4889SXin LI 		{
189*c77c4889SXin LI 			xbuf_add_char(xbuf, buf[i++]);
190*c77c4889SXin LI 		}
191*c77c4889SXin LI 	}
192*c77c4889SXin LI }
193