Expunge revision histories in files.
[sw-tools] / src / sw_info.c
CommitLineData
3315e8b3 1/* -*-c-*-
2 *
9796a787 3 * $Id: sw_info.c,v 1.6 2004/04/08 01:52:19 mdw Exp $
3315e8b3 4 *
5 * Maintenance of `.sw-info' files
6 *
7 * (c) 1999 EBI
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of sw-tools.
13 *
14 * sw-tools is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * sw-tools is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with sw-tools; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
3315e8b3 29/*----- Header files ------------------------------------------------------*/
30
31#include "config.h"
32
33#include <ctype.h>
34#include <errno.h>
9662b11b 35#include <signal.h>
3315e8b3 36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41
42#include <sys/types.h>
9662b11b 43#include <sys/wait.h>
3315e8b3 44#include <pwd.h>
45#include <unistd.h>
46
9662b11b 47#ifndef DECL_ENVIRON
48 extern char **environ;
49#endif
50
3315e8b3 51#include <mLib/alloc.h>
52#include <mLib/dstr.h>
53#include <mLib/lock.h>
54#include <mLib/quis.h>
55#include <mLib/report.h>
56
57#include "sw_arch.h"
9662b11b 58#include "sw_env.h"
3315e8b3 59#include "sw_info.h"
60
61/*----- Static variables --------------------------------------------------*/
62
63static struct swfield {
64 char *name;
65 size_t off;
66 char *desc;
67} swfields[] = {
68 { "package", offsetof(swinfo, package), "Package" },
69 { "version", offsetof(swinfo, version), "Version" },
70 { "maintainer", offsetof(swinfo, maintainer), "Maintained by" },
71 { "date", offsetof(swinfo, date), "Last modified" },
72 { "only-arch", offsetof(swinfo, only_arch), "Only build for" },
73 { "arch", offsetof(swinfo, arch), "Successfully built" },
74 { 0, 0 }
75};
76typedef struct swfield swfield;
77
78#define SWINFO(sw, off) (*(char **)((char *)sw + off))
79
80/*----- Main code ---------------------------------------------------------*/
81
82/* --- @swinfo_clear@ --- *
83 *
84 * Arguments: @swinfo *sw@ = pointe to info block
85 *
86 * Returns: ---
87 *
88 * Use: Clears an info block so that it doesn't contain anything.
89 * This is mainly useful when building skeletons to apply using
90 * @swinfo_update@.
91 */
92
93void swinfo_clear(swinfo *sw)
94{
95 swfield *f;
96 for (f = swfields; f->name; f++)
97 SWINFO(sw, f->off) = 0;
98}
99
100/* --- @swinfo_fetch@ --- *
101 *
102 * Arguments: @swinfo *sw@ = pointer to info block to fill in
103 *
104 * Returns: Zero if OK, else nonzero.
105 *
106 * Use: Fills in the info block if it can.
107 */
108
109int swinfo_fetch(swinfo *sw)
110{
111 FILE *fp;
112 dstr d = DSTR_INIT;
113 swfield *f;
114 int line = 0;
115
116 /* --- Initialize the various fields --- */
117
118 swinfo_clear(sw);
119
120 /* --- Open the data file --- */
121
122 if ((fp = fopen(".sw-info", "r")) == 0)
123 return (-1);
124
125 /* --- Read the lines from the file --- */
126
127 for (; dstr_putline(&d, fp) != EOF; DRESET(&d)) {
128 char *p = d.buf;
129 char *key, *value;
130 line++;
131
132 /* --- Find the field name --- */
133
134 while (isspace((unsigned char)*p))
135 p++;
136 if (*p == 0 || *p == '#')
137 continue;
138
139 /* --- Find the end of the field name --- */
140
141 key = p;
142 while (*p && *p != '=' && !isspace((unsigned char)*p))
143 p++;
144
145 /* --- Skip an optional `=' sign --- */
146
147 {
148 char ch = *p;
149 if (ch)
150 *p++ = 0;
151 while (isspace((unsigned char)*p))
152 p++;
153 if (*p == '=' && ch != '=') {
154 p++;
155 while (isspace((unsigned char)*p))
156 p++;
157 }
158 }
159
160 value = p;
161
162 /* --- Look up the key in the table --- */
163
164 for (f = swfields; f->name; f++) {
165 if (strcmp(key, f->name) == 0)
166 goto found;
167 }
168 moan("unknown key `%s' at line %i in `.sw-info'", key, line);
169 continue;
170
171 /* --- Put the value in --- */
172
173 found:
174 SWINFO(sw, f->off) = xstrdup(value);
175 }
176
177 fclose(fp);
178 dstr_destroy(&d);
179 return (0);
180}
181
182/* --- @swinfo_sanity@ --- *
183 *
184 * Arguments: @swinfo *sw@ = pointer to an info block
185 *
186 * Returns: Yes, if the block is OK.
187 *
188 * Use: Objects if the information in the info block is bad.
189 */
190
191void swinfo_sanity(swinfo *sw)
192{
193 if (!sw->package)
194 die(1, "unknown package name. Try `%s setup PACKAGE VERSION'.", QUIS);
195 if (!sw->version)
196 die(1, "unknown package version. Try `%s setup PACKAGE VERSION'.",
197 QUIS);
198 if (!sw->maintainer)
199 die(1, "unknown maintainer. Try `%s setup PACKAGE VERSION'.", QUIS);
200 if (!sw->date)
201 die(1, "unknown date. Try `%s setup PACKAGE VERSION'.", QUIS);
202}
203
204/* --- @swinfo_put@ --- *
205 *
206 * Arguments: @swinfo *sw@ = pointer to an info block
207 *
208 * Returns: Zero if it worked, nonzero if not.
209 *
210 * Use: Writes an info block to the appropriate file.
211 */
212
213int swinfo_put(swinfo *sw)
214{
215 FILE *fp;
216 swfield *f;
217 int e;
218
219 /* --- Quick sanity check --- */
220
221 swinfo_sanity(sw);
222
223 /* --- Write the new data out --- */
224
225 if ((fp = fopen(".sw-info.new", "w")) == 0)
226 return (-1);
227 for (f = swfields; f->name; f++) {
228 if (!SWINFO(sw, f->off))
229 continue;
230 if (fprintf(fp, "%s = %s\n", f->name, SWINFO(sw, f->off)) == EOF) {
231 e = errno;
232 fclose(fp);
233 goto tidy_0;
234 }
235 }
236 if (fclose(fp) == EOF) {
237 e = errno;
238 goto tidy_0;
239 }
240
241 /* --- Carefully! replace the old one --- *
242 *
243 * Keep an old one around just in case, unless the renames fail because
244 * files don't exist.
245 */
246
247 remove(".sw-info.older"); /* Don't care if this fails */
248 if (rename(".sw-info.old", ".sw-info.older") && errno != ENOENT)
249 { e = errno; goto tidy_0; }
250 if (rename(".sw-info", ".sw-info.old") && errno != ENOENT)
251 { e = errno; goto tidy_1; }
252 if (rename(".sw-info.new", ".sw-info"))
253 { e = errno; goto tidy_2; }
254 remove(".sw-info.older"); /* Don't care if this fails */
255 return (0);
256
257 /* --- Tidy up if anything went tits-up --- */
258
259tidy_2:
260 rename(".sw-info.old", ".sw-info");
261tidy_1:
262 rename(".sw-info.older", ".sw-info.old");
263tidy_0:
264 remove(".sw-info.new");
265 errno = e;
266 return (-1);
267}
268
269/* --- @swinfo_destroy@ --- *
270 *
271 * Arguments: @swinfo *sw@ = pointer to info block
272 *
273 * Returns: ---
274 *
275 * Use: Destroys an info block when it's not useful any more.
276 */
277
278void swinfo_destroy(swinfo *sw)
279{
280 swfield *f;
281
282 for (f = swfields; f->name; f++) {
283 if (SWINFO(sw, f->off))
284 free(SWINFO(sw, f->off));
285 }
286}
287
288/* --- @swinfo_update@ --- *
289 *
290 * Arguments: @swinfo *sw@ = pointer to info block
291 * @swinfo *skel@ = pointer to skeleton values
292 *
293 * Returns: ---
294 *
295 * Use: Updates the fields in an information block, except for the
296 * architecture names stuff. (I'll leave that for later,
297 * because it's rather more involved.
298 */
299
300void swinfo_update(swinfo *sw, swinfo *skel)
301{
302 char ubuf[20];
303 char dbuf[20];
304 swfield *f;
305
306 /* --- Set up a default maintainer --- */
307
308 if (!skel->maintainer && !sw->maintainer) {
309 char *u = getenv("USER");
310
311 if (!u)
312 u = getenv("LOGNAME");
313 if (!u) {
314 struct passwd *pw = getpwuid(getuid());
315 if (!pw) {
316 moan("you don't seem to exist");
317 sprintf(ubuf, "uid %i", (int)getuid());
318 u = ubuf;
319 } else
320 u = pw->pw_name;
321 }
322 skel->maintainer = u;
323 }
324
325 /* --- Set up a default date --- */
326
327 {
328 time_t t = time(0);
329 struct tm *tm = localtime(&t);
330 strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", tm);
331 skel->date = dbuf;
332 }
333
334 /* --- Set a default architecture list --- */
335
336 if (!skel->arch && !sw->arch)
337 skel->arch = "";
338
339 /* --- Grind through all the fields --- */
340
341 for (f = swfields; f->name; f++) {
342 if (!SWINFO(skel, f->off) || strcmp(SWINFO(skel, f->off), "-") == 0)
343 continue;
344 if (SWINFO(sw, f->off))
345 free(SWINFO(sw, f->off));
346 SWINFO(sw, f->off) = xstrdup(SWINFO(skel, f->off));
347 }
348}
349
350/*----- Subcommands -------------------------------------------------------*/
351
352/* --- @sw_setup@ --- */
353
354int sw_setup(int argc, char *argv[])
355{
356 swinfo sw, skel;
357 if (argc < 3 || argc > 4)
358 die(1, "Usage: setup PACKAGE VERSION [MAINTAINER]");
359 swinfo_fetch(&sw);
360 swinfo_clear(&skel);
361 skel.package = argv[1];
362 skel.version = argv[2];
363 if (argv[3])
364 skel.maintainer = argv[3];
365 swinfo_update(&sw, &skel);
366 swinfo_put(&sw);
367 return (0);
368}
369
370/* --- @sw_status@ --- */
371
372int sw_status(int argc, char *argv[])
373{
374 swinfo sw;
375 swfield *f;
376 if (swinfo_fetch(&sw)) {
377 die(1, "couldn't read build status: %s (try running setup)",
378 strerror(errno));
379 }
380 for (f = swfields; f->name; f++) {
381 if (SWINFO(&sw, f->off))
382 printf("%s: %s\n", f->desc, SWINFO(&sw, f->off));
383 }
384 return (0);
385}
386
387/* --- @sw_commit@ --- */
388
389int sw_commit(int argc, char *argv[])
390{
391 swinfo sw;
392
393 if (argc != 1)
394 die(1, "Usage: commit");
395 if (swinfo_fetch(&sw)) {
396 die(1, "couldn't read build status: %s (try running setup)",
397 strerror(errno));
398 }
399
400 /* --- Make sure everything has been built properly --- */
401
402 {
403 archcons *a, *aa;
404 archcons *all = arch_readtab();
405
406 if (sw.arch) {
407 for (a = aa = arch_filter(all, sw.arch, 0, 0); a; a = a->cdr)
408 a->car->flags |= archFlag_built;
409 arch_free(aa);
410 }
411
412 aa = arch_filter(all, sw.only_arch, archFlag_built, 0);
413 if (aa) {
e0465a2b 414 const char *sep = "";
415 fprintf(stderr, "%s: not built for ", QUIS);
3315e8b3 416 for (a = aa; a; a = a->cdr) {
417 fprintf(stderr, "%s%s", sep, a->car->arch);
418 sep = ", ";
419 }
420 fputc('\n', stderr);
421 exit(1);
422 }
423 arch_free(aa);
424 }
425
9662b11b 426 /* --- Run the local precommit check script --- */
427
428 {
429 pid_t kid;
430 struct sigaction sa, sa_int, sa_quit;
431 int status;
432
433 /* --- Make sure I can trap the child's death --- */
434
435 sa.sa_handler = SIG_IGN;
436 sigemptyset(&sa.sa_mask);
437 sa.sa_flags = 0;
438 if (sigaction(SIGINT, &sa, &sa_int) || sigaction(SIGQUIT, &sa, &sa_quit))
439 die(1, "couldn't set signal dispositions: %s", strerror(errno));
440
441 /* --- Spawn off a child process --- */
442
443 fflush(0);
444 kid = fork();
445 if (kid < 0)
446 die(1, "error from fork: %s", strerror(errno));
447 if (kid == 0) {
448 sym_table t;
449
450 /* --- Re-enable signals --- */
451
452 sigaction(SIGINT, &sa_int, 0);
453 sigaction(SIGQUIT, &sa_quit, 0);
454
455 /* --- Set up the environment --- */
456
457 sym_create(&t);
458 env_import(&t, environ);
459 env_put(&t, "SW_PACKAGE", sw.package);
460 env_put(&t, "SW_VERSION", sw.version);
461 env_put(&t, "SW_MAINTAINER", sw.maintainer);
462 env_put(&t, "SW_DATE", sw.date);
463 env_put(&t, "SW_ARCHLIST", sw.arch);
4cfc09d8 464 env_put(&t, "SW_PREFIX", PREFIX);
9662b11b 465 environ = env_export(&t);
466
467 /* --- Run the commit check script --- */
468
469 execl(PREFIX "/share/sw-precommit", "sw-precommit",
77bef548 470 sw.package, (char *)0);
9662b11b 471 if (errno == ENOENT)
472 _exit(0);
473 die(1, "couldn't run " PREFIX "/share/sw-precommit: %s",
474 strerror(errno));
475 }
476
477 /* --- Wait for the child to finish --- */
478
479 if (waitpid(kid, &status, 0) < 0)
480 die(1, "error waiting for child: %s", strerror(errno));
481 if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
482 die(1, "sw-precommit failed");
483 sigaction(SIGINT, &sa_int, 0);
484 sigaction(SIGQUIT, &sa_quit, 0);
485 }
486
3315e8b3 487 /* --- Write to the index file --- */
488
489 {
490 FILE *fp = fopen(PREFIX "/sw-index", "a");
491 swfield *f;
e0465a2b 492 const char *sep = "";
3315e8b3 493
494 if (!fp)
495 die(1, "couldn't open index file: %s", strerror(errno));
496 if (lock_file(fileno(fp), LOCK_EXCL))
497 die(1, "couldn't obtain lock on index file: %s", strerror(errno));
498
499 for (f = swfields; f->name; f++) {
500 if (SWINFO(&sw, f->off)) {
501 fprintf(fp, "%s%s = %s", sep, f->name, SWINFO(&sw, f->off));
502 sep = "; ";
503 }
504 }
505 fputc('\n', fp);
506 fclose(fp);
507 }
508
509 return (0);
510}
511
512/*----- That's all, folks -------------------------------------------------*/