Scanf зарегистрировать новый спецификатор преобразования - PullRequest
3 голосов
/ 10 мая 2019

На этой неделе я написал расширение для семейства функций printf, которое принимает %b для печати двоичного файла.Для этого я использовал функцию register_printf_specifier().

. Теперь мне интересно, могу ли я сделать то же самое в семействе функций scanf, чтобы принять двоичный ввод и записать его в переменную.

Есть ли расширение, позволяющее мне это сделать?

1 Ответ

1 голос
/ 11 мая 2019

TL; DR: Нет. По крайней мере, нет при использовании glibc.


Я скачал последнюю glibc версию:

% wget https://ftp.gnu.org/gnu/glibc/glibc-2.29.tar.gz
% tar -xzf glibc-2.29.tar.gz

И grep 'ed find, ища случайную scanf семейную функцию, которая пришла мне в голову - в данном случае это было vfscanf:

% find | grep "vfscanf"

Из моего опыта я знаючто реальные реализации находятся где-то в -internal, но я просмотрел вывод:

./stdio-common/iovfscanf.c
./stdio-common/isoc99_vfscanf.c
./stdio-common/vfscanf-internal.c
./stdio-common/vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-isoc99_vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c

И решил проверить ./stdio-common/vfscanf.c, что на самом деле содержит заглушку для внутренней функции:

% cat ./stdio-common/vfscanf.c

int
___vfscanf (FILE *s, const char *format, va_list argptr)
{
  return __vfscanf_internal (s, format, argptr, 0);
}

В дальнейшем я просмотрел файл и достиг синтаксического анализатора формата:

% cat ./stdio-common/vfscanf-internal.c | head -n 1390 | tail -n 20
          }
          break;

        case L_('x'):   /* Hexadecimal integer.  */
        case L_('X'):   /* Ditto.  */
          base = 16;
          goto number;

        case L_('o'):   /* Octal integer.  */
          base = 8;
          goto number;

        case L_('u'):   /* Unsigned decimal integer.  */
          base = 10;
          goto number;

        case L_('d'):   /* Signed decimal integer.  */
          base = 10;
          flags |= NUMBER_SIGNED;
          goto number;

Я просмотрел конец файла и обнаружил метку завершающего регистра:

% cat ./stdio-common/vfscanf-internal.c | tail -n 60
                  ++done;
                }
            }
          break;

        case L_('p'):   /* Generic pointer.  */
          base = 16;
          /* A PTR must be the same size as a `long int'.  */
          flags &= ~(SHORT|LONGDBL);
          if (need_long)
            flags |= LONG;
          flags |= READ_POINTER;
          goto number;

        default:
          /* If this is an unknown format character punt.  */
          conv_error ();
        }
    }

  /* The last thing we saw int the format string was a white space.
     Consume the last white spaces.  */
  if (skip_space)
    {
      do
        c = inchar ();
      while (ISSPACE (c));
      ungetc (c, s);
    }

 errout:
  /* Unlock stream.  */
  UNLOCK_STREAM (s);

  scratch_buffer_free (&charbuf.scratch);

  if (__glibc_unlikely (done == EOF))
    {
      if (__glibc_unlikely (ptrs_to_free != NULL))
        {
          struct ptrs_to_free *p = ptrs_to_free;
          while (p != NULL)
            {
              for (size_t cnt = 0; cnt < p->count; ++cnt)
                {
                  free (*p->ptrs[cnt]);
                  *p->ptrs[cnt] = NULL;
                }
              p = p->next;
              ptrs_to_free = p;
            }
        }
    }
  else if (__glibc_unlikely (strptr != NULL))
    {
      free (*strptr);
      *strptr = NULL;
    }
  return done;
}

И код, который завершил функцию.Это означает, что все спецификаторы формата постоянны для одной из scanf -семейных функций, и это означает, что вы не можете зарегистрировать новый обработчик, не связавшись с большим clusterf..k в источнике glibc (который, конечно, не будет переносимым).

...