Bug Summary

File:src/usr.sbin/pppd/chat/chat.c
Warning:line 778, column 10
Dereference of null pointer

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) {
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))
308 report_fp = stderr(&__sF[2]);
309
310 if (to_log) {
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)
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)) {
326 if (argc > 0)
327 usage();
328 else
329 do_file (chat_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))
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)) {
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 == '#' )
372 continue;
373
374 while (*sp != '\0') {
375 if (*sp == ' ' || *sp == '\t') {
376 ++sp;
377 continue;
378 }
379
380 if (*sp == '"' || *sp == '\'') {
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')
396 ++sp;
397 }
398
399 if (*sp != '\0')
400 *sp++ = '\0';
401
402 if (sendflg)
403 chat_send (arg);
404 else
405 chat_expect (arg);
406 sendflg = !sendflg;
407 }
408 }
409 fclose (cfp);
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++)
1
Loop condition is true. Entering loop body
11
Loop condition is false. Execution continues on line 655
609 if (*t == '^' && *(t+1) != '\0') {
2
Assuming the condition is false
610 t++;
611 new_length++;
612 } else if (*t != '\\') {
3
Assuming the condition is false
4
Taking false branch
613 new_length++;
614 } else {
615 t++;
616 switch (*t) {
5
Control jumps to the 'default' case at line 639
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'))) {
6
Assuming the condition is true
7
Assuming the condition is true
8
Taking true branch
641 t++;
642 if (isoctal(*t)(((*t) >= '0') && ((*t) <= '7'))) {
9
Assuming the condition is false
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')
10
Taking false branch
652 break;
653 }
654
655 new_length += 3; /* \r and two nuls */
656
657 ret = malloc(new_length);
658 if (ret == NULL((void *)0))
12
Assuming 'ret' is equal to NULL
13
Taking true branch
659 fatal(2, "cannot allocate memory");
660
661 s1 = ret;
662 while (*s) {
14
Loop condition is true. Entering loop body
663 cur_chr = *s++;
664 if (cur_chr == '^') {
15
Taking false branch
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 != '\\') {
16
Taking false branch
678 *s1++ = cur_chr;
679 continue;
680 }
681
682 cur_chr = *s++;
683 if (cur_chr == '\0') {
17
Taking false branch
684 if (sending) {
685 *s1++ = '\\';
686 *s1++ = '\\';
687 }
688 break;
689 }
690
691 switch (cur_chr) {
18
Control jumps to the 'default' case at line 764
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'))) {
19
Taking true branch
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) {
20
Assuming 'cur_chr' is equal to 0
21
Assuming 'sending' is not equal to 0
22
Taking true branch
777 if (sending
22.1
'sending' is not equal to 0
&& (cur_chr == '\\' || cur_chr
22.2
'cur_chr' is equal to 0
== 0))
23
Taking true branch
778 *s1++ = '\\';
24
Null pointer value stored to 's1'
25
Dereference of null pointer
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}