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