<commit_before>#include <stdbool.h>

#include "conv.h"
#include "sprintf.h"

// An output buffer cursor, used by sprintf and buffwrite to keep
// track of the write pointer and the space remaining.

typedef struct {
  char * p;
  int remaining;
} buffer_cursor_t;

// Write a character to a buffer.  If the buffer only has room for one
// more character, write a '\0' instead.  If the buffer is full, do
// nothing.

static void buffwrite(buffer_cursor_t * cursor, char c) {
  if(cursor->remaining <= 0)
    return;
  if(cursor->remaining == 1)
    c = '\0';
  *(cursor->p)++ = c;
  cursor->remaining--;
}

// Format a signed integer.

static void
vprintf_signed(vprintf_sink * sink,
                void * o,
                va_list varargs) {
  int n = va_arg(varargs, int);
  char buffer[ITODEC_BUFF_LEN];
  char * p = itodec(buffer, n);
  while(*p != '\0')
    sink(o, *p++);
}

// Format an unsigned integer;

static void
vprintf_unsigned(vprintf_sink * sink,
                void * o,
                va_list varargs) {
  int n = va_arg(varargs, int);
  char buffer[UTODEC_BUFF_LEN];
  char * p = utodec(buffer, n);
  while(*p != '\0')
    sink(o, *p++);
}

// Format a string.

static void
vprintf_string(vprintf_sink * sink,
               void * o,
               va_list varargs) {
  char * s = va_arg(varargs, char *);
  while(*s != '\0')
    sink(o, *s++);
}

// Format a directive.  Returns the new formatp.

static const char * 
vprintf_directive(vprintf_sink * sink,
                  void * o,
                  const char * formatp,
                  va_list varargs) {
  char c = *formatp++;
  switch(c) {
  case '\0':
    formatp--;
    sink(o, '%');
    break;
  case '%':
    sink(o, c);
    break;
  case 'd':
    vprintf_signed(sink, o, varargs);
    break;
  case 's':
    vprintf_string(sink, o, varargs);
    break;
  case 'u':
    vprintf_unsigned(sink, o, varargs);
    break;
  default:
    sink(o, '%');
    sink(o, c);
  }
  return formatp;
}

// Format taking a sink and va_list.
// Format specifiers:
//   %% - Print a percent sign
//   %d - Format decimal
//   %s - Format string

void vprintf(vprintf_sink * sink,
             void * o,
             const char * format,
             va_list varargs) {
  const char * formatp = format;
  for(;;) {
    char c = *formatp++;
    if(c == '%')
      formatp = vprintf_directive(sink, o, formatp, varargs);
    else {
      sink(o, c);<commit_msg>sprintf and friends handle multiple values properly<commit_after>#include <stdbool.h>

#include "conv.h"
#include "sprintf.h"

// An output buffer cursor, used by sprintf and buffwrite to keep
// track of the write pointer and the space remaining.

typedef struct {
  char * p;
  int remaining;
} buffer_cursor_t;

// Write a character to a buffer.  If the buffer only has room for one
// more character, write a '\0' instead.  If the buffer is full, do
// nothing.

static void buffwrite(buffer_cursor_t * cursor, char c) {
  if(cursor->remaining <= 0)
    return;
  if(cursor->remaining == 1)
    c = '\0';
  *(cursor->p)++ = c;
  cursor->remaining--;
}

// Format a signed integer.

static void
vprintf_signed(vprintf_sink * sink,
                void * o,
                va_list *varargs) {
  int n = va_arg(*varargs, int);
  char buffer[ITODEC_BUFF_LEN];
  char * p = itodec(buffer, n);
  while(*p != '\0')
    sink(o, *p++);
}

// Format an unsigned integer;

static void
vprintf_unsigned(vprintf_sink * sink,
                void * o,
                va_list *varargs) {
  int n = va_arg(*varargs, int);
  char buffer[UTODEC_BUFF_LEN];
  char * p = utodec(buffer, n);
  while(*p != '\0')
    sink(o, *p++);
}

// Format a string.

static void
vprintf_string(vprintf_sink * sink,
               void * o,
               va_list *varargs) {
  char * s = va_arg(*varargs, char *);
  while(*s != '\0')
    sink(o, *s++);
}

// Format a directive.  Returns the new formatp.

static const char * 
vprintf_directive(vprintf_sink * sink,
                  void * o,
                  const char * formatp,
                  va_list *varargs) {
  char c = *formatp++;
  switch(c) {
  case '\0':
    formatp--;
    sink(o, '%');
    break;
  case '%':
    sink(o, c);
    break;
  case 'd':
    vprintf_signed(sink, o, varargs);
    break;
  case 's':
    vprintf_string(sink, o, varargs);
    break;
  case 'u':
    vprintf_unsigned(sink, o, varargs);
    break;
  default:
    sink(o, '%');
    sink(o, c);
  }
  return formatp;
}

// Format taking a sink and va_list.
// Format specifiers:
//   %% - Print a percent sign
//   %d - Format decimal
//   %s - Format string

void vprintf(vprintf_sink * sink,
             void * o,
             const char * format,
             va_list varargs) {
  const char * formatp = format;
  for(;;) {
    char c = *formatp++;
    if(c == '%')
      formatp = vprintf_directive(sink, o, formatp, &varargs);
    else {
      sink(o, c);
{'fraction_non_alphanumeric': 0.1404081632653061, 'fraction_numerical': 0.0032653061224489797, 'mean_word_length': 2.2782608695652176, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5574852', 'n_tokens_mistral': 1746, 'n_tokens_neox': 1738, 'n_words': 501}