Import libarchive and bsdtar version 2.0.25
[dragonfly.git] / contrib / libarchive-2.0 / libarchive / archive_write_set_format_shar.c
1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.16 2007/03/03 07:37:36 kientzle Exp $");
28
29 #ifdef HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43
44 #include "archive.h"
45 #include "archive_entry.h"
46 #include "archive_private.h"
47 #include "archive_write_private.h"
48
49 struct shar {
50         int                      dump;
51         int                      end_of_line;
52         struct archive_entry    *entry;
53         int                      has_data;
54         char                    *last_dir;
55         char                     outbuff[1024];
56         size_t                   outbytes;
57         size_t                   outpos;
58         int                      uuavail;
59         char                     uubuffer[3];
60         int                      wrote_header;
61         struct archive_string    work;
62 };
63
64 static int      archive_write_shar_finish(struct archive_write *);
65 static int      archive_write_shar_destroy(struct archive_write *);
66 static int      archive_write_shar_header(struct archive_write *,
67                     struct archive_entry *);
68 static ssize_t  archive_write_shar_data_sed(struct archive_write *,
69                     const void * buff, size_t);
70 static ssize_t  archive_write_shar_data_uuencode(struct archive_write *,
71                     const void * buff, size_t);
72 static int      archive_write_shar_finish_entry(struct archive_write *);
73 static int      shar_printf(struct archive_write *, const char *fmt, ...);
74 static void     uuencode_group(struct shar *);
75
76 static int
77 shar_printf(struct archive_write *a, const char *fmt, ...)
78 {
79         struct shar *shar;
80         va_list ap;
81         int ret;
82
83         shar = (struct shar *)a->format_data;
84         va_start(ap, fmt);
85         archive_string_empty(&(shar->work));
86         archive_string_vsprintf(&(shar->work), fmt, ap);
87         ret = ((a->compression_write)(a, shar->work.s, strlen(shar->work.s)));
88         va_end(ap);
89         return (ret);
90 }
91
92 /*
93  * Set output format to 'shar' format.
94  */
95 int
96 archive_write_set_format_shar(struct archive *_a)
97 {
98         struct archive_write *a = (struct archive_write *)_a;
99         struct shar *shar;
100
101         /* If someone else was already registered, unregister them. */
102         if (a->format_destroy != NULL)
103                 (a->format_destroy)(a);
104
105         shar = (struct shar *)malloc(sizeof(*shar));
106         if (shar == NULL) {
107                 archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
108                 return (ARCHIVE_FATAL);
109         }
110         memset(shar, 0, sizeof(*shar));
111         a->format_data = shar;
112
113         a->pad_uncompressed = 0;
114         a->format_write_header = archive_write_shar_header;
115         a->format_finish = archive_write_shar_finish;
116         a->format_destroy = archive_write_shar_destroy;
117         a->format_write_data = archive_write_shar_data_sed;
118         a->format_finish_entry = archive_write_shar_finish_entry;
119         a->archive_format = ARCHIVE_FORMAT_SHAR_BASE;
120         a->archive_format_name = "shar";
121         return (ARCHIVE_OK);
122 }
123
124 /*
125  * An alternate 'shar' that uses uudecode instead of 'sed' to encode
126  * file contents and can therefore be used to archive binary files.
127  * In addition, this variant also attempts to restore ownership, file modes,
128  * and other extended file information.
129  */
130 int
131 archive_write_set_format_shar_dump(struct archive *_a)
132 {
133         struct archive_write *a = (struct archive_write *)_a;
134         struct shar *shar;
135
136         archive_write_set_format_shar(&a->archive);
137         shar = (struct shar *)a->format_data;
138         shar->dump = 1;
139         a->format_write_data = archive_write_shar_data_uuencode;
140         a->archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
141         a->archive_format_name = "shar dump";
142         return (ARCHIVE_OK);
143 }
144
145 static int
146 archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
147 {
148         const char *linkname;
149         const char *name;
150         char *p, *pp;
151         struct shar *shar;
152         const struct stat *st;
153         int ret;
154
155         shar = (struct shar *)a->format_data;
156         if (!shar->wrote_header) {
157                 ret = shar_printf(a, "#!/bin/sh\n");
158                 if (ret != ARCHIVE_OK)
159                         return (ret);
160                 ret = shar_printf(a, "# This is a shell archive\n");
161                 if (ret != ARCHIVE_OK)
162                         return (ret);
163                 shar->wrote_header = 1;
164         }
165
166         /* Save the entry for the closing. */
167         if (shar->entry)
168                 archive_entry_free(shar->entry);
169         shar->entry = archive_entry_clone(entry);
170         name = archive_entry_pathname(entry);
171         st = archive_entry_stat(entry);
172
173         /* Handle some preparatory issues. */
174         switch(st->st_mode & S_IFMT) {
175         case S_IFREG:
176                 /* Only regular files have non-zero size. */
177                 break;
178         case S_IFDIR:
179                 archive_entry_set_size(entry, 0);
180                 /* Don't bother trying to recreate '.' */
181                 if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
182                         return (ARCHIVE_OK);
183                 break;
184         case S_IFIFO:
185         case S_IFCHR:
186         case S_IFBLK:
187                 /* All other file types have zero size in the archive. */
188                 archive_entry_set_size(entry, 0);
189                 break;
190         default:
191                 archive_entry_set_size(entry, 0);
192                 if (archive_entry_hardlink(entry) == NULL &&
193                     archive_entry_symlink(entry) == NULL) {
194                         archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
195                             "shar format cannot archive this");
196                         return (ARCHIVE_WARN);
197                 }
198         }
199
200         /* Stock preparation for all file types. */
201         ret = shar_printf(a, "echo x %s\n", name);
202         if (ret != ARCHIVE_OK)
203                 return (ret);
204
205         if (!S_ISDIR(st->st_mode)) {
206                 /* Try to create the dir. */
207                 p = strdup(name);
208                 pp = strrchr(p, '/');
209                 /* If there is a / character, try to create the dir. */
210                 if (pp != NULL) {
211                         *pp = '\0';
212
213                         /* Try to avoid a lot of redundant mkdir commands. */
214                         if (strcmp(p, ".") == 0) {
215                                 /* Don't try to "mkdir ." */
216                         } else if (shar->last_dir == NULL) {
217                                 ret = shar_printf(a,
218                                     "mkdir -p %s > /dev/null 2>&1\n", p);
219                                 if (ret != ARCHIVE_OK)
220                                         return (ret);
221                                 shar->last_dir = p;
222                         } else if (strcmp(p, shar->last_dir) == 0) {
223                                 /* We've already created this exact dir. */
224                                 free(p);
225                         } else if (strlen(p) < strlen(shar->last_dir) &&
226                             strncmp(p, shar->last_dir, strlen(p)) == 0) {
227                                 /* We've already created a subdir. */
228                                 free(p);
229                         } else {
230                                 ret = shar_printf(a,
231                                     "mkdir -p %s > /dev/null 2>&1\n", p);
232                                 if (ret != ARCHIVE_OK)
233                                         return (ret);
234                                 free(shar->last_dir);
235                                 shar->last_dir = p;
236                         }
237                 }
238         }
239
240         /* Handle file-type specific issues. */
241         shar->has_data = 0;
242         if ((linkname = archive_entry_hardlink(entry)) != NULL) {
243                 ret = shar_printf(a, "ln -f %s %s\n", linkname, name);
244                 if (ret != ARCHIVE_OK)
245                         return (ret);
246         } else if ((linkname = archive_entry_symlink(entry)) != NULL) {
247                 ret = shar_printf(a, "ln -fs %s %s\n", linkname, name);
248                 if (ret != ARCHIVE_OK)
249                         return (ret);
250         } else {
251                 switch(st->st_mode & S_IFMT) {
252                 case S_IFREG:
253                         if (archive_entry_size(entry) == 0) {
254                                 /* More portable than "touch." */
255                                 ret = shar_printf(a, "test -e \"%s\" || :> \"%s\"\n", name, name);
256                                 if (ret != ARCHIVE_OK)
257                                         return (ret);
258                         } else {
259                                 if (shar->dump) {
260                                         ret = shar_printf(a,
261                                             "uudecode -o %s << 'SHAR_END'\n",
262                                             name);
263                                         if (ret != ARCHIVE_OK)
264                                                 return (ret);
265                                         ret = shar_printf(a, "begin %o %s\n",
266                                             archive_entry_mode(entry) & 0777,
267                                             name);
268                                         if (ret != ARCHIVE_OK)
269                                                 return (ret);
270                                 } else {
271                                         ret = shar_printf(a,
272                                             "sed 's/^X//' > %s << 'SHAR_END'\n",
273                                             name);
274                                         if (ret != ARCHIVE_OK)
275                                                 return (ret);
276                                 }
277                                 shar->has_data = 1;
278                                 shar->end_of_line = 1;
279                                 shar->outpos = 0;
280                                 shar->outbytes = 0;
281                         }
282                         break;
283                 case S_IFDIR:
284                         ret = shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n",
285                             name);
286                         if (ret != ARCHIVE_OK)
287                                 return (ret);
288                         /* Record that we just created this directory. */
289                         if (shar->last_dir != NULL)
290                                 free(shar->last_dir);
291
292                         shar->last_dir = strdup(name);
293                         /* Trim a trailing '/'. */
294                         pp = strrchr(shar->last_dir, '/');
295                         if (pp != NULL && pp[1] == '\0')
296                                 *pp = '\0';
297                         /*
298                          * TODO: Put dir name/mode on a list to be fixed
299                          * up at end of archive.
300                          */
301                         break;
302                 case S_IFIFO:
303                         ret = shar_printf(a, "mkfifo %s\n", name);
304                         if (ret != ARCHIVE_OK)
305                                 return (ret);
306                         break;
307                 case S_IFCHR:
308                         ret = shar_printf(a, "mknod %s c %d %d\n", name,
309                             archive_entry_rdevmajor(entry),
310                             archive_entry_rdevminor(entry));
311                         if (ret != ARCHIVE_OK)
312                                 return (ret);
313                         break;
314                 case S_IFBLK:
315                         ret = shar_printf(a, "mknod %s b %d %d\n", name,
316                             archive_entry_rdevmajor(entry),
317                             archive_entry_rdevminor(entry));
318                         if (ret != ARCHIVE_OK)
319                                 return (ret);
320                         break;
321                 default:
322                         return (ARCHIVE_WARN);
323                 }
324         }
325
326         return (ARCHIVE_OK);
327 }
328
329 /* XXX TODO: This could be more efficient XXX */
330 static ssize_t
331 archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
332 {
333         struct shar *shar;
334         const char *src;
335         int ret;
336         size_t written = n;
337
338         shar = (struct shar *)a->format_data;
339         if (!shar->has_data)
340                 return (0);
341
342         src = (const char *)buff;
343         ret = ARCHIVE_OK;
344         shar->outpos = 0;
345         while (n-- > 0) {
346                 if (shar->end_of_line) {
347                         shar->outbuff[shar->outpos++] = 'X';
348                         shar->end_of_line = 0;
349                 }
350                 if (*src == '\n')
351                         shar->end_of_line = 1;
352                 shar->outbuff[shar->outpos++] = *src++;
353
354                 if (shar->outpos > sizeof(shar->outbuff) - 2) {
355                         ret = (a->compression_write)(a, shar->outbuff,
356                             shar->outpos);
357                         if (ret != ARCHIVE_OK)
358                                 return (ret);
359                         shar->outpos = 0;
360                 }
361         }
362
363         if (shar->outpos > 0)
364                 ret = (a->compression_write)(a, shar->outbuff, shar->outpos);
365         if (ret != ARCHIVE_OK)
366                 return (ret);
367         return (written);
368 }
369
370 #define UUENC(c)        (((c)!=0) ? ((c) & 077) + ' ': '`')
371
372 /* XXX This could be a lot more efficient. XXX */
373 static void
374 uuencode_group(struct shar *shar)
375 {
376         int     t;
377
378         t = 0;
379         if (shar->uuavail > 0)
380                 t = 0xff0000 & (shar->uubuffer[0] << 16);
381         if (shar->uuavail > 1)
382                 t |= 0x00ff00 & (shar->uubuffer[1] << 8);
383         if (shar->uuavail > 2)
384                 t |= 0x0000ff & (shar->uubuffer[2]);
385         shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>18) );
386         shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>12) );
387         shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>6) );
388         shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t) );
389         shar->uuavail = 0;
390         shar->outbytes += shar->uuavail;
391         shar->outbuff[shar->outpos] = 0;
392 }
393
394 static ssize_t
395 archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
396     size_t length)
397 {
398         struct shar *shar;
399         const char *src;
400         size_t n;
401         int ret;
402
403         shar = (struct shar *)a->format_data;
404         if (!shar->has_data)
405                 return (ARCHIVE_OK);
406         src = (const char *)buff;
407         n = length;
408         while (n-- > 0) {
409                 if (shar->uuavail == 3)
410                         uuencode_group(shar);
411                 if (shar->outpos >= 60) {
412                         ret = shar_printf(a, "%c%s\n", UUENC(shar->outbytes),
413                             shar->outbuff);
414                         if (ret != ARCHIVE_OK)
415                                 return (ret);
416                         shar->outpos = 0;
417                         shar->outbytes = 0;
418                 }
419
420                 shar->uubuffer[shar->uuavail++] = *src++;
421                 shar->outbytes++;
422         }
423         return (length);
424 }
425
426 static int
427 archive_write_shar_finish_entry(struct archive_write *a)
428 {
429         const char *g, *p, *u;
430         struct shar *shar;
431         int ret;
432
433         shar = (struct shar *)a->format_data;
434         if (shar->entry == NULL)
435                 return (0);
436
437         if (shar->dump) {
438                 /* Finish uuencoded data. */
439                 if (shar->has_data) {
440                         if (shar->uuavail > 0)
441                                 uuencode_group(shar);
442                         if (shar->outpos > 0) {
443                                 ret = shar_printf(a, "%c%s\n",
444                                     UUENC(shar->outbytes), shar->outbuff);
445                                 if (ret != ARCHIVE_OK)
446                                         return (ret);
447                                 shar->outpos = 0;
448                                 shar->uuavail = 0;
449                                 shar->outbytes = 0;
450                         }
451                         ret = shar_printf(a, "%c\n", UUENC(0));
452                         if (ret != ARCHIVE_OK)
453                                 return (ret);
454                         ret = shar_printf(a, "end\n", UUENC(0));
455                         if (ret != ARCHIVE_OK)
456                                 return (ret);
457                         ret = shar_printf(a, "SHAR_END\n");
458                         if (ret != ARCHIVE_OK)
459                                 return (ret);
460                 }
461                 /* Restore file mode, owner, flags. */
462                 /*
463                  * TODO: Don't immediately restore mode for
464                  * directories; defer that to end of script.
465                  */
466                 ret = shar_printf(a, "chmod %o %s\n",
467                     archive_entry_mode(shar->entry) & 07777,
468                     archive_entry_pathname(shar->entry));
469                 if (ret != ARCHIVE_OK)
470                         return (ret);
471
472                 u = archive_entry_uname(shar->entry);
473                 g = archive_entry_gname(shar->entry);
474                 if (u != NULL || g != NULL) {
475                         ret = shar_printf(a, "chown %s%s%s %s\n",
476                             (u != NULL) ? u : "",
477                             (g != NULL) ? ":" : "", (g != NULL) ? g : "",
478                             archive_entry_pathname(shar->entry));
479                         if (ret != ARCHIVE_OK)
480                                 return (ret);
481                 }
482
483                 if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
484                         ret = shar_printf(a, "chflags %s %s\n", p,
485                             archive_entry_pathname(shar->entry));
486                         if (ret != ARCHIVE_OK)
487                                 return (ret);
488                 }
489
490                 /* TODO: restore ACLs */
491
492         } else {
493                 if (shar->has_data) {
494                         /* Finish sed-encoded data:  ensure last line ends. */
495                         if (!shar->end_of_line) {
496                                 ret = shar_printf(a, "\n");
497                                 if (ret != ARCHIVE_OK)
498                                         return (ret);
499                         }
500                         ret = shar_printf(a, "SHAR_END\n");
501                         if (ret != ARCHIVE_OK)
502                                 return (ret);
503                 }
504         }
505
506         archive_entry_free(shar->entry);
507         shar->entry = NULL;
508         return (0);
509 }
510
511 static int
512 archive_write_shar_finish(struct archive_write *a)
513 {
514         struct shar *shar;
515         int ret;
516
517         /*
518          * TODO: Accumulate list of directory names/modes and
519          * fix them all up at end-of-archive.
520          */
521
522         shar = (struct shar *)a->format_data;
523
524         /*
525          * Only write the end-of-archive markers if the archive was
526          * actually started.  This avoids problems if someone sets
527          * shar format, then sets another format (which would invoke
528          * shar_finish to free the format-specific data).
529          */
530         if (shar->wrote_header) {
531                 ret = shar_printf(a, "exit\n");
532                 if (ret != ARCHIVE_OK)
533                         return (ret);
534                 /* Shar output is never padded. */
535                 archive_write_set_bytes_in_last_block(&a->archive, 1);
536                 /*
537                  * TODO: shar should also suppress padding of
538                  * uncompressed data within gzip/bzip2 streams.
539                  */
540         }
541         return (ARCHIVE_OK);
542 }
543
544 static int
545 archive_write_shar_destroy(struct archive_write *a)
546 {
547         struct shar *shar;
548
549         shar = (struct shar *)a->format_data;
550         if (shar->entry != NULL)
551                 archive_entry_free(shar->entry);
552         if (shar->last_dir != NULL)
553                 free(shar->last_dir);
554         archive_string_free(&(shar->work));
555         free(shar);
556         a->format_data = NULL;
557         return (ARCHIVE_OK);
558 }