607764be71a758dc1c07cb6dc0ee672cac11c796
[platform/upstream/rpm.git] / build / pack.c
1 /* The very final packaging steps */
2
3 #include "miscfn.h"
4
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <sys/time.h>
9 #include <sys/resource.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <signal.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <errno.h>
17
18 #include <sys/time.h>  /* For 'select()' interfaces */
19
20 #include "pack.h"
21 #include "header.h"
22 #include "spec.h"
23 #include "specP.h"
24 #include "signature.h"
25 #include "rpmlead.h"
26 #include "rpmlib.h"
27 #include "misc.h"
28 #include "stringbuf.h"
29 #include "names.h"
30 #include "files.h"
31 #include "reqprov.h"
32 #include "trigger.h"
33 #include "messages.h"
34
35 static int writeMagic(int fd, char *name, unsigned short type);
36 static int cpio_gzip(int fd, char *tempdir, char *writePtr,
37                      int *archiveSize, char *prefix);
38 static int generateRPM(char *name,       /* name-version-release         */
39                        char *filename,   /* output filename              */
40                        int type,         /* source or binary             */
41                        Header header,    /* the header                   */
42                        char *stempdir,   /* directory containing sources */
43                        char *fileList,   /* list of files for cpio       */
44                        char *passPhrase, /* PGP passphrase               */
45                        char *prefix);
46
47
48 static int generateRPM(char *name,       /* name-version-release         */
49                        char *filename,   /* output filename              */
50                        int type,         /* source or binary             */
51                        Header header,    /* the header                   */
52                        char *stempdir,   /* directory containing sources */
53                        char *fileList,   /* list of files for cpio       */
54                        char *passPhrase,
55                        char *prefix)
56 {
57     int_32 sigtype;
58     char *sigtarget;
59     int fd, ifd, count, archiveSize;
60     unsigned char buffer[8192];
61     Header sig;
62
63     /* Add the a bogus archive size to the Header */
64     headerAddEntry(header, RPMTAG_ARCHIVESIZE, RPM_INT32_TYPE,
65                    &archiveSize, 1);
66
67     /* Write the header */
68     sigtarget = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
69     if ((fd = open(sigtarget, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
70         fprintf(stderr, "Could not open %s\n", sigtarget);
71         return 1;
72     }
73     headerWrite(fd, header, HEADER_MAGIC_YES);
74     
75     /* Write the archive and get the size */
76     if (cpio_gzip(fd, stempdir, fileList, &archiveSize, prefix)) {
77         close(fd);
78         unlink(sigtarget);
79         return 1;
80     }
81
82     /* Now set the real archive size in the Header */
83     headerModifyEntry(header, RPMTAG_ARCHIVESIZE,
84                       RPM_INT32_TYPE, &archiveSize, 1);
85
86     /* Rewind and rewrite the Header */
87     lseek(fd, 0,  SEEK_SET);
88     headerWrite(fd, header, HEADER_MAGIC_YES);
89     
90     close(fd);
91     
92     /* Now write the lead */
93     if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
94         fprintf(stderr, "Could not open %s\n", filename);
95         unlink(sigtarget);
96         unlink(filename);
97         return 1;
98     }
99     if (writeMagic(fd, name, type)) {
100         close(fd);
101         unlink(sigtarget);
102         unlink(filename);
103         return 1;
104     }
105
106     /* Generate the signature */
107     sigtype = rpmLookupSignatureType();
108     rpmMessage(RPMMESS_VERBOSE, "Generating signature: %d\n", sigtype);
109     fflush(stdout);
110     sig = rpmNewSignature();
111     rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase);
112     rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase);
113     if (sigtype>0) {
114         rpmAddSignature(sig, sigtarget, sigtype, passPhrase);
115     }
116     if (rpmWriteSignature(fd, sig)) {
117         close(fd);
118         unlink(sigtarget);
119         unlink(filename);
120         rpmFreeSignature(sig);
121         return 1;
122     }
123     rpmFreeSignature(sig);
124
125     /* Append the header and archive */
126     ifd = open(sigtarget, O_RDONLY);
127     while ((count = read(ifd, buffer, sizeof(buffer))) > 0) {
128         if (count == -1) {
129             perror("Couldn't read sigtarget");
130             close(ifd);
131             close(fd);
132             unlink(sigtarget);
133             unlink(filename);
134             return 1;
135         }
136         if (write(fd, buffer, count) < 0) {
137             perror("Couldn't write package");
138             close(ifd);
139             close(fd);
140             unlink(sigtarget);
141             unlink(filename);
142             return 1;
143         }
144     }
145     close(ifd);
146     close(fd);
147     unlink(sigtarget);
148
149     rpmMessage(RPMMESS_VERBOSE, "Wrote: %s\n", filename);
150     
151     return 0;
152 }
153
154 static int writeMagic(int fd, char *name,
155                       unsigned short type)
156 {
157     struct rpmlead lead;
158
159     /* There are the Major and Minor numbers */
160     lead.major = 3;
161     lead.minor = 0;
162     lead.type = type;
163     lead.archnum = rpmGetArchNum();
164     lead.osnum = rpmGetOsNum();
165     lead.signature_type = RPMSIG_HEADERSIG;  /* New-style signature */
166     strncpy(lead.name, name, sizeof(lead.name));
167
168     writeLead(fd, &lead);
169
170     return 0;
171 }
172
173 static int cpio_gzip(int fd, char *tempdir, char *writePtr,
174                      int *archiveSize, char *prefix)
175 {
176     int cpioPID, gzipPID;
177     int cpioDead, gzipDead;
178     int toCpio[2];
179     int fromCpio[2];
180     int toGzip[2];
181     char * cpiobin;
182     char * gzipbin;
183
184     int writeBytesLeft, bytesWritten;
185
186     int bytes;
187     unsigned char buf[8192];
188     fd_set read_fds, read_ok, write_fds, write_ok;
189     int num_fds, max_fd;
190     struct stat fd_info;
191
192     int status;
193     void *oldhandler;
194
195     cpiobin = rpmGetVar(RPMVAR_CPIOBIN);
196     gzipbin = rpmGetVar(RPMVAR_GZIPBIN);
197  
198     *archiveSize = 0;
199     
200     pipe(toCpio);
201     pipe(fromCpio);
202     
203     oldhandler = signal(SIGPIPE, SIG_IGN);
204
205     /* CPIO */
206     if (!(cpioPID = fork())) {
207         close(0);
208         close(1);
209         close(toCpio[1]);
210         close(fromCpio[0]);
211         close(fd);
212         
213         dup2(toCpio[0], 0);   /* Make stdin the in pipe */
214         dup2(fromCpio[1], 1); /* Make stdout the out pipe */
215
216         if (tempdir) {
217             chdir(tempdir);
218         } else if (rpmGetVar(RPMVAR_ROOT)) {
219             if (chdir(rpmGetVar(RPMVAR_ROOT))) {
220                 rpmError(RPMERR_EXEC, "Couldn't chdir to %s",
221                       rpmGetVar(RPMVAR_ROOT));
222                 exit(RPMERR_EXEC);
223             }
224         } else {
225             /* This is important! */
226             chdir("/");
227         }
228         if (prefix) {
229             if (chdir(prefix)) {
230                 rpmError(RPMERR_EXEC, "Couldn't chdir to %s", prefix);
231                 _exit(RPMERR_EXEC);
232             }
233         }
234
235         execlp(cpiobin, cpiobin,
236                (rpmIsVerbose()) ? "-ov" : "-o",
237                (tempdir) ? "-LH" : "-H",
238                "crc", NULL);
239         rpmError(RPMERR_EXEC, "Couldn't exec cpio");
240         _exit(RPMERR_EXEC);
241     }
242     if (cpioPID < 0) {
243         rpmError(RPMERR_FORK, "Couldn't fork cpio");
244         return RPMERR_FORK;
245     }
246
247     pipe(toGzip);
248     
249     /* GZIP */
250     if (!(gzipPID = fork())) {
251         close(0);
252         close(1);
253         close(toGzip[1]);
254         close(toCpio[0]);
255         close(toCpio[1]);
256         close(fromCpio[0]);
257         close(fromCpio[1]);
258         
259         dup2(toGzip[0], 0);  /* Make stdin the in pipe       */
260         dup2(fd, 1);         /* Make stdout the passed-in fd */
261
262         execlp(gzipbin, gzipbin, "-c9fn", NULL);
263         rpmError(RPMERR_EXEC, "Couldn't exec gzip");
264         _exit(RPMERR_EXEC);
265     }
266     if (gzipPID < 0) {
267         rpmError(RPMERR_FORK, "Couldn't fork gzip");
268         return RPMERR_FORK;
269     }
270
271     close(toCpio[0]);
272     close(fromCpio[1]);
273     close(toGzip[0]);
274
275     /* It is OK to block writing to gzip.  But it is not OK */
276     /* to block reading or writing from/to cpio.            */
277     fcntl(fromCpio[0], F_SETFL, O_NONBLOCK);
278     fcntl(toCpio[1], F_SETFL, O_NONBLOCK);
279     writeBytesLeft = strlen(writePtr);
280
281     /* Set up to use 'select()' to multiplex this I/O stream */
282     FD_ZERO(&read_fds);
283     FD_SET(fromCpio[0], &read_fds);
284     max_fd = fromCpio[0];
285     FD_ZERO(&write_fds);
286     FD_SET(toCpio[1], &write_fds);
287     if (toCpio[1] > max_fd) max_fd = toCpio[1];
288     
289     cpioDead = 0;
290     gzipDead = 0;
291     bytes = 0;
292     do {
293         if (waitpid(cpioPID, &status, WNOHANG)) {
294             cpioDead = 1;
295         }
296         if (waitpid(gzipPID, &status, WNOHANG)) {
297             gzipDead = 1;
298         }
299
300         /* Pause here until we could perform some I/O */
301         read_ok = read_fds;
302         write_ok = write_fds;
303         if ((num_fds = select(max_fd+1, &read_ok, &write_ok, 
304                               (fd_set *)NULL, (struct timeval *)NULL)) < 0) {
305                 /* One or more file connections has broken */
306                 if (fstat(fromCpio[0], &fd_info) < 0) {
307                         FD_CLR(fromCpio[0], &read_fds);
308                 }
309                 if (fstat(toCpio[1], &fd_info) < 0) {
310                         FD_CLR(toCpio[1], &write_fds);
311                 }
312                 continue;
313         }
314
315         /* Write some stuff to the cpio process if possible */
316         if (FD_ISSET(toCpio[1], &write_ok)) {
317                 if (writeBytesLeft) {
318                         if ((bytesWritten =
319                              write(toCpio[1], writePtr,
320                                    (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
321                                 if (errno != EAGAIN) {
322                                         perror("Damn!");
323                                         exit(1);
324                                 }
325                                 bytesWritten = 0;
326                         }
327                         writeBytesLeft -= bytesWritten;
328                         writePtr += bytesWritten;
329                 } else {
330                         close(toCpio[1]);
331                         FD_CLR(toCpio[1], &write_fds);
332                 }
333         }
334         
335         /* Read any data from cpio, write it to gzip */
336         bytes = 0;  /* So end condition works OK */
337         if (FD_ISSET(fromCpio[0], &read_ok)) {
338                 bytes = read(fromCpio[0], buf, sizeof(buf));
339                 while (bytes > 0) {
340                         *archiveSize += bytes;
341                         write(toGzip[1], buf, bytes);
342                         bytes = read(fromCpio[0], buf, sizeof(buf));
343                 }
344         }
345
346         /* while cpio is running, or we are writing to gzip */
347         /* terminate if gzip dies on us in the middle       */
348     } while (((!cpioDead) || bytes) && (!gzipDead));
349
350     if (gzipDead) {
351         rpmError(RPMERR_GZIP, "gzip died");
352         return 1;
353     }
354     
355     close(toGzip[1]); /* Terminates the gzip process */
356     close(toCpio[1]);
357     close(fromCpio[0]);
358     
359     signal(SIGPIPE, oldhandler);
360
361     if (writeBytesLeft) {
362         rpmError(RPMERR_CPIO, "failed to write all data to cpio");
363         return 1;
364     }
365     waitpid(cpioPID, &status, 0);
366     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
367         rpmError(RPMERR_CPIO, "cpio failed");
368         return 1;
369     }
370     waitpid(gzipPID, &status, 0);
371     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
372         rpmError(RPMERR_GZIP, "gzip failed");
373         return 1;
374     }
375
376     return 0;
377 }
378
379
380 int packageBinaries(Spec s, char *passPhrase, int doPackage)
381 {
382     char name[1024];
383     char *nametmp;
384     char filename[1024];
385     char sourcerpm[1024];
386     char *icon;
387     int iconFD;
388     struct stat statbuf;
389     struct PackageRec *pr;
390     Header outHeader;
391     HeaderIterator headerIter;
392     int_32 tag, type, c;
393     void *ptr;
394     char *version;
395     char *release;
396     char *vendor;
397     char *dist;
398     char *packager;
399     char *packageVersion, *packageRelease;
400     char *prefix, *prefixSave;
401     int prefixLen;
402     int size;
403     StringBuf cpioFileList;
404     char **farray, *file;
405     int count;
406     
407     if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
408                   (void *) &version, NULL)) {
409         rpmError(RPMERR_BADSPEC, "No version field");
410         return RPMERR_BADSPEC;
411     }
412     if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
413                   (void *) &release, NULL)) {
414         rpmError(RPMERR_BADSPEC, "No release field");
415         return RPMERR_BADSPEC;
416     }
417     /* after the headerGetEntry() these are just pointers into the   */
418     /* header structure, which can be moved around by headerAddEntry */
419     version = strdup(version);
420     release = strdup(release);
421
422     sprintf(sourcerpm, "%s-%s-%s.%ssrc.rpm", s->name, version, release,
423             (s->numNoPatch + s->numNoSource) ? "no" : "");
424
425     vendor = NULL;
426     if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
427         vendor = rpmGetVar(RPMVAR_VENDOR);
428     }
429     dist = NULL;
430     if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
431         dist = rpmGetVar(RPMVAR_DISTRIBUTION);
432     }
433     packager = NULL;
434     if (!headerIsEntry(s->packages->header, RPMTAG_PACKAGER)) {
435         packager = rpmGetVar(RPMVAR_PACKAGER);
436     }
437
438     /* Look through for each package */
439     pr = s->packages;
440     while (pr) {
441         /* A file count of -1 means no package */
442         if (pr->files == -1) {
443             pr = pr->next;
444             continue;
445         }
446
447         /* Handle subpackage version/release overrides */
448         if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
449                       (void *) &packageVersion, NULL)) {
450             packageVersion = version;
451         }
452         if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
453                       (void *) &packageRelease, NULL)) {
454             packageRelease = release;
455         }
456         /* after the headerGetEntry() these are just pointers into the   */
457         /* header structure, which can be moved around by headerAddEntry */
458         packageVersion = strdup(packageVersion);
459         packageRelease = strdup(packageRelease);
460         
461         /* Figure out the name of this package */
462         if (!headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *)&nametmp, NULL)) {
463             rpmError(RPMERR_INTERNAL, "Package has no name!");
464             return RPMERR_INTERNAL;
465         }
466         sprintf(name, "%s-%s-%s", nametmp, packageVersion, packageRelease);
467
468         if (doPackage == PACK_PACKAGE) {
469             rpmMessage(RPMMESS_VERBOSE, "Binary Packaging: %s\n", name);
470         } else {
471             rpmMessage(RPMMESS_VERBOSE, "File List Check: %s\n", name);
472         }
473        
474         /**** Generate the Header ****/
475         
476         /* Here's the plan: copy the package's header,  */
477         /* then add entries from the primary header     */
478         /* that don't already exist.                    */
479         outHeader = headerCopy(pr->header);
480         headerIter = headerInitIterator(s->packages->header);
481         while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
482             /* Some tags we don't copy */
483             switch (tag) {
484               case RPMTAG_PREIN:
485               case RPMTAG_POSTIN:
486               case RPMTAG_PREUN:
487               case RPMTAG_POSTUN:
488               case RPMTAG_VERIFYSCRIPT:
489                   continue;
490                   break;  /* Shouldn't need this */
491               default:
492                   if (! headerIsEntry(outHeader, tag)) {
493                       headerAddEntry(outHeader, tag, type, ptr, c);
494                   }
495             }
496         }
497         headerFreeIterator(headerIter);
498         
499         /* Add some final entries to the header */
500         headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
501         headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
502         headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
503         headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
504         headerAddEntry(outHeader, RPMTAG_SOURCERPM, RPM_STRING_TYPE, sourcerpm, 1);
505         headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
506         if (pr->icon) {
507             sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), pr->icon);
508             stat(filename, &statbuf);
509             icon = malloc(statbuf.st_size);
510             iconFD = open(filename, O_RDONLY, 0644);
511             read(iconFD, icon, statbuf.st_size);
512             close(iconFD);
513             if (! strncmp(icon, "GIF", 3)) {
514                 headerAddEntry(outHeader, RPMTAG_GIF, RPM_BIN_TYPE,
515                          icon, statbuf.st_size);
516             } else if (! strncmp(icon, "/* XPM", 6)) {
517                 headerAddEntry(outHeader, RPMTAG_XPM, RPM_BIN_TYPE,
518                          icon, statbuf.st_size);
519             } else {
520                rpmError(RPMERR_BADSPEC, "Unknown icon type");
521                return 1;
522             }
523             free(icon);
524         }
525         if (vendor) {
526             headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
527         }
528         if (dist) {
529             headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
530         }
531         if (packager) {
532             headerAddEntry(outHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, packager, 1);
533         }
534         
535         /**** Process the file list ****/
536
537         prefixSave = prefix = NULL;
538         prefixLen = 0;
539         if (headerGetEntry(outHeader, RPMTAG_DEFAULTPREFIX,
540                      NULL, (void **)&prefix, NULL)) {
541             /* after the headerGetEntry() this is just pointers into the     */
542             /* header structure, which can be moved around by headerAddEntry */
543             prefixSave = prefix = strdup(prefix);
544             while (*prefix && (*prefix == '/')) {
545                 prefix++;
546             }
547             if (! *prefix) {
548                 prefix = NULL;
549                 prefixLen = 0;
550             } else {
551                 prefixLen = strlen(prefix);
552                 rpmMessage(RPMMESS_VERBOSE, "Package Prefix = %s\n", prefix);
553             }
554         }
555         
556         if (process_filelist(outHeader, pr, pr->filelist, &size, nametmp,
557                              packageVersion, packageRelease, RPMLEAD_BINARY,
558                              prefix, NULL)) {
559             return 1;
560         }
561
562         if (!headerGetEntry(outHeader, RPMTAG_FILENAMES, NULL, (void **) &farray,
563                       &count)) {
564             /* count may already be 0, but this is safer */
565             count = 0;
566         }
567
568         cpioFileList = newStringBuf();
569         while (count--) {
570             file = *farray++;
571             while (*file == '/') {
572                 file++;  /* Skip leading "/" */
573             }
574             if (prefix) {
575                 if (strncmp(prefix, file, prefixLen)) {
576                     rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
577                           prefix, file);
578                     return 1;
579                 }
580                 file += prefixLen + 1; /* 1 for "/" */
581             }
582             appendLineStringBuf(cpioFileList, file);
583         }
584         
585         /* Generate any automatic require/provide entries */
586         /* Then add the whole thing to the header         */
587         if (s->autoReqProv) {
588             generateAutoReqProv(outHeader, pr);
589         }
590         processReqProv(outHeader, pr);
591
592         /* Generate the any trigger entries */
593         generateTriggerEntries(outHeader, pr);
594         
595         /* And add the final Header entry */
596         headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
597
598         /**** Make the RPM ****/
599
600         /* Make the output RPM filename */
601         if (doPackage == PACK_PACKAGE) {
602             sprintf(filename, "%s/%s/%s.%s.rpm", rpmGetVar(RPMVAR_RPMDIR),
603                     rpmGetArchName(), name, rpmGetArchName());
604
605             if (generateRPM(name, filename, RPMLEAD_BINARY, outHeader, NULL,
606                             getStringBuf(cpioFileList), passPhrase, prefix)) {
607                 /* Build failed */
608                 return 1;
609             }
610         }
611
612         freeStringBuf(cpioFileList);
613         headerFree(outHeader);
614        
615         if (prefixSave)
616             free(prefixSave);
617         free(packageVersion);
618         free(packageRelease);
619        
620         pr = pr->next;
621     }
622
623     free(version);
624     free(release);
625    
626     return 0;
627 }
628
629 /**************** SOURCE PACKAGING ************************/
630
631 int packageSource(Spec s, char *passPhrase)
632 {
633     struct sources *source;
634     struct PackageRec *package;
635     char *tempdir;
636     char src[1024], dest[1024], fullname[1024], filename[1024], specFile[1024];
637     char *version;
638     char *release;
639     char *vendor;
640     char *dist;
641     char *p;
642     Header outHeader;
643     StringBuf filelist;
644     StringBuf cpioFileList;
645     int size;
646     char **sources;
647     char **patches;
648     int scount, pcount;
649     int skipi;
650     int_32 *skip;
651
652     /**** Create links for all the sources ****/
653     
654     tempdir = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
655     mkdir(tempdir, 0700);
656
657     filelist = newStringBuf();     /* List in the header */
658     cpioFileList = newStringBuf(); /* List for cpio      */
659
660     sources = malloc((s->numSources+1) * sizeof(char *));
661     patches = malloc((s->numPatches+1) * sizeof(char *));
662     
663     /* Link in the spec file and all the sources */
664     p = strrchr(s->specfile, '/');
665     sprintf(dest, "%s%s", tempdir, p);
666     strcpy(specFile, dest);
667     symlink(s->specfile, dest);
668     appendLineStringBuf(filelist, dest);
669     appendLineStringBuf(cpioFileList, p+1);
670     source = s->sources;
671     scount = 0;
672     pcount = 0;
673     while (source) {
674         if (source->ispatch) {
675             skipi = s->numNoPatch - 1;
676             skip = s->noPatch;
677         } else {
678             skipi = s->numNoSource - 1;
679             skip = s->noSource;
680         }
681         while (skipi >= 0) {
682             if (skip[skipi] == source->num) {
683                 break;
684             }
685             skipi--;
686         }
687         sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
688         sprintf(dest, "%s/%s", tempdir, source->source);
689         if (skipi < 0) {
690             symlink(src, dest);
691             appendLineStringBuf(cpioFileList, source->source);
692         } else {
693             rpmMessage(RPMMESS_VERBOSE, "Skipping source/patch (%d): %s\n",
694                     source->num, source->source);
695         }
696         appendLineStringBuf(filelist, src);
697         if (source->ispatch) {
698             patches[pcount++] = source->fullSource;
699         } else {
700             sources[scount++] = source->fullSource;
701         }
702         source = source->next;
703     }
704     /* ... and icons */
705     package = s->packages;
706     while (package) {
707         if (package->icon) {
708             sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
709             sprintf(dest, "%s/%s", tempdir, package->icon);
710             appendLineStringBuf(filelist, dest);
711             appendLineStringBuf(cpioFileList, package->icon);
712             symlink(src, dest);
713         }
714         package = package->next;
715     }
716
717     /**** Generate the Header ****/
718     
719     if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
720                   (void *) &version, NULL)) {
721         rpmError(RPMERR_BADSPEC, "No version field");
722         return RPMERR_BADSPEC;
723     }
724     if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
725                   (void *) &release, NULL)) {
726         rpmError(RPMERR_BADSPEC, "No release field");
727         return RPMERR_BADSPEC;
728     }
729
730     outHeader = headerCopy(s->packages->header);
731     headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
732     headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
733     headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
734     headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
735     headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
736     if (scount) 
737         headerAddEntry(outHeader, RPMTAG_SOURCE, RPM_STRING_ARRAY_TYPE, sources, scount);
738     if (pcount)
739         headerAddEntry(outHeader, RPMTAG_PATCH, RPM_STRING_ARRAY_TYPE, patches, pcount);
740     if (s->numNoSource) {
741         headerAddEntry(outHeader, RPMTAG_NOSOURCE, RPM_INT32_TYPE, s->noSource,
742                  s->numNoSource);
743     }
744     if (s->numNoPatch) {
745         headerAddEntry(outHeader, RPMTAG_NOPATCH, RPM_INT32_TYPE, s->noPatch,
746                  s->numNoPatch);
747     }
748     if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
749         if ((vendor = rpmGetVar(RPMVAR_VENDOR))) {
750             headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
751         }
752     }
753     if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
754         if ((dist = rpmGetVar(RPMVAR_DISTRIBUTION))) {
755             headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
756         }
757     }
758
759     /* Process the file list */
760     if (process_filelist(outHeader, NULL, filelist, &size,
761                          s->name, version, release, RPMLEAD_SOURCE,
762                          NULL, specFile)) {
763         return 1;
764     }
765
766     /* And add the final Header entry */
767     headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
768
769     /**** Make the RPM ****/
770
771     sprintf(fullname, "%s-%s-%s", s->name, version, release);
772     sprintf(filename, "%s/%s.%ssrc.rpm", rpmGetVar(RPMVAR_SRPMDIR), fullname,
773             (s->numNoPatch + s->numNoSource) ? "no" : "");
774     rpmMessage(RPMMESS_VERBOSE, "Source Packaging: %s\n", fullname);
775
776     if (generateRPM(fullname, filename, RPMLEAD_SOURCE, outHeader,
777                     tempdir, getStringBuf(cpioFileList), passPhrase, NULL)) {
778         return 1;
779     }
780     
781     /**** Now clean up ****/
782
783     freeStringBuf(filelist);
784     freeStringBuf(cpioFileList);
785     
786     source = s->sources;
787     while (source) {
788         sprintf(dest, "%s/%s", tempdir, source->source);
789         unlink(dest);
790         source = source->next;
791     }
792     package = s->packages;
793     while (package) {
794         if (package->icon) {
795             sprintf(dest, "%s/%s", tempdir, package->icon);
796             unlink(dest);
797         }
798         package = package->next;
799     }
800     sprintf(dest, "%s%s", tempdir, strrchr(s->specfile, '/'));
801     unlink(dest);
802     rmdir(tempdir);
803     
804     return 0;
805 }