+/* Make printf-type calls using arguments supplied from the host. We
+ need to parse the format string ourselves, and call the formatting
+ function with one argument at a time, partly because there is no
+ safe portable way to construct a varargs call, and partly to serve
+ as a security barrier against bad format strings that might get
+ in. */
+
+static void
+ax_printf (CORE_ADDR fn, CORE_ADDR chan, const char *format,
+ int nargs, ULONGEST *args)
+{
+ const char *f = format;
+ int i;
+ const char *current_substring;
+ int nargs_wanted;
+
+ ax_debug ("Printf of \"%s\" with %d args", format, nargs);
+
+ format_pieces fpieces (&f);
+
+ nargs_wanted = 0;
+ for (auto &&piece : fpieces)
+ if (piece.argclass != literal_piece)
+ ++nargs_wanted;
+
+ if (nargs != nargs_wanted)
+ error (_("Wrong number of arguments for specified format-string"));
+
+ i = 0;
+ for (auto &&piece : fpieces)
+ {
+ current_substring = piece.string;
+ ax_debug ("current substring is '%s', class is %d",
+ current_substring, piece.argclass);
+ switch (piece.argclass)
+ {
+ case string_arg:
+ {
+ gdb_byte *str;
+ CORE_ADDR tem;
+ int j;
+
+ tem = args[i];
+ if (tem == 0)
+ {
+ printf (current_substring, "(null)");
+ break;
+ }
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0;; j++)
+ {
+ gdb_byte c;
+
+ read_inferior_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (gdb_byte *) alloca (j + 1);
+ if (j != 0)
+ read_inferior_memory (tem, str, j);
+ str[j] = 0;
+
+ printf (current_substring, (char *) str);
+ }
+ break;
+
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error (_("long long not supported in agent printf"));
+#endif
+ case int_arg:
+ {
+ int val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case long_arg:
+ {
+ long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ printf (current_substring, 0);
+ break;
+
+ default:
+ error (_("Format directive in '%s' not supported in agent printf"),
+ current_substring);
+ }
+
+ /* Maybe advance to the next argument. */
+ if (piece.argclass != literal_piece)
+ ++i;
+ }
+
+ fflush (stdout);
+}
+