source: libcfa/src/iostream.cfa @ 4817662

Last change on this file since 4817662 was 0860d9c, checked in by Michael Brooks <mlbrooks@…>, 9 months ago

Fix read-to-variable-length-string cases when internal buffer fills.

Also fix read-to-cstring ability to give no-exception cases when an entire buffer fills.

The added test cases run, and fail, when run against prior libcfa.
Doing so illustrates a CFA-string-level bug.
Doing so illustrates a C-string-level changed semantics.

At the CFA-string level, the bug was, when reading strings of just the right length,
what should be two reads ("abc" then "def") gets mashed into one ("abcdef").
These cases are clearly bugs because a test program that just echoes chuncks of delimeted input would do so inaccurately.
They're just hard to drive because the relevant chunk lengths are implementation-dependent, and sometimes big.

At the C-string level, the semantic change concerns when to throw the cstring_length exception.
By this change, make the original semantics,
"An exception means the maximum number of characters was read," into
"An exception means that if the buffer were larger, then more characters would have been read."

The added test cases cover the respective stop conditions for manipulator state "%s", include, exclude, getline, and getline/delimiter.

  • Property mode set to 100644
File size: 38.8 KB
Line 
1
2//
3// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
4//
5// The contents of this file are covered under the licence agreement in the
6// file "LICENCE" distributed with Cforall.
7//
8// iostream.cfa --
9//
10// Author           : Peter A. Buhr
11// Created On       : Wed May 27 17:56:53 2015
12// Last Modified By : Peter A. Buhr
13// Last Modified On : Sat Sep  2 14:42:01 2023
14// Update Count     : 1561
15//
16
17#include "iostream.hfa"
18
19#include <stdio.h>
20#include <stdbool.h>                                                                    // true/false
21#include <stdint.h>                                                                             // UINT64_MAX
22#include <float.h>                                                                              // DBL_DIG, LDBL_DIG
23#include <complex.h>                                                                    // creal, cimag
24#include <ctype.h>                                                                              // isspace
25//#include <stdio.h>
26
27extern "C" {
28extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
29extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
30extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
31extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
32extern char *strchr(const char *str, int ch);
33} // extern "C"
34
35#include "math.hfa"                                                                             // isfinite, floor, ceiling_div
36#include "bitmanip.hfa"                                                                 // high1
37
38#pragma GCC visibility push(default)
39
40
41// *********************************** ostream ***********************************
42
43
44forall( ostype & | basic_ostream( ostype ) ) {
45        ostype & ?|?( ostype & os, bool b ) {
46                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
47                fmt( os, "%s", b ? "true" : "false" );
48                return os;
49        } // ?|?
50        OSTYPE_VOID_IMPL( bool )
51
52        ostype & ?|?( ostype & os, char c ) {
53                fmt( os, "%c", c );
54                if ( c == '\n' ) setNL$( os, true );
55                return nosep( os );
56        } // ?|?
57        OSTYPE_VOID_IMPL( char )
58
59        ostype & ?|?( ostype & os, signed char sc ) {
60                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
61                fmt( os, "%'hhd", sc );
62                return os;
63        } // ?|?
64        OSTYPE_VOID_IMPL( signed char )
65
66        ostype & ?|?( ostype & os, unsigned char usc ) {
67                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
68                fmt( os, "%'hhu", usc );
69                return os;
70        } // ?|?
71        OSTYPE_VOID_IMPL( unsigned char )
72
73        ostype & ?|?( ostype & os, short int si ) {
74                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
75                fmt( os, "%'hd", si );
76                return os;
77        } // ?|?
78        OSTYPE_VOID_IMPL( short int )
79
80        ostype & ?|?( ostype & os, unsigned short int usi ) {
81                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
82                fmt( os, "%'hu", usi );
83                return os;
84        } // ?|?
85        OSTYPE_VOID_IMPL( unsigned short int )
86
87        ostype & ?|?( ostype & os, int i ) {
88                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
89                fmt( os, "%'d", i );
90                return os;
91        } // ?|?
92        OSTYPE_VOID_IMPL( int )
93
94        ostype & ?|?( ostype & os, unsigned int ui ) {
95                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
96                fmt( os, "%'u", ui );
97                return os;
98        } // ?|?
99        OSTYPE_VOID_IMPL( unsigned int )
100
101        ostype & ?|?( ostype & os, long int li ) {
102                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
103                fmt( os, "%'ld", li );
104                return os;
105        } // ?|?
106        OSTYPE_VOID_IMPL( long int )
107
108        ostype & ?|?( ostype & os, unsigned long int uli ) {
109                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
110                fmt( os, "%'lu", uli );
111                return os;
112        } // ?|?
113        OSTYPE_VOID_IMPL( unsigned long int )
114
115        ostype & ?|?( ostype & os, long long int lli ) {
116                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
117                fmt( os, "%'lld", lli );
118                return os;
119        } // ?|?
120        OSTYPE_VOID_IMPL( long long int )
121
122        ostype & ?|?( ostype & os, unsigned long long int ulli ) {
123                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
124                fmt( os, "%'llu", ulli );
125                return os;
126        } // ?|?
127        OSTYPE_VOID_IMPL( unsigned long long int )
128
129        #if defined( __SIZEOF_INT128__ )
130        //      UINT64_MAX 18_446_744_073_709_551_615_ULL
131        #define P10_UINT64 10_000_000_000_000_000_000_ULL       // 19 zeroes
132
133        static inline void base10_128( ostype & os, unsigned int128 val ) {
134                #if defined(__GNUC__) && __GNUC_PREREQ(7,0)             // gcc version >= 7
135                if ( val > P10_UINT64 ) {
136                #else
137                if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug
138                #endif // __GNUC_PREREQ(7,0)
139                        base10_128( os, val / P10_UINT64 );                     // recursive
140                        fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) );
141                } else {
142                        fmt( os, "%lu", (uint64_t)val );
143                } // if
144        } // base10_128
145
146        static inline void base10_128( ostype & os, int128 val ) {
147                if ( val < 0 ) {
148                        fmt( os, "-" );                                                         // leading negative sign
149                        val = -val;
150                } // if
151                base10_128( os, (unsigned int128)val );                 // print zero/positive value
152        } // base10_128
153
154        ostype & ?|?( ostype & os, int128 llli ) {
155                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
156                base10_128( os, llli );
157                return os;
158        } // ?|?
159        OSTYPE_VOID_IMPL( int128 )
160
161        ostype & ?|?( ostype & os, unsigned int128 ullli ) {
162                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
163                base10_128( os, ullli );
164                return os;
165        } // ?|?
166        OSTYPE_VOID_IMPL( unsigned int128 )
167        #endif // __SIZEOF_INT128__
168
169        #define PRINT_WITH_DP( os, format, val, ... ) \
170                { \
171                        enum { size = 48 }; \
172                        char buf[size]; \
173                        int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
174                        fmt( os, "%s", buf ); \
175                        if ( isfinite( val ) ) { /* if number, print decimal point when no fraction or exponent */ \
176                                for ( i; 0 ~ @ ) { \
177                                        if ( i == len ) { fmt( os, "." ); break; } \
178                                        if ( buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E' || \
179                                                 buf[i] == 'p' || buf[i] == 'P' ) break; /* decimal point or scientific ? */ \
180                                } /* for */ \
181                        } /* if */ \
182                }
183
184        ostype & ?|?( ostype & os, float f ) {
185                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
186                PRINT_WITH_DP( os, "%'g", f );
187                return os;
188        } // ?|?
189        OSTYPE_VOID_IMPL( float )
190
191        ostype & ?|?( ostype & os, double d ) {
192                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
193                PRINT_WITH_DP( os, "%'.*lg", d, DBL_DIG );
194                return os;
195        } // ?|?
196        OSTYPE_VOID_IMPL( double )
197
198        ostype & ?|?( ostype & os, long double ld ) {
199                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
200                PRINT_WITH_DP( os, "%'.*Lg", ld, LDBL_DIG );
201                return os;
202        } // ?|?
203        OSTYPE_VOID_IMPL( long double )
204
205        ostype & ?|?( ostype & os, float _Complex fc ) {
206                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
207//              os | crealf( fc ) | nonl;
208                PRINT_WITH_DP( os, "%'g", crealf( fc ) );
209                PRINT_WITH_DP( os, "%'+g", cimagf( fc ) );
210                fmt( os, "i" );
211                return os;
212        } // ?|?
213        OSTYPE_VOID_IMPL( float _Complex )
214
215        ostype & ?|?( ostype & os, double _Complex dc ) {
216                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
217//              os | creal( dc ) | nonl;
218                PRINT_WITH_DP( os, "%'.*lg", creal( dc ), DBL_DIG );
219                PRINT_WITH_DP( os, "%'+.*lg", cimag( dc ), DBL_DIG );
220                fmt( os, "i" );
221                return os;
222        } // ?|?
223        OSTYPE_VOID_IMPL( double _Complex )
224
225        ostype & ?|?( ostype & os, long double _Complex ldc ) {
226                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
227//              os | creall( ldc ) || nonl;
228                PRINT_WITH_DP( os, "%'.*Lg", creall( ldc ), LDBL_DIG );
229                PRINT_WITH_DP( os, "%'+.*Lg", cimagl( ldc ), LDBL_DIG );
230                fmt( os, "i" );
231                return os;
232        } // ?|?
233        OSTYPE_VOID_IMPL( long double _Complex )
234
235        ostype & ?|?( ostype & os, const char s[] ) {
236                enum { Open = 1, Close, OpenClose };
237                static const unsigned char mask[256] @= {               // 256 covers all Latin-1 characters
238                        // opening delimiters, no space after
239                        ['('] : Open, ['['] : Open, ['{'] : Open,
240                        ['='] : Open, ['$'] : Open, [(unsigned char)'£'] : Open, [(unsigned char)'¥'] : Open,
241                        [(unsigned char)'¡'] : Open, [(unsigned char)'¿'] : Open, [(unsigned char)'«'] : Open,
242                        // closing delimiters, no space before
243                        [','] : Close, ['.'] : Close, [';'] : Close, ['!'] : Close, ['?'] : Close,
244                        ['%'] : Close, [(unsigned char)'¢'] : Close, [(unsigned char)'»'] : Close,
245                        [')'] : Close, [']'] : Close, ['}'] : Close,
246                        // opening-closing delimiters, no space before or after
247                        ['\''] : OpenClose, ['`'] : OpenClose, ['"'] : OpenClose, [':'] : OpenClose,
248                        [' '] : OpenClose, ['\f'] : OpenClose, ['\n'] : OpenClose, ['\r'] : OpenClose, ['\t'] : OpenClose, ['\v'] : OpenClose, // isspace
249                }; // mask
250
251          if ( s == 0p ) { fmt( os, "%s", "0p" ); return os; } // null pointer
252          if ( s[0] == '\0' ) { nosep( os ); return os; }       // null string => no leading/trailing separator
253
254                // first character IS NOT spacing or closing punctuation => add left separator
255                unsigned char ch = s[0];                                                // must make unsigned
256                if ( sepPrt$( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) {
257                        fmt( os, "%s", sepGetCur$( os ) );
258                } // if
259
260                // if string starts line, must reset to determine open state because separator is off
261                sepReset$( os );                                                                // reset separator
262
263                // last character IS spacing or opening punctuation => turn off separator for next item
264                int len = strlen( s );
265                ch = s[len - 1];                                                                // must make unsigned
266                fmt( os, "%s", s );                                                             // fmt resets seperator, but reset it again
267                if ( sepPrt$( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) {
268                        sep( os );
269                } else {
270                        nosep( os );
271                } // if
272                if ( ch == '\n' ) setNL$( os, true );                   // check *AFTER* sepPrt$ call above as it resets NL flag
273                return os;
274//              return write( os, s, len );
275        } // ?|?
276        OSTYPE_VOID_IMPL( const char * )
277
278//      ostype & ?|?( ostype & os, const char16_t s[] ) {
279//              if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
280//              fmt( os, "%ls", s );
281//              return os;
282//      } // ?|?
283
284// #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous
285//      ostype & ?|?( ostype & os, const char32_t s[] ) {
286//              if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
287//              fmt( os, "%ls", s );
288//              return os;
289//      } // ?|?
290// #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 )
291
292//      ostype & ?|?( ostype & os, const wchar_t s[] ) {
293//              if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
294//              fmt( os, "%ls", s );
295//              return os;
296//      } // ?|?
297
298        ostype & ?|?( ostype & os, const void * p ) {
299                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
300                fmt( os, "%p", p );
301                return os;
302        } // ?|?
303        OSTYPE_VOID_IMPL( const void * )
304
305        // manipulators
306        ostype & ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
307                return manip( os );
308        } // ?|?
309        void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
310                manip( os );
311                if ( getPrt$( os ) ) ends( os );                                // something printed ?
312                setPrt$( os, false );                                                   // turn off
313        } // ?|?
314
315        ostype & nl( ostype & os ) {
316                (ostype &)(os | '\n');
317                setPrt$( os, false );                                                   // turn off
318                setNL$( os, true );
319                return nosep( os );                                                             // prepare for next line
320        } // nl
321
322        ostype & nonl( ostype & os ) {
323                setPrt$( os, false );                                                   // turn off
324                return os;
325        } // nonl
326
327        ostype & nlOn( ostype & os ) {
328                nlOn( os );                                                                             // call void returning
329                return os;
330        } // nlOn
331
332        ostype & nlOff( ostype & os ) {
333                nlOff( os );                                                                    // call void returning
334                return os;
335        } // nlOff
336
337        ostype & sepVal( ostype & os ) {
338                return (ostype &)(os | sepGet( os ));
339        } // sepVal
340
341        ostype & sepTupleVal( ostype & os ) {
342                return os | sepGetTuple( os );
343        } // sepTupleVal
344
345        ostype & sep( ostype & os ) {
346                sep( os );                                                                              // call void returning
347                return os;
348        } // sep
349
350        ostype & nosep( ostype & os ) {
351                nosep( os );                                                                    // call void returning
352                return os;
353        } // nosep
354
355        ostype & sepOn( ostype & os ) {
356                sepOn( os );                                                                    // call void returning
357                return os;
358        } // sepOn
359
360        ostype & sepOff( ostype & os ) {
361                sepOff( os );                                                                   // call void returning
362                return os;
363        } // sepOff
364} // distribution
365
366// tuples
367forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {
368        ostype & ?|?( ostype & os, T arg, Params rest ) {
369                (ostype &)(os | arg);                                                   // print first argument
370                sepSetCur$( os, sepGetTuple( os ) );                    // switch to tuple separator
371                (ostype &)(os | rest);                                                  // print remaining arguments
372                sepSetCur$( os, sepGet( os ) );                                 // switch to regular separator
373                return os;
374        } // ?|?
375        void ?|?( ostype & os, T arg, Params rest ) {
376                // (ostype &)(?|?( os, arg, rest )); ends( os );
377                (ostype &)(os | arg);                                                   // print first argument
378                sepSetCur$( os, sepGetTuple( os ) );                    // switch to tuple separator
379                (ostype &)(os | rest);                                                  // print remaining arguments
380                sepSetCur$( os, sepGet( os ) );                                 // switch to regular separator
381                ends( os );
382        } // ?|?
383} // distribution
384
385// writes the range [begin, end) to the given stream
386forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) {
387        void write( iterator_type begin, iterator_type end, ostype & os ) {
388                void print( elt_type i ) { os | i; }
389                for_each( begin, end, print );
390        } // ?|?
391
392        void write_reverse( iterator_type begin, iterator_type end, ostype & os ) {
393                void print( elt_type i ) { os | i; }
394                for_each_reverse( begin, end, print );
395        } // ?|?
396} // distribution
397
398// *********************************** manipulators ***********************************
399
400// *********************************** integral ***********************************
401
402static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
403static const char * longbin[]  = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
404
405// Default prefix for non-decimal prints is 0b, 0, 0x.
406#define INTEGRAL_FMT_IMPL( T, IFMTNP, IFMTP ) \
407forall( ostype & | basic_ostream( ostype ) ) { \
408        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
409                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \
410\
411                if ( f.base == 'b' || f.base == 'B' ) {                 /* bespoke binary format */ \
412                        int bits = high1( f.val );                                      /* position of most significant bit */ \
413                        if ( bits == 0 ) bits = 1;                                      /* 0 value => force one bit to print */ \
414                        int spaces; \
415                        if ( ! f.flags.left ) {                                         /* right justified ? */ \
416                                /* Note, base prefix then zero padding or spacing then prefix. */ \
417                                if ( f.flags.pc ) { \
418                                        spaces = f.wd - f.pc; \
419                                        if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
420                                        if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
421                                        if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
422                                        spaces = f.pc - bits; \
423                                        if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
424                                } else { \
425                                        spaces = f.wd - bits; \
426                                        if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
427                                        if ( f.flags.pad0 ) { \
428                                                if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
429                                                if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
430                                        } else { \
431                                                if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
432                                                if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
433                                        } /* if */ \
434                                } /* if */ \
435                        } else { \
436                                if ( ! f.flags.nobsdp ) fmt( os, "0%c", f.base ); \
437                                if ( f.flags.pc ) { \
438                                        spaces = f.pc - bits; \
439                                        if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
440                                        spaces = f.wd - f.pc; \
441                                } else { /* pad0 flag ignored with left flag */ \
442                                        spaces = f.wd - bits; \
443                                } /* if */ \
444                                if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
445                        } /* if */ \
446                        int shift = floor( bits - 1, 4 ); \
447                        typeof( f.val ) temp = f.val; \
448                        fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \
449                        for () { \
450                                shift -= 4; \
451                          if ( shift < 0 ) break; \
452                                temp = f.val; \
453                                fmt( os, "%s", longbin[(temp >> shift) & 0xf] ); \
454                        } /* for */ \
455                        if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \
456                        return os; \
457                } /* if */ \
458\
459                char fmtstr[sizeof(IFMTP)];                                             /* sizeof includes '\0' */ \
460                if ( ! f.flags.pc ) memcpy( &fmtstr, IFMTNP, sizeof(IFMTNP) ); \
461                else memcpy( &fmtstr, IFMTP, sizeof(IFMTP) ); \
462                int star = 5;                                                                   /* position before first '*' */ \
463\
464                /* Insert flags into spaces before '*', from right to left. */ \
465                if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \
466                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
467                if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
468                if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \
469                fmtstr[star] = '\''; star -= 1;                                 /* locale */ \
470                fmtstr[star] = '%'; \
471\
472                /* Special case printing 0 in hexadecimal as printf does not put the base. */ \
473                if ( (f.base == 'x' | f.base == 'X') && ! f.flags.nobsdp && f.val == 0 ) { \
474                        fmt( os, f.base == 'x' ? "0x" : "0X" ); \
475                        f.wd -= 2; \
476                        if ( f.wd < 0 ) f.wd = 1; \
477                } /* if */ \
478\
479                if ( ! f.flags.pc ) {                                                   /* no precision */ \
480                        fmtstr[sizeof(IFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
481                        /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
482                        fmt( os, &fmtstr[star], f.wd, f.val ); \
483                } else {                                                                                /* precision */ \
484                        fmtstr[sizeof(IFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
485                        /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
486                        fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \
487                } /* if */ \
488                return os; \
489        } /* ?|? */ \
490        OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
491} // distribution
492
493INTEGRAL_FMT_IMPL( signed char,            "      *hh ", "      *.*hh " )
494INTEGRAL_FMT_IMPL( unsigned char,          "      *hh ", "      *.*hh " )
495INTEGRAL_FMT_IMPL( signed short int,       "      *h ",  "      *.*h " )
496INTEGRAL_FMT_IMPL( unsigned short int,     "      *h ",  "      *.*h " )
497INTEGRAL_FMT_IMPL( signed int,             "      * ",   "      *.* " )
498INTEGRAL_FMT_IMPL( unsigned int,           "      * ",   "      *.* " )
499INTEGRAL_FMT_IMPL( signed long int,        "      *l ",  "      *.*l " )
500INTEGRAL_FMT_IMPL( unsigned long int,      "      *l ",  "      *.*l " )
501INTEGRAL_FMT_IMPL( signed long long int,   "      *ll ", "      *.*ll " )
502INTEGRAL_FMT_IMPL( unsigned long long int, "      *ll ", "      *.*ll " )
503
504
505#if defined( __SIZEOF_INT128__ )
506// Default prefix for non-decimal prints is 0b, 0, 0x.
507forall( ostype & | basic_ostream( ostype ) )
508static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) {
509        int wd = 1;                                                                                     // f.wd is never 0 because 0 implies left-pad
510        if ( val > power ) {                                                            // subdivide value into printable 64-bit values
511                base_128( os, val / power, power, f, maxdig, bits, cnt + 1 ); // recursive
512                f.val = val % power;
513                if ( cnt == 1 && f.flags.left ) { wd = f.wd; f.wd = maxdig; } // copy f.wd and reset for printing middle chunk
514                // printf( "R val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
515                //              f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
516                (ostype &)(os | f);
517                if ( cnt == 1 ) {
518                        if ( f.flags.left ) { wd -= maxdig; f.wd = wd < 0 ? 1 : wd; } // update and restore f.wd for printing end chunk
519                        nosep( os );                                                            // no seperator between chunks
520                } // if
521        } else {                                                                                        // print start chunk
522                f.val = val;
523                // f.pc is unsigned => use wd
524                if ( f.flags.pc && f.pc > maxdig * cnt ) { wd = f.pc - maxdig * cnt; f.pc = wd < 0 ? 0 : wd; }
525                else { f.flags.pc = false; f.pc = 0; }
526
527                if ( ! f.flags.left ) {                                                 // right justify
528                        wd = f.wd - maxdig * cnt;
529                        f.wd = wd < 0 ? 1 : wd;
530                        wd = maxdig;
531                } else {                                                                                // left justify
532                        if ( cnt != 0 ) {                                                       // value >= 2^64 ?
533                                unsigned int dig, bs = 0;
534                                // compute size of prefix digits and base
535                                if ( f.base == 'd' || f.base == 'u' ) { // no base prefix
536                                        dig = ceil( log10( f.val ) );           // use floating-point
537                                        if ( f.base == 'd' && (f.flags.neg || f.flags.sign) ) bs = 1; // sign ?
538                                } else {
539                                        dig = ceiling_div( high1( f.val ), bits );
540                                        if ( ! f.flags.nobsdp ) {                       // base prefix ?
541                                                if ( f.base == 'o' ) {
542                                                        // 0 prefix for octal is not added for precision with leading zero
543                                                        if ( f.pc <= dig ) bs = 1;      // 1 character prefix
544                                                } else bs = 2;                                  // 2 character prefix
545                                        } // if
546                                } // if
547                                wd = f.wd - (f.pc > dig ? f.pc : dig) - bs; // precision > leading digits ?
548                                if ( wd < 0 ) wd = 1;
549                                f.wd = 1;
550                        } // if
551                        // all manipulators handled implicitly for value < 2^64
552                } // if
553                // prior checks ensure wd not negative
554
555                if ( f.flags.neg ) f.val = -f.val;
556                // printf( "L val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
557                //              f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
558                (ostype &)(os | f);
559
560                // remaining middle and end chunks are padded with 0s on the left
561                if ( ! f.flags.left ) { f.flags.pad0 = true; f.flags.pc = false; } // left pad with 0s
562                else { f.pc = maxdig; f.flags.pc = true; }              // left pad with precision
563
564                if ( cnt != 0 ) nosep( os );                                    // no seperator between chunks
565                f.wd = wd;                                                                              // reset f.wd for next chunk
566                f.flags.sign = false;                                                   // no leading +/- sign
567                f.flags.nobsdp = true;                                                  // no leading base prefix
568        } // if
569} // base_128
570
571#define INTEGRAL_FMT_IMPL128( T ) \
572forall( ostype & | basic_ostream( ostype ) ) { \
573        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
574                _Ostream_Manip(uint64_t) fmt; \
575                fmt.[wd, pc, base, all] = f.[wd, pc, base, all]; \
576                if ( f.base == 'b' | f.base == 'B' ) { \
577                        base_128( os, f.val, (unsigned int128)1 << 64, fmt, 64, 1 ); \
578                } else if ( f.base == 'o' ) { \
579                        base_128( os, f.val, (unsigned int128)1 << 63, fmt, 21, 3 ); \
580                } else if ( f.base == 'd' || f.base == 'u' ) { \
581                        if ( f.base == 'd' && f.val < 0 ) { f.val = -f.val; fmt.flags.neg = true; } \
582                        base_128( os, f.val, (unsigned int128)10_000_000_000_000_000_000UL, fmt, 19, 0 ); \
583                } else { \
584                        base_128( os, f.val, (unsigned int128)1 << 64, fmt, 16, 4 ); \
585                } /* if */ \
586                return os; \
587        } /* ?|? */ \
588        OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
589} // distribution
590
591INTEGRAL_FMT_IMPL128( int128 )
592INTEGRAL_FMT_IMPL128( unsigned int128 )
593#endif // __SIZEOF_INT128__
594
595// *********************************** floating point ***********************************
596
597static const char *suffixes[] = {
598        "y", "z", "a", "f", "p", "n", "u", "m", "",
599        "K", "M", "G", "T", "P", "E", "Z", "Y"
600};
601#define SUFFIXES_START (-24) /* Smallest power for which there is a suffix defined. */
602#define SUFFIXES_END (SUFFIXES_START + (int)((sizeof(suffixes) / sizeof(char *) - 1) * 3))
603
604#define PRINT_WITH_DP2( os, format, ... ) \
605        { \
606                if ( ! f.flags.eng ) { \
607                        len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
608                        if ( isfinite( f.val ) && ! f.flags.nobsdp ) { /* if number, print decimal point when no fraction or exponent */ \
609                                for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E' && \
610                                                         buf[i] != 'p' && buf[i] != 'P'; i += 1 ); /* decimal point or scientific ? */ \
611                                if ( i == len ) { \
612                                        if ( ! f.flags.left ) { \
613                                                buf[i] = '.'; buf[i + 1] = '\0'; \
614                                                if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \
615                                        } else { \
616                                                for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \
617                                                buf[i] = '.'; \
618                                                if ( i == len ) buf[i + 1] = '\0'; \
619                                        } /* if */ \
620                                } /* if */ \
621                        } /* if */ \
622                } else { \
623                        int exp10, len2; \
624                        eng( f.val, f.pc, exp10 );                                      /* changes arguments */ \
625                        /* printf( "%g %d %d %d %s\n", f.val, f.wd, f.pc, exp10, format ); */ \
626                        if ( ! f.flags.left && f.wd > 1 ) { \
627                                /* Exponent size: 'e', optional minus sign, number of digits: log10(0) => undefined */ \
628                                f.wd -= 1 + (exp10 < 0 ? 1 : 0) + lrint( floor( exp10 == 0 ? 0 : log10( abs( exp10 ) ) ) ) + 1; \
629                                if ( f.wd < 1 ) f.wd = 1; \
630                        } /* if */ \
631                        len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
632                        if ( f.flags.left ) { \
633                                for ( len -= 1; len > 0 && buf[len] == ' '; len -= 1 ); \
634                                len += 1; \
635                        } /* if */ \
636                        if ( ! f.flags.nobsdp || (exp10 < SUFFIXES_START) || (exp10 > SUFFIXES_END) ) { \
637                                len2 = snprintf( &buf[len], size - len, "e%d", (int)exp10 /* ambiguity with function exp10 */ ); \
638                        } else { \
639                                len2 = snprintf( &buf[len], size - len, "%s", suffixes[(exp10 - SUFFIXES_START) / 3] ); \
640                        } /* if */ \
641                        if ( f.flags.left && len + len2 < f.wd ) buf[len + len2] = ' '; \
642                } /* if */ \
643                fmt( os, "%s", &buf[bufbeg] ); \
644        }
645
646#define FLOATING_POINT_FMT_IMPL( T, DFMTNP, DFMTP ) \
647forall( ostype & | basic_ostream( ostype ) ) { \
648        static void eng( T &value, int & pc, int & exp10 ) { \
649                exp10 = lrint( floor( log10( abs( value ) ) ) ); /* round to desired precision */ \
650                if ( exp10 < 0 ) exp10 -= 2; \
651                exp10 = floor( exp10, 3 ); \
652                value *= pow( 10.0, -exp10 ); \
653                if ( pc <= 3 ) pc = 3; \
654        } /* eng */ \
655\
656        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
657                enum { size = 48 }; \
658                char buf[size]; \
659                int bufbeg = 0, i, len; \
660\
661                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \
662                char fmtstr[sizeof(DFMTP) + 8];                                 /* sizeof includes '\0' */ \
663                if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \
664                else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \
665                int star = 5;                                                                   /* position before first '*' */ \
666\
667                /* Insert flags into spaces before '*', from right to left. */ \
668                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
669                if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
670                if ( f.flags.pad0 ) { fmtstr[star] = '0'; star -= 1; } \
671                fmtstr[star] = '\''; star -= 1;                                 /* locale */ \
672                fmtstr[star] = '%'; \
673\
674                if ( ! f.flags.pc ) {                                                   /* no precision */ \
675                        fmtstr[sizeof(DFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
676                        /* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star] ); */ \
677                        PRINT_WITH_DP2( os, &fmtstr[star], f.wd, f.val ) \
678                } else {                                                                                /* precision */ \
679                        fmtstr[sizeof(DFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
680                        /* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \
681                        PRINT_WITH_DP2( os, &fmtstr[star], f.wd, f.pc, f.val ) \
682                } /* if */ \
683                return os; \
684        } /* ?|? */ \
685\
686        OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
687} // distribution
688
689FLOATING_POINT_FMT_IMPL( double,      "      * ",  "      *.* " )
690FLOATING_POINT_FMT_IMPL( long double, "      *L ", "      *.*L " )
691
692// *********************************** character ***********************************
693
694forall( ostype & | basic_ostream( ostype ) ) {
695        ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) {
696                if ( f.base != 'c' ) {                                                  // bespoke binary/octal/hex format
697                        _Ostream_Manip(unsigned char) fmtuc @= { f.val, f.wd, f.pc, f.base, {'\0'} };
698                        fmtuc.flags.pc = f.flags.pc;
699                        fmtuc.flags.nobsdp = f.flags.nobsdp;
700//                      os | fmtuc | nonl;
701                        (ostype &)(os | fmtuc);
702                        return os;
703                } // if
704
705                if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
706
707                #define CFMTNP "% * "
708                char fmtstr[sizeof(CFMTNP)];                                    // sizeof includes '\0'
709                memcpy( &fmtstr, CFMTNP, sizeof(CFMTNP) );
710                int star = 1;                                                                   // position before first '*'
711
712                // Insert flags into spaces before '*', from right to left.
713                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
714                fmtstr[star] = '%';
715
716                fmtstr[sizeof(CFMTNP)-2] = f.base;                              // sizeof includes '\0'
717                // printf( "%d %s\n", f.wd, &fmtstr[star] );
718                fmt( os, &fmtstr[star], f.wd, f.val );
719                return os;
720        } // ?|?
721        OSTYPE_VOID_IMPL( _Ostream_Manip(char) )
722} // distribution
723
724// *********************************** C string ***********************************
725
726forall( ostype & | basic_ostream( ostype ) ) {
727        ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) {
728                if ( ! f.val ) return os;                                               // null pointer ?
729
730                if ( f.base != 's' ) {                                                  // bespoke binary/octal/hex format
731                        _Ostream_Manip(unsigned char) fmtuc @= { 0, f.wd, f.pc, f.base, {'\0'} };
732                        fmtuc.flags.pc = f.flags.pc;
733                        fmtuc.flags.nobsdp = f.flags.nobsdp;
734                        for ( i; 0 ~ @ : @; f.val[i] != '\0' ) {
735                                fmtuc.val = f.val[i];
736//                              os | fmtuc | nonl;
737                                (ostype &)(os | fmtuc);
738                        } // for
739                        return os;
740                } // if
741
742                if ( f.val[0] != '\0' &&                                                // null string => no leading separator
743                         sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
744
745                #define SFMTNP "% * "
746                #define SFMTP "% *.* "
747                char fmtstr[sizeof(SFMTP)];                                             // sizeof includes '\0'
748                if ( ! f.flags.pc ) memcpy( &fmtstr, SFMTNP, sizeof(SFMTNP) );
749                else memcpy( &fmtstr, SFMTP, sizeof(SFMTP) );
750                int star = 1;                                                                   // position before first '*'
751
752                // Insert flags into spaces before '*', from right to left.
753                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
754                fmtstr[star] = '%';
755
756                if ( ! f.flags.pc ) {                                                   // no precision
757                        // printf( "%d %s\n", f.wd, &fmtstr[star] );
758                        fmtstr[sizeof(SFMTNP)-2] = f.base;                      // sizeof includes '\0'
759                        fmt( os, &fmtstr[star], f.wd, f.val );
760                } else {                                                                                // precision
761                        fmtstr[sizeof(SFMTP)-2] = f.base;                       // sizeof includes '\0'
762                        // printf( "%d %d %s\n", f.wd, f.pc, &fmtstr[star] );
763                        fmt( os, &fmtstr[star], f.wd, f.pc, f.val );
764                } // if
765                if ( f.val[0] == '\0' ) { nosep( os ); }                // null string => no trailing separator
766                return os;
767        } // ?|?
768        OSTYPE_VOID_IMPL( _Ostream_Manip(const char *) )
769} // distribution
770
771
772// *********************************** istream ***********************************
773
774
775forall( istype & | basic_istream( istype ) ) {
776        istype & ?|?( istype & is, bool & b ) {
777                char val[6];
778                fmt( is, "%5s", val );
779                if ( strcmp( val, "true" ) == 0 ) b = true;
780                else if ( strcmp( val, "false" ) == 0 ) b = false;
781                else {
782                        fprintf( stderr, "invalid Boolean constant\n" );
783                        abort();                                                                        // cannot use abort stream
784                } // if
785                return is;
786        } // ?|?
787        ISTYPE_VOID_IMPL( bool & )
788
789        istype & ?|?( istype & is, char & c ) {
790                char temp;
791                for () {
792                        fmt( is, "%c", &temp );                                         // must pass pointer through varg to fmt
793                        // do not overwrite parameter with newline unless appropriate
794                        if ( temp != '\n' || getANL$( is ) ) { c = temp; break; }
795                        if ( eof( is ) ) break;
796                } // for
797                return is;
798        } // ?|?
799        ISTYPE_VOID_IMPL( char & )
800
801        istype & ?|?( istype & is, signed char & sc ) {
802                fmt( is, "%hhi", &sc );
803                return is;
804        } // ?|?
805        ISTYPE_VOID_IMPL( signed char & )
806
807        istype & ?|?( istype & is, unsigned char & usc ) {
808                fmt( is, "%hhi", &usc );
809                return is;
810        } // ?|?
811        ISTYPE_VOID_IMPL( unsigned char & )
812
813        istype & ?|?( istype & is, short int & si ) {
814                fmt( is, "%hi", &si );
815                return is;
816        } // ?|?
817        ISTYPE_VOID_IMPL( short int & )
818
819        istype & ?|?( istype & is, unsigned short int & usi ) {
820                fmt( is, "%hi", &usi );
821                return is;
822        } // ?|?
823        ISTYPE_VOID_IMPL( unsigned short int & )
824
825        istype & ?|?( istype & is, int & i ) {
826                fmt( is, "%i", &i );
827                return is;
828        } // ?|?
829        ISTYPE_VOID_IMPL( int & )
830
831        istype & ?|?( istype & is, unsigned int & ui ) {
832                fmt( is, "%i", &ui );
833                return is;
834        } // ?|?
835        ISTYPE_VOID_IMPL( unsigned int & )
836
837        istype & ?|?( istype & is, long int & li ) {
838                fmt( is, "%li", &li );
839                return is;
840        } // ?|?
841        ISTYPE_VOID_IMPL( long int & )
842
843        istype & ?|?( istype & is, unsigned long int & ulli ) {
844                fmt( is, "%li", &ulli );
845                return is;
846        } // ?|?
847        ISTYPE_VOID_IMPL( unsigned long int & )
848
849        istype & ?|?( istype & is, long long int & lli ) {
850                fmt( is, "%lli", &lli );
851                return is;
852        } // ?|?
853        ISTYPE_VOID_IMPL( long long int & )
854
855        istype & ?|?( istype & is, unsigned long long int & ulli ) {
856                fmt( is, "%lli", &ulli );
857                return is;
858        } // ?|?
859        ISTYPE_VOID_IMPL( unsigned long long int & )
860
861        #if defined( __SIZEOF_INT128__ )
862        istype & ?|?( istype & is, int128 & llli ) {
863                return (istype &)(is | (unsigned int128 &)llli);
864        } // ?|?
865        ISTYPE_VOID_IMPL( int128 & )
866
867        istype & ?|?( istype & is, unsigned int128 & ullli ) {
868                char s[40];
869                bool sign = false;
870
871                if ( fmt( is, " %[-]", s ) == 1 ) sign = true;  // skip whitespace, negative sign ?
872                // If the input is too large, the value returned is undefined. If there is no input, no value is returned
873                if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) {   // take first 39 characters, ignore remaining
874                        ullli = 0;
875                        for ( i; 0 ~ @ : @; s[i] != '\0' ) {
876                                ullli = ullli * 10 + s[i] - '0';
877                        } // for
878                        if ( sign ) ullli = -ullli;
879                } else if ( sign ) ungetc( is, '-' );                   // return minus when no digits
880                return is;
881        } // ?|?
882        ISTYPE_VOID_IMPL( unsigned int128 & )
883        #endif // __SIZEOF_INT128__
884
885        istype & ?|?( istype & is, float & f ) {
886                fmt( is, "%f", &f );
887                return is;
888        } // ?|?
889        ISTYPE_VOID_IMPL( float & )
890
891        istype & ?|?( istype & is, double & d ) {
892                fmt( is, "%lf", &d );
893                return is;
894        } // ?|?
895        ISTYPE_VOID_IMPL( double & )
896
897        istype & ?|?( istype & is, long double & ld ) {
898                fmt( is, "%Lf", &ld );
899                return is;
900        } // ?|?
901        ISTYPE_VOID_IMPL( long double & )
902
903        istype & ?|?( istype & is, float _Complex & fc ) {
904                float re, im;
905                fmt( is, "%f%fi", &re, &im );
906                fc = re + im * _Complex_I;
907                return is;
908        } // ?|?
909        ISTYPE_VOID_IMPL( float _Complex & )
910
911        istype & ?|?( istype & is, double _Complex & dc ) {
912                double re, im;
913                fmt( is, "%lf%lfi", &re, &im );
914                dc = re + im * _Complex_I;
915                return is;
916        } // ?|?
917        ISTYPE_VOID_IMPL( double _Complex & )
918
919        istype & ?|?( istype & is, long double _Complex & ldc ) {
920                long double re, im;
921                fmt( is, "%Lf%Lfi", &re, &im );
922                ldc = re + im * _Complex_I;
923                return is;
924        } // ?|?
925        ISTYPE_VOID_IMPL( long double _Complex & )
926
927        istype & ?|?( istype & is, const char fmt[] ) {
928                fmt( is, fmt, "" );
929                return is;
930        } // ?|?
931        ISTYPE_VOID_IMPL( const char * )
932
933        // manipulators
934        istype & ?|?( istype & is, istype & (* manip)( istype & ) ) {
935                return manip( is );
936        } // ?|?
937
938        void ?|?( istype & is, istype & (* manip)( istype & ) ) {
939                manip( is ); ends( is );
940        } // ?|?
941
942        istype & nl( istype & is ) {
943                fmt( is, "%*[^\n]" );                                                   // ignore characters to newline
944                if ( ! eof( is ) && getANL$( is ) ) fmt( is, "%*c" ); // read newline
945                return is;
946        } // nl
947
948        istype & nlOn( istype & is ) {
949                nlOn( is );                                                                             // call void returning
950                return is;
951        } // nlOn
952
953        istype & nlOff( istype & is ) {
954                nlOff( is );                                                                    // call void returning
955                return is;
956        } // nlOff
957} // distribution
958
959// *********************************** manipulators ***********************************
960
961forall( istype & | basic_istream( istype ) ) {
962        istype & ?|?( istype & is, _Istream_Cskip f ) {
963                // printf( "skip %s %d\n", f.scanset, f.wd );
964                if ( f.scanset ) {
965                        int nscanset = strlen(f.scanset);
966                        char fmtstr[ sizeof("%*[]") + nscanset ];
967                        int pos = 0;
968                        fmtstr[pos] = '%';                  pos += 1;
969                        fmtstr[pos] = '*';                  pos += 1;
970                        fmtstr[pos] = '[';                  pos += 1;
971                        strcpy( &fmtstr[pos], f.scanset );  pos += nscanset;
972                        fmtstr[pos] = ']';                  pos += 1;
973                        fmtstr[pos] = '\0';
974                        fmt( is, fmtstr, (void*)0 );  // last arg is dummy: suppress gcc warning
975                }
976                else for ( f.wd ) fmt( is, "%*c" );
977                return is;
978        }
979        ISTYPE_VOID_IMPL( _Istream_Cskip )
980
981        istype & ?|?( istype & is, _Istream_Cstr f ) {
982                const char * scanset = f.scanset;
983                if ( f.flags.delimiter ) scanset = f.delimiter; // getline ?
984
985                size_t len = 0;
986                if ( scanset ) len = strlen( scanset );
987                char fmtstr[len + 16];
988                int start = 1;
989                fmtstr[0] = '%';
990                if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; }
991                // no maximum width necessary because text ignored => width is read width
992                if ( f.wd != -1 ) {
993                        // wd is buffer bytes available (for input chars + null terminator)
994                        // rwd is count of input chars
995                        int rwd;
996                        if (f.flags.rwd) {
997                                verify (f.wd >= 0);
998                                rwd = f.wd;
999                        } else {
1000                                verify (f.wd >= 1);
1001                                rwd = f.wd - 1;
1002                        } // if
1003                        start += sprintf( &fmtstr[start], "%d", rwd );
1004                }
1005
1006                if ( ! scanset ) {
1007                        // %s, %*s, %ws, %*ws
1008                        fmtstr[start] = 's'; fmtstr[start + 1] = '\0';
1009                        // printf( "cstr %s\n", fmtstr );
1010                } else {
1011                        // incl %[xxx],  %*[xxx],  %w[xxx],  %*w[xxx]
1012                        // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx]
1013                        fmtstr[start] = '['; start += 1;
1014                        if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; }
1015                        strcpy( &fmtstr[start], scanset );                      // copy includes '\0'
1016                        len += start;
1017                        fmtstr[len] = ']'; fmtstr[len + 1] = '\0';
1018                        // printf( "incl/excl %s\n", fmtstr );
1019                } // if
1020
1021                int check = f.wd - 2;
1022                if (! f.flags.ignore ) {
1023                        f.s[0] = '\0';
1024                        if ( ! f.flags.rwd ) f.s[check] = '\0';         // insert sentinel
1025                }
1026                len = fmt( is, fmtstr, f.s );
1027                //fprintf( stderr, "KK %s %zd %d %c %s\n", fmtstr, len, check, f.s[check], f.s );
1028
1029                if ( ! f.flags.ignore && ! f.flags.rwd && f.s[check] != '\0' ) { // sentinel overwritten ?
1030                        // buffer filled, but would we have kept going?
1031                        if ( ! eof( is ) ) {
1032                                char peek;
1033                                fmt( is, "%c", &peek );
1034                                ungetc( is, peek );
1035                                bool hasMore;
1036                                if (f.flags.delimiter) { // getline
1037                                        hasMore = (peek != f.delimiter[0]);
1038                                } else if (f.scanset) { // incl/excl
1039                                        bool peekMatch = strchr(f.scanset, peek) != 0p;
1040                                        hasMore = f.flags.inex ? (!peekMatch) : (peekMatch);
1041                                } else { // %s
1042                                        hasMore = !isspace(peek);
1043                                }
1044                                if (hasMore) throw (cstring_length){ &cstring_length_vt };
1045                        } // if
1046                } // if
1047
1048                if ( f.flags.delimiter ) {                                              // getline ?
1049                        if ( len == 0 ) f.s[0] = '\0';                          // empty read => argument unchanged => set empty
1050                        if ( ! eof( is ) ) {                                            // ignore delimiter, may not be present because of width
1051                                char delimiter;
1052                                fmt( is, "%c", &delimiter );
1053                                if ( delimiter != f.delimiter[0] ) ungetc( is, delimiter );
1054                        } // if
1055                } //if
1056                return is;
1057        } // ?|?
1058        ISTYPE_VOID_IMPL( _Istream_Cstr )
1059
1060        istype & ?|?( istype & is, _Istream_Char f ) {
1061                fmt( is, "%*c" );                                                               // argument variable unused
1062                return is;
1063        } // ?|?
1064        ISTYPE_VOID_IMPL( _Istream_Char )
1065} // distribution
1066
1067#define INPUT_FMT_IMPL( T, CODE ) \
1068forall( istype & | basic_istream( istype ) ) { \
1069        istype & ?|?( istype & is, _Istream_Manip(T) f ) { \
1070                enum { size = 16 }; \
1071                char fmtstr[size]; \
1072                if ( f.wd == -1 ) { \
1073                        snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \
1074                } else { \
1075                        snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \
1076                } /* if */ \
1077                /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \
1078                fmt( is, fmtstr, &f.val ); \
1079                return is; \
1080        } /* ?|? */ \
1081        ISTYPE_VOID_IMPL( _Istream_Manip(T) ) \
1082} // distribution
1083
1084INPUT_FMT_IMPL( signed char, "hhi" )
1085INPUT_FMT_IMPL( unsigned char, "hhi" )
1086INPUT_FMT_IMPL( signed short int, "hi" )
1087INPUT_FMT_IMPL( unsigned short int, "hi" )
1088INPUT_FMT_IMPL( signed int, "i" )
1089INPUT_FMT_IMPL( unsigned int, "i" )
1090INPUT_FMT_IMPL( signed long int, "li" )
1091INPUT_FMT_IMPL( unsigned long int, "li" )
1092INPUT_FMT_IMPL( signed long long int, "lli" )
1093INPUT_FMT_IMPL( unsigned long long int, "lli" )
1094
1095INPUT_FMT_IMPL( float, "f" )
1096INPUT_FMT_IMPL( double, "lf" )
1097INPUT_FMT_IMPL( long double, "Lf" )
1098
1099forall( istype & | basic_istream( istype ) ) {
1100        istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) {
1101                float re, im;
1102                _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore };
1103                is | fmtuc;
1104                &fmtuc.val = &im;
1105                is | fmtuc;
1106                if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1107                return is;
1108        } // ?|?
1109        ISTYPE_VOID_IMPL( _Istream_Manip(float _Complex) )
1110
1111        istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) {
1112                double re, im;
1113                _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore };
1114                is | fmtuc;
1115                &fmtuc.val = &im;
1116                is | fmtuc;
1117                if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1118                return is;
1119        } // ?|?
1120        ISTYPE_VOID_IMPL( _Istream_Manip(double _Complex) )
1121
1122        istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) {
1123                long double re, im;
1124                _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore };
1125                is | fmtuc;
1126                &fmtuc.val = &im;
1127                is | fmtuc;
1128                if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I;     // re/im are uninitialized for ignore
1129                return is;
1130        } // ?|?
1131        ISTYPE_VOID_IMPL( _Istream_Manip(long double _Complex) )
1132} // distribution
1133
1134
1135// Local Variables: //
1136// tab-width: 4 //
1137// compile-command: "cfa iostream.cfa" //
1138// End: //
Note: See TracBrowser for help on using the repository browser.