source: libcfa/src/iostream.cfa @ 2fa0237

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

Fix cstring input length interpretation issue that had a buffer overflow case.

The cases added to the manipulatorsInput test are runnable against an old libcfa build. In this setup, the test fails with an illustration of the bug.

The testing in this commit drives the following inputs through a length-8 buffer.

  • 123456
  • 123456789

The obviously-missing cases, like 1234567, will be added later.
They will accompany fixes for further bugs not solved yet.

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