1
#define PY_SSIZE_T_CLEAN
2
#include <Python.h>
3

4
#include <locale.h>
5
#include <stdio.h>
6

7
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
8
#define _MULTIARRAYMODULE
9
#include "numpy/arrayobject.h"
10
#include "numpy/npy_math.h"
11

12
#include "npy_config.h"
13

14
#include "npy_pycompat.h"
15

16
#ifdef HAVE_STRTOLD_L
17
#include <stdlib.h>
18
#ifdef HAVE_XLOCALE_H
19
    /*
20
     * the defines from xlocale.h are included in locale.h on some systems;
21
     * see gh-8367
22
     */
23
    #include <xlocale.h>
24
#endif
25
#endif
26

27

28

29
/*
30
 * From the C99 standard, section 7.19.6: The exponent always contains at least
31
 * two digits, and only as many more digits as necessary to represent the
32
 * exponent.
33
 */
34

35
#define MIN_EXPONENT_DIGITS 2
36

37
/*
38
 * Ensure that any exponent, if present, is at least MIN_EXPONENT_DIGITS
39
 * in length.
40
 */
41
static void
42 1
ensure_minimum_exponent_length(char* buffer, size_t buf_size)
43
{
44 1
    char *p = strpbrk(buffer, "eE");
45 1
    if (p && (*(p + 1) == '-' || *(p + 1) == '+')) {
46 0
        char *start = p + 2;
47 0
        int exponent_digit_cnt = 0;
48 0
        int leading_zero_cnt = 0;
49 0
        int in_leading_zeros = 1;
50
        int significant_digit_cnt;
51

52
        /* Skip over the exponent and the sign. */
53 0
        p += 2;
54

55
        /* Find the end of the exponent, keeping track of leading zeros. */
56 0
        while (*p && isdigit(Py_CHARMASK(*p))) {
57 0
            if (in_leading_zeros && *p == '0') {
58 0
                ++leading_zero_cnt;
59
            }
60 0
            if (*p != '0') {
61 0
                in_leading_zeros = 0;
62
            }
63 0
            ++p;
64 0
            ++exponent_digit_cnt;
65
        }
66

67 0
        significant_digit_cnt = exponent_digit_cnt - leading_zero_cnt;
68 0
        if (exponent_digit_cnt == MIN_EXPONENT_DIGITS) {
69
            /*
70
             * If there are 2 exactly digits, we're done,
71
             * regardless of what they contain
72
             */
73
        }
74 0
        else if (exponent_digit_cnt > MIN_EXPONENT_DIGITS) {
75
            int extra_zeros_cnt;
76

77
            /*
78
             * There are more than 2 digits in the exponent.  See
79
             * if we can delete some of the leading zeros
80
             */
81 0
            if (significant_digit_cnt < MIN_EXPONENT_DIGITS) {
82 0
                significant_digit_cnt = MIN_EXPONENT_DIGITS;
83
            }
84 0
            extra_zeros_cnt = exponent_digit_cnt - significant_digit_cnt;
85

86
            /*
87
             * Delete extra_zeros_cnt worth of characters from the
88
             * front of the exponent
89
             */
90
            assert(extra_zeros_cnt >= 0);
91

92
            /*
93
             * Add one to significant_digit_cnt to copy the
94
             * trailing 0 byte, thus setting the length
95
             */
96 0
            memmove(start, start + extra_zeros_cnt, significant_digit_cnt + 1);
97
        }
98
        else {
99
            /*
100
             * If there are fewer than 2 digits, add zeros
101
             * until there are 2, if there's enough room
102
             */
103 0
            int zeros = MIN_EXPONENT_DIGITS - exponent_digit_cnt;
104 0
            if (start + zeros + exponent_digit_cnt + 1 < buffer + buf_size) {
105 0
                memmove(start + zeros, start, exponent_digit_cnt + 1);
106 0
                memset(start, '0', zeros);
107
            }
108
        }
109
    }
110
}
111

112
/*
113
 * Ensure that buffer has a decimal point in it.  The decimal point
114
 * will not be in the current locale, it will always be '.'
115
 */
116
static void
117 0
ensure_decimal_point(char* buffer, size_t buf_size)
118
{
119 0
    int insert_count = 0;
120
    char* chars_to_insert;
121

122
    /* search for the first non-digit character */
123 0
    char *p = buffer;
124 0
    if (*p == '-' || *p == '+')
125
        /*
126
         * Skip leading sign, if present.  I think this could only
127
         * ever be '-', but it can't hurt to check for both.
128
         */
129 0
        ++p;
130 0
    while (*p && isdigit(Py_CHARMASK(*p))) {
131 0
        ++p;
132
    }
133 0
    if (*p == '.') {
134 0
        if (isdigit(Py_CHARMASK(*(p+1)))) {
135
            /*
136
             * Nothing to do, we already have a decimal
137
             * point and a digit after it.
138
             */
139
        }
140
        else {
141
            /*
142
             * We have a decimal point, but no following
143
             * digit.  Insert a zero after the decimal.
144
             */
145 0
            ++p;
146 0
            chars_to_insert = "0";
147 0
            insert_count = 1;
148
        }
149
    }
150
    else {
151
        chars_to_insert = ".0";
152
        insert_count = 2;
153
    }
154 0
    if (insert_count) {
155 0
        size_t buf_len = strlen(buffer);
156 0
        if (buf_len + insert_count + 1 >= buf_size) {
157
            /*
158
             * If there is not enough room in the buffer
159
             * for the additional text, just skip it.  It's
160
             * not worth generating an error over.
161
             */
162
        }
163
        else {
164 0
            memmove(p + insert_count, p, buffer + strlen(buffer) - p + 1);
165 0
            memcpy(p, chars_to_insert, insert_count);
166
        }
167
    }
168
}
169

170
/* see FORMATBUFLEN in unicodeobject.c */
171
#define FLOAT_FORMATBUFLEN 120
172

173
/*
174
 * Given a string that may have a decimal point in the current
175
 * locale, change it back to a dot.  Since the string cannot get
176
 * longer, no need for a maximum buffer size parameter.
177
 */
178
static void
179 1
change_decimal_from_locale_to_dot(char* buffer)
180
{
181 1
    struct lconv *locale_data = localeconv();
182 1
    const char *decimal_point = locale_data->decimal_point;
183

184 1
    if (decimal_point[0] != '.' || decimal_point[1] != 0) {
185 0
        size_t decimal_point_len = strlen(decimal_point);
186

187 0
        if (*buffer == '+' || *buffer == '-') {
188 0
            buffer++;
189
        }
190 0
        while (isdigit(Py_CHARMASK(*buffer))) {
191 0
            buffer++;
192
        }
193 0
        if (strncmp(buffer, decimal_point, decimal_point_len) == 0) {
194 0
            *buffer = '.';
195 0
            buffer++;
196 0
            if (decimal_point_len > 1) {
197
                /* buffer needs to get smaller */
198 0
                size_t rest_len = strlen(buffer + (decimal_point_len - 1));
199 0
                memmove(buffer, buffer + (decimal_point_len - 1), rest_len);
200 0
                buffer[rest_len] = 0;
201
            }
202
        }
203
    }
204
}
205

206
/*
207
 * Check that the format string is a valid one for NumPyOS_ascii_format*
208
 */
209
static int
210 1
check_ascii_format(const char *format)
211
{
212
    char format_char;
213 1
    size_t format_len = strlen(format);
214

215
    /* The last character in the format string must be the format char */
216 1
    format_char = format[format_len - 1];
217

218 1
    if (format[0] != '%') {
219
        return -1;
220
    }
221

222
    /*
223
     * I'm not sure why this test is here.  It's ensuring that the format
224
     * string after the first character doesn't have a single quote, a
225
     * lowercase l, or a percent. This is the reverse of the commented-out
226
     * test about 10 lines ago.
227
     */
228 1
    if (strpbrk(format + 1, "'l%")) {
229
        return -1;
230
    }
231

232
    /*
233
     * Also curious about this function is that it accepts format strings
234
     * like "%xg", which are invalid for floats.  In general, the
235
     * interface to this function is not very good, but changing it is
236
     * difficult because it's a public API.
237
     */
238 1
    if (!(format_char == 'e' || format_char == 'E'
239 1
          || format_char == 'f' || format_char == 'F'
240 1
          || format_char == 'g' || format_char == 'G')) {
241
        return -1;
242
    }
243

244 1
    return 0;
245
}
246

247
/*
248
 * Fix the generated string: make sure the decimal is ., that exponent has a
249
 * minimal number of digits, and that it has a decimal + one digit after that
250
 * decimal if decimal argument != 0 (Same effect that 'Z' format in
251
 * PyOS_ascii_formatd)
252
 */
253
static char*
254 1
fix_ascii_format(char* buf, size_t buflen, int decimal)
255
{
256
    /*
257
     * Get the current locale, and find the decimal point string.
258
     * Convert that string back to a dot.
259
     */
260 1
    change_decimal_from_locale_to_dot(buf);
261

262
    /*
263
     * If an exponent exists, ensure that the exponent is at least
264
     * MIN_EXPONENT_DIGITS digits, providing the buffer is large enough
265
     * for the extra zeros.  Also, if there are more than
266
     * MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get
267
     * back to MIN_EXPONENT_DIGITS
268
     */
269 1
    ensure_minimum_exponent_length(buf, buflen);
270

271 1
    if (decimal != 0) {
272 0
        ensure_decimal_point(buf, buflen);
273
    }
274

275 1
    return buf;
276
}
277

278
/*
279
 * NumPyOS_ascii_format*:
280
 *      - buffer: A buffer to place the resulting string in
281
 *      - buf_size: The length of the buffer.
282
 *      - format: The printf()-style format to use for the code to use for
283
 *      converting.
284
 *      - value: The value to convert
285
 *      - decimal: if != 0, always has a decimal, and at leasat one digit after
286
 *      the decimal. This has the same effect as passing 'Z' in the original
287
 *      PyOS_ascii_formatd
288
 *
289
 * This is similar to PyOS_ascii_formatd in python > 2.6, except that it does
290
 * not handle 'n', and handles nan / inf.
291
 *
292
 * Converts a #gdouble to a string, using the '.' as decimal point. To format
293
 * the number you pass in a printf()-style format string. Allowed conversion
294
 * specifiers are 'e', 'E', 'f', 'F', 'g', 'G'.
295
 *
296
 * Return value: The pointer to the buffer with the converted string.
297
 */
298
#define ASCII_FORMAT(type, suffix, print_type)                          \
299
    NPY_NO_EXPORT char*                                                 \
300
    NumPyOS_ascii_format ## suffix(char *buffer, size_t buf_size,       \
301
                                   const char *format,                  \
302
                                   type val, int decimal)               \
303
    {                                                                   \
304
        if (npy_isfinite(val)) {                                        \
305
            if (check_ascii_format(format)) {                           \
306
                return NULL;                                            \
307
            }                                                           \
308
            PyOS_snprintf(buffer, buf_size, format, (print_type)val);   \
309
            return fix_ascii_format(buffer, buf_size, decimal);         \
310
        }                                                               \
311
        else if (npy_isnan(val)){                                       \
312
            if (buf_size < 4) {                                         \
313
                return NULL;                                            \
314
            }                                                           \
315
            strcpy(buffer, "nan");                                      \
316
        }                                                               \
317
        else {                                                          \
318
            if (npy_signbit(val)) {                                     \
319
                if (buf_size < 5) {                                     \
320
                    return NULL;                                        \
321
                }                                                       \
322
                strcpy(buffer, "-inf");                                 \
323
            }                                                           \
324
            else {                                                      \
325
                if (buf_size < 4) {                                     \
326
                    return NULL;                                        \
327
                }                                                       \
328
                strcpy(buffer, "inf");                                  \
329
            }                                                           \
330
        }                                                               \
331
        return buffer;                                                  \
332
    }
333

334 0
ASCII_FORMAT(float, f, float)
335 1
ASCII_FORMAT(double, d, double)
336
#ifndef FORCE_NO_LONG_DOUBLE_FORMATTING
337 0
ASCII_FORMAT(long double, l, long double)
338
#else
339
ASCII_FORMAT(long double, l, double)
340
#endif
341

342
/*
343
 * NumPyOS_ascii_isspace:
344
 *
345
 * Same as isspace under C locale
346
 */
347
NPY_NO_EXPORT int
348 1
NumPyOS_ascii_isspace(int c)
349
{
350 1
    return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t'
351 1
                    || c == '\v';
352
}
353

354

355
/*
356
 * NumPyOS_ascii_isalpha:
357
 *
358
 * Same as isalpha under C locale
359
 */
360
static int
361
NumPyOS_ascii_isalpha(char c)
362
{
363 1
    return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
364
}
365

366

367
/*
368
 * NumPyOS_ascii_isdigit:
369
 *
370
 * Same as isdigit under C locale
371
 */
372
static int
373
NumPyOS_ascii_isdigit(char c)
374
{
375 1
    return (c >= '0' && c <= '9');
376
}
377

378

379
/*
380
 * NumPyOS_ascii_isalnum:
381
 *
382
 * Same as isalnum under C locale
383
 */
384
static int
385
NumPyOS_ascii_isalnum(char c)
386
{
387 1
    return NumPyOS_ascii_isdigit(c) || NumPyOS_ascii_isalpha(c);
388
}
389

390

391
/*
392
 * NumPyOS_ascii_tolower:
393
 *
394
 * Same as tolower under C locale
395
 */
396
static int
397
NumPyOS_ascii_tolower(int c)
398
{
399 1
    if (c >= 'A' && c <= 'Z') {
400 1
        return c + ('a'-'A');
401
    }
402
    return c;
403
}
404

405

406
/*
407
 * NumPyOS_ascii_strncasecmp:
408
 *
409
 * Same as strncasecmp under C locale
410
 */
411
static int
412 1
NumPyOS_ascii_strncasecmp(const char* s1, const char* s2, size_t len)
413
{
414 1
    while (len > 0 && *s1 != '\0' && *s2 != '\0') {
415 1
        int diff = NumPyOS_ascii_tolower(*s1) - NumPyOS_ascii_tolower(*s2);
416 1
        if (diff != 0) {
417
            return diff;
418
        }
419 1
        ++s1;
420 1
        ++s2;
421 1
        --len;
422
    }
423 1
    if (len > 0) {
424 1
        return *s1 - *s2;
425
    }
426
    return 0;
427
}
428

429
/*
430
 * NumPyOS_ascii_strtod_plain:
431
 *
432
 * PyOS_ascii_strtod work-alike, with no enhanced features,
433
 * for forward compatibility with Python >= 2.7
434
 */
435
static double
436 1
NumPyOS_ascii_strtod_plain(const char *s, char** endptr)
437
{
438
    double result;
439
    NPY_ALLOW_C_API_DEF;
440 1
    NPY_ALLOW_C_API;
441 1
    result = PyOS_string_to_double(s, endptr, NULL);
442 1
    if (PyErr_Occurred()) {
443 1
        if (endptr) {
444 1
            *endptr = (char*)s;
445
        }
446 1
        PyErr_Clear();
447
    }
448 1
    NPY_DISABLE_C_API;
449 1
    return result;
450
}
451

452
/*
453
 * NumPyOS_ascii_strtod:
454
 *
455
 * Work around bugs in PyOS_ascii_strtod
456
 */
457
NPY_NO_EXPORT double
458 1
NumPyOS_ascii_strtod(const char *s, char** endptr)
459
{
460
    const char *p;
461
    double result;
462

463 1
    while (NumPyOS_ascii_isspace(*s)) {
464 1
        ++s;
465
    }
466

467
    /*
468
     * ##1
469
     *
470
     * Recognize POSIX inf/nan representations on all platforms.
471
     */
472 1
    p = s;
473 1
    result = 1.0;
474 1
    if (*p == '-') {
475 1
        result = -1.0;
476 1
        ++p;
477
    }
478 1
    else if (*p == '+') {
479 1
        ++p;
480
    }
481 1
    if (NumPyOS_ascii_strncasecmp(p, "nan", 3) == 0) {
482 1
        p += 3;
483 1
        if (*p == '(') {
484 1
            ++p;
485 1
            while (NumPyOS_ascii_isalnum(*p) || *p == '_') {
486 1
                ++p;
487
            }
488 1
            if (*p == ')') {
489 1
                ++p;
490
            }
491
        }
492 1
        if (endptr != NULL) {
493 1
            *endptr = (char*)p;
494
        }
495
        return NPY_NAN;
496
    }
497 1
    else if (NumPyOS_ascii_strncasecmp(p, "inf", 3) == 0) {
498 1
        p += 3;
499 1
        if (NumPyOS_ascii_strncasecmp(p, "inity", 5) == 0) {
500 1
            p += 5;
501
        }
502 1
        if (endptr != NULL) {
503 1
            *endptr = (char*)p;
504
        }
505 1
        return result*NPY_INFINITY;
506
    }
507
    /* End of ##1 */
508

509 1
    return NumPyOS_ascii_strtod_plain(s, endptr);
510
}
511

512
NPY_NO_EXPORT long double
513 1
NumPyOS_ascii_strtold(const char *s, char** endptr)
514
{
515
    const char *p;
516
    long double result;
517
#ifdef HAVE_STRTOLD_L
518
    locale_t clocale;
519
#endif
520

521 1
    while (NumPyOS_ascii_isspace(*s)) {
522 1
        ++s;
523
    }
524

525
    /*
526
     * ##1
527
     *
528
     * Recognize POSIX inf/nan representations on all platforms.
529
     */
530 1
    p = s;
531 1
    result = 1.0;
532 1
    if (*p == '-') {
533 1
        result = -1.0;
534 1
        ++p;
535
    }
536 1
    else if (*p == '+') {
537 0
        ++p;
538
    }
539 1
    if (NumPyOS_ascii_strncasecmp(p, "nan", 3) == 0) {
540 1
        p += 3;
541 1
        if (*p == '(') {
542 0
            ++p;
543 0
            while (NumPyOS_ascii_isalnum(*p) || *p == '_') {
544 0
                ++p;
545
            }
546 0
            if (*p == ')') {
547 0
                ++p;
548
            }
549
        }
550 1
        if (endptr != NULL) {
551 1
            *endptr = (char*)p;
552
        }
553
        return NPY_NAN;
554
    }
555 1
    else if (NumPyOS_ascii_strncasecmp(p, "inf", 3) == 0) {
556 1
        p += 3;
557 1
        if (NumPyOS_ascii_strncasecmp(p, "inity", 5) == 0) {
558 0
            p += 5;
559
        }
560 1
        if (endptr != NULL) {
561 1
            *endptr = (char*)p;
562
        }
563 1
        return result*NPY_INFINITY;
564
    }
565
    /* End of ##1 */
566

567
#ifdef HAVE_STRTOLD_L
568 1
    clocale = newlocale(LC_ALL_MASK, "C", NULL);
569 1
    if (clocale) {
570 1
        errno = 0;
571 1
        result = strtold_l(s, endptr, clocale);
572 1
        freelocale(clocale);
573
    }
574
    else {
575 0
        if (endptr != NULL) {
576 0
            *endptr = (char*)s;
577
        }
578
        result = 0;
579
    }
580
    return result;
581
#else
582
    return NumPyOS_ascii_strtod(s, endptr);
583
#endif
584
}
585

586
/*
587
 * read_numberlike_string:
588
 *      * fp: FILE pointer
589
 *      * value: Place to store the value read
590
 *
591
 * Read what looks like valid numeric input and store it in a buffer
592
 * for later parsing as a number.
593
 *
594
 * Similarly to fscanf, this function always consumes leading whitespace,
595
 * and any text that could be the leading part in valid input.
596
 *
597
 * Return value: similar to fscanf.
598
 *      * 0 if no number read,
599
 *      * 1 if a number read,
600
 *      * EOF if end-of-file met before reading anything.
601
 */
602
static int
603 1
read_numberlike_string(FILE *fp, char *buffer, size_t buflen)
604
{
605

606
    char *endp;
607
    char *p;
608
    int c;
609
    int ok;
610

611
    /*
612
     * Fill buffer with the leftmost matching part in regexp
613
     *
614
     *     \s*[+-]? ( [0-9]*\.[0-9]+([eE][+-]?[0-9]+)
615
     *              | nan  (  \([:alphanum:_]*\) )?
616
     *              | inf(inity)?
617
     *              )
618
     *
619
     * case-insensitively.
620
     *
621
     * The "do { ... } while (0)" wrapping in macros ensures that they behave
622
     * properly eg. in "if ... else" structures.
623
     */
624

625
#define END_MATCH()                                                         \
626
        goto buffer_filled
627

628
#define NEXT_CHAR()                                                         \
629
        do {                                                                \
630
            if (c == EOF || endp >= buffer + buflen - 1)            \
631
                END_MATCH();                                                \
632
            *endp++ = (char)c;                                              \
633
            c = getc(fp);                                                   \
634
        } while (0)
635

636
#define MATCH_ALPHA_STRING_NOCASE(string)                                   \
637
        do {                                                                \
638
            for (p=(string); *p!='\0' && (c==*p || c+('a'-'A')==*p); ++p)   \
639
                NEXT_CHAR();                                                \
640
            if (*p != '\0') END_MATCH();                                    \
641
        } while (0)
642

643
#define MATCH_ONE_OR_NONE(condition)                                        \
644
        do { if (condition) NEXT_CHAR(); } while (0)
645

646
#define MATCH_ONE_OR_MORE(condition)                                        \
647
        do {                                                                \
648
            ok = 0;                                                         \
649
            while (condition) { NEXT_CHAR(); ok = 1; }                      \
650
            if (!ok) END_MATCH();                                           \
651
        } while (0)
652

653
#define MATCH_ZERO_OR_MORE(condition)                                       \
654
        while (condition) { NEXT_CHAR(); }
655

656
    /* 1. emulate fscanf EOF handling */
657 1
    c = getc(fp);
658 1
    if (c == EOF) {
659
        return EOF;
660
    }
661
    /* 2. consume leading whitespace unconditionally */
662 1
    while (NumPyOS_ascii_isspace(c)) {
663 1
        c = getc(fp);
664
    }
665

666
    /* 3. start reading matching input to buffer */
667 1
    endp = buffer;
668

669
    /* 4.1 sign (optional) */
670 1
    MATCH_ONE_OR_NONE(c == '+' || c == '-');
671

672
    /* 4.2 nan, inf, infinity; [case-insensitive] */
673 1
    if (c == 'n' || c == 'N') {
674 1
        NEXT_CHAR();
675 1
        MATCH_ALPHA_STRING_NOCASE("an");
676

677
        /* accept nan([:alphanum:_]*), similarly to strtod */
678 1
        if (c == '(') {
679 1
            NEXT_CHAR();
680 1
            MATCH_ZERO_OR_MORE(NumPyOS_ascii_isalnum(c) || c == '_');
681 1
            if (c == ')') {
682 1
                NEXT_CHAR();
683
            }
684
        }
685
        END_MATCH();
686
    }
687 1
    else if (c == 'i' || c == 'I') {
688 1
        NEXT_CHAR();
689 1
        MATCH_ALPHA_STRING_NOCASE("nfinity");
690
        END_MATCH();
691
    }
692

693
    /* 4.3 mantissa */
694 1
    MATCH_ZERO_OR_MORE(NumPyOS_ascii_isdigit(c));
695

696 1
    if (c == '.') {
697 1
        NEXT_CHAR();
698 1
        MATCH_ONE_OR_MORE(NumPyOS_ascii_isdigit(c));
699
    }
700

701
    /* 4.4 exponent */
702 1
    if (c == 'e' || c == 'E') {
703 1
        NEXT_CHAR();
704 1
        MATCH_ONE_OR_NONE(c == '+' || c == '-');
705 1
        MATCH_ONE_OR_MORE(NumPyOS_ascii_isdigit(c));
706
    }
707

708
    END_MATCH();
709

710 1
buffer_filled:
711

712 1
    ungetc(c, fp);
713 1
    *endp = '\0';
714

715
    /* return 1 if something read, else 0 */
716 1
    return (buffer == endp) ? 0 : 1;
717
}
718

719
#undef END_MATCH
720
#undef NEXT_CHAR
721
#undef MATCH_ALPHA_STRING_NOCASE
722
#undef MATCH_ONE_OR_NONE
723
#undef MATCH_ONE_OR_MORE
724
#undef MATCH_ZERO_OR_MORE
725

726
/*
727
 * NumPyOS_ascii_ftolf:
728
 *      * fp: FILE pointer
729
 *      * value: Place to store the value read
730
 *
731
 * Similar to PyOS_ascii_strtod, except that it reads input from a file.
732
 *
733
 * Similarly to fscanf, this function always consumes leading whitespace,
734
 * and any text that could be the leading part in valid input.
735
 *
736
 * Return value: similar to fscanf.
737
 *      * 0 if no number read,
738
 *      * 1 if a number read,
739
 *      * EOF if end-of-file met before reading anything.
740
 */
741
NPY_NO_EXPORT int
742 1
NumPyOS_ascii_ftolf(FILE *fp, double *value)
743
{
744
    char buffer[FLOAT_FORMATBUFLEN + 1];
745
    char *p;
746
    int r;
747

748 1
    r = read_numberlike_string(fp, buffer, FLOAT_FORMATBUFLEN+1);
749

750 1
    if (r != EOF && r != 0) {
751 1
        *value = NumPyOS_ascii_strtod(buffer, &p);
752 1
        r = (p == buffer) ? 0 : 1;
753
    }
754 1
    return r;
755
}
756

757
NPY_NO_EXPORT int
758 1
NumPyOS_ascii_ftoLf(FILE *fp, long double *value)
759
{
760
    char buffer[FLOAT_FORMATBUFLEN + 1];
761
    char *p;
762
    int r;
763

764 1
    r = read_numberlike_string(fp, buffer, FLOAT_FORMATBUFLEN+1);
765

766 1
    if (r != EOF && r != 0) {
767 1
        *value = NumPyOS_ascii_strtold(buffer, &p);
768 1
        r = (p == buffer) ? 0 : 1;
769
    }
770 1
    return r;
771
}
772

773
NPY_NO_EXPORT npy_longlong
774 1
NumPyOS_strtoll(const char *str, char **endptr, int base)
775
{
776
#if defined HAVE_STRTOLL
777 1
    return strtoll(str, endptr, base);
778
#elif defined _MSC_VER
779
    return _strtoi64(str, endptr, base);
780
#else
781
    /* ok on 64 bit posix */
782
    return PyOS_strtol(str, endptr, base);
783
#endif
784
}
785

786
NPY_NO_EXPORT npy_ulonglong
787 0
NumPyOS_strtoull(const char *str, char **endptr, int base)
788
{
789
#if defined HAVE_STRTOULL
790 0
    return strtoull(str, endptr, base);
791
#elif defined _MSC_VER
792
    return _strtoui64(str, endptr, base);
793
#else
794
    /* ok on 64 bit posix */
795
    return PyOS_strtoul(str, endptr, base);
796
#endif
797
}
798

799

Read our documentation on viewing source code .

Loading