/* * 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) { }