A number of additional fixed from Pavel Roskin, note some more bugs in the
[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  */
25
26
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include "internal.h"
32
33 #define AR_BLOCK_SIZE 60
34 #define BB_DECLARE_EXTERN
35 #define bb_need_io_error
36 #include "messages.c"
37
38 struct ArHeader {                               /* Byte Offset */
39         char ar_name[16];                       /*  0-15 */
40         char ar_date[12];                       /* 16-27 */
41         char ar_uid[6], ar_gid[6];      /* 28-39 */
42         char ar_mode[8];                        /* 40-47 */
43         char ar_size[10];                       /* 48-57 */
44         char ar_fmag[2];                        /* 58-59 */
45 };
46 typedef struct ArHeader ArHeader;
47
48 struct ArInfo {
49         char name[17];                          /* File name */
50         time_t date;                            /* long int, No of seconds since epoch */
51         uid_t uid;                                      /* unsigned int, Numeric UID */
52         gid_t gid;                                      /* unsigned int, Numeric GID */
53         mode_t mode;                            /* unsigned int, Unix mode */
54         size_t size;                            /* int, Size of the file */
55 };
56 typedef struct ArInfo ArInfo;
57
58 static const char ar_usage[] = "ar [optxvV] archive [filenames] \n"
59 #ifndef BB_FEATURE_TRIVIAL_HELP
60         "\nExtract or list files from an ar archive.\n\n"
61         "Options:\n"
62         "\to\t\tpreserve original dates\n"
63         "\tp\t\textract to stdout\n"
64         "\tt\t\tlist\n"
65         "\tx\t\textract\n"
66         "\tv\t\tverbosely list files processed\n"
67 #endif
68         ;
69
70 static void displayContents(struct ArInfo *entry, int funct)
71 {
72         /* TODO convert mode to string */
73         if ((funct & 2) == 2)
74                 printf("%i %i/%i %6i %s ", entry->mode, entry->uid, entry->gid,
75                            entry->size, timeString(entry->date));
76         printf("%s\n", entry->name);
77 }
78
79 /* Converts to new typed struct */
80 static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
81 {
82         int count2;
83         int count;
84
85         for (count = 0; count < 16; count++) {
86                 if (rawHeader->ar_name[count] == ' ') {
87                         for (count2 = count; count2 < 16; count2++)
88                                 if (!isspace(rawHeader->ar_name[count2]))
89                                         break;
90                         if (count2 >= 16)
91                                 break;
92                 }
93                 if (rawHeader->ar_name[count] == '/')
94                         break;
95                 header->name[count] = rawHeader->ar_name[count];
96         }
97         header->name[count] = '\0';
98         header->date = atoi(rawHeader->ar_date);
99         header->uid = atoi(rawHeader->ar_uid);
100         header->gid = atoi(rawHeader->ar_gid);
101         header->mode = atoi(rawHeader->ar_mode);
102         header->size = atoi(rawHeader->ar_size);
103         return (TRUE);
104 }
105
106 /*
107  * Copy size bytes from current position if srcFd to current position in dstFd
108  * taken from tarExtractRegularFile in tar.c
109  * could be used for ar, tar and copyFile .  
110  */
111 static int copySubFile(int srcFd, int dstFd, int copySize)
112 {
113         int readSize, writeSize, doneSize;
114         char buffer[BUFSIZ];
115
116         while (copySize > 0) {
117                 if (copySize > BUFSIZ)
118                         readSize = BUFSIZ;
119                 else
120                         readSize = copySize;
121                 writeSize = fullRead(srcFd, buffer, readSize);
122                 if (writeSize <= 0) {
123                         errorMsg(io_error, "copySubFile :", strerror(errno));
124                         return (FALSE);
125                 }
126                 doneSize = fullWrite(dstFd, buffer, writeSize);
127                 if (doneSize <= 0) {
128                         errorMsg(io_error, "copySubFile :", strerror(errno));
129                         return (FALSE);
130                 }
131                 copySize -= doneSize;
132         }
133         return (TRUE);
134 }
135
136 /*
137  * Need to add checks, stat newfile before creation,change mode from 0777
138  * extract to current dir
139  * dstStat.st_size copied from current position of file pointed to by srcFd
140  */
141 static int extractToFile(int srcFd, const char *path, const char *name,
142                                                  int size)
143 {
144         int dstFd, temp;
145         struct stat tmpStat;
146         char *pathname = NULL;
147
148         if ((temp = isDirectory(path, TRUE, &tmpStat)) != TRUE) {
149                 if (!createPath(path, 0777)) {
150                         fatalError("Cannot extract to specified path");
151                         return (FALSE);
152                 }
153         }
154         temp = (strlen(path) + 16);
155         pathname = (char *) xmalloc(temp);
156         pathname = strcpy(pathname, path);
157         pathname = strcat(pathname, &name[0]);
158         dstFd = device_open(pathname, O_WRONLY | O_CREAT);
159         pathname = NULL;
160         temp = copySubFile(srcFd, dstFd, size);
161         close(dstFd);
162         return (TRUE);
163 }
164
165 /* Step through the ar file entries  */
166 static int readArFile(char **fileNames, int argc, int funct)
167 {
168         int arFd = 0;
169         int pdates = 0;
170         int verbose = 0;
171         int display = 0;
172         int extract = 0;
173         int extToStdout = 0;
174         int status = 0;
175         int found = 0;
176         ArHeader rawArHeader;
177         ArInfo arEntry;
178         char arVersion[8];
179         char *arName;
180         char *selName[argc - 2];
181         int i;
182
183         if ((funct & 1) == 1)
184                 pdates = 1;
185         if ((funct & 2) == 2)
186                 verbose = 1;
187         if ((funct & 4) == 4)
188                 display = 1;
189         if ((funct & 16) == 16) {       /* extract to stdout */
190                 extract = 1;
191                 extToStdout = 1;
192         }
193         if ((funct & 8) == 8) {         /* extract to file */
194                 extract = 1;
195         }
196         arName = fileNames[2];
197         for (i = 0; i < (argc - 3); i++)
198                 selName[i] = fileNames[i + 3];
199         arFd = open(arName, O_RDONLY);
200         if (arFd < 0) {
201                 errorMsg("Error opening '%s': %s\n", arName, strerror(errno));
202                 return (FALSE);
203         }
204         if (fullRead(arFd, arVersion, 8) <= 0) {
205                 errorMsg("ar: Unexpected EOF in archive\n");
206                 return (FALSE);
207         }
208         if (strncmp(arVersion, "!<arch>", 7) != 0) {
209                 errorMsg("ar header fails check ");
210                 return (FALSE);
211         }
212         while ((status = fullRead(arFd, (char *) &rawArHeader, AR_BLOCK_SIZE))
213                    == AR_BLOCK_SIZE) {
214                 readArHeader(&rawArHeader, &arEntry);
215
216                 if (display == 1) {
217                         displayContents(&arEntry, funct);
218                 }
219                 if (argc == 3)
220                         found = 1;
221                 else {
222                         found = 0;
223                         for (i = 0; i < (argc - 3); i++) {
224                                 if ((status = (strcmp(selName[i], arEntry.name))) == 0)
225                                         found = 1;
226                         }
227                 }
228                 if ((extract == 1) && (found == 1)) {
229                         if (extToStdout == 1) {
230                                 copySubFile(arFd, fileno(stdout), arEntry.size);
231                         } else {
232                                 extractToFile(arFd, "./", arEntry.name, arEntry.size);
233                         }
234                 } else
235                         lseek(arFd, arEntry.size, SEEK_CUR);
236         }
237         return (0);
238 }
239
240 extern int ar_main(int argc, char **argv)
241 {
242         int ret = 0;
243         char *opt_ptr;
244         char c;
245         int funct = 0;
246
247         if (argc < 2)
248                 usage(ar_usage);
249
250         opt_ptr = argv[1];
251         if (*opt_ptr == '-')
252                 ++opt_ptr;
253         while ((c = *opt_ptr++) != '\0') {
254                 switch (c) {
255                 case 'o':                               /* preserver original dates */
256                         funct = funct | 1;
257                         break;
258                 case 'p':                               /* extract to stdout */
259                         funct = funct | 16;
260                         break;
261                 case 't':                               /* display contents */
262                         funct = funct | 4;
263                         break;
264                 case 'x':                               /* extract contents of archive */
265                         funct = funct | 8;
266                         break;
267                 case 'v':                               /* be verbose */
268                         funct = funct | 2;
269                         break;
270                 default:
271                         usage(ar_usage);
272                 }
273         }
274         if (funct > 3)
275                 ret = readArFile(argv, argc, funct);
276         return (ret);
277 }