Back to index

enigmail  1.4.3
Classes | Defines | Typedefs | Functions
nsinstall_win.c File Reference
#include <direct.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <windows.h>

Go to the source code of this file.

Classes

struct  sh_FileData
struct  sh_CpCmdArg

Defines

#define LONGPATH_PREFIX   L"\\\\?\\"
#define ARRAY_LEN(a)   (sizeof(a) / sizeof(a[0]))
#define STR_LEN(a)   (ARRAY_LEN(a) - 1)

Typedefs

typedef BOOL(* sh_FileFcn )(wchar_t *pathName, WIN32_FIND_DATA *fileData, void *arg)

Functions

static int shellCp (wchar_t **pArgv)
static int shellNsinstall (wchar_t **pArgv)
static int shellMkdir (wchar_t **pArgv)
static BOOL sh_EnumerateFiles (const wchar_t *pattern, const wchar_t *where, sh_FileFcn fileFcn, void *arg, int *nFiles)
static const char * sh_GetLastErrorMessage (void)
static BOOL sh_DoCopy (wchar_t *srcFileName, DWORD srcFileAttributes, wchar_t *dstFileName, DWORD dstFileAttributes, int force, int recursive)
void changeForwardSlashesToBackSlashes (wchar_t *arg)
int wmain (int argc, wchar_t *argv[])
static BOOL sh_RecordFileData (wchar_t *pathName, WIN32_FIND_DATA *findData, void *arg)
static BOOL sh_CpFileCmd (wchar_t *pathName, WIN32_FIND_DATA *findData, void *cpArg)

Class Documentation

struct sh_FileData

Definition at line 232 of file nsinstall_win.c.

Collaboration diagram for sh_FileData:
Class Members
DWORD dwFileAttributes
wchar_t pathName
struct sh_CpCmdArg

Definition at line 314 of file nsinstall_win.c.

Class Members
wchar_t * dstFileName
wchar_t * dstFileNameMarker
int force
int recursive

Define Documentation

#define ARRAY_LEN (   a)    (sizeof(a) / sizeof(a[0]))

Definition at line 41 of file nsinstall_win.c.

#define LONGPATH_PREFIX   L"\\\\?\\"

Definition at line 40 of file nsinstall_win.c.

#define STR_LEN (   a)    (ARRAY_LEN(a) - 1)

Definition at line 42 of file nsinstall_win.c.


Typedef Documentation

typedef BOOL(* sh_FileFcn)(wchar_t *pathName, WIN32_FIND_DATA *fileData, void *arg)

Definition at line 25 of file nsinstall_win.c.


Function Documentation

void changeForwardSlashesToBackSlashes ( wchar_t *  arg)

Definition at line 77 of file nsinstall_win.c.

{
    if ( arg == NULL )
        return;

    while ( *arg ) {
        if ( *arg == '/' )
            *arg = '\\';
        arg++;
    }
}

Here is the caller graph for this function:

static BOOL sh_CpFileCmd ( wchar_t *  pathName,
WIN32_FIND_DATA *  findData,
void *  cpArg 
) [static]

Definition at line 340 of file nsinstall_win.c.

{
    BOOL retVal = TRUE;
    struct sh_CpCmdArg *arg = (struct sh_CpCmdArg *) cpArg;

    wcscpy(arg->dstFileNameMarker, findData->cFileName);
    return sh_DoCopy(pathName, findData->dwFileAttributes,
            arg->dstFileName, GetFileAttributes(arg->dstFileName),
            arg->force, arg->recursive);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static BOOL sh_DoCopy ( wchar_t *  srcFileName,
DWORD  srcFileAttributes,
wchar_t *  dstFileName,
DWORD  dstFileAttributes,
int  force,
int  recursive 
) [static]

Definition at line 261 of file nsinstall_win.c.

{
    if (dstFileAttributes != 0xFFFFFFFF) {
        if ((dstFileAttributes & FILE_ATTRIBUTE_READONLY) && force) {
            dstFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
            SetFileAttributes(dstFileName, dstFileAttributes);
        }
    }

    if (srcFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        fprintf(stderr, "nsinstall: %ls is a directory\n",
                srcFileName);
        return FALSE;
    } else {
        DWORD r;
        wchar_t longSrc[1004] = LONGPATH_PREFIX;
        wchar_t longDst[1004] = LONGPATH_PREFIX;
        r = GetFullPathName(srcFileName, 1000, longSrc + STR_LEN(LONGPATH_PREFIX), NULL);
        if (!r) {
            fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n",
                    srcFileName, sh_GetLastErrorMessage());
            return FALSE;
        }
        r = GetFullPathName(dstFileName, 1000, longDst + ARRAY_LEN(LONGPATH_PREFIX) - 1, NULL);
        if (!r) {
            fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n",
                    dstFileName, sh_GetLastErrorMessage());
            return FALSE;
        }

        if (!CopyFile(longSrc, longDst, FALSE)) {
            fprintf(stderr, "nsinstall: cannot copy %ls to %ls: %s\n",
                    srcFileName, dstFileName, sh_GetLastErrorMessage());
            return FALSE;
        }
    }
    return TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static BOOL sh_EnumerateFiles ( const wchar_t *  pattern,
const wchar_t *  where,
sh_FileFcn  fileFcn,
void *  arg,
int *  nFiles 
) [static]

Definition at line 536 of file nsinstall_win.c.

{
    WIN32_FIND_DATA fileData;
    HANDLE hSearch;
    const wchar_t *src;
    wchar_t *dst;
    wchar_t fileName[_MAX_PATH];
    wchar_t *fileNameMarker = fileName;
    wchar_t *oldFileNameMarker;
    BOOL hasWildcard = FALSE;
    BOOL retVal = TRUE;
    BOOL patternEndsInDotStar = FALSE;
    BOOL patternEndsInDot = FALSE;  /* a special case of
                                     * patternEndsInDotStar */
    int numDotsInPattern;
    int len;
    
    /*
     * Windows expands patterns ending in ".", ".*", ".**", etc.
     * differently from the glob expansion on Unix.  For example,
     * both "foo." and "foo.*" match "foo", and "*.*" matches
     * everything, including filenames with no dots.  So we need
     * to throw away extra files returned by the FindNextFile()
     * function.  We require that a matched filename have at least
     * the number of dots in the pattern.
     */
    len = wcslen(pattern);
    if (len >= 2) {
        /* Start from the end of pattern and go backward */
        const wchar_t *p = &pattern[len - 1];

        /* We can have zero or more *'s */
        while (p >= pattern && *p == '*') {
            p--;
        }
        if (p >= pattern && *p == '.') {
            patternEndsInDotStar = TRUE;
            if (p == &pattern[len - 1]) {
                patternEndsInDot = TRUE;
            }
            p--;
            numDotsInPattern = 1;
            while (p >= pattern && *p != '\\') {
                if (*p == '.') {
                    numDotsInPattern++;
                }
                p--;
            }
        }
    }

    *nFiles = 0;

    /*
     * Copy pattern to fileName, but only up to and not including
     * the first \ after the first wildcard letter.
     *
     * Make fileNameMarker point to one of the following:
     * - the start of fileName, if fileName does not contain any \.
     * - right after the \ before the first wildcard letter, if there is
     *   a wildcard character.
     * - right after the last \, if there is no wildcard character.
     */

    dst = fileName;
    src = pattern;
    while (src < where) {
        if (*src == '\\') {
            oldFileNameMarker = fileNameMarker;
            fileNameMarker = dst + 1;
        }
        *(dst++) = *(src++);
    }

    while (*src && *src != '*' && *src != '?') {
        if (*src == '\\') {
            oldFileNameMarker = fileNameMarker;
            fileNameMarker = dst + 1;
        }
        *(dst++) = *(src++);
    }

    if (*src) {
        /*
         * Must have seen the first wildcard letter
         */

        hasWildcard = TRUE;
        while (*src && *src != '\\') {
            *(dst++) = *(src++);
        }
    }
    
    /* Now src points to either null or \ */

    assert(*src == '\0' || *src == '\\');
    assert(hasWildcard || *src == '\0');
    *dst = '\0';

    /*
     * If the pattern does not contain any wildcard characters, then
     * we don't need to go the FindFirstFile route.
     */

    if (!hasWildcard) {
        /*
         * See if it is the root directory, \, or X:\.
         */

        assert(!wcscmp(fileName, pattern));
        assert(wcslen(fileName) >= 1);
        if (dst[-1] == '\\' && (dst == fileName + 1 || dst[-2] == ':')) {
            fileData.cFileName[0] = '\0';
        } else {
            /*
             * Do not include the trailing \, if any
             */

            if (dst[-1] == '\\') {
                assert(*fileNameMarker == '\0');
                dst[-1] = '\0';
                fileNameMarker = oldFileNameMarker;
            } 
            wcscpy(fileData.cFileName, fileNameMarker);
        }
        fileData.dwFileAttributes = GetFileAttributes(fileName);
        if (fileData.dwFileAttributes == 0xFFFFFFFF) {
            return TRUE;
        }
        *nFiles = 1;
        return (*fileFcn)(fileName, &fileData, arg);
    }

    hSearch = FindFirstFile(fileName, &fileData);
    if (hSearch == INVALID_HANDLE_VALUE) {
        return retVal;
    }

    do {
        if (!wcscmp(fileData.cFileName, L".")
                || !wcscmp(fileData.cFileName, L"..")) {
            /* 
             * Skip over . and ..
             */

            continue;
        }

        if (patternEndsInDotStar) {
            int nDots = 0;
            wchar_t *p = fileData.cFileName;
            while (*p) {
                if (*p == '.') {
                    nDots++;
                }
                p++;
            }
            /* Now p points to the null byte at the end of file name */
            if (patternEndsInDot && (p == fileData.cFileName
                    || p[-1] != '.')) {
                /*
                 * File name does not end in dot.  Skip this file.
                 * Note: windows file name probably cannot end in dot,
                 * but we do this check anyway.
                 */
                continue;
            }
            if (nDots < numDotsInPattern) {
                /*
                 * Not enough dots in file name.  Must be an extra
                 * file in matching .* pattern.  Skip this file.
                 */
                continue;
            }
        }

        wcscpy(fileNameMarker, fileData.cFileName);
        if (*src && *(src + 1)) {
            /*
             * More to go.  Recurse.
             */

            int n;

            assert(*src == '\\');
            where = fileName + wcslen(fileName);
            wcscat(fileName, src);
            sh_EnumerateFiles(fileName, where, fileFcn, arg, &n);
            *nFiles += n;
        } else {
            assert(wcschr(fileName, '*') == NULL);
            assert(wcschr(fileName, '?') == NULL);
            (*nFiles)++;
            if ((*fileFcn)(fileName, &fileData, arg) == FALSE) {
                retVal = FALSE;
            }
        }
    } while (FindNextFile(hSearch, &fileData));

    FindClose(hSearch);
    return retVal;
}

Here is the caller graph for this function:

static const char * sh_GetLastErrorMessage ( void  ) [static]

Definition at line 209 of file nsinstall_win.c.

{
    static char buf[128];

    FormatMessageA(
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  /* default language */
            buf,
            sizeof(buf),
            NULL
    );
    return buf;
}

Here is the caller graph for this function:

static BOOL sh_RecordFileData ( wchar_t *  pathName,
WIN32_FIND_DATA *  findData,
void *  arg 
) [static]

Definition at line 251 of file nsinstall_win.c.

{
    struct sh_FileData *fData = (struct sh_FileData *) arg;

    wcscpy(fData->pathName, pathName);
    fData->dwFileAttributes = findData->dwFileAttributes;
    return TRUE;
}

Here is the caller graph for this function:

static int shellCp ( wchar_t **  pArgv) [static]

Definition at line 352 of file nsinstall_win.c.

{
    int retVal = 0;
    wchar_t **pSrc;
    wchar_t **pDst;
    struct sh_CpCmdArg arg;
    struct sh_FileData dstData;
    int dstIsDir = 0;
    int n;

    arg.force = 0;
    arg.recursive = 0;
    arg.dstFileName = dstData.pathName;
    arg.dstFileNameMarker = 0;

    while (*pArgv && **pArgv == '-') {
        wchar_t *p = *pArgv;

        while (*(++p)) {
            if (*p == 'f') {
                arg.force = 1;
            }
        }
        pArgv++;
    }

    /* the first source file */
    if (*pArgv) {
        pSrc = pArgv++;
    } else {
        fprintf(stderr, "nsinstall: not enough arguments\n");
        return 3;
    }

    /* get to the last token to find destination */
    if (*pArgv) {
        pDst = pArgv++;
    } else {
        fprintf(stderr, "nsinstall: not enough arguments\n");
        return 3;
    }
    while (*pArgv) {
        pDst = pArgv++;
    }

    /*
     * The destination pattern must unambiguously expand to exactly
     * one file or directory.
     */

    changeForwardSlashesToBackSlashes(*pDst);
    sh_EnumerateFiles(*pDst, *pDst, sh_RecordFileData, &dstData, &n);
    assert(n >= 0);
    if (n == 1) {
        /*
         * Is the destination a file or directory?
         */

        if (dstData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            dstIsDir = 1;
        }
    } else if (n > 1) {
        fprintf(stderr, "nsinstall: %ls: ambiguous destination file "
                "or directory\n", *pDst);
        return 3;
    } else {
        /*
         * n == 0, meaning that destination file or directory does
         * not exist.  In this case the destination file directory
         * name must be fully specified.
         */

        wchar_t *p;

        for (p = *pDst; *p; p++) {
            if (*p == '*' || *p == '?') {
                fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
                        *pDst);
                return 3;
            }
        }

        /*
         * Do not include the trailing \, if any, unless it is a root
         * directory (\ or X:\).
         */

        if (p > *pDst && p[-1] == '\\' && p != *pDst + 1 && p[-2] != ':') {
            p[-1] = '\0';
        }
        wcscpy(dstData.pathName, *pDst);
        dstData.dwFileAttributes = 0xFFFFFFFF;
    }

    /*
     * If there are two or more source files, the destination has
     * to be a directory.
     */

    if (pDst - pSrc > 1 && !dstIsDir) {
        fprintf(stderr, "nsinstall: cannot copy more than"
                " one file to the same destination file\n");
        return 3;
    }

    if (dstIsDir) {
        arg.dstFileNameMarker = arg.dstFileName + wcslen(arg.dstFileName);

        /*
         * Now arg.dstFileNameMarker is pointing to the null byte at the
         * end of string.  We want to make sure that there is a \ at the
         * end of string, and arg.dstFileNameMarker should point right
         * after that \. 
         */

        if (arg.dstFileNameMarker[-1] != '\\') {
            *(arg.dstFileNameMarker++) = '\\';
        }
    }
    
    if (!dstIsDir) {
        struct sh_FileData srcData;

        assert(pDst - pSrc == 1);
        changeForwardSlashesToBackSlashes(*pSrc);
        sh_EnumerateFiles(*pSrc, *pSrc, sh_RecordFileData, &srcData, &n);
        if (n == 0) {
            fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
                    *pSrc);
            retVal = 3;
        } else if (n > 1) {
            fprintf(stderr, "nsinstall: cannot copy more than one file or "
                    "directory to the same destination\n");
            retVal = 3;
        } else {
            assert(n == 1);
            if (sh_DoCopy(srcData.pathName, srcData.dwFileAttributes,
                    dstData.pathName, dstData.dwFileAttributes,
                    arg.force, arg.recursive) == FALSE) {
                retVal = 3;
            }
        }
        return retVal;
    }

    for ( ; *pSrc != *pDst; pSrc++) {
        BOOL rv;

        changeForwardSlashesToBackSlashes(*pSrc);
        rv = sh_EnumerateFiles(*pSrc, *pSrc, sh_CpFileCmd, &arg, &n);
        if (rv == FALSE) {
            retVal = 3;
        } else {
            if (n == 0) {
                fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
                        *pSrc);
                retVal = 3;
            }
        }
    }

    return retVal;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int shellMkdir ( wchar_t **  pArgv) [static]

Definition at line 152 of file nsinstall_win.c.

{
    int retVal = 0; /* assume valid return */
    wchar_t *arg;
    wchar_t *pArg;
    wchar_t path[_MAX_PATH];
    wchar_t tmpPath[_MAX_PATH];
    wchar_t *pTmpPath = tmpPath;

    /* All the options are simply ignored in this implementation */
    while ( *pArgv && **pArgv == '-' ) {
        if ( (*pArgv)[1] == 'm' ) {
            pArgv++;  /* skip the next argument (mode) */
        }
        pArgv++;
    }

    while ( *pArgv ) {
        arg = *pArgv;
        changeForwardSlashesToBackSlashes ( arg );
        pArg = arg;
        pTmpPath = tmpPath;
        while ( 1 ) {
            /* create part of path */
            while ( *pArg ) {
                *pTmpPath++ = *pArg++;
                if ( *pArg == '\\' )
                    break;
            }
            *pTmpPath = '\0';

            /* check if directory already exists */
            _wgetcwd ( path, _MAX_PATH );
            if ( _wchdir ( tmpPath ) == -1 &&
                 _wmkdir ( tmpPath ) == -1 && // might have hit EEXIST
                 _wchdir ( tmpPath ) == -1) { // so try again
                char buf[2048];
                _snprintf(buf, 2048, "Could not create the directory: %S",
                          tmpPath);
                perror ( buf );
                retVal = 3;
                break;
            } else {
                // get back to the cwd
                _wchdir ( path );
            }
            if ( *pArg == '\0' )      /* complete path? */
                break;
            /* loop for next directory */
        }

        pArgv++;
    }
    return retVal;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int shellNsinstall ( wchar_t **  pArgv) [static]

Definition at line 95 of file nsinstall_win.c.

{
    int retVal = 0;     /* exit status */
    int dirOnly = 0;    /* 1 if and only if -D is specified */
    wchar_t **pSrc;
    wchar_t **pDst;

    /*
     * Process the command-line options.  We ignore the
     * options except for -D.  Some options, such as -m,
     * are followed by an argument.  We need to skip the
     * argument too.
     */
    while ( *pArgv && **pArgv == '-' ) {
        wchar_t c = (*pArgv)[1];  /* The char after '-' */

        if ( c == 'D' ) {
            dirOnly = 1;
        } else if ( c == 'm' ) {
            pArgv++;  /* skip the next argument */
        }
        pArgv++;
    }

    if ( !dirOnly ) {
        /* There are files to install.  Get source files */
        if ( *pArgv ) {
            pSrc = pArgv++;
        } else {
            fprintf( stderr, "nsinstall: not enough arguments\n");
            return 3;
        }
    }

    /* Get to last token to find destination directory */
    if ( *pArgv ) {
        pDst = pArgv++;
        if ( dirOnly && *pArgv ) {
            fprintf( stderr, "nsinstall: too many arguments with -D\n");
            return 3;
        }
    } else {
        fprintf( stderr, "nsinstall: not enough arguments\n");
        return 3;
    }
    while ( *pArgv ) 
        pDst = pArgv++;

    retVal = shellMkdir ( pDst );
    if ( retVal )
        return retVal;
    if ( !dirOnly )
        retVal = shellCp ( pSrc );
    return retVal;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int wmain ( int  argc,
wchar_t *  argv[] 
)

Definition at line 89 of file nsinstall_win.c.

{
    return shellNsinstall ( argv + 1 );
}

Here is the call graph for this function: