source: trunk/src/kmk/kmkbuiltin/append.c@ 3172

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

kmk/win: Make outsource the writing part of kmk_builtin_append to a worker thread to try avoid blocking the main kmk thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: append.c 3172 2018-03-21 14:21:23Z bird $ */
2/** @file
3 * kMk Builtin command - append text to file.
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#ifndef kmk_builtin_append
30# include "makeint.h"
31# include "filedef.h"
32# include "variable.h"
33#else
34# include "config.h"
35#endif
36#include <string.h>
37#include <stdio.h>
38#include <fcntl.h>
39#ifdef HAVE_UNISTD_H
40# include <unistd.h>
41#endif
42#ifdef _MSC_VER
43# include <io.h>
44#endif
45#ifdef HAVE_ALLOCA_H
46# include <alloca.h>
47#endif
48#if !defined(kmk_builtin_append) && defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
49# include "../w32/winchildren.h"
50#endif
51#include "err.h"
52#include "kmkbuiltin.h"
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58#define STR_TUPLE(a_sz) a_sz, (sizeof(a_sz) - 1)
59
60/** No-inherit open flag. */
61#ifdef _O_NOINHERIT
62# define MY_O_NOINHERIT _O_NOINHERIT
63#elif defined(O_NOINHERIT)
64# define MY_O_NOINHERIT O_NOINHERIT
65#elif defined(O_CLOEXEC)
66# define MY_O_NOINHERIT O_CLOEXEC
67#else
68# define MY_O_NOINHERIT 0
69#endif
70
71/** Binary mode open flag. */
72#ifdef _O_BINARY
73# define MY_O_BINARY _O_BINARY
74#elif defined(O_BINARY)
75# define MY_O_BINARY O_BINARY
76#else
77# define MY_O_BINARY 0
78#endif
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84/**
85 * Append output buffer.
86 */
87typedef struct KMKBUILTINAPPENDBUF
88{
89 /** Buffer pointer. */
90 char *pszBuf;
91 /** The buffer allocation size. */
92 size_t cbBuf;
93 /** The current buffer offset. */
94 size_t offBuf;
95 /** Set if we ran out of memory. */
96 int fOutOfMemory;
97} KMKBUILTINAPPENDBUF;
98
99
100/**
101 * Appends a substring to the output buffer.
102 *
103 * @param pBuf The output buffer.
104 * @param pch The substring pointer.
105 * @param cch The substring length.
106 */
107static void write_to_buf(KMKBUILTINAPPENDBUF *pBuf, const char *pch, size_t cch)
108{
109 size_t const offCur = pBuf->offBuf;
110 size_t offNew = offCur + cch;
111
112 if (offNew >= pBuf->cbBuf)
113 {
114 size_t cbNew = offNew + 1 + 256;
115 void *pvNew;
116 cbNew = (cbNew + 511) & ~(size_t)511;
117 pvNew = realloc(pBuf->pszBuf, cbNew);
118 if (pvNew)
119 pBuf->pszBuf = (char *)pvNew;
120 else
121 {
122 free(pBuf->pszBuf);
123 pBuf->pszBuf = NULL;
124 pBuf->cbBuf = 0;
125 pBuf->fOutOfMemory = 1;
126 return;
127 }
128 }
129
130 memcpy(&pBuf->pszBuf[offCur], pch, cch);
131 pBuf->pszBuf[offNew] = '\0';
132 pBuf->offBuf = offNew;
133}
134
135/**
136 * Adds a string to the output buffer.
137 *
138 * @param pBuf The output buffer.
139 * @param psz The string.
140 */
141static void string_to_buf(KMKBUILTINAPPENDBUF *pBuf, const char *psz)
142{
143 write_to_buf(pBuf, psz, strlen(psz));
144}
145
146
147/**
148 * Prints the usage and return 1.
149 */
150static int kmk_builtin_append_usage(const char *arg0, FILE *pf)
151{
152 fprintf(pf,
153 "usage: %s [-dcnNtv] file [string ...]\n"
154 " or: %s --version\n"
155 " or: %s --help\n"
156 "\n"
157 "Options:\n"
158 " -d Enclose the output in define ... endef, taking the name from\n"
159 " the first argument following the file name.\n"
160 " -c Output the command for specified target(s). [builtin only]\n"
161 " -i look for --insert-command=trg and --insert-variable=var. [builtin only]\n"
162 " -n Insert a newline between the strings.\n"
163 " -N Suppress the trailing newline.\n"
164 " -t Truncate the file instead of appending\n"
165 " -v Output the value(s) for specified variable(s). [builtin only]\n"
166 ,
167 arg0, arg0, arg0);
168 return 1;
169}
170
171/**
172 * Appends text to a textfile, creating the textfile if necessary.
173 */
174#ifndef kmk_builtin_append
175int kmk_builtin_append(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
176#else
177int main(int argc, char **argv, char **envp)
178#endif
179{
180#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
181 static const char s_szNewLine[] = "\r\n";
182#else
183 static const char s_szNewLine[] = "\n";
184#endif
185 KMKBUILTINAPPENDBUF OutBuf = { NULL, 0, 0, 0 };
186 const char *pszFilename;
187 int rc = 88;
188 int i;
189 int fFirst;
190 int fNewline = 0;
191 int fNoTrailingNewline = 0;
192 int fTruncate = 0;
193 int fDefine = 0;
194 int fVariables = 0;
195 int fCommands = 0;
196#ifndef kmk_builtin_append
197 int fLookForInserts = 0;
198#endif
199
200 g_progname = argv[0];
201
202 /*
203 * Parse options.
204 */
205 i = 1;
206 while (i < argc
207 && argv[i][0] == '-'
208 && argv[i][1] != '\0' /* '-' is a file */
209 && strchr("-cdinNtv", argv[i][1]) /* valid option char */
210 )
211 {
212 char *psz = &argv[i][1];
213 if (*psz != '-')
214 {
215 do
216 {
217 switch (*psz)
218 {
219 case 'c':
220 if (fVariables)
221 {
222 errx(1, "Option '-c' clashes with '-v'.");
223 return kmk_builtin_append_usage(argv[0], stderr);
224 }
225#ifndef kmk_builtin_append
226 fCommands = 1;
227 break;
228#else
229 errx(1, "Option '-c' isn't supported in external mode.");
230 return kmk_builtin_append_usage(argv[0], stderr);
231#endif
232 case 'd':
233 if (fVariables)
234 {
235 errx(1, "Option '-d' must come before '-v'!");
236 return kmk_builtin_append_usage(argv[0], stderr);
237 }
238 fDefine = 1;
239 break;
240 case 'i':
241 if (fVariables || fCommands)
242 {
243 errx(1, fVariables ? "Option '-i' clashes with '-v'." : "Option '-i' clashes with '-c'.");
244 return kmk_builtin_append_usage(argv[0], stderr);
245 }
246#ifndef kmk_builtin_append
247 fLookForInserts = 1;
248 break;
249#else
250 errx(1, "Option '-C' isn't supported in external mode.");
251 return kmk_builtin_append_usage(argv[0], stderr);
252#endif
253 case 'n':
254 fNewline = 1;
255 break;
256 case 'N':
257 fNoTrailingNewline = 1;
258 break;
259 case 't':
260 fTruncate = 1;
261 break;
262 case 'v':
263 if (fCommands)
264 {
265 errx(1, "Option '-v' clashes with '-c'.");
266 return kmk_builtin_append_usage(argv[0], stderr);
267 }
268#ifndef kmk_builtin_append
269 fVariables = 1;
270 break;
271#else
272 errx(1, "Option '-v' isn't supported in external mode.");
273 return kmk_builtin_append_usage(argv[0], stderr);
274#endif
275 default:
276 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
277 return kmk_builtin_append_usage(argv[0], stderr);
278 }
279 } while (*++psz);
280 }
281 else if (!strcmp(psz, "-help"))
282 {
283 kmk_builtin_append_usage(argv[0], stdout);
284 return 0;
285 }
286 else if (!strcmp(psz, "-version"))
287 return kbuild_version(argv[0]);
288 else
289 break;
290 i++;
291 }
292
293 /*
294 * Take down the filename.
295 */
296 if (i + fDefine < argc)
297 pszFilename = argv[i++];
298 else
299 {
300 if (i <= argc)
301 errx(1, "missing filename!");
302 else
303 errx(1, "missing define name!");
304 return kmk_builtin_append_usage(argv[0], stderr);
305 }
306
307 /* Start of no-return zone! */
308
309 /*
310 * Start define?
311 */
312 if (fDefine)
313 {
314 write_to_buf(&OutBuf, STR_TUPLE("define "));
315 string_to_buf(&OutBuf, argv[i]);
316 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
317 i++;
318 }
319
320 /*
321 * Append the argument strings to the file
322 */
323 fFirst = 1;
324 for (; i < argc; i++)
325 {
326 const char *psz = argv[i];
327 size_t cch = strlen(psz);
328 if (!fFirst)
329 {
330 if (fNewline)
331 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
332 else
333 write_to_buf(&OutBuf, STR_TUPLE(" "));
334 }
335#ifndef kmk_builtin_append
336 if (fCommands)
337 {
338 char *pszOldBuf;
339 unsigned cchOldBuf;
340 char *pchEnd;
341
342 install_variable_buffer(&pszOldBuf, &cchOldBuf);
343
344 pchEnd = func_commands(variable_buffer, &argv[i], "commands");
345 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
346
347 restore_variable_buffer(pszOldBuf, cchOldBuf);
348 }
349 else if (fVariables)
350 {
351 struct variable *pVar = lookup_variable(psz, cch);
352 if (!pVar)
353 continue;
354 if ( !pVar->recursive
355 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
356 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
357 else
358 {
359 char *pszExpanded = allocated_variable_expand(pVar->value);
360 string_to_buf(&OutBuf, pszExpanded);
361 free(pszExpanded);
362 }
363 }
364 else if (fLookForInserts && strncmp(psz, "--insert-command=", 17) == 0)
365 {
366 char *pszOldBuf;
367 unsigned cchOldBuf;
368 char *pchEnd;
369
370 install_variable_buffer(&pszOldBuf, &cchOldBuf);
371
372 psz += 17;
373 pchEnd = func_commands(variable_buffer, (char **)&psz, "commands");
374 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
375
376 restore_variable_buffer(pszOldBuf, cchOldBuf);
377 }
378 else if (fLookForInserts && strncmp(psz, "--insert-variable=", 18) == 0)
379 {
380 struct variable *pVar = lookup_variable(psz + 18, cch);
381 if (!pVar)
382 continue;
383 if ( !pVar->recursive
384 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
385 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
386 else
387 {
388 char *pszExpanded = allocated_variable_expand(pVar->value);
389 string_to_buf(&OutBuf, pszExpanded);
390 free(pszExpanded);
391 }
392 }
393 else
394#endif
395 write_to_buf(&OutBuf, psz, cch);
396 fFirst = 0;
397 }
398
399 /*
400 * End the define?
401 */
402 if (fDefine)
403 {
404 if (fFirst)
405 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
406 write_to_buf(&OutBuf, STR_TUPLE("endef"));
407 }
408
409 /*
410 * Add final newline (unless supressed) and check for errors.
411 */
412 if (!fNoTrailingNewline)
413 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
414
415 /*
416 * Write the buffer (unless we ran out of heap already).
417 */
418#if !defined(kmk_builtin_append) && defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
419 if (!OutBuf.fOutOfMemory)
420 {
421 rc = MkWinChildCreateAppend(pszFilename, &OutBuf.pszBuf, OutBuf.offBuf, fTruncate, pChild, pPidSpawned);
422 if (rc != 0)
423 rc = errx(rc, "MkWinChildCreateAppend failed: %u", rc);
424 if (OutBuf.pszBuf)
425 free(OutBuf.pszBuf);
426 }
427 else
428#endif
429 if (!OutBuf.fOutOfMemory)
430 {
431 int fd = open(pszFilename,
432 fTruncate ? O_WRONLY | O_TRUNC | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY
433 : O_WRONLY | O_APPEND | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY,
434 0666);
435 if (fd >= 0)
436 {
437 ssize_t cbWritten = write(fd, OutBuf.pszBuf, OutBuf.offBuf);
438 if (cbWritten == (ssize_t)OutBuf.offBuf)
439 rc = 0;
440 else
441 rc = err(1, "error writing %lu bytes to '%s'", (unsigned long)OutBuf.offBuf, pszFilename);
442 if (close(fd) < 0)
443 rc = err(1, "error closing '%s'", pszFilename);
444 }
445 else
446 rc = err(1, "failed to open '%s'", pszFilename);
447 free(OutBuf.pszBuf);
448 }
449 else
450 rc = errx(1, "out of memory!");
451 return rc;
452}
453
Note: See TracBrowser for help on using the repository browser.