#define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0400 #include <fcntl.h> #include <io.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <windows.h> static const char *p4user, *p4port, *p4client; /* * Extension list pulled shamelessly from * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sccvs70/html/vetskAddingSourceControlFileExtensions.asp * This must be NULL terminated because I'm too lazy to calculate how long the array is. */ static const char *extensions[] = {"*.sln", "*.vbproj", "*.vbdproj", "*.vbp", "*.csproj", "*.csdproj", "*.vjsproj", "*.vcproj", "*.dsp", "*.mdp", "*.mak", "*.vfpproj", "*.vdp", "*.vdproj", "*.dbp", "*.vsmproj", "*.vsmacros", "*.hwproj", "*.etp", "*.etpproxy", "*.actproj", "*.atp", "*.dmp", "*.mdmp", "*.dsw", NULL}; size_t stripchars = 1; FILE *fdTrace = 0; static void trace(const char *fmt, ...); static int scandir(const char *dirpath); static int scanfiles(const char *dirpath); static char *newPathWithoutDelimiters(const char *path); static char *striptail(char *path); static void usage(); static int writeinfo(const char *port, const char *user, const char *client, const char *file, const char *dir); static BOOL WriteNewLineToPrivateProfile(LPCTSTR lpFileName); static void trace(const char *fmt, ...) { const char *p = fmt; #if _DEBUG va_list ap; va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); #endif } static BOOL WriteNewLineToPrivateProfile(LPCTSTR lpFileName) { errno_t err; BOOL result = TRUE; FILE *fd; err = fopen_s(&fd, lpFileName, "a"); if (0 != err) { result = FALSE; return result; } fprintf_s(fd, "\n"); fclose(fd); return result; } static void usage() { fprintf(stderr, "Usage: p4bindgen <port> <user> <client> [directory ...]\n"); fprintf(stderr, "\tport = P4PORT\n"); fprintf(stderr, "\tuser = P4USER\n"); fprintf(stderr, "\tclient = P4CLIENT\n"); fprintf(stderr, "\tThis utility generates MSSCCPRJ.SCC files suitable for Visual Studio.\n"); fprintf(stderr, "\tUnless a list of directories is passed in on the commandline, the utility\n"); fprintf(stderr, "\tsearches for project files rooted in the current directory.\n"); exit(1); } /* Recursive function to scan the named path and all subdirectories. */ static int scandir(const char *dirpath) { char *dir = NULL; // removed static here, breaks recursion otherwise. static char *searchpattern = NULL; // this can be static, though WIN32_FIND_DATA dirdata; HANDLE findhandle; memset(&dirdata, 0, sizeof(WIN32_FIND_DATA)); if (searchpattern == NULL) { searchpattern = (char *)malloc(2048); if (searchpattern == NULL) return FALSE; } if (dir == NULL) { dir = (char *)malloc(2048); if (dir == NULL) return FALSE; } // Scan all of the files in this directory. scanfiles(dirpath); // Start the recursive scan. sprintf_s(searchpattern, 2048, "%s\\*", dirpath); findhandle = FindFirstFileEx(searchpattern, FindExInfoStandard, &dirdata, FindExSearchLimitToDirectories, NULL, 0); if (findhandle != INVALID_HANDLE_VALUE) { do { // We have to explicitly check the FILE_ATTRIBUTE_DIRECTORY since // the FindExSearchLimitToDirectories param to FindFirstFileEx is // only advisory. Go Microsoft! if ((dirdata.cFileName[0] != '.') && (dirdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { sprintf_s(dir, 2048, "%s\\%s", dirpath, dirdata.cFileName); scandir(dir); } } while (FindNextFile(findhandle, &dirdata)); FindClose(findhandle); } free(dir); return TRUE; } /* Function to scan for the named extensions. */ static int scanfiles(const char *dirpath) { static char *searchpattern = NULL; WIN32_FIND_DATA filedata; HANDLE findhandle; int i; memset(&filedata, 0, sizeof(WIN32_FIND_DATA)); if (searchpattern == NULL) { searchpattern = (char *)malloc(2048); if (searchpattern == NULL) return FALSE; } for(i = 0; extensions[i] != NULL; i++) { sprintf_s(searchpattern, 2048, "%s\\%s", dirpath, extensions[i]); findhandle = FindFirstFile(searchpattern, &filedata); if (findhandle != INVALID_HANDLE_VALUE) { do { writeinfo(p4port, p4user, p4client, filedata.cFileName, dirpath); } while (FindNextFile(findhandle, &filedata)); FindClose(findhandle); } } return TRUE; } static char * newPathWithoutDelimiters(const char *path) { size_t begin; size_t end; char *newPath; begin = 0; end = strlen(path); // Strip off CRLF if it is around. if ((end > 2) && (path[end-2] == (char)13) && (path[end-1] == (char)10)) { end -= 2; } // Strip of leading and trailing quotes, if found // if (path[begin] == '\"') { begin = 1; } if (path[end-1] == '\"') { end -= 1; } // Grotty way to strip \... from the end of the directory path. // Perforce likes to add it occasionally. Also strip trailing \ if ((end > 4) && (path[end-4] == '\\') && (path[end-3] == '.') && (path[end-2] == '.') && (path[end-1] == '.')) { end -= 4; } else if ((end > 1) && (path[end-1] == '\\')) { end -= 1; } // Create the sanitized path // newPath = (char *)malloc(end - begin); memcpy(newPath,&path[begin],end - begin); newPath[end - begin] = '\0'; // Get out // return newPath; } static char * striptail(char *path) { size_t end; end = strlen(path); // Strip off CRLF if it is around. if ((end > 2) && (path[end-2] == (char)13) && (path[end-1] == (char)10)) path[end-2] = '\0'; end = strlen(path); // Grotty way to strip \... from the end of the directory path. // Perforce likes to add it occasionally. Also strip trailing \ if ((end > 4) && (path[end-4] == '\\') && (path[end-3] == '.') && (path[end-2] == '.') && (path[end-1] == '.')) path[end-4] = '\0'; else if ((end > 1) && (path[end-1] == '\\')) path[end-1] = '\0'; return path; } /* * Write the appropriate entries into the MSSCCPRJ.SCC file. Create the * MSSCCPRJ.SCC file with the appropriate header if it doesn't already exist. */ static int writeinfo(const char *port, const char *user, const char *client, const char *file, const char *dir) { FILE *fd; static char *auxpath = NULL; static char *outputfile = NULL; static char *header = "SCC = This is a source code control file\n\n"; errno_t err; if (outputfile == NULL) { outputfile = (char*)malloc(2048); if (outputfile == NULL) return FALSE; // Only do this the first time, in the root directory sprintf_s(outputfile, 2048, "%s\\MSSCCPRJ.SCC", dir); } if (auxpath == NULL) { auxpath = (char*)malloc(1024); if (auxpath == NULL) return FALSE; sprintf_s(auxpath, 1024, "\"P4SCC#%s##%s##%s\"", port, user, client); } err = fopen_s(&fd, outputfile, "r"); if (fd == NULL) { // If the file doesn't exist, create it and add the appropriate header. printf("Creating new binding file: .%s\n", outputfile + stripchars); err = fopen_s(&fd, outputfile, "w"); fwrite(header, sizeof(char), strlen(header), fd); fclose(fd); } else { // Header should already exist. fclose(fd); } printf("Writing binding for: .%s\\%s\n", dir + stripchars, file); WritePrivateProfileString(file, "SCC_Aux_Path", auxpath, outputfile); WritePrivateProfileString(file, "SCC_Project_Name", "Perforce Project", outputfile); WriteNewLineToPrivateProfile(outputfile); return TRUE; } int main(int argc, char* argv[]) { int i, fd; FILE *file; size_t len; PROCESS_INFORMATION procinfo; STARTUPINFO startinfo; SECURITY_ATTRIBUTES attr; HANDLE mystdout, childstdout, temphandle, myread; char buf[1024]; char *buffer; char *pbuf; char *epbuf; char *process; size_t len2; if (argc < 4) usage(); p4port = argv[1]; p4user = argv[2]; p4client = argv[3]; if (4 <= argc) { trace("argv[1] = '%s'\n", p4port); trace("argv[2] = '%s'\n", p4user); trace("argv[3] = '%s'\n", p4client); } if (5 <= argc) { trace("argv[4] = '%s'\n", argv[4]); } // If we don't have a named directory, just look in the current one. if (argc == 4) scandir("."); else { // See how long the string will be, space separated (meaning +1). for(i = 4, len = 0; i < argc; i++) { if (argv[i][0] == '/' && argv[i][1] == '/') len += strlen(argv[i]) + 1; else { buffer = newPathWithoutDelimiters(argv[i]); stripchars = strlen(buffer); scandir(buffer); } } // Skip everything else if we don't have any perforce depot paths. if (len == 0) return 0; len2 = 11 + len + 1; process = (char *)malloc(len2); sprintf_s(process, len2, "p4 where "); for(i = 4; i < argc; i++) if (argv[i][0] == '/' && argv[i][1] == '/') sprintf_s(process, len2, "%s \"%s\"", process, argv[i]); trace("process = '%s'\n", process); // Goo for using CreateProcess ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION)); ZeroMemory(&startinfo, sizeof(STARTUPINFO)); startinfo.cb = sizeof(STARTUPINFO); // Goo for using CreatePipe attr.nLength = sizeof(SECURITY_ATTRIBUTES); attr.bInheritHandle = TRUE; attr.lpSecurityDescriptor = NULL; // Basically we set my stdout to a pipe. That way when we fork a // child, he inherits our stdout. We restore our stdout and use // the other half of the pipe to get what the child is outputting. mystdout = GetStdHandle(STD_OUTPUT_HANDLE); CreatePipe(&temphandle, &childstdout, &attr, 0); SetStdHandle(STD_OUTPUT_HANDLE, childstdout); DuplicateHandle(GetCurrentProcess(), temphandle, GetCurrentProcess(), &myread, 0, FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(temphandle); CreateProcess(NULL, process, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &procinfo); CloseHandle(procinfo.hProcess); CloseHandle(procinfo.hThread); SetStdHandle(STD_OUTPUT_HANDLE, mystdout); // If we don't close the childstdout, when we try to read, it will // block since there is an open reference. CloseHandle(childstdout); trace("made it here.\n"); // Convert from a Windows handle into a C standard descriptor and // finally into a stream. fd = _open_osfhandle((intptr_t)myread, _O_RDONLY); file = _fdopen(fd, "r"); while(fgets(buf, sizeof(buf), file)) { trace("buf = '%s'\n", buf); // Skip exclusionary mappings that start with '-' if (buf[0] == '-') continue; // Skip the first 2 tokens, we are interested in the last. // NOTE: first two tokens should end with separated by "/... ". // pbuf = buf; // skip first token while (*pbuf) { if (('/' == pbuf[0]) && ('.' == pbuf[1]) && ('.' == pbuf[2]) && ('.' == pbuf[3]) && (' ' == pbuf[4])) { pbuf += 5; break; } pbuf += 1; } // skip second token while (*pbuf) { if (('/' == pbuf[0]) && ('.' == pbuf[1]) && ('.' == pbuf[2]) && ('.' == pbuf[3]) && (' ' == pbuf[4])) { pbuf += 5; break; } pbuf += 1; } // find the end of the third token epbuf = pbuf; while (*epbuf) { if (('\\' == epbuf[0]) && ('.' == epbuf[1]) && ('.' == epbuf[2]) && ('.' == epbuf[3])) { epbuf[4] = '\0'; break; } epbuf += 1; } trace("pbuf = '%s'\n", pbuf); buffer = striptail(pbuf); trace("buffer = '%s'\n", buffer); stripchars = strlen(buffer); scandir(buffer); } // Clean up our handle. fclose(file); if (0 != fdTrace) { fclose(fdTrace); } } return 0; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 6108 | Reed Snellenberger | Updated to VS 2005; creates a single MSSCCPRJ.SCC file at the solution root rather than scattering them around... | ||
#2 | 6066 | Reed Snellenberger |
make dir non-static so that the recursion algorithm works correctly. If you don't do this, it only follows takes the first branch in each high-level directory... |
||
#1 | 6063 | Reed Snellenberger | Integrate the p4bindgen project to add changes/fixes etc | ||
//guest/gordon_tetlow/p4bindgen/src/p4bindgen.cpp | |||||
#1 | 5319 | Gordon Tetlow | Move the source files into a src directory. | ||
//guest/gordon_tetlow/p4bindgen/p4bindgen.cpp | |||||
#1 | 4957 | Gordon Tetlow |
Add my first version of p4bindgen. This tool is meant to be used in the tools menu of P4Win. You point P4Win at a tree and run this and it will generate the MSSCCPRJ.SCC files for any of the files that need binding information. |