utils.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. /*****************************************************************************
  2. *
  3. * Library of useful functions for plugins
  4. *
  5. * License: GPL
  6. * Copyright (c) 2000 Karl DeBisschop (karl@debisschop.net)
  7. * Copyright (c) 2002-2014 Nagios Plugin Development Team
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. *
  23. *****************************************************************************/
  24. #include "common.h"
  25. #include "utils.h"
  26. #include "utils_base.h"
  27. #include <stdarg.h>
  28. #include <limits.h>
  29. #include <ctype.h>
  30. #include <arpa/inet.h>
  31. extern void print_usage (void);
  32. extern const char *progname;
  33. #define STRLEN 64
  34. #define TXTBLK 128
  35. unsigned int timeout_state = STATE_CRITICAL;
  36. unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
  37. time_t start_time, end_time;
  38. /* **************************************************************************
  39. * max_state(STATE_x, STATE_y)
  40. * compares STATE_x to STATE_y and returns result based on the following
  41. * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
  42. *
  43. * Note that numerically the above does not hold
  44. ****************************************************************************/
  45. int
  46. max_state (int a, int b)
  47. {
  48. if (a == STATE_CRITICAL || b == STATE_CRITICAL)
  49. return STATE_CRITICAL;
  50. else if (a == STATE_WARNING || b == STATE_WARNING)
  51. return STATE_WARNING;
  52. else if (a == STATE_OK || b == STATE_OK)
  53. return STATE_OK;
  54. else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
  55. return STATE_UNKNOWN;
  56. else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
  57. return STATE_DEPENDENT;
  58. else
  59. return max (a, b);
  60. }
  61. /* **************************************************************************
  62. * min_state(STATE_x, STATE_y)
  63. * does the opposite of max_state(): returns the minimum of both states using
  64. * the ordder STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
  65. ****************************************************************************/
  66. int min_state (int a, int b)
  67. {
  68. if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
  69. return STATE_UNKNOWN;
  70. else if (a == STATE_OK || b == STATE_OK)
  71. return STATE_OK;
  72. else if (a == STATE_WARNING || b == STATE_WARNING)
  73. return STATE_WARNING;
  74. else if (a == STATE_CRITICAL || b == STATE_CRITICAL)
  75. return STATE_CRITICAL;
  76. else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
  77. return STATE_DEPENDENT;
  78. else
  79. return min(a, b);
  80. }
  81. /* **************************************************************************
  82. * max_state_alt(STATE_x, STATE_y)
  83. * compares STATE_x to STATE_y and returns result based on the following
  84. * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
  85. *
  86. * The main difference between max_state_alt and max_state it that it doesn't
  87. * allow setting a default to UNKNOWN. It will instead prioritixe any valid
  88. * non-OK state.
  89. ****************************************************************************/
  90. int
  91. max_state_alt (int a, int b)
  92. {
  93. if (a == STATE_CRITICAL || b == STATE_CRITICAL)
  94. return STATE_CRITICAL;
  95. else if (a == STATE_WARNING || b == STATE_WARNING)
  96. return STATE_WARNING;
  97. else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
  98. return STATE_UNKNOWN;
  99. else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
  100. return STATE_DEPENDENT;
  101. else if (a == STATE_OK || b == STATE_OK)
  102. return STATE_OK;
  103. else
  104. return max (a, b);
  105. }
  106. void usage (const char *msg)
  107. {
  108. printf ("%s\n", msg);
  109. print_usage ();
  110. exit (STATE_UNKNOWN);
  111. }
  112. void usage_va (const char *fmt, ...)
  113. {
  114. va_list ap;
  115. printf("%s: ", progname);
  116. va_start(ap, fmt);
  117. vprintf(fmt, ap);
  118. va_end(ap);
  119. printf("\n");
  120. exit (STATE_UNKNOWN);
  121. }
  122. void usage2(const char *msg, const char *arg)
  123. {
  124. printf ("%s: %s - %s\n", progname, msg, arg?arg:"(null)" );
  125. print_usage ();
  126. exit (STATE_UNKNOWN);
  127. }
  128. void
  129. usage3 (const char *msg, int arg)
  130. {
  131. printf ("%s: %s - %c\n", progname, msg, arg);
  132. print_usage();
  133. exit (STATE_UNKNOWN);
  134. }
  135. void
  136. usage4 (const char *msg)
  137. {
  138. printf ("%s: %s\n", progname, msg);
  139. print_usage();
  140. exit (STATE_UNKNOWN);
  141. }
  142. void
  143. usage5 (void)
  144. {
  145. print_usage();
  146. exit (STATE_UNKNOWN);
  147. }
  148. void
  149. print_revision (const char *command_name, const char *revision)
  150. {
  151. printf ("%s v%s (%s %s)\n",
  152. command_name, revision, PACKAGE, VERSION);
  153. }
  154. const char *
  155. state_text (int result)
  156. {
  157. switch (result) {
  158. case STATE_OK:
  159. return "OK";
  160. case STATE_WARNING:
  161. return "WARNING";
  162. case STATE_CRITICAL:
  163. return "CRITICAL";
  164. case STATE_DEPENDENT:
  165. return "DEPENDENT";
  166. default:
  167. return "UNKNOWN";
  168. }
  169. }
  170. void
  171. timeout_alarm_handler (int signo)
  172. {
  173. const char msg[] = " - Plugin timed out\n";
  174. if (signo == SIGALRM) {
  175. /* printf (_("%s - Plugin timed out after %d seconds\n"),
  176. state_text(timeout_state), timeout_interval); */
  177. switch(timeout_state) {
  178. case STATE_OK:
  179. write(STDOUT_FILENO, "OK", 2);
  180. break;
  181. case STATE_WARNING:
  182. write(STDOUT_FILENO, "WARNING", 7);
  183. break;
  184. case STATE_CRITICAL:
  185. write(STDOUT_FILENO, "CRITICAL", 8);
  186. break;
  187. case STATE_DEPENDENT:
  188. write(STDOUT_FILENO, "DEPENDENT", 9);
  189. break;
  190. default:
  191. write(STDOUT_FILENO, "UNKNOWN", 7);
  192. break;
  193. }
  194. write(STDOUT_FILENO, msg, sizeof(msg) - 1);
  195. exit (timeout_state);
  196. }
  197. }
  198. void
  199. set_timeout_state (char *state) {
  200. if ((timeout_state = translate_state(state)) == ERROR)
  201. usage4 (_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
  202. }
  203. int
  204. parse_timeout_string (char *timeout_str)
  205. {
  206. char *seperated_str;
  207. char *timeout_val = "";
  208. char *timeout_sta = NULL;
  209. if (strstr(timeout_str, ":") == NULL) {
  210. timeout_val = timeout_str;
  211. }
  212. else if (strncmp(timeout_str, ":", 1) == 0) {
  213. seperated_str = strtok(timeout_str, ":");
  214. if (seperated_str != NULL) {
  215. timeout_sta = seperated_str;
  216. }
  217. }
  218. else {
  219. seperated_str = strtok(timeout_str, ":");
  220. timeout_val = seperated_str;
  221. seperated_str = strtok(NULL, ":");
  222. if (seperated_str != NULL) {
  223. timeout_sta = seperated_str;
  224. }
  225. }
  226. if (timeout_sta != NULL) {
  227. set_timeout_state(timeout_sta);
  228. }
  229. if ((timeout_val == NULL) || (timeout_val[0] == '\0')) {
  230. return timeout_interval;
  231. }
  232. else if (is_intpos(timeout_val)) {
  233. return atoi(timeout_val);
  234. }
  235. else {
  236. usage4 (_("Timeout value must be a positive integer"));
  237. exit (STATE_UNKNOWN);
  238. }
  239. }
  240. int
  241. is_numeric (char *number)
  242. {
  243. char tmp[1];
  244. float x;
  245. if (!number)
  246. return FALSE;
  247. else if (sscanf (number, "%f%c", &x, tmp) == 1)
  248. return TRUE;
  249. else
  250. return FALSE;
  251. }
  252. int
  253. is_positive (char *number)
  254. {
  255. if (is_numeric (number) && atof (number) > 0.0)
  256. return TRUE;
  257. else
  258. return FALSE;
  259. }
  260. int
  261. is_negative (char *number)
  262. {
  263. if (is_numeric (number) && atof (number) < 0.0)
  264. return TRUE;
  265. else
  266. return FALSE;
  267. }
  268. int
  269. is_nonnegative (char *number)
  270. {
  271. if (is_numeric (number) && atof (number) >= 0.0)
  272. return TRUE;
  273. else
  274. return FALSE;
  275. }
  276. int
  277. is_percentage (char *number)
  278. {
  279. int x;
  280. if (is_numeric (number) && (x = atof (number)) >= 0 && x <= 100)
  281. return TRUE;
  282. else
  283. return FALSE;
  284. }
  285. int
  286. is_integer (char *number)
  287. {
  288. long int n;
  289. if (!number || (strspn (number, "-0123456789 ") != strlen (number)))
  290. return FALSE;
  291. n = strtol (number, NULL, 10);
  292. if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX)
  293. return TRUE;
  294. else
  295. return FALSE;
  296. }
  297. int
  298. is_intpos (char *number)
  299. {
  300. if (is_integer (number) && atoi (number) > 0)
  301. return TRUE;
  302. else
  303. return FALSE;
  304. }
  305. int
  306. is_intneg (char *number)
  307. {
  308. if (is_integer (number) && atoi (number) < 0)
  309. return TRUE;
  310. else
  311. return FALSE;
  312. }
  313. int
  314. is_intnonneg (char *number)
  315. {
  316. if (is_integer (number) && atoi (number) >= 0)
  317. return TRUE;
  318. else
  319. return FALSE;
  320. }
  321. int
  322. is_intpercent (char *number)
  323. {
  324. int i;
  325. if (is_integer (number) && (i = atoi (number)) >= 0 && i <= 100)
  326. return TRUE;
  327. else
  328. return FALSE;
  329. }
  330. int
  331. is_option (char *str)
  332. {
  333. if (!str)
  334. return FALSE;
  335. else if (strspn (str, "-") == 1 || strspn (str, "-") == 2)
  336. return TRUE;
  337. else
  338. return FALSE;
  339. }
  340. #ifdef NEED_GETTIMEOFDAY
  341. int
  342. gettimeofday (struct timeval *tv, struct timezone *tz)
  343. {
  344. tv->tv_usec = 0;
  345. tv->tv_sec = (long) time ((time_t) 0);
  346. }
  347. #endif
  348. double
  349. delta_time (struct timeval tv)
  350. {
  351. struct timeval now;
  352. gettimeofday (&now, NULL);
  353. return ((double)(now.tv_sec - tv.tv_sec) + (double)(now.tv_usec - tv.tv_usec) / (double)1000000);
  354. }
  355. long
  356. deltime (struct timeval tv)
  357. {
  358. struct timeval now;
  359. gettimeofday (&now, NULL);
  360. return (now.tv_sec - tv.tv_sec)*1000000 + now.tv_usec - tv.tv_usec;
  361. }
  362. void
  363. strip (char *buffer)
  364. {
  365. size_t x;
  366. int i;
  367. for (x = strlen (buffer); x >= 1; x--) {
  368. i = x - 1;
  369. if (buffer[i] == ' ' ||
  370. buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t')
  371. buffer[i] = '\0';
  372. else
  373. break;
  374. }
  375. return;
  376. }
  377. /******************************************************************************
  378. *
  379. * Copies one string to another. Any previously existing data in
  380. * the destination string is lost.
  381. *
  382. * Example:
  383. *
  384. * char *str=NULL;
  385. * str = strscpy("This is a line of text with no trailing newline");
  386. *
  387. *****************************************************************************/
  388. char *
  389. strscpy (char *dest, const char *src)
  390. {
  391. if (src == NULL)
  392. return NULL;
  393. xasprintf (&dest, "%s", src);
  394. return dest;
  395. }
  396. /******************************************************************************
  397. *
  398. * Returns a pointer to the next line of a multiline string buffer
  399. *
  400. * Given a pointer string, find the text following the next sequence
  401. * of \r and \n characters. This has the effect of skipping blank
  402. * lines as well
  403. *
  404. * Example:
  405. *
  406. * Given text as follows:
  407. *
  408. * ==============================
  409. * This
  410. * is
  411. * a
  412. *
  413. * multiline string buffer
  414. * ==============================
  415. *
  416. * int i=0;
  417. * char *str=NULL;
  418. * char *ptr=NULL;
  419. * str = strscpy(str,"This\nis\r\na\n\nmultiline string buffer\n");
  420. * ptr = str;
  421. * while (ptr) {
  422. * printf("%d %s",i++,firstword(ptr));
  423. * ptr = strnl(ptr);
  424. * }
  425. *
  426. * Produces the following:
  427. *
  428. * 1 This
  429. * 2 is
  430. * 3 a
  431. * 4 multiline
  432. *
  433. * NOTE: The 'firstword()' function is conceptual only and does not
  434. * exist in this package.
  435. *
  436. * NOTE: Although the second 'ptr' variable is not strictly needed in
  437. * this example, it is good practice with these utilities. Once
  438. * the * pointer is advance in this manner, it may no longer be
  439. * handled with * realloc(). So at the end of the code fragment
  440. * above, * strscpy(str,"foo") work perfectly fine, but
  441. * strscpy(ptr,"foo") will * cause the the program to crash with
  442. * a segmentation fault.
  443. *
  444. *****************************************************************************/
  445. char *
  446. strnl (char *str)
  447. {
  448. size_t len;
  449. if (str == NULL)
  450. return NULL;
  451. str = strpbrk (str, "\r\n");
  452. if (str == NULL)
  453. return NULL;
  454. len = strspn (str, "\r\n");
  455. if (str[len] == '\0')
  456. return NULL;
  457. str += len;
  458. if (strlen (str) == 0)
  459. return NULL;
  460. return str;
  461. }
  462. /******************************************************************************
  463. *
  464. * Like strscpy, except only the portion of the source string up to
  465. * the provided delimiter is copied.
  466. *
  467. * Example:
  468. *
  469. * str = strpcpy(str,"This is a line of text with no trailing newline","x");
  470. * printf("%s\n",str);
  471. *
  472. * Produces:
  473. *
  474. *This is a line of te
  475. *
  476. *****************************************************************************/
  477. char *
  478. strpcpy (char *dest, const char *src, const char *str)
  479. {
  480. size_t len;
  481. if (src)
  482. len = strcspn (src, str);
  483. else
  484. return NULL;
  485. if (dest == NULL || strlen (dest) < len)
  486. dest = realloc (dest, len + 1);
  487. if (dest == NULL)
  488. die (STATE_UNKNOWN, _("failed realloc in strpcpy\n"));
  489. strncpy (dest, src, len);
  490. dest[len] = '\0';
  491. return dest;
  492. }
  493. /******************************************************************************
  494. *
  495. * Like strscat, except only the portion of the source string up to
  496. * the provided delimiter is copied.
  497. *
  498. * str = strpcpy(str,"This is a line of text with no trailing newline","x");
  499. * str = strpcat(str,"This is a line of text with no trailing newline","x");
  500. * printf("%s\n",str);
  501. *
  502. *This is a line of texThis is a line of tex
  503. *
  504. *****************************************************************************/
  505. char *
  506. strpcat (char *dest, const char *src, const char *str)
  507. {
  508. size_t len, l2;
  509. if (dest)
  510. len = strlen (dest);
  511. else
  512. len = 0;
  513. if (src) {
  514. l2 = strcspn (src, str);
  515. }
  516. else {
  517. return dest;
  518. }
  519. dest = realloc (dest, len + l2 + 1);
  520. if (dest == NULL)
  521. die (STATE_UNKNOWN, _("failed malloc in strscat\n"));
  522. strncpy (dest + len, src, l2);
  523. dest[len + l2] = '\0';
  524. return dest;
  525. }
  526. /******************************************************************************
  527. *
  528. * asprintf, but die on failure
  529. *
  530. ******************************************************************************/
  531. int
  532. xvasprintf (char **strp, const char *fmt, va_list ap)
  533. {
  534. int result = vasprintf (strp, fmt, ap);
  535. if (result == -1 || *strp == NULL)
  536. die (STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
  537. return result;
  538. }
  539. int
  540. xasprintf (char **strp, const char *fmt, ...)
  541. {
  542. va_list ap;
  543. int result;
  544. va_start (ap, fmt);
  545. result = xvasprintf (strp, fmt, ap);
  546. va_end (ap);
  547. return result;
  548. }
  549. /******************************************************************************
  550. *
  551. * Print perfdata in a standard format
  552. *
  553. ******************************************************************************/
  554. char *perfdata (const char *label,
  555. long int val,
  556. const char *uom,
  557. int warnp,
  558. long int warn,
  559. int critp,
  560. long int crit,
  561. int minp,
  562. long int minv,
  563. int maxp,
  564. long int maxv)
  565. {
  566. char *data = NULL;
  567. if (strpbrk (label, "'= "))
  568. xasprintf (&data, "'%s'=%ld%s;", label, val, uom);
  569. else
  570. xasprintf (&data, "%s=%ld%s;", label, val, uom);
  571. if (warnp)
  572. xasprintf (&data, "%s%ld;", data, warn);
  573. else
  574. xasprintf (&data, "%s;", data);
  575. if (critp)
  576. xasprintf (&data, "%s%ld;", data, crit);
  577. else
  578. xasprintf (&data, "%s;", data);
  579. if (minp)
  580. xasprintf (&data, "%s%ld", data, minv);
  581. if (maxp)
  582. xasprintf (&data, "%s;%ld", data, maxv);
  583. return data;
  584. }
  585. char *fperfdata (const char *label,
  586. double val,
  587. const char *uom,
  588. int warnp,
  589. double warn,
  590. int critp,
  591. double crit,
  592. int minp,
  593. double minv,
  594. int maxp,
  595. double maxv)
  596. {
  597. char *data = NULL;
  598. if (strpbrk (label, "'= "))
  599. xasprintf (&data, "'%s'=", label);
  600. else
  601. xasprintf (&data, "%s=", label);
  602. xasprintf (&data, "%s%f", data, val);
  603. xasprintf (&data, "%s%s;", data, uom);
  604. if (warnp)
  605. xasprintf (&data, "%s%f", data, warn);
  606. xasprintf (&data, "%s;", data);
  607. if (critp)
  608. xasprintf (&data, "%s%f", data, crit);
  609. xasprintf (&data, "%s;", data);
  610. if (minp)
  611. xasprintf (&data, "%s%f", data, minv);
  612. if (maxp) {
  613. xasprintf (&data, "%s;", data);
  614. xasprintf (&data, "%s%f", data, maxv);
  615. }
  616. return data;
  617. }
  618. char *sperfdata (const char *label,
  619. double val,
  620. const char *uom,
  621. char *warn,
  622. char *crit,
  623. int minp,
  624. double minv,
  625. int maxp,
  626. double maxv)
  627. {
  628. char *data = NULL;
  629. if (strpbrk (label, "'= "))
  630. xasprintf (&data, "'%s'=", label);
  631. else
  632. xasprintf (&data, "%s=", label);
  633. xasprintf (&data, "%s%f", data, val);
  634. xasprintf (&data, "%s%s;", data, uom);
  635. if (warn!=NULL)
  636. xasprintf (&data, "%s%s", data, warn);
  637. xasprintf (&data, "%s;", data);
  638. if (crit!=NULL)
  639. xasprintf (&data, "%s%s", data, crit);
  640. xasprintf (&data, "%s;", data);
  641. if (minp)
  642. xasprintf (&data, "%s%f", data, minv);
  643. if (maxp) {
  644. xasprintf (&data, "%s;", data);
  645. xasprintf (&data, "%s%f", data, maxv);
  646. }
  647. return data;
  648. }
  649. char *sperfdata_int (const char *label,
  650. int val,
  651. const char *uom,
  652. char *warn,
  653. char *crit,
  654. int minp,
  655. int minv,
  656. int maxp,
  657. int maxv)
  658. {
  659. char *data = NULL;
  660. if (strpbrk (label, "'= "))
  661. xasprintf (&data, "'%s'=", label);
  662. else
  663. xasprintf (&data, "%s=", label);
  664. xasprintf (&data, "%s%d", data, val);
  665. xasprintf (&data, "%s%s;", data, uom);
  666. if (warn!=NULL)
  667. xasprintf (&data, "%s%s", data, warn);
  668. xasprintf (&data, "%s;", data);
  669. if (crit!=NULL)
  670. xasprintf (&data, "%s%s", data, crit);
  671. xasprintf (&data, "%s;", data);
  672. if (minp)
  673. xasprintf (&data, "%s%d", data, minv);
  674. if (maxp) {
  675. xasprintf (&data, "%s;", data);
  676. xasprintf (&data, "%s%d", data, maxv);
  677. }
  678. return data;
  679. }
  680. /* set entire string to lower, no need to return as it works on string in place */
  681. void strntolower (char * test_char, int size) {
  682. char * ptr = test_char;
  683. for (; ptr < test_char+size; ++ptr)
  684. *ptr = tolower(*ptr);
  685. }
  686. /* set entire string to lower, no need to return as it works on string in place */
  687. void strntoupper (char * test_char, int size) {
  688. char * ptr = test_char;
  689. for (; ptr < test_char+size; ++ptr)
  690. *ptr = toupper(*ptr);
  691. }