Changeset 32490deb for libcfa/src/iostream.hfa
- Timestamp:
- Jan 31, 2024, 6:40:25 PM (20 months ago)
- Branches:
- master
- Children:
- 496ffc17
- Parents:
- c75b30a (diff), e71b09a (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/iostream.hfa
rc75b30a r32490deb 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jan 3 10:53:18202413 // Update Count : 61012 // Last Modified On : Sun Jan 28 11:56:29 2024 13 // Update Count : 733 14 14 // 15 15 … … 196 196 // *********************************** integral *********************************** 197 197 198 // See 6.7.9. 19) The initialization shall occur in initializer list order, each initializer provided for a particular199 // subobject overriding any previously listed initializer for the same subobject; ***all subobjects that are not200 // initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.***201 202 198 #define INTEGRAL_FMT_DECL( T, CODE ) \ 203 199 static inline { \ 204 _Ostream_Manip(T) bin( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'b', { .all : 0 } }; } \205 _Ostream_Manip(T) oct( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'o', { .all : 0 } }; } \206 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'x', { .all : 0 } }; } \207 _Ostream_Manip(T) wd( unsigned int w , T val ) { return (_Ostream_Manip(T))@{ val, w, 0,CODE, { .all : 0 } }; } \208 _Ostream_Manip(T) wd( unsigned int w , unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc,CODE, { .flags.pc : true } }; } \209 _Ostream_Manip(T) & wd( unsigned int w , _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \210 _Ostream_Manip(T) & wd( unsigned int w , unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \200 _Ostream_Manip(T) bin( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'b', { .all : 0 } }; } \ 201 _Ostream_Manip(T) oct( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'o', { .all : 0 } }; } \ 202 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'x', { .all : 0 } }; } \ 203 _Ostream_Manip(T) wd( unsigned int wd, T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : wd, .pc : 0, .base : CODE, { .all : 0 } }; } \ 204 _Ostream_Manip(T) wd( unsigned int wd, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : wd, .pc : pc, .base : CODE, { .flags.pc : true } }; } \ 205 _Ostream_Manip(T) & wd( unsigned int wd, _Ostream_Manip(T) & fmt ) { fmt.wd = wd; return fmt; } \ 206 _Ostream_Manip(T) & wd( unsigned int wd, unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = wd; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 211 207 _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \ 212 208 _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; } \ 213 209 _Ostream_Manip(T) & nobase( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 214 210 _Ostream_Manip(T) & pad0( _Ostream_Manip(T) & fmt ) { fmt.flags.pad0 = true; return fmt; } \ 215 _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,CODE, { .flags.sign : true } }; } \211 _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : CODE, { .flags.sign : true } }; } \ 216 212 _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \ 217 213 } /* distribution */ \ … … 241 237 #define FLOATING_POINT_FMT_DECL( T ) \ 242 238 static inline { \ 243 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \ 244 _Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'e', { .all : 0 } }; } \ 245 _Ostream_Manip(T) eng( T val ) { return (_Ostream_Manip(T))@{ val, 1, -1, 'g', { .flags.eng : true } }; } \ 246 _Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'g', { .all : 0 } }; } \ 247 _Ostream_Manip(T) wd( unsigned int w, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \ 248 _Ostream_Manip(T) ws( unsigned int w, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \ 249 _Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; return fmt; } \ 250 _Ostream_Manip(T) & wd( unsigned int w, unsigned int pc, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 251 _Ostream_Manip(T) & ws( unsigned int w, unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 239 _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'a', { .all : 0 } }; } \ 240 _Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'e', { .all : 0 } }; } \ 241 _Ostream_Manip(T) eng( T val ) { return (_Ostream_Manip(T))@{ .val : val, 1, -1, .base : 'g', { .flags.eng : true } }; } \ 242 _Ostream_Manip(T) wd( unsigned int wd, T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : wd, .pc : 0, .base : 'g', { .all : 0 } }; } \ 243 _Ostream_Manip(T) wd( unsigned int wd, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : wd, .pc : pc, .base : 'f', { .flags.pc : true } }; } \ 244 _Ostream_Manip(T) ws( unsigned int wd, unsigned int pc, T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : wd, .pc : pc, .base : 'g', { .flags.pc : true } }; } \ 245 _Ostream_Manip(T) & wd( unsigned int wd, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = wd; return fmt; } \ 246 _Ostream_Manip(T) & wd( unsigned int wd, unsigned int pc, _Ostream_Manip(T) & fmt ) { \ 247 if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = wd; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 248 _Ostream_Manip(T) & ws( unsigned int wd, unsigned int pc, _Ostream_Manip(T) & fmt ) { fmt.wd = wd; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \ 252 249 _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \ 253 _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'G', { .all : 0 } }; } \250 _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'G', { .all : 0 } }; } \ 254 251 _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { fmt.base -= 32; /* upper case */ return fmt; } \ 255 252 _Ostream_Manip(T) & pad0( _Ostream_Manip(T) & fmt ) { fmt.flags.pad0 = true; return fmt; } \ 256 _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'g', { .flags.sign : true } }; } \253 _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'g', { .flags.sign : true } }; } \ 257 254 _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \ 258 _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'g', { .flags.nobsdp : true } }; } \255 _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'g', { .flags.nobsdp : true } }; } \ 259 256 _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 260 _Ostream_Manip(T) unit( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0,'g', { .flags.nobsdp : true } }; } \257 _Ostream_Manip(T) unit( T val ) { return (_Ostream_Manip(T))@{ .val : val, .wd : 1, .pc : 0, .base : 'g', { .flags.nobsdp : true } }; } \ 261 258 _Ostream_Manip(T) & unit( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 262 259 } /* distribution */ \ … … 272 269 273 270 static inline { 274 _Ostream_Manip(char) bin( char c ) { return (_Ostream_Manip(char))@{ c, 1, 0,'b', { .all : 0 } }; }275 _Ostream_Manip(char) oct( char c ) { return (_Ostream_Manip(char))@{ c, 1, 0,'o', { .all : 0 } }; }276 _Ostream_Manip(char) hex( char c ) { return (_Ostream_Manip(char))@{ c, 1, 0,'x', { .all : 0 } }; }277 _Ostream_Manip(char) wd( unsigned int w , char c ) { return (_Ostream_Manip(char))@{ c, w, 0,'c', { .all : 0 } }; }278 _Ostream_Manip(char) & wd( unsigned int w , _Ostream_Manip(char) & fmt ) { fmt.wd = w; return fmt; }271 _Ostream_Manip(char) bin( char c ) { return (_Ostream_Manip(char))@{ .val : c, .wd : 1, .pc : 0, .base : 'b', { .all : 0 } }; } 272 _Ostream_Manip(char) oct( char c ) { return (_Ostream_Manip(char))@{ .val : c, .wd : 1, .pc : 0, .base : 'o', { .all : 0 } }; } 273 _Ostream_Manip(char) hex( char c ) { return (_Ostream_Manip(char))@{ .val : c, .wd : 1, .pc : 0, .base : 'x', { .all : 0 } }; } 274 _Ostream_Manip(char) wd( unsigned int wd, char c ) { return (_Ostream_Manip(char))@{ c, wd, 0, .base : 'c', { .all : 0 } }; } 275 _Ostream_Manip(char) & wd( unsigned int wd, _Ostream_Manip(char) & fmt ) { fmt.wd = wd; return fmt; } 279 276 _Ostream_Manip(char) & left( _Ostream_Manip(char) & fmt ) { fmt.flags.left = true; return fmt; } 280 277 _Ostream_Manip(char) & upcase( _Ostream_Manip(char) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; } … … 289 286 290 287 static inline { 291 _Ostream_Manip(const char *) bin( const char s[] ) { return (_Ostream_Manip(const char *))@{ s, 1, 0,'b', { .all : 0 } }; }292 _Ostream_Manip(const char *) oct( const char s[] ) { return (_Ostream_Manip(const char *))@{ s, 1, 0,'o', { .all : 0 } }; }293 _Ostream_Manip(const char *) hex( const char s[] ) { return (_Ostream_Manip(const char *))@{ s, 1, 0,'x', { .all : 0 } }; }294 _Ostream_Manip(const char *) wd( unsigned int w , const char s[] ) { return (_Ostream_Manip(const char *))@{ s, w, 0,'s', { .all : 0 } }; }295 _Ostream_Manip(const char *) wd( unsigned int w , unsigned int pc, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, w, pc,'s', { .flags.pc : true } }; }296 _Ostream_Manip(const char *) & wd( unsigned int w , _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; return fmt; }297 _Ostream_Manip(const char *) & wd( unsigned int w , unsigned int pc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; }288 _Ostream_Manip(const char *) bin( const char s[] ) { return (_Ostream_Manip(const char *))@{ .val : s, .wd : 1, .pc : 0, .base : 'b', { .all : 0 } }; } 289 _Ostream_Manip(const char *) oct( const char s[] ) { return (_Ostream_Manip(const char *))@{ .val : s, .wd : 1, .pc : 0, .base : 'o', { .all : 0 } }; } 290 _Ostream_Manip(const char *) hex( const char s[] ) { return (_Ostream_Manip(const char *))@{ .val : s, .wd : 1, .pc : 0, .base : 'x', { .all : 0 } }; } 291 _Ostream_Manip(const char *) wd( unsigned int wd, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, wd, 0, .base : 's', { .all : 0 } }; } 292 _Ostream_Manip(const char *) wd( unsigned int wd, unsigned int pc, const char s[] ) { return (_Ostream_Manip(const char *))@{ s, .wd : wd, .pc : pc, .base : 's', { .flags.pc : true } }; } 293 _Ostream_Manip(const char *) & wd( unsigned int wd, _Ostream_Manip(const char *) & fmt ) { fmt.wd = wd; return fmt; } 294 _Ostream_Manip(const char *) & wd( unsigned int wd, unsigned int pc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = wd; fmt.pc = pc; fmt.flags.pc = true; return fmt; } 298 295 _Ostream_Manip(const char *) & left( _Ostream_Manip(const char *) & fmt ) { fmt.flags.left = true; return fmt; } 299 296 _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; } … … 385 382 386 383 static inline { 387 _Istream_Cskip skip( const char scanset[] ) { return (_Istream_Cskip)@{ scanset,0 }; }388 _Istream_Cskip skip( unsigned int wd ) { return (_Istream_Cskip)@{ 0p,wd }; }384 _Istream_Cskip skip( const char scanset[] ) { return (_Istream_Cskip)@{ .scanset : scanset, .wd : 0 }; } 385 _Istream_Cskip skip( unsigned int wd ) { return (_Istream_Cskip)@{ .scanset : 0p, .wd : wd }; } 389 386 } // distribution 390 387 … … 400 397 unsigned char ignore:1; // do not change input argument 401 398 unsigned char inex:1; // include/exclude characters in scanset 402 unsigned char delimiter:1; // delimit character 399 unsigned char delimiter:1; // delimit character(s) 403 400 unsigned char rwd:1; // read width 404 401 } flags; … … 406 403 }; // _Istream_str_base 407 404 408 struct _Istream_C str{405 struct _Istream_Cwidth { 409 406 char * s; 410 407 inline _Istream_str_base; 411 408 }; // _Istream_Cstr 412 409 410 // Restrict nesting of input manipulators to those combinations that make sense. 411 412 struct _Istream_Cstr { 413 _Istream_Cwidth cstr; 414 }; // _Istream_Cstr 415 413 416 struct _Istream_Cquoted { 414 _Istream_C strcstr;417 _Istream_Cwidth cstr; 415 418 }; // _Istream_Cquoted 416 419 417 420 static inline { 418 // width must include room for null terminator 419 _Istream_Cstr wdi( unsigned int wd, char s[] ) { return (_Istream_Cstr)@{ s, { {0p}, wd, {.all : 0} } }; } 420 _Istream_Cstr wdi( unsigned int wd, unsigned int rwd, char s[] ) { 421 if ( wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; 422 return (_Istream_Cstr)@{ s, { {0p}, rwd, {.flags.rwd : true} } }; 421 // width must include room for null terminator, (gcc) scanf does not allow a 0 width => wd > 1 (1 char and null) and rd > 0 (1 char); 422 _Istream_Cwidth wdi( unsigned int wd, char s[] ) { 423 if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator 424 return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } }; 423 425 } 424 _Istream_C quoted & quoted( _Istream_Cstr & fmt, const char Ldelimiter = '"', const char Rdelimiter = '\0') {425 fmt.delimiters[0] = Ldelimiter; fmt.delimiters[1] = Rdelimiter; fmt.delimiters[2] = '\0';426 return (_Istream_C quoted &)fmt;426 _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) { 427 if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset 428 return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; 427 429 } 428 _Istream_C str & getline( _Istream_Cstr & fmt, const char delimiter = '\n' ) {429 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;430 _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) { 431 return (_Istream_Cquoted)@{ { .s : &ch, { {.delimiters : { Ldelimiter, Rdelimiter, '\1' }}, .wd : 1, {.flags.rwd : true} } } }; 430 432 } 431 _Istream_Cstr & incl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; } 432 _Istream_Cstr & excl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; } 433 _Istream_Cstr ignore( char s[] ) { return (_Istream_Cstr)@{ s, { {0p}, -1, {.flags.ignore : true} } }; } 434 _Istream_Cstr & ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; } 433 _Istream_Cquoted & quoted( _Istream_Cwidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) { 434 f.delimiters[0] = Ldelimiter; f.delimiters[1] = Rdelimiter; f.delimiters[2] = '\0'; 435 return (_Istream_Cquoted &)f; 436 } 437 _Istream_Cstr & getline( _Istream_Cwidth & f, const char delimiter = '\n' ) { 438 f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Cstr &)f; 439 } 440 _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; } 441 _Istream_Cstr & excl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Cstr &)f; } 442 _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cwidth)@{ .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; } 443 _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; } 444 _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; } 445 _Istream_Cstr & ignore( _Istream_Cstr & f ) { f.cstr.flags.ignore = true; return (_Istream_Cstr &)f; } 435 446 } // distribution 436 447 437 448 forall( istype & | basic_istream( istype ) ) { 438 istype & ?|?( istype & is, _Istream_Cstr f );439 449 istype & ?|?( istype & is, _Istream_Cskip f ); 440 450 istype & ?|?( istype & is, _Istream_Cquoted f ); 441 } // distribution 442 443 struct _Istream_Char { 444 bool ignore; // do not change input argument 445 }; // _Istream_Char 446 447 static inline { 448 _Istream_Char ignore( const char ) { return (_Istream_Char)@{ true }; } 449 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 450 } // distribution 451 forall( istype & | basic_istream( istype ) ) { 452 istype & ?|?( istype & is, _Istream_Char f ); 453 } 451 istype & ?|?( istype & is, _Istream_Cstr f ); 452 static inline { 453 istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; } 454 } // distribution 455 } // distribution 454 456 455 457 forall( T & | sized( T ) ) … … 462 464 #define INPUT_FMT_DECL( T ) \ 463 465 static inline { \ 464 _Istream_Manip(T) ignore( const T & val ) { return (_Istream_Manip(T))@{ (T &)val, -1, true }; } \ 466 _Istream_Manip(T) wdi( unsigned int wd, T & val ) { return (_Istream_Manip(T))@{ .val : val, .wd : wd, .ignore : false }; } \ 467 _Istream_Manip(T) ignore( const T & val ) { return (_Istream_Manip(T))@{ .val : (T &)val, .wd : -1, .ignore : true }; } \ 465 468 _Istream_Manip(T) & ignore( _Istream_Manip(T) & fmt ) { fmt.ignore = true; return fmt; } \ 466 _Istream_Manip(T) wdi( unsigned int wd, T & val ) { return (_Istream_Manip(T))@{ val, wd, false }; } \467 _Istream_Manip(T) & wdi( unsigned int wd, _Istream_Manip(T) & fmt ) { fmt.wd = wd; return fmt; } \468 469 } /* distribution */ \ 469 470 forall( istype & | basic_istream( istype ) ) { \ … … 471 472 } // ?|? 472 473 474 INPUT_FMT_DECL( char ) 473 475 INPUT_FMT_DECL( signed char ) 474 476 INPUT_FMT_DECL( unsigned char )
Note:
See TracChangeset
for help on using the changeset viewer.