lclint annotations.
[platform/upstream/rpm.git] / lib / rpmrc.c
1 #include "system.h"
2
3 #include <stdarg.h>
4
5 #if HAVE_SYS_SYSTEMCFG_H
6 #include <sys/systemcfg.h>
7 #else
8 #define __power_pc() 0
9 #endif
10
11 #include <rpmlib.h>
12 #include <rpmmacro.h>
13
14 #include "misc.h"
15
16 static const char *defrcfiles = LIBRPMRC_FILENAME ":/etc/rpmrc:~/.rpmrc";
17
18 struct machCacheEntry {
19     char * name;
20     int count;
21     char ** equivs;
22     int visited;
23 };
24
25 struct machCache {
26     struct machCacheEntry * cache;
27     int size;
28 };
29
30 struct machEquivInfo {
31     char * name;
32     int score;
33 };
34
35 struct machEquivTable {
36     int count;
37     struct machEquivInfo * list;
38 };
39
40 struct rpmvarValue {
41     char * value;
42     /* eventually, this arch will be replaced with a generic condition */
43     char * arch;
44     struct rpmvarValue * next;
45 };
46
47 struct rpmOption {
48     char * name;
49     int var;
50     int archSpecific, required, macroize, localize;
51     struct rpmOptionValue * value;
52 };
53
54 struct defaultEntry {
55     char *name;
56     char *defName;
57 };
58
59 struct canonEntry {
60     char *name;
61     char *short_name;
62     short num;
63 };
64
65 /* tags are 'key'canon, 'key'translate, 'key'compat
66
67    for giggles, 'key'_canon, 'key'_compat, and 'key'_canon will also work */
68 struct tableType {
69     char * key;
70     int hasCanon, hasTranslate;
71     struct machEquivTable equiv;
72     struct machCache cache;
73     struct defaultEntry * defaults;
74     struct canonEntry * canons;
75     int defaultsLength;
76     int canonsLength;
77 };
78
79 /*@-fullinitblock@*/
80 static struct tableType tables[RPM_MACHTABLE_COUNT] = {
81     { "arch", 1, 0 },
82     { "os", 1, 0 },
83     { "buildarch", 0, 1 },
84     { "buildos", 0, 1 }
85 };
86
87 /* this *must* be kept in alphabetical order */
88 /* The order of the flags is archSpecific, required, macroize, localize */
89
90 static struct rpmOption optionTable[] = {
91     { "include",                RPMVAR_INCLUDE,                 0, 1,   0, 2 },
92     { "macrofiles",             RPMVAR_MACROFILES,              0, 0,   0, 1 },
93     { "optflags",               RPMVAR_OPTFLAGS,                1, 0,   1, 0 },
94     { "provides",               RPMVAR_PROVIDES,                0, 0,   0, 0 },
95 };
96 /*@=fullinitblock@*/
97 static int optionTableSize = sizeof(optionTable) / sizeof(*optionTable);
98
99 #define OS      0
100 #define ARCH    1
101
102 static char * current[2];
103 static int currTables[2] = { RPM_MACHTABLE_INSTOS, RPM_MACHTABLE_INSTARCH };
104 static struct rpmvarValue values[RPMVAR_NUM];
105
106 /* prototypes */
107 static void defaultMachine(/*@out@*/char ** arch, /*@out@*/char ** os);
108 static int doReadRC(FD_t fd, const char * filename);
109 static int optionCompare(const void * a, const void * b);
110 static int addCanon(struct canonEntry **table, int *tableLen, char *line,
111                         const char *fn, int lineNum);
112 static int addDefault(struct defaultEntry **table, int *tableLen, char *line,
113                         const char *fn, int lineNum);
114 static void freeRpmVar(struct rpmvarValue * orig);
115 static void rpmSetVarArch(int var, const char * val, const char * arch);
116 static struct canonEntry *lookupInCanonTable(char *name,
117                                            struct canonEntry *table,
118                                            int tableLen);
119 static const char *lookupInDefaultTable(const char *name,
120                                   struct defaultEntry *table,
121                                   int tableLen);
122
123 static void setVarDefault(int var, const char *macroname, const char *val, const char *body);
124 static void setPathDefault(int var, const char *macroname, const char *subdir);
125 static void setDefaults(void);
126
127 static void rebuildCompatTables(int type, char *name);
128
129 /* compatiblity tables */
130 static int machCompatCacheAdd(char * name, const char * fn, int linenum,
131                                 struct machCache * cache);
132 static struct machCacheEntry * machCacheFindEntry(struct machCache * cache,
133                                                   char * key);
134 static struct machEquivInfo * machEquivSearch(
135                 struct machEquivTable * table, char * name);
136 static void machAddEquiv(struct machEquivTable * table, char * name,
137                            int distance);
138 static void machCacheEntryVisit(struct machCache * cache,
139                                   struct machEquivTable * table,
140                                   char * name,
141                                   int distance);
142 static void machFindEquivs(struct machCache * cache,
143                              struct machEquivTable * table,
144                              char * key);
145
146 static int optionCompare(const void * a, const void * b) {
147     return strcasecmp(((struct rpmOption *) a)->name,
148                       ((struct rpmOption *) b)->name);
149 }
150
151 static void rpmRebuildTargetVars(const char **target, const char ** canontarget);
152
153
154 static struct machCacheEntry * machCacheFindEntry(struct machCache * cache,
155                                                   char * key)
156 {
157     int i;
158
159     for (i = 0; i < cache->size; i++)
160         if (!strcmp(cache->cache[i].name, key)) return cache->cache + i;
161
162     return NULL;
163 }
164
165 static int machCompatCacheAdd(char * name, const char * fn, int linenum,
166                                 struct machCache * cache)
167 {
168     char * chptr, * equivs;
169     int delEntry = 0;
170     int i;
171     struct machCacheEntry * entry = NULL;
172
173     while (*name && isspace(*name)) name++;
174
175     chptr = name;
176     while (*chptr && *chptr != ':') chptr++;
177     if (!*chptr) {
178         rpmError(RPMERR_RPMRC, _("missing second ':' at %s:%d"), fn, linenum);
179         return 1;
180     } else if (chptr == name) {
181         rpmError(RPMERR_RPMRC, _("missing architecture name at %s:%d"), fn,
182                              linenum);
183         return 1;
184     }
185
186     while (*chptr == ':' || isspace(*chptr)) chptr--;
187     *(++chptr) = '\0';
188     equivs = chptr + 1;
189     while (*equivs && isspace(*equivs)) equivs++;
190     if (!*equivs) {
191         delEntry = 1;
192     }
193
194     if (cache->size) {
195         entry = machCacheFindEntry(cache, name);
196         if (entry) {
197             for (i = 0; i < entry->count; i++)
198                 free(entry->equivs[i]);
199             if (entry->count) free(entry->equivs);
200             entry->count = 0;
201         }
202     }
203
204     if (!entry) {
205         cache->cache = realloc(cache->cache,
206                                (cache->size + 1) * sizeof(*cache->cache));
207         entry = cache->cache + cache->size++;
208         entry->name = strdup(name);
209         entry->count = 0;
210         entry->visited = 0;
211     }
212
213     if (delEntry) return 0;
214
215     while ((chptr = strtok(equivs, " ")) != NULL) {
216         equivs = NULL;
217         if (chptr[0] == '\0')   /* does strtok() return "" ever?? */
218             continue;
219         if (entry->count)
220             entry->equivs = realloc(entry->equivs, sizeof(*entry->equivs)
221                                         * (entry->count + 1));
222         else
223             entry->equivs = malloc(sizeof(*entry->equivs));
224
225         entry->equivs[entry->count] = strdup(chptr);
226         entry->count++;
227     }
228
229     return 0;
230 }
231
232 static struct machEquivInfo * machEquivSearch(
233                 struct machEquivTable * table, char * name)
234 {
235     int i;
236
237 /*
238  * XXX The strcasecmp below is necessary so the old (rpm < 2.90) style
239  * XXX os-from-uname (e.g. "Linux") is compatible with the new
240  * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
241  * XXX A copy of this string is embedded in headers and is
242  * XXX used by rpmInstallPackage->{os,arch}Okay->rpmMachineScore->.
243  * XXX to verify correct arch/os from headers.
244  */
245     for (i = 0; i < table->count; i++)
246         if (!strcasecmp(table->list[i].name, name))
247             return table->list + i;
248
249     return NULL;
250 }
251
252 static void machAddEquiv(struct machEquivTable * table, char * name,
253                            int distance)
254 {
255     struct machEquivInfo * equiv;
256
257     equiv = machEquivSearch(table, name);
258     if (!equiv) {
259         if (table->count)
260             table->list = realloc(table->list, (table->count + 1)
261                                     * sizeof(*table->list));
262         else
263             table->list = malloc(sizeof(*table->list));
264
265         table->list[table->count].name = strdup(name);
266         table->list[table->count++].score = distance;
267     }
268 }
269
270 static void machCacheEntryVisit(struct machCache * cache,
271                                   struct machEquivTable * table,
272                                   char * name,
273                                   int distance)
274 {
275     struct machCacheEntry * entry;
276     int i;
277
278     entry = machCacheFindEntry(cache, name);
279     if (!entry || entry->visited) return;
280
281     entry->visited = 1;
282
283     for (i = 0; i < entry->count; i++) {
284         machAddEquiv(table, entry->equivs[i], distance);
285     }
286
287     for (i = 0; i < entry->count; i++) {
288         machCacheEntryVisit(cache, table, entry->equivs[i], distance + 1);
289     }
290 }
291
292 static void machFindEquivs(struct machCache * cache,
293                              struct machEquivTable * table,
294                              char * key)
295 {
296     int i;
297
298     for (i = 0; i < cache->size; i++)
299         cache->cache[i].visited = 0;
300
301     table->count = 0;
302
303     /* We have a general graph built using strings instead of pointers.
304        Yuck. We have to start at a point at traverse it, remembering how
305        far away everything is. */
306     machAddEquiv(table, key, 1);
307     machCacheEntryVisit(cache, table, key, 2);
308 }
309
310 static int addCanon(struct canonEntry **table, int *tableLen, char *line,
311                     const char *fn, int lineNum)
312 {
313     struct canonEntry *t;
314     char *s, *s1;
315
316     if (! *tableLen) {
317         *tableLen = 2;
318         *table = malloc(2 * sizeof(struct canonEntry));
319     } else {
320         (*tableLen) += 2;
321         *table = realloc(*table, sizeof(struct canonEntry) * (*tableLen));
322     }
323     t = & ((*table)[*tableLen - 2]);
324
325     t->name = strtok(line, ": \t");
326     t->short_name = strtok(NULL, " \t");
327     s = strtok(NULL, " \t");
328     if (! (t->name && t->short_name && s)) {
329         rpmError(RPMERR_RPMRC, _("Incomplete data line at %s:%d"), fn, lineNum);
330         return RPMERR_RPMRC;
331     }
332     if (strtok(NULL, " \t")) {
333         rpmError(RPMERR_RPMRC, _("Too many args in data line at %s:%d"),
334               fn, lineNum);
335         return RPMERR_RPMRC;
336     }
337
338     t->num = strtoul(s, &s1, 10);
339     if ((*s1) || (s1 == s) || (t->num == ULONG_MAX)) {
340         rpmError(RPMERR_RPMRC, _("Bad arch/os number: %s (%s:%d)"), s,
341               fn, lineNum);
342         return(RPMERR_RPMRC);
343     }
344
345     t->name = strdup(t->name);
346     t->short_name = strdup(t->short_name);
347
348     /* From A B C entry */
349     /* Add  B B C entry */
350     t[1].name = strdup(t->short_name);
351     t[1].short_name = strdup(t->short_name);
352     t[1].num = t->num;
353
354     return 0;
355 }
356
357 static int addDefault(struct defaultEntry **table, int *tableLen, char *line,
358                         const char *fn, int lineNum)
359 {
360     struct defaultEntry *t;
361
362     if (! *tableLen) {
363         *tableLen = 1;
364         *table = malloc(sizeof(struct defaultEntry));
365     } else {
366         (*tableLen)++;
367         *table = realloc(*table, sizeof(struct defaultEntry) * (*tableLen));
368     }
369     t = & ((*table)[*tableLen - 1]);
370
371     t->name = strtok(line, ": \t");
372     t->defName = strtok(NULL, " \t");
373     if (! (t->name && t->defName)) {
374         rpmError(RPMERR_RPMRC, _("Incomplete default line at %s:%d"),
375                  fn, lineNum);
376         return RPMERR_RPMRC;
377     }
378     if (strtok(NULL, " \t")) {
379         rpmError(RPMERR_RPMRC, _("Too many args in default line at %s:%d"),
380               fn, lineNum);
381         return RPMERR_RPMRC;
382     }
383
384     t->name = strdup(t->name);
385     t->defName = strdup(t->defName);
386
387     return 0;
388 }
389
390 static struct canonEntry *lookupInCanonTable(char *name,
391                                              struct canonEntry *table,
392                                              int tableLen) {
393     while (tableLen) {
394         tableLen--;
395         if (!strcmp(name, table[tableLen].name)) {
396             return &(table[tableLen]);
397         }
398     }
399
400     return NULL;
401 }
402
403 static const char *lookupInDefaultTable(const char *name, struct defaultEntry *table,
404                                   int tableLen) {
405     while (tableLen) {
406         tableLen--;
407         if (!strcmp(name, table[tableLen].name)) {
408             return table[tableLen].defName;
409         }
410     }
411
412     return name;
413 }
414
415 int rpmReadConfigFiles(const char * file, const char * target)
416 {
417
418     /* Preset target macros */
419     rpmRebuildTargetVars(&target, NULL);
420
421     /* Read the files */
422     if (rpmReadRC(file)) return -1;
423
424     /* Reset target macros */
425     rpmRebuildTargetVars(&target, NULL);
426
427     /* Finally set target platform */
428     {   const char *cpu = rpmExpand("%{_target_cpu}", NULL);
429         const char *os = rpmExpand("%{_target_os}", NULL);
430         rpmSetMachine(cpu, os);
431         xfree(cpu);
432         xfree(os);
433     }
434
435     return 0;
436 }
437
438 static void setVarDefault(int var, const char *macroname, const char *val, const char *body)
439 {
440     if (var >= 0) {     /* XXX Dying ... */
441         if (rpmGetVar(var)) return;
442         rpmSetVar(var, val);
443     }
444     if (body == NULL)
445         body = val;
446     addMacro(NULL, macroname, NULL, body, RMIL_DEFAULT);
447 }
448
449 static void setPathDefault(int var, const char *macroname, const char *subdir)
450 {
451
452     if (var >= 0) {     /* XXX Dying ... */
453         const char * topdir;
454         char * fn;
455
456         if (rpmGetVar(var)) return;
457
458         topdir = rpmGetPath("%{_topdir}", NULL);
459
460         fn = alloca(strlen(topdir) + strlen(subdir) + 2);
461         strcpy(fn, topdir);
462         if (fn[strlen(topdir) - 1] != '/')
463             strcat(fn, "/");
464         strcat(fn, subdir);
465
466         rpmSetVar(var, fn);
467         if (topdir)     xfree(topdir);
468     }
469
470     if (macroname != NULL) {
471 #define _TOPDIRMACRO    "%{_topdir}/"
472         char *body = alloca(sizeof(_TOPDIRMACRO) + strlen(subdir));
473         strcpy(body, _TOPDIRMACRO);
474         strcat(body, subdir);
475         addMacro(NULL, macroname, NULL, body, RMIL_DEFAULT);
476 #undef _TOPDIRMACRO
477     }
478 }
479
480 static const char *prescriptenviron = "\n\
481 RPM_SOURCE_DIR=\"%{_sourcedir}\"\n\
482 RPM_BUILD_DIR=\"%{_builddir}\"\n\
483 RPM_OPT_FLAGS=\"%{optflags}\"\n\
484 RPM_ARCH=\"%{_arch}\"\n\
485 RPM_OS=\"%{_os}\"\n\
486 export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\n\
487 RPM_DOC_DIR=\"%{_docdir}\"\n\
488 export RPM_DOC_DIR\n\
489 RPM_PACKAGE_NAME=\"%{name}\"\n\
490 RPM_PACKAGE_VERSION=\"%{version}\"\n\
491 RPM_PACKAGE_RELEASE=\"%{release}\"\n\
492 export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE\n\
493 %{?buildroot:RPM_BUILD_ROOT=\"%{buildroot}\"\n\
494 export RPM_BUILD_ROOT\n}\
495 ";
496
497 static void setDefaults(void) {
498
499     addMacro(NULL, "_usr", NULL, "/usr", RMIL_DEFAULT);
500     addMacro(NULL, "_var", NULL, "/var", RMIL_DEFAULT);
501
502     addMacro(NULL, "_preScriptEnvironment",NULL, prescriptenviron,RMIL_DEFAULT);
503
504     setVarDefault(-1,                   "_topdir",
505                 "/usr/src/redhat",      "%{_usr}/src/redhat");
506     setVarDefault(-1,                   "_tmppath",
507                 "/var/tmp",             "%{_var}/tmp");
508     setVarDefault(-1,                   "_dbpath",
509                 "/var/lib/rpm",         "%{_var}/lib/rpm");
510     setVarDefault(-1,                   "_defaultdocdir",
511                 "/usr/doc",             "%{_usr}/doc");
512
513     setVarDefault(-1,                   "_rpmfilename",
514         "%%{ARCH}/%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm",NULL);
515
516     setVarDefault(RPMVAR_OPTFLAGS,      "optflags",
517                 "-O2",                  NULL);
518     setVarDefault(-1,                   "sigtype",
519                 "none",                 NULL);
520     setVarDefault(-1,                   "_buildshell",
521                 "/bin/sh",              NULL);
522
523     setPathDefault(-1,                  "_builddir",    "BUILD");
524     setPathDefault(-1,                  "_rpmdir",      "RPMS");
525     setPathDefault(-1,                  "_srcrpmdir",   "SRPMS");
526     setPathDefault(-1,                  "_sourcedir",   "SOURCES");
527     setPathDefault(-1,                  "_specdir",     "SPECS");
528
529 }
530
531 int rpmReadRC(const char * rcfiles)
532 {
533     char *myrcfiles, *r, *re;
534     int rc;
535     static int first = 1;
536
537     if (first) {
538         setDefaults();
539         first = 0;
540     }
541
542     if (rcfiles == NULL)
543         rcfiles = defrcfiles;
544
545     /* Read each file in rcfiles. */
546     rc = 0;
547     for (r = myrcfiles = strdup(rcfiles); *r != '\0'; r = re) {
548         char fn[4096];
549         FD_t fd;
550
551         /* Get pointer to rest of files */
552         if ((re = strchr(r, ':')) != NULL)
553             *re++ = '\0';
554         else
555             re = r + strlen(r);
556
557         /* Expand ~/ to $HOME/ */
558         fn[0] = '\0';
559         if (r[0] == '~' && r[1] == '/') {
560             char *home = getenv("HOME");
561             if (home == NULL) {
562                 rpmError(RPMERR_RPMRC, _("Cannot expand %s"), r);
563                 rc = 1;
564                 break;
565             }
566             strcpy(fn, home);
567             r++;
568         }
569         strcat(fn, r);
570
571         /* Read another rcfile */
572         fd = fdOpen(fn, O_RDONLY, 0);
573         if (fdFileno(fd) < 0) {
574             /* XXX Only /usr/lib/rpm/rpmrc must exist in default rcfiles list */
575             if (rcfiles == defrcfiles && myrcfiles != r)
576                 continue;
577             rpmError(RPMERR_RPMRC, _("Unable to open %s for reading: %s."),
578                  fn, strerror(errno));
579             rc = 1;
580             break;
581         }
582         rc = doReadRC(fd, fn);
583         fdClose(fd);
584         if (rc) break;
585     }
586     if (myrcfiles)      free(myrcfiles);
587     if (rc)
588         return rc;
589
590     rpmSetMachine(NULL, NULL);  /* XXX WTFO? Why bother? */
591
592     if ((r = rpmGetVar(RPMVAR_MACROFILES)) != NULL)
593         initMacros(NULL, r);
594
595     return rc;
596 }
597
598 static int doReadRC(FD_t fd, const char * filename)
599 {
600     char *s, *se, *next;
601     int linenum = 0;
602     struct rpmOption searchOption, * option;
603     int rc;
604
605   { struct stat sb;
606     fstat(fdFileno(fd), &sb);
607     next = alloca(sb.st_size + 2);
608     if (fdRead(fd, next, sb.st_size) != sb.st_size) {
609         rpmError(RPMERR_RPMRC, _("Failed to read %s: %s."), filename,
610                  strerror(errno));
611         return 1;
612     }
613     next[sb.st_size] = '\n';
614     next[sb.st_size + 1] = '\0';
615   }
616
617     while (*next) {
618         linenum++;
619
620         se = s = next;
621         while (*se && *se != '\n') se++;
622         if (*se) *se++ = '\0';
623         next = se;
624
625         while (*s && isspace(*s)) s++;
626
627         /* we used to allow comments to begin anywhere, but not anymore */
628         if (*s == '#' || *s == '\0') continue;
629
630         se = s;
631         while (*se && !isspace(*se) && *se != ':') se++;
632
633         if (isspace(*se)) {
634             *se++ = '\0';
635             while (*se && isspace(*se) && *se != ':') se++;
636         }
637
638         if (*se != ':') {
639             rpmError(RPMERR_RPMRC, _("missing ':' at %s:%d"),
640                      filename, linenum);
641             return 1;
642         }
643         *se++ = '\0';
644         while (*se && isspace(*se)) se++;
645
646         /* Find keyword in table */
647         searchOption.name = s;
648         option = bsearch(&searchOption, optionTable, optionTableSize,
649                          sizeof(struct rpmOption), optionCompare);
650
651         if (option) {   /* For configuration variables  ... */
652             const char *arch, *val, *fn;
653
654             arch = val = fn = NULL;
655             if (*se == '\0') {
656                 rpmError(RPMERR_RPMRC, _("missing argument for %s at %s:%d"),
657                       option->name, filename, linenum);
658                 return 1;
659             }
660
661             switch (option->var) {
662             case RPMVAR_INCLUDE:
663               { FD_t fdinc;
664
665                 s = se;
666                 while (*se && !isspace(*se)) se++;
667                 if (*se) *se++ = '\0';
668
669                 rpmRebuildTargetVars(NULL, NULL);
670
671                 fn = rpmGetPath(s, NULL);
672                 if (fn == NULL || *fn == '\0') {
673                     rpmError(RPMERR_RPMRC, _("%s expansion failed at %s:%d \"%s\""),
674                         option->name, filename, linenum, s);
675                     if (fn) xfree(fn);
676                     return 1;
677                 }
678
679                 fdinc = fdOpen(fn, O_RDONLY, 0);
680                 if (fdFileno(fdinc) < 0) {
681                     rpmError(RPMERR_RPMRC, _("cannot open %s at %s:%d"),
682                         fn, filename, linenum);
683                     rc = 1;
684                 } else {
685                     rc = doReadRC(fdinc, fn);
686                     fdClose(fdinc);
687                 }
688                 if (fn) xfree(fn);
689                 if (rc) return rc;
690                 continue;       /* XXX don't save include value as var/macro */
691               } break;
692             case RPMVAR_MACROFILES:
693                 fn = rpmGetPath(se, NULL);
694                 if (fn == NULL || *fn == '\0') {
695                     rpmError(RPMERR_RPMRC, _("%s expansion failed at %s:%d \"%s\""),
696                         option->name, filename, linenum, fn);
697                     if (fn) xfree(fn);
698                     return 1;
699                 }
700                 se = (char *)fn;
701                 break;
702             case RPMVAR_PROVIDES:
703               { char *t;
704                 s = rpmGetVar(RPMVAR_PROVIDES);
705                 if (s == NULL) s = "";
706                 fn = t = malloc(strlen(s) + strlen(se) + 2);
707                 while (*s) *t++ = *s++;
708                 *t++ = ' ';
709                 while (*se) *t++ = *se++;
710                 *t++ = '\0';
711                 se = (char *)fn;
712               } break;
713             default:
714                 break;
715             }
716
717             if (option->archSpecific) {
718                 arch = se;
719                 while (*se && !isspace(*se)) se++;
720                 if (*se == '\0') {
721                     rpmError(RPMERR_RPMRC,
722                                 _("missing architecture for %s at %s:%d"),
723                                 option->name, filename, linenum);
724                     return 1;
725                 }
726                 *se++ = '\0';
727                 while (*se && isspace(*se)) se++;
728                 if (*se == '\0') {
729                     rpmError(RPMERR_RPMRC,
730                                 _("missing argument for %s at %s:%d"),
731                                 option->name, filename, linenum);
732                     return 1;
733                 }
734             }
735         
736             val = se;
737
738             /* Only add macros if appropriate for this arch */
739             if (option->macroize &&
740               (arch == NULL || !strcmp(arch, current[ARCH]))) {
741                 char *n, *name;
742                 n = name = malloc(strlen(option->name)+2);
743                 if (option->localize)
744                     *n++ = '_';
745                 strcpy(n, option->name);
746                 addMacro(NULL, name, NULL, val, RMIL_RPMRC);
747                 free(name);
748             }
749             rpmSetVarArch(option->var, val, arch);
750             if (fn) xfree(fn);
751
752         } else {        /* For arch/os compatibilty tables ... */
753             int gotit;
754             int i;
755
756             gotit = 0;
757
758             for (i = 0; i < RPM_MACHTABLE_COUNT; i++) {
759                 if (!strncmp(tables[i].key, s, strlen(tables[i].key)))
760                     break;
761             }
762
763             if (i < RPM_MACHTABLE_COUNT) {
764                 char *rest = s + strlen(tables[i].key);
765                 if (*rest == '_') rest++;
766
767                 if (!strcmp(rest, "compat")) {
768                     if (machCompatCacheAdd(se, filename, linenum,
769                                                 &tables[i].cache))
770                         return 1;
771                     gotit = 1;
772                 } else if (tables[i].hasTranslate &&
773                            !strcmp(rest, "translate")) {
774                     if (addDefault(&tables[i].defaults,
775                                    &tables[i].defaultsLength,
776                                    se, filename, linenum))
777                         return 1;
778                     gotit = 1;
779                 } else if (tables[i].hasCanon &&
780                            !strcmp(rest, "canon")) {
781                     if (addCanon(&tables[i].canons, &tables[i].canonsLength,
782                                  se, filename, linenum))
783                         return 1;
784                     gotit = 1;
785                 }
786             }
787
788             if (!gotit) {
789                 rpmError(RPMERR_RPMRC, _("bad option '%s' at %s:%d"),
790                             s, filename, linenum);
791             }
792         }
793     }
794
795     return 0;
796 }
797
798 static void defaultMachine(char ** arch, char ** os) {
799     static struct utsname un;
800     static int gotDefaults = 0;
801     char * chptr;
802     struct canonEntry * canon;
803
804     if (!gotDefaults) {
805         uname(&un);
806
807 #if !defined(__linux__)
808 #ifdef SNI
809         /* USUALLY un.sysname on sinix does start with the word "SINIX"
810          * let's be absolutely sure
811          */
812         sprintf(un.sysname,"SINIX");
813 #endif
814         if (!strcmp(un.sysname, "AIX")) {
815             strcpy(un.machine, __power_pc() ? "ppc" : "rs6000");
816             sprintf(un.sysname,"aix%s.%s",un.version,un.release);
817         }
818         else if (!strcmp(un.sysname, "SunOS")) {
819            if (!strncmp(un.release,"4", 1)) /* SunOS 4.x */ {
820               int fd;
821               for (fd=0;(un.release[fd] != 0 && (fd < sizeof(un.release)));fd++)
822                  if (!isdigit(un.release[fd]) && (un.release[fd] != '.')) {
823                     un.release[fd] = 0;
824                     break;
825                  }
826               sprintf(un.sysname,"sunos%s",un.release);
827            }
828
829            else /* Solaris 2.x: n.x.x becomes n-3.x.x */
830               sprintf(un.sysname,"solaris%1d%s",atoi(un.release)-3,un.release+1+(atoi(un.release)/10));
831         }
832         else if (!strcmp(un.sysname, "HP-UX"))
833            /*make un.sysname look like hpux9.05 for example*/
834            sprintf(un.sysname,"hpux%s",strpbrk(un.release,"123456789"));
835         else if (!strcmp(un.sysname, "OSF1"))
836            /*make un.sysname look like osf3.2 for example*/
837            sprintf(un.sysname,"osf%s",strpbrk(un.release,"123456789"));
838         else if (!strncmp(un.sysname, "IP", 2))
839            un.sysname[2] = '\0';
840         else if (!strncmp(un.sysname, "SINIX", 5)) {
841            sprintf(un.sysname, "sinix%s",un.release);
842            if (!strncmp(un.machine, "RM", 2))
843               sprintf(un.machine, "mips");
844         }
845         else if ((!strncmp(un.machine, "34", 2) || \
846                  !strncmp(un.machine, "33", 2)) && \
847                  !strncmp(un.release, "4.0", 3)) {
848            /* we are on ncr-sysv4 */
849            char *prelid = NULL;
850            FD_t fd = fdOpen("/etc/.relid", O_RDONLY, 0700);
851            if (fdFileno(fd) > 0) {
852               chptr = (char *) calloc(256,1);
853               if (chptr != NULL) {
854                  int irelid = fdRead(fd, (void *)chptr, 256);
855                  fdClose(fd);
856                  /* example: "112393 RELEASE 020200 Version 01 OS" */
857                  if (irelid > 0) {
858                     if ((prelid=strstr(chptr, "RELEASE "))){
859                        prelid += strlen("RELEASE ")+1;
860                        sprintf(un.sysname,"ncr-sysv4.%.*s",1,prelid);
861                     }
862                  }
863                  free (chptr);
864               }
865            }
866            if (!prelid)
867               /* parsing /etc/.relid file failed */
868               strcpy(un.sysname,"ncr-sysv4");
869            /* wrong, just for now, find out how to look for i586 later*/
870            strcpy(un.machine,"i486");
871         }
872 #endif  /* __linux__ */
873
874         /* get rid of the hyphens in the sysname */
875         for (chptr = un.machine; *chptr; chptr++)
876             if (*chptr == '/') *chptr = '-';
877
878 #       if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL)
879             /* little endian */
880             strcpy(un.machine, "mipsel");
881 #       elif defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB)
882            /* big endian */
883                 strcpy(un.machine, "mipseb");
884 #       endif
885
886 #       if defined(__hpux) && defined(_SC_CPU_VERSION)
887         {
888 #           if !defined(CPU_PA_RISC1_2)
889 #                define CPU_PA_RISC1_2  0x211 /* HP PA-RISC1.2 */
890 #           endif
891 #           if !defined(CPU_PA_RISC2_0)
892 #               define CPU_PA_RISC2_0  0x214 /* HP PA-RISC2.0 */
893 #           endif
894             int cpu_version = sysconf(_SC_CPU_VERSION);
895
896 #           if defined(CPU_HP_MC68020)
897                 if (cpu_version == CPU_HP_MC68020)
898                     strcpy(un.machine, "m68k");
899 #           endif
900 #           if defined(CPU_HP_MC68030)
901                 if (cpu_version == CPU_HP_MC68030)
902                     strcpy(un.machine, "m68k");
903 #           endif
904 #           if defined(CPU_HP_MC68040)
905                 if (cpu_version == CPU_HP_MC68040)
906                     strcpy(un.machine, "m68k");
907 #           endif
908
909 #           if defined(CPU_PA_RISC1_0)
910                 if (cpu_version == CPU_PA_RISC1_0)
911                     strcpy(un.machine, "hppa1.0");
912 #           endif
913 #           if defined(CPU_PA_RISC1_1)
914                 if (cpu_version == CPU_PA_RISC1_1)
915                     strcpy(un.machine, "hppa1.1");
916 #           endif
917 #           if defined(CPU_PA_RISC1_2)
918                 if (cpu_version == CPU_PA_RISC1_2)
919                     strcpy(un.machine, "hppa1.2");
920 #           endif
921 #           if defined(CPU_PA_RISC2_0)
922                 if (cpu_version == CPU_PA_RISC2_0)
923                     strcpy(un.machine, "hppa2.0");
924 #           endif
925         }
926 #       endif   /* hpux */
927
928 #       if defined(__linux__) && defined(__sparc__)
929         if (!strcmp(un.machine, "sparc")) {
930             #define PERS_LINUX          0x00000000
931             #define PERS_LINUX_32BIT    0x00800000
932             #define PERS_LINUX32        0x00000008
933
934             extern int personality(unsigned long);
935             int oldpers;
936             
937             oldpers = personality(PERS_LINUX_32BIT);
938             if (oldpers != -1) {
939                 if (personality(PERS_LINUX) != -1) {
940                     uname(&un);
941                     if (! strcmp(un.machine, "sparc64")) {
942                         strcpy(un.machine, "sparcv9");
943                         oldpers = PERS_LINUX32;
944                     }
945                 }
946                 personality(oldpers);
947             }
948         }
949 #       endif   /* sparc*-linux */
950
951         /* the uname() result goes through the arch_canon table */
952         canon = lookupInCanonTable(un.machine,
953                                    tables[RPM_MACHTABLE_INSTARCH].canons,
954                                    tables[RPM_MACHTABLE_INSTARCH].canonsLength);
955         if (canon)
956             strcpy(un.machine, canon->short_name);
957
958         canon = lookupInCanonTable(un.sysname,
959                                    tables[RPM_MACHTABLE_INSTOS].canons,
960                                    tables[RPM_MACHTABLE_INSTOS].canonsLength);
961         if (canon)
962             strcpy(un.sysname, canon->short_name);
963         gotDefaults = 1;
964     }
965
966     if (arch) *arch = un.machine;
967     if (os) *os = un.sysname;
968 }
969
970 static char * rpmGetVarArch(int var, char * arch) {
971     struct rpmvarValue * next;
972
973     if (!arch) arch = current[ARCH];
974
975     if (arch) {
976         next = &values[var];
977         while (next) {
978             if (next->arch && !strcmp(next->arch, arch)) return next->value;
979             next = next->next;
980         }
981     }
982
983     next = values + var;
984     while (next && next->arch) next = next->next;
985
986     return next ? next->value : NULL;
987 }
988
989 char *rpmGetVar(int var)
990 {
991     return rpmGetVarArch(var, NULL);
992 }
993
994 /* this doesn't free the passed pointer! */
995 static void freeRpmVar(struct rpmvarValue * orig) {
996     struct rpmvarValue * next, * var = orig;
997
998     while (var) {
999         next = var->next;
1000         if (var->arch) {
1001             free(var->arch);
1002             var->arch = NULL;
1003         }
1004         if (var->value) {
1005             free(var->value);
1006             var->value = NULL;
1007         }
1008
1009         if (var != orig) free(var);
1010         var = next;
1011     }
1012 }
1013
1014 void rpmSetVar(int var, const char *val) {
1015     freeRpmVar(&values[var]);
1016     values[var].value = (val ? strdup(val) : NULL);
1017 }
1018
1019 static void rpmSetVarArch(int var, const char * val, const char * arch) {
1020     struct rpmvarValue * next = values + var;
1021
1022     if (next->value) {
1023         if (arch) {
1024             while (next->next) {
1025                 if (next->arch && !strcmp(next->arch, arch)) break;
1026                 next = next->next;
1027             }
1028         } else {
1029             while (next->next) {
1030                 if (!next->arch) break;
1031                 next = next->next;
1032             }
1033         }
1034
1035         if (next->arch && arch && !strcmp(next->arch, arch)) {
1036             if (next->value) free(next->value);
1037             if (next->arch) free(next->arch);
1038         } else if (next->arch || arch) {
1039             next->next = malloc(sizeof(*next->next));
1040             next = next->next;
1041             next->next = NULL;
1042         }
1043     }
1044
1045     next->value = strdup(val);
1046     next->arch = (arch ? strdup(arch) : NULL);
1047 }
1048
1049 void rpmSetTables(int archTable, int osTable) {
1050     char * arch, * os;
1051
1052     defaultMachine(&arch, &os);
1053
1054     if (currTables[ARCH] != archTable) {
1055         currTables[ARCH] = archTable;
1056         rebuildCompatTables(ARCH, arch);
1057     }
1058
1059     if (currTables[OS] != osTable) {
1060         currTables[OS] = osTable;
1061         rebuildCompatTables(OS, os);
1062     }
1063 }
1064
1065 int rpmMachineScore(int type, char * name) {
1066     struct machEquivInfo * info = machEquivSearch(&tables[type].equiv, name);
1067     return (info != NULL ? info->score : 0);
1068 }
1069
1070 void rpmGetMachine(char **arch, char **os)
1071 {
1072     if (arch)
1073         *arch = current[ARCH];
1074
1075     if (os)
1076         *os = current[OS];
1077 }
1078
1079 void rpmSetMachine(const char * arch, const char * os) {
1080     char * host_cpu, * host_os;
1081
1082     defaultMachine(&host_cpu, &host_os);
1083
1084     if (arch == NULL) {
1085         arch = host_cpu;
1086         if (tables[currTables[ARCH]].hasTranslate)
1087             arch = lookupInDefaultTable(arch,
1088                             tables[currTables[ARCH]].defaults,
1089                             tables[currTables[ARCH]].defaultsLength);
1090     }
1091
1092     if (os == NULL) {
1093         os = host_os;
1094         if (tables[currTables[OS]].hasTranslate)
1095             os = lookupInDefaultTable(os,
1096                             tables[currTables[OS]].defaults,
1097                             tables[currTables[OS]].defaultsLength);
1098     }
1099
1100     if (!current[ARCH] || strcmp(arch, current[ARCH])) {
1101         if (current[ARCH]) free(current[ARCH]);
1102         current[ARCH] = strdup(arch);
1103         rebuildCompatTables(ARCH, host_cpu);
1104     }
1105
1106     if (!current[OS] || strcmp(os, current[OS])) {
1107         if (current[OS]) free(current[OS]);
1108         current[OS] = strdup(os);
1109         /*
1110          * XXX Capitalizing the 'L' is needed to insure that old
1111          * XXX os-from-uname (e.g. "Linux") is compatible with the new
1112          * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
1113          * XXX A copy of this string is embedded in headers and is
1114          * XXX used by rpmInstallPackage->{os,arch}Okay->rpmMachineScore->
1115          * XXX to verify correct arch/os from headers.
1116          */
1117         if (!strcmp(current[OS], "linux"))
1118             *current[OS]= 'L';
1119         rebuildCompatTables(OS, host_os);
1120     }
1121 }
1122
1123 static void rebuildCompatTables(int type, char * name) {
1124     machFindEquivs(&tables[currTables[type]].cache,
1125                    &tables[currTables[type]].equiv,
1126                    name);
1127 }
1128
1129 static void getMachineInfo(int type, /*@out@*/char ** name, /*@out@*/int * num) {
1130     struct canonEntry * canon;
1131     int which = currTables[type];
1132
1133     /* use the normal canon tables, even if we're looking up build stuff */
1134     if (which >= 2) which -= 2;
1135
1136     canon = lookupInCanonTable(current[type],
1137                                tables[which].canons,
1138                                tables[which].canonsLength);
1139
1140     if (canon) {
1141         if (num) *num = canon->num;
1142         if (name) *name = canon->short_name;
1143     } else {
1144         if (num) *num = 255;
1145         if (name) *name = current[type];
1146
1147         if (tables[currTables[type]].hasCanon) {
1148             rpmMessage(RPMMESS_WARNING, _("Unknown system: %s\n"), current[type]);
1149             rpmMessage(RPMMESS_WARNING, _("Please contact rpm-list@redhat.com\n"));
1150         }
1151     }
1152 }
1153
1154 void rpmGetArchInfo(char ** name, int * num) {
1155     getMachineInfo(ARCH, name, num);
1156 }
1157
1158 void rpmGetOsInfo(char ** name, int * num) {
1159     getMachineInfo(OS, name, num);
1160 }
1161
1162 void rpmRebuildTargetVars(const char **buildtarget, const char ** canontarget)
1163 {
1164
1165     char *ca = NULL, *co = NULL, *ct = NULL;
1166     int x;
1167
1168     /* Rebuild the compat table to recalculate the current target arch.  */
1169
1170     rpmSetMachine(NULL, NULL);
1171     rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS);
1172     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
1173
1174     if (buildtarget && *buildtarget) {
1175         char *c;
1176         /* Set arch and os from specified build target */
1177         ca = strdup(*buildtarget);
1178         if ((c = strchr(ca, '-')) != NULL) {
1179             *c++ = '\0';
1180             
1181             if ((co = strrchr(c, '-')) == NULL) {
1182                 co = c;
1183             } else {
1184                 if (!strcasecmp(co, "-gnu"))
1185                     *co = '\0';
1186                 if ((co = strrchr(c, '-')) == NULL)
1187                     co = c;
1188                 else
1189                     co++;
1190             }
1191             if (co != NULL) co = strdup(co);
1192         }
1193     } else {
1194         /* Set build target from rpm arch and os */
1195         rpmGetArchInfo(&ca,NULL);
1196         if (ca) ca = strdup(ca);
1197         rpmGetOsInfo(&co,NULL);
1198         if (co) co = strdup(co);
1199     }
1200
1201     /* If still not set, Set target arch/os from default uname(2) values */
1202     if (ca == NULL) {
1203         defaultMachine(&ca, NULL);
1204         ca = strdup(ca);
1205      }
1206     for (x = 0; ca[x]; x++)
1207         ca[x] = tolower(ca[x]);
1208
1209     if (co == NULL) {
1210         defaultMachine(NULL, &co);
1211         co = strdup(co);
1212     }
1213     for (x = 0; co[x]; x++)
1214         co[x] = tolower(co[x]);
1215
1216     /* XXX For now, set canonical target to arch-os */
1217     if (ct == NULL) {
1218         ct = malloc(strlen(ca) + sizeof("-") + strlen(co));
1219         sprintf(ct, "%s-%s", ca, co);
1220     }
1221
1222 /*
1223  * XXX All this macro pokery/jiggery could be achieved by doing a delayed
1224  *      initMacros(NULL, PER-PLATFORM-MACRO-FILE-NAMES);
1225  */
1226     delMacro(NULL, "_target");
1227     addMacro(NULL, "_target", NULL, ct, RMIL_RPMRC);
1228     delMacro(NULL, "_target_cpu");
1229     addMacro(NULL, "_target_cpu", NULL, ca, RMIL_RPMRC);
1230     delMacro(NULL, "_target_os");
1231     addMacro(NULL, "_target_os", NULL, co, RMIL_RPMRC);
1232 /*
1233  * XXX Make sure that per-arch optflags is initialized correctly.
1234  */
1235   { char *optflags = rpmGetVarArch(RPMVAR_OPTFLAGS, ca);
1236     if (optflags != NULL) {
1237         delMacro(NULL, "optflags");
1238         addMacro(NULL, "optflags", NULL, optflags, RMIL_RPMRC);
1239     }
1240   }
1241
1242     if (canontarget)
1243         *canontarget = ct;
1244     else
1245         free(ct);
1246     free(ca);
1247     free(co);
1248 }
1249
1250 int rpmShowRC(FILE *f)
1251 {
1252     struct rpmOption *opt;
1253     int i;
1254     struct machEquivTable * equivTable;
1255
1256     /* the caller may set the build arch which should be printed here */
1257     fprintf(f, "ARCHITECTURE AND OS:\n");
1258     fprintf(f, "build arch            : %s\n", current[ARCH]);
1259
1260     fprintf(f, "compatible build archs:");
1261     equivTable = &tables[RPM_MACHTABLE_BUILDARCH].equiv;
1262     for (i = 0; i < equivTable->count; i++)
1263         fprintf(f," %s", equivTable->list[i].name);
1264     fprintf(f, "\n");
1265
1266     fprintf(f, "build os              : %s\n", current[OS]);
1267
1268     fprintf(f, "compatible build os's :");
1269     equivTable = &tables[RPM_MACHTABLE_BUILDOS].equiv;
1270     for (i = 0; i < equivTable->count; i++)
1271         fprintf(f," %s", equivTable->list[i].name);
1272     fprintf(f, "\n");
1273
1274     rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS);
1275     rpmSetMachine(NULL, NULL);  /* XXX WTFO? Why bother? */
1276
1277     fprintf(f, "install arch          : %s\n", current[ARCH]);
1278     fprintf(f, "install os            : %s\n", current[OS]);
1279
1280     fprintf(f, "compatible archs      :");
1281     equivTable = &tables[RPM_MACHTABLE_INSTARCH].equiv;
1282     for (i = 0; i < equivTable->count; i++)
1283         fprintf(f," %s", equivTable->list[i].name);
1284     fprintf(f, "\n");
1285
1286     fprintf(f, "compatible os's       :");
1287     equivTable = &tables[RPM_MACHTABLE_INSTOS].equiv;
1288     for (i = 0; i < equivTable->count; i++)
1289         fprintf(f," %s", equivTable->list[i].name);
1290     fprintf(f, "\n");
1291
1292     fprintf(f, "\nRPMRC VALUES:\n");
1293     for (i = 0, opt = optionTable; i < optionTableSize; i++, opt++) {
1294         char *s = rpmGetVar(opt->var);
1295         if (s != NULL || rpmGetVerbosity() < RPMMESS_NORMAL)
1296             fprintf(f, "%-21s : %s\n", opt->name, s ? s : "(not set)");
1297     }
1298
1299     dumpMacroTable(NULL, f);
1300
1301     return 0;
1302 }