PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/paramcontainer/ParamContainer.cpp

https://github.com/dereklmc/BrainGrid
C++ | 1000 lines | 935 code | 16 blank | 49 comment | 140 complexity | dc317a10b628a3e9464fa5d02dcd53e2 MD5 | raw file
  1. #include <stdio.h>
  2. #include "ParamContainer.h"
  3. // Платформенно-зависимый инклюд для использования getcwd
  4. #ifdef _WIN32
  5. #include <direct.h>
  6. #else
  7. #include <unistd.h>
  8. #define stricmp strcasecmp
  9. #endif
  10. #include <vector>
  11. /*
  12. Функции преобразования путей к файлам
  13. */
  14. static void absToRelPath(std::string spath, std::string &tpath)
  15. {
  16. size_t i,s;
  17. int j, k;
  18. const char* div;
  19. if(!spath.length()) return;
  20. if(spath[0]=='/')
  21. {
  22. // Unix
  23. i=1;
  24. s=0;
  25. div = "/";
  26. } else
  27. if(spath[1]==':') // Windows
  28. {
  29. if(tolower(spath[0])!=tolower(tpath[0])) // Разные диски
  30. return;
  31. i=3;
  32. s=2;
  33. div = "\\";
  34. }
  35. while(1)
  36. {
  37. j=spath.find_first_of(div, i);
  38. k=tpath.find_first_of(div, i);
  39. if(j==-1)
  40. {
  41. tpath=tpath.substr(i);
  42. return;
  43. }
  44. if(j==k && ((div[0]=='\\' && !stricmp(spath.substr(i, j-i).c_str(), tpath.substr(i, k-i).c_str())) || (div[0]=='/' && spath.substr(i, j-i)==tpath.substr(i, k-i))))
  45. {
  46. i=j+1;
  47. continue;
  48. }
  49. if(spath.find_first_of(div, j+1) == std::string::npos)
  50. {
  51. tpath=".."+(div+tpath.substr(i));
  52. } else
  53. tpath=tpath.substr(s);
  54. return;
  55. }
  56. }
  57. static void relToAbsPath(std::string spath, std::string &tpath)
  58. {
  59. const char* div;
  60. int s;
  61. if(spath[0]=='/')
  62. {
  63. // Unix
  64. if(tpath[0]!='/') tpath=spath.substr(0, spath.find_last_of("/")+1)+tpath;
  65. div="/";
  66. s=0;
  67. } else
  68. if(spath[1]==':')
  69. {
  70. // Windows
  71. if(tpath[1]!=':')
  72. {
  73. if(tpath[0]!='\\')
  74. {
  75. tpath=spath.substr(0, spath.find_last_of("\\")+1)+tpath;
  76. } else
  77. {
  78. tpath=spath.substr(0, 2)+tpath;
  79. }
  80. }
  81. div="\\";
  82. s=2;
  83. }
  84. int i,j;
  85. if(div[0]=='\\')
  86. for(i=s;i<(int)tpath.length();i++)
  87. if(tpath[i]=='/') tpath[i]='\\';
  88. for(i=s;i<(int)tpath.length();)
  89. {
  90. if(tpath.substr(i, 4)==std::string(div)+".."+div)
  91. {
  92. if(i>s)
  93. j=(int)tpath.find_last_of(div, i-1);
  94. else
  95. j=s;
  96. tpath=tpath.substr(0, j)+tpath.substr(i+3);
  97. i=j;
  98. } else
  99. if(tpath.substr(i, 3)==std::string(div)+"."+div)
  100. {
  101. tpath=tpath.substr(0, i)+tpath.substr(i+2);
  102. }
  103. else
  104. if(tpath.substr(i, 2)==std::string(div)+div)
  105. {
  106. tpath=tpath.substr(0, i)+tpath.substr(i+1);
  107. }
  108. else i++;
  109. }
  110. }
  111. using namespace std;
  112. static const char* parammessages[]=
  113. {
  114. // Сообщения об ошибках
  115. "No error",
  116. "Parameter name length exceed",
  117. "Parameter name cannot contain symbols '=', '[', ']' or whitespaces",
  118. "Parameter already exists",
  119. "Abbreviation already exists",
  120. "Type already exists",
  121. "%s: invalid parameter",
  122. "Missing required parameter %s",
  123. "%s: parameter argument missing",
  124. "Syntax error",
  125. "Missing enclosing quotemark",
  126. "%s: invalid parameter type",
  127. "Missing enclosing bracket",
  128. "Invalid file signature",
  129. "%s: unable to open file",
  130. // Другие сообщения
  131. "required parameter"
  132. };
  133. /*
  134. ParamContainer::TList class implementation
  135. */
  136. ParamContainer::TList::TList():tlist(new std::map<std::string, ParamContainer>)
  137. {
  138. }
  139. ParamContainer::TList::~TList()
  140. {
  141. delete tlist;
  142. }
  143. ParamContainer::TList::TList(const TList &c)
  144. :tlist(new std::map<std::string, ParamContainer>(*c.tlist))
  145. {
  146. }
  147. ParamContainer::TList & ParamContainer::TList::operator =(const ParamContainer::TList &c)
  148. {
  149. if(&c==this) return *this;
  150. delete tlist;
  151. tlist=new std::map<std::string, ParamContainer>(*c.tlist);
  152. return *this;
  153. }
  154. ParamContainer & ParamContainer::TList::operator[](std::string key)
  155. {
  156. return (*tlist)[key];
  157. }
  158. /*
  159. Реализация класса ParamContainer
  160. */
  161. // Максимальная длина имени параметра
  162. const size_t ParamContainer::maxpnamelength = 25;
  163. // Набор сообщений по умолчанию
  164. const char** ParamContainer::messages = parammessages;
  165. ParamContainer::param ParamContainer::dumbparam=ParamContainer::param();
  166. /*
  167. Добавление нового валидного параметра в список
  168. */
  169. ParamContainer::errcode ParamContainer::addParam(
  170. std::string pname, char abbr, int type,
  171. std::string helptopic, std::string defvalue, std::string allowedtypes)
  172. {
  173. if(pname.length()>maxpnamelength) return errParameterNameLengthExceed;
  174. // Символы = [] не могут встречаться в имени параметра
  175. if(pname.find_first_of("= []\t")!= std::string::npos) return errParameterNameInvalid;
  176. if(plist.find(pname)!=plist.end()) return errDublicateParameter;
  177. if(abbr && alist.find(abbr)!=alist.end()) return errDublicateAbbreviation;
  178. param p;
  179. p.value=p.defvalue=defvalue;
  180. p.help=helptopic;
  181. p.pflag=type;
  182. p.abbr=abbr;
  183. p.wasset=false;
  184. p.allowedtypes=allowedtypes;
  185. plist[pname]=p;
  186. if(abbr) alist[abbr]=pname;
  187. vlist.push_back(pname);
  188. return errOk;
  189. }
  190. /*
  191. Добавление нового составного типа параметров
  192. */
  193. ParamContainer::errcode ParamContainer::addParamType(std::string tname, const ParamContainer &p)
  194. {
  195. if(tname.find_first_of("= []\t") != std::string::npos) return errParameterNameInvalid;
  196. if(tlist->find(tname)!=tlist->end()) return errDublicateType;
  197. tlist[tname]=p;
  198. tindex.push_back(tname);
  199. return errOk;
  200. }
  201. ParamContainer::errcode ParamContainer::delParam(std::string pname)
  202. {
  203. std::map<std::string, param>::iterator it=plist.find(pname);
  204. if(it!=plist.end()) return errInvalidParameter;
  205. plist.erase(it);
  206. return errOk;
  207. }
  208. /*
  209. Сброс значений параметров
  210. */
  211. void ParamContainer::reset(void)
  212. {
  213. // Итератор по списку параметров
  214. map<string, param>::iterator it;
  215. for(it=plist.begin(); it!=plist.end(); it++)
  216. {
  217. it->second.value=it->second.defvalue;
  218. it->second.wasset=false;
  219. if(it->second.p)
  220. {
  221. delete it->second.p;
  222. it->second.p=NULL;
  223. }
  224. }
  225. }
  226. ParamContainer::errcode ParamContainer::parseCommandLine(std::string cmdline)
  227. {
  228. lastcmdline=cmdline;
  229. errcode err=lexicalAnalysis(cmdline);
  230. if(err!=errOk) return err;
  231. return syntaxAndSemanticsAnalysis();
  232. }
  233. ParamContainer::errcode ParamContainer::parseCommandLine(int argc, char *argv[])
  234. {
  235. std::string s;
  236. for(int i=1; i<argc; i++)
  237. s=s+argv[i]+" ";
  238. return parseCommandLine(s);
  239. }
  240. // Лексический анализ командной строки или файла проекта
  241. ParamContainer::errcode ParamContainer::lexicalAnalysis(std::string s)
  242. {
  243. // Статус анализатора
  244. enum {
  245. normal, // Между параметрами
  246. oneminus, // После одного минуса
  247. value, // Внутри текстовой строчки
  248. quotedvalue, // Внутри строчки в кавычках
  249. } state=normal;
  250. // Тип лексической единицы
  251. cmdlineel::elflag flag;
  252. // Стартовая позиция значения текущей лексической единицы
  253. int startpos, pos=0;
  254. std::string valpart="";
  255. // Очистка таблицы лексической свёртки
  256. lexconv.clear();
  257. // Прогон по строке
  258. int i;
  259. for(i=0; i<(int)s.length(); i++)
  260. {
  261. switch(state)
  262. {
  263. case normal:
  264. if(strchr("\t ", s[i])) continue;
  265. if(s[i]=='"')
  266. {
  267. flag=cmdlineel::value;
  268. state=quotedvalue;
  269. startpos=i+1;
  270. pos=i;
  271. continue;
  272. }
  273. if(s[i]=='\n') // \n = пробел, минус, минус
  274. {
  275. flag=cmdlineel::param;
  276. state=value;
  277. startpos=i+1;
  278. pos=i+1;
  279. continue;
  280. }
  281. if(s[i]=='-')
  282. {
  283. state=oneminus;
  284. pos=i;
  285. continue;
  286. }
  287. if(s[i]=='[')
  288. {
  289. lexconv.push_back(cmdlineel(cmdlineel::leftbracket, i));
  290. continue;
  291. }
  292. if(s[i]==']')
  293. {
  294. lexconv.push_back(cmdlineel(cmdlineel::rightbracket, i));
  295. continue;
  296. }
  297. if(s[i]=='=')
  298. {
  299. lexconv.push_back(cmdlineel(cmdlineel::equal, i));
  300. continue;
  301. }
  302. flag=cmdlineel::value;
  303. state=value;
  304. pos=startpos=i--;
  305. continue;
  306. case value:
  307. if(strchr("\t []=\n", s[i]))
  308. {
  309. state=normal;
  310. valpart+=s.substr(startpos, i-startpos);
  311. i--;
  312. lexconv.push_back(cmdlineel(flag, pos, valpart));
  313. valpart="";
  314. continue;
  315. }
  316. if(s[i]=='"')
  317. {
  318. state=quotedvalue;
  319. valpart+=s.substr(startpos, i-startpos);
  320. startpos=i+1;
  321. continue;
  322. }
  323. continue;
  324. case oneminus:
  325. if(strchr("\t []=\n", s[i]))
  326. {
  327. errpos=i;
  328. return errInvalidSyntax;
  329. }
  330. if(s[i]=='-')
  331. {
  332. flag=cmdlineel::param;
  333. state=value;
  334. startpos=i+1;
  335. continue;
  336. }
  337. flag=cmdlineel::abbrparam;
  338. state=value;
  339. startpos=i--;
  340. continue;
  341. case quotedvalue:
  342. if(s[i]=='"')
  343. {
  344. state=value;
  345. valpart+=s.substr(startpos, i-startpos);
  346. startpos=i+1;
  347. continue;
  348. }
  349. continue;
  350. }
  351. }
  352. switch(state)
  353. {
  354. case quotedvalue:
  355. errpos=startpos;
  356. return errMissingEnclosingQuotemark;
  357. case oneminus:
  358. errpos=i;
  359. return errInvalidSyntax;
  360. case value:
  361. valpart+=s.substr(startpos, i-startpos);
  362. lexconv.push_back(cmdlineel(flag, pos, valpart));
  363. }
  364. lexconv.push_back(cmdlineel(cmdlineel::eol, (int)lastcmdline.length()));
  365. return errOk;
  366. }
  367. // Синтаксический и семантический анализ командной строки
  368. ParamContainer::errcode ParamContainer::syntaxAndSemanticsAnalysis(bool fromproject)
  369. {
  370. // Сброс списка параметров
  371. reset();
  372. int nonameidx=0;
  373. size_t j;
  374. errcode err;
  375. char tmp[20];
  376. for(int i=0; i<(int)lexconv.size(); i++)
  377. {
  378. switch(lexconv[i].flag)
  379. {
  380. case cmdlineel::value: // Обычный параметр (не с '-')
  381. for(;nonameidx<(int)vlist.size() && !(plist[vlist[nonameidx]].pflag & noname);nonameidx++);
  382. if(nonameidx>=(int)vlist.size())
  383. {
  384. errinfo=lexconv[i].val;
  385. errpos=lexconv[i].pos;
  386. return errInvalidParameter;
  387. }
  388. err=parseComplexType(i, vlist[nonameidx], lexconv[i].val);
  389. if(err!=errOk) return err;
  390. nonameidx++;
  391. break;
  392. case cmdlineel::param: // Длинный параметр (с '--')
  393. if(plist.find(lexconv[i].val)==plist.end()) // Неизвестный параметр
  394. {
  395. if(allowunknownparams)
  396. {
  397. if(lexconv[i+1].flag==cmdlineel::equal)
  398. {
  399. i++;
  400. if(lexconv[i+1].flag==cmdlineel::value) i++;
  401. }
  402. break;
  403. }
  404. errinfo="--"+lexconv[i].val;
  405. errpos=lexconv[i].pos;
  406. return errInvalidParameter;
  407. }
  408. if(!fromproject && (plist[lexconv[i].val].pflag & novalue)) // У параметра не должно быть значения
  409. {
  410. sprintf(tmp, "%04d0000", i);
  411. plist[lexconv[i].val].value=tmp;
  412. plist[lexconv[i].val].wasset=true;
  413. break;
  414. }
  415. if(i+1>=(int)lexconv.size() ||
  416. lexconv[i+1].flag!=cmdlineel::equal) // У параметра не стоит значение, хотя должно быть
  417. {
  418. errinfo="--"+lexconv[i].val;
  419. errpos=lexconv[i].pos;
  420. return errParameterArgumentMissing;
  421. }
  422. i++;
  423. if(i+1>=(int)lexconv.size() ||
  424. lexconv[i+1].flag!=cmdlineel::value) // У параметра пустое значение
  425. {
  426. err=parseComplexType(i, lexconv[i-1].val, "");
  427. } else
  428. {
  429. // Иначе присваиваем параметру значение
  430. i++;
  431. err=parseComplexType(i, lexconv[i-2].val, lexconv[i].val);
  432. }
  433. if(err!=errOk) return err;
  434. break;
  435. case cmdlineel::abbrparam: // Короткий параметр или цепочка параметров
  436. for(j=0; j < lexconv[i].val.length(); j++)
  437. {
  438. char c=lexconv[i].val[j];
  439. // Неизвестный параметр
  440. if(alist.find(c)==alist.end())
  441. {
  442. if(allowunknownparams) break;
  443. sprintf(tmp, "-%c", c);
  444. errinfo=string(tmp);
  445. errpos=lexconv[i].pos+j+1;
  446. return errInvalidParameter;
  447. }
  448. string name=alist[c];
  449. plist[name].wasset=true;
  450. if(plist[name].pflag & novalue)
  451. {
  452. // У параметра не должно быть значения:
  453. // присваиваем идентификатор и переходим к следующей букве в цепочке
  454. sprintf(tmp, "%04d%04d", i, (int)j);
  455. plist[name].value=tmp;
  456. continue;
  457. }
  458. // Значение написано раздельно с параметром
  459. if(j+1==lexconv[i].val.length())
  460. {
  461. if(lexconv[i+1].flag!=cmdlineel::value) // У параметра не стоит значение, хотя должно быть
  462. {
  463. errinfo="--";errinfo[1]=c;
  464. errpos=lexconv[i].pos+j+1;
  465. return errParameterArgumentMissing;
  466. }
  467. i++;
  468. err=parseComplexType(i, name, lexconv[i].val);
  469. } else
  470. // Значение написано слитно с параметром
  471. err=parseComplexType(i, name, lexconv[i].val.substr(j+1));
  472. if(err!=errOk) return err;
  473. break;
  474. }
  475. break;
  476. case cmdlineel::eol:
  477. break;
  478. default:
  479. errpos=lexconv[i].pos;
  480. return errInvalidSyntax;
  481. }
  482. }
  483. // Проверка, все ли необходимые параметры были присвоены
  484. map<string, param>::iterator it;
  485. for(it=plist.begin(); it!=plist.end(); it++)
  486. {
  487. if((it->second.pflag & required) && !it->second.wasset)
  488. {
  489. if(it->second.pflag & noname)
  490. errinfo=it->first;
  491. else
  492. errinfo="--"+it->first;
  493. errpos=lexconv[lexconv.size()-1].pos;
  494. return errRequiredParameterMissing;
  495. }
  496. if(it->second.allowedtypes!="" && !it->second.wasset)
  497. {
  498. int i=0;
  499. err=parseComplexType(i, it->first, it->second.value);
  500. if(err!=errOk) return err;
  501. }
  502. }
  503. return errOk;
  504. }
  505. std::string ParamContainer::getErrCmdLine(int &pos, int maxlength) const
  506. {
  507. if((int)lastcmdline.length()<=maxlength)
  508. {
  509. pos=errpos;
  510. return lastcmdline;
  511. }
  512. if(errpos<maxlength*2/3)
  513. {
  514. pos=errpos;
  515. return lastcmdline.substr(0, maxlength-3)+"...";
  516. }
  517. if((int)lastcmdline.length()-errpos<maxlength*2/3)
  518. {
  519. pos=maxlength+errpos-(int)lastcmdline.length();
  520. return "..."+lastcmdline.substr(lastcmdline.length()-maxlength+3);
  521. }
  522. pos=maxlength/2;
  523. return "..."+lastcmdline.substr(errpos-maxlength/2+3, maxlength-6)+"...";
  524. }
  525. // Разбор составного типа
  526. ParamContainer::errcode ParamContainer::parseComplexType(int &i, std::string name, std::string value)
  527. {
  528. plist[name].wasset=true;
  529. if(plist[name].allowedtypes=="")
  530. {
  531. // Не составной тип
  532. plist[name].value=value;
  533. return errOk;
  534. }
  535. int pos=i+2, end=i+2, numbrackets=1;
  536. if(i+1<(int)lexconv.size() && lexconv[i+1].flag==cmdlineel::leftbracket)
  537. {
  538. for(i+=2; numbrackets; i++)
  539. {
  540. if((size_t)i==lexconv.size())
  541. {
  542. errpos=lexconv[pos-1].pos;
  543. return errMissingEnclosingBracket;
  544. }
  545. if(lexconv[i].flag==cmdlineel::leftbracket) numbrackets++;
  546. if(lexconv[i].flag==cmdlineel::rightbracket) numbrackets--;
  547. }
  548. end=--i;
  549. }
  550. // Недопустимый тип
  551. string s="|"+plist[name].allowedtypes+"|";
  552. if(s.find("|"+value+"|") == std::string::npos)
  553. {
  554. errinfo=value;
  555. errpos=lexconv[pos-2].pos;
  556. return errInvalidType;
  557. }
  558. // Несуществующий тип (как же он оказался допустимым?..)
  559. if(tlist->find(value)==tlist->end())
  560. {
  561. errinfo=value;
  562. errpos=lexconv[pos-2].pos;
  563. return errInvalidType;
  564. }
  565. plist[name].value=value;
  566. plist[name].p=new ParamContainer(tlist[value]);
  567. int j;
  568. for(j=pos; j<end; j++)
  569. plist[name].p->lexconv.push_back(lexconv[j]);
  570. plist[name].p->lexconv.push_back(cmdlineel(cmdlineel::eol, lexconv[pos==end?j-1:j].pos));
  571. errcode err=plist[name].p->syntaxAndSemanticsAnalysis();
  572. if(err!=errOk)
  573. {
  574. errinfo=plist[name].p->errinfo;
  575. errpos=plist[name].p->errpos;
  576. }
  577. return err;
  578. }
  579. // Восстановить командную строку
  580. std::string ParamContainer::getCmdLine()
  581. {
  582. int minnovalue=-1, curmin=-1;
  583. std::string s;
  584. map<string, param>::iterator it, it2;
  585. while(1)
  586. {
  587. for(it=plist.begin(); it!=plist.end(); it++)
  588. {
  589. if((it->second.pflag & novalue) && it->second.wasset)
  590. {
  591. int val=atoi(it->second.value.c_str());
  592. if(val>minnovalue && (curmin==minnovalue || val<curmin))
  593. {
  594. curmin=val;
  595. it2=it;
  596. }
  597. }
  598. }
  599. if(curmin==minnovalue) break;
  600. minnovalue=curmin;
  601. s+="--"+it2->first+" ";
  602. }
  603. for(int i=0;i<(int)vlist.size();i++)
  604. {
  605. if(!(plist[vlist[i]].pflag & novalue) && plist[vlist[i]].wasset)
  606. {
  607. if(!(plist[vlist[i]].pflag & noname))
  608. s+="--"+vlist[i]+"=";
  609. if(plist[vlist[i]].p) s+=plist[vlist[i]].value+"["+plist[vlist[i]].p->getCmdLine()+"] ";
  610. else s+="\""+plist[vlist[i]].value+"\" ";
  611. }
  612. }
  613. s=s.substr(0, s.length()-1);
  614. return s;
  615. }
  616. ParamContainer::errcode ParamContainer::unsetParam(std::string pname)
  617. {
  618. map<string, param>::iterator it=plist.find(pname);
  619. if(it==plist.end()) return errInvalidParameter;
  620. it->second.value=it->second.defvalue;
  621. it->second.wasset=false;
  622. if(it->second.p)
  623. {
  624. delete it->second.p;
  625. it->second.p=NULL;
  626. }
  627. return errOk;
  628. }
  629. /*
  630. Вывод текста справки по параметру
  631. */
  632. void ParamContainer::printHelpTopic(FILE *f, std::string topic, int indent, int width, bool linebreak)
  633. {
  634. if(!width)
  635. {
  636. fprintf(f, "%s%s", topic.c_str(), linebreak?"\n":"");
  637. return;
  638. }
  639. width-=indent;
  640. string s;
  641. char formatstr[10];
  642. sprintf(formatstr, "%%s\n%%%ds", indent);
  643. for(;;)
  644. {
  645. if((int)topic.length()<width)
  646. {
  647. fprintf(f, "%s%s", topic.c_str(), linebreak?"\n":"");
  648. return;
  649. }
  650. s=topic.substr(0, width);
  651. int pos = (int)s.rfind(" ");
  652. if(pos==-1) pos=width;
  653. s=s.substr(0, pos);
  654. topic=topic.substr(pos+1);
  655. fprintf(f, formatstr, s.c_str(), "");
  656. }
  657. }
  658. std::string ParamContainer::paramhelp(std::string param) const
  659. {
  660. if(plist.find(param)->second.allowedtypes=="") return "<"+param+">";
  661. string s, t=plist.find(param)->second.allowedtypes;
  662. int pos=0, npos;
  663. while(1)
  664. {
  665. npos=(int)t.find_first_of("|", pos);
  666. if(npos==-1) break;
  667. if(tlist->find(t.substr(pos, npos-pos))->second.plist.size())
  668. s=s+t.substr(pos, npos-pos)+"[...]|";
  669. else
  670. s=s+t.substr(pos, npos-pos)+"|";
  671. pos=npos+1;
  672. }
  673. s=s+t.substr(pos);
  674. if(tlist->find(t.substr(pos))->second.plist.size())
  675. s=s+"[...]";
  676. return s;
  677. }
  678. std::string ParamContainer::typelist(std::string param) const
  679. {
  680. string help=plist.find(param)->second.help;
  681. int tpos=(int)help.find("@types@");
  682. if(tpos==-1) return help;
  683. string s, t=plist.find(param)->second.allowedtypes;
  684. int pos=0, npos;
  685. while(1)
  686. {
  687. npos=(int)t.find_first_of("|", pos);
  688. if(npos==-1) break;
  689. s=s+t.substr(pos, npos-pos)+", ";
  690. pos=npos+1;
  691. }
  692. s=s+t.substr(pos);
  693. return help.substr(0, tpos)+s+help.substr(tpos+7);
  694. }
  695. /*
  696. Вывод справки о параметрах в текстовый поток
  697. */
  698. void ParamContainer::dumpHelp(FILE *f, bool showparamlist, unsigned int width, std::string subtopic) const
  699. {
  700. // Ширина левой колонки при выводе справки
  701. unsigned int helpindent=0;
  702. unsigned int l;
  703. // Итератор по списку параметров
  704. int i;
  705. char s[maxpnamelength*2+10];
  706. unsigned int pos;
  707. // Вывод списка параметров
  708. // Отступ на 8 позиций для красоты
  709. if(showparamlist)
  710. {
  711. printHelpTopic(f, helptopic.c_str(), 0, width, false);
  712. // fprintf(f, "%s", helptopic.c_str());
  713. pos = (int)(helptopic.length()-helptopic.find_last_of("\n"));
  714. if(subtopic!="" && plist.size())
  715. {
  716. fprintf(f, "\n %s[", subtopic.c_str());
  717. pos=9+(int)subtopic.length();
  718. }
  719. }
  720. if(!plist.size())
  721. {
  722. showparamlist=false;
  723. fprintf(f, "\n");
  724. }
  725. // Сперва - безымянные параметры
  726. for(i=0; i<(int)vlist.size(); i++)
  727. {
  728. if(plist.find(vlist[i])->second.pflag & noname)
  729. {
  730. sprintf(s, "%s ", paramhelp(vlist[i]).c_str());
  731. if(showparamlist)
  732. {
  733. pos += (int)strlen(s);
  734. if(width && (pos>width) )
  735. {
  736. pos = 8+(int)strlen(s);
  737. fprintf(f, "\n%8s", "");
  738. }
  739. fprintf(f, "%s", s);
  740. }
  741. l = (int)vlist[i].length()+7;
  742. if(l>helpindent) helpindent=l;
  743. }
  744. }
  745. // Потом - обязательные параметры
  746. for(i=0; i<(int)vlist.size(); i++)
  747. {
  748. if((plist.find(vlist[i])->second.pflag & required) && !(plist.find(vlist[i])->second.pflag & noname))
  749. {
  750. sprintf(s, "-%c %s ", plist.find(vlist[i])->second.abbr, paramhelp(vlist[i]).c_str());
  751. if(showparamlist)
  752. {
  753. pos += (int)strlen(s);
  754. if(width && (pos>width) )
  755. {
  756. pos = 8+(int)strlen(s);
  757. fprintf(f, "\n%8s", "");
  758. }
  759. fprintf(f, "%s", s);
  760. }
  761. l = (int)vlist[i].length()*2+7;
  762. if(l>helpindent) helpindent=l;
  763. }
  764. }
  765. // Затем все остальные
  766. for(i=0; i<(int)vlist.size(); i++)
  767. {
  768. if(plist.find(vlist[i])->second.pflag & (noname|required)) continue;
  769. if(plist.find(vlist[i])->second.pflag & novalue)
  770. {
  771. sprintf(s, "-%c ", plist.find(vlist[i])->second.abbr);
  772. l=(int)vlist[i].length();
  773. } else
  774. {
  775. sprintf(s, "-%c %s ", plist.find(vlist[i])->second.abbr, paramhelp(vlist[i]).c_str());
  776. l=(int)vlist[i].length()*2+7;
  777. }
  778. if(showparamlist)
  779. {
  780. pos += (int)strlen(s);
  781. if(width && (pos>width) )
  782. {
  783. pos = 8+(int)strlen(s);
  784. fprintf(f, "\n%8s", "");
  785. }
  786. fprintf(f, "%s", s);
  787. }
  788. if(l>helpindent) helpindent = l;
  789. }
  790. if(showparamlist)
  791. {
  792. if(subtopic!="") fprintf(f, "\b]\n");
  793. else fprintf(f, "\n\n");
  794. }
  795. // Объяснение каждого параметра
  796. char formatstr[10];
  797. sprintf(formatstr, "%%-%ds", helpindent);
  798. if(width<(helpindent+20)) width = helpindent+20;
  799. for(i=0; i<(int)vlist.size(); i++)
  800. {
  801. if(plist.find(vlist[i])->second.pflag & noname)
  802. {
  803. fprintf(f, " %c ", plist.find(vlist[i])->second.pflag & required?'*':' ');
  804. sprintf(s, "<%s> ", vlist[i].c_str());
  805. fprintf(f, formatstr, s);
  806. printHelpTopic(f, typelist(vlist[i]), helpindent+7, width);
  807. }
  808. }
  809. for(i=0; i<(int)vlist.size(); i++)
  810. {
  811. if((plist.find(vlist[i])->second.pflag & required) && !(plist.find(vlist[i])->second.pflag & noname))
  812. {
  813. if(plist.find(vlist[i])->second.abbr)
  814. fprintf(f, " * -%c, ", plist.find(vlist[i])->second.abbr);
  815. else
  816. fprintf(f, " * ");
  817. sprintf(s, "--%s=<%s> ", vlist[i].c_str(), vlist[i].c_str());
  818. fprintf(f, formatstr, s);
  819. printHelpTopic(f, typelist(vlist[i]), helpindent+7, width);
  820. }
  821. }
  822. for(i=0; i<(int)vlist.size(); i++)
  823. {
  824. if(plist.find(vlist[i])->second.pflag & (noname|required)) continue;
  825. if(plist.find(vlist[i])->second.abbr)
  826. fprintf(f, " -%c, ", plist.find(vlist[i])->second.abbr);
  827. else
  828. fprintf(f, " ");
  829. if(plist.find(vlist[i])->second.pflag & novalue)
  830. sprintf(s, "--%s ", vlist[i].c_str());
  831. else
  832. sprintf(s, "--%s=<%s> ", vlist[i].c_str(), vlist[i].c_str());
  833. fprintf(f, formatstr, s);
  834. printHelpTopic(f, typelist(vlist[i]), helpindent+7, width);
  835. }
  836. fprintf(f, "\n");
  837. if(dumpsubparamhelp)
  838. {
  839. for(i=0; i<(int)tindex.size(); i++)
  840. tlist->find(tindex[i])->second.dumpHelp(f, true, width, tlist->find(tindex[i])->first);
  841. }
  842. if(subtopic=="")
  843. {
  844. fprintf(f, " * = %s\n", messages[msgRequiredParameter]);
  845. }
  846. }
  847. /*
  848. Вывод списка параметров с присвоенными им значениями
  849. */
  850. void ParamContainer::saveParams(FILE *f, std::string signature) const
  851. {
  852. if(signature!="") fprintf(f, "[%s]\n", signature.c_str());
  853. // Итератор по списку параметров
  854. map<string, param>::const_iterator it;
  855. for(it=plist.begin(); it!=plist.end(); it++)
  856. {
  857. if(it->second.pflag & novalue)
  858. {
  859. if(it->second.wasset) fprintf(f, "%s=%s\n", it->first.c_str(), it->second.value.c_str());
  860. }
  861. else
  862. {
  863. if(it->second.p)
  864. fprintf(f, "%s=%s[%s]\n", it->first.c_str(), it->second.value.c_str(), it->second.p->getCmdLine().c_str());
  865. else
  866. if(it->second.wasset)
  867. fprintf(f, "%s=\"%s\"\n", it->first.c_str(), it->second.value.c_str());
  868. }
  869. }
  870. }
  871. /*
  872. Сохранение файла проекта
  873. */
  874. ParamContainer::errcode ParamContainer::saveParams(std::string filename, std::string signature)
  875. {
  876. lastcmdline="";
  877. errpos=0;
  878. errinfo=filename;
  879. FILE *f=fopen(filename.c_str(), "wt");
  880. if(!f) return errUnableToOpenFile;
  881. char cwd[1024];
  882. getcwd(cwd, 1024);
  883. strcat(cwd, cwd[0]=='/'?"/":"\\");
  884. relToAbsPath(cwd, filename);
  885. convertFilePaths(cwd, filename);
  886. saveParams(f, signature);
  887. fclose(f);
  888. return errOk;
  889. }
  890. /*
  891. Загрузить параметры из файла проекта с преобразованием имён файлов в абсолютные
  892. */
  893. ParamContainer::errcode ParamContainer::loadParams(std::string filename, std::string signature)
  894. {
  895. lastcmdline="";
  896. errpos=0;
  897. errinfo=filename;
  898. FILE *f=fopen(filename.c_str(), "rt");
  899. if(!f) return errUnableToOpenFile;
  900. errcode err=loadParams(f, signature);
  901. fclose(f);
  902. if(err!=errOk) return err;
  903. char cwd[1024];
  904. getcwd(cwd, 1024);
  905. strcat(cwd, cwd[0]=='/'?"/":"\\");
  906. relToAbsPath(cwd, filename);
  907. convertFilePaths(filename, "");
  908. return errOk;
  909. }
  910. /*
  911. Загрузить параметры из файла проекта
  912. */
  913. ParamContainer::errcode ParamContainer::loadParams(FILE *f, std::string signature)
  914. {
  915. int i=0;
  916. char str[1024];
  917. string s;
  918. if(signature!="")
  919. {
  920. fgets(str, 1024, f);
  921. if(!*str) return errInvalidSignature;
  922. if(str[strlen(str)-1]=='\n') str[strlen(str)-1]=0;
  923. s=str;
  924. if(s!="["+signature+"]") return errInvalidSignature;
  925. }
  926. // Сброс списка параметров
  927. reset();
  928. s="";
  929. while(!feof(f))
  930. {
  931. // Читаем по строчке из файла
  932. i++;
  933. *str=0;
  934. fgets(str, 1024, f);
  935. if(str[strlen(str)-1]=='\n') str[strlen(str)-1]=0;
  936. if(!str[0]) continue;
  937. s=s+"--"+str+" ";
  938. if(s.length()>1048576)
  939. return errInvalidSyntax;
  940. }
  941. lastcmdline=s;
  942. errcode err=lexicalAnalysis(s);
  943. if(err!=errOk) return err;
  944. return syntaxAndSemanticsAnalysis(true);
  945. }
  946. void ParamContainer::convertFilePaths(std::string dirfrom, std::string dirto)
  947. {
  948. // Итератор по списку параметров
  949. map<string, param>::iterator it;
  950. for(it=plist.begin(); it!=plist.end(); it++)
  951. {
  952. if((it->second.pflag & filename) && it->second.value!="")
  953. {
  954. // Параметр - непустое имя файла - преобразуем
  955. relToAbsPath(dirfrom, it->second.value);
  956. absToRelPath(dirto, it->second.value);
  957. }
  958. if(it->second.p)
  959. {
  960. // Проделать то же самое для вложенных ParamContainer
  961. it->second.p->convertFilePaths(dirfrom, dirto);
  962. }
  963. }
  964. }