/*
 - allcases - supply cvec for all case counterparts of a chr (including itself)
 * This is a shortcut, preferably an efficient one, for simple characters;
 * messy cases are done via range().
 ^ static struct cvec *allcases(struct vars *, pchr);
 */
static struct cvec *
allcases(
    struct vars *v,		/* context */
    pchr pc)			/* character to get case equivs of */
{
    struct cvec *cv;
    chr c = (chr)pc;
    chr lc, uc, tc;

    lc = Tcl_UniCharToLower((chr)c);
    uc = Tcl_UniCharToUpper((chr)c);
    tc = Tcl_UniCharToTitle((chr)c);

    if (tc != uc) {
	cv = getcvec(v, 3, 0);
	addchr(cv, tc);
    } else {
	cv = getcvec(v, 2, 0);
    }
    addchr(cv, lc);
    if (lc != uc) {
	addchr(cv, uc);
    }
    return cv;
}
/*
 - range - supply cvec for a range, including legality check
 ^ static struct cvec *range(struct vars *, celt, celt, int);
 */
static struct cvec *
range(
    struct vars *v,		/* context */
    celt a,			/* range start */
    celt b,			/* range end, might equal a */
    int cases)			/* case-independent? */
{
    int nchrs;
    struct cvec *cv;
    celt c, lc, uc, tc;

    if (a != b && !before(a, b)) {
	ERR(REG_ERANGE);
	return NULL;
    }

    if (!cases) {		/* easy version */
	cv = getcvec(v, 0, 1);
	NOERRN();
	addrange(cv, a, b);
	return cv;
    }

    /*
     * When case-independent, it's hard to decide when cvec ranges are usable,
     * so for now at least, we won't try. We allocate enough space for two
     * case variants plus a little extra for the two title case variants.
     */

    nchrs = (b - a + 1)*2 + 4;

    cv = getcvec(v, nchrs, 0);
    NOERRN();

    for (c=a; c<=b; c++) {
	addchr(cv, c);
	lc = Tcl_UniCharToLower((chr)c);
	uc = Tcl_UniCharToUpper((chr)c);
	tc = Tcl_UniCharToTitle((chr)c);
	if (c != lc) {
	    addchr(cv, lc);
	}
	if (c != uc) {
	    addchr(cv, uc);
	}
	if (c != tc && tc != uc) {
	    addchr(cv, tc);
	}
    }

    return cv;
}
Пример #3
0
static int
ConvertFileNameFormat(
    Tcl_Interp *interp,		/* The interp we are using for errors. */
    int objIndex,		/* The index of the attribute. */
    CONST char *fileName,	/* The name of the file. */
    int longShort,		/* 0 to short name, 1 to long name. */
    Tcl_Obj **attributePtrPtr)	/* A pointer to return the object with. */
{
    int pathc, i;
    char **pathv, **newv;
    char *resultStr;
    Tcl_DString resultDString;
    int result = TCL_OK;

    Tcl_SplitPath(fileName, &pathc, &pathv);
    newv = (char **) ckalloc(pathc * sizeof(char *));

    if (pathc == 0) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
		"could not read \"", fileName,
		"\": no such file or directory", 
		(char *) NULL);
	result = TCL_ERROR;
	goto cleanup;
    }
    
    for (i = 0; i < pathc; i++) {
	if ((pathv[i][0] == '/')
		|| ((strlen(pathv[i]) == 3) && (pathv[i][1] == ':'))
		|| (strcmp(pathv[i], ".") == 0)
		|| (strcmp(pathv[i], "..") == 0)) {
	    /*
	     * Handle "/", "//machine/export", "c:/", "." or ".." by just
	     * copying the string literally.  Uppercase the drive letter,
	     * just because it looks better under Windows to do so.
	     */

	    simple:
	    pathv[i][0] = (char) Tcl_UniCharToUpper(UCHAR(pathv[i][0]));
	    newv[i] = (char *) ckalloc(strlen(pathv[i]) + 1);
	    lstrcpyA(newv[i], pathv[i]);
	} else {
	    char *str;
	    TCHAR *nativeName;
	    Tcl_DString ds;
	    WIN32_FIND_DATAT data;
	    HANDLE handle;
	    DWORD attr;

	    Tcl_DStringInit(&resultDString);
	    str = Tcl_JoinPath(i + 1, pathv, &resultDString);
	    nativeName = Tcl_WinUtfToTChar(str, -1, &ds);
	    handle = (*tclWinProcs->findFirstFileProc)(nativeName, &data);
	    if (handle == INVALID_HANDLE_VALUE) {
		/*
		 * FindFirstFile() doesn't like root directories.  We 
		 * would only get a root directory here if the caller
		 * specified "c:" or "c:." and the current directory on the
		 * drive was the root directory
		 */

		attr = (*tclWinProcs->getFileAttributesProc)(nativeName);
		if ((attr != 0xFFFFFFFF) && (attr & FILE_ATTRIBUTE_DIRECTORY)) {
		    Tcl_DStringFree(&ds);
		    Tcl_DStringFree(&resultDString);

		    goto simple;
		}
	    }
	    Tcl_DStringFree(&ds);
	    Tcl_DStringFree(&resultDString);

	    if (handle == INVALID_HANDLE_VALUE) {
		pathc = i - 1;
		StatError(interp, fileName);
		result = TCL_ERROR;
		goto cleanup;
	    }
	    if (tclWinProcs->useWide) {
		nativeName = (TCHAR *) data.w.cAlternateFileName;
		if (longShort) {
		    if (data.w.cFileName[0] != '\0') {
			nativeName = (TCHAR *) data.w.cFileName;
		    } 
		} else {
		    if (data.w.cAlternateFileName[0] == '\0') {
			nativeName = (TCHAR *) data.w.cFileName;
		    }
		}
	    } else {
		nativeName = (TCHAR *) data.a.cAlternateFileName;
		if (longShort) {
		    if (data.a.cFileName[0] != '\0') {
			nativeName = (TCHAR *) data.a.cFileName;
		    } 
		} else {
		    if (data.a.cAlternateFileName[0] == '\0') {
			nativeName = (TCHAR *) data.a.cFileName;
		    }
		}
	    }

	    /*
	     * Purify reports a extraneous UMR in Tcl_WinTCharToUtf() trying 
	     * to dereference nativeName as a Unicode string.  I have proven 
	     * to myself that purify is wrong by running the following 
	     * example when nativeName == data.w.cAlternateFileName and 
	     * noting that purify doesn't complain about the first line,
	     * but does complain about the second.
	     *
	     *	fprintf(stderr, "%d\n", data.w.cAlternateFileName[0]);
	     *	fprintf(stderr, "%d\n", ((WCHAR *) nativeName)[0]);
	     */

	    Tcl_WinTCharToUtf(nativeName, -1, &ds);
	    newv[i] = ckalloc(Tcl_DStringLength(&ds) + 1);
	    lstrcpyA(newv[i], Tcl_DStringValue(&ds));
	    Tcl_DStringFree(&ds);
	    FindClose(handle);
	}
    }

    Tcl_DStringInit(&resultDString);
    resultStr = Tcl_JoinPath(pathc, newv, &resultDString);
    *attributePtrPtr = Tcl_NewStringObj(resultStr, 
	    Tcl_DStringLength(&resultDString));
    Tcl_DStringFree(&resultDString);

cleanup:
    for (i = 0; i < pathc; i++) {
	ckfree(newv[i]);
    }
    ckfree((char *) newv);
    ckfree((char *) pathv);
    return result;
}