ptype.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. /*
  2. * ptype.c
  3. */
  4. #include "private.h"
  5. #include "lub/string.h"
  6. #include "lub/ctype.h"
  7. #include "lub/argv.h"
  8. #include "lub/conv.h"
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <assert.h>
  12. #include <limits.h>
  13. #include <stdio.h>
  14. /*---------------------------------------------------------
  15. * PRIVATE METHODS
  16. *--------------------------------------------------------- */
  17. static char *clish_ptype_select__get_name(const clish_ptype_t * this,
  18. unsigned index)
  19. {
  20. char *result = NULL;
  21. const char *arg = lub_argv__get_arg(this->u.select.items, index);
  22. if (arg) {
  23. size_t name_len = strlen(arg);
  24. const char *lbrk = strchr(arg, '(');
  25. if (lbrk)
  26. name_len = (size_t) (lbrk - arg);
  27. result = lub_string_dupn(arg, name_len);
  28. }
  29. return result;
  30. }
  31. /*--------------------------------------------------------- */
  32. static char *clish_ptype_select__get_value(const clish_ptype_t * this,
  33. unsigned index)
  34. {
  35. char *result = NULL;
  36. const char *arg = lub_argv__get_arg(this->u.select.items, index);
  37. if (arg) {
  38. const char *lbrk = strchr(arg, '(');
  39. const char *rbrk = strchr(arg, ')');
  40. const char *value = arg;
  41. size_t value_len = strlen(arg);
  42. if (lbrk) {
  43. value = lbrk + 1;
  44. if (rbrk)
  45. value_len = (size_t) (rbrk - value);
  46. }
  47. result = lub_string_dupn(value, value_len);
  48. }
  49. return result;
  50. }
  51. /*--------------------------------------------------------- */
  52. static void clish_ptype__set_range(clish_ptype_t * this)
  53. {
  54. char tmp[80];
  55. /* Now set up the range values */
  56. switch (this->method) {
  57. /*------------------------------------------------- */
  58. case CLISH_PTYPE_REGEXP:
  59. /* Nothing more to do */
  60. break;
  61. /*------------------------------------------------- */
  62. case CLISH_PTYPE_INTEGER:
  63. /* Setup the integer range */
  64. snprintf(tmp, sizeof(tmp), "%d..%d",
  65. this->u.integer.min, this->u.integer.max);
  66. tmp[sizeof(tmp) - 1] = '\0';
  67. this->range = lub_string_dup(tmp);
  68. break;
  69. /*------------------------------------------------- */
  70. case CLISH_PTYPE_UNSIGNEDINTEGER:
  71. /* Setup the unsigned integer range */
  72. snprintf(tmp, sizeof(tmp), "%u..%u",
  73. (unsigned int)this->u.integer.min,
  74. (unsigned int)this->u.integer.max);
  75. tmp[sizeof(tmp) - 1] = '\0';
  76. this->range = lub_string_dup(tmp);
  77. break;
  78. /*------------------------------------------------- */
  79. case CLISH_PTYPE_SELECT:
  80. {
  81. /* Setup the selection values to the help text */
  82. unsigned int i;
  83. for (i = 0; i < lub_argv__get_count(this->u.select.items); i++) {
  84. char *name = clish_ptype_select__get_name(this, i);
  85. if (i > 0)
  86. lub_string_cat(&this->range, "/");
  87. snprintf(tmp, sizeof(tmp), "%s", name);
  88. tmp[sizeof(tmp) - 1] = '\0';
  89. lub_string_cat(&this->range, tmp);
  90. lub_string_free(name);
  91. }
  92. break;
  93. }
  94. /*------------------------------------------------- */
  95. }
  96. }
  97. /*---------------------------------------------------------
  98. * PUBLIC META FUNCTIONS
  99. *--------------------------------------------------------- */
  100. int clish_ptype_bt_compare(const void *clientnode, const void *clientkey)
  101. {
  102. const clish_ptype_t *this = clientnode;
  103. const char *key = clientkey;
  104. return strcmp(this->name, key);
  105. }
  106. /*-------------------------------------------------------- */
  107. void clish_ptype_bt_getkey(const void *clientnode, lub_bintree_key_t * key)
  108. {
  109. const clish_ptype_t *this = clientnode;
  110. /* fill out the opaque key */
  111. strcpy((char *)key, this->name);
  112. }
  113. /*--------------------------------------------------------- */
  114. size_t clish_ptype_bt_offset(void)
  115. {
  116. return offsetof(clish_ptype_t, bt_node);
  117. }
  118. /*--------------------------------------------------------- */
  119. static const char *method_names[] = {
  120. "regexp",
  121. "integer",
  122. "unsignedInteger",
  123. "select"
  124. };
  125. /*--------------------------------------------------------- */
  126. const char *clish_ptype_method__get_name(clish_ptype_method_e method)
  127. {
  128. unsigned int max_method = sizeof(method_names) / sizeof(char *);
  129. if (method >= max_method)
  130. return NULL;
  131. return method_names[method];
  132. }
  133. /*--------------------------------------------------------- */
  134. clish_ptype_method_e clish_ptype_method_resolve(const char *name)
  135. {
  136. clish_ptype_method_e result = CLISH_PTYPE_REGEXP;
  137. if (NULL != name) {
  138. unsigned int i;
  139. for (i = 0; i < CLISH_PTYPE_SELECT + 1; i++) {
  140. if (0 == strcmp(name, method_names[i])) {
  141. result = (clish_ptype_method_e) i;
  142. break;
  143. }
  144. }
  145. /* error for incorrect type spec */
  146. assert(i <= CLISH_PTYPE_SELECT);
  147. }
  148. return result;
  149. }
  150. /*--------------------------------------------------------- */
  151. static const char *preprocess_names[] = {
  152. "none",
  153. "toupper",
  154. "tolower"
  155. };
  156. /*--------------------------------------------------------- */
  157. const char *clish_ptype_preprocess__get_name(
  158. clish_ptype_preprocess_e preprocess)
  159. {
  160. return preprocess_names[preprocess];
  161. }
  162. /*--------------------------------------------------------- */
  163. clish_ptype_preprocess_e clish_ptype_preprocess_resolve(const char *name)
  164. {
  165. clish_ptype_preprocess_e result = CLISH_PTYPE_NONE;
  166. if (name) {
  167. unsigned i;
  168. for (i = 0; i < CLISH_PTYPE_TOLOWER + 1; i++) {
  169. if (0 == strcmp(name, preprocess_names[i])) {
  170. result = (clish_ptype_preprocess_e) i;
  171. break;
  172. }
  173. }
  174. /* error for incorrect type spec */
  175. assert((clish_ptype_preprocess_e) i <= CLISH_PTYPE_TOLOWER);
  176. }
  177. return result;
  178. }
  179. /*---------------------------------------------------------
  180. * PUBLIC METHODS
  181. *--------------------------------------------------------- */
  182. /*--------------------------------------------------------- */
  183. void clish_ptype_word_generator(clish_ptype_t * this,
  184. lub_argv_t *matches, const char *text)
  185. {
  186. char *result = NULL;
  187. unsigned int i = 0;
  188. /* Another ptypes has no completions */
  189. if (this->method != CLISH_PTYPE_SELECT)
  190. return;
  191. /* First of all simply try to validate the result */
  192. result = clish_ptype_validate(this, text);
  193. if (result) {
  194. lub_argv_add(matches, result);
  195. lub_string_free(result);
  196. return;
  197. }
  198. /* Iterate possible completion */
  199. while ((result = clish_ptype_select__get_name(this, i++))) {
  200. /* get the next item and check if it is a completion */
  201. if (result == lub_string_nocasestr(result, text))
  202. lub_argv_add(matches, result);
  203. lub_string_free(result);
  204. }
  205. }
  206. /*--------------------------------------------------------- */
  207. static char *clish_ptype_validate_or_translate(clish_ptype_t *this,
  208. const char *text, bool_t translate)
  209. {
  210. char *result = lub_string_dup(text);
  211. assert(this->pattern);
  212. switch (this->preprocess) {
  213. /*----------------------------------------- */
  214. case CLISH_PTYPE_NONE:
  215. break;
  216. /*----------------------------------------- */
  217. case CLISH_PTYPE_TOUPPER:
  218. {
  219. char *p = result;
  220. while (*p) {
  221. /*lint -e155 Ignoring { }'ed sequence within an expression, 0 assumed
  222. * MACRO implementation uses braces to prevent multiple increments
  223. * when called.
  224. */
  225. *p = lub_ctype_toupper(*p);
  226. p++;
  227. }
  228. break;
  229. }
  230. /*----------------------------------------- */
  231. case CLISH_PTYPE_TOLOWER:
  232. {
  233. char *p = result;
  234. while (*p) {
  235. *p = lub_ctype_tolower(*p);
  236. p++;
  237. }
  238. break;
  239. }
  240. /*----------------------------------------- */
  241. }
  242. /* Now validate according the specified method */
  243. switch (this->method) {
  244. /*------------------------------------------------- */
  245. case CLISH_PTYPE_REGEXP:
  246. /* Lazy compilation of the regular expression */
  247. if (!this->u.regex.is_compiled) {
  248. if (regcomp(&this->u.regex.re, this->pattern,
  249. REG_NOSUB | REG_EXTENDED)) {
  250. lub_string_free(result);
  251. result = NULL;
  252. break;
  253. }
  254. this->u.regex.is_compiled = BOOL_TRUE;
  255. }
  256. if (regexec(&this->u.regex.re, result, 0, NULL, 0)) {
  257. lub_string_free(result);
  258. result = NULL;
  259. }
  260. break;
  261. /*------------------------------------------------- */
  262. case CLISH_PTYPE_INTEGER:
  263. {
  264. /* first of all check that this is a number */
  265. bool_t ok = BOOL_TRUE;
  266. const char *p = result;
  267. if (*p == '-')
  268. p++;
  269. while (*p) {
  270. if (!lub_ctype_isdigit(*p++)) {
  271. ok = BOOL_FALSE;
  272. break;
  273. }
  274. }
  275. if (BOOL_TRUE == ok) {
  276. /* convert and check the range */
  277. int value = 0;
  278. if ((lub_conv_atoi(result, &value, 0) < 0) ||
  279. (value < this->u.integer.min) ||
  280. (value > this->u.integer.max)) {
  281. lub_string_free(result);
  282. result = NULL;
  283. }
  284. } else {
  285. lub_string_free(result);
  286. result = NULL;
  287. }
  288. break;
  289. }
  290. /*------------------------------------------------- */
  291. case CLISH_PTYPE_UNSIGNEDINTEGER:
  292. {
  293. /* first of all check that this is a number */
  294. bool_t ok = BOOL_TRUE;
  295. const char *p = result;
  296. while (p && *p) {
  297. if (!lub_ctype_isdigit(*p++)) {
  298. ok = BOOL_FALSE;
  299. break;
  300. }
  301. }
  302. if (BOOL_TRUE == ok) {
  303. /* convert and check the range */
  304. unsigned int value = 0;
  305. if ((lub_conv_atoui(result, &value, 0) < 0) ||
  306. (value < (unsigned)this->u.integer.min) ||
  307. (value > (unsigned)this->u.integer.max)) {
  308. lub_string_free(result);
  309. result = NULL;
  310. }
  311. } else {
  312. lub_string_free(result);
  313. result = NULL;
  314. }
  315. break;
  316. }
  317. /*------------------------------------------------- */
  318. case CLISH_PTYPE_SELECT:
  319. {
  320. unsigned i;
  321. for (i = 0; i < lub_argv__get_count(this->u.select.items);
  322. i++) {
  323. char *name = clish_ptype_select__get_name(this, i);
  324. char *value = clish_ptype_select__get_value(this, i);
  325. int tmp = lub_string_nocasecmp(result, name);
  326. lub_string_free((BOOL_TRUE ==
  327. translate) ? name : value);
  328. if (0 == tmp) {
  329. lub_string_free(result);
  330. result = ((BOOL_TRUE ==
  331. translate) ? value : name);
  332. break;
  333. } else {
  334. lub_string_free((BOOL_TRUE ==
  335. translate) ? value : name);
  336. }
  337. }
  338. if (i == lub_argv__get_count(this->u.select.items)) {
  339. /* failed to find a match */
  340. lub_string_free(result);
  341. result = NULL;
  342. }
  343. break;
  344. }
  345. /*------------------------------------------------- */
  346. }
  347. return (char *)result;
  348. }
  349. /*--------------------------------------------------------- */
  350. static void clish_ptype_init(clish_ptype_t * this,
  351. const char *name, const char *text, const char *pattern,
  352. clish_ptype_method_e method, clish_ptype_preprocess_e preprocess)
  353. {
  354. assert(name);
  355. this->name = lub_string_dup(name);
  356. this->text = NULL;
  357. this->pattern = NULL;
  358. this->preprocess = preprocess;
  359. this->range = NULL;
  360. /* Be a good binary tree citizen */
  361. lub_bintree_node_init(&this->bt_node);
  362. if (pattern) {
  363. /* set the pattern for this type */
  364. clish_ptype__set_pattern(this, pattern, method);
  365. } else {
  366. /* The method is regexp by default */
  367. this->method = CLISH_PTYPE_REGEXP;
  368. }
  369. /* set the help text for this type */
  370. if (text)
  371. clish_ptype__set_text(this, text);
  372. }
  373. /*--------------------------------------------------------- */
  374. char *clish_ptype_validate(clish_ptype_t * this, const char *text)
  375. {
  376. return clish_ptype_validate_or_translate(this, text, BOOL_FALSE);
  377. }
  378. /*--------------------------------------------------------- */
  379. char *clish_ptype_translate(clish_ptype_t * this, const char *text)
  380. {
  381. return clish_ptype_validate_or_translate(this, text, BOOL_TRUE);
  382. }
  383. /*--------------------------------------------------------- */
  384. clish_ptype_t *clish_ptype_new(const char *name,
  385. const char *help, const char *pattern,
  386. clish_ptype_method_e method, clish_ptype_preprocess_e preprocess)
  387. {
  388. clish_ptype_t *this = malloc(sizeof(clish_ptype_t));
  389. if (this)
  390. clish_ptype_init(this, name, help, pattern, method, preprocess);
  391. return this;
  392. }
  393. /*--------------------------------------------------------- */
  394. static void clish_ptype_fini(clish_ptype_t * this)
  395. {
  396. if (this->pattern) {
  397. switch (this->method) {
  398. case CLISH_PTYPE_REGEXP:
  399. if (this->u.regex.is_compiled)
  400. regfree(&this->u.regex.re);
  401. break;
  402. case CLISH_PTYPE_INTEGER:
  403. case CLISH_PTYPE_UNSIGNEDINTEGER:
  404. break;
  405. case CLISH_PTYPE_SELECT:
  406. lub_argv_delete(this->u.select.items);
  407. break;
  408. }
  409. }
  410. lub_string_free(this->name);
  411. this->name = NULL;
  412. lub_string_free(this->text);
  413. this->text = NULL;
  414. lub_string_free(this->pattern);
  415. this->pattern = NULL;
  416. lub_string_free(this->range);
  417. this->range = NULL;
  418. }
  419. /*--------------------------------------------------------- */
  420. void clish_ptype_delete(clish_ptype_t * this)
  421. {
  422. clish_ptype_fini(this);
  423. free(this);
  424. }
  425. /*--------------------------------------------------------- */
  426. const char *clish_ptype__get_name(const clish_ptype_t * this)
  427. {
  428. return (const char *)this->name;
  429. }
  430. /*--------------------------------------------------------- */
  431. const char *clish_ptype__get_text(const clish_ptype_t * this)
  432. {
  433. return (const char *)this->text;
  434. }
  435. /*--------------------------------------------------------- */
  436. void
  437. clish_ptype__set_pattern(clish_ptype_t * this,
  438. const char *pattern, clish_ptype_method_e method)
  439. {
  440. assert(NULL == this->pattern);
  441. this->method = method;
  442. switch (this->method) {
  443. /*------------------------------------------------- */
  444. case CLISH_PTYPE_REGEXP:
  445. {
  446. lub_string_cat(&this->pattern, "^");
  447. lub_string_cat(&this->pattern, pattern);
  448. lub_string_cat(&this->pattern, "$");
  449. /* Use lazy mechanism to compile regular expressions */
  450. this->u.regex.is_compiled = BOOL_FALSE;
  451. break;
  452. }
  453. /*------------------------------------------------- */
  454. case CLISH_PTYPE_INTEGER:
  455. /* default the range to that of an integer */
  456. this->u.integer.min = INT_MIN;
  457. this->u.integer.max = INT_MAX;
  458. this->pattern = lub_string_dup(pattern);
  459. /* now try and read the specified range */
  460. sscanf(this->pattern, "%d..%d",
  461. &this->u.integer.min, &this->u.integer.max);
  462. break;
  463. /*------------------------------------------------- */
  464. case CLISH_PTYPE_UNSIGNEDINTEGER:
  465. /* default the range to that of an unsigned integer */
  466. this->u.integer.min = 0;
  467. this->u.integer.max = (int)UINT_MAX;
  468. this->pattern = lub_string_dup(pattern);
  469. /* now try and read the specified range */
  470. sscanf(this->pattern, "%u..%u",
  471. (unsigned int *)&this->u.integer.min,
  472. (unsigned int *)&this->u.integer.max);
  473. break;
  474. /*------------------------------------------------- */
  475. case CLISH_PTYPE_SELECT:
  476. this->pattern = lub_string_dup(pattern);
  477. /* store a vector of item descriptors */
  478. this->u.select.items = lub_argv_new(this->pattern, 0);
  479. break;
  480. /*------------------------------------------------- */
  481. }
  482. /* now set up the range details */
  483. clish_ptype__set_range(this);
  484. }
  485. /*--------------------------------------------------------- */
  486. void clish_ptype__set_text(clish_ptype_t * this, const char *text)
  487. {
  488. assert(!this->text);
  489. this->text = lub_string_dup(text);
  490. }
  491. /*--------------------------------------------------------- */
  492. void
  493. clish_ptype__set_preprocess(clish_ptype_t * this,
  494. clish_ptype_preprocess_e preprocess)
  495. {
  496. assert(!this->preprocess);
  497. this->preprocess = preprocess;
  498. }
  499. /*--------------------------------------------------------- */
  500. const char *clish_ptype__get_range(const clish_ptype_t * this)
  501. {
  502. return (const char *)this->range;
  503. }
  504. /*--------------------------------------------------------- */