cdesktopenv/cde/lib/DtSvc/DtCodelibs/filegen.C

305 lines
7.6 KiB
C

/*
* 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 librararies 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 <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
strcpy(filebuf, &bufptr[long(vec[0])]);
for (i = 1; i < vec.size(); i++)
{
register char *ref = filebuf;
register 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)
{
register 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)