(o-JX2-244F): Add ideographic-structure.
[chise/xemacs-chise.git-] / netinstall / tar.cc
1 /*
2  * Copyright (c) 2000, Red Hat, Inc.
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     A copy of the GNU General Public License can be found at
10  *     http://www.gnu.org/
11  *
12  * Written by DJ Delorie <dj@cygnus.com>
13  *
14  */
15
16 /* Built-in tar functionality.  See tar.h for usage. */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include "win32.h"
24 #include <zlib.h>
25 #include "tar.h"
26 #include "mkdir.h"
27 #include "log.h"
28
29 #include "port.h"
30
31 #if defined(CYGWIN) || defined(MINGW)
32 #define FACTOR (0x19db1ded53ea710LL)
33 #define NSPERSEC 10000000LL
34 #else
35 __int64 FACTOR=0x19db1ded53ea710L;
36 __int64 NSPERSEC=10000000L;
37 #endif
38 #define SYMLINK_COOKIE "!<symlink>"
39
40 typedef struct {
41   char name[100];               /*   0 */
42   char mode[8];                 /* 100 */
43   char uid[8];                  /* 108 */
44   char gid[8];                  /* 116 */
45   char size[12];                /* 124 */
46   char mtime[12];               /* 136 */
47   char chksum[8];               /* 148 */
48   char typeflag;                /* 156 */
49   char linkname[100];           /* 157 */
50   char magic[6];                /* 257 */
51   char version[2];              /* 263 */
52   char uname[32];               /* 265 */
53   char gname[32];               /* 297 */
54   char devmajor[8];             /* 329 */
55   char devminor[8];             /* 337 */
56   char prefix[155];             /* 345 */
57   char junk[12];                /* 500 */
58 } tar_header_type;
59
60 typedef struct tar_map_result_type_s {
61   struct tar_map_result_type_s *next;
62   char *stored_name;
63   char *mapped_name;
64 } tar_map_result_type;
65
66 static tar_map_result_type *tar_map_result = 0;
67
68 static int err;
69
70 static char file_name[_MAX_PATH+512];
71 static char have_longname = 0;
72 static int  file_length;
73
74 static tar_header_type tar_header;
75 static char buf[512];
76
77 static int _tar_file_size = 0;
78 int _tar_verbose = 0;
79 FILE * _tar_vfile = 0;
80 #define vp if (_tar_verbose) fprintf
81 #define vp2 if (_tar_verbose>1) fprintf
82
83 static gzFile g = 0;
84
85 static char *
86 xstrdup (char *c)
87 {
88   char *r = (char *) malloc (strlen (c) + 1);
89   if (!r)
90     exit_setup (1);
91   strcpy (r, c);
92   return r;
93 }
94
95 int
96 tar_open (char *pathname)
97 {
98   struct stat s;
99   if (_tar_vfile == 0)
100     _tar_vfile = stderr;
101
102   vp2 (_tar_vfile, "tar: open `%s'\n", pathname);
103   if (stat (pathname, &s) < 0)
104     return 1;
105   _tar_file_size = s.st_size;
106
107   g = gzopen (pathname, "rb");
108   if (sizeof (tar_header) != 512)
109     {
110       /* drastic, but important */
111       fprintf (stderr, "compilation error: tar header struct not 512"
112                " bytes (it's %d)\n", sizeof (tar_header));
113       exit_setup (1);
114     }
115   err = 0;
116   return g ? 0 : 1;
117 }
118
119 /* For some reason the cygwin version uses a function that is not in
120    the original source. We duplicate it here - although this does mean
121    revealing some internals. */
122 extern "C" {
123   z_off_t ZEXPORT tar_gzctell (gzFile file);
124   typedef struct gz_stream {
125     z_stream stream;
126     int      z_err;   /* error code for last stream operation */
127     int      z_eof;   /* set if end of input file */
128     FILE     *file;   /* .gz file */
129     Byte     *inbuf;  /* input buffer */
130     Byte     *outbuf; /* output buffer */
131     uLong    crc;     /* crc32 of uncompressed data */
132     char     *msg;    /* error message */
133     char     *path;   /* path name for debugging only */
134     int      transparent; /* 1 if input file is not a .gz file */
135     char     mode;    /* 'w' or 'r' */
136     long     startpos; /* start of compressed data in file (header skipped) */
137   } gz_stream;
138 };
139
140 z_off_t ZEXPORT tar_gzctell (gzFile file)
141 {
142     gz_stream *s = (gz_stream *)file;
143     return ftell(s->file);
144 }
145
146 int
147 tar_ftell ()
148 {
149   return tar_gzctell (g);
150 }
151
152 static void
153 skip_file ()
154 {
155   while (file_length > 0)
156     {
157       gzread (g, buf, 512);
158       file_length -= 512;
159     }
160 }
161
162 char *
163 tar_next_file ()
164 {
165   int r, n;
166   char *c;
167   r = gzread (g, &tar_header, 512);
168
169   /* See if we're at end of file */
170   if (r != 512)
171     return 0;
172
173   /* See if the header is all zeros (i.e. last block) */
174   n = 0;
175   for (r = 512/sizeof (int); r; r--)
176     n |= ((int *)&tar_header)[r-1];
177   if (n == 0)
178     return 0;
179
180   if (!have_longname && tar_header.typeflag != 'L')
181     {
182       memcpy (file_name, tar_header.name, 100);
183       file_name[100] = 0;
184     }
185
186   sscanf (tar_header.size, "%o", &file_length);
187
188   vp2 (_tar_vfile, "%c %9d %s\n", tar_header.typeflag, file_length, file_name);
189
190   switch (tar_header.typeflag)
191     {
192     case 'L': /* GNU tar long name extension */
193       if (file_length > _MAX_PATH)
194         {
195           skip_file ();
196           fprintf (stderr, "error: long file name exceeds %d characters\n",
197                    _MAX_PATH);
198           err ++;
199           gzread (g, &tar_header, 512);
200           sscanf (tar_header.size, "%o", &file_length);
201           skip_file ();
202           return tar_next_file ();
203         }
204       c = file_name;
205       while (file_length > 0)
206         {
207           int need = file_length > 512 ? 512 : file_length;
208           if (gzread (g, buf, 512) < 512)
209             return 0;
210           memcpy (c, buf, need);
211           c += need;
212           file_length -= need;
213         }
214       *c = 0;
215       have_longname = 1;
216       return tar_next_file ();
217
218     case '3': /* char */
219     case '4': /* block */
220     case '6': /* fifo */
221       fprintf (stderr, "warning: not extracting special file %s\n",
222                file_name);
223       err ++;
224       return tar_next_file ();
225
226     case '0': /* regular file */
227     case 0: /* regular file also */
228     case '2': /* symbolic link */
229     case '5': /* directory */
230     case '7': /* contiguous file */
231       return file_name;
232
233     case '1': /* hard link, we just copy */
234       return file_name;
235
236     default:
237       fprintf (stderr, "error: unknown (or unsupported) file type `%c'\n",
238                tar_header.typeflag);
239       err ++;
240       skip_file ();
241       return tar_next_file ();
242     }
243 }
244
245 static void
246 fix_time_stamp (char *path)
247 {
248   int mtime;
249 #if defined(CYGWIN) || defined(MINGW)
250   long long ftimev;
251 #else
252   __int64 ftimev;
253 #endif
254   FILETIME ftime;
255   HANDLE h;
256
257   sscanf (tar_header.mtime, "%o", &mtime);
258   ftimev = mtime * NSPERSEC + FACTOR;
259   ftime.dwHighDateTime = ftimev >> 32;
260   ftime.dwLowDateTime = ftimev;
261   h = CreateFileA (path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
262                    0, OPEN_EXISTING,
263                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
264   if (h)
265     {
266       SetFileTime (h, 0, 0, &ftime);
267       CloseHandle (h);
268     }
269 }
270
271 static FILE *
272 common_fopen (char *path)
273 {
274   FILE *out;
275   out = fopen (path, "wb");
276   if (!out)
277     {
278       /* maybe we need to create a directory */
279       if (mkdir_p (0, path))
280         {
281           skip_file ();
282           return 0;
283         }
284       out = fopen (path, "wb");
285     }
286   if (!out)
287     {
288       fprintf (stderr, "unable to write to file %s\n", path);
289       perror ("The error was");
290       skip_file ();
291       return 0;
292     }
293   return out;
294 }
295
296 static void
297 prepare_for_file (char *path)
298 {
299   DWORD w;
300   mkdir_p (0, path);
301
302   w = GetFileAttributes (path);
303   if (w != 0xffffffff && w & FILE_ATTRIBUTE_DIRECTORY)
304     {
305       char *tmp = (char *) malloc (strlen (path) + 10);
306       int i = 0;
307       do {
308         i++;
309         sprintf (tmp, "%s.old-%d", path, i);
310       } while (GetFileAttributes (tmp) != 0xffffffff);
311       fprintf (stderr, "warning: moving directory \"%s\" out of the way.\n", path);
312       MoveFile (path, tmp);
313       free (tmp);
314     }
315
316   DeleteFileA (path);
317 }
318
319 int
320 tar_read_file (char *path)
321 {
322   FILE *out, *copy;
323   HANDLE h;
324   DWORD w;
325   int got;
326   tar_map_result_type *tmr;
327
328   switch (tar_header.typeflag)
329     {
330     case '0':   /* regular files */
331     case 0:
332     case '7':
333       vp (_tar_vfile, "F %s\n", path);
334       prepare_for_file (path);
335       out = common_fopen (path);
336       if (!out)
337         return 1;
338
339       while (file_length > 0)
340         {
341           int put;
342           int want = file_length > 512 ? 512 : file_length;
343           got = gzread (g, buf, 512);
344           if (got < 512)
345             {
346               fprintf (stderr, "tar: unexpected end of file reading %s\n", path);
347               fclose (out);
348               remove (path);
349               return 1;
350             }
351           put = fwrite (buf, 1, want, out);
352           if (put < want)
353             {
354               fprintf (stderr, "tar: out of disk space writing %s\n", path);
355               fclose (out);
356               remove (path);
357               skip_file ();
358               return 1;
359             }
360           file_length -= want;
361         }
362       fclose (out);
363
364       fix_time_stamp (path);
365
366       /* we need this to do hard links below */
367       tmr = (tar_map_result_type *) malloc (sizeof (tar_map_result_type));
368       tmr->next = tar_map_result;
369       tmr->stored_name = xstrdup (file_name);
370       tmr->mapped_name = xstrdup (path);
371       tar_map_result = tmr;
372
373       return 0;
374
375     case '1':   /* hard links; we just copy */
376       for (tmr = tar_map_result; tmr; tmr=tmr->next)
377         if (strcmp (tmr->stored_name, tar_header.linkname) == 0)
378           break;
379       if (!tmr)
380         {
381           fprintf (stderr, "tar: can't find %s to link %s to\n",
382                    tar_header.linkname, path);
383           return 1;
384         }
385       vp (_tar_vfile, "H %s <- %s\n", path, tmr->mapped_name);
386       prepare_for_file (path);
387       copy = fopen (tmr->mapped_name, "rb");
388       if (!copy)
389         {
390           fprintf (stderr, "tar: unable to read %s\n", tmr->mapped_name);
391           return 1;
392         }
393       out = common_fopen (path);
394       if (!out)
395         return 1;
396
397       while ((got = fread (buf, 1, 512, copy)) > 0)
398         {
399           int put = fwrite (buf, 1, got, out);
400           if (put < got)
401             {
402               fprintf (stderr, "tar: out of disk space writing %s\n", path);
403               fclose (out);
404               fclose (copy);
405               remove (path);
406               return 1;
407             }
408         }
409       fclose (out);
410       fclose (copy);
411
412       fix_time_stamp (path);
413       return 0;
414
415     case '5':   /* directories */
416       vp (_tar_vfile, "D %s\n", path);
417       while (path[0] && path[strlen (path)-1] == '/')
418         path[strlen (path) - 1] = 0;
419       return mkdir_p (1, path);
420
421
422     case '2':   /* symbolic links */
423       vp (_tar_vfile, "L %s -> %s\n", path, tar_header.linkname);
424       prepare_for_file (path);
425       h = CreateFileA (path, GENERIC_WRITE, 0, 0, CREATE_NEW,
426                       FILE_ATTRIBUTE_NORMAL, 0);
427       if (h == INVALID_HANDLE_VALUE)
428         {
429           fprintf (stderr, "error: unable to create symlink \"%s\" -> \"%s\"\n",
430                    path, tar_header.linkname);
431           return 1;
432         }
433       strcpy (buf, SYMLINK_COOKIE);
434       strcat (buf, tar_header.linkname);
435       if (WriteFile (h, buf, strlen (buf) + 1, &w, NULL))
436         {
437           CloseHandle (h);
438           SetFileAttributesA (path, FILE_ATTRIBUTE_SYSTEM);
439           return 0;
440         }
441       CloseHandle (h);
442       fprintf (stderr, "error: unable to write symlink \"%s\"\n", path);
443       DeleteFileA (path);
444       return 1;
445     }
446
447   return 0;
448 }
449
450 int
451 tar_close ()
452 {
453 #if 0
454   while (tar_map_result)
455     {
456       tar_map_result_type *t = tar_map_result->next;
457       free (tar_map_result->stored_name);
458       free (tar_map_result->mapped_name);
459       free (tar_map_result);
460       tar_map_result = t;
461     }
462 #endif
463   tar_map_result = 0;
464
465   if (gzclose (g))
466     err ++;
467   return err; /* includes errors for skipped files, etc */
468 }
469
470 typedef struct {
471   char *from;
472   int from_len;
473   char *to;
474   int to_len;
475 } map_type;
476
477 static map_type *map;
478 static int nmaps;
479
480 int
481 tar_auto (char *pathname, char **maplist)
482 {
483   char *c;
484   int errcount = 0;
485   int i, j;
486   map_type mtemp;
487   char newname[_MAX_PATH+512];
488   static char twiddles[] = "|\b/\b-\b\\\b";
489   int t = 0;
490
491   for (nmaps=0; maplist[nmaps*2]; nmaps++) ;
492   map = (map_type *) malloc ((nmaps+1) * sizeof (map_type));
493   for (nmaps=0; maplist[nmaps*2]; nmaps++)
494     {
495       map[nmaps].from = maplist[nmaps*2];
496       map[nmaps].from_len = strlen (maplist[nmaps*2]);
497       map[nmaps].to = maplist[nmaps*2+1];
498       map[nmaps].to_len = strlen (maplist[nmaps*2+1]);
499     }
500   /* bubble sort - expect the maps to be short */
501   for (i=0; i<nmaps-1; i++)
502     for (j=i+1; j<nmaps; j++)
503       if (map[i].from_len < map[j].from_len)
504         {
505           mtemp = map[i];
506           map[i] = map[j];
507           map[j] = mtemp;
508         }
509
510   if ((tar_open (pathname)))
511     return 1;
512   while ((c = tar_next_file ()))
513     {
514       int l = strlen (c);
515       for (i=0; i<nmaps; i++)
516         if (l >= map[i].from_len
517             && strncmp (c, map[i].from, map[i].from_len) == 0)
518           {
519             strcpy (newname, map[i].to);
520             strcpy (newname+map[i].to_len, c + map[i].from_len);
521             c = newname;
522             break;
523           }
524
525       t = (t+2) % 8;
526       fwrite (twiddles+t, 1, 2, stderr);
527
528       if (tar_read_file (c))
529         errcount ++;
530     }
531   if (tar_close ())
532     errcount ++;
533
534   fwrite (" \b", 1, 2, stderr);
535
536   vp2 (_tar_vfile, "tar_auto returns %d\n", errcount);
537   return errcount;
538 }