ptype.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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 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 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(const 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. /*
  243. * now validate according the specified method
  244. */
  245. switch (this->method) {
  246. /*------------------------------------------------- */
  247. case CLISH_PTYPE_REGEXP:
  248. /* test the regular expression against the string */
  249. /*lint -e64 Type mismatch (arg. no. 4) */
  250. /*
  251. * lint seems to equate regmatch_t[] as being of type regmatch_t !
  252. */
  253. if (0 != regexec(&this->u.regexp, result, 0, NULL, 0)) {
  254. lub_string_free(result);
  255. result = NULL;
  256. }
  257. /*lint +e64 */
  258. break;
  259. /*------------------------------------------------- */
  260. case CLISH_PTYPE_INTEGER:
  261. {
  262. /* first of all check that this is a number */
  263. bool_t ok = BOOL_TRUE;
  264. const char *p = result;
  265. if (*p == '-')
  266. p++;
  267. while (*p) {
  268. if (!lub_ctype_isdigit(*p++)) {
  269. ok = BOOL_FALSE;
  270. break;
  271. }
  272. }
  273. if (BOOL_TRUE == ok) {
  274. /* convert and check the range */
  275. int value = 0;
  276. if ((lub_conv_atoi(result, &value, 0) < 0) ||
  277. (value < this->u.integer.min) ||
  278. (value > this->u.integer.max)) {
  279. lub_string_free(result);
  280. result = NULL;
  281. }
  282. } else {
  283. lub_string_free(result);
  284. result = NULL;
  285. }
  286. break;
  287. }
  288. /*------------------------------------------------- */
  289. case CLISH_PTYPE_UNSIGNEDINTEGER:
  290. {
  291. /* first of all check that this is a number */
  292. bool_t ok = BOOL_TRUE;
  293. const char *p = result;
  294. while (p && *p) {
  295. if (!lub_ctype_isdigit(*p++)) {
  296. ok = BOOL_FALSE;
  297. break;
  298. }
  299. }
  300. if (BOOL_TRUE == ok) {
  301. /* convert and check the range */
  302. unsigned int value = 0;
  303. if ((lub_conv_atoui(result, &value, 0) < 0) ||
  304. (value < (unsigned)this->u.integer.min) ||
  305. (value > (unsigned)this->u.integer.max)) {
  306. lub_string_free(result);
  307. result = NULL;
  308. }
  309. } else {
  310. lub_string_free(result);
  311. result = NULL;
  312. }
  313. break;
  314. }
  315. /*------------------------------------------------- */
  316. case CLISH_PTYPE_SELECT:
  317. {
  318. unsigned i;
  319. for (i = 0; i < lub_argv__get_count(this->u.select.items);
  320. i++) {
  321. char *name = clish_ptype_select__get_name(this, i);
  322. char *value = clish_ptype_select__get_value(this, i);
  323. int tmp = lub_string_nocasecmp(result, name);
  324. lub_string_free((BOOL_TRUE ==
  325. translate) ? name : value);
  326. if (0 == tmp) {
  327. lub_string_free(result);
  328. result = ((BOOL_TRUE ==
  329. translate) ? value : name);
  330. break;
  331. } else {
  332. lub_string_free((BOOL_TRUE ==
  333. translate) ? value : name);
  334. }
  335. }
  336. if (i == lub_argv__get_count(this->u.select.items)) {
  337. /* failed to find a match */
  338. lub_string_free(result);
  339. result = NULL;
  340. }
  341. break;
  342. }
  343. /*------------------------------------------------- */
  344. }
  345. return (char *)result;
  346. }
  347. /*--------------------------------------------------------- */
  348. static void clish_ptype_init(clish_ptype_t * this,
  349. const char *name, const char *text, const char *pattern,
  350. clish_ptype_method_e method, clish_ptype_preprocess_e preprocess)
  351. {
  352. assert(name);
  353. this->name = lub_string_dup(name);
  354. this->text = NULL;
  355. this->pattern = NULL;
  356. this->preprocess = preprocess;
  357. this->range = NULL;
  358. /* Be a good binary tree citizen */
  359. lub_bintree_node_init(&this->bt_node);
  360. if (pattern) {
  361. /* set the pattern for this type */
  362. clish_ptype__set_pattern(this, pattern, method);
  363. } else {
  364. /* The method is regexp by default */
  365. this->method = CLISH_PTYPE_REGEXP;
  366. }
  367. /* set the help text for this type */
  368. if (text)
  369. clish_ptype__set_text(this, text);
  370. }
  371. /*--------------------------------------------------------- */
  372. char *clish_ptype_validate(const clish_ptype_t * this, const char *text)
  373. {
  374. return clish_ptype_validate_or_translate(this, text, BOOL_FALSE);
  375. }
  376. /*--------------------------------------------------------- */
  377. char *clish_ptype_translate(const clish_ptype_t * this, const char *text)
  378. {
  379. return clish_ptype_validate_or_translate(this, text, BOOL_TRUE);
  380. }
  381. /*--------------------------------------------------------- */
  382. clish_ptype_t *clish_ptype_new(const char *name,
  383. const char *help, const char *pattern,
  384. clish_ptype_method_e method, clish_ptype_preprocess_e preprocess)
  385. {
  386. clish_ptype_t *this = malloc(sizeof(clish_ptype_t));
  387. if (this)
  388. clish_ptype_init(this, name, help, pattern, method, preprocess);
  389. return this;
  390. }
  391. /*--------------------------------------------------------- */
  392. static void clish_ptype_fini(clish_ptype_t * this)
  393. {
  394. if (this->pattern) {
  395. switch (this->method) {
  396. case CLISH_PTYPE_REGEXP:
  397. regfree(&this->u.regexp);
  398. break;
  399. case CLISH_PTYPE_INTEGER:
  400. case CLISH_PTYPE_UNSIGNEDINTEGER:
  401. break;
  402. case CLISH_PTYPE_SELECT:
  403. lub_argv_delete(this->u.select.items);
  404. break;
  405. }
  406. }
  407. lub_string_free(this->name);
  408. this->name = NULL;
  409. lub_string_free(this->text);
  410. this->text = NULL;
  411. lub_string_free(this->pattern);
  412. this->pattern = NULL;
  413. lub_string_free(this->range);
  414. this->range = NULL;
  415. }
  416. /*--------------------------------------------------------- */
  417. void clish_ptype_delete(clish_ptype_t * this)
  418. {
  419. clish_ptype_fini(this);
  420. free(this);
  421. }
  422. /*--------------------------------------------------------- */
  423. const char *clish_ptype__get_name(const clish_ptype_t * this)
  424. {
  425. return (const char *)this->name;
  426. }
  427. /*--------------------------------------------------------- */
  428. const char *clish_ptype__get_text(const clish_ptype_t * this)
  429. {
  430. return (const char *)this->text;
  431. }
  432. /*--------------------------------------------------------- */
  433. void
  434. clish_ptype__set_pattern(clish_ptype_t * this,
  435. const char *pattern, clish_ptype_method_e method)
  436. {
  437. assert(NULL == this->pattern);
  438. this->method = method;
  439. switch (this->method) {
  440. /*------------------------------------------------- */
  441. case CLISH_PTYPE_REGEXP:
  442. {
  443. int result;
  444. /* only the expression is allowed */
  445. lub_string_cat(&this->pattern, "^");
  446. lub_string_cat(&this->pattern, pattern);
  447. lub_string_cat(&this->pattern, "$");
  448. /* compile the regular expression for later use */
  449. result = regcomp(&this->u.regexp, this->pattern,
  450. REG_NOSUB | REG_EXTENDED);
  451. assert(0 == result);
  452. break;
  453. }
  454. /*------------------------------------------------- */
  455. case CLISH_PTYPE_INTEGER:
  456. /* default the range to that of an integer */
  457. this->u.integer.min = INT_MIN;
  458. this->u.integer.max = INT_MAX;
  459. this->pattern = lub_string_dup(pattern);
  460. /* now try and read the specified range */
  461. sscanf(this->pattern, "%d..%d",
  462. &this->u.integer.min, &this->u.integer.max);
  463. break;
  464. /*------------------------------------------------- */
  465. case CLISH_PTYPE_UNSIGNEDINTEGER:
  466. /* default the range to that of an unsigned integer */
  467. this->u.integer.min = 0;
  468. this->u.integer.max = (int)UINT_MAX;
  469. this->pattern = lub_string_dup(pattern);
  470. /* now try and read the specified range */
  471. sscanf(this->pattern, "%u..%u",
  472. (unsigned int *)&this->u.integer.min,
  473. (unsigned int *)&this->u.integer.max);
  474. break;
  475. /*------------------------------------------------- */
  476. case CLISH_PTYPE_SELECT:
  477. this->pattern = lub_string_dup(pattern);
  478. /* store a vector of item descriptors */
  479. this->u.select.items = lub_argv_new(this->pattern, 0);
  480. break;
  481. /*------------------------------------------------- */
  482. }
  483. /* now set up the range details */
  484. clish_ptype__set_range(this);
  485. }
  486. /*--------------------------------------------------------- */
  487. void clish_ptype__set_text(clish_ptype_t * this, const char *text)
  488. {
  489. assert(!this->text);
  490. this->text = lub_string_dup(text);
  491. }
  492. /*--------------------------------------------------------- */
  493. void
  494. clish_ptype__set_preprocess(clish_ptype_t * this,
  495. clish_ptype_preprocess_e preprocess)
  496. {
  497. assert(!this->preprocess);
  498. this->preprocess = preprocess;
  499. }
  500. /*--------------------------------------------------------- */
  501. const char *clish_ptype__get_range(const clish_ptype_t * this)
  502. {
  503. return (const char *)this->range;
  504. }
  505. /*--------------------------------------------------------- */