chiark / gitweb /
Merge some changes from 1.0.4. Very odd.
[sw-tools] / src / sw_info.c
1 /* -*-c-*-
2  *
3  * $Id: sw_info.c,v 1.6 2004/04/08 01:52:19 mdw Exp $
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 /*----- Header files ------------------------------------------------------*/
30
31 #include "config.h"
32
33 #include <ctype.h>
34 #include <errno.h>
35 #include <signal.h>
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>
43 #include <sys/wait.h>
44 #include <pwd.h>
45 #include <unistd.h>
46
47 #ifndef DECL_ENVIRON
48   extern char **environ;
49 #endif
50
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"
58 #include "sw_env.h"
59 #include "sw_info.h"
60
61 /*----- Static variables --------------------------------------------------*/
62
63 static 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 };
76 typedef 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
93 void 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
109 int 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
191 void 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
213 int 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
259 tidy_2:
260   rename(".sw-info.old", ".sw-info");
261 tidy_1:
262   rename(".sw-info.older", ".sw-info.old");
263 tidy_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
278 void 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
300 void 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
354 int 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
372 int 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
389 int 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) {
414       const char *sep = "";
415       fprintf(stderr, "%s: not built for ", QUIS);
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
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);
464       env_put(&t, "SW_PREFIX", PREFIX);
465       environ = env_export(&t);
466
467       /* --- Run the commit check script --- */
468
469       execl(PREFIX "/share/sw-precommit", "sw-precommit",
470             sw.package, (char *)0);
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
487   /* --- Write to the index file --- */
488
489   {
490     FILE *fp = fopen(PREFIX "/sw-index", "a");
491     swfield *f;
492     const char *sep = "";
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 -------------------------------------------------*/