Bug Summary

File:src/usr.sbin/pppd/chat/chat.c
Warning:line 409, column 5
Address of stack memory associated with local variable 'buf' is still referred to by the static variable 'str' upon returning to the caller. This will be a dangling reference

Annotated Source Code

Press '?' to see keyboard shortcuts

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