chiark / gitweb /
Stupid bug fix. Code wouldn't even compile. Tut.
[sw-tools] / src / sw_info.c
1 /* -*-c-*-
2  *
3  * $Id: sw_info.c,v 1.4 1999/06/24 15:57:07 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.4  1999/06/24 15:57:07  mdw
33  * Stupid bug fix.  Code wouldn't even compile.  Tut.
34  *
35  * Revision 1.3  1999/06/24 15:51:59  mdw
36  * Add support for the `sw-precommit' script.
37  *
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.
43  *
44  */
45
46 /*----- Header files ------------------------------------------------------*/
47
48 #include "config.h"
49
50 #include <ctype.h>
51 #include <errno.h>
52 #include <signal.h>
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>
60 #include <sys/wait.h>
61 #include <pwd.h>
62 #include <unistd.h>
63
64 #ifndef DECL_ENVIRON
65   extern char **environ;
66 #endif
67
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"
75 #include "sw_env.h"
76 #include "sw_info.h"
77
78 /*----- Static variables --------------------------------------------------*/
79
80 static 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 };
93 typedef 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
110 void 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
126 int 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
208 void 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
230 int 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
276 tidy_2:
277   rename(".sw-info.old", ".sw-info");
278 tidy_1:
279   rename(".sw-info.older", ".sw-info.old");
280 tidy_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
295 void 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
317 void 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
371 int 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
389 int 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
406 int 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) {
431       const char *sep = "";
432       fprintf(stderr, "%s: not built for ", QUIS);
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
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);
481       env_put(&t, "SW_PREFIX", PREFIX);
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
504   /* --- Write to the index file --- */
505
506   {
507     FILE *fp = fopen(PREFIX "/sw-index", "a");
508     swfield *f;
509     const char *sep = "";
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 -------------------------------------------------*/