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