/
solidbase
/
C3_s21_stringplus
Обзор
Документация
Войти
/
solidbase
/
C3_s21_stringplus
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/s21_sscanf.c
652 строки
18 KB
maxiewal
valgrind and cppcheck
04 фев 2026, 17:55
04 фев 2026, 17:55
314143b
Код
Авторство
О чём код?
#include <stdarg.h> #include <stdint.h> #include <wchar.h> #include "s21_string.h" //------------------------------------------------------------------------------ // Part 4. Bonus. Implementation of the sscanf function //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Function Call Hierarchy // // s21_sscanf (main entry point) // │ // ├── skip_spaces ────────────────┐ // │ │ // └── Main parsing loop │ // │ │ // ├── Literal matching ───────┤ // │ │ // └── '%' handling │ // │ │ // ├── parse_spec ─────────┼──► builds Spec struct (flags, width, // length, specifier) │ │ └── Dispatch by // specifier // │ // ├── '[' → scan_scanset // │ │ // │ ├── parse_scanset_format ──► builds accept[256] table // │ │ // │ └── count_scanset_matches ─► counts matching chars // │ // └── Others → process_specifier // │ // ├── 'd','i','u','o','x' → process_int_specifier → // scan_int │ │ │ ├── parse_sign // │ ├── detect_base / // skip_hex_prefix │ └── // parse_digits │ ├── 'f','e','g' ────────→ scan_float // │ │ // │ ├── parse_integer_part // │ ├── parse_fraction_part // │ └── parse_exponent → // s21_powl10 │ ├── 's' ────────────────→ scan_string │ // ├── 'c' ────────────────→ scan_char // │ // ├── 'p' ────────────────→ scan_pointer // │ └── parse_digits (base 16) // │ // ├── 'n' ────────────────→ stores (*str - str_start) // │ // └── '%' ────────────────→ matches literal '%' // // Helper utilities used across modules: // - is_space, is_digit, char_to_digit // - get_max_width // - store_signed_int, store_unsigned_int, store_float (type dispatch by // length modifier) // // Data flow: // Input string ──► parsed by scanners ──► values stored via va_list (if not // suppressed) Format string ──► parsed by parse_spec ──► controls scanner // behavior //------------------------------------------------------------------------------ // Power of 10 for exponent parsing (replaces powl from math.h) static long double s21_powl10(int exp) { long double result = 1.0L; long double base = (exp >= 0) ? 10.0L : 0.1L; int n = (exp >= 0) ? exp : -exp; for (int i = 0; i < n; i++) { result *= base; } return result; } // Format specifier structure typedef struct { int suppress; // * flag int width; // field width (0 = unlimited) char length[3]; // "", "h", "hh", "l", "ll", "L" char specifier; // d, i, f, s, c, etc. } Spec; //------------------------------------------------------------------------------ // Helper functions //------------------------------------------------------------------------------ static int is_space(char c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'); } static const char* skip_spaces(const char* str) { while (*str && is_space(*str)) { str++; } return str; } static int is_digit(char c) { return (c >= '0' && c <= '9'); } static int is_octal_digit(char c) { return (c >= '0' && c <= '7'); } static int char_to_digit(char c, int base) { int result = -1; if (is_digit(c) && (c - '0') < base) { result = c - '0'; } else if (base == 16) { if (c >= 'a' && c <= 'f') { result = 10 + (c - 'a'); } else if (c >= 'A' && c <= 'F') { result = 10 + (c - 'A'); } } return result; } static int get_max_width(const Spec* spec) { return (spec->width > 0) ? spec->width : INT32_MAX; } // Parse format specifier: %[*][width][length]specifier static const char* parse_spec(const char* format, Spec* spec) { spec->suppress = 0; spec->width = 0; spec->length[0] = '\0'; spec->length[1] = '\0'; spec->length[2] = '\0'; spec->specifier = 0; if (*format == '*') { spec->suppress = 1; format++; } while (is_digit(*format)) { spec->width = spec->width * 10 + (*format - '0'); format++; } if (*format == 'h') { spec->length[0] = 'h'; format++; if (*format == 'h') { spec->length[1] = 'h'; format++; } } else if (*format == 'l') { spec->length[0] = 'l'; format++; if (*format == 'l') { spec->length[1] = 'l'; format++; } } else if (*format == 'L') { spec->length[0] = 'L'; format++; } if (*format) { spec->specifier = *format; format++; } return format; } //------------------------------------------------------------------------------ // Integer scanning helpers //------------------------------------------------------------------------------ static int parse_sign(const char** str, int* count, int max_w, int is_signed) { int sign = 1; if (is_signed && **str == '-' && *count < max_w) { sign = -1; (*str)++; (*count)++; } else if (**str == '+' && *count < max_w) { (*str)++; (*count)++; } return sign; } static int detect_base(const char** str, int* count, int max_w) { int base = 10; if (**str == '0' && *count < max_w) { if (((*str)[1] == 'x' || (*str)[1] == 'X') && *count + 1 < max_w) { base = 16; *str += 2; *count += 2; } else if (is_octal_digit((*str)[1])) { base = 8; (*str)++; (*count)++; } } return base; } static void skip_hex_prefix(const char** str, int* count, int max_w) { if (**str == '0' && *count + 1 < max_w && ((*str)[1] == 'x' || (*str)[1] == 'X')) { *str += 2; *count += 2; } } static long long parse_digits(const char** str, int* count, int max_w, int base, int* has_digits) { long long value = 0; int digit; while (*count < max_w && (digit = char_to_digit(**str, base)) >= 0) { value = value * base + digit; (*str)++; (*count)++; *has_digits = 1; } return value; } static void store_signed_int(va_list* args, const Spec* spec, long long value) { if (spec->length[0] == 'h' && spec->length[1] == 'h') { *va_arg(*args, signed char*) = (signed char)value; } else if (spec->length[0] == 'h') { *va_arg(*args, short*) = (short)value; } else if (spec->length[0] == 'l' && spec->length[1] == 'l') { *va_arg(*args, long long*) = value; } else if (spec->length[0] == 'l') { *va_arg(*args, long*) = (long)value; } else { *va_arg(*args, int*) = (int)value; } } static void store_unsigned_int(va_list* args, const Spec* spec, long long value) { if (spec->length[0] == 'h' && spec->length[1] == 'h') { *va_arg(*args, unsigned char*) = (unsigned char)value; } else if (spec->length[0] == 'h') { *va_arg(*args, unsigned short*) = (unsigned short)value; } else if (spec->length[0] == 'l' && spec->length[1] == 'l') { *va_arg(*args, unsigned long long*) = (unsigned long long)value; } else if (spec->length[0] == 'l') { *va_arg(*args, unsigned long*) = (unsigned long)value; } else { *va_arg(*args, unsigned int*) = (unsigned int)value; } } //------------------------------------------------------------------------------ // Float scanning helpers //------------------------------------------------------------------------------ static long double parse_integer_part(const char** str, int* count, int max_w, int* has_digits) { long double value = 0.0L; while (*count < max_w && is_digit(**str)) { value = value * 10.0L + (**str - '0'); (*str)++; (*count)++; *has_digits = 1; } return value; } static long double parse_fraction_part(const char** str, int* count, int max_w, int* has_digits) { long double frac_value = 0.0L; if (*count < max_w && **str == '.') { (*str)++; (*count)++; long double frac = 0.1L; while (*count < max_w && is_digit(**str)) { frac_value += (**str - '0') * frac; frac *= 0.1L; (*str)++; (*count)++; *has_digits = 1; } } return frac_value; } static long double parse_exponent(const char** str, int* count, int max_w) { long double multiplier = 1.0L; if (*count < max_w && (**str == 'e' || **str == 'E')) { (*str)++; (*count)++; int exp_sign = 1; int exp = 0; if (*count < max_w && **str == '-') { exp_sign = -1; (*str)++; (*count)++; } else if (*count < max_w && **str == '+') { (*str)++; (*count)++; } while (*count < max_w && is_digit(**str)) { exp = exp * 10 + (**str - '0'); (*str)++; (*count)++; } multiplier = s21_powl10(exp_sign * exp); } return multiplier; } static void store_float(va_list* args, const Spec* spec, long double value) { if (spec->length[0] == 'L') { *va_arg(*args, long double*) = value; } else if (spec->length[0] == 'l') { *va_arg(*args, double*) = (double)value; } else { *va_arg(*args, float*) = (float)value; } } //------------------------------------------------------------------------------ // Scan functions //------------------------------------------------------------------------------ static int scan_int(const char** str, Spec* spec, va_list* args, int base, int is_signed) { *str = skip_spaces(*str); int count = 0; int max_w = get_max_width(spec); int has_digits = 0; int sign = parse_sign(str, &count, max_w, is_signed); if (base == 0) { base = detect_base(str, &count, max_w); } else if (base == 16) { skip_hex_prefix(str, &count, max_w); } long long value = parse_digits(str, &count, max_w, base, &has_digits); int result = 0; if (has_digits && !spec->suppress) { if (is_signed) { store_signed_int(args, spec, sign * value); } else { store_unsigned_int(args, spec, value); } result = 1; } return result; } static int scan_float(const char** str, Spec* spec, va_list* args) { *str = skip_spaces(*str); int count = 0; int max_w = get_max_width(spec); int has_digits = 0; int sign = parse_sign(str, &count, max_w, 1); long double value = parse_integer_part(str, &count, max_w, &has_digits); value += parse_fraction_part(str, &count, max_w, &has_digits); if (has_digits) { value *= parse_exponent(str, &count, max_w); } int result = 0; if (has_digits && !spec->suppress) { store_float(args, spec, sign * value); result = 1; } return result; } static int scan_string(const char** str, Spec* spec, va_list* args) { *str = skip_spaces(*str); int result = 0; if (**str && !is_space(**str)) { int count = 0; int max_w = get_max_width(spec); if (spec->length[0] == 'l' && !spec->suppress) { wchar_t* dest = va_arg(*args, wchar_t*); while (**str && !is_space(**str) && count < max_w) { dest[count] = (wchar_t)(unsigned char)**str; (*str)++; count++; } dest[count] = L'\0'; } else { char* dest = spec->suppress ? S21_NULL : va_arg(*args, char*); while (**str && !is_space(**str) && count < max_w) { if (dest) { dest[count] = **str; } (*str)++; count++; } if (dest) { dest[count] = '\0'; } } if (count > 0 && !spec->suppress) { result = 1; } } return result; } static int scan_char(const char** str, Spec* spec, va_list* args) { int result = 0; int width = (spec->width > 0) ? spec->width : 1; if (**str) { if (spec->length[0] == 'l' && !spec->suppress) { wchar_t* dest = va_arg(*args, wchar_t*); for (int i = 0; i < width && (*str)[i]; i++) { dest[i] = (wchar_t)(unsigned char)(*str)[i]; } } else if (!spec->suppress) { char* dest = va_arg(*args, char*); for (int i = 0; i < width && (*str)[i]; i++) { dest[i] = (*str)[i]; } } *str += width; if (!spec->suppress) { result = 1; } } return result; } static int scan_pointer(const char** str, Spec* spec, va_list* args) { *str = skip_spaces(*str); int count = 0; int max_w = get_max_width(spec); int has_digits = 0; skip_hex_prefix(str, &count, max_w); unsigned long long value = (unsigned long long)parse_digits(str, &count, max_w, 16, &has_digits); int result = 0; if (has_digits && !spec->suppress) { *va_arg(*args, void**) = (void*)(uintptr_t)value; result = 1; } return result; } //------------------------------------------------------------------------------ // Scanset parsing //------------------------------------------------------------------------------ // Parse scanset format and build accept table static const char* parse_scanset_format(const char* format, int* accept, int* negated) { *negated = 0; if (*format == '^') { *negated = 1; format++; } // Special case: ']' at start is literal if (*format == ']') { accept[(unsigned char)']'] = 1; format++; } // Parse character set until ']' while (*format && *format != ']') { unsigned char c = (unsigned char)*format; if (format[1] == '-' && format[2] && format[2] != ']') { unsigned char end = (unsigned char)format[2]; for (int i = c; i <= end; i++) { accept[i] = 1; } format += 3; } else { accept[c] = 1; format++; } } if (*format == ']') { format++; } return format; } // Count matching characters from input static int count_scanset_matches(const char* src, int max_w, const int* accept, int negated) { int count = 0; int matching = 1; while (*src && count < max_w && matching) { unsigned char c = (unsigned char)*src; int in_set = accept[c]; int match = negated ? !in_set : in_set; if (match) { count++; src++; } else { matching = 0; } } return count; } static int scan_scanset(const char** str, const char** fmt, Spec* spec, va_list* args) { int accept[256] = {0}; int negated = 0; *fmt = parse_scanset_format(*fmt, accept, &negated); int max_w = get_max_width(spec); int count = count_scanset_matches(*str, max_w, accept, negated); int result = 0; if (count > 0) { if (!spec->suppress) { char* dest = va_arg(*args, char*); for (int i = 0; i < count; i++) { dest[i] = (*str)[i]; } dest[count] = '\0'; result = 1; } *str += count; } return result; } //------------------------------------------------------------------------------ // Specifier processing //------------------------------------------------------------------------------ static int process_int_specifier(const char** str, Spec* spec, va_list* args) { int scan_result = 0; switch (spec->specifier) { case 'd': scan_result = scan_int(str, spec, args, 10, 1); break; case 'i': scan_result = scan_int(str, spec, args, 0, 1); break; case 'u': scan_result = scan_int(str, spec, args, 10, 0); break; case 'o': scan_result = scan_int(str, spec, args, 8, 0); break; case 'x': case 'X': scan_result = scan_int(str, spec, args, 16, 0); break; } return scan_result; } static int process_specifier(const char** str, Spec* spec, va_list* args, const char* str_start) { int scan_result = 0; char s = spec->specifier; if (s == 'd' || s == 'i' || s == 'u' || s == 'o' || s == 'x' || s == 'X') { scan_result = process_int_specifier(str, spec, args); } else if (s == 'f' || s == 'e' || s == 'E' || s == 'g' || s == 'G') { scan_result = scan_float(str, spec, args); } else if (s == 's') { scan_result = scan_string(str, spec, args); } else if (s == 'c') { scan_result = scan_char(str, spec, args); } else if (s == 'p') { scan_result = scan_pointer(str, spec, args); } else if (s == 'n' && !spec->suppress) { *va_arg(*args, int*) = (int)(*str - str_start); } else if (s == '%') { *str = skip_spaces(*str); if (**str == '%') { (*str)++; } } return scan_result; } //------------------------------------------------------------------------------ // Main sscanf function //------------------------------------------------------------------------------ int s21_sscanf(const char* str, const char* format, ...) { if (!str || !format) { return -1; } va_list args; va_start(args, format); const char* str_start = str; int result = 0; int processing = 1; while (*format && processing) { if (is_space(*format)) { str = skip_spaces(str); while (is_space(*format)) { format++; } } else if (*format == '%') { format++; Spec spec; format = parse_spec(format, &spec); int scan_result = 0; if (spec.specifier == '[') { scan_result = scan_scanset(&str, &format, &spec, &args); } else { scan_result = process_specifier(&str, &spec, &args, str_start); } if (scan_result > 0) { result += scan_result; } } else { if (*str == *format) { str++; format++; } else { processing = 0; } } } va_end(args); return result; }