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

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

kmk_append: Debian build fix.

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