Patch (against current CVS) for Directory support

Tony Garnock-Jones tonyg at lshift.net
Mon Sep 27 15:31:32 PDT 2004


Hi all.

This is my first ever Slate code, so I'm anxious to hear whether it's 
considered good idiomatic code and any problems people can see. Note 
that the patch creates a few files as well as modifying some.

To try it out regenerate a VM and image and run this to see it work:

   (Directory newNamed: '.') sessionDo: [|:r| r reader upToEnd].


I have a few comments, all of which I imagine are dead obvious:

1. some kind of pluggable/dynamically-loaded prim mechanism is 
desperately needed :-)

2. could generateUtilities be factored out to avoid the repetition (for 
File, Directory, etc)? What would be a good way of doing that?

3. there are a couple of notes in the code too:

    a. the errno.slate is a really nasty quick hack which needs more
       thinking about

    b. the result of readdir() is unclear from the Darwin manpages,
       but the Single Unix Spec will do better, I just need to read
       it and update the code

    c. character encodings for strings such as filename, directory names
       - I imagine that this has already been discussed on this list or
       in IRC.

    d. weak pointers so that orphan directories can get collected if the
       programmer forgets to close them

Cheers,
   Tony

-------------- next part --------------
diff -Nu3 -r slate/src/lib/directory.slate ../slate/src/lib/directory.slate
--- slate/src/lib/directory.slate	1970-01-01 01:00:00.000000000 +0100
+++ ../slate/src/lib/directory.slate	2004-09-27 22:36:16.000000000 +0100
@@ -0,0 +1,95 @@
+Directory traits atSlotNamed: #parent0 put: ExternalResource traits.
+Directory addSlotsFrom: ExternalResource.
+
+dir@(Directory traits) newNamed: dirname
+[| newD |
+  newD: dir clone.
+  newD handle: Nil.
+  newD locator: (dirname as: File Locator).
+  newD
+].
+
+dir@(Directory traits) enable
+"Reset the directory."
+[| dirname result |
+  dir close.
+  dirname: (dir locator as: String).
+  result: (dir primitiveOpen: dirname).
+  result < 0 ifTrue: [(ErrnoError newErrno: result negated) signal].
+  dir handle: result.
+  dir
+].
+
+dir@(Directory traits) close
+"Call the close primitive and set the handle to Nil."
+[
+  dir handle ifNotNil: [| result |
+    result: (dir primitiveClose: dir handle).
+    dir handle: Nil.
+    result == 0 ifFalse: [(ErrnoError newErrno: result negated) signal].
+  ].
+].
+
+dir@(Directory traits) nextDirEntry
+[| buffer len |
+  dir handle ifNil: [error: 'Directory not open'].
+  buffer: (String newSize: 256).
+  len: (dir primitiveRead: dir handle into: buffer).
+  len < 0 ifTrue: [(ErrnoError newErrno: len negated) signal].
+  len == 0
+    ifTrue: [False]
+    ifFalse: [buffer copyFrom: 0 to: (len - 1)]
+].
+
+dir@(Directory traits) withOpenNamed: dirname do: block
+"Calls sessionDo: on a Directory made for the given dirname."
+[
+  (dir newNamed: dirname) sessionDo: block
+].
+
+loc@(File Locator traits) openDirectory
+[
+  (Directory newNamed: (l as: String)) open
+].
+
+Directory traits addPrototype: #Stream
+	    derivedFrom: {ExternalResource ReadStream. PeekableStream}.
+Directory Stream addSlot: #cache valued: Nil.
+
+Directory traits addPrototype: #ReadStream derivedFrom: {Directory Stream}.
+Directory traits removeSlot: #WriteStream.
+Directory traits removeSlot: #ReadWriteStream.
+
+ds@(Directory Stream traits) on: target@(String traits)
+"Open a Directory ReadStream on the String path."
+[
+  ds on: (Directory newNamed: target) open
+].
+
+ds@(Directory Stream traits) elementType
+[String].
+
+ds@(Directory Stream traits) arrayType
+[Array].
+
+ds@(Directory Stream traits) isAtEnd
+[
+  ds peek.
+  ds cache == False
+].
+
+ds@(Directory Stream traits) next
+[| result |
+  result: ds peek.
+  ds cache: Nil.
+  result
+].
+
+ds@(Directory Stream traits) peek
+[
+  ds cache ifNil: [| buffer |
+    buffer: (String newSize: 256).
+    ds cache: (ds resource nextDirEntry)
+  ].
+  ds cache
+].
diff -Nu3 -r slate/src/lib/errno.slate ../slate/src/lib/errno.slate
--- slate/src/lib/errno.slate	1970-01-01 01:00:00.000000000 +0100
+++ ../slate/src/lib/errno.slate	2004-09-27 22:36:16.000000000 +0100
@@ -0,0 +1,18 @@
+conditions addPrototype: #ErrnoError derivedFrom: {Error}.
+"A primitive signalled an error with an associated POSIX errno code.
+TODO: add primitive for strerror_r to get descriptive text, using
+DescriptiveConditionMixin"
+ErrnoError addSlot: #errno.
+
+e@(ErrnoError traits) newErrno: n
+[| newE |
+  newE: e new.
+  newE errno: n.
+  newE
+].
+
+e@(ErrnoError traits) describe
+[
+  DebugConsole ;
+    'A primitive error was signalled with POSIX errno code ' ; (e errno as: String) ; '\n'.
+].
diff -Nu3 -r slate/src/mobius/vm/bootstrap.slate ../slate/src/mobius/vm/bootstrap.slate
--- slate/src/mobius/vm/bootstrap.slate	2004-09-06 19:55:13.000000000 +0100
+++ ../slate/src/mobius/vm/bootstrap.slate	2004-09-27 22:35:16.000000000 +0100
@@ -837,11 +837,18 @@
 [
   gen addObjectNamed: #ConsoleObject valued: gen newObject.
   gen ConsoleObject makeDelegateNamed: #traits valued: gen OddballTraits.
+
   gen addObjectNamed: #FileTraits valued: gen newObject.
   gen FileTraits makeDelegateNamed: #traits valued: gen TraitsTraits.
   gen FileTraits makeDelegateNamed: #parent0 valued: gen CloneableTraits.
   gen addObjectNamed: #FileProto valued: gen newObject.
   gen FileProto makeDelegateNamed: #traits valued: gen FileTraits.
+
+  gen addObjectNamed: #DirectoryTraits valued: gen newObject.
+  gen DirectoryTraits makeDelegateNamed: #traits valued: gen TraitsTraits.
+  gen DirectoryTraits makeDelegateNamed: #parent0 valued: gen CloneableTraits.
+  gen addObjectNamed: #DirectoryProto valued: gen newObject.
+  gen DirectoryProto makeDelegateNamed: #traits valued: gen DirectoryTraits.
 ].
 
 gen@(VM Bootstrap Generator traits) generateLobby
@@ -870,6 +877,7 @@
     #CompiledMethod -> gen CompiledMethodProto.
     #Interpreter -> gen InterpreterProto.
     #File -> gen FileProto.
+    #Directory -> gen DirectoryProto.
   } do: [| :assoc |
     gen PrototypesObject makeSlotNamed: assoc key valued: assoc value.
     gen addAccessorFor: assoc key on: gen PrototypesObject].
diff -Nu3 -r slate/src/mobius/vm/build.slate ../slate/src/mobius/vm/build.slate
--- slate/src/mobius/vm/build.slate	2004-09-08 19:10:56.000000000 +0100
+++ ../slate/src/mobius/vm/build.slate	2004-09-27 22:35:44.000000000 +0100
@@ -12,7 +12,8 @@
     'src/mobius/vm/base/dispatch.slate'.
     'src/mobius/vm/base/vm.slate'.
     'src/mobius/vm/ext/prims.slate'.
-    'src/mobius/vm/ext/file.slate'
+    'src/mobius/vm/ext/file.slate'.
+    'src/mobius/vm/ext/directory.slate'.
     }.
 
 "The names of the sources used to build the kernel REPL image, in necessary
@@ -47,11 +48,13 @@
      'src/lib/tuple.slate'.
      'src/lib/condition.slate'.
      'src/lib/condition-epilogue.slate'.
+     'src/lib/errno.slate'.
      'src/lib/stream.slate'.
      'src/lib/iterator.slate'.
      'src/lib/external.slate'.
      'src/lib/debugger.slate'.
      'src/lib/file.slate'.
+     'src/lib/directory.slate'.
      'src/lib/print.slate'.
      'src/mobius/types.slate'.
      'src/mobius/syntax/syntax.slate'.
diff -Nu3 -r slate/src/mobius/vm/ext/directory.slate ../slate/src/mobius/vm/ext/directory.slate
--- slate/src/mobius/vm/ext/directory.slate	1970-01-01 01:00:00.000000000 +0100
+++ ../slate/src/mobius/vm/ext/directory.slate	2004-09-27 22:41:07.000000000 +0100
@@ -0,0 +1,32 @@
+addHeaderNamed: '"slateDirectory.h"'.
+
+d at DirectoryTraits primitiveOpen: dname
+[| handle!LongInt |
+  handle: 'slate_openDirectory((struct ByteArray *) dname)' directly!LongInt.
+  interpreter stackPush: handle asObject.
+] `pidginPrimitive.
+
+d at DirectoryTraits primitiveClose: dirHandle
+[| result!LongInt |
+  result: 'slate_closeDirectory(dirHandle >> 1)' directly!LongInt.
+  interpreter stackPush: result asObject.
+] `pidginPrimitive.
+
+d at DirectoryTraits primitiveRead: dirHandle into: entryName
+[| entryNameLen!LongInt |
+  entryNameLen: 'slate_readDirectory(dirHandle >> 1, (struct ByteArray *) entryName)'
+			directly!LongInt.
+  interpreter stackPush: entryNameLen asObject.
+] `pidginPrimitive.
+
+d at DirectoryTraits primitiveGetCurrentDirectory: buffer
+[| len!LongInt |
+  len: 'slate_getCurrentDirectory((struct ByteArray *) buffer)' directly!LongInt.
+  interpreter stackPush: len asObject.
+] `pidginPrimitive.
+
+d at DirectoryTraits primitiveSetCurrentDirectory: wd
+[| result!LongInt |
+  result: 'slate_setCurrentDirectory((struct ByteArray *) wd)' directly!LongInt.
+  interpreter stackPush: result asObject.
+] `pidginPrimitive.
diff -Nu3 -r slate/src/mobius/vm/platform/boot.c ../slate/src/mobius/vm/platform/boot.c
--- slate/src/mobius/vm/platform/boot.c	2004-09-26 03:02:49.000000000 +0100
+++ ../slate/src/mobius/vm/platform/boot.c	2004-09-27 23:03:36.000000000 +0100
@@ -2,6 +2,7 @@
 #include <stdio.h>
 
 #include "file.h"
+#include "slateDirectory.h"
 #include "image.h"
 #include "slate.h"
 
@@ -58,7 +59,7 @@
 initModules ()
 {
 	initFileModule ();
-	initDirectoryModule ();
+	slate_initDirectoryModule ();
 }
 
 int
diff -Nu3 -r slate/src/mobius/vm/platform/includes/slateDirectory.h ../slate/src/mobius/vm/platform/includes/slateDirectory.h
--- slate/src/mobius/vm/platform/includes/slateDirectory.h	1970-01-01 01:00:00.000000000 +0100
+++ ../slate/src/mobius/vm/platform/includes/slateDirectory.h	2004-09-27 23:03:24.000000000 +0100
@@ -0,0 +1,69 @@
+#ifndef __SLATE_DIRECTORY_H__
+#define __SLATE_DIRECTORY_H__
+
+/**
+ * Directory module for Slate.
+ *
+ * Open issues:
+ *  - character encodings for strings such as filename, directory names
+ *  - weak pointers so that orphan directories can get collected
+ **/
+
+struct ByteArray;
+
+/**
+ * Initialises static storage for this module. Make sure this is only
+ * called once, at the start of the program.
+ **/
+extern void slate_initDirectoryModule(void);
+
+/**
+ * Takes a ByteArray containing a directory name string, allocates
+ * internal resources for managing this directory pointer, and returns
+ * an integer handle that can be used with other functions in this
+ * module.
+ *
+ * Returns <0 on error - the exact value will be (-errno) for the
+ * condition causing the error.
+ *
+ * Returns a handle >=0 on success, in which case the handle must
+ * later be passed in to closeDirectory to release resources.
+ **/
+extern int slate_openDirectory(struct ByteArray *dirName);
+
+/**
+ * Takes a handle returned by openDirectory and releases any
+ * associated allocated resources.
+ *
+ * Returns 0 for success, or <0 for error (exact value is -errno).
+ **/
+extern int slate_closeDirectory(int dirHandle);
+
+/**
+ * Reads the next entry from the directory handle passed in. Stores
+ * the file name in entNameBuffer.
+ *
+ * Returns >0 for success, in which case the returned value is the
+ * number of bytes stored into entNameBuffer; 0 for no more entries
+ * available; or <0 for error (exact value is -errno).
+ **/
+extern int slate_readDirectory(int dirHandle, struct ByteArray *entNameBuffer);
+
+/**
+ * Retrieves the current working directory from the system and stores
+ * its name into wdBuffer.
+ *
+ * Returns >=0 for success, in which case the returned value is the
+ * number of bytes stored into entNameBuffer; or <0 for error (exact
+ * value is -errno).
+ **/
+extern int slate_getCurrentDirectory(struct ByteArray *wdBuffer);
+
+/**
+ * Changes the current working directory of the system.
+ *
+ * Returns 0 for success, or <0 for error (exact value is -errno).
+ **/
+extern int slate_setCurrentDirectory(struct ByteArray *newWd);
+
+#endif /* __SLATE_DIRECTORY_H__ */
diff -Nu3 -r slate/src/mobius/vm/platform/unix/Makefile ../slate/src/mobius/vm/platform/unix/Makefile
--- slate/src/mobius/vm/platform/unix/Makefile	2004-09-27 21:24:44.000000000 +0100
+++ ../slate/src/mobius/vm/platform/unix/Makefile	2004-09-27 23:00:23.000000000 +0100
@@ -8,7 +8,7 @@
 COPTFLAGS=-O2 -fomit-frame-pointer -DNDEBUG=1 -s
 CFLAGS=$(COPTFLAGS) -I../includes -I.. -I. -I${slateroot}
 LIBS=-lm
-OBJS=../boot.o ../ansifile.o directory.o file.o main.o ${slateroot}/$(VM).o
+OBJS=../boot.o ../ansifile.o slateDirectory.o file.o main.o ${slateroot}/$(VM).o
 prefix=/usr
 exec_prefix=$(prefix)/bin
 execname=slate
@@ -22,7 +22,7 @@
 clean:
 	-rm -f $(OBJS) ${slateroot}/$(VM) ${slateroot}/slatevm.h
 
-$(VM): $(OBJS)
+$(VM): ${slateroot}/slatevm.h $(OBJS)
 	$(CC) $(CFLAGS) -o $(slateroot)/$(VM) $(OBJS) $(LIBS)
 
 install: ${slateroot}/$(VM) ${slateroot}/$(VM).h
@@ -39,4 +39,4 @@
 $(OBJS) : ${slateroot}/slatevm.h
 
 ${slateroot}/slatevm.h : ${slateroot}/$(VM).h
-	ln -s -f $< $@
+	ln -s -f $(VM).h $@
diff -Nu3 -r slate/src/mobius/vm/platform/unix/slateDirectory.c ../slate/src/mobius/vm/platform/unix/slateDirectory.c
--- slate/src/mobius/vm/platform/unix/slateDirectory.c	1970-01-01 01:00:00.000000000 +0100
+++ ../slate/src/mobius/vm/platform/unix/slateDirectory.c	2004-09-27 23:03:21.000000000 +0100
@@ -0,0 +1,120 @@
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "slateDirectory.h"
+#include "slatevm.h"
+
+#define DIRS_MAXIMUM 256
+
+static DIR *dirs[DIRS_MAXIMUM];
+
+#define BYTEARRAY_LEN(x)	(PSObject_payloadSize((struct Object *) (x)))
+
+void slate_initDirectoryModule(void) {
+  int i;
+  for (i = 0; i < DIRS_MAXIMUM; i++) {
+    dirs[i] = NULL;
+  }
+}
+
+static int findDirectorySlot(void) {
+  int i;
+  for (i = 0; i < DIRS_MAXIMUM; i++) {
+    if (dirs[i] == NULL)
+      return i;
+  }
+  return -EMFILE;
+}
+
+int slate_openDirectory(struct ByteArray *dirName) {
+  int dirHandle = findDirectorySlot();
+  if (dirHandle < 0) {
+    return dirHandle;
+  } else {
+    size_t nameLen = BYTEARRAY_LEN(dirName);
+    char *name = malloc(nameLen + 1);
+    if (name == NULL) {
+      return -errno;
+    } else {
+      memcpy(name, dirName->elements, nameLen);
+      name[nameLen] = '\0';
+      dirs[dirHandle] = opendir(name);
+      if (dirs[dirHandle] == NULL) {
+	int savedErrno = errno;
+	free(name);
+	return -savedErrno;
+      } else {
+	free(name);
+	return dirHandle;
+      }
+    }
+  }
+}
+
+int slate_closeDirectory(int dirHandle) {
+  if (dirs[dirHandle] != NULL) {
+    closedir(dirs[dirHandle]);
+    dirs[dirHandle] = NULL;
+    return 0;
+  } else {
+    return -EINVAL;
+  }
+}
+
+int slate_readDirectory(int dirHandle, struct ByteArray *entNameBuffer) {
+  if (dirHandle < 0)
+    return -EINVAL;
+
+  struct dirent ent;
+  struct dirent *result = &ent;
+
+  int resultCode = readdir_r(dirs[dirHandle], result, &result);
+  if (resultCode != 0) {
+    /* An error situation. */
+    assert(resultCode >= 0);	/* TODO: read SUSv2. The Darwin manpage is unclear about this */
+    return -resultCode;
+  }
+
+  if (result == NULL) {
+    /* End of the directory. */
+    return 0;
+  }
+
+  int entLen = strlen(result->d_name);
+  if (entLen == 0) {
+    /* Bizarre! Make up a reasonable response. */
+    return -ENOENT;
+  }
+
+  assert(BYTEARRAY_LEN(entNameBuffer) >= entLen);
+  memcpy(entNameBuffer->elements, result->d_name, entLen);
+  return entLen;
+}
+
+int slate_getCurrentDirectory(struct ByteArray *wdBuffer) {
+  if (getcwd(wdBuffer->elements, BYTEARRAY_LEN(wdBuffer)) == NULL)
+    return -errno;
+
+  return strlen(wdBuffer->elements);
+}
+
+int slate_setCurrentDirectory(struct ByteArray *newWd) {
+  size_t wdLen = BYTEARRAY_LEN(newWd);
+  char *wd = malloc(wdLen + 1);
+  if (wd == NULL)
+    return -errno;
+
+  memcpy(wd, newWd->elements, wdLen);
+  wd[wdLen] = '\0';
+  if (chdir(wd) == -1) {
+    int savedErrno = errno;
+    free(wd);
+    return -savedErrno;
+  } else {
+    free(wd);
+    return 0;
+  }
+}


More information about the Slate mailing list