chiark / gitweb /
82204d390ac0f45bc1d39cb45d057f112f4b43d6
[sw-tools] / src / sw_info.c
1 /* -*-c-*-
2  *
3  * $Id: sw_info.c,v 1.3 1999/06/24 15:51:59 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 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: sw_info.c,v $
32  * Revision 1.3  1999/06/24 15:51:59  mdw
33  * Add support for the `sw-precommit' script.
34  *
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.
40  *
41  */
42
43 /*----- Header files ------------------------------------------------------*/
44
45 #include "config.h"
46
47 #include <ctype.h>
48 #include <errno.h>
49 #include <signal.h>
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>
57 #include <sys/wait.h>
58 #include <pwd.h>
59 #include <unistd.h>
60
61 #ifndef DECL_ENVIRON
62   extern char **environ;
63 #endif
64
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"
72 #include "sw_env.h"
73 #include "sw_info.h"
74
75 /*----- Static variables --------------------------------------------------*/
76
77 static 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 };
90 typedef 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
107 void 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
123 int 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
205 void 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
227 int 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
273 tidy_2:
274   rename(".sw-info.old", ".sw-info");
275 tidy_1:
276   rename(".sw-info.older", ".sw-info.old");
277 tidy_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
292 void 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
314 void 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
368 int 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
386 int 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
403 int 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) {
428       const char *sep = "";
429       fprintf(stderr, "%s: not built for ", QUIS);
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
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
501   /* --- Write to the index file --- */
502
503   {
504     FILE *fp = fopen(PREFIX "/sw-index", "a");
505     swfield *f;
506     const char *sep = "";
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 -------------------------------------------------*/