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