#!/bin/sh # Script: non8dot3 # Description: Converts 8dot3 formatted paths (permits spaces and long names) to non-8dot3 format (no spaces, short names). # This script may only convert existing paths on the current system. # Copyright: 2007 Johan Kuuse # Usage: non8dot3 [PATH]... # Example 1: # $ non8dot3 /c/Documents\ and\ Settings/Administrator/ # c:/DOCUME~1/ADMINI~1/ # # Example 2 (prints current directory in non-8dot3 format): # $ cd /c/Program\ Files/ # $ non8dot3 # PROGRA~1 # # Example 3 (lists 3 paths, 1 per line): # $ cd /c # $ non8dot3 .emacs MinGW/ msys/ # EMACS~1 # MinGW/ # msys/ # Help message usage() { script_name=`basename $0` echo "Usage: $script_name [PATH1] [PATH2] ..." } # Error messages error_unknown_option() { echo "Unknown option: $1" } error_path_must_exist() { echo "Path '$1' does not exist or is not readable." echo "This script only works with existing paths." } # Check for "single dash" options. # Return -1 for unknown option, 0 for help (-h). single_dash_option_check() { arg=$1 # Single dash help. if [ $arg = "-h" ]; then return 0 fi # Invalid option. return -1 } # Check for options. # Return -1 for unknown option, 0 for "--help" or "-h", 1 for non-option argument. option_check() { arg=$1 # Special case: option = "-" or "--" without following letter/number = invalid option. if [ $arg = "-" -o $arg = "--" ]; then return -1 fi # Non-option argument. first_char=`echo $arg | cut --characters=1` if [ $first_char != "-" ]; then return 1 fi # Special case: option = "--help" if [ $1 = "--help" ]; then return 0 fi # If second char is "--XXX", this is an invalid option. second_char=`echo $arg | cut --characters=2` if [ $second_char = "-" ]; then return -1 fi # This is a single dash argument. Check for syntax. single_dash_option_check $arg rc=$? return $rc } # Returns 0 if a char is alphabetic. is_alphabetic() { char=$1 # If char is empty, it is not alphabetic. if [ -z $char ];then return 1 fi upper_case=`echo $char|cut -c 1|tr a-z A-Z` lower_case=`echo $char|cut -c 1|tr A-Z a-z ` # If a conversion to upper case and to lower results in the same string, char is not alphabetic. if [ "$upper_case" == "$lower_case" ];then return 1 fi return 0 } # Returns 0 if a path starts with '/'. is_absolute_path() { unix_path=$1 first_char=`echo $unix_path|cut -c 1` if [ "$first_char" != '/' ];then return 1 fi return 0 } # Get the second char from a path. get_second_char() { unix_path=$1 second_char=`echo $unix_path|cut -c 2` if [ -z $second_char ]; then return 1 fi echo $second_char return 0 } # Return the DOS drive letter from a UNIX path format. get_dos_drive() { # Expected path format: /c/foo/bar/baz # First and third char in path should be slash (/). # Second char in path should be DOS drive letter. unix_path=$1 is_absolute_path $unix_path if [ $? -ne 0 ];then echo "Error: get_dos_drive() UNIX path '$path' is not an absolute path." return 1 fi third_char=`echo $unix_path|cut -c 3` if [ "$third_char" != '/' ];then echo "Error: get_dos_drive() third_char=$third_char" return 1 fi # Second character in path should correspond to DOS drive letter. drive_letter=`echo $unix_path|cut -c 2|tr a-z A-Z` if [ -z $drive_letter ];then echo "Error: get_dos_drive() drive_letter=$drive_letter" return 1 fi echo $drive_letter return 0 } # Return the corresponding "UNIX root path" from a DOS drive letter. get_unix_root_path() { # Expected path format: C:\foo\bar\baz # First char in path should correspond to an existing DOS drive. dos_path=$1 drive_letter=`echo $dos_path|cut -c 1|tr a-z A-Z` if [ -z $drive_letter ];then echo "Error: get_unix_root_path() empty DOS drive letter for DOS path '$dos_path'" return 1 fi is_alphabetic $drive_letter if [ $? -ne 0 ];then echo "Error: get_unix_root_path() non-alphabetic DOS drive_letter '$drive_letter' for DOS path '$dos_path'" return 1 fi colon=":" dos_drive="$drive_letter$colon" # Check that DOS drive can be accessed. dos_command="cmd //C DIR $dos_drive 2>&1" output=`$dos_command` if [ $? -ne 0 ]; then echo "Error: get_unix_root_path() cannot access DOS drive $dos_drive" return 1 fi slash="/" echo "$slash$drive_letter"|tr A-Z a-z return 0 } # Convert UNIX path to DOS path (change slash to backslash). get_dos_path_from_unix() { unix_path="$@" slash="\057" backslash="\134" drive_letter="" colon=":" # Try to convert root path to DOS drive. drive_letter=`get_dos_drive "$unix_path"` if [ $? -ne 0 ]; then return 1 fi the_rest=`echo "$unix_path"|cut -c 3-` dos_path="$drive_letter$colon$the_rest" dos_path=`echo "$dos_path"| tr $slash $backslash` echo "$dos_path" } # Convert DOS path to UNIX path (change backslash to slash). # If second argument is set, try to replace DOS drive (C:) with root path (/c). get_unix_path_from_dos() { dos_path=$1 convert_to_root_path=$2 slash="\057" backslash="\134" root_path="" # Try to convert DOS drive to root path. if [ $convert_to_root_path ];then root_path=`get_unix_root_path $dos_path` if [ $? -ne 0 ]; then echo $root_path return 1 fi fi the_rest=`echo $dos_path|cut -c 3-` unix_path="$root_path$the_rest" unix_path=`echo $unix_path|tr $backslash $slash` echo "$unix_path" } # List of MSYS default subdirectories. msys_subdirs() { echo "bin doc etc home mingw share uninstall" } # If first argument (directory) is MSYS root, we should be able to list MSYS default subdirectories. # Return 0 on success. is_msys_root() { dir=$1 cd "$dir" || return 1 subdirs=`msys_subdirs` paths=`ls -d $subdirs 2>&1` return $? } # Only try this function to get MSYS root path if other methods fail. get_msys_root_guessing() { msys_root='/c/msys/1.0' path=`ls -d $msys_root 2>&1` if [ $? -ne 0 ]; then return 1 fi is_msys_root "$msys_root" || return 1 echo "$msys_root (guessing)" return 0 } # Only try this function to get MSYS root path if get_msys_root() fails. get_msys_root_changing_dir() { cd $HOMEDRIVE || return 1 cd `ls |grep -i -e "^msys"` || return 1 cd `ls | head -1` || return 1 msys_root=`pwd` is_msys_root $msys_root || return 1 echo "$msys_root (changing dir)" return 0 } # Try to get MSYS root path through parsing the output from the DOS "DIR" command. get_msys_root_parsing() { msys_root_dos="" directory_of_root=`cd / && cmd //C DIR 2>&1|grep -i $HOMEDRIVE|head -1` homedrive_uppercase=`echo $HOMEDRIVE|tr a-z A-Z` drive_letter_upper_case=`echo $homedrive_uppercase|cut -c 1` for msys_root_dos in $directory_of_root do first_letter_upper_case=`echo $msys_root_dos|tr a-z A-Z|cut -c 1` second_letter_upper_case=`echo $msys_root_dos|tr a-z A-Z|cut -c 2` if [ "$first_letter_upper_case$second_letter_upper_case" = "$homedrive_uppercase" ]; then msys_root=`get_unix_path_from_dos $msys_root_dos DOSDRIVE` echo "$msys_root" return 0 fi done return 1 } # Try some different methods to get the MSYS root in DOS format. get_msys_root() { msys_root_dir=`get_msys_root_parsing` if [ $? -ne 0 ]; then msys_root_dir=`get_msys_root_changing_dir` if [ $? -ne 0 ]; then msys_root_dir=`get_msys_root_guessing` if [ $? -ne 0 ]; then return 1 fi fi fi echo $msys_root_dir return 0 } # Get a directory name in non-8dot3 DOS format. # Format: non8dot3 <8dot3-path-to-dir-or-file> <8dot3-dir-or-file-name> non8dot3() { dos_drive="$1" path_to_dir_or_file="$2" dir_name_8dot3="$3" backslash='\' # Strip possible leading backslash from path_to_dir_or_file. first_char=`echo $path_to_dir_or_file | cut --characters=1` if [ "$first_char" = "$backslash" ];then path_to_dir_or_file=`echo $path_to_dir_or_file | cut --characters=2-` fi #echo non8dot3:dos_drive=$dos_drive #echo non8dot3:path_to_dir_or_file=$path_to_dir_or_file #echo non8dot3:dir_name_8dot3=$dir_name_8dot3 if [ "$path_to_dir_or_file" ];then path_to_dir_or_file='"'$path_to_dir_or_file'"' fi dos_command="cmd //C cd $dos_drive && dir //X $path_to_dir_or_file" output=`$dos_command 2>&1` if [ $? -ne 0 ];then echo "Error: non8dot3() command '$dos_command' failed." return 1 fi dir_info=`echo "$output"|grep "$dir_name_8dot3"` dir_name=`echo $dir_info | cut --delimiter=" " --fields=4` echo $dir_name return 0 } main() { help=0 error=0 errstr="" ARGC=$# n_paths=0 backslash='\' slash='/' # Check for options from command line. # Shift out each command line argument. # (See http://www.grymoire.com/Unix/Sh.html#uh-42 how to use ARGV array.) for ((i = 0; i < $ARGC; i++)) do option_check $1 rc=$? case "$rc" in -1) error=1 errstr=$1 help=1;; 0) help=1;; 1) paths[$n_paths]=$1 n_paths=`expr $n_paths + 1`;; esac shift done # If an unknown option was given from the command line, display error message (implies help message and exit). if [ $error -eq 1 ]; then error_unknown_option $errstr fi # If the help option was given from the command line, display help message and exit. if [ $help -eq 1 ]; then usage return 1 fi # If no paths are given from the command line (no arguments or only options), the current directory will be assumed. n_paths=${#paths[*]} if [ $n_paths = 0 ]; then current_dir=`pwd`; n_paths=1 paths[0]=`ls -d "$current_dir" 2>&1` fi # Program loop start here. result_output="" # Iterate through paths. for ((i = 0; i < $n_paths; i++)) do path=${paths[$i]} # Test for existing dir. rc=`ls -d "$path" 2>&1` if [ $? -ne 0 ]; then error_path_must_exist "$path" usage return 1 fi # Check if path is absolute. is_absolute_path $path if [ $? -ne 0 ]; then # The current path is not an absolute path (does not start with /), but relative to the current path. # Prepend the current directory to path. current_dir=`pwd` path="$current_dir$slash$path" fi # Get second character from path. # Test if the second character can be successfully used in the following DOS command: # "cmd //C DIR $second_char:" output="" drive_letter="" second_char=`get_second_char $path` rc=$? if [ $rc -eq 0 ]; then drive_letter=`echo $second_char|tr a-z A-Z` output=`cmd //C DIR "$drive_letter:" 2>&1` rc=$? fi # If the DOS command works, the second char is a drive letter. # The current path is a "complete DOS path" (/c/foo/bar/baz, /d/foo/bar/baz, etc.). # If the DOS command does not work, the second char is NOT a drive letter. # This is a "MSYS path" (/home/foo/bar/baz, /usr/local/bin/foo/bar/baz, etc.). # In this case the MSYS root path has to be prepended to the current path to get a "complete DOS path". msys_root="" if [ $rc -ne 0 ]; then # Get MSYS root in UNIX format. msys_root=`get_msys_root` if [ $? -ne 0 ]; then echo "Error: msys_root=$msys_root" return 1 fi # Prepend the MSYS root path to get the absolute DOS path. # The absolute DOS path is already verified to work, get drive_letter from second char in path. path="$msys_root$path" second_char=`get_second_char $path` drive_letter=`echo $second_char|tr a-z A-Z` fi # Drive letter (C, D, etc) and DOS path ("Documents And Settings\Administrator", "Program Files", etc.). dos_path=`get_dos_path_from_unix $path` colon=":" backslash='\' dos_drive="$drive_letter$colon$backslash" dos_path_without_drive=`echo "$dos_path"|cut -c 4-` # Repeat DOS command "DIR" for each path component (separatated by backslash). dir_converted="" dir_to_convert="" rest_of_path="$dos_path_without_drive" growing_path="$dir_to_convert" non8dot3_path="" while [ "$rest_of_path" -a "$dir_to_convert" != "$rest_of_path" ]; do dir_to_convert=`echo "$rest_of_path"|cut --delimiter="$backslash" --fields=1` rest_of_path=`echo "$rest_of_path"|cut --delimiter="$backslash" --fields=2-` dir_converted=`non8dot3 "$dos_drive" "$growing_path" "$dir_to_convert"` if [ $? -ne 0 ];then echo "Error: non8dot3(): $dir_converted" return 1 fi growing_path="$growing_path$backslash$dir_to_convert" non8dot3_path="$non8dot3_path$backslash$dir_converted" done non8dot3_path="$drive_letter$colon$non8dot3_path" printf "%s\n" $non8dot3_path # Program loop ends here. done return 0 } main "$@" rc=$? exit $rc