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