// p4.e // // Wed, 08/16/2000 06:31:02 // // Purpose: // // This extension is designed to interact with the perforce // version control system. A problem with most VCS systems is // that in the typical multi-programmer environment files are // stored locally as Read Only files. This is only a problem in // the sense that the programmer must explicitly lock the file // before editing it. This extension handles typical a check out // with edit, differences with current tip revisions and current // revision history. // // Usage: // // Compile and load this extension. The only assumptions made by // this extensions are: // // 1) The VCS system is set up for Multi-Programmer lock // use with IDs and the local files are stored in read // only format when not checked out (a requirement for // most systems). // 2) The VCS system is configured to automatically find // it's log files based on the directory in which the // file being edited resides. // // If 1) is not true, you do not need this extension. If 4) is // not true, then you will need to modify the cfg file for the // system in use to make this true. // // Details: // // The basic thread of each command is to save the current editing // environment, perform the command and restore the system. In // order: // // The current point, mark, buffer and filename are stored. // If the file is modified, it is saved. A temporary buffer // is created to maintain the window space of the buffer (for // the current window only). At the end of this, the current // process buffer, error_point and grep variables are saved. // The reason the the process save is made is to facilitate // changes required as a result of compiler or grep searches // without interrupting the current search. // // Once the command is complete, the new file is re-read and // placed in the saved window. The buffer point and mark are // restored and the grep/compile state is reset. // // The commands are not bound to any keys. These are not really // keystroke operations and were forced to the A-X command line. // If you need them bound to keys, go right ahead. // // The modifications to this code are dedicated to the public domain // with the caveat that Lugaru is welcome to use this within their // distribution source code which is supplied with Epsilon. Use it // any way you want for whatever purposes. // // // John Kercheval // 4002 NE 204th St // Lake Forest Park, WA 98155-1606 // INTERNET: kercheval@bigfoot.com // // // Modification History: // // J. Kercheval Wed, 08/16/2000 05:40:09 Creation from VCS extension // J. Kercheval Wed, 10/20/2001 19:45:08 Add checks in readonly // checks to limit the calls to perforce when not needed. // J. Kercheval Mon, 10/22/2001 14:40:55 When perforce can not // check out a file. Modify the read only status of the file // anyway. // J. Kercheval Sat, 11/03/2001 15:30:46 Correct a process // buffer issue related to Epsilon 11 release. // J. Kercheval 6-09-2003 12:35:21 pm Use shell directly rather // than the dopush routines for V12 (which supports multiple // processes). // #include #include #define BOOLEAN int #define TRUE 1 #define FALSE 0 #define _A_NORMAL 0x00 // Normal file - No read/write restrictions #define _A_RDONLY 0x01 // Read only file #define _A_HIDDEN 0x02 // Hidden file #define _A_SYSTEM 0x04 // System file #define _A_VOLID 0x08 // Volume ID file #define _A_SUBDIR 0x10 // Subdirectory #define _A_ARCH 0x20 // Archive file #define PROCESS_RESULT_BUFFER "p4-result" #define PROMPT_LENGTH 2*FNAMELEN char _P4Buffer[FNAMELEN]; // current buffer name char _P4Filename[FNAMELEN]; // current buffer associated file name char _P4EditPath[FNAMELEN]; // the path of the current epsilon session char _P4TargetPath[FNAMELEN]; // the path of the current buffer char *_P4TempBuf; // a temp buffer to maintain the current window int _P4Point; // current buffer point int _P4Mark; // current buffer mark BOOLEAN _P4Translate; // current buffer is translating //////////////////////////////////////////////////////////////////////////// // // IsWritable() returns FALSE if the file is write only and TRUE // otherwise // // BOOLEAN IsWritable(f) char *f; { struct file_info fi; // // validate a valid parameter // if (!strlen(f)) { return FALSE; } // // check the file attribute, if it doesn't exist then it is writable // if (!check_file(f, &fi)) { return TRUE; } if (fi.attr & _A_RDONLY && !(fi.attr & _A_SUBDIR)) { return FALSE; } else { return TRUE; } } //////////////////////////////////////////////////////////////////////////// // // showResults will display the p4 result buffer appropriately // // void showResults(buf) char *buf; { quiet_set_bookmark(); bufname = buf; point = 0; locate_window(buf, ""); } //////////////////////////////////////////////////////////////////////////// // // P4Start() validates a buffer and saves the current buffer state // // BOOLEAN P4Start() { char *str; // // save the current buffer state and file // _P4Point = point; _P4Mark = mark; _P4Translate = translation_type; strcpy(_P4Buffer, bufname); strcpy(_P4Filename, filename); if (modified && IsWritable(filename)) { do_save_file(0, 1, 1); } // // obtain the current edit path and change to target path // getcd(_P4EditPath); str = get_tail(filename, TRUE); strcpy(_P4TargetPath, filename); _P4TargetPath[strlen(filename) - strlen(str)] = '\0'; chdir(_P4TargetPath); // // create a temporary buffer and use it to maintain the window // _P4TempBuf = temp_buf(); to_buffer(_P4TempBuf); // // remove the current file buffer // delete_buffer(_P4Buffer); // // move the process output buffer // delete_buffer(PROCESS_RESULT_BUFFER); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // P4End() restores a buffer // // BOOLEAN P4End() { // // restore the target buffers vital stats // find_in_other_buf(_P4Filename, _P4Translate); point = _P4Point <= size() ? _P4Point : size(); mark = _P4Mark <= size() ? _P4Mark : size(); chdir(_P4EditPath); delete_buffer(_P4TempBuf); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // P4Submit takes a command string and passes it to perforce // // void P4Submit(cmdString, showResult) char *cmdString; BOOLEAN showResult; { P4Submit_Result(cmdString, showResult, FALSE); } int P4Submit_Result(cmdString, showResult, ignoreResult) char *cmdString; BOOLEAN showResult; BOOLEAN ignoreResult; { char prompt[PROMPT_LENGTH]; char filename_nodir[FNAMELEN]; int result = 0; // // obtain the relative filename Create the command line // strcpy(filename_nodir, get_tail(filename, 0)); sprintf(prompt, cmdString, filename_nodir); // // perform the Check Out // P4Start(); result = shell("", prompt, PROCESS_RESULT_BUFFER); P4End(); // // view the results // if (!ignoreResult && (result || showResult)) { showResults(PROCESS_RESULT_BUFFER); } return result; } //////////////////////////////////////////////////////////////////////////// // // Perforce commands // command p4Add() { P4Submit("p4 add \"%s\"", FALSE); } command p4Edit() { P4Submit("p4 edit \"%s\"", FALSE); } command get() { P4Submit("p4 edit \"%s\"", FALSE); } command getl() { P4Submit("p4 edit \"%s\"", FALSE); } command p4Delete() { P4Submit("p4 delete \"%s\"", FALSE); } command p4Diff() { P4Submit("p4 diff \"%s\"", TRUE); } command vdiff() { P4Submit("p4 diff \"%s\"", TRUE); } command p4FileLog() { P4Submit("p4 filelog \"%s\"", TRUE); } command vlog() { P4Submit("p4 filelog \"%s\"", TRUE); } command p4Lock() { P4Submit("p4 lock \"%s\"", FALSE); } command p4Revert() { P4Submit("p4 revert \"%s\"", FALSE); } command p4Sync() { P4Submit("p4 sync \"%s\"", FALSE); } //////////////////////////////////////////////////////////////////////////// // // replace_in_readonly_hook() and replace_in_existing_hook() will // branch the file if it is readonly. This is the simplest error // handling and does not require user intervention if the file can // not be locked or the log files are not currently available. This // is also the most general solution. // // new_p4_replace_in_readonly_hook(old_readonly) { // // silence the compiler warning // old_readonly = old_readonly; if (file_info.check_type == CHECK_FILE && (file_info.attr & ATTR_READONLY)) { // // get the file for use // if (P4Submit_Result("p4 edit \"%s\"", FALSE, TRUE)) { // // Perforce could not check out. Just set the file to // writeable. // set_file_read_only(filename, 0); check_dates(0); } } } REPLACE_FUNC("p4", "replace_in_readonly_hook") new_p4_replace_in_existing_hook(old_readonly) { // // silence the compiler warning // old_readonly = old_readonly; if (file_info.check_type == CHECK_FILE && (file_info.attr & ATTR_READONLY)) { // // get the file for use // if (P4Submit_Result("p4 edit \"%s\"", FALSE, TRUE)) { // // Perforce could not check out. Just set the file to // writeable. // set_file_read_only(filename, 0); check_dates(0); } } } REPLACE_FUNC("p4", "replace_in_existing_hook")