expresp.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*+-------------------------------------------------------------------------
  2. expresp.c - HDB expect/respond per SCO Devices file
  3. wht@wht.net
  4. Meaning of some of the escape characters:
  5. \p - pause (approximately 1/4-1/2 second delay)
  6. \d - delay (2 seconds)
  7. \D - phone number/token
  8. \T - phone number with Dialcodes and character translation
  9. \N - null byte
  10. \K - insert a BREAK
  11. \E - turn on echo checking (for slow devices)
  12. \e - turn off echo checking
  13. \r - carriage return
  14. \c - no new-line
  15. \n - send new-line
  16. \nnn - send octal number
  17. \\ - send backslash
  18. \m### - sleep ### (decimal) milliseconds (non-standard)
  19. Speed - Hayes-specific "CONNECT" handler
  20. Defined functions:
  21. execute_expresp(expresp_script)
  22. expect(str)
  23. pcmd_expresp(param)
  24. respond(str)
  25. "Why should we live with such hurry and waste of life? We are
  26. determined to be starved before we are hungry? Men say that a
  27. stich in time saves nine, and so they take a thousand stiches
  28. today to save nine thousand tommorrow." -- Henry David Thoreau
  29. --------------------------------------------------------------------------*/
  30. /*+:EDITS:*/
  31. /*:04-26-2000-11:15-wht@bob-RELEASE 4.42 */
  32. /*:12-12-1997-21:11-wht@kepler-complete isolation of substituted ftime */
  33. /*:06-25-1997-00:16-wht@kepler-messed up a simple fix */
  34. /*:06-24-1997-20:24-wht@kepler-strdup clone allocated 1 too few bytes */
  35. /*:01-24-1997-02:37-wht@yuriatin-SOURCE RELEASE 4.00 */
  36. /*:09-11-1996-20:00-wht@yuriatin-3.48-major telnet,curses,structural overhaul */
  37. /*:08-20-1996-12:39-wht@kepler-locale/ctype fixes from ache@nagual.ru */
  38. /*:11-23-1995-11:20-wht@kepler-source control 3.37 for tsx-11 */
  39. /*:11-14-1995-10:23-wht@kepler-3.37.80-source control point: SOCKETS */
  40. /*:05-11-1995-15:55-wht@n4hgf-ck_sigint fools optimizing compilers */
  41. /*:03-21-1995-15:12-wht@n4hgf-ERDEBUG now essentially binary */
  42. /*:05-04-1994-04:39-wht@n4hgf-ECU release 3.30 */
  43. /*:01-04-1994-06:34-wht@n4hgf-CFG_DialTimeout + last_Speed_result fix */
  44. /*:09-10-1992-13:59-wht@n4hgf-ECU release 3.20 */
  45. /*:09-04-1992-19:07-wht@n4hgf-new msec delay syntax + harden */
  46. /*:08-22-1992-15:38-wht@n4hgf-ECU release 3.20 BETA */
  47. /*:12-16-1991-15:25-wht@n4hgf-allow for backslash in expect and respond */
  48. /*:10-09-1991-20:21-wht@n4hgf-bad llookfor echo argument */
  49. /*:08-01-1991-05:00-wht@n4hgf-\n sent CR not NL */
  50. /*:08-01-1991-04:31-wht@n4hgf-nap min of hzmsec if \m */
  51. /*:08-01-1991-04:22-wht@n4hgf-detect NULL expect string */
  52. /*:07-25-1991-12:57-wht@n4hgf-ECU release 3.10 */
  53. /*:07-17-1991-07:04-wht@n4hgf-avoid SCO UNIX nap bug */
  54. /*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
  55. #include "ecu.h"
  56. #include "ecuerror.h"
  57. #include "esd.h"
  58. #include "var.h"
  59. #include "procedure.h"
  60. #define MAX_FIELDS 50 /* max fields in a chat script */
  61. #define MAX_EXPECT 63 /* max length of expect string */
  62. #define DEFAULT_TIMEOUT_MSECS (10*1000L)
  63. #define ERDEBUG(verb,str,arg) if(expresp_verbosity > (verb)) \
  64. pprintf(str,arg)
  65. long atol();
  66. char *strip_phone_num();
  67. char *dialcodes_translate();
  68. int expresp_verbosity = 0;
  69. UINT32 expect_timeout_msecs = DEFAULT_TIMEOUT_MSECS;
  70. int expresp_echo_check = 0;
  71. char last_Speed_result[32];
  72. /*+-------------------------------------------------------------------------
  73. expect(str) - expect (read) string from remote
  74. return code on failure, 0 on success
  75. --------------------------------------------------------------------------*/
  76. int
  77. expect(str)
  78. char *str;
  79. {
  80. int erc;
  81. int itmp;
  82. char op;
  83. char s8[8];
  84. char parsebuf[MAX_EXPECT + 1];
  85. int remaining = MAX_EXPECT;
  86. long atol();
  87. char *cp;
  88. char *parsed = parsebuf;
  89. int old_ttymode = get_ttymode();
  90. if (!str)
  91. {
  92. ERDEBUG(0, "\nexpect string cannot be NULL\n", 0);
  93. return (eExpectRespondFail);
  94. }
  95. if (old_ttymode != 2)
  96. ttymode(2);
  97. /*
  98. * ~[?]
  99. */
  100. if ((*str == '~') && *(str + 1) && (*(str + 2) == '['))
  101. {
  102. op = *(str + 1);
  103. str += 3;
  104. switch (op)
  105. {
  106. case 'm': /* msec expect timeout */
  107. case 't': /* sec expect timeout */
  108. expect_timeout_msecs = atol(str);
  109. if (op == 't')
  110. expect_timeout_msecs *= 1000L;
  111. ERDEBUG(1, "expect timeout = %lu msec\n", expect_timeout_msecs);
  112. break;
  113. default:
  114. ERDEBUG(0, "\nexpect: invalid subop: ~%c[]\n", op);
  115. break;
  116. }
  117. if (cp = strchr(str, ']'))
  118. str = cp + 1;
  119. else
  120. {
  121. ERDEBUG(0, "\nexpect: missing ] after ~[%c\n", op);
  122. erc = eExpectRespondFail;
  123. goto DID_NOT_GET_EXPECTED;
  124. }
  125. }
  126. ERDEBUG(1, "expect: <<%s>>\n", str);
  127. if (!strlen(str) || !strcmp(str, "\"\""))
  128. goto GOT_EXPECTED;
  129. if (!strcmp(str, "Speed"))
  130. {
  131. LRWT lr;
  132. long ms_start;
  133. long ms_now;
  134. struct TIMEB now_timeb;
  135. Ftime(&now_timeb);
  136. ms_start = (now_timeb.time * 1000) + now_timeb.millitm;
  137. do
  138. {
  139. last_Speed_result[0] = 0;
  140. lr.to1 = CFG_DialTimeout * 1000L;
  141. lr.to2 = 300L;
  142. /* allow interrupts + cooked read */
  143. lr.raw_flag = 0x80;
  144. lr.buffer = last_Speed_result;
  145. lr.bufsize = sizeof(last_Speed_result);
  146. lr.delim = "\n";
  147. lr.echo_flag = !!expresp_verbosity;
  148. lgets_timeout(&lr);
  149. Ftime(&now_timeb);
  150. ms_now = (now_timeb.time * 1000) + now_timeb.millitm;
  151. }
  152. while (!ck_sigint() && !lr.count &&
  153. ((ms_now - ms_start) < CFG_DialTimeout * 1000L));
  154. if (ck_sigint() || strncmp(lr.buffer, "CONNECT", 7))
  155. goto DID_NOT_GET_EXPECTED;
  156. else
  157. goto GOT_EXPECTED;
  158. }
  159. cp = str;
  160. while (remaining && *cp)
  161. {
  162. if (*cp == '\\')
  163. {
  164. if (!*(++cp)) /* if no character after escape, ... */
  165. {
  166. ERDEBUG(1, " error: str ended with '\\'\n", 0);
  167. goto DID_NOT_GET_EXPECTED;
  168. }
  169. if (isdigit((uchar) * cp)) /* handle \ooo */
  170. {
  171. strncpy(s8, cp, 3);
  172. s8[3] = 0;
  173. sscanf(s8, "%o", &itmp);
  174. cp += strspn(s8, "01234567");
  175. *parsed++ = (char)itmp;
  176. remaining--;
  177. continue;
  178. }
  179. switch (*cp)
  180. {
  181. case 'n':
  182. *parsed++ = 0x0A;
  183. remaining--;
  184. break;
  185. case 'r':
  186. *parsed++ = 0x0D;
  187. remaining--;
  188. break;
  189. case '\\':
  190. *parsed++ = '\\';
  191. remaining--;
  192. break;
  193. case '~':
  194. *parsed++ = '~';
  195. remaining--;
  196. break;
  197. default:
  198. ERDEBUG(0, " meaningless here: \\%c\n", *cp);
  199. break;
  200. }
  201. cp++;
  202. }
  203. else
  204. {
  205. *parsed++ = *cp++;
  206. remaining--;
  207. }
  208. }
  209. *parsed = 0;
  210. if (!remaining)
  211. ERDEBUG(0, " expect string too long\n", 0);
  212. if (expresp_verbosity >= 1)
  213. hex_dump(parsebuf, strlen(parsebuf), "expecting", 1);
  214. if (llookfor(parsebuf, expect_timeout_msecs, !!expresp_verbosity))
  215. {
  216. GOT_EXPECTED:
  217. ERDEBUG(1, "[EXPECT SUCCEEDED]\n", 0);
  218. erc = 0;
  219. goto RESTORE_TTYMODE_AND_RETURN_ERC;
  220. }
  221. DID_NOT_GET_EXPECTED:
  222. ERDEBUG(1, "[EXPECT FAILED%s]\n", (ck_sigint())? " (interrupted)" : "");
  223. if (ck_sigint())
  224. {
  225. sigint = 0;
  226. erc = eCONINT;
  227. }
  228. else
  229. erc = eExpectRespondFail;
  230. goto RESTORE_TTYMODE_AND_RETURN_ERC;
  231. RESTORE_TTYMODE_AND_RETURN_ERC:
  232. if (old_ttymode != 2)
  233. ttymode(old_ttymode);
  234. return (erc);
  235. } /* end of expect */
  236. /*+-------------------------------------------------------------------------
  237. respond(str) - send to remote
  238. we enable SIGINT processing in here and return if 'sigint'
  239. detected, but here, unlike many other places, we do *not* reset
  240. sigint (since we do not really "handle" it)
  241. --------------------------------------------------------------------------*/
  242. void
  243. respond(str)
  244. char *str;
  245. {
  246. int itmp;
  247. long nap_msec;
  248. char s8[8];
  249. char *cp;
  250. char *phnum;
  251. char op;
  252. int send_no_cr = 0;
  253. int old_ttymode = get_ttymode();
  254. if (ck_sigint())
  255. return;
  256. ttymode(2); /* enable SIGINT/sigint */
  257. ERDEBUG(1, "respond: <<%s>>\n", str);
  258. while (*str)
  259. {
  260. if (*str == '\\')
  261. {
  262. if (isdigit((uchar) * ++str)) /* handle \ooo */
  263. {
  264. strncpy(s8, str, 3);
  265. s8[3] = 0;
  266. sscanf(s8, "%o", &itmp);
  267. str += strspn(s8, "01234567") - 1; /* -1 because str++
  268. * later */
  269. lputc((char)itmp);
  270. }
  271. else
  272. switch (*str)
  273. {
  274. case 'p': /* pause (approximately 1/4-1/2 second
  275. * delay) */
  276. ldraino(0); /* wait for output to drain */
  277. if (Nap(400L) < 0)
  278. goto FUNC_RETURN;
  279. break;
  280. case 'M': /* CLOCAL on */
  281. case 'm': /* CLOCAL off */
  282. itmp = (*str == 'M');
  283. lCLOCAL(itmp);
  284. ERDEBUG(1, "CLOCAL set %s\n", (itmp) ? "ON" : "OFF");
  285. break;
  286. case 'd': /* delay (2 seconds) */
  287. ldraino(0); /* wait for output to drain */
  288. if (Nap(2000L) < 0)
  289. goto FUNC_RETURN;
  290. break;
  291. case 'D': /* phone number/token */
  292. cp = strip_phone_num(shm->Ltelno);
  293. if (expresp_echo_check)
  294. lputs_paced(40, cp);
  295. else
  296. lputs(cp);
  297. break;
  298. case 'T': /* phnum with Dialcodes and char
  299. * translation */
  300. phnum = strip_phone_num(shm->Ltelno);
  301. cp = dialcodes_translate(&phnum);
  302. if (expresp_echo_check)
  303. {
  304. lputs_paced(40, cp);
  305. lputs_paced(40, phnum);
  306. }
  307. else
  308. {
  309. lputs(cp);
  310. lputs(phnum);
  311. }
  312. break;
  313. case 'N': /* null byte */
  314. lputc(0);
  315. break;
  316. case 'K': /* insert a BREAK */
  317. lbreak();
  318. break;
  319. case 'E': /* turn on echo checking (for slow
  320. * devices) */
  321. expresp_echo_check = 1;
  322. break;
  323. case 'e': /* turn off echo checking */
  324. expresp_echo_check = 0;
  325. break;
  326. case 'r': /* carriage return */
  327. lputc(0x0D);
  328. break;
  329. case 'c': /* no new-line */
  330. send_no_cr = 1;
  331. break;
  332. case 'n': /* send new-line */
  333. lputc(0x0A);
  334. break;
  335. case '\\': /* send backslash */
  336. lputc('\\');
  337. break;
  338. case '~': /* send tilde */
  339. lputc('~');
  340. break;
  341. }
  342. }
  343. else if ((*str == '~') && *(str + 1) && (*(str + 2) == '['))
  344. {
  345. op = *(str + 1);
  346. str += 3;
  347. switch (op)
  348. {
  349. case 'n': /* nap for milliseconds */
  350. nap_msec = atol(str);
  351. if (nap_msec < 0L)
  352. nap_msec = 0;
  353. if (nap_msec >= 500)
  354. ERDEBUG(1, "nap for %lu msec\n", nap_msec);
  355. Nap(nap_msec);
  356. break;
  357. default:
  358. ERDEBUG(0, "\nrespond: invalid subop: ~%c[]\n", op);
  359. break;
  360. }
  361. if (cp = strchr(str, ']'))
  362. str = cp + 1;
  363. else
  364. {
  365. ERDEBUG(0, "\nrespond: missing ] after ~[%c\n", op);
  366. goto FUNC_RETURN;
  367. }
  368. }
  369. else
  370. lputc(*str);
  371. if (expresp_echo_check)
  372. {
  373. ldraino(1); /* wait for output to drain, then flush input */
  374. Nap(40L); /* fake it */
  375. }
  376. str++;
  377. }
  378. if (!send_no_cr)
  379. lputc(0x0D);
  380. FUNC_RETURN:
  381. ttymode(old_ttymode);
  382. } /* end of respond */
  383. /*+-------------------------------------------------------------------------
  384. execute_expresp(expresp_script)
  385. return 0 on success, else error code
  386. --------------------------------------------------------------------------*/
  387. int
  388. execute_expresp(expresp_script)
  389. char *expresp_script;
  390. {
  391. char *fields[MAX_FIELDS + 1];
  392. int ifields;
  393. int nfields;
  394. int erc;
  395. char *expresp_copy = 0;
  396. char *expect_this;
  397. char *send_on_fail;
  398. #define EXPECT_STATE (!(ifields & 1)) /* even numbered fields are expect */
  399. expresp_echo_check = 0;
  400. last_Speed_result[0] = 0;
  401. ERDEBUG(1, "[EXPECT/RESPOND INITIAL TIMEOUT %ld MSEC]\n",
  402. expect_timeout_msecs);
  403. /*
  404. * dup script so we can poke holes in it with strtok()
  405. *
  406. * should be expresp_copy = strdup(expresp_script), but this was added
  407. * late in beta and I didn't know if NetBSD had strdup(); BSD 4.1 did
  408. * not; but, the following hack is mea culpa -- wht w/no remorse
  409. */
  410. if (!(expresp_copy = malloc(strlen(expresp_script) + 1)))
  411. return (eNoMemory);
  412. strcpy(expresp_copy, expresp_script);
  413. /*
  414. * tokenize the script
  415. */
  416. build_arg_array(expresp_copy, fields, MAX_FIELDS, &nfields);
  417. if (!nfields) /* if no script, assume success */
  418. {
  419. ERDEBUG(1, "[EMPTY SCRIPT - EXPECT/RESPOND SUCCEEDED]\n", 0);
  420. erc = 0;
  421. goto FUNC_RETURN;
  422. }
  423. /*
  424. * work the tokens, starting with expect
  425. */
  426. for (ifields = 0; ifields < nfields; ifields++)
  427. {
  428. if (ck_sigint())
  429. break;
  430. if (EXPECT_STATE)
  431. {
  432. expect_this = fields[ifields];
  433. while (1) /* until break or return(error) */
  434. {
  435. if (send_on_fail = strchr(expect_this, '-'))
  436. *send_on_fail++ = 0;
  437. if (!(erc = expect(expect_this)))
  438. goto OUT_OF_HERE; /* double break */
  439. if ((erc != eExpectRespondFail) || !send_on_fail)
  440. {
  441. ERDEBUG(1, "[EXPECT/RESPOND FAILED]\n", 0);
  442. erc = eExpectRespondFail;
  443. goto FUNC_RETURN;
  444. }
  445. if (expect_this = strchr(send_on_fail, '-'))
  446. *expect_this++ = 0;
  447. if (ck_sigint())
  448. break;
  449. respond(send_on_fail);
  450. }
  451. }
  452. else
  453. respond(fields[ifields]);
  454. }
  455. OUT_OF_HERE: ;
  456. /*
  457. * if console interrupt
  458. */
  459. if (ck_sigint())
  460. {
  461. sigint = 0;
  462. ERDEBUG(1, "[CONSOLE INTERRUPT]\n", 0);
  463. erc = eCONINT;
  464. goto FUNC_RETURN;
  465. }
  466. /*
  467. * wow
  468. */
  469. ERDEBUG(1, "[EXPECT/RESPOND SUCCEEDED]\n", 0);
  470. erc = 0;
  471. FUNC_RETURN:
  472. if (expresp_copy)
  473. free(expresp_copy);
  474. return (erc);
  475. } /* end of execute_expresp */
  476. /*+-------------------------------------------------------------------------
  477. pcmd_expresp(param)
  478. expresp [-v[v...]] <exp-resp-str> [<timeout_msecs>]
  479. --------------------------------------------------------------------------*/
  480. int
  481. pcmd_expresp(param)
  482. ESD *param;
  483. {
  484. int erc;
  485. int itmp;
  486. char *cp;
  487. ESD *tesd;
  488. char switches[8];
  489. if (!(tesd = esdalloc(ESD_NOMSZ)))
  490. return (eNoMemory);
  491. get_switches(param, switches, sizeof(switches));
  492. if (erc = gstr(param, tesd, 0))
  493. {
  494. esdfree(tesd);
  495. return (erc);
  496. }
  497. #define DEFAULT_TIMEOUT_MSECS (10*1000L)
  498. expect_timeout_msecs = DEFAULT_TIMEOUT_MSECS;
  499. if (!expresp_verbosity)
  500. expresp_verbosity = (!!strchr(switches, 'v')) || proc_trace;
  501. if (expresp_verbosity)
  502. {
  503. cp = switches;
  504. itmp = 0;
  505. while (*cp)
  506. itmp += (*cp++ == 'v');
  507. if (itmp > 1)
  508. expresp_verbosity = itmp;
  509. }
  510. if (erc = gint(param, &expect_timeout_msecs))
  511. {
  512. /* if something there non-integer */
  513. if (!end_of_cmd(param))
  514. {
  515. erc = eSyntaxError;
  516. goto FUNC_RETURN;
  517. }
  518. }
  519. erc = execute_expresp(tesd->pb);
  520. FUNC_RETURN:
  521. esdfree(tesd);
  522. iv[0] = !!erc;
  523. if (proc_trace)
  524. pprintf("$i00 = %7ld (0x%08lx,0%lo)\n", iv[0], iv[0], iv[0]);
  525. if (erc == eExpectRespondFail)
  526. erc = 0;
  527. return (erc);
  528. } /* end of pcmd_expresp */
  529. /* vi: set tabstop=4 shiftwidth=4: */
  530. /* end of expresp.c */