source: libcfa/src/iostream.cfa @ 8df19af

Last change on this file since 8df19af was 37ceccb, checked in by Michael Brooks <mlbrooks@…>, 8 months ago

Change string-read semantics so that reading nothing leaves the original value unmodified.

Logically revert #8cffa4f21, and apply the change onto work done since.

At cstring layer, libcfa change is a simplification, and test suite change is merely a different expectation.

At var-len-string layer, libcfa change is a patch to enable detecting the nothing-read case,
and test sute change (shows the different expectation but also) puts a similar patch into its "stop trying if nothing read" loop exits.

  • 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 : Sun Oct  8 12:10:21 2023
14// Update Count     : 1564
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 < 0 ) 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                        if ( ! f.flags.rwd ) f.s[check] = '\0';         // insert sentinel
1024                }
1025                len = fmt( is, fmtstr, f.s );
1026                //fprintf( stderr, "KK %s %zd %d %c %s\n", fmtstr, len, check, f.s[check], f.s );
1027
1028                if ( ! f.flags.ignore && ! f.flags.rwd && f.s[check] != '\0' ) { // sentinel overwritten ?
1029                        // buffer filled, but would we have kept going?
1030                        if ( ! eof( is ) ) {
1031                                char peek;
1032                                fmt( is, "%c", &peek );
1033                                ungetc( is, peek );
1034                                bool hasMore;
1035                                if (f.flags.delimiter) { // getline
1036                                        hasMore = (peek != f.delimiter[0]);
1037                                } else if (f.scanset) { // incl/excl
1038                                        bool peekMatch = strchr(f.scanset, peek) != 0p;
1039                                        hasMore = f.flags.inex ? (!peekMatch) : (peekMatch);
1040                                } else { // %s
1041                                        hasMore = !isspace(peek);
1042                                }
1043                                if (hasMore) throw (cstring_length){ &cstring_length_vt };
1044                        } // if
1045                } // if
1046
1047                if ( f.flags.delimiter ) {                                              // getline ?
1048                        if ( len == 0 ) f.s[0] = '\0';                          // empty read => argument unchanged => set empty
1049                        if ( ! eof( is ) ) {                                            // ignore delimiter, may not be present because of width
1050                                char delimiter;
1051                                fmt( is, "%c", &delimiter );
1052                                if ( delimiter != f.delimiter[0] ) ungetc( is, delimiter );
1053                        } // if
1054                } //if
1055                return is;
1056        } // ?|?
1057        ISTYPE_VOID_IMPL( _Istream_Cstr )
1058
1059        istype & ?|?( istype & is, _Istream_Char f ) {
1060                fmt( is, "%*c" );                                                               // argument variable unused
1061                return is;
1062        } // ?|?
1063        ISTYPE_VOID_IMPL( _Istream_Char )
1064} // distribution
1065
1066#define INPUT_FMT_IMPL( T, CODE ) \
1067forall( istype & | basic_istream( istype ) ) { \
1068        istype & ?|?( istype & is, _Istream_Manip(T) f ) { \
1069                enum { size = 16 }; \
1070                char fmtstr[size]; \
1071                if ( f.wd == -1 ) { \
1072                        snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \
1073                } else { \
1074                        snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \
1075                } /* if */ \
1076                /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \
1077                fmt( is, fmtstr, &f.val ); \
1078                return is; \
1079        } /* ?|? */ \
1080        ISTYPE_VOID_IMPL( _Istream_Manip(T) ) \
1081} // distribution
1082
1083INPUT_FMT_IMPL( signed char, "hhi" )
1084INPUT_FMT_IMPL( unsigned char, "hhi" )
1085INPUT_FMT_IMPL( signed short int, "hi" )
1086INPUT_FMT_IMPL( unsigned short int, "hi" )
1087INPUT_FMT_IMPL( signed int, "i" )
1088INPUT_FMT_IMPL( unsigned int, "i" )
1089INPUT_FMT_IMPL( signed long int, "li" )
1090INPUT_FMT_IMPL( unsigned long int, "li" )
1091INPUT_FMT_IMPL( signed long long int, "lli" )
1092INPUT_FMT_IMPL( unsigned long long int, "lli" )
1093
1094INPUT_FMT_IMPL( float, "f" )
1095INPUT_FMT_IMPL( double, "lf" )
1096INPUT_FMT_IMPL( long double, "Lf" )
1097
1098forall( istype & | basic_istream( istype ) ) {
1099        istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) {
1100                float re, im;
1101                _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore };
1102                is | fmtuc;
1103                &fmtuc.val = &im;
1104                is | fmtuc;
1105                if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1106                return is;
1107        } // ?|?
1108        ISTYPE_VOID_IMPL( _Istream_Manip(float _Complex) )
1109
1110        istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) {
1111                double re, im;
1112                _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore };
1113                is | fmtuc;
1114                &fmtuc.val = &im;
1115                is | fmtuc;
1116                if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1117                return is;
1118        } // ?|?
1119        ISTYPE_VOID_IMPL( _Istream_Manip(double _Complex) )
1120
1121        istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) {
1122                long double re, im;
1123                _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore };
1124                is | fmtuc;
1125                &fmtuc.val = &im;
1126                is | fmtuc;
1127                if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I;     // re/im are uninitialized for ignore
1128                return is;
1129        } // ?|?
1130        ISTYPE_VOID_IMPL( _Istream_Manip(long double _Complex) )
1131} // distribution
1132
1133
1134// Local Variables: //
1135// tab-width: 4 //
1136// compile-command: "cfa iostream.cfa" //
1137// End: //
Note: See TracBrowser for help on using the repository browser.