VM Maker: FileAttributesPlugin.oscog-AlistairGrant.40.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

VM Maker: FileAttributesPlugin.oscog-AlistairGrant.40.mcz

commits-2
 
Eliot Miranda uploaded a new version of FileAttributesPlugin to project VM Maker:
http://source.squeak.org/VMMaker/FileAttributesPlugin.oscog-AlistairGrant.40.mcz

==================== Summary ====================

Name: FileAttributesPlugin.oscog-AlistairGrant.40
Author: AlistairGrant
Time: 6 October 2018, 8:10:27.610572 pm
UUID: 2d975e6c-81fb-4b6d-9dab-35be5e978e22
Ancestors: FileAttributesPlugin.oscog-AlistairGrant.39

FileAttributesPlugin 2.0.2: free faPath

It's too easy to forget that C doesn't have garbage collection, especially in slang... :-)

=============== Diff against FileAttributesPlugin.oscog-AlistairGrant.28 ===============

Item was changed:
  ----- Method: FileAttributesPlugin class>>declareCVarsIn: (in category 'translation') -----
  declareCVarsIn: cg
 
  self declareC:  #('sCLPfn' 'sCOFfn')
  as: #'void *'
  in: cg.
  "Assume the security plugin can be loaded until proven otherwise"
  cg var: 'hasSecurityPlugin' declareC: 'int hasSecurityPlugin = 1'.
 
+ cg addHeaderFile: '<errno.h>'.
  cg addHeaderFile: '<limits.h>'.
  cg addHeaderFile: '<sys/types.h>'.
+ cg addHeaderFile: '<unistd.h>'.
  cg addHeaderFile: '<dirent.h>
+ #if _WIN32 || _WIN64
+ # include <windows.h>
+ # include <winbase.h>
+ # define FAIL() { return -1; }
+ # include "sqWin32File.h"
+ # if !!defined(PATH_MAX) /* work around bug in 64-bit cygwin; sigh :-( */
+ # define PATH_MAX 4096
+ # endif
+ # define HAVE_CHMOD 0
+ # define HAVE_CHOWN 0
+ #else
+ #define HAVE_CHMOD 1
+ #define HAVE_CHOWN 1
+ #endif'.
+ cg addHeaderFile: '"faCommon.h"'.
- #ifdef _WIN32
- #include <windows.h>
- #include <winbase.h>
- #define FAIL() { return -1; }
- #include "sqWin32File.h"
- #endif
- typedef struct dirptrstruct {
-     DIR *dp;
- int path_len;
-     char *path_file;
-     char path[PATH_MAX+4];
-     } osdir;'.
  cg addHeaderFile: '<sys/stat.h>
+ #if !!defined(HAVE_LSTAT) && !!defined(_WIN32) && !!defined(_WIN64)
+ # define HAVE_LSTAT 1
- #if !!defined(HAVE_LSTAT) && !!defined(_WIN32)
- #define HAVE_LSTAT 1
  #endif'.
  cg addHeaderFile: '<unistd.h>
+ /* AKG 2018 - ', self moduleName, '.c translated from class ', self name, ' */'!
- /* AKG 2017 - ', self moduleName, '.c translated from class ', self name, ' */'!

Item was added:
+ ----- Method: FileAttributesPlugin class>>primFileAttributeOf:number: (in category 'testing') -----
+ primFileAttributeOf: pathString number: attributeNumber
+ "Answer a single file attribute.
+ pathString is the path to the file
+ attributeNumber identifies which attribute to return:
+ 1 - 12: stat(): name, mode, ino, dev, nlink, uid, gid, size, accessDate, modifiedDate, changeDate, creationDate
+ 13 - 15: access(): R_OK, W_OK, X_OK
+ 16: isSymlink
+ On error, fail with an error code containing the appropriate OS error code."
+
+ "(1 to: 16) collect: [:i| self primFileAttributeOf: '.' number: i]"
+ "(1 to: 16) collect: [:i| self primFileAttributeOf: 'THIS HAS A GOOD CHANCE OF FAILING' number: i]"
+
+ <primitive: 'primitiveFileAttribute' module: 'FileAttributesPlugin' error: ec>
+ ^self primitiveFailed!

Item was added:
+ ----- Method: FileAttributesPlugin class>>primFileAttributes:attributeNumber: (in category 'testing') -----
+ primFileAttributes: pathString attributeNumber: attributeNumber
+ "Answer a single file attribute.
+ primFileAttributes: aString attributeNumber: attributeNumber
+ pathString is the path to the file
+ attributeNumber identifies which attribute to return:
+ 1 - 12: stat(): name, mode, ino, dev, nlink, uid, gid, size, accessDate, modifiedDate, changeDate, creationDate
+ 13 - 15: access(): R_OK, W_OK, X_OK
+ 16: isSymlink
+ On error, fail with an error code containing the appropriate OS error code."
+
+ "(1 to: 16) collect: [:i| self installedFileAttributeFor: '.' numbered: i]"
+ "(1 to: 16) collect: [:i| self installedFileAttributeFor: 'THIS HAS A GOOD CHANCE OF FAILING' numbered: i]"
+
+ <primitive: 'primitiveFileAttribute' module: 'FileAttributesPlugin' error: ec>
+ ^self primitiveFailed!

Item was removed:
- ----- Method: FileAttributesPlugin>>accessAttributesForFilename:into:startingAt: (in category 'private - file') -----
- accessAttributesForFilename: cPathName into: attributeArray startingAt: offset
- "Call access() for each access type (R, W, X) on the c string cPathName, storing the results in the st array attributeArray."
-
- | index boolean |
- <var: 'cPathName' type: 'char *'>
- index := offset.
- ((self acc: cPathName ess: self fileReadableFlag) = 0)
- ifTrue: [boolean := interpreterProxy trueObject]
- ifFalse: [boolean := interpreterProxy falseObject].
- interpreterProxy
- storePointer: index
- ofObject: attributeArray
- withValue: boolean.
- index := index + 1.
- boolean := ((self acc: cPathName ess: self fileWriteableFlag) = 0)
- ifTrue: [interpreterProxy trueObject]
- ifFalse: [interpreterProxy falseObject].
- interpreterProxy
- storePointer: index
- ofObject: attributeArray
- withValue: boolean.
- index := index + 1.
- boolean := ((self acc: cPathName ess: self fileExecutableFlag) = 0)
- ifTrue: [interpreterProxy trueObject]
- ifFalse: [interpreterProxy falseObject].
- interpreterProxy
- storePointer: index
- ofObject: attributeArray
- withValue: boolean.
- !

Item was changed:
  ----- Method: FileAttributesPlugin>>addressObjectFor: (in category 'private - directory') -----
  addressObjectFor: aMachineAddress
  "Answer an ExternalAddress object which represents aMachineAddress"
 
  | addressOop addressOopArrayPointer addressUnion idx |
+ <var: 'aMachineAddress' type: #'void *'>
+ <var: 'addressUnion' type: 'union {void *address; unsigned char bytes[sizeof(void *)];}'>
+ <var: 'addressOopArrayPointer' type: #'unsigned char *'>
+ self touch: addressUnion.
- <var: 'aMachineAddress' type: 'void *'>
- <var: 'addressUnion' declareC: 'union {void *address; unsigned char bytes[sizeof(void *)];} addressUnion'>
- <var: 'addressOopArrayPointer' declareC: 'unsigned char *addressOopArrayPointer'>
  addressOop := interpreterProxy
  instantiateClass: interpreterProxy classByteArray
  indexableSize: self sizeOfPointer.
  addressOop ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
  addressOopArrayPointer := interpreterProxy arrayValueOf: addressOop.
  self cCode: 'addressUnion.address = aMachineAddress'.
  idx := 0.
  [idx < self sizeOfPointer] whileTrue:
  [self cCode: 'addressOopArrayPointer[idx] = addressUnion.bytes[idx]'.
  idx := idx + 1].
  ^ addressOop
  !

Item was removed:
- ----- Method: FileAttributesPlugin>>byteArrayFromCString:to: (in category 'private') -----
- byteArrayFromCString: aCString to: byteArrayOop
- "Answer a new ByteArray copied from a null-terminated C string.
- Caution: This may invoke the garbage collector."
-
- | len newByteArray byteArrayPtr |
- <var: 'aCString' type: 'const char *'>
- <var: 'byteArrayPtr' type: 'unsigned char *'>
- <var: 'byteArrayOop' type: 'sqInt *'>
- len := self strlen: aCString.
- "We never return strings longer than PATH_MAX"
- len > #PATH_MAX ifTrue: [^self stringTooLong].
- newByteArray := interpreterProxy
- instantiateClass: interpreterProxy classByteArray
- indexableSize: len.
- newByteArray ifNil:
- [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
- byteArrayPtr := interpreterProxy arrayValueOf: newByteArray.
- self mem: byteArrayPtr cp: aCString y: len.
- byteArrayOop at: 0 put: newByteArray.
- ^0!

Item was changed:
  ----- Method: FileAttributesPlugin>>canOpenDirectoryStreamFor:length: (in category 'private - directory') -----
  canOpenDirectoryStreamFor: aPathCString length: length
  "Answer non-zero if security permits the directory to be listed."
 
  "FIXME: This function has not been tested. -dtl"
 
+ <var: 'aPathCString' type: #'char *'>
- <var: 'aPathCString' type: 'char *'>
  "If the security plugin can be loaded, use it to check .
  If not, assume it's ok"
  "The hasSecurityPlugin flag is set to 1 by default"
  (hasSecurityPlugin = 0) ifTrue: [^ true].
  sCLPfn ~= 0
  ifTrue:
  [^ self cCode: ' ((int (*) (char *, int)) sCLPfn)(aPathCString, length)'
  inSmalltalk: [true]]
  ifFalse:
  [hasSecurityPlugin := 0. "Reset the flag so we do not try again next time"
  ^ true]
  !

Item was changed:
  ----- Method: FileAttributesPlugin>>canStatFilePath:length: (in category 'private - file') -----
  canStatFilePath: aPathCString length: length
  "Answer non-zero if security permits the a stat() call on the file path. Allow a
  stat() call only on files which we are permitted to open."
 
  "FIXME: This function has not been tested. -dtl"
 
+ <var: 'aPathCString' type: #'char *'>
- <var: 'aPathCString' type: 'char *'>
  "If the security plugin can be loaded, use it to check .
  If not, assume it's ok"
  "The hasSecurityPlugin flag is set to 1 by default"
  (hasSecurityPlugin = 0) ifTrue: [^ true].
  sCOFfn ~= 0
  ifTrue:
  [^ self cCode: ' ((int (*) (char *, int, int)) sCOFfn)(aPathCString, length, 0)'
  inSmalltalk: [true]]
  ifFalse:
  [hasSecurityPlugin := 0. "Reset the flag so we do not try again next time"
  ^ true]
  !

Item was changed:
+ ----- Method: FileAttributesPlugin>>cantAllocateMemory (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>cantAllocateMemory (in category 'errors') -----
  cantAllocateMemory
  <inline: #always>
  ^-10!

Item was changed:
+ ----- Method: FileAttributesPlugin>>cantOpenDir (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>cantOpenDir (in category 'errors') -----
  cantOpenDir
  <inline: #always>
  ^-9!

Item was changed:
+ ----- Method: FileAttributesPlugin>>cantReadlink (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>cantReadlink (in category 'errors') -----
  cantReadlink
  <inline: #always>
  ^-8!

Item was changed:
+ ----- Method: FileAttributesPlugin>>cantStatPath (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>cantStatPath (in category 'errors') -----
  cantStatPath
  "SecurityPlugin determined that the requested path cannot be accessed."
  <inline: #always>
  ^-3!

Item was removed:
- ----- Method: FileAttributesPlugin>>checkAccess:mode:to: (in category 'private - file') -----
- checkAccess: pathString mode: mode to: flag
- "Check access to pathString."
-
- | cString len sPtr |
- <export: true>
- <var: 'cString' declareC: 'char cString[PATH_MAX]'>
- <var: 'sPtr' declareC: 'char *sPtr'>
- <var: 'flag' type: 'sqInt *'>
- len := interpreterProxy stSizeOf: pathString.
- "Note: The static sized string buffer is faster than using a Smalltalk allocated
- string as the buffer, and is unlikely to fail in practice. In the event that
- the buffer turns out to be too small, write an error message to stdout before
- failing."
- len >= #PATH_MAX ifTrue: [^self stringTooLong].
- "Copy pathString to the new string"
- sPtr := interpreterProxy arrayValueOf: pathString.
- ((self canStatFilePath: sPtr length: len) = 0) ifTrue: [^self cantStatPath].
- self mem: cString cp: sPtr y: len.
- cString at: len put: 0.
- flag at: 0 put: (self acc: cString ess: mode).
- ^0
- !

Item was added:
+ ----- Method: FileAttributesPlugin>>convertWinToSqueakTime: (in category 'private - windows') -----
+ convertWinToSqueakTime: st
+ "Convert the supplied Windows SYSTEMTIME to Squeak time"
+ <option: #_WIN32>
+
+ | dy secs nDaysPerMonth |
+
+ <returnTypeC: #'sqLong'>
+ <var: 'st' type: #'SYSTEMTIME'>
+ <var: 'dy' type: #'sqLong'>
+ <var: 'secs' type: #'sqLong'>
+ <var: 'nDaysPerMonth' declareC: 'static sqLong nDaysPerMonth[14] = {
+ 0,  0,  31,  59,  90, 120, 151,
+ 181, 212, 243, 273, 304, 334, 365 }'>
+ self touch: nDaysPerMonth.
+
+ "Squeak epoch is Jan 1, 1901"
+ "compute delta year"
+ dy := (self cCode: 'st.wYear') - 1901.
+ secs := (dy * 365 * 24 * 60 * 60)       "base seconds"
+ + ((dy bitShift: -2) * 24 * 60 * 60).   "seconds of leap years"
+ "check if month > 2 and current year is a leap year"
+ [ (self cCode: 'st.wMonth') > 2 and: [ (dy bitAnd: 16r0003) = 16r0003 ]] ifTrue: [
+ "add one day"
+ secs := secs + (24 * 60 * 60) ].
+ "add the days from the beginning of the year"
+ secs := secs + (self cCode: '(nDaysPerMonth[st.wMonth] + st.wDay - 1) * 24 * 60 * 60').
+ "add the hours, minutes, and seconds"
+ secs := secs + (self cCode: 'st.wSecond + 60*(st.wMinute + 60*st.wHour)').
+ ^secs
+ !

Item was changed:
+ ----- Method: FileAttributesPlugin>>corruptValue (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>corruptValue (in category 'errors') -----
  corruptValue
  <inline: #always>
  ^-7!

Item was removed:
- ----- Method: FileAttributesPlugin>>fileCreationTimeFor:length:to: (in category 'private - windows') -----
- fileCreationTimeFor: pathString length: pathLength to: creationDate
- "Get the creationDate for the supplied file.
- Linux kernel 4.11 should have statx(), so hopefully there will be a cross-platform
- solution soon.  Just dump the c-code and hope to remove it soon."
- <option: #_WIN32>
- | tm winAttrs win32Path sysTime |
- <var: 'pathString' type: 'char *'>
- <var: 'creationDate' type: 'time_t *'>
- <var: 'tm' type: 'struct tm'>
- <var: 'winAttrs' type: 'WIN32_FILE_ATTRIBUTE_DATA'>
- <var: 'win32Path' type: 'WCHAR *'>
- <var: 'sysTime' type: 'SYSTEMTIME'>
- self touch: winAttrs.
- self touch: sysTime.
- self me: (self addressOf: tm) ms: 0 et: tm sizeof.
- "convert the supplied path name into a wide string"
- self ALLOC_: win32Path WIN32_: pathString PATH: pathLength.
- (self cCode: 'GetFileAttributesExW(win32Path, 0, &winAttrs)') = 0 ifTrue:
-   [^self getAttributesFailed].
- (self cCode: 'FileTimeToSystemTime(&winAttrs.ftCreationTime, &sysTime)') = 0 ifTrue:
-   [^self timeConversionFailed].
-
- self cCode: '
- tm.tm_year = sysTime.wYear - 1900;
- tm.tm_mon = sysTime.wMonth - 1;
- tm.tm_mday = sysTime.wDay;
-
- tm.tm_hour = sysTime.wHour;
- tm.tm_min = sysTime.wMinute;
- tm.tm_sec = sysTime.wSecond;
- tm.tm_isdst = 0;
- *creationDate = mktime(&tm)'.
-
- ^0!

Item was changed:
  ----- Method: FileAttributesPlugin>>fileToAttributeArray:mask:array: (in category 'private - file') -----
+ fileToAttributeArray: faPath mask: attributeMask array: attributeArray
- fileToAttributeArray: cPathName mask: attributeMask array: attributeArray
  "Answer a file attribute array from pathNameOop."
 
  | getStats useLstat getAccess statArray accessArray combinedArray status fileNameOop statBuf  |
+ <returnTypeC: #'int'>
+ <var: 'faPath' type: #'fapath *'>
+ <var: 'attributeArray' type: #'sqInt *'>
+ <var: 'statBuf' type: #'faStatStruct'>
- <returnTypeC: 'int'>
- <var: 'cPathName' type: 'char *'>
- <var: 'attributeArray' type: 'sqInt *'>
- <var: 'statBuf' type: 'struct stat'>
 
  "Determine which attributes to retrieve"
  getStats := attributeMask anyMask: 1.
  getAccess := attributeMask anyMask: 2.
  (getStats or: [getAccess]) ifFalse:
  ["No information has been requested, which doesn't make sense"
  ^self invalidArguments].
  getStats ifTrue:
  [
  useLstat := attributeMask anyMask: 4.
  statArray := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 12.
  statArray ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
  status := useLstat ifFalse:
+ [ self faStat: faPath _: (self addressOf: statBuf ) _: (self addressOf: fileNameOop) ]
- [ self putStatFor: cPathName
- intoBuffer: (self addressOf: statBuf)
- targetName:  (self addressOf: fileNameOop) ]
  ifTrue:
+ [ self faLinkStat: faPath _: (self addressOf: statBuf ) _: (self addressOf: fileNameOop) ].
- [ self putLStatFor: cPathName
- intoBuffer: (self addressOf: statBuf)
- targetName:  (self addressOf: fileNameOop) ].
  status ~= 0 ifTrue: [^status].
+ status := self statArrayFor: faPath toArray: statArray from: (self addressOf: statBuf) fileName: fileNameOop.
- status := self statArrayFor: cPathName toArray: statArray from: (self addressOf: statBuf) fileName: fileNameOop.
  status ~= 0 ifTrue: [^status].
  "Set attributeArray in case only stat() attributes have been requested"
  attributeArray at: 0 put: statArray
  ].
  getAccess ifTrue:
  [
  accessArray := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 3.
  accessArray ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
+ self faAccessAttributes: faPath _: accessArray _: 0.
- self accessAttributesForFilename: cPathName into: accessArray startingAt: 0.
  "Set attributeArray in case only access() attributes have been requested"
  attributeArray at: 0 put: accessArray
  ].
  [getStats and: [getAccess]] ifTrue:
  [
  combinedArray := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 2.
  combinedArray ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
  attributeArray at: 0 put: combinedArray.
  interpreterProxy
  storePointer: 0 ofObject: combinedArray withValue: statArray;
  storePointer: 1 ofObject: combinedArray withValue: accessArray
  ].
 
  ^0!

Item was changed:
+ ----- Method: FileAttributesPlugin>>getAttributesFailed (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>getAttributesFailed (in category 'errors') -----
  getAttributesFailed
  <inline: #always>
  ^-4!

Item was changed:
+ ----- Method: FileAttributesPlugin>>invalidArguments (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>invalidArguments (in category 'errors') -----
  invalidArguments
  <inline: #always>
  ^-6!

Item was changed:
+ ----- Method: FileAttributesPlugin>>invalidRequest (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>invalidRequest (in category 'errors') -----
  invalidRequest
  <inline: #always>
  ^-11!

Item was removed:
- ----- Method: FileAttributesPlugin>>isSymlink:boolean: (in category 'private - file') -----
- isSymlink: cPathName boolean: resultOop
- "Set resultOop to a boolean indicating whether cPathName is a symbolic link.
- Answer status (0 = success)"
-
- | status statBuf |
- <var: 'cPathName' type: 'char *'>
- <var: 'resultOop' type: 'sqInt *'>
- <var: 'statBuf' type: 'struct stat'>
- self touch: statBuf.
- self cppIf: #HAVE_LSTAT == 1 ifTrue:
- [status := self lst: cPathName at: (self addressOf: statBuf).
- status ~= 0 ifTrue: [^self cantStatPath].
- (self S_ISLNK: statBuf st_mode) = 0
- ifFalse: [resultOop at: 0 put: interpreterProxy trueObject]
- ifTrue: [resultOop at: 0 put: interpreterProxy falseObject]].
- ^0!

Item was added:
+ ----- Method: FileAttributesPlugin>>noMoreData (in category 'errors / status') -----
+ noMoreData
+ <inline: #always>
+ ^1!

Item was removed:
- ----- Method: FileAttributesPlugin>>offsetStatBufTimesForWIN32: (in category 'private - windows') -----
- offsetStatBufTimesForWIN32: statBufPointer
- "Adjust the statBuffer to UTC, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx"
- <option: #_WIN32>
- <var: 'statBufPointer' type: 'struct stat *'>
- | status  dtzi offset |
- <var: 'offset' type: 'long'>
- <var: 'dtzi' type: 'TIME_ZONE_INFORMATION'>
- status := self GetTimeZoneInformation: (self addressOf: dtzi).
- offset := (self cCode: 'dtzi.Bias' inSmalltalk: [0]) * 60.
- "Adjust for DST if required"
- status = 2 ifTrue: [offset := offset - 3600].
- self cCode: 'statBufPointer->st_atime += offset'.
- self cCode: 'statBufPointer->st_mtime += offset'.
- self cCode: 'statBufPointer->st_ctime += offset'.!

Item was removed:
- ----- Method: FileAttributesPlugin>>oopFromTimeT: (in category 'private') -----
- oopFromTimeT: posixSeconds
- "Answer an object representation of the provided time (time_t).
- The size of time_t is platform dependent, so check the size and convert appropriately."
-
- <var: 'posixSeconds' type: 'time_t'>
-
- ^(self sizeof: #time_t) = 4 ifTrue:
- [interpreterProxy signed32BitIntegerFor: posixSeconds]
- ifFalse:
- [interpreterProxy signed64BitIntegerFor: posixSeconds].
- !

Item was removed:
- ----- Method: FileAttributesPlugin>>openDirectoryStream:ptr: (in category 'private - directory') -----
- openDirectoryStream: pathOOP ptr: osdirPtr
- "Open a new directory stream. Answer a pointer to the directory stream or NULL."
-
- | sPtr len dir |
- <var: 'osdirPtr' type: 'osdir **'>
- <var: 'sPtr' declareC: 'char *sPtr'>
- <var: 'dir' type: 'osdir *'>
- <returnTypeC: 'int'>
- sPtr := interpreterProxy arrayValueOf: pathOOP.
- len := interpreterProxy sizeOfSTArrayFromCPrimitive: sPtr.
- "The path buffer needs room for a trailing slash and the file name, so subtracting 2 is conservative"
- len > (#PATH_MAX - 2) ifTrue: [^self stringTooLong].
- (self canOpenDirectoryStreamFor: sPtr length: len)
- ifTrue:
- [
- dir := self cCode: '(osdir *) malloc(sizeof(osdir))'.
- dir = nil ifTrue: [^self cantAllocateMemory].
- self mem: dir path cp: sPtr y: len.
- "Ensure path has a trailing slash"
- self cCode: 'if (dir->path[len-1] !!= ''/'') {
- dir->path[len++] = ''/'';
- }'.
- self cCode: 'dir->path_file = dir->path + len'.
- self cCode: 'dir->path_file[0] = ''\0'''.
- self cCode: 'dir->path_len = len'.
- self cCode: 'dir->dp =  opendir(dir->path)'.
- dir dp ifNil:
- [self free: dir.
- ^self cantOpenDir].
- osdirPtr at: 0 put: dir.
- ^0
- ].
- "If we get here, we can't open the directory"
- ^self cantOpenDir
- !

Item was changed:
  ----- Method: FileAttributesPlugin>>pathOop:toBuffer:maxLen: (in category 'private - file') -----
  pathOop: pathNameOop toBuffer: cPathName maxLen: maxLen
  "Copy the supplied path name string object to the supplied c string buffer"
 
  | len sPtr |
+ <var: 'cPathName' type: #'char *'>
+ <var: 'sPtr' type: #'char *'>
+ <returnTypeC: #'int'>
- <var: 'cPathName' type: 'char *'>
- <var: 'sPtr' type: 'char *'>
- <returnTypeC: 'int'>
 
  len := interpreterProxy stSizeOf: pathNameOop.
  (len >= maxLen) ifTrue: [^self stringTooLong].
  "Copy pathName to the new string"
  sPtr := interpreterProxy arrayValueOf: pathNameOop.
  ((self canStatFilePath: sPtr length: len) = 0) ifTrue: [^self cantStatPath].
+ self memcpy: cPathName _: sPtr _: len.
- self mem: cPathName cp: sPtr y: len.
  cPathName at: len put: 0.
  ^0.
  !

Item was changed:
  ----- Method: FileAttributesPlugin>>pointerFrom: (in category 'private - directory') -----
  pointerFrom: directoryPointerBytes
  "Answer the machine address contained in anExternalAddressOop."
 
  | ptr addressUnion idx |
+ <returnTypeC: #'void *'>
+ <var: 'ptr' type: #'unsigned char *'>
- <returnTypeC: 'void *'>
- <var: 'ptr' type: 'unsigned char *'>
  <var: 'addressUnion' type: 'union {void *address; unsigned char bytes[sizeof(void *)];}'>
  ((interpreterProxy is: directoryPointerBytes KindOf: 'ByteArray') and:
  [(interpreterProxy stSizeOf: directoryPointerBytes) = self sizeOfPointer])
  ifFalse: [^ nil].
  ptr := interpreterProxy arrayValueOf: directoryPointerBytes.
  idx := 0.
  [idx < self sizeOfPointer] whileTrue:
  [self cCode: 'addressUnion.bytes[idx] = ptr[idx]'.
  idx := idx + 1].
  ^ self cCode: 'addressUnion.address' inSmalltalk: [addressUnion]
  !

Item was added:
+ ----- Method: FileAttributesPlugin>>posixFileTimesFrom:to: (in category 'private - posix') -----
+ posixFileTimesFrom: statBufPointer to: attributeArray
+ "Populate attributeArray with the file times from statBufPointer"
+
+ | attributeDate |
+
+ <var: 'statBufPointer' type: #'faStatStruct *'>
+ <var: 'attributeDate' type: #'sqLong'>
+
+ self cppIf: #_WIN32 defined ifTrue: [] ifFalse: [
+ attributeDate := self faConvertUnixToLongSqueakTime: (self cCode: 'statBufPointer->st_atime').
+ interpreterProxy
+ storePointer: 8
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate).
+ attributeDate := self faConvertUnixToLongSqueakTime: (self cCode: 'statBufPointer->st_mtime').
+ interpreterProxy
+ storePointer: 9
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate).
+ attributeDate := self faConvertUnixToLongSqueakTime: (self cCode: 'statBufPointer->st_ctime').
+ interpreterProxy
+ storePointer: 10
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate);
+ storePointer: 11
+ ofObject: attributeArray
+ withValue: interpreterProxy nilObject ].
+ ^0!

Item was added:
+ ----- Method: FileAttributesPlugin>>primitiveChangeMode (in category 'file primitives') -----
+ primitiveChangeMode
+ "Set the mode of the supplied file using chmod()."
+
+ | fileNameOop newMode status faPath |
+ <export: true>
+ <var: 'newMode' type: #'sqInt'>
+ <var: 'faPath' type: #'fapath *'>
+
+ fileNameOop := interpreterProxy stackObjectValue: 1.
+ newMode := interpreterProxy stackIntegerValue: 0.
+ (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileNameOop) not]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+ self cppIf: #HAVE_CHMOD ifTrue: [
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileNameOop.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ status := self chmod: (self faGetPlatPath: faPath) _: newMode.
+ self free: faPath.
+ status ~= 0 ifTrue: [
+ ^interpreterProxy primitiveFailForOSError: (self cCode: 'errno')].
+ ^interpreterProxy methodReturnValue: interpreterProxy nilObject.
+ ].
+ ^interpreterProxy primitiveFailForOSError: self unsupportedOperation.
+ !

Item was added:
+ ----- Method: FileAttributesPlugin>>primitiveChangeOwner (in category 'file primitives') -----
+ primitiveChangeOwner
+ "Set the owner of the supplied file using chown()."
+
+ | fileNameOop ownerId groupId faPath status |
+ <export: true>
+ <var: 'faPath' type: #'fapath *'>
+
+ fileNameOop := interpreterProxy stackObjectValue: 2.
+ ownerId := interpreterProxy stackIntegerValue: 1.
+ groupId := interpreterProxy stackIntegerValue: 0.
+ (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileNameOop) not]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+ self cppIf: #HAVE_CHOWN ifTrue: [
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileNameOop.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ status := self chown: (self faGetPlatPath: faPath) _: ownerId _: groupId.
+ self free: faPath.
+ status ~= 0 ifTrue: [
+ ^interpreterProxy primitiveFailForOSError: (self cCode: 'errno')].
+ ^interpreterProxy methodReturnValue: interpreterProxy nilObject.
+ ].
+ ^interpreterProxy primitiveFailForOSError: self unsupportedOperation.
+ !

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveClosedir (in category 'file primitives') -----
  primitiveClosedir
  "Close the directory stream for dirPointerOop. Answer dirPointerOop on success.
  Raise PrimErrBadArgument if the parameter is not a ByteArray length size(void *).
  If closedir() returns an error raise PrimitiveOSError."
 
+ | dirPointerOop faPath result |
- | dirPointerOop dirStream result |
  <export: true>
+ <var: 'fapath' type: #'faPath *'>
+
- <var: 'dirStream' type: 'osdir *'>
  dirPointerOop := interpreterProxy stackValue: 0.
+ faPath := self pointerFrom: dirPointerOop.
+ faPath ifNil:
- dirStream := self pointerFrom: dirPointerOop.
- dirStream ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ result := self faCloseDirectory: faPath.
- result := self closedir: dirStream dp.
  result = 0 ifFalse:
+ [^interpreterProxy primitiveFailForOSError: result].
+ self free: faPath.
- [^interpreterProxy primitiveFailForOSError: self unableToCloseDir].
- self free: dirStream.
  interpreterProxy pop: 2 thenPush: dirPointerOop!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveFileAttribute (in category 'file primitives') -----
  primitiveFileAttribute
  "Answer a single file attribute.
  primFileAttributes: aString attributeNumber: attributeNumber
  aString is the path to the file
  attributeNumber identifies which attribute to return:
  1 - 12: stat(): name, mode, ino, dev, nlink, uid, gid, size, accessDate, modifiedDate, changeDate, creationDate
  13 - 15: access(): R_OK, W_OK, X_OK
  16: isSymlink
  On error, answer a single element array containing the appropriate error code."
 
+ | fileName attributeNumber resultOop faPath |
- | fileName attributeNumber resultOop fileNameOop statBuf cPathName sizeIfFile mode creationDate status |
  <export: true>
+ <var: 'faPath' type: #'fapath *'>
- <var: 'statBuf' type: 'struct stat'>
- <var: 'cPathName' declareC: 'char cPathName[PATH_MAX]'>
- <var: 'creationDate' type: 'time_t'>
 
  fileName := interpreterProxy stackObjectValue: 1.
  attributeNumber := interpreterProxy stackIntegerValue: 0.
  (interpreterProxy failed
+ or: [(attributeNumber between: 1 and: 16) not
+ or: [(interpreterProxy isBytes: fileName) not]]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
- or: [(attributeNumber between: 1 and: 16) not
- or: [(interpreterProxy is: fileName KindOf: 'String') not]]) ifTrue:
- [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
- status := self pathOop: fileName toBuffer: cPathName maxLen: #PATH_MAX.
- status ~= 0 ifTrue:
- [^interpreterProxy primitiveFailForOSError: status].
- resultOop := 0.
 
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileName.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
- attributeNumber < 12 ifTrue:
- "Get requested stat entry"
- [
- status := self
- putStatFor: cPathName
- intoBuffer: (self addressOf: statBuf)
- targetName: (self addressOf: fileNameOop).
- status ~= 0 ifTrue:
- [^interpreterProxy primitiveFailForOSError: status].
- "In C, this would naturally be a switch statement,
- but I don't know to mix in the smalltalk code"
- attributeNumber = 1 ifTrue: [resultOop := fileNameOop].
- attributeNumber = 2 ifTrue:
- [resultOop := interpreterProxy positiveMachineIntegerFor: statBuf st_mode].
- attributeNumber = 3 ifTrue:
- [resultOop := interpreterProxy positive64BitIntegerFor: statBuf st_ino].
- attributeNumber = 4 ifTrue:
- [resultOop := interpreterProxy positive64BitIntegerFor: statBuf st_dev].
- attributeNumber = 5 ifTrue:
- [resultOop := interpreterProxy positive64BitIntegerFor: statBuf st_nlink].
- attributeNumber = 6 ifTrue:
- [resultOop := interpreterProxy positiveMachineIntegerFor: statBuf st_uid].
- attributeNumber = 7 ifTrue:
- [resultOop := interpreterProxy positiveMachineIntegerFor: statBuf st_gid].
- attributeNumber = 8 ifTrue:
- [
- sizeIfFile := ((self S_ISDIR: statBuf st_mode) = 0)
- ifTrue: [statBuf st_size]
- ifFalse: [0].
- resultOop := interpreterProxy positiveMachineIntegerFor: sizeIfFile
- ].
- attributeNumber = 9 ifTrue:
- [resultOop := self oopFromTimeT: statBuf st_atime].
- attributeNumber = 10 ifTrue:
- [resultOop := self oopFromTimeT: statBuf st_mtime].
- attributeNumber = 11 ifTrue:
- [resultOop := self oopFromTimeT: statBuf st_ctime].
- ]
- ifFalse: [attributeNumber = 12  ifTrue:
- [
- self cppIf: #_WIN32 defined ifTrue: [
- status := self fileCreationTimeFor: cPathName
- length: cPathName strlen
- to: (self addressOf: creationDate put: [:val| creationDate := val]).
- status ~= 0 ifTrue:
- [^interpreterProxy primitiveFailForOSError: status].
- resultOop := self oopFromTimeT: creationDate ]
- ifFalse: [
- resultOop := interpreterProxy nilObject ]
- ]
- ifFalse: [attributeNumber < 16 ifTrue:
- "Get requested access entry"
- [
- attributeNumber = 13 ifTrue: [mode := self fileReadableFlag].
- attributeNumber = 14 ifTrue: [mode := self fileWriteableFlag].
- attributeNumber = 15 ifTrue: [mode := self fileExecutableFlag].
- resultOop := ((self acc: cPathName ess: mode) = 0)
- ifTrue: [interpreterProxy trueObject]
- ifFalse: [interpreterProxy falseObject].
- ]
- ifFalse: "attributeNumber = 16"
- [
- status := self isSymlink: cPathName boolean: (self addressOf: resultOop put: [:val| resultOop := val]).
- status ~= 0 ifTrue:
- [^interpreterProxy primitiveFailForOSError: status].
- ]]].
 
+ resultOop := self faFileAttribute: faPath _: attributeNumber.
+ self free: faPath.
+ interpreterProxy failed ifTrue: [
+ ^interpreterProxy primitiveFailureCode].
+
  resultOop = 0
  ifTrue: ["It shouldn't be possible to get here"
+ interpreterProxy primitiveFailForOSError: self unexpectedError]
+ ifFalse: [interpreterProxy methodReturnValue: resultOop]!
- interpreterProxy primitiveFail]
- ifFalse: [interpreterProxy pop: 3 thenPush: resultOop]!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveFileAttributes (in category 'file primitives') -----
  primitiveFileAttributes
  "Answer an array of file attributes.
  primFileAttributes: aString mask: attributeMask
  aString is the path to the file
  attributeMask indicates which attributes to retrieve:
  bit 0 - get stat() attributes
  bit 1 - get access() attributes
  bit 2 - use lstat() instead of stat()
  On error answer the appropriate error code (Integer)"
 
+ | fileName attributeMask attributeArray faPath status |
- | fileName attributeMask attributeArray cPathName status |
  <export: true>
+ <var: 'faPath' type: #'fapath *'>
+
- <var: 'cPathName' declareC: 'char cPathName[PATH_MAX]'>
  fileName := interpreterProxy stackObjectValue: 1.
  attributeMask := interpreterProxy stackIntegerValue: 0.
  (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileName) not]) ifTrue:
- or: [(interpreterProxy is: fileName KindOf: 'String') not]) ifTrue:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
- status := self pathOop: fileName toBuffer: cPathName maxLen: #PATH_MAX.
- status ~= 0 ifTrue:
- [^interpreterProxy primitiveFailForOSError: status].
 
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSet: faPath StPathOop: fileName.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ status := self fileToAttributeArray: faPath
- status := self fileToAttributeArray: cPathName
  mask: attributeMask
  array: (self addressOf: attributeArray put: [:val| attributeArray := val]).
+ self free: faPath.
  status ~= 0
  ifTrue: [interpreterProxy primitiveFailForOSError: status]
+ ifFalse: [interpreterProxy methodReturnValue: attributeArray]!
- ifFalse: [interpreterProxy pop: 3 thenPush: attributeArray]!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveFileExists (in category 'file primitives') -----
  primitiveFileExists
  "Check for existence of a file with a call to access()."
 
+ | fileNameOop faPath resultOop |
- | pathString status accessFlag |
  <export: true>
+ <var: 'faPath'type: #'fapath *'>
+
+ fileNameOop := interpreterProxy stackObjectValue: 0.
+ (interpreterProxy isBytes: fileNameOop) ifFalse:
- <var: 'accessFlag' type: 'sqInt'>
- pathString := interpreterProxy stackObjectValue: 0.
- (interpreterProxy is: pathString KindOf: 'String') ifFalse:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileNameOop.
+ interpreterProxy failed ifTrue: [^interpreterProxy primitiveFailureCode].
+
+ resultOop := self faExists: faPath.
+ self free: faPath.
+ ^interpreterProxy methodReturnValue: resultOop.
+ !
- status := self checkAccess: pathString mode: self fileOKFlag to: (self addressOf: accessFlag).
- status = 0 ifFalse: [^interpreterProxy primitiveFailForOSError: status].
- interpreterProxy pop: 2 thenPush: (accessFlag = 0
- ifTrue: [interpreterProxy trueObject]
- ifFalse: [interpreterProxy falseObject])!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveLogicalDrives (in category 'file primitives') -----
  primitiveLogicalDrives
  "Answer the logical drive mask on windows"
 
+ | mask |
  <export: true>
  <var: 'mask' type: #'unsigned int'>
  self cppIf: #_WIN32 defined
  ifTrue:
+ [
- [| mask |
  mask := self GetLogicalDrives.
  mask ~= 0 ifTrue:
  [^interpreterProxy pop: 1 thenPush: (interpreterProxy positive32BitIntegerFor: mask)]].
  interpreterProxy primitiveFail!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveOpendir (in category 'file primitives') -----
  primitiveOpendir
 
  "self primOpendir: '/etc'"
 
+ | dirName faPath dirOop status resultOop |
- | dirName dir dirOop status |
  <export: true>
+ <var: 'faPath' type: #'fapath *'>
+
- <var: 'dir' type: 'osdir *'>
  dirName := interpreterProxy stackObjectValue: 0.
+ (interpreterProxy isBytes: dirName) ifFalse:
- (interpreterProxy is: dirName KindOf: 'String') ifFalse:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSet: faPath StDirOop: dirName.
+
+ (self canOpenDirectoryStreamFor: (self faGetStPath: faPath) length: (self faGetStPathLen: faPath)) ifFalse: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailForOSError: self cantOpenDir].
+
+ status := self faOpenDirectory: faPath.
+ status = self noMoreData ifTrue: [
+ self free: faPath.
+ ^interpreterProxy pop: 2 thenPush: interpreterProxy nilObject].
+ status < 0 ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailForOSError: status].
+ resultOop := self processDirectory: faPath.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode ].
+
+ self remapOop: resultOop in:
+ [ dirOop := self addressObjectFor: faPath ].
+ ^interpreterProxy
+ storePointer: 2 ofObject: resultOop withValue: dirOop;
+ methodReturnValue: resultOop.!
- status := self openDirectoryStream: dirName ptr: (self addressOf: dir).
- status ~= 0 ifTrue: [^interpreterProxy primitiveFailForOSError: status].
- dirOop := self addressObjectFor: dir.
- interpreterProxy pop: 2 thenPush: dirOop!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitivePathMax (in category 'file primitives') -----
  primitivePathMax
  "Answer the value of PATH_MAX for the current VM"
 
  <export: true>
+ ^interpreterProxy pop: 1 thenPush: (interpreterProxy integerObjectOf: #FA_PATH_MAX)!
- ^interpreterProxy pop: 1 thenPush: (interpreterProxy integerObjectOf: #PATH_MAX)!

Item was added:
+ ----- Method: FileAttributesPlugin>>primitivePlatToStPath (in category 'file primitives') -----
+ primitivePlatToStPath
+ "Convert the supplied file name (platform encoded) to the St UTF8 encoded byte array"
+
+ | fileName faPath resultOop byteArrayPtr |
+ <export: true>
+ <var: 'faPath' type: #'fapath *'>
+ <var: 'byteArrayPtr' type: #'unsigned char *'>
+
+ fileName := interpreterProxy stackObjectValue: 0.
+ (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileName) not]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetPlatPathOop: faPath _: fileName.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ resultOop := interpreterProxy
+ instantiateClass: interpreterProxy classByteArray
+ indexableSize: (self faGetStPathLen: faPath).
+ resultOop ifNil: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailFor: PrimErrNoMemory].
+ byteArrayPtr := interpreterProxy arrayValueOf: resultOop.
+ self memcpy: byteArrayPtr _: (self faGetStPath: faPath) _: (self faGetStPathLen: faPath).
+ self free: faPath.
+
+ ^interpreterProxy methodReturnValue: resultOop.
+ !

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveReaddir (in category 'file primitives') -----
  primitiveReaddir
  "Get the next entry in the directory stream. Answer the name of the entry, or
+ nil for the end of the directory stream.
+ Arguments:
+ - directoryPointer (ByteArray)"
- nil for the end of the directory stream."
 
+ | dirPointerOop faPath resultArray status |
- | dirPointerOop dirStream ent entryName attributeArray resultArray haveEntry entry_len status |
  <export: true>
+ <var: 'faPath' type: #'fapath *'>
- <var: 'ent' type: 'struct dirent *'>
- <var: 'dirStream' type: 'osdir *'>
- <var: 'haveEntry' type: #int>
 
  dirPointerOop := interpreterProxy stackValue: 0.
+ faPath := self pointerFrom: dirPointerOop.
+ faPath ifNil:
- dirStream := self pointerFrom: dirPointerOop.
- dirStream ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ status := self faReadDirectory: faPath.
+ status = self noMoreData ifTrue:
- haveEntry := 0.
- [ent := self readdir: dirStream dp.
- self cCode: 'if (ent == NULL ||
-                   ((!! (ent->d_name[0] == ''.'' && strlen(ent->d_name) == 1)) && strcmp(ent->d_name, "..")))
-                       haveEntry = 1'.
- haveEntry = 0] whileTrue.
- ent ifNil: "This is the normal case for the end of a directory stream,
- although it may indicate other error conditions for which errno would be updated.
- Assume the normal case here."
  [^interpreterProxy pop: 2 thenPush: interpreterProxy nilObject].
+ status < 0 ifTrue:
- status := self byteArrayFromCString: ent d_name to: (self addressOf: entryName put: [:val| entryName := val]).
- status ~= 0 ifTrue:
  [^interpreterProxy primitiveFailForOSError: status].
+ resultArray := self processDirectory: faPath.
+ interpreterProxy failed ifTrue: [^interpreterProxy primitiveFailureCode].
 
- "Build the path name (append the entry name to the path name)"
- entry_len := self strlen: ent d_name.
- [dirStream path_len + entry_len > (#PATH_MAX - 1)] ifTrue:
- [^interpreterProxy primitiveFailForOSError: self stringTooLong].
- self mem: dirStream path_file cp: ent d_name y: entry_len.
- dirStream path_file at: entry_len put: 0.
-
- status := self fileToAttributeArray: dirStream path mask: 1 array: (self addressOf: attributeArray put: [:val| attributeArray := val]).
- "If the stat() fails, still return the filename, just no attributes"
- status ~= 0 ifTrue: [attributeArray := interpreterProxy nilObject].
-
- self remapOop: #(entryName attributeArray)
- in: [resultArray := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 2].
- resultArray ifNil:
- [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
  interpreterProxy
- storePointer: 0 ofObject: resultArray withValue: entryName;
- storePointer: 1 ofObject: resultArray withValue: attributeArray;
  pop: 2 thenPush: resultArray!

Item was changed:
  ----- Method: FileAttributesPlugin>>primitiveRewinddir (in category 'file primitives') -----
  primitiveRewinddir
  "Set directoryStream to first entry. Answer dirPointerOop."
 
+ | dirPointerOop faPath status resultOop |
- | dirPointerOop dirStream |
  <export: true>
+ <var: 'faPath' type: #'fapath *'>
- <var: 'dirStream' declareC: 'osdir *dirStream'>
  dirPointerOop := interpreterProxy stackValue: 0.
+ faPath := self pointerFrom: dirPointerOop.
+ faPath ifNil:
- dirStream := self pointerFrom: dirPointerOop.
- dirStream ifNil:
  [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ status := self faRewindDirectory: faPath.
+ status < 0 ifTrue: [^interpreterProxy primitiveFailForOSError: status].
+ resultOop := self processDirectory: faPath.
+ interpreterProxy failed ifTrue: [^interpreterProxy primitiveFailureCode ].
+ ^interpreterProxy methodReturnValue: resultOop.!
- self rewinddir: dirStream dp.
- interpreterProxy pop: 2 thenPush: dirPointerOop!

Item was added:
+ ----- Method: FileAttributesPlugin>>primitiveStToPlatPath (in category 'file primitives') -----
+ primitiveStToPlatPath
+ "Convert the supplied file name (UTF8 encoded) to the platform encoded byte array"
+
+ | fileName faPath resultOop byteArrayPtr |
+ <export: true>
+ <var: 'faPath' type: #'fapath *'>
+ <var: 'byteArrayPtr' type: #'unsigned char *'>
+
+ fileName := interpreterProxy stackObjectValue: 0.
+ (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileName) not]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileName.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ resultOop := interpreterProxy
+ instantiateClass: interpreterProxy classByteArray
+ indexableSize: (self faGetPlatPathByteCount: faPath).
+ resultOop ifNil: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailFor: PrimErrNoMemory].
+ byteArrayPtr := interpreterProxy arrayValueOf: resultOop.
+ self memcpy: byteArrayPtr _: (self faGetPlatPath: faPath) _: (self faGetPlatPathByteCount: faPath).
+ self free: faPath.
+
+ ^interpreterProxy methodReturnValue: resultOop.
+ !

Item was added:
+ ----- Method: FileAttributesPlugin>>primitiveSymlinkChangeOwner (in category 'file primitives') -----
+ primitiveSymlinkChangeOwner
+ "Set the owner of the supplied file using chown()."
+
+ | fileNameOop ownerId groupId faPath status |
+ <export: true>
+ <var: 'faPath' type: #'fapath *'>
+
+ fileNameOop := interpreterProxy stackObjectValue: 2.
+ ownerId := interpreterProxy stackIntegerValue: 1.
+ groupId := interpreterProxy stackIntegerValue: 0.
+ (interpreterProxy failed
+ or: [(interpreterProxy isBytes: fileNameOop) not]) ifTrue:
+ [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
+ self cppIf: #HAVE_CHOWN ifTrue: [
+ faPath := self cCode: '(fapath *) calloc(1, sizeof(fapath))'.
+ faPath = nil ifTrue: [^interpreterProxy primitiveFailForOSError: self cantAllocateMemory].
+ self faSetStPathOop: faPath _: fileNameOop.
+ interpreterProxy failed ifTrue: [
+ self free: faPath.
+ ^interpreterProxy primitiveFailureCode].
+
+ status := self lchown: (self faGetPlatPath: faPath) _: ownerId _: groupId.
+ self free: faPath.
+ status ~= 0 ifTrue:
+ [^interpreterProxy primitiveFailForOSError: (self cCode: 'errno')].
+ ^interpreterProxy methodReturnValue: interpreterProxy nilObject.
+ ].
+ ^interpreterProxy primitiveFailForOSError: self unsupportedOperation.
+ !

Item was added:
+ ----- Method: FileAttributesPlugin>>processDirectory: (in category 'private') -----
+ processDirectory: faPath
+ "The supplied faPath contains the full path to the current entry while iterating over a directory.
+ Convert the file name to an object, get the attributes and answer the resulting array."
+
+ | status entryName attributeArray resultArray |
+ <var: 'faPath' type: #'fapath *'>
+
+ status := self faChar: (self faGetStFile: faPath)
+ ToByteArray: (self addressOf: entryName put: [:val | entryName := val]).
+ status ~= 0 ifTrue:
+ [ ^interpreterProxy primitiveFailForOSError: status].
+
+ status := self fileToAttributeArray: faPath mask: 1 array: (self addressOf: attributeArray put: [:val| attributeArray := val]).
+ "If the stat() fails, still return the filename, just no attributes"
+ status ~= 0 ifTrue: [attributeArray := interpreterProxy nilObject].
+
+ "resultArray: entryName, attributeArray, dirPtrOop"
+ self remapOop: #(entryName attributeArray)
+ in: [resultArray := interpreterProxy instantiateClass: interpreterProxy classArray indexableSize: 3].
+ resultArray ifNil:
+ [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
+ interpreterProxy
+ storePointer: 0 ofObject: resultArray withValue: entryName;
+ storePointer: 1 ofObject: resultArray withValue: attributeArray.
+ ^resultArray!

Item was removed:
- ----- Method: FileAttributesPlugin>>putLStatFor:intoBuffer:targetName: (in category 'private - file') -----
- putLStatFor: cPathName intoBuffer: statBufPointer targetName: fileNameOop
- "Call stat() on cPathName, storing the results in
- the buffer at statBufPointer."
-
- | cLinkName len status |
- <returnTypeC: 'sqInt'>
- <var: 'cPathName' type: 'char *'>
- <var: 'statBufPointer' type: 'struct stat *'>
- <var: 'cLinkName' declareC: 'char cLinkName[PATH_MAX]'>
- <var: 'fileNameOop' type: 'sqInt *'>
-
- self cppIf: #HAVE_LSTAT = 1 ifTrue: [
- status := self lst: cPathName at: statBufPointer.
- status ~= 0 ifTrue: [^self cantStatPath].
- (self S_ISLNK: statBufPointer st_mode) = 0
- ifFalse: [
- len := self readLink: cPathName into: cLinkName maxLength: #PATH_MAX.
- len < 0 ifTrue: [^len].
- status := self byteArrayFromCString: cLinkName to: fileNameOop]
- ifTrue:
- [fileNameOop at: 0 put: interpreterProxy nilObject].
- ] ifFalse: [ "#HAVE_LSTAT = 1"
- status := self invalidRequest.
- ].
- ^status
- !

Item was removed:
- ----- Method: FileAttributesPlugin>>putStatFor:intoBuffer:targetName: (in category 'private - file') -----
- putStatFor: cPathName intoBuffer: statBufPointer targetName: fileNameOop
- "Call stat() on cPathName, storing the results in
- the buffer at statBufPointer."
-
- | status |
- <var: 'cPathName' type: 'char *'>
- <var: 'statBufPointer' type: 'struct stat *'>
- <var: 'fileNameOop' type: 'sqInt *'>
-
- status := self st: cPathName at: statBufPointer.
- status ~= 0 ifTrue: [^self cantStatPath].
- self cppIf: #_WIN32 defined
- ifTrue: [self offsetStatBufTimesForWIN32: statBufPointer].
- fileNameOop at: 0 put: interpreterProxy nilObject.
- ^0!

Item was changed:
  ----- Method: FileAttributesPlugin>>readLink:into:maxLength: (in category 'private - file') -----
  readLink: cPathName into: cLinkPtr maxLength: maxLength
  "Get the target filename of the supplied symbolic link."
 
  | len |
+ <var: 'cPathName' type: #'char *'>
+ <var: 'cLinkPtr' type: #'char *'>
+ <var: 'maxLength' type: #'size_t'>
- <var: 'cPathName' type: 'char *'>
- <var: 'cLinkPtr' type: 'char *'>
- <var: 'maxLength' type: 'size_t'>
  <var: 'len' type: #sqInt> "len must be signed so that -1 can be communicated as an error."
 
  len := self cppIf: #_WIN32 defined
  ifTrue: [-1]
  ifFalse: [self read: cPathName li: cLinkPtr nk: maxLength].
  len < 0 ifTrue:
  [self cppIf: #'INDEBUG' defined ifTrue:
  [self cCode: 'fprintf(stderr, "FileAttributesPlugin: unable to readlink(): %d, errno=%d\n", len, errno)'].
  ^self cantReadlink].
  cLinkPtr at: len put: 0.
  ^len!

Item was changed:
  ----- Method: FileAttributesPlugin>>statArrayFor:toArray:from:fileName: (in category 'private - file') -----
+ statArrayFor: faPath toArray: attributeArray from: statBufPointer fileName: fileNameOop
- statArrayFor: cPathName toArray: attributeArray from: statBufPointer fileName: fileNameOop
  "Answer a file entry array from the supplied statBufPointer"
 
+ | sizeIfFile status |
+ <var: 'faPath' type: #'fapath *'>
+ <var: 'statBufPointer' type: #'faStatStruct *'>
- | sizeIfFile creationDate status |
- <var: 'cPathName' type: 'char *'>
- <var: 'statBufPointer' type: 'struct stat *'>
- <var: 'creationDate' type: 'time_t'>
 
+ sizeIfFile := (self cCode: 'S_ISDIR(statBufPointer->st_mode)') = 0
+ ifTrue: [self cCode: 'statBufPointer->st_size']
- sizeIfFile := (self S_ISDIR: statBufPointer st_mode) = 0
- ifTrue: [statBufPointer st_size]
  ifFalse: [0].
  interpreterProxy
  storePointer: 0
  ofObject: attributeArray
  withValue: fileNameOop;
  storePointer: 1
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_mode'));
- withValue: (interpreterProxy positiveMachineIntegerFor: statBufPointer st_mode);
  storePointer: 2
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_ino'));
- withValue: (interpreterProxy positive64BitIntegerFor: statBufPointer st_ino);
  storePointer: 3
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_dev'));
- withValue: (interpreterProxy positive64BitIntegerFor: statBufPointer st_dev);
  storePointer: 4
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_nlink'));
- withValue: (interpreterProxy positive64BitIntegerFor: statBufPointer st_nlink);
  storePointer: 5
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_uid'));
- withValue: (interpreterProxy positiveMachineIntegerFor: statBufPointer st_uid);
  storePointer: 6
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: (self cCode: 'statBufPointer->st_gid'));
- withValue: (interpreterProxy positiveMachineIntegerFor: statBufPointer st_gid);
  storePointer: 7
  ofObject: attributeArray
+ withValue: (interpreterProxy positive64BitIntegerFor: sizeIfFile).
+
- withValue: (interpreterProxy positive64BitIntegerFor: sizeIfFile);
- storePointer: 8
- ofObject: attributeArray
- withValue: (self oopFromTimeT: statBufPointer st_atime);
- storePointer: 9
- ofObject: attributeArray
- withValue: (self oopFromTimeT: statBufPointer st_mtime);
- storePointer: 10
- ofObject: attributeArray
- withValue: (self oopFromTimeT: statBufPointer st_ctime).
  self cppIf: #_WIN32 defined
+ ifTrue: [ status := self winFileTimesFor: faPath to: attributeArray ]
+ ifFalse: [ status := self posixFileTimesFrom: statBufPointer to: attributeArray ].
+
+ ^status
+ !
- ifTrue:
- [status := self fileCreationTimeFor: cPathName
- length: cPathName strlen
- to: (self addressOf: creationDate put: [:val| creationDate := val]).
- interpreterProxy
- storePointer: 11
- ofObject: attributeArray
- withValue: (status = 0
- ifTrue:  [self oopFromTimeT: creationDate]
- ifFalse: [interpreterProxy nilObject])]
- ifFalse:
- [interpreterProxy
- storePointer: 11
- ofObject: attributeArray
- withValue: interpreterProxy nilObject].
- ^0!

Item was changed:
+ ----- Method: FileAttributesPlugin>>statFailed (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>statFailed (in category 'errors') -----
  statFailed
  "A call to stat() failed"
  <inline: #always>
  ^-2!

Item was changed:
  ----- Method: FileAttributesPlugin>>stringFromCString: (in category 'private') -----
  stringFromCString: aCString
  "Answer a new String copied from a null-terminated C string.
  Caution: This may invoke the garbage collector."
 
  | len newString |
+ <var: 'aCString' type: #'const char *'>
- <var: 'aCString' type: 'const char *'>
  len := self strlen: aCString.
  newString := interpreterProxy
  instantiateClass: interpreterProxy classString
  indexableSize: len.
  newString ifNil: [^interpreterProxy primitiveFailFor: PrimErrNoMemory].
+ self strncpy: (interpreterProxy arrayValueOf: newString)
+ _: aCString
+ _: len. "(char *)strncpy()"
- self st: (interpreterProxy arrayValueOf: newString)
- rn: aCString
- cpy: len. "(char *)strncpy()"
  ^ newString
  !

Item was changed:
+ ----- Method: FileAttributesPlugin>>stringTooLong (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>stringTooLong (in category 'errors') -----
  stringTooLong
  "String too long.  A file path name was longer than PATH_MAX"
  <inline: #always>
  ^-1!

Item was changed:
+ ----- Method: FileAttributesPlugin>>timeConversionFailed (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>timeConversionFailed (in category 'errors') -----
  timeConversionFailed
  <inline: #always>
  ^-5!

Item was changed:
+ ----- Method: FileAttributesPlugin>>unableToCloseDir (in category 'errors / status') -----
- ----- Method: FileAttributesPlugin>>unableToCloseDir (in category 'errors') -----
  unableToCloseDir
  "The call to closedir() failed"
  <inline: #always>
  ^-12!

Item was added:
+ ----- Method: FileAttributesPlugin>>unexpectedError (in category 'errors / status') -----
+ unexpectedError
+ "This is normally used where a catch-all is placed, but not expected to be used"
+ <inline: #always>
+ ^-14!

Item was added:
+ ----- Method: FileAttributesPlugin>>unsupportedOperation (in category 'errors / status') -----
+ unsupportedOperation
+ "The requested operation is not supported on the current platform"
+ <inline: #always>
+ ^-13!

Item was changed:
  ----- Method: FileAttributesPlugin>>versionString (in category 'version string') -----
  versionString
  "Answer a string containing the version string for this plugin."
  <inline: #always>
+ ^'2.0.2'!
- ^'1.2.6'!

Item was added:
+ ----- Method: FileAttributesPlugin>>winFileCreationTimeFor:to: (in category 'private - windows') -----
+ winFileCreationTimeFor: winAttrs to: creationDate
+ "Set the file creation time from the supplied attributes."
+ <option: #_WIN32>
+
+ | sysTime fileTime |
+
+ <var: 'winAttrs' type: #'WIN32_FILE_ATTRIBUTE_DATA *'>
+ <var: 'creationDate' type: #'sqLong *'>
+ <var: 'fileTime' type: #'FILETIME'>
+ <var: 'sysTime' type: #'SYSTEMTIME'>
+
+ self touch: sysTime.
+ self touch: fileTime.
+
+ (self cCode: 'FileTimeToLocalFileTime(&winAttrs->ftCreationTime, &fileTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ (self cCode: 'FileTimeToSystemTime(&fileTime, &sysTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ self cCode: '*creationDate = convertWinToSqueakTime(sysTime)'.
+
+ ^0!

Item was added:
+ ----- Method: FileAttributesPlugin>>winFileLastAccessTimeFor:to: (in category 'private - windows') -----
+ winFileLastAccessTimeFor: winAttrs to: accessDate
+ "Set the file creation time from the supplied attributes."
+ <option: #_WIN32>
+
+ | sysTime fileTime |
+
+ <var: 'winAttrs' type: #'WIN32_FILE_ATTRIBUTE_DATA *'>
+ <var: 'accessDate' type: #'sqLong *'>
+ <var: 'fileTime' type: #'FILETIME'>
+ <var: 'sysTime' type: #'SYSTEMTIME'>
+
+ self touch: sysTime.
+ self touch: fileTime.
+
+ (self cCode: 'FileTimeToLocalFileTime(&winAttrs->ftLastAccessTime, &fileTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ (self cCode: 'FileTimeToSystemTime(&fileTime, &sysTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ self cCode: '*accessDate = convertWinToSqueakTime(sysTime)'.
+
+ ^0!

Item was added:
+ ----- Method: FileAttributesPlugin>>winFileLastWriteTimeFor:to: (in category 'private - windows') -----
+ winFileLastWriteTimeFor: winAttrs to: writeDate
+ "Set the file write time from the supplied attributes."
+ <option: #_WIN32>
+
+ | sysTime fileTime |
+
+ <var: 'winAttrs' type: #'WIN32_FILE_ATTRIBUTE_DATA *'>
+ <var: 'writeDate' type: #'sqLong *'>
+ <var: 'fileTime' type: #'FILETIME'>
+ <var: 'sysTime' type: #'SYSTEMTIME'>
+
+ self touch: sysTime.
+ self touch: fileTime.
+
+ (self cCode: 'FileTimeToLocalFileTime(&winAttrs->ftLastWriteTime, &fileTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ (self cCode: 'FileTimeToSystemTime(&fileTime, &sysTime)') = 0 ifTrue:
+   [^self timeConversionFailed].
+ self cCode: '*writeDate = convertWinToSqueakTime(sysTime)'.
+
+ ^0!

Item was added:
+ ----- Method: FileAttributesPlugin>>winFileTimesFor:to: (in category 'private - windows') -----
+ winFileTimesFor: faPath to: attributeArray
+ <inline: #never>
+ <option: #_WIN32>
+
+ | winAttrs attributeDate status |
+ <var: 'faPath' type: #'fapath *'>
+ <var: 'attributeDate' type: #'sqLong'>
+ <var: 'winAttrs' type: #'WIN32_FILE_ATTRIBUTE_DATA'>
+
+ "Get the file attributes"
+ status := self cCode: 'GetFileAttributesExW(faGetPlatPath(faPath), GetFileExInfoStandard, &winAttrs)'.
+ status = 0 ifTrue: [^self getAttributesFailed].
+
+ "Set the accessDate"
+ status := self winFileLastAccessTimeFor: (self addressOf: winAttrs) to: (self addressOf: attributeDate).
+ status = 0 ifFalse: [^status].
+ interpreterProxy
+ storePointer: 8
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate).
+
+ "Set the modifiedDate"
+ status := self winFileLastWriteTimeFor: (self addressOf: winAttrs) to: (self addressOf: attributeDate).
+ status = 0 ifFalse: [^status].
+ interpreterProxy
+ storePointer: 9
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate).
+
+ "Set the changeDate"
+ interpreterProxy
+ storePointer: 10
+ ofObject: attributeArray
+ withValue: interpreterProxy nilObject.
+
+ "Set the creationDate"
+ status := self winFileCreationTimeFor: (self addressOf: winAttrs) to: (self addressOf: attributeDate).
+ status = 0 ifFalse: [^status].
+ interpreterProxy
+ storePointer: 11
+ ofObject: attributeArray
+ withValue: (interpreterProxy signed64BitIntegerFor: attributeDate).
+
+ ^0
+ !