" ============================================================================
" CLASS: Path
"
" The Path class provides an abstracted representation of a file system
" pathname.  Various operations on pathnames are provided and a number of
" representations of a given path name can be accessed here.
" ============================================================================


let s:Path = {}
let g:NERDTreePath = s:Path

" FUNCTION: Path.AbsolutePathFor(pathStr) {{{1
function! s:Path.AbsolutePathFor(pathStr)
    let l:prependWorkingDir = 0

    if nerdtree#runningWindows()
        let l:prependWorkingDir = a:pathStr !~# '^.:\(\\\|\/\)\?' && a:pathStr !~# '^\(\\\\\|\/\/\)'
    else
        let l:prependWorkingDir = a:pathStr !~# '^/'
    endif

    let l:result = a:pathStr

    if l:prependWorkingDir
        let l:result = getcwd()

        if l:result[-1:] == nerdtree#slash()
            let l:result = l:result . a:pathStr
        else
            let l:result = l:result . nerdtree#slash() . a:pathStr
        endif
    endif

    return l:result
endfunction

" FUNCTION: Path.bookmarkNames() {{{1
function! s:Path.bookmarkNames()
    if !exists('self._bookmarkNames')
        call self.cacheDisplayString()
    endif
    return self._bookmarkNames
endfunction

" FUNCTION: Path.cacheDisplayString() {{{1
function! s:Path.cacheDisplayString() abort
    let self.cachedDisplayString = g:NERDTreeNodeDelimiter . self.getLastPathComponent(1)

    if self.isExecutable
        let self.cachedDisplayString = self.addDelimiter(self.cachedDisplayString) . '*'
    endif

    let self._bookmarkNames = []
    for i in g:NERDTreeBookmark.Bookmarks()
        if i.path.equals(self)
            call add(self._bookmarkNames, i.name)
        endif
    endfor
    if !empty(self._bookmarkNames) && g:NERDTreeMarkBookmarks ==# 1
        let self.cachedDisplayString = self.addDelimiter(self.cachedDisplayString) . ' {' . join(self._bookmarkNames) . '}'
    endif

    if self.isSymLink
        let self.cachedDisplayString = self.addDelimiter(self.cachedDisplayString) . ' -> ' . self.symLinkDest
    endif

    if !self.isDirectory && b:NERDTree.ui.getShowFileLines() != 0
        let l:bufname = self.str({'format': 'Edit'})
        let l:lines = 0
        if executable('wc') 
            let l:lines = split(system('wc -l "'.l:bufname.'"'))[0]
        elseif nerdtree#runningWindows()
            let l:lines = substitute(system('type "'.l:bufname.'" | find /c /v ""'), '\n', '', 'g')
        else 
            let s:lines = readfile(l:bufname)
            let l:lines = 0
            for s:line in s:lines
                let l:lines += 1
                if l:lines >= 20000 
                    break
                endif
            endfor
        endif
        let self.cachedDisplayString = self.addDelimiter(self.cachedDisplayString) . ' ('.l:lines.')'
    endif
    if self.isReadOnly
        let self.cachedDisplayString = self.addDelimiter(self.cachedDisplayString) . ' ['.g:NERDTreeGlyphReadOnly.']'
    endif
endfunction

" FUNCTION: Path.addDelimiter() {{{1
function! s:Path.addDelimiter(line)
    if a:line =~# '\(.*' . g:NERDTreeNodeDelimiter . '\)\{2}'
        return a:line
    else
        return a:line . g:NERDTreeNodeDelimiter
    endif
endfunction

" FUNCTION: Path.changeToDir() {{{1
function! s:Path.changeToDir()
    let dir = self.str({'format': 'Cd'})
    if self.isDirectory ==# 0
        let dir = self.getParent().str({'format': 'Cd'})
    endif

    try
        if g:NERDTreeUseTCD && exists(':tcd') ==# 2
            execute 'tcd ' . dir
            call nerdtree#echo("Tab's CWD is now: " . getcwd())
        else
            execute 'cd ' . dir
            call nerdtree#echo('CWD is now: ' . getcwd())
        endif
    catch
        throw 'NERDTree.PathChangeError: cannot change CWD to ' . dir
    endtry
endfunction

" FUNCTION: Path.Create(fullpath) {{{1
"
" Factory method.
"
" Creates a path object with the given path. The path is also created on the
" filesystem. If the path already exists, a NERDTree.Path.Exists exception is
" thrown. If any other errors occur, a NERDTree.Path exception is thrown.
"
" Args:
" fullpath: the full filesystem path to the file/dir to create
function! s:Path.Create(fullpath)
    "bail if the a:fullpath already exists
    if isdirectory(a:fullpath) || filereadable(a:fullpath)
        throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
    endif

    try

        "if it ends with a slash, assume its a dir create it
        if a:fullpath =~# '\(\\\|\/\)$'
            "whack the trailing slash off the end if it exists
            let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')

            call mkdir(fullpath, 'p')

        "assume its a file and create
        else
            call s:Path.createParentDirectories(a:fullpath)
            call writefile([], a:fullpath)
        endif
    catch
        throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
    endtry

    return s:Path.New(a:fullpath)
endfunction

" FUNCTION: Path.copy(dest) {{{1
"
" Copies the file/dir represented by this Path to the given location
"
" Args:
" dest: the location to copy this dir/file to
function! s:Path.copy(dest)
    if !s:Path.CopyingSupported()
        throw 'NERDTree.CopyingNotSupportedError: Copying is not supported on this OS'
    endif

    call s:Path.createParentDirectories(a:dest)

    if exists('g:NERDTreeCopyCmd')
        let cmd_prefix = g:NERDTreeCopyCmd
    else
        let cmd_prefix = (self.isDirectory ? g:NERDTreeCopyDirCmd : g:NERDTreeCopyFileCmd)
    endif

    let cmd = cmd_prefix . ' ' . shellescape(self.str()) . ' ' . shellescape(a:dest)
    let success = system(cmd)
    if v:shell_error !=# 0
        throw "NERDTree.CopyError: Could not copy '". self.str() ."' to: '" . a:dest . "'"
    endif
endfunction

" FUNCTION: Path.CopyingSupported() {{{1
"
" returns 1 if copying is supported for this OS
function! s:Path.CopyingSupported()
    return exists('g:NERDTreeCopyCmd') || (exists('g:NERDTreeCopyDirCmd') && exists('g:NERDTreeCopyFileCmd'))
endfunction

" FUNCTION: Path.copyingWillOverwrite(dest) {{{1
"
" returns 1 if copy this path to the given location will cause files to
" overwritten
"
" Args:
" dest: the location this path will be copied to
function! s:Path.copyingWillOverwrite(dest)
    if filereadable(a:dest)
        return 1
    endif

    if isdirectory(a:dest)
        let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
        if filereadable(path)
            return 1
        endif
    endif
endfunction

" FUNCTION: Path.createParentDirectories(path) {{{1
"
" create parent directories for this path if needed
" without throwing any errors if those directories already exist
"
" Args:
" path: full path of the node whose parent directories may need to be created
function! s:Path.createParentDirectories(path)
    let dir_path = fnamemodify(a:path, ':h')
    if !isdirectory(dir_path)
        call mkdir(dir_path, 'p')
    endif
endfunction

" FUNCTION: Path.delete() {{{1
"
" Deletes the file or directory represented by this path.
"
" Throws NERDTree.Path.Deletion exceptions
function! s:Path.delete()
    if self.isDirectory

        let cmd = g:NERDTreeRemoveDirCmd . self.str({'escape': 1})
        let success = system(cmd)

        if v:shell_error !=# 0
            throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
        endif
    else
        if exists('g:NERDTreeRemoveFileCmd')
            let cmd = g:NERDTreeRemoveFileCmd . self.str({'escape': 1})
            let success = system(cmd)
        else
            let success = delete(self.str())
        endif

        if success !=# 0
            throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
        endif
    endif

    "delete all bookmarks for this path
    for i in self.bookmarkNames()
        let bookmark = g:NERDTreeBookmark.BookmarkFor(i)
        call bookmark.delete()
    endfor
endfunction

" FUNCTION: Path.displayString() {{{1
"
" Returns a string that specifies how the path should be represented as a
" string
function! s:Path.displayString()
    if self.cachedDisplayString ==# ''
        call self.cacheDisplayString()
    endif

    return self.cachedDisplayString
endfunction

" FUNCTION: Path.edit() {{{1
function! s:Path.edit()
    let l:bufname = self.str({'format': 'Edit'})
    if bufname('%') !=# l:bufname
        exec 'edit ' . l:bufname
    endif
endfunction

" FUNCTION: Path.extractDriveLetter(fullpath) {{{1
"
" If running windows, cache the drive letter for this path
function! s:Path.extractDriveLetter(fullpath)
    if nerdtree#runningWindows()
        if a:fullpath =~# '^\(\\\\\|\/\/\)'
            "For network shares, the 'drive' consists of the first two parts of the path, i.e. \\boxname\share
            let self.drive = substitute(a:fullpath, '^\(\(\\\\\|\/\/\)[^\\\/]*\(\\\|\/\)[^\\\/]*\).*', '\1', '')
            let self.drive = substitute(self.drive, '/', '\', 'g')
        else
            let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
        endif
    else
        let self.drive = ''
    endif

endfunction

" FUNCTION: Path.exists() {{{1
" return 1 if this path points to a location that is readable or is a directory
function! s:Path.exists()
    let p = self.str()
    return filereadable(p) || isdirectory(p)
endfunction

" FUNCTION: Path._escChars() {{{1
function! s:Path._escChars()
    if nerdtree#runningWindows()
        return " `\|\"#%&,?()\*^<>$"
    endif

    return " \\`\|\"#%&,?()\*^<>[]{}$"
endfunction

" FUNCTION: Path.getDir() {{{1
"
" Returns this path if it is a directory, else this paths parent.
"
" Return:
" a Path object
function! s:Path.getDir()
    if self.isDirectory
        return self
    else
        return self.getParent()
    endif
endfunction

" FUNCTION: Path.getParent() {{{1
"
" Returns a new path object for this paths parent
"
" Return:
" a new Path object
function! s:Path.getParent()
    if nerdtree#runningWindows()
        let path = self.drive . '\' . join(self.pathSegments[0:-2], '\')
    else
        let path = '/'. join(self.pathSegments[0:-2], '/')
    endif

    return s:Path.New(path)
endfunction

" FUNCTION: Path.getLastPathComponent(dirSlash) {{{1
"
" Gets the last part of this path.
"
" Args:
" dirSlash: if 1 then a trailing slash will be added to the returned value for
" directory nodes.
function! s:Path.getLastPathComponent(dirSlash)
    if empty(self.pathSegments)
        return ''
    endif
    let toReturn = self.pathSegments[-1]
    if a:dirSlash && self.isDirectory
        let toReturn = toReturn . '/'
    endif
    return toReturn
endfunction

" FUNCTION: Path.getSortOrderIndex() {{{1
" returns the index of the pattern in g:NERDTreeSortOrder that this path matches
function! s:Path.getSortOrderIndex()
    let i = 0
    let l:lpc = self.getLastPathComponent(1)
    while i < len(g:NERDTreeSortOrder)
        if g:NERDTreeSortOrder[i] !~? '\[\[-\?\(timestamp\|size\|extension\)\]\]' &&
        \ l:lpc =~# g:NERDTreeSortOrder[i]
            return i
        endif
        let i = i + 1
    endwhile

    return index(g:NERDTreeSortOrder, '*')
endfunction

" FUNCTION: Path._splitChunks(path) {{{1
" returns a list of path chunks
function! s:Path._splitChunks(path)
    let chunks = split(a:path, '\(\D\+\|\d\+\)\zs')
    let i = 0
    while i < len(chunks)
        "convert number literals to numbers
        if match(chunks[i], '^\d\+$') ==# 0
            let chunks[i] = str2nr(chunks[i])
        endif
        let i = i + 1
    endwhile
    return chunks
endfunction

" FUNCTION: Path.getSortKey() {{{1
" returns a key used in compare function for sorting
function! s:Path.getSortKey()
    if !exists('self._sortKey') || g:NERDTreeSortOrder !=# g:NERDTreeOldSortOrder
        " Look for file metadata tags: [[timestamp]], [[extension]], [[size]]
        let metadata = []
        for tag in g:NERDTreeSortOrder
            if tag =~? '\[\[-\?timestamp\]\]'
                let metadata += [self.isDirectory ? 0 : getftime(self.str()) * (tag =~# '-' ? -1 : 1)]
            elseif tag =~? '\[\[-\?size\]\]'
                let metadata += [self.isDirectory ? 0 : getfsize(self.str()) * (tag =~# '-' ? -1 : 1)]
            elseif tag =~? '\[\[extension\]\]'
                let extension = matchstr(self.getLastPathComponent(0), '[^.]\+\.\zs[^.]\+$')
                let metadata += [self.isDirectory ? '' : (extension ==# '' ? nr2char(str2nr('0x10ffff',16)) : extension)]
            endif
        endfor

        if g:NERDTreeSortOrder[0] =~# '\[\[.*\]\]'
            " Apply tags' sorting first if specified first.
            let self._sortKey = metadata + [self.getSortOrderIndex()]
        else
            " Otherwise, do regex grouping first.
            let self._sortKey = [self.getSortOrderIndex()] + metadata
        endif

        let path = self.getLastPathComponent(0)
        if !g:NERDTreeSortHiddenFirst
            let path = substitute(path, '^[._]', '', '')
        endif
        if !g:NERDTreeCaseSensitiveSort
            let path = tolower(path)
        endif

        call extend(self._sortKey, (g:NERDTreeNaturalSort ? self._splitChunks(path) : [path]))
    endif
    return self._sortKey
endfunction

" FUNCTION: Path.isHiddenUnder(path) {{{1
function! s:Path.isHiddenUnder(path)

    if !self.isUnder(a:path)
        return 0
    endif

    let l:startIndex = len(a:path.pathSegments)
    let l:segments = self.pathSegments[l:startIndex : ]

    for l:segment in l:segments

        if l:segment =~# '^\.'
            return 1
        endif
    endfor

    return 0
endfunction

" FUNCTION: Path.isUnixHiddenFile() {{{1
" check for unix hidden files
function! s:Path.isUnixHiddenFile()
    return self.getLastPathComponent(0) =~# '^\.'
endfunction

" FUNCTION: Path.isUnixHiddenPath() {{{1
" check for unix path with hidden components
function! s:Path.isUnixHiddenPath()
    if self.getLastPathComponent(0) =~# '^\.'
        return 1
    else
        for segment in self.pathSegments
            if segment =~# '^\.'
                return 1
            endif
        endfor
        return 0
    endif
endfunction

" FUNCTION: Path.ignore(nerdtree) {{{1
" returns true if this path should be ignored
function! s:Path.ignore(nerdtree)
    "filter out the user specified paths to ignore
    if a:nerdtree.ui.isIgnoreFilterEnabled()
        for i in g:NERDTreeIgnore
            if self._ignorePatternMatches(i)
                return 1
            endif
        endfor

        for l:Callback in g:NERDTree.PathFilters()
            let l:Callback = type(l:Callback) ==# type(function('tr')) ? l:Callback : function(l:Callback)
            if l:Callback({'path': self, 'nerdtree': a:nerdtree})
               return 1
            endif
        endfor
    endif

    "dont show hidden files unless instructed to
    if !a:nerdtree.ui.getShowHidden() && self.isUnixHiddenFile()
        return 1
    endif

    if a:nerdtree.ui.getShowFiles() ==# 0 && self.isDirectory ==# 0
        return 1
    endif

    return 0
endfunction

" FUNCTION: Path._ignorePatternMatches(pattern) {{{1
" returns true if this path matches the given ignore pattern
function! s:Path._ignorePatternMatches(pattern)
    let pat = a:pattern
    if strpart(pat,len(pat)-8) ==# '[[path]]'
        let pat = strpart(pat,0, len(pat)-8)
        return self.str() =~# pat
    elseif strpart(pat,len(pat)-7) ==# '[[dir]]'
        if !self.isDirectory
            return 0
        endif
        let pat = strpart(pat,0, len(pat)-7)
    elseif strpart(pat,len(pat)-8) ==# '[[file]]'
        if self.isDirectory
            return 0
        endif
        let pat = strpart(pat,0, len(pat)-8)
    endif

    return self.getLastPathComponent(0) =~# pat
endfunction

" FUNCTION: Path.isAncestor(path) {{{1
" return 1 if this path is somewhere above the given path in the filesystem.
"
" a:path should be a dir
function! s:Path.isAncestor(child)
    return a:child.isUnder(self)
endfunction

" FUNCTION: Path.isUnder(path) {{{1
" return 1 if this path is somewhere under the given path in the filesystem.
function! s:Path.isUnder(parent)
    if a:parent.isDirectory ==# 0
        return 0
    endif
    if nerdtree#runningWindows() && a:parent.drive !=# self.drive
        return 0
    endif
    let l:this_count = len(self.pathSegments)
    if l:this_count ==# 0
        return 0
    endif
    let l:that_count = len(a:parent.pathSegments)
    if l:that_count ==# 0
        return 1
    endif
    if l:that_count >= l:this_count
        return 0
    endif
    for i in range(0, l:that_count-1)
        if !nerdtree#pathEquals(self.pathSegments[i], a:parent.pathSegments[i])
            return 0
        endif
    endfor
    return 1
endfunction

" FUNCTION: Path.JoinPathStrings(...) {{{1
function! s:Path.JoinPathStrings(...)
    let components = []
    for i in a:000
        let components = extend(components, split(i, '/'))
    endfor
    return '/' . join(components, '/')
endfunction

" FUNCTION: Path.equals() {{{1
"
" Determines whether 2 path objects are "equal".
" They are equal if the paths they represent are the same
"
" Args:
" path: the other path obj to compare this with
function! s:Path.equals(path)
    return nerdtree#pathEquals(self.str(), a:path.str())
endfunction

" FUNCTION: Path.New(pathStr) {{{1
function! s:Path.New(pathStr)
    let l:newPath = copy(self)

    call l:newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:pathStr))

    let l:newPath.cachedDisplayString = ''
    let l:newPath.flagSet = g:NERDTreeFlagSet.New()

    return l:newPath
endfunction

" FUNCTION: Path.Resolve() {{{1
" Invoke the vim resolve() function and return the result
" This is necessary because in some versions of vim resolve() removes trailing
" slashes while in other versions it doesn't.  This always removes the trailing
" slash
function! s:Path.Resolve(path)
    let tmp = resolve(a:path)
    return tmp =~# '.\+/$' ? substitute(tmp, '/$', '', '') : tmp
endfunction

" FUNCTION: Path.readInfoFromDisk(fullpath) {{{1
"
"
" Throws NERDTree.Path.InvalidArguments exception.
function! s:Path.readInfoFromDisk(fullpath)
    call self.extractDriveLetter(a:fullpath)

    let fullpath = s:Path.WinToUnixPath(a:fullpath)

    if getftype(fullpath) ==# 'fifo'
        throw 'NERDTree.InvalidFiletypeError: Cant handle FIFO files: ' . a:fullpath
    endif

    let self.pathSegments = filter(split(fullpath, '/'), '!empty(v:val)')

    let self.isReadOnly = 0
    if isdirectory(a:fullpath)
        let self.isDirectory = 1
    elseif filereadable(a:fullpath)
        let self.isDirectory = 0
        let self.isReadOnly = filewritable(a:fullpath) ==# 0
    else
        throw 'NERDTree.InvalidArgumentsError: Invalid path = ' . a:fullpath
    endif

    let self.isExecutable = 0
    if !self.isDirectory
        let self.isExecutable = getfperm(a:fullpath) =~# 'x'
    endif

    "grab the last part of the path (minus the trailing slash)
    let lastPathComponent = self.getLastPathComponent(0)

    "get the path to the new node with the parent dir fully resolved
    let hardPath = s:Path.Resolve(self.strTrunk()) . '/' . lastPathComponent

    "if  the last part of the path is a symlink then flag it as such
    let self.isSymLink = (s:Path.Resolve(hardPath) !=# hardPath)
    if self.isSymLink
        let self.symLinkDest = s:Path.Resolve(fullpath)

        "if the link is a dir then slap a / on the end of its dest
        if isdirectory(self.symLinkDest)

            "we always wanna treat MS windows shortcuts as files for
            "simplicity
            if hardPath !~# '\.lnk$'

                let self.symLinkDest = self.symLinkDest . '/'
            endif
        endif
    endif
endfunction

" FUNCTION: Path.refresh(nerdtree) {{{1
function! s:Path.refresh(nerdtree)
    call self.readInfoFromDisk(self.str())
    call g:NERDTreePathNotifier.NotifyListeners('refresh', self, a:nerdtree, {})
    call self.cacheDisplayString()
endfunction

" FUNCTION: Path.refreshFlags(nerdtree) {{{1
function! s:Path.refreshFlags(nerdtree)
    call g:NERDTreePathNotifier.NotifyListeners('refreshFlags', self, a:nerdtree, {})
    call self.cacheDisplayString()
endfunction

" FUNCTION: Path.rename() {{{1
"
" Renames this node on the filesystem
function! s:Path.rename(newPath)
    if a:newPath ==# ''
        throw 'NERDTree.InvalidArgumentsError: Invalid newPath for renaming = '. a:newPath
    endif

    call s:Path.createParentDirectories(a:newPath)

    let success =  rename(self.str(), a:newPath)
    if success !=# 0
        throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath
    endif
    call self.readInfoFromDisk(a:newPath)

    for i in self.bookmarkNames()
        let b = g:NERDTreeBookmark.BookmarkFor(i)
        call b.setPath(copy(self))
    endfor
    call g:NERDTreeBookmark.Write()
endfunction

" FUNCTION: Path.str() {{{1
" Return a string representation of this Path object.
"
" Args:
" This function takes a single dictionary (optional) with keys and values that
" specify how the returned pathname should be formatted.
"
" The dictionary may have the following keys:
"  'format'
"  'escape'
"  'truncateTo'
"
" The 'format' key may have a value of:
"  'Cd' - a string to be used with ":cd" and similar commands
"  'Edit' - a string to be used with ":edit" and similar commands
"  'UI' - a string to be displayed in the NERDTree user interface
"
" The 'escape' key, if specified, will cause the output to be escaped with
" Vim's internal "shellescape()" function.
"
" The 'truncateTo' key shortens the length of the path to that given by the
" value associated with 'truncateTo'. A '<' is prepended.
function! s:Path.str(...)
    let options = a:0 ? a:1 : {}
    let toReturn = ''

    if has_key(options, 'format')
        let format = options['format']
        if has_key(self, '_strFor' . format)
            exec 'let toReturn = self._strFor' . format . '()'
        else
            throw 'NERDTree.UnknownFormatError: unknown format "'. format .'"'
        endif
    else
        let toReturn = self._str()
    endif

    if nerdtree#has_opt(options, 'escape')
        let toReturn = shellescape(toReturn)
    endif

    if has_key(options, 'truncateTo')
        let limit = options['truncateTo']
        if strdisplaywidth(toReturn) > limit-1
            while strdisplaywidth(toReturn) > limit-1 && strchars(toReturn) > 0
                let toReturn = substitute(toReturn, '^.', '', '')
            endwhile
            if len(split(toReturn, '/')) > 1
                let toReturn = '</' . join(split(toReturn, '/')[1:], '/') . '/'
            else
                let toReturn = '<' . toReturn
            endif
        endif
    endif

    return toReturn
endfunction

" FUNCTION: Path._strForUI() {{{1
function! s:Path._strForUI()
    let toReturn = '/' . join(self.pathSegments, '/')
    if self.isDirectory && toReturn !=# '/'
        let toReturn  = toReturn . '/'
    endif
    return toReturn
endfunction

" FUNCTION: Path._strForCd() {{{1
" Return a string representation of this Path that is suitable for use as an
" argument to Vim's internal ":cd" command.
function! s:Path._strForCd()
    return fnameescape(self.str())
endfunction

" FUNCTION: Path._strForEdit() {{{1
" Return a string representation of this Path that is suitable for use as an
" argument to Vim's internal ":edit" command.
function! s:Path._strForEdit()

    " Make the path relative to the current working directory, if possible.
    let l:result = fnamemodify(self.str(), ':.')

    " On Windows, the drive letter may be removed by "fnamemodify()".  Add it
    " back, if necessary.
    if nerdtree#runningWindows() && l:result[0] == nerdtree#slash()
        let l:result = self.drive . l:result
    endif

    let l:result = fnameescape(l:result)

    if empty(l:result)
        let l:result = '.'
    endif

    return l:result
endfunction

" FUNCTION: Path._strForGlob() {{{1
function! s:Path._strForGlob()
    let lead = nerdtree#slash()

    "if we are running windows then slap a drive letter on the front
    if nerdtree#runningWindows()
        let lead = self.drive . '\'
    endif

    let toReturn = lead . join(self.pathSegments, nerdtree#slash())

    if !nerdtree#runningWindows()
        let toReturn = escape(toReturn, self._escChars())
    endif
    return toReturn
endfunction

" FUNCTION: Path._str() {{{1
" Return the absolute pathname associated with this Path object.  The pathname
" returned is appropriate for the underlying file system.
function! s:Path._str()
    let l:separator = nerdtree#slash()
    let l:leader = l:separator

    if nerdtree#runningWindows()
        let l:leader = self.drive . l:separator
    endif

    return l:leader . join(self.pathSegments, l:separator)
endfunction

" FUNCTION: Path.strTrunk() {{{1
" Gets the path without the last segment on the end.
function! s:Path.strTrunk()
    return self.drive . '/' . join(self.pathSegments[0:-2], '/')
endfunction

" FUNCTION: Path.tabnr() {{{1
" return the number of the first tab that is displaying this file
"
" return 0 if no tab was found
function! s:Path.tabnr()
    let str = self.str()
    for t in range(tabpagenr('$'))
        for b in tabpagebuflist(t+1)
            if str ==# expand('#' . b . ':p')
                return t+1
            endif
        endfor
    endfor
    return 0
endfunction

" FUNCTION: Path.WinToUnixPath(pathstr){{{1
" Takes in a windows path and returns the unix equiv
"
" A class level method
"
" Args:
" pathstr: the windows path to convert
function! s:Path.WinToUnixPath(pathstr)
    if !nerdtree#runningWindows()
        return a:pathstr
    endif

    let toReturn = a:pathstr

    "remove the x:\ of the front
    let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', '')

    "remove the \\ network share from the front
    let toReturn = substitute(toReturn, '^\(\\\\\|\/\/\)[^\\\/]*\(\\\|\/\)[^\\\/]*\(\\\|\/\)\?', '/', '')

    "convert all \ chars to /
    let toReturn = substitute(toReturn, '\', '/', 'g')

    return toReturn
endfunction

" vim: set sw=4 sts=4 et fdm=marker:
