aboutsummaryrefslogblamecommitdiffstats
path: root/src/stdio/printf.c
blob: 53be281863d6cabf2b765de768021a2aa899e4fb (plain) (tree)

























                                                                        



















































                                                                                         
                                                              
     
               






                      

                                                






                                            
  























































































                                                                                       
                                                  


















                                                                          
                                                  


















                                                                          

































                                                                      
                                                                          
 
                          

















                                                                               
                                                                                     
            

                                             
     





                                                                               






                                                                        
                                             

















                                                                                   
                                                                                     
               
  
                                             
     





                                                                                  






                                                                        
                                             

























                                                                    

                  

























                                                                    

                  


























                                                                    

                  


























                                                                             

                  







                                                     






                                                    
                                                       













                                                                    

                  







                                                                   
                                           
                                              
   
                                       

                                     





























                                                                            












                                                                    

                  


























                                                                    

                  





























                                                                                  

                  














































                                                                      

                  
























                                                                         

                  
























                                                                    

                  































                                                                              

                  







                                                                                       
                                           















                                                                    

                  







                                                               





                                                         
                                                       













                                                                    

                  








                                                                             















                                                                    

                  



























                                                                              

                  





























                                                                                            

                  

















































                                                                                

                  






                                                                          
                   
























                                                                    

                  
























                                                                    

                  


























                                                                        

                  


























                                                                                 

                  







                                                         





                                                    
                                                       













                                                                    

                  







                                                                       


















                                                                    

                  





























                                                                                        

                  















































                                                                             

                  
























                                                                             

                  
























                                                                    

                  































                                                                                  

                  


                                                                                           
                                          



   
                                            

















                                                                    

                  







                                                                   





                                                         
                                                       













                                                                    

                  








                                                                                 


















                                                                    

                  






























                                                                                                  

                  


















































                                                                                       

                  






                                                                              
                   






                                        
/**
 * slibc — Yet another C library
 * Copyright © 2015  Mattias Andrée (maandree@member.fsf.org)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <slibc-print.h>
#include <inttypes.h>
#include <unistd.h>
#include <slibc-alloc.h>
#include <string.h>
#include <wchar.h>


#define INT_MAX  0x7FFFFFFF  /* TODO temporary */

#define V(C)			\
  int r;			\
  va_list args;			\
  va_start(args, format);	\
  r = v##C;			\
  va_end(args);			\
  return r

#define P_CHAR(UNDERLAYING, MAXIMUM, LIMITED, TERMINATE, DATA)				\
  size_t length;									\
  int r = vgeneric_printf((generic_printf_write_func_t)UNDERLAYING, NULL,		\
                          MAXIMUM, LIMITED, &length, TERMINATE, DATA, format, args);	\
  return r < 0 ? -1 : length < INT_MAX ? (int)length : INT_MAX

#define P_WCHAR(UNDERLAYING, MAXIMUM, LIMITED, TERMINATE, DATA)				\
  size_t length;									\
  int r = vgeneric_wprintf((generic_wprintf_write_func_t)UNDERLAYING, NULL,		\
                           MAXIMUM, LIMITED, &length, TERMINATE, DATA, format, args);	\
  return r < 0 ? -1 : length < INT_MAX ? (int)length : INT_MAX

#define FLOCK(F)    /* TODO lock stream */
#define FUNLOCK(F)  /* TODO unlock stream */



/**
 * Buffer information.
 */
struct buffer
{
  /**
   * The buffer.
   */
  union
  {
    /**
     * Byte-oriented buffer.
     */
    char* str;
    
    /**
     * Wide character-oriented buffer.
     */
    wchar_t* wcs;
    
  } buf;
  
  /**
   * Pointer to the size of the buffer, in number of elements.
   */
  size_t* size;
  
  /**
   * The write offset.
   */
  size_t off;
  
  /**
   * Whether `EXTALLOC_CLEAR`, `secure_realloc`,
   * or `secure_free` shall be used.
   */
  int secure;
  
  /**
   * Whether `.buf` shall be freed on error.
   */
  int free_on_error;
  
};


/**
 * Write a string segment to a buffer.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   buffer  Pointer to the output buffer, will be
 *                  updated to point to the end of the write.
 * @return          Zero on success, -1 on error.
 *                  This function is always successful.
 */
static int write_string(const char* text, size_t length, char* restrict* buffer)
{
  memcpy(*buffer, text, length);
  (*buffer) += length;
  return 0;
}


/**
 * Write a string segment to a buffer.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   buffer  Pointer to the output buffer, will be
 *                  updated to point to the end of the write.
 * @return          Zero on success, -1 on error.
 *                  This function is always successful.
 */
static int wwrite_string(const wchar_t* text, size_t length, wchar_t* restrict* buffer)
{
  wmemcpy(*buffer, text, length);
  (*buffer) += length;
  return 0;
}


/**
 * Write a string segment to a file.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   fdp     Pointer to the file descriptor of the file.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  Any error specified for `write`.
 */
static int write_fd(const char* text, size_t length, int* fdp)
{
  /* TODO write_fd  */
  /*
  ssize_t wrote;
  size_t ptr = 0;
  while (ptr < length)
    {
      wrote = write(*fdp, text + ptr, length - ptr);
      if (wrote < 0)
	return -1;
      ptr += (size_t)wrote;
    }
  return 0;
  */
  return 0;
  (void) text, (void) length, (void) fdp;
}


/**
 * Write a string segment to a file.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   fdp     Pointer to the file descriptor of the file.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  Any error specified for `write`.
 */
static int wwrite_fd(const wchar_t* text, size_t length, int* fdp)
{
  /* TODO wwrite_fd  */
  return 0;
  (void) text, (void) length, (void) fdp;
}


/**
 * Write a string segment to a socket with `send`.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   input   Element 0: pointer to the file descriptor of the file.
 *                  Element 1: flags for `send`.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  Any error specified for `write`.
 */
static int send_fd(const char* text, size_t length, int* input)
{
  int fd = input[0], flags = input[1];
  /* TODO send_fd  */
  return 0;
  (void) text, (void) length, (void) fd, (void) flags;
}


/**
 * Write a string segment to a socket with `send`.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   input   Element 0: pointer to the file descriptor of the file.
 *                  Element 1: flags for `send`.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  Any error specified for `write`.
 */
static int wsend_fd(const wchar_t* text, size_t length, int* input)
{
  int fd = input[0], flags = input[1];
  /* TODO wsend_fd  */
  return 0;
  (void) text, (void) length, (void) fd, (void) flags;
}


/**
 * Write a string segment to a stream.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   stream  The output stream.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  Any error specified for `fwrite_unlocked`.
 */
static int write_stream(const char* text, size_t length, FILE* stream)
{
  /* TODO write_stream */
  /*
  size_t wrote = fwrite_unlocked(text, 1, length, stream);
  return wrote == length ? 0 : -1;
  */
  return 0;
  (void) text, (void) length, (void) stream;
}


/**
 * Write a string segment to a stream.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   stream  The output stream.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @throws  Any error specified for `fwrite_unlocked`.
 */
static int wwrite_stream(const wchar_t* text, size_t length, FILE* stream)
{
  /* TODO wwrite_stream */
  return 0;
  (void) text, (void) length, (void) stream;
}


/**
 * Write a string segment to a buffer and reallocate it necessary.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   buffer  Information about the buffer.
 * @return          Zero on success, -1 on error.
 * 
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 */
static int write_buffer(const char* text, size_t length, struct buffer* buffer)
{
  enum extalloc_mode flags = EXTALLOC_MALLOC | (buffer->secure ? EXTALLOC_CLEAR : 0);
  char* new;
  
  if (buffer->off + length > *(buffer->size))
    {
      if (buffer->off || !*(buffer->size))
	new = (buffer->secure ? secure_realloc : fast_realloc)
	  (buffer->buf.str, *(buffer->size) * sizeof(char));
      else
	new = extalloc(buffer->buf.str, *(buffer->size) * sizeof(char), flags);
      
      if (new == NULL)
	{
	  if (buffer->free_on_error)
	    (buffer->secure ? secure_free : fast_free)(buffer->buf.str),
	      buffer->buf.str = NULL;
	  return -1;
	}
      *(buffer->size) = buffer->off + length;
      buffer->buf.str = new;
    }
  memcpy(buffer->buf.str, text, length);
  buffer->off += length;
  return 0;
}


/**
 * Write a string segment to a buffer and reallocate it necessary.
 * 
 * @param   text    The text to write, not NUL terminated.
 * @param   length  The length of `text`.
 * @param   buffer  Information about the buffer.
 * @return          Zero on success, -1 on error.
 */
static int wwrite_buffer(const wchar_t* text, size_t length, struct buffer* buffer)
{
  enum extalloc_mode flags = EXTALLOC_MALLOC | (buffer->secure ? EXTALLOC_CLEAR : 0);
  wchar_t* new;
  
  if (buffer->off + length > *(buffer->size))
    {
      if (buffer->off || !*(buffer->size))
	new = (buffer->secure ? secure_realloc : fast_realloc)
	  (buffer->buf.wcs, *(buffer->size) * sizeof(wchar_t));
      else
	new = extalloc(buffer->buf.wcs, *(buffer->size) * sizeof(wchar_t), flags);
      
      if (new == NULL)
	{
	  if (buffer->free_on_error)
	    (buffer->secure ? secure_free : fast_free)(buffer->buf.wcs),
	      buffer->buf.wcs = NULL;
	  return -1;
	}
      *(buffer->size) = buffer->off + length;
      buffer->buf.wcs = new;
    }
  wmemcpy(buffer->buf.wcs, text, length);
  buffer->off += length;
  return 0;
}



/**
 * This function is identical to `fprintf` with
 * `stdout` as the first argument.
 * 
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int printf(const char* restrict format, ...)
{
  V(printf(format, args));
}


/**
 * Print a formatted string to a stream.
 * 
 * TODO list format rules for fprintf
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int fprintf(FILE* restrict stream, const char* restrict format, ...)
{
  V(fprintf(stream, format, args));
}


/**
 * This function is identical to `fprintf`,
 * except it does not lock the stream.
 * 
 * This is a slibc extension.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite_unlocked`.
 * 
 * @since  Always.
 */
int fprintf_unlocked(FILE* restrict stream, const char* restrict format, ...)
{
  V(fprintf_unlocked(stream, format, args));
}


/**
 * This function is identical to `fprintf`,
 * except it is limited to file descriptor-backed
 * streams, and uses the file descriptor as the
 * first argument rather than the stream.
 * 
 * @param   fd      The file descriptor.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int dprintf(int fd, const char* restrict format, ...)
{
  V(dprintf(fd, format, args));
}


/**
 * This function is identical to `dprintf`,
 * except it uses `send` instead of `write`
 * and supports transmissions flags.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   flags   Flags to pass to `send`, see `send`
 *                  for more information.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int sockprintf(int fd, int flags, const char* format restrict, ...)
{
  V(sockprintf(fd, flags, format, args));
}


/**
 * This function is identical to `fprintf`,
 * it prints to a buffer rather than a stream.
 * 
 * This is identical to `snprintf` with
 * `SIZE_MAX` as the second argument.
 * 
 * `sprintf` is(!) safe to use. As long as you allocate
 * the a large enough buffer. One way to do this is
 * by measuring the length of the result first.
 * 
 *     ssize_t n;
 *     char* buffer = NULL;
 *     snprintf(NULL, 0, "%zu%zn", your_value, &n);
 *     buffer = malloc((size_t)n * sizeof(char));
 *     if (buffer == NULL) goto fail;
 *     sprintf(buffer, "%zu", your_value);
 * 
 * If you don't care about portability, you can use
 * `asprintf`, `asprintfa` or `bprintf`. However, you
 * often do not need to measure the result with `snprintf`.
 * A maximum possible length can often be determined
 * at compile-time or only using `strlen'.
 * 
 *     char buffer[3 * sizeof(ssize_t) + 2]; // A very poor, but still safe,
 *                                           // approximation to log₁₀.
 *     spritnf(buffer, "%zi", your_signed_value);
 * 
 * These techniques guarantees that all of the string is
 * written, and is therefore preferable over replacing
 * `sprintf` with `snprintf`. Because of this, slibc
 * does not consider `sprintf` unsafe like some of the
 * more hokey C standard library implementations. This
 * is C damn it!
 * 
 *   “Almost anything can be unsafe if you don't use it properly.”
 * 
 * @param   buffer  The output buffer.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int sprintf(char* restrict buffer, const char* restrict format, ...)
{
  V(sprintf(buffer, format, args));
}


/**
 * This function is identical to `sprintf`,
 * expect it truncates the output.
 * 
 * @param   buffer  The output buffer.
 * @param   size    The allocation size of `buffer`.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte, that would have been written
 *                  if `size` was ignored. On error, a negative
 *                  value (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes would have been written; in slibc,
 *                  `INT_MAX` is returned if more would have
 *                  been written, you can use "%zn" to find the
 *                  actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int snprintf(char* restrict buffer, size_t size, const char* restrict format, ...)
{
  V(snprintf(buffer, size, format, args));
}


/**
 * This function is identical to `sprintf`,
 * except it allocates a sufficiently large
 * buffer.
 * 
 * This is a GNU extension.
 * 
 * @param   buffer  Output parameter for the output buffer.
 *                  On error the content of this pointer is undefined.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int asprintf(char** restrict buffer, const char* restrict format, ...)
{
  V(asprintf(buffer, format, args));
}


/**
 * This function is identical to `asprintf`,
 * except it can reuse allocated buffers.
 * 
 * This is a slibc extension.
 * 
 * @param   buffer  Reference parameter for the output buffer.
 *                  It should point to the buffer than shall
 *                  be used, or point to `NULL` if a new buffer
 *                  shall be allocated. It will be updated with
 *                  a new buffer if it points to `NULL`, or the
 *                  new pointer if `buffer` needed to be reallocated.
 *                  On error, this pointer will only have been
 *                  updated if the buffer was reallocated during
 *                  the call; if it pointed to `NULL`, it will
 *                  still point to `NULL`.
 * @param   size    Reference parameter for the buffer size.
 *                  It shall point to a variable whose value is
 *                  the allocation size of `*buffer`, or point to
 *                  a variable whose value is zero if `*buffer`
 *                  is `NULL`
 * @param   offset  The offset in the buffer where the function
 *                  shall start the printing.
 * @param   secure  Non-zero if the function must override the
 *                  buffer with zero before freeing it if it
 *                  creates a new allocation.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int bprintf(char** restrict buffer, size_t* restrict size, size_t offset,
	    int secure, const char* restrict format, ...)
{
  V(bprintf(buffer, size, offset, secure, format, args));
}


/**
 * This function is identical to `printf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int vprintf(const char* restrict format, va_list args)
{
  return vfprintf(stdout, format, args);
}


/**
 * This function is identical to `fprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int vfprintf(FILE* restrict stream, const char* restrict format, va_list args)
{
  int r, saved_errno;
  FLOCK(stream);
  r = vfprintf_unlocked(stream, format, args);
  saved_errno = errno;
  FUNLOCK(stream);
  return errno = saved_errno, r;
}


/**
 * This function is identical to `fprintf_unlocked`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite_unlocked`.
 * 
 * @since  Always.
 */
int vfprintf_unlocked(FILE* restrict stream, const char* restrict format, va_list args)
{
  P_CHAR(write_stream, 0, 0, 0, stream);
}


/**
 * This function is identical to `dprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   fd      The file descriptor.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `write`.
 * 
 * @since  Always.
 */
int vdprintf(int fd, const char* restrict format, va_list args)
{
  P_CHAR(write_fd, 0, 0, 0, &fd);
}


/**
 * This function is identical to `sockprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   flags   Flags to pass to `send`, see `send`
 *                  for more information.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int vsockprintf(int fd, int flags, const char* restrict format, va_list args)
{
  int input[] = {fd, flags};
  P_CHAR(send_fd, 0, 0, 0, input);
}


/**
 * This function is identical to `sprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   buffer  The output buffer.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int vsprintf(char* restrict buffer, const char* restrict format, va_list args)
{
  char* buf = buffer;
  P_CHAR(write_string, 0, 0, 1, &buf);
}


/**
 * This function is identical to `snprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   buffer  The output buffer.
 * @param   size    The allocation size of `buffer`.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte, that would have been written
 *                  if `size` was ignored. On error, a negative
 *                  value (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes would have been written; in slibc,
 *                  `INT_MAX` is returned if more would have
 *                  been written, you can use "%zn" to find the
 *                  actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int vsnprintf(char* restrict buffer, size_t size, const char* restrict format, va_list args)
{
  char* buf = buffer;
  P_CHAR(write_string, size, 1, 1, &buf);
}


/**
 * This function is identical to `asprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a GNU extension.
 * 
 * @param   buffer  Output parameter for the output buffer.
 *                  On error the content of this pointer is undefined.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int vasprintf(char** restrict buffer, const char* restrict format, va_list args)
{
  char* buf = NULL;
  size_t _size = 0;
  int r = vbprintf(&buf, &_size, 0, 0, format, args);
  return r ? r : (*buffer = buf, 0);
}


/**
 * This function is identical to `bprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension.
 * 
 * @param   buffer  Reference parameter for the output buffer.
 *                  It should point to the buffer than shall
 *                  be used, or point to `NULL` if a new buffer
 *                  shall be allocated. It will be updated with
 *                  a new buffer if it points to `NULL`, or the
 *                  new pointer if `buffer` needed to be reallocated.
 *                  On error, this pointer will only have been
 *                  updated if the buffer was reallocated during
 *                  the call; if it pointed to `NULL`, it will
 *                  still point to `NULL`.
 * @param   size    Reference parameter for the buffer size.
 *                  It shall point to a variable whose value is
 *                  the allocation size of `*buffer`, or point to
 *                  a variable whose value is zero if `*buffer`
 *                  is `NULL`
 * @param   offset  The offset in the buffer where the function
 *                  shall start the printing.
 * @param   secure  Non-zero if the function must override the
 *                  buffer with zero before freeing it if it
 *                  creates a new allocation.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes, excluding
 *                  the NUL byte. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  bytes are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int vbprintf(char** restrict buffer, size_t* restrict size, size_t offset,
	     int secure, const char* restrict format, va_list args)
{
  struct buffer buf =
    {
      .buf.str = *buffer,
      .size = size,
      .off = offset,
      .secure = secure,
      .free_on_error = buffer == NULL,
    };
  P_CHAR(write_buffer, 0, 0, 1, &buf);
}


/**
 * This function is identical to `printf` except
 * it uses wide characters.
 * 
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int wprintf(const wchar_t* restrict format, ...)
{
  V(wprintf(format, args));
}


/**
 * This function is identical to `fprintf` except
 * it uses wide characters.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int fwprintf(FILE* restrict stream, const wchar_t* restrict format, ...)
{
  V(fwprintf(stream, format, args));
}


/**
 * This function is identical to `fprintf_unlocked` except
 * it uses wide characters.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite_unlocked`.
 * 
 * @since  Always.
 */
int fwprintf_unlocked(FILE* restrict stream, const wchar_t* restrict format, ...)
{
  V(fwprintf_unlocked(stream, format, args));
}


/**
 * This function is identical to `dprintf` except
 * it uses wide characters.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int dwprintf(int fd, const wchar_t* restrict format, ...)
{
  V(dwprintf(fd, format, args));
}


/**
 * This function is identical to `sockprintf`
 * except it uses wide characters.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   flags   Flags to pass to `send`, see `send`
 *                  for more information.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int sockwprintf(int fd, int flags, const wchar_t* restrict format, ...)
{
  V(sockwprintf(fd, flags, format, args));
}


/**
 * This function is identical to `snprintf`
 * (not `sprintf`) except it uses wide characters.
 * 
 * @param   buffer  The output buffer.
 * @param   size    The allocation size of `buffer`.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character, that would have been written
 *                  if `size` was ignored. On error, a negative
 *                  value (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters would have been written; in slibc,
 *                  `INT_MAX` is returned if more would have
 *                  been written, you can use "%zn" to find the
 *                  actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int swprintf(wchar_t* restrict buffer, size_t size, const wchar_t* restrict format, ...)
{
  V(swprintf(buffer, size, format, args));
}


/**
 * This function is identical to `aswprintf` except
 * it uses wide characters.
 * 
 * This is a slibc extension added for completeness.
 * This is only available if GNU extensions are enabled.
 * 
 * @param   buffer  Output parameter for the output buffer.
 *                  On error the content of this pointer is undefined.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int aswprintf(wchar_t** restrict buffer, const wchar_t* restrict format, ...)
{
  V(aswprintf(buffer, format, args));
}


/**
 * This function is identical to `bprintf` except
 * it uses wide characters.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   buffer  Reference parameter for the output buffer.
 *                  It should point to the buffer than shall
 *                  be used, or point to `NULL` if a new buffer
 *                  shall be allocated. It will be updated with
 *                  a new buffer if it points to `NULL`, or the
 *                  new pointer if `buffer` needed to be reallocated.
 *                  On error, this pointer will only have been
 *                  updated if the buffer was reallocated during
 *                  the call; if it pointed to `NULL`, it will
 *                  still point to `NULL`.
 * @param   size    Reference parameter for the buffer size,
 *                  in `wchar_t`.
 *                  It shall point to a variable whose value is
 *                  the allocation size of `*buffer`, or point to
 *                  a variable whose value is zero if `*buffer`
 *                  is `NULL`
 * @param   offset  The offset in the buffer where the function
 *                  shall start the printing.
 * @param   secure  Non-zero if the function must override the
 *                  buffer with zero before freeing it if it
 *                  creates a new allocation.
 * @param   format  The formatting-string.
 * @param   ...     The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int bwprintf(wchar_t** restrict buffer, size_t* restrict size, size_t offset,
	     int secure, const wchar_t* restrict format, ...)
{
  V(bwprintf(buffer, size, offset, secure, format, args));
}


/**
 * This function is identical to `wprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int vwprintf(const wchar_t* restrict format, va_list args)
{
  return vfwprintf(stdout, format, args);
}


/**
 * This function is identical to `fwprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite`.
 * 
 * @since  Always.
 */
int vfwprintf(FILE* restrict stream, const wchar_t* restrict format, va_list args)
{
  int r, saved_errno;
  FLOCK(stream);
  r = vfwprintf_unlocked(stream, format, args);
  saved_errno = errno;
  FUNLOCK(stream);
  return errno = saved_errno, r;
}


/**
 * This function is identical to `fwprintf_unlocked`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   stream  The output stream.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws          Any error specified for `fwrite_unlocked`.
 * 
 * @since  Always.
 */
int vfwprintf_unlocked(FILE* restrict stream, const wchar_t* restrict format, va_list args)
{
  P_WCHAR(wwrite_stream, 0, 0, 0, stream);
}


/**
 * This function is identical to `dwprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` characters are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int vdwprintf(int fd, const wchar_t* restrict format, va_list args)
{
  P_WCHAR(wwrite_fd, 0, 0, 0, &fd);
}


/**
 * This function is identical to `sockwprintf`
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   fd      The file descriptor.
 * @param   flags   Flags to pass to `send`, see `send`
 *                  for more information.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written bytes.
 *                  On error, a negative value (namely -1
 *                  in this implementation) is returned.
 *                  It is unspecified what shall happen if
 *                  more than `INT_MAX` bytes are written;
 *                  in slibc, `INT_MAX` is returned if more
 *                  is written, you can use "%zn" to find
 *                  the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws           Any error specified for `write`.
 * 
 * @since  Always.
 */
int vsockwprintf(int fd, int flags, const wchar_t* restrict format, va_list args)
{
  int input[] = {fd, flags};
  P_WCHAR(wsend_fd, 0, 0, 0, input);
}


/**
 * This function is identical to `swprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * @param   buffer  The output buffer.
 * @param   size    The allocation size of `buffer`.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character, that would have been written
 *                  if `size` was ignored. On error, a negative
 *                  value (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters would have been written; in slibc,
 *                  `INT_MAX` is returned if more would have
 *                  been written, you can use "%zn" to find the
 *                  actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * 
 * @since  Always.
 */
int vswprintf(wchar_t* restrict buffer, size_t size, const wchar_t* restrict format, va_list args)
{
  wchar_t* buf = buffer;
  P_WCHAR(wwrite_string, size, 1, 1, &buf);
}


/**
 * This function is identical to `aswprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * This is only available if GNU extensions are enabled.
 * 
 * @param   buffer  Output parameter for the output buffer.
 *                  On error the content of this pointer is undefined.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int vaswprintf(wchar_t** restrict buffer, const wchar_t* restrict format, va_list args)
{
  wchar_t* buf = NULL;
  size_t _size = 0;
  int r = vbwprintf(&buf, &_size, 0, 0, format, args);
  return r ? r : (*buffer = buf, 0);
}


/**
 * This function is identical to `bwprintf`,
 * except it uses `va_list` instead of variadic argument.
 * 
 * This is a slibc extension added for completeness.
 * 
 * @param   buffer  Reference parameter for the output buffer.
 *                  It should point to the buffer than shall
 *                  be used, or point to `NULL` if a new buffer
 *                  shall be allocated. It will be updated with
 *                  a new buffer if it points to `NULL`, or the
 *                  new pointer if `buffer` needed to be reallocated.
 *                  On error, this pointer will only have been
 *                  updated if the buffer was reallocated during
 *                  the call; if it pointed to `NULL`, it will
 *                  still point to `NULL`.
 * @param   size    Reference parameter for the buffer size,
 *                  in `wchar_t`.
 *                  It shall point to a variable whose value is
 *                  the allocation size of `*buffer`, or point to
 *                  a variable whose value is zero if `*buffer`
 *                  is `NULL`
 * @param   offset  The offset in the buffer where the function
 *                  shall start the printing.
 * @param   secure  Non-zero if the function must override the
 *                  buffer with zero before freeing it if it
 *                  creates a new allocation.
 * @param   format  The formatting-string.
 * @param   args    The formatting-arguments.
 * @return          The number of written characters, excluding
 *                  the NUL character. On error, a negative value
 *                  (namely -1 in this implementation) is
 *                  returned. It is unspecified what shall
 *                  happen if more than `INT_MAX` non-NUL
 *                  characters are written; in slibc, `INT_MAX`
 *                  is returned if more is written, you can
 *                  use "%zn" to find the actual length.
 * 
 * @throws  EINVAL  `format` contained unsupported formatting codes.
 * @throws  ENOMEM  The process cannot allocation the
 *                  sufficient amount of memory.
 * 
 * @since  Always.
 */
int vbwprintf(wchar_t** restrict buffer, size_t* restrict size, size_t offset,
	      int secure, const wchar_t* restrict format, va_list args)
{
  struct buffer buf =
    {
      .buf.wcs = *buffer,
      .size = size,
      .off = offset,
      .secure = secure,
      .free_on_error = buffer == NULL,
    };
  P_WCHAR(wwrite_buffer, 0, 0, 1, &buf);
}