14374Slars/* 24374Slars * Chat -- a program for automatic session establishment (i.e. dial 34374Slars * the phone and log in). 44374Slars * 511990Speter * Standard termination codes: 611990Speter * 0 - successful completion of the script 711990Speter * 1 - invalid argument, expect string too large, etc. 834766Speter * 2 - error on an I/O operation or fatal error condition. 911990Speter * 3 - timeout waiting for a simple string. 1011990Speter * 4 - the first string declared as "ABORT" 1111990Speter * 5 - the second string declared as "ABORT" 1211990Speter * 6 - ... and so on for successive ABORT strings. 1311990Speter * 144374Slars * This software is in the public domain. 154374Slars * 1628597Speter * ----------------- 1734766Speter * added -T and -U option and \T and \U substitution to pass a phone 1834766Speter * number into chat script. Two are needed for some ISDN TA applications. 1934766Speter * Keith Dart <kdart@cisco.com> 2034766Speter * 214374Slars * 2228597Speter * Added SAY keyword to send output to stderr. 2328597Speter * This allows to turn ECHO OFF and to output specific, user selected, 2428597Speter * text to give progress messages. This best works when stderr 2528597Speter * exists (i.e.: pppd in nodetach mode). 264374Slars * 2728597Speter * Added HANGUP directives to allow for us to be called 2828597Speter * back. When HANGUP is set to NO, chat will not hangup at HUP signal. 2928597Speter * We rely on timeouts in that case. 3028597Speter * 3128597Speter * Added CLR_ABORT to clear previously set ABORT string. This has been 3228597Speter * dictated by the HANGUP above as "NO CARRIER" (for example) must be 3328597Speter * an ABORT condition until we know the other host is going to close 3428597Speter * the connection for call back. As soon as we have completed the 3528597Speter * first stage of the call back sequence, "NO CARRIER" is a valid, non 3628597Speter * fatal string. As soon as we got called back (probably get "CONNECT"), 3728597Speter * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. 3828597Speter * Note that CLR_ABORT packs the abort_strings[] array so that we do not 3928597Speter * have unused entries not being reclaimed. 4028597Speter * 4128597Speter * In the same vein as above, added CLR_REPORT keyword. 4228597Speter * 4328597Speter * Allow for comments. Line starting with '#' are comments and are 4428597Speter * ignored. If a '#' is to be expected as the first character, the 4528597Speter * expect string must be quoted. 4628597Speter * 4728597Speter * 4828597Speter * Francis Demierre <Francis@SwissMail.Com> 4928597Speter * Thu May 15 17:15:40 MET DST 1997 5028597Speter * 5128597Speter * 5211990Speter * Added -r "report file" switch & REPORT keyword. 5311990Speter * Robert Geer <bgeer@xmission.com> 5411990Speter * 5534766Speter * Added -s "use stderr" and -S "don't use syslog" switches. 5634766Speter * June 18, 1997 5734766Speter * Karl O. Pinc <kop@meme.com> 5828597Speter * 5934766Speter * 6028597Speter * Added -e "echo" switch & ECHO keyword 6128597Speter * Dick Streefland <dicks@tasking.nl> 6228597Speter * 6328597Speter * 6428597Speter * Considerable updates and modifications by 6528597Speter * Al Longyear <longyear@pobox.com> 6628597Speter * Paul Mackerras <paulus@cs.anu.edu.au> 6728597Speter * 6828597Speter * 694374Slars * The original author is: 704374Slars * 714374Slars * Karl Fox <karl@MorningStar.Com> 724374Slars * Morning Star Technologies, Inc. 734374Slars * 1760 Zollinger Road 744374Slars * Columbus, OH 43221 754374Slars * (614)451-1883 7611990Speter * 7728597Speter * 784374Slars */ 794374Slars 80119316Smarkm#include <sys/cdefs.h> 81119316Smarkm__FBSDID("$FreeBSD$"); 824374Slars 83119316Smarkm#include <sys/types.h> 84119316Smarkm#include <sys/stat.h> 8528597Speter#include <ctype.h> 86119316Smarkm#include <errno.h> 874374Slars#include <fcntl.h> 884374Slars#include <signal.h> 89119316Smarkm#include <stdarg.h> 90119316Smarkm#include <stdio.h> 91119316Smarkm#include <stdlib.h> 924374Slars#include <string.h> 934374Slars#include <syslog.h> 944374Slars#include <termios.h> 95119316Smarkm#include <time.h> 96119316Smarkm#include <unistd.h> 974374Slars 984374Slars#define STR_LEN 1024 994374Slars 1004374Slars#ifndef SIGTYPE 1014374Slars#define SIGTYPE void 1024374Slars#endif 1034374Slars 10411990Speter#ifndef O_NONBLOCK 10511990Speter#define O_NONBLOCK O_NDELAY 10611990Speter#endif 10711990Speter 1084374Slars#define MAX_ABORTS 50 10911990Speter#define MAX_REPORTS 50 1104374Slars#define DEFAULT_CHAT_TIMEOUT 45 1114374Slars 11228597Speterint echo = 0; 11311990Speterint verbose = 0; 11434766Speterint to_log = 1; 11534766Speterint to_stderr = 0; 11628597Speterint Verbose = 0; 11711990Speterint quiet = 0; 11811990Speterint exit_code = 0; 11911990SpeterFILE* report_fp = (FILE *) 0; 12011990Speterchar *report_file = (char *) 0; 12111990Speterchar *chat_file = (char *) 0; 12234766Speterchar *phone_num = (char *) 0; 12334766Speterchar *phone_num2 = (char *) 0; 12411990Speterint timeout = DEFAULT_CHAT_TIMEOUT; 1254374Slars 126119316Smarkmstatic char blank[] = ""; 127119316Smarkm 1284374Slarsint have_tty_parameters = 0; 12911990Speter 13011990Speter#define term_parms struct termios 13111990Speter#define get_term_param(param) tcgetattr(0, param) 13211990Speter#define set_term_param(param) tcsetattr(0, TCSANOW, param) 1334374Slarsstruct termios saved_tty_parameters; 1344374Slars 1354374Slarschar *abort_string[MAX_ABORTS], *fail_reason = (char *)0, 1364374Slars fail_buffer[50]; 13728597Speterint n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; 13828597Speterint clear_abort_next = 0; 1394374Slars 14011990Speterchar *report_string[MAX_REPORTS] ; 14111990Speterchar report_buffer[50] ; 14211990Speterint n_reports = 0, report_next = 0, report_gathering = 0 ; 14328597Speterint clear_report_next = 0; 14411990Speter 14528597Speterint say_next = 0, hup_next = 0; 14628597Speter 14792920Simpvoid *dup_mem(void *b, size_t c); 14892920Simpvoid *copy_of(char *s); 14992920Simpstatic void usage(void); 150121784Skientzlevoid chat_logf(const char *fmt, ...); 15192920Simpvoid fatal(int code, const char *fmt, ...); 15292920SimpSIGTYPE sigalrm(int signo); 15392920SimpSIGTYPE sigint(int signo); 15492920SimpSIGTYPE sigterm(int signo); 15592920SimpSIGTYPE sighup(int signo); 15692920Simpvoid init(void); 15792920Simpvoid set_tty_parameters(void); 15892920Simpvoid echo_stderr(int); 15992920Simpvoid break_sequence(void); 16092920Simpvoid terminate(int status); 161119316Smarkmvoid do_file(char *chatfile); 162119316Smarkmint get_string(char *string); 163119316Smarkmint put_string(char *s); 16492920Simpint write_char(int c); 16592920Simpint put_char(int c); 16692920Simpint get_char(void); 167119316Smarkmvoid chat_send(char *s); 16892920Simpchar *character(int c); 169119316Smarkmvoid chat_expect(char *s); 170119316Smarkmchar *clean(char *s, int sending); 17192920Simpvoid pack_array(char **array, int end); 172119316Smarkmchar *expect_strtok(char *, const char *); 17392920Simpint vfmtmsg(char *, int, const char *, va_list); /* vsprintf++ */ 1744374Slars 175119316Smarkmvoid * 176119316Smarkmdup_mem(void *b, size_t c) 17734766Speter{ 1784374Slars void *ans = malloc (c); 1794374Slars if (!ans) 18034766Speter fatal(2, "memory error!"); 18134766Speter 1824374Slars memcpy (ans, b, c); 1834374Slars return ans; 18434766Speter} 1854374Slars 186119316Smarkmvoid * 187119316Smarkmcopy_of(char *s) 18834766Speter{ 1894374Slars return dup_mem (s, strlen (s) + 1); 19034766Speter} 1914374Slars 1924374Slars/* 193176888Sdelphij * chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout] 194176888Sdelphij * [-T phone-number] [-U phone-number2] [chat-script] 195176888Sdelphij * where chat-script has the form: 196176888Sdelphij * [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]] 1974374Slars * 198176888Sdelphij * Perform a UUCP-dialer-like chat script on stdin and stdout. 1994374Slars */ 2004374Slarsint 201119316Smarkmmain(int argc, char *argv[]) 20234766Speter{ 2034374Slars int option; 2044374Slars 20511990Speter tzset(); 2064374Slars 207176888Sdelphij while ((option = getopt(argc, argv, "ef:r:sSt:T:U:vV")) != -1) { 20834766Speter switch (option) { 20934766Speter case 'e': 21034766Speter ++echo; 21134766Speter break; 21228597Speter 213176888Sdelphij case 'f': 214176888Sdelphij if (chat_file != NULL) 215176888Sdelphij free(chat_file); 216176888Sdelphij chat_file = copy_of(optarg); 21734766Speter break; 2184374Slars 219176888Sdelphij case 'r': 220176888Sdelphij if (report_fp != NULL) 221176888Sdelphij fclose(report_fp); 222176888Sdelphij if (report_file != NULL) 223176888Sdelphij free(report_file); 224176888Sdelphij report_file = copy_of(optarg); 225176888Sdelphij report_fp = fopen(report_file, "a"); 226176888Sdelphij if (report_fp != NULL) { 227176888Sdelphij if (verbose) 228176888Sdelphij fprintf(report_fp, "Opening \"%s\"...\n", report_file); 229176888Sdelphij } else 230176888Sdelphij fatal(2, "cannot open \"%s\" for appending", report_file); 23134766Speter break; 23228597Speter 23334766Speter case 's': 23434766Speter ++to_stderr; 23534766Speter break; 23634766Speter 23734766Speter case 'S': 23834766Speter to_log = 0; 23934766Speter break; 24034766Speter 241176888Sdelphij case 't': 242176888Sdelphij timeout = atoi(optarg); 24334766Speter break; 2444374Slars 245176888Sdelphij case 'T': 246176888Sdelphij if (phone_num != NULL) 247176888Sdelphij free(phone_num); 248176888Sdelphij phone_num = copy_of(optarg); 24934766Speter break; 2504374Slars 251176888Sdelphij case 'U': 252176888Sdelphij if (phone_num2 != NULL) 253176888Sdelphij free(phone_num2); 254176888Sdelphij phone_num2 = copy_of(optarg); 25534766Speter break; 2564374Slars 257176888Sdelphij case 'v': 258176888Sdelphij ++verbose; 25934766Speter break; 26034766Speter 261176888Sdelphij case 'V': 262176888Sdelphij ++Verbose; 26334766Speter break; 26434766Speter 26534766Speter default: 26634766Speter usage(); 26734766Speter break; 26834766Speter } 26934766Speter } 270176888Sdelphij 271176888Sdelphij argc -= optind; 272176888Sdelphij argv += optind; 273176888Sdelphij 27411990Speter/* 27511990Speter * Default the report file to the stderr location 27611990Speter */ 27711990Speter if (report_fp == NULL) 27811990Speter report_fp = stderr; 2794374Slars 28034766Speter if (to_log) { 28134766Speter openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 2824374Slars 28334766Speter if (verbose) 28434766Speter setlogmask(LOG_UPTO(LOG_INFO)); 28534766Speter else 28634766Speter setlogmask(LOG_UPTO(LOG_WARNING)); 28734766Speter } 2884374Slars 28934766Speter if (chat_file != NULL) { 290176888Sdelphij if (*argv != NULL) 2914374Slars usage(); 292176888Sdelphij else { 293176888Sdelphij init(); 294176888Sdelphij do_file(chat_file); 295176888Sdelphij } 29634766Speter } else { 297176888Sdelphij init(); 298176888Sdelphij while (*argv != NULL && argc > 0) { 299176888Sdelphij chat_expect(*argv); 300176888Sdelphij argv++; 301176888Sdelphij argc--; 3024374Slars 303176888Sdelphij if (*argv != NULL && argc > 0) { 304176888Sdelphij chat_send(*argv); 305176888Sdelphij argv++; 306176888Sdelphij argc--; 307176888Sdelphij } 3084374Slars } 30934766Speter } 3104374Slars 3114374Slars terminate(0); 31228597Speter return 0; 31334766Speter} 3144374Slars 3154374Slars/* 3164374Slars * Process a chat script when read from a file. 3174374Slars */ 3184374Slars 319119316Smarkmvoid 320119316Smarkmdo_file(char *chatfile) 32134766Speter{ 32232069Salex int linect, sendflg; 3234374Slars char *sp, *arg, quote; 3244374Slars char buf [STR_LEN]; 3254374Slars FILE *cfp; 3264374Slars 327119316Smarkm cfp = fopen (chatfile, "r"); 32811990Speter if (cfp == NULL) 329119316Smarkm fatal(1, "%s -- open failed: %m", chatfile); 3304374Slars 3314374Slars linect = 0; 3324374Slars sendflg = 0; 3334374Slars 33434766Speter while (fgets(buf, STR_LEN, cfp) != NULL) { 3354374Slars sp = strchr (buf, '\n'); 3364374Slars if (sp) 3374374Slars *sp = '\0'; 3384374Slars 3394374Slars linect++; 3404374Slars sp = buf; 34128597Speter 34228597Speter /* lines starting with '#' are comments. If a real '#' 34328597Speter is to be expected, it should be quoted .... */ 34434766Speter if ( *sp == '#' ) 34534766Speter continue; 34628597Speter 34734766Speter while (*sp != '\0') { 34834766Speter if (*sp == ' ' || *sp == '\t') { 3494374Slars ++sp; 3504374Slars continue; 35134766Speter } 3524374Slars 35334766Speter if (*sp == '"' || *sp == '\'') { 3544374Slars quote = *sp++; 3554374Slars arg = sp; 35634766Speter while (*sp != quote) { 3574374Slars if (*sp == '\0') 35834766Speter fatal(1, "unterminated quote (line %d)", linect); 35934766Speter 36034766Speter if (*sp++ == '\\') { 3614374Slars if (*sp != '\0') 3624374Slars ++sp; 3634374Slars } 3644374Slars } 36534766Speter } 36634766Speter else { 3674374Slars arg = sp; 3684374Slars while (*sp != '\0' && *sp != ' ' && *sp != '\t') 3694374Slars ++sp; 37034766Speter } 3714374Slars 3724374Slars if (*sp != '\0') 3734374Slars *sp++ = '\0'; 3744374Slars 3754374Slars if (sendflg) 3764374Slars chat_send (arg); 3774374Slars else 3784374Slars chat_expect (arg); 3794374Slars sendflg = !sendflg; 3804374Slars } 38134766Speter } 3824374Slars fclose (cfp); 38334766Speter} 3844374Slars 3854374Slars/* 3864374Slars * We got an error parsing the command line. 3874374Slars */ 38826880Scharnierstatic void 389119316Smarkmusage(void) 39034766Speter{ 391176888Sdelphij fprintf(stderr, 392176888Sdelphij "Usage: chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout]\n" 393176888Sdelphij " [-T phone-number] [-U phone-number2] [chat-script]\n" 394176888Sdelphij "where chat-script has the form:\n" 395176888Sdelphij " [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]]\n"); 3964374Slars exit(1); 39734766Speter} 3984374Slars 39934766Speterchar line[1024]; 4004374Slars 40134766Speter/* 40234766Speter * Send a message to syslog and/or stderr. 40334766Speter */ 404119316Smarkmvoid 405121784Skientzlechat_logf(const char *fmt, ...) 40628597Speter{ 40734766Speter va_list args; 4084374Slars 40934766Speter va_start(args, fmt); 41034766Speter vfmtmsg(line, sizeof(line), fmt, args); 41134766Speter if (to_log) 41228597Speter syslog(LOG_INFO, "%s", line); 41334766Speter if (to_stderr) 41434766Speter fprintf(stderr, "%s\n", line); 41528597Speter} 4164374Slars 4174374Slars/* 4184374Slars * Print an error message and terminate. 4194374Slars */ 4204374Slars 421119316Smarkmvoid 422119316Smarkmfatal(int code, const char *fmt, ...) 42334766Speter{ 42434766Speter va_list args; 4254374Slars 42634766Speter va_start(args, fmt); 42734766Speter vfmtmsg(line, sizeof(line), fmt, args); 42834766Speter if (to_log) 42934766Speter syslog(LOG_ERR, "%s", line); 43034766Speter if (to_stderr) 43134766Speter fprintf(stderr, "%s\n", line); 43234766Speter terminate(code); 43334766Speter} 4344374Slars 4354374Slarsint alarmed = 0; 4364374Slars 437119316SmarkmSIGTYPE sigalrm(int signo __unused) 43834766Speter{ 4394374Slars int flags; 4404374Slars 4414374Slars alarm(1); 4424374Slars alarmed = 1; /* Reset alarm to avoid race window */ 4434374Slars signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 4444374Slars 4454374Slars if ((flags = fcntl(0, F_GETFL, 0)) == -1) 44634766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 4474374Slars 44834766Speter if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 44934766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 45034766Speter 4514374Slars if (verbose) 452121784Skientzle chat_logf("alarm"); 45334766Speter} 4544374Slars 455119316SmarkmSIGTYPE sigint(int signo __unused) 45634766Speter{ 45734766Speter fatal(2, "SIGINT"); 45834766Speter} 4594374Slars 460119316SmarkmSIGTYPE sigterm(int signo __unused) 46134766Speter{ 46234766Speter fatal(2, "SIGTERM"); 46334766Speter} 4644374Slars 465119316SmarkmSIGTYPE sighup(int signo __unused) 46634766Speter{ 46734766Speter fatal(2, "SIGHUP"); 46834766Speter} 4694374Slars 470119316Smarkmvoid init(void) 47134766Speter{ 4724374Slars signal(SIGINT, sigint); 4734374Slars signal(SIGTERM, sigterm); 4744374Slars signal(SIGHUP, sighup); 4754374Slars 4764374Slars set_tty_parameters(); 4774374Slars signal(SIGALRM, sigalrm); 4784374Slars alarm(0); 4794374Slars alarmed = 0; 48034766Speter} 4814374Slars 482119316Smarkmvoid set_tty_parameters(void) 48334766Speter{ 48411990Speter#if defined(get_term_param) 48511990Speter term_parms t; 4864374Slars 48711990Speter if (get_term_param (&t) < 0) 48834766Speter fatal(2, "Can't get terminal parameters: %m"); 4894374Slars 4904374Slars saved_tty_parameters = t; 49111990Speter have_tty_parameters = 1; 4924374Slars 49311990Speter t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 49411990Speter t.c_oflag = 0; 49511990Speter t.c_lflag = 0; 49611990Speter t.c_cc[VERASE] = 49711990Speter t.c_cc[VKILL] = 0; 49811990Speter t.c_cc[VMIN] = 1; 49911990Speter t.c_cc[VTIME] = 0; 5004374Slars 50111990Speter if (set_term_param (&t) < 0) 50234766Speter fatal(2, "Can't set terminal parameters: %m"); 5034374Slars#endif 50434766Speter} 5054374Slars 506119316Smarkmvoid break_sequence(void) 50734766Speter{ 5084374Slars tcsendbreak (0, 0); 50934766Speter} 5104374Slars 511119316Smarkmvoid terminate(int status) 51234766Speter{ 51328597Speter echo_stderr(-1); 51434766Speter if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 51528597Speter/* 51628597Speter * Allow the last of the report string to be gathered before we terminate. 51728597Speter */ 51828597Speter if (report_gathering) { 519119316Smarkm int c; 520119316Smarkm size_t rep_len; 52128597Speter 52228597Speter rep_len = strlen(report_buffer); 52328597Speter while (rep_len + 1 <= sizeof(report_buffer)) { 52428597Speter alarm(1); 52528597Speter c = get_char(); 52628597Speter alarm(0); 52728597Speter if (c < 0 || iscntrl(c)) 52828597Speter break; 52928597Speter report_buffer[rep_len] = c; 53028597Speter ++rep_len; 53128597Speter } 53228597Speter report_buffer[rep_len] = 0; 53328597Speter fprintf (report_fp, "chat: %s\n", report_buffer); 53428597Speter } 53511990Speter if (verbose) 53611990Speter fprintf (report_fp, "Closing \"%s\".\n", report_file); 53711990Speter fclose (report_fp); 53828597Speter report_fp = (FILE *) NULL; 53934766Speter } 5404374Slars 54111990Speter#if defined(get_term_param) 54234766Speter if (have_tty_parameters) { 54311990Speter if (set_term_param (&saved_tty_parameters) < 0) 54434766Speter fatal(2, "Can't restore terminal parameters: %m"); 54534766Speter } 54611990Speter#endif 5474374Slars 54811990Speter exit(status); 54934766Speter} 5504374Slars 5514374Slars/* 5524374Slars * 'Clean up' this string. 5534374Slars */ 554119316Smarkmchar * 555119316Smarkmclean(char *s, int sending) 55634766Speter{ 5574374Slars char temp[STR_LEN], cur_chr; 558119316Smarkm char *s1, *phchar; 5594374Slars int add_return = sending; 5604374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 5614374Slars 5624374Slars s1 = temp; 56353686Skris /* Don't overflow buffer, leave room for chars we append later */ 564119316Smarkm while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) { 5654374Slars cur_chr = *s++; 56634766Speter if (cur_chr == '^') { 5674374Slars cur_chr = *s++; 56834766Speter if (cur_chr == '\0') { 5694374Slars *s1++ = '^'; 5704374Slars break; 57134766Speter } 5724374Slars cur_chr &= 0x1F; 57334766Speter if (cur_chr != 0) { 5744374Slars *s1++ = cur_chr; 57534766Speter } 5764374Slars continue; 57734766Speter } 5784374Slars 57934766Speter if (cur_chr != '\\') { 5804374Slars *s1++ = cur_chr; 5814374Slars continue; 58234766Speter } 5834374Slars 5844374Slars cur_chr = *s++; 58534766Speter if (cur_chr == '\0') { 58634766Speter if (sending) { 5874374Slars *s1++ = '\\'; 5884374Slars *s1++ = '\\'; 58934766Speter } 5904374Slars break; 59134766Speter } 5924374Slars 59334766Speter switch (cur_chr) { 5944374Slars case 'b': 5954374Slars *s1++ = '\b'; 5964374Slars break; 5974374Slars 5984374Slars case 'c': 5994374Slars if (sending && *s == '\0') 6004374Slars add_return = 0; 6014374Slars else 6024374Slars *s1++ = cur_chr; 6034374Slars break; 6044374Slars 6054374Slars case '\\': 6064374Slars case 'K': 6074374Slars case 'p': 6084374Slars case 'd': 6094374Slars if (sending) 6104374Slars *s1++ = '\\'; 6114374Slars 6124374Slars *s1++ = cur_chr; 6134374Slars break; 6144374Slars 61534766Speter case 'T': 61634766Speter if (sending && phone_num) { 61734766Speter for ( phchar = phone_num; *phchar != '\0'; phchar++) 61834766Speter *s1++ = *phchar; 61934766Speter } 62034766Speter else { 62134766Speter *s1++ = '\\'; 62234766Speter *s1++ = 'T'; 62334766Speter } 62434766Speter break; 62534766Speter 62634766Speter case 'U': 62734766Speter if (sending && phone_num2) { 62834766Speter for ( phchar = phone_num2; *phchar != '\0'; phchar++) 62934766Speter *s1++ = *phchar; 63034766Speter } 63134766Speter else { 63234766Speter *s1++ = '\\'; 63334766Speter *s1++ = 'U'; 63434766Speter } 63534766Speter break; 63634766Speter 6374374Slars case 'q': 63828597Speter quiet = 1; 6394374Slars break; 6404374Slars 6414374Slars case 'r': 6424374Slars *s1++ = '\r'; 6434374Slars break; 6444374Slars 6454374Slars case 'n': 6464374Slars *s1++ = '\n'; 6474374Slars break; 6484374Slars 6494374Slars case 's': 6504374Slars *s1++ = ' '; 6514374Slars break; 6524374Slars 6534374Slars case 't': 6544374Slars *s1++ = '\t'; 6554374Slars break; 6564374Slars 6574374Slars case 'N': 65834766Speter if (sending) { 6594374Slars *s1++ = '\\'; 6604374Slars *s1++ = '\0'; 66134766Speter } 6624374Slars else 6634374Slars *s1++ = 'N'; 6644374Slars break; 66511990Speter 6664374Slars default: 66734766Speter if (isoctal (cur_chr)) { 6684374Slars cur_chr &= 0x07; 66934766Speter if (isoctal (*s)) { 6704374Slars cur_chr <<= 3; 6714374Slars cur_chr |= *s++ - '0'; 67234766Speter if (isoctal (*s)) { 6734374Slars cur_chr <<= 3; 6744374Slars cur_chr |= *s++ - '0'; 6754374Slars } 67634766Speter } 6774374Slars 67834766Speter if (cur_chr != 0 || sending) { 6794374Slars if (sending && (cur_chr == '\\' || cur_chr == 0)) 6804374Slars *s1++ = '\\'; 6814374Slars *s1++ = cur_chr; 68234766Speter } 6834374Slars break; 68434766Speter } 6854374Slars 6864374Slars if (sending) 6874374Slars *s1++ = '\\'; 6884374Slars *s1++ = cur_chr; 6894374Slars break; 6904374Slars } 69134766Speter } 6924374Slars 6934374Slars if (add_return) 6944374Slars *s1++ = '\r'; 6954374Slars 6964374Slars *s1++ = '\0'; /* guarantee closure */ 6974374Slars *s1++ = '\0'; /* terminate the string */ 6984374Slars return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 69934766Speter} 7004374Slars 7014374Slars/* 70228597Speter * A modified version of 'strtok'. This version skips \ sequences. 70328597Speter */ 70428597Speter 705119316Smarkmchar * 706119316Smarkmexpect_strtok (char *s, const char *term) 70734766Speter{ 708119316Smarkm static char *str = blank; 70928597Speter int escape_flag = 0; 71028597Speter char *result; 71134766Speter 71228597Speter/* 71328597Speter * If a string was specified then do initial processing. 71428597Speter */ 71528597Speter if (s) 71628597Speter str = s; 71734766Speter 71828597Speter/* 71928597Speter * If this is the escape flag then reset it and ignore the character. 72028597Speter */ 72128597Speter if (*str) 72228597Speter result = str; 72328597Speter else 72428597Speter result = (char *) 0; 72528597Speter 72634766Speter while (*str) { 72734766Speter if (escape_flag) { 72828597Speter escape_flag = 0; 72928597Speter ++str; 73028597Speter continue; 73134766Speter } 73228597Speter 73334766Speter if (*str == '\\') { 73428597Speter ++str; 73528597Speter escape_flag = 1; 73628597Speter continue; 73734766Speter } 73834766Speter 73928597Speter/* 74028597Speter * If this is not in the termination string, continue. 74128597Speter */ 74234766Speter if (strchr (term, *str) == (char *) 0) { 74328597Speter ++str; 74428597Speter continue; 74534766Speter } 74634766Speter 74728597Speter/* 74828597Speter * This is the terminator. Mark the end of the string and stop. 74928597Speter */ 75028597Speter *str++ = '\0'; 75128597Speter break; 75234766Speter } 75328597Speter return (result); 75434766Speter} 75528597Speter 75628597Speter/* 7574374Slars * Process the expect string 7584374Slars */ 75928597Speter 760119316Smarkmvoid 761119316Smarkmchat_expect(char *s) 76234766Speter{ 76328597Speter char *expect; 76428597Speter char *reply; 76528597Speter 76634766Speter if (strcmp(s, "HANGUP") == 0) { 76728597Speter ++hup_next; 76828597Speter return; 76934766Speter } 77028597Speter 77134766Speter if (strcmp(s, "ABORT") == 0) { 7724374Slars ++abort_next; 7734374Slars return; 77434766Speter } 7754374Slars 77634766Speter if (strcmp(s, "CLR_ABORT") == 0) { 77728597Speter ++clear_abort_next; 77828597Speter return; 77934766Speter } 78028597Speter 78134766Speter if (strcmp(s, "REPORT") == 0) { 78211990Speter ++report_next; 78311990Speter return; 78434766Speter } 78511990Speter 78634766Speter if (strcmp(s, "CLR_REPORT") == 0) { 78728597Speter ++clear_report_next; 78828597Speter return; 78934766Speter } 79028597Speter 79134766Speter if (strcmp(s, "TIMEOUT") == 0) { 7924374Slars ++timeout_next; 7934374Slars return; 79434766Speter } 7954374Slars 79634766Speter if (strcmp(s, "ECHO") == 0) { 79728597Speter ++echo_next; 79828597Speter return; 79934766Speter } 80034766Speter 80134766Speter if (strcmp(s, "SAY") == 0) { 80228597Speter ++say_next; 80328597Speter return; 80434766Speter } 80534766Speter 80628597Speter/* 80728597Speter * Fetch the expect and reply string. 80828597Speter */ 80934766Speter for (;;) { 81028597Speter expect = expect_strtok (s, "-"); 81128597Speter s = (char *) 0; 8124374Slars 81328597Speter if (expect == (char *) 0) 81428597Speter return; 81528597Speter 81628597Speter reply = expect_strtok (s, "-"); 81734766Speter 81828597Speter/* 81928597Speter * Handle the expect string. If successful then exit. 82028597Speter */ 82128597Speter if (get_string (expect)) 82228597Speter return; 82334766Speter 82428597Speter/* 82528597Speter * If there is a sub-reply string then send it. Otherwise any condition 82628597Speter * is terminal. 82728597Speter */ 82828597Speter if (reply == (char *) 0 || exit_code != 3) 82928597Speter break; 8304374Slars 83128597Speter chat_send (reply); 83234766Speter } 83334766Speter 83428597Speter/* 83528597Speter * The expectation did not occur. This is terminal. 83628597Speter */ 83728597Speter if (fail_reason) 838121784Skientzle chat_logf("Failed (%s)", fail_reason); 83928597Speter else 840121784Skientzle chat_logf("Failed"); 84128597Speter terminate(exit_code); 84234766Speter} 8434374Slars 84428597Speter/* 84528597Speter * Translate the input character to the appropriate string for printing 84628597Speter * the data. 84728597Speter */ 84828597Speter 849119316Smarkmchar * 850119316Smarkmcharacter(int c) 85134766Speter{ 8524374Slars static char string[10]; 853119316Smarkm const char *meta; 8544374Slars 8554374Slars meta = (c & 0x80) ? "M-" : ""; 8564374Slars c &= 0x7F; 8574374Slars 8584374Slars if (c < 32) 8594374Slars sprintf(string, "%s^%c", meta, (int)c + '@'); 86034766Speter else if (c == 127) 86134766Speter sprintf(string, "%s^?", meta); 8624374Slars else 86334766Speter sprintf(string, "%s%c", meta, c); 8644374Slars 8654374Slars return (string); 86634766Speter} 8674374Slars 8684374Slars/* 8694374Slars * process the reply string 8704374Slars */ 871119316Smarkmvoid 872119316Smarkmchat_send(char *s) 87334766Speter{ 87434766Speter if (say_next) { 87528597Speter say_next = 0; 87628597Speter s = clean(s,0); 87780381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 87828597Speter free(s); 87928597Speter return; 88034766Speter } 88134766Speter 88234766Speter if (hup_next) { 88328597Speter hup_next = 0; 88428597Speter if (strcmp(s, "OFF") == 0) 88528597Speter signal(SIGHUP, SIG_IGN); 88628597Speter else 88728597Speter signal(SIGHUP, sighup); 88828597Speter return; 88934766Speter } 89034766Speter 89134766Speter if (echo_next) { 89228597Speter echo_next = 0; 89328597Speter echo = (strcmp(s, "ON") == 0); 89428597Speter return; 89534766Speter } 89634766Speter 89734766Speter if (abort_next) { 8984374Slars char *s1; 89911990Speter 9004374Slars abort_next = 0; 90111990Speter 9024374Slars if (n_aborts >= MAX_ABORTS) 90334766Speter fatal(2, "Too many ABORT strings"); 90411990Speter 9054374Slars s1 = clean(s, 0); 90611990Speter 90711990Speter if (strlen(s1) > strlen(s) 90811990Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 90934766Speter fatal(1, "Illegal or too-long ABORT string ('%v')", s); 9104374Slars 9114374Slars abort_string[n_aborts++] = s1; 9124374Slars 9134374Slars if (verbose) 914121784Skientzle chat_logf("abort on (%v)", s); 91511990Speter return; 91634766Speter } 91711990Speter 91834766Speter if (clear_abort_next) { 91928597Speter char *s1; 92028597Speter int i; 92128597Speter int old_max; 92228597Speter int pack = 0; 92328597Speter 92428597Speter clear_abort_next = 0; 92528597Speter 92628597Speter s1 = clean(s, 0); 92728597Speter 92828597Speter if (strlen(s1) > strlen(s) 92928597Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 93034766Speter fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 93128597Speter 93228597Speter old_max = n_aborts; 93334766Speter for (i=0; i < n_aborts; i++) { 93434766Speter if ( strcmp(s1,abort_string[i]) == 0 ) { 93534766Speter free(abort_string[i]); 93634766Speter abort_string[i] = NULL; 93734766Speter pack++; 93834766Speter n_aborts--; 93934766Speter if (verbose) 940121784Skientzle chat_logf("clear abort on (%v)", s); 94128597Speter } 94234766Speter } 94328597Speter free(s1); 94434766Speter if (pack) 94534766Speter pack_array(abort_string,old_max); 94628597Speter return; 94734766Speter } 94828597Speter 94934766Speter if (report_next) { 95011990Speter char *s1; 95111990Speter 95211990Speter report_next = 0; 95311990Speter if (n_reports >= MAX_REPORTS) 95434766Speter fatal(2, "Too many REPORT strings"); 95511990Speter 95611990Speter s1 = clean(s, 0); 95711990Speter 95811990Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 95934766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 96011990Speter 96111990Speter report_string[n_reports++] = s1; 96211990Speter 96311990Speter if (verbose) 964121784Skientzle chat_logf("report (%v)", s); 96511990Speter return; 96634766Speter } 9674374Slars 96834766Speter if (clear_report_next) { 96928597Speter char *s1; 97028597Speter int i; 97128597Speter int old_max; 97228597Speter int pack = 0; 97328597Speter 97428597Speter clear_report_next = 0; 97528597Speter 97628597Speter s1 = clean(s, 0); 97728597Speter 97828597Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 97934766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 98028597Speter 98128597Speter old_max = n_reports; 98234766Speter for (i=0; i < n_reports; i++) { 98334766Speter if ( strcmp(s1,report_string[i]) == 0 ) { 98434766Speter free(report_string[i]); 98534766Speter report_string[i] = NULL; 98634766Speter pack++; 98734766Speter n_reports--; 98834766Speter if (verbose) 989121784Skientzle chat_logf("clear report (%v)", s); 99028597Speter } 99134766Speter } 99228597Speter free(s1); 99334766Speter if (pack) 99434766Speter pack_array(report_string,old_max); 99528597Speter 99628597Speter return; 99734766Speter } 99828597Speter 99934766Speter if (timeout_next) { 100011990Speter timeout_next = 0; 100111990Speter timeout = atoi(s); 100211990Speter 100311990Speter if (timeout <= 0) 100411990Speter timeout = DEFAULT_CHAT_TIMEOUT; 10054374Slars 100611990Speter if (verbose) 1007121784Skientzle chat_logf("timeout set to %d seconds", timeout); 100834766Speter 100911990Speter return; 101034766Speter } 101111990Speter 101211990Speter if (strcmp(s, "EOT") == 0) 1013119316Smarkm s = strdup("^D\\c"); 101434766Speter else if (strcmp(s, "BREAK") == 0) 1015119316Smarkm s = strdup("\\K\\c"); 101611990Speter 101711990Speter if (!put_string(s)) 101834766Speter fatal(1, "Failed"); 101934766Speter} 10204374Slars 1021119316Smarkmint 1022119316Smarkmget_char(void) 102334766Speter{ 10244374Slars int status; 10254374Slars char c; 10264374Slars 102780381Ssheldonh status = read(STDIN_FILENO, &c, 1); 10284374Slars 102934766Speter switch (status) { 103011990Speter case 1: 103111990Speter return ((int)c & 0x7F); 10324374Slars 103311990Speter default: 1034121784Skientzle chat_logf("warning: read() on stdin returned %d", status); 10354374Slars 103611990Speter case -1: 103711990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 103834766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 103934766Speter 104034766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 104134766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 104211990Speter 104311990Speter return (-1); 10444374Slars } 104534766Speter} 10464374Slars 1047119316Smarkmint put_char(int c) 104834766Speter{ 10494374Slars int status; 105011990Speter char ch = c; 10514374Slars 105211990Speter usleep(10000); /* inter-character typing delay (?) */ 10534374Slars 105480381Ssheldonh status = write(STDOUT_FILENO, &ch, 1); 10554374Slars 105634766Speter switch (status) { 105711990Speter case 1: 105811990Speter return (0); 105911990Speter 106011990Speter default: 1061121784Skientzle chat_logf("warning: write() on stdout returned %d", status); 106211990Speter 106311990Speter case -1: 106411990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 106534766Speter fatal(2, "Can't get file mode flags on stdin, %m"); 106634766Speter 106734766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 106834766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 106911990Speter 107011990Speter return (-1); 10714374Slars } 107234766Speter} 10734374Slars 1074119316Smarkmint 1075119316Smarkmwrite_char(int c) 107634766Speter{ 107734766Speter if (alarmed || put_char(c) < 0) { 107811990Speter alarm(0); 107911990Speter alarmed = 0; 10804374Slars 108134766Speter if (verbose) { 10824374Slars if (errno == EINTR || errno == EWOULDBLOCK) 1083121784Skientzle chat_logf(" -- write timed out"); 10844374Slars else 1085121784Skientzle chat_logf(" -- write failed: %m"); 108634766Speter } 10874374Slars return (0); 108834766Speter } 10894374Slars return (1); 109034766Speter} 10914374Slars 1092119316Smarkmint 1093119316Smarkmput_string(char *s) 109434766Speter{ 109528597Speter quiet = 0; 10964374Slars s = clean(s, 1); 10974374Slars 1098119316Smarkm if (verbose) 1099121784Skientzle chat_logf("send (%v)", quiet ? "??????" : s); 11004374Slars 11014374Slars alarm(timeout); alarmed = 0; 11024374Slars 110334766Speter while (*s) { 1104119316Smarkm char c = *s++; 11054374Slars 110634766Speter if (c != '\\') { 11074374Slars if (!write_char (c)) 11084374Slars return 0; 11094374Slars continue; 111034766Speter } 11114374Slars 11124374Slars c = *s++; 111334766Speter switch (c) { 11144374Slars case 'd': 11154374Slars sleep(1); 11164374Slars break; 11174374Slars 11184374Slars case 'K': 11194374Slars break_sequence(); 11204374Slars break; 11214374Slars 11224374Slars case 'p': 112311990Speter usleep(10000); /* 1/100th of a second (arg is microseconds) */ 11244374Slars break; 11254374Slars 11264374Slars default: 11274374Slars if (!write_char (c)) 11284374Slars return 0; 11294374Slars break; 11304374Slars } 113134766Speter } 11324374Slars 11334374Slars alarm(0); 11344374Slars alarmed = 0; 11354374Slars return (1); 113634766Speter} 11374374Slars 11384374Slars/* 113928597Speter * Echo a character to stderr. 114028597Speter * When called with -1, a '\n' character is generated when 114128597Speter * the cursor is not at the beginning of a line. 114228597Speter */ 1143119316Smarkmvoid 1144119316Smarkmecho_stderr(int n) 114534766Speter{ 114634766Speter static int need_lf; 114734766Speter char *s; 114828597Speter 114934766Speter switch (n) { 115034766Speter case '\r': /* ignore '\r' */ 115134766Speter break; 115234766Speter case -1: 115334766Speter if (need_lf == 0) 115428597Speter break; 1155102412Scharnier /* FALLTHROUGH */ 115634766Speter case '\n': 115780381Ssheldonh write(STDERR_FILENO, "\n", 1); 115834766Speter need_lf = 0; 115934766Speter break; 116034766Speter default: 116134766Speter s = character(n); 116280381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 116334766Speter need_lf = 1; 116434766Speter break; 116528597Speter } 116634766Speter} 116728597Speter 116828597Speter/* 11694374Slars * 'Wait for' this string to appear on this file descriptor. 11704374Slars */ 1171119316Smarkmint 1172119316Smarkmget_string(char *string) 117334766Speter{ 11744374Slars char temp[STR_LEN]; 1175119316Smarkm int c, printed = 0; 1176119316Smarkm size_t len, minlen; 1177119316Smarkm char *s = temp, *end = s + STR_LEN; 117834766Speter char *logged = temp; 11794374Slars 11804374Slars fail_reason = (char *)0; 118153686Skris 118253686Skris if (strlen(string) > STR_LEN) { 1183121784Skientzle chat_logf("expect string is too long"); 118453686Skris exit_code = 1; 118553686Skris return 0; 118653686Skris } 118753686Skris 11884374Slars string = clean(string, 0); 11894374Slars len = strlen(string); 11904374Slars minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 11914374Slars 11924374Slars if (verbose) 1193121784Skientzle chat_logf("expect (%v)", string); 11944374Slars 119534766Speter if (len == 0) { 11964374Slars if (verbose) 1197121784Skientzle chat_logf("got it"); 11984374Slars return (1); 119934766Speter } 12004374Slars 120111990Speter alarm(timeout); 120211990Speter alarmed = 0; 12034374Slars 120434766Speter while ( ! alarmed && (c = get_char()) >= 0) { 120511990Speter int n, abort_len, report_len; 12064374Slars 120728597Speter if (echo) 120828597Speter echo_stderr(c); 120934766Speter if (verbose && c == '\n') { 121034766Speter if (s == logged) 1211121784Skientzle chat_logf(""); /* blank line */ 12124374Slars else 1213121784Skientzle chat_logf("%0.*v", s - logged, logged); 121434766Speter logged = s + 1; 121534766Speter } 12164374Slars 121734766Speter *s++ = c; 121834766Speter 121934766Speter if (verbose && s >= logged + 80) { 1220121784Skientzle chat_logf("%0.*v", s - logged, logged); 122134766Speter logged = s; 122234766Speter } 122334766Speter 122428597Speter if (Verbose) { 122534766Speter if (c == '\n') 122634766Speter fputc( '\n', stderr ); 122734766Speter else if (c != '\r') 122834766Speter fprintf( stderr, "%s", character(c) ); 122928597Speter } 123028597Speter 123134766Speter if (!report_gathering) { 123234766Speter for (n = 0; n < n_reports; ++n) { 123311990Speter if ((report_string[n] != (char*) NULL) && 123411990Speter s - temp >= (report_len = strlen(report_string[n])) && 123534766Speter strncmp(s - report_len, report_string[n], report_len) == 0) { 123611990Speter time_t time_now = time ((time_t*) NULL); 123711990Speter struct tm* tm_now = localtime (&time_now); 123811990Speter 123911990Speter strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 124011990Speter strcat (report_buffer, report_string[n]); 124111990Speter 124211990Speter report_string[n] = (char *) NULL; 124311990Speter report_gathering = 1; 124411990Speter break; 124534766Speter } 124611990Speter } 124734766Speter } 124834766Speter else { 124934766Speter if (!iscntrl (c)) { 125011990Speter int rep_len = strlen (report_buffer); 125111990Speter report_buffer[rep_len] = c; 125211990Speter report_buffer[rep_len + 1] = '\0'; 125334766Speter } 125434766Speter else { 125511990Speter report_gathering = 0; 125611990Speter fprintf (report_fp, "chat: %s\n", report_buffer); 125711990Speter } 125834766Speter } 125911990Speter 1260119316Smarkm if ((size_t)(s - temp) >= len && 126128597Speter c == string[len - 1] && 126234766Speter strncmp(s - len, string, len) == 0) { 126334766Speter if (verbose) { 126434766Speter if (s > logged) 1265121784Skientzle chat_logf("%0.*v", s - logged, logged); 1266121784Skientzle chat_logf(" -- got it\n"); 126734766Speter } 126828597Speter 126928597Speter alarm(0); 127028597Speter alarmed = 0; 127128597Speter return (1); 127234766Speter } 127328597Speter 127434766Speter for (n = 0; n < n_aborts; ++n) { 127528597Speter if (s - temp >= (abort_len = strlen(abort_string[n])) && 127634766Speter strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 127734766Speter if (verbose) { 127834766Speter if (s > logged) 1279121784Skientzle chat_logf("%0.*v", s - logged, logged); 1280121784Skientzle chat_logf(" -- failed"); 128134766Speter } 128234766Speter 128328597Speter alarm(0); 128428597Speter alarmed = 0; 128528597Speter exit_code = n + 4; 128628597Speter strcpy(fail_reason = fail_buffer, abort_string[n]); 128728597Speter return (0); 128828597Speter } 128934766Speter } 129028597Speter 129134766Speter if (s >= end) { 129234766Speter if (logged < s - minlen) { 1293121784Skientzle chat_logf("%0.*v", s - logged, logged); 129434766Speter logged = s; 129534766Speter } 129634766Speter s -= minlen; 129734766Speter memmove(temp, s, minlen); 129834766Speter logged = temp + (logged - s); 12994374Slars s = temp + minlen; 130034766Speter } 13014374Slars 13024374Slars if (alarmed && verbose) 1303121784Skientzle chat_logf("warning: alarm synchronization problem"); 130434766Speter } 13054374Slars 13064374Slars alarm(0); 130711990Speter 130834766Speter if (verbose && printed) { 13094374Slars if (alarmed) 1310121784Skientzle chat_logf(" -- read timed out"); 13114374Slars else 1312121784Skientzle chat_logf(" -- read failed: %m"); 131334766Speter } 13144374Slars 131511990Speter exit_code = 3; 131611990Speter alarmed = 0; 13174374Slars return (0); 131834766Speter} 13194374Slars 132028597Spetervoid 1321119316Smarkmpack_array(char **array, int end) 132228597Speter{ 132328597Speter int i, j; 132428597Speter 132528597Speter for (i = 0; i < end; i++) { 132628597Speter if (array[i] == NULL) { 132728597Speter for (j = i+1; j < end; ++j) 132828597Speter if (array[j] != NULL) 132928597Speter array[i++] = array[j]; 133028597Speter for (; i < end; ++i) 133128597Speter array[i] = NULL; 133228597Speter break; 133328597Speter } 133428597Speter } 133528597Speter} 133634766Speter 133734766Speter/* 133834766Speter * vfmtmsg - format a message into a buffer. Like vsprintf except we 133934766Speter * also specify the length of the output buffer, and we handle the 134034766Speter * %m (error message) format. 134134766Speter * Doesn't do floating-point formats. 134234766Speter * Returns the number of chars put into buf. 134334766Speter */ 134434766Speter#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 134534766Speter 134634766Speterint 1347119316Smarkmvfmtmsg(char *buf, int buflen, const char *fmt, va_list args) 134834766Speter{ 134934766Speter int c, i, n; 135034766Speter int width, prec, fillch; 135134766Speter int base, len, neg, quoted; 135234766Speter unsigned long val = 0; 135334766Speter char *str, *buf0; 135434766Speter const char *f; 135534766Speter unsigned char *p; 135634766Speter char num[32]; 135734766Speter static char hexchars[] = "0123456789abcdef"; 135834766Speter 135934766Speter buf0 = buf; 136034766Speter --buflen; 136134766Speter while (buflen > 0) { 136234766Speter for (f = fmt; *f != '%' && *f != 0; ++f) 136334766Speter ; 136434766Speter if (f > fmt) { 136534766Speter len = f - fmt; 136634766Speter if (len > buflen) 136734766Speter len = buflen; 136834766Speter memcpy(buf, fmt, len); 136934766Speter buf += len; 137034766Speter buflen -= len; 137134766Speter fmt = f; 137234766Speter } 137334766Speter if (*fmt == 0) 137434766Speter break; 137534766Speter c = *++fmt; 137634766Speter width = prec = 0; 137734766Speter fillch = ' '; 137834766Speter if (c == '0') { 137934766Speter fillch = '0'; 138034766Speter c = *++fmt; 138134766Speter } 138234766Speter if (c == '*') { 138334766Speter width = va_arg(args, int); 138434766Speter c = *++fmt; 138534766Speter } else { 138634766Speter while (isdigit(c)) { 138734766Speter width = width * 10 + c - '0'; 138834766Speter c = *++fmt; 138934766Speter } 139034766Speter } 139134766Speter if (c == '.') { 139234766Speter c = *++fmt; 139334766Speter if (c == '*') { 139434766Speter prec = va_arg(args, int); 139534766Speter c = *++fmt; 139634766Speter } else { 139734766Speter while (isdigit(c)) { 139834766Speter prec = prec * 10 + c - '0'; 139934766Speter c = *++fmt; 140034766Speter } 140134766Speter } 140234766Speter } 140334766Speter str = 0; 140434766Speter base = 0; 140534766Speter neg = 0; 140634766Speter ++fmt; 140734766Speter switch (c) { 140834766Speter case 'd': 140934766Speter i = va_arg(args, int); 141034766Speter if (i < 0) { 141134766Speter neg = 1; 141234766Speter val = -i; 141334766Speter } else 141434766Speter val = i; 141534766Speter base = 10; 141634766Speter break; 141734766Speter case 'o': 141834766Speter val = va_arg(args, unsigned int); 141934766Speter base = 8; 142034766Speter break; 142134766Speter case 'x': 142234766Speter val = va_arg(args, unsigned int); 142334766Speter base = 16; 142434766Speter break; 142534766Speter case 'p': 142634766Speter val = (unsigned long) va_arg(args, void *); 142734766Speter base = 16; 142834766Speter neg = 2; 142934766Speter break; 143034766Speter case 's': 143134766Speter str = va_arg(args, char *); 143234766Speter break; 143334766Speter case 'c': 143434766Speter num[0] = va_arg(args, int); 143534766Speter num[1] = 0; 143634766Speter str = num; 143734766Speter break; 143834766Speter case 'm': 143934766Speter str = strerror(errno); 144034766Speter break; 144134766Speter case 'v': /* "visible" string */ 144234766Speter case 'q': /* quoted string */ 144334766Speter quoted = c == 'q'; 144434766Speter p = va_arg(args, unsigned char *); 144534766Speter if (fillch == '0' && prec > 0) { 144634766Speter n = prec; 144734766Speter } else { 144834766Speter n = strlen((char *)p); 144934766Speter if (prec > 0 && prec < n) 145034766Speter n = prec; 145134766Speter } 145234766Speter while (n > 0 && buflen > 0) { 145334766Speter c = *p++; 145434766Speter --n; 145534766Speter if (!quoted && c >= 0x80) { 145634766Speter OUTCHAR('M'); 145734766Speter OUTCHAR('-'); 145834766Speter c -= 0x80; 145934766Speter } 146034766Speter if (quoted && (c == '"' || c == '\\')) 146134766Speter OUTCHAR('\\'); 146234766Speter if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 146334766Speter if (quoted) { 146434766Speter OUTCHAR('\\'); 146534766Speter switch (c) { 146634766Speter case '\t': OUTCHAR('t'); break; 146734766Speter case '\n': OUTCHAR('n'); break; 146834766Speter case '\b': OUTCHAR('b'); break; 146934766Speter case '\f': OUTCHAR('f'); break; 147034766Speter default: 147134766Speter OUTCHAR('x'); 147234766Speter OUTCHAR(hexchars[c >> 4]); 147334766Speter OUTCHAR(hexchars[c & 0xf]); 147434766Speter } 147534766Speter } else { 147634766Speter if (c == '\t') 147734766Speter OUTCHAR(c); 147834766Speter else { 147934766Speter OUTCHAR('^'); 148034766Speter OUTCHAR(c ^ 0x40); 148134766Speter } 148234766Speter } 148334766Speter } else 148434766Speter OUTCHAR(c); 148534766Speter } 148634766Speter continue; 148734766Speter default: 148834766Speter *buf++ = '%'; 148934766Speter if (c != '%') 149034766Speter --fmt; /* so %z outputs %z etc. */ 149134766Speter --buflen; 149234766Speter continue; 149334766Speter } 149434766Speter if (base != 0) { 149534766Speter str = num + sizeof(num); 149634766Speter *--str = 0; 149734766Speter while (str > num + neg) { 149834766Speter *--str = hexchars[val % base]; 149934766Speter val = val / base; 150034766Speter if (--prec <= 0 && val == 0) 150134766Speter break; 150234766Speter } 150334766Speter switch (neg) { 150434766Speter case 1: 150534766Speter *--str = '-'; 150634766Speter break; 150734766Speter case 2: 150834766Speter *--str = 'x'; 150934766Speter *--str = '0'; 151034766Speter break; 151134766Speter } 151234766Speter len = num + sizeof(num) - 1 - str; 151334766Speter } else { 151434766Speter len = strlen(str); 151534766Speter if (prec > 0 && len > prec) 151634766Speter len = prec; 151734766Speter } 151834766Speter if (width > 0) { 151934766Speter if (width > buflen) 152034766Speter width = buflen; 152134766Speter if ((n = width - len) > 0) { 152234766Speter buflen -= n; 152334766Speter for (; n > 0; --n) 152434766Speter *buf++ = fillch; 152534766Speter } 152634766Speter } 152734766Speter if (len > buflen) 152834766Speter len = buflen; 152934766Speter memcpy(buf, str, len); 153034766Speter buf += len; 153134766Speter buflen -= len; 153234766Speter } 153334766Speter *buf = 0; 153434766Speter return buf - buf0; 153534766Speter} 1536