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