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