+ static int can_convert = -1;
+
+ int assigned = 0;
+ int consumed = 0;
+
+ T((T_CALLED("vsscanf(%s,%s,...)"),
+ _nc_visbuf2(1, str),
+ _nc_visbuf2(2, format)));
+
+ /*
+ * This relies on having a working "%n" format conversion. Check if it
+ * works. Only very old C libraries do not support it.
+ *
+ * FIXME: move this check into the configure script.
+ */
+ if (can_convert < 0) {
+ int check1;
+ int check2;
+ if (sscanf("123", "%d%n", &check1, &check2) > 0
+ && check1 == 123
+ && check2 == 3) {
+ can_convert = 1;
+ } else {
+ can_convert = 0;
+ }
+ }
+
+ if (can_convert) {
+ size_t len_fmt = strlen(format) + 32;
+ char *my_fmt = malloc(len_fmt);
+ ChunkType chunk, ctest;
+ OtherType other, otest;
+ ScanState state;
+ unsigned n;
+ int eaten;
+ void *pointer;
+
+ if (my_fmt != 0) {
+ /*
+ * Split the original format into chunks, adding a "%n" to the end
+ * of each (except of course if it used %n), and use that
+ * information to decide where to start scanning the next chunk.
+ *
+ * FIXME: does %n count bytes or characters? If the latter, this
+ * will require further work for multibyte strings.
+ */
+ while (*format != '\0') {
+ /* find a chunk */
+ state = sUnknown;
+ chunk = cUnknown;
+ other = oUnknown;
+ pointer = 0;
+ for (n = 0; format[n] != 0 && state != sFinal; ++n) {
+ my_fmt[n] = format[n];
+ switch (state) {
+ case sUnknown:
+ if (format[n] == '%')
+ state = sPercent;
+ break;
+ case sPercent:
+ if (format[n] == '%') {
+ state = sUnknown;
+ } else if (format[n] == L_SQUARE) {
+ state = sLeft;
+ } else {
+ state = sNormal;
+ --n;
+ }
+ break;
+ case sLeft:
+ state = sRange;
+ if (format[n] == '^') {
+ ++n;
+ my_fmt[n] = format[n];
+ }
+ break;
+ case sRange:
+ if (format[n] == R_SQUARE) {
+ state = sFinal;
+ chunk = cRange;
+ }
+ break;
+ case sNormal:
+ if (format[n] == '*') {
+ state = sUnknown;
+ } else {
+ if ((ctest = final_ch(format[n], other)) != cUnknown) {
+ state = sFinal;
+ chunk = ctest;
+ } else if ((otest = other_ch(format[n])) != oUnknown) {
+ other = otest;
+ } else if (isalpha(UChar(format[n]))) {
+ state = sFinal;
+ chunk = cError;
+ }
+ }
+ break;
+ case sFinal:
+ break;
+ }
+ }
+ my_fmt[n] = '\0';
+ format += n;
+
+ if (chunk == cUnknown
+ || chunk == cError) {
+ if (assigned == 0)
+ assigned = EOF;
+ break;
+ }
+
+ /* add %n, if the format was not that */
+ if (chunk != cAssigned) {
+ strcat(my_fmt, "%n");
+ }
+
+ switch (chunk) {
+ case cAssigned:
+ strcat(my_fmt, "%n");
+ pointer = &eaten;
+ break;
+ case cInt:
+ pointer = va_arg(ap, int *);
+ break;
+ case cShort:
+ pointer = va_arg(ap, short *);
+ break;
+ case cFloat:
+ pointer = va_arg(ap, float *);
+ break;
+ case cDouble:
+ pointer = va_arg(ap, double *);
+ break;
+ case cLong:
+ pointer = va_arg(ap, long *);
+ break;
+ case cPointer:
+ pointer = va_arg(ap, void *);
+ break;
+ case cChar:
+ case cRange:
+ case cString:
+ pointer = va_arg(ap, char *);
+ break;
+ case cError:
+ case cUnknown:
+ break;
+ }
+ /* do the conversion */
+ T(("...converting chunk #%d type %d(%s,%s)",
+ assigned + 1, chunk,
+ _nc_visbuf2(1, str + consumed),
+ _nc_visbuf2(2, my_fmt)));
+ if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
+ consumed += eaten;
+ else
+ break;
+ ++assigned;
+ }
+ free(my_fmt);
+ }
+ }
+ returnCode(assigned);