Simplify a few little things, and merge in a patch from
[oweals/busybox.git] / ar.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ar implementation for busybox 
4  *
5  * Copyright (C) 2000 by Glenn McGrath
6  * Written by Glenn McGrath <bug1@netconnect.com.au> 1 June 2000
7  *              
8  * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  * Last modified 9 September 2000
25  */
26 #include <stdio.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <utime.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <malloc.h>
38 #include "internal.h"
39
40 #define BLOCK_SIZE 60
41 #define PRESERVE_DATE 1 /* preserve original dates */
42 #define VERBOSE       2 /* be verbose */
43 #define DISPLAY       4 /* display contents */
44 #define EXT_TO_FILE   8 /* extract contents of archive */
45 #define EXT_TO_STDOUT 16        /* extract to stdout */
46 #define RECURSIVE     32         
47
48 #define MAX_NAME_LENGTH 100
49
50 //#define BB_DECLARE_EXTERN
51 //#define bb_need_io_error
52 //#include "messages.c"
53
54 typedef struct rawArHeader {    /* Byte Offset */
55         char name[16];          /*  0-15 */
56         char date[12];          /* 16-27 */
57         char uid[6], gid[6];    /* 28-39 */
58         char mode[8];           /* 40-47 */
59         char size[10];          /* 48-57 */
60         char fmag[2];           /* 58-59 */
61 } rawArHeader_t;
62
63 typedef struct headerL {
64         char name[MAX_NAME_LENGTH];
65         size_t size;
66         uid_t uid;
67         gid_t gid;
68         mode_t mode;
69         time_t mtime;
70         off_t offset;
71         struct headerL *next;
72 } headerL_t;
73
74 /*
75  * identify Ar header (magic) and set srcFd to first header entry 
76  */
77 static int checkArMagic(int srcFd)
78 {
79         char arMagic[8];
80         if (fullRead(srcFd, arMagic, 8) != 8)
81                 return (FALSE);
82         
83         if (strncmp(arMagic,"!<arch>",7) != 0)
84                 return(FALSE);
85         return(TRUE);
86 }
87
88 /*
89  * read, convert and check the raw ar header
90  * srcFd should be pointing to the start of header prior to entry
91  * srcFd will be pointing at the start of data after successful exit
92  * if returns FALSE srcFd is reset to initial position
93  */
94 static int readRawArHeader(int srcFd, headerL_t *header)
95 {
96         rawArHeader_t rawArHeader;
97         off_t   initialOffset;
98         size_t nameLength;
99         
100         initialOffset = lseek(srcFd, 0, SEEK_CUR);
101         if (fullRead(srcFd, (char *) &rawArHeader, 60) != 60) {
102                 lseek(srcFd, initialOffset, SEEK_SET);
103                 return(FALSE);
104         }
105         if ((rawArHeader.fmag[0]!='`') || (rawArHeader.fmag[1]!='\n')) {
106                 lseek(srcFd, initialOffset, SEEK_SET);
107                 return(FALSE);
108         }
109
110         strncpy(header->name, rawArHeader.name, 16);
111         nameLength=strcspn(header->name, " \\");
112         header->name[nameLength]='\0';
113         parse_mode(rawArHeader.mode, &header->mode);
114         header->mtime = atoi(rawArHeader.date);
115         header->uid = atoi(rawArHeader.uid);
116         header->gid = atoi(rawArHeader.gid);
117         header->size = (size_t) atoi(rawArHeader.size);
118         header->offset = initialOffset + (off_t) 60;
119         return(TRUE); 
120 }
121
122 /*
123  * get, check and correct the converted header
124  */ 
125 static int readArEntry(int srcFd, headerL_t *newEntry)
126 {
127         size_t nameLength;
128
129         if(readRawArHeader(srcFd, newEntry)==FALSE)
130                 return(FALSE);
131         
132         nameLength = strcspn(newEntry->name, "/");
133         
134         /* handle GNU style short filenames, strip trailing '/' */
135         if (nameLength > 0)
136                 newEntry->name[nameLength]='\0';
137         
138         /* handle GNU style long filenames */ 
139         if (nameLength == 0) {
140                 /* escape from recursive call */
141                 if (newEntry->name[1]=='0') 
142                         return(TRUE);
143
144                 /* the data section contains the real filename */
145                 if (newEntry->name[1]=='/') {
146                         char tempName[MAX_NAME_LENGTH];
147
148                         if (newEntry->size > MAX_NAME_LENGTH)
149                                 newEntry->size = MAX_NAME_LENGTH;
150                         fullRead(srcFd, tempName, newEntry->size);
151                         tempName[newEntry->size-3]='\0';
152                         
153                         /* read the second header for this entry */
154                         /* be carefull, this is recursive */
155                         if (readArEntry(srcFd, newEntry)==FALSE)
156                                 return(FALSE);
157                 
158                         if ((newEntry->name[0]='/') && (newEntry->name[1]='0'))
159                                 strcpy(newEntry->name, tempName);
160                         else {
161                                 errorMsg("Invalid long filename\n");
162                                 return(FALSE);
163                         }
164                 }
165         }
166         return(TRUE);   
167 }
168
169 /*
170  * return the headerL_t struct for the specified filename
171  */
172 static headerL_t *getHeaders(int srcFd, headerL_t *head, int funct)
173 {
174         headerL_t *list;
175         list = (headerL_t *) malloc(sizeof(headerL_t));
176
177         if (checkArMagic(srcFd)==TRUE) {
178                 while(readArEntry(srcFd, list) == TRUE) {
179                         list->next = (headerL_t *) malloc(sizeof(headerL_t));
180                         *list->next = *head;
181                         *head = *list;
182                 
183                         /* recursive check for sub-archives */
184                         if ( funct & RECURSIVE ) 
185                                 head = getHeaders(srcFd, head, funct);
186                         lseek(srcFd, head->offset + head->size, SEEK_SET);
187                 }
188         }
189         return(head);
190 }
191
192 /*
193  * find an entry in the linked list matching the filename
194  */
195 static headerL_t *findEntry(headerL_t *head, const char *filename)
196 {
197         while(head->next != NULL) {
198                 if (strcmp(filename, head->name)==0) 
199                         return(head);
200                 head=head->next;
201         }
202         return(NULL);
203 }
204
205 /*
206  * populate linked list with all ar file entries and offset 
207  */
208 static int displayEntry(headerL_t *head, int funct)
209 {
210         if ( funct & VERBOSE ) {
211                 printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime));
212         }
213         printf("%s\n", head->name);
214         head = head->next;
215         return(TRUE);
216 }
217
218 static int extractAr(int srcFd, int dstFd, headerL_t *file)
219 {
220         lseek(srcFd, file->offset, SEEK_SET);
221         if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE)
222                 return(TRUE);   
223         return(FALSE);
224 }
225
226 extern int ar_main(int argc, char **argv)
227 {
228         int funct = 0, opt=0;
229         int srcFd=0, dstFd=0;
230         headerL_t *header, *entry, *extractList;
231
232         while ((opt = getopt(argc, argv, "ovtpxR")) != -1) {
233                 switch (opt) {
234                 case 'o':
235                         funct |= PRESERVE_DATE;
236                         break;
237                 case 'v':
238                         funct |= VERBOSE;
239                         break;
240                 case 't':
241                         funct |= DISPLAY;
242                         break;
243                 case 'x':
244                         funct |= EXT_TO_FILE;
245                         break;
246                 case 'p':
247                         funct |= EXT_TO_STDOUT;
248                         break;
249                 case 'R':
250                         funct |= RECURSIVE;
251                         break;
252                 default:
253                         usage(ar_usage);
254                 }
255         }
256  
257         /* check the src filename was specified */
258         if (optind == argc) {
259                 usage(ar_usage);
260                 return(FALSE);
261         }
262         
263         if ( (srcFd = open(argv[optind], O_RDONLY)) < 0) {
264                 errorMsg("Cannot read %s\n", optarg);
265                 return (FALSE);
266         }
267         optind++;       
268         entry = (headerL_t *) malloc(sizeof(headerL_t));
269         header = (headerL_t *) malloc(sizeof(headerL_t));
270         extractList = (headerL_t *) malloc(sizeof(headerL_t));  
271
272         header = getHeaders(srcFd, header, funct);
273         
274         /* find files to extract or display */
275         if (optind<argc) {
276                 /* only handle specified files */
277                 while(optind < argc) { 
278                         if ( (entry = findEntry(header, argv[optind])) != NULL) {
279                                 entry->next = (headerL_t *) malloc(sizeof(headerL_t));
280                                 *entry->next = *extractList;
281                                 *extractList = *entry;
282                         }
283                         optind++;
284                 }       
285         }
286         else 
287                 /* extract everything */
288                 extractList = header;
289         
290        while(extractList->next != NULL) {       
291                 if ( funct & EXT_TO_FILE ) {
292                         dstFd = open(extractList->name, O_WRONLY | O_CREAT, extractList->mode);
293                         
294                         extractAr(srcFd, dstFd, extractList);
295                 }
296                 if ( funct & EXT_TO_STDOUT )    
297                         extractAr(srcFd, fileno(stdout), extractList);  
298                 if ( (funct & DISPLAY) || (funct & VERBOSE))
299                         displayEntry(extractList, funct);
300                 extractList=extractList->next;
301         }
302         return (TRUE);
303 }