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