Add StreamReader and rebuild Parser/LineParser/IMFHeader/IMFBoundary on it

Introduces StreamReader to accumulate socket data delivered in arbitrary
chunks into one buffer, so a Parser can build an object (a line, a header
block, a MIME boundary) that spans any number of packets without losing
its place. Parser is redesigned around this: no longer inherits MString
(was redundant with its own buffer member), takes a StreamReader&, and
drives a resumable COMPLETE/INCOMPLETE/DATA_AVAIL state-machine protocol
via doParse(). LineParser, IMFHeader, and IMFBoundary are rewritten as
real, working resumable parsers against the shared buffer instead of the
previous non-compiling stubs (mismatched constructors/return types, a
syntax error, checkParse() never implemented).

IMFFormData is left as the stub it already was, just restored to
compiling against the new IMFHeader signature - fully wiring it up needs
IMFMultipart/IMFText migrated to StreamReader too, out of scope here.

Verified with real runtime tests (not just compilation) covering data
split mid-line, mid-CRLF, mid-header-fold, and mid-boundary-marker across
separate feed() calls, plus StreamReader::compact(). Along the way fixed
a header-value bug (space after ':' wasn't skipped) and a double
cursor-offset subtraction bug in two places caused by forgetting that
ZString::getLength() is already cursor-relative, not the raw buffer size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-08-23 14:25:58 -07:00
1 parent 94d4a30e88
commit a033cc6718
13 files changed
+443 -136

No files matched your search

+30 -8
View File
@@ -1,26 +1,48 @@
#ifndef __IMFHeader_h__
# define __IMFHeader_h__
# include "StreamReader.h"
# include "Parser.h"
# include "MString.h"
# include "IMFHeaderField.h"
# include <vector>
# include <map>
namespace coreutils {
///
/// IMFHeader parses a full block of IMF/MIME-style headers (RFC 5322
/// "name: value" pairs, one per line, with folded continuation lines -
/// any line starting with a space or tab is appended to the previous
/// field's value) from the shared StreamReader, stopping at the blank
/// line that terminates the header block. Resumable across any number of
/// StreamReader::feed() calls - partially-read names/values accumulate
/// in the name/field members rather than being discarded between calls.
///
class IMFHeader : public Parser {
public:
IMFHeader(StreamReader &reader);
virtual int parse();
///
/// Valid once doParse() has returned Status::COMPLETE - every header
/// field parsed, keyed by field name.
///
std::map<MString, MString> &getHeaders();
protected:
Status parse() override;
private:
enum State { NAME, SKIP_SPACE, FIELD, FOLD_CHECK } state = NAME;
MString name;
MString field;
std::map<MString, MString> headers;
void commitField();
};
}