/*
			DIRLIB for OS/2
			---------------

Enclosed is an implementation of the `dirlib' package for OS/2.  It is
derived from the MS-DOS implementation by Matt whose (slightly edited)
comments follow.

The implementation is targeted for MSC, although any reasonably
competent C compiler should manage.  The package consists of:

	dir.h		the header file
	dir.c		the functions
	testdir.c	a q&d test program

The package tries to view directory naming in a Un*x light; in particular,
directories such as '/.' and '/..' (as well as `.' and `..' if your
current directory is root) are understood.   Indefinite paths like
`/../.././../..' will correctly refer to the root (of the particular disk).
Names such as `a:////./../' are okay too.

I've tried to be as sensible about DTA's as possible, since you never
know who will be using one; they are set before use, and reset afterwards.

There is some cruft in the package, namely the way `seekdir' and
`telldir' are done.  The code was derived from a little experimentation,
and may not work after a certain point (although I believe the 2.x version
to be solid).  Caveat utilitor.

Documentation for the package is available in the public domain; the
package's functionality was derived from this documentation.

Bug reports and comments are welcome.  Enjoy!

				- Matt

-------
UUCP:	{ucbvax,ihnp4,randvax,trwrb!trwspp,ism780}!ucla-cs!matt
ARPA:	matt@LOCUS.UCLA.EDU
Ph:	(213) 825-2756

--------
Modified for use in dmake by Dennis Vadura.  Mostly just clean up and an
effort to make correctly typed objects are passed to functions in find.c.
Also deleted all dos version 2.0 specific code.  It is not required any
more.
*/

/*
 * revision history:
 *
 *	VER	MM/DD/YY	COMMENTS
 *	----	--------	--------
 *	0.99	02/24/86	Beta release to INTERNET
 */

#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <dos.h>

#include "dirent.h"
#include "slash.h"

extern int find_err;


static char *getdcwd(int drive)
int drive;
{
	static char xcwd[260];
	unsigned long cc = sizeof xcwd;
	if (DosQueryCurrentDir(drive, xcwd, &cc))
		xcwd[0] = 0;
	return xcwd;
}



/*
 * opendir
 */

/*
#define SUFFIX	"\\*.*"
#define	SLASH	"\\"

Changed by Jon Saxton on 15 Mar 2004 to conform with UNIX conventions on
OS/2. The *.* usage was a carryover from CP/M and has no place in modern
implementations.  The use of \ instead of / is really only pertinent to
the command processor.  In C code the / works quite well.  */

#include "slash.h"
#define SUFFIX SLASH "*"

#define streq(a,b)	(strcmp(a,b)==0)

DIR *
opendir(name)
char *name;
{
   register DIR *nd;
   char *cwd;
   char drive[3];
   int atroot = 0;
   int rooted = 0;

   /*
   * hack off drive designator if present
   */

   if (name[1] == ':') {
      cwd = getdcwd(toupper(name[0]) - 'A' + 1);
      drive[0] = name[0]; drive[1] = ':'; drive[2] = '\0';
      name += 2;
   }
   else {
      cwd = getdcwd(0);
      drive[0] = '\0';
   }

   /* is the name 'rooted'? */
   if ((*name == '/') || (*name == '\\')) ++rooted;

   /* see if we are at the root directory for this device */
   if (!*cwd) ++atroot;

   /*
   * MSDOS '/' doesn't have a '.' or '..'
   * also, double '/' sequences don't make sense.
   * many ported programs expect them to work, so we fix it up...
   */

   /* chop off leading . and .. if at root */
   if (atroot && (*name == '.')) {
      switch (*++name) {
	 case '\0':
	 case '/':
	 case '\\':
	    break;

	 case '.':
	    switch (*++name) {
	       case '\0':
	       case '/':
	       case '\\':
		    break;
	       default:
		    --name;
		    --name;
	    }
	    break;

	 default:
	    --name;
      }
   }

   /* chop off leading /'s, /.'s and /..'s to make naming sensible */
   while (*name && ((*name == '/') || (*name == '\\'))) {
      if (*++name == '.') {
	 switch (*++name) {
	    case '\0':
	    case '/':
	    case '\\':
	       break;

	    case '.':
	       switch (*++name) {
		  case '\0':
		  case '/':
		  case '\\':
		      break;

		 default:
		      --name;
		      --name;
	       }
	       break;

	    default:
	       --name;
	 }
      }
   }


   /*
   * name should now look like: path/path/path
   * we must now construct name based on whether or not it
   * was 'rooted' (started with a /)
   */

   if (rooted) cwd = "";

   /* construct DIR */
   if (!(nd = (DIR *)malloc(
      sizeof(DIR)+strlen(drive)+strlen(cwd)+strlen(SLASH)+
      strlen(name)+strlen(SUFFIX))))
	  return (DIR *) 0;

   /* create long name */
   strcpy(nd->dd_name, drive);
   if (*cwd) {
      strcat(nd->dd_name, SLASH);
      strcat(nd->dd_name, cwd);
   }
   if (*name) {
      strcat(nd->dd_name, SLASH);
      strcat(nd->dd_name, name);
   }
   strcat(nd->dd_name, SUFFIX);

   /* search */
   if (!findfirst(&nd->dd_name[0], &nd->dd_dta)) {
      free((char *)nd);
      errno = ENOENT;
      return (DIR *) 0;
   }
   nd->dd_stat = 0;
   return nd;
}


struct dirent *
readdir(dirp)
DIR *dirp;
{
   static struct dirent dir;

   if (dirp->dd_stat)
      return (struct dirent *) 0;

   /* format structure */
   dir.d_ino = 0; /* not valid for DOS */
   dir.d_reclen = 0;
   strcpy(dir.d_name, dirp->dd_dta.name);
   dir.d_namlen = strlen(dir.d_name);
   strlwr(dir.d_name); /* DOSism */

   /* read ahead */
   if (findnext(&dirp->dd_dta) != NULL)
      dirp->dd_stat = 0;
   else
      dirp->dd_stat = find_err;

   return &dir;
}


void
closedir(dirp)
DIR *dirp;
{
   free((char *)dirp);
}


void
seekdir(dirp, pos)
DIR *dirp;
long pos;
{
   /*
   * check against DOS limits
   */

   if ((pos < 0) || (pos > 4095)) {
      dirp->dd_stat = 1;
      return;
   }

   *(short *)&dirp->dd_dta.fcb[13] = pos + 1;

   /* read ahead */
   if (findnext(&dirp->dd_dta))
      dirp->dd_stat = 0;
   else
      dirp->dd_stat = find_err;
}


long
telldir(dirp)
DIR *dirp;
{
   return (long) (*(short *)&dirp->dd_dta.fcb[13] - 2);
}
