source: rtems-tools/rtemstoolkit/path.py

Last change on this file was 2852cac, checked in by Vijay Kumar Banerjee <vijaykumar9597@…>, on 01/24/20 at 09:57:15

rtemstoolkit/path: Add support to copy single file

  • Property mode set to 100644
File size: 13.8 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# Manage paths locally. The internally the path is in Unix or shell format and
33# we convert to the native format when performing operations at the Python
34# level. This allows macro expansion to work.
35#
36
37from __future__ import print_function
38
39import glob
40import os
41import shutil
42import string
43import sys
44
45from rtemstoolkit import error
46from rtemstoolkit import log
47windows_posix = sys.platform == 'msys'
48windows = os.name == 'nt'
49
50win_maxpath = 254
51
52def host(path):
53    if path is not None:
54        while '//' in path:
55            path = path.replace('//', '/')
56        if windows:
57            if len(path) > 2 and \
58               path[0] == '/' and path[2] == '/' and \
59               (path[1] in string.ascii_lowercase or \
60                path[1] in string.ascii_uppercase):
61                path = '%s:%s' % (path[1], path[2:])
62            path = path.replace('/', '\\')
63            if len(path) > win_maxpath:
64                if path.startswith('\\\\?\\'):
65                    path = path[4:]
66                path = u'\\'.join([u'\\\\?', path])
67    return path
68
69def shell(path):
70    if isinstance(path, bytes):
71        path = path.decode('ascii')
72    if path is not None:
73        if windows or windows_posix:
74            path = path.encode('ascii', 'ignore').decode('ascii')
75            if path.startswith('\\\\?\\'):
76                path = path[4:]
77            if len(path) > 1 and path[1] == ':':
78                path = '/%s%s' % (path[0].lower(), path[2:])
79            path = path.replace('\\', '/')
80        while '//' in path:
81            path = path.replace('//', '/')
82    return path
83
84def basename(path):
85    path = shell(path)
86    return shell(os.path.basename(path))
87
88def dirname(path):
89    path = shell(path)
90    return shell(os.path.dirname(path))
91
92def is_abspath(path):
93    if path is not None and len(path) > 0:
94        return '/' == path[0]
95    return False
96
97def join(path, *args):
98    path = shell(path)
99    for arg in args:
100        if len(path):
101            path += '/' + shell(arg)
102        else:
103            path = shell(arg)
104    return shell(path)
105
106def abspath(path):
107    path = shell(path)
108    return shell(os.path.abspath(host(path)))
109
110def relpath(path, start = None):
111    path = shell(path)
112    if start is None:
113        path = os.path.relpath(host(path))
114    else:
115        path = os.path.relpath(host(path), start)
116    return shell(path)
117
118def splitext(path):
119    path = shell(path)
120    root, ext = os.path.splitext(host(path))
121    return shell(root), ext
122
123def listdir(path, error = True):
124    path = host(path)
125    files = []
126    if not os.path.exists(path):
127        if error:
128            raise error.general('path does not exist : %s' % (path))
129    elif not isdir(path):
130        if error:
131            raise error.general('path is not a directory: %s' % (path))
132    else:
133        if windows:
134            try:
135                files = os.listdir(host(path))
136            except IOError:
137                raise error.general('Could not list files: %s' % (path))
138            except OSError as e:
139                raise error.general('Could not list files: %s: %s' % (path, str(e)))
140            except WindowsError as e:
141                raise error.general('Could not list files: %s: %s' % (path, str(e)))
142        else:
143            try:
144                files = os.listdir(host(path))
145            except IOError:
146                raise error.general('Could not list files: %s' % (path))
147            except OSError as e:
148                raise error.general('Could not list files: %s: %s' % (path, str(e)))
149    return files
150
151def exists(paths):
152    def _exists(p):
153        if not is_abspath(p):
154            p = shell(join(os.getcwd(), host(p)))
155        return basename(p) in ['.'] + listdir(dirname(p), error = False)
156
157    if type(paths) == list:
158        results = []
159        for p in paths:
160            results += [_exists(shell(p))]
161        return results
162    return _exists(shell(paths))
163
164def isdir(path):
165    path = shell(path)
166    return os.path.isdir(host(path))
167
168def isfile(path):
169    path = shell(path)
170    return os.path.isfile(host(path))
171
172def isabspath(path):
173    path = shell(path)
174    return path[0] == '/'
175
176def isreadable(path):
177    path = shell(path)
178    return os.access(host(path), os.R_OK)
179
180def iswritable(path):
181    path = shell(path)
182    return os.access(host(path), os.W_OK)
183
184def isreadwritable(path):
185    path = shell(path)
186    return isreadable(path) and iswritable(path)
187
188def ispathwritable(path):
189    path = host(path)
190    while len(path) != 0:
191        if exists(path):
192            return iswritable(path)
193        path = os.path.dirname(path)
194    return False
195
196def mkdir(path):
197    path = host(path)
198    if exists(path):
199        if not isdir(path):
200            raise error.general('path exists and is not a directory: %s' % (path))
201    else:
202        if windows:
203            try:
204                os.makedirs(host(path))
205            except IOError:
206                raise error.general('cannot make directory: %s' % (path))
207            except OSError as e:
208                raise error.general('cannot make directory: %s: %s' % (path, str(e)))
209            except WindowsError as e:
210                raise error.general('cannot make directory: %s: %s' % (path, str(e)))
211        else:
212            try:
213                os.makedirs(host(path))
214            except IOError:
215                raise error.general('cannot make directory: %s' % (path))
216            except OSError as e:
217                raise error.general('cannot make directory: %s: %s' % (path, str(e)))
218
219def chdir(path):
220    path = shell(path)
221    os.chdir(host(path))
222
223def removeall(path):
224    #
225    # Perform the removal of the directory tree manually so we can
226    # make sure on Windows the files are correctly encoded to avoid
227    # the file name size limit. On Windows the os.walk fails once we
228    # get to the max path length.
229    #
230    def _isdir(path):
231        hpath = host(path)
232        return os.path.isdir(hpath) and not os.path.islink(hpath)
233
234    def _remove_node(path):
235        hpath = host(path)
236        if not os.path.islink(hpath) and not os.access(hpath, os.W_OK):
237            os.chmod(hpath, stat.S_IWUSR)
238        if _isdir(path):
239            os.rmdir(hpath)
240        else:
241            os.unlink(hpath)
242
243    def _remove(path):
244        dirs = []
245        for name in listdir(path):
246            path_ = join(path, name)
247            hname = host(path_)
248            if _isdir(path_):
249                dirs += [name]
250            else:
251                _remove_node(path_)
252        for name in dirs:
253            dir = join(path, name)
254            _remove(dir)
255            _remove_node(dir)
256
257    path = shell(path)
258    hpath = host(path)
259
260    if os.path.exists(hpath):
261        _remove(path)
262        _remove_node(path)
263
264def expand(name, paths):
265    l = []
266    for p in paths:
267        l += [join(shell(p), name)]
268    return l
269
270def expanduser(path):
271    path = host(path)
272    path = os.path.expanduser(path)
273    return shell(path)
274
275def collect_files(path_):
276    #
277    # Convert to shell paths and return shell paths.
278    #
279    # @fixme should this use a passed in set of defaults and not
280    #        not the initial set of values ?
281    #
282    path_ = shell(path_)
283    if '*' in path_ or '?' in path_:
284        dir = dirname(path_)
285        base = basename(path_)
286        if len(base) == 0:
287            base = '*'
288        files = []
289        for p in dir.split(':'):
290            hostdir = host(p)
291            for f in glob.glob(os.path.join(hostdir, base)):
292                files += [host(f)]
293    else:
294        files = [host(path_)]
295    return sorted(files)
296
297def copy(src, dst):
298    src = shell(src)
299    dst = shell(dst)
300    hsrc = host(src)
301    hdst = host(dst)
302    try:
303        shutil.copy(hsrc, hdst)
304    except OSError as why:
305        if windows:
306            if WindowsError is not None and isinstance(why, WindowsError):
307                pass
308        else:
309            raise error.general('copying tree (1): %s -> %s: %s' % (hsrc, hdst, str(why)))
310
311def copy_tree(src, dst):
312    trace = False
313
314    hsrc = host(src)
315    hdst = host(dst)
316
317    if exists(hsrc):
318        if isdir(hsrc):
319            names = listdir(hsrc)
320        else:
321            names = [basename(hsrc)]
322            hsrc = dirname(hsrc)
323    else:
324        names = []
325
326    if trace:
327        print('path.copy_tree:')
328        print('   src: "%s"' % (src))
329        print('  hsrc: "%s"' % (hsrc))
330        print('   dst: "%s"' % (dst))
331        print('  hdst: "%s"' % (hdst))
332        print(' names: %r' % (names))
333
334    if not os.path.isdir(hdst):
335        if trace:
336            print(' mkdir: %s' % (hdst))
337        try:
338            os.makedirs(hdst)
339        except OSError as why:
340            raise error.general('copying tree: cannot create target directory %s: %s' % \
341                                (hdst, str(why)))
342
343    for name in names:
344        srcname = host(os.path.join(hsrc, name))
345        dstname = host(os.path.join(hdst, name))
346        try:
347            if os.path.islink(srcname):
348                linkto = os.readlink(srcname)
349                if exists(shell(dstname)):
350                    if os.path.islink(dstname):
351                        dstlinkto = os.readlink(dstname)
352                        if linkto != dstlinkto:
353                            log.warning('copying tree: link does not match: %s -> %s' % \
354                                            (dstname, dstlinkto))
355                            os.remove(dstname)
356                    else:
357                        log.warning('copying tree: destination is not a link: %s' % \
358                                        (dstname))
359                        os.remove(dstname)
360                else:
361                    os.symlink(linkto, dstname)
362            elif os.path.isdir(srcname):
363                copy_tree(srcname, dstname)
364            else:
365                shutil.copyfile(host(srcname), host(dstname))
366                shutil.copystat(host(srcname), host(dstname))
367        except shutil.Error as err:
368            raise error.general('copying tree (2): %s -> %s: %s' % \
369                                (hsrc, hdst, str(err)))
370        except EnvironmentError as why:
371            raise error.general('copying tree (3): %s -> %s: %s' % \
372                                (srcname, dstname, str(why)))
373    try:
374        shutil.copystat(hsrc, hdst)
375    except OSError as why:
376        if windows:
377            if WindowsError is not None and isinstance(why, WindowsError):
378                pass
379        else:
380            raise error.general('copying tree (4): %s -> %s: %s' % (hsrc, hdst, str(why)))
381
382def get_size(path, depth = -1):
383    #
384    # Get the size the directory tree manually to the required depth.
385    # This makes sure on Windows the files are correctly encoded to avoid
386    # the file name size limit. On Windows the os.walk fails once we
387    # get to the max path length on Windows.
388    #
389    def _isdir(path):
390        hpath = host(path)
391        return os.path.isdir(hpath) and not os.path.islink(hpath)
392
393    def _node_size(path):
394        hpath = host(path)
395        size = 0
396        if not os.path.islink(hpath):
397            size = os.path.getsize(hpath)
398        return size
399
400    def _get_size(path, depth, level = 0):
401        level += 1
402        dirs = []
403        size = 0
404        for name in listdir(path):
405            path_ = join(path, shell(name))
406            hname = host(path_)
407            if _isdir(path_):
408                dirs += [shell(name)]
409            else:
410                size += _node_size(path_)
411        if depth < 0 or level < depth:
412            for name in dirs:
413                dir = join(path, name)
414                size += _get_size(dir, depth, level)
415        return size
416
417    path = shell(path)
418    hpath = host(path)
419    size = 0
420
421    if os.path.exists(hpath):
422        size = _get_size(path, depth)
423
424    return size
425
426def get_humanize_size(path, depth = -1):
427    size = get_size(path, depth)
428    for unit in ['','K','M','G','T','P','E','Z']:
429        if abs(size) < 1024.0:
430            return "%5.3f%sB" % (size, unit)
431        size /= 1024.0
432    return "%.3f%sB" % (size, 'Y')
433
434if __name__ == '__main__':
435    print(host('/a/b/c/d-e-f'))
436    print(host('//a/b//c/d-e-f'))
437    print(shell('/w/x/y/z'))
438    print(basename('/as/sd/df/fg/me.txt'))
439    print(dirname('/as/sd/df/fg/me.txt'))
440    print(join('/d', 'g', '/tyty/fgfg'))
441    windows = True
442    print(host('/a/b/c/d-e-f'))
443    print(host('//a/b//c/d-e-f'))
444    print(shell('/w/x/y/z'))
445    print(shell('w:/x/y/z'))
446    print(basename('x:/sd/df/fg/me.txt'))
447    print(dirname('x:/sd/df/fg/me.txt'))
448    print(join('s:/d/', '/g', '/tyty/fgfg'))
449    print(join('s:/d/e\\f/g', '/h', '/tyty/zxzx', '\\mm\\nn/p'))
Note: See TracBrowser for help on using the repository browser.