Converging on lclint-3.0.17 strict level.
[platform/upstream/rpm.git] / lib / rpmrc.c
1 /*@-mods@*/
2 #include "system.h"
3
4 #include <stdarg.h>
5
6 #if HAVE_SYS_SYSTEMCFG_H
7 #include <sys/systemcfg.h>
8 #else
9 #define __power_pc() 0
10 #endif
11
12 #include <rpmlib.h>
13 #include <rpmmacro.h>
14
15 #include "misc.h"
16 #include "debug.h"
17
18 /*@access FD_t@*/               /* compared with NULL */
19
20 /*@observer@*/ /*@unchecked@*/
21 static const char *defrcfiles = LIBRPMRC_FILENAME ":/etc/rpmrc:~/.rpmrc";
22
23 /*@observer@*/ /*@checked@*/
24 const char * macrofiles = MACROFILES;
25
26 typedef /*@owned@*/ const char * cptr_t;
27
28 typedef struct machCacheEntry_s {
29     const char * name;
30     int count;
31     cptr_t * equivs;
32     int visited;
33 } * machCacheEntry;
34
35 typedef struct machCache_s {
36     machCacheEntry cache;
37     int size;
38 } * machCache;
39
40 typedef struct machEquivInfo_s {
41     const char * name;
42     int score;
43 } * machEquivInfo;
44
45 typedef struct machEquivTable_s {
46     int count;
47     machEquivInfo list;
48 } * machEquivTable;
49
50 struct rpmvarValue {
51     const char * value;
52     /* eventually, this arch will be replaced with a generic condition */
53     const char * arch;
54 /*@only@*/ /*@null@*/ struct rpmvarValue * next;
55 };
56
57 struct rpmOption {
58     const char * name;
59     int var;
60     int archSpecific;
61 /*@unused@*/ int required;
62     int macroize;
63     int localize;
64 /*@unused@*/ struct rpmOptionValue * value;
65 };
66
67 typedef struct defaultEntry_s {
68 /*@owned@*/ /*@null@*/ const char * name;
69 /*@owned@*/ /*@null@*/ const char * defName;
70 } * defaultEntry;
71
72 typedef struct canonEntry_s {
73 /*@owned@*/ const char * name;
74 /*@owned@*/ const char * short_name;
75     short num;
76 } * canonEntry;
77
78 /* tags are 'key'canon, 'key'translate, 'key'compat
79  *
80  * for giggles, 'key'_canon, 'key'_compat, and 'key'_canon will also work
81  */
82 typedef struct tableType_s {
83 /*@observer@*/ const char * const key;
84     const int hasCanon;
85     const int hasTranslate;
86     struct machEquivTable_s equiv;
87     struct machCache_s cache;
88     defaultEntry defaults;
89     canonEntry canons;
90     int defaultsLength;
91     int canonsLength;
92 } * tableType;
93
94 /*@-fullinitblock@*/
95 /*@unchecked@*/
96 static struct tableType_s tables[RPM_MACHTABLE_COUNT] = {
97     { "arch", 1, 0 },
98     { "os", 1, 0 },
99     { "buildarch", 0, 1 },
100     { "buildos", 0, 1 }
101 };
102
103 /* this *must* be kept in alphabetical order */
104 /* The order of the flags is archSpecific, required, macroize, localize */
105
106 /*@unchecked@*/
107 static struct rpmOption optionTable[] = {
108     { "include",                RPMVAR_INCLUDE,                 0, 1,   0, 2 },
109     { "macrofiles",             RPMVAR_MACROFILES,              0, 0,   0, 1 },
110     { "optflags",               RPMVAR_OPTFLAGS,                1, 0,   1, 0 },
111     { "provides",               RPMVAR_PROVIDES,                0, 0,   0, 0 },
112 };
113 /*@=fullinitblock@*/
114
115 /*@unchecked@*/
116 static int optionTableSize = sizeof(optionTable) / sizeof(*optionTable);
117
118 #define OS      0
119 #define ARCH    1
120
121 /*@unchecked@*/
122 static cptr_t current[2];
123
124 /*@unchecked@*/
125 static int currTables[2] = { RPM_MACHTABLE_INSTOS, RPM_MACHTABLE_INSTARCH };
126
127 /*@unchecked@*/
128 static struct rpmvarValue values[RPMVAR_NUM];
129
130 /*@unchecked@*/
131 static int defaultsInitialized = 0;
132
133 /* prototypes */
134 static int doReadRC( /*@killref@*/ FD_t fd, const char * urlfn)
135         /*@globals rpmGlobalMacroContext,
136                 fileSystem, internalState @*/
137         /*@modifies fd, fileSystem, internalState @*/;
138
139 static void rpmSetVarArch(int var, const char * val,
140                 /*@null@*/ const char * arch)
141         /*@globals internalState @*/
142         /*@modifies internalState @*/;
143
144 static void rebuildCompatTables(int type, const char * name)
145         /*@globals internalState @*/
146         /*@modifies internalState @*/;
147
148 static void rpmRebuildTargetVars(/*@null@*/ const char **target, /*@null@*/ const char ** canontarget)
149         /*@globals rpmGlobalMacroContext,
150                 fileSystem, internalState @*/
151         /*@modifies *canontarget, fileSystem, internalState @*/;
152
153 static int optionCompare(const void * a, const void * b)
154         /*@*/
155 {
156     return xstrcasecmp(((struct rpmOption *) a)->name,
157                       ((struct rpmOption *) b)->name);
158 }
159
160 static /*@observer@*/ /*@null@*/ machCacheEntry
161 machCacheFindEntry(const machCache cache, const char * key)
162         /*@*/
163 {
164     int i;
165
166     for (i = 0; i < cache->size; i++)
167         if (!strcmp(cache->cache[i].name, key)) return cache->cache + i;
168
169     return NULL;
170 }
171
172 static int machCompatCacheAdd(char * name, const char * fn, int linenum,
173                                 machCache cache)
174         /*@globals internalState @*/
175         /*@modifies *name, cache->cache, cache->size, internalState @*/
176 {
177     machCacheEntry entry = NULL;
178     char * chptr;
179     char * equivs;
180     int delEntry = 0;
181     int i;
182
183     while (*name && xisspace(*name)) name++;
184
185     chptr = name;
186     while (*chptr && *chptr != ':') chptr++;
187     if (!*chptr) {
188         rpmError(RPMERR_RPMRC, _("missing second ':' at %s:%d\n"), fn, linenum);
189         return 1;
190     } else if (chptr == name) {
191         rpmError(RPMERR_RPMRC, _("missing architecture name at %s:%d\n"), fn,
192                              linenum);
193         return 1;
194     }
195
196     while (*chptr == ':' || xisspace(*chptr)) chptr--;
197     *(++chptr) = '\0';
198     equivs = chptr + 1;
199     while (*equivs && xisspace(*equivs)) equivs++;
200     if (!*equivs) {
201         delEntry = 1;
202     }
203
204     if (cache->size) {
205         entry = machCacheFindEntry(cache, name);
206         if (entry) {
207             for (i = 0; i < entry->count; i++)
208                 entry->equivs[i] = _free(entry->equivs[i]);
209             entry->equivs = _free(entry->equivs);
210             entry->count = 0;
211         }
212     }
213
214     if (!entry) {
215         cache->cache = xrealloc(cache->cache,
216                                (cache->size + 1) * sizeof(*cache->cache));
217         entry = cache->cache + cache->size++;
218         entry->name = xstrdup(name);
219         entry->count = 0;
220         entry->visited = 0;
221     }
222
223     if (delEntry) return 0;
224
225     while ((chptr = strtok(equivs, " ")) != NULL) {
226         equivs = NULL;
227         if (chptr[0] == '\0')   /* does strtok() return "" ever?? */
228             continue;
229         if (entry->count)
230             entry->equivs = xrealloc(entry->equivs, sizeof(*entry->equivs)
231                                         * (entry->count + 1));
232         else
233             entry->equivs = xmalloc(sizeof(*entry->equivs));
234
235         entry->equivs[entry->count] = xstrdup(chptr);
236         entry->count++;
237     }
238
239     return 0;
240 }
241
242 static /*@observer@*/ /*@null@*/ machEquivInfo
243 machEquivSearch(const machEquivTable table, const char * name)
244         /*@*/
245 {
246     int i;
247
248     for (i = 0; i < table->count; i++)
249         if (!xstrcasecmp(table->list[i].name, name))
250             return table->list + i;
251
252     return NULL;
253 }
254
255 static void machAddEquiv(machEquivTable table, const char * name,
256                            int distance)
257         /*@modifies table->list, table->count @*/
258 {
259     machEquivInfo equiv;
260
261     equiv = machEquivSearch(table, name);
262     if (!equiv) {
263         if (table->count)
264             table->list = xrealloc(table->list, (table->count + 1)
265                                     * sizeof(*table->list));
266         else
267             table->list = xmalloc(sizeof(*table->list));
268
269         table->list[table->count].name = xstrdup(name);
270         table->list[table->count++].score = distance;
271     }
272 }
273
274 static void machCacheEntryVisit(machCache cache,
275                 machEquivTable table, const char * name, int distance)
276         /*@modifies table->list, table->count @*/
277 {
278     machCacheEntry entry;
279     int i;
280
281     entry = machCacheFindEntry(cache, name);
282     if (!entry || entry->visited) return;
283
284     entry->visited = 1;
285
286     for (i = 0; i < entry->count; i++) {
287         machAddEquiv(table, entry->equivs[i], distance);
288     }
289
290     for (i = 0; i < entry->count; i++) {
291         machCacheEntryVisit(cache, table, entry->equivs[i], distance + 1);
292     }
293 }
294
295 static void machFindEquivs(machCache cache, machEquivTable table,
296                 const char * key)
297         /*@modifies cache->cache, table->list, table->count @*/
298 {
299     int i;
300
301     for (i = 0; i < cache->size; i++)
302         cache->cache[i].visited = 0;
303
304     while (table->count > 0) {
305         --table->count;
306         table->list[table->count].name = _free(table->list[table->count].name);
307     }
308     table->count = 0;
309     table->list = _free(table->list);
310
311     /*
312      *  We have a general graph built using strings instead of pointers.
313      *  Yuck. We have to start at a point at traverse it, remembering how
314      *  far away everything is.
315      */
316     /*@-nullstate@*/    /* FIX: table->list may be NULL. */
317     machAddEquiv(table, key, 1);
318     machCacheEntryVisit(cache, table, key, 2);
319     return;
320     /*@=nullstate@*/
321 }
322
323 static int addCanon(canonEntry * table, int * tableLen, char * line,
324                     const char * fn, int lineNum)
325         /*@globals internalState @*/
326         /*@modifies *table, *tableLen, *line, internalState @*/
327 {
328     canonEntry t;
329     char *s, *s1;
330     const char * tname;
331     const char * tshort_name;
332     int tnum;
333
334     (*tableLen) += 2;
335     /*@-unqualifiedtrans@*/
336     *table = xrealloc(*table, sizeof(**table) * (*tableLen));
337     /*@=unqualifiedtrans@*/
338
339     t = & ((*table)[*tableLen - 2]);
340
341     tname = strtok(line, ": \t");
342     tshort_name = strtok(NULL, " \t");
343     s = strtok(NULL, " \t");
344     if (! (tname && tshort_name && s)) {
345         rpmError(RPMERR_RPMRC, _("Incomplete data line at %s:%d\n"),
346                 fn, lineNum);
347         return RPMERR_RPMRC;
348     }
349     if (strtok(NULL, " \t")) {
350         rpmError(RPMERR_RPMRC, _("Too many args in data line at %s:%d\n"),
351               fn, lineNum);
352         return RPMERR_RPMRC;
353     }
354
355     /*@-nullpass@*/     /* LCL: s != NULL here. */
356     tnum = strtoul(s, &s1, 10);
357     if ((*s1) || (s1 == s) || (tnum == ULONG_MAX)) {
358         rpmError(RPMERR_RPMRC, _("Bad arch/os number: %s (%s:%d)\n"), s,
359               fn, lineNum);
360         return(RPMERR_RPMRC);
361     }
362     /*@=nullpass@*/
363
364     t[0].name = xstrdup(tname);
365     t[0].short_name = (tshort_name ? xstrdup(tshort_name) : xstrdup(""));
366     t[0].num = tnum;
367
368     /* From A B C entry */
369     /* Add  B B C entry */
370     t[1].name = (tshort_name ? xstrdup(tshort_name) : xstrdup(""));
371     t[1].short_name = (tshort_name ? xstrdup(tshort_name) : xstrdup(""));
372     t[1].num = tnum;
373
374     return 0;
375 }
376
377 static int addDefault(defaultEntry * table, int * tableLen, char * line,
378                         const char * fn, int lineNum)
379         /*@globals internalState @*/
380         /*@modifies *table, *tableLen, *line, internalState @*/
381 {
382     defaultEntry t;
383
384     (*tableLen)++;
385     /*@-unqualifiedtrans@*/
386     *table = xrealloc(*table, sizeof(**table) * (*tableLen));
387     /*@=unqualifiedtrans@*/
388
389     t = & ((*table)[*tableLen - 1]);
390
391     /*@-temptrans@*/
392     t->name = strtok(line, ": \t");
393     t->defName = strtok(NULL, " \t");
394     if (! (t->name && t->defName)) {
395         rpmError(RPMERR_RPMRC, _("Incomplete default line at %s:%d\n"),
396                  fn, lineNum);
397         return RPMERR_RPMRC;
398     }
399     if (strtok(NULL, " \t")) {
400         rpmError(RPMERR_RPMRC, _("Too many args in default line at %s:%d\n"),
401               fn, lineNum);
402         return RPMERR_RPMRC;
403     }
404
405     t->name = xstrdup(t->name);
406     t->defName = (t->defName ? xstrdup(t->defName) : NULL);
407     /*@=temptrans@*/
408
409     return 0;
410 }
411
412 static /*@null@*/ const canonEntry lookupInCanonTable(const char * name,
413                 const canonEntry table, int tableLen)
414         /*@*/
415 {
416     while (tableLen) {
417         tableLen--;
418         if (strcmp(name, table[tableLen].name))
419             continue;
420         /*@-immediatetrans -retalias@*/
421         return &(table[tableLen]);
422         /*@=immediatetrans =retalias@*/
423     }
424
425     return NULL;
426 }
427
428 static /*@observer@*/ /*@null@*/
429 const char * lookupInDefaultTable(const char * name,
430                 const defaultEntry table, int tableLen)
431         /*@*/
432 {
433     while (tableLen) {
434         tableLen--;
435         if (table[tableLen].name && !strcmp(name, table[tableLen].name))
436             return table[tableLen].defName;
437     }
438
439     return name;
440 }
441
442 static void setVarDefault(int var, const char * macroname, const char * val,
443                 /*@null@*/ const char * body)
444         /*@globals rpmGlobalMacroContext,
445                 internalState @*/
446         /*@modifies internalState @*/
447 {
448     if (var >= 0) {     /* XXX Dying ... */
449         if (rpmGetVar(var)) return;
450         rpmSetVar(var, val);
451     }
452     if (body == NULL)
453         body = val;
454     addMacro(NULL, macroname, NULL, body, RMIL_DEFAULT);
455 }
456
457 static void setPathDefault(int var, const char * macroname, const char * subdir)
458         /*@globals rpmGlobalMacroContext,
459                 internalState @*/
460         /*@modifies internalState @*/
461 {
462
463     if (var >= 0) {     /* XXX Dying ... */
464         const char * topdir;
465         char * fn;
466
467         if (rpmGetVar(var)) return;
468
469         topdir = rpmGetPath("%{_topdir}", NULL);
470
471         fn = alloca(strlen(topdir) + strlen(subdir) + 2);
472         strcpy(fn, topdir);
473         if (fn[strlen(topdir) - 1] != '/')
474             strcat(fn, "/");
475         strcat(fn, subdir);
476
477         rpmSetVar(var, fn);
478         topdir = _free(topdir);
479     }
480
481     if (macroname != NULL) {
482 #define _TOPDIRMACRO    "%{_topdir}/"
483         char *body = alloca(sizeof(_TOPDIRMACRO) + strlen(subdir));
484         strcpy(body, _TOPDIRMACRO);
485         strcat(body, subdir);
486         addMacro(NULL, macroname, NULL, body, RMIL_DEFAULT);
487 #undef _TOPDIRMACRO
488     }
489 }
490
491 /*@observer@*/ /*@unchecked@*/
492 static const char * prescriptenviron = "\n\
493 RPM_SOURCE_DIR=\"%{_sourcedir}\"\n\
494 RPM_BUILD_DIR=\"%{_builddir}\"\n\
495 RPM_OPT_FLAGS=\"%{optflags}\"\n\
496 RPM_ARCH=\"%{_arch}\"\n\
497 RPM_OS=\"%{_os}\"\n\
498 export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\n\
499 RPM_DOC_DIR=\"%{_docdir}\"\n\
500 export RPM_DOC_DIR\n\
501 RPM_PACKAGE_NAME=\"%{name}\"\n\
502 RPM_PACKAGE_VERSION=\"%{version}\"\n\
503 RPM_PACKAGE_RELEASE=\"%{release}\"\n\
504 export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE\n\
505 %{?buildroot:RPM_BUILD_ROOT=\"%{buildroot}\"\n\
506 export RPM_BUILD_ROOT\n}\
507 ";
508
509 static void setDefaults(void)
510         /*@globals rpmGlobalMacroContext,
511                 internalState @*/
512         /*@modifies internalState @*/
513 {
514
515     addMacro(NULL, "_usr", NULL, "/usr", RMIL_DEFAULT);
516     addMacro(NULL, "_var", NULL, "/var", RMIL_DEFAULT);
517
518     addMacro(NULL, "_preScriptEnvironment",NULL, prescriptenviron,RMIL_DEFAULT);
519
520     setVarDefault(-1,                   "_topdir",
521                 "/usr/src/redhat",      "%{_usr}/src/redhat");
522     setVarDefault(-1,                   "_tmppath",
523                 "/var/tmp",             "%{_var}/tmp");
524     setVarDefault(-1,                   "_dbpath",
525                 "/var/lib/rpm",         "%{_var}/lib/rpm");
526     setVarDefault(-1,                   "_defaultdocdir",
527                 "/usr/doc",             "%{_usr}/doc");
528
529     setVarDefault(-1,                   "_rpmfilename",
530         "%%{ARCH}/%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm",NULL);
531
532     setVarDefault(RPMVAR_OPTFLAGS,      "optflags",
533                 "-O2",                  NULL);
534     setVarDefault(-1,                   "sigtype",
535                 "none",                 NULL);
536     setVarDefault(-1,                   "_buildshell",
537                 "/bin/sh",              NULL);
538
539     setPathDefault(-1,                  "_builddir",    "BUILD");
540     setPathDefault(-1,                  "_rpmdir",      "RPMS");
541     setPathDefault(-1,                  "_srcrpmdir",   "SRPMS");
542     setPathDefault(-1,                  "_sourcedir",   "SOURCES");
543     setPathDefault(-1,                  "_specdir",     "SPECS");
544
545 }
546
547 /*@-usedef@*/   /*@ FIX: se usage inconsistent, W2DO? */
548 static int doReadRC( /*@killref@*/ FD_t fd, const char * urlfn)
549         /*@globals rpmGlobalMacroContext,
550                 fileSystem, internalState @*/
551         /*@modifies fd, fileSystem, internalState @*/
552 {
553     const char *s;
554     char *se, *next;
555     int linenum = 0;
556     struct rpmOption searchOption, * option;
557     int rc;
558
559     /* XXX really need rc = Slurp(fd, const char * filename, char ** buf) */
560   { off_t size = fdSize(fd);
561     size_t nb = (size >= 0 ? size : (8*BUFSIZ - 2));
562     if (nb == 0) {
563         (void) Fclose(fd);
564         return 0;
565     }
566     next = alloca(nb + 2);
567     next[0] = '\0';
568     rc = Fread(next, sizeof(*next), nb, fd);
569     if (Ferror(fd) || (size > 0 && rc != nb)) { /* XXX Feof(fd) */
570         rpmError(RPMERR_RPMRC, _("Failed to read %s: %s.\n"), urlfn,
571                  Fstrerror(fd));
572         rc = 1;
573     } else
574         rc = 0;
575     (void) Fclose(fd);
576     if (rc) return rc;
577     next[nb] = '\n';
578     next[nb + 1] = '\0';
579   }
580
581     /*@-branchstate@*/
582     while (*next != '\0') {
583         linenum++;
584
585         s = se = next;
586
587         /* Find end-of-line. */
588         while (*se && *se != '\n') se++;
589         if (*se != '\0') *se++ = '\0';
590         next = se;
591
592         /* Trim leading spaces */
593         while (*s && xisspace(*s)) s++;
594
595         /* We used to allow comments to begin anywhere, but not anymore. */
596         if (*s == '#' || *s == '\0') continue;
597
598         /* Find end-of-keyword. */
599         se = (char *)s;
600         while (*se && !xisspace(*se) && *se != ':') se++;
601
602         if (xisspace(*se)) {
603             *se++ = '\0';
604             while (*se && xisspace(*se) && *se != ':') se++;
605         }
606
607         if (*se != ':') {
608             rpmError(RPMERR_RPMRC, _("missing ':' (found 0x%02x) at %s:%d\n"),
609                      (unsigned)(0xff & *se), urlfn, linenum);
610             return 1;
611         }
612         *se++ = '\0';   /* terminate keyword or option, point to value */
613         while (*se && xisspace(*se)) se++;
614
615         /* Find keyword in table */
616         searchOption.name = s;
617         option = bsearch(&searchOption, optionTable, optionTableSize,
618                          sizeof(optionTable[0]), optionCompare);
619
620         if (option) {   /* For configuration variables  ... */
621             const char *arch, *val, *fn;
622
623             arch = val = fn = NULL;
624             if (*se == '\0') {
625                 rpmError(RPMERR_RPMRC, _("missing argument for %s at %s:%d\n"),
626                       option->name, urlfn, linenum);
627                 return 1;
628             }
629
630             switch (option->var) {
631             case RPMVAR_INCLUDE:
632               { FD_t fdinc;
633
634                 s = se;
635                 while (*se && !xisspace(*se)) se++;
636                 if (*se != '\0') *se++ = '\0';
637
638                 rpmRebuildTargetVars(NULL, NULL);
639
640                 fn = rpmGetPath(s, NULL);
641                 if (fn == NULL || *fn == '\0') {
642                     rpmError(RPMERR_RPMRC, _("%s expansion failed at %s:%d \"%s\"\n"),
643                         option->name, urlfn, linenum, s);
644                     fn = _free(fn);
645                     return 1;
646                     /*@notreached@*/
647                 }
648
649                 fdinc = Fopen(fn, "r.fpio");
650                 if (fdinc == NULL || Ferror(fdinc)) {
651                     rpmError(RPMERR_RPMRC, _("cannot open %s at %s:%d: %s\n"),
652                         fn, urlfn, linenum, Fstrerror(fdinc));
653                     rc = 1;
654                 } else {
655                     rc = doReadRC(fdinc, fn);
656                 }
657                 fn = _free(fn);
658                 if (rc) return rc;
659                 continue;       /* XXX don't save include value as var/macro */
660               } /*@notreached@*/ /*@switchbreak@*/ break;
661             case RPMVAR_MACROFILES:
662                 fn = rpmGetPath(se, NULL);
663                 if (fn == NULL || *fn == '\0') {
664                     rpmError(RPMERR_RPMRC, _("%s expansion failed at %s:%d \"%s\"\n"),
665                         option->name, urlfn, linenum, fn);
666                     fn = _free(fn);
667                     return 1;
668                 }
669                 se = (char *)fn;
670                 /*@switchbreak@*/ break;
671             case RPMVAR_PROVIDES:
672               { char *t;
673                 s = rpmGetVar(RPMVAR_PROVIDES);
674                 if (s == NULL) s = "";
675                 fn = t = xmalloc(strlen(s) + strlen(se) + 2);
676                 while (*s != '\0') *t++ = *s++;
677                 *t++ = ' ';
678                 while (*se != '\0') *t++ = *se++;
679                 *t++ = '\0';
680                 se = (char *)fn;
681               } /*@switchbreak@*/ break;
682             default:
683                 /*@switchbreak@*/ break;
684             }
685
686             if (option->archSpecific) {
687                 arch = se;
688                 while (*se && !xisspace(*se)) se++;
689                 if (*se == '\0') {
690                     rpmError(RPMERR_RPMRC,
691                                 _("missing architecture for %s at %s:%d\n"),
692                                 option->name, urlfn, linenum);
693                     return 1;
694                 }
695                 *se++ = '\0';
696                 while (*se && xisspace(*se)) se++;
697                 if (*se == '\0') {
698                     rpmError(RPMERR_RPMRC,
699                                 _("missing argument for %s at %s:%d\n"),
700                                 option->name, urlfn, linenum);
701                     return 1;
702                 }
703             }
704         
705             val = se;
706
707             /* Only add macros if appropriate for this arch */
708             if (option->macroize &&
709               (arch == NULL || !strcmp(arch, current[ARCH]))) {
710                 char *n, *name;
711                 n = name = xmalloc(strlen(option->name)+2);
712                 if (option->localize)
713                     *n++ = '_';
714                 strcpy(n, option->name);
715                 addMacro(NULL, name, NULL, val, RMIL_RPMRC);
716                 free(name);
717             }
718             rpmSetVarArch(option->var, val, arch);
719             fn = _free(fn);
720
721         } else {        /* For arch/os compatibilty tables ... */
722             int gotit;
723             int i;
724
725             gotit = 0;
726
727             for (i = 0; i < RPM_MACHTABLE_COUNT; i++) {
728                 if (!strncmp(tables[i].key, s, strlen(tables[i].key)))
729                     /*@innerbreak@*/ break;
730             }
731
732             if (i < RPM_MACHTABLE_COUNT) {
733                 const char *rest = s + strlen(tables[i].key);
734                 if (*rest == '_') rest++;
735
736                 if (!strcmp(rest, "compat")) {
737                     if (machCompatCacheAdd(se, urlfn, linenum,
738                                                 &tables[i].cache))
739                         return 1;
740                     gotit = 1;
741                 } else if (tables[i].hasTranslate &&
742                            !strcmp(rest, "translate")) {
743                     if (addDefault(&tables[i].defaults,
744                                    &tables[i].defaultsLength,
745                                    se, urlfn, linenum))
746                         return 1;
747                     gotit = 1;
748                 } else if (tables[i].hasCanon &&
749                            !strcmp(rest, "canon")) {
750                     if (addCanon(&tables[i].canons, &tables[i].canonsLength,
751                                  se, urlfn, linenum))
752                         return 1;
753                     gotit = 1;
754                 }
755             }
756
757             if (!gotit) {
758                 rpmError(RPMERR_RPMRC, _("bad option '%s' at %s:%d\n"),
759                             s, urlfn, linenum);
760             }
761         }
762     }
763     /*@=branchstate@*/
764
765     return 0;
766 }
767 /*@=usedef@*/
768
769 #       if defined(__linux__) && defined(__i386__)
770 #include <setjmp.h>
771 #include <signal.h>
772
773 /*
774  * Generic CPUID function
775  */
776 static inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
777         /*@modifies *eax, *ebx, *ecx, *edx @*/
778 {
779 #ifdef  __LCLINT__
780     *eax = *ebx = *ecx = *edx = 0;
781 #endif
782 #ifdef PIC
783         __asm__("pushl %%ebx; cpuid; movl %%ebx,%1; popl %%ebx"
784                 : "=a"(*eax), "=g"(*ebx), "=&c"(*ecx), "=&d"(*edx)
785                 : "a" (op));
786 #else
787         __asm__("cpuid"
788                 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
789                 : "a" (op));
790 #endif
791
792 }
793
794 /*
795  * CPUID functions returning a single datum
796  */
797 static inline unsigned int cpuid_eax(unsigned int op)
798         /*@*/
799 {
800         unsigned int val;
801
802 #ifdef PIC
803         __asm__("pushl %%ebx; cpuid; popl %%ebx"
804                 : "=a" (val) : "a" (op) : "ecx", "edx");
805 #else
806         __asm__("cpuid"
807                 : "=a" (val) : "a" (op) : "ebx", "ecx", "edx");
808 #endif
809         return val;
810 }
811
812 static inline unsigned int cpuid_ebx(unsigned int op)
813         /*@*/
814 {
815         unsigned int tmp, val;
816
817 #ifdef PIC
818         __asm__("pushl %%ebx; cpuid; movl %%ebx,%1; popl %%ebx"
819                 : "=a" (tmp), "=g" (val) : "a" (op) : "ecx", "edx");
820 #else
821         __asm__("cpuid"
822                 : "=a" (tmp), "=b" (val) : "a" (op) : "ecx", "edx");
823 #endif
824         return val;
825 }
826
827 static inline unsigned int cpuid_ecx(unsigned int op)
828         /*@*/
829 {
830         unsigned int tmp, val;
831 #ifdef PIC
832         __asm__("pushl %%ebx; cpuid; popl %%ebx"
833                 : "=a" (tmp), "=c" (val) : "a" (op) : "edx");
834 #else
835         __asm__("cpuid"
836                 : "=a" (tmp), "=c" (val) : "a" (op) : "ebx", "edx");
837 #endif
838         return val;
839
840 }
841
842 static inline unsigned int cpuid_edx(unsigned int op)
843         /*@*/
844 {
845         unsigned int tmp, val;
846 #ifdef PIC
847         __asm__("pushl %%ebx; cpuid; popl %%ebx"
848                 : "=a" (tmp), "=d" (val) : "a" (op) : "ecx");
849 #else
850         __asm__("cpuid"
851                 : "=a" (tmp), "=d" (val) : "a" (op) : "ebx", "ecx");
852 #endif
853         return val;
854
855 }
856
857 /*@unchecked@*/
858 static sigjmp_buf jenv;
859
860 static inline void model3(int _unused)
861         /*@globals internalState @*/
862         /*@modifies internalState @*/
863 {
864         siglongjmp(jenv, 1);
865 }
866
867 static inline int RPMClass(void)
868         /*@globals internalState @*/
869         /*@modifies internalState @*/
870 {
871         int cpu;
872         unsigned int tfms, junk, cap;
873         
874         signal(SIGILL, model3);
875         
876         if(sigsetjmp(jenv, 1))
877                 return 3;
878                 
879         if(cpuid_eax(0x000000000)==0)
880                 return 4;
881         cpuid(0x000000001, &tfms, &junk, &junk, &cap);
882         
883         cpu = (tfms>>8)&15;
884         
885         if(cpu < 6)
886                 return cpu;
887                 
888         if(cap & (1<<15))
889                 return 6;
890                 
891         return 5;
892 }
893
894 /* should only be called for model 6 CPU's */
895 static int is_athlon(void)
896         /*@*/
897 {
898         unsigned int eax, ebx, ecx, edx;
899         char vendor[16];
900         int i;
901         
902         cpuid (0, &eax, &ebx, &ecx, &edx);
903
904         /* If you care about space, you can just check ebx, ecx and edx directly
905            instead of forming a string first and then doing a strcmp */
906         memset(vendor, 0, sizeof(vendor));
907         
908         for (i=0; i<4; i++)
909                 vendor[i] = (unsigned char) (ebx >>(8*i));
910         for (i=0; i<4; i++)
911                 vendor[4+i] = (unsigned char) (edx >>(8*i));
912         for (i=0; i<4; i++)
913                 vendor[8+i] = (unsigned char) (ecx >>(8*i));
914                 
915         if (strcmp(vendor, "AuthenticAMD") != 0)  
916                 return 0;
917
918         return 1;
919 }
920
921 #endif
922
923 static void defaultMachine(/*@out@*/ const char ** arch,
924                 /*@out@*/ const char ** os)
925         /*@globals fileSystem@*/
926         /*@modifies *arch, *os, fileSystem @*/
927 {
928     static struct utsname un;
929     static int gotDefaults = 0;
930     char * chptr;
931     canonEntry canon;
932     int rc;
933
934     if (!gotDefaults) {
935         rc = uname(&un);
936         if (rc < 0) return;
937
938 #if !defined(__linux__)
939 #ifdef SNI
940         /* USUALLY un.sysname on sinix does start with the word "SINIX"
941          * let's be absolutely sure
942          */
943         strncpy(un.sysname, "SINIX", sizeof(un.sysname));
944 #endif
945         /*@-nullpass@*/
946         if (!strcmp(un.sysname, "AIX")) {
947             strcpy(un.machine, __power_pc() ? "ppc" : "rs6000");
948             sprintf(un.sysname,"aix%s.%s", un.version, un.release);
949         }
950         else if (!strcmp(un.sysname, "SunOS")) {
951             if (!strncmp(un.release,"4", 1)) /* SunOS 4.x */ {
952                 int fd;
953                 for (fd = 0;
954                     (un.release[fd] != 0 && (fd < sizeof(un.release)));
955                     fd++) {
956                       if (!xisdigit(un.release[fd]) && (un.release[fd] != '.')) {
957                         un.release[fd] = 0;
958                         break;
959                       }
960                     }
961                     sprintf(un.sysname,"sunos%s",un.release);
962             }
963
964             else /* Solaris 2.x: n.x.x becomes n-3.x.x */
965                 sprintf(un.sysname, "solaris%1d%s", atoi(un.release)-3,
966                         un.release+1+(atoi(un.release)/10));
967         }
968         else if (!strcmp(un.sysname, "HP-UX"))
969             /*make un.sysname look like hpux9.05 for example*/
970             sprintf(un.sysname, "hpux%s", strpbrk(un.release, "123456789"));
971         else if (!strcmp(un.sysname, "OSF1"))
972             /*make un.sysname look like osf3.2 for example*/
973             sprintf(un.sysname, "osf%s", strpbrk(un.release, "123456789"));
974         else if (!strncmp(un.sysname, "IP", 2))
975             un.sysname[2] = '\0';
976         else if (!strncmp(un.sysname, "SINIX", 5)) {
977             sprintf(un.sysname, "sinix%s",un.release);
978             if (!strncmp(un.machine, "RM", 2))
979                 sprintf(un.machine, "mips");
980         }
981         else if ((!strncmp(un.machine, "34", 2) ||
982                 !strncmp(un.machine, "33", 2)) && \
983                 !strncmp(un.release, "4.0", 3))
984         {
985             /* we are on ncr-sysv4 */
986             char * prelid = NULL;
987             FD_t fd = Fopen("/etc/.relid", "r.fdio");
988             int gotit = 0;
989             /*@-branchstate@*/
990             if (fd != NULL && !Ferror(fd)) {
991                 chptr = xcalloc(1, 256);
992                 {   int irelid = Fread(chptr, sizeof(*chptr), 256, fd);
993                     (void) Fclose(fd);
994                     /* example: "112393 RELEASE 020200 Version 01 OS" */
995                     if (irelid > 0) {
996                         if ((prelid = strstr(chptr, "RELEASE "))){
997                             prelid += strlen("RELEASE ")+1;
998                             sprintf(un.sysname,"ncr-sysv4.%.*s",1,prelid);
999                             gotit = 1;
1000                         }
1001                     }
1002                 }
1003                 chptr = _free (chptr);
1004             }
1005             /*@=branchstate@*/
1006             if (!gotit) /* parsing /etc/.relid file failed? */
1007                 strcpy(un.sysname,"ncr-sysv4");
1008             /* wrong, just for now, find out how to look for i586 later*/
1009             strcpy(un.machine,"i486");
1010         }
1011         /*@=nullpass@*/
1012 #endif  /* __linux__ */
1013
1014         /* get rid of the hyphens in the sysname */
1015         for (chptr = un.machine; *chptr != '\0'; chptr++)
1016             if (*chptr == '/') *chptr = '-';
1017
1018 #       if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL)
1019             /* little endian */
1020             strcpy(un.machine, "mipsel");
1021 #       elif defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB)
1022            /* big endian */
1023                 strcpy(un.machine, "mipseb");
1024 #       endif
1025
1026 #       if defined(__hpux) && defined(_SC_CPU_VERSION)
1027         {
1028 #           if !defined(CPU_PA_RISC1_2)
1029 #                define CPU_PA_RISC1_2  0x211 /* HP PA-RISC1.2 */
1030 #           endif
1031 #           if !defined(CPU_PA_RISC2_0)
1032 #               define CPU_PA_RISC2_0  0x214 /* HP PA-RISC2.0 */
1033 #           endif
1034             int cpu_version = sysconf(_SC_CPU_VERSION);
1035
1036 #           if defined(CPU_HP_MC68020)
1037                 if (cpu_version == CPU_HP_MC68020)
1038                     strcpy(un.machine, "m68k");
1039 #           endif
1040 #           if defined(CPU_HP_MC68030)
1041                 if (cpu_version == CPU_HP_MC68030)
1042                     strcpy(un.machine, "m68k");
1043 #           endif
1044 #           if defined(CPU_HP_MC68040)
1045                 if (cpu_version == CPU_HP_MC68040)
1046                     strcpy(un.machine, "m68k");
1047 #           endif
1048
1049 #           if defined(CPU_PA_RISC1_0)
1050                 if (cpu_version == CPU_PA_RISC1_0)
1051                     strcpy(un.machine, "hppa1.0");
1052 #           endif
1053 #           if defined(CPU_PA_RISC1_1)
1054                 if (cpu_version == CPU_PA_RISC1_1)
1055                     strcpy(un.machine, "hppa1.1");
1056 #           endif
1057 #           if defined(CPU_PA_RISC1_2)
1058                 if (cpu_version == CPU_PA_RISC1_2)
1059                     strcpy(un.machine, "hppa1.2");
1060 #           endif
1061 #           if defined(CPU_PA_RISC2_0)
1062                 if (cpu_version == CPU_PA_RISC2_0)
1063                     strcpy(un.machine, "hppa2.0");
1064 #           endif
1065         }
1066 #       endif   /* hpux */
1067
1068 #       if HAVE_PERSONALITY && defined(__linux__) && defined(__sparc__)
1069         if (!strcmp(un.machine, "sparc")) {
1070             #define PERS_LINUX          0x00000000
1071             #define PERS_LINUX_32BIT    0x00800000
1072             #define PERS_LINUX32        0x00000008
1073
1074             extern int personality(unsigned long);
1075             int oldpers;
1076             
1077             oldpers = personality(PERS_LINUX_32BIT);
1078             if (oldpers != -1) {
1079                 if (personality(PERS_LINUX) != -1) {
1080                     uname(&un);
1081                     if (! strcmp(un.machine, "sparc64")) {
1082                         strcpy(un.machine, "sparcv9");
1083                         oldpers = PERS_LINUX32;
1084                     }
1085                 }
1086                 personality(oldpers);
1087             }
1088         }
1089 #       endif   /* sparc*-linux */
1090
1091 #       if defined(__GNUC__) && defined(__alpha__)
1092         {
1093             unsigned long amask, implver;
1094             register long v0 __asm__("$0") = -1;
1095             __asm__ (".long 0x47e00c20" : "=r"(v0) : "0"(v0));
1096             amask = ~v0;
1097             __asm__ (".long 0x47e03d80" : "=r"(v0));
1098             implver = v0;
1099             switch (implver) {
1100             case 1:
1101                 switch (amask) {
1102                 case 0: strcpy(un.machine, "alphaev5"); break;
1103                 case 1: strcpy(un.machine, "alphaev56"); break;
1104                 case 0x101: strcpy(un.machine, "alphapca56"); break;
1105                 }
1106                 break;
1107             case 2:
1108                 switch (amask) {
1109                 case 0x303: strcpy(un.machine, "alphaev6"); break;
1110                 case 0x307: strcpy(un.machine, "alphaev67"); break;
1111                 }
1112                 break;
1113             }
1114         }
1115 #       endif
1116
1117 #       if defined(__linux__) && defined(__i386__)
1118         {
1119             char class = (char) (RPMClass() | '0');
1120
1121             if (class == '6' && is_athlon())
1122                 strcpy(un.machine, "athlon");
1123             else if (strchr("3456", un.machine[1]) && un.machine[1] != class)
1124                 un.machine[1] = class;
1125         }
1126 #       endif
1127
1128 #       if defined(__linux__) && defined(__powerpc__)
1129         {
1130             unsigned pvr;
1131             __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
1132
1133             pvr >>= 16;
1134             if ( pvr >= 0x40)
1135                 strcpy(un.machine, "ppcpseries");
1136             else if ( (pvr == 0x36) || (pvr == 0x37) )
1137                 strcpy(un.machine, "ppciseries");
1138             else
1139                 strcpy(un.machine, "ppc");
1140         }
1141 #       endif
1142
1143         /* the uname() result goes through the arch_canon table */
1144         canon = lookupInCanonTable(un.machine,
1145                                    tables[RPM_MACHTABLE_INSTARCH].canons,
1146                                    tables[RPM_MACHTABLE_INSTARCH].canonsLength);
1147         if (canon)
1148             strcpy(un.machine, canon->short_name);
1149
1150         canon = lookupInCanonTable(un.sysname,
1151                                    tables[RPM_MACHTABLE_INSTOS].canons,
1152                                    tables[RPM_MACHTABLE_INSTOS].canonsLength);
1153         if (canon)
1154             strcpy(un.sysname, canon->short_name);
1155         gotDefaults = 1;
1156     }
1157
1158     if (arch) *arch = un.machine;
1159     if (os) *os = un.sysname;
1160 }
1161
1162 static /*@observer@*/ /*@null@*/
1163 const char * rpmGetVarArch(int var, /*@null@*/ const char * arch)
1164         /*@*/
1165 {
1166     const struct rpmvarValue * next;
1167
1168     if (arch == NULL) arch = current[ARCH];
1169
1170     if (arch) {
1171         next = &values[var];
1172         while (next) {
1173             if (next->arch && !strcmp(next->arch, arch)) return next->value;
1174             next = next->next;
1175         }
1176     }
1177
1178     next = values + var;
1179     while (next && next->arch) next = next->next;
1180
1181     return next ? next->value : NULL;
1182 }
1183
1184 const char *rpmGetVar(int var)
1185 {
1186     return rpmGetVarArch(var, NULL);
1187 }
1188
1189 /* this doesn't free the passed pointer! */
1190 static void freeRpmVar(/*@only@*/ struct rpmvarValue * orig)
1191         /*@modifies *orig @*/
1192 {
1193     struct rpmvarValue * next, * var = orig;
1194
1195     while (var) {
1196         next = var->next;
1197         var->arch = _free(var->arch);
1198         var->value = _free(var->value);
1199
1200         /*@-branchstate@*/
1201         if (var != orig) var = _free(var);
1202         /*@=branchstate@*/
1203         var = next;
1204     }
1205 }
1206
1207 void rpmSetVar(int var, const char * val)
1208 {
1209     /*@-immediatetrans@*/
1210     freeRpmVar(&values[var]);
1211     /*@=immediatetrans@*/
1212     values[var].value = (val ? xstrdup(val) : NULL);
1213 }
1214
1215 static void rpmSetVarArch(int var, const char * val, const char * arch)
1216         /*@*/
1217 {
1218     struct rpmvarValue * next = values + var;
1219
1220     if (next->value) {
1221         if (arch) {
1222             while (next->next) {
1223                 if (next->arch && !strcmp(next->arch, arch)) break;
1224                 next = next->next;
1225             }
1226         } else {
1227             while (next->next) {
1228                 if (!next->arch) break;
1229                 next = next->next;
1230             }
1231         }
1232
1233         /*@-nullpass@*/ /* LCL: arch != NULL here. */
1234         if (next->arch && arch && !strcmp(next->arch, arch)) {
1235         /*@=nullpass@*/
1236             next->value = _free(next->value);
1237             next->arch = _free(next->arch);
1238         } else if (next->arch || arch) {
1239             next->next = xmalloc(sizeof(*next->next));
1240             next = next->next;
1241             next->value = NULL;
1242             next->arch = NULL;
1243             next->next = NULL;
1244         }
1245     }
1246
1247     next->value = xstrdup(val);         /* XXX memory leak, hard to plug */
1248     next->arch = (arch ? xstrdup(arch) : NULL);
1249 }
1250
1251 void rpmSetTables(int archTable, int osTable)
1252 {
1253     const char * arch, * os;
1254
1255     defaultMachine(&arch, &os);
1256
1257     if (currTables[ARCH] != archTable) {
1258         currTables[ARCH] = archTable;
1259         rebuildCompatTables(ARCH, arch);
1260     }
1261
1262     if (currTables[OS] != osTable) {
1263         currTables[OS] = osTable;
1264         rebuildCompatTables(OS, os);
1265     }
1266 }
1267
1268 int rpmMachineScore(int type, const char * name)
1269 {
1270     machEquivInfo info = machEquivSearch(&tables[type].equiv, name);
1271     return (info != NULL ? info->score : 0);
1272 }
1273
1274 void rpmGetMachine(const char ** arch, const char ** os)
1275 {
1276     if (arch)
1277         *arch = current[ARCH];
1278
1279     if (os)
1280         *os = current[OS];
1281 }
1282
1283 void rpmSetMachine(const char * arch, const char * os)
1284 {
1285     const char * host_cpu, * host_os;
1286
1287     defaultMachine(&host_cpu, &host_os);
1288
1289     if (arch == NULL) {
1290         arch = host_cpu;
1291         if (tables[currTables[ARCH]].hasTranslate)
1292             arch = lookupInDefaultTable(arch,
1293                             tables[currTables[ARCH]].defaults,
1294                             tables[currTables[ARCH]].defaultsLength);
1295     }
1296     if (arch == NULL) return;   /* XXX can't happen */
1297
1298     if (os == NULL) {
1299         os = host_os;
1300         if (tables[currTables[OS]].hasTranslate)
1301             os = lookupInDefaultTable(os,
1302                             tables[currTables[OS]].defaults,
1303                             tables[currTables[OS]].defaultsLength);
1304     }
1305     if (os == NULL) return;     /* XXX can't happen */
1306
1307     if (!current[ARCH] || strcmp(arch, current[ARCH])) {
1308         current[ARCH] = _free(current[ARCH]);
1309         current[ARCH] = xstrdup(arch);
1310         rebuildCompatTables(ARCH, host_cpu);
1311     }
1312
1313     if (!current[OS] || strcmp(os, current[OS])) {
1314         char * t = xstrdup(os);
1315         current[OS] = _free(current[OS]);
1316         /*
1317          * XXX Capitalizing the 'L' is needed to insure that old
1318          * XXX os-from-uname (e.g. "Linux") is compatible with the new
1319          * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
1320          * XXX A copy of this string is embedded in headers and is
1321          * XXX used by rpmInstallPackage->{os,arch}Okay->rpmMachineScore->
1322          * XXX to verify correct arch/os from headers.
1323          */
1324         if (!strcmp(t, "linux"))
1325             *t = 'L';
1326         current[OS] = t;
1327         
1328         rebuildCompatTables(OS, host_os);
1329     }
1330 }
1331
1332 static void rebuildCompatTables(int type, const char * name)
1333         /*@*/
1334 {
1335     machFindEquivs(&tables[currTables[type]].cache,
1336                    &tables[currTables[type]].equiv,
1337                    name);
1338 }
1339
1340 static void getMachineInfo(int type, /*@null@*/ /*@out@*/ const char ** name,
1341                         /*@null@*/ /*@out@*/int * num)
1342         /*@modifies *name, *num @*/
1343 {
1344     canonEntry canon;
1345     int which = currTables[type];
1346
1347     /* use the normal canon tables, even if we're looking up build stuff */
1348     if (which >= 2) which -= 2;
1349
1350     canon = lookupInCanonTable(current[type],
1351                                tables[which].canons,
1352                                tables[which].canonsLength);
1353
1354     if (canon) {
1355         if (num) *num = canon->num;
1356         if (name) *name = canon->short_name;
1357     } else {
1358         if (num) *num = 255;
1359         if (name) *name = current[type];
1360
1361         if (tables[currTables[type]].hasCanon) {
1362             rpmMessage(RPMMESS_WARNING, _("Unknown system: %s\n"), current[type]);
1363             rpmMessage(RPMMESS_WARNING, _("Please contact rpm-list@redhat.com\n"));
1364         }
1365     }
1366 }
1367
1368 void rpmGetArchInfo(const char ** name, int * num)
1369 {
1370     getMachineInfo(ARCH, name, num);
1371 }
1372
1373 void rpmGetOsInfo(const char ** name, int * num)
1374 {
1375     getMachineInfo(OS, name, num);
1376 }
1377
1378 void rpmRebuildTargetVars(const char ** target, const char ** canontarget)
1379 {
1380
1381     char *ca = NULL, *co = NULL, *ct = NULL;
1382     int x;
1383
1384     /* Rebuild the compat table to recalculate the current target arch.  */
1385
1386     rpmSetMachine(NULL, NULL);
1387     rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS);
1388     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
1389
1390     /*@-branchstate@*/
1391     if (target && *target) {
1392         char *c;
1393         /* Set arch and os from specified build target */
1394         ca = xstrdup(*target);
1395         if ((c = strchr(ca, '-')) != NULL) {
1396             *c++ = '\0';
1397             
1398             if ((co = strrchr(c, '-')) == NULL) {
1399                 co = c;
1400             } else {
1401                 if (!xstrcasecmp(co, "-gnu"))
1402                     *co = '\0';
1403                 if ((co = strrchr(c, '-')) == NULL)
1404                     co = c;
1405                 else
1406                     co++;
1407             }
1408             if (co != NULL) co = xstrdup(co);
1409         }
1410     } else {
1411         const char *a = NULL;
1412         const char *o = NULL;
1413         /* Set build target from rpm arch and os */
1414         rpmGetArchInfo(&a, NULL);
1415         ca = (a) ? xstrdup(a) : NULL;
1416         rpmGetOsInfo(&o, NULL);
1417         co = (o) ? xstrdup(o) : NULL;
1418     }
1419     /*@=branchstate@*/
1420
1421     /* If still not set, Set target arch/os from default uname(2) values */
1422     if (ca == NULL) {
1423         const char *a = NULL;
1424         defaultMachine(&a, NULL);
1425         ca = (a) ? xstrdup(a) : NULL;
1426     }
1427     for (x = 0; ca[x] != '\0'; x++)
1428         ca[x] = xtolower(ca[x]);
1429
1430     if (co == NULL) {
1431         const char *o = NULL;
1432         defaultMachine(NULL, &o);
1433         co = (o) ? xstrdup(o) : NULL;
1434     }
1435     for (x = 0; co[x] != '\0'; x++)
1436         co[x] = xtolower(co[x]);
1437
1438     /* XXX For now, set canonical target to arch-os */
1439     if (ct == NULL) {
1440         ct = xmalloc(strlen(ca) + sizeof("-") + strlen(co));
1441         sprintf(ct, "%s-%s", ca, co);
1442     }
1443
1444 /*
1445  * XXX All this macro pokery/jiggery could be achieved by doing a delayed
1446  *      rpmInitMacros(NULL, PER-PLATFORM-MACRO-FILE-NAMES);
1447  */
1448     delMacro(NULL, "_target");
1449     addMacro(NULL, "_target", NULL, ct, RMIL_RPMRC);
1450     delMacro(NULL, "_target_cpu");
1451     addMacro(NULL, "_target_cpu", NULL, ca, RMIL_RPMRC);
1452     delMacro(NULL, "_target_os");
1453     addMacro(NULL, "_target_os", NULL, co, RMIL_RPMRC);
1454 /*
1455  * XXX Make sure that per-arch optflags is initialized correctly.
1456  */
1457   { const char *optflags = rpmGetVarArch(RPMVAR_OPTFLAGS, ca);
1458     if (optflags != NULL) {
1459         delMacro(NULL, "optflags");
1460         addMacro(NULL, "optflags", NULL, optflags, RMIL_RPMRC);
1461     }
1462   }
1463
1464     /*@-branchstate@*/
1465     if (canontarget)
1466         *canontarget = ct;
1467     else
1468         ct = _free(ct);
1469     /*@=branchstate@*/
1470     ca = _free(ca);
1471     /*@-usereleased@*/
1472     co = _free(co);
1473     /*@=usereleased@*/
1474 }
1475
1476 void rpmFreeRpmrc(void)
1477 {
1478     int i, j, k;
1479
1480     for (i = 0; i < RPM_MACHTABLE_COUNT; i++) {
1481         tableType t;
1482         t = tables + i;
1483         if (t->equiv.list) {
1484             for (j = 0; j < t->equiv.count; j++)
1485                 t->equiv.list[j].name = _free(t->equiv.list[j].name);
1486             t->equiv.list = _free(t->equiv.list);
1487             t->equiv.count = 0;
1488         }
1489         if (t->cache.cache) {
1490             for (j = 0; j < t->cache.size; j++) {
1491                 machCacheEntry e;
1492                 e = t->cache.cache + j;
1493                 if (e == NULL)
1494                     /*@innercontinue@*/ continue;
1495                 e->name = _free(e->name);
1496                 if (e->equivs) {
1497                     for (k = 0; k < e->count; k++)
1498                         e->equivs[k] = _free(e->equivs[k]);
1499                     e->equivs = _free(e->equivs);
1500                 }
1501             }
1502             t->cache.cache = _free(t->cache.cache);
1503             t->cache.size = 0;
1504         }
1505         if (t->defaults) {
1506             for (j = 0; j < t->defaultsLength; j++) {
1507                 t->defaults[j].name = _free(t->defaults[j].name);
1508                 t->defaults[j].defName = _free(t->defaults[j].defName);
1509             }
1510             t->defaults = _free(t->defaults);
1511             t->defaultsLength = 0;
1512         }
1513         if (t->canons) {
1514             for (j = 0; j < t->canonsLength; j++) {
1515                 t->canons[j].name = _free(t->canons[j].name);
1516                 t->canons[j].short_name = _free(t->canons[j].short_name);
1517             }
1518             t->canons = _free(t->canons);
1519             t->canonsLength = 0;
1520         }
1521     }
1522
1523     for (i = 0; i < RPMVAR_NUM; i++) {
1524         /*@only@*/ /*@null@*/ struct rpmvarValue * vp;
1525         while ((vp = values[i].next) != NULL) {
1526             values[i].next = vp->next;
1527             vp->value = _free(vp->value);
1528             vp->arch = _free(vp->arch);
1529             vp = _free(vp);
1530         }
1531         values[i].value = _free(values[i].value);
1532         values[i].arch = _free(values[i].arch);
1533     }
1534     current[OS] = _free(current[OS]);
1535     current[ARCH] = _free(current[ARCH]);
1536     defaultsInitialized = 0;
1537 /*@-nullstate@*/ /* FIX: current may be NULL */
1538     return;
1539 /*@=nullstate@*/
1540 }
1541
1542 /** \ingroup rpmrc
1543  * Read rpmrc (and macro) configuration file(s).
1544  * @param rcfiles       colon separated files to read (NULL uses default)
1545  * @return              0 on succes
1546  */
1547 static int rpmReadRC(/*@null@*/ const char * rcfiles)
1548         /*@globals rpmGlobalMacroContext, rpmCLIMacroContext,
1549                 fileSystem, internalState @*/
1550         /*@modifies rpmGlobalMacroContext,
1551                 fileSystem, internalState @*/
1552 {
1553     char *myrcfiles, *r, *re;
1554     int rc;
1555
1556     if (!defaultsInitialized) {
1557         setDefaults();
1558         defaultsInitialized = 1;
1559     }
1560
1561     if (rcfiles == NULL)
1562         rcfiles = defrcfiles;
1563
1564     /* Read each file in rcfiles. */
1565     rc = 0;
1566     for (r = myrcfiles = xstrdup(rcfiles); r && *r != '\0'; r = re) {
1567         char fn[4096];
1568         FD_t fd;
1569
1570         /* Get pointer to rest of files */
1571         for (re = r; (re = strchr(re, ':')) != NULL; re++) {
1572             if (!(re[1] == '/' && re[2] == '/'))
1573                 /*@innerbreak@*/ break;
1574         }
1575         if (re && *re == ':')
1576             *re++ = '\0';
1577         else
1578             re = r + strlen(r);
1579
1580         /* Expand ~/ to $HOME/ */
1581         fn[0] = '\0';
1582         if (r[0] == '~' && r[1] == '/') {
1583             const char * home = getenv("HOME");
1584             if (home == NULL) {
1585             /* XXX Only /usr/lib/rpm/rpmrc must exist in default rcfiles list */
1586                 if (rcfiles == defrcfiles && myrcfiles != r)
1587                     continue;
1588                 rpmError(RPMERR_RPMRC, _("Cannot expand %s\n"), r);
1589                 rc = 1;
1590                 break;
1591             }
1592             if (strlen(home) > (sizeof(fn) - strlen(r))) {
1593                 rpmError(RPMERR_RPMRC, _("Cannot read %s, HOME is too large.\n"),
1594                                 r);
1595                 rc = 1;
1596                 break;
1597             }
1598             strcpy(fn, home);
1599             r++;
1600         }
1601         strncat(fn, r, sizeof(fn) - (strlen(fn) + 1));
1602         fn[sizeof(fn)-1] = '\0';
1603
1604         /* Read another rcfile */
1605         fd = Fopen(fn, "r.fpio");
1606         if (fd == NULL || Ferror(fd)) {
1607             /* XXX Only /usr/lib/rpm/rpmrc must exist in default rcfiles list */
1608             if (rcfiles == defrcfiles && myrcfiles != r)
1609                 continue;
1610             rpmError(RPMERR_RPMRC, _("Unable to open %s for reading: %s.\n"),
1611                  fn, Fstrerror(fd));
1612             rc = 1;
1613             break;
1614         } else {
1615             rc = doReadRC(fd, fn);
1616         }
1617         if (rc) break;
1618     }
1619     myrcfiles = _free(myrcfiles);
1620     if (rc)
1621         return rc;
1622
1623     rpmSetMachine(NULL, NULL);  /* XXX WTFO? Why bother? */
1624
1625     {   const char *mfpath;
1626         /*@-branchstate@*/
1627         if ((mfpath = rpmGetVar(RPMVAR_MACROFILES)) != NULL) {
1628             mfpath = xstrdup(mfpath);
1629             rpmInitMacros(NULL, mfpath);
1630             mfpath = _free(mfpath);
1631         }
1632         /*@=branchstate@*/
1633     }
1634
1635     return rc;
1636 }
1637
1638 int rpmReadConfigFiles(const char * file, const char * target)
1639 {
1640
1641     /* Preset target macros */
1642     /*@-nullstate@*/    /* FIX: target can be NULL */
1643     rpmRebuildTargetVars(&target, NULL);
1644
1645     /* Read the files */
1646     if (rpmReadRC(file)) return -1;
1647
1648     /* Reset target macros */
1649     rpmRebuildTargetVars(&target, NULL);
1650     /*@=nullstate@*/
1651
1652     /* Finally set target platform */
1653     {   const char *cpu = rpmExpand("%{_target_cpu}", NULL);
1654         const char *os = rpmExpand("%{_target_os}", NULL);
1655         rpmSetMachine(cpu, os);
1656         cpu = _free(cpu);
1657         os = _free(os);
1658     }
1659
1660     return 0;
1661 }
1662
1663 int rpmShowRC(FILE * fp)
1664 {
1665     struct rpmOption *opt;
1666     int i;
1667     machEquivTable equivTable;
1668
1669     /* the caller may set the build arch which should be printed here */
1670     fprintf(fp, "ARCHITECTURE AND OS:\n");
1671     fprintf(fp, "build arch            : %s\n", current[ARCH]);
1672
1673     fprintf(fp, "compatible build archs:");
1674     equivTable = &tables[RPM_MACHTABLE_BUILDARCH].equiv;
1675     for (i = 0; i < equivTable->count; i++)
1676         fprintf(fp," %s", equivTable->list[i].name);
1677     fprintf(fp, "\n");
1678
1679     fprintf(fp, "build os              : %s\n", current[OS]);
1680
1681     fprintf(fp, "compatible build os's :");
1682     equivTable = &tables[RPM_MACHTABLE_BUILDOS].equiv;
1683     for (i = 0; i < equivTable->count; i++)
1684         fprintf(fp," %s", equivTable->list[i].name);
1685     fprintf(fp, "\n");
1686
1687     rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS);
1688     rpmSetMachine(NULL, NULL);  /* XXX WTFO? Why bother? */
1689
1690     fprintf(fp, "install arch          : %s\n", current[ARCH]);
1691     fprintf(fp, "install os            : %s\n", current[OS]);
1692
1693     fprintf(fp, "compatible archs      :");
1694     equivTable = &tables[RPM_MACHTABLE_INSTARCH].equiv;
1695     for (i = 0; i < equivTable->count; i++)
1696         fprintf(fp," %s", equivTable->list[i].name);
1697     fprintf(fp, "\n");
1698
1699     fprintf(fp, "compatible os's       :");
1700     equivTable = &tables[RPM_MACHTABLE_INSTOS].equiv;
1701     for (i = 0; i < equivTable->count; i++)
1702         fprintf(fp," %s", equivTable->list[i].name);
1703     fprintf(fp, "\n");
1704
1705     fprintf(fp, "\nRPMRC VALUES:\n");
1706     for (i = 0, opt = optionTable; i < optionTableSize; i++, opt++) {
1707         const char *s = rpmGetVar(opt->var);
1708         if (s != NULL || rpmIsVerbose())
1709             fprintf(fp, "%-21s : %s\n", opt->name, s ? s : "(not set)");
1710     }
1711     fprintf(fp, "\n");
1712
1713     fprintf(fp, "Features supported by rpmlib:\n");
1714     rpmShowRpmlibProvides(fp);
1715     fprintf(fp, "\n");
1716
1717     rpmDumpMacroTable(NULL, fp);
1718
1719     return 0;
1720 }
1721 /*@=mods@*/