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: stable/11/usr.bin/chat/chat.c 313100 2017-02-02 18:27:20Z asomers $"); 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 112241737Sedstatic int echo; 113241737Sedstatic int verbose; 114241737Sedstatic int to_log; 115241737Sedstatic int to_stderr; 116241737Sedstatic int Verbose; 117241737Sedstatic int quiet; 118241737Sedstatic int exit_code; 119241737Sedstatic FILE* report_fp; 120241737Sedstatic char *report_file; 121241737Sedstatic char *chat_file; 122241737Sedstatic char *phone_num; 123241737Sedstatic char *phone_num2; 124241737Sedstatic int timeout = DEFAULT_CHAT_TIMEOUT; 1254374Slars 126119316Smarkmstatic char blank[] = ""; 127119316Smarkm 128241737Sedstatic int have_tty_parameters; 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) 133241737Sedstatic struct termios saved_tty_parameters; 1344374Slars 135241737Sedstatic char *abort_string[MAX_ABORTS], *fail_reason, fail_buffer[50]; 136241737Sedstatic int n_aborts, abort_next, timeout_next, echo_next; 137241737Sedstatic int clear_abort_next; 1384374Slars 139241737Sedstatic char *report_string[MAX_REPORTS]; 140241737Sedstatic char report_buffer[50]; 141241737Sedstatic int n_reports, report_next, report_gathering; 142241737Sedstatic int clear_report_next; 14311990Speter 144241737Sedstatic int say_next, hup_next; 14528597Speter 14692920Simpvoid *dup_mem(void *b, size_t c); 14792920Simpvoid *copy_of(char *s); 14892920Simpstatic void usage(void); 149121784Skientzlevoid chat_logf(const char *fmt, ...); 15092920Simpvoid fatal(int code, const char *fmt, ...); 15192920SimpSIGTYPE sigalrm(int signo); 15292920SimpSIGTYPE sigint(int signo); 15392920SimpSIGTYPE sigterm(int signo); 15492920SimpSIGTYPE sighup(int signo); 15592920Simpvoid init(void); 15692920Simpvoid set_tty_parameters(void); 15792920Simpvoid echo_stderr(int); 15892920Simpvoid break_sequence(void); 15992920Simpvoid terminate(int status); 160119316Smarkmvoid do_file(char *chatfile); 161119316Smarkmint get_string(char *string); 162119316Smarkmint put_string(char *s); 16392920Simpint write_char(int c); 16492920Simpint put_char(int c); 16592920Simpint get_char(void); 166119316Smarkmvoid chat_send(char *s); 16792920Simpchar *character(int c); 168119316Smarkmvoid chat_expect(char *s); 169119316Smarkmchar *clean(char *s, int sending); 17092920Simpvoid pack_array(char **array, int end); 171119316Smarkmchar *expect_strtok(char *, const char *); 17292920Simpint vfmtmsg(char *, int, const char *, va_list); /* vsprintf++ */ 1734374Slars 174119316Smarkmvoid * 175119316Smarkmdup_mem(void *b, size_t c) 17634766Speter{ 1774374Slars void *ans = malloc (c); 1784374Slars if (!ans) 17934766Speter fatal(2, "memory error!"); 18034766Speter 1814374Slars memcpy (ans, b, c); 1824374Slars return ans; 18334766Speter} 1844374Slars 185119316Smarkmvoid * 186119316Smarkmcopy_of(char *s) 18734766Speter{ 1884374Slars return dup_mem (s, strlen (s) + 1); 18934766Speter} 1904374Slars 1914374Slars/* 192176888Sdelphij * chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout] 193176888Sdelphij * [-T phone-number] [-U phone-number2] [chat-script] 194176888Sdelphij * where chat-script has the form: 195176888Sdelphij * [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]] 1964374Slars * 197176888Sdelphij * Perform a UUCP-dialer-like chat script on stdin and stdout. 1984374Slars */ 1994374Slarsint 200119316Smarkmmain(int argc, char *argv[]) 20134766Speter{ 2024374Slars int option; 2034374Slars 20411990Speter tzset(); 2054374Slars 206176888Sdelphij while ((option = getopt(argc, argv, "ef:r:sSt:T:U:vV")) != -1) { 20734766Speter switch (option) { 20834766Speter case 'e': 20934766Speter ++echo; 21034766Speter break; 21128597Speter 212176888Sdelphij case 'f': 213176888Sdelphij if (chat_file != NULL) 214176888Sdelphij free(chat_file); 215176888Sdelphij chat_file = copy_of(optarg); 21634766Speter break; 2174374Slars 218176888Sdelphij case 'r': 219176888Sdelphij if (report_fp != NULL) 220176888Sdelphij fclose(report_fp); 221176888Sdelphij if (report_file != NULL) 222176888Sdelphij free(report_file); 223176888Sdelphij report_file = copy_of(optarg); 224176888Sdelphij report_fp = fopen(report_file, "a"); 225176888Sdelphij if (report_fp != NULL) { 226176888Sdelphij if (verbose) 227176888Sdelphij fprintf(report_fp, "Opening \"%s\"...\n", report_file); 228176888Sdelphij } else 229176888Sdelphij fatal(2, "cannot open \"%s\" for appending", report_file); 23034766Speter break; 23128597Speter 23234766Speter case 's': 23334766Speter ++to_stderr; 23434766Speter break; 23534766Speter 23634766Speter case 'S': 23734766Speter to_log = 0; 23834766Speter break; 23934766Speter 240176888Sdelphij case 't': 241176888Sdelphij timeout = atoi(optarg); 24234766Speter break; 2434374Slars 244176888Sdelphij case 'T': 245176888Sdelphij if (phone_num != NULL) 246176888Sdelphij free(phone_num); 247176888Sdelphij phone_num = copy_of(optarg); 24834766Speter break; 2494374Slars 250176888Sdelphij case 'U': 251176888Sdelphij if (phone_num2 != NULL) 252176888Sdelphij free(phone_num2); 253176888Sdelphij phone_num2 = copy_of(optarg); 25434766Speter break; 2554374Slars 256176888Sdelphij case 'v': 257176888Sdelphij ++verbose; 25834766Speter break; 25934766Speter 260176888Sdelphij case 'V': 261176888Sdelphij ++Verbose; 26234766Speter break; 26334766Speter 26434766Speter default: 26534766Speter usage(); 26634766Speter break; 26734766Speter } 26834766Speter } 269176888Sdelphij 270176888Sdelphij argc -= optind; 271176888Sdelphij argv += optind; 272176888Sdelphij 27311990Speter/* 27411990Speter * Default the report file to the stderr location 27511990Speter */ 27611990Speter if (report_fp == NULL) 27711990Speter report_fp = stderr; 2784374Slars 27934766Speter if (to_log) { 28034766Speter openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 2814374Slars 28234766Speter if (verbose) 28334766Speter setlogmask(LOG_UPTO(LOG_INFO)); 28434766Speter else 28534766Speter setlogmask(LOG_UPTO(LOG_WARNING)); 28634766Speter } 2874374Slars 28834766Speter if (chat_file != NULL) { 289176888Sdelphij if (*argv != NULL) 2904374Slars usage(); 291176888Sdelphij else { 292176888Sdelphij init(); 293176888Sdelphij do_file(chat_file); 294176888Sdelphij } 29534766Speter } else { 296176888Sdelphij init(); 297176888Sdelphij while (*argv != NULL && argc > 0) { 298176888Sdelphij chat_expect(*argv); 299176888Sdelphij argv++; 300176888Sdelphij argc--; 3014374Slars 302176888Sdelphij if (*argv != NULL && argc > 0) { 303176888Sdelphij chat_send(*argv); 304176888Sdelphij argv++; 305176888Sdelphij argc--; 306176888Sdelphij } 3074374Slars } 30834766Speter } 3094374Slars 3104374Slars terminate(0); 31128597Speter return 0; 31234766Speter} 3134374Slars 3144374Slars/* 3154374Slars * Process a chat script when read from a file. 3164374Slars */ 3174374Slars 318119316Smarkmvoid 319119316Smarkmdo_file(char *chatfile) 32034766Speter{ 32132069Salex int linect, sendflg; 3224374Slars char *sp, *arg, quote; 3234374Slars char buf [STR_LEN]; 3244374Slars FILE *cfp; 3254374Slars 326119316Smarkm cfp = fopen (chatfile, "r"); 32711990Speter if (cfp == NULL) 328119316Smarkm fatal(1, "%s -- open failed: %m", chatfile); 3294374Slars 3304374Slars linect = 0; 3314374Slars sendflg = 0; 3324374Slars 33334766Speter while (fgets(buf, STR_LEN, cfp) != NULL) { 3344374Slars sp = strchr (buf, '\n'); 3354374Slars if (sp) 3364374Slars *sp = '\0'; 3374374Slars 3384374Slars linect++; 3394374Slars sp = buf; 34028597Speter 34128597Speter /* lines starting with '#' are comments. If a real '#' 34228597Speter is to be expected, it should be quoted .... */ 34334766Speter if ( *sp == '#' ) 34434766Speter continue; 34528597Speter 34634766Speter while (*sp != '\0') { 34734766Speter if (*sp == ' ' || *sp == '\t') { 3484374Slars ++sp; 3494374Slars continue; 35034766Speter } 3514374Slars 35234766Speter if (*sp == '"' || *sp == '\'') { 3534374Slars quote = *sp++; 3544374Slars arg = sp; 35534766Speter while (*sp != quote) { 3564374Slars if (*sp == '\0') 35734766Speter fatal(1, "unterminated quote (line %d)", linect); 35834766Speter 35934766Speter if (*sp++ == '\\') { 3604374Slars if (*sp != '\0') 3614374Slars ++sp; 3624374Slars } 3634374Slars } 36434766Speter } 36534766Speter else { 3664374Slars arg = sp; 3674374Slars while (*sp != '\0' && *sp != ' ' && *sp != '\t') 3684374Slars ++sp; 36934766Speter } 3704374Slars 3714374Slars if (*sp != '\0') 3724374Slars *sp++ = '\0'; 3734374Slars 3744374Slars if (sendflg) 3754374Slars chat_send (arg); 3764374Slars else 3774374Slars chat_expect (arg); 3784374Slars sendflg = !sendflg; 3794374Slars } 38034766Speter } 3814374Slars fclose (cfp); 38234766Speter} 3834374Slars 3844374Slars/* 3854374Slars * We got an error parsing the command line. 3864374Slars */ 38726880Scharnierstatic void 388119316Smarkmusage(void) 38934766Speter{ 390176888Sdelphij fprintf(stderr, 391176888Sdelphij "Usage: chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout]\n" 392176888Sdelphij " [-T phone-number] [-U phone-number2] [chat-script]\n" 393176888Sdelphij "where chat-script has the form:\n" 394176888Sdelphij " [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]]\n"); 3954374Slars exit(1); 39634766Speter} 3974374Slars 39834766Speter/* 39934766Speter * Send a message to syslog and/or stderr. 40034766Speter */ 401119316Smarkmvoid 402121784Skientzlechat_logf(const char *fmt, ...) 40328597Speter{ 404241737Sed char line[1024]; 40534766Speter va_list args; 4064374Slars 40734766Speter va_start(args, fmt); 40834766Speter vfmtmsg(line, sizeof(line), fmt, args); 409236213Skevlo va_end(args); 41034766Speter if (to_log) 41128597Speter syslog(LOG_INFO, "%s", line); 41234766Speter if (to_stderr) 41334766Speter fprintf(stderr, "%s\n", line); 41428597Speter} 4154374Slars 4164374Slars/* 4174374Slars * Print an error message and terminate. 4184374Slars */ 4194374Slars 420119316Smarkmvoid 421119316Smarkmfatal(int code, const char *fmt, ...) 42234766Speter{ 423241737Sed char line[1024]; 42434766Speter va_list args; 4254374Slars 42634766Speter va_start(args, fmt); 42734766Speter vfmtmsg(line, sizeof(line), fmt, args); 428236213Skevlo va_end(args); 42934766Speter if (to_log) 43034766Speter syslog(LOG_ERR, "%s", line); 43134766Speter if (to_stderr) 43234766Speter fprintf(stderr, "%s\n", line); 43334766Speter terminate(code); 43434766Speter} 4354374Slars 436241737Sedstatic int alarmed; 4374374Slars 438119316SmarkmSIGTYPE sigalrm(int signo __unused) 43934766Speter{ 4404374Slars int flags; 4414374Slars 4424374Slars alarm(1); 4434374Slars alarmed = 1; /* Reset alarm to avoid race window */ 4444374Slars signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 4454374Slars 4464374Slars if ((flags = fcntl(0, F_GETFL, 0)) == -1) 44734766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 4484374Slars 44934766Speter if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 45034766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 45134766Speter 4524374Slars if (verbose) 453121784Skientzle chat_logf("alarm"); 45434766Speter} 4554374Slars 456119316SmarkmSIGTYPE sigint(int signo __unused) 45734766Speter{ 45834766Speter fatal(2, "SIGINT"); 45934766Speter} 4604374Slars 461119316SmarkmSIGTYPE sigterm(int signo __unused) 46234766Speter{ 46334766Speter fatal(2, "SIGTERM"); 46434766Speter} 4654374Slars 466119316SmarkmSIGTYPE sighup(int signo __unused) 46734766Speter{ 46834766Speter fatal(2, "SIGHUP"); 46934766Speter} 4704374Slars 471119316Smarkmvoid init(void) 47234766Speter{ 4734374Slars signal(SIGINT, sigint); 4744374Slars signal(SIGTERM, sigterm); 4754374Slars signal(SIGHUP, sighup); 4764374Slars 4774374Slars set_tty_parameters(); 4784374Slars signal(SIGALRM, sigalrm); 4794374Slars alarm(0); 4804374Slars alarmed = 0; 48134766Speter} 4824374Slars 483119316Smarkmvoid set_tty_parameters(void) 48434766Speter{ 48511990Speter#if defined(get_term_param) 48611990Speter term_parms t; 4874374Slars 48811990Speter if (get_term_param (&t) < 0) 48934766Speter fatal(2, "Can't get terminal parameters: %m"); 4904374Slars 4914374Slars saved_tty_parameters = t; 49211990Speter have_tty_parameters = 1; 4934374Slars 49411990Speter t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 49511990Speter t.c_oflag = 0; 49611990Speter t.c_lflag = 0; 49711990Speter t.c_cc[VERASE] = 49811990Speter t.c_cc[VKILL] = 0; 49911990Speter t.c_cc[VMIN] = 1; 50011990Speter t.c_cc[VTIME] = 0; 5014374Slars 50211990Speter if (set_term_param (&t) < 0) 50334766Speter fatal(2, "Can't set terminal parameters: %m"); 5044374Slars#endif 50534766Speter} 5064374Slars 507119316Smarkmvoid break_sequence(void) 50834766Speter{ 5094374Slars tcsendbreak (0, 0); 51034766Speter} 5114374Slars 512119316Smarkmvoid terminate(int status) 51334766Speter{ 51428597Speter echo_stderr(-1); 51534766Speter if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 51628597Speter/* 51728597Speter * Allow the last of the report string to be gathered before we terminate. 51828597Speter */ 51928597Speter if (report_gathering) { 520119316Smarkm int c; 521119316Smarkm size_t rep_len; 52228597Speter 52328597Speter rep_len = strlen(report_buffer); 524299971Struckman while (rep_len + 1 < sizeof(report_buffer)) { 52528597Speter alarm(1); 52628597Speter c = get_char(); 52728597Speter alarm(0); 52828597Speter if (c < 0 || iscntrl(c)) 52928597Speter break; 53028597Speter report_buffer[rep_len] = c; 53128597Speter ++rep_len; 53228597Speter } 53328597Speter report_buffer[rep_len] = 0; 53428597Speter fprintf (report_fp, "chat: %s\n", report_buffer); 53528597Speter } 53611990Speter if (verbose) 53711990Speter fprintf (report_fp, "Closing \"%s\".\n", report_file); 53811990Speter fclose (report_fp); 53928597Speter report_fp = (FILE *) NULL; 54034766Speter } 5414374Slars 54211990Speter#if defined(get_term_param) 54334766Speter if (have_tty_parameters) { 54411990Speter if (set_term_param (&saved_tty_parameters) < 0) 54534766Speter fatal(2, "Can't restore terminal parameters: %m"); 54634766Speter } 54711990Speter#endif 5484374Slars 54911990Speter exit(status); 55034766Speter} 5514374Slars 5524374Slars/* 5534374Slars * 'Clean up' this string. 5544374Slars */ 555119316Smarkmchar * 556119316Smarkmclean(char *s, int sending) 55734766Speter{ 5584374Slars char temp[STR_LEN], cur_chr; 559119316Smarkm char *s1, *phchar; 5604374Slars int add_return = sending; 5614374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 5624374Slars 5634374Slars s1 = temp; 56453686Skris /* Don't overflow buffer, leave room for chars we append later */ 565119316Smarkm while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) { 5664374Slars cur_chr = *s++; 56734766Speter if (cur_chr == '^') { 5684374Slars cur_chr = *s++; 56934766Speter if (cur_chr == '\0') { 5704374Slars *s1++ = '^'; 5714374Slars break; 57234766Speter } 5734374Slars cur_chr &= 0x1F; 57434766Speter if (cur_chr != 0) { 5754374Slars *s1++ = cur_chr; 57634766Speter } 5774374Slars continue; 57834766Speter } 5794374Slars 58034766Speter if (cur_chr != '\\') { 5814374Slars *s1++ = cur_chr; 5824374Slars continue; 58334766Speter } 5844374Slars 5854374Slars cur_chr = *s++; 58634766Speter if (cur_chr == '\0') { 58734766Speter if (sending) { 5884374Slars *s1++ = '\\'; 5894374Slars *s1++ = '\\'; 59034766Speter } 5914374Slars break; 59234766Speter } 5934374Slars 59434766Speter switch (cur_chr) { 5954374Slars case 'b': 5964374Slars *s1++ = '\b'; 5974374Slars break; 5984374Slars 5994374Slars case 'c': 6004374Slars if (sending && *s == '\0') 6014374Slars add_return = 0; 6024374Slars else 6034374Slars *s1++ = cur_chr; 6044374Slars break; 6054374Slars 6064374Slars case '\\': 6074374Slars case 'K': 6084374Slars case 'p': 6094374Slars case 'd': 6104374Slars if (sending) 6114374Slars *s1++ = '\\'; 6124374Slars 6134374Slars *s1++ = cur_chr; 6144374Slars break; 6154374Slars 61634766Speter case 'T': 61734766Speter if (sending && phone_num) { 61834766Speter for ( phchar = phone_num; *phchar != '\0'; phchar++) 61934766Speter *s1++ = *phchar; 62034766Speter } 62134766Speter else { 62234766Speter *s1++ = '\\'; 62334766Speter *s1++ = 'T'; 62434766Speter } 62534766Speter break; 62634766Speter 62734766Speter case 'U': 62834766Speter if (sending && phone_num2) { 62934766Speter for ( phchar = phone_num2; *phchar != '\0'; phchar++) 63034766Speter *s1++ = *phchar; 63134766Speter } 63234766Speter else { 63334766Speter *s1++ = '\\'; 63434766Speter *s1++ = 'U'; 63534766Speter } 63634766Speter break; 63734766Speter 6384374Slars case 'q': 63928597Speter quiet = 1; 6404374Slars break; 6414374Slars 6424374Slars case 'r': 6434374Slars *s1++ = '\r'; 6444374Slars break; 6454374Slars 6464374Slars case 'n': 6474374Slars *s1++ = '\n'; 6484374Slars break; 6494374Slars 6504374Slars case 's': 6514374Slars *s1++ = ' '; 6524374Slars break; 6534374Slars 6544374Slars case 't': 6554374Slars *s1++ = '\t'; 6564374Slars break; 6574374Slars 6584374Slars case 'N': 65934766Speter if (sending) { 6604374Slars *s1++ = '\\'; 6614374Slars *s1++ = '\0'; 66234766Speter } 6634374Slars else 6644374Slars *s1++ = 'N'; 6654374Slars break; 66611990Speter 6674374Slars default: 66834766Speter if (isoctal (cur_chr)) { 6694374Slars cur_chr &= 0x07; 67034766Speter if (isoctal (*s)) { 6714374Slars cur_chr <<= 3; 6724374Slars cur_chr |= *s++ - '0'; 67334766Speter if (isoctal (*s)) { 6744374Slars cur_chr <<= 3; 6754374Slars cur_chr |= *s++ - '0'; 6764374Slars } 67734766Speter } 6784374Slars 67934766Speter if (cur_chr != 0 || sending) { 6804374Slars if (sending && (cur_chr == '\\' || cur_chr == 0)) 6814374Slars *s1++ = '\\'; 6824374Slars *s1++ = cur_chr; 68334766Speter } 6844374Slars break; 68534766Speter } 6864374Slars 6874374Slars if (sending) 6884374Slars *s1++ = '\\'; 6894374Slars *s1++ = cur_chr; 6904374Slars break; 6914374Slars } 69234766Speter } 6934374Slars 6944374Slars if (add_return) 6954374Slars *s1++ = '\r'; 6964374Slars 6974374Slars *s1++ = '\0'; /* guarantee closure */ 6984374Slars *s1++ = '\0'; /* terminate the string */ 6994374Slars return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 70034766Speter} 7014374Slars 7024374Slars/* 70328597Speter * A modified version of 'strtok'. This version skips \ sequences. 70428597Speter */ 70528597Speter 706119316Smarkmchar * 707119316Smarkmexpect_strtok (char *s, const char *term) 70834766Speter{ 709119316Smarkm static char *str = blank; 71028597Speter int escape_flag = 0; 71128597Speter char *result; 71234766Speter 71328597Speter/* 71428597Speter * If a string was specified then do initial processing. 71528597Speter */ 71628597Speter if (s) 71728597Speter str = s; 71834766Speter 71928597Speter/* 72028597Speter * If this is the escape flag then reset it and ignore the character. 72128597Speter */ 72228597Speter if (*str) 72328597Speter result = str; 72428597Speter else 72528597Speter result = (char *) 0; 72628597Speter 72734766Speter while (*str) { 72834766Speter if (escape_flag) { 72928597Speter escape_flag = 0; 73028597Speter ++str; 73128597Speter continue; 73234766Speter } 73328597Speter 73434766Speter if (*str == '\\') { 73528597Speter ++str; 73628597Speter escape_flag = 1; 73728597Speter continue; 73834766Speter } 73934766Speter 74028597Speter/* 74128597Speter * If this is not in the termination string, continue. 74228597Speter */ 74334766Speter if (strchr (term, *str) == (char *) 0) { 74428597Speter ++str; 74528597Speter continue; 74634766Speter } 74734766Speter 74828597Speter/* 74928597Speter * This is the terminator. Mark the end of the string and stop. 75028597Speter */ 75128597Speter *str++ = '\0'; 75228597Speter break; 75334766Speter } 75428597Speter return (result); 75534766Speter} 75628597Speter 75728597Speter/* 7584374Slars * Process the expect string 7594374Slars */ 76028597Speter 761119316Smarkmvoid 762119316Smarkmchat_expect(char *s) 76334766Speter{ 76428597Speter char *expect; 76528597Speter char *reply; 76628597Speter 76734766Speter if (strcmp(s, "HANGUP") == 0) { 76828597Speter ++hup_next; 76928597Speter return; 77034766Speter } 77128597Speter 77234766Speter if (strcmp(s, "ABORT") == 0) { 7734374Slars ++abort_next; 7744374Slars return; 77534766Speter } 7764374Slars 77734766Speter if (strcmp(s, "CLR_ABORT") == 0) { 77828597Speter ++clear_abort_next; 77928597Speter return; 78034766Speter } 78128597Speter 78234766Speter if (strcmp(s, "REPORT") == 0) { 78311990Speter ++report_next; 78411990Speter return; 78534766Speter } 78611990Speter 78734766Speter if (strcmp(s, "CLR_REPORT") == 0) { 78828597Speter ++clear_report_next; 78928597Speter return; 79034766Speter } 79128597Speter 79234766Speter if (strcmp(s, "TIMEOUT") == 0) { 7934374Slars ++timeout_next; 7944374Slars return; 79534766Speter } 7964374Slars 79734766Speter if (strcmp(s, "ECHO") == 0) { 79828597Speter ++echo_next; 79928597Speter return; 80034766Speter } 80134766Speter 80234766Speter if (strcmp(s, "SAY") == 0) { 80328597Speter ++say_next; 80428597Speter return; 80534766Speter } 80634766Speter 80728597Speter/* 80828597Speter * Fetch the expect and reply string. 80928597Speter */ 81034766Speter for (;;) { 81128597Speter expect = expect_strtok (s, "-"); 81228597Speter s = (char *) 0; 8134374Slars 81428597Speter if (expect == (char *) 0) 81528597Speter return; 81628597Speter 81728597Speter reply = expect_strtok (s, "-"); 81834766Speter 81928597Speter/* 82028597Speter * Handle the expect string. If successful then exit. 82128597Speter */ 82228597Speter if (get_string (expect)) 82328597Speter return; 82434766Speter 82528597Speter/* 82628597Speter * If there is a sub-reply string then send it. Otherwise any condition 82728597Speter * is terminal. 82828597Speter */ 82928597Speter if (reply == (char *) 0 || exit_code != 3) 83028597Speter break; 8314374Slars 83228597Speter chat_send (reply); 83334766Speter } 83434766Speter 83528597Speter/* 83628597Speter * The expectation did not occur. This is terminal. 83728597Speter */ 83828597Speter if (fail_reason) 839121784Skientzle chat_logf("Failed (%s)", fail_reason); 84028597Speter else 841121784Skientzle chat_logf("Failed"); 84228597Speter terminate(exit_code); 84334766Speter} 8444374Slars 84528597Speter/* 84628597Speter * Translate the input character to the appropriate string for printing 84728597Speter * the data. 84828597Speter */ 84928597Speter 850119316Smarkmchar * 851119316Smarkmcharacter(int c) 85234766Speter{ 8534374Slars static char string[10]; 854119316Smarkm const char *meta; 8554374Slars 8564374Slars meta = (c & 0x80) ? "M-" : ""; 8574374Slars c &= 0x7F; 8584374Slars 8594374Slars if (c < 32) 8604374Slars sprintf(string, "%s^%c", meta, (int)c + '@'); 86134766Speter else if (c == 127) 86234766Speter sprintf(string, "%s^?", meta); 8634374Slars else 86434766Speter sprintf(string, "%s%c", meta, c); 8654374Slars 8664374Slars return (string); 86734766Speter} 8684374Slars 8694374Slars/* 8704374Slars * process the reply string 8714374Slars */ 872119316Smarkmvoid 873119316Smarkmchat_send(char *s) 87434766Speter{ 87534766Speter if (say_next) { 87628597Speter say_next = 0; 87728597Speter s = clean(s,0); 87880381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 87928597Speter free(s); 88028597Speter return; 88134766Speter } 88234766Speter 88334766Speter if (hup_next) { 88428597Speter hup_next = 0; 88528597Speter if (strcmp(s, "OFF") == 0) 88628597Speter signal(SIGHUP, SIG_IGN); 88728597Speter else 88828597Speter signal(SIGHUP, sighup); 88928597Speter return; 89034766Speter } 89134766Speter 89234766Speter if (echo_next) { 89328597Speter echo_next = 0; 89428597Speter echo = (strcmp(s, "ON") == 0); 89528597Speter return; 89634766Speter } 89734766Speter 89834766Speter if (abort_next) { 8994374Slars char *s1; 90011990Speter 9014374Slars abort_next = 0; 90211990Speter 9034374Slars if (n_aborts >= MAX_ABORTS) 90434766Speter fatal(2, "Too many ABORT strings"); 90511990Speter 9064374Slars s1 = clean(s, 0); 90711990Speter 90811990Speter if (strlen(s1) > strlen(s) 90911990Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 91034766Speter fatal(1, "Illegal or too-long ABORT string ('%v')", s); 9114374Slars 9124374Slars abort_string[n_aborts++] = s1; 9134374Slars 9144374Slars if (verbose) 915121784Skientzle chat_logf("abort on (%v)", s); 91611990Speter return; 91734766Speter } 91811990Speter 91934766Speter if (clear_abort_next) { 92028597Speter char *s1; 92128597Speter int i; 92228597Speter int old_max; 92328597Speter int pack = 0; 92428597Speter 92528597Speter clear_abort_next = 0; 92628597Speter 92728597Speter s1 = clean(s, 0); 92828597Speter 92928597Speter if (strlen(s1) > strlen(s) 93028597Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 93134766Speter fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 93228597Speter 93328597Speter old_max = n_aborts; 93434766Speter for (i=0; i < n_aborts; i++) { 93534766Speter if ( strcmp(s1,abort_string[i]) == 0 ) { 93634766Speter free(abort_string[i]); 93734766Speter abort_string[i] = NULL; 93834766Speter pack++; 93934766Speter n_aborts--; 94034766Speter if (verbose) 941121784Skientzle chat_logf("clear abort on (%v)", s); 94228597Speter } 94334766Speter } 94428597Speter free(s1); 94534766Speter if (pack) 94634766Speter pack_array(abort_string,old_max); 94728597Speter return; 94834766Speter } 94928597Speter 95034766Speter if (report_next) { 95111990Speter char *s1; 95211990Speter 95311990Speter report_next = 0; 95411990Speter if (n_reports >= MAX_REPORTS) 95534766Speter fatal(2, "Too many REPORT strings"); 95611990Speter 95711990Speter s1 = clean(s, 0); 95811990Speter 95911990Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 96034766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 96111990Speter 96211990Speter report_string[n_reports++] = s1; 96311990Speter 96411990Speter if (verbose) 965121784Skientzle chat_logf("report (%v)", s); 96611990Speter return; 96734766Speter } 9684374Slars 96934766Speter if (clear_report_next) { 97028597Speter char *s1; 97128597Speter int i; 97228597Speter int old_max; 97328597Speter int pack = 0; 97428597Speter 97528597Speter clear_report_next = 0; 97628597Speter 97728597Speter s1 = clean(s, 0); 97828597Speter 97928597Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 98034766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 98128597Speter 98228597Speter old_max = n_reports; 98334766Speter for (i=0; i < n_reports; i++) { 98434766Speter if ( strcmp(s1,report_string[i]) == 0 ) { 98534766Speter free(report_string[i]); 98634766Speter report_string[i] = NULL; 98734766Speter pack++; 98834766Speter n_reports--; 98934766Speter if (verbose) 990121784Skientzle chat_logf("clear report (%v)", s); 99128597Speter } 99234766Speter } 99328597Speter free(s1); 99434766Speter if (pack) 99534766Speter pack_array(report_string,old_max); 99628597Speter 99728597Speter return; 99834766Speter } 99928597Speter 100034766Speter if (timeout_next) { 100111990Speter timeout_next = 0; 100211990Speter timeout = atoi(s); 100311990Speter 100411990Speter if (timeout <= 0) 100511990Speter timeout = DEFAULT_CHAT_TIMEOUT; 10064374Slars 100711990Speter if (verbose) 1008121784Skientzle chat_logf("timeout set to %d seconds", timeout); 100934766Speter 101011990Speter return; 101134766Speter } 101211990Speter 101311990Speter if (strcmp(s, "EOT") == 0) 1014119316Smarkm s = strdup("^D\\c"); 101534766Speter else if (strcmp(s, "BREAK") == 0) 1016119316Smarkm s = strdup("\\K\\c"); 101711990Speter 101811990Speter if (!put_string(s)) 101934766Speter fatal(1, "Failed"); 102034766Speter} 10214374Slars 1022119316Smarkmint 1023119316Smarkmget_char(void) 102434766Speter{ 10254374Slars int status; 10264374Slars char c; 10274374Slars 102880381Ssheldonh status = read(STDIN_FILENO, &c, 1); 10294374Slars 103034766Speter switch (status) { 103111990Speter case 1: 103211990Speter return ((int)c & 0x7F); 10334374Slars 103411990Speter default: 1035121784Skientzle chat_logf("warning: read() on stdin returned %d", status); 10364374Slars 103711990Speter case -1: 103811990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 103934766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 104034766Speter 104134766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 104234766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 104311990Speter 104411990Speter return (-1); 10454374Slars } 104634766Speter} 10474374Slars 1048119316Smarkmint put_char(int c) 104934766Speter{ 10504374Slars int status; 105111990Speter char ch = c; 10524374Slars 105311990Speter usleep(10000); /* inter-character typing delay (?) */ 10544374Slars 105580381Ssheldonh status = write(STDOUT_FILENO, &ch, 1); 10564374Slars 105734766Speter switch (status) { 105811990Speter case 1: 105911990Speter return (0); 106011990Speter 106111990Speter default: 1062121784Skientzle chat_logf("warning: write() on stdout returned %d", status); 106311990Speter 106411990Speter case -1: 106511990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 106634766Speter fatal(2, "Can't get file mode flags on stdin, %m"); 106734766Speter 106834766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 106934766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 107011990Speter 107111990Speter return (-1); 10724374Slars } 107334766Speter} 10744374Slars 1075119316Smarkmint 1076119316Smarkmwrite_char(int c) 107734766Speter{ 107834766Speter if (alarmed || put_char(c) < 0) { 107911990Speter alarm(0); 108011990Speter alarmed = 0; 10814374Slars 108234766Speter if (verbose) { 10834374Slars if (errno == EINTR || errno == EWOULDBLOCK) 1084121784Skientzle chat_logf(" -- write timed out"); 10854374Slars else 1086121784Skientzle chat_logf(" -- write failed: %m"); 108734766Speter } 10884374Slars return (0); 108934766Speter } 10904374Slars return (1); 109134766Speter} 10924374Slars 1093119316Smarkmint 1094119316Smarkmput_string(char *s) 109534766Speter{ 109628597Speter quiet = 0; 10974374Slars s = clean(s, 1); 10984374Slars 1099119316Smarkm if (verbose) 1100121784Skientzle chat_logf("send (%v)", quiet ? "??????" : s); 11014374Slars 11024374Slars alarm(timeout); alarmed = 0; 11034374Slars 110434766Speter while (*s) { 1105119316Smarkm char c = *s++; 11064374Slars 110734766Speter if (c != '\\') { 11084374Slars if (!write_char (c)) 11094374Slars return 0; 11104374Slars continue; 111134766Speter } 11124374Slars 11134374Slars c = *s++; 111434766Speter switch (c) { 11154374Slars case 'd': 11164374Slars sleep(1); 11174374Slars break; 11184374Slars 11194374Slars case 'K': 11204374Slars break_sequence(); 11214374Slars break; 11224374Slars 11234374Slars case 'p': 112411990Speter usleep(10000); /* 1/100th of a second (arg is microseconds) */ 11254374Slars break; 11264374Slars 11274374Slars default: 11284374Slars if (!write_char (c)) 11294374Slars return 0; 11304374Slars break; 11314374Slars } 113234766Speter } 11334374Slars 11344374Slars alarm(0); 11354374Slars alarmed = 0; 11364374Slars return (1); 113734766Speter} 11384374Slars 11394374Slars/* 114028597Speter * Echo a character to stderr. 114128597Speter * When called with -1, a '\n' character is generated when 114228597Speter * the cursor is not at the beginning of a line. 114328597Speter */ 1144119316Smarkmvoid 1145119316Smarkmecho_stderr(int n) 114634766Speter{ 114734766Speter static int need_lf; 114834766Speter char *s; 114928597Speter 115034766Speter switch (n) { 115134766Speter case '\r': /* ignore '\r' */ 115234766Speter break; 115334766Speter case -1: 115434766Speter if (need_lf == 0) 115528597Speter break; 1156102412Scharnier /* FALLTHROUGH */ 115734766Speter case '\n': 115880381Ssheldonh write(STDERR_FILENO, "\n", 1); 115934766Speter need_lf = 0; 116034766Speter break; 116134766Speter default: 116234766Speter s = character(n); 116380381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 116434766Speter need_lf = 1; 116534766Speter break; 116628597Speter } 116734766Speter} 116828597Speter 116928597Speter/* 11704374Slars * 'Wait for' this string to appear on this file descriptor. 11714374Slars */ 1172119316Smarkmint 1173119316Smarkmget_string(char *string) 117434766Speter{ 11754374Slars char temp[STR_LEN]; 1176313100Sasomers int c; 1177119316Smarkm size_t len, minlen; 1178119316Smarkm char *s = temp, *end = s + STR_LEN; 117934766Speter char *logged = temp; 11804374Slars 11814374Slars fail_reason = (char *)0; 118253686Skris 118353686Skris if (strlen(string) > STR_LEN) { 1184121784Skientzle chat_logf("expect string is too long"); 118553686Skris exit_code = 1; 118653686Skris return 0; 118753686Skris } 118853686Skris 11894374Slars string = clean(string, 0); 11904374Slars len = strlen(string); 11914374Slars minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 11924374Slars 11934374Slars if (verbose) 1194121784Skientzle chat_logf("expect (%v)", string); 11954374Slars 119634766Speter if (len == 0) { 11974374Slars if (verbose) 1198121784Skientzle chat_logf("got it"); 11994374Slars return (1); 120034766Speter } 12014374Slars 120211990Speter alarm(timeout); 120311990Speter alarmed = 0; 12044374Slars 120534766Speter while ( ! alarmed && (c = get_char()) >= 0) { 120611990Speter int n, abort_len, report_len; 12074374Slars 120828597Speter if (echo) 120928597Speter echo_stderr(c); 121034766Speter if (verbose && c == '\n') { 121134766Speter if (s == logged) 1212121784Skientzle chat_logf(""); /* blank line */ 12134374Slars else 1214121784Skientzle chat_logf("%0.*v", s - logged, logged); 121534766Speter logged = s + 1; 121634766Speter } 12174374Slars 121834766Speter *s++ = c; 121934766Speter 122034766Speter if (verbose && s >= logged + 80) { 1221121784Skientzle chat_logf("%0.*v", s - logged, logged); 122234766Speter logged = s; 122334766Speter } 122434766Speter 122528597Speter if (Verbose) { 122634766Speter if (c == '\n') 122734766Speter fputc( '\n', stderr ); 122834766Speter else if (c != '\r') 122934766Speter fprintf( stderr, "%s", character(c) ); 123028597Speter } 123128597Speter 123234766Speter if (!report_gathering) { 123334766Speter for (n = 0; n < n_reports; ++n) { 123411990Speter if ((report_string[n] != (char*) NULL) && 123511990Speter s - temp >= (report_len = strlen(report_string[n])) && 123634766Speter strncmp(s - report_len, report_string[n], report_len) == 0) { 123711990Speter time_t time_now = time ((time_t*) NULL); 123811990Speter struct tm* tm_now = localtime (&time_now); 123911990Speter 124011990Speter strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 124111990Speter strcat (report_buffer, report_string[n]); 124211990Speter 124311990Speter report_string[n] = (char *) NULL; 124411990Speter report_gathering = 1; 124511990Speter break; 124634766Speter } 124711990Speter } 124834766Speter } 124934766Speter else { 125034766Speter if (!iscntrl (c)) { 125111990Speter int rep_len = strlen (report_buffer); 125211990Speter report_buffer[rep_len] = c; 125311990Speter report_buffer[rep_len + 1] = '\0'; 125434766Speter } 125534766Speter else { 125611990Speter report_gathering = 0; 125711990Speter fprintf (report_fp, "chat: %s\n", report_buffer); 125811990Speter } 125934766Speter } 126011990Speter 1261119316Smarkm if ((size_t)(s - temp) >= len && 126228597Speter c == string[len - 1] && 126334766Speter strncmp(s - len, string, len) == 0) { 126434766Speter if (verbose) { 126534766Speter if (s > logged) 1266121784Skientzle chat_logf("%0.*v", s - logged, logged); 1267121784Skientzle chat_logf(" -- got it\n"); 126834766Speter } 126928597Speter 127028597Speter alarm(0); 127128597Speter alarmed = 0; 127228597Speter return (1); 127334766Speter } 127428597Speter 127534766Speter for (n = 0; n < n_aborts; ++n) { 127628597Speter if (s - temp >= (abort_len = strlen(abort_string[n])) && 127734766Speter strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 127834766Speter if (verbose) { 127934766Speter if (s > logged) 1280121784Skientzle chat_logf("%0.*v", s - logged, logged); 1281121784Skientzle chat_logf(" -- failed"); 128234766Speter } 128334766Speter 128428597Speter alarm(0); 128528597Speter alarmed = 0; 128628597Speter exit_code = n + 4; 128728597Speter strcpy(fail_reason = fail_buffer, abort_string[n]); 128828597Speter return (0); 128928597Speter } 129034766Speter } 129128597Speter 129234766Speter if (s >= end) { 129334766Speter if (logged < s - minlen) { 1294121784Skientzle chat_logf("%0.*v", s - logged, logged); 129534766Speter logged = s; 129634766Speter } 129734766Speter s -= minlen; 129834766Speter memmove(temp, s, minlen); 129934766Speter logged = temp + (logged - s); 13004374Slars s = temp + minlen; 130134766Speter } 13024374Slars 13034374Slars if (alarmed && verbose) 1304121784Skientzle chat_logf("warning: alarm synchronization problem"); 130534766Speter } 13064374Slars 13074374Slars alarm(0); 130811990Speter 130911990Speter exit_code = 3; 131011990Speter alarmed = 0; 13114374Slars return (0); 131234766Speter} 13134374Slars 131428597Spetervoid 1315119316Smarkmpack_array(char **array, int end) 131628597Speter{ 131728597Speter int i, j; 131828597Speter 131928597Speter for (i = 0; i < end; i++) { 132028597Speter if (array[i] == NULL) { 132128597Speter for (j = i+1; j < end; ++j) 132228597Speter if (array[j] != NULL) 132328597Speter array[i++] = array[j]; 132428597Speter for (; i < end; ++i) 132528597Speter array[i] = NULL; 132628597Speter break; 132728597Speter } 132828597Speter } 132928597Speter} 133034766Speter 133134766Speter/* 133234766Speter * vfmtmsg - format a message into a buffer. Like vsprintf except we 133334766Speter * also specify the length of the output buffer, and we handle the 133434766Speter * %m (error message) format. 133534766Speter * Doesn't do floating-point formats. 133634766Speter * Returns the number of chars put into buf. 133734766Speter */ 133834766Speter#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 133934766Speter 134034766Speterint 1341119316Smarkmvfmtmsg(char *buf, int buflen, const char *fmt, va_list args) 134234766Speter{ 134334766Speter int c, i, n; 134434766Speter int width, prec, fillch; 134534766Speter int base, len, neg, quoted; 134634766Speter unsigned long val = 0; 134734766Speter char *str, *buf0; 134834766Speter const char *f; 134934766Speter unsigned char *p; 135034766Speter char num[32]; 135134766Speter static char hexchars[] = "0123456789abcdef"; 135234766Speter 135334766Speter buf0 = buf; 135434766Speter --buflen; 135534766Speter while (buflen > 0) { 135634766Speter for (f = fmt; *f != '%' && *f != 0; ++f) 135734766Speter ; 135834766Speter if (f > fmt) { 135934766Speter len = f - fmt; 136034766Speter if (len > buflen) 136134766Speter len = buflen; 136234766Speter memcpy(buf, fmt, len); 136334766Speter buf += len; 136434766Speter buflen -= len; 136534766Speter fmt = f; 136634766Speter } 136734766Speter if (*fmt == 0) 136834766Speter break; 136934766Speter c = *++fmt; 137034766Speter width = prec = 0; 137134766Speter fillch = ' '; 137234766Speter if (c == '0') { 137334766Speter fillch = '0'; 137434766Speter c = *++fmt; 137534766Speter } 137634766Speter if (c == '*') { 137734766Speter width = va_arg(args, int); 137834766Speter c = *++fmt; 137934766Speter } else { 138034766Speter while (isdigit(c)) { 138134766Speter width = width * 10 + c - '0'; 138234766Speter c = *++fmt; 138334766Speter } 138434766Speter } 138534766Speter if (c == '.') { 138634766Speter c = *++fmt; 138734766Speter if (c == '*') { 138834766Speter prec = va_arg(args, int); 138934766Speter c = *++fmt; 139034766Speter } else { 139134766Speter while (isdigit(c)) { 139234766Speter prec = prec * 10 + c - '0'; 139334766Speter c = *++fmt; 139434766Speter } 139534766Speter } 139634766Speter } 1397299500Spfg str = NULL; 139834766Speter base = 0; 139934766Speter neg = 0; 140034766Speter ++fmt; 140134766Speter switch (c) { 140234766Speter case 'd': 140334766Speter i = va_arg(args, int); 140434766Speter if (i < 0) { 140534766Speter neg = 1; 140634766Speter val = -i; 140734766Speter } else 140834766Speter val = i; 140934766Speter base = 10; 141034766Speter break; 141134766Speter case 'o': 141234766Speter val = va_arg(args, unsigned int); 141334766Speter base = 8; 141434766Speter break; 141534766Speter case 'x': 141634766Speter val = va_arg(args, unsigned int); 141734766Speter base = 16; 141834766Speter break; 141934766Speter case 'p': 142034766Speter val = (unsigned long) va_arg(args, void *); 142134766Speter base = 16; 142234766Speter neg = 2; 142334766Speter break; 142434766Speter case 's': 142534766Speter str = va_arg(args, char *); 142634766Speter break; 142734766Speter case 'c': 142834766Speter num[0] = va_arg(args, int); 142934766Speter num[1] = 0; 143034766Speter str = num; 143134766Speter break; 143234766Speter case 'm': 143334766Speter str = strerror(errno); 143434766Speter break; 143534766Speter case 'v': /* "visible" string */ 143634766Speter case 'q': /* quoted string */ 143734766Speter quoted = c == 'q'; 143834766Speter p = va_arg(args, unsigned char *); 143934766Speter if (fillch == '0' && prec > 0) { 144034766Speter n = prec; 144134766Speter } else { 144234766Speter n = strlen((char *)p); 144334766Speter if (prec > 0 && prec < n) 144434766Speter n = prec; 144534766Speter } 144634766Speter while (n > 0 && buflen > 0) { 144734766Speter c = *p++; 144834766Speter --n; 144934766Speter if (!quoted && c >= 0x80) { 145034766Speter OUTCHAR('M'); 145134766Speter OUTCHAR('-'); 145234766Speter c -= 0x80; 145334766Speter } 145434766Speter if (quoted && (c == '"' || c == '\\')) 145534766Speter OUTCHAR('\\'); 145634766Speter if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 145734766Speter if (quoted) { 145834766Speter OUTCHAR('\\'); 145934766Speter switch (c) { 146034766Speter case '\t': OUTCHAR('t'); break; 146134766Speter case '\n': OUTCHAR('n'); break; 146234766Speter case '\b': OUTCHAR('b'); break; 146334766Speter case '\f': OUTCHAR('f'); break; 146434766Speter default: 146534766Speter OUTCHAR('x'); 146634766Speter OUTCHAR(hexchars[c >> 4]); 146734766Speter OUTCHAR(hexchars[c & 0xf]); 146834766Speter } 146934766Speter } else { 147034766Speter if (c == '\t') 147134766Speter OUTCHAR(c); 147234766Speter else { 147334766Speter OUTCHAR('^'); 147434766Speter OUTCHAR(c ^ 0x40); 147534766Speter } 147634766Speter } 147734766Speter } else 147834766Speter OUTCHAR(c); 147934766Speter } 148034766Speter continue; 148134766Speter default: 148234766Speter *buf++ = '%'; 148334766Speter if (c != '%') 148434766Speter --fmt; /* so %z outputs %z etc. */ 148534766Speter --buflen; 148634766Speter continue; 148734766Speter } 148834766Speter if (base != 0) { 148934766Speter str = num + sizeof(num); 149034766Speter *--str = 0; 149134766Speter while (str > num + neg) { 149234766Speter *--str = hexchars[val % base]; 149334766Speter val = val / base; 149434766Speter if (--prec <= 0 && val == 0) 149534766Speter break; 149634766Speter } 149734766Speter switch (neg) { 149834766Speter case 1: 149934766Speter *--str = '-'; 150034766Speter break; 150134766Speter case 2: 150234766Speter *--str = 'x'; 150334766Speter *--str = '0'; 150434766Speter break; 150534766Speter } 150634766Speter len = num + sizeof(num) - 1 - str; 150734766Speter } else { 150834766Speter len = strlen(str); 150934766Speter if (prec > 0 && len > prec) 151034766Speter len = prec; 151134766Speter } 151234766Speter if (width > 0) { 151334766Speter if (width > buflen) 151434766Speter width = buflen; 151534766Speter if ((n = width - len) > 0) { 151634766Speter buflen -= n; 151734766Speter for (; n > 0; --n) 151834766Speter *buf++ = fillch; 151934766Speter } 152034766Speter } 152134766Speter if (len > buflen) 152234766Speter len = buflen; 152334766Speter memcpy(buf, str, len); 152434766Speter buf += len; 152534766Speter buflen -= len; 152634766Speter } 152734766Speter *buf = 0; 152834766Speter return buf - buf0; 152934766Speter} 1530