/* * printf.c * * Copyright (C) 2018 Aleksandar Andrejevic * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include "io_priv.h" #define __CRT_PRINTF_FLAG_ALIGN_LEFT (1 << 0) #define __CRT_PRINTF_FLAG_PLUS (1 << 1) #define __CRT_PRINTF_FLAG_SPACE (1 << 2) #define __CRT_PRINTF_FLAG_EXTRA (1 << 3) #define __CRT_PRINTF_FLAG_ZERO_PAD (1 << 4) #define __CRT_PRINTF_FLAG_EXTERNAL_WIDTH (1 << 5) #define __CRT_PRINTF_FLAG_EXTERNAL_PRECISION (1 << 6) #define __CRT_PRINTF_FLAG_DEFAULT_WIDTH (1 << 7) #define __CRT_PRINTF_FLAG_DEFAULT_PRECISION (1 << 8) typedef struct { FILE *stream; char *string; size_t size; } __crt_stream_or_string_t; static int __crt_strputc(__crt_stream_or_string_t *str, char c) { if (str->stream) { return fputc_unlocked(c, str->stream) != EOF; } else { if (str->size > 1) { *str->string++ = c; str->size--; } return 1; } } static int __crt_strputs(__crt_stream_or_string_t *str, const char *s) { if (str->stream) { return fputs_unlocked(s, str->stream) != EOF ? strlen(s) : 0; } else { int length = strlen(s); if (str->size > 1) { strncpy(str->string, s, str->size - 1); if (length < str->size) { str->string += length; str->size -= length; } else { str->string += str->size - 1; str->size = 0; } } return length; } } static inline int __crt_vstrprintf(__crt_stream_or_string_t *str, const char *format, va_list ap) { int ret = 0; if (str->stream) syscall_wait_mutex(str->stream->mutex, NO_TIMEOUT); const char *ptr; for (ptr = format; *ptr; ptr++) { if (*ptr == '%') { unsigned long flags = 0; unsigned int width = 0, precision = 0; ptr++; while (*ptr) { if (*ptr == '-') flags |= __CRT_PRINTF_FLAG_ALIGN_LEFT; else if (*ptr == '+') flags |= __CRT_PRINTF_FLAG_PLUS; else if (*ptr == ' ') flags |= __CRT_PRINTF_FLAG_SPACE; else if (*ptr == '#') flags |= __CRT_PRINTF_FLAG_EXTRA; else if (*ptr == '0') flags |= __CRT_PRINTF_FLAG_ZERO_PAD; else break; ptr++; } if (*ptr == '*') flags |= __CRT_PRINTF_FLAG_EXTERNAL_WIDTH; else if (isdigit(*ptr)) width = strtoul(ptr, (char**)&ptr, 10); else flags |= __CRT_PRINTF_FLAG_DEFAULT_WIDTH; if (*ptr == '.') { ptr++; if (*ptr == '*') flags |= __CRT_PRINTF_FLAG_EXTERNAL_PRECISION; else if (isdigit(*ptr)) precision = strtoul(ptr, (char**)&ptr, 10); else flags |= __CRT_PRINTF_FLAG_DEFAULT_PRECISION; } else { flags |= __CRT_PRINTF_FLAG_DEFAULT_PRECISION; } int variable_size = 0; if (*ptr == 'h') { variable_size--; if (*++ptr == 'h') { variable_size--; ptr++; } } else if (*ptr == 'l') { variable_size++; if (*++ptr == 'l') { variable_size++; ptr++; } } const char *data = NULL; char buffer[512]; int radix = 10; switch (*ptr) { case 's': data = va_arg(ap, const char*); break; case 'c': data = buffer; buffer[0] = (char)va_arg(ap, int); buffer[1] = '\0'; break; case 'd': case 'i': if (flags & __CRT_PRINTF_FLAG_DEFAULT_WIDTH) width = 0; if (flags & __CRT_PRINTF_FLAG_DEFAULT_PRECISION) precision = 1; data = buffer; switch (variable_size) { case -2: itoa((char)va_arg(ap, int), buffer, 10); break; case -1: itoa((short)va_arg(ap, int), buffer, 10); break; case 0: itoa(va_arg(ap, int), buffer, 10); break; case 1: ltoa(va_arg(ap, long), buffer, 10); break; case 2: lltoa(va_arg(ap, long long), buffer, 10); break; } break; case 'o': radix = 8; goto read_variable; case 'x': case 'X': radix = 16; case 'u': read_variable: if (flags & __CRT_PRINTF_FLAG_DEFAULT_WIDTH) width = 0; if (flags & __CRT_PRINTF_FLAG_DEFAULT_PRECISION) precision = 1; data = buffer; switch (variable_size) { case -2: uitoa((unsigned char)va_arg(ap, unsigned int), buffer, radix); break; case -1: uitoa((unsigned short)va_arg(ap, unsigned int), buffer, radix); break; case 0: uitoa(va_arg(ap, unsigned int), buffer, radix); break; case 1: ultoa(va_arg(ap, unsigned long), buffer, radix); break; case 2: ulltoa(va_arg(ap, unsigned long long), buffer, radix); break; } break; case '%': data = buffer; strcpy(buffer, "%"); break; } if (flags & __CRT_PRINTF_FLAG_EXTERNAL_WIDTH) width = va_arg(ap, unsigned int); if (flags & __CRT_PRINTF_FLAG_EXTERNAL_PRECISION) precision = va_arg(ap, unsigned int); int length = strlen(data); char padding = (flags &__CRT_PRINTF_FLAG_ZERO_PAD) ? '0' : ' '; switch (*ptr) { case 's': if (!(flags & __CRT_PRINTF_FLAG_ALIGN_LEFT)) { while (length < width) { ret += __crt_strputc(str, padding); length++; } } if (precision) { while (*data && precision > 0) { ret += __crt_strputc(str, *data++); precision--; } } else { ret += __crt_strputs(str, data ? data : "(null)"); } break; case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': if (flags & (__CRT_PRINTF_FLAG_PLUS | __CRT_PRINTF_FLAG_SPACE)) { if (*data == '-') { ret += __crt_strputc(str, '-'); data++; } else { ret += __crt_strputc(str, (flags & __CRT_PRINTF_FLAG_PLUS) ? '+' : ' '); length++; } } if (!(flags & __CRT_PRINTF_FLAG_ALIGN_LEFT) && width > precision) { while (length < width - precision) { ret += __crt_strputc(str, padding); length++; } while (length < width) { ret += __crt_strputc(str, '0'); length++; } } else { while (length < precision) { ret += __crt_strputc(str, '0'); length++; } } ret += __crt_strputs(str, data); break; } while (length < width) { ret += __crt_strputc(str, padding); length++; } } else { ret += __crt_strputc(str, *ptr); } } if (str->string) *str->string = '\0'; if (str->stream) syscall_release_mutex(str->stream->mutex); return ret; } int vsnprintf(char *string, size_t size, const char *format, va_list ap) { __crt_stream_or_string_t str = { .stream = NULL, .string = string, .size = size }; return __crt_vstrprintf(&str, format, ap); } int vsprintf(char *str, const char *format, va_list ap) { return vsnprintf(str, -1, format, ap); } int vprintf(const char *format, va_list ap) { return vfprintf(stdout, format, ap); } int vfprintf(FILE *stream, const char *format, va_list ap) { __crt_stream_or_string_t str = { .stream = stream, .string = NULL, .size = 0 }; return __crt_vstrprintf(&str, format, ap); } int fprintf(FILE *stream, const char *format, ...) { va_list ap; va_start(ap, format); int ret = vfprintf(stream, format, ap); va_end(ap); return ret; } int snprintf(char *str, size_t size, const char *format, ...) { va_list ap; va_start(ap, format); int ret = vsnprintf(str, size, format, ap); va_end(ap); return ret; } int sprintf(char *str, const char *format, ...) { va_list ap; va_start(ap, format); int ret = vsprintf(str, format, ap); va_end(ap); return ret; } int printf(const char *format, ...) { va_list ap; va_start(ap, format); int ret = vprintf(format, ap); va_end(ap); return ret; }