Skip to content

Simplify OSEnvironment #18038

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: Pharo13
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions src/System-OSEnvironments-Tests/OSEnvironmentTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,6 @@ OSEnvironmentTest >> testAtPut [
self deny: (env includesKey: key)
]

{ #category : 'tests' }
OSEnvironmentTest >> testEnvironmentFor [

| compareDictionary |

compareDictionary := ((Smalltalk hasClassNamed: #UnixEnvironment) and: [Smalltalk hasClassNamed: #Win32Environment])
ifTrue: [ "NativeBoost is present in the image"
{ MacOSPlatform -> #UnixEnvironment.
MacOSXPlatform -> #UnixEnvironment.
UnixPlatform -> #UnixEnvironment.
Win32Platform -> #Win32Environment } asDictionary ]
ifFalse: [ "NativeBoost is NOT present in the image"
{ MacOSPlatform -> #NullOSEnvironment.
MacOSXPlatform -> #NullOSEnvironment.
UnixPlatform -> #NullOSEnvironment.
Win32Platform -> #NullOSEnvironment } asDictionary ].

compareDictionary keysAndValuesDo: [ :platformClass :envClassName |
self
assert: (OSEnvironment environmentFor: platformClass new) class name
equals: envClassName ]
]

{ #category : 'tests' }
OSEnvironmentTest >> testKeys [
| env keys |
Expand Down
161 changes: 161 additions & 0 deletions src/System-OSEnvironments/AbstractUnixPlatform.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
Extension { #name : 'AbstractUnixPlatform' }

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> at: aKey encoding: anEncoding ifAbsent: aBlock [
"Gets the value of an environment variable called `aKey`.
Execute aBlock if absent.
Use `anEncoding` to encode the arguments and return values.

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

| result |
result := self rawAt: (aKey encodeWith: anEncoding) ifAbsent: [ ^ aBlock value ].
^ result decodeWith: anEncoding
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> at: aKey put: aValue encoding: anEncoding [
"Sets the value of an environment variable called `aKey` to `aValue`.
Use `anEncoding` to encode both arguments.

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

^ self rawAt: (aKey encodeWith: anEncoding) put: (aValue encodeWith: anEncoding)
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> basicGetEnvRaw: encodedVariableName [

"PRIVATE: This primitive call works on Strings, while the correct way to manage encodings is with raw data.
Use me through #rawAt: to correctly marshall data."

"Gets the value of an environment variable called `anEncodedVariableName` already encoded but in ByteString form."

<primitive: 'primitiveGetenv' module: '' error: ec>
ec ifNil: [ ^ (self basicGetEnvRawViaFFI: encodedVariableName asString) asByteArray].
self primitiveFail
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> basicGetEnvRawViaFFI: arg1 [

"PRIVATE: This FFI call works on Strings, while the correct way to manage encodings is with raw data.
Use me through #basicGetEnvRaw: to correctly marshall data."

"This method calls the Standard C Library getenv() function.
The name of the argument (arg1) should fit decompiled version."

^ self ffiCall: #( String getenv (String arg1) ) module: LibC
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> environ [
"Return the address of the array holding the environment variables"

^ FFIExternalArray fromPointer: (ExternalAddress loadSymbol: 'environ' from: LibC) type: String
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> environAt: index [

^ self environ at: index
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> environmentVariableNamed: aKey ifAbsent: aBlock [
"See super
Uses a single encoding determined dynamically"

^ self at: aKey encoding: self defaultEncoding ifAbsent: aBlock
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> environmentVariableNamed: aKey put: aValue [
"See super
Uses a single encoding determined dynamically"

^ self at: aKey put: aValue encoding: self defaultEncoding
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> environmentVariablesDo: aBlock [

| index associationString |
index := 1.
[
associationString := self environAt: index.
associationString ifNil: [ ^ self ].
self keysAndValuesDo: aBlock withAssociationString: associationString.
index := index + 1 ] repeat
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> rawAt: anEncodedKey ifAbsent: aBlock [
"Gets the value of an environment variable called `anEncodedKey` that is already encoded (i.e., it is a byte array).
Execute aBlock if absent.

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

| rawValue |
rawValue := self basicGetEnvRaw: anEncodedKey asString.
^ rawValue
ifNil: [ aBlock value ]
ifNotNil: [ rawValue asByteArray ]
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> rawAt: anEncodedKey put: someBytes [
"Sets the value of an environment variable called `anEncodedKey` to `someBytes`.
Both arguments should be already encoded (i.e., they are byte arrays).

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

^ self setEnv: anEncodedKey asString value: someBytes asString
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> rawRemoveKey: anEncodedKey [
"Removes an environment variable called `anEncodedKey` that is already encoded (i.e., it is a byte array).

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

^ self unsetEnv: anEncodedKey asString
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> removeEnvironmentVariableNamed: key [
"See super
Uses a single encoding determined dynamically"

^ self removeKey: key encoded: self defaultEncoding
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> removeKey: key encoded: anEncoding [
"Removes the entry `aKey` from the environment variables.
Use `anEncoding` to encode the arguments.

This is a *nix specific API.
Rationale: In *nix systems (compared to windows systems) environment variables are stored as raw bytes and can be encoded in different forms."

^ self rawRemoveKey: (key encodeWith: anEncoding)
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> setEnv: nameString value: valueString [
"int setenv(const char *name, const char *value, int overwrite);"

^ self ffiCall: #( int setenv #( String nameString #, String valueString #, int 1 ) ) module: LibC
]

{ #category : '*System-OSEnvironments' }
AbstractUnixPlatform >> unsetEnv: string [
"This method calls the the platform specific unset environment routine"

^ self ffiCall: #(int unsetenv #(String string)) module: LibC
]
46 changes: 0 additions & 46 deletions src/System-OSEnvironments/NullOSEnvironment.class.st

This file was deleted.

54 changes: 6 additions & 48 deletions src/System-OSEnvironments/OSEnvironment.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ In other words, methods
- #at:put:

and its variants receive normal strings and decide whether they have to encode those strings to platform bytes or not depending on the platform.

My subclasses may or may not provide additional APIs to have more control on the particular encoding used.
"
Class {
#name : 'OSEnvironment',
Expand All @@ -31,20 +29,7 @@ Class {

{ #category : 'instance creation' }
OSEnvironment class >> current [
^ Current ifNil: [ Current := self environmentFor: OSPlatform current ]
]

{ #category : 'private - accessing' }
OSEnvironment class >> environmentFor: aPlatform [
| environmentClass |

environmentClass := self allSubclasses
detect: [ :each | each isDefaultFor: aPlatform ]
ifNone: [ nil ].

^ (environmentClass isNotNil and: [ environmentClass isAvailable ])
ifTrue: [ environmentClass platform: aPlatform ]
ifFalse: [ NullOSEnvironment platform: aPlatform ]
^ Current ifNil: [ Current := self platform: OSPlatform current ]
]

{ #category : 'examples' }
Expand All @@ -59,18 +44,6 @@ OSEnvironment class >> initialize [
registerSystemClassNamed: self name
]

{ #category : 'testing' }
OSEnvironment class >> isAvailable [
self flag: #pharoTodo. "Replace this for a check of FFI available"
self environment at: #FFICalloutAPI ifAbsent: [ ^ false ].
^ true
]

{ #category : 'testing' }
OSEnvironment class >> isDefaultFor: aPlatform [
^ false
]

{ #category : 'instance creation' }
OSEnvironment class >> platform: anOSPlatform [
^ self basicNew initializeWith: anOSPlatform
Expand Down Expand Up @@ -126,7 +99,7 @@ OSEnvironment >> at: aKey ifAbsent: aBlock [
This is the common denominator API for all platforms.
Rationale: Windows does not (compared to *nix systems) provide a encoded byte representation of the value. Windows has instead its own wide string representation."

self subclassResponsibility
^ self platform environmentVariableNamed: aKey ifAbsent: aBlock
]

{ #category : 'accessing' }
Expand Down Expand Up @@ -175,7 +148,7 @@ OSEnvironment >> at: aKey put: aValue [
This is the common denominator API for all platforms.
Rationale: Windows does not (compared to *nix systems) provide a encoded byte representation of the value. Windows has instead its own wide string representation."

^ self subclassResponsibility
^ self platform environmentVariableNamed: aKey put: aValue
]

{ #category : 'enumeration' }
Expand Down Expand Up @@ -211,16 +184,8 @@ OSEnvironment >> keys [

{ #category : 'enumeration' }
OSEnvironment >> keysAndValuesDo: aBlock [
self subclassResponsibility
]

{ #category : 'enumeration' }
OSEnvironment >> keysAndValuesDo: aBlock withAssociationString: associationString [
| equalsIndex |
equalsIndex := associationString indexOf: $=.
aBlock
value: (associationString first: equalsIndex-1)
value: (associationString allButFirst: equalsIndex)
^ self platform environmentVariablesDo: aBlock
]

{ #category : 'enumeration' }
Expand All @@ -242,14 +207,14 @@ OSEnvironment >> removeKey: aKey [
This is the common denominator API for all platforms.
Rationale: Windows does not (compared to *nix systems) provide a encoded byte representation of the value. Windows has instead its own wide string representation."

^ self subclassResponsibility
^ self platform removeEnvironmentVariableNamed: aKey
]

{ #category : 'accessing' }
OSEnvironment >> setEnv: nameString value: valueString [
"This method calls the the platform specific set environment routine"

^ self subclassResponsibility
^ self platform setEnv: nameString value: valueString
]

{ #category : 'accessing' }
Expand All @@ -265,13 +230,6 @@ OSEnvironment >> setEnv: nameString value: valueString during: aBlock [
ifNotNil: [ self setEnv: nameString value: oldValue ] ]
]

{ #category : 'accessing' }
OSEnvironment >> unsetEnv: string [
"This method calls the the platform specific unset environment routine"

^ self ffiCall: #(int unsetenv #(String string)) module: LibC
]

{ #category : 'accessing' }
OSEnvironment >> values [
"Answer a Collection containing the receiver's values."
Expand Down
Loading