📄 tclglob.c
字号:
/*
* tclGlob.c --
*
* This file provides procedures and commands for file name
* manipulation, such as tilde expansion and globbing.
*
* Copyright 1990-1991 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*
* $Id: tclGlob.c,v 1.1.1.1 2001/04/29 20:34:49 karll Exp $
*/
#include "tclInt.h"
#include "tclEcos.h"
#include <pwd.h>
/*
* The structure below is used to keep track of a globbing result
* being built up (i.e. a partial list of file names). The list
* grows dynamically to be as big as needed.
*/
typedef struct {
char *result; /* Pointer to result area. */
int totalSpace; /* Total number of characters allocated
* for result. */
int spaceUsed; /* Number of characters currently in use
* to hold the partial result (not including
* the terminating NULL). */
int dynamic; /* 0 means result is static space, 1 means
* it's dynamic. */
} GlobResult;
/*
* Declarations for procedures local to this file:
*/
static void AppendResult _ANSI_ARGS_((Tcl_Interp *interp,
char *dir, char *separator, char *name,
int nameLength));
static int DoGlob _ANSI_ARGS_((Tcl_Interp *interp, char *dir,
char *rem));
/*
*----------------------------------------------------------------------
*
* AppendResult --
*
* Given two parts of a file name (directory and element within
* directory), concatenate the two together and append them to
* the result building up in interp.
*
* Results:
* There is no return value.
*
* Side effects:
* Interp->result gets extended.
*
*----------------------------------------------------------------------
*/
static void
AppendResult(interp, dir, separator, name, nameLength)
Tcl_Interp *interp; /* Interpreter whose result should be
* appended to. */
char *dir; /* Name of directory, without trailing
* slash except for root directory. */
char *separator; /* Separator string so use between dir and
* name: either "/" or "" depending on dir. */
char *name; /* Name of file withing directory (NOT
* necessarily null-terminated!). */
int nameLength; /* Number of characters in name. */
{
int dirFlags, nameFlags;
char *p, saved;
/*
* Next, see if we can put together a valid list element from dir
* and name by calling Tcl_AppendResult.
*/
if (*dir == 0) {
dirFlags = 0;
} else {
Tcl_ScanElement(dir, &dirFlags);
}
saved = name[nameLength];
name[nameLength] = 0;
Tcl_ScanElement(name, &nameFlags);
if ((dirFlags == 0) && (nameFlags == 0)) {
if (*interp->result != 0) {
Tcl_AppendResult(interp, " ", dir, separator, name, (char *) NULL);
} else {
Tcl_AppendResult(interp, dir, separator, name, (char *) NULL);
}
name[nameLength] = saved;
return;
}
/*
* This name has weird characters in it, so we have to convert it to
* a list element. To do that, we have to merge the characters
* into a single name. To do that, malloc a buffer to hold everything.
*/
p = (char *) ckalloc((unsigned) (strlen(dir) + strlen(separator)
+ nameLength + 1));
sprintf(p, "%s%s%s", dir, separator, name);
name[nameLength] = saved;
Tcl_AppendElement(interp, p, 0);
ckfree(p);
}
/*
*----------------------------------------------------------------------
*
* DoGlob --
*
* This recursive procedure forms the heart of the globbing
* code. It performs a depth-first traversal of the tree
* given by the path name to be globbed.
*
* Results:
* The return value is a standard Tcl result indicating whether
* an error occurred in globbing. After a normal return the
* result in interp will be set to hold all of the file names
* given by the dir and rem arguments. After an error the
* result in interp will hold an error message.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int DoGlob(Tcl_Interp *interp, char *dir, char *rem)
// Tcl_Interp *interp; /* Interpreter to use for error
// * reporting (e.g. unmatched brace). */
// char *dir; /* Name of a directory at which to
// * start glob expansion. This name
// * is fixed: it doesn't contain any
// * globbing chars. */
// char *rem; /* Path to glob-expand. */
{
/*
* When this procedure is entered, the name to be globbed may
* already have been partly expanded by ancestor invocations of
* DoGlob. The part that's already been expanded is in "dir"
* (this may initially be empty), and the part still to expand
* is in "rem". This procedure expands "rem" one level, making
* recursive calls to itself if there's still more stuff left
* in the remainder.
*/
register char *p;
register char c;
char *openBrace, *closeBrace;
int gotSpecial, result;
char *separator;
/*
* Figure out whether we'll need to add a slash between the directory
* name and file names within the directory when concatenating them
* together.
*/
if ((dir[0] == 0) || ((dir[0] == '/') && (dir[1] == 0))) {
separator = "";
} else {
separator = "/";
}
/*
* When generating information for the next lower call,
* use static areas if the name is short, and malloc if the name
* is longer.
*/
#define STATIC_SIZE 200
/*
* First, find the end of the next element in rem, checking
* along the way for special globbing characters.
*/
gotSpecial = 0;
openBrace = closeBrace = NULL;
for (p = rem; ; p++) {
c = *p;
if ((c == '\0') || (c == '/')) {
break;
}
if ((c == '{') && (openBrace == NULL)) {
openBrace = p;
}
if ((c == '}') && (closeBrace == NULL)) {
closeBrace = p;
}
if ((c == '*') || (c == '[') || (c == '\\') || (c == '?')) {
gotSpecial = 1;
}
}
/*
* If there is an open brace in the argument, then make a recursive
* call for each element between the braces. In this case, the
* recursive call to DoGlob uses the same "dir" that we got.
* If there are several brace-pairs in a single name, we just handle
* one here, and the others will be handled in recursive calls.
*/
if (openBrace != NULL) {
int remLength, l1, l2;
char static1[STATIC_SIZE];
char *element, *newRem;
if (closeBrace == NULL) {
Tcl_ResetResult(interp);
interp->result = "unmatched open-brace in file name";
return TCL_ERROR;
}
remLength = strlen(rem) + 1;
if (remLength <= STATIC_SIZE) {
newRem = static1;
} else {
newRem = (char *) ckalloc((unsigned) remLength);
}
l1 = openBrace-rem;
strncpy(newRem, rem, l1);
p = openBrace;
for (p = openBrace; *p != '}'; ) {
element = p+1;
for (p = element; ((*p != '}') && (*p != ',')); p++) {
/* Empty loop body: just find end of this element. */
}
l2 = p - element;
strncpy(newRem+l1, element, l2);
strcpy(newRem+l1+l2, closeBrace+1);
if (DoGlob(interp, dir, newRem) != TCL_OK) {
return TCL_ERROR;
}
}
if (remLength > STATIC_SIZE) {
ckfree(newRem);
}
return TCL_OK;
}
/*
* If there were any pattern-matching characters, then scan through
* the directory to find all the matching names.
*/
if (gotSpecial) {
DIR *d;
struct dirent *entryPtr;
int l1, l2;
char *pattern, *newDir, *dirName;
char static1[STATIC_SIZE], static2[STATIC_SIZE];
struct stat statBuf;
/*
* Be careful not to do any actual file system operations on a
* directory named ""; instead, use ".". This is needed because
* some versions of UNIX don't treat "" like "." automatically.
*/
if (*dir == '\0') {
dirName = ".";
} else {
dirName = dir;
}
if ((stat(dirName, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
return TCL_OK;
}
d = opendir(dirName);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -