update Mon Jun 14 06:37:00 PDT 2010
[pkgsrc.git] / pkgtools / rpm2pkg / files / rpm2pkg.c
1 /*      $NetBSD: rpm2pkg.c,v 1.10 2010/06/14 11:24:48 tron Exp $        */
2
3 /*-
4  * Copyright (c) 2004-2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matthias Scheler.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <bzlib.h>
44 #include <zlib.h>
45
46 /*
47  * Lead of an RPM archive as described here:
48  * http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
49  */
50 static const unsigned char RPMMagic[] = { 0xed, 0xab, 0xee, 0xdb };
51
52 #define RPM_LEAD_SIZE   96
53
54 /* Magic bytes for "bzip2" and "gzip" compressed files. */
55 static const unsigned char BZipMagic[] = { 'B', 'Z', 'h' };
56 static const unsigned char GZipMagic[] = { 0x1f, 0x8b, 0x08 };
57
58 /* Structure of a cpio(1) archive. */
59 #define C_IRUSR                 0000400
60 #define C_IWUSR                 0000200
61 #define C_IXUSR                 0000100
62 #define C_IRGRP                 0000040
63 #define C_IWGRP                 0000020
64 #define C_IXGRP                 0000010
65 #define C_IROTH                 0000004
66 #define C_IWOTH                 0000002
67 #define C_IXOTH                 0000001
68 #define C_ISUID                 0004000
69 #define C_ISGID                 0002000
70 #define C_ISVTX                 0001000
71 #define C_ISDIR                 0040000
72 #define C_ISREG                 0100000
73 #define C_ISCHR                 0020000
74 #define C_ISLNK                 0120000
75  
76 static const unsigned char CPIOMagic[] = {'0','7','0','7','0','1'};
77
78 #define CPIO_END_MARKER         "TRAILER!!!"
79 #define CPIO_FIELD_LENGTH       8
80
81 #define CPIO_HDR_INODE          0
82 #define CPIO_HDR_MODE           1
83 #define CPIO_HDR_FILESIZE       6
84 #define CPIO_HDR_NAMESIZE       11
85 #define CPIO_NUM_HEADERS        13
86
87 #define CP_IFMT                 0170000
88
89 typedef struct ModeMapStruct {
90         unsigned long   mm_CPIOMode;
91         mode_t          mm_SysMode;
92 } ModeMap;
93
94 ModeMap ModeMapTab[] = {
95         {C_IRUSR, S_IRUSR},
96         {C_IWUSR, S_IWUSR},
97         {C_IXUSR, S_IXUSR},
98         {C_IRGRP, S_IRGRP},
99         {C_IWGRP, S_IWGRP},
100         {C_IXGRP, S_IXGRP},
101         {C_IROTH, S_IROTH},
102         {C_IWOTH, S_IWOTH},
103         {C_IXOTH, S_IXOTH},
104         {C_ISUID, S_ISUID},
105         {C_ISGID, S_ISGID},
106         {C_ISVTX, S_ISVTX},
107         {0, 0}
108 };
109
110 typedef struct PListEntryStruct PListEntry;
111 struct PListEntryStruct {
112         PListEntry      *pe_Childs[2];
113         int             pe_DirEmpty;
114         mode_t          pe_DirMode;
115         unsigned long   pe_INode;
116         char            *pe_Link;
117         char            pe_Name[1];
118 };
119
120 #define pe_Left         pe_Childs[0]
121 #define pe_Right        pe_Childs[1]
122
123 typedef void PListEntryFunc(PListEntry *,FILE *);
124
125 #define PLIST_ORDER_FORWARD     0
126 #define PLIST_ORDER_BACKWARD    1
127
128 #define INVERT_PLIST_ORDER(o)   (1 - (o))
129
130 typedef struct FileHandleStruct {
131         FILE    *fh_File;
132         BZFILE  *fh_BZFile;
133         gzFile  *fh_GZFile;
134         off_t   fh_Pos;
135 } FileHandle;
136
137 static bool
138 InitBuffer(void **Buffer, size_t *BufferSizePtr)
139 {
140         if (*Buffer == NULL) {
141                 size_t BufferSize;
142
143                 BufferSize = sysconf(_SC_PAGESIZE) * 256;
144                 while ((*Buffer = malloc(BufferSize)) == NULL) {
145                         BufferSize >>= 1;
146                         if (BufferSize == 0)
147                                 return false;
148                 }
149                 *BufferSizePtr = BufferSize;
150         }
151         return true;
152 }
153
154 static void
155 Close(FileHandle *fh)
156 {
157         if (fh->fh_BZFile != NULL) {
158                 int     bzerror;
159
160                 (void)BZ2_bzReadClose(&bzerror, fh->fh_BZFile);
161                 (void)fclose(fh->fh_File);
162         } else {
163                 (void)gzclose(fh->fh_GZFile);
164         }
165         free(fh);
166 }
167
168 static bool
169 IsRPMFile(int fd)
170 {
171         char buffer[RPM_LEAD_SIZE];
172
173         if (read(fd, buffer, sizeof(buffer)) != sizeof(buffer))
174                 return false;
175
176         return (memcmp(buffer, RPMMagic, sizeof(RPMMagic)) == 0);
177 }
178
179 static FileHandle *
180 Open(int fd)
181 {
182         unsigned char   buffer[16384];
183         size_t          bzMatch, gzMatch;
184         int             archive_type;
185         off_t           offset;
186         FileHandle      *fh;
187
188         bzMatch = 0;
189         gzMatch = 0;
190         archive_type = 0;
191         offset = 0;
192         do {
193                 ssize_t bytes, i;
194
195                 bytes = read(fd, buffer, sizeof(buffer));
196                 if (bytes <= 0)
197                         return NULL;
198
199                 for (i = 0; i < bytes; i++) {
200                         unsigned char cur_char;
201
202                         cur_char = buffer[i];
203
204                         /* Look for bzip2 header. */
205                         if (cur_char == BZipMagic[bzMatch]) {
206                                 bzMatch++;
207                                 if (bzMatch == sizeof(BZipMagic)) {
208                                         archive_type = 1;
209                                         offset = i - bytes -
210                                             sizeof(BZipMagic) + 1;
211                                         break;
212                                 }
213                         } else {
214                                 bzMatch = (cur_char == BZipMagic[0]) ? 1 : 0;
215                         }
216
217                         /* Look for gzip header. */
218                         if (cur_char == GZipMagic[gzMatch]) {
219                                 gzMatch++;
220                                 if (gzMatch == sizeof(GZipMagic)) {
221                                         archive_type = 2;
222                                         offset = i - bytes -
223                                             sizeof(GZipMagic) + 1;
224                                         break;
225                                 }
226                         } else {
227                                 gzMatch = (cur_char == GZipMagic[0]) ? 1 : 0;
228                         }
229                 }
230         } while (archive_type == 0);
231
232         /* Go back to the beginning of the archive. */
233         if (lseek(fd, offset, SEEK_CUR) < RPM_LEAD_SIZE)
234                 return NULL;
235
236         if ((fh = calloc(1, sizeof (FileHandle))) == NULL)
237                 return NULL;
238
239         if (archive_type == 1) {
240                 /* bzip2 archive */
241                 int     bzerror;
242
243                 if ((fd = dup(fd)) < 0) {
244                         free(fh);
245                         return NULL;
246                 }
247                 if ((fh->fh_File = fdopen(fd, "rb")) == NULL) {
248                         perror("fdopen");
249                         (void)close(fd);
250                         free(fh);
251                         return NULL;
252                 }
253                 if ((fh->fh_BZFile = BZ2_bzReadOpen(&bzerror, fh->fh_File, 0,
254                     0, NULL, 0)) == NULL) {
255                         (void)fclose(fh->fh_File);
256                         free(fh);
257                         return (NULL);
258                 }
259         } else {
260                 /* gzip archive */
261                 if ((fh->fh_GZFile = gzdopen(fd, "r")) == NULL) {
262                         free(fh);
263                         return (NULL);
264                 }
265         }
266
267         return (fh);
268 }
269
270 static int
271 Read(FileHandle *fh, void *buffer, int length)
272 {
273         int     bzerror, bytes;
274
275         bytes = (fh->fh_BZFile != NULL) ?
276             BZ2_bzRead(&bzerror, fh->fh_BZFile, buffer, length) :
277             gzread(fh->fh_GZFile, buffer, length);
278         if (bytes > 0)
279                 fh->fh_Pos += bytes;
280
281         return (bytes == length);
282 }
283
284 static bool
285 SkipAndAlign(FileHandle *fh, off_t Skip)
286
287 {
288         off_t           NewPos;
289
290         NewPos = (fh->fh_Pos + Skip + 3) & ~3;
291         if (fh->fh_Pos == NewPos)
292                 return true;
293
294         if (fh->fh_GZFile != NULL) {
295                 if (gzseek(fh->fh_GZFile, NewPos, SEEK_SET) == NewPos) {
296                         fh->fh_Pos = NewPos;
297                         return true;
298                 }
299                 return false;
300         } else {
301                 static void     *Buffer = NULL;
302                 static size_t   BufferSize = 0;
303
304                 if (!InitBuffer(&Buffer, &BufferSize))
305                         return false;
306
307                 while (fh->fh_Pos < NewPos) {
308                         off_t   Length;
309                         int     Chunk;
310
311                         Length = NewPos - fh->fh_Pos;
312                         Chunk = (Length > (off_t)BufferSize) ?
313                             (off_t)BufferSize : Length;
314                         if (!Read(fh, Buffer, Chunk))
315                                 return false;
316                 }
317         }
318
319         return true;
320 }
321
322 static PListEntry *
323 InsertPListEntry(PListEntry **Tree,char *Name)
324 {
325         PListEntry *Node;
326
327         while ((Node = *Tree) != NULL) {
328                 Tree = (strcmp(Name, Node->pe_Name) <0) ?
329                     &Node->pe_Left : &Node->pe_Right;
330         }
331
332         if ((Node = calloc(1, sizeof (PListEntry) + strlen(Name))) == NULL) {
333                 perror("calloc");
334                 exit(EXIT_FAILURE);
335         }
336
337         (void)strcpy(Node->pe_Name, Name);
338
339         return *Tree = Node;
340 }
341
342 static PListEntry *
343 FindPListEntry(PListEntry *Tree, char *Name)
344 {
345         while (Tree != NULL) {
346                 int Result;
347
348                 if ((Result = strcmp(Name, Tree->pe_Name)) == 0) break;
349                 Tree = (Result < 0) ? Tree->pe_Left : Tree->pe_Right;
350         }
351
352         return Tree;
353 }
354
355 static void
356 PListEntryFile(PListEntry *Node, FILE *Out)
357
358 {
359         (void)fputs(Node->pe_Name, Out);
360         (void)fputc('\n', Out);
361 }
362
363 static char *
364 StrCat(char *Prefix, char *Suffix)
365 {
366         int     Length;
367         char    *Str;
368
369         Length = strlen(Prefix);
370         if ((Str = malloc(Length + strlen(Suffix) + 1)) == NULL) {
371              perror("malloc");
372              exit(EXIT_FAILURE);
373         }
374
375         (void)memcpy(Str, Prefix, Length);
376         (void)strcpy(&Str[Length], Suffix);
377
378         return Str;
379 }
380
381 static void
382 PListEntryLink(PListEntry *Node, FILE *Out)
383
384 {
385         char            *Ptr;
386         struct stat     Stat;
387         int             Result;
388
389         if ((Ptr = strrchr(Node->pe_Name, '/')) != NULL) {
390                 char    Old, *Targetname;
391
392                 Old = Ptr[1];
393                 Ptr[1] = '\0';
394                 Targetname = StrCat(Node->pe_Name, Node->pe_Link);
395                 Ptr[1] = Old;
396
397                 Result = stat(Targetname, &Stat);
398                 free(Targetname);
399         } else {
400                 Result = stat(Node->pe_Link, &Stat);
401         }
402
403         if ((Result == 0) && ((Stat.st_mode & S_IFMT) == S_IFREG)) {
404                 PListEntryFile(Node, Out);
405                 return;
406         }
407
408         (void)fprintf(Out, "@exec ln -fs %s %%D/%s\n@unexec rm -f %%D/%s\n",
409             Node->pe_Link, Node->pe_Name, Node->pe_Name);
410 }
411
412 static void
413 PListEntryMakeDir(PListEntry *Node, FILE *Out)
414
415 {
416         if (Node->pe_DirEmpty) {
417                 (void)fprintf(Out, "@exec mkdir -m %o -p %%D/%s\n",
418                      Node->pe_DirMode, Node->pe_Name);
419         }
420 }
421
422 static void
423 ProcessPList(PListEntry *Tree, PListEntryFunc Func, int Order, FILE *Out)
424
425 {
426         while (Tree != NULL) {
427                 if (Tree->pe_Childs[Order] != NULL)
428                         ProcessPList(Tree->pe_Childs[Order], Func, Order, Out);
429                 Func(Tree, Out);
430                 Tree = Tree->pe_Childs[INVERT_PLIST_ORDER(Order)];
431         }
432 }
433
434 static char **
435 ArrayAdd(char **Array, char *String)
436
437 {
438         int     Old;
439
440         Old = 0;
441         if (Array != NULL) {
442                 while (Array[Old] != NULL)
443                         Old ++;
444         }
445         if ((Array = realloc(Array, sizeof (char *) * (Old + 2))) == NULL) {
446                 perror("realloc");
447                 exit(EXIT_FAILURE);
448         }
449
450         Array[Old++] = String;
451         Array[Old] = NULL;
452
453         return Array;
454 }
455
456 static void
457 Usage(char *Progname)
458 {
459         (void)fprintf(stderr, 
460             "Usage: %s [-d directory] [-f packlist] [[-i ignorepath] ...]\n"
461             "               [-p prefix] [-s stripcount] rpmfile [...]\n", 
462             Progname);
463         exit(EXIT_FAILURE);
464 }
465
466 static char *
467 GetData(FileHandle *In, unsigned long Length)
468 {
469         char *Ptr;
470
471         if ((Ptr = malloc(Length + 1)) != NULL) {
472                 if (Read(In, Ptr, Length) && SkipAndAlign(In, 0)) {
473                         Ptr[Length] = '\0';
474                         return Ptr;
475                 }
476                 free(Ptr);
477         }
478
479         return NULL;
480 }
481
482 static bool
483 GetCPIOHeader(FileHandle *In, unsigned long *Fields, char **Name)
484 {
485         char    Buffer[CPIO_NUM_HEADERS * CPIO_FIELD_LENGTH], *Ptr;
486         int     Index;
487         unsigned long   Value;
488
489         *Name = NULL;
490
491         if (!Read(In, Buffer, sizeof (CPIOMagic)))
492                 return false;
493         if (memcmp(Buffer, CPIOMagic, sizeof (CPIOMagic)) != 0)
494                 return false;
495
496         if (!Read(In, Buffer, sizeof (Buffer)))
497                 return false;
498
499         Ptr = Buffer;
500         Index = sizeof (Buffer);
501         Value = 0;
502         while (Index-- > 0) {
503                 Value <<= 4;
504                 if ((*Ptr >= '0') && (*Ptr <= '9')) {
505                         Value += (unsigned long)(*Ptr++-'0');
506                 } else if ((*Ptr >= 'A') && (*Ptr <= 'F')) {
507                                 Value += (unsigned long)(*Ptr++-'A') + 10;
508                 } else if ((*Ptr >= 'a') && (*Ptr <= 'f')) {
509                                 Value += (unsigned long)(*Ptr++-'a') + 10;
510                 } else {
511                         return false;
512                 }
513    
514                 if ((Index % CPIO_FIELD_LENGTH) == 0) {
515                         *Fields++ = Value;
516                         Value = 0;
517                 }
518         }
519
520         Value = Fields[CPIO_HDR_NAMESIZE - CPIO_NUM_HEADERS];
521         if ((*Name = GetData(In, Value)) == NULL)
522                 return false;
523         return ((*Name)[Value -1 ] == '\0');
524 }
525
526 static mode_t
527 ConvertMode(unsigned long CPIOMode)
528 {
529         mode_t  Mode;
530         ModeMap *Ptr;
531
532         Mode = 0;
533         Ptr = ModeMapTab;
534         while (Ptr->mm_CPIOMode != 0) {
535                 if ((CPIOMode & Ptr->mm_CPIOMode) != 0)
536                         Mode |= Ptr->mm_SysMode;
537                 Ptr++;
538         }
539
540         return Mode;
541 }
542
543 static bool
544 MakeTargetDir(char *Name, PListEntry **Dirs, int MarkNonEmpty)
545 {
546         char            *Basename;
547         PListEntry      *Dir;
548         struct stat     Stat;
549         int     Result;
550
551         if ((Basename = strrchr(Name, '/')) == NULL)
552                 return true;
553
554         *Basename = '\0';
555         if ((Dir = FindPListEntry(*Dirs, Name)) != NULL) {
556                 *Basename = '/';
557                 Dir->pe_DirEmpty = !MarkNonEmpty;
558                 return true;
559         }
560
561         if (!MakeTargetDir(Name, Dirs, true)) {
562                 *Basename = '/';
563                 return false;
564         }
565
566         if (stat(Name, &Stat) == 0) {
567                 Result = S_ISDIR(Stat.st_mode);
568         } else if (errno != ENOENT) {
569                 Result = false;
570         } else if ((Result = (mkdir(Name, S_IRWXU|S_IRWXG|S_IRWXO) == 0))) {
571                 InsertPListEntry(Dirs, Name)->pe_DirMode =
572                     S_IRWXU|S_IRWXG|S_IRWXO;
573         }
574
575         *Basename = '/';
576         return Result;
577 }
578
579 static bool
580 MakeDir(char *Name, mode_t Mode, int *OldDir)
581 {
582         struct stat Stat;
583
584         *OldDir = false;
585         if (mkdir(Name, Mode) == 0)
586                 return true;
587
588         if ((errno != EEXIST) || (lstat(Name, &Stat) < 0) || 
589             !S_ISDIR(Stat.st_mode)) {
590                 return false;
591         }
592
593         *OldDir = true;
594         return true;
595 }
596
597 static bool
598 MakeSymLink(char *Link, char *Name)
599 {
600         struct stat Stat;
601
602         if (symlink(Link, Name) == 0) return true;
603
604         if ((errno != EEXIST) || (lstat(Name, &Stat) < 0) || 
605             !S_ISLNK(Stat.st_mode)) {
606                 return false;
607         }
608
609         return ((unlink(Name) == 0) && (symlink(Link, Name) == 0));
610 }
611
612 static bool
613 WriteFile(FileHandle *In, char *Name, mode_t Mode, unsigned long Length,
614     char *Link)
615 {
616         int             Out;
617         struct stat     Stat;
618         static void     *Buffer = NULL;
619         static size_t   BufferSize = 0;
620
621         if ((lstat(Name, &Stat) == 0) &&
622             (!S_ISREG(Stat.st_mode) || (unlink(Name) < 0))) {
623                 return false;
624         }
625
626         if (!InitBuffer(&Buffer, &BufferSize))
627                 return false;
628
629         if (Link != NULL) {
630                 if (link(Link, Name) < 0)
631                         return false;
632                 Out = open(Name, O_WRONLY, Mode);
633          } else {
634                 Out = open(Name, O_WRONLY|O_CREAT, Mode);
635         }
636         if (Out < 0)
637                 return false;
638
639         while (Length > 0) {
640                 int     Chunk;
641
642                 Chunk = (Length > BufferSize) ? BufferSize : Length;
643                 if (!Read(In, Buffer, Chunk) ||
644                     (write(Out, Buffer, Chunk) != Chunk))
645                         break;
646                 Length -= Chunk;
647         }
648
649         if ((close(Out) == 0) && (Length == 0))
650                 return SkipAndAlign(In, 0);
651
652         (void)unlink(Name);
653         return false;
654 }
655
656 static void
657 CheckSymLinks(PListEntry **Links, PListEntry **Files, PListEntry **Dirs)
658 {
659         PListEntry      *Link;
660
661         while ((Link = *Links) != NULL) {
662                 struct stat     Stat;
663                 PListEntry      *Ptr;
664                 char            *Basename;
665
666                 if (Link->pe_Left != NULL)
667                         CheckSymLinks(&Link->pe_Left, Files, Dirs);
668
669                 if ((stat(Link->pe_Name, &Stat) < 0) ||
670                     !S_ISREG(Stat.st_mode)) {
671                         Links = &Link->pe_Right;
672                         continue;
673                 }
674
675                 (void)InsertPListEntry(Files, Link->pe_Name);
676                 if ((Basename = strrchr(Link->pe_Name, '/')) != NULL) {
677                         *Basename = '\0';
678                         if ((Ptr = FindPListEntry(*Dirs,
679                             Link->pe_Name)) != NULL)
680                                 Ptr->pe_DirEmpty = false;
681                 }
682
683                 if (Link->pe_Right == NULL) {
684                         *Links = Link->pe_Left;
685                         free(Link);
686                         break;
687                 }
688
689                 *Links = Link->pe_Right;
690                 Ptr = Link->pe_Left;
691                 free(Link);
692
693                 if (Ptr == NULL)
694                         continue;
695
696                 Link = *Links;
697                 while (Link->pe_Left != NULL)
698                         Link = Link->pe_Left;
699                 Link->pe_Left = Ptr;
700         }
701 }
702
703 static bool
704 CheckPrefix(char *Prefix, char *Name)
705 {
706         int     Length;
707
708         Length = strlen(Prefix);
709         return ((strncmp(Prefix, Name, Length) == 0) && 
710             ((Name[Length] == '\0') || (Name[Length] == '/')));
711 }
712
713 static char *
714 StripPrefix(char *Name, int Count)
715 {
716         char *NewName;
717
718         if (Count <= 0)
719                 return Name;
720
721         NewName = Name;
722         while (Count-- > 0) {
723                 NewName = strchr(NewName, '/');
724                 if (NewName == NULL)
725                         return NULL;
726                 NewName++;
727         }
728         (void)memmove(Name, NewName, strlen(NewName) + 1);
729
730         return Name;
731 }
732
733 int
734 main(int argc, char **argv)
735 {
736         char            *Progname;
737         FILE            *PListFile;
738         char            **Ignore, *Prefix;
739         int             Opt, Index, FD, StripCount;
740         PListEntry      *Files, *Links, *Dirs;
741         FileHandle      *In;
742
743         Progname = strrchr(argv[0], '/');
744         if (Progname == NULL)
745                 Progname = argv[0];
746         else
747                 Progname ++;
748
749         PListFile = NULL;
750         Ignore = NULL;
751         Prefix = NULL;
752         StripCount = 0;
753         while ((Opt = getopt(argc, argv, "s:d:f:i:p:")) != -1) {
754                 switch (Opt) {
755                 case 's':
756                         StripCount = atoi(optarg);
757                         if (StripCount <= 0) {
758                                 (void)fprintf(stderr,
759                                     "%s: -s argument \"%s\" "
760                                     "must be a positive integer.\n",
761                                     Progname, optarg);
762                                 return EXIT_FAILURE;
763                         }
764                         break;
765                 case 'f':
766                         if (PListFile != NULL)
767                                 (void)fclose(PListFile);
768                         if ((PListFile = fopen(optarg, "a")) == NULL) {
769                                 perror(optarg);
770                                 return EXIT_FAILURE;
771                         }
772                         break;
773                 case 'i':
774                         Ignore = ArrayAdd(Ignore, optarg);
775                         break;
776                 case 'd':
777                         if (chdir(optarg)) {
778                                 perror(optarg);
779                                 return EXIT_FAILURE;
780                         }
781                         break;
782                 case 'p':
783                         if (strlen(optarg) > 0)
784                                 Prefix = optarg;
785                         break;
786                 default:
787                         Usage(Progname);
788                 }
789         }
790
791         argc -= optind;
792         argv += optind;
793         if (argc == 0)
794                 Usage(Progname);
795
796         if ((Prefix != NULL) && (Prefix[strlen(Prefix) - 1] != '/'))
797                 Prefix = StrCat(Prefix, "/");
798
799         Files = NULL;
800         Links = NULL;
801         Dirs = NULL;
802         for (Index = 0; Index < argc ; Index++) {
803                 PListEntry      *Last;
804
805                 if ((FD = open(argv[Index], O_RDONLY, 0)) < 0) {
806                         perror(argv[Index]);
807                         return EXIT_FAILURE;
808                 }
809
810                 if (!IsRPMFile(FD)) {
811                         (void)fprintf(stderr,
812                             "%s: file is not an RPM package.\n", argv[Index]);
813                         return EXIT_FAILURE;
814                 }
815
816                 if ((In = Open(FD)) == NULL) {
817                         (void)fprintf(stderr,
818                             "%s: cannot read cpio data.\n", argv[Index]);
819                         return EXIT_FAILURE;
820                 }
821
822                 Last = NULL;
823                 for (;;) {
824                         unsigned long   Fields[CPIO_NUM_HEADERS];
825                         char            *Name;
826                         mode_t          Mode;
827                         unsigned long Length;
828
829                         if (!GetCPIOHeader(In, Fields, &Name)) {
830                                 (void)fprintf(stderr,
831                                     "%s: error in cpio header.\n",
832                                     argv[Index]);
833                                 return EXIT_FAILURE;
834                         }
835                         if (strcmp(Name, CPIO_END_MARKER) == 0) {
836                                 free(Name);
837                                 break;
838                         }
839                         if (*Name == '\0')
840                                 Fields[CPIO_HDR_MODE] = 0;
841
842                         if (Ignore != NULL) {
843                                 char **Ptr;
844
845                                 Ptr = Ignore;
846                                 while (*Ptr != NULL) {
847                                         if (CheckPrefix(*Ptr, Name)) {
848                                                 Fields[CPIO_HDR_MODE] = 0;
849                                                 break;
850                                         }
851                                         Ptr++;
852                                 }
853                         }
854
855                         if ((Name = StripPrefix(Name, StripCount)) == NULL) {
856                                 (void)fprintf(stderr,
857                                             "%s: Leading path to strip too "
858                                             "big (-s %d)\n", 
859                                             argv[Index], StripCount);
860                                 return EXIT_FAILURE;
861                         }
862
863                         if (Prefix != NULL) {
864                                 char *Fullname;
865
866                                 Fullname = StrCat(Prefix, Name);
867                                 free(Name);
868                                 Name = Fullname;
869                         }
870
871                         Mode = ConvertMode(Fields[CPIO_HDR_MODE]);
872                         Length = Fields[CPIO_HDR_FILESIZE];
873                         switch (Fields[CPIO_HDR_MODE] & CP_IFMT) {
874                         case C_ISDIR: {
875                                 PListEntry      *Dir;
876                                 int             OldDir;
877
878                                 if (Length != 0) {
879                                         (void)fprintf(stderr,
880                                             "%s: error in cpio file.\n",
881                                         argv[Index]);
882                                         return EXIT_FAILURE;
883                                 }
884
885                                 if (!MakeTargetDir(Name, &Dirs, true)) {
886                                         (void)fprintf(stderr, 
887                                             "%s: can't create parent "
888                                             "directories for \"%s\".\n", 
889                                             argv[Index], Name);
890                                         return EXIT_FAILURE;
891                                 }
892
893                                 if (!MakeDir(Name, Mode, &OldDir)) {
894                                         (void)fprintf(stderr, 
895                                             "%s: can't create directory "
896                                             "\"%s\".\n", argv[Index], Name);
897                                         return EXIT_FAILURE;
898                                 }
899
900                                 if (!OldDir) {
901                                         Dir = InsertPListEntry(&Dirs, Name);
902                                         Dir->pe_DirEmpty = true;
903                                         Dir->pe_DirMode = Mode;
904                                 }
905                                 break;
906                         }
907                         case C_ISLNK: {
908                                 char    *Link;
909
910                                 if ((Link = GetData(In, Length)) == NULL) {
911                                         (void)fprintf(stderr,
912                                             "%s: error in cpio file.\n",
913                                         argv[Index]);
914                                         return EXIT_FAILURE;
915                                 }
916
917                                 if (!MakeTargetDir(Name, &Dirs, true)) {
918                                         (void)fprintf(stderr, 
919                                             "%s: can't create parent "
920                                             "directories for \"%s\".\n", 
921                                             argv[Index], Name);
922                                         return EXIT_FAILURE;
923                                 }
924
925                                 if (*Link == '/') {
926                                         char    *Ptr;
927
928                                         (void)memmove(Link, Link + 1,
929                                             strlen(Link + 1) + 1);
930                                         Ptr = Name;
931                                         if (Prefix != NULL)
932                                                 Ptr += strlen(Prefix);
933
934                                         while ((Ptr = strchr(Ptr, '/'))
935                                             != NULL) {
936                                                 char *NewLink;
937
938                                                 NewLink = StrCat("../", Link);
939                                                 free(Link);
940                                                 Link = NewLink;
941                                                 Ptr++;
942                                         }
943                                 }
944
945                                 if (!MakeSymLink(Link, Name)) {
946                                         (void)fprintf(stderr, 
947                                             "%s: can't create symbolic link "
948                                             "\"%s\".\n", argv[Index], Name);
949                                         return EXIT_FAILURE;
950                                 }
951
952                                 InsertPListEntry(&Links, Name)->pe_Link = Link;
953                                 break;
954                         }
955                         case C_ISREG:
956                                 if (!MakeTargetDir(Name, &Dirs, true)) {
957                                         (void)fprintf(stderr, 
958                                             "%s: can't create parent "
959                                             "directories for \"%s\".\n", 
960                                             argv[Index], Name);
961                                         return EXIT_FAILURE;
962                                 }
963
964
965                                 if ((Last != NULL) && (Last->pe_INode != 
966                                     Fields[CPIO_HDR_INODE])) {
967                                         Last = NULL;
968                                 }
969
970                                 if (!WriteFile(In, Name, Mode, Length,
971                                     (Last != NULL)? Last->pe_Name : NULL)) {
972                                         (void)fprintf(stderr, 
973                                             "%s: can't write file \"%s\".\n", 
974                                             argv[Index], 
975                                             Name);
976                                         return EXIT_FAILURE;
977                                 }
978
979                                 Last = InsertPListEntry(&Files, Name);
980                                 Last->pe_INode = Fields[CPIO_HDR_INODE];
981                                 break;
982                         default:
983                                 if ((Length > 0) &&
984                                     !SkipAndAlign(In, Length)) {
985                                         (void)fprintf(stderr,
986                                             "%s: error in cpio file.\n",
987                                             argv[Index]);
988                                         return EXIT_FAILURE;
989                                 }
990                                                 
991                         }
992
993                         free(Name);
994                 }
995
996                 Close(In);
997                 (void)close(FD);
998         }
999
1000         if (PListFile != NULL) {
1001                 ProcessPList(Files, PListEntryFile, PLIST_ORDER_FORWARD,
1002                     PListFile);
1003                 ProcessPList(Dirs, PListEntryMakeDir, PLIST_ORDER_FORWARD,
1004                     PListFile);
1005                 ProcessPList(Links, PListEntryLink, PLIST_ORDER_FORWARD,
1006                     PListFile);
1007                 (void)fclose(PListFile);
1008         }
1009
1010         return EXIT_SUCCESS;
1011 }