VirtualBox

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

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

kmk/win: Run kDepObj on worker thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: kmkbuiltin.c 3169 2018-03-21 11:27:47Z 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#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
39# include "makeint.h"
40# include "job.h"
41# include "w32/winchildren.h"
42#endif
43#include "kmkbuiltin/err.h"
44#include "kmkbuiltin.h"
45
46#ifndef _MSC_VER
47extern char **environ;
48#endif
49
50
51int kmk_builtin_command(const char *pszCmd, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
52{
53 int argc;
54 char **argv;
55 int rc;
56 char *pszzCmd;
57 char *pszDst;
58 int fOldStyle = 0;
59
60 /*
61 * Check and skip the prefix.
62 */
63 if (strncmp(pszCmd, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
64 {
65 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
66 return 1;
67 }
68
69 /*
70 * Parse arguments.
71 */
72 rc = 0;
73 argc = 0;
74 argv = NULL;
75 pszzCmd = pszDst = (char *)strdup(pszCmd);
76 if (!pszDst)
77 {
78 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
79 return 1;
80 }
81 do
82 {
83 const char * const pszSrcStart = pszCmd;
84 char ch;
85 char chQuote;
86
87 /*
88 * Start new argument.
89 */
90 if (!(argc % 16))
91 {
92 void *pv = realloc(argv, sizeof(char *) * (argc + 17));
93 if (!pv)
94 {
95 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
96 rc = 1;
97 break;
98 }
99 argv = (char **)pv;
100 }
101 argv[argc++] = pszDst;
102 argv[argc] = NULL;
103
104 if (!fOldStyle)
105 {
106 /*
107 * Process the next argument, bourne style.
108 */
109 chQuote = 0;
110 ch = *pszCmd++;
111 do
112 {
113 /* Unquoted mode? */
114 if (chQuote == 0)
115 {
116 if (ch != '\'' && ch != '"')
117 {
118 if (!isspace(ch))
119 {
120 if (ch != '\\')
121 *pszDst++ = ch;
122 else
123 {
124 ch = *pszCmd++;
125 if (ch)
126 *pszDst++ = ch;
127 else
128 {
129 fprintf(stderr, "kmk_builtin: Incomplete escape sequence in argument %d: %s\n",
130 argc, pszSrcStart);
131 rc = 1;
132 break;
133 }
134 }
135 }
136 else
137 break;
138 }
139 else
140 chQuote = ch;
141 }
142 /* Quoted mode */
143 else if (ch != chQuote)
144 {
145 if ( ch != '\\'
146 || chQuote == '\'')
147 *pszDst++ = ch;
148 else
149 {
150 ch = *pszCmd++;
151 if (ch)
152 {
153 if ( ch != '\\'
154 && ch != '"'
155 && ch != '`'
156 && ch != '$'
157 && ch != '\n')
158 *pszDst++ = '\\';
159 *pszDst++ = ch;
160 }
161 else
162 {
163 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
164 rc = 1;
165 break;
166 }
167 }
168 }
169 else
170 chQuote = 0;
171 } while ((ch = *pszCmd++) != '\0');
172 }
173 else
174 {
175 /*
176 * Old style in case we ever need it.
177 */
178 ch = *pszCmd++;
179 if (ch != '"' && ch != '\'')
180 {
181 do
182 *pszDst++ = ch;
183 while ((ch = *pszCmd++) != '\0' && !isspace(ch));
184 }
185 else
186 {
187 chQuote = ch;
188 for (;;)
189 {
190 char *pszEnd = strchr(pszCmd, chQuote);
191 if (pszEnd)
192 {
193 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
194 rc = 1;
195 break;
196 }
197 memcpy(pszDst, pszCmd, pszEnd - pszCmd);
198 pszDst += pszEnd - pszCmd;
199 if (pszEnd[1] != chQuote)
200 break;
201 *pszDst++ = chQuote;
202 }
203 }
204 }
205 *pszDst++ = '\0';
206
207 /*
208 * Skip argument separators (IFS=space() for now). Check for EOS.
209 */
210 if (ch != 0)
211 while ((ch = *pszCmd) && isspace(ch))
212 pszCmd++;
213 if (ch == 0)
214 break;
215 } while (rc == 0);
216
217 /*
218 * Execute the command if parsing was successful.
219 */
220 if (rc == 0)
221 rc = kmk_builtin_command_parsed(argc, argv, pChild, ppapszArgvToSpawn, pPidSpawned);
222
223 /* clean up and return. */
224 free(argv);
225 free(pszzCmd);
226 return rc;
227}
228
229
230/**
231 * kmk built command.
232 */
233static const KMKBUILTINENTRY g_aBuiltins[] =
234{
235#define BUILTIN_ENTRY(a_fn, a_sz, a_uFnSignature, fMpSafe, fNeedEnv) \
236 { { { sizeof(a_sz) - 1, a_sz, } }, \
237 (uintptr_t)a_fn, a_uFnSignature, fMpSafe, fNeedEnv }
238
239 /* More frequently used commands: */
240 BUILTIN_ENTRY(kmk_builtin_append, "append", FN_SIG_MAIN, 0, 0),
241 BUILTIN_ENTRY(kmk_builtin_printf, "printf", FN_SIG_MAIN, 0, 0),
242 BUILTIN_ENTRY(kmk_builtin_echo, "echo", FN_SIG_MAIN, 0, 0),
243 BUILTIN_ENTRY(kmk_builtin_install, "install", FN_SIG_MAIN, 0, 0),
244 BUILTIN_ENTRY(kmk_builtin_kDepObj, "kDepObj", FN_SIG_MAIN, 1, 0),
245#ifdef KBUILD_OS_WINDOWS
246 BUILTIN_ENTRY(kmk_builtin_kSubmit, "kSubmit", FN_SIG_MAIN_SPAWNS, 0, 0),
247#endif
248 BUILTIN_ENTRY(kmk_builtin_mkdir, "mkdir", FN_SIG_MAIN, 0, 0),
249 BUILTIN_ENTRY(kmk_builtin_mv, "mv", FN_SIG_MAIN, 0, 0),
250 BUILTIN_ENTRY(kmk_builtin_redirect, "redirect", FN_SIG_MAIN_SPAWNS, 0, 1),
251 BUILTIN_ENTRY(kmk_builtin_rm, "rm", FN_SIG_MAIN, 0, 1),
252 BUILTIN_ENTRY(kmk_builtin_rmdir, "rmdir", FN_SIG_MAIN, 0, 0),
253 BUILTIN_ENTRY(kmk_builtin_test, "test", FN_SIG_MAIN_TO_SPAWN, 0, 0),
254 /* Less frequently used commands: */
255 BUILTIN_ENTRY(kmk_builtin_kDepIDB, "kDepIDB", FN_SIG_MAIN, 0, 0),
256 BUILTIN_ENTRY(kmk_builtin_chmod, "chmod", FN_SIG_MAIN, 0, 0),
257 BUILTIN_ENTRY(kmk_builtin_cp, "cp", FN_SIG_MAIN, 0, 1),
258 BUILTIN_ENTRY(kmk_builtin_expr, "expr", FN_SIG_MAIN, 0, 0),
259 BUILTIN_ENTRY(kmk_builtin_ln, "ln", FN_SIG_MAIN, 0, 0),
260 BUILTIN_ENTRY(kmk_builtin_md5sum, "md5sum", FN_SIG_MAIN, 0, 0),
261 BUILTIN_ENTRY(kmk_builtin_cmp, "cmp", FN_SIG_MAIN, 0, 0),
262 BUILTIN_ENTRY(kmk_builtin_cat, "cat", FN_SIG_MAIN, 0, 0),
263 BUILTIN_ENTRY(kmk_builtin_touch, "touch", FN_SIG_MAIN, 0, 0),
264 BUILTIN_ENTRY(kmk_builtin_sleep, "sleep", FN_SIG_MAIN, 0, 0),
265 BUILTIN_ENTRY(kmk_builtin_dircache, "dircache", FN_SIG_MAIN, 0, 0),
266};
267
268
269int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
270{
271 /*
272 * Check and skip the prefix.
273 */
274 static const char s_szPrefix[] = "kmk_builtin_";
275 const char *pszCmd = argv[0];
276 if (strncmp(pszCmd, s_szPrefix, sizeof(s_szPrefix) - 1) == 0)
277 {
278 struct KMKBUILTINENTRY const *pEntry;
279 size_t cchAndStart;
280 int cLeft;
281
282 pszCmd += sizeof(s_szPrefix) - 1;
283
284 /*
285 * Calc the length and start word to avoid calling memcmp/strcmp on each entry.
286 */
287#if K_ARCH_BITS != 64 && K_ARCH_BITS != 32
288# error "PORT ME!"
289#endif
290 cchAndStart = strlen(pszCmd);
291#if K_ENDIAN == K_ENDIAN_BIG
292 cchAndStart <<= K_ARCH_BITS - 8;
293 switch (cchAndStart)
294 {
295 default: /* fall thru */
296# if K_ARCH_BITS >= 64
297 case 7: cchAndStart |= (size_t)pszCmd[6] << (K_ARCH_BITS - 56); /* fall thru */
298 case 6: cchAndStart |= (size_t)pszCmd[5] << (K_ARCH_BITS - 48); /* fall thru */
299 case 5: cchAndStart |= (size_t)pszCmd[4] << (K_ARCH_BITS - 40); /* fall thru */
300 case 4: cchAndStart |= (size_t)pszCmd[3] << (K_ARCH_BITS - 32); /* fall thru */
301# endif
302 case 3: cchAndStart |= (size_t)pszCmd[2] << (K_ARCH_BITS - 24); /* fall thru */
303 case 2: cchAndStart |= (size_t)pszCmd[1] << (K_ARCH_BITS - 16); /* fall thru */
304 case 1: cchAndStart |= (size_t)pszCmd[0] << (K_ARCH_BITS - 8); /* fall thru */
305 case 0: break;
306 }
307#else
308 switch (cchAndStart)
309 {
310 default: /* fall thru */
311# if K_ARCH_BITS >= 64
312 case 7: cchAndStart |= (size_t)pszCmd[6] << 56; /* fall thru */
313 case 6: cchAndStart |= (size_t)pszCmd[5] << 48; /* fall thru */
314 case 5: cchAndStart |= (size_t)pszCmd[4] << 40; /* fall thru */
315 case 4: cchAndStart |= (size_t)pszCmd[3] << 32; /* fall thru */
316# endif
317 case 3: cchAndStart |= (size_t)pszCmd[2] << 24; /* fall thru */
318 case 2: cchAndStart |= (size_t)pszCmd[1] << 16; /* fall thru */
319 case 1: cchAndStart |= (size_t)pszCmd[0] << 8; /* fall thru */
320 case 0: break;
321 }
322#endif
323
324 /*
325 * Look up the builtin command in the table.
326 */
327 pEntry = &g_aBuiltins[0];
328 cLeft = sizeof(g_aBuiltins) / sizeof(g_aBuiltins[0]);
329 while (cLeft-- > 0)
330 if ( pEntry->uName.cchAndStart != cchAndStart
331 || ( pEntry->uName.s.cch >= sizeof(cchAndStart)
332 && memcmp(pEntry->uName.s.sz, pszCmd, pEntry->uName.s.cch) != 0) )
333 pEntry++;
334 else
335 {
336 /*
337 * That's a match!
338 */
339 int rc;
340#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
341 if (pEntry->fMpSafe)
342 rc = MkWinChildCreateBuiltIn(pEntry, argc, argv, pEntry->fNeedEnv ? pChild->environment : NULL,
343 pChild, pPidSpawned);
344 else
345#endif
346 {
347 char **envp = pChild->environment ? pChild->environment : environ;
348
349 /*
350 * Call the worker function, making sure to preserve umask.
351 */
352 int const iUmask = umask(0); /* save umask */
353 umask(iUmask);
354
355 if (pEntry->uFnSignature == FN_SIG_MAIN)
356 rc = pEntry->u.pfnMain(argc, argv, envp);
357 else if (pEntry->uFnSignature == FN_SIG_MAIN_SPAWNS)
358 rc = pEntry->u.pfnMainSpawns(argc, argv, envp, pChild, pPidSpawned);
359 else if (pEntry->uFnSignature == FN_SIG_MAIN_TO_SPAWN)
360 {
361 /*
362 * When we got something to execute, check if the child is a kmk_builtin thing.
363 * We recurse here, both because I'm lazy and because it's easier to debug a
364 * problem then (the call stack shows what's been going on).
365 */
366 rc = pEntry->u.pfnMainToSpawn(argc, argv, envp, ppapszArgvToSpawn);
367 if ( !rc
368 && *ppapszArgvToSpawn
369 && !strncmp(**ppapszArgvToSpawn, s_szPrefix, sizeof(s_szPrefix) - 1))
370 {
371 char **argv_new = *ppapszArgvToSpawn;
372 int argc_new = 1;
373 while (argv_new[argc_new])
374 argc_new++;
375
376 assert(argv_new[0] != argv[0]);
377 assert(!*pPidSpawned);
378
379 *ppapszArgvToSpawn = NULL;
380 rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
381
382 free(argv_new[0]);
383 free(argv_new);
384 }
385 }
386 else
387 rc = 99;
388 g_progname = "kmk"; /* paranoia, make sure it's not pointing at a freed argv[0]. */
389 umask(iUmask); /* restore it */
390 }
391 return rc;
392 }
393
394 /*
395 * No match! :-(
396 */
397 fprintf(stderr, "kmk_builtin: Unknown command '%s%s'!\n", s_szPrefix, pszCmd);
398 }
399 else
400 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
401 return 1;
402}
403
404#ifndef KBUILD_OS_WINDOWS
405/** Dummy. */
406int kmk_builtin_dircache(int argc, char **argv, char **envp)
407{
408 (void)argc; (void)argv; (void)envp;
409 return 0;
410}
411#endif
412
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