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

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

kmk/win: Some fixes & docs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.1 KB
Line 
1/* $Id: append.c 3159 2018-03-19 13:37:13Z 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#ifdef HAVE_ALLOCA_H
39# include <alloca.h>
40#endif
41#include "err.h"
42#include "kmkbuiltin.h"
43
44
45/**
46 * Prints the usage and return 1.
47 */
48static int usage(FILE *pf)
49{
50 fprintf(pf,
51 "usage: %s [-dcnNtv] file [string ...]\n"
52 " or: %s --version\n"
53 " or: %s --help\n"
54 "\n"
55 "Options:\n"
56 " -d Enclose the output in define ... endef, taking the name from\n"
57 " the first argument following the file name.\n"
58 " -c Output the command for specified target(s). [builtin only]\n"
59 " -i look for --insert-command=trg and --insert-variable=var. [builtin only]\n"
60 " -n Insert a newline between the strings.\n"
61 " -N Suppress the trailing newline.\n"
62 " -t Truncate the file instead of appending\n"
63 " -v Output the value(s) for specified variable(s). [builtin only]\n"
64 ,
65 g_progname, g_progname, g_progname);
66 return 1;
67}
68
69
70/**
71 * Appends text to a textfile, creating the textfile if necessary.
72 */
73int kmk_builtin_append(int argc, char **argv, char **envp)
74{
75 int i;
76 int fFirst;
77 int iFile;
78 FILE *pFile;
79 int fNewline = 0;
80 int fNoTrailingNewline = 0;
81 int fTruncate = 0;
82 int fDefine = 0;
83 int fVariables = 0;
84 int fCommands = 0;
85#ifndef kmk_builtin_append
86 int fLookForInserts = 0;
87#endif
88
89 g_progname = argv[0];
90
91 /*
92 * Parse options.
93 */
94 i = 1;
95 while (i < argc
96 && argv[i][0] == '-'
97 && argv[i][1] != '\0' /* '-' is a file */
98 && strchr("-cdinNtv", argv[i][1]) /* valid option char */
99 )
100 {
101 char *psz = &argv[i][1];
102 if (*psz != '-')
103 {
104 do
105 {
106 switch (*psz)
107 {
108 case 'c':
109 if (fVariables)
110 {
111 errx(1, "Option '-c' clashes with '-v'.");
112 return usage(stderr);
113 }
114#ifndef kmk_builtin_append
115 fCommands = 1;
116 break;
117#else
118 errx(1, "Option '-c' isn't supported in external mode.");
119 return usage(stderr);
120#endif
121 case 'd':
122 if (fVariables)
123 {
124 errx(1, "Option '-d' must come before '-v'!");
125 return usage(stderr);
126 }
127 fDefine = 1;
128 break;
129 case 'i':
130 if (fVariables || fCommands)
131 {
132 errx(1, fVariables ? "Option '-i' clashes with '-v'." : "Option '-i' clashes with '-c'.");
133 return usage(stderr);
134 }
135#ifndef kmk_builtin_append
136 fLookForInserts = 1;
137 break;
138#else
139 errx(1, "Option '-C' isn't supported in external mode.");
140 return usage(stderr);
141#endif
142 case 'n':
143 fNewline = 1;
144 break;
145 case 'N':
146 fNoTrailingNewline = 1;
147 break;
148 case 't':
149 fTruncate = 1;
150 break;
151 case 'v':
152 if (fCommands)
153 {
154 errx(1, "Option '-v' clashes with '-c'.");
155 return usage(stderr);
156 }
157#ifndef kmk_builtin_append
158 fVariables = 1;
159 break;
160#else
161 errx(1, "Option '-v' isn't supported in external mode.");
162 return usage(stderr);
163#endif
164 default:
165 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
166 return usage(stderr);
167 }
168 } while (*++psz);
169 }
170 else if (!strcmp(psz, "-help"))
171 {
172 usage(stdout);
173 return 0;
174 }
175 else if (!strcmp(psz, "-version"))
176 return kbuild_version(argv[0]);
177 else
178 break;
179 i++;
180 }
181
182 if (i + fDefine >= argc)
183 {
184 if (i <= argc)
185 errx(1, "missing filename!");
186 else
187 errx(1, "missing define name!");
188 return usage(stderr);
189 }
190
191 /*
192 * Open the output file, preferrably with close-on-exec.
193 */
194 iFile = i;
195 pFile = fopen(argv[i],
196 fTruncate ? "w" KMK_FOPEN_NO_INHERIT_MODE
197 : "a" KMK_FOPEN_NO_INHERIT_MODE);
198 if (!pFile)
199 return err(1, "failed to open '%s'", argv[i]);
200
201 /*
202 * Start define?
203 */
204 if (fDefine)
205 {
206 i++;
207 fprintf(pFile, "define %s\n", argv[i]);
208 }
209
210 /*
211 * Append the argument strings to the file
212 */
213 fFirst = 1;
214 for (i++; i < argc; i++)
215 {
216 const char *psz = argv[i];
217 size_t cch = strlen(psz);
218 if (!fFirst)
219 fputc(fNewline ? '\n' : ' ', pFile);
220#ifndef kmk_builtin_append
221 if (fCommands)
222 {
223 char *pszOldBuf;
224 unsigned cchOldBuf;
225 char *pchEnd;
226
227 install_variable_buffer(&pszOldBuf, &cchOldBuf);
228
229 pchEnd = func_commands(variable_buffer, &argv[i], "commands");
230 fwrite(variable_buffer, 1, pchEnd - variable_buffer, pFile);
231
232 restore_variable_buffer(pszOldBuf, cchOldBuf);
233 }
234 else if (fVariables)
235 {
236 struct variable *pVar = lookup_variable(psz, cch);
237 if (!pVar)
238 continue;
239 if ( !pVar->recursive
240 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
241 fwrite(pVar->value, 1, pVar->value_length, pFile);
242 else
243 {
244 char *pszExpanded = allocated_variable_expand(pVar->value);
245 fwrite(pszExpanded, 1, strlen(pszExpanded), pFile);
246 free(pszExpanded);
247 }
248 }
249 else if (fLookForInserts && strncmp(psz, "--insert-command=", 17) == 0)
250 {
251 char *pszOldBuf;
252 unsigned cchOldBuf;
253 char *pchEnd;
254
255 install_variable_buffer(&pszOldBuf, &cchOldBuf);
256
257 psz += 17;
258 pchEnd = func_commands(variable_buffer, (char **)&psz, "commands");
259 fwrite(variable_buffer, 1, pchEnd - variable_buffer, pFile);
260
261 restore_variable_buffer(pszOldBuf, cchOldBuf);
262 }
263 else if (fLookForInserts && strncmp(psz, "--insert-variable=", 18) == 0)
264 {
265 struct variable *pVar = lookup_variable(psz + 18, cch);
266 if (!pVar)
267 continue;
268 if ( !pVar->recursive
269 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
270 fwrite(pVar->value, 1, pVar->value_length, pFile);
271 else
272 {
273 char *pszExpanded = allocated_variable_expand(pVar->value);
274 fwrite(pszExpanded, 1, strlen(pszExpanded), pFile);
275 free(pszExpanded);
276 }
277 }
278 else
279#endif
280 fwrite(psz, 1, cch, pFile);
281 fFirst = 0;
282 }
283
284 /*
285 * End the define?
286 */
287 if (fDefine)
288 {
289 if (fFirst)
290 fwrite("\nendef", 1, sizeof("\nendef") - 1, pFile);
291 else
292 fwrite("endef", 1, sizeof("endef") - 1, pFile);
293 }
294
295 /*
296 * Add the final newline (unless supressed) and close the file.
297 */
298 if ( ( !fNoTrailingNewline
299 && fputc('\n', pFile) == EOF)
300 || ferror(pFile))
301 {
302 fclose(pFile);
303 return errx(1, "error writing to '%s'!", argv[iFile]);
304 }
305 if (fclose(pFile))
306 return err(1, "failed to fclose '%s'!", argv[iFile]);
307 return 0;
308}
309
Note: See TracBrowser for help on using the repository browser.