/* 
 *	Directory and path functions
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "types.h"

#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#ifdef HAVE_PATHS_H
#  include <paths.h>	/* for _PATH_TMP */
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif


#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#  define NAMLEN(dirent) zlen((dirent)->d_name)
#else
#  ifdef HAVE_SYS_DIRENT_H
#    include <sys/dirent.h>
#    define NAMLEN(dirent) zlen((dirent)->d_name)
#  else
#    define direct dirent
#    define NAMLEN(dirent) (dirent)->d_namlen
#    ifdef HAVE_SYS_NDIR_H
#      include <sys/ndir.h>
#    else
#      ifdef HAVE_SYS_DIR_H
#        include <sys/dir.h>
#      else
#        ifdef HAVE_NDIR_H
#          include <ndir.h>
#        else
#          ifndef __MSDOS__
#            include "dir.c"
#          endif
#        endif
#      endif
#    endif
#  endif
#endif

#ifdef __MSDOS__	/* paths in MS-DOS can include a drive letter followed by semicolon */
#define	do_if_drive_letter(path, command) do { \
						if ((path)[0] && (path)[1] == ':') { \
							command; \
						} \
					} while(0)
#else
#define do_if_drive_letter(path, command)	do { } while(0)
#endif
#define skip_drive_letter(path)	do_if_drive_letter((path), (path) += 2)

#ifndef		_PATH_TMP
#  ifdef __MSDOS__
#    define	_PATH_TMP	""
#  else
#    define	_PATH_TMP	"/tmp/"
#  endif
#endif

#ifndef PATH_MAX
/* #warning is gcc extension
  #warning What should we include to have PATH_MAX defined?
*/
#define PATH_MAX	4096
#endif

/********************************************************************/
char *joesep(char *path)
{
#ifdef __MSDOS__
	int x;

	for (x = 0; path[x]; ++x)
		if (path[x] == '\\')
			path[x] = '/';
#endif
	return path;
}
/********************************************************************/
char *namprt(const char *path)
{
	const char *z;

	skip_drive_letter(path);
	z = path + slen(path);
	while ((z != path) && (z[-1] != '/'))
		--z;
	return vsncpy(NULL, 0, sz(z));
}
/********************************************************************/
char *namepart(char *tmp, ptrdiff_t tmpsiz, const char *path)
{
	const char *z;

	skip_drive_letter(path);
	z = path + zlen(path);
	while ((z != path) && (z[-1] != '/'))
		--z;
	return zlcpy(tmp, tmpsiz, z);
}
/********************************************************************/
char *dirprt(const char *path)
{
	const char *b = path;
	const char *z = path + slen(path);

	skip_drive_letter(b);
	while ((z != b) && (z[-1] != '/'))
		--z;
	return vsncpy(NULL, 0, path, z - path);
}
/********************************************************************/
char *begprt(const char *path)
{
	const char *z = path + slen(path);
	int drv = 0;

	do_if_drive_letter(path, drv = 2);
	while ((z != path + drv) && (z[-1] == '/'))
		--z;
	if (z == path + drv)
		return vsncpy(NULL, 0, sz(path));
	else {
		while ((z != path + drv) && (z[-1] != '/'))
			--z;
		return vsncpy(NULL, 0, path, z - path);
	}
}
/********************************************************************/
char *endprt(const char *path)
{
	const char *z = path + slen(path);
	int drv = 0;

	do_if_drive_letter(path, drv = 2);
	while ((z != path + drv) && (z[-1] == '/'))
		--z;
	if (z == path + drv)
		return vsncpy(NULL, 0, sc(""));
	else {
		while (z != path + drv && z[-1] != '/')
			--z;
		return vsncpy(NULL, 0, sz(z));
	}
}
/********************************************************************/
int mkpath(char *path)
{
	char *s;

	if (path[0] == '/') {
		if (chddir("/"))
			return 1;
		s = path;
		goto in;
	}

	while (path[0]) {
		char c;

		for (s = path; (*s) && (*s != '/'); s++) ;
		c = *s;
		*s = 0;
		if (chddir(path)) {
			if (mkdir(path, 0777))
				return 1;
			if (chddir(path))
				return 1;
		}
		*s = c;
	      in:
		while (*s == '/')
			++s;
		path = s;
	}
	return 0;
}
/********************************************************************/
/* Create a temporary file */
/********************************************************************/
char *mktmp(const char *where)
{
#ifndef HAVE_MKSTEMP
	static unsigned seq = 0;
#endif
	char *name;
	int fd;
	ptrdiff_t namesize;

	if (!where)
		where = getenv("TEMP");
	if (!where)
		where = _PATH_TMP;

	namesize = zlen(where) + 16;
	name = vsmk(namesize);	/* [G.Ghibo'] we need to use vsmk() and not malloc() as
				   area returned by mktmp() is destroyed later with
				   vsrm(); */
#ifdef HAVE_MKSTEMP
	joe_snprintf_1(name, (size_t)namesize, "%s/joe.tmp.XXXXXX", where);
	if((fd = mkstemp(name)) == -1)
		return NULL;	/* FIXME: vflsh() and vflshf() */
				/* expect mktmp() always succeed!!! */

	fchmod(fd, 0600);       /* Linux glibc 2.0 mkstemp() creates it with */
				/* 0666 mode --> change it to 0600, so nobody */
				/* else sees content of temporary file */
	close(fd);

#else
      loop:
	seq = (seq + 1) % 1000;
	joe_snprintf_3(name, namesize, "%s/joe.tmp.%03u%03u", where, seq, (unsigned) time(NULL) % 1000);
	if ((fd = open(name, O_RDONLY)) != -1) {
		close(fd);
		goto loop;	/* FIXME: possible endless loop --> DoS attack */
	}
	if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1)
		return NULL;	/* FIXME: see above */
	else
		close(fd);
#endif
	return name;
}
/********************************************************************/
int rmatch(const char *a, const char *b)
{
	int flag, inv, c;

	for (;;)
		switch (*a) {
		case '*':
			++a;
			do {
				if (rmatch(a, b))
					return 1;
			} while (*b++);
			return 0;
		case '[':
			++a;
			flag = 0;
			if (*a == '^') {
				++a;
				inv = 1;
			} else
				inv = 0;
			if (*a == ']')
				if (*b == *a++)
					flag = 1;
			while (*a && (c = *(const unsigned char *)a++) != ']')
				if ((c == '-') && (a[-2] != '[') && (*a)) {
					if ((*(const unsigned char *)b >= ((const unsigned char *)a)[-2]) && (*(const unsigned char *)b <= *(const unsigned char *)a))
						flag = 1;
				} else if (*(const unsigned char *)b == c)
					flag = 1;
			if ((!flag && !inv) || (flag && inv) || (!*b))
				return 0;
			++b;
			break;
		case '?':
			++a;
			if (!*b)
				return 0;
			++b;
			break;
		case 0:
			if (!*b)
				return 1;
			else
				return 0;
		default:
			if (*a++ != *b++)
				return 0;
		}
}
/********************************************************************/
int isreg(const char *s)
{
	int x;

	for (x = 0; s[x]; ++x)
		if ((s[x] == '*') || (s[x] == '?') || (s[x] == '['))
			return 1;
	return 0;
}
/********************************************************************/
#ifdef __MSDOS__
#include <dos.h>
#include <dir.h>

struct direct {
	char d_name[16];
} direc;
int dirstate = 0;
struct ffblk ffblk;
char *dirpath = NULL;

void *opendir(char *path)
{
	dirstate = 0;
	return &direc;
}

void closedir()
{
}

struct direct *readdir()
{
	int x;

	if (dirstate) {
		if (findnext(&ffblk))
			return NULL;
	} else {
		if (findfirst("*.*", &ffblk, FA_DIREC))
			return NULL;
		dirstate = 1;
	}

	zlcpy(direc.d_name, SIZEOF(direc.d_name), ffblk.ff_name);
	for (x = 0; direc.d_name[x]; ++x)
		direc.d_name[x] = tolower(direc.d_name[x]);
	return &direc;
}
#endif
/********************************************************************/
char **rexpnd(const char *word)
{
	DIR *dir;
	char **lst = NULL;

	struct dirent *de;
	dir = opendir(".");
	if (dir) {
		while ((de = readdir(dir)) != NULL)
			if (zcmp(".", de->d_name))
				if (rmatch(word, de->d_name))
					lst = vaadd(lst, vsncpy(NULL, 0, sz(de->d_name)));
		closedir(dir);
	}
	return lst;
}

char **rexpnd_cmd_cd(const char *word)
{
	DIR *dir;
	char **lst = NULL;

	struct dirent *de;
	dir = opendir(".");
	if (dir) {
		while ((de = readdir(dir)) != NULL)
			if (zcmp(".", de->d_name))
				if (rmatch(word, de->d_name) && !access(de->d_name, X_OK))
					lst = vaadd(lst, vsncpy(NULL, 0, sz(de->d_name)));
		closedir(dir);
	}
	return lst;
}

char **rexpnd_cmd_path(const char *word)
{
	char *raw_path = getenv("PATH");
	char **lst = NULL;
	struct dirent *de;
	char **path;
	ptrdiff_t x;
	if (raw_path) {
		path = vawords(NULL, sz(raw_path), sc(":"));
		for (x = 0; path[x]; ++x) {
			DIR *dir;
			if (!chpwd(path[x])) {
				dir = opendir(".");
				if (dir) {
					while ((de = readdir(dir)) != NULL) {
						struct stat buf[1];
						if (rmatch(word, de->d_name) && !stat(de->d_name, buf) && S_ISREG(buf->st_mode) && !access(de->d_name, X_OK))
							lst = vaadd(lst, vsncpy(NULL, 0, sz(de->d_name)));
					}
					closedir(dir);
				}
			}
		}
		varm(path);
	}
	return lst;
}

/********************************************************************/
char **rexpnd_users(const char *word)
{
	char **lst = NULL;
	struct passwd *pw;

	while((pw=getpwent()))
		if (rmatch(word+1, pw->pw_name)) {
			char *t = vsncpy(NULL,0,sc("~"));
			lst = vaadd(lst, vsncpy(sv(t),sz(pw->pw_name)));
			}
	endpwent();

	return lst;
}
/********************************************************************/
int chpwd(const char *path)
{
#ifdef __MSDOS__
	char buf[256];
	int x;

	if (!path)
		return 0;
	if ((path[0]) && (path[1] == ':')) {
		if (_chdrive(path[0] & 0x1F))
			return -1;
		path += 2;
	}
	if (!path[0])
		return 0;
	zlcpy(buf, SIZEOF(buf), path);
	x = zlen(buf);
	while (x > 1) {
		--x;
		if ((buf[x] == '/') || (buf[x] == '\\'))
			buf[x] = 0;
		else
			break;
	}
	return chdir(buf);
#else
	if ((!path) || (!path[0]))
		return 0;
	return chdir(path);
#endif
}

/* The pwd function */
char *pwd(void)
{
	static char buf[PATH_MAX];
	char	*ret;

#ifdef HAVE_GETCWD
	ret = getcwd(buf, PATH_MAX - 1);
#else
	ret = getwd(buf);
#endif
	buf[PATH_MAX - 1] = '\0';

	return ret;
}

/* Simplify prefix by using ~ */
/* Expects s to have trailing / */

char *simplify_prefix(const char *s)
{
	const char *t = getenv("HOME");
	char *n;

#ifdef junk
	char *org = pwd();
	/* Normalize home */
	if (t && !chpwd(t)) {
		t = pwd();
	} else {
		t = 0;
	}
	chpwd(org);
#endif

	/* If current directory is prefixed with home directory, use ~... */
	if (t && !zncmp(s,t,zlen(t)) && (!s[zlen(t)] || s[zlen(t)]=='/')) {
		n = vsncpy(NULL,0,sc("~/"));
		/* If anything more than just the home directory, add it */
		if (s[zlen(t)]) {
			n = vsncpy(sv(n),s+zlen(t)+1,zlen(s+zlen(t)+1));
		}
	} else {
		n = vsncpy(NULL,0,sz(s));
	}
	return n;
}

char *dequotevs(char *s)
{
	ptrdiff_t x;
	char *d = vsensure(NULL, sLEN(s));
	for (x = 0; x != sLEN(s); ++x)
		if (s[x] == '\\') {
			if (x + 1 != sLEN(s)) {
				++x;
				d = vsadd(d, s[x]);
			}
		} else
			d = vsadd(d, s[x]);
	vsrm(s);
	return d;
}
