+ info_rdaddc(text, '\n');
+}
+
+/*
+ * These functions implement my wrapper on the rdadd* calls which
+ * allows me to switch arbitrarily between literal octet-string
+ * text and charset-translated Unicode. (Because no matter what
+ * character set I write the actual text in, I expect info readers
+ * to treat node names and file names literally and to expect
+ * keywords like `*Note' in their canonical form, so I have to take
+ * steps to ensure that those structural elements of the file
+ * aren't messed with.)
+ */
+static int info_rdadds(info_data *d, wchar_t const *wcs)
+{
+ if (!d->wcmode) {
+ d->state = charset_init_state;
+ d->wcmode = TRUE;
+ }
+
+ if (wcs) {
+ char buf[256];
+ int len, origlen, ret;
+
+ origlen = len = ustrlen(wcs);
+ while (len > 0) {
+ int prevlen = len;
+
+ ret = charset_from_unicode(&wcs, &len, buf, lenof(buf),
+ d->charset, &d->state, NULL);
+
+ assert(len < prevlen);
+
+ if (ret > 0) {
+ buf[ret] = '\0';
+ rdaddsc(&d->output, buf);
+ }
+ }
+
+ return origlen;
+ } else
+ return 0;
+}
+
+static int info_rdaddsc(info_data *d, char const *cs)
+{
+ if (d->wcmode) {
+ char buf[256];
+ int ret;
+
+ ret = charset_from_unicode(NULL, 0, buf, lenof(buf),
+ d->charset, &d->state, NULL);
+ if (ret > 0) {
+ buf[ret] = '\0';
+ rdaddsc(&d->output, buf);
+ }
+
+ d->wcmode = FALSE;
+ }
+
+ if (cs) {
+ rdaddsc(&d->output, cs);
+ return strlen(cs);
+ } else
+ return 0;
+}
+
+static int info_rdadd(info_data *d, wchar_t wc)
+{
+ wchar_t wcs[2];
+ wcs[0] = wc;
+ wcs[1] = L'\0';
+ return info_rdadds(d, wcs);
+}
+
+static int info_rdaddc(info_data *d, char c)
+{
+ char cs[2];
+ cs[0] = c;
+ cs[1] = '\0';
+ return info_rdaddsc(d, cs);