2 * Parses unix mail boxes into headers and bodies.
14 /* A growable buffer for collecting headers. */
17 Buffer() : data(0), allocated(0), length(0) { }
18 ~Buffer() { empty(); }
20 void append( char p ) {
21 if ( ++length > allocated )
22 upAllocate( length*2 );
26 void clear() { length = 0; }
27 void upAllocate( int len );
41 int cs, top, stack[1];
44 int execute( const char *data, int len, bool isEof );
49 machine MailboxScanner;
51 # Buffer the header names.
52 action bufHeadName { headName.append(fc); }
54 # Prints a blank line after the end of the headers of each message.
55 action blankLine { cout << endl; }
57 # Helpers we will use in matching the date section of the from line.
58 day = /[A-Z][a-z][a-z]/;
59 month = /[A-Z][a-z][a-z]/;
60 year = /[0-9][0-9][0-9][0-9]/;
61 time = /[0-9][0-9]:[0-9][0-9]/ . ( /:[0-9][0-9]/ | '' );
62 letterZone = /[A-Z][A-Z][A-Z]/;
63 numZone = /[+\-][0-9][0-9][0-9][0-9]/;
64 zone = letterZone | numZone;
65 dayNum = /[0-9 ][0-9]/;
67 # These are the different formats of the date minus an obscure
68 # type that has a funny string 'remote from xxx' on the end. Taken
69 # from c-client in the imap-2000 distribution.
70 date = day . ' ' . month . ' ' . dayNum . ' ' . time . ' ' .
71 ( year | year . ' ' . zone | zone . ' ' . year );
73 # From lines separate messages. We will exclude fromLine from a message
74 # body line. This will cause us to stay in message line up until an
75 # entirely correct from line is matched.
76 fromLine = 'From ' . (any-'\n')* . ' ' . date . '\n';
78 # The types of characters that can be used as a header name.
81 # Simply eat up an uninteresting header. Return at the first non-ws
82 # character following a newline.
86 '\n' [^ \t] @{fhold; fret;}
89 action hchar {headContent.append(fc);}
90 action hspace {headContent.append(' ');}
93 headContent.append(0);
94 cout << headContent.data << endl;
100 # Display the contents of a header as it is consumed. Collapses line
101 # continuations to a single space.
104 ( '\n' ( [ \t]+ '\n' )* [ \t]+ ) %hspace
110 if ( strcmp( headName.data, "From" ) == 0 ||
111 strcmp( headName.data, "To" ) == 0 ||
112 strcmp( headName.data, "Subject" ) == 0 )
114 /* Print the header name, then jump to a machine the will display
116 cout << headName.data << ":";
125 header = hchar+ $bufHeadName ':' @onHeader;
127 # Exclude fromLine from a messageLine, otherwise when encountering a
128 # fromLine we will be simultaneously matching the old message and a new
130 messageLine = ( [^\n]* '\n' - fromLine );
133 message = ( fromLine . header* . '\n' @blankLine . messageLine* );
135 # File is a series of messages.
141 int MailboxScanner::init( )
147 int MailboxScanner::execute( const char *data, int len, bool isEof )
149 const char *p = data;
150 const char *pe = data + len;
151 const char *eof = isEof ? pe : 0;
155 if ( cs == MailboxScanner_error )
157 if ( cs >= MailboxScanner_first_final )
162 int MailboxScanner::finish( )
164 if ( cs == MailboxScanner_error )
166 if ( cs >= MailboxScanner_first_final )
183 void Buffer::upAllocate( int len )
186 data = (char*) malloc( len );
188 data = (char*) realloc( data, len );
192 MailboxScanner mailbox;
199 int len = fread( buf, 1, BUFSIZE, stdin );
200 mailbox.execute( buf, len, len != BUFSIZE );
201 if ( len != BUFSIZE )
204 if ( mailbox.finish() <= 0 )
205 cerr << "mailbox: error parsing input" << endl;