| 156 | } |
| 157 | |
| 158 | unsigned long strtoul(const char* str, char** endptr, int base) { |
| 159 | if (!str) { |
| 160 | if (endptr) { |
| 161 | *endptr = const_cast<char*>(str); |
| 162 | } |
| 163 | return 0; |
| 164 | } |
| 165 | |
| 166 | const char* p = str; |
| 167 | unsigned long result = 0; |
| 168 | |
| 169 | // Skip leading whitespace |
| 170 | while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') { |
| 171 | p++; |
| 172 | } |
| 173 | |
| 174 | // Handle optional + sign (ignore - for unsigned) |
| 175 | if (*p == '+') { |
| 176 | p++; |
| 177 | } else if (*p == '-') { |
| 178 | // Negative values wrap around for unsigned |
| 179 | p++; |
| 180 | } |
| 181 | |
| 182 | // Auto-detect base if base is 0 |
| 183 | if (base == 0) { |
| 184 | if (*p == '0') { |
| 185 | if (*(p + 1) == 'x' || *(p + 1) == 'X') { |
| 186 | base = 16; |
| 187 | p += 2; |
| 188 | } else { |
| 189 | base = 8; |
| 190 | p++; |
| 191 | } |
| 192 | } else { |
| 193 | base = 10; |
| 194 | } |
| 195 | } else if (base == 16) { |
| 196 | // Skip optional 0x/0X prefix for base 16 |
| 197 | if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) { |
| 198 | p += 2; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // Validate base |
| 203 | if (base < 2 || base > 36) { |
| 204 | if (endptr) { |
| 205 | *endptr = const_cast<char*>(str); |
| 206 | } |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | // Parse digits |
| 211 | const char* start = p; |
| 212 | while (*p && isDigitInBase(*p, base)) { |
| 213 | result = result * base + charToDigit(*p); |
| 214 | p++; |
| 215 | } |
no test coverage detected