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