VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin.c@ 3168

Last change on this file since 3168 was 3168, checked in by bird, 7 years ago

kmkbuiltin.c: Prepping for async builtin command execution in threads on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.8 KB
Line 
1/* $Id: kmkbuiltin.c 3168 2018-03-21 00:04:56Z 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
42extern char **environ;
43#endif
44
45
46int 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
225/**
226 * kmk built command.
227 */
228static struct KMKBUILTINENTRY
229{
230 const char *pszName;
231 size_t cchName;
232 union
233 {
234 uintptr_t uPfn;
235#define FN_SIG_MAIN 0
236 int (* pfnMain)(int argc, char **argv, char **envp);
237#define FN_SIG_MAIN_SPAWNS 1
238 int (* pfnMainSpawns)(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPid);
239#define FN_SIG_MAIN_TO_SPAWN 2
240 int (* pfnMainToSpawn)(int argc, char **argv, char **envp, char ***ppapszArgvToSpawn);
241 } u;
242 size_t uFnSignature : 8;
243 size_t fMpSafe : 1;
244 size_t fNeedEnv : 1;
245} const g_aBuiltins[] =
246{
247#define BUILTIN_ENTRY(a_fn, a_uFnSignature, fMpSafe, fNeedEnv) \
248 { &(#a_fn)[12], sizeof(#a_fn) - 12 - 1, \
249 (uintptr_t)a_fn, a_uFnSignature, fMpSafe, fNeedEnv }
250
251 /* More frequently used commands: */
252 BUILTIN_ENTRY(kmk_builtin_append, FN_SIG_MAIN, 0, 0),
253 BUILTIN_ENTRY(kmk_builtin_printf, FN_SIG_MAIN, 0, 0),
254 BUILTIN_ENTRY(kmk_builtin_echo, FN_SIG_MAIN, 0, 0),
255 BUILTIN_ENTRY(kmk_builtin_install, FN_SIG_MAIN, 0, 0),
256 BUILTIN_ENTRY(kmk_builtin_kDepObj, FN_SIG_MAIN, 0, 0),
257#ifdef KBUILD_OS_WINDOWS
258 BUILTIN_ENTRY(kmk_builtin_kSubmit, FN_SIG_MAIN_SPAWNS, 0, 0),
259#endif
260 BUILTIN_ENTRY(kmk_builtin_mkdir, FN_SIG_MAIN, 0, 0),
261 BUILTIN_ENTRY(kmk_builtin_mv, FN_SIG_MAIN, 0, 0),
262 BUILTIN_ENTRY(kmk_builtin_redirect, FN_SIG_MAIN_SPAWNS, 0, 0),
263 BUILTIN_ENTRY(kmk_builtin_rm, FN_SIG_MAIN, 0, 0),
264 BUILTIN_ENTRY(kmk_builtin_rmdir, FN_SIG_MAIN, 0, 0),
265 BUILTIN_ENTRY(kmk_builtin_test, FN_SIG_MAIN_TO_SPAWN, 0, 0),
266 /* Less frequently used commands: */
267 BUILTIN_ENTRY(kmk_builtin_kDepIDB, FN_SIG_MAIN, 0, 0),
268 BUILTIN_ENTRY(kmk_builtin_chmod, FN_SIG_MAIN, 0, 0),
269 BUILTIN_ENTRY(kmk_builtin_cp, FN_SIG_MAIN, 0, 0),
270 BUILTIN_ENTRY(kmk_builtin_expr, FN_SIG_MAIN, 0, 0),
271 BUILTIN_ENTRY(kmk_builtin_ln, FN_SIG_MAIN, 0, 0),
272 BUILTIN_ENTRY(kmk_builtin_md5sum, FN_SIG_MAIN, 0, 0),
273 BUILTIN_ENTRY(kmk_builtin_cmp, FN_SIG_MAIN, 0, 0),
274 BUILTIN_ENTRY(kmk_builtin_cat, FN_SIG_MAIN, 0, 0),
275 BUILTIN_ENTRY(kmk_builtin_touch, FN_SIG_MAIN, 0, 0),
276 BUILTIN_ENTRY(kmk_builtin_sleep, FN_SIG_MAIN, 0, 0),
277 BUILTIN_ENTRY(kmk_builtin_dircache, FN_SIG_MAIN, 0, 0),
278};
279
280
281int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
282{
283 /*
284 * Check and skip the prefix.
285 */
286 static const char s_szPrefix[] = "kmk_builtin_";
287 const char *pszCmd = argv[0];
288 if (strncmp(pszCmd, s_szPrefix, sizeof(s_szPrefix) - 1) == 0)
289 {
290 struct KMKBUILTINENTRY const *pEntry;
291 size_t cchCmd;
292 char ch0;
293 int cLeft;
294
295 pszCmd += sizeof(s_szPrefix) - 1;
296
297 /*
298 * Look up the builtin command in the table.
299 */
300 cchCmd = strlen(pszCmd);
301 ch0 = *pszCmd;
302 pEntry = &g_aBuiltins[0];
303 cLeft = sizeof(g_aBuiltins) / sizeof(g_aBuiltins[0]);
304 while (cLeft-- > 0)
305 if ( *pEntry->pszName != ch0
306 || pEntry->cchName != cchCmd
307 || memcmp(pEntry->pszName, pszCmd, cchCmd) != 0)
308 pEntry++;
309 else
310 {
311 int rc;
312#if defined(KBUILD_OS_WINDOWS) && CONFIG_NEW_WIN_CHILDREN
313 if (pEntry->fMpSafe)
314 {
315 rc = 98;
316 }
317 else
318#endif
319 {
320 char **envp = environ; /** @todo fixme? */
321
322 /*
323 * Call the worker function, making sure to preserve umask.
324 */
325 int const iUmask = umask(0); /* save umask */
326 umask(iUmask);
327
328 if (pEntry->uFnSignature == FN_SIG_MAIN)
329 rc = pEntry->u.pfnMain(argc, argv, envp);
330 else if (pEntry->uFnSignature == FN_SIG_MAIN_SPAWNS)
331 rc = pEntry->u.pfnMainSpawns(argc, argv, envp, pChild, pPidSpawned);
332 else if (pEntry->uFnSignature == FN_SIG_MAIN_TO_SPAWN)
333 {
334 /*
335 * When we got something to execute, check if the child is a kmk_builtin thing.
336 * We recurse here, both because I'm lazy and because it's easier to debug a
337 * problem then (the call stack shows what's been going on).
338 */
339 rc = pEntry->u.pfnMainToSpawn(argc, argv, envp, ppapszArgvToSpawn);
340 if ( !rc
341 && *ppapszArgvToSpawn
342 && !strncmp(**ppapszArgvToSpawn, s_szPrefix, sizeof(s_szPrefix) - 1))
343 {
344 char **argv_new = *ppapszArgvToSpawn;
345 int argc_new = 1;
346 while (argv_new[argc_new])
347 argc_new++;
348
349 assert(argv_new[0] != argv[0]);
350 assert(!*pPidSpawned);
351
352 *ppapszArgvToSpawn = NULL;
353 rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
354
355 free(argv_new[0]);
356 free(argv_new);
357 }
358 }
359 else
360 rc = 99;
361 g_progname = "kmk"; /* paranoia, make sure it's not pointing at a freed argv[0]. */
362 umask(iUmask); /* restore it */
363 }
364 return rc;
365 }
366 fprintf(stderr, "kmk_builtin: Unknown command '%s%s'!\n", s_szPrefix, pszCmd);
367 }
368 else
369 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
370 return 1;
371}
372
373#ifndef KBUILD_OS_WINDOWS
374/** Dummy. */
375int kmk_builtin_dircache(int argc, char **argv, char **envp)
376{
377 (void)argc; (void)argv; (void)envp;
378 return 0;
379}
380#endif
381
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette