Much of spec internals is exposed, let them be const for now
[platform/upstream/rpm.git] / build / spec.c
1 /** \ingroup rpmbuild
2  * \file build/spec.c
3  * Handle spec data structure.
4  */
5
6 #include "system.h"
7
8 #include "build/buildio.h"
9 #include <rpm/rpmds.h>
10 #include <rpm/rpmfi.h>
11 #include <rpm/rpmts.h>
12 #include <rpm/rpmlog.h>
13 #include <rpm/rpmfileutil.h>
14
15 #include "debug.h"
16
17 extern int specedit;
18
19 #define SKIPSPACE(s) { while (*(s) && xisspace(*(s))) (s)++; }
20 #define SKIPWHITE(_x)   {while(*(_x) && (xisspace(*_x) || *(_x) == ',')) (_x)++;}
21 #define SKIPNONWHITE(_x){while(*(_x) &&!(xisspace(*_x) || *(_x) == ',')) (_x)++;}
22
23 /**
24  * @param p             trigger entry chain
25  * @return              NULL always
26  */
27 static inline
28 struct TriggerFileEntry * freeTriggerFiles(struct TriggerFileEntry * p)
29 {
30     struct TriggerFileEntry *o, *q = p;
31     
32     while (q != NULL) {
33         o = q;
34         q = q->next;
35         o->fileName = _free(o->fileName);
36         o->script = _free(o->script);
37         o->prog = _free(o->prog);
38         o = _free(o);
39     }
40     return NULL;
41 }
42
43 /**
44  * Destroy source component chain.
45  * @param s             source component chain
46  * @return              NULL always
47  */
48 static inline
49 struct Source * freeSources(struct Source * s)
50 {
51     struct Source *r, *t = s;
52
53     while (t != NULL) {
54         r = t;
55         t = t->next;
56         r->fullSource = _free(r->fullSource);
57         r = _free(r);
58     }
59     return NULL;
60 }
61
62 rpmRC lookupPackage(rpmSpec spec, const char *name, int flag,Package *pkg)
63 {
64     const char *pname;
65     const char *fullName;
66     Package p;
67     
68     /* "main" package */
69     if (name == NULL) {
70         if (pkg)
71             *pkg = spec->packages;
72         return RPMRC_OK;
73     }
74
75     /* Construct package name */
76   { char *n;
77     if (flag == PART_SUBNAME) {
78         (void) headerNVR(spec->packages->header, &pname, NULL, NULL);
79         fullName = n = alloca(strlen(pname) + 1 + strlen(name) + 1);
80         while (*pname != '\0') *n++ = *pname++;
81         *n++ = '-';
82     } else {
83         fullName = n = alloca(strlen(name)+1);
84     }
85     strcpy(n, name);
86   }
87
88     /* Locate package with fullName */
89     for (p = spec->packages; p != NULL; p = p->next) {
90         (void) headerNVR(p->header, &pname, NULL, NULL);
91         if (pname && (! strcmp(fullName, pname))) {
92             break;
93         }
94     }
95
96     if (pkg)
97         *pkg = p;
98     return ((p == NULL) ? RPMRC_FAIL : RPMRC_OK);
99 }
100
101 Package newPackage(rpmSpec spec)
102 {
103     Package p;
104     Package pp;
105
106     p = xcalloc(1, sizeof(*p));
107
108     p->header = headerNew();
109     p->ds = NULL;
110     p->icon = NULL;
111
112     p->autoProv = 1;
113     p->autoReq = 1;
114     
115 #if 0    
116     p->reqProv = NULL;
117     p->triggers = NULL;
118     p->triggerScripts = NULL;
119 #endif
120
121     p->triggerFiles = NULL;
122     
123     p->fileFile = NULL;
124     p->fileList = NULL;
125
126     p->cpioList = NULL;
127
128     p->preInFile = NULL;
129     p->postInFile = NULL;
130     p->preUnFile = NULL;
131     p->postUnFile = NULL;
132     p->verifyFile = NULL;
133
134     p->specialDoc = NULL;
135
136     if (spec->packages == NULL) {
137         spec->packages = p;
138     } else {
139         /* Always add package to end of list */
140         for (pp = spec->packages; pp->next != NULL; pp = pp->next)
141             {};
142         pp->next = p;
143     }
144     p->next = NULL;
145
146     return p;
147 }
148
149 Package freePackage(Package pkg)
150 {
151     if (pkg == NULL) return NULL;
152     
153     pkg->preInFile = _constfree(pkg->preInFile);
154     pkg->postInFile = _constfree(pkg->postInFile);
155     pkg->preUnFile = _constfree(pkg->preUnFile);
156     pkg->postUnFile = _constfree(pkg->postUnFile);
157     pkg->verifyFile = _constfree(pkg->verifyFile);
158
159     pkg->header = headerFree(pkg->header);
160     pkg->ds = rpmdsFree(pkg->ds);
161     pkg->fileList = freeStringBuf(pkg->fileList);
162     pkg->fileFile = _constfree(pkg->fileFile);
163     if (pkg->cpioList) {
164         rpmfi fi = pkg->cpioList;
165         pkg->cpioList = NULL;
166         fi = rpmfiFree(fi);
167     }
168
169     pkg->specialDoc = freeStringBuf(pkg->specialDoc);
170     pkg->icon = freeSources(pkg->icon);
171     pkg->triggerFiles = freeTriggerFiles(pkg->triggerFiles);
172
173     pkg = _free(pkg);
174     return NULL;
175 }
176
177 Package freePackages(Package packages)
178 {
179     Package p;
180
181     while ((p = packages) != NULL) {
182         packages = p->next;
183         p->next = NULL;
184         p = freePackage(p);
185     }
186     return NULL;
187 }
188
189 /**
190  */
191 static inline struct Source *findSource(rpmSpec spec, int num, int flag)
192 {
193     struct Source *p;
194
195     for (p = spec->sources; p != NULL; p = p->next)
196         if ((num == p->num) && (p->flags & flag)) return p;
197
198     return NULL;
199 }
200
201 int parseNoSource(rpmSpec spec, const char * field, rpm_tag_t tag)
202 {
203     const char *f, *fe;
204     const char *name;
205     int num, flag;
206
207     if (tag == RPMTAG_NOSOURCE) {
208         flag = RPMBUILD_ISSOURCE;
209         name = "source";
210     } else {
211         flag = RPMBUILD_ISPATCH;
212         name = "patch";
213     }
214     
215     fe = field;
216     for (f = fe; *f != '\0'; f = fe) {
217         struct Source *p;
218
219         SKIPWHITE(f);
220         if (*f == '\0')
221             break;
222         fe = f;
223         SKIPNONWHITE(fe);
224         if (*fe != '\0') fe++;
225
226         if (parseNum(f, &num)) {
227             rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
228                      spec->lineNum, f);
229             return RPMRC_FAIL;
230         }
231
232         if (! (p = findSource(spec, num, flag))) {
233             rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %d\n"),
234                      spec->lineNum, name, num);
235             return RPMRC_FAIL;
236         }
237
238         p->flags |= RPMBUILD_ISNO;
239
240     }
241
242     return 0;
243 }
244
245 int addSource(rpmSpec spec, Package pkg, const char *field, rpm_tag_t tag)
246 {
247     struct Source *p;
248     int flag = 0;
249     const char *name = NULL;
250     char *nump;
251     const char *fieldp = NULL;
252     char buf[BUFSIZ];
253     int num = 0;
254
255     buf[0] = '\0';
256     switch (tag) {
257       case RPMTAG_SOURCE:
258         flag = RPMBUILD_ISSOURCE;
259         name = "source";
260         fieldp = spec->line + 6;
261         break;
262       case RPMTAG_PATCH:
263         flag = RPMBUILD_ISPATCH;
264         name = "patch";
265         fieldp = spec->line + 5;
266         break;
267       case RPMTAG_ICON:
268         flag = RPMBUILD_ISICON;
269         fieldp = NULL;
270         break;
271     }
272
273     /* Get the number */
274     if (tag != RPMTAG_ICON) {
275         /* We already know that a ':' exists, and that there */
276         /* are no spaces before it.                          */
277         /* This also now allows for spaces and tabs between  */
278         /* the number and the ':'                            */
279
280         nump = buf;
281         while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) {
282             *nump++ = *fieldp++;
283         }
284         *nump = '\0';
285
286         nump = buf;
287         SKIPSPACE(nump);
288         if (nump == NULL || *nump == '\0') {
289             num = 0;
290         } else {
291             if (parseNum(buf, &num)) {
292                 rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"),
293                          spec->lineNum, name, spec->line);
294                 return RPMRC_FAIL;
295             }
296         }
297     }
298
299     /* Create the entry and link it in */
300     p = xmalloc(sizeof(*p));
301     p->num = num;
302     p->fullSource = xstrdup(field);
303     p->flags = flag;
304     p->source = strrchr(p->fullSource, '/');
305     if (p->source) {
306         p->source++;
307     } else {
308         p->source = p->fullSource;
309     }
310
311     if (tag != RPMTAG_ICON) {
312         p->next = spec->sources;
313         spec->sources = p;
314     } else {
315         p->next = pkg->icon;
316         pkg->icon = p;
317     }
318
319     spec->numSources++;
320
321     if (tag != RPMTAG_ICON) {
322         char *body = rpmGetPath("%{_sourcedir}/", p->source, NULL);
323
324         sprintf(buf, "%s%d",
325                 (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
326         addMacro(spec->macros, buf, NULL, body, RMIL_SPEC);
327         sprintf(buf, "%sURL%d",
328                 (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
329         addMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC);
330         body = _free(body);
331     }
332     
333     return 0;
334 }
335
336 /**
337  */
338 static inline speclines newSl(void)
339 {
340     speclines sl = NULL;
341     if (specedit) {
342         sl = xmalloc(sizeof(*sl));
343         sl->sl_lines = NULL;
344         sl->sl_nalloc = 0;
345         sl->sl_nlines = 0;
346     }
347     return sl;
348 }
349
350 /**
351  */
352 static inline speclines freeSl(speclines sl)
353 {
354     int i;
355     if (sl == NULL) return NULL;
356     for (i = 0; i < sl->sl_nlines; i++)
357         sl->sl_lines[i] = _free(sl->sl_lines[i]);
358     sl->sl_lines = _free(sl->sl_lines);
359     return _free(sl);
360 }
361
362 /**
363  */
364 static inline spectags newSt(void)
365 {
366     spectags st = NULL;
367     if (specedit) {
368         st = xmalloc(sizeof(*st));
369         st->st_t = NULL;
370         st->st_nalloc = 0;
371         st->st_ntags = 0;
372     }
373     return st;
374 }
375
376 /**
377  */
378 static inline spectags freeSt(spectags st)
379 {
380     int i;
381     if (st == NULL) return NULL;
382     for (i = 0; i < st->st_ntags; i++) {
383         spectag t = st->st_t + i;
384         t->t_lang = _constfree(t->t_lang);
385         t->t_msgid = _constfree(t->t_msgid);
386     }
387     st->st_t = _free(st->st_t);
388     return _free(st);
389 }
390
391 rpmSpec newSpec(void)
392 {
393     rpmSpec spec = xcalloc(1, sizeof(*spec));
394     
395     spec->specFile = NULL;
396
397     spec->sl = newSl();
398     spec->st = newSt();
399
400     spec->fileStack = NULL;
401     spec->lbuf[0] = '\0';
402     spec->line = spec->lbuf;
403     spec->nextline = NULL;
404     spec->nextpeekc = '\0';
405     spec->lineNum = 0;
406     spec->readStack = xcalloc(1, sizeof(*spec->readStack));
407     spec->readStack->next = NULL;
408     spec->readStack->reading = 1;
409
410     spec->rootURL = NULL;
411     spec->prep = NULL;
412     spec->build = NULL;
413     spec->install = NULL;
414     spec->check = NULL;
415     spec->clean = NULL;
416
417     spec->sources = NULL;
418     spec->packages = NULL;
419     spec->noSource = 0;
420     spec->numSources = 0;
421
422     spec->sourceRpmName = NULL;
423     spec->sourcePkgId = NULL;
424     spec->sourceHeader = NULL;
425     spec->sourceCpioList = NULL;
426     
427     spec->gotBuildRootURL = 0;
428     spec->buildRootURL = NULL;
429     spec->buildSubdir = NULL;
430
431     spec->passPhrase = NULL;
432     spec->timeCheck = 0;
433     spec->cookie = NULL;
434
435     spec->buildRestrictions = headerNew();
436     spec->BANames = NULL;
437     spec->BACount = 0;
438     spec->recursing = 0;
439     spec->BASpecs = NULL;
440
441     spec->force = 0;
442     spec->anyarch = 0;
443
444     spec->macros = rpmGlobalMacroContext;
445     
446     return spec;
447 }
448
449 rpmSpec freeSpec(rpmSpec spec)
450 {
451     struct ReadLevelEntry *rl;
452
453     if (spec == NULL) return NULL;
454
455     spec->sl = freeSl(spec->sl);
456     spec->st = freeSt(spec->st);
457
458     spec->prep = freeStringBuf(spec->prep);
459     spec->build = freeStringBuf(spec->build);
460     spec->install = freeStringBuf(spec->install);
461     spec->check = freeStringBuf(spec->check);
462     spec->clean = freeStringBuf(spec->clean);
463
464     spec->buildRootURL = _constfree(spec->buildRootURL);
465     spec->buildSubdir = _constfree(spec->buildSubdir);
466     spec->rootURL = _constfree(spec->rootURL);
467     spec->specFile = _constfree(spec->specFile);
468
469 #ifdef  DEAD
470   { struct OpenFileInfo *ofi;
471     while (spec->fileStack) {
472         ofi = spec->fileStack;
473         spec->fileStack = ofi->next;
474         ofi->next = NULL;
475         ofi->fileName = _free(ofi->fileName);
476         ofi = _free(ofi);
477     }
478   }
479 #else
480     closeSpec(spec);
481 #endif
482
483     while (spec->readStack) {
484         rl = spec->readStack;
485         spec->readStack = rl->next;
486         rl->next = NULL;
487         rl = _free(rl);
488     }
489     
490     spec->sourceRpmName = _constfree(spec->sourceRpmName);
491     spec->sourcePkgId = _free(spec->sourcePkgId);
492     spec->sourceHeader = headerFree(spec->sourceHeader);
493
494     if (spec->sourceCpioList) {
495         rpmfi fi = spec->sourceCpioList;
496         spec->sourceCpioList = NULL;
497         fi = rpmfiFree(fi);
498     }
499     
500     spec->buildRestrictions = headerFree(spec->buildRestrictions);
501
502     if (!spec->recursing) {
503         if (spec->BASpecs != NULL)
504         while (spec->BACount--) {
505             spec->BASpecs[spec->BACount] =
506                         freeSpec(spec->BASpecs[spec->BACount]);
507         }
508         spec->BASpecs = _free(spec->BASpecs);
509     }
510     spec->BANames = _free(spec->BANames);
511
512     spec->passPhrase = _free(spec->passPhrase);
513     spec->cookie = _constfree(spec->cookie);
514
515     spec->sources = freeSources(spec->sources);
516     spec->packages = freePackages(spec->packages);
517     
518     spec = _free(spec);
519
520     return spec;
521 }
522
523 struct OpenFileInfo * newOpenFileInfo(void)
524 {
525     struct OpenFileInfo *ofi;
526
527     ofi = xmalloc(sizeof(*ofi));
528     ofi->fd = NULL;
529     ofi->fileName = NULL;
530     ofi->lineNum = 0;
531     ofi->readBuf[0] = '\0';
532     ofi->readPtr = NULL;
533     ofi->next = NULL;
534
535     return ofi;
536 }
537
538 /**
539  * Print copy of spec file, filling in Group/Description/Summary from specspo.
540  * @param spec          spec file control structure
541  */
542 static void
543 printNewSpecfile(rpmSpec spec)
544 {
545     Header h;
546     speclines sl = spec->sl;
547     spectags st = spec->st;
548     char * msgstr = NULL;
549     int i, j;
550
551     if (sl == NULL || st == NULL)
552         return;
553
554     for (i = 0; i < st->st_ntags; i++) {
555         spectag t = st->st_t + i;
556         const char * tn = rpmTagGetName(t->t_tag);
557         const char * errstr;
558         char fmt[1024];
559
560         fmt[0] = '\0';
561         if (t->t_msgid == NULL)
562             h = spec->packages->header;
563         else {
564             Package pkg;
565             char *fe;
566
567             strcpy(fmt, t->t_msgid);
568             for (fe = fmt; *fe && *fe != '('; fe++)
569                 {} ;
570             if (*fe == '(') *fe = '\0';
571             h = NULL;
572             for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
573                 const char *pkgname;
574                 h = pkg->header;
575                 (void) headerNVR(h, &pkgname, NULL, NULL);
576                 if (!strcmp(pkgname, fmt))
577                     break;
578             }
579             if (pkg == NULL || h == NULL)
580                 h = spec->packages->header;
581         }
582
583         if (h == NULL)
584             continue;
585
586         fmt[0] = '\0';
587         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tn), "}");
588         msgstr = _free(msgstr);
589
590         /* XXX this should use queryHeader(), but prints out tn as well. */
591         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
592         if (msgstr == NULL) {
593             rpmlog(RPMLOG_ERR, _("can't query %s: %s\n"), tn, errstr);
594             return;
595         }
596
597         switch(t->t_tag) {
598         case RPMTAG_SUMMARY:
599         case RPMTAG_GROUP:
600             sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
601             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
602                 continue;
603             {   char *buf = xmalloc(strlen(tn) + sizeof(": ") + strlen(msgstr));
604                 (void) stpcpy( stpcpy( stpcpy(buf, tn), ": "), msgstr);
605                 sl->sl_lines[t->t_startx] = buf;
606             }
607             break;
608         case RPMTAG_DESCRIPTION:
609             for (j = 1; j < t->t_nlines; j++) {
610                 if (*sl->sl_lines[t->t_startx + j] == '%')
611                     continue;
612                 sl->sl_lines[t->t_startx + j] =
613                         _free(sl->sl_lines[t->t_startx + j]);
614             }
615             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
616                 sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
617                 continue;
618             }
619             sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
620             if (t->t_nlines > 2)
621                 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
622             break;
623         }
624     }
625     msgstr = _free(msgstr);
626
627     for (i = 0; i < sl->sl_nlines; i++) {
628         const char * s = sl->sl_lines[i];
629         if (s == NULL)
630             continue;
631         printf("%s", s);
632         if (strchr(s, '\n') == NULL && s[strlen(s)-1] != '\n')
633             printf("\n");
634     }
635 }
636
637 int rpmspecQuery(rpmts ts, QVA_t qva, const char * arg)
638 {
639     rpmSpec spec = NULL;
640     Package pkg;
641     char * buildRoot = NULL;
642     int recursing = 0;
643     char * passPhrase = "";
644     char *cookie = NULL;
645     int anyarch = 1;
646     int force = 1;
647     int res = 1;
648     int xx;
649
650     if (qva->qva_showPackage == NULL)
651         goto exit;
652
653     /* FIX: make spec abstract */
654     if (parseSpec(ts, arg, "/", buildRoot, recursing, passPhrase,
655                 cookie, anyarch, force)
656       || (spec = rpmtsSetSpec(ts, NULL)) == NULL)
657     {
658         rpmlog(RPMLOG_ERR,
659                         _("query of specfile %s failed, can't parse\n"), arg);
660         goto exit;
661     }
662
663     res = 0;
664     if (specedit) {
665         printNewSpecfile(spec);
666         goto exit;
667     }
668
669     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next)
670         xx = qva->qva_showPackage(qva, ts, pkg->header);
671
672 exit:
673     spec = freeSpec(spec);
674     return res;
675 }