1 /* $Id: kmkbuiltin.c 3059 2017-09-21 13:34:15Z bird $ */
2 /** @file
3  * kMk Builtin command execution.
4  */
5 
6 /*
7  * Copyright (c) 2005-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8  *
9  * This file is part of kBuild.
10  *
11  * kBuild is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * kBuild is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with kBuild.  If not, see <http://www.gnu.org/licenses/>
23  *
24  */
25 
26 /*******************************************************************************
27 *   Header Files                                                               *
28 *******************************************************************************/
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <assert.h>
34 #include <sys/stat.h>
35 #ifdef _MSC_VER
36 # include <io.h>
37 #endif
38 #include "kmkbuiltin/err.h"
39 #include "kmkbuiltin.h"
40 
41 #ifndef _MSC_VER
42 extern char **environ;
43 #endif
44 
45 
kmk_builtin_command(const char * pszCmd,struct child * pChild,char *** ppapszArgvToSpawn,pid_t * pPidSpawned)46 int kmk_builtin_command(const char *pszCmd, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
47 {
48     int         argc;
49     char      **argv;
50     int         rc;
51     char       *pszzCmd;
52     char       *pszDst;
53     int         fOldStyle = 0;
54 
55     /*
56      * Check and skip the prefix.
57      */
58     if (strncmp(pszCmd, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
59     {
60         fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
61         return 1;
62     }
63 
64     /*
65      * Parse arguments.
66      */
67     rc      = 0;
68     argc    = 0;
69     argv    = NULL;
70     pszzCmd = pszDst = (char *)strdup(pszCmd);
71     if (!pszDst)
72     {
73         fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
74         return 1;
75     }
76     do
77     {
78         const char * const pszSrcStart = pszCmd;
79         char ch;
80         char chQuote;
81 
82         /*
83          * Start new argument.
84          */
85         if (!(argc % 16))
86         {
87             void *pv = realloc(argv, sizeof(char *) * (argc + 17));
88             if (!pv)
89             {
90                 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
91                 rc = 1;
92                 break;
93             }
94             argv = (char **)pv;
95         }
96         argv[argc++] = pszDst;
97         argv[argc]   = NULL;
98 
99         if (!fOldStyle)
100         {
101             /*
102              * Process the next argument, bourne style.
103              */
104             chQuote = 0;
105             ch = *pszCmd++;
106             do
107             {
108                 /* Unquoted mode? */
109                 if (chQuote == 0)
110                 {
111                     if (ch != '\'' && ch != '"')
112                     {
113                         if (!isspace(ch))
114                         {
115                             if (ch != '\\')
116                                 *pszDst++ = ch;
117                             else
118                             {
119                                 ch = *pszCmd++;
120                                 if (ch)
121                                     *pszDst++ = ch;
122                                 else
123                                 {
124                                     fprintf(stderr, "kmk_builtin: Incomplete escape sequence in argument %d: %s\n",
125                                             argc, pszSrcStart);
126                                     rc = 1;
127                                     break;
128                                 }
129                             }
130                         }
131                         else
132                             break;
133                     }
134                     else
135                         chQuote = ch;
136                 }
137                 /* Quoted mode */
138                 else if (ch != chQuote)
139                 {
140                     if (   ch != '\\'
141                         || chQuote == '\'')
142                         *pszDst++ = ch;
143                     else
144                     {
145                         ch = *pszCmd++;
146                         if (ch)
147                         {
148                             if (   ch != '\\'
149                                 && ch != '"'
150                                 && ch != '`'
151                                 && ch != '$'
152                                 && ch != '\n')
153                                 *pszDst++ = '\\';
154                             *pszDst++ = ch;
155                         }
156                         else
157                         {
158                             fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
159                             rc = 1;
160                             break;
161                         }
162                     }
163                 }
164                 else
165                     chQuote = 0;
166             } while ((ch = *pszCmd++) != '\0');
167         }
168         else
169         {
170             /*
171              * Old style in case we ever need it.
172              */
173             ch = *pszCmd++;
174             if (ch != '"' && ch != '\'')
175             {
176                 do
177                     *pszDst++ = ch;
178                 while ((ch = *pszCmd++) != '\0' && !isspace(ch));
179             }
180             else
181             {
182                 chQuote = ch;
183                 for (;;)
184                 {
185                     char *pszEnd = strchr(pszCmd, chQuote);
186                     if (pszEnd)
187                     {
188                         fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
189                         rc = 1;
190                         break;
191                     }
192                     memcpy(pszDst, pszCmd, pszEnd - pszCmd);
193                     pszDst += pszEnd - pszCmd;
194                     if (pszEnd[1] != chQuote)
195                         break;
196                     *pszDst++ = chQuote;
197                 }
198             }
199         }
200         *pszDst++ = '\0';
201 
202         /*
203          * Skip argument separators (IFS=space() for now).  Check for EOS.
204          */
205         if (ch != 0)
206             while ((ch = *pszCmd) && isspace(ch))
207                 pszCmd++;
208         if (ch == 0)
209             break;
210     } while (rc == 0);
211 
212     /*
213      * Execute the command if parsing was successful.
214      */
215     if (rc == 0)
216         rc = kmk_builtin_command_parsed(argc, argv, pChild, ppapszArgvToSpawn, pPidSpawned);
217 
218     /* clean up and return. */
219     free(argv);
220     free(pszzCmd);
221     return rc;
222 }
223 
224 
kmk_builtin_command_parsed(int argc,char ** argv,struct child * pChild,char *** ppapszArgvToSpawn,pid_t * pPidSpawned)225 int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
226 {
227     const char *pszCmd = argv[0];
228     int         iUmask;
229     int         rc;
230 
231     /*
232      * Check and skip the prefix.
233      */
234     if (strncmp(pszCmd, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
235     {
236         fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
237         return 1;
238     }
239     pszCmd += sizeof("kmk_builtin_") - 1;
240 
241     /*
242      * String switch on the command (frequent stuff at the top).
243      */
244     iUmask = umask(0);
245     umask(iUmask);
246     if (!strcmp(pszCmd, "append"))
247         rc = kmk_builtin_append(argc, argv, environ);
248     else if (!strcmp(pszCmd, "printf"))
249         rc = kmk_builtin_printf(argc, argv, environ);
250     else if (!strcmp(pszCmd, "echo"))
251         rc = kmk_builtin_echo(argc, argv, environ);
252     else if (!strcmp(pszCmd, "install"))
253         rc = kmk_builtin_install(argc, argv, environ);
254     else if (!strcmp(pszCmd, "kDepIDB"))
255         rc = kmk_builtin_kDepIDB(argc, argv, environ);
256 #ifdef KBUILD_OS_WINDOWS
257     else if (!strcmp(pszCmd, "kSubmit"))
258         rc = kmk_builtin_kSubmit(argc, argv, environ, pChild, pPidSpawned);
259 #endif
260     else if (!strcmp(pszCmd, "mkdir"))
261         rc = kmk_builtin_mkdir(argc, argv, environ);
262     else if (!strcmp(pszCmd, "mv"))
263         rc = kmk_builtin_mv(argc, argv, environ);
264     else if (!strcmp(pszCmd, "redirect"))
265         rc = kmk_builtin_redirect(argc, argv, environ, pChild, pPidSpawned);
266     else if (!strcmp(pszCmd, "rm"))
267         rc = kmk_builtin_rm(argc, argv, environ);
268     else if (!strcmp(pszCmd, "rmdir"))
269         rc = kmk_builtin_rmdir(argc, argv, environ);
270     else if (!strcmp(pszCmd, "test"))
271         rc = kmk_builtin_test(argc, argv, environ, ppapszArgvToSpawn);
272     /* rarely used commands: */
273     else if (!strcmp(pszCmd, "kDepObj"))
274         rc = kmk_builtin_kDepObj(argc, argv, environ);
275     else if (!strcmp(pszCmd, "chmod"))
276         rc = kmk_builtin_chmod(argc, argv, environ);
277     else if (!strcmp(pszCmd, "cp"))
278         rc = kmk_builtin_cp(argc, argv, environ);
279     else if (!strcmp(pszCmd, "expr"))
280         rc = kmk_builtin_expr(argc, argv, environ);
281     else if (!strcmp(pszCmd, "ln"))
282         rc = kmk_builtin_ln(argc, argv, environ);
283     else if (!strcmp(pszCmd, "md5sum"))
284         rc = kmk_builtin_md5sum(argc, argv, environ);
285     else if (!strcmp(pszCmd, "cmp"))
286         rc = kmk_builtin_cmp(argc, argv, environ);
287     else if (!strcmp(pszCmd, "cat"))
288         rc = kmk_builtin_cat(argc, argv, environ);
289     else if (!strcmp(pszCmd, "touch"))
290         rc = kmk_builtin_touch(argc, argv, environ);
291     else if (!strcmp(pszCmd, "sleep"))
292         rc = kmk_builtin_sleep(argc, argv, environ);
293     else if (!strcmp(pszCmd, "dircache"))
294 #ifdef KBUILD_OS_WINDOWS
295         rc = kmk_builtin_dircache(argc, argv, environ);
296 #else
297         rc = 0;
298 #endif
299     else
300     {
301         fprintf(stderr, "kmk_builtin: Unknown command '%s'!\n", pszCmd);
302         return 1;
303     }
304 
305     /*
306      * Cleanup.
307      */
308     g_progname = "kmk";                 /* paranoia, make sure it's not pointing at a freed argv[0]. */
309     umask(iUmask);
310 
311 
312     /*
313      * If we've executed a conditional test or something that wishes to execute
314      * some child process, check if the child is a kmk_builtin thing. We recurse
315      * here, both because I'm lazy and because it's easier to debug a problem then
316      * (the call stack shows what's been going on).
317      */
318     if (    !rc
319         &&  *ppapszArgvToSpawn
320         &&  !strncmp(**ppapszArgvToSpawn, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
321     {
322         char **argv_new = *ppapszArgvToSpawn;
323         int argc_new = 1;
324         while (argv_new[argc_new])
325           argc_new++;
326 
327         assert(argv_new[0] != argv[0]);
328         assert(!*pPidSpawned);
329 
330         *ppapszArgvToSpawn = NULL;
331         rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
332 
333         free(argv_new[0]);
334         free(argv_new);
335     }
336 
337     return rc;
338 }
339 
340