Files
2017-05-26 17:37:05 +02:00

176 lines
6.4 KiB
VimL

" ingo/fs/path.vim: Functions for manipulating a file system path.
"
" DEPENDENCIES:
" - ingo/compat.vim autoload script
" - ingo/os.vim autoload script
" - ingo/escape/file.vim autoload script
"
" Copyright: (C) 2012-2014 Ingo Karkat
" The VIM LICENSE applies to this script; see ':help copyright'.
"
" Maintainer: Ingo Karkat <ingo@karkat.de>
"
" REVISION DATE REMARKS
" 1.022.010 22-Sep-2014 Use new ingo#compat#glob().
" 1.019.009 23-May-2014 Add ingo#fs#path#Exists().
" 1.019.008 21-May-2014 Add ingo#fs#path#IsCaseInsensitive().
" 1.019.007 07-May-2014 ingo#fs#path#Normalize(): Don't normalize to
" Cygwin /cygdrive/x/... when the chosen path
" separator is "\". This would result in a mixed
" separator style that is not actually handled.
" Add special normalization to "C:/" on Cygwin via
" ":/" path separator argument.
" 1.014.006 26-Sep-2013 ingo#fs#path#Normalize(): Also convert between
" the different D:\ and /cygdrive/d/ notations on
" Windows and Cygwin.
" 1.013.005 13-Sep-2013 Use operating system detection functions from
" ingo/os.vim.
" 1.011.004 01-Aug-2013 Extract ingo#fs#path#IsUncPathRoot().
" 1.010.003 08-Jul-2013 Add prefix to exception thrown from
" ingo#fs#path#GetRootDir().
" 1.009.002 26-Jun-2013 Add ingo#fs#path#Equals().
" Minor: Remove duplication.
" 1.007.001 01-Jun-2013 file creation from ingofile.vim
function! ingo#fs#path#Separator()
return (exists('+shellslash') && ! &shellslash ? '\' : '/')
endfunction
function! ingo#fs#path#Normalize( filespec, ... )
"******************************************************************************
"* PURPOSE:
" Change all path separators in a:filespec to the passed or the typical format
" for the current platform.
" On Windows and Cygwin, also converts between the different D:\ and
" /cygdrive/d/ notations.
"* ASSUMPTIONS / PRECONDITIONS:
" None.
"* EFFECTS / POSTCONDITIONS:
" None.
"* INPUTS:
" a:filespec Filespec, potentially with mixed / and \ path separators.
" a:pathSeparator Optional path separator to be used. With the special value
" of ":/", normalizes to "/", but keeps a "C:/" drive letter
" prefix instead of translating to "/cygdrive/c/".
"* RETURN VALUES:
" a:filespec with uniform path separators, according to the platform.
"******************************************************************************
let l:pathSeparator = (a:0 ? (a:1 ==# ':/' ? '/' : a:1) : ingo#fs#path#Separator())
let l:badSeparator = (l:pathSeparator ==# '/' ? '\' : '/')
let l:result = tr(a:filespec, l:badSeparator, l:pathSeparator)
if ingo#os#IsWinOrDos()
let l:result = substitute(l:result, '^[/\\]cygdrive[/\\]\(\a\)\ze[/\\]', '\u\1:', '')
elseif ingo#os#IsCygwin() && l:pathSeparator ==# '/' && ! (a:0 && a:1 ==# ':/')
let l:result = substitute(l:result, '^\(\a\):', '/cygdrive/\l\1', '')
endif
return l:result
endfunction
function! ingo#fs#path#Combine( first, ... )
"******************************************************************************
"* PURPOSE:
" Concatenate the passed filespec fragments into a filespec, ensuring that all
" fragments are combined with proper path separators.
"* ASSUMPTIONS / PRECONDITIONS:
" None.
"* EFFECTS / POSTCONDITIONS:
" None.
"* INPUTS:
" Either pass a dirspec and one or many filenames:
" a:dirspec, a:filename [, a:filename2, ...]
" Or a single list containing all filespec fragments.
" [a:dirspec, a:filename, ...]
"* RETURN VALUES:
" Combined filespec.
"******************************************************************************
if type(a:first) == type([])
let l:dirspec = a:first[0]
let l:filenames = a:first[1:]
else
let l:dirspec = a:first
let l:filenames = a:000
endif
" Use path separator as exemplified by the passed dirspec.
if l:dirspec =~# '\' && l:dirspec !~# '/'
let l:pathSeparator = '\'
elseif l:dirspec =~# '/'
let l:pathSeparator = '/'
else
" The dirspec doesn't contain a path separator, fall back to the
" system's default.
let l:pathSeparator = ingo#fs#path#Separator()
endif
let l:filespec = l:dirspec
for l:filename in l:filenames
let l:filename = substitute(l:filename, '^[/\\]', '', '')
let l:filespec .= (l:filespec =~# '^$\|[/\\]$' ? '' : l:pathSeparator) . l:filename
endfor
return l:filespec
endfunction
function! ingo#fs#path#IsUncPathRoot( filespec )
let l:ps = escape(ingo#fs#path#Separator(), '\')
let l:uncPathPattern = printf('^%s%s[^%s]\+%s[^%s]\+$', l:ps, l:ps, l:ps, l:ps, l:ps)
return (a:filespec =~# l:uncPathPattern)
endfunction
function! ingo#fs#path#GetRootDir( filespec )
if ! ingo#os#IsWinOrDos()
return '/'
endif
let l:dir = a:filespec
while fnamemodify(l:dir, ':h') !=# l:dir && ! ingo#fs#path#IsUncPathRoot(l:dir)
let l:dir = fnamemodify(l:dir, ':h')
endwhile
if empty(l:dir)
throw 'GetRootDir: Could not determine root dir!'
endif
return l:dir
endfunction
function! ingo#fs#path#IsCaseInsensitive( ... )
return ingo#os#IsWinOrDos() " Note: Check based on path not yet implemented.
endfunction
function! ingo#fs#path#Equals( p1, p2 )
if ingo#fs#path#IsCaseInsensitive(a:p1) || ingo#fs#path#IsCaseInsensitive(a:p2)
return a:p1 ==? a:p2 || ingo#fs#path#Normalize(fnamemodify(a:p1, ':p')) ==? ingo#fs#path#Normalize(fnamemodify(a:p2, ':p'))
else
return a:p1 ==# a:p2 || ingo#fs#path#Normalize(fnamemodify(resolve(a:p1), ':p')) ==# ingo#fs#path#Normalize(fnamemodify(resolve(a:p2), ':p'))
endif
endfunction
function! ingo#fs#path#Exists( filespec )
"******************************************************************************
"* PURPOSE:
" Test whether the passed a:filespec exists (as a file or directory). This is
" like the combination of filereadable() and isdirectory(), but without the
" requirement that the file must be readable.
"* ASSUMPTIONS / PRECONDITIONS:
" None.
"* EFFECTS / POSTCONDITIONS:
" None.
"* INPUTS:
" a:filespec Filespec or dirspec.
"* RETURN VALUES:
" 0 if there's no such file or directory, 1 if it exists.
"******************************************************************************
" I suppose these are faster than the glob(), and this avoids any escaping
" issues, too, so it is more robust.
if filereadable(a:filespec) || isdirectory(a:filespec)
return 1
endif
let l:filespec = ingo#escape#file#wildcardescape(a:filespec)
return ! empty(ingo#compat#glob(l:filespec, 1))
endfunction
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :