1 /** \ingroup rpmbuild
2 * \file build/parsePreamble.c
3 * Parse tags in global section from spec file.
4 */
5
6 #include "system.h"
7
8 #include <ctype.h>
9 #include <errno.h>
10
11 #include <rpm/header.h>
12 #include <rpm/rpmlog.h>
13 #include <rpm/rpmurl.h>
14 #include <rpm/rpmfileutil.h>
15 #include "rpmio/rpmlua.h"
16 #include "build/rpmbuild_internal.h"
17 #include "build/rpmbuild_misc.h"
18 #include "debug.h"
19
20 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
21 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
22 #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
23 #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
24
25 /**
26 */
27 static const rpmTagVal copyTagsDuringParse[] = {
28 RPMTAG_EPOCH,
29 RPMTAG_VERSION,
30 RPMTAG_RELEASE,
31 RPMTAG_LICENSE,
32 RPMTAG_PACKAGER,
33 RPMTAG_DISTRIBUTION,
34 RPMTAG_DISTURL,
35 RPMTAG_VENDOR,
36 RPMTAG_ICON,
37 RPMTAG_URL,
38 RPMTAG_VCS,
39 RPMTAG_CHANGELOGTIME,
40 RPMTAG_CHANGELOGNAME,
41 RPMTAG_CHANGELOGTEXT,
42 RPMTAG_PREFIXES,
43 RPMTAG_DISTTAG,
44 RPMTAG_BUGURL,
45 RPMTAG_GROUP,
46 RPMTAG_MODULARITYLABEL,
47 0
48 };
49
50 /**
51 */
52 static const rpmTagVal requiredTags[] = {
53 RPMTAG_NAME,
54 RPMTAG_VERSION,
55 RPMTAG_RELEASE,
56 RPMTAG_SUMMARY,
57 RPMTAG_LICENSE,
58 0
59 };
60
61 /**
62 */
addOrAppendListEntry(Header h,rpmTagVal tag,const char * line)63 static rpmRC addOrAppendListEntry(Header h, rpmTagVal tag, const char * line)
64 {
65 int xx;
66 int argc;
67 const char **argv;
68
69 if ((xx = poptParseArgvString(line, &argc, &argv))) {
70 rpmlog(RPMLOG_ERR, _("Error parsing tag field: %s\n"), poptStrerror(xx));
71 return RPMRC_FAIL;
72 }
73 if (argc)
74 headerPutStringArray(h, tag, argv, argc);
75 argv = _free(argv);
76
77 return RPMRC_OK;
78 }
79
80 /* Parse a simple part line that only take -n <pkg> or <pkg> */
81 /* <pkg> is returned in name as a pointer into a dynamic buffer */
82
83 /**
84 */
parseSimplePart(const char * line,char ** name,int * flag)85 static int parseSimplePart(const char *line, char **name, int *flag)
86 {
87 char *tok;
88 char *linebuf = xstrdup(line);
89 int rc;
90
91 /* Throw away the first token (the %xxxx) */
92 (void)strtok(linebuf, " \t\n");
93 *name = NULL;
94
95 if (!(tok = strtok(NULL, " \t\n"))) {
96 rc = 1;
97 goto exit;
98 }
99
100 if (rstreq(tok, "-n")) {
101 if (!(tok = strtok(NULL, " \t\n"))) {
102 rc = 1;
103 goto exit;
104 }
105 *flag = PART_NAME;
106 } else {
107 *flag = PART_SUBNAME;
108 }
109 *name = xstrdup(tok);
110 rc = strtok(NULL, " \t\n") ? 1 : 0;
111
112 exit:
113 free(linebuf);
114 return rc;
115 }
116
117 /**
118 */
parseYesNo(const char * s)119 static inline int parseYesNo(const char * s)
120 {
121 return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
122 !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
123 ? 0 : 1);
124 }
125
newSource(uint32_t num,const char * path,int flags)126 static struct Source *newSource(uint32_t num, const char *path, int flags)
127 {
128 struct Source *p = xmalloc(sizeof(*p));
129 p->num = num;
130 p->fullSource = xstrdup(path);
131 p->flags = flags;
132 p->source = strrchr(p->fullSource, '/');
133 if (p->source) {
134 const char *buf = strrchr(p->source,'=');
135 if (buf)
136 p->source = buf;
137 p->source++;
138 } else {
139 p->source = p->fullSource;
140 }
141 p->path = rpmGetPath("%{_sourcedir}/", p->source, NULL);
142 return p;
143 }
144
findSource(rpmSpec spec,uint32_t num,int flag)145 struct Source *findSource(rpmSpec spec, uint32_t num, int flag)
146 {
147 struct Source *p;
148
149 for (p = spec->sources; p != NULL; p = p->next)
150 if ((num == p->num) && (p->flags & flag)) return p;
151
152 return NULL;
153 }
154
parseNoSource(rpmSpec spec,const char * field,rpmTagVal tag)155 static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag)
156 {
157 const char *f, *fe;
158 const char *name;
159 int flag;
160 uint32_t num;
161
162 if (tag == RPMTAG_NOSOURCE) {
163 flag = RPMBUILD_ISSOURCE;
164 name = "source";
165 } else {
166 flag = RPMBUILD_ISPATCH;
167 name = "patch";
168 }
169
170 fe = field;
171 for (f = fe; *f != '\0'; f = fe) {
172 struct Source *p;
173
174 SKIPWHITE(f);
175 if (*f == '\0')
176 break;
177 fe = f;
178 SKIPNONWHITE(fe);
179 if (*fe != '\0') fe++;
180
181 if (parseUnsignedNum(f, &num)) {
182 rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
183 spec->lineNum, f);
184 return RPMRC_FAIL;
185 }
186
187 if (! (p = findSource(spec, num, flag))) {
188 rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"),
189 spec->lineNum, name, num);
190 return RPMRC_FAIL;
191 }
192
193 p->flags |= RPMBUILD_ISNO;
194
195 }
196
197 return 0;
198 }
199
addLuaSource(const struct Source * p)200 static void addLuaSource(const struct Source *p)
201 {
202 #ifdef WITH_LUA
203 rpmlua lua = NULL; /* global state */
204 const char * what = (p->flags & RPMBUILD_ISPATCH) ? "patches" : "sources";
205 rpmluaPushTable(lua, what);
206 rpmluav var = rpmluavNew();
207 rpmluavSetListMode(var, 1);
208 rpmluavSetValue(var, RPMLUAV_STRING, p->path);
209 rpmluaSetVar(lua, var);
210 rpmluavFree(var);
211 rpmluaPop(lua);
212
213 what = (p->flags & RPMBUILD_ISPATCH) ? "patch_nums" : "source_nums";
214 rpmluaPushTable(lua, what);
215 var = rpmluavNew();
216 rpmluavSetListMode(var, 1);
217 rpmluavSetValueNum(var, p->num);
218 rpmluaSetVar(lua, var);
219 rpmluavFree(var);
220 rpmluaPop(lua);
221 #endif
222 }
223
tryDownload(const struct Source * p)224 static int tryDownload(const struct Source *p)
225 {
226 int rc = 0;
227 struct stat st;
228
229 /* try to download source/patch if it's missing */
230 if (lstat(p->path, &st) != 0 && errno == ENOENT) {
231 char *url = NULL;
232 if (urlIsURL(p->fullSource) != URL_IS_UNKNOWN) {
233 url = rstrdup(p->fullSource);
234 } else {
235 url = rpmExpand("%{_default_source_url}", NULL);
236 rstrcat(&url, p->source);
237 if (*url == '%') url = _free(url);
238 }
239 if (url) {
240 rpmlog(RPMLOG_WARNING, _("Downloading %s to %s\n"), url, p->path);
241 if (urlGetFile(url, p->path) != 0) {
242 rpmlog(RPMLOG_ERR, _("Couldn't download %s\n"), p->fullSource);
243 rc = -1;
244 }
245 }
246 free(url);
247 }
248 return rc;
249 }
250
251 /*
252 * Parse an option number of a tag, such as in sources and patches.
253 * Return -1 on error, 0 if number present and 1 if no number found.
254 */
parseTagNumber(const char * line,uint32_t * snum)255 static int parseTagNumber(const char *line, uint32_t *snum)
256 {
257 int rc = 0;
258 char *l = xstrdup(line);
259 char *fieldp = l;
260 char *nump = l;
261
262 /* We already know that a ':' exists, and that there */
263 /* are no spaces before it. */
264 /* This also now allows for spaces and tabs between */
265 /* the number and the ':' */
266 while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) {
267 fieldp++;
268 }
269 *fieldp = '\0';
270
271 SKIPSPACE(nump);
272 if (nump == NULL || *nump == '\0') {
273 rc = 1;
274 } else {
275 rc = parseUnsignedNum(l, snum);
276 }
277 free(l);
278 return rc;
279 }
280
addSource(rpmSpec spec,int specline,const char * srcname,rpmTagVal tag)281 int addSource(rpmSpec spec, int specline, const char *srcname, rpmTagVal tag)
282 {
283 struct Source *p;
284 int flag = 0;
285 int nonum = 1; /* assume autonumbering */
286 const char *name = NULL;
287 char *buf = NULL;
288 uint32_t num = 0;
289 int *autonum = NULL;
290 int nofetch = (spec->flags & RPMSPEC_FORCE) ||
291 rpmExpandNumeric("%{_disable_source_fetch}");
292
293 switch (tag) {
294 case RPMTAG_SOURCE:
295 flag = RPMBUILD_ISSOURCE;
296 name = "source";
297 autonum = &spec->autonum_source;
298 break;
299 case RPMTAG_PATCH:
300 flag = RPMBUILD_ISPATCH;
301 name = "patch";
302 autonum = &spec->autonum_patch;
303 break;
304 default:
305 return -1;
306 break;
307 }
308
309 if (specline) {
310 nonum = parseTagNumber(spec->line + strlen(name), &num);
311 if (nonum < 0) {
312 rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"),
313 spec->lineNum, name, spec->line);
314 return RPMRC_FAIL;
315 }
316 }
317
318 if (nonum > 0) {
319 /* No number specified, use autonumbering */
320 (*autonum)++;
321 num = *autonum;
322 } else {
323 /* Autonumbering continues from last specified number */
324 if ((int)num > *autonum)
325 *autonum = num;
326 }
327
328 /* Check whether tags of the same number haven't already been defined */
329 if (findSource(spec, num, flag)) {
330 rpmlog(RPMLOG_ERR, _("%s %d defined multiple times\n"), name, num);
331 return RPMRC_FAIL;
332 }
333
334 /* Create the entry and link it in */
335 p = newSource(num, srcname, flag);
336 p->next = spec->sources;
337 spec->sources = p;
338 spec->numSources++;
339
340 rasprintf(&buf, "%s%d",
341 (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
342 rpmPushMacro(spec->macros, buf, NULL, p->path, RMIL_SPEC);
343 free(buf);
344 rasprintf(&buf, "%sURL%d",
345 (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
346 rpmPushMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC);
347 free(buf);
348
349 addLuaSource(p);
350
351 if (!nofetch && tryDownload(p))
352 return RPMRC_FAIL;
353
354 return 0;
355 }
356
357 typedef const struct tokenBits_s {
358 const char * name;
359 rpmsenseFlags bits;
360 } * tokenBits;
361
362 /**
363 */
364 static struct tokenBits_s const installScriptBits[] = {
365 { "interp", RPMSENSE_INTERP },
366 { "meta", RPMSENSE_META },
367 { "preun", RPMSENSE_SCRIPT_PREUN },
368 { "pre", RPMSENSE_SCRIPT_PRE },
369 { "postun", RPMSENSE_SCRIPT_POSTUN },
370 { "post", RPMSENSE_SCRIPT_POST },
371 { "rpmlib", RPMSENSE_RPMLIB },
372 { "verify", RPMSENSE_SCRIPT_VERIFY },
373 { "pretrans", RPMSENSE_PRETRANS },
374 { "posttrans", RPMSENSE_POSTTRANS },
375 { NULL, 0 }
376 };
377
378 /**
379 */
parseBits(const char * s,const tokenBits tokbits,rpmsenseFlags * bp)380 static int parseBits(const char * s, const tokenBits tokbits,
381 rpmsenseFlags * bp)
382 {
383 tokenBits tb = NULL;
384 const char * se;
385 rpmsenseFlags bits = RPMSENSE_ANY;
386 int c = 0;
387 int rc = RPMRC_OK;
388
389 if (s) {
390 for (;;) {
391 while ((c = *s) && risspace(c)) s++;
392 se = s;
393 while ((c = *se) && risalpha(c)) se++;
394 if (s == se) {
395 if (c || tb)
396 rc = RPMRC_FAIL;
397 break;
398 }
399 for (tb = tokbits; tb->name; tb++) {
400 if (tb->name != NULL &&
401 strlen(tb->name) == (se-s) && rstreqn(tb->name, s, (se-s)))
402 break;
403 }
404 if (tb->name == NULL) {
405 rc = RPMRC_FAIL;
406 break;
407 }
408 bits |= tb->bits;
409 while ((c = *se) && risspace(c)) se++;
410 if (c != ',') {
411 if (c)
412 rc = RPMRC_FAIL;
413 break;
414 }
415 s = ++se;
416 }
417 }
418 *bp |= bits;
419 return rc;
420 }
421
422 /**
423 */
findLastChar(char * s)424 static inline char * findLastChar(char * s)
425 {
426 char *res = s;
427
428 while (*s != '\0') {
429 if (! risspace(*s))
430 res = s;
431 s++;
432 }
433
434 return res;
435 }
436
437 /**
438 */
isMemberInEntry(Header h,const char * name,rpmTagVal tag)439 static int isMemberInEntry(Header h, const char *name, rpmTagVal tag)
440 {
441 struct rpmtd_s td;
442 int found = 0;
443 const char *str;
444
445 if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
446 return -1;
447
448 while ((str = rpmtdNextString(&td))) {
449 if (!rstrcasecmp(str, name)) {
450 found = 1;
451 break;
452 }
453 }
454 rpmtdFreeData(&td);
455
456 return found;
457 }
458
459 /**
460 */
checkForValidArchitectures(rpmSpec spec)461 static rpmRC checkForValidArchitectures(rpmSpec spec)
462 {
463 char *arch = rpmExpand("%{_target_cpu}", NULL);
464 char *os = rpmExpand("%{_target_os}", NULL);
465 rpmRC rc = RPMRC_FAIL; /* assume failure */
466
467 if (!strcmp(arch, "noarch")) {
468 free(arch);
469 arch = rpmExpand("%{_build_cpu}", NULL);
470 }
471
472 if (isMemberInEntry(spec->buildRestrictions,
473 arch, RPMTAG_EXCLUDEARCH) == 1) {
474 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
475 goto exit;
476 }
477 if (isMemberInEntry(spec->buildRestrictions,
478 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
479 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
480 goto exit;
481 }
482 if (isMemberInEntry(spec->buildRestrictions,
483 os, RPMTAG_EXCLUDEOS) == 1) {
484 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
485 goto exit;
486 }
487 if (isMemberInEntry(spec->buildRestrictions,
488 os, RPMTAG_EXCLUSIVEOS) == 0) {
489 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
490 goto exit;
491 }
492 rc = RPMRC_OK;
493
494 exit:
495 free(arch);
496 free(os);
497
498 return rc;
499 }
500
501 /**
502 * Check that required tags are present in header.
503 * @param h header
504 * @param NVR package name-version-release
505 * @return RPMRC_OK if OK
506 */
checkForRequired(Header h,const char * NVR)507 static int checkForRequired(Header h, const char * NVR)
508 {
509 int res = RPMRC_OK;
510 const rpmTagVal * p;
511
512 for (p = requiredTags; *p != 0; p++) {
513 if (!headerIsEntry(h, *p)) {
514 rpmlog(RPMLOG_ERR,
515 _("%s field must be present in package: %s\n"),
516 rpmTagGetName(*p), NVR);
517 res = RPMRC_FAIL;
518 }
519 }
520
521 return res;
522 }
523
524 /**
525 * Check that no duplicate tags are present in header.
526 * @param h header
527 * @param NVR package name-version-release
528 * @return RPMRC_OK if OK
529 */
checkForDuplicates(Header h,const char * NVR)530 static int checkForDuplicates(Header h, const char * NVR)
531 {
532 int res = RPMRC_OK;
533 rpmTagVal tag, lastTag = RPMTAG_NOT_FOUND;
534 HeaderIterator hi = headerInitIterator(h);
535
536 while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) {
537 if (tag == lastTag) {
538 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
539 rpmTagGetName(tag), NVR);
540 res = RPMRC_FAIL;
541 }
542 lastTag = tag;
543 }
544 headerFreeIterator(hi);
545
546 return res;
547 }
548
549 /**
550 */
551 static struct optionalTag {
552 rpmTagVal ot_tag;
553 const char * ot_mac;
554 } const optionalTags[] = {
555 { RPMTAG_VENDOR, "%{vendor}" },
556 { RPMTAG_PACKAGER, "%{packager}" },
557 { RPMTAG_DISTRIBUTION, "%{distribution}" },
558 { RPMTAG_DISTURL, "%{disturl}" },
559 { RPMTAG_DISTTAG, "%{disttag}" },
560 { RPMTAG_BUGURL, "%{bugurl}" },
561 { RPMTAG_MODULARITYLABEL, "%{modularitylabel}"},
562 { -1, NULL }
563 };
564
565 /**
566 */
fillOutMainPackage(Header h)567 static void fillOutMainPackage(Header h)
568 {
569 const struct optionalTag *ot;
570
571 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
572 if (!headerIsEntry(h, ot->ot_tag)) {
573 char *val = rpmExpand(ot->ot_mac, NULL);
574 if (val && *val != '%') {
575 headerPutString(h, ot->ot_tag, val);
576 }
577 free(val);
578 }
579 }
580 }
581
582 /**
583 */
copyInheritedTags(Header h,Header fromh)584 void copyInheritedTags(Header h, Header fromh)
585 {
586 headerCopyTags(fromh, h, (rpmTagVal *)copyTagsDuringParse);
587 }
588
589 /**
590 */
addIcon(Package pkg,const char * file)591 static rpmRC addIcon(Package pkg, const char * file)
592 {
593 struct Source *p = newSource(0, file, RPMBUILD_ISICON);
594 char *fn = NULL;
595 uint8_t *icon = NULL;
596 FD_t fd = NULL;
597 rpmRC rc = RPMRC_FAIL; /* assume failure */
598 off_t size;
599 size_t nb, iconsize;
600
601 p->next = pkg->icon;
602 pkg->icon = p;
603
604 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
605 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
606
607 fd = Fopen(fn, "r.ufdio");
608 if (fd == NULL) {
609 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
610 fn, Fstrerror(fd));
611 goto exit;
612 }
613 size = fdSize(fd);
614 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
615 if (iconsize == 0) {
616 rc = RPMRC_OK; /* XXX Eh? */
617 goto exit;
618 }
619
620 icon = xmalloc(iconsize + 1);
621 *icon = '\0';
622
623 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
624 if (Ferror(fd) || (size >= 0 && nb != size)) {
625 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
626 fn, Fstrerror(fd));
627 goto exit;
628 }
629
630 if (rstreqn((char*)icon, "GIF", sizeof("GIF")-1)) {
631 headerPutBin(pkg->header, RPMTAG_GIF, icon, iconsize);
632 } else if (rstreqn((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
633 headerPutBin(pkg->header, RPMTAG_XPM, icon, iconsize);
634 } else {
635 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
636 goto exit;
637 }
638 rc = RPMRC_OK;
639
640 exit:
641 Fclose(fd);
642 free(fn);
643 free(icon);
644 return rc;
645 }
646
647 #define SINGLE_TOKEN_ONLY \
648 if (multiToken) { \
649 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
650 spec->lineNum, spec->line); \
651 return RPMRC_FAIL; \
652 }
653
specLog(rpmSpec spec,int lvl,const char * line,const char * msg)654 static void specLog(rpmSpec spec, int lvl, const char *line, const char *msg)
655 {
656 if (spec) {
657 rpmlog(lvl, _("line %d: %s in: %s\n"), spec->lineNum, msg, spec->line);
658 } else {
659 rpmlog(lvl, _("%s in: %s\n"), msg, line);
660 }
661 }
662
663 /**
664 * Check for inappropriate characters. All alphanums are considered sane.
665 * @param spec spec (or NULL)
666 * @param field string to check
667 * @param whitelist string of permitted characters
668 * @return RPMRC_OK if OK
669 */
rpmCharCheck(rpmSpec spec,const char * field,const char * whitelist)670 rpmRC rpmCharCheck(rpmSpec spec, const char *field, const char *whitelist)
671 {
672 const char *ch;
673 char *err = NULL;
674 rpmRC rc = RPMRC_OK;
675
676 for (ch=field; *ch; ch++) {
677 if (risalnum(*ch) || strchr(whitelist, *ch)) continue;
678 rasprintf(&err, _("Illegal char '%c' (0x%x)"),
679 isprint(*ch) ? *ch : '?', *ch);
680 }
681 for (ch=field; *ch; ch++) {
682 if (strchr("%{}", *ch)) {
683 specLog(spec, RPMLOG_WARNING, field,
684 _("Possible unexpanded macro"));
685 break;
686 }
687 }
688
689 if (err == NULL && strstr(field, "..") != NULL) {
690 rasprintf(&err, _("Illegal sequence \"..\""));
691 }
692
693 if (err) {
694 specLog(spec, RPMLOG_ERR, field, err);
695 free(err);
696 rc = RPMRC_FAIL;
697 }
698 return rc;
699 }
700
haveLangTag(Header h,rpmTagVal tag,const char * lang)701 static int haveLangTag(Header h, rpmTagVal tag, const char *lang)
702 {
703 int rc = 0; /* assume tag not present */
704 int langNum = -1;
705
706 if (lang && *lang) {
707 /* See if the language is in header i18n table */
708 struct rpmtd_s langtd;
709 const char *s = NULL;
710 headerGet(h, RPMTAG_HEADERI18NTABLE, &langtd, HEADERGET_MINMEM);
711 while ((s = rpmtdNextString(&langtd)) != NULL) {
712 if (rstreq(s, lang)) {
713 langNum = rpmtdGetIndex(&langtd);
714 break;
715 }
716 }
717 rpmtdFreeData(&langtd);
718 } else {
719 /* C locale */
720 langNum = 0;
721 }
722
723 /* If locale is present, check the actual tag content */
724 if (langNum >= 0) {
725 struct rpmtd_s td;
726 headerGet(h, tag, &td, HEADERGET_MINMEM|HEADERGET_RAW);
727 if (rpmtdSetIndex(&td, langNum) == langNum) {
728 const char *s = rpmtdGetString(&td);
729 /* non-empty string means a dupe */
730 if (s && *s)
731 rc = 1;
732 }
733 rpmtdFreeData(&td);
734 };
735
736 return rc;
737 }
738
addLangTag(rpmSpec spec,Header h,rpmTagVal tag,const char * field,const char * lang)739 int addLangTag(rpmSpec spec, Header h, rpmTagVal tag,
740 const char *field, const char *lang)
741 {
742 int skip = 0;
743
744 if (haveLangTag(h, tag, lang)) {
745 /* Turn this into an error eventually */
746 rpmlog(RPMLOG_WARNING, _("line %d: second %s\n"),
747 spec->lineNum, rpmTagGetName(tag));
748 }
749
750 if (!*lang) {
751 headerPutString(h, tag, field);
752 } else {
753 skip = ((spec->flags & RPMSPEC_NOLANG) &&
754 !rstreq(lang, RPMBUILD_DEFAULT_LANG));
755 if (skip)
756 return 0;
757 headerAddI18NString(h, tag, field, lang);
758 }
759
760 return 0;
761 }
762
handlePreambleTag(rpmSpec spec,Package pkg,rpmTagVal tag,const char * macro,const char * lang)763 static rpmRC handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag,
764 const char *macro, const char *lang)
765 {
766 char * field = spec->line;
767 char * end;
768 int multiToken = 0;
769 rpmsenseFlags tagflags = RPMSENSE_ANY;
770 rpmRC rc = RPMRC_FAIL;
771
772 if (field == NULL) /* XXX can't happen */
773 goto exit;
774 /* Find the start of the "field" and strip trailing space */
775 while ((*field) && (*field != ':'))
776 field++;
777 if (*field != ':') {
778 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
779 spec->lineNum, spec->line);
780 goto exit;
781 }
782 field++;
783 SKIPSPACE(field);
784 if (!*field) {
785 /* Empty field */
786 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
787 spec->lineNum, spec->line);
788 goto exit;
789 }
790 end = findLastChar(field);
791 *(end+1) = '\0';
792
793 /* See if this is multi-token */
794 end = field;
795 SKIPNONSPACE(end);
796 if (*end != '\0')
797 multiToken = 1;
798
799 switch (tag) {
800 case RPMTAG_NAME:
801 SINGLE_TOKEN_ONLY;
802 if (rpmCharCheck(spec, field, WHITELIST_NAME))
803 goto exit;
804 headerPutString(pkg->header, tag, field);
805 /* Main pkg name is unknown at the start, populate as soon as we can */
806 if (pkg == spec->packages)
807 pkg->name = rpmstrPoolId(spec->pool, field, 1);
808 break;
809 case RPMTAG_VERSION:
810 case RPMTAG_RELEASE:
811 SINGLE_TOKEN_ONLY;
812 if (rpmCharCheck(spec, field, WHITELIST_VERREL))
813 goto exit;
814 headerPutString(pkg->header, tag, field);
815 break;
816 case RPMTAG_URL:
817 case RPMTAG_DISTTAG:
818 case RPMTAG_BUGURL:
819 case RPMTAG_MODULARITYLABEL:
820 /* XXX TODO: validate format somehow */
821 case RPMTAG_VCS:
822 SINGLE_TOKEN_ONLY;
823 headerPutString(pkg->header, tag, field);
824 break;
825 case RPMTAG_GROUP:
826 case RPMTAG_SUMMARY:
827 case RPMTAG_DISTRIBUTION:
828 case RPMTAG_VENDOR:
829 case RPMTAG_LICENSE:
830 case RPMTAG_PACKAGER:
831 if (addLangTag(spec, pkg->header, tag, field, lang))
832 goto exit;
833 break;
834 case RPMTAG_BUILDROOT:
835 /* just silently ignore BuildRoot */
836 break;
837 case RPMTAG_PREFIXES: {
838 struct rpmtd_s td;
839 const char *str;
840 if (addOrAppendListEntry(pkg->header, tag, field))
841 goto exit;
842 headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
843 while ((str = rpmtdNextString(&td))) {
844 size_t len = strlen(str);
845 if (len > 1 && str[len-1] == '/') {
846 rpmlog(RPMLOG_ERR,
847 _("line %d: Prefixes must not end with \"/\": %s\n"),
848 spec->lineNum, spec->line);
849 rpmtdFreeData(&td);
850 goto exit;
851 }
852 }
853 rpmtdFreeData(&td);
854 break;
855 }
856 case RPMTAG_DOCDIR:
857 SINGLE_TOKEN_ONLY;
858 if (field[0] != '/') {
859 rpmlog(RPMLOG_ERR, _("line %d: Docdir must begin with '/': %s\n"),
860 spec->lineNum, spec->line);
861 goto exit;
862 }
863 rpmPopMacro(NULL, "_docdir");
864 rpmPushMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
865 break;
866 case RPMTAG_EPOCH: {
867 SINGLE_TOKEN_ONLY;
868 uint32_t epoch;
869 if (parseUnsignedNum(field, &epoch)) {
870 rpmlog(RPMLOG_ERR,
871 _("line %d: Epoch field must be an unsigned number: %s\n"),
872 spec->lineNum, spec->line);
873 goto exit;
874 }
875 headerPutUint32(pkg->header, tag, &epoch, 1);
876 break;
877 }
878 case RPMTAG_AUTOREQPROV:
879 pkg->autoReq = parseYesNo(field);
880 pkg->autoProv = pkg->autoReq;
881 break;
882 case RPMTAG_AUTOREQ:
883 pkg->autoReq = parseYesNo(field);
884 break;
885 case RPMTAG_AUTOPROV:
886 pkg->autoProv = parseYesNo(field);
887 break;
888 case RPMTAG_SOURCE:
889 case RPMTAG_PATCH:
890 if (addSource(spec, 1, field, tag))
891 goto exit;
892 break;
893 case RPMTAG_ICON:
894 SINGLE_TOKEN_ONLY;
895 if (addIcon(pkg, field))
896 goto exit;
897 spec->numSources++;
898 break;
899 case RPMTAG_NOSOURCE:
900 case RPMTAG_NOPATCH:
901 spec->noSource = 1;
902 if (parseNoSource(spec, field, tag))
903 goto exit;
904 break;
905 case RPMTAG_ORDERNAME:
906 case RPMTAG_REQUIRENAME:
907 if (parseBits(lang, installScriptBits, &tagflags)) {
908 rpmlog(RPMLOG_ERR, _("line %d: Bad %s: qualifiers: %s\n"),
909 spec->lineNum, rpmTagGetName(tag), spec->line);
910 goto exit;
911 }
912 /* fallthrough */
913 case RPMTAG_PREREQ:
914 case RPMTAG_RECOMMENDNAME:
915 case RPMTAG_SUGGESTNAME:
916 case RPMTAG_SUPPLEMENTNAME:
917 case RPMTAG_ENHANCENAME:
918 case RPMTAG_CONFLICTNAME:
919 case RPMTAG_OBSOLETENAME:
920 case RPMTAG_PROVIDENAME:
921 if (parseRCPOT(spec, pkg, field, tag, 0, tagflags, addReqProvPkg, NULL))
922 goto exit;
923 break;
924 case RPMTAG_BUILDPREREQ:
925 case RPMTAG_BUILDREQUIRES:
926 case RPMTAG_BUILDCONFLICTS:
927 if (parseRCPOT(spec, spec->sourcePackage, field, tag, 0, tagflags, addReqProvPkg, NULL))
928 goto exit;
929 break;
930 case RPMTAG_EXCLUDEARCH:
931 case RPMTAG_EXCLUSIVEARCH:
932 case RPMTAG_EXCLUDEOS:
933 case RPMTAG_EXCLUSIVEOS:
934 if (addOrAppendListEntry(spec->buildRestrictions, tag, field))
935 goto exit;
936 break;
937 case RPMTAG_BUILDARCHS: {
938 int BACount;
939 const char **BANames = NULL;
940 if (poptParseArgvString(field, &BACount, &BANames)) {
941 rpmlog(RPMLOG_ERR,
942 _("line %d: Bad BuildArchitecture format: %s\n"),
943 spec->lineNum, spec->line);
944 goto exit;
945 }
946 if (spec->packages == pkg) {
947 if (spec->BANames) {
948 rpmlog(RPMLOG_ERR,
949 _("line %d: Duplicate BuildArch entry: %s\n"),
950 spec->lineNum, spec->line);
951 BANames = _free(BANames);
952 goto exit;
953 }
954 spec->BACount = BACount;
955 spec->BANames = BANames;
956 } else {
957 if (BACount != 1 || !rstreq(BANames[0], "noarch")) {
958 rpmlog(RPMLOG_ERR,
959 _("line %d: Only noarch subpackages are supported: %s\n"),
960 spec->lineNum, spec->line);
961 BANames = _free(BANames);
962 goto exit;
963 }
964 headerPutString(pkg->header, RPMTAG_ARCH, "noarch");
965 }
966 if (!BACount)
967 spec->BANames = _free(spec->BANames);
968 break;
969 }
970 case RPMTAG_REMOVEPATHPOSTFIXES:
971 argvSplit(&pkg->removePostfixes, field, ":");
972 break;
973 default:
974 rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
975 goto exit;
976 }
977
978 if (macro) {
979 rpmPushMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
980 /* Add a separate uppercase macro for tags from the main package */
981 if (pkg == spec->packages) {
982 char *m = xstrdup(macro);
983 for (char *p = m; *p; ++p)
984 *p = rtoupper(*p);
985 rpmPushMacro(spec->macros, m, NULL, field, RMIL_SPEC);
986 free(m);
987 }
988 }
989 rc = RPMRC_OK;
990 exit:
991 return rc;
992 }
993
994 /* This table has to be in a peculiar order. If one tag is the */
995 /* same as another, plus a few letters, it must come first. */
996
997 /**
998 */
999 typedef const struct PreambleRec_s {
1000 rpmTagVal tag;
1001 int type;
1002 int deprecated;
1003 int ismacro;
1004 size_t len;
1005 const char * token;
1006 } * PreambleRec;
1007
1008 static struct PreambleRec_s const preambleList[] = {
1009 {RPMTAG_NAME, 0, 0, 1, LEN_AND_STR("name")},
1010 {RPMTAG_VERSION, 0, 0, 1, LEN_AND_STR("version")},
1011 {RPMTAG_RELEASE, 0, 0, 1, LEN_AND_STR("release")},
1012 {RPMTAG_EPOCH, 0, 0, 1, LEN_AND_STR("epoch")},
1013 {RPMTAG_SUMMARY, 1, 0, 1, LEN_AND_STR("summary")},
1014 {RPMTAG_LICENSE, 0, 0, 1, LEN_AND_STR("license")},
1015 {RPMTAG_DISTRIBUTION, 0, 0, 1, LEN_AND_STR("distribution")},
1016 {RPMTAG_DISTURL, 0, 0, 1, LEN_AND_STR("disturl")},
1017 {RPMTAG_VENDOR, 0, 0, 1, LEN_AND_STR("vendor")},
1018 {RPMTAG_GROUP, 1, 0, 1, LEN_AND_STR("group")},
1019 {RPMTAG_PACKAGER, 0, 0, 1, LEN_AND_STR("packager")},
1020 {RPMTAG_URL, 0, 0, 1, LEN_AND_STR("url")},
1021 {RPMTAG_VCS, 0, 0, 1, LEN_AND_STR("vcs")},
1022 {RPMTAG_SOURCE, 0, 0, 0, LEN_AND_STR("source")},
1023 {RPMTAG_PATCH, 0, 0, 0, LEN_AND_STR("patch")},
1024 {RPMTAG_NOSOURCE, 0, 0, 0, LEN_AND_STR("nosource")},
1025 {RPMTAG_NOPATCH, 0, 0, 0, LEN_AND_STR("nopatch")},
1026 {RPMTAG_EXCLUDEARCH, 0, 0, 0, LEN_AND_STR("excludearch")},
1027 {RPMTAG_EXCLUSIVEARCH, 0, 0, 0, LEN_AND_STR("exclusivearch")},
1028 {RPMTAG_EXCLUDEOS, 0, 0, 0, LEN_AND_STR("excludeos")},
1029 {RPMTAG_EXCLUSIVEOS, 0, 0, 0, LEN_AND_STR("exclusiveos")},
1030 {RPMTAG_ICON, 0, 0, 0, LEN_AND_STR("icon")},
1031 {RPMTAG_PROVIDENAME, 0, 0, 0, LEN_AND_STR("provides")},
1032 {RPMTAG_REQUIRENAME, 2, 0, 0, LEN_AND_STR("requires")},
1033 {RPMTAG_RECOMMENDNAME, 0, 0, 0, LEN_AND_STR("recommends")},
1034 {RPMTAG_SUGGESTNAME, 0, 0, 0, LEN_AND_STR("suggests")},
1035 {RPMTAG_SUPPLEMENTNAME, 0, 0, 0, LEN_AND_STR("supplements")},
1036 {RPMTAG_ENHANCENAME, 0, 0, 0, LEN_AND_STR("enhances")},
1037 {RPMTAG_PREREQ, 2, 1, 0, LEN_AND_STR("prereq")},
1038 {RPMTAG_CONFLICTNAME, 0, 0, 0, LEN_AND_STR("conflicts")},
1039 {RPMTAG_OBSOLETENAME, 0, 0, 0, LEN_AND_STR("obsoletes")},
1040 {RPMTAG_PREFIXES, 0, 0, 1, LEN_AND_STR("prefixes")},
1041 {RPMTAG_PREFIXES, 0, 0, 1, LEN_AND_STR("prefix")},
1042 {RPMTAG_BUILDROOT, 0, 0, 0, LEN_AND_STR("buildroot")},
1043 {RPMTAG_BUILDARCHS, 0, 0, 0, LEN_AND_STR("buildarchitectures")},
1044 {RPMTAG_BUILDARCHS, 0, 0, 0, LEN_AND_STR("buildarch")},
1045 {RPMTAG_BUILDCONFLICTS, 0, 0, 0, LEN_AND_STR("buildconflicts")},
1046 {RPMTAG_BUILDPREREQ, 0, 1, 0, LEN_AND_STR("buildprereq")},
1047 {RPMTAG_BUILDREQUIRES, 0, 0, 0, LEN_AND_STR("buildrequires")},
1048 {RPMTAG_AUTOREQPROV, 0, 0, 0, LEN_AND_STR("autoreqprov")},
1049 {RPMTAG_AUTOREQ, 0, 0, 0, LEN_AND_STR("autoreq")},
1050 {RPMTAG_AUTOPROV, 0, 0, 0, LEN_AND_STR("autoprov")},
1051 {RPMTAG_DOCDIR, 0, 0, 0, LEN_AND_STR("docdir")},
1052 {RPMTAG_DISTTAG, 0, 0, 1, LEN_AND_STR("disttag")},
1053 {RPMTAG_BUGURL, 0, 0, 1, LEN_AND_STR("bugurl")},
1054 {RPMTAG_ORDERNAME, 2, 0, 0, LEN_AND_STR("orderwithrequires")},
1055 {RPMTAG_REMOVEPATHPOSTFIXES,0, 0, 1, LEN_AND_STR("removepathpostfixes")},
1056 {RPMTAG_MODULARITYLABEL, 0, 0, 1, LEN_AND_STR("modularitylabel")},
1057 {0, 0, 0, 0}
1058 };
1059
1060 /**
1061 */
findPreambleTag(rpmSpec spec,rpmTagVal * tag,const char ** macro,char * lang)1062 static int findPreambleTag(rpmSpec spec,rpmTagVal * tag,
1063 const char ** macro, char * lang)
1064 {
1065 PreambleRec p;
1066 char *s;
1067
1068 for (p = preambleList; p->token != NULL; p++) {
1069 if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
1070 continue;
1071 if (p->deprecated) {
1072 rpmlog(RPMLOG_WARNING, _("line %d: %s is deprecated: %s\n"),
1073 spec->lineNum, p->token, spec->line);
1074 }
1075 break;
1076 }
1077 if (p == NULL || p->token == NULL)
1078 return 1;
1079
1080 s = spec->line + p->len;
1081 SKIPSPACE(s);
1082
1083 switch (p->type) {
1084 default:
1085 case 0:
1086 /* Unless this is a source or a patch, a ':' better be next */
1087 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
1088 if (*s != ':') return 1;
1089 }
1090 *lang = '\0';
1091 break;
1092 case 1: /* Parse optional ( <token> ). */
1093 case 2:
1094 if (*s == ':') {
1095 /* Type 1 is multilang, 2 is qualifiers with no defaults */
1096 strcpy(lang, (p->type == 1) ? RPMBUILD_DEFAULT_LANG : "");
1097 break;
1098 }
1099 if (*s != '(') return 1;
1100 s++;
1101 SKIPSPACE(s);
1102 while (!risspace(*s) && *s != ')')
1103 *lang++ = *s++;
1104 *lang = '\0';
1105 SKIPSPACE(s);
1106 if (*s != ')') return 1;
1107 s++;
1108 SKIPSPACE(s);
1109 if (*s != ':') return 1;
1110 break;
1111 }
1112
1113 *tag = p->tag;
1114 *macro = p->ismacro ? p->token : NULL;
1115 return 0;
1116 }
1117
parsePreamble(rpmSpec spec,int initialPackage)1118 int parsePreamble(rpmSpec spec, int initialPackage)
1119 {
1120 int nextPart = PART_ERROR;
1121 int res = PART_ERROR; /* assume failure */
1122 int rc;
1123 char *linep;
1124 int flag = 0;
1125 Package pkg;
1126 char *name = NULL;
1127 char *NVR = NULL;
1128 char lang[BUFSIZ];
1129
1130 if (! initialPackage) {
1131 /* There is one option to %package: <pkg> or -n <pkg> */
1132 if (parseSimplePart(spec->line, &name, &flag)) {
1133 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
1134 spec->line);
1135 goto exit;
1136 }
1137
1138 if (rpmCharCheck(spec, name, WHITELIST_NAME))
1139 goto exit;
1140
1141 if (!lookupPackage(spec, name, flag, NULL))
1142 goto exit;
1143
1144 /* Construct the package */
1145 if (flag == PART_SUBNAME) {
1146 rasprintf(&NVR, "%s-%s",
1147 headerGetString(spec->packages->header, RPMTAG_NAME), name);
1148 } else
1149 NVR = xstrdup(name);
1150 pkg = newPackage(NVR, spec->pool, &spec->packages);
1151 headerPutString(pkg->header, RPMTAG_NAME, NVR);
1152 } else {
1153 NVR = xstrdup("(main package)");
1154 pkg = newPackage(NULL, spec->pool, &spec->packages);
1155 spec->sourcePackage = newPackage(NULL, spec->pool, NULL);
1156
1157 }
1158
1159 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
1160 nextPart = PART_NONE;
1161 } else if (rc < 0) {
1162 goto exit;
1163 } else {
1164 while (! (nextPart = isPart(spec->line))) {
1165 const char * macro;
1166 rpmTagVal tag;
1167
1168 /* Skip blank lines */
1169 linep = spec->line;
1170 SKIPSPACE(linep);
1171 if (*linep != '\0') {
1172 if (findPreambleTag(spec, &tag, ¯o, lang)) {
1173 if (spec->lineNum == 1 &&
1174 (unsigned char)(spec->line[0]) == 0xed &&
1175 (unsigned char)(spec->line[1]) == 0xab &&
1176 (unsigned char)(spec->line[2]) == 0xee &&
1177 (unsigned char)(spec->line[3]) == 0xdb) {
1178 rpmlog(RPMLOG_ERR, _("Binary rpm package found. Expected spec file!\n"));
1179 goto exit;
1180 }
1181 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
1182 spec->lineNum, spec->line);
1183 goto exit;
1184 }
1185 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
1186 goto exit;
1187 }
1188 if (spec->BANames && !spec->recursing) {
1189 res = PART_BUILDARCHITECTURES;
1190 goto exit;
1191 }
1192 }
1193 if ((rc =
1194 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
1195 nextPart = PART_NONE;
1196 break;
1197 }
1198 if (rc) {
1199 goto exit;
1200 }
1201 }
1202 }
1203
1204 /*
1205 * Expand buildroot one more time to get %{version} and the like
1206 * from the main package, validate sanity. The spec->buildRoot could
1207 * still contain unexpanded macros but it cannot be empty or '/', and it
1208 * can't be messed with by anything spec does beyond this point.
1209 */
1210 if (initialPackage) {
1211 char *buildRoot = rpmGetPath(spec->buildRoot, NULL);
1212 free(spec->buildRoot);
1213 spec->buildRoot = buildRoot;
1214 rpmPushMacro(spec->macros, "buildroot", NULL, spec->buildRoot, RMIL_SPEC);
1215 if (*buildRoot == '\0') {
1216 rpmlog(RPMLOG_ERR, _("%%{buildroot} couldn't be empty\n"));
1217 goto exit;
1218 }
1219 if (rstreq(buildRoot, "/")) {
1220 rpmlog(RPMLOG_ERR, _("%%{buildroot} can not be \"/\"\n"));
1221 goto exit;
1222 }
1223 }
1224
1225 /* XXX Skip valid arch check if not building binary package */
1226 if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) {
1227 goto exit;
1228 }
1229
1230 /* It is the main package */
1231 if (pkg == spec->packages) {
1232 fillOutMainPackage(pkg->header);
1233 /* Define group tag to something when group is undefined in main package*/
1234 if (!headerIsEntry(pkg->header, RPMTAG_GROUP)) {
1235 headerPutString(pkg->header, RPMTAG_GROUP, "Unspecified");
1236 }
1237 }
1238
1239 if (checkForDuplicates(pkg->header, NVR)) {
1240 goto exit;
1241 }
1242
1243 if (pkg != spec->packages) {
1244 copyInheritedTags(pkg->header, spec->packages->header);
1245 }
1246
1247 if (checkForRequired(pkg->header, NVR)) {
1248 goto exit;
1249 }
1250
1251 /* if we get down here nextPart has been set to non-error */
1252 res = nextPart;
1253
1254 exit:
1255 free(name);
1256 free(NVR);
1257 return res;
1258 }
1259