DirStr.st
author claus
Sun, 17 Sep 1995 19:57:55 +0200
changeset 438 6c03b347369f
parent 437 a005e97d261e
child 475 b57530aa1b0a
permissions -rw-r--r--
.

"
 COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

FileStream subclass:#DirectoryStream
       instanceVariableNames:'dirPointer readAhead'
       classVariableNames:''
       poolDictionaries:''
       category:'Streams-External'
!

DirectoryStream comment:'
COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

$Header: /cvs/stx/stx/libbasic/Attic/DirStr.st,v 1.23 1995-09-17 17:56:01 claus Exp $
'!

!DirectoryStream class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libbasic/Attic/DirStr.st,v 1.23 1995-09-17 17:56:01 claus Exp $
"
!

documentation
"
    Instances of DirectoryStream allow reading a file-directory,
    as if it was a stream of filenames.
    Basically, its an interface to opendir, readdir and closedir.
"
! !

!DirectoryStream primitiveDefinitions!

%{

#include <stdio.h>
#define _STDIO_H_INCLUDED_

#include <errno.h>
#define _ERRNO_H_INCLUDED_

#ifndef transputer
# include <sys/types.h>
# include <sys/stat.h>

# ifdef HAS_OPENDIR
#  include <sys/types.h>
#  ifdef NEXT
#   include <sys/dir.h>
#  else
#   ifndef VMS
#    include <dirent.h>
#   endif /* not VMS */
#  endif
# endif
#endif

/*
 * on some systems errno is a macro ... check for it here
 */
#ifndef errno
 extern errno;
#endif

%}
! !

!DirectoryStream primitiveFunctions!

%{
#ifdef VMS

/*
**  VMS readdir() routines.
**  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
**  This code has no copyright.
*/

/* 12-NOV-1990 added d_namlen field and special case "." name -GJC@MITECH.COM 
 */

#ifndef _STDIO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
#endif

#ifndef _CTYPE_H_INCLUDED_
# include <ctype.h>
# define _CTYPE_H_INCLUDED_
#endif

#ifndef _ERRNO_H_INCLUDED_
# include <errno.h>
# define _ERRNO_H_INCLUDED_
#endif

#ifndef _DESCRIP_H_INCLUDED_
# include <descrip.h>
# define _DESCRIP_H_INCLUDED_
#endif

#ifndef _RMSDEF_H_INCLUDED_
# include <rmsdef.h>
# define _RMSDEF_H_INCLUDED_
#endif

/*
 * actually, the following has to go into dirent.h ...
 */
/* BEGIN dirent.h
 *
**  Header file for VMS readdir() routines.
**  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
**  This code has no copyright.
**
**  You must #include <descrip.h> before this file.
*/

/* 12-NOV-1990 added d_namlen field -GJC@MITECH.COM */

    /* Data structure returned by READDIR(). */
struct dirent {
    char        d_name[100];            /* File name            */
    int         d_namlen;
    int                 vms_verscount;          /* Number of versions   */
    int                 vms_versions[20];       /* Version numbers      */
};

    /* Handle returned by opendir(), used by the other routines.  You
     * are not supposed to care what's inside this structure. */
typedef struct _dirdesc {
    long                        context;
    int                                 vms_wantversions;
    char                        *pattern;
    struct dirent               entry;
    struct dsc$descriptor_s     pat;
} DIR;


#define rewinddir(dirp)                 seekdir((dirp), 0L)


extern DIR              *opendir();
extern struct dirent    *readdir();
extern long             telldir();
extern void             seekdir();
extern void             closedir();
extern void             vmsreaddirversions();
#define _DIRENT_H_INCLUDED_
/*
 * END dirent.h
 */


    /* Number of elements in vms_versions array */
#define VERSIZE(e)      (sizeof e->vms_versions / sizeof e->vms_versions[0])

    /* Linked in later. */
extern char     *strrchr();
extern char     *strcpy();
/*  Don't need this when all these programs are lumped together.    RLD
extern char     *malloc();
*/

/*
**  Open a directory, return a handle for later use.
*/
DIR *
opendir(name)
    char        *name;
{
    DIR                 *dd;

    /* Get memory for the handle, and the pattern. */
    if ((dd = (DIR *)malloc(sizeof *dd)) == NULL) {
	errno = ENOMEM;
	return NULL;
    }

    if (strcmp(".",name) == 0) name = "";

    dd->pattern = malloc((unsigned int)(strlen(name) + sizeof "*.*" + 1));
    if (dd->pattern == NULL) {
	free((char *)dd);
	errno = ENOMEM;
	return NULL;
    }

    /* Fill in the fields; mainly playing with the descriptor. */
    (void)sprintf(dd->pattern, "%s*.*", name);
    dd->context = 0;
    dd->vms_wantversions = 0;
    dd->pat.dsc$a_pointer = dd->pattern;
    dd->pat.dsc$w_length = strlen(dd->pattern);
    dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
    dd->pat.dsc$b_class = DSC$K_CLASS_S;

    return dd;
}

/*
**  Set the flag to indicate we want versions or not.
*/
void
vmsreaddirversions(dd, flag)
    DIR                 *dd;
    int                 flag;
{
    dd->vms_wantversions = flag;
}

/*
**  Free up an opened directory.
*/
void
closedir(dd)
    DIR                 *dd;
{
    free(dd->pattern);
    free((char *)dd);
}

/*
**  Collect all the version numbers for the current file.
*/
static void
collectversions(dd)
    DIR                                 *dd;
{
    struct dsc$descriptor_s     pat;
    struct dsc$descriptor_s     res;
    struct dirent               *e;
    char                        *p;
    char                        buff[sizeof dd->entry.d_name];
    int                                 i;
    char                        *text;
    long                        context;

    /* Convenient shorthand. */
    e = &dd->entry;

    /* Add the version wildcard, ignoring the "*.*" put on before */
    i = strlen(dd->pattern);
    text = malloc((unsigned int)(i + strlen(e->d_name)+ 2 + 1));
    if (text == NULL)
	return;
    (void)strcpy(text, dd->pattern);
    (void)sprintf(&text[i - 3], "%s;*", e->d_name);

    /* Set up the pattern descriptor. */
    pat.dsc$a_pointer = text;
    pat.dsc$w_length = strlen(text);
    pat.dsc$b_dtype = DSC$K_DTYPE_T;
    pat.dsc$b_class = DSC$K_CLASS_S;

    /* Set up result descriptor. */
    res.dsc$a_pointer = buff;
    res.dsc$w_length = sizeof buff - 2;
    res.dsc$b_dtype = DSC$K_DTYPE_T;
    res.dsc$b_class = DSC$K_CLASS_S;

    /* Read files, collecting versions. */
    for (context = 0; e->vms_verscount < VERSIZE(e); e->vms_verscount++) {
	if (lib$find_file(&pat, &res, &context) == RMS$_NMF || context == 0)
	    break;
	buff[sizeof buff - 1] = '\0';
	if (p = strchr(buff, ';'))
	    e->vms_versions[e->vms_verscount] = atoi(p + 1);
	else
	    e->vms_versions[e->vms_verscount] = -1;
    }

    free(text);
}

/*
**  Read the next entry from the directory.
*/
struct dirent *
readdir(dd)
    DIR                                 *dd;
{
    struct dsc$descriptor_s     res;
    char                        *p;
    char                        buff[sizeof dd->entry.d_name];
    int                                 i;

    /* Set up result descriptor, and get next file. */
    res.dsc$a_pointer = buff;
    res.dsc$w_length = sizeof buff - 2;
    res.dsc$b_dtype = DSC$K_DTYPE_T;
    res.dsc$b_class = DSC$K_CLASS_S;
    if (lib$find_file(&dd->pat, &res, &dd->context) == RMS$_NMF
     || dd->context == 0L)
	/* None left... */
	return NULL;

    /* Force the buffer to end with a NUL. */
    buff[sizeof buff - 1] = '\0';
    for (p = buff; !isspace(*p); p++)
	;
    *p = '\0';

    /* Skip any directory component and just copy the name. */
    if (p = strchr(buff, ']'))
	(void)strcpy(dd->entry.d_name, p + 1);
    else
	(void)strcpy(dd->entry.d_name, buff);

    /* Clobber the version. */
    if (p = strchr(dd->entry.d_name, ';'))
	*p = '\0';

    dd->entry.d_namlen = strlen(dd->entry.d_name);

    dd->entry.vms_verscount = 0;
    if (dd->vms_wantversions)
	collectversions(dd);
    return &dd->entry;
}

/*
**  Return something that can be used in a seekdir later.
*/
long
telldir(dd)
    DIR                 *dd;
{
    return dd->context;
}

/*
**  Return to a spot where we used to be.
*/
void
seekdir(dd, pos)
    DIR                 *dd;
    long        pos;
{
    dd->context = pos;
}

#endif /* VMS */
%}
! !

!DirectoryStream methodsFor:'instance release'!

closeFile
    "low level close of a directoryStream"
%{
#ifdef HAS_OPENDIR
    OBJ dp;

    if ((dp = _INST(dirPointer)) != nil) {
	_INST(dirPointer) = nil;
	closedir( (DIR *)(MKFD(dp)) );
    }
#endif
%}
! !

!DirectoryStream class methodsFor:'instance creation'!

directoryNamed:dirName
    "return a DirectoryStream for directory named dirName, aString"

    |newStream|

    newStream := (self basicNew) pathName:dirName.
    newStream openForReading isNil ifTrue:[^nil].
    ^ newStream
! !

!DirectoryStream methodsFor:'access reading'!

nextLine
    "return the next filename as a string"

    |prevEntry nextEntry|
%{
#ifdef HAS_OPENDIR
    DIR *d;
#ifdef NEXT
    struct direct *dp;
#else
    struct dirent *dp;
#endif
    OBJ dirP;

    if ((dirP = _INST(dirPointer)) != nil) {
	d = (DIR *)MKFD(dirP);

	__BEGIN_INTERRUPTABLE__
	errno = 0;
	do {
	    dp = readdir(d);
	} while ((dp == NULL) && (errno == EINTR));
	__END_INTERRUPTABLE__

	if (dp != NULL) {
	    nextEntry = _MKSTRING((char *)(dp->d_name) COMMA_CON);
	} else {
	    _INST(hitEOF) = true;
	    if (errno) {
		_INST(lastErrorNumber) = _MKSMALLINT(errno);
	    }
	}
    }
#endif
%}.
    lastErrorNumber notNil ifTrue:[^ self ioError].
    prevEntry := readAhead.
    readAhead := nextEntry.
    ^ prevEntry
! !

!DirectoryStream methodsFor:'private'!

openForReading
    "open the file for readonly"

    |ok|

    mode := #readonly.
%{
#ifdef HAS_OPENDIR
    DIR *d;
    OBJ path;

    ok = false;
    if (_INST(dirPointer) == nil) {
	path = _INST(pathName);
	if (__isString(path)) {
	    __BEGIN_INTERRUPTABLE__
	    errno = 0;
	    do {
		d = opendir((char *) _stringVal(path));
	    } while ((d == NULL) && (errno == EINTR));
	    __END_INTERRUPTABLE__

	    if (d == NULL) {
		_INST(lastErrorNumber) = _MKSMALLINT(errno);
	    } else {
		_INST(dirPointer) = MKOBJ(d);
		ok = true;
	    }
	}
    }
#endif
%}.
    ok isNil ifTrue:[
	"
	 opendir not avalable - use slower pipe
	"
	^ PipeStream readingFrom:('cd ' , pathName , '; ls -a')
    ].

    (ok == true) ifTrue:[
	Lobby register:self.
	self nextLine. "read 1st entry into readAhead buffer"
	^ self
    ].
    dirPointer notNil ifTrue:[^ self errorOpen].
    lastErrorNumber notNil ifTrue:[^ self openError].
    ^ nil
!

reOpen
    "reOpen the stream after image restart"

    dirPointer := nil.
    super reOpen
! !

!DirectoryStream methodsFor:'testing'!

atEnd
    "return true, if position is at end"

    ^ readAhead == nil
! !

!DirectoryStream methodsFor:'closing'!

close
    "close the stream - tell operating system"

    dirPointer notNil ifTrue:[
	Lobby unregister:self.
	self closeFile.
	dirPointer := nil
    ]
! !