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