2 * Parses unix mail boxes into headers and bodies.
13 /* A growable buffer for collecting headers. */
16 Buffer() : data(0), allocated(0), length(0) { }
17 ~Buffer() { empty(); }
19 void append( char p ) {
20 if ( ++length > allocated )
21 upAllocate( length*2 );
25 void clear() { length = 0; }
26 void upAllocate( int len );
40 int cs, top, stack[1];
43 int execute( const char *data, int len );
48 machine MailboxScanner;
50 # Buffer the header names.
51 action bufHeadName { headName.append(fc); }
53 # Prints a blank line after the end of the headers of each message.
54 action blankLine { cout << endl; }
56 # Helpers we will use in matching the date section of the from line.
57 day = /[A-Z][a-z][a-z]/;
58 month = /[A-Z][a-z][a-z]/;
59 year = /[0-9][0-9][0-9][0-9]/;
60 time = /[0-9][0-9]:[0-9][0-9]/ . ( /:[0-9][0-9]/ | '' );
61 letterZone = /[A-Z][A-Z][A-Z]/;
62 numZone = /[+\-][0-9][0-9][0-9][0-9]/;
63 zone = letterZone | numZone;
64 dayNum = /[0-9 ][0-9]/;
66 # These are the different formats of the date minus an obscure
67 # type that has a funny string 'remote from xxx' on the end. Taken
68 # from c-client in the imap-2000 distribution.
69 date = day . ' ' . month . ' ' . dayNum . ' ' . time . ' ' .
70 ( year | year . ' ' . zone | zone . ' ' . year );
72 # From lines separate messages. We will exclude fromLine from a message
73 # body line. This will cause us to stay in message line up until an
74 # entirely correct from line is matched.
75 fromLine = 'From ' . (any-'\n')* . ' ' . date . '\n';
77 # The types of characters that can be used as a header name.
80 # Simply eat up an uninteresting header. Return at the first non-ws
81 # character following a newline.
85 '\n' [^ \t] @{fhold; fret;}
88 action hchar {headContent.append(fc);}
89 action hspace {headContent.append(' ');}
92 headContent.append(0);
93 cout << headContent.data << endl;
99 # Display the contents of a header as it is consumed. Collapses line
100 # continuations to a single space.
103 ( '\n' ( [ \t]+ '\n' )* [ \t]+ ) %hspace
109 if ( strcmp( headName.data, "From" ) == 0 ||
110 strcmp( headName.data, "To" ) == 0 ||
111 strcmp( headName.data, "Subject" ) == 0 )
113 /* Print the header name, then jump to a machine the will display
115 cout << headName.data << ":";
124 header = hchar+ $bufHeadName ':' @onHeader;
126 # Exclude fromLine from a messageLine, otherwise when encountering a
127 # fromLine we will be simultaneously matching the old message and a new
129 messageLine = ( [^\n]* '\n' - fromLine );
132 message = ( fromLine . header* . '\n' @blankLine . messageLine* );
134 # File is a series of messages.
140 int MailboxScanner::init( )
146 int MailboxScanner::execute( const char *data, int len )
148 const char *p = data;
149 const char *pe = data + len;
153 if ( cs == MailboxScanner_error )
155 if ( cs >= MailboxScanner_first_final )
160 int MailboxScanner::finish( )
163 if ( cs == MailboxScanner_error )
165 if ( cs >= MailboxScanner_first_final )
182 void Buffer::upAllocate( int len )
185 data = (char*) malloc( len );
187 data = (char*) realloc( data, len );
191 MailboxScanner mailbox;
198 int len = fread( buf, 1, BUFSIZE, stdin );
199 mailbox.execute( buf, len );
200 if ( len != BUFSIZE )
203 if ( mailbox.finish() <= 0 )
204 cerr << "mailbox: error parsing input" << endl;