chat.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788
  1. /*
  2. * Chat -- a program for automatic session establishment (i.e. dial
  3. * the phone and log in).
  4. *
  5. * Standard termination codes:
  6. * 0 - successful completion of the script
  7. * 1 - invalid argument, expect string too large, etc.
  8. * 2 - error on an I/O operation or fatal error condition.
  9. * 3 - timeout waiting for a simple string.
  10. * 4 - the first string declared as "ABORT"
  11. * 5 - the second string declared as "ABORT"
  12. * 6 - ... and so on for successive ABORT strings.
  13. *
  14. * This software is in the public domain.
  15. *
  16. * -----------------
  17. * 22-May-99 added environment substitutuion, enabled with -E switch.
  18. * Andreas Arens <andras@cityweb.de>.
  19. *
  20. * 12-May-99 added a feature to read data to be sent from a file,
  21. * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
  22. *
  23. * added -T and -U option and \T and \U substitution to pass a phone
  24. * number into chat script. Two are needed for some ISDN TA applications.
  25. * Keith Dart <kdart@cisco.com>
  26. *
  27. *
  28. * Added SAY keyword to send output to stderr.
  29. * This allows to turn ECHO OFF and to output specific, user selected,
  30. * text to give progress messages. This best works when stderr
  31. * exists (i.e.: pppd in nodetach mode).
  32. *
  33. * Added HANGUP directives to allow for us to be called
  34. * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
  35. * We rely on timeouts in that case.
  36. *
  37. * Added CLR_ABORT to clear previously set ABORT string. This has been
  38. * dictated by the HANGUP above as "NO CARRIER" (for example) must be
  39. * an ABORT condition until we know the other host is going to close
  40. * the connection for call back. As soon as we have completed the
  41. * first stage of the call back sequence, "NO CARRIER" is a valid, non
  42. * fatal string. As soon as we got called back (probably get "CONNECT"),
  43. * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
  44. * Note that CLR_ABORT packs the abort_strings[] array so that we do not
  45. * have unused entries not being reclaimed.
  46. *
  47. * In the same vein as above, added CLR_REPORT keyword.
  48. *
  49. * Allow for comments. Line starting with '#' are comments and are
  50. * ignored. If a '#' is to be expected as the first character, the
  51. * expect string must be quoted.
  52. *
  53. *
  54. * Francis Demierre <Francis@SwissMail.Com>
  55. * Thu May 15 17:15:40 MET DST 1997
  56. *
  57. *
  58. * Added -r "report file" switch & REPORT keyword.
  59. * Robert Geer <bgeer@xmission.com>
  60. *
  61. * Added -s "use stderr" and -S "don't use syslog" switches.
  62. * June 18, 1997
  63. * Karl O. Pinc <kop@meme.com>
  64. *
  65. *
  66. * Added -e "echo" switch & ECHO keyword
  67. * Dick Streefland <dicks@tasking.nl>
  68. *
  69. *
  70. * Considerable updates and modifications by
  71. * Al Longyear <longyear@pobox.com>
  72. * Paul Mackerras <paulus@cs.anu.edu.au>
  73. *
  74. *
  75. * The original author is:
  76. *
  77. * Karl Fox <karl@MorningStar.Com>
  78. * Morning Star Technologies, Inc.
  79. * 1760 Zollinger Road
  80. * Columbus, OH 43221
  81. * (614)451-1883
  82. *
  83. */
  84. #ifndef __STDC__
  85. #define const
  86. #endif
  87. #ifndef lint
  88. static const char rcsid[] = "$Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp $";
  89. #endif
  90. #include <stdio.h>
  91. #include <ctype.h>
  92. #include <time.h>
  93. #include <fcntl.h>
  94. #include <signal.h>
  95. #include <errno.h>
  96. #include <string.h>
  97. #include <stdlib.h>
  98. #include <unistd.h>
  99. #include <sys/types.h>
  100. #include <sys/stat.h>
  101. #include <syslog.h>
  102. #ifndef TERMIO
  103. #undef TERMIOS
  104. #define TERMIOS
  105. #endif
  106. #ifdef TERMIO
  107. #include <termio.h>
  108. #endif
  109. #ifdef TERMIOS
  110. #include <termios.h>
  111. #endif
  112. #define STR_LEN 1024
  113. #ifndef SIGTYPE
  114. #define SIGTYPE void
  115. #endif
  116. #undef __P
  117. #undef __V
  118. #ifdef __STDC__
  119. #include <stdarg.h>
  120. #define __V(x) x
  121. #define __P(x) x
  122. #else
  123. #include <varargs.h>
  124. #define __V(x) (va_alist) va_dcl
  125. #define __P(x) ()
  126. #define const
  127. #endif
  128. #ifndef O_NONBLOCK
  129. #define O_NONBLOCK O_NDELAY
  130. #endif
  131. #ifdef SUNOS
  132. extern int sys_nerr;
  133. extern char *sys_errlist[];
  134. #define memmove(to, from, n) bcopy(from, to, n)
  135. #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
  136. "unknown error")
  137. #endif
  138. /*************** Micro getopt() *********************************************/
  139. #define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
  140. (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
  141. &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
  142. #define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
  143. (_O=4,(char*)0):(char*)0)
  144. #define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
  145. #define ARG(c,v) (c?(--c,*v++):(char*)0)
  146. static int _O = 0; /* Internal state */
  147. /*************** Micro getopt() *********************************************/
  148. char *program_name;
  149. #define MAX_ABORTS 50
  150. #define MAX_REPORTS 50
  151. #define DEFAULT_CHAT_TIMEOUT 45
  152. int echo = 0;
  153. int verbose = 0;
  154. int to_log = 1;
  155. int to_stderr = 0;
  156. int Verbose = 0;
  157. int quiet = 0;
  158. int report = 0;
  159. int use_env = 0;
  160. int exit_code = 0;
  161. FILE* report_fp = (FILE *) 0;
  162. char *report_file = (char *) 0;
  163. char *chat_file = (char *) 0;
  164. char *phone_num = (char *) 0;
  165. char *phone_num2 = (char *) 0;
  166. int timeout = DEFAULT_CHAT_TIMEOUT;
  167. int have_tty_parameters = 0;
  168. #ifdef TERMIO
  169. #define term_parms struct termio
  170. #define get_term_param(param) ioctl(0, TCGETA, param)
  171. #define set_term_param(param) ioctl(0, TCSETA, param)
  172. struct termio saved_tty_parameters;
  173. #endif
  174. #ifdef TERMIOS
  175. #define term_parms struct termios
  176. #define get_term_param(param) tcgetattr(0, param)
  177. #define set_term_param(param) tcsetattr(0, TCSANOW, param)
  178. struct termios saved_tty_parameters;
  179. #endif
  180. char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
  181. fail_buffer[50];
  182. int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
  183. int clear_abort_next = 0;
  184. char *report_string[MAX_REPORTS] ;
  185. char report_buffer[256] ;
  186. int n_reports = 0, report_next = 0, report_gathering = 0 ;
  187. int clear_report_next = 0;
  188. int say_next = 0, hup_next = 0;
  189. void *dup_mem __P((void *b, size_t c));
  190. void *copy_of __P((char *s));
  191. char *grow __P((char *s, char **p, size_t len));
  192. void usage __P((void));
  193. void msgf __P((const char *fmt, ...));
  194. void fatal __P((int code, const char *fmt, ...));
  195. SIGTYPE sigalrm __P((int signo));
  196. SIGTYPE sigint __P((int signo));
  197. SIGTYPE sigterm __P((int signo));
  198. SIGTYPE sighup __P((int signo));
  199. void unalarm __P((void));
  200. void init __P((void));
  201. void set_tty_parameters __P((void));
  202. void echo_stderr __P((int));
  203. void break_sequence __P((void));
  204. void terminate __P((int status));
  205. void do_file __P((char *chat_file));
  206. int get_string __P((register char *string));
  207. int put_string __P((register char *s));
  208. int write_char __P((int c));
  209. int put_char __P((int c));
  210. int get_char __P((void));
  211. void chat_send __P((register char *s));
  212. char *character __P((int c));
  213. void chat_expect __P((register char *s));
  214. char *clean __P((register char *s, int sending));
  215. void break_sequence __P((void));
  216. void terminate __P((int status));
  217. void pack_array __P((char **array, int end));
  218. char *expect_strtok __P((char *, char *));
  219. int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
  220. int main __P((int, char *[]));
  221. void *dup_mem(b, c)
  222. void *b;
  223. size_t c;
  224. {
  225. void *ans = malloc (c);
  226. if (!ans)
  227. fatal(2, "memory error!");
  228. memcpy (ans, b, c);
  229. return ans;
  230. }
  231. void *copy_of (s)
  232. char *s;
  233. {
  234. return dup_mem (s, strlen (s) + 1);
  235. }
  236. /* grow a char buffer and keep a pointer offset */
  237. char *grow(s, p, len)
  238. char *s;
  239. char **p;
  240. size_t len;
  241. {
  242. size_t l = *p - s; /* save p as distance into s */
  243. s = realloc(s, len);
  244. if (!s)
  245. fatal(2, "memory error!");
  246. *p = s + l; /* restore p */
  247. return s;
  248. }
  249. /*
  250. * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
  251. * [ -r report-file ] \
  252. * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
  253. *
  254. * Perform a UUCP-dialer-like chat script on stdin and stdout.
  255. */
  256. int
  257. main(argc, argv)
  258. int argc;
  259. char **argv;
  260. {
  261. int option;
  262. char *arg;
  263. program_name = *argv;
  264. tzset();
  265. while ((option = OPTION(argc, argv)) != 0) {
  266. switch (option) {
  267. case 'e':
  268. ++echo;
  269. break;
  270. case 'E':
  271. ++use_env;
  272. break;
  273. case 'v':
  274. ++verbose;
  275. break;
  276. case 'V':
  277. ++Verbose;
  278. break;
  279. case 's':
  280. ++to_stderr;
  281. break;
  282. case 'S':
  283. to_log = 0;
  284. break;
  285. case 'f':
  286. if ((arg = OPTARG(argc, argv)) != NULL)
  287. chat_file = copy_of(arg);
  288. else
  289. usage();
  290. break;
  291. case 't':
  292. if ((arg = OPTARG(argc, argv)) != NULL)
  293. timeout = atoi(arg);
  294. else
  295. usage();
  296. break;
  297. case 'r':
  298. arg = OPTARG (argc, argv);
  299. if (arg) {
  300. if (report_fp != NULL)
  301. fclose (report_fp);
  302. report_file = copy_of (arg);
  303. report_fp = fopen (report_file, "a");
  304. if (report_fp != NULL) {
  305. if (verbose)
  306. fprintf (report_fp, "Opening \"%s\"...\n",
  307. report_file);
  308. report = 1;
  309. }
  310. }
  311. break;
  312. case 'T':
  313. if ((arg = OPTARG(argc, argv)) != NULL)
  314. phone_num = copy_of(arg);
  315. else
  316. usage();
  317. break;
  318. case 'U':
  319. if ((arg = OPTARG(argc, argv)) != NULL)
  320. phone_num2 = copy_of(arg);
  321. else
  322. usage();
  323. break;
  324. default:
  325. usage();
  326. break;
  327. }
  328. }
  329. /*
  330. * Default the report file to the stderr location
  331. */
  332. if (report_fp == NULL)
  333. report_fp = stderr;
  334. if (to_log) {
  335. #ifdef ultrix
  336. openlog("chat", LOG_PID);
  337. #else
  338. openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
  339. if (verbose)
  340. setlogmask(LOG_UPTO(LOG_INFO));
  341. else
  342. setlogmask(LOG_UPTO(LOG_WARNING));
  343. #endif
  344. }
  345. init();
  346. if (chat_file != NULL) {
  347. arg = ARG(argc, argv);
  348. if (arg != NULL)
  349. usage();
  350. else
  351. do_file (chat_file);
  352. } else {
  353. while ((arg = ARG(argc, argv)) != NULL) {
  354. chat_expect(arg);
  355. if ((arg = ARG(argc, argv)) != NULL)
  356. chat_send(arg);
  357. }
  358. }
  359. terminate(0);
  360. return 0;
  361. }
  362. /*
  363. * Process a chat script when read from a file.
  364. */
  365. void do_file (chat_file)
  366. char *chat_file;
  367. {
  368. int linect, sendflg;
  369. char *sp, *arg, quote;
  370. char buf [STR_LEN];
  371. FILE *cfp;
  372. cfp = fopen (chat_file, "r");
  373. if (cfp == NULL)
  374. fatal(1, "%s -- open failed: %m", chat_file);
  375. linect = 0;
  376. sendflg = 0;
  377. while (fgets(buf, STR_LEN, cfp) != NULL) {
  378. sp = strchr (buf, '\n');
  379. if (sp)
  380. *sp = '\0';
  381. linect++;
  382. sp = buf;
  383. /* lines starting with '#' are comments. If a real '#'
  384. is to be expected, it should be quoted .... */
  385. if ( *sp == '#' )
  386. continue;
  387. while (*sp != '\0') {
  388. if (*sp == ' ' || *sp == '\t') {
  389. ++sp;
  390. continue;
  391. }
  392. if (*sp == '"' || *sp == '\'') {
  393. quote = *sp++;
  394. arg = sp;
  395. while (*sp != quote) {
  396. if (*sp == '\0')
  397. fatal(1, "unterminated quote (line %d)", linect);
  398. if (*sp++ == '\\') {
  399. if (*sp != '\0')
  400. ++sp;
  401. }
  402. }
  403. }
  404. else {
  405. arg = sp;
  406. while (*sp != '\0' && *sp != ' ' && *sp != '\t')
  407. ++sp;
  408. }
  409. if (*sp != '\0')
  410. *sp++ = '\0';
  411. if (sendflg)
  412. chat_send (arg);
  413. else
  414. chat_expect (arg);
  415. sendflg = !sendflg;
  416. }
  417. }
  418. fclose (cfp);
  419. }
  420. /*
  421. * We got an error parsing the command line.
  422. */
  423. void usage()
  424. {
  425. fprintf(stderr, "\
  426. Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\
  427. [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
  428. exit(1);
  429. }
  430. char line[1024];
  431. /*
  432. * Send a message to syslog and/or stderr.
  433. */
  434. void msgf __V((const char *fmt, ...))
  435. {
  436. va_list args;
  437. #ifdef __STDC__
  438. va_start(args, fmt);
  439. #else
  440. char *fmt;
  441. va_start(args);
  442. fmt = va_arg(args, char *);
  443. #endif
  444. vfmtmsg(line, sizeof(line), fmt, args);
  445. if (to_log)
  446. syslog(LOG_INFO, "%s", line);
  447. if (to_stderr)
  448. fprintf(stderr, "%s\n", line);
  449. }
  450. /*
  451. * Print an error message and terminate.
  452. */
  453. void fatal __V((int code, const char *fmt, ...))
  454. {
  455. va_list args;
  456. #ifdef __STDC__
  457. va_start(args, fmt);
  458. #else
  459. int code;
  460. char *fmt;
  461. va_start(args);
  462. code = va_arg(args, int);
  463. fmt = va_arg(args, char *);
  464. #endif
  465. vfmtmsg(line, sizeof(line), fmt, args);
  466. if (to_log)
  467. syslog(LOG_ERR, "%s", line);
  468. if (to_stderr)
  469. fprintf(stderr, "%s\n", line);
  470. terminate(code);
  471. }
  472. int alarmed = 0;
  473. SIGTYPE sigalrm(signo)
  474. int signo;
  475. {
  476. int flags;
  477. alarm(1);
  478. alarmed = 1; /* Reset alarm to avoid race window */
  479. signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
  480. if ((flags = fcntl(0, F_GETFL, 0)) == -1)
  481. fatal(2, "Can't get file mode flags on stdin: %m");
  482. if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
  483. fatal(2, "Can't set file mode flags on stdin: %m");
  484. if (verbose)
  485. msgf("alarm");
  486. }
  487. void unalarm()
  488. {
  489. int flags;
  490. if ((flags = fcntl(0, F_GETFL, 0)) == -1)
  491. fatal(2, "Can't get file mode flags on stdin: %m");
  492. if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
  493. fatal(2, "Can't set file mode flags on stdin: %m");
  494. }
  495. SIGTYPE sigint(signo)
  496. int signo;
  497. {
  498. fatal(2, "SIGINT");
  499. }
  500. SIGTYPE sigterm(signo)
  501. int signo;
  502. {
  503. fatal(2, "SIGTERM");
  504. }
  505. SIGTYPE sighup(signo)
  506. int signo;
  507. {
  508. fatal(2, "SIGHUP");
  509. }
  510. void init()
  511. {
  512. signal(SIGINT, sigint);
  513. signal(SIGTERM, sigterm);
  514. signal(SIGHUP, sighup);
  515. set_tty_parameters();
  516. signal(SIGALRM, sigalrm);
  517. alarm(0);
  518. alarmed = 0;
  519. }
  520. void set_tty_parameters()
  521. {
  522. #if defined(get_term_param)
  523. term_parms t;
  524. if (get_term_param (&t) < 0)
  525. fatal(2, "Can't get terminal parameters: %m");
  526. saved_tty_parameters = t;
  527. have_tty_parameters = 1;
  528. t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
  529. t.c_oflag = 0;
  530. t.c_lflag = 0;
  531. t.c_cc[VERASE] =
  532. t.c_cc[VKILL] = 0;
  533. t.c_cc[VMIN] = 1;
  534. t.c_cc[VTIME] = 0;
  535. if (set_term_param (&t) < 0)
  536. fatal(2, "Can't set terminal parameters: %m");
  537. #endif
  538. }
  539. void break_sequence()
  540. {
  541. #ifdef TERMIOS
  542. tcsendbreak (0, 0);
  543. #endif
  544. }
  545. void terminate(status)
  546. int status;
  547. {
  548. static int terminating = 0;
  549. if (terminating)
  550. exit(status);
  551. terminating = 1;
  552. echo_stderr(-1);
  553. /*
  554. * Allow the last of the report string to be gathered before we terminate.
  555. */
  556. if (report_gathering) {
  557. int c, rep_len;
  558. rep_len = strlen(report_buffer);
  559. while (rep_len + 1 <= sizeof(report_buffer)) {
  560. alarm(1);
  561. c = get_char();
  562. alarm(0);
  563. if (c < 0 || iscntrl(c))
  564. break;
  565. report_buffer[rep_len] = c;
  566. ++rep_len;
  567. }
  568. report_buffer[rep_len] = 0;
  569. fprintf (report_fp, "chat: %s\n", report_buffer);
  570. }
  571. if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
  572. if (verbose)
  573. fprintf (report_fp, "Closing \"%s\".\n", report_file);
  574. fclose (report_fp);
  575. report_fp = (FILE *) NULL;
  576. }
  577. #if defined(get_term_param)
  578. if (have_tty_parameters) {
  579. if (set_term_param (&saved_tty_parameters) < 0)
  580. fatal(2, "Can't restore terminal parameters: %m");
  581. }
  582. #endif
  583. exit(status);
  584. }
  585. /*
  586. * 'Clean up' this string.
  587. */
  588. char *clean(s, sending)
  589. register char *s;
  590. int sending; /* set to 1 when sending (putting) this string. */
  591. {
  592. char cur_chr;
  593. char *s1, *p, *phchar;
  594. int add_return = sending;
  595. size_t len = strlen(s) + 3; /* see len comments below */
  596. #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
  597. #define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
  598. || (((chr) >= 'a') && ((chr) <= 'z')) \
  599. || (((chr) >= 'A') && ((chr) <= 'Z')) \
  600. || (chr) == '_')
  601. p = s1 = malloc(len);
  602. if (!p)
  603. fatal(2, "memory error!");
  604. while (*s) {
  605. cur_chr = *s++;
  606. if (cur_chr == '^') {
  607. cur_chr = *s++;
  608. if (cur_chr == '\0') {
  609. *p++ = '^';
  610. break;
  611. }
  612. cur_chr &= 0x1F;
  613. if (cur_chr != 0) {
  614. *p++ = cur_chr;
  615. }
  616. continue;
  617. }
  618. if (use_env && cur_chr == '$') { /* ARI */
  619. char c;
  620. phchar = s;
  621. while (isalnumx(*s))
  622. s++;
  623. c = *s; /* save */
  624. *s = '\0';
  625. phchar = getenv(phchar);
  626. *s = c; /* restore */
  627. if (phchar) {
  628. len += strlen(phchar);
  629. s1 = grow(s1, &p, len);
  630. while (*phchar)
  631. *p++ = *phchar++;
  632. }
  633. continue;
  634. }
  635. if (cur_chr != '\\') {
  636. *p++ = cur_chr;
  637. continue;
  638. }
  639. cur_chr = *s++;
  640. if (cur_chr == '\0') {
  641. if (sending) {
  642. *p++ = '\\';
  643. *p++ = '\\'; /* +1 for len */
  644. }
  645. break;
  646. }
  647. switch (cur_chr) {
  648. case 'b':
  649. *p++ = '\b';
  650. break;
  651. case 'c':
  652. if (sending && *s == '\0')
  653. add_return = 0;
  654. else
  655. *p++ = cur_chr;
  656. break;
  657. case '\\':
  658. case 'K':
  659. case 'p':
  660. case 'd':
  661. if (sending)
  662. *p++ = '\\';
  663. *p++ = cur_chr;
  664. break;
  665. case 'T':
  666. if (sending && phone_num) {
  667. len += strlen(phone_num);
  668. s1 = grow(s1, &p, len);
  669. for (phchar = phone_num; *phchar != '\0'; phchar++)
  670. *p++ = *phchar;
  671. }
  672. else {
  673. *p++ = '\\';
  674. *p++ = 'T';
  675. }
  676. break;
  677. case 'U':
  678. if (sending && phone_num2) {
  679. len += strlen(phone_num2);
  680. s1 = grow(s1, &p, len);
  681. for (phchar = phone_num2; *phchar != '\0'; phchar++)
  682. *p++ = *phchar;
  683. }
  684. else {
  685. *p++ = '\\';
  686. *p++ = 'U';
  687. }
  688. break;
  689. case 'q':
  690. quiet = 1;
  691. break;
  692. case 'r':
  693. *p++ = '\r';
  694. break;
  695. case 'n':
  696. *p++ = '\n';
  697. break;
  698. case 's':
  699. *p++ = ' ';
  700. break;
  701. case 't':
  702. *p++ = '\t';
  703. break;
  704. case 'N':
  705. if (sending) {
  706. *p++ = '\\';
  707. *p++ = '\0';
  708. }
  709. else
  710. *p++ = 'N';
  711. break;
  712. case '$': /* ARI */
  713. if (use_env) {
  714. *p++ = cur_chr;
  715. break;
  716. }
  717. /* FALL THROUGH */
  718. default:
  719. if (isoctal (cur_chr)) {
  720. cur_chr &= 0x07;
  721. if (isoctal (*s)) {
  722. cur_chr <<= 3;
  723. cur_chr |= *s++ - '0';
  724. if (isoctal (*s)) {
  725. cur_chr <<= 3;
  726. cur_chr |= *s++ - '0';
  727. }
  728. }
  729. if (cur_chr != 0 || sending) {
  730. if (sending && (cur_chr == '\\' || cur_chr == 0))
  731. *p++ = '\\';
  732. *p++ = cur_chr;
  733. }
  734. break;
  735. }
  736. if (sending)
  737. *p++ = '\\';
  738. *p++ = cur_chr;
  739. break;
  740. }
  741. }
  742. if (add_return)
  743. *p++ = '\r'; /* +2 for len */
  744. *p = '\0'; /* +3 for len */
  745. return s1;
  746. }
  747. /*
  748. * A modified version of 'strtok'. This version skips \ sequences.
  749. */
  750. char *expect_strtok (s, term)
  751. char *s, *term;
  752. {
  753. static char *str = "";
  754. int escape_flag = 0;
  755. char *result;
  756. /*
  757. * If a string was specified then do initial processing.
  758. */
  759. if (s)
  760. str = s;
  761. /*
  762. * If this is the escape flag then reset it and ignore the character.
  763. */
  764. if (*str)
  765. result = str;
  766. else
  767. result = (char *) 0;
  768. while (*str) {
  769. if (escape_flag) {
  770. escape_flag = 0;
  771. ++str;
  772. continue;
  773. }
  774. if (*str == '\\') {
  775. ++str;
  776. escape_flag = 1;
  777. continue;
  778. }
  779. /*
  780. * If this is not in the termination string, continue.
  781. */
  782. if (strchr (term, *str) == (char *) 0) {
  783. ++str;
  784. continue;
  785. }
  786. /*
  787. * This is the terminator. Mark the end of the string and stop.
  788. */
  789. *str++ = '\0';
  790. break;
  791. }
  792. return (result);
  793. }
  794. /*
  795. * Process the expect string
  796. */
  797. void chat_expect (s)
  798. char *s;
  799. {
  800. char *expect;
  801. char *reply;
  802. if (strcmp(s, "HANGUP") == 0) {
  803. ++hup_next;
  804. return;
  805. }
  806. if (strcmp(s, "ABORT") == 0) {
  807. ++abort_next;
  808. return;
  809. }
  810. if (strcmp(s, "CLR_ABORT") == 0) {
  811. ++clear_abort_next;
  812. return;
  813. }
  814. if (strcmp(s, "REPORT") == 0) {
  815. ++report_next;
  816. return;
  817. }
  818. if (strcmp(s, "CLR_REPORT") == 0) {
  819. ++clear_report_next;
  820. return;
  821. }
  822. if (strcmp(s, "TIMEOUT") == 0) {
  823. ++timeout_next;
  824. return;
  825. }
  826. if (strcmp(s, "ECHO") == 0) {
  827. ++echo_next;
  828. return;
  829. }
  830. if (strcmp(s, "SAY") == 0) {
  831. ++say_next;
  832. return;
  833. }
  834. /*
  835. * Fetch the expect and reply string.
  836. */
  837. for (;;) {
  838. expect = expect_strtok (s, "-");
  839. s = (char *) 0;
  840. if (expect == (char *) 0)
  841. return;
  842. reply = expect_strtok (s, "-");
  843. /*
  844. * Handle the expect string. If successful then exit.
  845. */
  846. if (get_string (expect))
  847. return;
  848. /*
  849. * If there is a sub-reply string then send it. Otherwise any condition
  850. * is terminal.
  851. */
  852. if (reply == (char *) 0 || exit_code != 3)
  853. break;
  854. chat_send (reply);
  855. }
  856. /*
  857. * The expectation did not occur. This is terminal.
  858. */
  859. if (fail_reason)
  860. msgf("Failed (%s)", fail_reason);
  861. else
  862. msgf("Failed");
  863. terminate(exit_code);
  864. }
  865. /*
  866. * Translate the input character to the appropriate string for printing
  867. * the data.
  868. */
  869. char *character(c)
  870. int c;
  871. {
  872. static char string[10];
  873. char *meta;
  874. meta = (c & 0x80) ? "M-" : "";
  875. c &= 0x7F;
  876. if (c < 32)
  877. sprintf(string, "%s^%c", meta, (int)c + '@');
  878. else if (c == 127)
  879. sprintf(string, "%s^?", meta);
  880. else
  881. sprintf(string, "%s%c", meta, c);
  882. return (string);
  883. }
  884. /*
  885. * process the reply string
  886. */
  887. void chat_send (s)
  888. register char *s;
  889. {
  890. char file_data[STR_LEN];
  891. if (say_next) {
  892. say_next = 0;
  893. s = clean(s, 1);
  894. write(2, s, strlen(s));
  895. free(s);
  896. return;
  897. }
  898. if (hup_next) {
  899. hup_next = 0;
  900. if (strcmp(s, "OFF") == 0)
  901. signal(SIGHUP, SIG_IGN);
  902. else
  903. signal(SIGHUP, sighup);
  904. return;
  905. }
  906. if (echo_next) {
  907. echo_next = 0;
  908. echo = (strcmp(s, "ON") == 0);
  909. return;
  910. }
  911. if (abort_next) {
  912. char *s1;
  913. abort_next = 0;
  914. if (n_aborts >= MAX_ABORTS)
  915. fatal(2, "Too many ABORT strings");
  916. s1 = clean(s, 0);
  917. if (strlen(s1) > strlen(s)
  918. || strlen(s1) + 1 > sizeof(fail_buffer))
  919. fatal(1, "Illegal or too-long ABORT string ('%v')", s);
  920. abort_string[n_aborts++] = s1;
  921. if (verbose)
  922. msgf("abort on (%v)", s);
  923. return;
  924. }
  925. if (clear_abort_next) {
  926. char *s1;
  927. int i;
  928. int old_max;
  929. int pack = 0;
  930. clear_abort_next = 0;
  931. s1 = clean(s, 0);
  932. if (strlen(s1) > strlen(s)
  933. || strlen(s1) + 1 > sizeof(fail_buffer))
  934. fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
  935. old_max = n_aborts;
  936. for (i=0; i < n_aborts; i++) {
  937. if ( strcmp(s1,abort_string[i]) == 0 ) {
  938. free(abort_string[i]);
  939. abort_string[i] = NULL;
  940. pack++;
  941. n_aborts--;
  942. if (verbose)
  943. msgf("clear abort on (%v)", s);
  944. }
  945. }
  946. free(s1);
  947. if (pack)
  948. pack_array(abort_string,old_max);
  949. return;
  950. }
  951. if (report_next) {
  952. char *s1;
  953. report_next = 0;
  954. if (n_reports >= MAX_REPORTS)
  955. fatal(2, "Too many REPORT strings");
  956. s1 = clean(s, 0);
  957. if (strlen(s1) > strlen(s)
  958. || strlen(s1) + 1 > sizeof(fail_buffer))
  959. fatal(1, "Illegal or too-long REPORT string ('%v')", s);
  960. report_string[n_reports++] = s1;
  961. if (verbose)
  962. msgf("report (%v)", s);
  963. return;
  964. }
  965. if (clear_report_next) {
  966. char *s1;
  967. int i;
  968. int old_max;
  969. int pack = 0;
  970. clear_report_next = 0;
  971. s1 = clean(s, 0);
  972. if (strlen(s1) > strlen(s)
  973. || strlen(s1) + 1 > sizeof(fail_buffer))
  974. fatal(1, "Illegal or too-long REPORT string ('%v')", s);
  975. old_max = n_reports;
  976. for (i=0; i < n_reports; i++) {
  977. if ( strcmp(s1,report_string[i]) == 0 ) {
  978. free(report_string[i]);
  979. report_string[i] = NULL;
  980. pack++;
  981. n_reports--;
  982. if (verbose)
  983. msgf("clear report (%v)", s);
  984. }
  985. }
  986. free(s1);
  987. if (pack)
  988. pack_array(report_string,old_max);
  989. return;
  990. }
  991. if (timeout_next) {
  992. timeout_next = 0;
  993. s = clean(s, 0);
  994. timeout = atoi(s);
  995. if (timeout <= 0)
  996. timeout = DEFAULT_CHAT_TIMEOUT;
  997. if (verbose)
  998. msgf("timeout set to %d seconds", timeout);
  999. return;
  1000. }
  1001. /*
  1002. * The syntax @filename means read the string to send from the
  1003. * file `filename'.
  1004. */
  1005. if (s[0] == '@') {
  1006. /* skip the @ and any following white-space */
  1007. char *fn = s;
  1008. while (*++fn == ' ' || *fn == '\t')
  1009. ;
  1010. if (*fn != 0) {
  1011. FILE *f;
  1012. int n = 0;
  1013. /* open the file and read until STR_LEN-1 bytes or end-of-file */
  1014. f = fopen(fn, "r");
  1015. if (f == NULL)
  1016. fatal(1, "%s -- open failed: %m", fn);
  1017. while (n < STR_LEN - 1) {
  1018. int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
  1019. if (nr < 0)
  1020. fatal(1, "%s -- read error", fn);
  1021. if (nr == 0)
  1022. break;
  1023. n += nr;
  1024. }
  1025. fclose(f);
  1026. /* use the string we got as the string to send,
  1027. but trim off the final newline if any. */
  1028. if (n > 0 && file_data[n-1] == '\n')
  1029. --n;
  1030. file_data[n] = 0;
  1031. s = file_data;
  1032. }
  1033. }
  1034. if (strcmp(s, "EOT") == 0)
  1035. s = "^D\\c";
  1036. else if (strcmp(s, "BREAK") == 0)
  1037. s = "\\K\\c";
  1038. if (!put_string(s))
  1039. fatal(1, "Failed");
  1040. }
  1041. int get_char()
  1042. {
  1043. int status;
  1044. char c;
  1045. status = read(0, &c, 1);
  1046. switch (status) {
  1047. case 1:
  1048. return ((int)c & 0x7F);
  1049. default:
  1050. msgf("warning: read() on stdin returned %d", status);
  1051. case -1:
  1052. if ((status = fcntl(0, F_GETFL, 0)) == -1)
  1053. fatal(2, "Can't get file mode flags on stdin: %m");
  1054. if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
  1055. fatal(2, "Can't set file mode flags on stdin: %m");
  1056. return (-1);
  1057. }
  1058. }
  1059. int put_char(c)
  1060. int c;
  1061. {
  1062. int status;
  1063. char ch = c;
  1064. usleep(10000); /* inter-character typing delay (?) */
  1065. status = write(1, &ch, 1);
  1066. switch (status) {
  1067. case 1:
  1068. return (0);
  1069. default:
  1070. msgf("warning: write() on stdout returned %d", status);
  1071. case -1:
  1072. if ((status = fcntl(0, F_GETFL, 0)) == -1)
  1073. fatal(2, "Can't get file mode flags on stdin, %m");
  1074. if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
  1075. fatal(2, "Can't set file mode flags on stdin: %m");
  1076. return (-1);
  1077. }
  1078. }
  1079. int write_char (c)
  1080. int c;
  1081. {
  1082. if (alarmed || put_char(c) < 0) {
  1083. alarm(0);
  1084. alarmed = 0;
  1085. if (verbose) {
  1086. if (errno == EINTR || errno == EWOULDBLOCK)
  1087. msgf(" -- write timed out");
  1088. else
  1089. msgf(" -- write failed: %m");
  1090. }
  1091. return (0);
  1092. }
  1093. return (1);
  1094. }
  1095. int put_string (s)
  1096. register char *s;
  1097. {
  1098. quiet = 0;
  1099. s = clean(s, 1);
  1100. if (verbose) {
  1101. if (quiet)
  1102. msgf("send (?????\?)");
  1103. else
  1104. msgf("send (%v)", s);
  1105. }
  1106. alarm(timeout); alarmed = 0;
  1107. while (*s) {
  1108. register char c = *s++;
  1109. if (c != '\\') {
  1110. if (!write_char (c))
  1111. return 0;
  1112. continue;
  1113. }
  1114. c = *s++;
  1115. switch (c) {
  1116. case 'd':
  1117. sleep(1);
  1118. break;
  1119. case 'K':
  1120. break_sequence();
  1121. break;
  1122. case 'p':
  1123. usleep(10000); /* 1/100th of a second (arg is microseconds) */
  1124. break;
  1125. default:
  1126. if (!write_char (c))
  1127. return 0;
  1128. break;
  1129. }
  1130. }
  1131. alarm(0);
  1132. alarmed = 0;
  1133. return (1);
  1134. }
  1135. /*
  1136. * Echo a character to stderr.
  1137. * When called with -1, a '\n' character is generated when
  1138. * the cursor is not at the beginning of a line.
  1139. */
  1140. void echo_stderr(n)
  1141. int n;
  1142. {
  1143. static int need_lf;
  1144. char *s;
  1145. switch (n) {
  1146. case '\r': /* ignore '\r' */
  1147. break;
  1148. case -1:
  1149. if (need_lf == 0)
  1150. break;
  1151. /* fall through */
  1152. case '\n':
  1153. write(2, "\n", 1);
  1154. need_lf = 0;
  1155. break;
  1156. default:
  1157. s = character(n);
  1158. write(2, s, strlen(s));
  1159. need_lf = 1;
  1160. break;
  1161. }
  1162. }
  1163. /*
  1164. * 'Wait for' this string to appear on this file descriptor.
  1165. */
  1166. int get_string(string)
  1167. register char *string;
  1168. {
  1169. char temp[STR_LEN];
  1170. int c, printed = 0, len, minlen;
  1171. register char *s = temp, *end = s + STR_LEN;
  1172. char *logged = temp;
  1173. fail_reason = (char *)0;
  1174. string = clean(string, 0);
  1175. len = strlen(string);
  1176. minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
  1177. if (verbose)
  1178. msgf("expect (%v)", string);
  1179. if (len > STR_LEN) {
  1180. msgf("expect string is too long");
  1181. exit_code = 1;
  1182. return 0;
  1183. }
  1184. if (len == 0) {
  1185. if (verbose)
  1186. msgf("got it");
  1187. return (1);
  1188. }
  1189. alarm(timeout);
  1190. alarmed = 0;
  1191. while ( ! alarmed && (c = get_char()) >= 0) {
  1192. int n, abort_len, report_len;
  1193. if (echo)
  1194. echo_stderr(c);
  1195. if (verbose && c == '\n') {
  1196. if (s == logged)
  1197. msgf(""); /* blank line */
  1198. else
  1199. msgf("%0.*v", s - logged, logged);
  1200. logged = s + 1;
  1201. }
  1202. *s++ = c;
  1203. if (verbose && s >= logged + 80) {
  1204. msgf("%0.*v", s - logged, logged);
  1205. logged = s;
  1206. }
  1207. if (Verbose) {
  1208. if (c == '\n')
  1209. fputc( '\n', stderr );
  1210. else if (c != '\r')
  1211. fprintf( stderr, "%s", character(c) );
  1212. }
  1213. if (!report_gathering) {
  1214. for (n = 0; n < n_reports; ++n) {
  1215. if ((report_string[n] != (char*) NULL) &&
  1216. s - temp >= (report_len = strlen(report_string[n])) &&
  1217. strncmp(s - report_len, report_string[n], report_len) == 0) {
  1218. time_t time_now = time ((time_t*) NULL);
  1219. struct tm* tm_now = localtime (&time_now);
  1220. strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
  1221. strcat (report_buffer, report_string[n]);
  1222. report_string[n] = (char *) NULL;
  1223. report_gathering = 1;
  1224. break;
  1225. }
  1226. }
  1227. }
  1228. else {
  1229. if (!iscntrl (c)) {
  1230. int rep_len = strlen (report_buffer);
  1231. report_buffer[rep_len] = c;
  1232. report_buffer[rep_len + 1] = '\0';
  1233. }
  1234. else {
  1235. report_gathering = 0;
  1236. fprintf (report_fp, "chat: %s\n", report_buffer);
  1237. }
  1238. }
  1239. if (s - temp >= len &&
  1240. c == string[len - 1] &&
  1241. strncmp(s - len, string, len) == 0) {
  1242. if (verbose) {
  1243. if (s > logged)
  1244. msgf("%0.*v", s - logged, logged);
  1245. msgf(" -- got it\n");
  1246. }
  1247. alarm(0);
  1248. alarmed = 0;
  1249. return (1);
  1250. }
  1251. for (n = 0; n < n_aborts; ++n) {
  1252. if (s - temp >= (abort_len = strlen(abort_string[n])) &&
  1253. strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
  1254. if (verbose) {
  1255. if (s > logged)
  1256. msgf("%0.*v", s - logged, logged);
  1257. msgf(" -- failed");
  1258. }
  1259. alarm(0);
  1260. alarmed = 0;
  1261. exit_code = n + 4;
  1262. strcpy(fail_reason = fail_buffer, abort_string[n]);
  1263. return (0);
  1264. }
  1265. }
  1266. if (s >= end) {
  1267. if (logged < s - minlen) {
  1268. if (verbose)
  1269. msgf("%0.*v", s - logged, logged);
  1270. logged = s;
  1271. }
  1272. s -= minlen;
  1273. memmove(temp, s, minlen);
  1274. logged = temp + (logged - s);
  1275. s = temp + minlen;
  1276. }
  1277. if (alarmed && verbose)
  1278. msgf("warning: alarm synchronization problem");
  1279. }
  1280. alarm(0);
  1281. if (verbose && printed) {
  1282. if (alarmed)
  1283. msgf(" -- read timed out");
  1284. else
  1285. msgf(" -- read failed: %m");
  1286. }
  1287. exit_code = 3;
  1288. alarmed = 0;
  1289. return (0);
  1290. }
  1291. /*
  1292. * Gross kludge to handle Solaris versions >= 2.6 having usleep.
  1293. */
  1294. #ifdef SOL2
  1295. #include <sys/param.h>
  1296. #if MAXUID > 65536 /* then this is Solaris 2.6 or later */
  1297. #undef NO_USLEEP
  1298. #endif
  1299. #endif /* SOL2 */
  1300. #ifdef NO_USLEEP
  1301. #include <sys/types.h>
  1302. #include <sys/time.h>
  1303. /*
  1304. usleep -- support routine for 4.2BSD system call emulations
  1305. last edit: 29-Oct-1984 D A Gwyn
  1306. */
  1307. extern int select();
  1308. int
  1309. usleep( usec ) /* returns 0 if ok, else -1 */
  1310. long usec; /* delay in microseconds */
  1311. {
  1312. static struct { /* `timeval' */
  1313. long tv_sec; /* seconds */
  1314. long tv_usec; /* microsecs */
  1315. } delay; /* _select() timeout */
  1316. delay.tv_sec = usec / 1000000L;
  1317. delay.tv_usec = usec % 1000000L;
  1318. return select(0, (long *)0, (long *)0, (long *)0, &delay);
  1319. }
  1320. #endif
  1321. void
  1322. pack_array (array, end)
  1323. char **array; /* The address of the array of string pointers */
  1324. int end; /* The index of the next free entry before CLR_ */
  1325. {
  1326. int i, j;
  1327. for (i = 0; i < end; i++) {
  1328. if (array[i] == NULL) {
  1329. for (j = i+1; j < end; ++j)
  1330. if (array[j] != NULL)
  1331. array[i++] = array[j];
  1332. for (; i < end; ++i)
  1333. array[i] = NULL;
  1334. break;
  1335. }
  1336. }
  1337. }
  1338. /*
  1339. * vfmtmsg - format a message into a buffer. Like vsprintf except we
  1340. * also specify the length of the output buffer, and we handle the
  1341. * %m (error message) format.
  1342. * Doesn't do floating-point formats.
  1343. * Returns the number of chars put into buf.
  1344. */
  1345. #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
  1346. int
  1347. vfmtmsg(buf, buflen, fmt, args)
  1348. char *buf;
  1349. int buflen;
  1350. const char *fmt;
  1351. va_list args;
  1352. {
  1353. int c, i, n;
  1354. int width, prec, fillch;
  1355. int base, len, neg, quoted;
  1356. unsigned long val = 0;
  1357. char *str, *buf0;
  1358. const char *f;
  1359. unsigned char *p;
  1360. char num[32];
  1361. static char hexchars[] = "0123456789abcdef";
  1362. buf0 = buf;
  1363. --buflen;
  1364. while (buflen > 0) {
  1365. for (f = fmt; *f != '%' && *f != 0; ++f)
  1366. ;
  1367. if (f > fmt) {
  1368. len = f - fmt;
  1369. if (len > buflen)
  1370. len = buflen;
  1371. memcpy(buf, fmt, len);
  1372. buf += len;
  1373. buflen -= len;
  1374. fmt = f;
  1375. }
  1376. if (*fmt == 0)
  1377. break;
  1378. c = *++fmt;
  1379. width = prec = 0;
  1380. fillch = ' ';
  1381. if (c == '0') {
  1382. fillch = '0';
  1383. c = *++fmt;
  1384. }
  1385. if (c == '*') {
  1386. width = va_arg(args, int);
  1387. c = *++fmt;
  1388. } else {
  1389. while (isdigit(c)) {
  1390. width = width * 10 + c - '0';
  1391. c = *++fmt;
  1392. }
  1393. }
  1394. if (c == '.') {
  1395. c = *++fmt;
  1396. if (c == '*') {
  1397. prec = va_arg(args, int);
  1398. c = *++fmt;
  1399. } else {
  1400. while (isdigit(c)) {
  1401. prec = prec * 10 + c - '0';
  1402. c = *++fmt;
  1403. }
  1404. }
  1405. }
  1406. str = 0;
  1407. base = 0;
  1408. neg = 0;
  1409. ++fmt;
  1410. switch (c) {
  1411. case 'd':
  1412. i = va_arg(args, int);
  1413. if (i < 0) {
  1414. neg = 1;
  1415. val = -i;
  1416. } else
  1417. val = i;
  1418. base = 10;
  1419. break;
  1420. case 'o':
  1421. val = va_arg(args, unsigned int);
  1422. base = 8;
  1423. break;
  1424. case 'x':
  1425. val = va_arg(args, unsigned int);
  1426. base = 16;
  1427. break;
  1428. case 'p':
  1429. val = (unsigned long) va_arg(args, void *);
  1430. base = 16;
  1431. neg = 2;
  1432. break;
  1433. case 's':
  1434. str = va_arg(args, char *);
  1435. break;
  1436. case 'c':
  1437. num[0] = va_arg(args, int);
  1438. num[1] = 0;
  1439. str = num;
  1440. break;
  1441. case 'm':
  1442. str = strerror(errno);
  1443. break;
  1444. case 'v': /* "visible" string */
  1445. case 'q': /* quoted string */
  1446. quoted = c == 'q';
  1447. p = va_arg(args, unsigned char *);
  1448. if (fillch == '0' && prec > 0) {
  1449. n = prec;
  1450. } else {
  1451. n = strlen((char *)p);
  1452. if (prec > 0 && prec < n)
  1453. n = prec;
  1454. }
  1455. while (n > 0 && buflen > 0) {
  1456. c = *p++;
  1457. --n;
  1458. if (!quoted && c >= 0x80) {
  1459. OUTCHAR('M');
  1460. OUTCHAR('-');
  1461. c -= 0x80;
  1462. }
  1463. if (quoted && (c == '"' || c == '\\'))
  1464. OUTCHAR('\\');
  1465. if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
  1466. if (quoted) {
  1467. OUTCHAR('\\');
  1468. switch (c) {
  1469. case '\t': OUTCHAR('t'); break;
  1470. case '\n': OUTCHAR('n'); break;
  1471. case '\b': OUTCHAR('b'); break;
  1472. case '\f': OUTCHAR('f'); break;
  1473. default:
  1474. OUTCHAR('x');
  1475. OUTCHAR(hexchars[c >> 4]);
  1476. OUTCHAR(hexchars[c & 0xf]);
  1477. }
  1478. } else {
  1479. if (c == '\t')
  1480. OUTCHAR(c);
  1481. else {
  1482. OUTCHAR('^');
  1483. OUTCHAR(c ^ 0x40);
  1484. }
  1485. }
  1486. } else
  1487. OUTCHAR(c);
  1488. }
  1489. continue;
  1490. default:
  1491. *buf++ = '%';
  1492. if (c != '%')
  1493. --fmt; /* so %z outputs %z etc. */
  1494. --buflen;
  1495. continue;
  1496. }
  1497. if (base != 0) {
  1498. str = num + sizeof(num);
  1499. *--str = 0;
  1500. while (str > num + neg) {
  1501. *--str = hexchars[val % base];
  1502. val = val / base;
  1503. if (--prec <= 0 && val == 0)
  1504. break;
  1505. }
  1506. switch (neg) {
  1507. case 1:
  1508. *--str = '-';
  1509. break;
  1510. case 2:
  1511. *--str = 'x';
  1512. *--str = '0';
  1513. break;
  1514. }
  1515. len = num + sizeof(num) - 1 - str;
  1516. } else {
  1517. len = strlen(str);
  1518. if (prec > 0 && len > prec)
  1519. len = prec;
  1520. }
  1521. if (width > 0) {
  1522. if (width > buflen)
  1523. width = buflen;
  1524. if ((n = width - len) > 0) {
  1525. buflen -= n;
  1526. for (; n > 0; --n)
  1527. *buf++ = fillch;
  1528. }
  1529. }
  1530. if (len > buflen)
  1531. len = buflen;
  1532. memcpy(buf, str, len);
  1533. buf += len;
  1534. buflen -= len;
  1535. }
  1536. *buf = 0;
  1537. return buf - buf0;
  1538. }