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