+ return p;
+}
+
+/* The following three functions, btoa_big, otoa_big, and xtoa_big, are needed
+ to convert large reals with kind sizes that exceed the largest integer type
+ available on certain platforms. In these cases, byte by byte conversion is
+ performed. Endianess is taken into account. */
+
+/* Conversion to binary. */
+
+static const char *
+btoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+ char *q;
+ int i, j;
+
+ q = buffer;
+ if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ {
+ const char *p = s;
+ for (i = 0; i < len; i++)
+ {
+ char c = *p;
+
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ for (j = 0; j < 8; j++)
+ {
+ *q++ = (c & 128) ? '1' : '0';
+ c <<= 1;
+ }
+ p++;
+ }
+ }
+ else
+ {
+ const char *p = s + len - 1;
+ for (i = 0; i < len; i++)
+ {
+ char c = *p;
+
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ for (j = 0; j < 8; j++)
+ {
+ *q++ = (c & 128) ? '1' : '0';
+ c <<= 1;
+ }
+ p--;
+ }
+ }
+
+ if (*n == 0)
+ return "0";
+
+ /* Move past any leading zeros. */
+ while (*buffer == '0')
+ buffer++;
+
+ return buffer;
+
+}
+
+/* Conversion to octal. */
+
+static const char *
+otoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+ char *q;
+ int i, j, k;
+ uint8_t octet;
+
+ q = buffer + GFC_OTOA_BUF_SIZE - 1;
+ *q = '\0';
+ i = k = octet = 0;
+
+ if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ {
+ const char *p = s + len - 1;
+ char c = *p;
+ while (i < len)
+ {
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ for (j = 0; j < 3 && i < len; j++)
+ {
+ octet |= (c & 1) << j;
+ c >>= 1;
+ if (++k > 7)
+ {
+ i++;
+ k = 0;
+ c = *--p;
+ }
+ }
+ *--q = '0' + octet;
+ octet = 0;
+ }
+ }
+ else
+ {
+ const char *p = s;
+ char c = *p;
+ while (i < len)
+ {
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ for (j = 0; j < 3 && i < len; j++)
+ {
+ octet |= (c & 1) << j;
+ c >>= 1;
+ if (++k > 7)
+ {
+ i++;
+ k = 0;
+ c = *++p;
+ }
+ }
+ *--q = '0' + octet;
+ octet = 0;
+ }
+ }
+
+ if (*n == 0)
+ return "0";
+
+ /* Move past any leading zeros. */
+ while (*q == '0')
+ q++;
+
+ return q;
+}
+
+/* Conversion to hexadecimal. */
+
+static const char *
+xtoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+ static char a[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ char *q;
+ uint8_t h, l;
+ int i;
+
+ q = buffer;
+
+ if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ {
+ const char *p = s;
+ for (i = 0; i < len; i++)
+ {
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ h = (*p >> 4) & 0x0F;
+ l = *p++ & 0x0F;
+ *q++ = a[h];
+ *q++ = a[l];
+ }
+ }
+ else
+ {
+ const char *p = s + len - 1;
+ for (i = 0; i < len; i++)
+ {
+ /* Test for zero. Needed by write_boz later. */
+ if (*p != 0)
+ *n = 1;
+
+ h = (*p >> 4) & 0x0F;
+ l = *p-- & 0x0F;
+ *q++ = a[h];
+ *q++ = a[l];
+ }
+ }
+
+ /* write_z, which calls xtoa_big, is called from transfer.c,
+ formatted_transfer_scalar_write. There it is passed the kind as
+ argument, which means a maximum of 16. The buffer is large
+ enough, but the compiler does not know that, so shut up the
+ warning here. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+ *q = '\0';
+#pragma GCC diagnostic pop
+
+ if (*n == 0)
+ return "0";
+
+ /* Move past any leading zeros. */
+ while (*buffer == '0')
+ buffer++;
+
+ return buffer;
+}
+
+
+void
+write_i (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
+{
+ write_decimal (dtp, f, p, len);
+}
+
+
+void
+write_b (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
+{
+ const char *p;
+ char itoa_buf[GFC_BTOA_BUF_SIZE];
+ GFC_UINTEGER_LARGEST n = 0;
+
+ /* Ensure we end up with a null terminated string. */
+ memset(itoa_buf, '\0', GFC_BTOA_BUF_SIZE);
+
+ if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+ {
+ p = btoa_big (source, itoa_buf, len, &n);
+ write_boz (dtp, f, p, n, len);
+ }
+ else
+ {
+ n = extract_uint (source, len);
+ p = btoa (n, itoa_buf, sizeof (itoa_buf));
+ write_boz (dtp, f, p, n, len);
+ }
+}
+
+
+void
+write_o (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
+{
+ const char *p;
+ char itoa_buf[GFC_OTOA_BUF_SIZE];
+ GFC_UINTEGER_LARGEST n = 0;
+
+ if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+ {
+ p = otoa_big (source, itoa_buf, len, &n);
+ write_boz (dtp, f, p, n, len);
+ }
+ else
+ {
+ n = extract_uint (source, len);
+ p = otoa (n, itoa_buf, sizeof (itoa_buf));
+ write_boz (dtp, f, p, n, len);
+ }
+}
+
+void
+write_z (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
+{
+ const char *p;
+ char itoa_buf[GFC_XTOA_BUF_SIZE];
+ GFC_UINTEGER_LARGEST n = 0;
+
+ if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+ {
+ p = xtoa_big (source, itoa_buf, len, &n);
+ write_boz (dtp, f, p, n, len);
+ }
+ else
+ {
+ n = extract_uint (source, len);
+ p = xtoa (n, itoa_buf, sizeof (itoa_buf));
+ write_boz (dtp, f, p, n, len);
+ }
+}
+
+/* Take care of the X/TR descriptor. */
+
+void
+write_x (st_parameter_dt *dtp, int len, int nspaces)
+{
+ char *p;
+
+ p = write_block (dtp, len);
+ if (p == NULL)
+ return;
+ if (nspaces > 0 && len - nspaces >= 0)
+ {
+ if (unlikely (is_char4_unit (dtp)))
+ {
+ gfc_char4_t *p4 = (gfc_char4_t *) p;
+ memset4 (&p4[len - nspaces], ' ', nspaces);
+ }
+ else
+ memset (&p[len - nspaces], ' ', nspaces);
+ }
+}
+
+
+/* List-directed writing. */
+
+
+/* Write a single character to the output. Returns nonzero if
+ something goes wrong. */
+
+static int
+write_char (st_parameter_dt *dtp, int c)
+{
+ char *p;
+
+ p = write_block (dtp, 1);
+ if (p == NULL)
+ return 1;
+ if (unlikely (is_char4_unit (dtp)))
+ {
+ gfc_char4_t *p4 = (gfc_char4_t *) p;
+ *p4 = c;
+ return 0;
+ }
+
+ *p = (uchar) c;
+
+ return 0;
+}
+
+
+/* Write a list-directed logical value. */
+
+static void
+write_logical (st_parameter_dt *dtp, const char *source, int length)
+{
+ write_char (dtp, extract_int (source, length) ? 'T' : 'F');
+}
+
+
+/* Write a list-directed integer value. */
+
+static void
+write_integer (st_parameter_dt *dtp, const char *source, int kind)
+{
+ int width;
+ fnode f;
+
+ switch (kind)
+ {
+ case 1:
+ width = 4;
+ break;
+
+ case 2:
+ width = 6;
+ break;
+
+ case 4:
+ width = 11;
+ break;
+
+ case 8:
+ width = 20;
+ break;
+
+ case 16:
+ width = 40;
+ break;
+
+ default:
+ width = 0;
+ break;
+ }
+ f.u.integer.w = width;
+ f.u.integer.m = -1;
+ f.format = FMT_NONE;
+ write_decimal (dtp, &f, source, kind);
+}
+
+
+/* Write a list-directed string. We have to worry about delimiting
+ the strings if the file has been opened in that mode. */
+
+#define DELIM 1
+#define NODELIM 0
+
+static void
+write_character (st_parameter_dt *dtp, const char *source, int kind, size_t length, int mode)
+{
+ size_t extra;
+ char *p, d;
+
+ if (mode == DELIM)
+ {
+ switch (dtp->u.p.current_unit->delim_status)
+ {
+ case DELIM_APOSTROPHE:
+ d = '\'';
+ break;
+ case DELIM_QUOTE:
+ d = '"';
+ break;
+ default:
+ d = ' ';
+ break;
+ }
+ }
+ else
+ d = ' ';
+
+ if (kind == 1)
+ {
+ if (d == ' ')
+ extra = 0;
+ else
+ {
+ extra = 2;
+
+ for (size_t i = 0; i < length; i++)
+ if (source[i] == d)
+ extra++;
+ }
+
+ p = write_block (dtp, length + extra);
+ if (p == NULL)
+ return;
+
+ if (unlikely (is_char4_unit (dtp)))
+ {
+ gfc_char4_t d4 = (gfc_char4_t) d;
+ gfc_char4_t *p4 = (gfc_char4_t *) p;
+
+ if (d4 == ' ')
+ memcpy4 (p4, source, length);
+ else
+ {
+ *p4++ = d4;
+
+ for (size_t i = 0; i < length; i++)
+ {
+ *p4++ = (gfc_char4_t) source[i];
+ if (source[i] == d)
+ *p4++ = d4;
+ }
+
+ *p4 = d4;
+ }
+ return;
+ }
+
+ if (d == ' ')
+ memcpy (p, source, length);
+ else
+ {
+ *p++ = d;
+
+ for (size_t i = 0; i < length; i++)
+ {
+ *p++ = source[i];
+ if (source[i] == d)
+ *p++ = d;
+ }
+
+ *p = d;
+ }
+ }
+ else
+ {
+ if (d == ' ')
+ {
+ if (dtp->u.p.current_unit->flags.encoding == ENCODING_UTF8)
+ write_utf8_char4 (dtp, (gfc_char4_t *) source, length, 0);
+ else
+ write_default_char4 (dtp, (gfc_char4_t *) source, length, 0);
+ }
+ else
+ {
+ p = write_block (dtp, 1);
+ *p = d;
+
+ if (dtp->u.p.current_unit->flags.encoding == ENCODING_UTF8)
+ write_utf8_char4 (dtp, (gfc_char4_t *) source, length, 0);
+ else
+ write_default_char4 (dtp, (gfc_char4_t *) source, length, 0);
+
+ p = write_block (dtp, 1);
+ *p = d;
+ }
+ }
+}
+
+/* Floating point helper functions. */
+
+#define BUF_STACK_SZ 384
+
+static int
+get_precision (st_parameter_dt *dtp, const fnode *f, const char *source, int kind)
+{
+ if (f->format != FMT_EN)
+ return determine_precision (dtp, f, kind);
+ else
+ return determine_en_precision (dtp, f, source, kind);
+}
+
+/* 4932 is the maximum exponent of long double and quad precision, 3
+ extra characters for the sign, the decimal point, and the
+ trailing null. Extra digits are added by the calling functions for
+ requested precision. Likewise for float and double. F0 editing produces
+ full precision output. */
+static int
+size_from_kind (st_parameter_dt *dtp, const fnode *f, int kind)
+{
+ int size;
+
+ if ((f->format == FMT_F && f->u.real.w == 0) || f->u.real.w == DEFAULT_WIDTH)
+ {
+ switch (kind)
+ {
+ case 4:
+ size = 38 + 3; /* These constants shown for clarity. */
+ break;
+ case 8:
+ size = 308 + 3;
+ break;
+ case 10:
+ size = 4932 + 3;
+ break;
+ case 16:
+#ifdef HAVE_GFC_REAL_17
+ case 17:
+#endif
+ size = 4932 + 3;
+ break;
+ default:
+ internal_error (&dtp->common, "bad real kind");
+ break;
+ }
+ }
+ else
+ size = f->u.real.w + 1; /* One byte for a NULL character. */
+
+ return size;