chat.c revision 121784
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 121784 2003-10-31 06:22:03Z kientzle $"); 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/*************** Micro getopt() *********************************************/ 1094374Slars#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ 1104374Slars (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ 1114374Slars &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) 1124374Slars#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ 1134374Slars (_O=4,(char*)0):(char*)0) 1144374Slars#define ARG(c,v) (c?(--c,*v++):(char*)0) 1154374Slars 1164374Slarsstatic int _O = 0; /* Internal state */ 1174374Slars/*************** Micro getopt() *********************************************/ 1184374Slars 1194374Slars#define MAX_ABORTS 50 12011990Speter#define MAX_REPORTS 50 1214374Slars#define DEFAULT_CHAT_TIMEOUT 45 1224374Slars 12328597Speterint echo = 0; 12411990Speterint verbose = 0; 12534766Speterint to_log = 1; 12634766Speterint to_stderr = 0; 12728597Speterint Verbose = 0; 12811990Speterint quiet = 0; 12911990Speterint exit_code = 0; 13011990SpeterFILE* report_fp = (FILE *) 0; 13111990Speterchar *report_file = (char *) 0; 13211990Speterchar *chat_file = (char *) 0; 13334766Speterchar *phone_num = (char *) 0; 13434766Speterchar *phone_num2 = (char *) 0; 13511990Speterint timeout = DEFAULT_CHAT_TIMEOUT; 1364374Slars 137119316Smarkmstatic char blank[] = ""; 138119316Smarkm 1394374Slarsint have_tty_parameters = 0; 14011990Speter 14111990Speter#define term_parms struct termios 14211990Speter#define get_term_param(param) tcgetattr(0, param) 14311990Speter#define set_term_param(param) tcsetattr(0, TCSANOW, param) 1444374Slarsstruct termios saved_tty_parameters; 1454374Slars 1464374Slarschar *abort_string[MAX_ABORTS], *fail_reason = (char *)0, 1474374Slars fail_buffer[50]; 14828597Speterint n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; 14928597Speterint clear_abort_next = 0; 1504374Slars 15111990Speterchar *report_string[MAX_REPORTS] ; 15211990Speterchar report_buffer[50] ; 15311990Speterint n_reports = 0, report_next = 0, report_gathering = 0 ; 15428597Speterint clear_report_next = 0; 15511990Speter 15628597Speterint say_next = 0, hup_next = 0; 15728597Speter 15892920Simpvoid *dup_mem(void *b, size_t c); 15992920Simpvoid *copy_of(char *s); 16092920Simpstatic void usage(void); 161121784Skientzlevoid chat_logf(const char *fmt, ...); 16292920Simpvoid fatal(int code, const char *fmt, ...); 16392920SimpSIGTYPE sigalrm(int signo); 16492920SimpSIGTYPE sigint(int signo); 16592920SimpSIGTYPE sigterm(int signo); 16692920SimpSIGTYPE sighup(int signo); 16792920Simpvoid init(void); 16892920Simpvoid set_tty_parameters(void); 16992920Simpvoid echo_stderr(int); 17092920Simpvoid break_sequence(void); 17192920Simpvoid terminate(int status); 172119316Smarkmvoid do_file(char *chatfile); 173119316Smarkmint get_string(char *string); 174119316Smarkmint put_string(char *s); 17592920Simpint write_char(int c); 17692920Simpint put_char(int c); 17792920Simpint get_char(void); 178119316Smarkmvoid chat_send(char *s); 17992920Simpchar *character(int c); 180119316Smarkmvoid chat_expect(char *s); 181119316Smarkmchar *clean(char *s, int sending); 18292920Simpvoid pack_array(char **array, int end); 183119316Smarkmchar *expect_strtok(char *, const char *); 18492920Simpint vfmtmsg(char *, int, const char *, va_list); /* vsprintf++ */ 1854374Slars 186119316Smarkmvoid * 187119316Smarkmdup_mem(void *b, size_t c) 18834766Speter{ 1894374Slars void *ans = malloc (c); 1904374Slars if (!ans) 19134766Speter fatal(2, "memory error!"); 19234766Speter 1934374Slars memcpy (ans, b, c); 1944374Slars return ans; 19534766Speter} 1964374Slars 197119316Smarkmvoid * 198119316Smarkmcopy_of(char *s) 19934766Speter{ 2004374Slars return dup_mem (s, strlen (s) + 1); 20134766Speter} 2024374Slars 2034374Slars/* 20434766Speter * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ 20534766Speter * [ -r report-file ] \ 2064374Slars * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] 2074374Slars * 2084374Slars * Perform a UUCP-dialer-like chat script on stdin and stdout. 2094374Slars */ 2104374Slarsint 211119316Smarkmmain(int argc, char *argv[]) 21234766Speter{ 2134374Slars int option; 2144374Slars char *arg; 2154374Slars 21611990Speter tzset(); 2174374Slars 21834766Speter while ((option = OPTION(argc, argv)) != 0) { 21934766Speter switch (option) { 22034766Speter case 'e': 22134766Speter ++echo; 22234766Speter break; 22328597Speter 22434766Speter case 'v': 22534766Speter ++verbose; 22634766Speter break; 2274374Slars 22834766Speter case 'V': 22934766Speter ++Verbose; 23034766Speter break; 23128597Speter 23234766Speter case 's': 23334766Speter ++to_stderr; 23434766Speter break; 23534766Speter 23634766Speter case 'S': 23734766Speter to_log = 0; 23834766Speter break; 23934766Speter 24034766Speter case 'f': 24134766Speter if ((arg = OPTARG(argc, argv)) != NULL) 2424374Slars chat_file = copy_of(arg); 24334766Speter else 24434766Speter usage(); 24534766Speter break; 2464374Slars 24734766Speter case 't': 24834766Speter if ((arg = OPTARG(argc, argv)) != NULL) 24934766Speter timeout = atoi(arg); 25034766Speter else 25134766Speter usage(); 25234766Speter break; 2534374Slars 25434766Speter case 'r': 25534766Speter arg = OPTARG (argc, argv); 25634766Speter if (arg) { 25734766Speter if (report_fp != NULL) 25834766Speter fclose (report_fp); 25934766Speter report_file = copy_of (arg); 26034766Speter report_fp = fopen (report_file, "a"); 26134766Speter if (report_fp != NULL) { 26234766Speter if (verbose) 26334766Speter fprintf (report_fp, "Opening \"%s\"...\n", 26434766Speter report_file); 26534766Speter } 26634766Speter } 26734766Speter break; 2684374Slars 26934766Speter case 'T': 27034766Speter if ((arg = OPTARG(argc, argv)) != NULL) 27134766Speter phone_num = copy_of(arg); 27234766Speter else 2734374Slars usage(); 27434766Speter break; 27534766Speter 27634766Speter case 'U': 27734766Speter if ((arg = OPTARG(argc, argv)) != NULL) 27834766Speter phone_num2 = copy_of(arg); 27934766Speter else 28034766Speter usage(); 28134766Speter break; 28234766Speter 28334766Speter default: 28434766Speter usage(); 28534766Speter break; 28634766Speter } 28734766Speter } 28811990Speter/* 28911990Speter * Default the report file to the stderr location 29011990Speter */ 29111990Speter if (report_fp == NULL) 29211990Speter report_fp = stderr; 2934374Slars 29434766Speter if (to_log) { 29534766Speter openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 2964374Slars 29734766Speter if (verbose) 29834766Speter setlogmask(LOG_UPTO(LOG_INFO)); 29934766Speter else 30034766Speter setlogmask(LOG_UPTO(LOG_WARNING)); 30134766Speter } 3024374Slars 3034374Slars init(); 30411990Speter 30534766Speter if (chat_file != NULL) { 3064374Slars arg = ARG(argc, argv); 3074374Slars if (arg != NULL) 3084374Slars usage(); 3094374Slars else 3104374Slars do_file (chat_file); 31134766Speter } else { 31234766Speter while ((arg = ARG(argc, argv)) != NULL) { 3134374Slars chat_expect(arg); 3144374Slars 31528597Speter if ((arg = ARG(argc, argv)) != NULL) 3164374Slars chat_send(arg); 3174374Slars } 31834766Speter } 3194374Slars 3204374Slars terminate(0); 32128597Speter return 0; 32234766Speter} 3234374Slars 3244374Slars/* 3254374Slars * Process a chat script when read from a file. 3264374Slars */ 3274374Slars 328119316Smarkmvoid 329119316Smarkmdo_file(char *chatfile) 33034766Speter{ 33132069Salex int linect, sendflg; 3324374Slars char *sp, *arg, quote; 3334374Slars char buf [STR_LEN]; 3344374Slars FILE *cfp; 3354374Slars 336119316Smarkm cfp = fopen (chatfile, "r"); 33711990Speter if (cfp == NULL) 338119316Smarkm fatal(1, "%s -- open failed: %m", chatfile); 3394374Slars 3404374Slars linect = 0; 3414374Slars sendflg = 0; 3424374Slars 34334766Speter while (fgets(buf, STR_LEN, cfp) != NULL) { 3444374Slars sp = strchr (buf, '\n'); 3454374Slars if (sp) 3464374Slars *sp = '\0'; 3474374Slars 3484374Slars linect++; 3494374Slars sp = buf; 35028597Speter 35128597Speter /* lines starting with '#' are comments. If a real '#' 35228597Speter is to be expected, it should be quoted .... */ 35334766Speter if ( *sp == '#' ) 35434766Speter continue; 35528597Speter 35634766Speter while (*sp != '\0') { 35734766Speter if (*sp == ' ' || *sp == '\t') { 3584374Slars ++sp; 3594374Slars continue; 36034766Speter } 3614374Slars 36234766Speter if (*sp == '"' || *sp == '\'') { 3634374Slars quote = *sp++; 3644374Slars arg = sp; 36534766Speter while (*sp != quote) { 3664374Slars if (*sp == '\0') 36734766Speter fatal(1, "unterminated quote (line %d)", linect); 36834766Speter 36934766Speter if (*sp++ == '\\') { 3704374Slars if (*sp != '\0') 3714374Slars ++sp; 3724374Slars } 3734374Slars } 37434766Speter } 37534766Speter else { 3764374Slars arg = sp; 3774374Slars while (*sp != '\0' && *sp != ' ' && *sp != '\t') 3784374Slars ++sp; 37934766Speter } 3804374Slars 3814374Slars if (*sp != '\0') 3824374Slars *sp++ = '\0'; 3834374Slars 3844374Slars if (sendflg) 3854374Slars chat_send (arg); 3864374Slars else 3874374Slars chat_expect (arg); 3884374Slars sendflg = !sendflg; 3894374Slars } 39034766Speter } 3914374Slars fclose (cfp); 39234766Speter} 3934374Slars 3944374Slars/* 3954374Slars * We got an error parsing the command line. 3964374Slars */ 39726880Scharnierstatic void 398119316Smarkmusage(void) 39934766Speter{ 40034766Speter fprintf(stderr, "\ 40134766SpeterUsage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\ 40234766Speter [-U phone-number2] {-f chat-file | chat-script}\n"); 4034374Slars exit(1); 40434766Speter} 4054374Slars 40634766Speterchar line[1024]; 4074374Slars 40834766Speter/* 40934766Speter * Send a message to syslog and/or stderr. 41034766Speter */ 411119316Smarkmvoid 412121784Skientzlechat_logf(const char *fmt, ...) 41328597Speter{ 41434766Speter va_list args; 4154374Slars 41634766Speter va_start(args, fmt); 41734766Speter vfmtmsg(line, sizeof(line), fmt, args); 41834766Speter if (to_log) 41928597Speter syslog(LOG_INFO, "%s", line); 42034766Speter if (to_stderr) 42134766Speter fprintf(stderr, "%s\n", line); 42228597Speter} 4234374Slars 4244374Slars/* 4254374Slars * Print an error message and terminate. 4264374Slars */ 4274374Slars 428119316Smarkmvoid 429119316Smarkmfatal(int code, const char *fmt, ...) 43034766Speter{ 43134766Speter va_list args; 4324374Slars 43334766Speter va_start(args, fmt); 43434766Speter vfmtmsg(line, sizeof(line), fmt, args); 43534766Speter if (to_log) 43634766Speter syslog(LOG_ERR, "%s", line); 43734766Speter if (to_stderr) 43834766Speter fprintf(stderr, "%s\n", line); 43934766Speter terminate(code); 44034766Speter} 4414374Slars 4424374Slarsint alarmed = 0; 4434374Slars 444119316SmarkmSIGTYPE sigalrm(int signo __unused) 44534766Speter{ 4464374Slars int flags; 4474374Slars 4484374Slars alarm(1); 4494374Slars alarmed = 1; /* Reset alarm to avoid race window */ 4504374Slars signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 4514374Slars 4524374Slars if ((flags = fcntl(0, F_GETFL, 0)) == -1) 45334766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 4544374Slars 45534766Speter if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 45634766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 45734766Speter 4584374Slars if (verbose) 459121784Skientzle chat_logf("alarm"); 46034766Speter} 4614374Slars 462119316SmarkmSIGTYPE sigint(int signo __unused) 46334766Speter{ 46434766Speter fatal(2, "SIGINT"); 46534766Speter} 4664374Slars 467119316SmarkmSIGTYPE sigterm(int signo __unused) 46834766Speter{ 46934766Speter fatal(2, "SIGTERM"); 47034766Speter} 4714374Slars 472119316SmarkmSIGTYPE sighup(int signo __unused) 47334766Speter{ 47434766Speter fatal(2, "SIGHUP"); 47534766Speter} 4764374Slars 477119316Smarkmvoid init(void) 47834766Speter{ 4794374Slars signal(SIGINT, sigint); 4804374Slars signal(SIGTERM, sigterm); 4814374Slars signal(SIGHUP, sighup); 4824374Slars 4834374Slars set_tty_parameters(); 4844374Slars signal(SIGALRM, sigalrm); 4854374Slars alarm(0); 4864374Slars alarmed = 0; 48734766Speter} 4884374Slars 489119316Smarkmvoid set_tty_parameters(void) 49034766Speter{ 49111990Speter#if defined(get_term_param) 49211990Speter term_parms t; 4934374Slars 49411990Speter if (get_term_param (&t) < 0) 49534766Speter fatal(2, "Can't get terminal parameters: %m"); 4964374Slars 4974374Slars saved_tty_parameters = t; 49811990Speter have_tty_parameters = 1; 4994374Slars 50011990Speter t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 50111990Speter t.c_oflag = 0; 50211990Speter t.c_lflag = 0; 50311990Speter t.c_cc[VERASE] = 50411990Speter t.c_cc[VKILL] = 0; 50511990Speter t.c_cc[VMIN] = 1; 50611990Speter t.c_cc[VTIME] = 0; 5074374Slars 50811990Speter if (set_term_param (&t) < 0) 50934766Speter fatal(2, "Can't set terminal parameters: %m"); 5104374Slars#endif 51134766Speter} 5124374Slars 513119316Smarkmvoid break_sequence(void) 51434766Speter{ 5154374Slars tcsendbreak (0, 0); 51634766Speter} 5174374Slars 518119316Smarkmvoid terminate(int status) 51934766Speter{ 52028597Speter echo_stderr(-1); 52134766Speter if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 52228597Speter/* 52328597Speter * Allow the last of the report string to be gathered before we terminate. 52428597Speter */ 52528597Speter if (report_gathering) { 526119316Smarkm int c; 527119316Smarkm size_t rep_len; 52828597Speter 52928597Speter rep_len = strlen(report_buffer); 53028597Speter while (rep_len + 1 <= sizeof(report_buffer)) { 53128597Speter alarm(1); 53228597Speter c = get_char(); 53328597Speter alarm(0); 53428597Speter if (c < 0 || iscntrl(c)) 53528597Speter break; 53628597Speter report_buffer[rep_len] = c; 53728597Speter ++rep_len; 53828597Speter } 53928597Speter report_buffer[rep_len] = 0; 54028597Speter fprintf (report_fp, "chat: %s\n", report_buffer); 54128597Speter } 54211990Speter if (verbose) 54311990Speter fprintf (report_fp, "Closing \"%s\".\n", report_file); 54411990Speter fclose (report_fp); 54528597Speter report_fp = (FILE *) NULL; 54634766Speter } 5474374Slars 54811990Speter#if defined(get_term_param) 54934766Speter if (have_tty_parameters) { 55011990Speter if (set_term_param (&saved_tty_parameters) < 0) 55134766Speter fatal(2, "Can't restore terminal parameters: %m"); 55234766Speter } 55311990Speter#endif 5544374Slars 55511990Speter exit(status); 55634766Speter} 5574374Slars 5584374Slars/* 5594374Slars * 'Clean up' this string. 5604374Slars */ 561119316Smarkmchar * 562119316Smarkmclean(char *s, int sending) 56334766Speter{ 5644374Slars char temp[STR_LEN], cur_chr; 565119316Smarkm char *s1, *phchar; 5664374Slars int add_return = sending; 5674374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 5684374Slars 5694374Slars s1 = temp; 57053686Skris /* Don't overflow buffer, leave room for chars we append later */ 571119316Smarkm while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) { 5724374Slars cur_chr = *s++; 57334766Speter if (cur_chr == '^') { 5744374Slars cur_chr = *s++; 57534766Speter if (cur_chr == '\0') { 5764374Slars *s1++ = '^'; 5774374Slars break; 57834766Speter } 5794374Slars cur_chr &= 0x1F; 58034766Speter if (cur_chr != 0) { 5814374Slars *s1++ = cur_chr; 58234766Speter } 5834374Slars continue; 58434766Speter } 5854374Slars 58634766Speter if (cur_chr != '\\') { 5874374Slars *s1++ = cur_chr; 5884374Slars continue; 58934766Speter } 5904374Slars 5914374Slars cur_chr = *s++; 59234766Speter if (cur_chr == '\0') { 59334766Speter if (sending) { 5944374Slars *s1++ = '\\'; 5954374Slars *s1++ = '\\'; 59634766Speter } 5974374Slars break; 59834766Speter } 5994374Slars 60034766Speter switch (cur_chr) { 6014374Slars case 'b': 6024374Slars *s1++ = '\b'; 6034374Slars break; 6044374Slars 6054374Slars case 'c': 6064374Slars if (sending && *s == '\0') 6074374Slars add_return = 0; 6084374Slars else 6094374Slars *s1++ = cur_chr; 6104374Slars break; 6114374Slars 6124374Slars case '\\': 6134374Slars case 'K': 6144374Slars case 'p': 6154374Slars case 'd': 6164374Slars if (sending) 6174374Slars *s1++ = '\\'; 6184374Slars 6194374Slars *s1++ = cur_chr; 6204374Slars break; 6214374Slars 62234766Speter case 'T': 62334766Speter if (sending && phone_num) { 62434766Speter for ( phchar = phone_num; *phchar != '\0'; phchar++) 62534766Speter *s1++ = *phchar; 62634766Speter } 62734766Speter else { 62834766Speter *s1++ = '\\'; 62934766Speter *s1++ = 'T'; 63034766Speter } 63134766Speter break; 63234766Speter 63334766Speter case 'U': 63434766Speter if (sending && phone_num2) { 63534766Speter for ( phchar = phone_num2; *phchar != '\0'; phchar++) 63634766Speter *s1++ = *phchar; 63734766Speter } 63834766Speter else { 63934766Speter *s1++ = '\\'; 64034766Speter *s1++ = 'U'; 64134766Speter } 64234766Speter break; 64334766Speter 6444374Slars case 'q': 64528597Speter quiet = 1; 6464374Slars break; 6474374Slars 6484374Slars case 'r': 6494374Slars *s1++ = '\r'; 6504374Slars break; 6514374Slars 6524374Slars case 'n': 6534374Slars *s1++ = '\n'; 6544374Slars break; 6554374Slars 6564374Slars case 's': 6574374Slars *s1++ = ' '; 6584374Slars break; 6594374Slars 6604374Slars case 't': 6614374Slars *s1++ = '\t'; 6624374Slars break; 6634374Slars 6644374Slars case 'N': 66534766Speter if (sending) { 6664374Slars *s1++ = '\\'; 6674374Slars *s1++ = '\0'; 66834766Speter } 6694374Slars else 6704374Slars *s1++ = 'N'; 6714374Slars break; 67211990Speter 6734374Slars default: 67434766Speter if (isoctal (cur_chr)) { 6754374Slars cur_chr &= 0x07; 67634766Speter if (isoctal (*s)) { 6774374Slars cur_chr <<= 3; 6784374Slars cur_chr |= *s++ - '0'; 67934766Speter if (isoctal (*s)) { 6804374Slars cur_chr <<= 3; 6814374Slars cur_chr |= *s++ - '0'; 6824374Slars } 68334766Speter } 6844374Slars 68534766Speter if (cur_chr != 0 || sending) { 6864374Slars if (sending && (cur_chr == '\\' || cur_chr == 0)) 6874374Slars *s1++ = '\\'; 6884374Slars *s1++ = cur_chr; 68934766Speter } 6904374Slars break; 69134766Speter } 6924374Slars 6934374Slars if (sending) 6944374Slars *s1++ = '\\'; 6954374Slars *s1++ = cur_chr; 6964374Slars break; 6974374Slars } 69834766Speter } 6994374Slars 7004374Slars if (add_return) 7014374Slars *s1++ = '\r'; 7024374Slars 7034374Slars *s1++ = '\0'; /* guarantee closure */ 7044374Slars *s1++ = '\0'; /* terminate the string */ 7054374Slars return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 70634766Speter} 7074374Slars 7084374Slars/* 70928597Speter * A modified version of 'strtok'. This version skips \ sequences. 71028597Speter */ 71128597Speter 712119316Smarkmchar * 713119316Smarkmexpect_strtok (char *s, const char *term) 71434766Speter{ 715119316Smarkm static char *str = blank; 71628597Speter int escape_flag = 0; 71728597Speter char *result; 71834766Speter 71928597Speter/* 72028597Speter * If a string was specified then do initial processing. 72128597Speter */ 72228597Speter if (s) 72328597Speter str = s; 72434766Speter 72528597Speter/* 72628597Speter * If this is the escape flag then reset it and ignore the character. 72728597Speter */ 72828597Speter if (*str) 72928597Speter result = str; 73028597Speter else 73128597Speter result = (char *) 0; 73228597Speter 73334766Speter while (*str) { 73434766Speter if (escape_flag) { 73528597Speter escape_flag = 0; 73628597Speter ++str; 73728597Speter continue; 73834766Speter } 73928597Speter 74034766Speter if (*str == '\\') { 74128597Speter ++str; 74228597Speter escape_flag = 1; 74328597Speter continue; 74434766Speter } 74534766Speter 74628597Speter/* 74728597Speter * If this is not in the termination string, continue. 74828597Speter */ 74934766Speter if (strchr (term, *str) == (char *) 0) { 75028597Speter ++str; 75128597Speter continue; 75234766Speter } 75334766Speter 75428597Speter/* 75528597Speter * This is the terminator. Mark the end of the string and stop. 75628597Speter */ 75728597Speter *str++ = '\0'; 75828597Speter break; 75934766Speter } 76028597Speter return (result); 76134766Speter} 76228597Speter 76328597Speter/* 7644374Slars * Process the expect string 7654374Slars */ 76628597Speter 767119316Smarkmvoid 768119316Smarkmchat_expect(char *s) 76934766Speter{ 77028597Speter char *expect; 77128597Speter char *reply; 77228597Speter 77334766Speter if (strcmp(s, "HANGUP") == 0) { 77428597Speter ++hup_next; 77528597Speter return; 77634766Speter } 77728597Speter 77834766Speter if (strcmp(s, "ABORT") == 0) { 7794374Slars ++abort_next; 7804374Slars return; 78134766Speter } 7824374Slars 78334766Speter if (strcmp(s, "CLR_ABORT") == 0) { 78428597Speter ++clear_abort_next; 78528597Speter return; 78634766Speter } 78728597Speter 78834766Speter if (strcmp(s, "REPORT") == 0) { 78911990Speter ++report_next; 79011990Speter return; 79134766Speter } 79211990Speter 79334766Speter if (strcmp(s, "CLR_REPORT") == 0) { 79428597Speter ++clear_report_next; 79528597Speter return; 79634766Speter } 79728597Speter 79834766Speter if (strcmp(s, "TIMEOUT") == 0) { 7994374Slars ++timeout_next; 8004374Slars return; 80134766Speter } 8024374Slars 80334766Speter if (strcmp(s, "ECHO") == 0) { 80428597Speter ++echo_next; 80528597Speter return; 80634766Speter } 80734766Speter 80834766Speter if (strcmp(s, "SAY") == 0) { 80928597Speter ++say_next; 81028597Speter return; 81134766Speter } 81234766Speter 81328597Speter/* 81428597Speter * Fetch the expect and reply string. 81528597Speter */ 81634766Speter for (;;) { 81728597Speter expect = expect_strtok (s, "-"); 81828597Speter s = (char *) 0; 8194374Slars 82028597Speter if (expect == (char *) 0) 82128597Speter return; 82228597Speter 82328597Speter reply = expect_strtok (s, "-"); 82434766Speter 82528597Speter/* 82628597Speter * Handle the expect string. If successful then exit. 82728597Speter */ 82828597Speter if (get_string (expect)) 82928597Speter return; 83034766Speter 83128597Speter/* 83228597Speter * If there is a sub-reply string then send it. Otherwise any condition 83328597Speter * is terminal. 83428597Speter */ 83528597Speter if (reply == (char *) 0 || exit_code != 3) 83628597Speter break; 8374374Slars 83828597Speter chat_send (reply); 83934766Speter } 84034766Speter 84128597Speter/* 84228597Speter * The expectation did not occur. This is terminal. 84328597Speter */ 84428597Speter if (fail_reason) 845121784Skientzle chat_logf("Failed (%s)", fail_reason); 84628597Speter else 847121784Skientzle chat_logf("Failed"); 84828597Speter terminate(exit_code); 84934766Speter} 8504374Slars 85128597Speter/* 85228597Speter * Translate the input character to the appropriate string for printing 85328597Speter * the data. 85428597Speter */ 85528597Speter 856119316Smarkmchar * 857119316Smarkmcharacter(int c) 85834766Speter{ 8594374Slars static char string[10]; 860119316Smarkm const char *meta; 8614374Slars 8624374Slars meta = (c & 0x80) ? "M-" : ""; 8634374Slars c &= 0x7F; 8644374Slars 8654374Slars if (c < 32) 8664374Slars sprintf(string, "%s^%c", meta, (int)c + '@'); 86734766Speter else if (c == 127) 86834766Speter sprintf(string, "%s^?", meta); 8694374Slars else 87034766Speter sprintf(string, "%s%c", meta, c); 8714374Slars 8724374Slars return (string); 87334766Speter} 8744374Slars 8754374Slars/* 8764374Slars * process the reply string 8774374Slars */ 878119316Smarkmvoid 879119316Smarkmchat_send(char *s) 88034766Speter{ 88134766Speter if (say_next) { 88228597Speter say_next = 0; 88328597Speter s = clean(s,0); 88480381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 88528597Speter free(s); 88628597Speter return; 88734766Speter } 88834766Speter 88934766Speter if (hup_next) { 89028597Speter hup_next = 0; 89128597Speter if (strcmp(s, "OFF") == 0) 89228597Speter signal(SIGHUP, SIG_IGN); 89328597Speter else 89428597Speter signal(SIGHUP, sighup); 89528597Speter return; 89634766Speter } 89734766Speter 89834766Speter if (echo_next) { 89928597Speter echo_next = 0; 90028597Speter echo = (strcmp(s, "ON") == 0); 90128597Speter return; 90234766Speter } 90334766Speter 90434766Speter if (abort_next) { 9054374Slars char *s1; 90611990Speter 9074374Slars abort_next = 0; 90811990Speter 9094374Slars if (n_aborts >= MAX_ABORTS) 91034766Speter fatal(2, "Too many ABORT strings"); 91111990Speter 9124374Slars s1 = clean(s, 0); 91311990Speter 91411990Speter if (strlen(s1) > strlen(s) 91511990Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 91634766Speter fatal(1, "Illegal or too-long ABORT string ('%v')", s); 9174374Slars 9184374Slars abort_string[n_aborts++] = s1; 9194374Slars 9204374Slars if (verbose) 921121784Skientzle chat_logf("abort on (%v)", s); 92211990Speter return; 92334766Speter } 92411990Speter 92534766Speter if (clear_abort_next) { 92628597Speter char *s1; 92728597Speter int i; 92828597Speter int old_max; 92928597Speter int pack = 0; 93028597Speter 93128597Speter clear_abort_next = 0; 93228597Speter 93328597Speter s1 = clean(s, 0); 93428597Speter 93528597Speter if (strlen(s1) > strlen(s) 93628597Speter || strlen(s1) + 1 > sizeof(fail_buffer)) 93734766Speter fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 93828597Speter 93928597Speter old_max = n_aborts; 94034766Speter for (i=0; i < n_aborts; i++) { 94134766Speter if ( strcmp(s1,abort_string[i]) == 0 ) { 94234766Speter free(abort_string[i]); 94334766Speter abort_string[i] = NULL; 94434766Speter pack++; 94534766Speter n_aborts--; 94634766Speter if (verbose) 947121784Skientzle chat_logf("clear abort on (%v)", s); 94828597Speter } 94934766Speter } 95028597Speter free(s1); 95134766Speter if (pack) 95234766Speter pack_array(abort_string,old_max); 95328597Speter return; 95434766Speter } 95528597Speter 95634766Speter if (report_next) { 95711990Speter char *s1; 95811990Speter 95911990Speter report_next = 0; 96011990Speter if (n_reports >= MAX_REPORTS) 96134766Speter fatal(2, "Too many REPORT strings"); 96211990Speter 96311990Speter s1 = clean(s, 0); 96411990Speter 96511990Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 96634766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 96711990Speter 96811990Speter report_string[n_reports++] = s1; 96911990Speter 97011990Speter if (verbose) 971121784Skientzle chat_logf("report (%v)", s); 97211990Speter return; 97334766Speter } 9744374Slars 97534766Speter if (clear_report_next) { 97628597Speter char *s1; 97728597Speter int i; 97828597Speter int old_max; 97928597Speter int pack = 0; 98028597Speter 98128597Speter clear_report_next = 0; 98228597Speter 98328597Speter s1 = clean(s, 0); 98428597Speter 98528597Speter if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 98634766Speter fatal(1, "Illegal or too-long REPORT string ('%v')", s); 98728597Speter 98828597Speter old_max = n_reports; 98934766Speter for (i=0; i < n_reports; i++) { 99034766Speter if ( strcmp(s1,report_string[i]) == 0 ) { 99134766Speter free(report_string[i]); 99234766Speter report_string[i] = NULL; 99334766Speter pack++; 99434766Speter n_reports--; 99534766Speter if (verbose) 996121784Skientzle chat_logf("clear report (%v)", s); 99728597Speter } 99834766Speter } 99928597Speter free(s1); 100034766Speter if (pack) 100134766Speter pack_array(report_string,old_max); 100228597Speter 100328597Speter return; 100434766Speter } 100528597Speter 100634766Speter if (timeout_next) { 100711990Speter timeout_next = 0; 100811990Speter timeout = atoi(s); 100911990Speter 101011990Speter if (timeout <= 0) 101111990Speter timeout = DEFAULT_CHAT_TIMEOUT; 10124374Slars 101311990Speter if (verbose) 1014121784Skientzle chat_logf("timeout set to %d seconds", timeout); 101534766Speter 101611990Speter return; 101734766Speter } 101811990Speter 101911990Speter if (strcmp(s, "EOT") == 0) 1020119316Smarkm s = strdup("^D\\c"); 102134766Speter else if (strcmp(s, "BREAK") == 0) 1022119316Smarkm s = strdup("\\K\\c"); 102311990Speter 102411990Speter if (!put_string(s)) 102534766Speter fatal(1, "Failed"); 102634766Speter} 10274374Slars 1028119316Smarkmint 1029119316Smarkmget_char(void) 103034766Speter{ 10314374Slars int status; 10324374Slars char c; 10334374Slars 103480381Ssheldonh status = read(STDIN_FILENO, &c, 1); 10354374Slars 103634766Speter switch (status) { 103711990Speter case 1: 103811990Speter return ((int)c & 0x7F); 10394374Slars 104011990Speter default: 1041121784Skientzle chat_logf("warning: read() on stdin returned %d", status); 10424374Slars 104311990Speter case -1: 104411990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 104534766Speter fatal(2, "Can't get file mode flags on stdin: %m"); 104634766Speter 104734766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 104834766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 104911990Speter 105011990Speter return (-1); 10514374Slars } 105234766Speter} 10534374Slars 1054119316Smarkmint put_char(int c) 105534766Speter{ 10564374Slars int status; 105711990Speter char ch = c; 10584374Slars 105911990Speter usleep(10000); /* inter-character typing delay (?) */ 10604374Slars 106180381Ssheldonh status = write(STDOUT_FILENO, &ch, 1); 10624374Slars 106334766Speter switch (status) { 106411990Speter case 1: 106511990Speter return (0); 106611990Speter 106711990Speter default: 1068121784Skientzle chat_logf("warning: write() on stdout returned %d", status); 106911990Speter 107011990Speter case -1: 107111990Speter if ((status = fcntl(0, F_GETFL, 0)) == -1) 107234766Speter fatal(2, "Can't get file mode flags on stdin, %m"); 107334766Speter 107434766Speter if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 107534766Speter fatal(2, "Can't set file mode flags on stdin: %m"); 107611990Speter 107711990Speter return (-1); 10784374Slars } 107934766Speter} 10804374Slars 1081119316Smarkmint 1082119316Smarkmwrite_char(int c) 108334766Speter{ 108434766Speter if (alarmed || put_char(c) < 0) { 108511990Speter alarm(0); 108611990Speter alarmed = 0; 10874374Slars 108834766Speter if (verbose) { 10894374Slars if (errno == EINTR || errno == EWOULDBLOCK) 1090121784Skientzle chat_logf(" -- write timed out"); 10914374Slars else 1092121784Skientzle chat_logf(" -- write failed: %m"); 109334766Speter } 10944374Slars return (0); 109534766Speter } 10964374Slars return (1); 109734766Speter} 10984374Slars 1099119316Smarkmint 1100119316Smarkmput_string(char *s) 110134766Speter{ 110228597Speter quiet = 0; 11034374Slars s = clean(s, 1); 11044374Slars 1105119316Smarkm if (verbose) 1106121784Skientzle chat_logf("send (%v)", quiet ? "??????" : s); 11074374Slars 11084374Slars alarm(timeout); alarmed = 0; 11094374Slars 111034766Speter while (*s) { 1111119316Smarkm char c = *s++; 11124374Slars 111334766Speter if (c != '\\') { 11144374Slars if (!write_char (c)) 11154374Slars return 0; 11164374Slars continue; 111734766Speter } 11184374Slars 11194374Slars c = *s++; 112034766Speter switch (c) { 11214374Slars case 'd': 11224374Slars sleep(1); 11234374Slars break; 11244374Slars 11254374Slars case 'K': 11264374Slars break_sequence(); 11274374Slars break; 11284374Slars 11294374Slars case 'p': 113011990Speter usleep(10000); /* 1/100th of a second (arg is microseconds) */ 11314374Slars break; 11324374Slars 11334374Slars default: 11344374Slars if (!write_char (c)) 11354374Slars return 0; 11364374Slars break; 11374374Slars } 113834766Speter } 11394374Slars 11404374Slars alarm(0); 11414374Slars alarmed = 0; 11424374Slars return (1); 114334766Speter} 11444374Slars 11454374Slars/* 114628597Speter * Echo a character to stderr. 114728597Speter * When called with -1, a '\n' character is generated when 114828597Speter * the cursor is not at the beginning of a line. 114928597Speter */ 1150119316Smarkmvoid 1151119316Smarkmecho_stderr(int n) 115234766Speter{ 115334766Speter static int need_lf; 115434766Speter char *s; 115528597Speter 115634766Speter switch (n) { 115734766Speter case '\r': /* ignore '\r' */ 115834766Speter break; 115934766Speter case -1: 116034766Speter if (need_lf == 0) 116128597Speter break; 1162102412Scharnier /* FALLTHROUGH */ 116334766Speter case '\n': 116480381Ssheldonh write(STDERR_FILENO, "\n", 1); 116534766Speter need_lf = 0; 116634766Speter break; 116734766Speter default: 116834766Speter s = character(n); 116980381Ssheldonh write(STDERR_FILENO, s, strlen(s)); 117034766Speter need_lf = 1; 117134766Speter break; 117228597Speter } 117334766Speter} 117428597Speter 117528597Speter/* 11764374Slars * 'Wait for' this string to appear on this file descriptor. 11774374Slars */ 1178119316Smarkmint 1179119316Smarkmget_string(char *string) 118034766Speter{ 11814374Slars char temp[STR_LEN]; 1182119316Smarkm int c, printed = 0; 1183119316Smarkm size_t len, minlen; 1184119316Smarkm char *s = temp, *end = s + STR_LEN; 118534766Speter char *logged = temp; 11864374Slars 11874374Slars fail_reason = (char *)0; 118853686Skris 118953686Skris if (strlen(string) > STR_LEN) { 1190121784Skientzle chat_logf("expect string is too long"); 119153686Skris exit_code = 1; 119253686Skris return 0; 119353686Skris } 119453686Skris 11954374Slars string = clean(string, 0); 11964374Slars len = strlen(string); 11974374Slars minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 11984374Slars 11994374Slars if (verbose) 1200121784Skientzle chat_logf("expect (%v)", string); 12014374Slars 120234766Speter if (len == 0) { 12034374Slars if (verbose) 1204121784Skientzle chat_logf("got it"); 12054374Slars return (1); 120634766Speter } 12074374Slars 120811990Speter alarm(timeout); 120911990Speter alarmed = 0; 12104374Slars 121134766Speter while ( ! alarmed && (c = get_char()) >= 0) { 121211990Speter int n, abort_len, report_len; 12134374Slars 121428597Speter if (echo) 121528597Speter echo_stderr(c); 121634766Speter if (verbose && c == '\n') { 121734766Speter if (s == logged) 1218121784Skientzle chat_logf(""); /* blank line */ 12194374Slars else 1220121784Skientzle chat_logf("%0.*v", s - logged, logged); 122134766Speter logged = s + 1; 122234766Speter } 12234374Slars 122434766Speter *s++ = c; 122534766Speter 122634766Speter if (verbose && s >= logged + 80) { 1227121784Skientzle chat_logf("%0.*v", s - logged, logged); 122834766Speter logged = s; 122934766Speter } 123034766Speter 123128597Speter if (Verbose) { 123234766Speter if (c == '\n') 123334766Speter fputc( '\n', stderr ); 123434766Speter else if (c != '\r') 123534766Speter fprintf( stderr, "%s", character(c) ); 123628597Speter } 123728597Speter 123834766Speter if (!report_gathering) { 123934766Speter for (n = 0; n < n_reports; ++n) { 124011990Speter if ((report_string[n] != (char*) NULL) && 124111990Speter s - temp >= (report_len = strlen(report_string[n])) && 124234766Speter strncmp(s - report_len, report_string[n], report_len) == 0) { 124311990Speter time_t time_now = time ((time_t*) NULL); 124411990Speter struct tm* tm_now = localtime (&time_now); 124511990Speter 124611990Speter strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 124711990Speter strcat (report_buffer, report_string[n]); 124811990Speter 124911990Speter report_string[n] = (char *) NULL; 125011990Speter report_gathering = 1; 125111990Speter break; 125234766Speter } 125311990Speter } 125434766Speter } 125534766Speter else { 125634766Speter if (!iscntrl (c)) { 125711990Speter int rep_len = strlen (report_buffer); 125811990Speter report_buffer[rep_len] = c; 125911990Speter report_buffer[rep_len + 1] = '\0'; 126034766Speter } 126134766Speter else { 126211990Speter report_gathering = 0; 126311990Speter fprintf (report_fp, "chat: %s\n", report_buffer); 126411990Speter } 126534766Speter } 126611990Speter 1267119316Smarkm if ((size_t)(s - temp) >= len && 126828597Speter c == string[len - 1] && 126934766Speter strncmp(s - len, string, len) == 0) { 127034766Speter if (verbose) { 127134766Speter if (s > logged) 1272121784Skientzle chat_logf("%0.*v", s - logged, logged); 1273121784Skientzle chat_logf(" -- got it\n"); 127434766Speter } 127528597Speter 127628597Speter alarm(0); 127728597Speter alarmed = 0; 127828597Speter return (1); 127934766Speter } 128028597Speter 128134766Speter for (n = 0; n < n_aborts; ++n) { 128228597Speter if (s - temp >= (abort_len = strlen(abort_string[n])) && 128334766Speter strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 128434766Speter if (verbose) { 128534766Speter if (s > logged) 1286121784Skientzle chat_logf("%0.*v", s - logged, logged); 1287121784Skientzle chat_logf(" -- failed"); 128834766Speter } 128934766Speter 129028597Speter alarm(0); 129128597Speter alarmed = 0; 129228597Speter exit_code = n + 4; 129328597Speter strcpy(fail_reason = fail_buffer, abort_string[n]); 129428597Speter return (0); 129528597Speter } 129634766Speter } 129728597Speter 129834766Speter if (s >= end) { 129934766Speter if (logged < s - minlen) { 1300121784Skientzle chat_logf("%0.*v", s - logged, logged); 130134766Speter logged = s; 130234766Speter } 130334766Speter s -= minlen; 130434766Speter memmove(temp, s, minlen); 130534766Speter logged = temp + (logged - s); 13064374Slars s = temp + minlen; 130734766Speter } 13084374Slars 13094374Slars if (alarmed && verbose) 1310121784Skientzle chat_logf("warning: alarm synchronization problem"); 131134766Speter } 13124374Slars 13134374Slars alarm(0); 131411990Speter 131534766Speter if (verbose && printed) { 13164374Slars if (alarmed) 1317121784Skientzle chat_logf(" -- read timed out"); 13184374Slars else 1319121784Skientzle chat_logf(" -- read failed: %m"); 132034766Speter } 13214374Slars 132211990Speter exit_code = 3; 132311990Speter alarmed = 0; 13244374Slars return (0); 132534766Speter} 13264374Slars 132728597Spetervoid 1328119316Smarkmpack_array(char **array, int end) 132928597Speter{ 133028597Speter int i, j; 133128597Speter 133228597Speter for (i = 0; i < end; i++) { 133328597Speter if (array[i] == NULL) { 133428597Speter for (j = i+1; j < end; ++j) 133528597Speter if (array[j] != NULL) 133628597Speter array[i++] = array[j]; 133728597Speter for (; i < end; ++i) 133828597Speter array[i] = NULL; 133928597Speter break; 134028597Speter } 134128597Speter } 134228597Speter} 134334766Speter 134434766Speter/* 134534766Speter * vfmtmsg - format a message into a buffer. Like vsprintf except we 134634766Speter * also specify the length of the output buffer, and we handle the 134734766Speter * %m (error message) format. 134834766Speter * Doesn't do floating-point formats. 134934766Speter * Returns the number of chars put into buf. 135034766Speter */ 135134766Speter#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 135234766Speter 135334766Speterint 1354119316Smarkmvfmtmsg(char *buf, int buflen, const char *fmt, va_list args) 135534766Speter{ 135634766Speter int c, i, n; 135734766Speter int width, prec, fillch; 135834766Speter int base, len, neg, quoted; 135934766Speter unsigned long val = 0; 136034766Speter char *str, *buf0; 136134766Speter const char *f; 136234766Speter unsigned char *p; 136334766Speter char num[32]; 136434766Speter static char hexchars[] = "0123456789abcdef"; 136534766Speter 136634766Speter buf0 = buf; 136734766Speter --buflen; 136834766Speter while (buflen > 0) { 136934766Speter for (f = fmt; *f != '%' && *f != 0; ++f) 137034766Speter ; 137134766Speter if (f > fmt) { 137234766Speter len = f - fmt; 137334766Speter if (len > buflen) 137434766Speter len = buflen; 137534766Speter memcpy(buf, fmt, len); 137634766Speter buf += len; 137734766Speter buflen -= len; 137834766Speter fmt = f; 137934766Speter } 138034766Speter if (*fmt == 0) 138134766Speter break; 138234766Speter c = *++fmt; 138334766Speter width = prec = 0; 138434766Speter fillch = ' '; 138534766Speter if (c == '0') { 138634766Speter fillch = '0'; 138734766Speter c = *++fmt; 138834766Speter } 138934766Speter if (c == '*') { 139034766Speter width = va_arg(args, int); 139134766Speter c = *++fmt; 139234766Speter } else { 139334766Speter while (isdigit(c)) { 139434766Speter width = width * 10 + c - '0'; 139534766Speter c = *++fmt; 139634766Speter } 139734766Speter } 139834766Speter if (c == '.') { 139934766Speter c = *++fmt; 140034766Speter if (c == '*') { 140134766Speter prec = va_arg(args, int); 140234766Speter c = *++fmt; 140334766Speter } else { 140434766Speter while (isdigit(c)) { 140534766Speter prec = prec * 10 + c - '0'; 140634766Speter c = *++fmt; 140734766Speter } 140834766Speter } 140934766Speter } 141034766Speter str = 0; 141134766Speter base = 0; 141234766Speter neg = 0; 141334766Speter ++fmt; 141434766Speter switch (c) { 141534766Speter case 'd': 141634766Speter i = va_arg(args, int); 141734766Speter if (i < 0) { 141834766Speter neg = 1; 141934766Speter val = -i; 142034766Speter } else 142134766Speter val = i; 142234766Speter base = 10; 142334766Speter break; 142434766Speter case 'o': 142534766Speter val = va_arg(args, unsigned int); 142634766Speter base = 8; 142734766Speter break; 142834766Speter case 'x': 142934766Speter val = va_arg(args, unsigned int); 143034766Speter base = 16; 143134766Speter break; 143234766Speter case 'p': 143334766Speter val = (unsigned long) va_arg(args, void *); 143434766Speter base = 16; 143534766Speter neg = 2; 143634766Speter break; 143734766Speter case 's': 143834766Speter str = va_arg(args, char *); 143934766Speter break; 144034766Speter case 'c': 144134766Speter num[0] = va_arg(args, int); 144234766Speter num[1] = 0; 144334766Speter str = num; 144434766Speter break; 144534766Speter case 'm': 144634766Speter str = strerror(errno); 144734766Speter break; 144834766Speter case 'v': /* "visible" string */ 144934766Speter case 'q': /* quoted string */ 145034766Speter quoted = c == 'q'; 145134766Speter p = va_arg(args, unsigned char *); 145234766Speter if (fillch == '0' && prec > 0) { 145334766Speter n = prec; 145434766Speter } else { 145534766Speter n = strlen((char *)p); 145634766Speter if (prec > 0 && prec < n) 145734766Speter n = prec; 145834766Speter } 145934766Speter while (n > 0 && buflen > 0) { 146034766Speter c = *p++; 146134766Speter --n; 146234766Speter if (!quoted && c >= 0x80) { 146334766Speter OUTCHAR('M'); 146434766Speter OUTCHAR('-'); 146534766Speter c -= 0x80; 146634766Speter } 146734766Speter if (quoted && (c == '"' || c == '\\')) 146834766Speter OUTCHAR('\\'); 146934766Speter if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 147034766Speter if (quoted) { 147134766Speter OUTCHAR('\\'); 147234766Speter switch (c) { 147334766Speter case '\t': OUTCHAR('t'); break; 147434766Speter case '\n': OUTCHAR('n'); break; 147534766Speter case '\b': OUTCHAR('b'); break; 147634766Speter case '\f': OUTCHAR('f'); break; 147734766Speter default: 147834766Speter OUTCHAR('x'); 147934766Speter OUTCHAR(hexchars[c >> 4]); 148034766Speter OUTCHAR(hexchars[c & 0xf]); 148134766Speter } 148234766Speter } else { 148334766Speter if (c == '\t') 148434766Speter OUTCHAR(c); 148534766Speter else { 148634766Speter OUTCHAR('^'); 148734766Speter OUTCHAR(c ^ 0x40); 148834766Speter } 148934766Speter } 149034766Speter } else 149134766Speter OUTCHAR(c); 149234766Speter } 149334766Speter continue; 149434766Speter default: 149534766Speter *buf++ = '%'; 149634766Speter if (c != '%') 149734766Speter --fmt; /* so %z outputs %z etc. */ 149834766Speter --buflen; 149934766Speter continue; 150034766Speter } 150134766Speter if (base != 0) { 150234766Speter str = num + sizeof(num); 150334766Speter *--str = 0; 150434766Speter while (str > num + neg) { 150534766Speter *--str = hexchars[val % base]; 150634766Speter val = val / base; 150734766Speter if (--prec <= 0 && val == 0) 150834766Speter break; 150934766Speter } 151034766Speter switch (neg) { 151134766Speter case 1: 151234766Speter *--str = '-'; 151334766Speter break; 151434766Speter case 2: 151534766Speter *--str = 'x'; 151634766Speter *--str = '0'; 151734766Speter break; 151834766Speter } 151934766Speter len = num + sizeof(num) - 1 - str; 152034766Speter } else { 152134766Speter len = strlen(str); 152234766Speter if (prec > 0 && len > prec) 152334766Speter len = prec; 152434766Speter } 152534766Speter if (width > 0) { 152634766Speter if (width > buflen) 152734766Speter width = buflen; 152834766Speter if ((n = width - len) > 0) { 152934766Speter buflen -= n; 153034766Speter for (; n > 0; --n) 153134766Speter *buf++ = fillch; 153234766Speter } 153334766Speter } 153434766Speter if (len > buflen) 153534766Speter len = buflen; 153634766Speter memcpy(buf, str, len); 153734766Speter buf += len; 153834766Speter buflen -= len; 153934766Speter } 154034766Speter *buf = 0; 154134766Speter return buf - buf0; 154234766Speter} 1543