source: libcfa/src/iostream.cfa @ 64c4b4d

Last change on this file since 64c4b4d was 3db78b89, checked in by Michael Brooks <mlbrooks@…>, 8 months ago

Fix string reading bug: Manipulator ignore should not write to its output argument.

The prior test expectation was wrong.
The test was not following its rule that it should expect
the CFA library to show scanf-analogous behaviour.

Note, the corresponding expectation in collections/string-istream-manip was already correct.

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