chiark / gitweb /
Merge some changes from 1.0.4. Very odd.
[sw-tools] / src / sw_arch.c
1 /* -*-c-*-
2  *
3  * $Id: sw_arch.c,v 1.2 2004/04/08 01:52:19 mdw Exp $
4  *
5  * Messing with architectures
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 <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <mLib/alloc.h>
40 #include <mLib/dstr.h>
41 #include <mLib/quis.h>
42 #include <mLib/report.h>
43 #include <mLib/sub.h>
44
45 #include "sw_arch.h"
46 #include "sw_info.h"
47
48 /*----- Static variables --------------------------------------------------*/
49
50 static archcons *tab = 0;
51
52 /*----- Main code ---------------------------------------------------------*/
53
54 /* --- @arch_readtab@ --- *
55  *
56  * Arguments:   ---
57  *
58  * Returns:     The address of the archtab list.
59  *
60  * Use:         Reads the archtab file (if necessary) and returns a list of
61  *              its contents.
62  */
63
64 archcons *arch_readtab(void)
65 {
66   archcons **p;
67   FILE *fp;
68   dstr d = DSTR_INIT;
69
70   /* --- Return the list if I've already got it --- */
71
72   if (tab)
73     return (tab);
74
75   /* --- Open the file --- */
76
77   if ((fp = fopen(ARCHTAB, "r")) == 0)
78     die(1, "couldn't open `%s': %s", ARCHTAB, strerror(errno));
79
80   /* --- Read it in line-by-line --- */
81
82   p = &tab;
83   for (; dstr_putline(&d, fp) != EOF; DRESET(&d)) {
84     archent *a;
85     char *q = d.buf;
86     char *arch, *host;
87
88     /* --- Pick out the architecture name --- */
89
90     while (isspace((unsigned char)*q))
91       q++;
92     if (*q == '#' || *q == 0)
93       continue;
94     arch = q;
95     while (*q && !isspace((unsigned char)*q))
96       q++;
97     if (*q)
98       *q++ = 0;
99
100     /* --- Pick out the hostname --- */
101
102     while (isspace((unsigned char)*q))
103       q++;
104     if (!q)
105       continue;
106     host = q;
107     while (*q && !isspace((unsigned char)*q))
108       q++;
109     if (*q)
110       *q++ = 0;
111
112     /* --- Allocate a new link --- */
113
114     a = xmalloc(sizeof(*a));
115     a->arch = xstrdup(arch);
116     a->host = xstrdup(host);
117     a->cons.car = a;
118     a->r = 0;
119     a->pres = 0;
120     a->flags = 0;
121     if (strcmp(arch, ARCH) == 0)
122       a->flags |= archFlag_home;
123     *p = &a->cons;
124     p = &a->cons.cdr;
125   }
126
127   /* --- Done --- */
128
129   dstr_destroy(&d);
130   fclose(fp);
131   *p = 0;
132   return (tab);
133 }
134
135 /* --- @arch_lookup@ --- *
136  *
137  * Arguments:   @const char *arch@ = pointer to archtecture name
138  *              @int abbrev@ = whether abbreviations are OK
139  *
140  * Returns:     Pointer to archtab block, or null.
141  *
142  * Use:         Translates an architecture name into the name of a host
143  *              supporting that architecture.
144  */
145
146 archent *arch_lookup(const char *arch, int abbrev)
147 {
148   size_t sz = strlen(arch);
149   archcons *a;
150   archent *chosen = 0;
151
152   for (a = arch_readtab(); a; a = a->cdr) {
153     archent *e = a->car;
154     if (strncmp(arch, e->arch, sz) == 0) {
155       if (e->arch[sz] == 0)
156         return (e);
157       else if (!abbrev)
158         continue;
159       else if (chosen)
160         return (0);
161       else
162         chosen = e;
163     }
164   }
165   return (chosen);
166 }
167
168 /* --- @arch_filter@ --- *
169  *
170  * Arguments:   @archcons *a@ = input list to filter
171  *              @const char *p@ = pointer to a textual list of architectures
172  *              @unsigned and@, @unsigned xor@ = flags to look for
173  *
174  * Returns:     A newly constructed architecture list containing only the
175  *              listed architectures.
176  *
177  * Use:         Filters the architecture list down to a few interesting
178  *              architectures.
179  *
180  *              If @p@ is non-null, it is a textual list of architecture
181  *              names (possibly abbreviated), and separated by whitespace
182  *              and/or commas: only the named architectures are included in
183  *              the resulting list.  If @p@ is null, all architectures are
184  *              considered.
185  *
186  *              The list is further trimmed down by examining the flags words
187  *              in each entry.  Only entries with flags @f@ where @(f ^ xor)
188  *              & and@ is zero are left in the resulting list.  (To include
189  *              all entries, clear @and@ to zero.  To require a flag to be
190  *              clear, set the corresponding bit in @and@.  To require a flag
191  *              to be set, set the corresponding bit in both @and@ and @xor@.
192  *
193  *              (Don't try to filter on the @archFlag_touched@ flag.  That
194  *              flag is for the internal use of this routine.)
195  */
196
197 archcons *arch_filter(archcons *a, const char *p,
198                       unsigned and, unsigned xor)
199 {
200   archcons *h;
201
202   /* --- How this works --- *
203    *
204    * If I have a list of architecture names in @p@, then I scan the list and
205    * set the `touch' bit on matching entries, and clear it on nonmatching
206    * ones.  Then I set the `touch' bit in the @and@ and @xor@ arguments.
207    * Otherwise I skip all of that, and clear the `touch' bit in @and@.  After
208    * all that, I just scoop up the architectures with appropriate flags.
209    */
210
211   and &= ~archFlag_touched;
212   if (p) {
213
214     /* --- Clear all the touch flags --- */
215
216     {
217       archcons *aa;
218       for (aa = a; aa; aa = aa->cdr)
219         aa->car->flags &= ~archFlag_touched;
220     }
221
222     /* --- Set flags for entries in my list --- */
223
224     {
225       char *q = xstrdup(p);
226       for (p = strtok(q, ", \t"); p; p = strtok(0, ", \t")) {
227         archent *e = arch_lookup(p, 1);
228         if (!e)
229           die(1, "architecture `%s' not found", p);
230         e->flags |= archFlag_touched;
231       }
232       free(q);
233     }
234
235     /* --- Only include touched entries --- */
236
237     and |= archFlag_touched;
238     xor |= archFlag_touched;
239   }
240
241   /* --- Now trundle through the list accumulating matching entries --- */
242
243   {
244     archcons **p = &h;
245
246     for (; a; a = a->cdr) {
247       if (((a->car->flags ^ xor) & and) == 0) {
248         archcons *aa = CREATE(archcons);
249         aa->car = a->car;
250         *p = aa;
251         p = &aa->cdr;
252       }
253     }
254     *p = 0;
255   }
256
257   /* --- Done --- */
258
259   return (h);
260 }
261
262 /* --- @arch_free@ --- *
263  *
264  * Arguments:   @archcons *a@ = pointer to a list
265  *
266  * Returns:     ---
267  *
268  * Use:         Contrary to anything you might have expected from the Lispy
269  *              naming, old architecture lists don't get garbage collected.
270  *              This routine throws away an old list when you don't want it
271  *              any more.  Don't call this on the main list!  It will fail
272  *              miserably, because the cons cells in the main list are
273  *              faked.
274  */
275
276 void arch_free(archcons *a)
277 {
278   while (a) {
279     archcons *p = a->cdr;
280     DESTROY(a);
281     a = p;
282   }
283 }
284
285 /* --- @arch_toText@ --- *
286  *
287  * Arguments:   @dstr *d@ = pointer to dynamic string to build result in
288  *              @archcons *a@ = list to write into the string
289  *              @unsigned and@, @unsigned xor@ = flags to look for
290  *
291  * Returns:     ---
292  *
293  * Use:         Writes a textual list of architectures to a string.  This can
294  *              then be used (for example) as the `only-arch' list in the
295  *              info file, by filling it into a skeleton and calling
296  *              @swinfo_update@.  The @and@ and @xor@ arguments work in the
297  *              same way as with @arch_filter@.
298  */
299
300 void arch_toText(dstr *d, archcons *a, unsigned and, unsigned xor)
301 {
302   int spc = 0;
303
304   for (; a; a = a->cdr) {
305     archent *e = a->car;
306     if (((e->flags ^ xor) & and) == 0) {
307       if (spc)
308         dstr_putc(d, ' ');
309       dstr_puts(d, e->arch);
310       spc = 1;
311     }
312   }
313 }
314
315 /*----- Subcommands -------------------------------------------------------*/
316
317 /* --- @sw_arch@ --- */
318
319 int sw_arch(int argc, char *argv[])
320 {
321   if (argc != 1)
322     die(1, "Usage: arch");
323   puts(ARCH);
324   return (0);
325 }
326
327 /* --- @sw_host@ --- */
328
329 int sw_host(int argc, char *argv[])
330 {
331   archent *a;
332   if (argc != 2)
333     die(1, "Usage: host ARCH");
334   if ((a = arch_lookup(argv[1], 1)) == 0)
335     die(1, "no host for architecture `%s'", argv[1]);
336   puts(a->host);
337   return (0);
338 }
339
340 /* --- @sw_listarch@ --- */
341
342 int sw_listarch(int argc, char *argv[])
343 {
344   archcons *a;
345   if (argc != 1)
346     die(1, "Usage: listarch");
347   for (a = arch_readtab(); a; a = a->cdr)
348     puts(a->car->arch);
349   return (0);
350 }
351
352 /* --- @sw_all@ --- */
353
354 int sw_all(int argc, char *argv[])
355 {
356   swinfo sw;
357   if (argc != 1)
358     die(1, "Usage: all");
359   if (swinfo_fetch(&sw)) {
360     die(1, "couldn't read build status: %s (try running setup)",
361         strerror(errno));
362   }
363   if (sw.only_arch) {
364     free(sw.only_arch);
365     sw.only_arch = 0;
366     swinfo_put(&sw);
367   }
368   return (0);
369 }  
370
371 /* --- @sw_only@ --- */
372
373 int sw_only(int argc, char *argv[])
374 {
375   dstr d = DSTR_INIT;
376
377   /* --- Validate the arguments --- */
378
379   if (argc < 2)
380     die(1, "Usage: only-arch ARCH,...");
381
382   /* --- Gather the arguments into one buffer --- */
383
384   {
385     int i;
386     for (i = 1; i < argc; i++) {
387       dstr_putc(&d, ' ');
388       dstr_puts(&d, argv[i]);
389     }
390   }
391
392   /* --- Convert into a canonical list --- */
393
394   {
395     archcons *a = arch_filter(arch_readtab(), d.buf, 0, 0);
396     DRESET(&d);
397     arch_toText(&d, a, 0, 0);
398     arch_free(a);
399   }
400
401   /* --- Put the list into the information table --- */
402
403   {
404     swinfo sw, skel;
405
406     if (swinfo_fetch(&sw)) {
407       die(1, "couldn't read build status: %s (try running setup)",
408           strerror(errno));
409     }
410
411     swinfo_clear(&skel);
412     skel.only_arch = d.buf;
413     swinfo_update(&sw, &skel);
414
415     swinfo_put(&sw);
416     dstr_destroy(&d);
417   }
418
419   return (0);
420 }
421
422 /*----- That's all, folks -------------------------------------------------*/