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