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