/*
    This file is part of SUPPL - the supplemental library for DOS
    Copyright (C) 1996-2000 Steffen Kaiser

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $RCSfile: DFNSEARC.C $
   $Locker: ska $	$Name:  $	$State: Exp $

ob(ject): dfnsearch
su(bsystem): dfn
ty(pe): H
sh(ort description): Search for a file in a given set of paths
he(ader files): 
lo(ng description): Searches for a file in, if not already specified,
	a given set of paths and, if not already specified, with one of a
	set of given extensions.\par
	The individual components of \para{path} must be separated by a
	single semicolon ';' and must not contain superflous whitespaces. The
	behaviour of empty (zero-length components) is not defined. Each
	component is tested in the specified order.\newline
	The extensions must be delimited by a dot '.', they cannot contain
	a dot themselves. One leading dot will be ignored. Each extension
	will be tested in the specified order.\newline
	Both the filename and the extension may contain wildcards, which
	are passed unchanged to the DOS API and which are expanded with the
	first matching file.\newline
	If neither path nor extension is specified in the filename, first
	all extensions are probed within a path, then the next path component
	is used and all extensions are tested again.\par
	If there is no "." component in the \para{path}, the current working
	directory is tested at first.\newline
	If \tok{\para{path} == NULL}, the contents of the PATH environment
	variable is retreived and used.\newline
	If a path component is not fully-qualified, the result is not
	an abolsute one either.\newline
	If \tok{\para{ext} == NULL}, the list \tok{".COM.EXE.BAT"} will be
	used.\par
	Unless the macro \tok{SUPPORT_UNC_PATH} was defined prior \tok{#include}'ing
	the header file, this function does not support UNC paths.
pr(erequistes): 
va(lue): NULL: not found or out of memory or \tok{fnam == NULL}
	\item else: pointer to dynamically
	allocated complete filename
re(lated to): dfnusearch dfnsplit dfnmerge dfnpath
se(condary subsystems): 
in(itialized by): 
wa(rning): 
bu(gs): 
fi(le): dfnsearc.c

ob(ject): dfnusearch
su(bsystem): dfn
ty(pe): H
sh(ort description): Search for a file in a given set of paths
he(ader files): 
lo(ng description): As \tok{dfnsearch()}, but supports UNC paths.
pr(erequistes): 
va(lue): NULL: not found or out of memory or \tok{fnam == NULL}
	\item else: pointer to dynamically
	allocated complete filename
re(lated to): dfnsearch dfnusplit dfnumerge 
se(condary subsystems): 
in(itialized by): 
wa(rning): 
bu(gs): 
fi(le): dfnsearc.c
*/

#include "initsupl.loc"

#ifndef _MICROC_
#include <string.h>
#include <stdlib.h>
#include <io.h>
#ifndef _PAC_NOCLIB_
#include "dir.loc"
#endif
#else
#include <file.h>
#endif
#include "dfn.loc"
#include "dynstr.h"
#include "environ.h"
#include "suppl.h"

#include "suppldbg.h"

#ifdef RCS_Version
static char const rcsid[] = 
	"$Id: DFNSEARC.C 1.18 2001/10/13 18:44:34 ska Exp ska $";
#endif

static char *searchFile(char *fnam)
{
#ifdef _MICROC_
	struct FF_block ff;
#else
	struct ffblk ff;
#endif

	return findfirst(fnam, aS(ff), 0)
		? 0								/* no match found */
		: strdup(inM(ff.FF_name, ff.ff_name));	/* return file name */
}

static char *testFile(char *pa, char *na, char *ex)
{	char *fnam, *tname, *found;

	fnam = 0;
	if((tname = dfnmerge(0, 0, pa, na, ex)) != 0
	 && (found = searchFile(tname)) != 0) {		/* found */
	 	fnam = dfnmerge(0, 0, pa, found, 0);
	 	free(found);
	}

	free(tname);
	return fnam;
}

static char *scanDir(char *na, char *ex, char *ext, char *pa)
{	char *h, *found;
	STR_SAVED_TOKENS st;

	if(ex || !*ext)
		return testFile(pa, na, ex);

	StrTokSave(aS(st));
	found = 0;
	if((h = StrTokenize(ext, ".")) != 0) do {
		if((found = testFile(pa, na, h)) != 0)
			StrTokStop();
	} while((h = StrTokenize(0, ".")) != 0);

	StrTokRestore(aS(st));
	return found;
}

char *dfnsearch(const char * const fnam, char *searchpath, char *searchext)
{	int freePath, scanCwd;
	char *dr, *pa, *na, *ex;
	char *found, *p;
	STR_SAVED_TOKENS st;

	DBG_ENTER("dfnsearch", Suppl_dfn)
	DBG_ARGUMENTS( ("fnam=\"%s\", path=\"%s\", extensions=\"%s\"", fnam, searchpath, searchext) )

	if(!fnam || !*fnam)
		DBG_RETURN_S( 0)

/* What was specified? */
	chkHeap
	if(!dfnsplit(fnam, &dr, &pa, &na, &ex))
		DBG_RETURN_S( 0)

/* What can be searched? */
	chkHeap
	StrTokSave(aS(st));
	freePath = 0;
	found = 0;

	if(dr || pa) searchpath = "";		/* drive or path spec --> no search */
	else if(!searchpath) {
		if((searchpath = dupvar("PATH")) == 0) searchpath = "";
		else freePath = 1;
	}

	if(ex) searchext = "";
	else if(!searchext) searchext = "COM.EXE.BAT";
	else if(*searchext == '.') ++searchext;

	chkHeap
	if(*searchpath) {			/* there is a search path (dr==pa==NULL) */
		/* check, if the current working directory must be searched */
		p = searchpath - 1;
			/* scanCwd := first entry of %PATH% == "." ? */
		scanCwd = !(*searchpath == '.' &&
			(searchpath[1] == ';' || !searchpath[1]));
		while(scanCwd && (p = strstr(p + 1, ";.")) != 0)
			scanCwd = p[3] != ';' && p[3] != '\0';

		chkHeap
		if(!scanCwd || (found = scanDir(na, ex, searchext, 0)) == 0) {
			if((p = StrTokenize(searchpath, ";")) != 0) do {
				if(*p && (found = scanDir(na, ex, searchext, p)) != 0)
					StrTokStop();
			} while((p = StrTokenize(0, ";")) != 0);
		}
		chkHeap
	}
	else {
		chkHeap
		if(freePath)
			free(searchpath);
		chkHeap
		if((searchpath = dfnmerge(0, dr, pa, 0, 0)) != 0) {
			freePath = 1;
			if(StrRepl(pa, dfnexpand(searchpath, 0)) != 0)
				found = scanDir(na, ex, searchext, pa);
		}
		chkHeap
	}


	if(freePath) free(searchpath);

	chkHeap
	free(ex);
	free(na);
	free(pa);
	free(dr);

	chkHeap
	StrTokRestore(aS(st));

	chkHeap
	DBG_RETURN_S( found)
}
