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