[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/miscutils/chat.c b/ap/app/busybox/src/miscutils/chat.c
new file mode 100644
index 0000000..ce994f8
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/chat.c
@@ -0,0 +1,445 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones chat utility
+ * inspired by ppp's chat
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define chat_trivial_usage
+//usage:       "EXPECT [SEND [EXPECT [SEND...]]]"
+//usage:#define chat_full_usage "\n\n"
+//usage:       "Useful for interacting with a modem connected to stdin/stdout.\n"
+//usage:       "A script consists of one or more \"expect-send\" pairs of strings,\n"
+//usage:       "each pair is a pair of arguments. Example:\n"
+//usage:       "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'"
+
+#include "libbb.h"
+
+// default timeout: 45 sec
+#define DEFAULT_CHAT_TIMEOUT 45*1000
+// max length of "abort string",
+// i.e. device reply which causes termination
+#define MAX_ABORT_LEN 50
+
+// possible exit codes
+enum {
+	ERR_OK = 0,     // all's well
+	ERR_MEM,        // read too much while expecting
+	ERR_IO,         // signalled or I/O error
+	ERR_TIMEOUT,    // timed out while expecting
+	ERR_ABORT,      // first abort condition was met
+//	ERR_ABORT2,     // second abort condition was met
+//	...
+};
+
+// exit code
+#define exitcode bb_got_signal
+
+// trap for critical signals
+static void signal_handler(UNUSED_PARAM int signo)
+{
+	// report I/O error condition
+	exitcode = ERR_IO;
+}
+
+#if !ENABLE_FEATURE_CHAT_IMPLICIT_CR
+#define unescape(s, nocr) unescape(s)
+#endif
+static size_t unescape(char *s, int *nocr)
+{
+	char *start = s;
+	char *p = s;
+
+	while (*s) {
+		char c = *s;
+		// do we need special processing?
+		// standard escapes + \s for space and \N for \0
+		// \c inhibits terminating \r for commands and is noop for expects
+		if ('\\' == c) {
+			c = *++s;
+			if (c) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				if ('c' == c) {
+					*nocr = 1;
+					goto next;
+				}
+#endif
+				if ('N' == c) {
+					c = '\0';
+				} else if ('s' == c) {
+					c = ' ';
+#if ENABLE_FEATURE_CHAT_NOFAIL
+				// unescape leading dash only
+				// TODO: and only for expect, not command string
+				} else if ('-' == c && (start + 1 == s)) {
+					//c = '-';
+#endif
+				} else {
+					c = bb_process_escape_sequence((const char **)&s);
+					s--;
+				}
+			}
+		// ^A becomes \001, ^B -- \002 and so on...
+		} else if ('^' == c) {
+			c = *++s-'@';
+		}
+		// put unescaped char
+		*p++ = c;
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ next:
+#endif
+		// next char
+		s++;
+	}
+	*p = '\0';
+
+	return p - start;
+}
+
+int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chat_main(int argc UNUSED_PARAM, char **argv)
+{
+	int record_fd = -1;
+	bool echo = 0;
+	// collection of device replies which cause unconditional termination
+	llist_t *aborts = NULL;
+	// inactivity period
+	int timeout = DEFAULT_CHAT_TIMEOUT;
+	// maximum length of abort string
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+	size_t max_abort_len = 0;
+#else
+#define max_abort_len MAX_ABORT_LEN
+#endif
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	struct termios tio0, tio;
+#endif
+	// directive names
+	enum {
+		DIR_HANGUP = 0,
+		DIR_ABORT,
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+		DIR_CLR_ABORT,
+#endif
+		DIR_TIMEOUT,
+		DIR_ECHO,
+		DIR_SAY,
+		DIR_RECORD,
+	};
+
+	// make x* functions fail with correct exitcode
+	xfunc_error_retval = ERR_IO;
+
+	// trap vanilla signals to prevent process from being killed suddenly
+	bb_signals(0
+		+ (1 << SIGHUP)
+		+ (1 << SIGINT)
+		+ (1 << SIGTERM)
+		+ (1 << SIGPIPE)
+		, signal_handler);
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	tcgetattr(STDIN_FILENO, &tio);
+	tio0 = tio;
+	cfmakeraw(&tio);
+	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
+#endif
+
+#if ENABLE_FEATURE_CHAT_SWALLOW_OPTS
+	getopt32(argv, "vVsSE");
+	argv += optind;
+#else
+	argv++; // goto first arg
+#endif
+	// handle chat expect-send pairs
+	while (*argv) {
+		// directive given? process it
+		int key = index_in_strings(
+			"HANGUP\0" "ABORT\0"
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+			"CLR_ABORT\0"
+#endif
+			"TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0"
+			, *argv
+		);
+		if (key >= 0) {
+			// cache directive value
+			char *arg = *++argv;
+			// OFF -> 0, anything else -> 1
+			bool onoff = (0 != strcmp("OFF", arg));
+			// process directive
+			if (DIR_HANGUP == key) {
+				// turn SIGHUP on/off
+				signal(SIGHUP, onoff ? signal_handler : SIG_IGN);
+			} else if (DIR_ABORT == key) {
+				// append the string to abort conditions
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+				size_t len = strlen(arg);
+				if (len > max_abort_len)
+					max_abort_len = len;
+#endif
+				llist_add_to_end(&aborts, arg);
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+			} else if (DIR_CLR_ABORT == key) {
+				llist_t *l;
+				// remove the string from abort conditions
+				// N.B. gotta refresh maximum length too...
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+				max_abort_len = 0;
+# endif
+				for (l = aborts; l; l = l->link) {
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+					size_t len = strlen(l->data);
+# endif
+					if (strcmp(arg, l->data) == 0) {
+						llist_unlink(&aborts, l);
+						continue;
+					}
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+					if (len > max_abort_len)
+						max_abort_len = len;
+# endif
+				}
+#endif
+			} else if (DIR_TIMEOUT == key) {
+				// set new timeout
+				// -1 means OFF
+				timeout = atoi(arg) * 1000;
+				// 0 means default
+				// >0 means value in msecs
+				if (!timeout)
+					timeout = DEFAULT_CHAT_TIMEOUT;
+			} else if (DIR_ECHO == key) {
+				// turn echo on/off
+				// N.B. echo means dumping device input/output to stderr
+				echo = onoff;
+			} else if (DIR_RECORD == key) {
+				// turn record on/off
+				// N.B. record means dumping device input to a file
+					// close previous record_fd
+				if (record_fd > 0)
+					close(record_fd);
+				// N.B. do we have to die here on open error?
+				record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1;
+			} else if (DIR_SAY == key) {
+				// just print argument verbatim
+				// TODO: should we use full_write() to avoid unistd/stdio conflict?
+				bb_error_msg("%s", arg);
+			}
+			// next, please!
+			argv++;
+		// ordinary expect-send pair!
+		} else {
+			//-----------------------
+			// do expect
+			//-----------------------
+			int expect_len;
+			size_t buf_len = 0;
+			size_t max_len = max_abort_len;
+
+			struct pollfd pfd;
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			int nofail = 0;
+#endif
+			char *expect = *argv++;
+
+			// sanity check: shall we really expect something?
+			if (!expect)
+				goto expect_done;
+
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			// if expect starts with -
+			if ('-' == *expect) {
+				// swallow -
+				expect++;
+				// and enter nofail mode
+				nofail++;
+			}
+#endif
+
+#ifdef ___TEST___BUF___ // test behaviour with a small buffer
+#	undef COMMON_BUFSIZE
+#	define COMMON_BUFSIZE 6
+#endif
+			// expand escape sequences in expect
+			expect_len = unescape(expect, &expect_len /*dummy*/);
+			if (expect_len > max_len)
+				max_len = expect_len;
+			// sanity check:
+			// we should expect more than nothing but not more than input buffer
+			// TODO: later we'll get rid of fixed-size buffer
+			if (!expect_len)
+				goto expect_done;
+			if (max_len >= COMMON_BUFSIZE) {
+				exitcode = ERR_MEM;
+				goto expect_done;
+			}
+
+			// get reply
+			pfd.fd = STDIN_FILENO;
+			pfd.events = POLLIN;
+			while (!exitcode
+			    && poll(&pfd, 1, timeout) > 0
+			    && (pfd.revents & POLLIN)
+			) {
+#define buf bb_common_bufsiz1
+				llist_t *l;
+				ssize_t delta;
+
+				// read next char from device
+				if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) {
+					// dump device input if RECORD fname
+					if (record_fd > 0) {
+						full_write(record_fd, buf+buf_len, 1);
+					}
+					// dump device input if ECHO ON
+					if (echo > 0) {
+//						if (buf[buf_len] < ' ') {
+//							full_write(STDERR_FILENO, "^", 1);
+//							buf[buf_len] += '@';
+//						}
+						full_write(STDERR_FILENO, buf+buf_len, 1);
+					}
+					buf_len++;
+					// move input frame if we've reached higher bound
+					if (buf_len > COMMON_BUFSIZE) {
+						memmove(buf, buf+buf_len-max_len, max_len);
+						buf_len = max_len;
+					}
+				}
+				// N.B. rule of thumb: values being looked for can
+				// be found only at the end of input buffer
+				// this allows to get rid of strstr() and memmem()
+
+				// TODO: make expect and abort strings processed uniformly
+				// abort condition is met? -> bail out
+				for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) {
+					size_t len = strlen(l->data);
+					delta = buf_len-len;
+					if (delta >= 0 && !memcmp(buf+delta, l->data, len))
+						goto expect_done;
+				}
+				exitcode = ERR_OK;
+
+				// expected reply received? -> goto next command
+				delta = buf_len - expect_len;
+				if (delta >= 0 && !memcmp(buf+delta, expect, expect_len))
+					goto expect_done;
+#undef buf
+			} /* while (have data) */
+
+			// device timed out or unexpected reply received
+			exitcode = ERR_TIMEOUT;
+ expect_done:
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			// on success and when in nofail mode
+			// we should skip following subsend-subexpect pairs
+			if (nofail) {
+				if (!exitcode) {
+					// find last send before non-dashed expect
+					while (*argv && argv[1] && '-' == argv[1][0])
+						argv += 2;
+					// skip the pair
+					// N.B. do we really need this?!
+					if (!*argv++ || !*argv++)
+						break;
+				}
+				// nofail mode also clears all but IO errors (or signals)
+				if (ERR_IO != exitcode)
+					exitcode = ERR_OK;
+			}
+#endif
+			// bail out unless we expected successfully
+			if (exitcode)
+				break;
+
+			//-----------------------
+			// do send
+			//-----------------------
+			if (*argv) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				int nocr = 0; // inhibit terminating command with \r
+#endif
+				char *loaded = NULL; // loaded command
+				size_t len;
+				char *buf = *argv++;
+
+				// if command starts with @
+				// load "real" command from file named after @
+				if ('@' == *buf) {
+					// skip the @ and any following white-space
+					trim(++buf);
+					buf = loaded = xmalloc_xopen_read_close(buf, NULL);
+				}
+				// expand escape sequences in command
+				len = unescape(buf, &nocr);
+
+				// send command
+				alarm(timeout);
+				pfd.fd = STDOUT_FILENO;
+				pfd.events = POLLOUT;
+				while (len && !exitcode
+				    && poll(&pfd, 1, -1) > 0
+				    && (pfd.revents & POLLOUT)
+				) {
+#if ENABLE_FEATURE_CHAT_SEND_ESCAPES
+					// "\\d" means 1 sec delay, "\\p" means 0.01 sec delay
+					// "\\K" means send BREAK
+					char c = *buf;
+					if ('\\' == c) {
+						c = *++buf;
+						if ('d' == c) {
+							sleep(1);
+							len--;
+							continue;
+						}
+						if ('p' == c) {
+							usleep(10000);
+							len--;
+							continue;
+						}
+						if ('K' == c) {
+							tcsendbreak(STDOUT_FILENO, 0);
+							len--;
+							continue;
+						}
+						buf--;
+					}
+					if (safe_write(STDOUT_FILENO, buf, 1) != 1)
+						break;
+					len--;
+					buf++;
+#else
+					len -= full_write(STDOUT_FILENO, buf, len);
+#endif
+				} /* while (can write) */
+				alarm(0);
+
+				// report I/O error if there still exists at least one non-sent char
+				if (len)
+					exitcode = ERR_IO;
+
+				// free loaded command (if any)
+				if (loaded)
+					free(loaded);
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				// or terminate command with \r (if not inhibited)
+				else if (!nocr)
+					xwrite(STDOUT_FILENO, "\r", 1);
+#endif
+				// bail out unless we sent command successfully
+				if (exitcode)
+					break;
+			} /* if (*argv) */
+		}
+	} /* while (*argv) */
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
+#endif
+
+	return exitcode;
+}