Index: libcfa/src/iostream.cfa
===================================================================
--- libcfa/src/iostream.cfa	(revision be100795327fa2f8e243b578f49574d6eef346ba)
+++ libcfa/src/iostream.cfa	(revision 4aae2bde4957a0391e41ead76b6043b2962a56c7)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Nov 17 13:33:12 2023
-// Update Count     : 1853
+// Last Modified On : Wed Jan  3 10:53:13 2024
+// Update Count     : 1898
 //
 
@@ -984,5 +984,5 @@
 	}
 
-	istype & ?|?( istype & is, _Istream_Cquoted f ) {
+	istype & ?|?( istype & is, _Istream_Cquoted f ) with( f ) {
 		char fmtstr[32];								// storage scanset and format codes
 		fmtstr[0] = '%';
@@ -992,36 +992,33 @@
 		bool check = true;
 
-		if ( f.flags.ignore ) { check = false; fmtstr[1] = '*'; pos += 1; }
-		int rwd = f.wd;
-		if ( f.wd != -1 ) {								// => just ignore versus ignore with width
+		if ( cstr.flags.ignore ) { check = false; fmtstr[1] = '*'; pos += 1; }
+		int rwd = cstr.wd;
+		if ( cstr.wd != -1 ) {						// => just ignore versus ignore with width
 			// wd is buffer bytes available (for input chars + null terminator)
 			// rwd is count of input chars
 			// no maximum width necessary because text ignored => width is read width
-			if ( f.flags.rwd ) check = false;
-			else rwd = f.wd - 1;
+			if ( cstr.flags.rwd ) check = false;
+			else rwd = cstr.wd - 1;
 			pos += sprintf( &fmtstr[pos], "%d", rwd );
 		} // if
 
 		int len = 0;									// may not be set in fmt
-		if ( ! f.flags.inex ) {							// => quoted getline
-			// fprintf( stderr, "quoted\n" );
+		char enddelim;
+		if ( ! cstr.flags.inex ) {						// => quoted getline
 			args = fmt( is, "%*[ \f\n\r\t\v]" );		// remove leading whitespace
 			if ( eof( is ) ) goto Eof;
-//			args = fmt( is, (const char *)f.delimiter ); // remove leading quote
-			args = fmt( is, "'%n", &len ); // remove leading quote
-			fprintf( stderr, "quoted %d %d\n", args, len );
+			char rfmt[4] = { cstr.delimiters[0], '%', 'n', '\0' };
+			args = fmt( is, rfmt, &len );				// remove leading quote
 			if ( len == 0 || eof( is ) ) goto Eof;
 		} // if
-		sprintf( &fmtstr[pos], "[^%c]%%n", f.delimiter[0] );
-		// fprintf( stderr, "getline %s %d\n", fmtstr, f.wd );
-		if ( f.flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
-		else args = fmt( is, fmtstr, f.s, &len );
-		// fprintf( stderr, "getline %s %d %d %d\n", fmtstr, args, f.wd, eof( is ) );
+		enddelim = cstr.delimiters[1] == '\0' ? cstr.delimiters[0] : cstr.delimiters[1];
+		sprintf( &fmtstr[pos], "[^%c]%%n", enddelim );
+		if ( cstr.flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
+		else args = fmt( is, fmtstr, cstr.s, &len );
 		if ( check && len == rwd && ! eof( is ) ) {		// might not fit
 			char peek;
 			fmt( is, "%c", &peek );						// check for delimiter
-			// fprintf( stderr, "peek %d '%c'\n", args, peek );
 			if ( ! eof( is ) ) {
-				if ( peek != f.delimiter[0] ) {
+				if ( peek != enddelim ) {
 					ungetc( is, peek );
 					throwResume ExceptionInst( cstring_length );
@@ -1030,7 +1027,6 @@
 		} else fmt( is, "%*c" );						// remove delimiter
 	  Eof: ;
-		if ( rwd > 0 && args == 0 ) f.s[0] = '\0';		// read failed => no pattern match => set string to null
+		if ( rwd > 0 && args == 0 ) cstr.s[0] = '\0';	// read failed => no pattern match => set string to null
 		if ( args == 1 && eof( is ) ) {					// data but scan ended at EOF
-			// fprintf( stderr, "clear\n" );
 			clear( is );								// => reset EOF => detect again on next read
 		} // if
@@ -1038,8 +1034,8 @@
 	}
 
-	istype & ?|?( istype & is, _Istream_Cstr f ) {
+	istype & ?|?( istype & is, _Istream_Cstr f ) with( f ) {
 		const char * scanset;
 		size_t nscanset = 0;
-		if ( f.flags.delimiter ) scanset = f.delimiter;	// getline ?
+		if ( flags.delimiter ) scanset = delimiters;	// getline ?
 		else scanset = f.scanset;
 		if ( scanset ) nscanset = strlen( scanset );
@@ -1084,30 +1080,18 @@
 			if ( f.flags.delimiter ) {					// getline
 				int len = 0;							// may not be set in fmt
-				if ( ! f.flags.inex ) {					// => quoted getline
-					// fprintf( stderr, "quoted\n" );
-					args = fmt( is, "%*[ \f\n\r\t\v]" ); // remove leading whitespace
-					if ( eof( is ) ) goto X;
-					args = fmt( is, "\"" );				// remove leading quote
-					if ( eof( is ) ) goto X;
-				} // if
-				// fprintf( stderr, "getline\n" );
-				// sprintf( &fmtstr[pos], "[%s%s]%%n", f.flags.inex ? "^" : "", scanset );
-				sprintf( &fmtstr[pos], "[^%s]%%n", scanset );
-				// fprintf( stderr, "getline %s %d\n", fmtstr, f.wd );
+				sprintf( &fmtstr[pos], "[^%c]%%n", f.delimiters[0] );
 				if ( f.flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
 				else args = fmt( is, fmtstr, f.s, &len );
-				// fprintf( stderr, "getline %s %d %d %d\n", fmtstr, args, f.wd, eof( is ) );
 				if ( check && len == rwd && ! eof( is ) ) {	// might not fit
-					char peek;
-					fmt( is, "%c", &peek );				// check for delimiter
-					// fprintf( stderr, "peek %d '%c'\n", args, peek );
+					fmtstr[0] = f.delimiters[0]; fmtstr[1] = '%'; fmtstr[2] = 'n'; fmtstr[3] = '\0';
+					fmt( is, fmtstr, &len );			// remove delimiter
 					if ( ! eof( is ) ) {
-						if ( peek != f.delimiter[0] ) {
-							ungetc( is, peek );
+//						if ( peek != f.delimiter[0] ) {
+						if ( len != 1 ) {
+//							ungetc( is, peek );
 							throwResume ExceptionInst( cstring_length );
 						} // if
 					} // if
-				} else fmt( is, "%*c" );			// remove delimiter
-			  X: ;
+				} else fmt( is, "%*c" );				// remove delimiter
 			} else {
 				// incl %[xxx],  %*[xxx],  %w[xxx],  %*w[xxx]
Index: libcfa/src/iostream.hfa
===================================================================
--- libcfa/src/iostream.hfa	(revision be100795327fa2f8e243b578f49574d6eef346ba)
+++ libcfa/src/iostream.hfa	(revision 4aae2bde4957a0391e41ead76b6043b2962a56c7)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Nov 15 17:55:31 2023
-// Update Count     : 596
+// Last Modified On : Wed Jan  3 10:53:18 2024
+// Update Count     : 610
 //
 
@@ -392,5 +392,5 @@
 	union {
 		const char * scanset;
-		char delimiter[2];
+		char delimiters[3];								// [0] => left, [1] => right
 	};
 	int wd;												// width
@@ -412,6 +412,5 @@
 
 struct _Istream_Cquoted {
-	char * s;
-	inline _Istream_str_base;
+	_Istream_Cstr cstr;
 }; // _Istream_Cquoted
 
@@ -419,15 +418,15 @@
 	// width must include room for null terminator
 	_Istream_Cstr wdi( unsigned int wd, char s[] ) { return (_Istream_Cstr)@{ s, { {0p}, wd, {.all : 0} } }; }
-	// read width does not include null terminator
 	_Istream_Cstr wdi( unsigned int wd, unsigned int rwd, char s[] ) {
 		if ( wd <= rwd ) throw (cstring_length){ &cstring_length_vt };
 		return (_Istream_Cstr)@{ s, { {0p}, rwd, {.flags.rwd : true} } };
 	}
-	_Istream_Cquoted & quoted( _Istream_Cstr & fmt, const char delimiter = '"' ) {
-		fmt.delimiter[0] = delimiter; fmt.delimiter[1] = '\0';
+	_Istream_Cquoted & quoted( _Istream_Cstr & fmt, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) {
+		fmt.delimiters[0] = Ldelimiter;  fmt.delimiters[1] = Rdelimiter;  fmt.delimiters[2] = '\0';
 		return (_Istream_Cquoted &)fmt;
 	}
 	_Istream_Cstr & getline( _Istream_Cstr & fmt, const char delimiter = '\n' ) {
-		fmt.delimiter[0] = delimiter; fmt.delimiter[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt; }
+		fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;
+	}
 	_Istream_Cstr & incl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
 	_Istream_Cstr & excl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
