00001
00006 #ifdef _WIN32
00007 # include <windows.h>
00008 # include <direct.h>
00009 # include <process.h>
00010 # ifndef MSYS_NOTHREADS
00011 # define THREAD_HANDLE HANDLE
00012 # define THREAD_HANDLE_INIT(x) x = NULL
00013 # endif
00014 #else
00015 # include <unistd.h>
00016 # include <sys/stat.h>
00017 # ifndef MSYS_NOTHREADS
00018 # include <pthread.h>
00019 # define THREAD_HANDLE pthread_t
00020 # define THREAD_HANDLE_INIT(x)
00021 # endif
00022 # include <limits.h>
00023 # define MAX_PATH PATH_MAX
00024 #endif
00025
00026 #include <stdarg.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <assert.h>
00030 #include <errno.h>
00031 #include <sys/timeb.h>
00032 #include <sys/types.h>
00033 #include <time.h>
00034
00035 #include <queue>
00036 #include <set>
00037 #include <map>
00038 #include <algorithm>
00039
00040 #include "menusys.h"
00041 #include "menusysu.h"
00042 #ifdef _UNICODE
00043 # include "ConvertUTF.h"
00044 #endif
00045
00046
00047
00048
00049
00055 #if defined(USING_ICU) && !defined(_WIN32)
00056 #define StringConstant(name, str) \
00057 static const struct _str_ ## name { \
00058 static const size_t len = sizeof(str)-1; \
00059 static const UChar data[sizeof str]; \
00060 _str_ ## name() { \
00061 u_charsToUChars(str, (UChar*) data, sizeof str); \
00062 } \
00063 operator const UChar *() const { return data; } \
00064 } name
00065 #else
00066 # ifdef _UNICODE
00067 # define StringConstant(name, str) static const TCHAR name[] = L ## str;
00068 # else
00069 # define StringConstant(name, str) static const TCHAR name[] = str;
00070 # endif
00071 #endif
00072
00073
00074
00075 static const TCHAR OPTION_PREFIX[] = _T("_setopt_");
00076 static const size_t OPTION_PREFIX_LEN = (sizeof(OPTION_PREFIX) / sizeof(TCHAR)) - 1;
00077
00078
00079 extern const TCHAR * MSYS_HEADER = _T("");
00080
00081
00082
00083
00084
00085
00089 typedef std::map<std::tstring,long> ProcMap;
00090
00093 typedef std::vector<long> ProcStack;
00094
00095 struct InputFile
00096 {
00097 FILE * mFile;
00098 std::tstring mPath;
00099 bool mIsUtf8;
00100 ProcMap mProc;
00101 ProcStack mProcStack;
00102 #ifdef _WIN32
00103 HANDLE mFileLock;
00104 #endif
00105
00106 InputFile(FILE * aFile = NULL, const TCHAR * aPath = _T(""), bool aIsUtf8 = false);
00107 InputFile(const InputFile & rhs);
00108 InputFile & operator=(const InputFile & rhs);
00109 };
00110
00111 template <typename T, size_t INIT_SIZE = 1024>
00112 struct BufferImpl
00113 {
00114 T * mBuf;
00115 size_t mBufSiz;
00116
00117 BufferImpl();
00118 ~BufferImpl();
00119 void Grow();
00120 void Format(const TCHAR * aFormat, ...);
00121 void FormatV(const TCHAR * aFormat, va_list ap);
00122 };
00123
00124 typedef BufferImpl<char> BufferA;
00125 typedef BufferImpl<TCHAR> Buffer;
00126
00127 typedef std::vector<long> LapArray;
00128
00129 class Timer {
00130 struct timeb mStart;
00131 long mElapsed;
00132 bool mRunning;
00133 LapArray mLaps;
00134
00135 public:
00136 Timer();
00137 void Start(bool aReset = false);
00138 void Stop();
00139 void Reset();
00140 long GetCurrentElapsed() const;
00141 bool IsRunning() const;
00142 void Lap();
00143 const LapArray & Laps() const { return mLaps; }
00144
00145 private:
00146 long CalculateElapsed(struct timeb * aFinish = NULL) const;
00147 };
00148
00149 typedef std::map<std::tstring,Timer> TimerMap;
00150
00151 enum DISPLAY {
00152 DISPLAY_NONE, DISPLAY_PROMPT, DISPLAY_OUTPUT, DISPLAY_ALL
00153 };
00154
00155 struct MSYS_ThreadImpl : public MSYS_Thread
00156 {
00157 bool bStopThread;
00158 bool bIsRunning;
00159 std::tstring mThreadName;
00160 #ifndef MSYS_NOTHREADS
00161 THREAD_HANDLE mThreadHandle;
00162 #endif
00163 std::vector<InputFile> mInputStack;
00164 InputFile mInputCurr;
00165 FILE * mTeeFile;
00166 bool mUseNeedHelp;
00167 DISPLAY mOptionConsole;
00168 TimerMap mTimerMap;
00169 Buffer mHandlerBuf;
00170 BufferA mCharBuf;
00171 bool mStringReplacements;
00172
00173 MSYS_ThreadImpl(const TCHAR * aThreadName = _T(""), bool a_bRootThread = false);
00174 ~MSYS_ThreadImpl();
00175
00176 public:
00177 void SetCurrCommand(const TCHAR * aCommand) { mCurrCommand = aCommand; }
00178 void LoopStartCounted(long aProcedure, int aCount);
00179 void LoopStartTimed(long aProcedure, time_t aExitTime);
00180 bool Loop();
00181
00182 private:
00183 long mLoopProcedure;
00184 time_t mLoopLimit;
00185
00186 void LoopStart();
00187 bool LoopTestConditions();
00188
00189 private:
00190 MSYS_ThreadImpl(const MSYS_ThreadImpl & rhs);
00191 MSYS_ThreadImpl & operator=(const MSYS_ThreadImpl & rhs);
00192 };
00193
00194 typedef std::map<std::tstring, MSYS_ThreadImpl*> ThreadMap;
00195
00196 #ifdef MSYS_NOTHREADS
00197 class ThreadLock {
00198 public:
00199 ThreadLock() { }
00200 ~ThreadLock() { }
00201 void Lock() { }
00202 void Unlock() { }
00203 };
00204 #else
00205 # ifdef _WIN32
00206 class ThreadLock {
00207 CRITICAL_SECTION m_lock;
00208 public:
00209 ThreadLock() { InitializeCriticalSection(&m_lock); }
00210 ~ThreadLock() { DeleteCriticalSection(&m_lock); }
00211 void Lock() { EnterCriticalSection(&m_lock); }
00212 void Unlock() { LeaveCriticalSection(&m_lock); }
00213 };
00214 # else // !_WIN32
00215 class ThreadLock {
00216 pthread_mutex_t m_lock;
00217 public:
00218 ThreadLock() { pthread_mutex_init(&m_lock, NULL); }
00219 ~ThreadLock() { pthread_mutex_destroy(&m_lock); }
00220 void Lock() { pthread_mutex_lock(&m_lock); }
00221 void Unlock() { pthread_mutex_unlock(&m_lock); }
00222 };
00223 # endif // _WIN32
00224 #endif // MSYS_NOTHREADS
00225
00226 struct AutoLock {
00227 ThreadLock & mLock;
00228 AutoLock(ThreadLock & aLock) : mLock(aLock) { mLock.Lock(); }
00229 ~AutoLock() { mLock.Unlock(); }
00230
00231 private:
00232 AutoLock(const AutoLock &);
00233 AutoLock & operator=(const AutoLock &);
00234 };
00235
00236 enum PARSE { PARSE_DEFAULT, PARSE_ON, PARSE_OFF };
00237
00238
00239
00240
00241
00242
00243 InputFile::InputFile(
00244 FILE * aFile,
00245 const TCHAR * aPath,
00246 bool aIsUtf8
00247 )
00248 : mFile(aFile)
00249 , mPath(aPath)
00250 , mIsUtf8(aIsUtf8)
00251 { }
00252
00253 InputFile::InputFile(const InputFile & rhs)
00254 {
00255 operator=(rhs);
00256 }
00257
00258 InputFile &
00259 InputFile::operator=(
00260 const InputFile & rhs
00261 )
00262 {
00263 mFile = rhs.mFile;
00264 mPath = rhs.mPath;
00265 mIsUtf8 = rhs.mIsUtf8;
00266 mProc = rhs.mProc;
00267 mProcStack = rhs.mProcStack;
00268 #ifdef _WIN32
00269 mFileLock = rhs.mFileLock;
00270 #endif
00271
00272 return *this;
00273 }
00274
00275 template <typename T, size_t INIT_SIZE>
00276 BufferImpl<T,INIT_SIZE>::BufferImpl()
00277 : mBuf(NULL)
00278 , mBufSiz(INIT_SIZE)
00279 {
00280 Grow();
00281 }
00282
00283 template <typename T, size_t INIT_SIZE>
00284 BufferImpl<T,INIT_SIZE>::~BufferImpl()
00285 {
00286 if (mBuf) free(mBuf);
00287 }
00288
00289 template <typename T, size_t INIT_SIZE>
00290 void
00291 BufferImpl<T,INIT_SIZE>::Grow()
00292 {
00293 mBufSiz *= 2;
00294 mBuf = (T*) realloc(mBuf, mBufSiz * sizeof(T));
00295 if (!mBuf) {
00296 _ftprintf(stderr, _T("Out of memory.\n"));
00297 exit(1);
00298 }
00299 }
00300
00301 template <typename T, size_t INIT_SIZE>
00302 void
00303 BufferImpl<T,INIT_SIZE>::Format(
00304 const TCHAR * aFormat,
00305 ...
00306 )
00307 {
00308 va_list ap;
00309 va_start(ap, aFormat);
00310 FormatV(aFormat, ap);
00311 va_end(ap);
00312 }
00313
00314 template <typename T, size_t INIT_SIZE>
00315 void
00316 BufferImpl<T,INIT_SIZE>::FormatV(
00317 const TCHAR * aFormat,
00318 va_list ap
00319 )
00320 {
00321 int nLength = 0;
00322 do {
00323 if (nLength == -1) Grow();
00324 nLength = _vsntprintf(mBuf, mBufSiz, aFormat, ap);
00325 } while (nLength == -1);
00326 }
00327
00328 Timer::Timer()
00329 {
00330 mElapsed = 0;
00331 mStart.time = 0;
00332 mStart.millitm = 0;
00333 mRunning = false;
00334 }
00335
00336 void
00337 Timer::Start(
00338 bool aReset
00339 )
00340 {
00341 if (aReset) {
00342 Reset();
00343 }
00344 ftime(&mStart);
00345 mRunning = true;
00346 }
00347
00348 void
00349 Timer::Stop()
00350 {
00351 if (mRunning) {
00352 mRunning = false;
00353 mElapsed += CalculateElapsed();
00354 }
00355 }
00356
00357 void
00358 Timer::Reset()
00359 {
00360 mElapsed = 0;
00361 mRunning = false;
00362 mLaps.clear();
00363 }
00364
00365 long
00366 Timer::GetCurrentElapsed() const
00367 {
00368 if (mRunning) {
00369 return mElapsed + CalculateElapsed();
00370 }
00371 return mElapsed;
00372 }
00373
00374 bool
00375 Timer::IsRunning() const
00376 {
00377 return mRunning;
00378 }
00379
00380 void
00381 Timer::Lap()
00382 {
00383 long nLapTime = mElapsed;
00384 mElapsed = 0;
00385 if (mRunning) {
00386 struct timeb timeEnd;
00387 ftime(&timeEnd);
00388 nLapTime += CalculateElapsed(&timeEnd);
00389 mStart = timeEnd;
00390 }
00391 mLaps.push_back(nLapTime);
00392 }
00393
00394 long
00395 Timer::CalculateElapsed(
00396 struct timeb * aFinish
00397 ) const
00398 {
00399 struct timeb timeEnd;
00400 if (aFinish) {
00401 timeEnd = *aFinish;
00402 }
00403 else {
00404 ftime(&timeEnd);
00405 }
00406 time_t nPeriodSecs = timeEnd.time - mStart.time;
00407 int nPeriodMillis = (int) timeEnd.millitm - (int) mStart.millitm;
00408 return (long) (nPeriodSecs * 1000) + nPeriodMillis;
00409 }
00410
00411 MSYS_ThreadImpl::MSYS_ThreadImpl(
00412 const TCHAR * aThreadName,
00413 bool a_bRootThread
00414 )
00415 : bStopThread(false)
00416 , bIsRunning(true)
00417 , mThreadName(aThreadName)
00418 , mTeeFile(NULL)
00419 , mUseNeedHelp(true)
00420 , mOptionConsole(DISPLAY_ALL)
00421 , mStringReplacements(true)
00422 , mLoopProcedure(0)
00423 , mLoopLimit(0)
00424 {
00425 #ifndef MSYS_NOTHREADS
00426 THREAD_HANDLE_INIT(mThreadHandle);
00427 #endif
00428 static int nNextThreadId = ROOT_THREAD;
00429 if (a_bRootThread) {
00430 nNextThreadId = ROOT_THREAD;
00431 }
00432 mThreadId = nNextThreadId++;
00433 mInputCurr = InputFile(stdin, _T("stdin"));
00434 }
00435
00436 MSYS_ThreadImpl::~MSYS_ThreadImpl()
00437 {
00438 if (mTeeFile) {
00439 fclose(mTeeFile);
00440 mTeeFile = NULL;
00441 }
00442 while (!mInputStack.empty()) {
00443 if (mInputStack.back().mFile != stdin) {
00444 fclose(mInputStack.back().mFile);
00445 #ifdef _WIN32
00446 CloseHandle(mInputStack.back().mFileLock);
00447 #endif
00448 }
00449 mInputStack.pop_back();
00450 }
00451 MSYS_ThreadUserDestroy(this);
00452 }
00453
00454 void
00455 MSYS_ThreadImpl::LoopStartCounted(
00456 long aProcedure,
00457 int aCount
00458 )
00459 {
00460 assert(mInputCurr.mFile != stdin);
00461
00462 if (aCount < 0) aCount = 0;
00463 mLoopProcedure = aProcedure;
00464 mLoopLimit = -aCount;
00465 if (!LoopTestConditions()) return;
00466 LoopStart();
00467 }
00468
00469 void
00470 MSYS_ThreadImpl::LoopStartTimed(
00471 long aProcedure,
00472 time_t aExitTime
00473 )
00474 {
00475 assert(mInputCurr.mFile != stdin);
00476
00477 mLoopProcedure = aProcedure;
00478 mLoopLimit = aExitTime;
00479 if (!LoopTestConditions()) return;
00480 LoopStart();
00481 }
00482
00483 bool
00484 MSYS_ThreadImpl::Loop()
00485 {
00486 assert(mInputCurr.mFile != stdin);
00487 if (!LoopTestConditions()) return false;
00488
00489
00490 if (fseek(mInputCurr.mFile, mLoopProcedure, SEEK_SET) != 0) {
00491 _ftprintf(stderr, _T("Failed loop procedure call. Exiting loop.\n"));
00492 mLoopLimit = 0;
00493 return false;
00494 }
00495
00496 return true;
00497 }
00498
00499 void
00500 MSYS_ThreadImpl::LoopStart()
00501 {
00502 assert(mInputCurr.mFile != stdin);
00503
00504
00505 long lCurr = ftell(mInputCurr.mFile);
00506 if (fseek(mInputCurr.mFile, mLoopProcedure, SEEK_SET) != 0) {
00507 _ftprintf(stderr, _T("Failed loop procedure call. Exiting loop.\n"));
00508 mLoopLimit = 0;
00509 return;
00510 }
00511
00512
00513 mInputCurr.mProcStack.push_back(lCurr);
00514 }
00515
00516 bool
00517 MSYS_ThreadImpl::LoopTestConditions()
00518 {
00519 assert(mInputCurr.mFile != stdin);
00520 if (mLoopLimit == 0) return false;
00521
00522
00523 if (mLoopLimit < 0) {
00524
00525 if (mLoopLimit++ == 0) return false;
00526 }
00527 else {
00528
00529 time_t tNow;
00530 time(&tNow);
00531 if (tNow >= mLoopLimit) {
00532 mLoopLimit = 0;
00533 return false;
00534 }
00535 }
00536
00537 return true;
00538 }
00539
00540 bool MSYS_DispatchEntry::operator==(const MSYS_DispatchEntry & rhs) const {
00541 return _tcsicmp(cmd, rhs.cmd) == 0;
00542 }
00543
00544
00545
00546
00547
00548
00549
00550 static void InitInternal();
00551 static bool FileExists(const TCHAR * aFilePath);
00552 static bool StringReplacements(MSYS_ThreadImpl * aThread, Buffer & aBuf);
00553 #ifdef _WIN32
00554 static void InputFromConsole(Buffer & aBuf);
00555 #endif // _WIN32
00556 static void OutputToConsole(const TCHAR * aString);
00557 static void OutputToTeeFile(MSYS_ThreadImpl * aThread, const Buffer & aBuf);
00558
00559
00560 static bool Input(MSYS_ThreadImpl * aThread, Buffer & aBuf);
00561 static void Parse(TCHAR * aBuf, TCHAR *& aCommand, TCHAR *& aArgs);
00562 static void ParseArgs(TCHAR * aBuf, bool aUnquote, MSYS_Args & aArgs);
00563 static bool Dispatch(MSYS_ThreadImpl * aThread, TCHAR * aCommand, TCHAR * aArgs, PARSE aParse);
00564
00565
00566 static void DoChdir(MSYS_Thread * thrd, MSYS_Args & aArgs);
00567 static void DoEcho(MSYS_Thread * thrd, MSYS_Args & aArgs);
00568 static void DoExit(MSYS_Thread * thrd, MSYS_Args & aArgs);
00569 static void DoHelp(MSYS_Thread * thrd, MSYS_Args & aArgs);
00570 static void DoLoop(MSYS_Thread * thrd, MSYS_Args & aArgs);
00571 static void DoProc(MSYS_Thread * thrd, MSYS_Args & aArgs);
00572 static void DoPwd(MSYS_Thread * thrd, MSYS_Args & aArgs);
00573 static void DoRead(MSYS_Thread * thrd, MSYS_Args & aArgs);
00574 static void DoSetOpt(MSYS_Thread * thrd, MSYS_Args & aArgs);
00575 static void DoShell(MSYS_Thread * thrd, MSYS_Args & aArgs);
00576 static void DoSleep(MSYS_Thread * thrd, MSYS_Args & aArgs);
00577 static void DoTee(MSYS_Thread * thrd, MSYS_Args & aArgs);
00578 static void DoTimer(MSYS_Thread * thrd, MSYS_Args & aArgs);
00579 #ifndef MSYS_NOTHREADS
00580 static void DoThreads(MSYS_Thread * thrd, MSYS_Args & aArgs);
00581 #endif // MSYS_NOTHREADS
00582
00583
00584 static void OptConsole(MSYS_Thread * thrd, MSYS_Args & aArgs);
00585 static void OptThreads(MSYS_Thread * thrd, MSYS_Args & aArgs);
00586 static void OptUtf8(MSYS_Thread * thrd, MSYS_Args & aArgs);
00587
00588
00589
00590
00591
00592
00593 extern const MSYS_DispatchEntry g_rgInternal[];
00594
00595 static MSYS_DispatchEntry * g_rgDispatch = NULL;
00596 static bool g_bEnableThreads = false;
00597 static ThreadMap g_mapThreads;
00598 static ThreadLock g_lockThreadsMap;
00599 static ThreadLock g_lockOutputConsole;
00600 static ThreadLock g_lockOutputTeeFile;
00601 static MSYS_ThreadImpl * g_pRootThread = NULL;
00602
00603
00604
00605
00606
00607
00613 #ifdef _WIN32
00614 static unsigned __stdcall InputLoop(void * thrd)
00615 #else
00616 static void * InputLoop(void * thrd)
00617 #endif
00618 {
00619 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
00620
00621 Buffer inputBuffer;
00622 TCHAR *pCommand, *pArgs;
00623
00624
00625 TCHAR szPrompt[200];
00626 bool bInCommentBlock = false;
00627 bool bStringReps = false;
00628 for (bool isOK = true; isOK && !aThread->bStopThread; bStringReps = false) {
00629 MSYS_GetPrompt(aThread, szPrompt, sizeof(szPrompt)/sizeof(szPrompt[0]));
00630 if (aThread->mInputCurr.mFile == stdin) {
00631 if (aThread->mTeeFile) fflush(aThread->mTeeFile);
00632 _ftprintf(stdout, _T("%s"), szPrompt);
00633 Input(aThread, inputBuffer);
00634 bStringReps = StringReplacements(aThread, inputBuffer);
00635 }
00636 else {
00637
00638
00639 if (!Input(aThread, inputBuffer)) {
00640 if (!aThread->mInputCurr.mProcStack.empty()) {
00641 MSYS_Display(aThread,
00642 _T("WARNING: procedure stack not empty at end of file.\n"));
00643 }
00644 fclose(aThread->mInputCurr.mFile);
00645 #ifdef _WIN32
00646 CloseHandle(aThread->mInputCurr.mFileLock);
00647 #endif
00648 isOK = !aThread->mInputStack.empty();
00649 if (isOK) {
00650 aThread->mInputCurr = aThread->mInputStack.back();
00651 aThread->mInputStack.pop_back();
00652 }
00653 bInCommentBlock = false;
00654 continue;
00655 }
00656 bStringReps = StringReplacements(aThread, inputBuffer);
00657 }
00658
00659 Parse(inputBuffer.mBuf, pCommand, pArgs);
00660 if (_tcsicmp(pCommand, _T("*/")) == 0) {
00661 bInCommentBlock = false;
00662 continue;
00663 }
00664 if (bInCommentBlock || *pCommand == ';' || *pCommand == '#') {
00665 continue;
00666 }
00667 if (_tcsicmp(pCommand, _T("/*")) == 0) {
00668 bInCommentBlock = true;
00669 continue;
00670 }
00671
00672
00673
00674 const TCHAR szPrefix[] = _T("@$=-+");
00675 enum { NO_ECHO, TIMING, NO_HELP, NO_PARSE, USE_PARSE, COUNT };
00676 assert(_tcslen(szPrefix) == COUNT);
00677 bool rgFlags[COUNT] = { false };
00678 const TCHAR * pItem = _tcschr(szPrefix, *pCommand);
00679 while (pItem && *pCommand) {
00680 ++pCommand;
00681 rgFlags[pItem - szPrefix] = true;
00682 pItem = _tcschr(szPrefix, *pCommand);
00683 }
00684 if (!*pCommand) {
00685 Parse(pArgs, pCommand, pArgs);
00686 }
00687
00688
00689 Timer lineTimer;
00690 aThread->mUseNeedHelp = !rgFlags[NO_HELP];
00691 if (rgFlags[TIMING]) {
00692 lineTimer.Start(true);
00693 }
00694 if (aThread->mOptionConsole == DISPLAY_ALL || aThread->mOptionConsole == DISPLAY_PROMPT) {
00695 if (!rgFlags[NO_ECHO]) {
00696 if (bStringReps || aThread->mInputCurr.mFile != stdin) {
00697 MSYS_Display(aThread, _T("%s%s %s\n"), szPrompt, pCommand, pArgs);
00698 }
00699 else if (aThread->mTeeFile) {
00700 Buffer buf;
00701 buf.Format(_T("%s%s %s\n"), szPrompt, pCommand, pArgs);
00702 OutputToTeeFile(aThread, buf);
00703 }
00704 }
00705 }
00706 if (*pCommand) {
00707 PARSE p = PARSE_DEFAULT;
00708 if (rgFlags[NO_PARSE])
00709 p = PARSE_OFF;
00710 else if (rgFlags[USE_PARSE])
00711 p = PARSE_ON;
00712 isOK = Dispatch(aThread, pCommand, pArgs, p);
00713 }
00714 if (rgFlags[TIMING]) {
00715 lineTimer.Stop();
00716 long nElapsed = lineTimer.GetCurrentElapsed();
00717 if (aThread->mOptionConsole == DISPLAY_PROMPT) {
00718
00719 TCHAR szResults[100];
00720 _sntprintf(szResults, 100, _T("Elapsed time %ld.%03ld seconds.\n"),
00721 nElapsed / 1000, nElapsed % 1000);
00722 OutputToConsole(szResults);
00723 }
00724 else {
00725 MSYS_Display(aThread, _T("Elapsed time %ld.%03ld seconds.\n"),
00726 nElapsed / 1000, nElapsed % 1000);
00727 }
00728 }
00729 aThread->mUseNeedHelp = true;
00730 }
00731
00732 aThread->bIsRunning = false;
00733 return 0;
00734 }
00735
00736 extern bool
00737 MSYS_Process(
00738 const TCHAR * aInputFile,
00739 bool aEnableThreads
00740 )
00741 {
00742
00743 static bool bIsRunning = false;
00744 assert(!bIsRunning);
00745 if (bIsRunning) return false;
00746 bIsRunning = true;
00747
00748
00749 InitInternal();
00750 g_pRootThread = new MSYS_ThreadImpl(_T("<main>"), true);
00751 if (!g_pRootThread) {
00752 _ftprintf(stderr, _T("Out of memory.\n"));
00753 exit(1);
00754 }
00755 MSYS_ThreadUserCreate(g_pRootThread, NULL);
00756 assert(g_pRootThread->GetThreadId() == MSYS_Thread::ROOT_THREAD);
00757
00758 #ifdef MSYS_NOTHREADS
00759 if (aEnableThreads) {
00760 _ftprintf(stderr, _T("Threads cannot be enabled. Thread support is compiled out.\n"));
00761 }
00762 g_bEnableThreads = false;
00763 #else
00764 g_bEnableThreads = aEnableThreads;
00765 #endif // MSYS_NOTHREADS
00766
00767
00768 bool bRunInputLoop = true;
00769 MSYS_Args args;
00770 if (aInputFile) {
00771 args.resize(1);
00772
00773 args[0] = const_cast<TCHAR*>(aInputFile);
00774 DoRead(g_pRootThread, args);
00775 if (g_pRootThread->mInputCurr.mFile == stdin) {
00776 bRunInputLoop = false;
00777 }
00778 else {
00779
00780 g_pRootThread->mInputStack.pop_back();
00781 }
00782 }
00783 if (bRunInputLoop) {
00784
00785
00786 if (FileExists(MSYS_DefaultOptionsFile)) {
00787 args.resize(1);
00788
00789 args[0] = const_cast<TCHAR*>(MSYS_DefaultOptionsFile);
00790 DoRead(g_pRootThread, args);
00791 }
00792
00793
00794 InputLoop(g_pRootThread);
00795 }
00796
00797
00798 #ifndef MSYS_NOTHREADS
00799 if (g_bEnableThreads) {
00800 args.clear();
00801 args.push_back(_T("joinall"));
00802 DoThreads(g_pRootThread, args);
00803 }
00804 assert(g_mapThreads.empty());
00805 #endif // MSYS_NOTHREADS
00806
00807
00808 free(g_rgDispatch);
00809 g_rgDispatch = NULL;
00810 delete g_pRootThread;
00811 g_pRootThread = NULL;
00812
00813 bIsRunning = false;
00814 return true;
00815 }
00816
00817 static bool
00818 Input(
00819 MSYS_ThreadImpl * aThread,
00820 Buffer & aBuf
00821 )
00822 {
00823 #ifdef _WIN32
00824 if (aThread->mInputCurr.mFile == stdin) {
00825 InputFromConsole(aBuf);
00826 return true;
00827 }
00828 #endif
00829
00830 #ifdef _UNICODE
00831 char * pBuf = aThread->mCharBuf.mBuf;
00832 size_t nBufSiz = aThread->mCharBuf.mBufSiz;
00833 #else
00834 char * pBuf = aBuf.mBuf;
00835 size_t nBufSiz = aBuf.mBufSiz;
00836 #endif
00837
00838
00839
00840
00841 if (!fgets(pBuf, (int)nBufSiz, aThread->mInputCurr.mFile)) {
00842 if (ferror(aThread->mInputCurr.mFile)) {
00843 _ftprintf(stderr, _T("Error while reading input file: %s\n"),
00844 aThread->mInputCurr.mPath.c_str());
00845 }
00846 return false;
00847 }
00848
00849
00850 size_t nBufLen = strlen(pBuf);
00851 if (nBufLen >= nBufSiz - 3) {
00852 _ftprintf(stderr, _T("Line too long while reading input file: %s\n"),
00853 aThread->mInputCurr.mPath.c_str());
00854 return false;
00855 }
00856
00857 #ifdef _UNICODE
00858
00859 if (aThread->mInputCurr.mIsUtf8) {
00860 const UTF8 *pIn = (UTF8*)pBuf, *pInEnd = (UTF8*)(pBuf + nBufLen + 1);
00861 UTF16 *pOut = (UTF16*)aBuf.mBuf, *pOutEnd = (UTF16*)(aBuf.mBuf + aBuf.mBufSiz);
00862 ConversionResult rc = ConvertUTF8toUTF16(
00863 &pIn, pInEnd, &pOut, pOutEnd, lenientConversion);
00864 if (rc != conversionOK) {
00865 _ftprintf(stderr, _T("UTF-8 encoding error reading input file: %s\n"),
00866 aThread->mInputCurr.mPath.c_str());
00867 return false;
00868 }
00869 }
00870 else {
00871 size_t nCount = mbstowcs(aBuf.mBuf, pBuf, aBuf.mBufSiz);
00872 if (nCount == (size_t) -1) {
00873 _ftprintf(stderr, _T("Native encoding error reading input file: %s\n"),
00874 aThread->mInputCurr.mPath.c_str());
00875 return false;
00876 }
00877 if (nCount == aBuf.mBufSiz) {
00878 _ftprintf(stderr, _T("Line too long while reading input file: %s\n"),
00879 aThread->mInputCurr.mPath.c_str());
00880 return false;
00881 }
00882 }
00883 #endif
00884
00885 return true;
00886 }
00887
00888 static void
00889 Parse(
00890 TCHAR * aBuf,
00891 TCHAR *& aCommand,
00892 TCHAR *& aArgs
00893 )
00894 {
00895
00896 while (_istspace(*aBuf)) ++aBuf;
00897
00898
00899 aCommand = aBuf;
00900
00901
00902 while (*aBuf && !_istspace(*aBuf)) ++aBuf;
00903
00904
00905 if (!*aBuf) {
00906 aArgs = aBuf;
00907 return;
00908 }
00909
00910
00911 *aBuf++ = 0;
00912 aArgs = aBuf;
00913
00914
00915 aBuf += _tcslen(aBuf);
00916 for (--aBuf; aBuf >= aArgs && _istspace(*aBuf); --aBuf) {
00917 *aBuf = 0;
00918 }
00919 }
00920
00921 static void
00922 ParseArgs(
00923 TCHAR * aBuf,
00924 bool aFullParse,
00925 MSYS_Args & aArgs
00926 )
00927 {
00928 aArgs.clear();
00929
00930 bool isDone;
00931 do {
00932
00933 while (_istspace(*aBuf)) ++aBuf;
00934 if (!*aBuf) break;
00935
00936
00937 TCHAR * pArg = aBuf;
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951 TCHAR * pOut = aBuf;
00952 bool inQuotes = false;
00953 for (;;) {
00954 if (!*aBuf) {
00955 break;
00956 }
00957 if (!inQuotes && (*aBuf == ' ' || *aBuf == '\t')) {
00958 break;
00959 }
00960 if (*aBuf == '"') {
00961 inQuotes = !inQuotes;
00962 aBuf++;
00963 continue;
00964 }
00965 if (*aBuf != '\\') {
00966 *pOut++ = *aBuf++;
00967 continue;
00968 }
00969
00970
00971 if (*aBuf == '\\') {
00972 TCHAR * pLast = aBuf + 1;
00973 while (*pLast == '\\') ++pLast;
00974 if (*pLast != '"') {
00975
00976 while (*aBuf == '\\') {
00977 *pOut++ = *aBuf++;
00978 }
00979 continue;
00980 }
00981
00982
00983 while (*aBuf == '\\') {
00984 if (!*++aBuf) break;
00985 *pOut++ = *aBuf++;
00986 }
00987 continue;
00988 }
00989 }
00990
00991 isDone = (*aBuf == 0);
00992 *pOut = 0;
00993 ++aBuf;
00994
00995
00996 aArgs.push_back(pArg);
00997
00998
00999 if (aFullParse) {
01000 TCHAR * out = pArg;
01001 while (*pArg) {
01002
01003
01004 if (*pArg == '\\') {
01005 ++pArg;
01006 switch (*pArg) {
01007 case 'n': *out++ = '\n'; pArg++; break;
01008 case 'r': *out++ = '\r'; pArg++; break;
01009 case 't': *out++ = '\t'; pArg++; break;
01010 default:
01011 *out++ = '\\';
01012 *out++ = *pArg++;
01013 }
01014 }
01015 else {
01016 *out++ = *pArg++;
01017 }
01018 }
01019 *out = 0;
01020 }
01021 }
01022 while (!isDone);
01023 }
01024
01025 static void DoExit(MSYS_Thread * thrd, MSYS_Args & aArgs)
01026 {
01027 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01028
01029 if (MSYS_NeedHelp(aThread, DoExit, aArgs)) return;
01030
01031 TCHAR * pAction = aArgs.size() >= 1 ? aArgs[0] : NULL;
01032 if (!pAction) return;
01033
01034 if (_tcsicmp(_T("now"), pAction) == 0) {
01035 #ifndef MSYS_NOTHREADS
01036 MSYS_Args args;
01037 args.push_back(_T("stopall"));
01038 DoThreads(aThread, args);
01039 #endif // MSYS_NOTHREADS
01040
01041 g_pRootThread->bStopThread = true;
01042 return;
01043 }
01044
01045 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01046 }
01047
01048 static bool ReplaceString(Buffer & aBuf, const TCHAR * aSearch, const TCHAR * aReplace)
01049 {
01050 TCHAR * pLoc = _tcsstr(aBuf.mBuf, aSearch);
01051 if (pLoc) {
01052 int nBuf = (int) _tcslen(aBuf.mBuf);
01053 int nSearch = (int) _tcslen(aSearch);
01054 int nReplace = (int) _tcslen(aReplace);
01055 int nDiff = nReplace - nSearch;
01056 while (pLoc) {
01057 while (nBuf + nReplace - nSearch >= (int) aBuf.mBufSiz) {
01058 aBuf.Grow();
01059 }
01060 if (nDiff != 0) {
01061 int nRemaining = (int) (nBuf - (pLoc - aBuf.mBuf) - nSearch);
01062 memmove(pLoc + nSearch + nDiff, pLoc + nSearch,
01063 nRemaining * sizeof(TCHAR));
01064 pLoc[nSearch + nDiff + nRemaining] = 0;
01065 }
01066 memcpy(pLoc, aReplace, nReplace * sizeof(TCHAR));
01067 pLoc = _tcsstr(pLoc + nReplace, aSearch);
01068 }
01069 return true;
01070 }
01071 return false;
01072 }
01073
01074 static bool StringReplacements(MSYS_ThreadImpl * aThread, Buffer & aBuf)
01075 {
01076 if (!aThread->mStringReplacements) return false;
01077 if (!_tcsstr(aBuf.mBuf, _T("{{"))) return false;
01078
01079 TCHAR szBuf[200];
01080 const size_t nBufSiz = sizeof(szBuf)/sizeof(szBuf[0]);
01081 bool bMadeReplacement = false;
01082 if (g_bEnableThreads) {
01083 _sntprintf(szBuf, nBufSiz, _T("%d"), aThread->GetThreadId());
01084 if (ReplaceString(aBuf, _T("{{TID}}"), szBuf)) {
01085 bMadeReplacement = true;
01086 }
01087 if (ReplaceString(aBuf, _T("{{TNAME}}"), aThread->mThreadName.c_str())) {
01088 bMadeReplacement = true;
01089 }
01090 }
01091
01092 if (_tcsstr(aBuf.mBuf, _T("{{NOW}}"))) {
01093 time_t tNow;
01094 time(&tNow);
01095 struct tm tmNow = *localtime(&tNow);
01096 _sntprintf(szBuf, nBufSiz, _T("%02d/%02d/%04d %02d:%02d:%02d"),
01097 tmNow.tm_mday, tmNow.tm_mon+1, tmNow.tm_year+1900,
01098 tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec);
01099 ReplaceString(aBuf, _T("{{NOW}}"), szBuf);
01100 bMadeReplacement = true;
01101 }
01102
01103 return bMadeReplacement;
01104 }
01105
01106 static void OutputToTeeFile(MSYS_ThreadImpl * aThread, const Buffer & aBuf)
01107 {
01108 if (!aThread->mTeeFile) return;
01109
01110 #ifdef _UNICODE
01111
01112 while (aThread->mCharBuf.mBufSiz < aBuf.mBufSiz) {
01113 aThread->mCharBuf.Grow();
01114 }
01115 UTF8 *pOut = (UTF8 *) aThread->mCharBuf.mBuf;
01116 UTF8 *pOutEnd = (UTF8 *) aThread->mCharBuf.mBuf + aThread->mCharBuf.mBufSiz;
01117 size_t nLen = _tcslen(aBuf.mBuf) + 1;
01118 const UTF16 *pIn = (UTF16*) aBuf.mBuf;
01119 const UTF16 *pInEnd = (UTF16*) (aBuf.mBuf + nLen);
01120 ConversionResult rc = ConvertUTF16toUTF8(
01121 &pIn, pInEnd, &pOut, pOutEnd, lenientConversion);
01122 if (rc != conversionOK) {
01123 _ftprintf(stderr, _T("UTF-8 encoding error writing output file\n"));
01124 return;
01125 }
01126 size_t nBytes = (pOut - (UTF8*)aThread->mCharBuf.mBuf) - 1;
01127 AutoLock lock(g_lockOutputTeeFile);
01128 if (nBytes != fwrite(aThread->mCharBuf.mBuf, 1, nBytes, aThread->mTeeFile)) {
01129 _ftprintf(stderr, _T("Error writing output file\n"));
01130 return;
01131 }
01132 #else // _UNICODE
01133 AutoLock lock(g_lockOutputTeeFile);
01134 _ftprintf(aThread->mTeeFile, _T("%s"), aBuf.mBuf);
01135 #endif
01136 }
01137
01138 extern void MSYS_Display(MSYS_Thread * thrd, const TCHAR * aFormat, ...)
01139 {
01140 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01141
01142
01143 va_list ap;
01144 va_start(ap, aFormat);
01145 aThread->mHandlerBuf.FormatV(aFormat, ap);
01146 va_end(ap);
01147
01148 StringReplacements(aThread, aThread->mHandlerBuf);
01149
01150 if (aThread->mOptionConsole >= DISPLAY_OUTPUT) {
01151
01152
01153 OutputToConsole(aThread->mHandlerBuf.mBuf);
01154 }
01155 if (aThread->mTeeFile) {
01156 OutputToTeeFile(aThread, aThread->mHandlerBuf);
01157 }
01158 }
01159
01160 extern void MSYS_DisplayWrapped(MSYS_Thread * thrd, const TCHAR * aText)
01161 {
01162 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01163
01164 assert(aText);
01165
01166 std::tstring sText(aText);
01167 size_t n = sText.find(_T('\t'));
01168 while (n != std::tstring::npos) {
01169 sText.replace(n, 1, _T(" "));
01170 n = sText.find(_T('\t'), n + 3);
01171 }
01172 aText = sText.c_str();
01173
01174 const int MAX_LINE_LEN = 79;
01175 int nLineLen, nStartIdx, nCurrIdx = 0, nLastBreakIdx = -1;
01176 while (nCurrIdx < (int) sText.size()) {
01177 nLineLen = 0;
01178 nStartIdx = nCurrIdx = nLastBreakIdx + 1;
01179
01180
01181 while (nCurrIdx < (int) sText.size() && nLineLen < MAX_LINE_LEN) {
01182 if (_istspace(sText[nCurrIdx])) {
01183 nLastBreakIdx = nCurrIdx;
01184 if (sText[nCurrIdx] == _T('\n')) {
01185 break;
01186 }
01187 }
01188
01189 ++nLineLen;
01190 ++nCurrIdx;
01191 }
01192 if (!sText[nCurrIdx]) {
01193 nLastBreakIdx = nCurrIdx;
01194 }
01195
01196
01197 std::tstring sLine(sText, nStartIdx, nLastBreakIdx - nStartIdx);
01198 MSYS_Display(aThread, _T("%s\n"), sLine.c_str());
01199 }
01200 }
01201
01202 extern bool MSYS_NeedHelp(MSYS_Thread * thrd, MSYS_Handler fn, MSYS_Args & aArgs, bool aShowHelp)
01203 {
01204 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01205
01206 if (aThread->mUseNeedHelp) {
01207 for (size_t n = 0; n < aArgs.size(); ++n) {
01208 if (!_tcsicmp(aArgs[n], _T("-?")) || !_tcsicmp(aArgs[n], _T("/?"))) {
01209 if (aShowHelp) {
01210 MSYS_ShowHelp(aThread, fn);
01211 }
01212 return true;
01213 }
01214 }
01215 }
01216 return false;
01217 }
01218
01219
01220 static void HelpExternal(MSYS_Thread *, MSYS_Args &) { }
01221 static void HelpInternal(MSYS_Thread *, MSYS_Args &) { }
01222 static void HelpAll(MSYS_Thread *, MSYS_Args &) { }
01223
01224 static void DoHelp(MSYS_Thread * thrd, MSYS_Args & aArgs)
01225 {
01226 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01227
01228 if (MSYS_NeedHelp(aThread, DoHelp, aArgs)) return;
01229
01230 if (aArgs.empty()) {
01231 MSYS_ShowHelp(aThread, HelpExternal);
01232 return;
01233 }
01234
01235
01236 if (aArgs[0][0] == '/') {
01237 TCHAR ch = (TCHAR) _totlower(aArgs[0][1]);
01238 if (ch == 'i') {
01239 MSYS_ShowHelp(aThread, HelpInternal);
01240 }
01241 else if (ch == 'a') {
01242 MSYS_ShowHelp(aThread, HelpAll);
01243 }
01244 else {
01245 MSYS_Display(aThread, _T("Unknown command.\n"));
01246 }
01247 return;
01248 }
01249
01250
01251 const TCHAR * cmd = aArgs[0];
01252 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
01253 if (_tcsicmp(cmd, g_rgDispatch[n].cmd) == 0) {
01254
01255 aArgs.clear();
01256 if (g_rgDispatch[n].fn == DoShell) {
01257 aArgs.push_back(_T(""));
01258 }
01259 aArgs.push_back(_T("-?"));
01260 g_rgDispatch[n].fn(aThread, aArgs);
01261 return;
01262 }
01263 }
01264
01265
01266 MSYS_Display(aThread, _T("Unknown command.\n"));
01267 }
01268
01269 static void DoSleep(MSYS_Thread * thrd, MSYS_Args & aArgs)
01270 {
01271 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01272
01273 if (MSYS_NeedHelp(aThread, DoSleep, aArgs)) return;
01274
01275 if (aArgs.size() != 1) {
01276 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01277 return;
01278 }
01279
01280 int nMillis = _ttoi(aArgs[0]);
01281 if (nMillis < 0) {
01282 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01283 return;
01284 }
01285
01286 #ifdef _WIN32
01287 Sleep((DWORD)nMillis);
01288 #else
01289 usleep(nMillis * 1000);
01290 #endif
01291 }
01292
01293 static void DoEcho(MSYS_Thread * thrd, MSYS_Args & aArgs)
01294 {
01295 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01296
01297 if (MSYS_NeedHelp(aThread, DoEcho, aArgs)) return;
01298
01299 for (size_t n = 0; n < aArgs.size(); ++n) {
01300 MSYS_Display(aThread, _T("%s"), aArgs[n]);
01301 }
01302 MSYS_Display(aThread, _T("\n"));
01303 }
01304
01305 static void DoPwd(MSYS_Thread * thrd, MSYS_Args & aArgs)
01306 {
01307 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01308
01309 if (MSYS_NeedHelp(aThread, DoPwd, aArgs)) return;
01310
01311 TCHAR buf[MAX_PATH];
01312 if (_tgetcwd(buf, sizeof(buf))) {
01313 MSYS_Display(aThread, _T("%s\n"), buf);
01314 }
01315 }
01316
01317 static void DoChdir(MSYS_Thread * thrd, MSYS_Args & aArgs)
01318 {
01319 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01320
01321 if (MSYS_NeedHelp(aThread, DoChdir, aArgs)) return;
01322
01323 if (aArgs.size() != 1) {
01324 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01325 return;
01326 }
01327
01328 const TCHAR * pPath = aArgs[0];
01329 if (_tchdir(pPath) != 0) {
01330 if (errno == ENOENT) {
01331 MSYS_Display(aThread, _T("Invalid directory.\n"));
01332 }
01333 else {
01334 MSYS_Display(aThread, _T("chdir() failed.\n"));
01335 }
01336 return;
01337 }
01338 }
01339
01340 static void DoShell(MSYS_Thread * thrd, MSYS_Args & aArgs)
01341 {
01342 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01343
01344 assert(aArgs.size() == 2);
01345
01346 const TCHAR * pCommand = aArgs[0];
01347 const TCHAR * pArgs = aArgs[1];
01348
01349
01350 if (!*pCommand && (!_tcsicmp(pArgs, _T("-?")) || !_tcsicmp(pArgs, _T("/?")))) {
01351 MSYS_ShowHelp(aThread, DoShell);
01352 return;
01353 }
01354
01355 fflush(stdout);
01356 fflush(stderr);
01357
01358 std::tstring sCommandLine;
01359 if (!*pCommand && !*pArgs) {
01360 #ifdef _WIN32
01361 pCommand = _tgetenv(_T("COMSPEC"));
01362 if (!pCommand) {
01363 pCommand = _T("cmd.exe");
01364 }
01365 #else
01366 pCommand = _tgetenv(_T("SH"));
01367 if (!pCommand) {
01368 pCommand = _T("/bin/sh");
01369 }
01370 #endif
01371 }
01372
01373 sCommandLine = pCommand;
01374 sCommandLine += _T(' ');
01375 sCommandLine += pArgs;
01376 _tsystem(sCommandLine.c_str());
01377 }
01378
01379 static void DoRead(MSYS_Thread * thrd, MSYS_Args & aArgs)
01380 {
01381 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01382
01383 if (MSYS_NeedHelp(aThread, DoRead, aArgs)) return;
01384
01385 if (aArgs.size() != 1) {
01386 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01387 return;
01388 }
01389
01390 const TCHAR * pPath = aArgs[0];
01391
01392 FILE * fp = _tfopen(pPath, _T("rb"));
01393 #ifdef _WIN32
01394
01395
01396 HANDLE hLock = fp ? CreateFile(pPath, GENERIC_READ, FILE_SHARE_READ,
01397 NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL) : INVALID_HANDLE_VALUE;
01398 if (fp && hLock == INVALID_HANDLE_VALUE) {
01399 fclose(fp);
01400 fp = NULL;
01401 }
01402 #endif
01403 if (!fp) {
01404 _ftprintf(stderr, _T("Error opening file '%s'\n"), pPath);
01405 return;
01406 }
01407
01408
01409 bool bIsUtf8 = false;
01410 const char * szUtf8Signature = "\xEF\xBB\xBF";
01411 char szSig[3];
01412 if (3 == fread(szSig, 1, 3, fp) && 0 == memcmp(szSig, szUtf8Signature, 3)) {
01413 bIsUtf8 = true;
01414 }
01415 else {
01416 fseek(fp, 0, SEEK_SET);
01417 }
01418
01419 aThread->mInputStack.push_back(aThread->mInputCurr);
01420 aThread->mInputCurr = InputFile(fp, pPath, bIsUtf8);
01421 #ifdef _WIN32
01422
01423 aThread->mInputCurr.mFileLock = hLock;
01424 #endif
01425 }
01426
01427 #ifndef MSYS_NOTHREADS
01428 static void ThreadCreate(MSYS_ThreadImpl * aThread, TCHAR * aThreadName, TCHAR * aPath)
01429 {
01430 if (!aThreadName || !aPath) {
01431 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01432 return;
01433 }
01434
01435 if (*aThreadName) {
01436 ThreadMap::const_iterator i = g_mapThreads.find(aThreadName);
01437 if (i != g_mapThreads.end()) {
01438 MSYS_Display(aThread, _T("Thread '%s' is already registered.\n"), aThreadName);
01439 return;
01440 }
01441 }
01442
01443
01444 MSYS_ThreadImpl * pNewThread = new MSYS_ThreadImpl(aThreadName);
01445 if (!pNewThread) {
01446 _ftprintf(stderr, _T("Out of memory.\n"));
01447 exit(1);
01448 }
01449 MSYS_ThreadUserCreate(pNewThread, aThread);
01450
01451
01452 if (!*aThreadName) {
01453 TCHAR szThreadId[100];
01454 _sntprintf(szThreadId, sizeof(szThreadId)/sizeof(szThreadId[0]),
01455 _T("%d"), pNewThread->GetThreadId());
01456 ThreadMap::const_iterator i = g_mapThreads.find(szThreadId);
01457 for (int nCount = 0; i != g_mapThreads.end(); ++nCount) {
01458 _sntprintf(szThreadId, sizeof(szThreadId)/sizeof(szThreadId[0]),
01459 _T("%d-%d"), pNewThread->GetThreadId(), nCount);
01460 i = g_mapThreads.find(szThreadId);
01461 }
01462 pNewThread->mThreadName = szThreadId;
01463 aThreadName = NULL;
01464 }
01465
01466
01467 pNewThread->mOptionConsole = DISPLAY_NONE;
01468
01469
01470 MSYS_Args args;
01471 args.push_back(aPath);
01472 DoRead(pNewThread, args);
01473 if (pNewThread->mInputCurr.mFile == stdin) {
01474 delete pNewThread;
01475 return;
01476 }
01477 pNewThread->mInputStack.pop_back();
01478
01479
01480 #ifdef _WIN32
01481 unsigned threadID;
01482 pNewThread->mThreadHandle = (HANDLE) _beginthreadex(
01483 NULL, 0, InputLoop, pNewThread, 0, &threadID);
01484 if (!pNewThread->mThreadHandle) {
01485 delete pNewThread;
01486 _ftprintf(stderr, _T("Failed to start thread.\n"));
01487 return;
01488 }
01489 #else
01490 int rc = pthread_create(&pNewThread->mThreadHandle,
01491 NULL, InputLoop, pNewThread);
01492 if (rc != 0) {
01493 delete pNewThread;
01494 _ftprintf(stderr, _T("Failed to start thread.\n"));
01495 return;
01496 }
01497 #endif
01498
01499
01500 g_mapThreads.insert(ThreadMap::value_type(pNewThread->mThreadName, pNewThread));
01501 }
01502 #endif // MSYS_NOTHREADS
01503
01504 #ifndef MSYS_NOTHREADS
01505 static void ThreadJoin(MSYS_ThreadImpl * aThread, TCHAR * aThreadName)
01506 {
01507 if (aThreadName) {
01508 ThreadMap::const_iterator i = g_mapThreads.find(aThreadName);
01509 if (i == g_mapThreads.end()) {
01510 MSYS_Display(aThread, _T("Thread '%s' is not registered.\n"), aThreadName);
01511 return;
01512 }
01513 if (i->second == aThread) {
01514 MSYS_Display(aThread, _T("Cannot wait on self.\n"));
01515 return;
01516 }
01517 }
01518 if (g_mapThreads.empty()) {
01519 return;
01520 }
01521
01522 ThreadMap::iterator i = g_mapThreads.begin();
01523 for (; i != g_mapThreads.end(); ++i) {
01524 MSYS_ThreadImpl * pThread = i->second;
01525 if (pThread == aThread) continue;
01526 if (aThreadName && pThread->mThreadName != aThreadName) continue;
01527 #ifdef _WIN32
01528 DWORD dwResult = WaitForSingleObject(pThread->mThreadHandle, INFINITE);
01529 if (dwResult != WAIT_OBJECT_0) {
01530 #else
01531 int rc = pthread_join(pThread->mThreadHandle, NULL);
01532 if (rc != 0) {
01533 #endif
01534 MSYS_Display(aThread, _T("Wait for thread '%s' failed.\n"),
01535 pThread->mThreadName.c_str());
01536 }
01537 delete pThread;
01538 if (aThreadName) {
01539 g_mapThreads.erase(i);
01540 break;
01541 }
01542 }
01543 if (!aThreadName) {
01544 g_mapThreads.clear();
01545 }
01546 }
01547 #endif // MSYS_NOTHREADS
01548
01549 #ifndef MSYS_NOTHREADS
01550 static void ThreadStop(MSYS_ThreadImpl * aThread, TCHAR * aThreadName)
01551 {
01552 ThreadMap::const_iterator i = g_mapThreads.end();
01553 if (aThreadName) {
01554 i = g_mapThreads.find(aThreadName);
01555 if (i == g_mapThreads.end()) {
01556 MSYS_Display(aThread, _T("Thread '%s' is not registered.\n"), aThreadName);
01557 return;
01558 }
01559 }
01560 if (g_mapThreads.empty()) {
01561 return;
01562 }
01563
01564 if (aThreadName) {
01565 i->second->bStopThread = true;
01566 }
01567 else {
01568 i = g_mapThreads.begin();
01569 for (int n = 0; i != g_mapThreads.end(); ++i, ++n) {
01570 i->second->bStopThread = true;
01571 }
01572 }
01573 }
01574 #endif // MSYS_NOTHREADS
01575
01576 #ifndef MSYS_NOTHREADS
01577 static void DoThreads(MSYS_Thread * thrd, MSYS_Args & aArgs)
01578 {
01579 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01580
01581 if (MSYS_NeedHelp(aThread, DoThreads, aArgs)) return;
01582
01583 if (!g_bEnableThreads) {
01584 MSYS_Display(aThread, _T("Threads are disabled.\n"));
01585 return;
01586 }
01587 AutoLock lock(g_lockThreadsMap);
01588
01589 TCHAR * pAction = aArgs.size() >= 1 ? aArgs[0] : _T("list");
01590 TCHAR * pName = aArgs.size() >= 2 ? aArgs[1] : NULL;
01591 TCHAR * pPath = aArgs.size() >= 3 ? aArgs[2] : NULL;
01592
01593
01594 if (_tcsicmp(_T("list"), pAction) == 0) {
01595 if (g_mapThreads.empty()) {
01596 MSYS_Display(aThread, _T("No current threads.\n"));
01597 return;
01598 }
01599
01600 ThreadMap::const_iterator i = g_mapThreads.begin();
01601 for (; i != g_mapThreads.end(); ++i) {
01602 const MSYS_ThreadImpl * pThread = i->second;
01603 MSYS_Display(aThread, _T(" %-2d %-12s %s\n"),
01604 pThread->GetThreadId(), i->first.c_str(),
01605 pThread->bIsRunning ? _T("running") : _T("done"));
01606 }
01607 return;
01608 }
01609
01610
01611 if (_tcsicmp(_T("start"), pAction) == 0) {
01612 ThreadCreate(aThread, pName, pPath);
01613 return;
01614 }
01615
01616
01617 if (_tcsicmp(_T("stop"), pAction) == 0) {
01618 if (!pName) {
01619 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01620 return;
01621 }
01622 ThreadStop(aThread, pName);
01623 return;
01624 }
01625
01626
01627 if (_tcsicmp(_T("stopall"), pAction) == 0) {
01628 ThreadStop(aThread, NULL);
01629 return;
01630 }
01631
01632
01633 if (_tcsicmp(_T("join"), pAction) == 0) {
01634 if (!pName) {
01635 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01636 return;
01637 }
01638 ThreadJoin(aThread, pName);
01639 return;
01640 }
01641
01642
01643 if (_tcsicmp(_T("joinall"), pAction) == 0) {
01644 ThreadJoin(aThread, NULL);
01645 return;
01646 }
01647
01648 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01649 }
01650 #endif // MSYS_NOTHREADS
01651
01652 static void DoTee(MSYS_Thread * thrd, MSYS_Args & aArgs)
01653 {
01654 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01655
01656 if (MSYS_NeedHelp(aThread, DoTee, aArgs)) return;
01657
01658 const TCHAR * pPath = aArgs.empty() ? NULL : aArgs[0];
01659 const TCHAR * pMode = _T("wb");
01660 if (aArgs.size() == 2 && !_tcsicmp(aArgs[0], _T("-append"))) {
01661 pMode = _T("ab");
01662 pPath = aArgs[1];
01663 }
01664
01665 FILE * fpNew = NULL;
01666 if (pPath) {
01667 fpNew = _tfopen(pPath, pMode);
01668 if (!fpNew) {
01669 _ftprintf(stderr, _T("Error opening file '%s'\n"), pPath);
01670 return;
01671 }
01672 }
01673
01674 if (aThread->mTeeFile) {
01675 fclose(aThread->mTeeFile);
01676 aThread->mTeeFile = NULL;
01677 _ftprintf(stdout, _T("Closed tee output file\n"));
01678 }
01679
01680 if (fpNew) {
01681 aThread->mTeeFile = fpNew;
01682 _ftprintf(stdout, _T("Started to tee output to '%s'\n"), pPath);
01683 }
01684 }
01685
01686 extern void
01687 MSYS_DisplayOption(
01688 MSYS_Thread * aThread,
01689 const TCHAR * aOption,
01690 const TCHAR * aValue
01691 )
01692 {
01693 if (!aOption || !aValue) return;
01694
01695 if (!*aValue || _tcschr(aValue, ' ')) {
01696 MSYS_Display(aThread, _T("setopt %-12s \"%s\"\n"), aOption, aValue);
01697 }
01698 else {
01699 MSYS_Display(aThread, _T("setopt %-12s %s\n"), aOption, aValue);
01700 }
01701 }
01702
01703 static const TCHAR *
01704 GetConsoleType(
01705 DISPLAY aType
01706 )
01707 {
01708 switch (aType) {
01709 case DISPLAY_ALL: return _T("all");
01710 case DISPLAY_PROMPT: return _T("prompt");
01711 case DISPLAY_OUTPUT: return _T("output");
01712 case DISPLAY_NONE: return _T("none");
01713 default: return NULL;
01714 }
01715 }
01716
01717 static void DoSetOpt(MSYS_Thread * thrd, MSYS_Args & aArgs)
01718 {
01719 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01720
01721 if (MSYS_NeedHelp(aThread, DoSetOpt, aArgs, false)) {
01722
01723 if (aArgs.size() == 2) {
01724 std::tstring sCommand = OPTION_PREFIX;
01725 sCommand += aArgs[0];
01726 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
01727 if (0 != _tcsicmp(g_rgDispatch[n].cmd, sCommand.c_str())) continue;
01728 MSYS_ShowHelp(aThread, g_rgDispatch[n].fn);
01729 return;
01730 }
01731 }
01732
01733 MSYS_ShowHelp(aThread, DoSetOpt);
01734 MSYS_Display(aThread, _T("\nAvailable options are:\n"));
01735 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
01736 if (_tcsnicmp(g_rgDispatch[n].cmd, OPTION_PREFIX, OPTION_PREFIX_LEN)) continue;
01737 MSYS_Display(aThread, _T(" %-15s%s\n"),
01738 g_rgDispatch[n].cmd + OPTION_PREFIX_LEN, g_rgDispatch[n].oneHelp);
01739 }
01740 return;
01741 }
01742
01743 std::tstring opt;
01744 std::tstring cmd;
01745 if (!aArgs.empty()) {
01746 opt = aArgs[0];
01747 cmd = OPTION_PREFIX;
01748 cmd += opt;
01749 }
01750
01751
01752 if (!aArgs.empty() && aArgs[0][0] == '-') {
01753 const TCHAR * pAction = aArgs[0];
01754 const TCHAR * pFile = aArgs.size() > 1 ? aArgs[1] : MSYS_DefaultOptionsFile;
01755
01756 if (!_tcsicmp(pAction, _T("-load"))) {
01757 MSYS_Args args;
01758
01759 args.push_back(const_cast<TCHAR*>(pFile));
01760 DoRead(aThread, args);
01761 return;
01762 }
01763
01764 if (!_tcsicmp(pAction, _T("-save"))) {
01765
01766 FILE * fpSave = aThread->mTeeFile;
01767 aThread->mTeeFile = _tfopen(pFile, _T("wb"));
01768 if (!aThread->mTeeFile) {
01769 aThread->mTeeFile = fpSave;
01770 MSYS_Display(aThread, _T("Failed to open options file.\n"));
01771 return;
01772 }
01773
01774 DISPLAY nSave = aThread->mOptionConsole;
01775 aThread->mOptionConsole = DISPLAY_NONE;
01776
01777
01778 aThread->mHandlerBuf.Format(_T("%s"),
01779 _T("@setopt console none\n")
01780 _T("@setopt utf8 true\n")
01781 _T("\n")
01782 );
01783 OutputToTeeFile(aThread, aThread->mHandlerBuf);
01784
01785
01786 MSYS_Args args;
01787 args.resize(2);
01788 TCHAR szCommand[MAX_PATH+1];
01789 for (int n = 0; MSYS_Commands[n].cmd; ++n) {
01790 if (_tcsnicmp(MSYS_Commands[n].cmd, OPTION_PREFIX, OPTION_PREFIX_LEN) == 0) {
01791 _tcsncpy(szCommand, MSYS_Commands[n].cmd + OPTION_PREFIX_LEN, MAX_PATH);
01792 szCommand[MAX_PATH] = 0;
01793 args[0] = _T("getopt");
01794 args[1] = szCommand;
01795 MSYS_Commands[n].fn(aThread, args);
01796 }
01797 }
01798
01799
01800
01801 MSYS_DisplayOption(aThread, _T("console"), GetConsoleType(nSave));
01802
01803 aThread->mOptionConsole = nSave;
01804 fclose(aThread->mTeeFile);
01805 aThread->mTeeFile = fpSave;
01806 return;
01807 }
01808 }
01809
01810 if (aArgs.size() < 2) {
01811 aArgs.insert(aArgs.begin(), _T("getopt"));
01812 if (aArgs.size() < 2) {
01813 aArgs.push_back(_T(""));
01814 }
01815
01816 int nFound = 0;
01817 TCHAR szCommand[MAX_PATH+1];
01818 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
01819 if (_tcsnicmp(g_rgDispatch[n].cmd, OPTION_PREFIX, OPTION_PREFIX_LEN) == 0) {
01820 if (!cmd.empty() && _tcsicmp(g_rgDispatch[n].cmd, cmd.c_str()) != 0) continue;
01821 _tcsncpy(szCommand, g_rgDispatch[n].cmd + OPTION_PREFIX_LEN, MAX_PATH);
01822 szCommand[MAX_PATH] = 0;
01823 aArgs[1] = szCommand;
01824 g_rgDispatch[n].fn(aThread, aArgs);
01825 ++nFound;
01826 }
01827 }
01828
01829 if (aArgs.size() == 2 && !nFound) {
01830 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01831 return;
01832 }
01833 }
01834 else {
01835 aArgs.insert(aArgs.begin(), _T("setopt"));
01836
01837 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
01838 if (_tcsicmp(cmd.c_str(), g_rgDispatch[n].cmd) == 0) {
01839 g_rgDispatch[n].fn(aThread, aArgs);
01840 return;
01841 }
01842 }
01843 MSYS_Display(aThread, _T("Unrecognized option: %s\n"), opt.c_str());
01844 }
01845 }
01846
01847 static void OptConsole(MSYS_Thread * thrd, MSYS_Args & aArgs)
01848 {
01849 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01850
01851 if (MSYS_NeedHelp(aThread, OptConsole, aArgs)) return;
01852
01853 assert(!aArgs.empty());
01854
01855 const TCHAR * pAction = aArgs[0];
01856 const TCHAR * pValue = aArgs.size() > 2 ? aArgs[2] : NULL;
01857
01858
01859 if (*pAction == _T('s')) {
01860 TCHAR cValue = *pValue;
01861 if (cValue >= 'A' && cValue <= 'Z') {
01862 cValue -= 'A' + 'a';
01863 }
01864 switch (cValue) {
01865 case 'a': aThread->mOptionConsole = DISPLAY_ALL; break;
01866 case 'p': aThread->mOptionConsole = DISPLAY_PROMPT; break;
01867 case 'o': aThread->mOptionConsole = DISPLAY_OUTPUT; break;
01868 case 'n': aThread->mOptionConsole = DISPLAY_NONE; break;
01869 default:
01870 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01871 return;
01872 }
01873 }
01874
01875
01876 if (*pAction == _T('g')) {
01877 MSYS_DisplayOption(aThread, _T("console"),
01878 GetConsoleType(aThread->mOptionConsole));
01879 }
01880 }
01881
01882 static void OptThreads(MSYS_Thread * thrd, MSYS_Args & aArgs)
01883 {
01884 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01885
01886 if (MSYS_NeedHelp(aThread, OptThreads, aArgs)) return;
01887
01888 assert(!aArgs.empty());
01889
01890 const TCHAR * pAction = aArgs[0];
01891
01892
01893 if (*pAction == _T('s')) {
01894 MSYS_Display(aThread, _T("This option is read-only and cannot be changed.\n"));
01895 return;
01896 }
01897
01898
01899 if (*pAction == _T('g')) {
01900 const TCHAR * pValue = g_bEnableThreads ? _T("true") : _T("false");
01901 MSYS_DisplayOption(aThread, _T("threads"), pValue);
01902 }
01903 }
01904
01905 static void OptUtf8(MSYS_Thread * thrd, MSYS_Args & aArgs)
01906 {
01907 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01908
01909 if (MSYS_NeedHelp(aThread, OptUtf8, aArgs)) return;
01910
01911 assert(!aArgs.empty());
01912
01913 const TCHAR * pAction = aArgs[0];
01914 const TCHAR * pValue = aArgs.size() > 2 ? aArgs[2] : NULL;
01915
01916
01917 if (*pAction == _T('s')) {
01918 aThread->mInputCurr.mIsUtf8 =
01919 pValue[0] == _T('t') || pValue[0] == _T('T') || pValue[0] == _T('1')
01920 || _tcsicmp(pValue, _T("on")) == 0;
01921 }
01922
01923
01924 if (*pAction == _T('g')) {
01925 const TCHAR * pValue = aThread->mInputCurr.mIsUtf8 ? _T("true") : _T("false");
01926 MSYS_DisplayOption(aThread, _T("utf8"), pValue);
01927 }
01928 }
01929
01930 static void DoProc(MSYS_Thread * thrd, MSYS_Args & aArgs)
01931 {
01932 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
01933
01934 if (MSYS_NeedHelp(aThread, DoProc, aArgs)) return;
01935
01936 if (aArgs.empty()) {
01937 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
01938 return;
01939 }
01940
01941 if (aThread->mInputCurr.mFile == stdin) {
01942 MSYS_Display(aThread, _T("Invalid during console input.\n"));
01943 return;
01944 }
01945
01946 const TCHAR * pAction = aArgs[0];
01947 const TCHAR * pName = aArgs.size() > 0 ? aArgs[1] : NULL;
01948
01949
01950 if (_tcsicmp(pAction, _T("begin")) == 0) {
01951 long lProc = ftell(aThread->mInputCurr.mFile);
01952 bool bInserted = aThread->mInputCurr.mProc.insert(
01953 ProcMap::value_type(pName, lProc)).second;
01954 if (!bInserted) {
01955 MSYS_Display(aThread, _T("Ignoring duplicate procedure definition: \"%s\".\n"), pName);
01956 }
01957
01958
01959 Buffer buf;
01960 TCHAR *pCommand, *pArgs;
01961 for (;;) {
01962 if (!Input(aThread, buf)) {
01963 MSYS_Display(aThread, _T("End of procedure definition not found: \"%s\".\n"), pName);
01964 break;
01965 }
01966 Parse(buf.mBuf, pCommand, pArgs);
01967 if (_tcsicmp(pCommand, _T("proc")) == 0 && _tcsicmp(pArgs, _T("end")) == 0) {
01968 break;
01969 }
01970 }
01971 return;
01972 }
01973
01974
01975 if (_tcsicmp(pAction, _T("end")) == 0) {
01976 if (aThread->mInputCurr.mProcStack.empty()) {
01977 MSYS_Display(aThread, _T("Invalid return from procedure call.\n"));
01978 return;
01979 }
01980
01981
01982 if (aThread->Loop()) return;
01983
01984
01985 long lReturn = aThread->mInputCurr.mProcStack.back();
01986 aThread->mInputCurr.mProcStack.pop_back();
01987 if (fseek(aThread->mInputCurr.mFile, lReturn, SEEK_SET) != 0) {
01988 MSYS_Display(aThread, _T("Failed return from procedure call.\n"));
01989 return;
01990 }
01991 return;
01992 }
01993
01994
01995 if (_tcsicmp(pAction, _T("call")) == 0) {
01996 ProcMap::const_iterator i = aThread->mInputCurr.mProc.find(pName);
01997 if (i == aThread->mInputCurr.mProc.end()) {
01998 MSYS_Display(aThread, _T("Unknown procedure call: \"%s\".\n"), pName);
01999 return;
02000 }
02001
02002
02003 long lCurr = ftell(aThread->mInputCurr.mFile);
02004 aThread->mInputCurr.mProcStack.push_back(lCurr);
02005
02006
02007 if (fseek(aThread->mInputCurr.mFile, i->second, SEEK_SET) != 0) {
02008 MSYS_Display(aThread, _T("Failed procedure call: \"%s\".\n"), pName);
02009 }
02010 return;
02011 }
02012
02013 MSYS_Display(aThread, _T("Invalid command.\n"));
02014 }
02015
02016 static int ParseTime(const TCHAR * aTimeString, struct tm & aTime)
02017 {
02018 if (3 == _stscanf(aTimeString, _T("%u:%u:%u"),
02019 &aTime.tm_hour, &aTime.tm_min, &aTime.tm_sec))
02020 {
02021 return 3;
02022 }
02023
02024 aTime.tm_hour = 0;
02025 if (2 == _stscanf(aTimeString, _T("%u:%u"),
02026 &aTime.tm_min, &aTime.tm_sec))
02027 {
02028 return 2;
02029 }
02030
02031 aTime.tm_min = 0;
02032 if (1 == _stscanf(aTimeString, _T("%u"),
02033 &aTime.tm_sec))
02034 {
02035 return 1;
02036 }
02037
02038 aTime.tm_sec = 0;
02039 return 0;
02040 }
02041
02042 static void DoLoop(MSYS_Thread * thrd, MSYS_Args & aArgs)
02043 {
02044 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
02045
02046 if (MSYS_NeedHelp(aThread, DoLoop, aArgs)) return;
02047
02048 if (aArgs.size() != 3) {
02049 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
02050 return;
02051 }
02052
02053 if (aThread->mInputCurr.mFile == stdin) {
02054 MSYS_Display(aThread, _T("Invalid during console input.\n"));
02055 return;
02056 }
02057
02058 const TCHAR * pProc = aArgs[0];
02059 const TCHAR * pType = aArgs[1];
02060 const TCHAR * pLimit = aArgs[2];
02061
02062 ProcMap::const_iterator i = aThread->mInputCurr.mProc.find(pProc);
02063 if (i == aThread->mInputCurr.mProc.end()) {
02064 MSYS_Display(aThread, _T("Unknown procedure call: \"%s\".\n"), pProc);
02065 return;
02066 }
02067
02068
02069 long lProcedure = i->second;
02070
02071
02072 if (_tcsicmp(_T("count"), pType) == 0) {
02073 int nCount = _ttoi(pLimit);
02074 aThread->LoopStartCounted(lProcedure, nCount);
02075 return;
02076 }
02077
02078
02079 if (_tcsicmp(_T("for"), pType) == 0 || _tcsicmp(_T("until"), pType) == 0) {
02080 time_t tNow, tLimit;
02081 time(&tNow);
02082
02083 struct tm tmNow = *localtime(&tNow);
02084 int nTimeLen = ParseTime(pLimit, tmNow);
02085
02086 if (_tcsicmp(_T("for"), pType) == 0) {
02087
02088 tLimit = tNow + tmNow.tm_sec + (tmNow.tm_min * 60) + (tmNow.tm_hour * 60 * 60);
02089 }
02090 else {
02091
02092 if (nTimeLen < 2) {
02093 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
02094 return;
02095 }
02096 if (nTimeLen == 2) {
02097 tmNow.tm_hour = tmNow.tm_min;
02098 tmNow.tm_min = tmNow.tm_sec;
02099 tmNow.tm_sec = 0;
02100 }
02101 tLimit = mktime(&tmNow);
02102
02103
02104 if (tLimit < tNow) {
02105 tLimit += (24 * 60 * 60 );
02106 }
02107 }
02108
02109 const time_t MAX_PERIOD = (7 * 24 * 60 * 60);
02110 if ((tLimit - tNow) > MAX_PERIOD) {
02111 MSYS_Display(aThread, _T("Period too long. See help.\n"));
02112 return;
02113 }
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126 aThread->LoopStartTimed(lProcedure, tLimit);
02127 return;
02128 }
02129
02130 MSYS_Display(aThread, _T("Invalid loop type.\n"));
02131 }
02132
02133 static void TimerTell(MSYS_ThreadImpl * aThread, const TCHAR * aTimer, bool aShowRaw)
02134 {
02135 if (aThread->mTimerMap.empty()) {
02136 MSYS_Display(aThread, _T("No timers.\n"));
02137 }
02138 TimerMap::const_iterator iTimer = aThread->mTimerMap.begin();
02139 for (; iTimer != aThread->mTimerMap.end(); ++iTimer) {
02140 if (aTimer && iTimer->first != aTimer) continue;
02141
02142 const Timer & timer = iTimer->second;
02143 long nElapsed = timer.GetCurrentElapsed();
02144 if (aShowRaw) {
02145 MSYS_Display(aThread, _T("\"timer\",\"%s\"\n"),
02146 iTimer->first.c_str());
02147 MSYS_Display(aThread, _T("\"current\",%ld.%03ld seconds\n"),
02148 nElapsed / 1000, nElapsed % 1000);
02149 }
02150 else {
02151 MSYS_Display(aThread, _T("%c%-10s %5ld.%03ld seconds\n"),
02152 timer.IsRunning() ? '*' : ' ',
02153 iTimer->first.c_str(), nElapsed / 1000, nElapsed % 1000);
02154 }
02155 if (!timer.Laps().empty()) {
02156
02157 long nCount = (long) timer.Laps().size();
02158 long nTotal = 0;
02159 LapArray::const_iterator iLap = timer.Laps().begin();
02160 for (int n = 1; iLap != timer.Laps().end(); ++iLap, ++n) {
02161 nTotal += *iLap;
02162 if (aShowRaw) {
02163 MSYS_Display(aThread, _T("%d,%ld.%03ld\n"),
02164 n, *iLap / 1000, *iLap % 1000);
02165 }
02166 else {
02167 MSYS_Display(aThread, _T(" %3d: %3ld.%03ld"),
02168 n, *iLap / 1000, *iLap % 1000);
02169 if (n % 5 == 0) MSYS_Display(aThread, _T("\n"));
02170 }
02171 }
02172 if (!aShowRaw && (nCount % 5) != 0) {
02173 MSYS_Display(aThread, _T("\n"));
02174 }
02175
02176 if (aShowRaw) {
02177 MSYS_Display(aThread, _T("\n"));
02178 }
02179 else {
02180
02181 long nAverage = nTotal / nCount;
02182 MSYS_Display(aThread, _T(" Total: %ld.%03ld Laps: %ld Average: %ld.%03ld\n"),
02183 nTotal / 1000, nTotal % 1000, nCount,
02184 nAverage / 1000, nAverage % 1000);
02185
02186
02187 LapArray elim(timer.Laps());
02188 std::sort(elim.begin(), elim.end());
02189 long nOutrider = nCount / 10;
02190 elim.erase(elim.begin(), elim.begin() + nOutrider);
02191 elim.erase(elim.end() - nOutrider, elim.end());
02192 nTotal = 0;
02193 for (size_t n = 0; n < elim.size(); ++n) {
02194 nTotal += elim[n];
02195 }
02196 nCount -= 2 * nOutrider;
02197 nAverage = nTotal / nCount;
02198 MSYS_Display(aThread, _T(" Total: %ld.%03ld Laps: %ld Average: %ld.%03ld (outrider adjusted)\n"),
02199 nTotal / 1000, nTotal % 1000, nCount,
02200 nAverage / 1000, nAverage % 1000);
02201 }
02202 }
02203 }
02204 }
02205
02206 static void DoTimer(MSYS_Thread * thrd, MSYS_Args & aArgs)
02207 {
02208 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
02209
02210 if (MSYS_NeedHelp(aThread, DoTimer, aArgs)) return;
02211
02212 if (aArgs.size() > 2) {
02213 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
02214 return;
02215 }
02216
02217 const TCHAR * pAction = (aArgs.size() > 0) ? aArgs[0] : _T("tell");
02218 const TCHAR * pId = (aArgs.size() > 1) ? aArgs[1] : NULL;
02219
02220
02221 if (_tcsicmp(_T("tell"), pAction) == 0 || _tcsicmp(_T("tellraw"), pAction) == 0) {
02222 bool bIsRaw = (pAction[4] != 0);
02223 TimerTell(aThread, pId, bIsRaw);
02224 return;
02225 }
02226
02227 if (!pId) {
02228 MSYS_Display(aThread, _T("Invalid arguments. See help.\n"));
02229 return;
02230 }
02231
02232
02233 TimerMap::iterator iTimer = aThread->mTimerMap.find(pId);
02234
02235
02236 if (_tcsicmp(_T("start"), pAction) == 0) {
02237
02238 if (iTimer == aThread->mTimerMap.end()) {
02239 iTimer = aThread->mTimerMap.insert(
02240 TimerMap::value_type(pId, Timer())).first;
02241 }
02242 iTimer->second.Start();
02243 return;
02244 }
02245
02246
02247 if (_tcsicmp(_T("delete"), pAction) == 0) {
02248 if (iTimer != aThread->mTimerMap.end()) {
02249 aThread->mTimerMap.erase(iTimer);
02250 }
02251 return;
02252 }
02253
02254 if (iTimer == aThread->mTimerMap.end()) {
02255 MSYS_Display(aThread, _T("Invalid timer.\n"));
02256 return;
02257 }
02258
02259
02260 if (_tcsicmp(_T("stop"), pAction) == 0) {
02261 iTimer->second.Stop();
02262 return;
02263 }
02264
02265
02266 if (_tcsicmp(_T("lap"), pAction) == 0) {
02267 iTimer->second.Lap();
02268 return;
02269 }
02270
02271
02272 if (_tcsicmp(_T("reset"), pAction) == 0) {
02273 iTimer->second.Reset();
02274 return;
02275 }
02276
02277 MSYS_Display(aThread, _T("Invalid command.\n"));
02278 }
02279
02280 static bool
02281 Dispatch(
02282 MSYS_ThreadImpl * aThread,
02283 TCHAR * aCommand,
02284 TCHAR * aArgs,
02285 PARSE aParse
02286 )
02287 {
02288 MSYS_Args args;
02289
02290
02291 if (*aCommand == '!' || !_tcsicmp(aCommand, _T("shell"))) {
02292 if (*aCommand == '!') {
02293 ++aCommand;
02294 }
02295 else {
02296 aCommand = _T("");
02297 }
02298 args.push_back(aCommand);
02299 args.push_back(aArgs);
02300 DoShell(aThread, args);
02301 return true;
02302 }
02303
02304 if (*aCommand != _T('_')) {
02305 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
02306 if (_tcsicmp(aCommand, g_rgDispatch[n].cmd) == 0) {
02307 bool bFullParse = (aParse != PARSE_OFF) &&
02308 (g_rgDispatch[n].unquote || (aParse == PARSE_ON));
02309 ParseArgs(aArgs, bFullParse, args);
02310 aThread->SetCurrCommand(g_rgDispatch[n].cmd);
02311 g_rgDispatch[n].fn(aThread, args);
02312 aThread->SetCurrCommand(NULL);
02313 return (g_rgDispatch[n].fn != DoExit);
02314 }
02315 }
02316 }
02317 MSYS_Display(aThread, _T("Unrecognized command: %s\n"), aCommand);
02318 return true;
02319 }
02320
02321 static void InitInternal()
02322 {
02323
02324 int nInternal = 0, nExternal = 0;
02325 for (int n = 0; g_rgInternal[n].cmd; ++n) ++nInternal;
02326 for (int n = 0; MSYS_Commands[n].cmd; ++n) {
02327 const MSYS_DispatchEntry * pItem = std::find(
02328 &g_rgInternal[0], &g_rgInternal[nInternal], MSYS_Commands[n]);
02329 if (pItem->cmd != MSYS_HEADER && pItem != &g_rgInternal[nInternal]) {
02330 _ftprintf(stderr,
02331 _T("Command '%s' is shadowed by internal command of the same name\n"),
02332 pItem->cmd);
02333 assert(!"Command shadowed by internal command.");
02334 }
02335 ++nExternal;
02336 }
02337
02338
02339 g_rgDispatch = (MSYS_DispatchEntry*)
02340 calloc(sizeof(MSYS_DispatchEntry), nInternal+nExternal+1);
02341 if (!g_rgDispatch) {
02342 _ftprintf(stderr, _T("Out of memory.\n"));
02343 exit(1);
02344 }
02345
02346
02347 for (int n = 0; n < nInternal; ++n) {
02348 g_rgDispatch[n] = g_rgInternal[n];
02349 }
02350 for (int n = 0; n < nExternal; ++n) {
02351 g_rgDispatch[nInternal + n] = MSYS_Commands[n];
02352 }
02353 }
02354
02355 static bool
02356 FileExists(
02357 const TCHAR * aFilePath
02358 )
02359 {
02360 #ifdef _WIN32
02361 DWORD dwAttribs = GetFileAttributes(aFilePath);
02362 if (dwAttribs == INVALID_FILE_ATTRIBUTES) {
02363 return false;
02364 }
02365 if (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
02366 return false;
02367 }
02368 return true;
02369 #else
02370 struct stat st;
02371 if (-1 == stat(aFilePath, &st)) {
02372 return false;
02373 }
02374 if (st.st_mode & S_IFDIR) {
02375 return false;
02376 }
02377 return true;
02378 #endif
02379 }
02380
02381 #ifdef _WIN32
02382 void
02383 InputFromConsole(
02384 Buffer & aBuf
02385 )
02386 {
02387
02388
02389
02390 DWORD dwChars = 0;
02391 ReadConsole(GetStdHandle(STD_INPUT_HANDLE),
02392 aBuf.mBuf, (DWORD) aBuf.mBufSiz, &dwChars, NULL);
02393 aBuf.mBuf[dwChars] = L'\0';
02394 }
02395 #endif
02396
02397 void
02398 OutputToConsole(
02399 const TCHAR * aString
02400 )
02401 {
02402 AutoLock lock(g_lockOutputConsole);
02403
02404 #ifdef _WIN32
02405
02406
02407 DWORD dwWritten;
02408 const DWORD CHUNK = 10000;
02409 DWORD nLength = (DWORD) _tcslen(aString);
02410 for (DWORD nOffset = 0; nOffset < nLength; nOffset += CHUNK) {
02411 DWORD nOutputLen = nLength - nOffset > CHUNK ? CHUNK : nLength - nOffset;
02412 static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
02413 WriteConsole(hStdOut, aString + nOffset, nOutputLen, &dwWritten, NULL);
02414 }
02415 #else
02416 _ftprintf(stdout, "%s", aString);
02417 #endif
02418 }
02419
02420 #ifdef _WIN32
02421 extern void
02422 MSYS_DisplayWindowsError(
02423 MSYS_Thread * aThread,
02424 const TCHAR * aLocation,
02425 HRESULT aError
02426 )
02427 {
02428 DWORD dwFormatFlags =
02429 FORMAT_MESSAGE_IGNORE_INSERTS |
02430 FORMAT_MESSAGE_FROM_SYSTEM;
02431
02432 TCHAR szMessage[1000];
02433 DWORD dwResult = FormatMessage(
02434 dwFormatFlags,
02435 NULL,
02436 aError,
02437 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
02438 szMessage,
02439 sizeof(szMessage)/sizeof(szMessage[0]),
02440 NULL );
02441
02442 MSYS_Display(aThread, _T("%s failed with hr = 0x%08x, %s"),
02443 aLocation, aError,
02444 dwResult ? szMessage : _T("<failed to get error string>") );
02445 }
02446 #endif
02447
02448 const MSYS_DispatchEntry g_rgInternal[] = {
02449
02450 { MSYS_HEADER, NULL, false, _T("GENERAL"), NULL },
02451 { _T("?"), DoHelp, false, NULL, NULL },
02452 { _T("help"), DoHelp, false, _T("Display command help. Use \"help /?\" for more details."),
02453 _T("Usage: help [<command> | /internal | /all]\n")
02454 _T("\n")
02455 _T(" <command> Show detailed help for a specific command. By default only\n")
02456 _T(" summary help is shown for commands. Use \"help <command>\" or\n")
02457 _T(" \"<command> -?\" for full help for that command.\n")
02458 _T(" /internal Show help for internal commands. By default only external\n")
02459 _T(" commands are shown in the command list.\n")
02460 _T(" /all Show help for both internal and external commands.\n")
02461 _T("\n")
02462 _T("Note: the interpretation of an -? argument as a request for help can be disabled ")
02463 _T("so that it is passed through to the command by prefixing the command with the ")
02464 _T("'-' character.")
02465 },
02466
02467 { _T("x"), DoExit, false, NULL, NULL },
02468 { _T("exit"), DoExit, false, _T("Exit the application."),
02469 _T("By default the exit command will only exit the currently executing thread. ")
02470 _T("If the current thread is the main thread, it will wait for all other threads ")
02471 _T("to exit before returning. To force an immediate exit from all threads, use ")
02472 _T("the command \"exit now\". This may be called from any executing thread.\n")
02473 _T("\n")
02474 _T("Usage: exit [now]")
02475 },
02476 { _T("setopt"), DoSetOpt, false, _T("Set or display options."),
02477 _T("Usage: setopt [<option>] [<value>]\n")
02478 _T("\n")
02479 _T("If no option or value to set is supplied the current values will be displayed. ")
02480 _T("Use \"setopt <option> -?\" for help on available options.\n")
02481 _T("\n")
02482 _T("Usage: setopt {-save|-load} [<file>]\n")
02483 _T("\n")
02484 _T("Save or load options with an options file. If no file name is supplied ")
02485 _T("then \"options.txt\" will be used by default.")
02486 },
02487
02488
02489 { MSYS_HEADER, NULL, false, _T("FILE SYSTEM"), NULL },
02490 { _T("pwd"), DoPwd, false, _T("Display the current working directory."),
02491 NULL
02492 },
02493 { _T("cd"), DoChdir, false, _T("Change the current working directory."),
02494 _T("Usage: cd <path>")
02495 },
02496 { _T("!"), DoShell, false, NULL, NULL },
02497 { _T("shell"), DoShell, false, _T("Execute a shell command."),
02498 _T("If no command is supplied then a shell will be started. All characters are passed to the shell ")
02499 _T("unchanged (no quoted characters are unquoted).\n")
02500 _T("\n")
02501 _T("Usage: shell [<command> [<arguments>]]")
02502 },
02503
02504
02505 { MSYS_HEADER, NULL, false, _T("INPUT AND OUTPUT"), NULL },
02506 { _T("e"), DoEcho, true, NULL, NULL },
02507 { _T("echo"), DoEcho, true, _T("Echo all arguments."),
02508 _T("Each argument is echoed with all quoted characters (e.g. \\t \\n \\\" etc) converted. ")
02509 _T("Arguments are echoed immediately following each other, so to echo a full line of text, ")
02510 _T("start the line with a quotation mark. For example:\n")
02511 _T("\n")
02512 _T("echo \"This full line will be echoed.")
02513 },
02514 { _T("r"), DoRead, false, NULL, NULL },
02515 { _T("read"), DoRead, false, _T("Read and execute a script file."),
02516 _T("Usage: read <path>")
02517 },
02518 { _T("tee"), DoTee, false, _T("Copy all output to a file."),
02519 _T("When a tee has been created, all output that is sent to the screen is also written ")
02520 _T("to the output file. The tee file is created by overwriting any existing file by ")
02521 _T("default. Specify \"-append\" to force data to be appended to the tee file. ")
02522 _T("Stop output from being written to the tee file by omitting the file path.")
02523 _T("Shell command output cannot be redirected with a tee.\n")
02524 _T("\n")
02525 _T("Usage: tee [-append] [<path>]")
02526 },
02527
02528
02529 { MSYS_HEADER, NULL, false, _T("SCRIPTING"), NULL },
02530 { _T("timer"), DoTimer, false, _T("Start, stop and display timers."),
02531 _T("If no arguments are given, all timers are reported.\n")
02532 _T("\n")
02533 _T("Usage: timer [<command> <id>]\n")
02534 _T("\n")
02535 _T(" start Create a new timer running or restart an existing one.\n")
02536 _T(" stop Stop an existing timer from running. The timer is not deleted.\n")
02537 _T(" lap Add the current time as a lap. The run state is not affected.\n")
02538 _T(" reset Reset the current elapsed time and all lap details of the timer.\n")
02539 _T(" tell Display the current timer details.\n")
02540 _T(" tellraw Display the current timer details in raw format.\n")
02541 _T(" delete Delete an existing timer.")
02542 },
02543 { _T("sleep"), DoSleep, false, _T("Pause script execution for a period of time."),
02544 _T("Usage: sleep <milliseconds>")
02545 },
02546 { _T("proc"), DoProc, false, _T("Start, end or call a procedure."),
02547 _T("This may only be used when reading from a file. The procedure name must not be ")
02548 _T("specified for \"proc end\".\n")
02549 _T("\n")
02550 _T("Usage: proc {begin|end|call} [<name>]")
02551 },
02552 { _T("loop"), DoLoop, false, _T("Repetitively call a procedure."),
02553 _T("The number of times the procedure will be called depends on the type of ")
02554 _T("limit that is created. A procedure name always needs to be supplied. The ")
02555 _T("<limit> text depends on the <type> that is specified. When using \"until\", ")
02556 _T("the time is specified using the 24 hour clock. Times in the past are assumed ")
02557 _T("to be 24 hours in the future. When using \"for\", the maximum period that can ")
02558 _T("be specified is 7 days.\n")
02559 _T("\n")
02560 _T("Usage: loop <proc-name> <type> <limit>\n")
02561 _T("\n")
02562 _T(" count <iterations> Loop a fixed number of times.\n")
02563 _T(" until HH:MM[:SS] Loop until the specified time (24 hour time).\n")
02564 _T(" for [[HH:]MM:]SS Loop for the specified period of time.")
02565 },
02566 #ifndef MSYS_NOTHREADS
02567 { _T("threads"), DoThreads, false, _T("Manage execution threads."),
02568 _T("Threads must always execute on an input file. By default, a new thread will start ")
02569 _T("with console display set to \"off\". If threads are not supported by the application ")
02570 _T("then this command will return an error. If no name is supplied for a new thread then ")
02571 _T("a name will be generated from the thread ID (e.g. threads start \"\" file.txt).\n")
02572 _T("\n")
02573 _T("Usage: threads <command> [<name> [<path>]]\n")
02574 _T("\n")
02575 _T(" start <name> <path> Create a new thread with <name> using file <path>.\n")
02576 _T(" stop <name> Stop the thread with <name> immediately.\n")
02577 _T(" stopall Stop all threads immediately.\n")
02578 _T(" join <name> Join with the thread with <name>.\n")
02579 _T(" joinall Join with all threads.\n")
02580 _T(" list List all threads.")
02581 },
02582 #endif // MSYS_NOTHREADS
02583
02584
02585 { _T("_setopt_console"), OptConsole, false, _T("Enable or disable console display."),
02586 _T("The display of command output and the echoing of the command prompt can be ")
02587 _T("selectively disabled. For example, if input is from a file and is being tee'd ")
02588 _T("to an output file, all console output can be disabled so that only the tee file ")
02589 _T("receives the output. Note that disabling command prompt echo only affects the ")
02590 _T("echoing of command prompt during file input. Standard console input will always ")
02591 _T("display the command prompt. Shell output cannot be disabled (or tee'd).\n")
02592 _T("\n")
02593 _T("Usage: setopt console {all|prompt|output|none}\n")
02594 _T("\n")
02595 _T(" all Command prompt: echoed. Command output: echoed.\n")
02596 _T(" prompt Command prompt: echoed. Command output: none.\n")
02597 _T(" output Command prompt: none. Command output: echoed.\n")
02598 _T(" none Command prompt: none. Command output: none.\n")
02599 },
02600 { _T("_setopt_threads"), OptThreads, false, _T("Are threads enabled (read-only)."),
02601 NULL
02602 },
02603 { _T("_setopt_utf8"), OptUtf8, false, _T("Force file input to use UTF-8 encoding."),
02604 _T("Usage: setopt utf8 {true|false}")
02605 },
02606
02607 { NULL, NULL, false, NULL, NULL }
02608 };
02609
02610 extern void MSYS_ShowHelp(MSYS_Thread * thrd, MSYS_Handler fn)
02611 {
02612 MSYS_ThreadImpl * aThread = reinterpret_cast<MSYS_ThreadImpl*>(thrd);
02613
02614 bool bSaved = aThread->mStringReplacements;
02615 aThread->mStringReplacements = false;
02616
02617 std::tstring sCommands;
02618
02619 bool bDisplayInternal = false;
02620 const MSYS_DispatchEntry * rgDispatch = MSYS_Commands;
02621 if (fn == HelpAll) {
02622 rgDispatch = g_rgDispatch;
02623 fn = NULL;
02624 bDisplayInternal = true;
02625 }
02626 else if (fn == HelpInternal) {
02627 rgDispatch = g_rgInternal;
02628 fn = NULL;
02629 bDisplayInternal = true;
02630 }
02631 else if (fn == HelpExternal) {
02632 fn = NULL;
02633 }
02634
02635
02636 if (!fn) {
02637 bool bIsFirst = true;
02638
02639
02640 int nSwitchHeader = 0;
02641 if (!bDisplayInternal) {
02642 rgDispatch = g_rgInternal;
02643 nSwitchHeader = 2;
02644 }
02645
02646 for (int n = 0; rgDispatch[n].cmd; ++n) {
02647 if (rgDispatch[n].cmd[0] == _T('_')) continue;
02648 if (rgDispatch[n].cmd == MSYS_HEADER) {
02649 if (--nSwitchHeader == 0) {
02650 assert(!bDisplayInternal && rgDispatch == g_rgInternal);
02651 rgDispatch = MSYS_Commands;
02652 n = 0;
02653 continue;
02654 }
02655
02656 MSYS_Display(aThread, _T("%s%s\n"),
02657 bIsFirst ? _T("") : _T("\n"),
02658 rgDispatch[n].oneHelp);
02659 bIsFirst = false;
02660 continue;
02661 }
02662
02663 if (!sCommands.empty()) {
02664 sCommands += _T(", ");
02665 }
02666 sCommands += rgDispatch[n].cmd;
02667 if (!rgDispatch[n].oneHelp) continue;
02668
02669 MSYS_Display(aThread, _T("%-15s%s\n"),
02670 sCommands.c_str(), rgDispatch[n].oneHelp);
02671 sCommands.erase();
02672 bIsFirst = false;
02673 }
02674
02675 if (bDisplayInternal) {
02676 MSYS_Display(aThread, _T("%s"),
02677 _T("\n")
02678 _T("Special line prefixes to change default behaviour are:\n")
02679 _T(" ; Single line comment.\n")
02680 _T(" # Single line comment.\n")
02681 _T(" @ Suppress the echoing of the prompt and command.\n")
02682 _T(" $ Report the execution time of the command.\n")
02683 _T(" - Force off the parsing of special characters.\n")
02684 _T(" + Force on the parsing of special characters.\n")
02685 _T(" = Prevent \"-?\" or \"/?\" from being interpreted as a help request.\n")
02686 _T(" ! Execute the command as a shell command.\n")
02687 _T(" /* Start multiple line comment (must be the only text on a line).\n")
02688 _T(" */ End multiple line comment (must be the only text on a line).\n")
02689 _T("\n")
02690 _T("Special variables:\n")
02691 _T(" {{NOW}} Replaced with the current time and date\n")
02692 );
02693 if (g_bEnableThreads) {
02694 MSYS_Display(aThread, _T("%s"),
02695 _T(" {{TID}} Replaced with the current thread ID\n")
02696 _T(" {{TNAME}} Replaced with the current thread name\n")
02697 );
02698 }
02699 }
02700 }
02701 else {
02702
02703 for (int n = 0; g_rgDispatch[n].cmd; ++n) {
02704 if (g_rgDispatch[n].fn != fn) continue;
02705 if (!sCommands.empty()) {
02706 sCommands += _T(", ");
02707 }
02708 if (0 == _tcsnicmp(g_rgDispatch[n].cmd, OPTION_PREFIX, OPTION_PREFIX_LEN)) {
02709 sCommands += _T("setopt ");
02710 sCommands += (g_rgDispatch[n].cmd + OPTION_PREFIX_LEN);
02711 }
02712 else {
02713 sCommands += g_rgDispatch[n].cmd;
02714 }
02715 if (!g_rgDispatch[n].oneHelp) continue;
02716
02717 MSYS_Display(aThread, _T("Commands: %s\nSummary: %s\n"),
02718 sCommands.c_str(), g_rgDispatch[n].oneHelp);
02719 if (g_rgDispatch[n].longHelp) {
02720 MSYS_Display(aThread, _T("\n"));
02721 MSYS_DisplayWrapped(aThread, g_rgDispatch[n].longHelp);
02722 }
02723 break;
02724 }
02725 }
02726
02727 aThread->mStringReplacements = bSaved;
02728 }
02729