1 /**
2 * Copyright (c) 2015, Timothy Stack
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of Timothy Stack nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @file shlex.cc
30 */
31
32 #include "config.h"
33
34 #include "shlex.hh"
35
tokenize(pcre_context::capture_t & cap_out,shlex_token_t & token_out)36 bool shlex::tokenize(pcre_context::capture_t &cap_out, shlex_token_t &token_out)
37 {
38 while (this->s_index < this->s_len) {
39 switch (this->s_str[this->s_index]) {
40 case '\\':
41 cap_out.c_begin = this->s_index;
42 if (this->s_index + 1 < this->s_len) {
43 token_out = shlex_token_t::ST_ESCAPE;
44 this->s_index += 2;
45 cap_out.c_end = this->s_index;
46 }
47 else {
48 this->s_index += 1;
49 cap_out.c_end = this->s_index;
50 token_out = shlex_token_t::ST_ERROR;
51 }
52 return true;
53 case '\"':
54 if (!this->s_ignore_quotes) {
55 switch (this->s_state) {
56 case state_t::STATE_NORMAL:
57 cap_out.c_begin = this->s_index;
58 this->s_index += 1;
59 cap_out.c_end = this->s_index;
60 token_out = shlex_token_t::ST_DOUBLE_QUOTE_START;
61 this->s_state = state_t::STATE_IN_DOUBLE_QUOTE;
62 return true;
63 case state_t::STATE_IN_DOUBLE_QUOTE:
64 cap_out.c_begin = this->s_index;
65 this->s_index += 1;
66 cap_out.c_end = this->s_index;
67 token_out = shlex_token_t::ST_DOUBLE_QUOTE_END;
68 this->s_state = state_t::STATE_NORMAL;
69 return true;
70 default:
71 break;
72 }
73 }
74 break;
75 case '\'':
76 if (!this->s_ignore_quotes) {
77 switch (this->s_state) {
78 case state_t::STATE_NORMAL:
79 cap_out.c_begin = this->s_index;
80 this->s_index += 1;
81 cap_out.c_end = this->s_index;
82 token_out = shlex_token_t::ST_SINGLE_QUOTE_START;
83 this->s_state = state_t::STATE_IN_SINGLE_QUOTE;
84 return true;
85 case state_t::STATE_IN_SINGLE_QUOTE:
86 cap_out.c_begin = this->s_index;
87 this->s_index += 1;
88 cap_out.c_end = this->s_index;
89 token_out = shlex_token_t::ST_SINGLE_QUOTE_END;
90 this->s_state = state_t::STATE_NORMAL;
91 return true;
92 default:
93 break;
94 }
95 }
96 break;
97 case '$':
98 switch (this->s_state) {
99 case state_t::STATE_NORMAL:
100 case state_t::STATE_IN_DOUBLE_QUOTE:
101 this->scan_variable_ref(cap_out, token_out);
102 return true;
103 default:
104 break;
105 }
106 break;
107 case '~':
108 switch (this->s_state) {
109 case state_t::STATE_NORMAL:
110 cap_out.c_begin = this->s_index;
111 this->s_index += 1;
112 while (this->s_index < this->s_len &&
113 (isalnum(this->s_str[this->s_index]) ||
114 this->s_str[this->s_index] == '_' ||
115 this->s_str[this->s_index] == '-')) {
116 this->s_index += 1;
117 }
118 cap_out.c_end = this->s_index;
119 token_out = shlex_token_t::ST_TILDE;
120 return true;
121 default:
122 break;
123 }
124 break;
125 case ' ':
126 case '\t':
127 switch (this->s_state) {
128 case state_t::STATE_NORMAL:
129 cap_out.c_begin = this->s_index;
130 while (isspace(this->s_str[this->s_index])) {
131 this->s_index += 1;
132 }
133 cap_out.c_end = this->s_index;
134 token_out = shlex_token_t::ST_WHITESPACE;
135 return true;
136 default:
137 break;
138 }
139 break;
140 default:
141 break;
142 }
143
144 this->s_index += 1;
145 }
146
147 return false;
148 }
149
scan_variable_ref(pcre_context::capture_t & cap_out,shlex_token_t & token_out)150 void shlex::scan_variable_ref(pcre_context::capture_t &cap_out,
151 shlex_token_t &token_out)
152 {
153 cap_out.c_begin = this->s_index;
154 this->s_index += 1;
155 if (this->s_index >= this->s_len) {
156 cap_out.c_end = this->s_index;
157 token_out = shlex_token_t::ST_ERROR;
158 return;
159 }
160
161 if (this->s_str[this->s_index] == '{') {
162 token_out = shlex_token_t::ST_QUOTED_VARIABLE_REF;
163 this->s_index += 1;
164 } else {
165 token_out = shlex_token_t::ST_VARIABLE_REF;
166 }
167
168 while (this->s_index < this->s_len) {
169 if (token_out == shlex_token_t::ST_VARIABLE_REF) {
170 if (isalnum(this->s_str[this->s_index]) ||
171 this->s_str[this->s_index] == '#' ||
172 this->s_str[this->s_index] == '_') {
173 this->s_index += 1;
174 }
175 else {
176 break;
177 }
178 }
179 else {
180 if (this->s_str[this->s_index] == '}') {
181 this->s_index += 1;
182 break;
183 }
184 this->s_index += 1;
185 }
186 }
187
188 cap_out.c_end = this->s_index;
189 if (token_out == shlex_token_t::ST_QUOTED_VARIABLE_REF &&
190 this->s_str[this->s_index - 1] != '}') {
191 cap_out.c_begin += 1;
192 cap_out.c_end = cap_out.c_begin + 1;
193 token_out = shlex_token_t::ST_ERROR;
194 }
195 }
196
resolve_home_dir(std::string & result,const pcre_context::capture_t cap) const197 void shlex::resolve_home_dir(std::string &result,
198 const pcre_context::capture_t cap) const
199 {
200 if (cap.length() == 1) {
201 result.append(getenv_opt("HOME").value_or("~"));
202 } else {
203 auto username = (char *) alloca(cap.length());
204
205 memcpy(username, &this->s_str[cap.c_begin + 1], cap.length() - 1);
206 username[cap.length() - 1] = '\0';
207 auto pw = getpwnam(username);
208 if (pw != nullptr) {
209 result.append(pw->pw_dir);
210 } else {
211 result.append(&this->s_str[cap.c_begin], cap.length());
212 }
213 }
214 }
215