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