+/* --- @caps_padchars@ --- *
+ *
+ * Arguments: @struct tty *tty@ = extended control block pointer
+ * @unsigned delay@ = tenths of milliseconds required
+ * @unsigned f@ = flags
+ *
+ * Returns: The number of padding characters to send.
+ *
+ * Use: Determine the number of padding characters to send to achieve
+ * a delay of a given duration. If @CPF_FORCE@ is set, then
+ * ignore the @pb@ and @xon@ capabilities.
+ */
+
+#define CPF_FORCE 1u
+static size_t caps_padchars(struct tty *tty, unsigned delay, unsigned f)
+{
+ struct tty_caps *t = (struct tty_caps *)tty;
+
+ if (!(f&CPF_FORCE) && (t->tty.baud < t->cap.pb || t->cap.xon)) {
+ /* We're not forced to pad, and the baud rate is sufficiently low or we
+ * have flow control, then there's nothing to do.
+ */
+
+ return (0);
+ } else {
+ /* We're transmitting at R b/s, and we want to send N B of data so that
+ * this takes D/10000 s. We're likely sending either seven bits with
+ * parity or eight bits without, plus one stop bit, per character, so we
+ * must send 9 N b, which will take 9 N/R s = D/10000 s. Rearranging
+ * gives
+ *
+ * N = R D/90000 ,
+ *
+ * and we should round upwards.
+ */
+
+ return ((unsigned long)t->tty.baud*delay + 89999/90000);
+ }
+}
+
+#if defined(HAVE_TERMCAP) || defined(HAVE_TERMINFO)
+
+/* --- @caps_cost@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @unsigned npad@ = number of lines affected
+ * @const char *ctrl@ = formatted control string
+ *
+ * Returns: A linear `cost' for sending the capability.
+ *
+ * Use: Determines the cost for a `termcap' or `terminfo' capability
+ * by parsing a @tputs@-format string.
+ */
+
+static unsigned scan_delay(const char **p_inout, unsigned npad)
+{
+ const char *p = *p_inout;
+ unsigned t, f;
+
+#define f_padmul 1u
+
+ f = 0; t = 0;
+ while (ISDIGIT(*p)) t = 10*t + (*p++ - '0');
+ t *= 10;
+ if (*p == '.') {
+ p++;
+ if (ISDIGIT(*p)) {
+ t += *p++ - '0';
+ while (ISDIGIT(*p)) p++;
+ }
+ }
+ for (;;)
+ switch (*p) {
+ case '*': p++; f |= f_padmul; break;
+ case '/': p++; break;
+ default: goto done;
+ }
+done:
+ if (f&f_padmul) t *= npad;
+ *p_inout = p; return (npad);
+
+#undef f_padmul
+}
+
+static size_t caps_cost(struct tty *tty, unsigned npad, const char *ctrl)
+{
+ size_t n;
+ unsigned t;
+ const char *p;
+
+ p = ctrl;
+ if (ISDIGIT(*p)) {
+ /* The string starts with a number, so it's a `termcap'-style string with
+ * a leading millisecond count.
+ */
+
+ t = scan_delay(&p, npad);
+ n = strlen(p);
+ } else {
+ /* No initial number. Search for `terminfo'-style %|$<NNN.N[*|/]|%
+ * droppings.
+ */
+
+ n = 0; t = 0;
+ while (*p)
+ if (*p != '$' || p[1] != '<' || (!ISDIGIT(p[2] && p[2] != '>')))
+ { n++; p++; }
+ else {
+ p += 2; t += scan_delay(&p, npad);
+ if (*p == '>') p++;
+ }
+ }
+
+ /* All done. */
+ return (n + caps_padchars(tty, t, CPF_FORCE));
+}
+
+#endif
+
+/* Macros for formatting capabilities. The @...V@ macros evaluate the cap
+ * name, while the unmarked macros interpret it as a slot name.
+ */