chat.c revision 236213
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: head/usr.bin/chat/chat.c 236213 2012-05-29 01:48:06Z kevlo $"); 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); 411236213Skevlo va_end(args); 41234766Speter if (to_log) 41328597Speter syslog(LOG_INFO, "%s", line); 41434766Speter if (to_stderr) 41534766Speter fprintf(stderr, "%s\n", line); 41628597Speter} 4174374Slars 4184374Slars/* 4194374Slars * Print an error message and terminate. 4204374Slars */ 4214374Slars 422119316Smarkmvoid 423119316Smarkmfatal(int code, const char *fmt, ...) 42434766Speter{ 42534766Speter va_list args; 4264374Slars 42734766Speter va_start(args, fmt); 42834766Speter vfmtmsg(line, sizeof(line), fmt, args); 429236213Skevlo va_end(args); 43034766Speter if (to_log) 43134766Speter syslog(LOG_ERR, "%s", line); 43234766Speter if (to_stderr) 43334766Speter fprintf(stderr, "%s\n", line); 43434766Speter terminate(code); 43534766Speter} 4364374Slars 4374374Slarsint alarmed = 0; 4384374Slars 439119316SmarkmSIGTYPE sigalrm(int signo __unused) 44034766Speter{ 4414374Slars int flags; 4424374Slars 4434374Slars alarm(1); 4444374Slars alarmed = 1; /* Reset alarm to avoid race window */ 4454374Slars signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 4464374Slars 4474374Slars if ((flags = fcntl(0, F_GETFL, 0)) == -1) 44834766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 4494374Slars 45034766Speter if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 45134766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 45234766Speter 4534374Slars if (verbose) 454121784Skientzle chat_logf("alarm"); 45534766Speter} 4564374Slars 457119316SmarkmSIGTYPE sigint(int signo __unused) 45834766Speter{ 45934766Speter fatal(2, "SIGINT"); 46034766Speter} 4614374Slars 462119316SmarkmSIGTYPE sigterm(int signo __unused) 46334766Speter{ 46434766Speter fatal(2, "SIGTERM"); 46534766Speter} 4664374Slars 467119316SmarkmSIGTYPE sighup(int signo __unused) 46834766Speter{ 46934766Speter fatal(2, "SIGHUP"); 47034766Speter} 4714374Slars 472119316Smarkmvoid init(void) 47334766Speter{ 4744374Slars signal(SIGINT, sigint); 4754374Slars signal(SIGTERM, sigterm); 4764374Slars signal(SIGHUP, sighup); 4774374Slars 4784374Slars set_tty_parameters(); 4794374Slars signal(SIGALRM, sigalrm); 4804374Slars alarm(0); 4814374Slars alarmed = 0; 48234766Speter} 4834374Slars 484119316Smarkmvoid set_tty_parameters(void) 48534766Speter{ 48611990Speter#if defined(get_term_param) 48711990Speter term_parms t; 4884374Slars 48911990Speter if (get_term_param (&t) < 0) 49034766Speter fatal(2, "Can't get terminal parameters: %m"); 4914374Slars 4924374Slars saved_tty_parameters = t; 49311990Speter have_tty_parameters = 1; 4944374Slars 49511990Speter t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 49611990Speter t.c_oflag = 0; 49711990Speter t.c_lflag = 0; 49811990Speter t.c_cc[VERASE] = 49911990Speter t.c_cc[VKILL] = 0; 50011990Speter t.c_cc[VMIN] = 1; 50111990Speter t.c_cc[VTIME] = 0; 5024374Slars 50311990Speter if (set_term_param (&t) < 0) 50434766Speter fatal(2, "Can't set terminal parameters: %m"); 5054374Slars#endif 50634766Speter} 5074374Slars 508119316Smarkmvoid break_sequence(void) 50934766Speter{ 5104374Slars tcsendbreak (0, 0); 51134766Speter} 5124374Slars 513119316Smarkmvoid terminate(int status) 51434766Speter{ 51528597Speter echo_stderr(-1); 51634766Speter if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 51728597Speter/* 51828597Speter * Allow the last of the report string to be gathered before we terminate. 51928597Speter */ 52028597Speter if (report_gathering) { 521119316Smarkm int c; 522119316Smarkm size_t rep_len; 52328597Speter 52428597Speter rep_len = strlen(report_buffer); 52528597Speter while (rep_len + 1 <= sizeof(report_buffer)) { 52628597Speter alarm(1); 52728597Speter c = get_char(); 52828597Speter alarm(0); 52928597Speter if (c < 0 || iscntrl(c)) 53028597Speter break; 53128597Speter report_buffer[rep_len] = c; 53228597Speter ++rep_len; 53328597Speter } 53428597Speter report_buffer[rep_len] = 0; 53528597Speter fprintf (report_fp, "chat: %s\n", report_buffer); 53628597Speter } 53711990Speter if (verbose) 53811990Speter fprintf (report_fp, "Closing \"%s\".\n", report_file); 53911990Speter fclose (report_fp); 54028597Speter report_fp = (FILE *) NULL; 54134766Speter } 5424374Slars 54311990Speter#if defined(get_term_param) 54434766Speter if (have_tty_parameters) { 54511990Speter if (set_term_param (&saved_tty_parameters) < 0) 54634766Speter fatal(2, "Can't restore terminal parameters: %m"); 54734766Speter } 54811990Speter#endif 5494374Slars 55011990Speter exit(status); 55134766Speter} 5524374Slars 5534374Slars/* 5544374Slars * 'Clean up' this string. 5554374Slars */ 556119316Smarkmchar * 557119316Smarkmclean(char *s, int sending) 55834766Speter{ 5594374Slars char temp[STR_LEN], cur_chr; 560119316Smarkm char *s1, *phchar; 5614374Slars int add_return = sending; 5624374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 5634374Slars 5644374Slars s1 = temp; 56553686Skris /* Don't overflow buffer, leave room for chars we append later */ 566119316Smarkm while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) { 5674374Slars cur_chr = *s++; 56834766Speter if (cur_chr == '^') { 5694374Slars cur_chr = *s++; 57034766Speter if (cur_chr == '\0') { 5714374Slars *s1++ = '^'; 5724374Slars break; 57334766Speter } 5744374Slars cur_chr &= 0x1F; 57534766Speter if (cur_chr != 0) { 5764374Slars *s1++ = cur_chr; 57734766Speter } 5784374Slars continue; 57934766Speter } 5804374Slars 58134766Speter if (cur_chr != '\\') { 5824374Slars *s1++ = cur_chr; 5834374Slars continue; 58434766Speter } 5854374Slars 5864374Slars cur_chr = *s++; 58734766Speter if (cur_chr == '\0') { 58834766Speter if (sending) { 5894374Slars *s1++ = '\\'; 5904374Slars *s1++ = '\\'; 59134766Speter } 5924374Slars break; 59334766Speter } 5944374Slars 59534766Speter switch (cur_chr) { 5964374Slars case 'b': 5974374Slars *s1++ = '\b'; 5984374Slars break; 5994374Slars 6004374Slars case 'c': 6014374Slars if (sending && *s == '\0') 6024374Slars add_return = 0; 6034374Slars else 6044374Slars *s1++ = cur_chr; 6054374Slars break; 6064374Slars 6074374Slars case '\\': 6084374Slars case 'K': 6094374Slars case 'p': 6104374Slars case 'd': 6114374Slars if (sending) 6124374Slars *s1++ = '\\'; 6134374Slars 6144374Slars *s1++ = cur_chr; 6154374Slars break; 6164374Slars 61734766Speter case 'T': 61834766Speter if (sending && phone_num) { 61934766Speter for ( phchar = phone_num; *phchar != '\0'; phchar++) 62034766Speter *s1++ = *phchar; 62134766Speter } 62234766Speter else { 62334766Speter *s1++ = '\\'; 62434766Speter *s1++ = 'T'; 62534766Speter } 62634766Speter break; 62734766Speter 62834766Speter case 'U': 62934766Speter if (sending && phone_num2) { 63034766Speter for ( phchar = phone_num2; *phchar != '\0'; phchar++) 63134766Speter *s1++ = *phchar; 63234766Speter } 63334766Speter else { 63434766Speter *s1++ = '\\'; 63534766Speter *s1++ = 'U'; 63634766Speter } 63734766Speter break; 63834766Speter 6394374Slars case 'q': 64028597Speter quiet = 1; 6414374Slars break; 6424374Slars 6434374Slars case 'r': 6444374Slars *s1++ = '\r'; 6454374Slars break; 6464374Slars 6474374Slars case 'n': 6484374Slars *s1++ = '\n'; 6494374Slars break; 6504374Slars 6514374Slars case 's': 6524374Slars *s1++ = ' '; 6534374Slars break; 6544374Slars 6554374Slars case 't': 6564374Slars *s1++ = '\t'; 6574374Slars break; 6584374Slars 6594374Slars case 'N': 66034766Speter if (sending) { 6614374Slars *s1++ = '\\'; 6624374Slars *s1++ = '\0'; 66334766Speter } 6644374Slars else 6654374Slars *s1++ = 'N'; 6664374Slars break; 66711990Speter 6684374Slars default: 66934766Speter if (isoctal (cur_chr)) { 6704374Slars cur_chr &= 0x07; 67134766Speter if (isoctal (*s)) { 6724374Slars cur_chr <<= 3; 6734374Slars cur_chr |= *s++ - '0'; 67434766Speter if (isoctal (*s)) { 6754374Slars cur_chr <<= 3; 6764374Slars cur_chr |= *s++ - '0'; 6774374Slars } 67834766Speter } 6794374Slars 68034766Speter if (cur_chr != 0 || sending) { 6814374Slars if (sending && (cur_chr == '\\' || cur_chr == 0)) 6824374Slars *s1++ = '\\'; 6834374Slars *s1++ = cur_chr; 68434766Speter } 6854374Slars break; 68634766Speter } 6874374Slars 6884374Slars if (sending) 6894374Slars *s1++ = '\\'; 6904374Slars *s1++ = cur_chr; 6914374Slars break; 6924374Slars } 69334766Speter } 6944374Slars 6954374Slars if (add_return) 6964374Slars *s1++ = '\r'; 6974374Slars 6984374Slars *s1++ = '\0'; /* guarantee closure */ 6994374Slars *s1++ = '\0'; /* terminate the string */ 7004374Slars return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 70134766Speter} 7024374Slars 7034374Slars/* 70428597Speter * A modified version of 'strtok'. This version skips \ sequences. 70528597Speter */ 70628597Speter 707119316Smarkmchar * 708119316Smarkmexpect_strtok (char *s, const char *term) 70934766Speter{ 710119316Smarkm static char *str = blank; 71128597Speter int escape_flag = 0; 71228597Speter char *result; 71334766Speter 71428597Speter/* 71528597Speter * If a string was specified then do initial processing. 71628597Speter */ 71728597Speter if (s) 71828597Speter str = s; 71934766Speter 72028597Speter/* 72128597Speter * If this is the escape flag then reset it and ignore the character. 72228597Speter */ 72328597Speter if (*str) 72428597Speter result = str; 72528597Speter else 72628597Speter result = (char *) 0; 72728597Speter 72834766Speter while (*str) { 72934766Speter if (escape_flag) { 73028597Speter escape_flag = 0; 73128597Speter ++str; 73228597Speter continue; 73334766Speter } 73428597Speter 73534766Speter if (*str == '\\') { 73628597Speter ++str; 73728597Speter escape_flag = 1; 73828597Speter continue; 73934766Speter } 74034766Speter 74128597Speter/* 74228597Speter * If this is not in the termination string, continue. 74328597Speter */ 74434766Speter if (strchr (term, *str) == (char *) 0) { 74528597Speter ++str; 74628597Speter continue; 74734766Speter } 74834766Speter 74928597Speter/* 75028597Speter * This is the terminator. Mark the end of the string and stop. 75128597Speter */ 75228597Speter *str++ = '\0'; 75328597Speter break; 75434766Speter } 75528597Speter return (result); 75634766Speter} 75728597Speter 75828597Speter/* 7594374Slars * Process the expect string 7604374Slars */ 76128597Speter 762119316Smarkmvoid 763119316Smarkmchat_expect(char *s) 76434766Speter{ 76528597Speter char *expect; 76628597Speter char *reply; 76728597Speter 76834766Speter if (strcmp(s, "HANGUP") == 0) { 76928597Speter ++hup_next; 77028597Speter return; 77134766Speter } 77228597Speter 77334766Speter if (strcmp(s, "ABORT") == 0) { 7744374Slars ++abort_next; 7754374Slars return; 77634766Speter } 7774374Slars 77834766Speter if (strcmp(s, "CLR_ABORT") == 0) { 77928597Speter ++clear_abort_next; 78028597Speter return; 78134766Speter } 78228597Speter 78334766Speter if (strcmp(s, "REPORT") == 0) { 78411990Speter ++report_next; 78511990Speter return; 78634766Speter } 78711990Speter 78834766Speter if (strcmp(s, "CLR_REPORT") == 0) { 78928597Speter ++clear_report_next; 79028597Speter return; 79134766Speter } 79228597Speter 79334766Speter if (strcmp(s, "TIMEOUT") == 0) { 7944374Slars ++timeout_next; 7954374Slars return; 79634766Speter } 7974374Slars 79834766Speter if (strcmp(s, "ECHO") == 0) { 79928597Speter ++echo_next; 80028597Speter return; 80134766Speter } 80234766Speter 80334766Speter if (strcmp(s, "SAY") == 0) { 80428597Speter ++say_next; 80528597Speter return; 80634766Speter } 80734766Speter 80828597Speter/* 80928597Speter * Fetch the expect and reply string. 81028597Speter */ 81134766Speter for (;;) { 81228597Speter expect = expect_strtok (s, "-"); 81328597Speter s = (char *) 0; 8144374Slars 81528597Speter if (expect == (char *) 0) 81628597Speter return; 81728597Speter 81828597Speter reply = expect_strtok (s, "-"); 81934766Speter 82028597Speter/* 82128597Speter * Handle the expect string. If successful then exit. 82228597Speter */ 82328597Speter if (get_string (expect)) 82428597Speter return; 82534766Speter 82628597Speter/* 82728597Speter * If there is a sub-reply string then send it. Otherwise any condition 82828597Speter * is terminal. 82928597Speter */ 83028597Speter if (reply == (char *) 0 || exit_code != 3) 83128597Speter break; 8324374Slars 83328597Speter chat_send (reply); 83434766Speter } 83534766Speter 83628597Speter/* 83728597Speter * The expectation did not occur. This is terminal. 83828597Speter */ 83928597Speter if (fail_reason) 840121784Skientzle chat_logf("Failed (%s)", fail_reason); 84128597Speter else 842121784Skientzle chat_logf("Failed"); 84328597Speter terminate(exit_code); 84434766Speter} 8454374Slars 84628597Speter/* 84728597Speter * Translate the input character to the appropriate string for printing 84828597Speter * the data. 84928597Speter */ 85028597Speter 851119316Smarkmchar * 852119316Smarkmcharacter(int c) 85334766Speter{ 8544374Slars static char string[10]; 855119316Smarkm const char *meta; 8564374Slars 8574374Slars meta = (c & 0x80) ? "M-" : ""; 8584374Slars c &= 0x7F; 8594374Slars 8604374Slars if (c < 32) 8614374Slars sprintf(string, "%s^%c", meta, (int)c + '@'); 86234766Speter else if (c == 127) 86334766Speter sprintf(string, "%s^?", meta); 8644374Slars else 86534766Speter sprintf(string, "%s%c", meta, c); 8664374Slars 8674374Slars return (string); 86834766Speter} 8694374Slars 8704374Slars/* 8714374Slars * process the reply string 8724374Slars */ 873119316Smarkmvoid 874119316Smarkmchat_send(char *s) 87534766Speter{ 87634766Speter if (say_next) { 87728597Speter say_next = 0; 87828597Speter s = clean(s,0); 87980381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 88028597Speter free(s); 88128597Speter return; 88234766Speter } 88334766Speter 88434766Speter if (hup_next) { 88528597Speter hup_next = 0; 88628597Speter if (strcmp(s, "OFF") == 0) 88728597Speter signal(SIGHUP, SIG_IGN); 88828597Speter else 88928597Speter signal(SIGHUP, sighup); 89028597Speter return; 89134766Speter } 89234766Speter 89334766Speter if (echo_next) { 89428597Speter echo_next = 0; 89528597Speter echo = (strcmp(s, "ON") == 0); 89628597Speter return; 89734766Speter } 89834766Speter 89934766Speter if (abort_next) { 9004374Slars char *s1; 90111990Speter 9024374Slars abort_next = 0; 90311990Speter 9044374Slars if (n_aborts >= MAX_ABORTS) 90534766Speter fatal(2, "Too many ABORT strings"); 90611990Speter 9074374Slars s1 = clean(s, 0); 90811990Speter 90911990Speter if (strlen(s1) > strlen(s) 91011990Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 91134766Speter fatal(1, "Illegal or too-long ABORT string ('%v')", s); 9124374Slars 9134374Slars abort_string[n_aborts++] = s1; 9144374Slars 9154374Slars if (verbose) 916121784Skientzle chat_logf("abort on (%v)", s); 91711990Speter return; 91834766Speter } 91911990Speter 92034766Speter if (clear_abort_next) { 92128597Speter char *s1; 92228597Speter int i; 92328597Speter int old_max; 92428597Speter int pack = 0; 92528597Speter 92628597Speter clear_abort_next = 0; 92728597Speter 92828597Speter s1 = clean(s, 0); 92928597Speter 93028597Speter if (strlen(s1) > strlen(s) 93128597Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 93234766Speter fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 93328597Speter 93428597Speter old_max = n_aborts; 93534766Speter for (i=0; i < n_aborts; i++) { 93634766Speter if ( strcmp(s1,abort_string[i]) == 0 ) { 93734766Speter free(abort_string[i]); 93834766Speter abort_string[i] = NULL; 93934766Speter pack++; 94034766Speter n_aborts--; 94134766Speter if (verbose) 942121784Skientzle chat_logf("clear abort on (%v)", s); 94328597Speter } 94434766Speter } 94528597Speter free(s1); 94634766Speter if (pack) 94734766Speter pack_array(abort_string,old_max); 94828597Speter return; 94934766Speter } 95028597Speter 95134766Speter if (report_next) { 95211990Speter char *s1; 95311990Speter 95411990Speter report_next = 0; 95511990Speter if (n_reports >= MAX_REPORTS) 95634766Speter fatal(2, "Too many REPORT strings"); 95711990Speter 95811990Speter s1 = clean(s, 0); 95911990Speter 96011990Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 96134766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 96211990Speter 96311990Speter report_string[n_reports++] = s1; 96411990Speter 96511990Speter if (verbose) 966121784Skientzle chat_logf("report (%v)", s); 96711990Speter return; 96834766Speter } 9694374Slars 97034766Speter if (clear_report_next) { 97128597Speter char *s1; 97228597Speter int i; 97328597Speter int old_max; 97428597Speter int pack = 0; 97528597Speter 97628597Speter clear_report_next = 0; 97728597Speter 97828597Speter s1 = clean(s, 0); 97928597Speter 98028597Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 98134766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 98228597Speter 98328597Speter old_max = n_reports; 98434766Speter for (i=0; i < n_reports; i++) { 98534766Speter if ( strcmp(s1,report_string[i]) == 0 ) { 98634766Speter free(report_string[i]); 98734766Speter report_string[i] = NULL; 98834766Speter pack++; 98934766Speter n_reports--; 99034766Speter if (verbose) 991121784Skientzle chat_logf("clear report (%v)", s); 99228597Speter } 99334766Speter } 99428597Speter free(s1); 99534766Speter if (pack) 99634766Speter pack_array(report_string,old_max); 99728597Speter 99828597Speter return; 99934766Speter } 100028597Speter 100134766Speter if (timeout_next) { 100211990Speter timeout_next = 0; 100311990Speter timeout = atoi(s); 100411990Speter 100511990Speter if (timeout <= 0) 100611990Speter timeout = DEFAULT_CHAT_TIMEOUT; 10074374Slars 100811990Speter if (verbose) 1009121784Skientzle chat_logf("timeout set to %d seconds", timeout); 101034766Speter 101111990Speter return; 101234766Speter } 101311990Speter 101411990Speter if (strcmp(s, "EOT") == 0) 1015119316Smarkm s = strdup("^D\\c"); 101634766Speter else if (strcmp(s, "BREAK") == 0) 1017119316Smarkm s = strdup("\\K\\c"); 101811990Speter 101911990Speter if (!put_string(s)) 102034766Speter fatal(1, "Failed"); 102134766Speter} 10224374Slars 1023119316Smarkmint 1024119316Smarkmget_char(void) 102534766Speter{ 10264374Slars int status; 10274374Slars char c; 10284374Slars 102980381Ssheldonh status = read(STDIN_FILENO, &c, 1); 10304374Slars 103134766Speter switch (status) { 103211990Speter case 1: 103311990Speter return ((int)c & 0x7F); 10344374Slars 103511990Speter default: 1036121784Skientzle chat_logf("warning: read() on stdin returned %d", status); 10374374Slars 103811990Speter case -1: 103911990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 104034766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 104134766Speter 104234766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 104334766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 104411990Speter 104511990Speter return (-1); 10464374Slars } 104734766Speter} 10484374Slars 1049119316Smarkmint put_char(int c) 105034766Speter{ 10514374Slars int status; 105211990Speter char ch = c; 10534374Slars 105411990Speter usleep(10000); /* inter-character typing delay (?) */ 10554374Slars 105680381Ssheldonh status = write(STDOUT_FILENO, &ch, 1); 10574374Slars 105834766Speter switch (status) { 105911990Speter case 1: 106011990Speter return (0); 106111990Speter 106211990Speter default: 1063121784Skientzle chat_logf("warning: write() on stdout returned %d", status); 106411990Speter 106511990Speter case -1: 106611990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 106734766Speter fatal(2, "Can't get file mode flags on stdin, %m"); 106834766Speter 106934766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 107034766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 107111990Speter 107211990Speter return (-1); 10734374Slars } 107434766Speter} 10754374Slars 1076119316Smarkmint 1077119316Smarkmwrite_char(int c) 107834766Speter{ 107934766Speter if (alarmed || put_char(c) < 0) { 108011990Speter alarm(0); 108111990Speter alarmed = 0; 10824374Slars 108334766Speter if (verbose) { 10844374Slars if (errno == EINTR || errno == EWOULDBLOCK) 1085121784Skientzle chat_logf(" -- write timed out"); 10864374Slars else 1087121784Skientzle chat_logf(" -- write failed: %m"); 108834766Speter } 10894374Slars return (0); 109034766Speter } 10914374Slars return (1); 109234766Speter} 10934374Slars 1094119316Smarkmint 1095119316Smarkmput_string(char *s) 109634766Speter{ 109728597Speter quiet = 0; 10984374Slars s = clean(s, 1); 10994374Slars 1100119316Smarkm if (verbose) 1101121784Skientzle chat_logf("send (%v)", quiet ? "??????" : s); 11024374Slars 11034374Slars alarm(timeout); alarmed = 0; 11044374Slars 110534766Speter while (*s) { 1106119316Smarkm char c = *s++; 11074374Slars 110834766Speter if (c != '\\') { 11094374Slars if (!write_char (c)) 11104374Slars return 0; 11114374Slars continue; 111234766Speter } 11134374Slars 11144374Slars c = *s++; 111534766Speter switch (c) { 11164374Slars case 'd': 11174374Slars sleep(1); 11184374Slars break; 11194374Slars 11204374Slars case 'K': 11214374Slars break_sequence(); 11224374Slars break; 11234374Slars 11244374Slars case 'p': 112511990Speter usleep(10000); /* 1/100th of a second (arg is microseconds) */ 11264374Slars break; 11274374Slars 11284374Slars default: 11294374Slars if (!write_char (c)) 11304374Slars return 0; 11314374Slars break; 11324374Slars } 113334766Speter } 11344374Slars 11354374Slars alarm(0); 11364374Slars alarmed = 0; 11374374Slars return (1); 113834766Speter} 11394374Slars 11404374Slars/* 114128597Speter * Echo a character to stderr. 114228597Speter * When called with -1, a '\n' character is generated when 114328597Speter * the cursor is not at the beginning of a line. 114428597Speter */ 1145119316Smarkmvoid 1146119316Smarkmecho_stderr(int n) 114734766Speter{ 114834766Speter static int need_lf; 114934766Speter char *s; 115028597Speter 115134766Speter switch (n) { 115234766Speter case '\r': /* ignore '\r' */ 115334766Speter break; 115434766Speter case -1: 115534766Speter if (need_lf == 0) 115628597Speter break; 1157102412Scharnier /* FALLTHROUGH */ 115834766Speter case '\n': 115980381Ssheldonh write(STDERR_FILENO, "\n", 1); 116034766Speter need_lf = 0; 116134766Speter break; 116234766Speter default: 116334766Speter s = character(n); 116480381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 116534766Speter need_lf = 1; 116634766Speter break; 116728597Speter } 116834766Speter} 116928597Speter 117028597Speter/* 11714374Slars * 'Wait for' this string to appear on this file descriptor. 11724374Slars */ 1173119316Smarkmint 1174119316Smarkmget_string(char *string) 117534766Speter{ 11764374Slars char temp[STR_LEN]; 1177119316Smarkm int c, printed = 0; 1178119316Smarkm size_t len, minlen; 1179119316Smarkm char *s = temp, *end = s + STR_LEN; 118034766Speter char *logged = temp; 11814374Slars 11824374Slars fail_reason = (char *)0; 118353686Skris 118453686Skris if (strlen(string) > STR_LEN) { 1185121784Skientzle chat_logf("expect string is too long"); 118653686Skris exit_code = 1; 118753686Skris return 0; 118853686Skris } 118953686Skris 11904374Slars string = clean(string, 0); 11914374Slars len = strlen(string); 11924374Slars minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 11934374Slars 11944374Slars if (verbose) 1195121784Skientzle chat_logf("expect (%v)", string); 11964374Slars 119734766Speter if (len == 0) { 11984374Slars if (verbose) 1199121784Skientzle chat_logf("got it"); 12004374Slars return (1); 120134766Speter } 12024374Slars 120311990Speter alarm(timeout); 120411990Speter alarmed = 0; 12054374Slars 120634766Speter while ( ! alarmed && (c = get_char()) >= 0) { 120711990Speter int n, abort_len, report_len; 12084374Slars 120928597Speter if (echo) 121028597Speter echo_stderr(c); 121134766Speter if (verbose && c == '\n') { 121234766Speter if (s == logged) 1213121784Skientzle chat_logf(""); /* blank line */ 12144374Slars else 1215121784Skientzle chat_logf("%0.*v", s - logged, logged); 121634766Speter logged = s + 1; 121734766Speter } 12184374Slars 121934766Speter *s++ = c; 122034766Speter 122134766Speter if (verbose && s >= logged + 80) { 1222121784Skientzle chat_logf("%0.*v", s - logged, logged); 122334766Speter logged = s; 122434766Speter } 122534766Speter 122628597Speter if (Verbose) { 122734766Speter if (c == '\n') 122834766Speter fputc( '\n', stderr ); 122934766Speter else if (c != '\r') 123034766Speter fprintf( stderr, "%s", character(c) ); 123128597Speter } 123228597Speter 123334766Speter if (!report_gathering) { 123434766Speter for (n = 0; n < n_reports; ++n) { 123511990Speter if ((report_string[n] != (char*) NULL) && 123611990Speter s - temp >= (report_len = strlen(report_string[n])) && 123734766Speter strncmp(s - report_len, report_string[n], report_len) == 0) { 123811990Speter time_t time_now = time ((time_t*) NULL); 123911990Speter struct tm* tm_now = localtime (&time_now); 124011990Speter 124111990Speter strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 124211990Speter strcat (report_buffer, report_string[n]); 124311990Speter 124411990Speter report_string[n] = (char *) NULL; 124511990Speter report_gathering = 1; 124611990Speter break; 124734766Speter } 124811990Speter } 124934766Speter } 125034766Speter else { 125134766Speter if (!iscntrl (c)) { 125211990Speter int rep_len = strlen (report_buffer); 125311990Speter report_buffer[rep_len] = c; 125411990Speter report_buffer[rep_len + 1] = '\0'; 125534766Speter } 125634766Speter else { 125711990Speter report_gathering = 0; 125811990Speter fprintf (report_fp, "chat: %s\n", report_buffer); 125911990Speter } 126034766Speter } 126111990Speter 1262119316Smarkm if ((size_t)(s - temp) >= len && 126328597Speter c == string[len - 1] && 126434766Speter strncmp(s - len, string, len) == 0) { 126534766Speter if (verbose) { 126634766Speter if (s > logged) 1267121784Skientzle chat_logf("%0.*v", s - logged, logged); 1268121784Skientzle chat_logf(" -- got it\n"); 126934766Speter } 127028597Speter 127128597Speter alarm(0); 127228597Speter alarmed = 0; 127328597Speter return (1); 127434766Speter } 127528597Speter 127634766Speter for (n = 0; n < n_aborts; ++n) { 127728597Speter if (s - temp >= (abort_len = strlen(abort_string[n])) && 127834766Speter strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 127934766Speter if (verbose) { 128034766Speter if (s > logged) 1281121784Skientzle chat_logf("%0.*v", s - logged, logged); 1282121784Skientzle chat_logf(" -- failed"); 128334766Speter } 128434766Speter 128528597Speter alarm(0); 128628597Speter alarmed = 0; 128728597Speter exit_code = n + 4; 128828597Speter strcpy(fail_reason = fail_buffer, abort_string[n]); 128928597Speter return (0); 129028597Speter } 129134766Speter } 129228597Speter 129334766Speter if (s >= end) { 129434766Speter if (logged < s - minlen) { 1295121784Skientzle chat_logf("%0.*v", s - logged, logged); 129634766Speter logged = s; 129734766Speter } 129834766Speter s -= minlen; 129934766Speter memmove(temp, s, minlen); 130034766Speter logged = temp + (logged - s); 13014374Slars s = temp + minlen; 130234766Speter } 13034374Slars 13044374Slars if (alarmed && verbose) 1305121784Skientzle chat_logf("warning: alarm synchronization problem"); 130634766Speter } 13074374Slars 13084374Slars alarm(0); 130911990Speter 131034766Speter if (verbose && printed) { 13114374Slars if (alarmed) 1312121784Skientzle chat_logf(" -- read timed out"); 13134374Slars else 1314121784Skientzle chat_logf(" -- read failed: %m"); 131534766Speter } 13164374Slars 131711990Speter exit_code = 3; 131811990Speter alarmed = 0; 13194374Slars return (0); 132034766Speter} 13214374Slars 132228597Spetervoid 1323119316Smarkmpack_array(char **array, int end) 132428597Speter{ 132528597Speter int i, j; 132628597Speter 132728597Speter for (i = 0; i < end; i++) { 132828597Speter if (array[i] == NULL) { 132928597Speter for (j = i+1; j < end; ++j) 133028597Speter if (array[j] != NULL) 133128597Speter array[i++] = array[j]; 133228597Speter for (; i < end; ++i) 133328597Speter array[i] = NULL; 133428597Speter break; 133528597Speter } 133628597Speter } 133728597Speter} 133834766Speter 133934766Speter/* 134034766Speter * vfmtmsg - format a message into a buffer. Like vsprintf except we 134134766Speter * also specify the length of the output buffer, and we handle the 134234766Speter * %m (error message) format. 134334766Speter * Doesn't do floating-point formats. 134434766Speter * Returns the number of chars put into buf. 134534766Speter */ 134634766Speter#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 134734766Speter 134834766Speterint 1349119316Smarkmvfmtmsg(char *buf, int buflen, const char *fmt, va_list args) 135034766Speter{ 135134766Speter int c, i, n; 135234766Speter int width, prec, fillch; 135334766Speter int base, len, neg, quoted; 135434766Speter unsigned long val = 0; 135534766Speter char *str, *buf0; 135634766Speter const char *f; 135734766Speter unsigned char *p; 135834766Speter char num[32]; 135934766Speter static char hexchars[] = "0123456789abcdef"; 136034766Speter 136134766Speter buf0 = buf; 136234766Speter --buflen; 136334766Speter while (buflen > 0) { 136434766Speter for (f = fmt; *f != '%' && *f != 0; ++f) 136534766Speter ; 136634766Speter if (f > fmt) { 136734766Speter len = f - fmt; 136834766Speter if (len > buflen) 136934766Speter len = buflen; 137034766Speter memcpy(buf, fmt, len); 137134766Speter buf += len; 137234766Speter buflen -= len; 137334766Speter fmt = f; 137434766Speter } 137534766Speter if (*fmt == 0) 137634766Speter break; 137734766Speter c = *++fmt; 137834766Speter width = prec = 0; 137934766Speter fillch = ' '; 138034766Speter if (c == '0') { 138134766Speter fillch = '0'; 138234766Speter c = *++fmt; 138334766Speter } 138434766Speter if (c == '*') { 138534766Speter width = va_arg(args, int); 138634766Speter c = *++fmt; 138734766Speter } else { 138834766Speter while (isdigit(c)) { 138934766Speter width = width * 10 + c - '0'; 139034766Speter c = *++fmt; 139134766Speter } 139234766Speter } 139334766Speter if (c == '.') { 139434766Speter c = *++fmt; 139534766Speter if (c == '*') { 139634766Speter prec = va_arg(args, int); 139734766Speter c = *++fmt; 139834766Speter } else { 139934766Speter while (isdigit(c)) { 140034766Speter prec = prec * 10 + c - '0'; 140134766Speter c = *++fmt; 140234766Speter } 140334766Speter } 140434766Speter } 140534766Speter str = 0; 140634766Speter base = 0; 140734766Speter neg = 0; 140834766Speter ++fmt; 140934766Speter switch (c) { 141034766Speter case 'd': 141134766Speter i = va_arg(args, int); 141234766Speter if (i < 0) { 141334766Speter neg = 1; 141434766Speter val = -i; 141534766Speter } else 141634766Speter val = i; 141734766Speter base = 10; 141834766Speter break; 141934766Speter case 'o': 142034766Speter val = va_arg(args, unsigned int); 142134766Speter base = 8; 142234766Speter break; 142334766Speter case 'x': 142434766Speter val = va_arg(args, unsigned int); 142534766Speter base = 16; 142634766Speter break; 142734766Speter case 'p': 142834766Speter val = (unsigned long) va_arg(args, void *); 142934766Speter base = 16; 143034766Speter neg = 2; 143134766Speter break; 143234766Speter case 's': 143334766Speter str = va_arg(args, char *); 143434766Speter break; 143534766Speter case 'c': 143634766Speter num[0] = va_arg(args, int); 143734766Speter num[1] = 0; 143834766Speter str = num; 143934766Speter break; 144034766Speter case 'm': 144134766Speter str = strerror(errno); 144234766Speter break; 144334766Speter case 'v': /* "visible" string */ 144434766Speter case 'q': /* quoted string */ 144534766Speter quoted = c == 'q'; 144634766Speter p = va_arg(args, unsigned char *); 144734766Speter if (fillch == '0' && prec > 0) { 144834766Speter n = prec; 144934766Speter } else { 145034766Speter n = strlen((char *)p); 145134766Speter if (prec > 0 && prec < n) 145234766Speter n = prec; 145334766Speter } 145434766Speter while (n > 0 && buflen > 0) { 145534766Speter c = *p++; 145634766Speter --n; 145734766Speter if (!quoted && c >= 0x80) { 145834766Speter OUTCHAR('M'); 145934766Speter OUTCHAR('-'); 146034766Speter c -= 0x80; 146134766Speter } 146234766Speter if (quoted && (c == '"' || c == '\\')) 146334766Speter OUTCHAR('\\'); 146434766Speter if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 146534766Speter if (quoted) { 146634766Speter OUTCHAR('\\'); 146734766Speter switch (c) { 146834766Speter case '\t': OUTCHAR('t'); break; 146934766Speter case '\n': OUTCHAR('n'); break; 147034766Speter case '\b': OUTCHAR('b'); break; 147134766Speter case '\f': OUTCHAR('f'); break; 147234766Speter default: 147334766Speter OUTCHAR('x'); 147434766Speter OUTCHAR(hexchars[c >> 4]); 147534766Speter OUTCHAR(hexchars[c & 0xf]); 147634766Speter } 147734766Speter } else { 147834766Speter if (c == '\t') 147934766Speter OUTCHAR(c); 148034766Speter else { 148134766Speter OUTCHAR('^'); 148234766Speter OUTCHAR(c ^ 0x40); 148334766Speter } 148434766Speter } 148534766Speter } else 148634766Speter OUTCHAR(c); 148734766Speter } 148834766Speter continue; 148934766Speter default: 149034766Speter *buf++ = '%'; 149134766Speter if (c != '%') 149234766Speter --fmt; /* so %z outputs %z etc. */ 149334766Speter --buflen; 149434766Speter continue; 149534766Speter } 149634766Speter if (base != 0) { 149734766Speter str = num + sizeof(num); 149834766Speter *--str = 0; 149934766Speter while (str > num + neg) { 150034766Speter *--str = hexchars[val % base]; 150134766Speter val = val / base; 150234766Speter if (--prec <= 0 && val == 0) 150334766Speter break; 150434766Speter } 150534766Speter switch (neg) { 150634766Speter case 1: 150734766Speter *--str = '-'; 150834766Speter break; 150934766Speter case 2: 151034766Speter *--str = 'x'; 151134766Speter *--str = '0'; 151234766Speter break; 151334766Speter } 151434766Speter len = num + sizeof(num) - 1 - str; 151534766Speter } else { 151634766Speter len = strlen(str); 151734766Speter if (prec > 0 && len > prec) 151834766Speter len = prec; 151934766Speter } 152034766Speter if (width > 0) { 152134766Speter if (width > buflen) 152234766Speter width = buflen; 152334766Speter if ((n = width - len) > 0) { 152434766Speter buflen -= n; 152534766Speter for (; n > 0; --n) 152634766Speter *buf++ = fillch; 152734766Speter } 152834766Speter } 152934766Speter if (len > buflen) 153034766Speter len = buflen; 153134766Speter memcpy(buf, str, len); 153234766Speter buf += len; 153334766Speter buflen -= len; 153434766Speter } 153534766Speter *buf = 0; 153634766Speter return buf - buf0; 153734766Speter} 1538