#!/bin/sh
# cvt2iso9660 -- pervert filenames to conform to ISO9660 format. 
#
# Copyright (C) 1992 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, you can either send email to this program's
# author (see below) or write to: The Free Software Foundation, Inc; 675
# Mass Ave; Cambridge, MA 02139, USA.
#
# Please send bug reports, etc. to friedman@prep.ai.mit.edu
#
# This script traverses down directories specified on the command line
# renaming things so they conform to the ISO9660 character set with no more
# than 8 characters plus a three-character extension separated by a `.' to
# a file.
# A translation table of munged file name to original name is also created
# in each directory.
#
# This script is known not to work with /bin/sh on some versions of Ultrix
# (though it works with sh5 on those systems).  The problem stems from
# being unable to redirect io with the `read' builtin. 
#
# Using this script is likely to prevert your precious bodily fluids.
#
# Created 27-Oct-92, last modified 30-Oct-92
# Version 1.0
#

umask 000

# Name by which this script was invoked. 
progname=`basename $0`

usage="Usage: ${progname} {-D} {-h} {-v} [dir1] {dir2} {...}
	{--debug} {--help} {--verbose}
"

# To prevent hairy quoting and escaping later.
bq="\`"
eq="'"

# Unset any variables set by options below, if you don't want to take the
# chance that they may be exported from the environment
# Don't actually use `unset' since old bourne shells don't have this
# command.  Instead, assign them an empty value. 
debug=""
verbose=""

# Parse command line arguments. 
# If you add new options be sure to change the wildcards below to make
# sure they are unambiguous (i.e. only match one possible long option)
# Be sure to show at least one instance of the full long option name to
# document what the long option is canonically called. 
# Long options which take arguments will need a `*' appended to the
# canonical name to match the value appended after the `=' character. 
while [ "$#" != "0" ]; do
   case z${1} in 
      z-D | z--debug | z--d* )
         debug=t
         shift
        ;;
      z-h | z--help | z--h* )
         echo "${usage}" 1>&2
         exit 1
        ;;
      z-v | z--verbose | z--v* )
         verbose="t"
         shift
        ;;
      z-- )     # Stop option processing
        shift
        break
       ;;
      z-* )
        echo "${progname}: unknown option \`$1'" 1>&2
        echo "${usage}" 1>&2
        exit 1
       ;;
      * )
        break
       ;;
   esac
done

test "z${debug}" != "z" && set -x

if [ $# = 0 ]; then
   echo "${progname}: At least one directory argument is required." 1>&2
   echo "${usage}" 1>&2
   exit 1

fi

file_list="/tmp/${progname}$$"
newfile_list="/tmp/${progname}n$$"

# Name of table of mutated-filename to original-filename list. 
transtab_basename="transtab."

orig_pwd="`pwd`"

# Remaining arguments are path trees to convert. 
for directory in "$@" ; do
   cd "${orig_pwd}"  # Restore from any previous loop
   cd "${directory}" # May be relative

   # First build a list of all the files and directories.  Hopefully `find'
   # will print directories before the files in them, so that renames will
   # happen in the correct order.
   if [ -n "${verbose}" ]; then
      echo "*** ${progname}: Building list of files in ${directory}..." 1>&2
   fi
   find . -print | sed 's/^\.\/*//;/^$/d' > "${file_list}"

   if [ $? != 0 ]; then
      echo "*** ${progname}: Couldn't operate on ${directory}.  Skipping." 1>&2
      continue
   fi

   # Build list of files with their properly preverted names
   # Comments for sed commands here because not all versions of sed have
   # comments.  Each label here corresponds to label in sed commands.
   # :l1  Convert any non d-chars to underscore (see ISO9660 7.6.1)
   # :l2  Remove all but first 8 chars from each pathname component. 
   tr '[A-Z]' '[a-z]' < "${file_list}" | sed '
      : l1
      s/[^\/A-Za-z0-9_]/_/g;
      : l2
      s/\([^\/][^\/][^\/][^\/][^\/][^\/][^\/][^\/]\)[^\/]*/\1/g;
    ' > "${newfile_list}"

   if [ $? != 0 ]; then
      echo "*** ${progname}: Couldn't operate on ${directory}.  Skipping." 1>&2
      continue
   fi

   if [ -n "${verbose}" ]; then
      echo "*** ${progname}: Renaming files..." 1>&2
   fi

   exec 3< "${file_list}" 4< "${newfile_list}"

   # Unique file magic counters. Hopefully no more than a 1000 conflicts
   # will ever occur in a single directory. 
   d_i=0
   f_i=0

   while read orig_file 0<&3 ; do
      orig_file_basename=`basename "${orig_file}"`

      read newfile 0<&4 

      previous_newfile_dirname="${newfile_dirname}"

      # Roughly equivalent to `dirname ...`, but more portable (dirname is
      # known to be missing on many machines)
      # Note that this is done *after* the sed perversions above so that
      # the entire pathname is munged right.  
      newfile_dirname="`echo ${newfile} | sed 's/\/[^\/]*$//'`"
      if [ "z${newfile_dirname}" = "z${newfile}" ]; then
         newfile_dirname="."
      fi

      # If newfile isn't a directory, fix basename so it complies with
      # ISO9660 7.5.1 (except no version number is appended).
      # Section 7.5.2 is adhered to because the name+extension is kept
      # under 13 characters, rather than the 30 which 7.5.2 allows. 
      #
      # Comments for sed commands here because not all versions of sed have
      # comments.  Each label here corresponds to label in sed commands.
      # :l1  Convert any non d-chars to underscore (see ISO9660 7.4.1)
      #      except for `.', which is kept as SEPARATOR 1.
      # :l2  Move any characters after the first 8 past the `.'
      # :l3  Get rid of any `.' chars in filename after first one.
      # :l4  Strip all but first 3 chars after extension (if any)
      # :l5  If name has no `.' in it already, append one at end. 
      if [ ! -d "${newfile_dirname}/${orig_file_basename}" ]; then
         newfile_basename="`echo \"${orig_file_basename}\" \
                             | tr '[A-Z]' '[a-z]' \
                             | sed ': l1
                                    s/[^A-Za-z0-9_.]/_/g;
                                    : l2
                                    s/\([^.][^.][^.][^.][^.][^.][^.][^.]\)/\1./g;
                                    : l3
                                    /\.[^.]*\./{
                                       s/\.\([^.]*\)\./.\1/g;
                                       t l3
                                    }
                                    : l4
                                    s/\.\(...\).*/.\1/g;
                                    : l5
                                    s/^\([^.]*\)$/\1./;
                                   '`"
      else
         newfile_basename="`basename ${newfile}`"
      fi
      
      if [ "z${newfile_dirname}" = "z." ]; then
         newfile="${newfile_basename}"
      else
         newfile="${newfile_dirname}/${newfile_basename}"
      fi

      # Make unique filename, if necessary.  (This is only necessary if the
      # new file name is different from the old one.  If it isn't, the file
      # is already unique.)
      # By the way, how do you catch a unique lizard?  Unique up on it. 
      if [ "z${newfile_basename}" != "z${orig_file_basename}" ]; then
         # reset unique file number counters if directory name has changed
         # since last loop.
         if [ "z${newfile_dirname}" != "z${previous_newfile_dirname}" ]; then
            d_i=0
            f_i=0
         fi

         if [ -f "${newfile}" ]; then
            newfile_sans_extension="`echo ${newfile} | sed 's/\.[^.\/]*$//'`"
            while [ -f "${newfile}" ]; do
               newfile="${newfile_sans_extension}.${f_i}"
               f_i=`expr ${f_i} + 1`
            done
         elif [ -d "${newfile}" ]; then
            # Technique for unique-ifying directories is different.
            newfile_trunc="`echo ${newfile} \
                             | sed 's/\([^\/][^\/][^\/][^\/][^\/]\)[^\/]*$/\1/'`"
            while [ -d "${newfile}" ]; do
               newfile="${newfile_trunc}${d_i}"
               d_i=`expr ${d_i} + 1`
            done
         fi

         newfile_basename="`basename ${newfile}`"

         transtab="${newfile_dirname}/${transtab_basename}"
         : >> "${transtab}"  # Create transtab if not there already.
         echo "mv ${newfile_basename} ${orig_file_basename}" >> "${transtab}"

         mv "${newfile_dirname}/${orig_file_basename}" "${newfile}"
         if [ $? = 0 -a "z${verbose}" != "z" ]; then
            if [ "${newfile_dirname}" = "." ]; then
               echo "${directory}/${orig_file_basename} -> ${directory}/${newfile}"
            else
               echo "${directory}/${newfile_dirname}/${orig_file_basename} -> ${directory}/${newfile}"
            fi
         fi
      else
         if [ "z${verbose}" != "z" ]; then
            echo "${directory}/${newfile} not renamed"
         fi
      fi
   done
done

if [ -n "${verbose}" ]; then
   echo "*** ${progname}: Done." 1>&2
fi

rm -f "${file_list}" "${newfile_list}"

exit 0

# eof
