/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/*
 * File:	filegen.C $TOG: filegen.C /main/7 1999/10/14 15:05:25 mgreess $
 *
 * (c) Copyright 1993, 1994 Hewlett-Packard Company
 * (c) Copyright 1993, 1994 International Business Machines Corp.
 * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 * (c) Copyright 1993, 1994 Novell, Inc.
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#define X_INCLUDE_DIRENT_H
#define XOS_USE_XT_LOCKING
#include <X11/Xos_r.h>
#include <codelibs/stringx.h>
#include <codelibs/nl_hack.h>
#include "buf.h"
#include "DtSvcLock.h"

#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif

#ifdef XTHREADS
extern "C" {
    extern void XtProcessLock(void);
    extern void XtProcessUnlock(void);
}
#endif

struct _SHXcomponent
{
    boolean is_pattern;
    long offset;		// subscript in path buffer
    char *ptr;                  // pointer into path buffer
};

declare_array(_SHXcomponents, _SHXcomponent, 4)

// recursive routine to expand the wildcard path represented in stack into
// all possible expansions.  The expansions are appended to _SHXbuf::vec.
// filebuf is a scratch buffer passed in by the caller and is used to build
// intermediate paths.  end is a pointer to the position in filebuf where
// the calling routine left off.
void
_SHXbuf::expand(_SHXcomponents &stack,
	char *const filebuf, char *end, int compnum)
{
    *end = '\0';

    if (compnum == stack.size())
	return;

    _SHXcomponent &comp = stack[compnum];

    // double-slash?
    if (comp.ptr[0] == '\0')
    {
	if (compnum + 1 == stack.size())
	    append(filebuf, EXPANDQUOTE);
	else
	{
	    *end++ = '/';
	    expand(stack, filebuf, end, compnum + 1);
	}
	return;
    }

    // performance optimization:  if this path component
    // doesn't contain a wildcard, avoid doing an opendir()
    if (!comp.is_pattern)
    {
	strcpy(end, comp.ptr);
	if (compnum + 1 == stack.size())
	{
	    // last component, just see if the path really points to something
	    if (access(filebuf, F_OK) != -1)
		append(filebuf, EXPANDQUOTE);
	}
	else
	{
	    // intermediate directory just append this component and keep going
	    char *end2 = strend(end);
	    *end2++ = '/';
	    expand(stack, filebuf, end2, compnum + 1);
	}
	return;
    }

    // We have a wildcard component, open and scan its parent directory
    // and look for matches.
    DIR *dir = opendir(filebuf[0] == '\0' ? "." : filebuf);
    if (dir == NULL)
	return;

    _Xreaddirparams dir_buf;
    struct dirent *ent;

    memset((char*) &dir_buf, 0, sizeof(_Xreaddirparams));
    while ((ent = _XReaddir(dir, dir_buf)) != NULL)
    {
	// deleted file?
	if (ent->d_ino == 0 || ent->d_name[0] == '\0')
	    continue;

	// right name?
	if (comp.is_pattern)
	{
	    wchar_t __nlh_char[1];

	    // Must have explicit match for leading '.'
	    if (CHARAT(ent->d_name) == '.' && CHARAT(comp.ptr) != '.')
		continue;

	    if (strwcmp(comp.ptr, ent->d_name) != 0)
		continue;
	}
	else if (strcmp(comp.ptr, ent->d_name) != 0)
	    continue;

	strcpy(end, ent->d_name);
	if (compnum + 1 == stack.size())
	    append(filebuf, EXPANDQUOTE);
	else
	{
	    char *end2 = end + strlen (ent->d_name);
	    *end2++ = '/';
	    expand(stack, filebuf, end2, compnum + 1);
	}
    }

    closedir(dir);
}


//extern "C" { void qsort(void *, unsigned, int, ...); };

static char *bufptr;

static int
//compar(int &v1, int &v2)
compar(const void *v1, const void *v2)
{
    int result;

    _DtSvcProcessLock();
    result = strcmp(&bufptr[*(int*)v1], &bufptr[*(int*)v2]);
    _DtSvcProcessUnlock();
    return (result);
}

void
_SHXbuf::filegen()
{
    privbuf_charbuf path;
    _SHXcomponents stack;

    long vecstart = vec.size() - 1;
    if (vecstart < 0)
	return;

    long bufstart = long(vec[vecstart]);

    // Parse the file path, breaking it up into individual components.
    // Each component is marked as being either a wildcard component
    // or not.  The wildcard components will have a '\' placed before
    // any quoted wildcard characters.  The non-wildcard components
    // will be left unchanged.
    int bufpos = (int) bufstart;
    while (bufpos < buf.size())
    {
	_SHXcomponent & comp = stack[stack.size()];
	comp.is_pattern = FALSE;
	comp.offset = path.size();
	comp.ptr = NULL;
	int startpos = bufpos;

	int ch;
	do
	{
	    ch = buf[bufpos];
	    switch (ch)
	    {
	    case '/': 
		ch = '\0';
		break;
	    case '*': 
	    case '?': 
	    case '[': 
		if (flags[bufpos] == NOQUOTE)
		    comp.is_pattern = TRUE;
		else
		    path[path.size()] = '\\';
		break;
	    }

	    path[path.size()] = ch;
	    bufpos++;
	} while (ch != '\0');

	// Add a '*' to the end of the last component if needed
	// for completion.
	if (bufpos >= buf.size())   // last component?
	    if (bufpos > bufstart + 1)	// non-null string?
		if (completion && !is_pattern)
		{
		    path[path.size() - 1] = '*';
		    path[path.size()] = '\0';
		    comp.is_pattern = TRUE;
		    break;
		}

	// If it wasn't a pattern, remove all of the '\' characters
	// that were added.
	if (!comp.is_pattern)
	{
	    int len = bufpos - startpos - 1;
	    strncpy(&path[comp.offset], &buf[startpos], len);
	    path[comp.offset + len] = '\0';
	}
    }

    // Fill in the character pointer values for all of the components.
    // We couldn't do this in the first pass because path is a
    // dynamic array.
    char *pathbuf = path.getarr();
    for (int i = 0; i < stack.size(); i++)
	stack[i].ptr = &pathbuf[stack[i].offset];

    // Remove the token that we just copied from the return vector
    // so that we can replace it with its expansion.
    vec.reset(vecstart);

    char filebuf[MAXPATHLEN];
    expand(stack, filebuf, filebuf, 0);

    if (vec.size() == vecstart)	// no matches?
    {
	vec[vecstart] = (char *)bufstart;   // restore orig. token
	return;
    }

    // alphabetize the expansion to make it look pretty like ksh does.
    _DtSvcProcessLock();
    bufptr = buf.getarr();
    qsort(&vec[vecstart], (unsigned int)(vec.size() - vecstart),
	    sizeof (char *), compar);

    // Find the longest match if we are doing completion:
    if (completion)
    {
	long i;
	// compare all entries to a copy of the first entry
	snprintf(filebuf, sizeof(filebuf), "%s", &bufptr[long(vec[0])]);

	for (i = 1; i < vec.size(); i++)
	{
	    char *ref = filebuf;
	    char *ptr = &bufptr[long(vec[i])];
	    while (*ref == *ptr && *ref != '\0' && *ptr != '\0')
		ref++, ptr++;

	    *ref = '\0';	// shorten the reference copy
	}

	// Now store the best match as the first token.  We will
	// have to shift the expansion vector down by one to
	// make room.
	for (i = vec.size(); i > 0; --i)
	{
	    char *val = vec[i - 1];
	    vec[i] = val;
	}
	vec[0] = (char *)buf.size();
	append(filebuf, EXPANDQUOTE);
	vec.reset(vec.size() - 1);  // adjust for the append
    }
    _DtSvcProcessUnlock();
}

implement_array(_SHXcomponents, _SHXcomponent, 4)
