/* * Private headers */ #ifndef STRICT #define STRICT #endif #ifndef WIN32_LEAN_AND_MEAN /* build.exe will define it for us on NT */ #define WIN32_LEAN_AND_MEAN #endif #undef WINVER /* build process defines this */ #define WINVER 0x0400 /* Windows 95 compatible */ #define _WIN32_WINDOWS 0x0400 /* Windows 95 compatible */ #include /* Everybody's favourite */ #include #ifndef RC_INVOKED #include /* Message crackers */ #include #include #endif /***************************************************************************** * * Resources * *****************************************************************************/ /* * Icons */ #define IDI_SDV 0x0001 /* * Bitmaps */ #define IDB_PLUS 0x0001 #define IDB_IMAGES 0x0002 /* * Strings */ #define IDS_TITLE 0x0001 #define IDS_IE4 0x0002 #define IDS_SD_EXEC_ERR 0x0003 #define IDS_DEFAULT_BUGPAGE 0x0004 #define IDS_VIEWBUG_FORMAT 0x0005 #define IDS_VIEWBUG_NONE 0x0006 #define IDS_COL_REV 0x0100 #define IDS_COL_CHANGE 0x0101 #define IDS_COL_OP 0x0102 #define IDS_COL_DATE 0x0103 #define IDS_COL_DEV 0x0104 #define IDS_COL_CHURN 0x0105 #define IDS_COL_COMMENT 0x0106 /* * Menus */ #define IDM_CHANGES 1 #define IDM_CHANGES_POPUP 2 #define IDM_DESCRIBE 3 #define IDM_DESCRIBE_POPUP 4 #define IDM_FILELOG 5 #define IDM_FILELOG_POPUP 6 #define IDM_OPENED 7 #define IDM_OPENED_POPUP 8 #define IDM_EXIT 100 #define IDM_EXITALL 101 #define IDM_COPY 102 #define IDM_COPYALL 103 #define IDM_VIEWDESC 104 #define IDM_VIEWFILEDIFF 105 #define IDM_VIEWWINDIFF 106 #define IDM_VIEWBUG 107 #define IDM_VIEWFILELOG 108 #define IDM_HELP 200 /* * Accelerators */ #define IDA_CHANGES 1 #define IDA_DESCRIBE 2 #define IDA_FILELOG 3 #define IDA_OPENED 4 /***************************************************************************** * * Assorted goo * *****************************************************************************/ #ifndef RC_INVOKED extern HINSTANCE g_hinst; extern HCURSOR g_hcurWait; extern HCURSOR g_hcurArrow; extern HCURSOR g_hcurAppStarting; extern LONG g_lThreads; extern TCHAR g_szTitle[MAX_PATH]; extern UINT g_wShowWindow; DWORD EndThreadTask(DWORD dwExitCode); #ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #endif #ifndef NO_VTABLE #define NO_VTABLE __declspec(novtable) #endif /* * Because C++ syntax is so ugly. */ #define SAFECAST(T, p) static_cast(p) #define RECAST(T, p) reinterpret_cast(p) #define CCAST(T, p) const_cast(p) /***************************************************************************** * * Utility goo * *****************************************************************************/ class String; int ListView_GetCurSel(HWND hwnd); void ListView_SetCurSel(HWND hwnd, int iIndex); int ListView_GetSubItemText(HWND hwnd, int iItem, int iSubItem, LPTSTR pszBuf, int cch); #undef ListView_GetItemText #define ListView_GetItemText(hwnd, iItem, pszBuf, cch) \ ListView_GetSubItemText(hwnd, iItem, 0, pszBuf, cch) void ChangeTabsToSpaces(LPTSTR psz); void PremungeFilespec(LPTSTR psz); void PostmungeFilespec(LPTSTR psz); BOOL ContainsWildcards(LPCTSTR psz); enum MAPTOX { MAPTOX_DEPOT, // //depot/path MAPTOX_CLIENT, // //CLIENT/path MAPTOX_LOCAL, // C:\src\path }; BOOL MapToXPath(LPCTSTR pszSD, String& strOut, MAPTOX X); inline BOOL MapToFullDepotPath(LPCTSTR pszSD, String& strOut) { return MapToXPath(pszSD, strOut, MAPTOX_DEPOT); } inline BOOL MapToClientPath(LPCTSTR pszSD, String& strOut) { return MapToXPath(pszSD, strOut, MAPTOX_CLIENT); } BOOL MapToLocalPath(LPCTSTR pszSD, String& strOut); void Help(HWND hwnd, LPCTSTR pszAnchor); BOOL SpawnProcess(LPTSTR pszCommand); void WindiffChangelist(int iChange); void WindiffOneChange(LPTSTR pszPath); int ParseBugNumber(LPCTSTR psz); int ParseBugNumberFromSubItem(HWND hwnd, int iItem, int iSubItem); void AdjustBugMenu(HMENU hmenu, int iBug, BOOL fContextMenu); void OpenBugWindow(HWND hwnd, int iBug); void JiggleMouse(); HMENU LoadPopupMenu(LPCTSTR pszMenu); void MakeMenuPretty(HMENU hmenu); void EnableDisableOrRemoveMenuItem(HMENU hmenu, UINT id, BOOL fEnable, BOOL fDelete); void SetClipboardText(HWND hwnd, LPCTSTR psz); // SCHAR is the opposite of TCHAR #ifdef UNICODE typedef CHAR SCHAR; #else typedef WCHAR SCHAR; #endif typedef SCHAR *LPSSTR; typedef const SCHAR *LPCSSTR; /***************************************************************************** * * Change types * *****************************************************************************/ enum { OP_UNKNOWN, OP_EDIT, OP_DELETE, OP_ADD, OP_INTEGRATE, OP_MERGE, OP_BRANCH, OP_COPY, OP_IGNORED, OP_MAX, }; extern struct LogEntryImageMap { LPCTSTR _pszOp; int _iImage; } c_rgleim[]; int ParseOp(LPCTSTR psz); /***************************************************************************** * * Assertion goo * *****************************************************************************/ #ifdef DEBUG void AssertFailed(char *psz, char *pszFile, int iLine); #define ASSERT(f) ((f) || (AssertFailed(#f, __FILE__, __LINE__), 0)) #else #define ASSERT sizeof #endif #define COMPILETIME_ASSERT(f) switch (0) case 0: case f: /***************************************************************************** * * Downlevel OS support (is this still needed?) * *****************************************************************************/ #undef SUPPORT_DOWNLEVEL #ifdef SUPPORT_DOWNLEVEL typedef BOOL (WINAPI *QUEUEUSERWORKITEM)(LPTHREAD_START_ROUTINE, LPVOID, ULONG); typedef BOOL (WINAPI *ALLOWSETFOREGROUNDWINDOW)(DWORD); extern QUEUEUSERWORKITEM _QueueUserWorkItem; extern ALLOWSETFOREGROUNDWINDOW _AllowSetForegroundWindow; #else #define _QueueUserWorkItem QueueUserWorkItem #define _AllowSetForegroundWindow AllowSetForegroundWindow #endif /***************************************************************************** * * Ctype * *****************************************************************************/ #define C_NONE 0x00 #define C_SPACE 0x01 #define C_DIGIT 0x02 #define C_ALPHA 0x04 #define C_DASH 0x08 #define C_BRNCH 0x10 #define B_DEFAULT C_NONE // Characters above 128 are this extern const BYTE c_rgbCtype[128]; inline BOOL _InOrder(UINT tch1, UINT tch2, UINT tch3) { return tch2 - tch1 <= tch3 - tch1; } inline BOOL _Ctype(TCHAR tch, BYTE fl) { UINT ui = (UINT)tch; BYTE b; if (ui < ARRAYSIZE(c_rgbCtype)) { b = c_rgbCtype[ui]; } else { b = B_DEFAULT; } return b & fl; } inline BOOL IsSpace(TCHAR tch) { return _Ctype(tch, C_SPACE); } inline BOOL IsDigit(TCHAR tch) { return _Ctype(tch, C_DIGIT); } inline BOOL IsNZDigit(TCHAR tch) { return _InOrder(TEXT('1'), tch, TEXT('9')); } inline BOOL IsAlpha(TCHAR tch) { return _Ctype(tch, C_ALPHA); } inline BOOL IsAlias(TCHAR tch) { return _Ctype(tch, C_DASH | C_DIGIT | C_ALPHA); } inline BOOL IsBranch(TCHAR tch) { return _Ctype(tch, C_DASH | C_DIGIT | C_ALPHA | C_BRNCH); } inline BOOL _IsPrint(TCHAR tch) { return _InOrder(TEXT(' '), tch, TEXT('~')); } #define _IsWord(ch) ((UINT)(ch) > TEXT(' ')) /***************************************************************************** * * Commands * *****************************************************************************/ extern DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter); extern DWORD CALLBACK CDescribe_ThreadProc(LPVOID lpParameter); extern DWORD CALLBACK CFileLog_ThreadProc(LPVOID lpParameter); extern DWORD CALLBACK CFileOut_ThreadProc(LPVOID lpParameter); extern DWORD CALLBACK COpened_ThreadProc(LPVOID lpParameter); BOOL LaunchThreadTask(LPTHREAD_START_ROUTINE pfn, LPCTSTR pszArgs); /***************************************************************************** * * String, _String, OutputStringBuffer * * An extremely lame low-performance string class * Be careful what you do with it since you can't do much. * * _String is the base class that does the heavy lifting and shunts long * strings into the heap. * * String collects strings into a private buffer (which can overflow into * the heap). * * OutputStringBuffer collects strings into the buffer provided * (which can overflow into the heap). On destruction, copies the result * if necessary back to the buffer provided. * *****************************************************************************/ class _String { public: explicit _String(LPTSTR pszBufOrig, UINT cchBufOrig); ~_String(); void Reset(); operator LPTSTR() const { return _pszBuf; } LPTSTR Buffer() const { return _pszBuf; } int BufferLength() const { return _cchBuf; } _String& operator<<(LPCTSTR psz) { Append(psz); return *this; } _String& operator<<(const _String& str) { Append(str); return *this; } _String& operator<<(int i); _String& operator<<(TCHAR tch); _String& operator=(LPCTSTR psz) { Reset(); Append(psz); return *this; } int Length() const { return _cchLen; } BOOL Append(LPCTSTR psz); BOOL Append(const _String& str) { return Append(str, str.Length()); } BOOL Append(LPCTSTR psz, int cchLen); BOOL Grow(int cchLen) { return Append(NULL, cchLen); } BOOL Ensure(int cchLen); void Trim(int cchTrim = 1) { _pszBuf[_cchLen -= cchTrim] = TEXT('\0'); } void SetLength(int cchLen) { ASSERT(_cchLen < _cchBuf); _cchLen = cchLen; } void Chomp(); protected: LPTSTR OriginalBuffer() const { return _pszBufOrig; } private: // Disallow the default copy constructor and assignment operator. // Since our class has pointers, a block copy is never correct. _String(const _String&); // never defined _String& operator=(const _String&); // never defined LPTSTR _pszBuf; int _cchLen; int _cchBuf; LPTSTR _pszBufOrig; }; class String : public _String { public: explicit String() : _String(_szBuf, ARRAYSIZE(_szBuf)) { } explicit String(LPCTSTR psz) : _String(_szBuf, ARRAYSIZE(_szBuf)) { Append(psz); } String& operator=(LPCTSTR psz) { _String::operator=(psz); return *this; } private: /* Almost all strings are smaller than this */ TCHAR _szBuf[MAX_PATH]; }; class OutputStringBuffer : public _String { public: OutputStringBuffer(LPTSTR pszBuf, UINT cchBuf) : _String(pszBuf, cchBuf) , _cchBufOrig(cchBuf) { } ~OutputStringBuffer(); private: int _cchBufOrig; }; /***************************************************************************** * * Custom output formats for "str << blah" * *****************************************************************************/ class _StringFormat { public: operator LPCTSTR() const { return _pszDepotPath; } _StringFormat(LPCTSTR pszDepotPath) : _pszDepotPath(pszDepotPath) { } protected: LPCTSTR _pszDepotPath; }; #define MakeStringFormat(T) \ class T : public _StringFormat { \ public: \ T(LPCTSTR pszDepotPath) : _StringFormat(pszDepotPath) { } \ }; \ _String& operator<<(_String& str, T t); \ MakeStringFormat(QuoteSpaces) MakeStringFormat(BranchOf) MakeStringFormat(FilenameOf) MakeStringFormat(ResolveBranchAndQuoteSpaces) class StringResource { public: operator UINT() const { return _ids; } StringResource(UINT ids) : _ids(ids) { } protected: UINT _ids; }; _String& operator<<(_String& str, StringResource sr); #define StringBeginsWith(psz, sz) (StrCmpN(psz, sz, ARRAYSIZE(sz) - 1) == 0) /***************************************************************************** * * StringCache is a class that babysits a string pointer. * * _StringCache is the version that requires explicit * construction/destruction. It is safe to use in global structures. * *****************************************************************************/ struct _StringCache { public: _StringCache& operator=(LPCTSTR psz); BOOL IsEmpty() const { return _psz == NULL; } operator LPTSTR() const { return Value(); } LPTSTR Value() const { return IsEmpty() ? TEXT("") : _psz; } public: LPTSTR _psz; }; class StringCache : public _StringCache { public: StringCache() { _psz = NULL; } StringCache(LPCTSTR psz) { _psz = NULL; *this = psz; } ~StringCache() { *this = NULL; } StringCache& operator=(LPCTSTR psz) { *SAFECAST(_StringCache*,this) = psz; return *this; } }; /* * match.cpp - Highly-specialized depot path matching class */ class Match { public: Match(LPCTSTR pszPattern); ~Match() { delete [] _pszzPats; } BOOL Matches(LPCTSTR pszPath); private: void _AddPattern(LPTSTR pszPat, String& strPats); BOOL _Matches(LPCTSTR pszPat, LPCTSTR pszPath); private: LPTSTR _pszzPats; LPTSTR _pszEnd; }; /* * pipe.cpp */ class ChildProcess { public: explicit ChildProcess() { Construct(); } explicit ChildProcess(LPCTSTR pszCommand) { Construct(); Start(pszCommand); } void Start(LPCTSTR pszCommand); ~ChildProcess() { Stop(); } BOOL IsRunning() const { return _dwPid; } HANDLE Handle() const { return _hRead; } void Kill(); void Stop(); private: void Construct() { _hProcess = NULL; _hRead = NULL; _dwPid = 0; } private: HANDLE _hRead; HANDLE _hProcess; DWORD _dwPid; }; class SDChildProcess : public ChildProcess { public: explicit SDChildProcess(LPCTSTR pszCommand); }; /* * buffer.cpp */ class IOBuffer { public: IOBuffer(HANDLE hRead) { Init(hRead); } void Init(HANDLE hRead) { _hRead = hRead; _cchBufUsed = 0; } BOOL NextLine(String &); private: enum { _cchBuf = MAX_PATH, }; HANDLE _hRead; TCHAR _rgchBuf[_cchBuf]; int _cchBufUsed; /* Number of bytes already in buffer */ }; /***************************************************************************** * * LVInfoTip - lvframe.cpp * * Special hack class to support extra-long infotips in listview. * *****************************************************************************/ class LVInfoTip { public: void Attach(HWND hwnd); ~LVInfoTip() { FreeLastTipAlt(); } void SetInfoTip(NMLVGETINFOTIP *pgit, LPCTSTR pszTip); private: void FreeLastTipAlt(); BOOL ThunkLastTip(); static LRESULT SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LPCTSTR GetSubclassProperty() { return TEXT("LVInfoTip"); } private: WNDPROC _wndprocPrev; BOOL _fGotInfoTip; LPCTSTR _pszLastTip; LPSSTR _pszLastTipAlt; }; /***************************************************************************** * * TreeList - treelist.cpp * * A tree-like listview. * *****************************************************************************/ /* * We maintain our own tree structure and add/delete items in the * listview as necessary as tree nodes are expanded/collapsed. */ #define PTI_ONDEMAND RECAST(TreeItem*, -1) #define PTI_APPEND RECAST(TreeItem*, -1) class TreeItem { public: TreeItem * Parent() const { return _ptiParent; } TreeItem * NextSibling() const { return _ptiNext; } TreeItem * FirstChild() const { return _ptiChild; } TreeItem * NextVisible(); BOOL IsExpanded() const { return _cVisKids > 0; } BOOL IsExpandable() const { return _ptiChild != NULL; } BOOL IsVisible(); BOOL IsVisibleOrRoot(); void SetExpandable() { SetFirstChild(PTI_ONDEMAND); } void SetNotExpandable() { SetFirstChild(NULL); } private: friend class Tree; BOOL IsSentinel() const { return this == NULL || this == PTI_ONDEMAND; } void SetFirstChild(TreeItem *pti) { ASSERT(_ptiChild->IsSentinel()); _ptiChild = pti; } private: TreeItem * _ptiParent; TreeItem * _ptiNext; TreeItem * _ptiChild; int _iDepth; int _iVisIndex; int _cVisKids; }; #define TLN_GETDISPINFO 100 // Get pszText/cchTextMax given pti/iSubItem #define TLN_FILLCHILDREN 101 // Fill children of pti #define TLN_ITEMACTIVATE 102 // Default action on pti #define TLN_GETINFOTIP 103 // Get pszText/cchTextMax given pti #define TLN_DELETEITEM 104 // Destruct the node #define TLN_GETCONTEXTMENU 105 // Display a context menu struct NMTREELIST { NMHDR hdr; TreeItem *pti; int iSubItem; LPTSTR pszText; int cchTextMax; // Doubles as state }; class Tree { public: Tree(TreeItem *ptiRoot); ~Tree(); TreeItem* GetRoot() { return _ptiRoot; } BOOL Insert(TreeItem *pti, TreeItem *ptiParent, TreeItem *ptiAfter); void SetHWND(HWND hwnd); int Expand(TreeItem *pti); int Collapse(TreeItem *pti); int ToggleExpand(TreeItem *pti); void RedrawItem(TreeItem *pti); TreeItem *GetCurSel(); void SetCurSel(TreeItem *pti); HIMAGELIST SetImageList(HIMAGELIST himl); public: //$$// make these protected someday LRESULT OnGetDispInfo(NMLVDISPINFO *plvd); LRESULT OnCacheHint(NMLVCACHEHINT *phint); LRESULT OnItemActivate(int iItem); LRESULT OnKeyDown(NMLVKEYDOWN *pkd); LRESULT OnClick(NMITEMACTIVATE *pia); LRESULT OnGetInfoTip(NMLVGETINFOTIP *pgit); LRESULT OnGetContextMenu(int iItem); LRESULT OnCopyToClipboard(int iMin, int iMax); private: void Recalc(TreeItem *pti); void UpdateCache(TreeItem *pti, int iItem); LRESULT SendNotify(int code, NMHDR *pnm); TreeItem* IndexToItem(int iItem); int InsertListviewItem(int iItem); void UpdateVisibleCounts(TreeItem *pti, int cDelta); void DeleteNode(TreeItem *pti); void SendDeleteNotify(TreeItem *pti); private: HWND _hwnd; TreeItem* _ptiRoot; int _iHint; TreeItem* _ptiHint; }; /***************************************************************************** * * FrameWindow - window.cpp * * A window that frames an inner control. The inner control is resized * to fill the client area. * *****************************************************************************/ #define FW_MSG(msg) case msg: return ON_##msg(uiMsg, wParam, lParam) class NO_VTABLE FrameWindow { public: static DWORD RunThread(FrameWindow *self, LPVOID lpParameter); protected: void SetAcceleratorTable(LPCTSTR pszAccel) { _haccel = LoadAccelerators(g_hinst, pszAccel); } BOOL SetWindowMenu(LPCTSTR pszMenu) { return SetMenu(_hwnd, LoadMenu(g_hinst, pszMenu)); } virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); virtual ~FrameWindow() { } // // For talking back to yourself (typically from a base class back // to a derived class). Short-circuits the WndProc wrapper for // perf. Do not use cross-thread! // LRESULT SendSelfMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { return HandleMessage(uMsg, wParam, lParam); } private: static LRESULT CALLBACK WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); HWND CreateFrameWindow(); protected: HWND _hwnd; HWND _hwndChild; HACCEL _haccel; LPTSTR _pszQuery; }; /***************************************************************************** * * LVFrame - lvframe.cpp * * A FrameWindow that frames a listview in report mode. * *****************************************************************************/ #define LM_ITEMACTIVATE (WM_USER + 100) // wParam = iItem #define LM_GETINFOTIP (WM_USER + 101) // wParam = iItem, lParam -> NMLVGETINFOTIP #define LM_GETCONTEXTMENU (WM_USER + 102) // wParam = iItem #define LM_COPYTOCLIPBOARD (WM_USER + 103) // wParam = iMin, lParam = iMax (exclusive) #define LM_DELETEITEM (WM_USER + 104) // wParam = iItem, lParam = lParam typedef struct LVFCOLUMN { UINT cch; UINT ids; UINT fmt; } LVFCOLUMN; class NO_VTABLE LVFrame : public FrameWindow { typedef FrameWindow super; protected: enum { IDC_LIST = 1 }; BOOL CreateChild(DWORD dwStyle, DWORD dwExStyle); BOOL AddColumns(const LVFCOLUMN *rgcol); void *GetLVItem(int iItem); int GetCurSel() { return ListView_GetCurSel(_hwndChild); } LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam); private: LRESULT ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_WM_CONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam); private: LVInfoTip _it; }; /***************************************************************************** * * TLFrame - tlframe.cpp * * Wraps the Tree class. If I were less lazy, I would merge it into * this class, but right now Tree is a separate class because I stole * the code from sdflog... * *****************************************************************************/ class NO_VTABLE TLFrame : public LVFrame { typedef LVFrame super; protected: TLFrame(TreeItem *ptiRoot) : _tree(ptiRoot) { } LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam); BOOL CreateChild(DWORD dwStyle, DWORD dwExStyle); TreeItem *TLGetCurSel() { return _tree.GetCurSel(); }; private: LRESULT ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_LM_GETINFOTIP(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam); LRESULT ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam); protected: Tree _tree; }; /***************************************************************************** * * BGTask * *****************************************************************************/ class BGTask { protected: BGTask() : _hDone(CreateEvent(NULL, TRUE, TRUE, NULL)), _fPending(FALSE) { } BOOL BGConstructed() const { return _hDone != NULL; } BOOL BGTaskPending() const { return _fPending; } ~BGTask(); BOOL BGStartTask(LPTHREAD_START_ROUTINE pfn, LPVOID Context); void BGEndTask(); LRESULT BGFilterSetCursor(LRESULT lres); private: BOOL _fPending; HANDLE _hDone; }; /***************************************************************************** * * Parser * *****************************************************************************/ class Substring { public: LPTSTR _pszMin; // First character of the substring LPTSTR _pszMax; // One past the last character of the substring explicit Substring() { } explicit Substring(LPCTSTR pszMin, LPCTSTR pszMax) : _pszMin(CCAST(LPTSTR, pszMin)), _pszMax(CCAST(LPTSTR, pszMax)) { } LPTSTR SetStart(LPCTSTR psz) { return _pszMin = CCAST(LPTSTR, psz); } LPTSTR Start() { return _pszMin; } SIZE_T Length() const { return _pszMax - _pszMin; } SIZE_T SetEnd(LPCTSTR psz) { _pszMax = CCAST(LPTSTR, psz); return Length(); } LPTSTR Finalize() // This method works only on mutable substrings { *_pszMax = TEXT('\0'); return _pszMin; } }; LPTSTR Parse(LPCTSTR pszFormat, LPCTSTR pszParse, Substring *rgss); inline _String& operator<<(_String& str, Substring ss) { str.Append(ss._pszMin, (int)(ss._pszMax - ss._pszMin)); return str; } /***************************************************************************** * * CommentParser - Parses checkin comments * *****************************************************************************/ class NO_VTABLE CommentParser { public: virtual void SetDev(LPCTSTR psz) = 0; virtual void SetComment(LPCTSTR psz) = 0; void Reset() { _fHaveComment = FALSE; } void AddComment(LPTSTR psz); CommentParser() { Reset(); } private: BOOL _fHaveComment; }; /***************************************************************************** * * Tokenizer * *****************************************************************************/ class Tokenizer { public: explicit Tokenizer() { } explicit Tokenizer(LPCTSTR psz) { Restart(psz); } void Restart(LPCTSTR psz); LPCTSTR Unparsed() const { return _psz; } BOOL Token(String& str); BOOL Finished() const { return !*_psz; } private: LPCTSTR _psz; }; /***************************************************************************** * * GetOpt * * pszParams is the list of switches that take parameters. By default, * switches do not take parameters. * *****************************************************************************/ class GetOpt { public: GetOpt(LPCTSTR pszParams, LPCTSTR pszArg) : _pszParams(pszParams), _pszUnparsed(NULL), _tok(pszArg) { } TCHAR NextSwitch(); BOOL Token() { return _tok.Token(_str); } BOOL Finished() { return _tok.Finished(); } LPCTSTR GetValue() const { return _pszValue; } Tokenizer GetTokenizer() const { return _tok; } private: LPCTSTR _pszParams; LPTSTR _pszUnparsed; LPTSTR _pszValue; Tokenizer _tok; String _str; }; /***************************************************************************** * * WaitCursor * *****************************************************************************/ class WaitCursor { public: explicit WaitCursor() : _hcur(SetCursor(g_hcurWait)) { } ~WaitCursor() { SetCursor(_hcur); } private: HCURSOR _hcur; }; /***************************************************************************** * * Annoying version-checking functions * *****************************************************************************/ inline BOOL VER1GE(UINT A, UINT a) { return A >= a; } inline BOOL VER2GE(UINT A, UINT B, UINT a, UINT b) { return A > a || (A == a && VER1GE(B,b)); } inline BOOL VER3GE(UINT A, UINT B, UINT C, UINT a, UINT b, UINT c) { return A > a || (A == a && VER2GE(B,C,b,c)); } inline BOOL VER4GE(UINT A, UINT B, UINT C, UINT D, UINT a, UINT b, UINT c, UINT d) { return A > a || (A == a && VER3GE(B,C,D,b,c,d)); } /***************************************************************************** * * Globals * *****************************************************************************/ struct CGlobals { public: void Initialize(); BOOL IsChurnEnabled() const { return _fChurn; } const _StringCache& GetSdOpts() const { return _pszSdOpts; } const _StringCache& GetUserName() const { return _rgpszSettings[SETTING_USERNAME]; } const _StringCache& GetClientName() const { return _rgpszSettings[SETTING_CLIENTNAME]; } const _StringCache& GetClientRoot() const { return _rgpszSettings[SETTING_CLIENTROOT]; } const _StringCache& GetFakeDir() const { return _pszFakeDir; } const _StringCache& GetLocalRoot() const { return _pszLocalRoot; } LPCTSTR GetSdPath() const { return _szSd; } String& FormatBugUrl(String& str, int iBug) const { str << _pszBugPagePre << iBug << _pszBugPagePost; return str; } void SetChurn(BOOL fChurn) { _fChurn = fChurn; } void SetSdOpts(LPCTSTR psz) { _pszSdOpts = psz; } BOOL IsVersion(UINT major) { return VER1GE(_Major(), major); } BOOL IsVersion(UINT major, UINT minor) { return VER2GE(_Major(), _Minor(), major, minor); } private: void _InitSdPath(); void _InitInfo(); void _InitFakeDir(); void _InitServerVersion(); void _InitBugPage(); UINT _Major() { return _rguiVer[VERSION_MAJOR]; } UINT _Minor() { return _rguiVer[VERSION_MINOR]; } enum { SETTING_USERNAME, SETTING_CLIENTNAME, SETTING_CLIENTROOT, SETTING_LOCALDIR, SETTING_SERVERVERSION, SETTING_MAX }; enum { VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_QFE, VERSION_MAX, }; private: BOOL _fChurn; _StringCache _pszSdOpts; _StringCache _pszLocalRoot; _StringCache _pszFakeDir; _StringCache _pszBugPagePre; _StringCache _pszBugPagePost; _StringCache _rgpszSettings[SETTING_MAX]; UINT _rguiVer[VERSION_MAX]; TCHAR _szSd[MAX_PATH]; }; extern CGlobals GlobalSettings; #endif