Index: libcfa/src/fstream.cfa
===================================================================
--- libcfa/src/fstream.cfa	(revision ca995e3090a31786ce34129347ad66e6dd17c3a9)
+++ libcfa/src/fstream.cfa	(revision e0dc038fbe4972034fd6a262cc70c8bb4258cd32)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Aug 18 10:41:17 2023
-// Update Count     : 541
+// Last Modified On : Tue Oct 17 08:38:49 2023
+// Update Count     : 544
 //
 
@@ -311,4 +311,5 @@
 	} // if
 	va_end( args );
+//	if ( len == 0 ) throw ExceptionInst( missing_data );
 	return len;
 } // fmt
Index: libcfa/src/fstream.hfa
===================================================================
--- libcfa/src/fstream.hfa	(revision ca995e3090a31786ce34129347ad66e6dd17c3a9)
+++ libcfa/src/fstream.hfa	(revision e0dc038fbe4972034fd6a262cc70c8bb4258cd32)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Aug 18 10:41:15 2023
-// Update Count     : 258
+// Last Modified On : Fri Oct 13 13:55:21 2023
+// Update Count     : 260
 //
 
@@ -119,14 +119,13 @@
 void ends( ifstream & );
 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
+ifstream & ungetc( ifstream & is, char c );
+bool eof( ifstream & is );
 
 bool fail( ifstream & is );
 void clear( ifstream & );
-bool eof( ifstream & is );
 void open( ifstream & is, const char name[], const char mode[] ); // FIX ME: use default = "r"
 void open( ifstream & is, const char name[] );
 void close( ifstream & is );
-
 ifstream & read( ifstream & is, char data[], size_t size );
-ifstream & ungetc( ifstream & is, char c );
 
 void ?{}( ifstream & is );
Index: libcfa/src/iostream.cfa
===================================================================
--- libcfa/src/iostream.cfa	(revision ca995e3090a31786ce34129347ad66e6dd17c3a9)
+++ libcfa/src/iostream.cfa	(revision e0dc038fbe4972034fd6a262cc70c8bb4258cd32)
@@ -1,3 +1,2 @@
-
 //
 // Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
@@ -11,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Oct  8 12:10:21 2023
-// Update Count     : 1564
+// Last Modified On : Tue Oct 17 20:57:05 2023
+// Update Count     : 1795
 //
 
@@ -966,13 +965,17 @@
 			char fmtstr[ sizeof("%*[]") + nscanset ];
 			int pos = 0;
-			fmtstr[pos] = '%';                  pos += 1;
-			fmtstr[pos] = '*';                  pos += 1;
-			fmtstr[pos] = '[';                  pos += 1;
+			strcpy( &fmtstr[pos], "%*[" );  pos += 3;
 			strcpy( &fmtstr[pos], f.scanset );  pos += nscanset;
-			fmtstr[pos] = ']';                  pos += 1;
-			fmtstr[pos] = '\0';
-			fmt( is, fmtstr, (void*)0 );  // last arg is dummy: suppress gcc warning
-		}
-		else for ( f.wd ) fmt( is, "%*c" );
+			strcpy( &fmtstr[pos], "]" );
+			fmt( is, fmtstr, "" );						// skip scanset
+		} else {
+			char ch;
+//			fprintf( stderr, "skip " );
+			for ( f.wd ) {								// skip N characters
+			  if ( eof( is ) ) break;
+				fmt( is, "%c", &ch );
+//				fprintf( stderr, "`%c' ", ch );
+			} // for
+		} // if
 		return is;
 	}
@@ -980,77 +983,89 @@
 
 	istype & ?|?( istype & is, _Istream_Cstr f ) {
-		const char * scanset = f.scanset;
+		const char * scanset;
+		size_t nscanset = 0;
 		if ( f.flags.delimiter ) scanset = f.delimiter;	// getline ?
-
-		size_t len = 0;
-		if ( scanset ) len = strlen( scanset );
-		char fmtstr[len + 16];
-		int start = 1;
+		else scanset = f.scanset;
+		if ( scanset ) nscanset = strlen( scanset );
+
+		char fmtstr[nscanset + 32];						// storage for scanset and format codes
 		fmtstr[0] = '%';
-		if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; }
-		// no maximum width necessary because text ignored => width is read width
-		if ( f.wd != -1 ) {
+
+		int pos = 1;
+		int args;
+		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
 			// wd is buffer bytes available (for input chars + null terminator)
 			// rwd is count of input chars
-			int rwd;
-			if (f.flags.rwd) {
-				verify (f.wd >= 0);
-				rwd = f.wd;
+			// no maximum width necessary because text ignored => width is read width
+			if ( f.flags.rwd ) check = false;
+			else rwd = f.wd - 1;
+			pos += sprintf( &fmtstr[pos], "%d", rwd );
+		} // if
+
+		if ( ! scanset ) {								// %s, %*s, %ws, %*ws
+//			fprintf( stderr, "cstr %s\n", f.s );
+			strcpy( &fmtstr[pos], "s%n" );
+			int len = 0;								// may not be set in fmt
+			if ( f.flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
+			else args = fmt( is, fmtstr, f.s, &len );
+//			fprintf( stderr, "cstr %s %d %d %d %s\n", fmtstr, args, len, f.wd, f.s );
+			if ( check && len >= rwd && ! eof( is ) ) {	// might not fit
+				char peek;
+				fmt( is, "%c", &peek );					// check for whitespace terminator
+//				fprintf( stderr, "peek %d '%c'\n", args, peek );
+				if ( ! eof( is ) ) {
+					ungetc( is, peek );
+					if ( ! isspace( peek ) ) throw ExceptionInst( cstring_length );
+				} // if
+			} // if
+		} else {
+			if ( f.flags.delimiter ) {					// getline
+//				fprintf( stderr, "getline\n" );
+				sprintf( &fmtstr[pos], "[%s%s]%%n", f.flags.inex ? "^" : "", scanset );
+//				fprintf( stderr, "getline %s %d\n", fmtstr, f.wd );
+				int len = 0;							// may not be set in fmt
+				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 );
+					if ( ! eof( is ) ) {
+						if ( peek != f.delimiter[0] ) {
+							ungetc( is, peek );
+							throw ExceptionInst( cstring_length );
+						} // if
+					} // if
+				} else fmt( is, "%*c" );				//  remove delimiter
 			} else {
-				verify (f.wd >= 1);
-				rwd = f.wd - 1;
+				// incl %[xxx],  %*[xxx],  %w[xxx],  %*w[xxx]
+				// excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx]
+				sprintf( &fmtstr[pos], "[%s%s]%%n", f.flags.inex ? "^" : "", scanset );
+//				fprintf( stderr, "incl/excl %s %d\n", fmtstr, f.wd );
+				int len = 0;							// may not be set in fmt
+				if ( f.flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
+				else args = fmt( is, fmtstr, f.s, &len );
+//				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] );
+				if ( check && len == rwd && ! eof( is ) ) {	// might not fit
+//					fprintf( stderr, "overflow\n" );
+					char peek;
+					fmt( is, "%c", &peek );				// check for whitespace terminator
+//					fprintf( stderr, "peek %d '%c'\n", args, peek );
+					if ( ! eof( is ) ) {
+						ungetc( is, peek );
+						if ( f.flags.inex ^ strchr( f.scanset, peek ) != 0p ) throw ExceptionInst( cstring_length );
+					} // if
+				} // if
 			} // if
-			start += sprintf( &fmtstr[start], "%d", rwd );
-		}
-
-		if ( ! scanset ) {
-			// %s, %*s, %ws, %*ws
-			fmtstr[start] = 's'; fmtstr[start + 1] = '\0';
-			// printf( "cstr %s\n", fmtstr );
-		} else {
-			// incl %[xxx],  %*[xxx],  %w[xxx],  %*w[xxx]
-			// excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx]
-			fmtstr[start] = '['; start += 1;
-			if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; }
-			strcpy( &fmtstr[start], scanset );			// copy includes '\0'
-			len += start;
-			fmtstr[len] = ']'; fmtstr[len + 1] = '\0';
-			// printf( "incl/excl %s\n", fmtstr );
-		} // if
-
-		int check = f.wd - 2;
-		if (! f.flags.ignore ) {
-			if ( ! f.flags.rwd ) f.s[check] = '\0';		// insert sentinel
-		}
-		len = fmt( is, fmtstr, f.s );
-		//fprintf( stderr, "KK %s %zd %d %c %s\n", fmtstr, len, check, f.s[check], f.s );
-
-		if ( ! f.flags.ignore && ! f.flags.rwd && f.s[check] != '\0' ) { // sentinel overwritten ?
-			// buffer filled, but would we have kept going?
-			if ( ! eof( is ) ) {
-				char peek;
-				fmt( is, "%c", &peek );
-				ungetc( is, peek );
-				bool hasMore;
-				if (f.flags.delimiter) { // getline
-					hasMore = (peek != f.delimiter[0]);
-				} else if (f.scanset) { // incl/excl
-					bool peekMatch = strchr(f.scanset, peek) != 0p;
-					hasMore = f.flags.inex ? (!peekMatch) : (peekMatch);
-				} else { // %s
-					hasMore = !isspace(peek);
-				}
-				if (hasMore) throw (cstring_length){ &cstring_length_vt };
-			} // if
-		} // if
-
-		if ( f.flags.delimiter ) {						// getline ?
-			if ( len == 0 ) f.s[0] = '\0';				// empty read => argument unchanged => set empty
-			if ( ! eof( is ) ) {						// ignore delimiter, may not be present because of width
-				char delimiter;
-				fmt( is, "%c", &delimiter );
-				if ( delimiter != f.delimiter[0] ) ungetc( is, delimiter );
-			} // if
-		} //if
+		} // if
+		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
 		return is;
 	} // ?|?
Index: libcfa/src/iostream.hfa
===================================================================
--- libcfa/src/iostream.hfa	(revision ca995e3090a31786ce34129347ad66e6dd17c3a9)
+++ libcfa/src/iostream.hfa	(revision e0dc038fbe4972034fd6a262cc70c8bb4258cd32)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Oct  8 12:02:55 2023
-// Update Count     : 568
+// Last Modified On : Mon Oct 16 21:12:01 2023
+// Update Count     : 573
 //
 
@@ -324,4 +324,5 @@
 	istype & ungetc( istype &, char );
 	bool eof( istype & );
+	void clear( istype & );
 }; // basic_istream
 
@@ -329,5 +330,5 @@
 trait istream {
 	bool fail( istype & );
-	void clear( istype & );
+	void open( istype & is, const char name[], const char mode[] );
 	void open( istype & is, const char name[] );
 	void close( istype & is );
@@ -402,7 +403,9 @@
 
 ExceptionDecl( cstring_length );
+ExceptionDecl( missing_data );
 
 // *********************************** manipulators ***********************************
 
+// skip does not compose with other C string manipulators.
 struct _Istream_Cskip {
 	const char * scanset;
