aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/obm/Tcl/tclGlob.c
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /vendor/x11iraf/obm/Tcl/tclGlob.c
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'vendor/x11iraf/obm/Tcl/tclGlob.c')
-rw-r--r--vendor/x11iraf/obm/Tcl/tclGlob.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/vendor/x11iraf/obm/Tcl/tclGlob.c b/vendor/x11iraf/obm/Tcl/tclGlob.c
new file mode 100644
index 00000000..a7f29d3d
--- /dev/null
+++ b/vendor/x11iraf/obm/Tcl/tclGlob.c
@@ -0,0 +1,455 @@
+/*
+ * tclGlob.c --
+ *
+ * This file provides procedures and commands for file name
+ * manipulation, such as tilde expansion and globbing.
+ *
+ * Copyright (c) 1990-1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclGlob.c,v 1.36 93/10/14 15:14:08 ouster Exp $ SPRITE (Berkeley)";
+#endif /* not lint */
+
+#include "tclInt.h"
+#include "tclUnix.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 int DoGlob _ANSI_ARGS_((Tcl_Interp *interp, char *dir,
+ char *rem));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(interp, dir, 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.
+ */
+
+ Tcl_DString newName; /* Holds new name consisting of
+ * dir plus the first part of rem. */
+ register char *p;
+ register char c;
+ char *openBrace, *closeBrace, *name, *dirName;
+ int gotSpecial, baseLength;
+ int result = TCL_OK;
+ struct stat statBuf;
+
+ /*
+ * Make sure that the directory part of the name really is a
+ * directory. If the directory name is "", use the name "."
+ * instead, because some UNIX systems don't treat "" like "."
+ * automatically. Keep the "" for use in generating file names,
+ * otherwise "glob foo.c" would return "./foo.c".
+ */
+
+ if (*dir == '\0') {
+ dirName = ".";
+ } else {
+ dirName = dir;
+ }
+ if ((stat(dirName, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
+ return TCL_OK;
+ }
+ Tcl_DStringInit(&newName);
+
+ /*
+ * 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') || ((openBrace == NULL) && (c == '/'))) {
+ break;
+ }
+ if ((c == '{') && (openBrace == NULL)) {
+ openBrace = p;
+ }
+ if ((c == '}') && (openBrace != NULL) && (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) {
+ char *element;
+
+ if (closeBrace == NULL) {
+ Tcl_ResetResult(interp);
+ interp->result = "unmatched open-brace in file name";
+ result = TCL_ERROR;
+ goto done;
+ }
+ Tcl_DStringAppend(&newName, rem, openBrace-rem);
+ baseLength = newName.length;
+ for (p = openBrace; *p != '}'; ) {
+ element = p+1;
+ for (p = element; ((*p != '}') && (*p != ',')); p++) {
+ /* Empty loop body. */
+ }
+ Tcl_DStringAppend(&newName, element, p-element);
+ Tcl_DStringAppend(&newName, closeBrace+1, -1);
+ result = DoGlob(interp, dir, newName.string);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ newName.length = baseLength;
+ }
+ goto done;
+ }
+
+ /*
+ * Start building up the next-level name with dir plus a slash if
+ * needed to separate it from the next file name.
+ */
+
+ Tcl_DStringAppend(&newName, dir, -1);
+ if ((dir[0] != 0) && (newName.string[newName.length-1] != '/')) {
+ Tcl_DStringAppend(&newName, "/", 1);
+ }
+ baseLength = newName.length;
+
+ /*
+ * 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;
+ char savedChar;
+
+ d = opendir(dirName);
+ if (d == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "couldn't read directory \"",
+ dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+
+ /*
+ * Temporarily store a null into rem so that the pattern string
+ * is now null-terminated.
+ */
+
+ savedChar = *p;
+ *p = 0;
+
+ while (1) {
+ entryPtr = readdir(d);
+ if (entryPtr == NULL) {
+ break;
+ }
+
+ /*
+ * Don't match names starting with "." unless the "." is
+ * present in the pattern.
+ */
+
+ if ((*entryPtr->d_name == '.') && (*rem != '.')) {
+ continue;
+ }
+ if (Tcl_StringMatch(entryPtr->d_name, rem)) {
+ newName.length = baseLength;
+ Tcl_DStringAppend(&newName, entryPtr->d_name, -1);
+ if (savedChar == 0) {
+ Tcl_AppendElement(interp, newName.string);
+ } else {
+ result = DoGlob(interp, newName.string, p+1);
+ if (result != TCL_OK) {
+ break;
+ }
+ }
+ }
+ }
+ closedir(d);
+ *p = savedChar;
+ goto done;
+ }
+
+ /*
+ * The current element is a simple one with no fancy features. Add
+ * it to the new name. If there are more elements still to come,
+ * then recurse to process them.
+ */
+
+ Tcl_DStringAppend(&newName, rem, p-rem);
+ if (*p != 0) {
+ result = DoGlob(interp, newName.string, p+1);
+ goto done;
+ }
+
+ /*
+ * There are no more elements in the pattern. Check to be sure the
+ * file actually exists, then add its name to the list being formed
+ * in interp-result.
+ */
+
+ name = newName.string;
+ if (*name == 0) {
+ name = ".";
+ }
+ if (access(name, F_OK) != 0) {
+ goto done;
+ }
+ Tcl_AppendElement(interp, name);
+
+ done:
+ Tcl_DStringFree(&newName);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_TildeSubst --
+ *
+ * Given a name starting with a tilde, produce a name where
+ * the tilde and following characters have been replaced by
+ * the home directory location for the named user.
+ *
+ * Results:
+ * The result is a pointer to a static string containing
+ * the new name. If there was an error in processing the
+ * tilde, then an error message is left in interp->result
+ * and the return value is NULL. The result may be stored
+ * in bufferPtr; the caller must call Tcl_DStringFree(bufferPtr)
+ * to free the name.
+ *
+ * Side effects:
+ * Information may be left in bufferPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Tcl_TildeSubst(interp, name, bufferPtr)
+ Tcl_Interp *interp; /* Interpreter in which to store error
+ * message (if necessary). */
+ char *name; /* File name, which may begin with "~/"
+ * (to indicate current user's home directory)
+ * or "~<user>/" (to indicate any user's
+ * home directory). */
+ Tcl_DString *bufferPtr; /* May be used to hold result. Must not hold
+ * anything at the time of the call, and need
+ * not even be initialized. */
+{
+ char *dir;
+ register char *p;
+
+ Tcl_DStringInit(bufferPtr);
+ if (name[0] != '~') {
+ return name;
+ }
+
+ if ((name[1] == '/') || (name[1] == '\0')) {
+ dir = getenv("HOME");
+ if (dir == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "couldn't find HOME environment ",
+ "variable to expand \"", name, "\"", (char *) NULL);
+ return NULL;
+ }
+ Tcl_DStringAppend(bufferPtr, dir, -1);
+ Tcl_DStringAppend(bufferPtr, name+1, -1);
+ } else {
+ struct passwd *pwPtr;
+
+ for (p = &name[1]; (*p != 0) && (*p != '/'); p++) {
+ /* Null body; just find end of name. */
+ }
+ Tcl_DStringAppend(bufferPtr, name+1, p - (name+1));
+ pwPtr = getpwnam(bufferPtr->string);
+ if (pwPtr == NULL) {
+ endpwent();
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "user \"", bufferPtr->string,
+ "\" doesn't exist", (char *) NULL);
+ return NULL;
+ }
+ Tcl_DStringFree(bufferPtr);
+ Tcl_DStringAppend(bufferPtr, pwPtr->pw_dir, -1);
+ Tcl_DStringAppend(bufferPtr, p, -1);
+ endpwent();
+ }
+ return bufferPtr->string;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GlobCmd --
+ *
+ * This procedure is invoked to process the "glob" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+Tcl_GlobCmd(dummy, interp, argc, argv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int i, result, noComplain, firstArg;
+
+ if (argc < 2) {
+ notEnoughArgs:
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?switches? name ?name ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ noComplain = 0;
+ for (firstArg = 1; (firstArg < argc) && (argv[firstArg][0] == '-');
+ firstArg++) {
+ if (strcmp(argv[firstArg], "-nocomplain") == 0) {
+ noComplain = 1;
+ } else if (strcmp(argv[firstArg], "--") == 0) {
+ firstArg++;
+ break;
+ } else {
+ Tcl_AppendResult(interp, "bad switch \"", argv[firstArg],
+ "\": must be -nocomplain or --", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (firstArg >= argc) {
+ goto notEnoughArgs;
+ }
+
+ for (i = firstArg; i < argc; i++) {
+ char *thisName;
+ Tcl_DString buffer;
+
+ thisName = Tcl_TildeSubst(interp, argv[i], &buffer);
+ if (thisName == NULL) {
+ return TCL_ERROR;
+ }
+ if (*thisName == '/') {
+ if (thisName[1] == '/') {
+ /*
+ * This is a special hack for systems like those from Apollo
+ * where there is a super-root at "//": need to treat the
+ * double-slash as a single name.
+ */
+ result = DoGlob(interp, "//", thisName+2);
+ } else {
+ result = DoGlob(interp, "/", thisName+1);
+ }
+ } else {
+ result = DoGlob(interp, "", thisName);
+ }
+ Tcl_DStringFree(&buffer);
+ if (result != TCL_OK) {
+ return result;
+ }
+ }
+ if ((*interp->result == 0) && !noComplain) {
+ char *sep = "";
+
+ Tcl_AppendResult(interp, "no files matched glob pattern",
+ (argc == 2) ? " \"" : "s \"", (char *) NULL);
+ for (i = firstArg; i < argc; i++) {
+ Tcl_AppendResult(interp, sep, argv[i], (char *) NULL);
+ sep = " ";
+ }
+ Tcl_AppendResult(interp, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}