@@ -302,7 +302,7 @@ class ContextType(object):
302302 >>> context.os == 'linux'
303303 True
304304 >>> context.arch = 'arm'
305- >>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux'}
305+ >>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux', 'newline': b'\n' }
306306 True
307307 >>> context.endian
308308 'little'
@@ -376,8 +376,19 @@ class ContextType(object):
376376 'timeout' : Timeout .maximum ,
377377 }
378378
379- #: Valid values for :meth:`pwnlib.context.ContextType.os`
380- oses = sorted (('linux' ,'freebsd' ,'windows' ,'cgc' ,'android' ,'baremetal' ,'darwin' ))
379+ unix_like = {'newline' : b'\n ' }
380+ windows_like = {'newline' : b'\r \n ' }
381+
382+ #: Keys are valid values for :meth:`pwnlib.context.ContextType.os`
383+ oses = _longest ({
384+ 'linux' : unix_like ,
385+ 'freebsd' : unix_like ,
386+ 'windows' : windows_like ,
387+ 'cgc' : unix_like ,
388+ 'android' : unix_like ,
389+ 'baremetal' : unix_like ,
390+ 'darwin' : unix_like ,
391+ })
381392
382393 big_32 = {'endian' : 'big' , 'bits' : 32 }
383394 big_64 = {'endian' : 'big' , 'bits' : 64 }
@@ -446,14 +457,14 @@ def __init__(self, **kwargs):
446457
447458
448459 def copy (self ):
449- """copy() -> dict
460+ r """copy() -> dict
450461 Returns a copy of the current context as a dictionary.
451462
452463 Examples:
453464
454465 >>> context.clear()
455466 >>> context.os = 'linux'
456- >>> vars(context) == {'os': 'linux'}
467+ >>> vars(context) == {'os': 'linux', 'newline': b'\n' }
457468 True
458469 """
459470 return self ._tls .copy ()
@@ -1104,25 +1115,73 @@ def mask(self):
11041115
11051116 @_validator
11061117 def os (self , os ):
1107- """
1118+ r """
11081119 Operating system of the target machine.
11091120
11101121 The default value is ``linux``.
11111122
11121123 Allowed values are listed in :attr:`pwnlib.context.ContextType.oses`.
11131124
1125+ Side Effects:
1126+
1127+ If an os is specified some attributes will be set on the context
1128+ if a user has not already set a value.
1129+
1130+ The following property may be modified:
1131+
1132+ - :attr:`newline`
1133+
1134+ Raises:
1135+ AttributeError: An invalid os was specified
1136+
11141137 Examples:
11151138
1116- >>> context.os = 'linux'
1139+ >>> context.clear()
1140+ >>> context.os == 'linux' # Default os
1141+ True
1142+
1143+ >>> context.os = 'freebsd'
1144+ >>> context.os == 'freebsd'
1145+ True
1146+
11171147 >>> context.os = 'foobar' #doctest: +ELLIPSIS
11181148 Traceback (most recent call last):
11191149 ...
11201150 AttributeError: os must be one of ['android', 'baremetal', 'cgc', 'freebsd', 'linux', 'windows']
1151+
1152+ >>> context.clear()
1153+ >>> context.newline == b'\n' # Default value
1154+ True
1155+ >>> context.os = 'windows'
1156+ >>> context.newline == b'\r\n' # New value
1157+ True
1158+
1159+ Note that expressly setting :attr:`newline` means that we use
1160+ that value instead of the default
1161+
1162+ >>> context.clear()
1163+ >>> context.newline = b'\n'
1164+ >>> context.os = 'windows'
1165+ >>> context.newline == b'\n'
1166+ True
1167+
1168+ Setting the os can override the default for :attr:`newline`
1169+
1170+ >>> context.clear()
1171+ >>> context.os = 'windows'
1172+ >>> vars(context) == {'os': 'windows', 'newline': b'\r\n'}
1173+ True
11211174 """
11221175 os = os .lower ()
11231176
1124- if os not in self .oses :
1125- raise AttributeError ("os must be one of %r" % self .oses )
1177+ try :
1178+ defaults = self .oses [os ]
1179+ except KeyError :
1180+ raise AttributeError ("os must be one of %r" % sorted (self .oses ))
1181+
1182+ for k ,v in defaults .items ():
1183+ if k not in self ._tls :
1184+ self ._tls [k ] = v
11261185
11271186 return os
11281187
0 commit comments