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