1#!/usr/bin/env python
2
3""" This module tries to retrieve as much platform-identifying data as
4    possible. It makes this information available via function APIs.
5
6    If called from the command line, it prints the platform
7    information concatenated as single string to stdout. The output
8    format is useable as part of a filename.
9
10"""
11#    This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
12#    If you find problems, please submit bug reports/patches via the
13#    Python bug tracker (http://bugs.python.org) and assign them to "lemburg".
14#
15#    Note: Please keep this module compatible to Python 1.5.2.
16#
17#    Still needed:
18#    * more support for WinCE
19#    * support for MS-DOS (PythonDX ?)
20#    * support for Amiga and other still unsupported platforms running Python
21#    * support for additional Linux distributions
22#
23#    Many thanks to all those who helped adding platform-specific
24#    checks (in no particular order):
25#
26#      Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
27#      Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
28#      Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
29#      Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
30#      Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
31#      Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
32#      Dower
33#
34#    History:
35#
36#    <see CVS and SVN checkin messages for history>
37#
38#    1.0.8 - changed Windows support to read version from kernel32.dll
39#    1.0.7 - added DEV_NULL
40#    1.0.6 - added linux_distribution()
41#    1.0.5 - fixed Java support to allow running the module on Jython
42#    1.0.4 - added IronPython support
43#    1.0.3 - added normalization of Windows system name
44#    1.0.2 - added more Windows support
45#    1.0.1 - reformatted to make doc.py happy
46#    1.0.0 - reformatted a bit and checked into Python CVS
47#    0.8.0 - added sys.version parser and various new access
48#            APIs (python_version(), python_compiler(), etc.)
49#    0.7.2 - fixed architecture() to use sizeof(pointer) where available
50#    0.7.1 - added support for Caldera OpenLinux
51#    0.7.0 - some fixes for WinCE; untabified the source file
52#    0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
53#            vms_lib.getsyi() configured
54#    0.6.1 - added code to prevent 'uname -p' on platforms which are
55#            known not to support it
56#    0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
57#            did some cleanup of the interfaces - some APIs have changed
58#    0.5.5 - fixed another type in the MacOS code... should have
59#            used more coffee today ;-)
60#    0.5.4 - fixed a few typos in the MacOS code
61#    0.5.3 - added experimental MacOS support; added better popen()
62#            workarounds in _syscmd_ver() -- still not 100% elegant
63#            though
64#    0.5.2 - fixed uname() to return '' instead of 'unknown' in all
65#            return values (the system uname command tends to return
66#            'unknown' instead of just leaving the field empty)
67#    0.5.1 - included code for slackware dist; added exception handlers
68#            to cover up situations where platforms don't have os.popen
69#            (e.g. Mac) or fail on socket.gethostname(); fixed libc
70#            detection RE
71#    0.5.0 - changed the API names referring to system commands to *syscmd*;
72#            added java_ver(); made syscmd_ver() a private
73#            API (was system_ver() in previous versions) -- use uname()
74#            instead; extended the win32_ver() to also return processor
75#            type information
76#    0.4.0 - added win32_ver() and modified the platform() output for WinXX
77#    0.3.4 - fixed a bug in _follow_symlinks()
78#    0.3.3 - fixed popen() and "file" command invokation bugs
79#    0.3.2 - added architecture() API and support for it in platform()
80#    0.3.1 - fixed syscmd_ver() RE to support Windows NT
81#    0.3.0 - added system alias support
82#    0.2.3 - removed 'wince' again... oh well.
83#    0.2.2 - added 'wince' to syscmd_ver() supported platforms
84#    0.2.1 - added cache logic and changed the platform string format
85#    0.2.0 - changed the API to use functions instead of module globals
86#            since some action take too long to be run on module import
87#    0.1.0 - first release
88#
89#    You can always get the latest version of this module at:
90#
91#             http://www.egenix.com/files/python/platform.py
92#
93#    If that URL should fail, try contacting the author.
94
95__copyright__ = """
96    Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
97    Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
98
99    Permission to use, copy, modify, and distribute this software and its
100    documentation for any purpose and without fee or royalty is hereby granted,
101    provided that the above copyright notice appear in all copies and that
102    both that copyright notice and this permission notice appear in
103    supporting documentation or portions thereof, including modifications,
104    that you make.
105
106    EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
107    THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
108    FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
109    INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
110    FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
111    NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
112    WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
113
114"""
115
116__version__ = '1.0.7'
117
118import sys,string,os,re
119
120### Globals & Constants
121
122# Determine the platform's /dev/null device
123try:
124    DEV_NULL = os.devnull
125except AttributeError:
126    # os.devnull was added in Python 2.4, so emulate it for earlier
127    # Python versions
128    if sys.platform in ('dos','win32','win16','os2'):
129        # Use the old CP/M NUL as device name
130        DEV_NULL = 'NUL'
131    else:
132        # Standard Unix uses /dev/null
133        DEV_NULL = '/dev/null'
134
135### Platform specific APIs
136
137_libc_search = re.compile(r'(__libc_init)'
138                          '|'
139                          '(GLIBC_([0-9.]+))'
140                          '|'
141                          '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)')
142
143def libc_ver(executable=sys.executable,lib='',version='',
144
145             chunksize=2048):
146
147    """ Tries to determine the libc version that the file executable
148        (which defaults to the Python interpreter) is linked against.
149
150        Returns a tuple of strings (lib,version) which default to the
151        given parameters in case the lookup fails.
152
153        Note that the function has intimate knowledge of how different
154        libc versions add symbols to the executable and thus is probably
155        only useable for executables compiled using gcc.
156
157        The file is read and scanned in chunks of chunksize bytes.
158
159    """
160    if hasattr(os.path, 'realpath'):
161        # Python 2.2 introduced os.path.realpath(); it is used
162        # here to work around problems with Cygwin not being
163        # able to open symlinks for reading
164        executable = os.path.realpath(executable)
165    f = open(executable,'rb')
166    binary = f.read(chunksize)
167    pos = 0
168    while 1:
169        m = _libc_search.search(binary,pos)
170        if not m:
171            binary = f.read(chunksize)
172            if not binary:
173                break
174            pos = 0
175            continue
176        libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
177        if libcinit and not lib:
178            lib = 'libc'
179        elif glibc:
180            if lib != 'glibc':
181                lib = 'glibc'
182                version = glibcversion
183            elif glibcversion > version:
184                version = glibcversion
185        elif so:
186            if lib != 'glibc':
187                lib = 'libc'
188                if soversion and soversion > version:
189                    version = soversion
190                if threads and version[-len(threads):] != threads:
191                    version = version + threads
192        pos = m.end()
193    f.close()
194    return lib,version
195
196def _dist_try_harder(distname,version,id):
197
198    """ Tries some special tricks to get the distribution
199        information in case the default method fails.
200
201        Currently supports older SuSE Linux, Caldera OpenLinux and
202        Slackware Linux distributions.
203
204    """
205    if os.path.exists('/var/adm/inst-log/info'):
206        # SuSE Linux stores distribution information in that file
207        info = open('/var/adm/inst-log/info').readlines()
208        distname = 'SuSE'
209        for line in info:
210            tv = string.split(line)
211            if len(tv) == 2:
212                tag,value = tv
213            else:
214                continue
215            if tag == 'MIN_DIST_VERSION':
216                version = string.strip(value)
217            elif tag == 'DIST_IDENT':
218                values = string.split(value,'-')
219                id = values[2]
220        return distname,version,id
221
222    if os.path.exists('/etc/.installed'):
223        # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
224        info = open('/etc/.installed').readlines()
225        for line in info:
226            pkg = string.split(line,'-')
227            if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
228                # XXX does Caldera support non Intel platforms ? If yes,
229                #     where can we find the needed id ?
230                return 'OpenLinux',pkg[1],id
231
232    if os.path.isdir('/usr/lib/setup'):
233        # Check for slackware version tag file (thanks to Greg Andruk)
234        verfiles = os.listdir('/usr/lib/setup')
235        for n in range(len(verfiles)-1, -1, -1):
236            if verfiles[n][:14] != 'slack-version-':
237                del verfiles[n]
238        if verfiles:
239            verfiles.sort()
240            distname = 'slackware'
241            version = verfiles[-1][14:]
242            return distname,version,id
243
244    return distname,version,id
245
246_release_filename = re.compile(r'(\w+)[-_](release|version)')
247_lsb_release_version = re.compile(r'(.+)'
248                                   ' release '
249                                   '([\d.]+)'
250                                   '[^(]*(?:\((.+)\))?')
251_release_version = re.compile(r'([^0-9]+)'
252                               '(?: release )?'
253                               '([\d.]+)'
254                               '[^(]*(?:\((.+)\))?')
255
256# See also http://www.novell.com/coolsolutions/feature/11251.html
257# and http://linuxmafia.com/faq/Admin/release-files.html
258# and http://data.linux-ntfs.org/rpm/whichrpm
259# and http://www.die.net/doc/linux/man/man1/lsb_release.1.html
260
261_supported_dists = (
262    'SuSE', 'debian', 'fedora', 'redhat', 'centos',
263    'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',
264    'UnitedLinux', 'turbolinux')
265
266def _parse_release_file(firstline):
267
268    # Default to empty 'version' and 'id' strings.  Both defaults are used
269    # when 'firstline' is empty.  'id' defaults to empty when an id can not
270    # be deduced.
271    version = ''
272    id = ''
273
274    # Parse the first line
275    m = _lsb_release_version.match(firstline)
276    if m is not None:
277        # LSB format: "distro release x.x (codename)"
278        return tuple(m.groups())
279
280    # Pre-LSB format: "distro x.x (codename)"
281    m = _release_version.match(firstline)
282    if m is not None:
283        return tuple(m.groups())
284
285    # Unknown format... take the first two words
286    l = string.split(string.strip(firstline))
287    if l:
288        version = l[0]
289        if len(l) > 1:
290            id = l[1]
291    return '', version, id
292
293def linux_distribution(distname='', version='', id='',
294
295                       supported_dists=_supported_dists,
296                       full_distribution_name=1):
297
298    """ Tries to determine the name of the Linux OS distribution name.
299
300        The function first looks for a distribution release file in
301        /etc and then reverts to _dist_try_harder() in case no
302        suitable files are found.
303
304        supported_dists may be given to define the set of Linux
305        distributions to look for. It defaults to a list of currently
306        supported Linux distributions identified by their release file
307        name.
308
309        If full_distribution_name is true (default), the full
310        distribution read from the OS is returned. Otherwise the short
311        name taken from supported_dists is used.
312
313        Returns a tuple (distname,version,id) which default to the
314        args given as parameters.
315
316    """
317    try:
318        etc = os.listdir('/etc')
319    except os.error:
320        # Probably not a Unix system
321        return distname,version,id
322    etc.sort()
323    for file in etc:
324        m = _release_filename.match(file)
325        if m is not None:
326            _distname,dummy = m.groups()
327            if _distname in supported_dists:
328                distname = _distname
329                break
330    else:
331        return _dist_try_harder(distname,version,id)
332
333    # Read the first line
334    f = open('/etc/'+file, 'r')
335    firstline = f.readline()
336    f.close()
337    _distname, _version, _id = _parse_release_file(firstline)
338
339    if _distname and full_distribution_name:
340        distname = _distname
341    if _version:
342        version = _version
343    if _id:
344        id = _id
345    return distname, version, id
346
347# To maintain backwards compatibility:
348
349def dist(distname='',version='',id='',
350
351         supported_dists=_supported_dists):
352
353    """ Tries to determine the name of the Linux OS distribution name.
354
355        The function first looks for a distribution release file in
356        /etc and then reverts to _dist_try_harder() in case no
357        suitable files are found.
358
359        Returns a tuple (distname,version,id) which default to the
360        args given as parameters.
361
362    """
363    return linux_distribution(distname, version, id,
364                              supported_dists=supported_dists,
365                              full_distribution_name=0)
366
367class _popen:
368
369    """ Fairly portable (alternative) popen implementation.
370
371        This is mostly needed in case os.popen() is not available, or
372        doesn't work as advertised, e.g. in Win9X GUI programs like
373        PythonWin or IDLE.
374
375        Writing to the pipe is currently not supported.
376
377    """
378    tmpfile = ''
379    pipe = None
380    bufsize = None
381    mode = 'r'
382
383    def __init__(self,cmd,mode='r',bufsize=None):
384
385        if mode != 'r':
386            raise ValueError,'popen()-emulation only supports read mode'
387        import tempfile
388        self.tmpfile = tmpfile = tempfile.mktemp()
389        os.system(cmd + ' > %s' % tmpfile)
390        self.pipe = open(tmpfile,'rb')
391        self.bufsize = bufsize
392        self.mode = mode
393
394    def read(self):
395
396        return self.pipe.read()
397
398    def readlines(self):
399
400        if self.bufsize is not None:
401            return self.pipe.readlines()
402
403    def close(self,
404
405              remove=os.unlink,error=os.error):
406
407        if self.pipe:
408            rc = self.pipe.close()
409        else:
410            rc = 255
411        if self.tmpfile:
412            try:
413                remove(self.tmpfile)
414            except error:
415                pass
416        return rc
417
418    # Alias
419    __del__ = close
420
421def popen(cmd, mode='r', bufsize=None):
422
423    """ Portable popen() interface.
424    """
425    # Find a working popen implementation preferring win32pipe.popen
426    # over os.popen over _popen
427    popen = None
428    if os.environ.get('OS','') == 'Windows_NT':
429        # On NT win32pipe should work; on Win9x it hangs due to bugs
430        # in the MS C lib (see MS KnowledgeBase article Q150956)
431        try:
432            import win32pipe
433        except ImportError:
434            pass
435        else:
436            popen = win32pipe.popen
437    if popen is None:
438        if hasattr(os,'popen'):
439            popen = os.popen
440            # Check whether it works... it doesn't in GUI programs
441            # on Windows platforms
442            if sys.platform == 'win32': # XXX Others too ?
443                try:
444                    popen('')
445                except os.error:
446                    popen = _popen
447        else:
448            popen = _popen
449    if bufsize is None:
450        return popen(cmd,mode)
451    else:
452        return popen(cmd,mode,bufsize)
453
454def _norm_version(version, build=''):
455
456    """ Normalize the version and build strings and return a single
457        version string using the format major.minor.build (or patchlevel).
458    """
459    l = string.split(version,'.')
460    if build:
461        l.append(build)
462    try:
463        ints = map(int,l)
464    except ValueError:
465        strings = l
466    else:
467        strings = map(str,ints)
468    version = string.join(strings[:3],'.')
469    return version
470
471_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
472                         '.*'
473                         '\[.* ([\d.]+)\])')
474
475# Examples of VER command output:
476#
477#   Windows 2000:  Microsoft Windows 2000 [Version 5.00.2195]
478#   Windows XP:    Microsoft Windows XP [Version 5.1.2600]
479#   Windows Vista: Microsoft Windows [Version 6.0.6002]
480#
481# Note that the "Version" string gets localized on different
482# Windows versions.
483
484def _syscmd_ver(system='', release='', version='',
485
486               supported_platforms=('win32','win16','dos','os2')):
487
488    """ Tries to figure out the OS version used and returns
489        a tuple (system,release,version).
490
491        It uses the "ver" shell command for this which is known
492        to exists on Windows, DOS and OS/2. XXX Others too ?
493
494        In case this fails, the given parameters are used as
495        defaults.
496
497    """
498    if sys.platform not in supported_platforms:
499        return system,release,version
500
501    # Try some common cmd strings
502    for cmd in ('ver','command /c ver','cmd /c ver'):
503        try:
504            pipe = popen(cmd)
505            info = pipe.read()
506            if pipe.close():
507                raise os.error,'command failed'
508            # XXX How can I suppress shell errors from being written
509            #     to stderr ?
510        except os.error,why:
511            #print 'Command %s failed: %s' % (cmd,why)
512            continue
513        except IOError,why:
514            #print 'Command %s failed: %s' % (cmd,why)
515            continue
516        else:
517            break
518    else:
519        return system,release,version
520
521    # Parse the output
522    info = string.strip(info)
523    m = _ver_output.match(info)
524    if m is not None:
525        system,release,version = m.groups()
526        # Strip trailing dots from version and release
527        if release[-1] == '.':
528            release = release[:-1]
529        if version[-1] == '.':
530            version = version[:-1]
531        # Normalize the version and build strings (eliminating additional
532        # zeros)
533        version = _norm_version(version)
534    return system,release,version
535
536_WIN32_CLIENT_RELEASES = {
537    (5, 0): "2000",
538    (5, 1): "XP",
539    # Strictly, 5.2 client is XP 64-bit, but platform.py historically
540    # has always called it 2003 Server
541    (5, 2): "2003Server",
542    (5, None): "post2003",
543
544    (6, 0): "Vista",
545    (6, 1): "7",
546    (6, 2): "8",
547    (6, 3): "8.1",
548    (6, None): "post8.1",
549
550    (10, 0): "10",
551    (10, None): "post10",
552}
553
554# Server release name lookup will default to client names if necessary
555_WIN32_SERVER_RELEASES = {
556    (5, 2): "2003Server",
557
558    (6, 0): "2008Server",
559    (6, 1): "2008ServerR2",
560    (6, 2): "2012Server",
561    (6, 3): "2012ServerR2",
562    (6, None): "post2012ServerR2",
563}
564
565def _get_real_winver(maj, min, build):
566    if maj < 6 or (maj == 6 and min < 2):
567        return maj, min, build
568
569    from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer,
570                        Structure, WinDLL, _Pointer)
571    from ctypes.wintypes import DWORD, HANDLE
572
573    class VS_FIXEDFILEINFO(Structure):
574        _fields_ = [
575            ("dwSignature", DWORD),
576            ("dwStrucVersion", DWORD),
577            ("dwFileVersionMS", DWORD),
578            ("dwFileVersionLS", DWORD),
579            ("dwProductVersionMS", DWORD),
580            ("dwProductVersionLS", DWORD),
581            ("dwFileFlagsMask", DWORD),
582            ("dwFileFlags", DWORD),
583            ("dwFileOS", DWORD),
584            ("dwFileType", DWORD),
585            ("dwFileSubtype", DWORD),
586            ("dwFileDateMS", DWORD),
587            ("dwFileDateLS", DWORD),
588        ]
589    class PVS_FIXEDFILEINFO(_Pointer):
590        _type_ = VS_FIXEDFILEINFO
591
592    kernel32 = WinDLL('kernel32')
593    version = WinDLL('version')
594
595    # We will immediately double the length up to MAX_PATH, but the
596    # path may be longer, so we retry until the returned string is
597    # shorter than our buffer.
598    name_len = actual_len = 130
599    while actual_len == name_len:
600        name_len *= 2
601        name = create_unicode_buffer(name_len)
602        actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle),
603                                                 name, len(name))
604        if not actual_len:
605            return maj, min, build
606
607    size = version.GetFileVersionInfoSizeW(name, None)
608    if not size:
609        return maj, min, build
610
611    ver_block = c_buffer(size)
612    if (not version.GetFileVersionInfoW(name, None, size, ver_block) or
613        not ver_block):
614        return maj, min, build
615
616    pvi = PVS_FIXEDFILEINFO()
617    if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())):
618        return maj, min, build
619
620    maj = pvi.contents.dwProductVersionMS >> 16
621    min = pvi.contents.dwProductVersionMS & 0xFFFF
622    build = pvi.contents.dwProductVersionLS >> 16
623
624    return maj, min, build
625
626def win32_ver(release='', version='', csd='', ptype=''):
627    try:
628        from sys import getwindowsversion
629    except ImportError:
630        return release, version, csd, ptype
631    try:
632        from winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
633    except ImportError:
634        from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
635
636    winver = getwindowsversion()
637    maj, min, build = _get_real_winver(*winver[:3])
638    version = '{0}.{1}.{2}'.format(maj, min, build)
639
640    release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
641               _WIN32_CLIENT_RELEASES.get((maj, None)) or
642               release)
643
644    # getwindowsversion() reflect the compatibility mode Python is
645    # running under, and so the service pack value is only going to be
646    # valid if the versions match.
647    if winver[:2] == (maj, min):
648        try:
649            csd = 'SP{}'.format(winver.service_pack_major)
650        except AttributeError:
651            if csd[:13] == 'Service Pack ':
652                csd = 'SP' + csd[13:]
653
654    # VER_NT_SERVER = 3
655    if getattr(winver, 'product_type', None) == 3:
656        release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
657                   _WIN32_SERVER_RELEASES.get((maj, None)) or
658                   release)
659
660    key = None
661    try:
662        key = OpenKeyEx(HKEY_LOCAL_MACHINE,
663                        r'SOFTWARE\Microsoft\Windows NT\CurrentVersion')
664        ptype = QueryValueEx(key, 'CurrentType')[0]
665    except:
666        pass
667    finally:
668        if key:
669            CloseKey(key)
670
671    return release, version, csd, ptype
672
673def _mac_ver_lookup(selectors,default=None):
674
675    from gestalt import gestalt
676    import MacOS
677    l = []
678    append = l.append
679    for selector in selectors:
680        try:
681            append(gestalt(selector))
682        except (RuntimeError, MacOS.Error):
683            append(default)
684    return l
685
686def _bcd2str(bcd):
687
688    return hex(bcd)[2:]
689
690def _mac_ver_gestalt():
691    """
692        Thanks to Mark R. Levinson for mailing documentation links and
693        code examples for this function. Documentation for the
694        gestalt() API is available online at:
695
696           http://www.rgaros.nl/gestalt/
697    """
698    # Check whether the version info module is available
699    try:
700        import gestalt
701        import MacOS
702    except ImportError:
703        return None
704    # Get the infos
705    sysv,sysa = _mac_ver_lookup(('sysv','sysa'))
706    # Decode the infos
707    if sysv:
708        major = (sysv & 0xFF00) >> 8
709        minor = (sysv & 0x00F0) >> 4
710        patch = (sysv & 0x000F)
711
712        if (major, minor) >= (10, 4):
713            # the 'sysv' gestald cannot return patchlevels
714            # higher than 9. Apple introduced 3 new
715            # gestalt codes in 10.4 to deal with this
716            # issue (needed because patch levels can
717            # run higher than 9, such as 10.4.11)
718            major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
719            release = '%i.%i.%i' %(major, minor, patch)
720        else:
721            release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
722
723    if sysa:
724        machine = {0x1: '68k',
725                   0x2: 'PowerPC',
726                   0xa: 'i386'}.get(sysa,'')
727
728    versioninfo=('', '', '')
729    return release,versioninfo,machine
730
731def _mac_ver_xml():
732    fn = '/System/Library/CoreServices/SystemVersion.plist'
733    if not os.path.exists(fn):
734        return None
735
736    try:
737        import plistlib
738    except ImportError:
739        return None
740
741    pl = plistlib.readPlist(fn)
742    release = pl['ProductVersion']
743    versioninfo=('', '', '')
744    machine = os.uname()[4]
745    if machine in ('ppc', 'Power Macintosh'):
746        # for compatibility with the gestalt based code
747        machine = 'PowerPC'
748
749    return release,versioninfo,machine
750
751
752def mac_ver(release='',versioninfo=('','',''),machine=''):
753
754    """ Get MacOS version information and return it as tuple (release,
755        versioninfo, machine) with versioninfo being a tuple (version,
756        dev_stage, non_release_version).
757
758        Entries which cannot be determined are set to the parameter values
759        which default to ''. All tuple entries are strings.
760    """
761
762    # First try reading the information from an XML file which should
763    # always be present
764    info = _mac_ver_xml()
765    if info is not None:
766        return info
767
768    # If that doesn't work for some reason fall back to reading the
769    # information using gestalt calls.
770    info = _mac_ver_gestalt()
771    if info is not None:
772        return info
773
774    # If that also doesn't work return the default values
775    return release,versioninfo,machine
776
777def _java_getprop(name,default):
778
779    from java.lang import System
780    try:
781        value = System.getProperty(name)
782        if value is None:
783            return default
784        return value
785    except AttributeError:
786        return default
787
788def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
789
790    """ Version interface for Jython.
791
792        Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
793        a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
794        tuple (os_name,os_version,os_arch).
795
796        Values which cannot be determined are set to the defaults
797        given as parameters (which all default to '').
798
799    """
800    # Import the needed APIs
801    try:
802        import java.lang
803    except ImportError:
804        return release,vendor,vminfo,osinfo
805
806    vendor = _java_getprop('java.vendor', vendor)
807    release = _java_getprop('java.version', release)
808    vm_name, vm_release, vm_vendor = vminfo
809    vm_name = _java_getprop('java.vm.name', vm_name)
810    vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
811    vm_release = _java_getprop('java.vm.version', vm_release)
812    vminfo = vm_name, vm_release, vm_vendor
813    os_name, os_version, os_arch = osinfo
814    os_arch = _java_getprop('java.os.arch', os_arch)
815    os_name = _java_getprop('java.os.name', os_name)
816    os_version = _java_getprop('java.os.version', os_version)
817    osinfo = os_name, os_version, os_arch
818
819    return release, vendor, vminfo, osinfo
820
821### System name aliasing
822
823def system_alias(system,release,version):
824
825    """ Returns (system,release,version) aliased to common
826        marketing names used for some systems.
827
828        It also does some reordering of the information in some cases
829        where it would otherwise cause confusion.
830
831    """
832    if system == 'Rhapsody':
833        # Apple's BSD derivative
834        # XXX How can we determine the marketing release number ?
835        return 'MacOS X Server',system+release,version
836
837    elif system == 'SunOS':
838        # Sun's OS
839        if release < '5':
840            # These releases use the old name SunOS
841            return system,release,version
842        # Modify release (marketing release = SunOS release - 3)
843        l = string.split(release,'.')
844        if l:
845            try:
846                major = int(l[0])
847            except ValueError:
848                pass
849            else:
850                major = major - 3
851                l[0] = str(major)
852                release = string.join(l,'.')
853        if release < '6':
854            system = 'Solaris'
855        else:
856            # XXX Whatever the new SunOS marketing name is...
857            system = 'Solaris'
858
859    elif system == 'IRIX64':
860        # IRIX reports IRIX64 on platforms with 64-bit support; yet it
861        # is really a version and not a different platform, since 32-bit
862        # apps are also supported..
863        system = 'IRIX'
864        if version:
865            version = version + ' (64bit)'
866        else:
867            version = '64bit'
868
869    elif system in ('win32','win16'):
870        # In case one of the other tricks
871        system = 'Windows'
872
873    return system,release,version
874
875### Various internal helpers
876
877def _platform(*args):
878
879    """ Helper to format the platform string in a filename
880        compatible format e.g. "system-version-machine".
881    """
882    # Format the platform string
883    platform = string.join(
884        map(string.strip,
885            filter(len, args)),
886        '-')
887
888    # Cleanup some possible filename obstacles...
889    replace = string.replace
890    platform = replace(platform,' ','_')
891    platform = replace(platform,'/','-')
892    platform = replace(platform,'\\','-')
893    platform = replace(platform,':','-')
894    platform = replace(platform,';','-')
895    platform = replace(platform,'"','-')
896    platform = replace(platform,'(','-')
897    platform = replace(platform,')','-')
898
899    # No need to report 'unknown' information...
900    platform = replace(platform,'unknown','')
901
902    # Fold '--'s and remove trailing '-'
903    while 1:
904        cleaned = replace(platform,'--','-')
905        if cleaned == platform:
906            break
907        platform = cleaned
908    while platform[-1] == '-':
909        platform = platform[:-1]
910
911    return platform
912
913def _node(default=''):
914
915    """ Helper to determine the node name of this machine.
916    """
917    try:
918        import socket
919    except ImportError:
920        # No sockets...
921        return default
922    try:
923        return socket.gethostname()
924    except socket.error:
925        # Still not working...
926        return default
927
928# os.path.abspath is new in Python 1.5.2:
929if not hasattr(os.path,'abspath'):
930
931    def _abspath(path,
932
933                 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
934                 normpath=os.path.normpath):
935
936        if not isabs(path):
937            path = join(getcwd(), path)
938        return normpath(path)
939
940else:
941
942    _abspath = os.path.abspath
943
944def _follow_symlinks(filepath):
945
946    """ In case filepath is a symlink, follow it until a
947        real file is reached.
948    """
949    filepath = _abspath(filepath)
950    while os.path.islink(filepath):
951        filepath = os.path.normpath(
952            os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
953    return filepath
954
955def _syscmd_uname(option,default=''):
956
957    """ Interface to the system's uname command.
958    """
959    if sys.platform in ('dos','win32','win16','os2'):
960        # XXX Others too ?
961        return default
962    try:
963        f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
964    except (AttributeError,os.error):
965        return default
966    output = string.strip(f.read())
967    rc = f.close()
968    if not output or rc:
969        return default
970    else:
971        return output
972
973def _syscmd_file(target,default=''):
974
975    """ Interface to the system's file command.
976
977        The function uses the -b option of the file command to have it
978        ommit the filename in its output and if possible the -L option
979        to have the command follow symlinks. It returns default in
980        case the command should fail.
981
982    """
983
984    # We do the import here to avoid a bootstrap issue.
985    # See c73b90b6dadd changeset.
986    #
987    # [..]
988    # ranlib libpython2.7.a
989    # gcc   -o python \
990    #        Modules/python.o \
991    #        libpython2.7.a -lsocket -lnsl -ldl    -lm
992    # Traceback (most recent call last):
993    #  File "./setup.py", line 8, in <module>
994    #    from platform import machine as platform_machine
995    #  File "[..]/build/Lib/platform.py", line 116, in <module>
996    #    import sys,string,os,re,subprocess
997    #  File "[..]/build/Lib/subprocess.py", line 429, in <module>
998    #    import select
999    # ImportError: No module named select
1000
1001    import subprocess
1002
1003    if sys.platform in ('dos','win32','win16','os2'):
1004        # XXX Others too ?
1005        return default
1006    target = _follow_symlinks(target)
1007    try:
1008        proc = subprocess.Popen(['file', target],
1009                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1010
1011    except (AttributeError,os.error):
1012        return default
1013    output = proc.communicate()[0]
1014    rc = proc.wait()
1015    if not output or rc:
1016        return default
1017    else:
1018        return output
1019
1020### Information about the used architecture
1021
1022# Default values for architecture; non-empty strings override the
1023# defaults given as parameters
1024_default_architecture = {
1025    'win32': ('','WindowsPE'),
1026    'win16': ('','Windows'),
1027    'dos': ('','MSDOS'),
1028}
1029
1030_architecture_split = re.compile(r'[\s,]').split
1031
1032def architecture(executable=sys.executable,bits='',linkage=''):
1033
1034    """ Queries the given executable (defaults to the Python interpreter
1035        binary) for various architecture information.
1036
1037        Returns a tuple (bits,linkage) which contains information about
1038        the bit architecture and the linkage format used for the
1039        executable. Both values are returned as strings.
1040
1041        Values that cannot be determined are returned as given by the
1042        parameter presets. If bits is given as '', the sizeof(pointer)
1043        (or sizeof(long) on Python version < 1.5.2) is used as
1044        indicator for the supported pointer size.
1045
1046        The function relies on the system's "file" command to do the
1047        actual work. This is available on most if not all Unix
1048        platforms. On some non-Unix platforms where the "file" command
1049        does not exist and the executable is set to the Python interpreter
1050        binary defaults from _default_architecture are used.
1051
1052    """
1053    # Use the sizeof(pointer) as default number of bits if nothing
1054    # else is given as default.
1055    if not bits:
1056        import struct
1057        try:
1058            size = struct.calcsize('P')
1059        except struct.error:
1060            # Older installations can only query longs
1061            size = struct.calcsize('l')
1062        bits = str(size*8) + 'bit'
1063
1064    # Get data from the 'file' system command
1065    if executable:
1066        output = _syscmd_file(executable, '')
1067    else:
1068        output = ''
1069
1070    if not output and \
1071       executable == sys.executable:
1072        # "file" command did not return anything; we'll try to provide
1073        # some sensible defaults then...
1074        if sys.platform in _default_architecture:
1075            b, l = _default_architecture[sys.platform]
1076            if b:
1077                bits = b
1078            if l:
1079                linkage = l
1080        return bits, linkage
1081
1082    # Split the output into a list of strings omitting the filename
1083    fileout = _architecture_split(output)[1:]
1084
1085    if 'executable' not in fileout:
1086        # Format not supported
1087        return bits,linkage
1088
1089    # Bits
1090    if '32-bit' in fileout:
1091        bits = '32bit'
1092    elif 'N32' in fileout:
1093        # On Irix only
1094        bits = 'n32bit'
1095    elif '64-bit' in fileout:
1096        bits = '64bit'
1097
1098    # Linkage
1099    if 'ELF' in fileout:
1100        linkage = 'ELF'
1101    elif 'PE' in fileout:
1102        # E.g. Windows uses this format
1103        if 'Windows' in fileout:
1104            linkage = 'WindowsPE'
1105        else:
1106            linkage = 'PE'
1107    elif 'COFF' in fileout:
1108        linkage = 'COFF'
1109    elif 'MS-DOS' in fileout:
1110        linkage = 'MSDOS'
1111    else:
1112        # XXX the A.OUT format also falls under this class...
1113        pass
1114
1115    return bits,linkage
1116
1117### Portable uname() interface
1118
1119_uname_cache = None
1120
1121def uname():
1122
1123    """ Fairly portable uname interface. Returns a tuple
1124        of strings (system,node,release,version,machine,processor)
1125        identifying the underlying platform.
1126
1127        Note that unlike the os.uname function this also returns
1128        possible processor information as an additional tuple entry.
1129
1130        Entries which cannot be determined are set to ''.
1131
1132    """
1133    global _uname_cache
1134    no_os_uname = 0
1135
1136    if _uname_cache is not None:
1137        return _uname_cache
1138
1139    processor = ''
1140
1141    # Get some infos from the builtin os.uname API...
1142    try:
1143        system,node,release,version,machine = os.uname()
1144    except AttributeError:
1145        no_os_uname = 1
1146
1147    if no_os_uname or not filter(None, (system, node, release, version, machine)):
1148        # Hmm, no there is either no uname or uname has returned
1149        #'unknowns'... we'll have to poke around the system then.
1150        if no_os_uname:
1151            system = sys.platform
1152            release = ''
1153            version = ''
1154            node = _node()
1155            machine = ''
1156
1157        use_syscmd_ver = 1
1158
1159        # Try win32_ver() on win32 platforms
1160        if system == 'win32':
1161            release,version,csd,ptype = win32_ver()
1162            if release and version:
1163                use_syscmd_ver = 0
1164            # Try to use the PROCESSOR_* environment variables
1165            # available on Win XP and later; see
1166            # http://support.microsoft.com/kb/888731 and
1167            # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
1168            if not machine:
1169                # WOW64 processes mask the native architecture
1170                if "PROCESSOR_ARCHITEW6432" in os.environ:
1171                    machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
1172                else:
1173                    machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
1174            if not processor:
1175                processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
1176
1177        # Try the 'ver' system command available on some
1178        # platforms
1179        if use_syscmd_ver:
1180            system,release,version = _syscmd_ver(system)
1181            # Normalize system to what win32_ver() normally returns
1182            # (_syscmd_ver() tends to return the vendor name as well)
1183            if system == 'Microsoft Windows':
1184                system = 'Windows'
1185            elif system == 'Microsoft' and release == 'Windows':
1186                # Under Windows Vista and Windows Server 2008,
1187                # Microsoft changed the output of the ver command. The
1188                # release is no longer printed.  This causes the
1189                # system and release to be misidentified.
1190                system = 'Windows'
1191                if '6.0' == version[:3]:
1192                    release = 'Vista'
1193                else:
1194                    release = ''
1195
1196        # In case we still don't know anything useful, we'll try to
1197        # help ourselves
1198        if system in ('win32','win16'):
1199            if not version:
1200                if system == 'win32':
1201                    version = '32bit'
1202                else:
1203                    version = '16bit'
1204            system = 'Windows'
1205
1206        elif system[:4] == 'java':
1207            release,vendor,vminfo,osinfo = java_ver()
1208            system = 'Java'
1209            version = string.join(vminfo,', ')
1210            if not version:
1211                version = vendor
1212
1213    # System specific extensions
1214    if system == 'OpenVMS':
1215        # OpenVMS seems to have release and version mixed up
1216        if not release or release == '0':
1217            release = version
1218            version = ''
1219        # Get processor information
1220        try:
1221            import vms_lib
1222        except ImportError:
1223            pass
1224        else:
1225            csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
1226            if (cpu_number >= 128):
1227                processor = 'Alpha'
1228            else:
1229                processor = 'VAX'
1230    if not processor:
1231        # Get processor information from the uname system command
1232        processor = _syscmd_uname('-p','')
1233
1234    #If any unknowns still exist, replace them with ''s, which are more portable
1235    if system == 'unknown':
1236        system = ''
1237    if node == 'unknown':
1238        node = ''
1239    if release == 'unknown':
1240        release = ''
1241    if version == 'unknown':
1242        version = ''
1243    if machine == 'unknown':
1244        machine = ''
1245    if processor == 'unknown':
1246        processor = ''
1247
1248    #  normalize name
1249    if system == 'Microsoft' and release == 'Windows':
1250        system = 'Windows'
1251        release = 'Vista'
1252
1253    _uname_cache = system,node,release,version,machine,processor
1254    return _uname_cache
1255
1256### Direct interfaces to some of the uname() return values
1257
1258def system():
1259
1260    """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
1261
1262        An empty string is returned if the value cannot be determined.
1263
1264    """
1265    return uname()[0]
1266
1267def node():
1268
1269    """ Returns the computer's network name (which may not be fully
1270        qualified)
1271
1272        An empty string is returned if the value cannot be determined.
1273
1274    """
1275    return uname()[1]
1276
1277def release():
1278
1279    """ Returns the system's release, e.g. '2.2.0' or 'NT'
1280
1281        An empty string is returned if the value cannot be determined.
1282
1283    """
1284    return uname()[2]
1285
1286def version():
1287
1288    """ Returns the system's release version, e.g. '#3 on degas'
1289
1290        An empty string is returned if the value cannot be determined.
1291
1292    """
1293    return uname()[3]
1294
1295def machine():
1296
1297    """ Returns the machine type, e.g. 'i386'
1298
1299        An empty string is returned if the value cannot be determined.
1300
1301    """
1302    return uname()[4]
1303
1304def processor():
1305
1306    """ Returns the (true) processor name, e.g. 'amdk6'
1307
1308        An empty string is returned if the value cannot be
1309        determined. Note that many platforms do not provide this
1310        information or simply return the same value as for machine(),
1311        e.g.  NetBSD does this.
1312
1313    """
1314    return uname()[5]
1315
1316### Various APIs for extracting information from sys.version
1317
1318_sys_version_parser = re.compile(
1319    r'([\w.+]+)\s*'  # "version<space>"
1320    r'\(#?([^,]+)'  # "(#buildno"
1321    r'(?:,\s*([\w ]*)'  # ", builddate"
1322    r'(?:,\s*([\w :]*))?)?\)\s*'  # ", buildtime)<space>"
1323    r'\[([^\]]+)\]?')  # "[compiler]"
1324
1325_ironpython_sys_version_parser = re.compile(
1326    r'IronPython\s*'
1327    '([\d\.]+)'
1328    '(?: \(([\d\.]+)\))?'
1329    ' on (.NET [\d\.]+)')
1330
1331# IronPython covering 2.6 and 2.7
1332_ironpython26_sys_version_parser = re.compile(
1333    r'([\d.]+)\s*'
1334    '\(IronPython\s*'
1335    '[\d.]+\s*'
1336    '\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
1337)
1338
1339_pypy_sys_version_parser = re.compile(
1340    r'([\w.+]+)\s*'
1341    '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1342    '\[PyPy [^\]]+\]?')
1343
1344_sys_version_cache = {}
1345
1346def _sys_version(sys_version=None):
1347
1348    """ Returns a parsed version of Python's sys.version as tuple
1349        (name, version, branch, revision, buildno, builddate, compiler)
1350        referring to the Python implementation name, version, branch,
1351        revision, build number, build date/time as string and the compiler
1352        identification string.
1353
1354        Note that unlike the Python sys.version, the returned value
1355        for the Python version will always include the patchlevel (it
1356        defaults to '.0').
1357
1358        The function returns empty strings for tuple entries that
1359        cannot be determined.
1360
1361        sys_version may be given to parse an alternative version
1362        string, e.g. if the version was read from a different Python
1363        interpreter.
1364
1365    """
1366    # Get the Python version
1367    if sys_version is None:
1368        sys_version = sys.version
1369
1370    # Try the cache first
1371    result = _sys_version_cache.get(sys_version, None)
1372    if result is not None:
1373        return result
1374
1375    # Parse it
1376    if 'IronPython' in sys_version:
1377        # IronPython
1378        name = 'IronPython'
1379        if sys_version.startswith('IronPython'):
1380            match = _ironpython_sys_version_parser.match(sys_version)
1381        else:
1382            match = _ironpython26_sys_version_parser.match(sys_version)
1383
1384        if match is None:
1385            raise ValueError(
1386                'failed to parse IronPython sys.version: %s' %
1387                repr(sys_version))
1388
1389        version, alt_version, compiler = match.groups()
1390        buildno = ''
1391        builddate = ''
1392
1393    elif sys.platform.startswith('java'):
1394        # Jython
1395        name = 'Jython'
1396        match = _sys_version_parser.match(sys_version)
1397        if match is None:
1398            raise ValueError(
1399                'failed to parse Jython sys.version: %s' %
1400                repr(sys_version))
1401        version, buildno, builddate, buildtime, _ = match.groups()
1402        if builddate is None:
1403            builddate = ''
1404        compiler = sys.platform
1405
1406    elif "PyPy" in sys_version:
1407        # PyPy
1408        name = "PyPy"
1409        match = _pypy_sys_version_parser.match(sys_version)
1410        if match is None:
1411            raise ValueError("failed to parse PyPy sys.version: %s" %
1412                             repr(sys_version))
1413        version, buildno, builddate, buildtime = match.groups()
1414        compiler = ""
1415
1416    else:
1417        # CPython
1418        match = _sys_version_parser.match(sys_version)
1419        if match is None:
1420            raise ValueError(
1421                'failed to parse CPython sys.version: %s' %
1422                repr(sys_version))
1423        version, buildno, builddate, buildtime, compiler = \
1424              match.groups()
1425        name = 'CPython'
1426        if builddate is None:
1427            builddate = ''
1428        elif buildtime:
1429            builddate = builddate + ' ' + buildtime
1430
1431    if hasattr(sys, 'subversion'):
1432        # sys.subversion was added in Python 2.5
1433        _, branch, revision = sys.subversion
1434    else:
1435        branch = ''
1436        revision = ''
1437
1438    # Add the patchlevel version if missing
1439    l = string.split(version, '.')
1440    if len(l) == 2:
1441        l.append('0')
1442        version = string.join(l, '.')
1443
1444    # Build and cache the result
1445    result = (name, version, branch, revision, buildno, builddate, compiler)
1446    _sys_version_cache[sys_version] = result
1447    return result
1448
1449def python_implementation():
1450
1451    """ Returns a string identifying the Python implementation.
1452
1453        Currently, the following implementations are identified:
1454          'CPython' (C implementation of Python),
1455          'IronPython' (.NET implementation of Python),
1456          'Jython' (Java implementation of Python),
1457          'PyPy' (Python implementation of Python).
1458
1459    """
1460    return _sys_version()[0]
1461
1462def python_version():
1463
1464    """ Returns the Python version as string 'major.minor.patchlevel'
1465
1466        Note that unlike the Python sys.version, the returned value
1467        will always include the patchlevel (it defaults to 0).
1468
1469    """
1470    return _sys_version()[1]
1471
1472def python_version_tuple():
1473
1474    """ Returns the Python version as tuple (major, minor, patchlevel)
1475        of strings.
1476
1477        Note that unlike the Python sys.version, the returned value
1478        will always include the patchlevel (it defaults to 0).
1479
1480    """
1481    return tuple(string.split(_sys_version()[1], '.'))
1482
1483def python_branch():
1484
1485    """ Returns a string identifying the Python implementation
1486        branch.
1487
1488        For CPython this is the Subversion branch from which the
1489        Python binary was built.
1490
1491        If not available, an empty string is returned.
1492
1493    """
1494
1495    return _sys_version()[2]
1496
1497def python_revision():
1498
1499    """ Returns a string identifying the Python implementation
1500        revision.
1501
1502        For CPython this is the Subversion revision from which the
1503        Python binary was built.
1504
1505        If not available, an empty string is returned.
1506
1507    """
1508    return _sys_version()[3]
1509
1510def python_build():
1511
1512    """ Returns a tuple (buildno, builddate) stating the Python
1513        build number and date as strings.
1514
1515    """
1516    return _sys_version()[4:6]
1517
1518def python_compiler():
1519
1520    """ Returns a string identifying the compiler used for compiling
1521        Python.
1522
1523    """
1524    return _sys_version()[6]
1525
1526### The Opus Magnum of platform strings :-)
1527
1528_platform_cache = {}
1529
1530def platform(aliased=0, terse=0):
1531
1532    """ Returns a single string identifying the underlying platform
1533        with as much useful information as possible (but no more :).
1534
1535        The output is intended to be human readable rather than
1536        machine parseable. It may look different on different
1537        platforms and this is intended.
1538
1539        If "aliased" is true, the function will use aliases for
1540        various platforms that report system names which differ from
1541        their common names, e.g. SunOS will be reported as
1542        Solaris. The system_alias() function is used to implement
1543        this.
1544
1545        Setting terse to true causes the function to return only the
1546        absolute minimum information needed to identify the platform.
1547
1548    """
1549    result = _platform_cache.get((aliased, terse), None)
1550    if result is not None:
1551        return result
1552
1553    # Get uname information and then apply platform specific cosmetics
1554    # to it...
1555    system,node,release,version,machine,processor = uname()
1556    if machine == processor:
1557        processor = ''
1558    if aliased:
1559        system,release,version = system_alias(system,release,version)
1560
1561    if system == 'Windows':
1562        # MS platforms
1563        rel,vers,csd,ptype = win32_ver(version)
1564        if terse:
1565            platform = _platform(system,release)
1566        else:
1567            platform = _platform(system,release,version,csd)
1568
1569    elif system in ('Linux',):
1570        # Linux based systems
1571        distname,distversion,distid = dist('')
1572        if distname and not terse:
1573            platform = _platform(system,release,machine,processor,
1574                                 'with',
1575                                 distname,distversion,distid)
1576        else:
1577            # If the distribution name is unknown check for libc vs. glibc
1578            libcname,libcversion = libc_ver(sys.executable)
1579            platform = _platform(system,release,machine,processor,
1580                                 'with',
1581                                 libcname+libcversion)
1582    elif system == 'Java':
1583        # Java platforms
1584        r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
1585        if terse or not os_name:
1586            platform = _platform(system,release,version)
1587        else:
1588            platform = _platform(system,release,version,
1589                                 'on',
1590                                 os_name,os_version,os_arch)
1591
1592    elif system == 'MacOS':
1593        # MacOS platforms
1594        if terse:
1595            platform = _platform(system,release)
1596        else:
1597            platform = _platform(system,release,machine)
1598
1599    else:
1600        # Generic handler
1601        if terse:
1602            platform = _platform(system,release)
1603        else:
1604            bits,linkage = architecture(sys.executable)
1605            platform = _platform(system,release,machine,processor,bits,linkage)
1606
1607    _platform_cache[(aliased, terse)] = platform
1608    return platform
1609
1610### Command line interface
1611
1612if __name__ == '__main__':
1613    # Default is to print the aliased verbose platform string
1614    terse = ('terse' in sys.argv or '--terse' in sys.argv)
1615    aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
1616    print platform(aliased,terse)
1617    sys.exit(0)
1618