diff --git a/buildtools/os2/source/nsinstall/nsinstall.c b/buildtools/os2/source/nsinstall/nsinstall.c new file mode 100644 index 00000000000..1ef323caadc --- /dev/null +++ b/buildtools/os2/source/nsinstall/nsinstall.c @@ -0,0 +1,727 @@ +/* + * The nsinstall command for OS/2 + * + * Our gmake makefiles use the nsinstall command to create the + * object directories or installing headers and libs to ns/dist. + * The shmsdos shell has nsinstall as a built-in command. However, + * if we use another shell like MKS toolkit's sh, we need to have + * the nsinstall command separately. + * + * This file was generated by modifying the Windows nsinstall.c. + * + * To build, say + * icc nsinstall.c + */ + +#include +#include +#include +#include +#define INCL_DOSFILEMGR +#define INCL_DOSERRORS +#include +#pragma hdrstop + +/* + * sh_FileFcn -- + * + * A function that operates on a file. The pathname is either + * absolute or relative to the current directory, and contains + * no wildcard characters such as * and ?. Additional arguments + * can be passed to the function via the arg pointer. + */ + +typedef BOOL (*sh_FileFcn)( + char *pathName, + FILEFINDBUF3 *fileData, + void *arg); + +static int shellCp (char **pArgv); +static int shellNsinstall (char **pArgv); +static int shellMkdir (char **pArgv); +static BOOL sh_EnumerateFiles(const char *pattern, const char *where, + sh_FileFcn fileFcn, void *arg, int *nFiles); +static const char *sh_GetLastErrorMessage(void); +static BOOL sh_DoCopy(char *srcFileName, ULONG srcFileAttributes, + char *dstFileName, ULONG dstFileAttributes, + int force, int recursive); + +static ULONG GetFileAttributes(PSZ pszFileName); +static APIRET SetFileAttributes(PSZ pszFileName, ULONG ulFileAttributes); + +/* changes all forward slashes in token to back slashes */ +void changeForwardSlashesTpBackSlashes ( char *arg ) +{ + if ( arg == NULL ) + return; + + while ( *arg ) { + if ( *arg == '/' ) + *arg = '\\'; + arg++; + } +} + +int main(int argc, char *argv[ ]) +{ + return shellNsinstall ( argv + 1 ); +} + +static int +shellNsinstall (char **pArgv) +{ + int retVal = 0; /* exit status */ + int dirOnly = 0; /* 1 if and only if -D is specified */ + char **pSrc; + char **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 == '-' ) { + char 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; +} + +static int +shellMkdir (char **pArgv) +{ + int retVal = 0; /* assume valid return */ + char *arg; + char *pArg; + char path[CCHMAXPATH]; + char tmpPath[CCHMAXPATH]; + char *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; + changeForwardSlashesTpBackSlashes ( arg ); + pArg = arg; + pTmpPath = tmpPath; + while ( 1 ) { + /* create part of path */ + while ( *pArg ) { + *pTmpPath++ = *pArg++; + if ( *pArg == '\\' ) + break; + } + *pTmpPath = '\0'; + + /* check if directory alreay exists */ + _getcwd ( path, sizeof (path) ); + if (( _chdir ( tmpPath ) != -1 ) || ((tmpPath[1] == ':') && (tmpPath[2] == '\0'))) { + _chdir ( path ); + } else { + if ( _mkdir ( tmpPath ) == -1 ) { +// while ( waitForDebug ); + printf ( "%s: ", tmpPath ); + perror ( "Could not create the directory" ); + retVal = 3; + break; + } + } + if ( *pArg == '\0' ) /* complete path? */ + break; + /* loop for next directory */ + } + + pArgv++; + } + return retVal; +} + +static const char * +sh_GetLastErrorMessage() +{ + static char buf[128]; + +#ifdef OLDCODE + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* default language */ + buf, + sizeof(buf), + NULL + ); +#endif + return buf; +} + +/* + * struct sh_FileData -- + * + * A pointer to the sh_FileData structure is passed into sh_RecordFileData, + * which will fill in the fields. + */ + +struct sh_FileData { + char pathName[CCHMAXPATH]; + ULONG attrFile; +}; + +/* + * sh_RecordFileData -- + * + * Record the pathname and attributes of the file in + * the sh_FileData structure pointed to by arg. + * + * Always return TRUE (successful completion). + * + * This function is intended to be passed into sh_EnumerateFiles + * to see if a certain pattern expands to exactly one file/directory, + * and if so, record its pathname and attributes. + */ + +static BOOL +sh_RecordFileData(char *pathName, FILEFINDBUF3 *findData, void *arg) +{ + struct sh_FileData *fData = (struct sh_FileData *) arg; + + strcpy(fData->pathName, pathName); + fData->attrFile = findData->attrFile; + return TRUE; +} + +static BOOL +sh_DoCopy(char *srcFileName, + ULONG srcFileAttributes, + char *dstFileName, + ULONG dstFileAttributes, + int force, + int recursive +) +{ + if (dstFileAttributes != 0xFFFFFFFF) { + if ((dstFileAttributes & FILE_READONLY) && force) { + dstFileAttributes &= ~FILE_READONLY; + SetFileAttributes(dstFileName, dstFileAttributes); + } + } + + if (srcFileAttributes & FILE_DIRECTORY) { + fprintf(stderr, "nsinstall: %s is a directory\n", + srcFileName); + return FALSE; + } else { + if (DosCopy(srcFileName, dstFileName, DCPY_EXISTING) != NO_ERROR) { + fprintf(stderr, "nsinstall: cannot copy %s to %s: %s\n", + srcFileName, dstFileName, sh_GetLastErrorMessage()); + return FALSE; + } + } + return TRUE; +} + +/* + * struct sh_CpCmdArg -- + * + * A pointer to the sh_CpCmdArg structure is passed into sh_CpFileCmd. + * The sh_CpCmdArg contains information about the cp command, and + * provide a buffer for constructing the destination file name. + */ + +struct sh_CpCmdArg { + int force; /* -f option, ok to overwrite an existing + * read-only destination file */ + int recursive; /* -r or -R option, recursively copy + * directories. Note: this field is not used + * by nsinstall and should always be 0. */ + char *dstFileName; /* a buffer for constructing the destination + * file name */ + char *dstFileNameMarker; /* points to where in the dstFileName buffer + * we should write the file component of the + * destination file */ +}; + +/* + * sh_CpFileCmd -- + * + * Copy a file to the destination directory + * + * This function is intended to be passed into sh_EnumerateFiles to + * copy all the files specified by the pattern to the destination + * directory. + * + * Return TRUE if the file is successfully copied, and FALSE otherwise. + */ + +static BOOL +sh_CpFileCmd(char *pathName, FILEFINDBUF3 *findData, void *cpArg) +{ + BOOL retVal = TRUE; + struct sh_CpCmdArg *arg = (struct sh_CpCmdArg *) cpArg; + + strcpy(arg->dstFileNameMarker, findData->achName); + return sh_DoCopy(pathName, findData->attrFile, + arg->dstFileName, GetFileAttributes(arg->dstFileName), + arg->force, arg->recursive); +} + +static int +shellCp (char **pArgv) +{ + int retVal = 0; + char **pSrc; + char **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 == '-') { + char *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. + */ + + changeForwardSlashesTpBackSlashes(*pDst); + sh_EnumerateFiles(*pDst, *pDst, sh_RecordFileData, &dstData, &n); + assert(n >= 0); + if (n == 1) { + /* + * Is the destination a file or directory? + */ + + if (dstData.attrFile & FILE_DIRECTORY) { + dstIsDir = 1; + } + } else if (n > 1) { + fprintf(stderr, "nsinstall: %s: 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. + */ + + char *p; + + for (p = *pDst; *p; p++) { + if (*p == '*' || *p == '?') { + fprintf(stderr, "nsinstall: %s: 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'; + } + strcpy(dstData.pathName, *pDst); + dstData.attrFile = 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 + strlen(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); + changeForwardSlashesTpBackSlashes(*pSrc); + sh_EnumerateFiles(*pSrc, *pSrc, sh_RecordFileData, &srcData, &n); + if (n == 0) { + fprintf(stderr, "nsinstall: %s: 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.attrFile, + dstData.pathName, dstData.attrFile, + arg.force, arg.recursive) == FALSE) { + retVal = 3; + } + } + return retVal; + } + + for ( ; *pSrc != *pDst; pSrc++) { + BOOL rv; + + changeForwardSlashesTpBackSlashes(*pSrc); + rv = sh_EnumerateFiles(*pSrc, *pSrc, sh_CpFileCmd, &arg, &n); + if (rv == FALSE) { + retVal = 3; + } else { + if (n == 0) { + fprintf(stderr, "nsinstall: %s: No such file or directory\n", + *pSrc); + retVal = 3; + } + } + } + + return retVal; +} + +/* + * sh_EnumerateFiles -- + * + * Enumerate all the files in the specified pattern, which is a pathname + * containing possibly wildcard characters such as * and ?. fileFcn + * is called on each file, passing the expanded file name, a pointer + * to the file's FILEFINDBUF3, and the arg pointer. + * + * It is assumed that there are no wildcard characters before the + * character pointed to by 'where'. + * + * On return, *nFiles stores the number of files enumerated. *nFiles is + * set to this number whether sh_EnumerateFiles or 'fileFcn' succeeds + * or not. + * + * Return TRUE if the files are successfully enumerated and all + * 'fileFcn' invocations succeeded. Return FALSE if something went + * wrong. + */ + +static BOOL sh_EnumerateFiles( + const char *pattern, + const char *where, + sh_FileFcn fileFcn, + void *arg, + int *nFiles + ) +{ + FILEFINDBUF3 fileData = {0}; + HDIR hSearch; + APIRET ulrc; + ULONG ulFindCount = 1; + const char *src; + char *dst; + char fileName[CCHMAXPATH]; + char *fileNameMarker = fileName; + char *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 = strlen(pattern); + if (len >= 2) { + /* Start from the end of pattern and go backward */ + const char *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(!strcmp(fileName, pattern)); + assert(strlen(fileName) >= 1); + if (dst[-1] == '\\' && (dst == fileName + 1 || dst[-2] == ':')) { + fileData.achName[0] = '\0'; + } else { + /* + * Do not include the trailing \, if any + */ + + if (dst[-1] == '\\') { + assert(*fileNameMarker == '\0'); + dst[-1] = '\0'; + fileNameMarker = oldFileNameMarker; + } + strcpy(fileData.achName, fileNameMarker); + } + fileData.attrFile = GetFileAttributes(fileName); + if (fileData.attrFile == 0xFFFFFFFF) { + return TRUE; + } + *nFiles = 1; + return (*fileFcn)(fileName, &fileData, arg); + } + + hSearch = HDIR_CREATE; + ulrc = DosFindFirst(fileName, &hSearch, FILE_NORMAL, &fileData, sizeof(fileData), + &ulFindCount, FIL_STANDARD); + if (ulrc == ERROR_INVALID_HANDLE) { + return retVal; + } + + do { + if (!strcmp(fileData.achName, ".") + || !strcmp(fileData.achName, "..")) { + /* + * Skip over . and .. + */ + + continue; + } + + if (patternEndsInDotStar) { + int nDots = 0; + char *p = fileData.achName; + while (*p) { + if (*p == '.') { + nDots++; + } + p++; + } + /* Now p points to the null byte at the end of file name */ + if (patternEndsInDot && (p == fileData.achName + || 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; + } + } + + strcpy(fileNameMarker, fileData.achName); + if (*src && *(src + 1)) { + /* + * More to go. Recurse. + */ + + int n; + + assert(*src == '\\'); + where = fileName + strlen(fileName); + strcat(fileName, src); + sh_EnumerateFiles(fileName, where, fileFcn, arg, &n); + *nFiles += n; + } else { + assert(strchr(fileName, '*') == NULL); + assert(strchr(fileName, '?') == NULL); + (*nFiles)++; + if ((*fileFcn)(fileName, &fileData, arg) == FALSE) { + retVal = FALSE; + } + } + } while (DosFindNext(hSearch, &fileData, sizeof(fileData), &ulFindCount) == NO_ERROR); + + DosFindClose(hSearch); + return retVal; +} + +static ULONG GetFileAttributes(PSZ pszFileName) +{ + FILESTATUS3 fsts3; + APIRET rc; + + rc = DosQueryPathInfo(pszFileName, + FIL_STANDARD, + &fsts3, + sizeof(FILESTATUS3)); + if (rc != NO_ERROR) { + return -1; + } /* endif */ + return fsts3.attrFile; +} + +static APIRET SetFileAttributes(PSZ pszFileName, ULONG ulFileAttributes) +{ +} +