summaryrefslogtreecommitdiff
path: root/lib/printf.c
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-04-08 10:39:48 -0400
committerFreya Murphy <freya@freyacat.org>2025-04-08 10:39:48 -0400
commit8a19547957a86bed3f58c9abc1ac218d04698faf (patch)
treeed7ccc6f3a8902915dfe6c9bf763fc45d752b3c4 /lib/printf.c
parentfmt (diff)
downloadcomus-8a19547957a86bed3f58c9abc1ac218d04698faf.tar.gz
comus-8a19547957a86bed3f58c9abc1ac218d04698faf.tar.bz2
comus-8a19547957a86bed3f58c9abc1ac218d04698faf.zip
break apart c libaray
Diffstat (limited to 'lib/printf.c')
-rw-r--r--lib/printf.c602
1 files changed, 0 insertions, 602 deletions
diff --git a/lib/printf.c b/lib/printf.c
deleted file mode 100644
index 4a85956..0000000
--- a/lib/printf.c
+++ /dev/null
@@ -1,602 +0,0 @@
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdint.h>
-
-#define PRINTF_NUMERIC_BUF_LEN 50
-
-typedef union {
- unsigned long long int u;
- signed long long int i;
- char *str;
- char c;
-} data_t;
-
-/// options that can be set inside a specifier
-/// flags, width, precision, length, and data type
-typedef struct {
- /* flags */
- /// left justify content
- uint8_t left : 1;
- /// force sign (+/-) on numeric output
- uint8_t sign : 1;
- /// leave space if no printed sign on numeric output
- uint8_t space : 1;
- /// preceed hex/octal output with '0x'
- uint8_t hash : 1;
- /// left pads numeric output with zeros
- uint8_t zero : 1;
- uint8_t : 3;
-
- /* width & precision */
- /// minimum number of characters to be printed (padding if origonal is less)
- int width;
- /// digit precision used when printing numerical answers
- int precision;
- /// if a fixed minimum width has been provided
- uint8_t width_set : 1;
- /// if the provided minimum width is in the next variable argument
- uint8_t width_varies : 1;
- /// if a fixed digit precision has been provided
- uint8_t precision_set : 1;
- /// if the provided digit precision is in the next variable argument
- uint8_t precision_varies : 1;
- uint8_t : 4;
-
- /* length */
- /// what size to read argument as
- enum printf_len {
- PRINTF_LEN_CHAR,
- PRINTF_LEN_SHORT_INT,
- PRINTF_LEN_INT,
- PRINTF_LEN_LONG_INT,
- PRINTF_LEN_LONG_LONG_INT,
- PRINTF_LEN_SIZE_T,
- } len;
-
- /* other */
- /// radix to print the numerical answers as
- uint8_t radix;
- /// case to print hexadecimal values as
- bool is_uppercase;
-} options_t;
-
-typedef struct {
- /* input */
- /// the origonal format string
- const char *format;
- /// maximum allowed output length
- size_t max_len;
- /// if a maximum output length is set
- bool has_max_len;
-
- /* output */
- size_t written_len;
- bool to_file;
- union {
- FILE *file;
- char *buf;
- } out;
-
- /* pass 2 */
- char *output;
-} context_t;
-
-static void printf_putc(context_t *ctx, char c)
-{
- // bounds check
- if (ctx->has_max_len)
- if (ctx->written_len >= ctx->max_len)
- return;
-
- // write to correct
- if (ctx->to_file)
- fputc(ctx->out.file, c);
- else
- *(ctx->out.buf++) = c;
-
- ctx->written_len++;
-}
-
-static int parse_flag(const char **res, options_t *opts)
-{
- const char *fmt = *res;
- switch (*(fmt++)) {
- case '-':
- opts->left = 1;
- break;
- case '+':
- opts->sign = 1;
- break;
- case ' ':
- opts->space = 1;
- break;
- case '#':
- opts->hash = 1;
- break;
- case '0':
- opts->zero = 1;
- break;
- default:
- return 0;
- }
-
- *res = fmt;
- return 1;
-}
-
-static void parse_width(const char **res, options_t *opts)
-{
- const char *fmt = *res;
- char *end = NULL;
-
- // check varies
- if (*fmt == '*') {
- opts->width_varies = true;
- *res = fmt++;
- return;
- }
-
- // parse num
- long width = strtol(fmt, &end, 10);
- if (end != NULL) {
- opts->width_set = 1;
- opts->width = width;
- *res = end;
- return;
- }
-}
-
-static void parse_precision(const char **res, options_t *opts)
-{
- const char *fmt = *res;
- char *end = NULL;
-
- // check for dot
- if (*(fmt++) != '.')
- return;
-
- // check varies
- if (*fmt == '*') {
- opts->precision_varies = true;
- *res = fmt++;
- return;
- }
-
- // parse num
- long precision = strtol(fmt, &end, 10);
- if (end != NULL) {
- opts->precision_set = 1;
- opts->precision = precision;
- *res = end;
- return;
- }
-}
-
-static void parse_length(const char **res, options_t *opts)
-{
- const char *fmt = *res;
-
- switch (*(fmt++)) {
- // half
- case 'h':
- if (*fmt == 'h') {
- opts->len = PRINTF_LEN_CHAR;
- fmt++;
- } else {
- opts->len = PRINTF_LEN_SHORT_INT;
- }
- break;
- // long
- case 'l':
- if (*fmt == 'l') {
- opts->len = PRINTF_LEN_LONG_LONG_INT;
- fmt++;
- } else {
- opts->len = PRINTF_LEN_LONG_INT;
- }
- break;
- // size_t
- case 'z':
- opts->len = PRINTF_LEN_SIZE_T;
- break;
- default:
- opts->len = PRINTF_LEN_INT;
- return;
- }
-
- *res = fmt;
-}
-
-static void get_radix(char spec, options_t *opts)
-{
- switch (spec) {
- case 'x':
- case 'X':
- opts->radix = 16;
- break;
- case 'o':
- opts->radix = 8;
- break;
- default:
- opts->radix = 10;
- break;
- }
-}
-
-static void get_case(char spec, options_t *opts)
-{
- if (spec == 'X')
- opts->is_uppercase = 1;
-}
-
-static char printf_itoc(int uppercase, int i)
-{
- // decimal
- if (i < 10) {
- return i + '0';
- }
- // hex
- if (uppercase) {
- return (i - 10) + 'A';
- } else {
- return (i - 10) + 'a';
- }
-}
-
-static int printf_lltoa(char *buf, options_t *opts, bool is_neg,
- unsigned long long int num)
-{
- int precision = 0;
- char *start = buf;
-
- // get width of number
- int len = 0;
- unsigned long long int temp = num;
- if (temp == 0)
- len = 1;
- while (temp) {
- if (opts->precision_set && precision++ >= opts->precision)
- break;
- temp /= opts->radix;
- len++;
- }
- precision = 0;
-
- // sign
- if (is_neg) {
- *(buf++) = '-';
- } else if (opts->sign) {
- *(buf++) = '+';
- } else if (opts->space) {
- *(buf++) = ' ';
- }
-
- // radix specifier
- if (opts->hash) {
- if (opts->radix == 8) {
- *(buf++) = '0';
- *(buf++) = 'o';
- }
- if (opts->radix == 16) {
- *(buf++) = '0';
- *(buf++) = 'x';
- }
- }
-
- // print zeros if needed
- if (opts->width_set && len < opts->width && opts->zero) {
- while (len++ < opts->width)
- *(buf++) = '0';
- }
-
- // write number
- if (num == 0) {
- *(buf++) = '0';
- }
- while (num) {
- if (opts->precision_set && precision++ >= opts->precision)
- break;
- *(buf++) = printf_itoc(opts->is_uppercase, num % opts->radix);
- num /= opts->radix;
- }
- *(buf++) = '\0';
-
- return buf - start;
-}
-
-static void handle_int_specifier(context_t *ctx, options_t *const opts,
- bool has_sign_bit, data_t num)
-{
- bool is_neg = false;
-
- // get sign if possible neg
- if (has_sign_bit) {
- if (num.i < 0) {
- num.i = -num.i;
- is_neg = true;
- }
- }
-
- // get length of number and number
- char buf[PRINTF_NUMERIC_BUF_LEN];
- int buf_len = printf_lltoa(buf, opts, is_neg, num.u);
-
- // get needed padding
- int padding = 0;
- if (opts->width_set && (buf_len < opts->width))
- padding = opts->width - buf_len;
-
- /* print */
- // left padding
- if (opts->left == 0) {
- for (int i = 0; i < padding; i++)
- printf_putc(ctx, opts->zero ? '0' : ' ');
- }
- // number
- for (int i = 0; i < buf_len; i++)
- printf_putc(ctx, buf[i]);
- // right padding
- if (opts->left == 1) {
- for (int i = 0; i < padding; i++)
- printf_putc(ctx, opts->zero ? '0' : ' ');
- }
-}
-
-static void handle_char_specifier(context_t *ctx, data_t c)
-{
- printf_putc(ctx, c.c);
-}
-
-static void handle_string_specifier(context_t *ctx, options_t *opts,
- data_t data)
-{
- char *str = data.str;
- int str_len = 0;
-
- // get length of string
- if (opts->precision_set)
- str_len = opts->precision;
- else
- str_len = strlen(str);
-
- // get needed padding
- int padding = 0;
- if (opts->width_set && (str_len < opts->width))
- padding = opts->width - str_len;
-
- /* print */
- // left padding
- if (opts->left == 0) {
- for (int i = 0; i < padding; i++)
- printf_putc(ctx, ' ');
- }
- // string
- for (int i = 0; i < str_len; i++)
- printf_putc(ctx, str[i]);
- // right padding
- if (opts->left == 1) {
- for (int i = 0; i < padding; i++)
- printf_putc(ctx, ' ');
- }
-}
-
-static void do_printf(context_t *ctx, va_list args)
-{
- const char *fmt = ctx->format;
-
- char c;
- while (c = *fmt++, c != '\0') {
- // save start of fmt for current iteration
- const char *start = fmt - 1;
-
- // ignore if not %
- if (c != '%') {
- printf_putc(ctx, c);
- continue;
- }
-
- // read opts
- options_t opts = { 0 };
- while (parse_flag(&fmt, &opts))
- ;
- parse_width(&fmt, &opts);
- parse_precision(&fmt, &opts);
- parse_length(&fmt, &opts);
-
- // read specifier
- char spec = *fmt++;
- get_radix(spec, &opts);
- get_case(spec, &opts);
-
- // read varied width / precision
- if (opts.width_varies) {
- opts.width_set = 1;
- opts.width = va_arg(args, int);
- }
- if (opts.precision_varies) {
- opts.precision_set = 1;
- opts.precision = va_arg(args, int);
- }
- // read data from args
- data_t data;
- switch (spec) {
- case 'p':
- opts.len = PRINTF_LEN_SIZE_T;
- opts.width_set = true;
- opts.radix = 16;
- opts.hash = true;
- opts.zero = true;
- case 'd':
- case 'i':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- // read number from arg
- switch (opts.len) {
- case PRINTF_LEN_CHAR:
- data.u = va_arg(args, unsigned int); // char
- break;
- case PRINTF_LEN_SHORT_INT:
- data.u = va_arg(args, unsigned int); // short int
- break;
- case PRINTF_LEN_INT:
- data.u = va_arg(args, unsigned int);
- break;
- case PRINTF_LEN_LONG_INT:
- data.u = va_arg(args, unsigned long int);
- break;
- case PRINTF_LEN_LONG_LONG_INT:
- data.u = va_arg(args, unsigned long long int);
- break;
- case PRINTF_LEN_SIZE_T:
- data.u = va_arg(args, size_t);
- break;
- }
- break;
- // end int
- case 's':
- // read string
- data.str = va_arg(args, void *);
- break;
- // end string
- case 'c':
- // read char
- data.c = va_arg(args, int);
- break;
- // end char
- }
-
- switch (spec) {
- // signed int
- case 'd':
- case 'i':
- handle_int_specifier(ctx, &opts, true, data);
- break;
- // unsigned int
- case 'p':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- handle_int_specifier(ctx, &opts, false, data);
- break;
- // character
- case 'c':
- handle_char_specifier(ctx, data);
- break;
- // string
- case 's':
- handle_string_specifier(ctx, &opts, data);
- break;
- // unknown
- default:
- // print from % to current
- for (; start < fmt; start++)
- printf_putc(ctx, *start);
- break;
- }
- }
-}
-
-void printf(const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- vprintf(format, args);
- va_end(args);
-}
-
-size_t sprintf(char *restrict s, const char *format, ...)
-{
- va_list args;
- size_t amt;
- va_start(args, format);
- amt = vsprintf(s, format, args);
- va_end(args);
- return amt;
-}
-
-size_t snprintf(char *restrict s, size_t maxlen, const char *format, ...)
-{
- va_list args;
- size_t amt;
- va_start(args, format);
- amt = vsnprintf(s, maxlen, format, args);
- va_end(args);
- return amt;
-}
-
-void vprintf(const char *format, va_list args)
-{
- vfprintf(stdout, format, args);
-}
-
-size_t vsprintf(char *restrict s, const char *format, va_list args)
-{
- // create context
- context_t ctx = { 0 };
- ctx.format = format;
- // sprintf buffer
- ctx.out.buf = s;
- ctx.to_file = 0;
- // print
- do_printf(&ctx, args);
- return ctx.written_len;
-}
-
-size_t vsnprintf(char *restrict s, size_t maxlen, const char *format,
- va_list args)
-{
- // create context
- context_t ctx = { 0 };
- ctx.format = format;
- // sprintf buffer
- ctx.out.buf = s;
- ctx.to_file = 0;
- // sprintf max_len
- ctx.has_max_len = 1;
- ctx.max_len = maxlen;
- // print
- do_printf(&ctx, args);
- return ctx.written_len;
-}
-
-void fprintf(FILE *stream, const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- vfprintf(stream, format, args);
- va_end(args);
-}
-
-void vfprintf(FILE *stream, const char *format, va_list args)
-{
- // create context
- context_t ctx = { 0 };
- ctx.format = format;
- // fprintf stream
- ctx.out.file = stream;
- ctx.to_file = 1;
- // print
- do_printf(&ctx, args);
-}
-
-void putc(char c)
-{
- fputc(stdout, c);
-}
-
-void puts(const char *str)
-{
- fputs(stdout, str);
-}
-
-void fputs(FILE *stream, const char *s)
-{
- while (*s)
- fputc(stream, *s++);
-}