4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
31 * Parse a command from a string.
34 int cmd_string_getc(const char *, size_t *);
35 void cmd_string_ungetc(size_t *);
36 char *cmd_string_string(const char *, size_t *, char, int);
37 char *cmd_string_variable(const char *, size_t *);
38 char *cmd_string_expand_tilde(const char *, size_t *);
41 cmd_string_getc(const char *s
, size_t *p
)
43 const u_char
*ucs
= s
;
51 cmd_string_ungetc(size_t *p
)
57 * Parse command string. Returns -1 on error. If returning -1, cause is error
58 * string, or NULL for empty command.
61 cmd_string_parse(const char *s
, struct cmd_list
**cmdlist
, char **cause
)
64 int ch
, i
, argc
, rval
;
65 char **argv
, *buf
, *t
;
66 const char *whitespace
, *equals
;
82 ch
= cmd_string_getc(s
, &p
);
85 if ((t
= cmd_string_string(s
, &p
, '\'', 0)) == NULL
)
87 buf
= xrealloc(buf
, 1, len
+ strlen(t
) + 1);
88 strlcpy(buf
+ len
, t
, strlen(t
) + 1);
93 if ((t
= cmd_string_string(s
, &p
, '"', 1)) == NULL
)
95 buf
= xrealloc(buf
, 1, len
+ strlen(t
) + 1);
96 strlcpy(buf
+ len
, t
, strlen(t
) + 1);
101 if ((t
= cmd_string_variable(s
, &p
)) == NULL
)
103 buf
= xrealloc(buf
, 1, len
+ strlen(t
) + 1);
104 strlcpy(buf
+ len
, t
, strlen(t
) + 1);
109 /* Comment: discard rest of line. */
110 while ((ch
= cmd_string_getc(s
, &p
)) != EOF
)
117 buf
= xrealloc(buf
, 1, len
+ 1);
120 argv
= xrealloc(argv
, argc
+ 1, sizeof *argv
);
131 equals
= strchr(argv
[0], '=');
132 whitespace
= argv
[0] + strcspn(argv
[0], " \t");
133 if (equals
== NULL
|| equals
> whitespace
)
135 environ_put(&global_environ
, argv
[0]);
137 memmove(argv
, argv
+ 1, argc
* (sizeof *argv
));
142 *cmdlist
= cmd_list_parse(argc
, argv
, cause
);
143 if (*cmdlist
== NULL
)
150 if ((t
= cmd_string_expand_tilde(s
, &p
)) == NULL
)
152 buf
= xrealloc(buf
, 1, len
+ strlen(t
) + 1);
153 strlcpy(buf
+ len
, t
, strlen(t
) + 1);
160 if (len
>= SIZE_MAX
- 2)
163 buf
= xrealloc(buf
, 1, len
+ 1);
170 xasprintf(cause
, "invalid or unknown command: %s", s
);
177 for (i
= 0; i
< argc
; i
++)
186 cmd_string_string(const char *s
, size_t *p
, char endch
, int esc
)
195 while ((ch
= cmd_string_getc(s
, p
)) != endch
) {
202 switch (ch
= cmd_string_getc(s
, p
)) {
222 if ((t
= cmd_string_variable(s
, p
)) == NULL
)
224 buf
= xrealloc(buf
, 1, len
+ strlen(t
) + 1);
225 strlcpy(buf
+ len
, t
, strlen(t
) + 1);
231 if (len
>= SIZE_MAX
- 2)
233 buf
= xrealloc(buf
, 1, len
+ 1);
237 buf
= xrealloc(buf
, 1, len
+ 1);
248 cmd_string_variable(const char *s
, size_t *p
)
254 #define cmd_string_first(ch) ((ch) == '_' || \
255 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
256 #define cmd_string_other(ch) ((ch) == '_' || \
257 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
258 ((ch) >= '0' && (ch) <= '9'))
264 switch (ch
= cmd_string_getc(s
, p
)) {
270 ch
= cmd_string_getc(s
, p
);
271 if (!cmd_string_first(ch
))
275 if (!cmd_string_first(ch
)) {
276 xasprintf(&t
, "$%c", ch
);
280 buf
= xrealloc(buf
, 1, len
+ 1);
284 ch
= cmd_string_getc(s
, p
);
285 if (ch
== EOF
|| !cmd_string_other(ch
))
288 if (len
>= SIZE_MAX
- 3)
290 buf
= xrealloc(buf
, 1, len
+ 1);
296 if (fch
== '{' && ch
!= '}')
298 if (ch
!= EOF
&& fch
!= '{')
299 cmd_string_ungetc(p
); /* ch */
301 buf
= xrealloc(buf
, 1, len
+ 1);
304 if ((t
= getenv(buf
)) == NULL
) {
306 return (xstrdup(""));
318 cmd_string_expand_tilde(const char *s
, size_t *p
)
321 char *home
, *path
, *username
;
324 if (cmd_string_getc(s
, p
) == '/') {
325 if ((home
= getenv("HOME")) == NULL
|| *home
== '\0') {
326 if ((pw
= getpwuid(getuid())) != NULL
)
330 cmd_string_ungetc(p
);
331 if ((username
= cmd_string_string(s
, p
, '/', 0)) == NULL
)
333 if ((pw
= getpwnam(username
)) != NULL
)
340 xasprintf(&path
, "%s/", home
);