ptype.c 14 KB

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