dclib-ui.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. /***************************************************************************
  2. * *
  3. * _____ ____ *
  4. * | __ \ / __ \ _ _ _____ *
  5. * | | \ \ / / \_\ | | | | _ \ *
  6. * | | \ \| | | | | | |_| | *
  7. * | | | || | | | | | ___/ *
  8. * | | / /| | __ | | | | _ \ *
  9. * | |__/ / \ \__/ / | |___| | |_| | *
  10. * |_____/ \____/ |_____|_|_____/ *
  11. * *
  12. * Wiimms source code library *
  13. * *
  14. ***************************************************************************
  15. * *
  16. * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> *
  17. * *
  18. ***************************************************************************
  19. * *
  20. * This library is free software; you can redistribute it and/or modify *
  21. * it under the terms of the GNU General Public License as published by *
  22. * the Free Software Foundation; either version 2 of the License, or *
  23. * (at your option) any later version. *
  24. * *
  25. * This library is distributed in the hope that it will be useful, *
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  28. * GNU General Public License for more details. *
  29. * *
  30. * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
  31. * *
  32. ***************************************************************************/
  33. #include <ctype.h>
  34. #include <getopt.h>
  35. #include <string.h>
  36. #include "dclib-basics.h"
  37. #include "dclib-ui.h"
  38. #include "dclib-debug.h"
  39. //
  40. ///////////////////////////////////////////////////////////////////////////////
  41. /////////////// register options ///////////////
  42. ///////////////////////////////////////////////////////////////////////////////
  43. enumError RegisterOptionByIndex
  44. (
  45. const InfoUI_t * iu, // valid pointer
  46. int opt_index, // index of option (OPT_*)
  47. int level, // the level of registration
  48. bool is_env // true: register environment pre setting
  49. )
  50. {
  51. ASSERT(iu);
  52. DASSERT(iu->opt_info);
  53. DASSERT(iu->opt_used);
  54. DASSERT(iu->opt_index);
  55. if ( level <= 0 )
  56. return ERR_OK;
  57. if ( opt_index >= 0
  58. && opt_index < iu->n_opt_total
  59. && opt_index < UIOPT_INDEX_SIZE )
  60. {
  61. TRACE("OPT: RegisterOptionByIndex(,%02x,%d,%d) opt_index=%02x,%s\n",
  62. opt_index, level, is_env, opt_index,
  63. iu->opt_info[opt_index].long_name );
  64. u8 * obj = iu->opt_used + opt_index;
  65. u32 count = *obj;
  66. if (is_env)
  67. {
  68. if ( count < 0x7f )
  69. {
  70. count += level;
  71. *obj = count < 0x7f ? count : 0x7f;
  72. }
  73. }
  74. else
  75. {
  76. if ( count < 0x80 )
  77. count = 0x80;
  78. count += level;
  79. *obj = count < 0xff ? count : 0xff;
  80. }
  81. return ERR_OK;
  82. }
  83. PRINT("OPT: RegisterOptionbyIndex(,%02x/%02x/%02x,%d,%d) => WARNING\n",
  84. opt_index, iu->n_opt_total, UIOPT_INDEX_SIZE, level, is_env );
  85. return ERR_WARNING;
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////
  88. enumError RegisterOptionByName
  89. (
  90. const InfoUI_t * iu, // valid pointer
  91. int opt_name, // short name of GO_* value of option
  92. int level, // the level of registration
  93. bool is_env // true: register environment pre setting
  94. )
  95. {
  96. ASSERT(iu);
  97. DASSERT(iu->opt_index);
  98. if ( opt_name >= 0 && opt_name < UIOPT_INDEX_SIZE )
  99. return RegisterOptionByIndex(iu,iu->opt_index[opt_name],level,is_env);
  100. PRINT("OPT: RegisterOptionbyName(,%02x,%d,%d) => WARNING\n",
  101. opt_name, level, is_env );
  102. return ERR_WARNING;
  103. }
  104. ///////////////////////////////////////////////////////////////////////////////
  105. enumError VerifySpecificOptions ( const InfoUI_t * iu, const KeywordTab_t * cmd )
  106. {
  107. ASSERT(iu);
  108. ASSERT(cmd);
  109. ASSERT( cmd->id > 0 && cmd->id < iu->n_cmd );
  110. const InfoCommand_t * ic = iu->cmd_info + cmd->id;
  111. ASSERT(ic->opt_allowed);
  112. u8 * allow = ic->opt_allowed;
  113. u8 * active = iu->opt_used;
  114. TRACE("ALLOWED SPECIFIC OPTIONS:\n");
  115. TRACE_HEXDUMP(10,0,0,-20,allow,iu->n_opt_specific);
  116. TRACE("ACTIVE SPECIFIC OPTIONS:\n");
  117. TRACE_HEXDUMP(10,0,0,-20,active,iu->n_opt_specific);
  118. enumError err = ERR_OK;
  119. int idx;
  120. for ( idx = 0; idx < iu->n_opt_specific; idx++, allow++, active++ )
  121. {
  122. if (!*allow)
  123. {
  124. if ( *active & 0x80 )
  125. {
  126. const InfoOption_t * io = iu->opt_info + idx;
  127. if (io->short_name)
  128. ERROR0(ERR_SEMANTIC,"Command '%s' doesn't allow the option --%s (-%c).\n",
  129. cmd->name1, io->long_name, io->short_name );
  130. else
  131. ERROR0(ERR_SEMANTIC,"Command '%s' doesn't allow the option --%s.\n",
  132. cmd->name1, io->long_name );
  133. err = ERR_SEMANTIC;
  134. }
  135. else
  136. *active = 0; // clear environ settings
  137. }
  138. }
  139. return err;
  140. }
  141. ///////////////////////////////////////////////////////////////////////////////
  142. int GetOptionCount ( const InfoUI_t * iu, int option )
  143. {
  144. ASSERT(iu);
  145. ASSERT(iu->opt_info);
  146. ASSERT(iu->opt_used);
  147. if ( option > 0 && option < iu->n_opt_total )
  148. {
  149. TRACE("GetOptionCount(,%02x) option=%s => %d\n",
  150. option, iu->opt_info[option].long_name,
  151. iu->opt_used[option] & UIOPT_USED_MASK );
  152. return iu->opt_used[option] & UIOPT_USED_MASK;
  153. }
  154. TRACE("GetOptionCount(,%02x) => INVALID [-1]\n",option);
  155. return -1;
  156. }
  157. ///////////////////////////////////////////////////////////////////////////////
  158. void DumpUsedOptions ( const InfoUI_t * iu, FILE * f, int indent )
  159. {
  160. ASSERT(iu);
  161. ASSERT(iu->opt_info);
  162. ASSERT(iu->opt_used);
  163. TRACE("OptionUsed[]:\n");
  164. TRACE_HEXDUMP16(9,0,iu->opt_used,iu->n_opt_total+1);
  165. if (!f)
  166. return;
  167. indent = NormalizeIndent(indent);
  168. int i;
  169. for ( i = 0; i < iu->n_opt_total; i++ )
  170. {
  171. const u8 opt = iu->opt_used[i];
  172. if (opt)
  173. fprintf(f,"%*s%s %s %2u* [%02x,%s]\n",
  174. indent, "",
  175. i <= iu->n_opt_specific ? "CMD" : "GLB",
  176. opt & 0x80 ? "PRM" : "ENV",
  177. opt & UIOPT_USED_MASK,
  178. i, iu->opt_info[i].long_name );
  179. }
  180. };
  181. ///////////////////////////////////////////////////////////////////////////////
  182. void WarnDepractedOptions ( const struct InfoUI_t * iu )
  183. {
  184. DASSERT(iu);
  185. uint i, count = 0;
  186. char buf[PATH_MAX];
  187. char *dest = buf;
  188. for ( i = 0; i < iu->n_opt_total; i++ )
  189. {
  190. if ( iu->opt_used[i] )
  191. {
  192. const InfoOption_t *io = iu->opt_info + i;
  193. if ( io->deprecated )
  194. {
  195. if (io->long_name)
  196. {
  197. count++;
  198. dest = snprintfE(dest,buf+sizeof(buf)," --%s",io->long_name);
  199. }
  200. else if (io->short_name)
  201. {
  202. count++;
  203. dest = snprintfE(dest,buf+sizeof(buf)," -%c",io->short_name);
  204. }
  205. }
  206. }
  207. }
  208. if (count)
  209. ERROR0(ERR_WARNING,
  210. "Attention: Deprecated option%s:%s\n",
  211. count == 1 ? "" : "s", buf );
  212. }
  213. ///////////////////////////////////////////////////////////////////////////////
  214. enumError CheckEnvOptions ( ccp varname, check_opt_func func )
  215. {
  216. TRACE("CheckEnvOptions(%s,%p)\n",varname,func);
  217. ccp env = getenv(varname);
  218. if ( !env || !*env )
  219. return ERR_OK;
  220. PRINT("env[%s] = %s\n",varname,env);
  221. #if 1 // [[new]] with ArgManager_t
  222. ArgManager_t am = {0};
  223. AppendArgManager(&am,ProgInfo.progname,0,false);
  224. ScanQuotedArgManager(&am,env,true);
  225. #ifdef DEBUG
  226. for ( int i = 0; i < am.argc; i++ )
  227. printf(" [%02u] |%s|\n",i,am.argv[i]);
  228. #endif
  229. enumError stat = func(am.argc,am.argv,true);
  230. #else // [[old]]
  231. const int envlen = strlen(env);
  232. char * buf = MALLOC(envlen+1);
  233. char * dest = buf;
  234. int argc = 1; // argv[0] = ProgInfo.progname
  235. ccp src = env;
  236. while (*src)
  237. {
  238. while ( *src > 0 && *src <= ' ' ) // skip blanks & control
  239. src++;
  240. if (!*src)
  241. break;
  242. argc++;
  243. while ( *(u8*)src > ' ' )
  244. *dest++ = *src++;
  245. *dest++ = 0;
  246. DASSERT( dest <= buf+envlen+1 );
  247. }
  248. TRACE("argc = %d\n",argc);
  249. char ** argv = MALLOC((argc+1)*sizeof(*argv));
  250. argv[0] = (char*)ProgInfo.progname;
  251. argv[argc] = 0;
  252. dest = buf;
  253. int i;
  254. for ( i = 1; i < argc; i++ )
  255. {
  256. TRACE("argv[%d] = %s\n",i,dest);
  257. argv[i] = dest;
  258. while (*dest)
  259. dest++;
  260. dest++;
  261. DASSERT( dest <= buf+envlen+1 );
  262. }
  263. enumError stat = func(argc,argv,true);
  264. #endif
  265. // don't free() because of possible pointers into argv[]
  266. if (stat)
  267. fprintf(stderr,
  268. "Error while scanning the environment variable '%s'\n",varname);
  269. return stat;
  270. }
  271. //
  272. ///////////////////////////////////////////////////////////////////////////////
  273. /////////////// text helpers ///////////////
  274. ///////////////////////////////////////////////////////////////////////////////
  275. void DumpText
  276. (
  277. FILE * f, // output file, if NULL: 'pbuf' must be set
  278. char * pbuf, // output buffer, ignored if 'f' not NULL
  279. char * pbuf_end, // end of output buffer
  280. ccp text, // source text
  281. int text_len, // >=0: length of text, <0: use strlen(text)
  282. bool is_makedoc, // true: write makedoc format
  283. ccp end // write this text at the end of all
  284. )
  285. {
  286. ASSERT(f||pbuf);
  287. ccp tie = is_makedoc ? " \\" : "";
  288. char buf[100], *buf_end = buf + 70, *dest = buf;
  289. bool set_apos = false;
  290. bool brace_active = false;
  291. if (!text)
  292. text = "";
  293. if ( text_len < 0 )
  294. text_len = strlen(text) ;
  295. ccp text_end = text + text_len;
  296. while ( text < text_end )
  297. {
  298. char * dest_break = 0;
  299. ccp text_break = 0;
  300. bool eol = false;
  301. while ( !eol && text < text_end && dest < buf_end )
  302. {
  303. if ( *text >= 1 && *text <= 3 )
  304. {
  305. // \1 : Text only for built in help
  306. // \2 : Text only for ui.def
  307. // \3 : Text for all (default)
  308. const bool skip = *text == ( is_makedoc ? 1 : 2 );
  309. //PRINT("CH=%d, SKIP=%d\n",*text,skip);
  310. text++;
  311. if (skip)
  312. while ( text < text_end && (u8)*text > 3 )
  313. text++;
  314. continue;
  315. }
  316. switch (*text)
  317. {
  318. case 0:
  319. // ignore
  320. break;
  321. case ' ':
  322. if ( dest > buf )
  323. {
  324. dest_break = dest;
  325. text_break = text;
  326. }
  327. *dest++ = *text++;
  328. break;
  329. case '\n':
  330. eol = true;
  331. *dest++ = '\\';
  332. *dest++ = 'n';
  333. text++;
  334. dest_break = 0;
  335. text_break = 0;
  336. break;
  337. case '\\':
  338. case '"':
  339. *dest++ = '\\';
  340. *dest++ = *text++;
  341. break;
  342. case '{':
  343. if (is_makedoc)
  344. *dest++ = *text++;
  345. else if ( *++text == '{' )
  346. *dest++ = *text++;
  347. else
  348. {
  349. brace_active = true;
  350. ccp ptr = text;
  351. while ( ptr < text_end && *ptr > ' ' && *ptr != '}' )
  352. ptr++;
  353. set_apos = *ptr != '}';
  354. if (set_apos)
  355. *dest++ = '\'';
  356. }
  357. break;
  358. case '}':
  359. if ( is_makedoc || !brace_active )
  360. *dest++ = *text++;
  361. else if ( *++text == '}' )
  362. *dest++ = *text++;
  363. else
  364. {
  365. brace_active = false;
  366. if (set_apos)
  367. {
  368. set_apos = false;
  369. *dest++ = '\'';
  370. }
  371. }
  372. break;
  373. case '@':
  374. case '$':
  375. if (!is_makedoc)
  376. {
  377. if ( text[1] == *text )
  378. *dest++ = *text++;
  379. text++;
  380. break;
  381. }
  382. // fall through
  383. default:
  384. *dest++ = *text++;
  385. }
  386. DASSERT( dest < buf_end + 10 );
  387. }
  388. if ( dest >= buf_end && dest_break )
  389. {
  390. dest = dest_break;
  391. text = text_break;
  392. }
  393. if ( dest > buf && text < text_end )
  394. {
  395. *dest = 0;
  396. if (f)
  397. fprintf(f,"\t\"%s\"%s\n",buf,tie);
  398. else
  399. pbuf = snprintfE(pbuf,pbuf_end,"\t\"%s\"%s\n",buf,tie);
  400. dest = buf;
  401. }
  402. }
  403. *dest = 0;
  404. if (f)
  405. fprintf(f,"\t\"%s\"%s",buf,end);
  406. else
  407. snprintf(pbuf,pbuf_end-pbuf,"\t\"%s\"%s",buf,end);
  408. }
  409. //
  410. ///////////////////////////////////////////////////////////////////////////////
  411. /////////////// print options ///////////////
  412. ///////////////////////////////////////////////////////////////////////////////
  413. static void print_single_option
  414. (
  415. FILE * f, // valid output stream
  416. int indent, // normalized indent of output
  417. const InfoOption_t * io, // option to print
  418. uint opt_fw, // field width for options names
  419. uint disp_fw // total field width of displayed output
  420. )
  421. {
  422. DASSERT(f);
  423. DASSERT(io);
  424. if ( !io->short_name && !io->long_name )
  425. {
  426. fputc('\n',f);
  427. return;
  428. }
  429. ccp col_reset, col_param;
  430. if (IsFileColorized(f))
  431. {
  432. fputs(GetTextMode(1,TTM_COL_OPTION),f);
  433. col_param = io->param ? GetTextMode(1,TTM_COL_PARAM) : "";
  434. col_reset = TermTextModeReset;
  435. }
  436. else
  437. col_reset = col_param = "";
  438. ccp iop = io->param;
  439. ccp sep = !iop ? ""
  440. : !io->optional_parm ? " "
  441. : iop[0] == '=' || iop[0] == '[' && iop[0] == '=' ? ""
  442. : "=";
  443. int len;
  444. if (!io->short_name)
  445. len = fprintf(f,"%*s --%s%s%s%s ",
  446. indent, "",
  447. io->long_name,
  448. sep,
  449. col_param, iop ? iop : "" );
  450. else if (!io->long_name)
  451. len = fprintf(f,"%*s -%c%s%s%s ",
  452. indent, "",
  453. io->short_name,
  454. iop ? " " : "",
  455. col_param, iop ? iop : "" );
  456. else
  457. len = fprintf(f,"%*s -%c --%s%s%s%s ",
  458. indent, "",
  459. io->short_name,
  460. io->long_name,
  461. sep,
  462. col_param, iop ? iop : "" );
  463. len -= strlen(col_param);
  464. if ( len > opt_fw )
  465. {
  466. fprintf(f,"%s\n",col_reset);
  467. PutLines(f,opt_fw,disp_fw,0,0,io->help,0);
  468. }
  469. else
  470. {
  471. fputs(col_reset,f);
  472. PutLines(f,opt_fw,disp_fw,len,0,io->help,0);
  473. }
  474. }
  475. ///////////////////////////////////////////////////////////////////////////////
  476. static inline bool is_opt_selected
  477. (
  478. const InfoUI_t * iu, // valid pointer
  479. const InfoOption_t * io, // active option
  480. bool use_filter // true: user filter
  481. )
  482. {
  483. DASSERT(io);
  484. return use_filter
  485. ? ( iu->opt_used[io->id] & 0x80 ) != 0
  486. : !io->hidden;
  487. }
  488. ///////////////////////////////////////////////////////////////////////////////
  489. static void print_help_options
  490. (
  491. const InfoUI_t * iu, // valid pointer
  492. FILE * f, // valid output stream
  493. bool filter_opt, // true: filter options by 'iu->opt_used'
  494. int indent, // indent of output
  495. uint beg_index, // index of first option to print
  496. uint end_index, // index of last option to print +1
  497. ccp title // NULL or title text
  498. )
  499. {
  500. DASSERT(iu);
  501. if ( end_index > iu->n_opt_total )
  502. end_index = iu->n_opt_total;
  503. if ( beg_index >= end_index || !f )
  504. return;
  505. if (title)
  506. {
  507. const ColorMode_t colmode = GetFileColorized(f);
  508. fprintf(f,"%*s%s%s:%s\n\n",
  509. indent,"",
  510. GetTextMode(colmode,TTM_COL_HEADING), title,
  511. GetTextMode(colmode,TTM_RESET) );
  512. }
  513. // const int fw = GetTermWidth(80,40) - 1;
  514. const int fw = GetGoodTermWidth(0,false) - 1;
  515. const InfoOption_t *io_beg = iu->opt_info + beg_index;
  516. const InfoOption_t *io_end = iu->opt_info + end_index;
  517. const InfoOption_t *io;
  518. // calculate the base option field width
  519. int base_opt_fw = 0;
  520. for ( io = io_beg; io < io_end; io++ )
  521. {
  522. if ( io->long_name && is_opt_selected(iu,io,filter_opt) )
  523. {
  524. int len = strlen(io->long_name);
  525. if (io->param)
  526. len += 1 + strlen(io->param);
  527. if ( base_opt_fw < len )
  528. base_opt_fw = len;
  529. }
  530. }
  531. const int max_fw = 2 + (fw+5)/8;
  532. if ( base_opt_fw > max_fw )
  533. base_opt_fw = max_fw;
  534. // calculate the option field width
  535. int opt_fw = 0;
  536. for ( io = io_beg; io < io_end; io++ )
  537. {
  538. if ( io->long_name && is_opt_selected(iu,io,filter_opt) )
  539. {
  540. int len = strlen(io->long_name);
  541. if (io->param)
  542. len += 1 + strlen(io->param);
  543. if ( len <= base_opt_fw && opt_fw < len )
  544. opt_fw = len;
  545. }
  546. }
  547. opt_fw += indent + 9;
  548. for ( io = io_beg; io < io_end; io++ )
  549. {
  550. if ( is_opt_selected(iu,io,filter_opt) )
  551. {
  552. if (io->separator)
  553. fputc('\n',f);
  554. print_single_option(f,indent,io,opt_fw,fw);
  555. }
  556. }
  557. fputc('\n',f);
  558. }
  559. ///////////////////////////////////////////////////////////////////////////////
  560. void PrintHelpOptions
  561. (
  562. const InfoUI_t * iu, // valid pointer
  563. FILE * f, // valid output stream
  564. int indent // indent of output
  565. )
  566. {
  567. DASSERT(iu);
  568. bool filter_opt = false;
  569. uint i;
  570. for ( i = 1; i < iu->n_opt_total; i++ )
  571. {
  572. const u8 used = iu->opt_used[i];
  573. if ( used & 0x80 && ( used != 0x81 || !iu->opt_info[i].ignore ) )
  574. {
  575. filter_opt = true;
  576. break;
  577. }
  578. }
  579. fputc('\n',f);
  580. if (filter_opt)
  581. {
  582. print_help_options( iu, f, true, indent,
  583. 1, iu->n_opt_total,
  584. "Selected options with common description" );
  585. }
  586. else
  587. {
  588. print_help_options( iu, f, false, indent,
  589. iu->n_opt_specific+1, iu->n_opt_total,
  590. "Global options" );
  591. print_help_options( iu, f, false, indent,
  592. 1, iu->n_opt_specific,
  593. "Command specific options with common description" );
  594. }
  595. }
  596. ///////////////////////////////////////////////////////////////////////////////
  597. void PrintHelpCommands
  598. (
  599. const InfoUI_t * iu, // valid pointer
  600. FILE * f, // valid output stream
  601. int indent, // indent of output
  602. ccp help_cmd // NULL or name of help command
  603. )
  604. {
  605. DASSERT(iu);
  606. if ( !iu->n_cmd )
  607. return;
  608. uint col_len;
  609. ccp col_head, col_cmd, col_reset;
  610. if ( IsFileColorized(f))
  611. {
  612. col_head = GetTextMode(1,TTM_COL_HEADING);
  613. col_cmd = GetTextMode(1,TTM_COL_CMD);
  614. col_reset = TermTextModeReset;
  615. col_len = strlen(col_cmd) + strlen(col_reset);
  616. }
  617. else
  618. {
  619. col_head = col_cmd = col_reset = "";
  620. col_len = 0;
  621. }
  622. fprintf(f,"\n%*s%sCommands:%s\n\n", indent,"", col_head, col_reset );
  623. // const int fw = GetTermWidth(80,40) - 1;
  624. const int fw = GetGoodTermWidth(0,false) - 1;
  625. int fw1 = 0, fw2 = 0;
  626. const InfoCommand_t * ic;
  627. for ( ic = iu->cmd_info; ic->name1; ic++ )
  628. if (!ic->hidden)
  629. {
  630. const int len = strlen(ic->name1);
  631. if ( fw1 < len )
  632. fw1 = len;
  633. if ( ic->name2 )
  634. {
  635. const int len = strlen(ic->name2);
  636. if ( fw2 < len )
  637. fw2 = len;
  638. }
  639. }
  640. const int fw12 = fw1 + ( fw2 ? fw2 + 3 : 0 );
  641. for ( ic = iu->cmd_info+1; ic->name1; ic++ )
  642. if (!ic->hidden)
  643. {
  644. if (ic->separator)
  645. fputc('\n',f);
  646. int len;
  647. if ( ic->name2 )
  648. len = fprintf(f,"%*s %s%-*s%s | %s%-*s%s : ",
  649. indent, "",
  650. col_cmd, fw1, ic->name1, col_reset,
  651. col_cmd, fw2, ic->name2, col_reset )
  652. - 2*col_len;
  653. else
  654. len = fprintf(f,"%*s %s%-*s%s : ",
  655. indent, "",
  656. col_cmd, fw12, ic->name1, col_reset )
  657. - col_len;
  658. PutLines(f,indent+len,fw,len,0,ic->help,0);
  659. }
  660. if (help_cmd)
  661. fprintf(f,
  662. "\n%*sType '%s %s command' to get command specific help.\n\n",
  663. indent, "", iu->tool_name, help_cmd );
  664. }
  665. //
  666. ///////////////////////////////////////////////////////////////////////////////
  667. /////////////// print help ///////////////
  668. ///////////////////////////////////////////////////////////////////////////////
  669. void PrintHelp
  670. (
  671. const InfoUI_t * iu, // valid pointer
  672. FILE * f, // valid output stream
  673. int indent, // indent of output
  674. ccp help_cmd, // NULL or name of help command
  675. ccp info, // NULL or additional text
  676. ccp base_uri, // NULL or base URI for a external help link
  677. ccp first_param // NULL or first param of argv[]
  678. )
  679. {
  680. ASSERT(iu);
  681. int cmd = 0;
  682. if ( iu->n_cmd && first_param && *first_param )
  683. {
  684. int cmd_stat;
  685. const KeywordTab_t * ct = ScanKeyword(&cmd_stat,first_param,iu->cmd_tab);
  686. if (ct)
  687. cmd = ct->id;
  688. else
  689. {
  690. char buf[100];
  691. StringCat2S(buf,sizeof(buf),"+",first_param);
  692. ct = ScanKeyword(&cmd_stat,buf,iu->cmd_tab);
  693. if (ct)
  694. cmd = ct->id;
  695. else
  696. {
  697. char key_buf[12];
  698. char *dest = key_buf;
  699. char *end = key_buf + sizeof(key_buf) - 1;
  700. while ( *first_param && dest < end )
  701. *dest++ = toupper((int)*first_param++);
  702. *dest = 0;
  703. const int key_len = dest - key_buf;
  704. if ( key_len >= 3 )
  705. {
  706. if (!memcmp(key_buf,"OPTIONS",key_len) )
  707. {
  708. PrintHelpOptions(iu,f,indent);
  709. return;
  710. }
  711. if ( !memcmp(key_buf,"COMMANDS",key_len)
  712. || !memcmp(key_buf,"CMD",key_len) )
  713. {
  714. PrintHelpCommands(iu,f,indent,help_cmd);
  715. return;
  716. }
  717. }
  718. }
  719. }
  720. }
  721. PrintHelpCmd(iu,f,indent,cmd,help_cmd,info,base_uri);
  722. }
  723. ///////////////////////////////////////////////////////////////////////////////
  724. void PrintHelpCmd
  725. (
  726. const InfoUI_t * iu, // valid pointer
  727. FILE * f, // valid output stream
  728. int indent, // indent of output
  729. int cmd, // index of command
  730. ccp help_cmd, // NULL or name of help command
  731. ccp info, // NULL or poiner to additional text
  732. ccp base_uri // NULL or base URI for a external help link
  733. )
  734. {
  735. ASSERT(iu);
  736. if (!f)
  737. return;
  738. indent = NormalizeIndent(indent);
  739. if ( cmd < 0 || cmd >= iu->n_cmd )
  740. cmd = 0;
  741. const InfoCommand_t * ic = iu->cmd_info + cmd;
  742. // const int fw = GetTermWidth(80,40) - 1;
  743. const int fw = GetGoodTermWidth(0,false) - 1;
  744. uint col_len;
  745. ccp col_head, col_reset;
  746. if (IsFileColorized(f))
  747. {
  748. col_head = GetTextMode(1,TTM_COL_HEADING);
  749. col_reset = GetTextMode(1,TTM_RESET);
  750. col_len = strlen(col_head) + strlen(col_reset);
  751. }
  752. else
  753. {
  754. col_head = col_reset = "";
  755. col_len = 0;
  756. }
  757. char iobuf[10000];
  758. if (!cmd)
  759. snprintf(iobuf,sizeof(iobuf),"%s",iu->tool_name );
  760. else if (ic->name2)
  761. snprintf(iobuf,sizeof(iobuf),"%s %s|%s",iu->tool_name,ic->name1,ic->name2);
  762. else
  763. snprintf(iobuf,sizeof(iobuf),"%s %s",iu->tool_name,ic->name1);
  764. int len = fprintf(f,"\n%*s%s : ",indent,"",iobuf) - 1;
  765. PutLines(f,indent+len,fw,len,0, ic->xhelp ? ic->xhelp : ic->help, 0);
  766. len = fprintf(f,"\n%*s%sSyntax:%s ", indent, "", col_head, col_reset ) - 1 - col_len;
  767. PutLines(f,indent+len,fw,len,0,ic->syntax,0);
  768. fputc('\n',f);
  769. if (!cmd)
  770. PrintHelpCommands(iu,f,indent,help_cmd);
  771. if ( ic->n_opt )
  772. {
  773. fprintf(f,"%*s%s%sptions:%s\n\n",
  774. indent,"", col_head,
  775. !cmd && iu->n_cmd ? "Global o" : "O",
  776. col_reset );
  777. const InfoOption_t ** iop;
  778. // calculate the base option field width
  779. int base_opt_fw = 0;
  780. for ( iop = ic->opt; *iop; iop++ )
  781. {
  782. const InfoOption_t * io = *iop;
  783. if (io->long_name)
  784. {
  785. int len = strlen(io->long_name);
  786. if (io->param)
  787. len += 1 + strlen(io->param);
  788. if ( base_opt_fw < len )
  789. base_opt_fw = len;
  790. }
  791. }
  792. const int max_fw = 2 + (fw+5)/8;
  793. if ( base_opt_fw > max_fw )
  794. base_opt_fw = max_fw;
  795. // calculate the option field width
  796. int opt_fw = 0;
  797. for ( iop = ic->opt; *iop; iop++ )
  798. {
  799. const InfoOption_t * io = *iop;
  800. if (io->long_name)
  801. {
  802. int len = strlen(io->long_name);
  803. if (io->param)
  804. len += 1 + strlen(io->param);
  805. if ( len <= base_opt_fw && opt_fw < len )
  806. opt_fw = len;
  807. }
  808. }
  809. opt_fw += indent + 9;
  810. for ( iop = ic->opt; *iop; iop++ )
  811. print_single_option(f,indent,*iop,opt_fw,fw);
  812. fputc('\n',f);
  813. }
  814. if (info)
  815. fputs(info,f);
  816. if ( base_uri && *base_uri )
  817. {
  818. if (!cmd)
  819. fprintf(f,"%*sMore help is available at %s%s\n\n",
  820. indent, "", base_uri, iu->tool_name );
  821. else if (!ic->hidden)
  822. {
  823. char *dest = iobuf;
  824. ccp src;
  825. for ( src = ic->name1; *src; src++ )
  826. *dest++ = *src == '_' ? '-' : tolower((int)*src);
  827. *dest = 0;
  828. fprintf(f,"%*sMore help is available at %scmd/%s/%s\n\n",
  829. indent, "", base_uri, iu->tool_name, iobuf );
  830. }
  831. }
  832. }
  833. //
  834. ///////////////////////////////////////////////////////////////////////////////
  835. /////////////// END ///////////////
  836. ///////////////////////////////////////////////////////////////////////////////