diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 30066f70..87aba7d8 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1825,6 +1825,37 @@ jobs: fi echo "[compiled-bridge] interpreter-only builtins work in a compiled analyzer" + - name: Compiled analyzer runs zone mode + shell: bash + run: | + set -eo pipefail + # A compiled analyzer used to ignore zone and endzone lines and run + # every pass on the whole document. run_analyzer() now hands the + # zone passes to Arun::zones. Build the zone-mode fixture compiled + # and compare it with the interpreted run, -DEV trees included. + FIX=.github/workflows/tests/zone-mode + LIBS="$(cygpath -m "$GITHUB_WORKSPACE")/compile-libs" + rm -rf "$FIX/logs" "$FIX"/input/*_log "$FIX/output" "$FIX/run" "$FIX/build" + mkdir -p "$FIX/logs" + ./bin/Release/nlp.exe -ANA "$FIX" -WORK ./ -DEV "$FIX/input/text.txt" | tr -d '\r' | grep -E '^(zone|summary)' > zone-interpreted.txt + (cd "$FIX/input/text.txt_log" && ls *.tree) > zone-trees.txt + rm -rf "$FIX/input/text.txt_log" + cp .github/workflows/tests/compiled-bridge/CMakeLists.txt .github/workflows/tests/compiled-bridge/StdAfx.h "$FIX/" + ./bin/Release/nlp.exe -COMPILEANA -ANA "$FIX" -WORK ./ "$FIX/input/text.txt" + grep -q "Arun::zones" "$FIX/run/analyzer.cpp" + cmake -S "$FIX" -B "$FIX/build" -A x64 -DENGINE_LIBS="$LIBS" + cmake --build "$FIX/build" --config Release + rm -rf "$FIX/input/text.txt_log" "$FIX/output" + ./bin/Release/nlp.exe -COMPILED -ANA "$FIX" -WORK ./ -DEV "$FIX/input/text.txt" | tr -d '\r' | grep -E '^(zone|summary)' > zone-compiled.txt + if ! diff zone-interpreted.txt zone-compiled.txt; then + echo "FAIL(compiled zone mode): compiled output differs from interpreted"; exit 1 + fi + if ! diff zone-trees.txt <(cd "$FIX/input/text.txt_log" && ls *.tree); then + echo "FAIL(compiled zone mode): compiled -DEV trees differ from interpreted"; exit 1 + fi + rm -f "$FIX/CMakeLists.txt" "$FIX/StdAfx.h" zone-interpreted.txt zone-compiled.txt zone-trees.txt + echo "[compiled zone mode] a compiled analyzer runs the zone passes once per zone" + - name: Upload nlpengine-compile-libs.zip uses: actions/upload-artifact@v4 with: diff --git a/README.md b/README.md index cc771f4f..b0ce1535 100644 --- a/README.md +++ b/README.md @@ -176,8 +176,8 @@ runs once per node of that type: `_TEXTZONE` nodes into sections); that stops the run with an error. - With `-DEV`, the first three zones write their pass trees as `z001-ana040.tree` and so on. -- A compiled analyzer ignores the `zone` and `endzone` lines and runs every - pass on the whole document. +- Compiled analyzers run zones the same way (since 4.2.49; before that they + ignored the `zone` and `endzone` lines). On *Pride and Prejudice* (738 KB) with parse-en-us zoned by `_TEXTZONE`, peak memory went from 473 MB to 153 MB and time from 275 to 283 seconds; diff --git a/include/Api/lite/Arun.h b/include/Api/lite/Arun.h index 2a2be584..347be1bc 100644 --- a/include/Api/lite/Arun.h +++ b/include/Api/lite/Arun.h @@ -202,6 +202,8 @@ class LITE_API Arun static bool python(Parse *, int, const _TCHAR *); // 07/14/26. compiled python pass. static bool lines(Parse *, int); static void runPostcode(Parse *parse, const PASS *pass); // #392. + static bool zones(Parse *parse, const _TCHAR *ztype, // Zone mode. + bool (*zonefn)(Parse *), bool (*endfn)(Parse *)); static bool patExecute( Parse *, const PASS *, diff --git a/include/Api/lite/parse.h b/include/Api/lite/parse.h index 06460c85..f9d7c144 100644 --- a/include/Api/lite/parse.h +++ b/include/Api/lite/parse.h @@ -22,6 +22,7 @@ All rights reserved. #include #include #include +#include typedef void TREE; // 07/07/03 AM. typedef void LTREE; // 07/07/03 AM. @@ -231,6 +232,14 @@ class LITE_API Parse int runPass(Delt *seq, long &num); // Zone mode: the passes after a "zone _type" line run once per _type node. int executeZones(Delt *first, _TCHAR *ztype); + // The same for a compiled analyzer: zonefn runs the zone passes, endfn + // the passes after endzone (or 0). Called by Arun::zones. + bool runZones(const _TCHAR *ztype, bool (*zonefn)(Parse *), + bool (*endfn)(Parse *)); + int zoneLoop(const _TCHAR *ztype, const std::function &zone, + const std::function &wrap, const std::function &whole); + // Dump-file prefix for a pass in zone mode; may turn dumps off. + _TCHAR *zonePrefix(_TCHAR *prefix, bool &flogfiles, bool &ftimepass); long getZoneNum() {return zoneNum_;} bool getZoneFirst() {return zoneFirst_;} bool getZoneLast() {return zoneLast_;} @@ -533,10 +542,7 @@ class LITE_API Parse bool ftimepass, clock_t &s_time ); -#ifndef LINUX - inline -#endif - void iniPass( // 05/20/00 AM. + void iniPass( // Not inline: Arun.cpp calls it too. // 05/20/00 AM. int num, // Current pass number. _TCHAR *prefix, // Prefix for naming dump files. bool flogfiles, @@ -688,6 +694,8 @@ class LITE_API Parse unsigned long runTicks_ = 0; // Calls since the clock was last read. bool runStopped_ = false; + _TCHAR zprefix_[32] = {0}; // zonePrefix()'s "z001-ana". + #ifndef STABLE_ public: static int getCount(); diff --git a/lite/Arun.cpp b/lite/Arun.cpp index 07fbcef6..2e813cc9 100644 --- a/lite/Arun.cpp +++ b/lite/Arun.cpp @@ -119,6 +119,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, // 05/21/00 AM. /*DU*/fout,sout,s_time,pretname); @@ -153,6 +154,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, /*DU*/fout,sout,s_time,pretname); @@ -188,6 +190,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, /*DU*/fout,sout,s_time,pretname); @@ -224,6 +227,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, /*DU*/fout,sout,s_time,pretname); @@ -261,6 +265,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, /*DU*/fout,sout,s_time,pretname); @@ -294,6 +299,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,0,salgo, // 05/21/00 AM. /*DU*/fout,sout,s_time,pretname); @@ -306,6 +312,19 @@ return true; } +/******************************************** +* FN: ZONES +* SUBJ: Zone mode in a compiled analyzer. +* NOTE: run_analyzer() ends with this after a zone line; see Seqn::gen. +********************************************/ + +bool Arun::zones(Parse *parse, const _TCHAR *ztype, + bool (*zonefn)(Parse *), bool (*endfn)(Parse *)) +{ +return parse->runZones(ztype, zonefn, endfn); +} + + /******************************************** * FN: RUNPOSTCODE * SUBJ: Run a compiled pass's @POSTCODE region, after its rules (#392). @@ -355,6 +374,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,sfile,salgo, // 05/20/00 AM. /*DU*/fout,sout,s_time,pretname); @@ -415,6 +435,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = parse->eana_->getFlogfiles(); bool ftimepass = parse->eana_->getFtimepass(); +prefix = parse->zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. parse->iniPass(num,prefix,flogfiles,ftimepass,true,sfile,salgo, // 05/20/00 AM. /*DU*/fout,sout,s_time,pretname); diff --git a/lite/Aseq.cpp b/lite/Aseq.cpp index cc3333f4..01555086 100644 --- a/lite/Aseq.cpp +++ b/lite/Aseq.cpp @@ -818,7 +818,7 @@ Ifile *rf; // 12/20/01 AM. for (selt = (Delt *)Aseq::firstPass(nlp); selt; selt = selt->Right()) { seqn = selt->getData(); - seqn->setPassnum(++currseqpass); // In case... // 05/20/01 AM. + seqn->setPassnum(seqn->isZoneMarker() ? 0 : ++currseqpass); // 05/20/01 AM. if (seqn->getActive()) // Pass that will be executed. { if (!(seqn->getAlgo()) // 12/20/01 AM. @@ -865,7 +865,7 @@ int num = 0; // Current pass number. num = 0; for (seq = Aseq::firstPass(nlp); seq; seq = Aseq::nextPass(seq)) - Aseq::setPassnum(seq, ++num); + Aseq::setPassnum(seq, ((Delt *) seq)->getData()->isZoneMarker() ? 0 : ++num); } diff --git a/lite/ana.cpp b/lite/ana.cpp index d3f99517..e7b32478 100644 --- a/lite/ana.cpp +++ b/lite/ana.cpp @@ -511,7 +511,7 @@ Ifile *rf = 0; // 12/20/01 AM. for (selt = slist->getFirst(); selt; selt = selt->Right()) { seqn = selt->getData(); - seqn->setPassnum(++currseqpass); // 05/10/00 AM. + seqn->setPassnum(seqn->isZoneMarker() ? 0 : ++currseqpass); // 05/10/00 AM. if (seqn->getActive()) { if (!internPass(seqn, rfa, erfa)) // Asynchrony. // 12/11/99 AM. @@ -599,9 +599,6 @@ else if ( !strcmp_i(s_algo, _T("zone"))) // "zone _type": the passes below errOut(&gerrStr,false); return false; } - if (gen) - std::_t_cerr << _T("[Warning: a compiled analyzer ignores the zone line; ") - << _T("its later passes run on the whole document.]") << std::endl; return true; } else if ( !strcmp_i(s_algo, _T("endzone"))) // The passes below run once, diff --git a/lite/parse.cpp b/lite/parse.cpp index a848d8c4..b3cae3d7 100644 --- a/lite/parse.cpp +++ b/lite/parse.cpp @@ -567,8 +567,8 @@ return RUN_OK; * An endzone line ends the zone passes: the passes after it run once, * after the last zone, on the stubs with the whole KB, for work that * belongs to the whole document (eg, dumping the KB). -* With -DEV, the first ZONE_DUMPS zones write z001-ana075.tree and -* so on; later zones write no pass trees or pass times. +* With -DEV, only the first ZONE_DUMPS zones write pass trees +* (see zonePrefix). ********************************************/ #define ZONE_DUMPS 3 @@ -582,7 +582,7 @@ struct ZoneLink { // One level of the path from the root to a zone. }; // The ztype nodes under node, in document order. Zones do not nest. -static void findZones(Node *node, _TCHAR *ztype, std::vector *> &zones) +static void findZones(Node *node, const _TCHAR *ztype, std::vector *> &zones) { for (; node; node = node->pRight) { @@ -645,15 +645,8 @@ for (size_t i = 0; i < links.size(); ++i) int Parse::executeZones(Delt *first, _TCHAR *ztype) { -Tree *tree = (Tree *) getTree(); -Node *root = tree ? tree->getRoot() : 0; -std::vector *> zones; -if (root) - findZones(root->pDown, ztype, zones); - long base = currpass_; // The last pass before the zone line. long num = base; -int result = RUN_OK; Delt *seq; // An endzone line ends the zone passes; the passes after it run once, @@ -664,17 +657,76 @@ for (seq = first; seq && !endz; seq = seq->Right()) && !strcmp_i(str(seq->getData()->getAlgoname()), _T("endzone"))) endz = seq; +int result = zoneLoop(ztype, + [&]() { + int r = RUN_OK; + num = base; + for (seq = first; seq && seq != endz && r == RUN_OK; seq = seq->Right()) + r = runPass(seq, num); + return r; + }, + [&]() { + int r = RUN_OK; + for (seq = endz ? endz->Right() : 0; seq && r == RUN_OK; seq = seq->Right()) + r = runPass(seq, num); + return r; + }, + [&]() { // No zones: the zone passes run on the whole document. + int r = RUN_OK; + for (seq = first; seq && r == RUN_OK; seq = seq->Right()) + r = runPass(seq, num); // stepExecute skips the endzone line. + return r; + }); +currpass_ = num; +return result; +} + +/******************************************** +* FN: RUNZONES +* SUBJ: Zone mode for a compiled analyzer. +* NOTE: The generated run_analyzer() puts the passes between the zone and +* endzone lines in zonefn, and the passes after endzone in endfn +* (0 without an endzone line); see Seqn::gen. +********************************************/ + +bool Parse::runZones(const _TCHAR *ztype, bool (*zonefn)(Parse *), + bool (*endfn)(Parse *)) +{ +auto run = [this](bool (*fn)(Parse *)) { + return (fn && !fn(this)) ? RUN_ABORT : RUN_OK; + }; +return zoneLoop(ztype, + [&]() { return run(zonefn); }, + [&]() { return run(endfn); }, + [&]() { int r = run(zonefn); return r == RUN_OK ? run(endfn) : r; } + ) != RUN_ABORT; +} + +/******************************************** +* FN: ZONELOOP +* SUBJ: Run zone() once per ztype zone, then wrap() once. +* NOTE: Shared by executeZones() and runZones(). With no ztype zones, +* runs whole() on the whole document instead. +********************************************/ + +int Parse::zoneLoop(const _TCHAR *ztype, const std::function &zone, + const std::function &wrap, const std::function &whole) +{ +Tree *tree = (Tree *) getTree(); +Node *root = tree ? tree->getRoot() : 0; +std::vector *> zones; +if (root) + findZones(root->pDown, ztype, zones); + if (zones.empty()) { std::_t_cerr << _T("[zone: no ") << ztype << _T(" nodes, so the passes after the zone line ran on the whole document.]") << std::endl; - for (seq = first; seq && result == RUN_OK; seq = seq->Right()) - result = runPass(seq, num); // stepExecute skips the endzone line. - currpass_ = num; - return result; + return whole(); } +int result = RUN_OK; std::vector links; long nzones = (long) zones.size(); for (long z = 0; z < nzones && result == RUN_OK; ++z) @@ -684,9 +736,7 @@ for (long z = 0; z < nzones && result == RUN_OK; ++z) zoneLast_ = (z == nzones - 1); hidePath(root, zones[z], links); - num = base; - for (seq = first; seq && seq != endz && result == RUN_OK; seq = seq->Right()) - result = runPass(seq, num); + result = zone(); if (!pathIntact(root, links)) { @@ -711,13 +761,30 @@ zoneNum_ = 0; zoneFirst_ = zoneLast_ = false; // The wrap-up passes, once, on the whole document. -if (endz && result == RUN_OK) +if (result == RUN_OK) + result = wrap(); +return result; +} + +/******************************************** +* FN: ZONEPREFIX +* SUBJ: Name a pass's dump files in zone mode. +* NOTE: With -DEV, the first ZONE_DUMPS zones write z001-ana075.tree and +* so on; 2,500 zones of a 130-pass analyzer would otherwise write +* 325,000 trees. Later zones write no pass trees or pass times. +* Called before iniPass() by interpreted and compiled passes. +********************************************/ + +_TCHAR *Parse::zonePrefix(_TCHAR *prefix, bool &flogfiles, bool &ftimepass) +{ +if (zoneNum_ > ZONE_DUMPS) + flogfiles = ftimepass = false; +else if (zoneNum_ > 0) { - for (seq = endz->Right(); seq && result == RUN_OK; seq = seq->Right()) - result = runPass(seq, num); + _stprintf(zprefix_, _T("z%03ld-%s"), zoneNum_ % 1000, prefix); + prefix = zprefix_; } -currpass_ = num; -return result; +return prefix; } void Parse::execute() @@ -1085,18 +1152,7 @@ _TCHAR *prefix = _T("ana"); // Prefix for naming files. bool flogfiles = eana_->getFlogfiles(); // 10/13/99 AM. bool ftimepass = eana_->getFtimepass(); // 10/13/99 AM. -// In zone mode, only the first ZONE_DUMPS zones write pass trees and times; -// 2,500 zones of a 130-pass analyzer would otherwise write 325,000 trees. -_TCHAR zprefix[] = _T("z000-ana"); -if (zoneNum_ > ZONE_DUMPS) - flogfiles = ftimepass = false; -else if (zoneNum_ > 0) - { - zprefix[1] = (_TCHAR) ('0' + zoneNum_ / 100 % 10); - zprefix[2] = (_TCHAR) ('0' + zoneNum_ / 10 % 10); - zprefix[3] = (_TCHAR) ('0' + zoneNum_ % 10); - prefix = zprefix; - } +prefix = zonePrefix(prefix, flogfiles, ftimepass); // Zone mode. iniPass(num,prefix,flogfiles,ftimepass,pass->getActive(),sfile,salgo, // 05/20/00 AM. /*DU*/fout,sout,s_time,pretname); @@ -1151,9 +1207,7 @@ return true; * SUBJ: Initial work for one pass of a parse. ********************************************/ -#ifndef LINUX -inline -#endif +// Not inline: compiled passes (Arun.cpp) call it too. void Parse::iniPass( int num, // Current pass number. _TCHAR *prefix, // Prefix for naming dump files. diff --git a/lite/seqn.cpp b/lite/seqn.cpp index f9ed7f86..5476d6ab 100644 --- a/lite/seqn.cpp +++ b/lite/seqn.cpp @@ -191,6 +191,14 @@ void Seqn::setRulesfile(Ifile *x) {rulesfile_ = x;} void Seqn::setActive(bool x) {active_ = x;} void Seqn::setDisplaytree(bool x) {displaytree_ = x;} void Seqn::setPassnum(int x) {passnum_ = x;} // 09/23/99 AM. + +// zone and endzone lines are markers, not passes: they take no pass number, +// as in the run (Parse::runPass) and the sequence view. +bool Seqn::isZoneMarker() +{ +return algo_name_ && (!strcmp_i(algo_name_, _T("zone")) + || !strcmp_i(algo_name_, _T("endzone"))); +} void Seqn::setRecid(int x) {recid_ = x;} void Seqn::setComment(_TCHAR *x) { @@ -588,18 +596,58 @@ Gen::nl(fcode); // 04/04/03 AM. *fcode << _T("if (!Arun::init_analyzer(parse)) return false;"); Gen::nl(fcode); // 04/04/03 AM. -int count = 0; Delt *dseq; Seqn *seq; _TCHAR *name; bool active = false; // 08/04/02 AM. +int count = 0; // Pass number, as Ana numbers passes: zone lines take none. +// Zone mode: run_analyzer() runs the passes before the zone line, then hands +// zonepasses() and endzonepasses() to Arun::zones (see Parse::executeZones). +int part = 0; // 0 before the zone line, 1 zone passes, 2 after endzone. for (dseq = seqs->getFirst(); dseq; dseq = dseq->Right()) { // Should have enum type for algorithm type. seq = dseq->getData(); name = seq->getAlgoname(); // 06/04/00 AM. active = seq->getActive(); // 08/04/02 AM. - ++count; + if (!seq->isZoneMarker()) + ++count; + if (active && part == 0 && !_tcscmp(_T("zone"),name)) + { + bool endz = false; + for (Delt *d = dseq->Right(); d && !endz; d = d->Right()) + endz = d->getData()->getActive() + && !_tcscmp(_T("endzone"), d->getData()->getAlgoname()); + *fcode << _T("return Arun::zones(parse,_T(\"") << seq->getRulesfilename() + << _T("\"),zonepasses,") << (endz ? _T("endzonepasses") : _T("0")) + << _T(");"); + Gen::nl(fcode); + *fcode << _T("}"); + Gen::eol(fcode); + *fhead << _T("bool zonepasses(Parse *);"); + Gen::nl(fhead); + *fcode << _T("bool zonepasses(Parse *parse)"); + Gen::nl(fcode); + *fcode << _T("{"); + Gen::nl(fcode); + part = 1; + continue; + } + if (active && part == 1 && !_tcscmp(_T("endzone"),name)) + { + *fcode << _T("return true;"); + Gen::nl(fcode); + *fcode << _T("}"); + Gen::eol(fcode); + *fhead << _T("bool endzonepasses(Parse *);"); + Gen::nl(fhead); + *fcode << _T("bool endzonepasses(Parse *parse)"); + Gen::nl(fcode); + *fcode << _T("{"); + Gen::nl(fcode); + part = 2; + continue; + } if (!_tcscmp(_T("nlp"), name)) { if (active) // 08/04 02 AM. @@ -614,7 +662,7 @@ for (dseq = seqs->getFirst(); dseq; dseq = dseq->Right()) } else if (!_tcscmp(_T("stub"),name) || !_tcscmp(_T("end"),name) // 06/04/00 AM. || !_tcscmp(_T("folder"),name) // 08/24/08 AM. - || !_tcscmp(_T("zone"),name) // Interpreted runs only, for now. + || !_tcscmp(_T("zone"),name) || !_tcscmp(_T("endzone"),name)) ; // Ignore these. // 06/04/00 AM. else diff --git a/lite/seqn.h b/lite/seqn.h index 50077027..bd03f341 100644 --- a/lite/seqn.h +++ b/lite/seqn.h @@ -88,6 +88,7 @@ class Seqn void setAlgoname(_TCHAR *); // 01/08/99 AM. void setComment(_TCHAR *); // 01/08/99 AM. void setPassnum(int); // 09/23/99 AM. + bool isZoneMarker(); // A zone or endzone line: not a pass, no number. void setRecid(int); // 05/31/00 AM. // General diff --git a/nlp/main.cpp b/nlp/main.cpp index 02fe886e..49f71a28 100644 --- a/nlp/main.cpp +++ b/nlp/main.cpp @@ -15,7 +15,7 @@ All rights reserved. #include "lite/nlp_engine.h" #include "version.h" -#define NLP_ENGINE_VERSION "4.2.48" +#define NLP_ENGINE_VERSION "4.2.49" bool cmdReadArgs(int, _TCHAR *argv[], _TCHAR *&, _TCHAR *&, _TCHAR *&, _TCHAR *&, bool &, bool &, bool &, bool &, bool &, bool &, int &, bool &, bool &, _TCHAR *&, double &); void cmdHelpargs(_TCHAR *);