/*++ Copyright (c) 1993 Microsoft Corporation Module Name: pch.c Abstract: This file is a tool for converting an existing slm project to build using pre-compiled header files. Author: Wesley Witt (wesw) 1-Oct-1993 Environment: User Mode --*/ #include #include #include #include typedef struct _tagFILEINFO { HANDLE hFile; HANDLE hMap; LPSTR fptr; CHAR fname[MAX_PATH]; } FILEINFO, *PFILEINFO; char hfiles[5000][20]; DWORD idx; CHAR ipath[100][MAX_PATH]; DWORD cipath; #define BAD_FILE (DWORD)-1 // // options flags // BOOL fModifySource; BOOL fVerbose; BOOL fGenerateHeader; BOOL fRecurseDirs; BOOL fDoReadOnly; BOOL fCheckOut; BOOL fModifySlmSourcesFile; DWORD LinenoLimit = 0xefffffff; CHAR szHeaderFileName[MAX_PATH]; CHAR szRecurseDir[MAX_PATH]; VOID GetCommandLineArgs( VOID ); BOOL MapInputFile ( PFILEINFO lpfi ); VOID UnMapFile( PFILEINFO lpfi ); VOID FindHeaderFiles( LPSTR SourceFileName ); VOID ChangeSourceFile( LPSTR fname ); VOID GenerateHeaderFile( LPSTR fname ); VOID ProcessFilesInTree( LPSTR RootPath, DWORD Action ); VOID Usage( VOID ); BOOL IsValidSourceFile( LPSTR fname ); BOOL CheckOutSourceFile( LPSTR fname ); VOID ChangeSlmSourcesFile( VOID ); DWORD FindHeaderFile( LPSTR HeaderFileName ); VOID GetIncludePath( VOID ); void _cdecl main( void ) /*++ Routine Description: This is the entry point for the pch tool. Arguments: None. Return Value: None. --*/ { GetCommandLineArgs(); GetIncludePath(); if (fVerbose) { printf("processing source files\n"); } ProcessFilesInTree( szRecurseDir, 1 ); if (fGenerateHeader) { if (fVerbose) { printf("creating headerfile: %s\n", szHeaderFileName); } GenerateHeaderFile( szHeaderFileName ); } if (fModifySource) { if (fVerbose) { printf("modifying source files\n"); } ProcessFilesInTree( szRecurseDir, 2 ); } if (fModifySlmSourcesFile) { ChangeSlmSourcesFile(); } } BOOL MapInputFile ( PFILEINFO lpfi ) /*++ Routine Description: Opens and maps a file or reading only. Arguments: lpfi - pointer to a FILEINFO structure, only the file name need be filled in before calling. Return Value: None. --*/ { lpfi->hFile = CreateFile( lpfi->fname, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (lpfi->hFile == INVALID_HANDLE_VALUE) { return FALSE; } lpfi->hMap = CreateFileMapping( lpfi->hFile, NULL, PAGE_READONLY, 0, 0, NULL ); if (lpfi->hMap == INVALID_HANDLE_VALUE) { CloseHandle( lpfi->hFile ); return FALSE; } lpfi->fptr = MapViewOfFile( lpfi->hMap, FILE_MAP_READ, 0, 0, 0 ); if (lpfi->fptr == NULL) { CloseHandle( lpfi->hFile ); CloseHandle( lpfi->hMap ); return FALSE; } return TRUE; } BOOL MapOutputFile ( PFILEINFO lpfi, DWORD fsize ) /*++ Routine Description: Opens and maps a file or reading and writing. Arguments: lpfi - pointer to a FILEINFO structure, only the file name need be filled in before calling. Return Value: None. --*/ { lpfi->hFile = CreateFile( lpfi->fname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL ); if (lpfi->hFile == INVALID_HANDLE_VALUE) { return FALSE; } lpfi->hMap = CreateFileMapping( lpfi->hFile, NULL, PAGE_READWRITE, 0, fsize, NULL ); if (lpfi->hMap == INVALID_HANDLE_VALUE) { CloseHandle( lpfi->hFile ); return FALSE; } lpfi->fptr = MapViewOfFile( lpfi->hMap, FILE_MAP_WRITE, 0, 0, 0 ); if (lpfi->fptr == NULL) { CloseHandle( lpfi->hFile ); CloseHandle( lpfi->hMap ); return FALSE; } return TRUE; } VOID UnMapFile( PFILEINFO lpfi ) /*++ Routine Description: Closes all handles and views for an opened/mapped file. Arguments: lpfi - pointer to a FILEINFO structure Return Value: None. --*/ { CloseHandle( lpfi->hFile ); CloseHandle( lpfi->hMap ); UnmapViewOfFile( lpfi->fptr ); } VOID FindHeaderFiles( LPSTR SourceFileName ) /*++ Routine Description: Scans a source file an locates all header files used by the source file. The names are stored in a global array. Arguments: SourceFileName - name of source file to begin search Return Value: None. --*/ { LPSTR p; DWORD i; CHAR fname[MAX_PATH]; CHAR drive[_MAX_FNAME]; CHAR dir[_MAX_DIR]; CHAR name[_MAX_FNAME]; CHAR ext[_MAX_EXT]; CHAR buf[MAX_PATH]; FILEINFO fi; strcpy( fi.fname, SourceFileName ); MapInputFile( &fi ); p = fi.fptr; while (p && *p) { if (*p == '#') { if (strncmp(p,"#include",8)==0) { p += 8; while (p && *p==' ') { p++; } p++; i = 0; while (*p != '>' && *p != '"') { fname[i] = *p; p++; i++; } fname[i] = '\0'; if (IsValidSourceFile(fname)) { _splitpath( SourceFileName, drive, dir, name, ext ); sprintf( buf, "%s%s", drive, dir ); _splitpath( fname, drive, dir, name, ext ); strcat( buf, dir ); sprintf( fname, "%s%s", name, ext ); strcat( buf, fname ); _fullpath( fname, buf, sizeof(fname) ); FindHeaderFiles( fname ); } else { for (i=0; i 1) { sprintf( p, "#include \"%s\"\r\n", hfiles[i] ); } else { sprintf( p, "#include <%s>\r\n", hfiles[i] ); } p += strlen(p); } SetFilePointer( fi.hFile, p-fi.fptr, NULL, FILE_BEGIN ); SetEndOfFile( fi.hFile ); UnMapFile( &fi ); } VOID ChangeSlmSourcesFile( VOID ) /*++ Routine Description: Makes all necessary changes to the project's sources file. This is simply to add the necessary macros for builing with pch files. Arguments: None. Return Value: None. --*/ { #define strout(s) strcpy(p,s); p+=strlen(p) LPSTR p,p1; FILEINFO fi; FILEINFO fiout; DWORD dwHigh; DWORD fsize; DWORD i; CHAR fname[MAX_PATH]; if (fVerbose) { printf("modifying slm sources file\n"); } _fullpath( fname, "sources", sizeof(fname) ); CheckOutSourceFile( fname ); strcpy( fi.fname, "sources" ); MapInputFile( &fi ); fsize = GetFileSize( fi.hFile, &dwHigh ); strcpy( fiout.fname, "temp.xxx" ); MapOutputFile( &fiout, fsize + 1024 ); p1 = fi.fptr; p = fiout.fptr; while (p1 && *p1) { if (*p1 == 0x1a) { break; } if (strncmp(p1,"SOURCES=",8)==0) { for (i=0; i<8; i++) { *p++ = *p1++; } sprintf(p, "%s ", szHeaderFileName); p += strlen(p); } *p++ = *p1++; } _splitpath( szHeaderFileName, NULL, NULL, fname, NULL ); strout( "\r\n" ); // strout( "!IFNDEF NTNOPCH\r\n" ); strout( "PRECOMPILED_INCLUDE=" ); strout( fname ); strout( ".h\r\n" ); strout( "PRECOMPILED_PCH=" ); strout( fname ); strout( ".pch\r\n" ); strout( "PRECOMPILED_OBJ=" ); strout( fname ); strout( ".obj\r\n" ); // strout( "!ENDIF\r\n" ); strout( "\r\n" ); SetFilePointer( fiout.hFile, p-fiout.fptr, NULL, FILE_BEGIN ); SetEndOfFile( fiout.hFile ); UnMapFile( &fiout ); UnMapFile( &fi ); DeleteFile( "sources" ); MoveFile( "temp.xxx", "sources" ); } VOID GetCommandLineArgs( VOID ) /*++ Routine Description: Retreives the command line arguments and sets the appropriate global variables. Arguments: None. Return Value: None. --*/ { char *lpstrCmd = GetCommandLine(); UCHAR ch; DWORD i = 0; // skip over program name do { ch = *lpstrCmd++; } while (ch != ' ' && ch != '\t' && ch != '\0'); // skip over any following white space while (ch == ' ' || ch == '\t') { ch = *lpstrCmd++; } // process each switch character '-' as encountered while (ch == '-' || ch == '/') { ch = tolower(*lpstrCmd++); // process multiple switch characters as needed do { switch (ch) { case 'h': // skip whitespace do { ch = *lpstrCmd++; } while (ch == ' ' || ch == '\t'); i=0; while (ch != ' ' && ch != '\0') { szHeaderFileName[i++] = ch; ch = *lpstrCmd++; } szHeaderFileName[i] = '\0'; fGenerateHeader = TRUE; break; case 'm': ch = *lpstrCmd++; fModifySource = TRUE; break; case 'r': // skip whitespace do { ch = *lpstrCmd++; } while (ch == ' ' || ch == '\t'); i=0; while (ch != ' ' && ch != '\0') { szRecurseDir[i++] = ch; ch = *lpstrCmd++; } szRecurseDir[i] = '\0'; fRecurseDirs = TRUE; break; case 'v': ch = *lpstrCmd++; fVerbose = TRUE; break; case 'o': ch = *lpstrCmd++; fDoReadOnly = TRUE; break; case 't': ch = *lpstrCmd++; fCheckOut = TRUE; break; case 'l': ch = *lpstrCmd++; fModifySlmSourcesFile = TRUE; break; case 'n': // skip whitespace do { ch = *lpstrCmd++; } while (ch == ' ' || ch == '\t'); i=0; while (ch >= '0' && ch <= '9') { i = i * 10 + ch - '0'; ch = *lpstrCmd++; } LinenoLimit = i; break; case '?': Usage(); ch = *lpstrCmd++; break; default: return; } } while (ch != ' ' && ch != '\t' && ch != '\0'); while (ch == ' ' || ch == '\t') { ch = *lpstrCmd++; } } return; } VOID Usage( VOID ) /*++ Routine Description: Prints usage text for this tool. Arguments: None. Return Value: None. --*/ { fprintf( stderr, "Microsoft (R) Windows NT (TM) Version 3.1 PCH\n" ); fprintf( stderr, "Copyright (C) 1993 Microsoft Corp. All rights reserved\n\n" ); fprintf( stderr, "usage: PCH [-?] [-v] [-m] [-o] [-t] [-l] [-n lines][-h headerfile] [-r directory]\n" ); fprintf( stderr, " [-?] display this message\n" ); fprintf( stderr, " [-v] verbose output\n" ); fprintf( stderr, " [-m] modify source files\n" ); fprintf( stderr, " [-o] override readonly\n" ); fprintf( stderr, " [-t] check out if readonly\n" ); fprintf( stderr, " [-l] modify slm sources file\n" ); fprintf( stderr, " [-n lines] limit header file scan to n lines\n" ); fprintf( stderr, " [-h headerfile] - create pch header file\n" ); fprintf( stderr, " [-r directory] - recusively process all source files\n" ); ExitProcess(0); } VOID ProcessFilesInTree( LPSTR RootPath, DWORD Action ) /*++ Routine Description: Locates all source files for a given tree and performs a requested action on the source file. Arguments: RootPath - Root of the tree to process Action - Action to perform 1 = Find all header files used by source file 2 = Change the source file to use pch file Return Value: None. --*/ { #define MAX_DEPTH 32 LPSTR FilePart; PUCHAR Prefix = ""; CHAR PathBuffer[ MAX_PATH ]; ULONG Depth; PCHAR PathTail[ MAX_DEPTH ]; PCHAR FindHandle[ MAX_DEPTH ]; LPWIN32_FIND_DATA FindFileData; UCHAR FindFileBuffer[ MAX_PATH + sizeof( WIN32_FIND_DATA ) ]; CHAR CurrentImageName[ MAX_PATH ]; strcpy( PathBuffer, RootPath ); FindFileData = (LPWIN32_FIND_DATA)FindFileBuffer; Depth = 0; while (TRUE) { startDirectorySearch: PathTail[ Depth ] = strchr( PathBuffer, '\0' ); if (PathTail[ Depth ] > PathBuffer && PathTail[ Depth ][ -1 ] != '\\') { *(PathTail[ Depth ])++ = '\\'; } strcpy( PathTail[ Depth ], "*.*" ); FindHandle[ Depth ] = FindFirstFile( PathBuffer, FindFileData ); if (FindHandle[ Depth ] != INVALID_HANDLE_VALUE) { do { if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (strcmp( FindFileData->cFileName, "." ) && strcmp( FindFileData->cFileName, ".." ) && Depth < MAX_DEPTH ) { sprintf( PathTail[ Depth ], "%s\\", FindFileData->cFileName ); Depth++; if (fVerbose) { printf( "processing dir %s\n", FindFileData->cFileName ); } goto startDirectorySearch; } goto restartDirectorySearch; } else if (!IsValidSourceFile( FindFileData->cFileName )) { goto restartDirectorySearch; } else if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) { if (fCheckOut) { } else if (!fDoReadOnly) { if (fVerbose) { printf( "skipping %s - readonly file\n", FindFileData->cFileName ); } goto restartDirectorySearch; } } strcpy( PathTail[ Depth ], FindFileData->cFileName ); if (!GetFullPathName( PathBuffer, sizeof( CurrentImageName ), CurrentImageName, &FilePart )) { printf( "invalid file name - %s (%u)\n", PathBuffer, GetLastError() ); } else { if (Action == 1) { if (fVerbose) { printf("parsing %s\n", CurrentImageName); } FindHeaderFiles( CurrentImageName ); } else if (Action == 2) { if (fVerbose) { printf("modifying %s\n", CurrentImageName); } ChangeSourceFile( CurrentImageName ); } } restartDirectorySearch: ; } while (FindNextFile( FindHandle[ Depth ], FindFileData )); FindClose( FindHandle[ Depth ] ); if (Depth == 0) { break; } Depth--; goto restartDirectorySearch; } } return; } BOOL IsValidSourceFile( LPSTR fname ) /*++ Routine Description: Determines if a file name is a source file. The determination is based on the extension of the file. Arguments: fname - name of file Return Value: TRUE - file is a source file FALSE - file is NOT a source file --*/ { char ext[20]; _splitpath( fname, NULL, NULL, NULL, ext ); if (_stricmp(ext,".c")==0) { return TRUE; } else if (_stricmp(ext,".cxx")==0) { return TRUE; } else if (_stricmp(ext,".cpp")==0) { return TRUE; } else { return FALSE; } } BOOL CheckOutSourceFile( LPSTR fname ) /*++ Routine Description: Checks the file out of the SLM project so that it may be changed. Arguments: fname - fully qualified file name Return Value: TRUE - file was checked ouy FALSE - file was NOT checked out --*/ { CHAR szCmdLine[256]; STARTUPINFO si; PROCESS_INFORMATION pi; DWORD dwExitCode; CHAR dir[_MAX_DIR]; CHAR name[_MAX_FNAME]; CHAR ext[_MAX_EXT]; if (!(GetFileAttributes( fname ) & FILE_ATTRIBUTE_READONLY)) { return TRUE; } if (fVerbose) { printf( "checking out %s\n", fname ); } _splitpath( fname, NULL, dir, name, ext ); sprintf( szCmdLine, "out -f %s%s", name, ext ); GetStartupInfo( &si ); if (!CreateProcess( NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, dir, &si, &pi )) { return FALSE; } WaitForSingleObject( pi.hProcess, INFINITE ); if (!GetExitCodeProcess( pi.hProcess, &dwExitCode )) { return FALSE; } if (dwExitCode) { return FALSE; } return TRUE; } VOID GetIncludePath( VOID ) /*++ Routine Description: Gets the include path from the slm sources file. Arguments: None. Return Value: None. --*/ { FILEINFO fi; LPSTR p; LPSTR p2; DWORD i; DWORD j; CHAR s[MAX_PATH]; strcpy( fi.fname, "sources" ); MapInputFile( &fi ); p = fi.fptr; while (p && *p) { if (strncmp(p,"INCLUDES",8)==0) { p+=9; i=0; while (*p != '\n') { s[i] = *p; p++; i++; } s[i] = '\0'; break; } p++; } UnMapFile( &fi ); strcpy( ipath[0], "\\nt\\public\\sdk\\inc" ); strcpy( ipath[1], "\\nt\\public\\sdk\\inc\\crt" ); strcpy( ipath[2], "." ); if (*s) { p = p2 = s; j = 3; while (*p && *p != '\n') { if (*p == ';') { *p = '\0'; strcpy( ipath[j++], p2 ); p2 = p + 1; } p++; } if (*p != ';') { *(p-1) = '\0'; strcpy( ipath[j++], p2 ); } cipath = j; } return; } DWORD FindHeaderFile( LPSTR HeaderFileName ) /*++ Routine Description: Gets the path where a header file is located. The file is searched for in the public directorys, the current directory, and all paths specified in the slm sources file. Arguments: HeaderFileName - name of the header file Return Value: Index of the path where the header file is located. --*/ { DWORD i; WIN32_FIND_DATA fd; HANDLE hfind; CHAR fname[MAX_PATH]; for (i=0; i