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