home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1361 / tarfix < prev   
Encoding:
Text File  |  1990-12-28  |  10.0 KB  |  407 lines

  1. #!/bin/perl
  2. @REM=("
  3. @perl -S %0.bat %1 %2 %3 %4 %5 %6 %7 %8 %9
  4. @goto end ") if 0 ;
  5. #
  6. # Fix and convert tape archives
  7. #
  8. # (C) Copyright 1990 Diomidis Spinellis.  All rights reserved.
  9. #
  10. # Can be copied and used as long as the copyright notice is retained.
  11. # Modified copies must be marked as such.
  12. # This is a beta release.  Use this at your own risk.
  13. # I would appreciate feedback on bugs, improvements etc.
  14. #
  15. # dds@cc.ic.ac.uk
  16. #
  17. # This program IS NOT EFFICIENT.
  18. # A have tried instead to make it readable, flexible and easy to use.
  19. # For this I use lots of subroutines, local variables and higher order 
  20. # functions.  If you want something efficient rewrite it in C.
  21. #
  22. # You can very easily add a new conversion mode.  Just add the conversion
  23. # function.  If the conversion function needs to be applied to every
  24. # filename component add a map function and a function to prepare a component
  25. # to have acounter added to it.
  26. # If you need to convert another part of the header (e.g. uid) search for the
  27. # string HEADERMOD in this file.
  28.  
  29. do 'getopts.pl' || die "$0: Unable to find getopts library: $!\n";
  30. &Getopts('mncaf:t:s:') || $usage++;
  31.  
  32. $count = 0;
  33. $proc = 'copy';
  34.  
  35. if ($opt_c) {
  36.     $proc = 'canonic';
  37.     $count++;
  38. }
  39. if ($opt_a) {
  40.     $proc = 'noabs';
  41.     $count++;
  42. }
  43. if ($opt_f) {
  44.     $proc = 'from_' . $opt_f;
  45.     $count++;
  46. }
  47. if ($opt_t) {
  48.     $proc = 'to_' . $opt_t;
  49.     $count++;
  50. }
  51. if ($opt_s) {
  52.     $proc = 'usershort';
  53.     $usershortlen = $opt_s;
  54.     $count++;
  55. }
  56.  
  57. if ($count > 1) {
  58.     print STDERR "$0: Only one of -c -a -f -t -s can be specified\n";
  59.     $usage++;
  60. }
  61.  
  62. if (! eval('&' . $proc . '("foo");')) {
  63.     print STDERR "$0: Bad option specified $proc\n";
  64.     $usage++;
  65. }
  66. delete $map{'foo'};
  67.  
  68.  
  69. if ($usage) {
  70.     print STDERR "Usage: $0 -n -c -a -s N -f msdos|vms -t msdos|v7";
  71.     print STDERR "
  72.     -n Do not work on tar files.  Read and print a list of file names.
  73.     -c Canonicalise filenames by removing /../, /./ and //.
  74.     -a Fix absolute filenames by removing leading /.
  75.     -s Convert filenames to the specified length N.
  76.     -f Convert from format.  Format can be msdos or vms.
  77.     -t Convert to format.  Format can be v7 (7th Edition) or msdos.
  78.     -m Print a map table containing initial and final name on stderr.
  79.  
  80.     Only one of -c -a -f -t -s can be specified.\n";
  81.     exit 1;
  82. }
  83.  
  84. if ($opt_n) {
  85.     while (<>) {
  86.         s/\n$//;
  87.         print &$proc($_), "\n";
  88.     }
  89. } else {
  90.     ©tar();
  91. }
  92.  
  93. if ($opt_m) {
  94.     while (($from, $to) = each(%map)) {
  95.         print STDERR "$from $to\n";
  96.     }
  97. }
  98.  
  99. exit 0;
  100.  
  101. # Remove absolute file names.
  102. # We canonicalise since foo//bar is /bar on many Unixes
  103. sub noabs {
  104.     local($name) = $_[0];
  105.  
  106.      $name = &canonic($name);
  107.      $name =~ s/^\///;
  108.      return $name;
  109. }
  110.  
  111. # Convert to MS-DOS
  112. # - Shorten name to 8 characters
  113. # - Remove all dots, but the last one
  114. # - Shorten extension to 3 characters
  115. # - Convert ,=+<>|; *?:[]\" to ^
  116. # - Convert device name (con, aux ...) to _device
  117. sub to_msdos {
  118.     local($nm);
  119.  
  120.     $nm = $_[0];
  121.     return &filemap($nm, 'mapmsdos', 'countprepmsdos');
  122.     # The following line fails on perl 3.0 patchlevel 18 XXX
  123.     # return &filemap($_[0], 'mapmsdos', 'countprepmsdos');
  124. }
  125.  
  126. # Shortening routine for MS-DOS
  127. sub mapmsdos {
  128.     local($name) = $_[0];
  129.     local($ext);
  130.  
  131.     # Leave only the last dot
  132.     while ($name =~ s/(.*)\.(.*)\.(.*)/\1_\2.\3/g) { 
  133.         ;
  134.     }
  135.     # Convert funny characters to ^
  136.     $name =~ s/[,=+<>|; *?:[\]\\]/^/g;
  137.     # Shorten the name
  138.     if ($name =~ m/\./) {
  139.         ($name, $ext) = split(/\./, $name);
  140.         $name =~ s/^((con)|(aux[1-4]?)|(prn)|(lpt[1-3])|(clock\$))$/_\1/i;
  141.         return &shorten($name, 8) . '.' . &shorten($ext, 3);
  142.     } else {
  143.         $name =~ s/^((con)|(aux[1-4]?)|(prn)|(lpt[1-3])|(clock\$))$/_\1/i;
  144.         return &shorten($name, 8);
  145.     }
  146. }
  147.  
  148. # Count preparation routine for MS-DOS
  149. sub countprepmsdos {
  150.     local($name) = $_[0];
  151.     local($ext);
  152.     
  153.     if ($name =~ m/\./) {
  154.         ($name, $ext) = split(/\./, $name);
  155.         return $name . '.' . substr($ext, 0, 1);
  156.     } else {
  157.         return $name . '.';
  158.     }
  159. }
  160.  
  161.  
  162. # Convert to 7th Edition type filesystems
  163. # - Shorten filenames to 14 characters
  164. sub to_v7 {
  165.     local($nm);
  166.  
  167.     $nm = $_[0];
  168.     return &filemap($nm, 'mapv7', 'countprepv7');
  169. }
  170.  
  171. # Shortening routine for V7
  172. sub mapv7 {
  173.     return &shorten($_[0], 14);
  174. }
  175.  
  176. # Count preparation routine for V7
  177. sub countprepv7 {
  178.     return substr($_[0], 0, 12);
  179. }
  180.  
  181. # Shorten the filename components by a user specified amount
  182. sub usershort {
  183.     local($nm);
  184.  
  185.     $nm = $_[0];
  186.     return &filemap($nm, 'mapusershort', 'countprepusershort');
  187. }
  188.  
  189. # Shortening routine for usershort
  190. sub mapusershort {
  191.     return &shorten($_[0], $usershortlen);
  192. }
  193.  
  194. # Count preparation routine for usershort
  195. sub countprepusershort {
  196.     return substr($_[0], 0, $usershortlen);
  197. }
  198.  
  199. # Convert from VMS
  200. # - Convert uppercase to lowercase
  201. # - Remove leading device name: or node::
  202. # - Convert directory form [xxx] to xxx/
  203. # - Remove trailing generation number
  204. # - Remove quoting characters ^V and " (XXX)
  205. # NOTE:  I am an ignorant on VMS, so this probably need fixing.  UNTESTED
  206. sub from_vms {
  207.     local($name) = $_[0];
  208.  
  209.     $name =~ tr/[A-Z]\\/[a-z]\//;
  210.     $name =~ s/^[a-z]*::?//;
  211.     $name =~ s/\[(.*)\](.*)/\1\/\2/;
  212.     $name =~ s/;[0-9]+$//;
  213.     $name =~ s/["\026]//g;
  214.     return $name;
  215. }
  216.  
  217. # Convert from MS-DOS
  218. # - Convert \ to /
  219. # - Convert uppercase to lowercase
  220. # - Remove leading device names
  221. sub from_msdos {
  222.     local($name) = $_[0];
  223.  
  224.     $name =~ tr/[A-Z]\\/[a-z]\//;
  225.     $name =~ s/^[a-z]://;
  226.     return $name;
  227. }
  228.  
  229. # filemap(name, mapfunc, countprepfunc)
  230. # Go through every path element of name substituting it with the result
  231. # of mapfunc(element).  If the filename is already used then substitute it
  232. # with the result of applying countprepfunc to with a two letter counter 
  233. # appended.
  234. # Two associative arrays are kept to avoid the chance of re-using a name
  235. # %map contains the mappings from big names to small names
  236. # %used contains 1 for every short name that has been used
  237. # We keep partial file names to speed up the process
  238. # The filenames are always canonicalised
  239. sub filemap {
  240.     local(
  241.         @big,        # Contains components of original
  242.         @small,        # Result is built in here
  243.         @bigpart,    # Part of big that has been done
  244.         @s,        # To try alternative mappings
  245.         $name,        # Part of path we are dealing with
  246.         $count,        # To create distinct names
  247.         $try,        # Remember map result
  248.         $mapfunc,    # Function to create new elements
  249.         $countprepfunc    # Function to prepare for counting
  250.  
  251.     );
  252.  
  253.     $mapfunc = $_[1];
  254.     $countprepfunc = $_[2];
  255.     @big = split(/\//, &canonic($_[0]));
  256.     @small = @bigpart = ();
  257.     while (defined($name = shift(@big))) {
  258.         push(@bigpart, $name);
  259.         if (defined($try = $map{join('/', @bigpart)})) {
  260.             # Found in map
  261.             @small = split(/\//, $try);
  262.             # The next line is needed because of buggy split
  263.             # split(/x/, '') should give ('') not ()
  264.             @small = ('') if $#small == -1;
  265.         } else {
  266.             # Create new map
  267.             # Even if the name is short we may have used it up
  268.             # by shortening up a bigger one, so we may have to
  269.             # count
  270.             $name = &$mapfunc($name);
  271.             $count = '';
  272.             while ($used{join('/', @s = (@small, $name . $count))}) {
  273.                 if ($count eq '') {
  274.                     $name = &$countprepfunc($name);
  275.                     $count = 'AA';
  276.                 } else {
  277.                     $count++;
  278.                 }
  279.             }
  280.             @small = @s;
  281.             $used{join('/', @small)} = 1;
  282.             $map{join('/', @bigpart)} = join('/', @small);
  283.         }
  284.     }
  285.     return join('/', @small);
  286. }
  287.  
  288. #
  289. # Convert a single string to something close to it with length up
  290. # to length given
  291. sub shorten {
  292.     local($str, $len) = @_;
  293.  
  294.     # Do "fonetic speling" from end to beginning
  295.     while (
  296.         length($str) > $len && (
  297.             $str =~ s/(.*)([fglmnprst])\2(.*)/\1\2\3/i ||
  298.             $str =~ s/(.*)(ou)(.*)/\1u\3/i ||
  299.             $str =~ s/(.*)(ck)(.*)/\1k\3/i ||
  300.             $str =~ s/(.*)(ks)(.*)/\1x\3/i ||
  301.             $str =~ s/(.*)(sh)(.*)/\1s\3/i ||
  302.             $str =~ s/(.*)(ph)(.*)/\1f\3/i ||
  303.             $str =~ s/(.*)(oo)(.*)/\1u\3/i
  304.         )
  305.     ) { ; }
  306.     # Remove lowercase vowels from the end to the beginning
  307.     while (
  308.         length($str) > $len && 
  309.         $str =~ s/(.*)[aeiou](.*)/\1\2/
  310.     ) { ; }
  311.     # Remove uppercase vowels from the end to the beginning
  312.     while (
  313.         length($str) > $len && 
  314.         $str =~ s/(.*)[AEIOU](.*)/\1\2/
  315.     ) { ; }
  316.     # Finally cut characters from the end
  317.     $str = substr($str, 0, $len);
  318.     return $str;
  319. }
  320.  
  321. # Create a canonic file name out of one containing .. and . 
  322. # Employ Unix semantics: empty file means root directory.
  323. sub canonic {
  324.     local(@comp, @can);
  325.  
  326.     @comp = split(/\//, $_[0]);
  327.     for ($i = 0; $i <= $#comp; $i++) {
  328.         if ($comp[$i] eq '.') {
  329.             ;
  330.         } elsif ($comp[$i] eq '') {
  331.             @can = ();
  332.             push(@can, '');
  333.         } elsif ($comp[$i] eq '..') {
  334.             pop(@can);
  335.         } else {
  336.             push(@can, $comp[$i]);
  337.         }
  338.     }
  339.     return join('/', @can);
  340. }
  341.  
  342. # A do nothing procedure
  343. sub copy {
  344.     return $_[0];
  345. }
  346.  
  347. # Copy a tape archive from stdin to stdout
  348. sub copytar {
  349.     binmode STDIN;
  350.     binmode STDOUT;
  351.  
  352.     forloop: for(;;) {
  353.         read(STDIN, $header, 512) == 512 || die "$0: Couldn't read header: $!\n";
  354.         if ($header eq "\0" x 512) {
  355.             last forloop;
  356.         }
  357.         ($name, $mode, $uid, $gid, $size, $mtime, $checksum, $linkflag, $linkname) = unpack("a100 A7x A7x A7x A12 A12 a8 a1 a100", $header);
  358.         #
  359.         # Header modification code should be put here
  360.         # HEADERMOD
  361.         $name =~ s/[\000 ]*//g;
  362.         $name = &$proc($name);
  363.         if ($linkflag != "\0") {
  364.             $linkname =~ s/[\000 ]*//g;
  365.             $linkname = &$proc($linkname);
  366.         }
  367.  
  368.         # Create dummy header for checksum calculation (checksum is blanks)
  369.         $hnew = pack("a99x A6a1x A6a1x A6a1x A11a1 A11a1 a8 a1 a99x x255", 
  370.             ($name, $mode, ' ', $uid, ' ', $gid, ' ', $size, ' ', $mtime, ' ', ' ' x 8, $linkflag, $linkname));
  371.         $sz = $size;
  372.         $sz =~ s/ *//g;
  373.         $sz = oct($sz);
  374.         $checksum =~ s/ *//g;
  375.         $checksum = oct($checksum);
  376.         $newcheck = &check($hnew);
  377.         # Create the header with the new checksum
  378.         $hnew = pack("a99x A6a1x A6a1x A6a1x A11a1 A11a1 a6xa1 a1 a99x x255", 
  379.             ($name, $mode, ' ', $uid, ' ', $gid, ' ', $size, ' ', $mtime, ' ', sprintf('%6o', $newcheck), ' ', $linkflag, $linkname));
  380.         print STDOUT $hnew;
  381.         # Copy contents
  382.         for ($i = 0; $i < $sz; $i += 512) {
  383.             read(STDIN, $contents, 512) == 512 || die "$0: Couldn't read data: $!\n";
  384.             print STDOUT $contents;
  385.         }
  386.         #seek(STDIN, (int($sz / 512) + 1) * 512, 1) unless $sz == 0;
  387.     }
  388.     # Write EOF
  389.     print STDOUT pack("x512", ());
  390.     print STDOUT pack("x512", ());
  391. }
  392.  
  393. # Return checksum for tar header block
  394. sub check {
  395.     $h = $_[0];
  396.     local($i, $s);
  397.  
  398.     $s = 0;
  399.     for($i = 0; $i < 512; $i++) {
  400.         $s += unpack('C', substr($h, $i, 1));
  401.     }
  402.     return $s;
  403. }
  404.  
  405. "
  406. :end ", 0;
  407.