/* Glob.c */

#include "Sys.h"

#include <ctype.h>
#include <pwd.h>
#include <signal.h>
#include <setjmp.h>

#include "Util.h"
#include "RCmd.h"
#include "Glob.h"
#include "Xfer.h"
#include "List.h"
#include "Recent.h"
#include "Main.h"

/* Needed in case we're interrupted during local globbing. */
jmp_buf gLocalGlobJmp;

extern UserInfo gUserInfo;
extern RemoteSiteInfo gRmtInfo;
extern int gTrace;


/* This does "tilde-expansion."  Examples:
 * ~/pub         -->  /usr/gleason/pub
 * ~pdietz/junk  -->  /usr/pdietz/junk
 */
void ExpandTilde(char *pattern, size_t siz)
{
	string pat;
	char *cp, *rest, *firstent;
	struct passwd *pw;

	if ((pattern[0] == '~') &&
	(isalnum(pattern[1]) || (pattern[1] == '/') || (pattern[1] == '\0'))) {
		STRNCPY(pat, pattern);
		if ((cp = strchr(pat, '/')) != NULL) {
			*cp = 0;
			rest = cp + 1;	/* Remember stuff after the ~/ part. */
		} else {
			rest = NULL;	/* Was just a ~ or ~username.  */
		}
		if (pat[1] == '\0') {
			/* Was just a ~ or ~/rest type.  */
			firstent = gUserInfo.home;
		} else {
			/* Was just a ~username or ~username/rest type.  */
			pw = getpwnam(pat + 1);
			if (pw != NULL)
				firstent = pw->pw_dir;
			else
				return;		/* Bad user -- leave it alone. */
		}
		
		Strncpy(pattern, firstent, siz);
		if (rest != NULL) {
			Strncat(pattern, "/", siz);
			Strncat(pattern, rest, siz);
		}
	}
}	/* ExpandTilde */



/*ARGSUSED*/
static
void LGlobHandler(int sigNum)
{
	longjmp(gLocalGlobJmp, 1);
}	/* LGlobHandler */



	
void LocalGlob(LineListPtr fileList, char *pattern)
{
	string pattern2;
	string cmd;
	longstring gfile;
	volatile FILE *fp;
	volatile Sig_t si, sp;

	STRNCPY(pattern2, pattern);	/* Don't nuke the original. */
	
	/* Pre-process for ~'s. */ 
	ExpandTilde(pattern2, sizeof(pattern2));
	
	/* Initialize the list. */
	fileList->first = fileList->last = NULL;
	
	if (GLOBCHARSINSTR(pattern2)) {
		/* Do it the easy way and have the shell do the dirty
		 * work for us.
		 */
		/* May need "-1" flag here. */
		sprintf(cmd, "%s -c \"%s -d %s\"", gUserInfo.shell, LS, pattern2);
	
		fp = NULL;
		if (setjmp(gLocalGlobJmp) == 0) {			
			fp = (volatile FILE *) POpen(cmd, "r");
			if (fp == NULL) {
				DebugMsg("Could not lglob: %s\n", cmd);
				return;
			}
			sp = SIGNAL(SIGPIPE, LGlobHandler);
			si = SIGNAL(SIGINT, LGlobHandler);
			while (FGets(gfile, sizeof(gfile), (FILE *) fp) != NULL) {
				TraceMsg("Lglob [%s]\n", gfile);
				AddLine(fileList, gfile);
			}
		}
		(void) SIGNAL(SIGPIPE, SIG_IGN);
		if (fp != NULL)
			(void) PClose((FILE *) fp);
		(void) SIGNAL(SIGPIPE, sp);
		(void) SIGNAL(SIGINT, si);
	} else {
		/* Or, if there were no globbing characters in 'pattern', then the
		 * pattern is really just a single pathname.
		 */
		AddLine(fileList, pattern2);
	}
}	/* LocalGlob */




void RemoteGlob(LineListPtr fileList, char *pattern, char *lsFlags)
{
	char *cp;
	LinePtr lp;

	/* Note that we do attempt to use glob characters even if the remote
	 * host isn't UNIX.  Most non-UNIX remote FTP servers look for UNIX
	 * style wildcards.
	 */
	if (GLOBCHARSINSTR(pattern)) {
		/* Use NLST, which lists files one per line. */
		ListToMemory(fileList, "NLST", lsFlags, pattern);
		if ((fileList->first != NULL) && (fileList->first == fileList->last)) {
			/* If we have only one item in the list, see if it really was
			 * an error message we would recognize.
			 */
			cp = strchr(fileList->first->line, ':');
			if ((cp != NULL) && STREQ(cp, ": No such file or directory")) {
				RemoveLine(fileList, fileList->first);
			}
		}
		if (gTrace == kTracingOn) {
			for (lp=fileList->first; lp != NULL; lp = lp->next)
				TraceMsg("Rglob [%s]\n", lp->line);
		}
	} else {
		/* Or, if there were no globbing characters in 'pattern', then the
		 * pattern is really just a filename.  So for this case the
		 * file list is really just a single file.
		 */
		fileList->first = fileList->last = NULL;
		AddLine(fileList, pattern);
	}
}	/* RemoteGlob */
