1 /*
2 * Copyright (C) 1997-2009, Michael Jennings
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 static const char cvs_ident[] = "$Id: misc.c 51650 2010-08-26 01:34:13Z lucas $";
25
26 #include "config.h"
27 #include "feature.h"
28
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <errno.h>
38
39 #include "command.h"
40 #include "startup.h"
41 #include "misc.h"
42 #include "options.h"
43
44 const char *
my_basename(const char * str)45 my_basename(const char *str)
46 {
47 const char *base = strrchr(str, '/');
48
49 return (base ? base + 1 : str);
50 }
51
52 /*
53 * Compares the first n characters of s1 and s2, where n is strlen(s2)
54 * Returns strlen(s2) if they match, 0 if not.
55 */
56 unsigned long
str_leading_match(register const char * s1,register const char * s2)57 str_leading_match(register const char *s1, register const char *s2)
58 {
59 register unsigned long n;
60
61 if (!s1 || !s2) {
62 return (0);
63 }
64 for (n = 0; *s2; n++, s1++, s2++) {
65 if (*s1 != *s2) {
66 return 0;
67 }
68 }
69
70 return n;
71 }
72
73 /* Strip leading and trailing whitespace and quotes from a string */
74 char *
str_trim(char * str)75 str_trim(char *str)
76 {
77
78 register spif_charptr_t tmp = (spif_charptr_t) str;
79 size_t n;
80
81 if (str && *str) {
82
83 spiftool_chomp(str);
84 n = strlen(str);
85
86 if (!n) {
87 *str = 0;
88 return str;
89 }
90 /* strip leading/trailing quotes */
91 if (*tmp == '"') {
92 tmp++;
93 n--;
94 if (!n) {
95 *str = 0;
96 return str;
97 } else if (tmp[n - 1] == '"') {
98 tmp[--n] = '\0';
99 }
100 }
101 if (tmp != str) {
102 memmove(str, tmp, (strlen(tmp)) + 1);
103 }
104 }
105 return str;
106 }
107
108 /*
109 * in-place interpretation of string:
110 *
111 * backslash-escaped: "\a\b\E\e\n\r\t", "\octal"
112 * Ctrl chars: ^@ .. ^_, ^?
113 *
114 * Emacs-style: "M-" prefix
115 *
116 * Also,
117 * "M-x" prefixed strings, append "\r" if needed
118 * "\E]" prefixed strings (XTerm escape sequence) append "\a" if needed
119 *
120 * returns the converted string length
121 */
122
123 int
parse_escaped_string(char * str)124 parse_escaped_string(char *str)
125 {
126
127 register char *pold = str, *pnew;
128 unsigned char i;
129
130 D_STRINGS(("parse_escaped_string(\"%s\")\n", str));
131
132 if (!BEG_STRCASECMP(pold, "m-")) {
133 *pold = '\\';
134 *(pold + 1) = 'e';
135 }
136 for (pold = pnew = str; *pold; pold++, pnew++) {
137 D_STRINGS(("Looking at \"%s\"\n", pold));
138 if (!BEG_STRCASECMP(pold, "m-") && (isspace(*(pold - 1)) || !isprint(*(pold - 1)))) {
139 *pold = '\\';
140 *(pold + 1) = 'e';
141 } else if (!BEG_STRCASECMP(pold, "c-")) {
142 *(++pold) = '^';
143 }
144 D_STRINGS(("Operating on \'%c\'\n", *pold));
145 switch (*pold) {
146 case '\\':
147 D_STRINGS(("Backslash + %c\n", *(pold + 1)));
148 switch (tolower(*(++pold))) {
149 case '0':
150 case '1':
151 case '2':
152 case '3':
153 case '4':
154 case '5':
155 case '6':
156 case '7':
157 for (i = 0; *pold >= '0' && *pold <= '7'; pold++) {
158 i = (i * 8) + (*pold - '0');
159 }
160 pold--;
161 D_STRINGS(("Octal number evaluates to %d\n", i));
162 *pnew = i;
163 break;
164 case 'n':
165 *pnew = '\n';
166 break;
167 case 'r':
168 *pnew = '\r';
169 break;
170 case 't':
171 *pnew = '\t';
172 break;
173 case 'b':
174 *pnew = '\b';
175 break;
176 case 'f':
177 *pnew = '\f';
178 break;
179 case 'a':
180 *pnew = '\a';
181 break;
182 case 'v':
183 *pnew = '\v';
184 break;
185 case 'e':
186 *pnew = '\033';
187 break;
188 case 'c':
189 pold++;
190 *pnew = MAKE_CTRL_CHAR(*pold);
191 break;
192 default:
193 *pnew = *pold;
194 break;
195 }
196 break;
197 case '^':
198 D_STRINGS(("Caret + %c\n", *(pold + 1)));
199 pold++;
200 *pnew = MAKE_CTRL_CHAR(*pold);
201 break;
202 default:
203 *pnew = *pold;
204 }
205 }
206
207 if (!BEG_STRCASECMP(str, "\033x") && *(pnew - 1) != '\r') {
208 D_STRINGS(("Adding carriage return\n"));
209 *(pnew++) = '\r';
210 } else if (!BEG_STRCASECMP(str, "\033]") && *(pnew - 1) != '\a') {
211 D_STRINGS(("Adding bell character\n"));
212 *(pnew++) = '\a';
213 }
214 *pnew = 0;
215
216 #if DEBUG >= DEBUG_STRINGS
217 if (DEBUG_LEVEL >= DEBUG_STRINGS) {
218 D_STRINGS(("New value is:\n"));
219 spiftool_hex_dump(str, (size_t) (pnew - str));
220 }
221 #endif
222
223 return (pnew - str);
224 }
225
226 spif_charptr_t
escape_string(spif_charptr_t str,spif_char_t quote,spif_int32_t maxlen)227 escape_string(spif_charptr_t str, spif_char_t quote, spif_int32_t maxlen)
228 {
229 spif_charptr_t buff, s = str, pbuff;
230
231 D_STRINGS(("escape_string(%s %c %ld)\n", (char *) str, quote, maxlen));
232 if (!quote) {
233 quote = '\"';
234 }
235
236 /* The escaped string will be at most twice the length of the original. */
237 buff = (spif_charptr_t) MALLOC(strlen((char *) str) * 2 + 1);
238
239 /* Copy and escape the string from str into buff. */
240 for (pbuff = buff; (*s); s++, pbuff++) {
241 if (*s == quote) {
242 D_STRINGS(("Double-escaping \'%c\' at position %d\n", *s, s - str));
243 *pbuff = '\\';
244 pbuff++;
245 *pbuff = '\\';
246 pbuff++;
247 } else {
248 if (quote == '\"') {
249 if ((*s == '\\') || (*s == '`')) {
250 D_STRINGS(("Escaping \'%c\' at position %d\n", *s, s - str));
251 *pbuff = '\\';
252 pbuff++;
253 }
254 }
255 }
256 D_STRINGS(("Copying \'%c\' at position %d\n", *s, s - str));
257 *pbuff = *s;
258 }
259 *pbuff = 0;
260
261 if (maxlen) {
262 /* Given maxlen, we know "str" can hold at least "maxlen" chars. */
263 if (!spiftool_safe_strncpy(str, buff, maxlen)) {
264 str[maxlen] = 0;
265 }
266 FREE(buff);
267 return str;
268 } else {
269 return buff;
270 }
271 }
272
273 char *
safe_print_string(const char * str,unsigned long len)274 safe_print_string(const char *str, unsigned long len)
275 {
276 static char *ret_buff = NULL;
277 static unsigned long rb_size = 0;
278 char *p;
279 unsigned long n = 0, i;
280
281 if (len == ((unsigned long) -1)) {
282 len = strlen(str);
283 } else if (len == ((unsigned long) -2)) {
284 FREE(ret_buff);
285 rb_size = 0;
286 return ((char *) NULL);
287 }
288 if (!ret_buff) {
289 rb_size = len;
290 ret_buff = (char *) MALLOC(rb_size + 1);
291 } else if (len > rb_size) {
292 rb_size = len;
293 ret_buff = (char *) REALLOC(ret_buff, rb_size + 1);
294 }
295 for (i = 0, p = ret_buff; i < len; i++, str++, n++) {
296 if (n + 2 >= rb_size) {
297 rb_size *= 2;
298 ret_buff = (char *) REALLOC(ret_buff, rb_size + 1);
299 p = ret_buff + n;
300 }
301 if ((unsigned char) *str < ' ') {
302 *p++ = '^';
303 *p++ = *str + '@';
304 n++;
305 } else {
306 *p++ = *str;
307 }
308 }
309 *p = 0;
310 return ret_buff;
311 }
312
313 unsigned long
add_carriage_returns(unsigned char * buff,unsigned long cnt)314 add_carriage_returns(unsigned char *buff, unsigned long cnt)
315 {
316 register unsigned char *out, *outp, *in;
317 register unsigned long i;
318
319 D_CMD(("buff == %8p \"%s\", cnt == %lu\n", buff, safe_print_string((spif_charptr_t) buff, cnt), cnt));
320 outp = out = (unsigned char *) MALLOC(cnt * 2);
321 for (i = 0, in = buff; i < cnt; i++) {
322 if (*in == '\n') {
323 *out++ = '\r';
324 }
325 *out++ = *in++;
326 }
327 i = (unsigned long) (out - outp);
328 memcpy(buff, outp, i);
329 FREE(outp);
330 D_CMD(("buff == %8p \"%s\", i == %lu\n", buff, safe_print_string((spif_charptr_t) buff, i), i));
331 return i;
332 }
333
334 unsigned char
mkdirhier(const char * path)335 mkdirhier(const char *path)
336 {
337 spif_charptr_t str, pstr;
338 struct stat dst;
339
340 D_CMD(("path == %s\n", path));
341 str = STRDUP(path); /* We need to modify it. */
342 pstr = str;
343 if (*pstr == '/') {
344 pstr++;
345 }
346 for (; (pstr = strchr(pstr, '/'));) {
347 *pstr = 0;
348 D_CMD(("Looking at \"%s\"\n", str));
349 if (stat(str, &dst)) {
350 /* It's not there. Create it. */
351 D_CMD(("stat() failed. Attempting to create it.\n"));
352 if (mkdir(str, 0755)) {
353 /* Couldn't create it. Return failure. */
354 D_CMD(("mkdir(%s, 0755) failed -- %s\n", str, strerror(errno)));
355 return 0;
356 }
357 } else if (!S_ISDIR(dst.st_mode)) {
358 /* It's there, but it's not a directory. Fail. */
359 D_CMD(("\"%s\" exists, but it's not a directory.\n", str));
360 return 0;
361 }
362 *pstr++ = '/';
363 }
364 D_CMD(("Looking at \"%s\"\n", str));
365 if (stat(str, &dst)) {
366 /* It's not there. Create it. */
367 D_CMD(("stat() failed. Attempting to create it.\n"));
368 if (mkdir(str, 0755)) {
369 /* Couldn't create it. Return failure. */
370 D_CMD(("mkdir(%s, 0755) failed -- %s\n", str, strerror(errno)));
371 return 0;
372 }
373 } else if (!S_ISDIR(dst.st_mode)) {
374 /* It's there, but it's not a directory. Fail. */
375 D_CMD(("\"%s\" exists, but it's not a directory.\n", str));
376 return 0;
377 }
378 D_CMD(("Done!\n"));
379 return 1;
380 }
381