eaa15a516b44e2e2bc1a7249154cc46dadcb95fb
[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
47 #define MAX_NAME_LENGTH 100
48
49 //#define BB_DECLARE_EXTERN
50 //#define bb_need_io_error
51 //#include "messages.c"
52
53 typedef struct rawArHeader {    /* Byte Offset */
54         char name[16];          /*  0-15 */
55         char date[12];          /* 16-27 */
56         char uid[6], gid[6];    /* 28-39 */
57         char mode[8];           /* 40-47 */
58         char size[10];          /* 48-57 */
59         char fmag[2];           /* 58-59 */
60 } rawArHeader_t;
61
62 typedef struct headerL {
63         char name[MAX_NAME_LENGTH];
64         size_t size;
65         uid_t uid;
66         gid_t gid;
67         mode_t mode;
68         time_t mtime;
69         off_t offset;
70         struct headerL *next;
71 } headerL_t;
72
73 /*
74  * identify Ar header (magic) and set srcFd to first header entry 
75  */
76 static int checkArMagic(int srcFd)
77 {
78         char arMagic[8];
79         if (fullRead(srcFd, arMagic, 8) != 8)
80                 return (FALSE);
81         
82         if (strncmp(arMagic,"!<arch>",7) != 0)
83                 return(FALSE);
84         return(TRUE);
85 }
86
87 /*
88  * read, convert and check the raw ar header
89  * srcFd should be pointing to the start of header prior to entry
90  * srcFd will be pointing at the start of data after successful exit
91  * if returns FALSE srcFd is reset to initial position
92  */
93 static int readRawArHeader(int srcFd, headerL_t *header)
94 {
95         rawArHeader_t rawArHeader;
96         off_t   initialOffset;
97         size_t nameLength;
98         
99         initialOffset = lseek(srcFd, 0, SEEK_CUR);
100         if (fullRead(srcFd, (char *) &rawArHeader, 60) != 60) {
101                 lseek(srcFd, initialOffset, SEEK_SET);
102                 return(FALSE);
103         }
104         if ((rawArHeader.fmag[0]!='`') || (rawArHeader.fmag[1]!='\n')) {
105                 lseek(srcFd, initialOffset, SEEK_SET);
106                 return(FALSE);
107         }
108
109         strncpy(header->name, rawArHeader.name, 16);
110         nameLength=strcspn(header->name, " \\");
111         header->name[nameLength]='\0';
112         parse_mode(rawArHeader.mode, &header->mode);
113         header->mtime = atoi(rawArHeader.date);
114         header->uid = atoi(rawArHeader.uid);
115         header->gid = atoi(rawArHeader.gid);
116         header->size = (size_t) atoi(rawArHeader.size);
117         header->offset = initialOffset + (off_t) 60;
118         return(TRUE); 
119 }
120
121 /*
122  * get, check and correct the converted header
123  */ 
124 static int readArEntry(int srcFd, headerL_t *newEntry)
125 {
126         size_t nameLength;
127
128         if(readRawArHeader(srcFd, newEntry)==FALSE)
129                 return(FALSE);
130         
131         nameLength = strcspn(newEntry->name, "/");
132         
133         /* handle GNU style short filenames, strip trailing '/' */
134         if (nameLength > 0)
135                 newEntry->name[nameLength]='\0';
136         
137         /* handle GNU style long filenames */ 
138         if (nameLength == 0) {
139                 /* escape from recursive call */
140                 if (newEntry->name[1]=='0') 
141                         return(TRUE);
142
143                 /* the data section contains the real filename */
144                 if (newEntry->name[1]=='/') {
145                         char tempName[MAX_NAME_LENGTH];
146
147                         if (newEntry->size > MAX_NAME_LENGTH)
148                                 newEntry->size = MAX_NAME_LENGTH;
149                         fullRead(srcFd, tempName, newEntry->size);
150                         tempName[newEntry->size-3]='\0';
151                         
152                         /* read the second header for this entry */
153                         /* be carefull, this is recursive */
154                         if (readArEntry(srcFd, newEntry)==FALSE)
155                                 return(FALSE);
156                 
157                         if ((newEntry->name[0]='/') && (newEntry->name[1]='0'))
158                                 strcpy(newEntry->name, tempName);
159                         else {
160                                 errorMsg("Invalid long filename\n");
161                                 return(FALSE);
162                         }
163                 }
164         }
165         return(TRUE);   
166 }
167
168 /*
169  * return the headerL_t struct for the specified filename
170  */
171 static headerL_t *getHeaders(int srcFd, headerL_t *head)
172 {
173         int arEntry=FALSE;
174         headerL_t *list;
175         list = (headerL_t *) malloc(sizeof(headerL_t));
176
177         if (checkArMagic(srcFd)==TRUE)
178                 arEntry=TRUE;
179         else
180                 errorMsg("isnt an ar archive\n");
181
182         if (arEntry==TRUE) { 
183                 while(readArEntry(srcFd, list) == TRUE) {
184                         list->next = (headerL_t *) malloc(sizeof(headerL_t));
185                         *list->next = *head;
186                         *head = *list;
187
188                         /* recursive check for sub-archives */
189                         lseek(srcFd, list->size, SEEK_CUR);
190 /*                      printf("checking for sub headers\n");
191                         if ((subList = getHeaders(srcFd, list->next)) != NULL) {
192                                 printf("found a sub archive !\n");
193                         }
194                         else    
195                                 printf("didnt find a sub header\n"); */
196                 }
197         }
198
199         return(head);
200 }
201
202 /*
203  * find an entry in the linked list matching the filename
204  */
205 static headerL_t *findEntry(headerL_t *head, const char *filename)
206 {
207         while(head->next != NULL) {
208                 if (strcmp(filename, head->name)==0) 
209                         return(head);
210                 head=head->next;
211         }
212         return(NULL);
213 }
214
215 /*
216  * populate linked list with all ar file entries and offset 
217  */
218 static int displayEntry(headerL_t *head, int funct)
219 {
220         if ((funct & VERBOSE) == VERBOSE) {
221                 printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime));
222         }
223         printf("%s\n", head->name);
224         head = head->next;
225         return(TRUE);
226 }
227
228 static int extractAr(int srcFd, int dstFd, headerL_t *file)
229 {
230         lseek(srcFd, file->offset, SEEK_SET);
231         if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE)
232                 return(TRUE);   
233         return(FALSE);
234 }
235
236 extern int ar_main(int argc, char **argv)
237 {
238         int funct = 0, opt=0;
239         int srcFd=0, dstFd=0;
240         headerL_t *header, *entry, *extractList;
241
242         while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) {
243                 switch (opt) {
244                 case 'o':
245                         funct = funct | PRESERVE_DATE;
246                         break;
247                 case 'v':
248                         funct = funct | VERBOSE;
249                         break;
250                 case 't':
251                         funct = funct | DISPLAY;
252                 case 'x':
253                         if (opt=='x') {
254                                 funct = funct | EXT_TO_FILE;
255                         }
256                 case 'p':
257                         if (opt=='p') {
258                                 funct = funct | EXT_TO_STDOUT;
259                         }
260                         /* following is common to 't','x' and 'p' */
261                         if ( (srcFd = open(optarg, O_RDONLY)) < 0) {
262                                 errorMsg("Cannot read %s\n", optarg);
263                                 return (FALSE);
264                         }
265                         break;
266                 default:
267                         usage(ar_usage);
268                 }
269         }
270  
271         /* check options not just preserve_dates and/or verbose */  
272         if (funct < 4) {
273                 usage(ar_usage);
274                 return(FALSE);
275         }
276         
277         entry = (headerL_t *) malloc(sizeof(headerL_t));
278         header = (headerL_t *) malloc(sizeof(headerL_t));
279         extractList = (headerL_t *) malloc(sizeof(headerL_t));  
280
281         header = getHeaders(srcFd, header);
282         
283         /* find files to extract or display */
284         if (optind<argc) {
285                 /* only handle specified files */
286                 while(optind < argc) { 
287                         if ( (entry = findEntry(header, argv[optind])) != NULL) {
288                                 entry->next = (headerL_t *) malloc(sizeof(headerL_t));
289                                 *entry->next = *extractList;
290                                 *extractList = *entry;
291                         }
292                         optind++;
293                 }       
294         }
295         else 
296                 /* extract everything */
297                 extractList = header;
298         
299        while(extractList->next != NULL) {       
300                 if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) {
301                         dstFd = open(extractList->name, O_WRONLY | O_CREAT, extractList->mode);
302                         
303                         extractAr(srcFd, dstFd, extractList);
304                 }
305                 if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT)  
306                         extractAr(srcFd, fileno(stdout), extractList);  
307                 if ( (funct & DISPLAY) == DISPLAY)
308                         displayEntry(extractList, funct);
309                 extractList=extractList->next;
310         }
311         return (TRUE);
312 }