libbb: introduce and use strcpy_and_process_escape_sequences

function                                             old     new   delta
strcpy_and_process_escape_sequences                    -      50     +50
bb_process_escape_sequence                           148     138     -10
printf_main                                          789     776     -13
getty_main                                          1897    1831     -66
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/3 up/down: 50/-89)            Total: -39 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 2cc2384..c8395fa 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -122,16 +122,14 @@
 	return result;
 }
 
-static void print_esc_string(char *str)
+static void print_esc_string(const char *str)
 {
-	while (*str) {
-		if (*str == '\\') {
-			str++;
-			bb_putchar(bb_process_escape_sequence((const char **)&str));
-		} else {
-			bb_putchar(*str);
-			str++;
-		}
+	char c;
+	while ((c = *str) != '\0') {
+		str++;
+		if (c == '\\')
+			c = bb_process_escape_sequence(&str);
+		putchar(c);
 	}
 }
 
@@ -344,7 +342,7 @@
 			f--;
 			break;
 		default:
-			bb_putchar(*f);
+			putchar(*f);
 		}
 	}
 
diff --git a/e2fsprogs/old_e2fsprogs/fsck.c b/e2fsprogs/old_e2fsprogs/fsck.c
index 524b846..3a0743b 100644
--- a/e2fsprogs/old_e2fsprogs/fsck.c
+++ b/e2fsprogs/old_e2fsprogs/fsck.c
@@ -349,15 +349,7 @@
 	if (!word)
 		return;
 
-	for (p = q = word; *p; q++) {
-		c = *p++;
-		if (c != '\\') {
-			*q = c;
-		} else {
-			*q = bb_process_escape_sequence(&p);
-		}
-	}
-	*q = 0;
+	strcpy_and_process_escape_sequences(word, word);
 }
 
 static void free_instance(struct fsck_instance *i)
diff --git a/include/libbb.h b/include/libbb.h
index 409c434..c85dab2 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -324,6 +324,7 @@
 /* this helper yells "short read!" if param is not -1 */
 extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC;
 extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC;
+char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC;
 /* xxxx_strip version can modify its parameter:
  * "/"        -> "/"
  * "abc"      -> "abc"
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 3fff9f2..9dbfaf5 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -187,19 +187,7 @@
 
 #if 0 /* unused so far */
 		if (flags & PARSE_ESCAPE) {
-			const char *from;
-			char *to;
-
-			from = to = tokens[t];
-			while (*from) {
-				if (*from == '\\') {
-					from++;
-					*to++ = bb_process_escape_sequence(&from);
-				} else {
-					*to++ = *from++;
-				}
-			}
-			*to = '\0';
+			strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
 		}
 #endif
 		/* Skip possible delimiters */
diff --git a/libbb/process_escape_sequence.c b/libbb/process_escape_sequence.c
index 82cbe10..dd6e076 100644
--- a/libbb/process_escape_sequence.c
+++ b/libbb/process_escape_sequence.c
@@ -31,14 +31,13 @@
 	unsigned num_digits;
 	unsigned r;
 	unsigned n;
-	unsigned d;
 	unsigned base;
 
 	num_digits = n = 0;
 	base = 8;
 	q = *ptr;
 
-#ifdef WANT_HEX_ESCAPES
+#if WANT_HEX_ESCAPES
 	if (*q == 'x') {
 		++q;
 		base = 16;
@@ -50,20 +49,21 @@
 	 * \02 works, \2 does not (prints \ and 2).
 	 * We treat \2 as a valid octal escape sequence. */
 	do {
-		d = (unsigned char)(*q) - '0';
-#ifdef WANT_HEX_ESCAPES
-		if (d >= 10) {
-			d = (unsigned char)(_tolower(*q)) - 'a' + 10;
-		}
+#if !WANT_HEX_ESCAPES
+		unsigned d = (unsigned char)(*q) - '0';
+#else
+		unsigned d = (unsigned char)_tolower(*q) - '0';
+		if (d >= 10)
+			d += ('0' - 'a' + 10);
 #endif
-
 		if (d >= base) {
-#ifdef WANT_HEX_ESCAPES
-			if ((base == 16) && (!--num_digits)) {
-/*				return '\\'; */
-				--q;
+			if (WANT_HEX_ESCAPES && base == 16) {
+				--num_digits;
+				if (num_digits == 0) {
+					/* \x<bad_char> */
+					--q; /* go back to x */
+				}
 			}
-#endif
 			break;
 		}
 
@@ -85,7 +85,9 @@
 			}
 		} while (*++p);
 		/* p points to found escape char or NUL,
-		 * advance it and find what it translates to */
+		 * advance it and find what it translates to.
+		 * Note that unrecognized sequence \z returns '\'
+		 * and leaves ptr pointing to z. */
 		p += sizeof(charmap) / 2;
 		n = *p;
 	}
@@ -94,3 +96,17 @@
 
 	return (char) n;
 }
+
+char* FAST_FUNC strcpy_and_process_escape_sequences(char *dst, const char *src)
+{
+	while (1) {
+		char c, c1;
+		c = c1 = *src++;
+		if (c1 == '\\')
+			c1 = bb_process_escape_sequence(&src);
+		*dst = c1;
+		if (c == '\0')
+			return dst;
+		dst++;
+	}
+}
diff --git a/loginutils/getty.c b/loginutils/getty.c
index b1cd235..ab55ea4 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -188,21 +188,9 @@
 		&(op->login), &op->timeout);
 	argv += optind;
 	if (op->flags & F_INITSTRING) {
-		const char *p = op->initstring;
-		char *q;
-
-		op->initstring = q = xstrdup(p);
-		/* copy optarg into op->initstring decoding \ddd
-		   octal codes into chars */
-		while (*p) {
-			if (*p == '\\') {
-				p++;
-				*q++ = bb_process_escape_sequence(&p);
-			} else {
-				*q++ = *p++;
-			}
-		}
-		*q = '\0';
+		op->initstring = xstrdup(op->initstring);
+		/* decode \ddd octal codes into chars */
+		strcpy_and_process_escape_sequences((char*)op->initstring, op->initstring);
 	}
 	op->flags ^= F_ISSUE;           /* invert flag "show /etc/issue" */
 	debug("after getopt\n");