diff options
Diffstat (limited to 'maildrop/reformail.C')
| -rw-r--r-- | maildrop/reformail.C | 1204 |
1 files changed, 1204 insertions, 0 deletions
diff --git a/maildrop/reformail.C b/maildrop/reformail.C new file mode 100644 index 0000000..be1e9cb --- /dev/null +++ b/maildrop/reformail.C @@ -0,0 +1,1204 @@ +/* +** Copyright 1998 - 2009 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include "config.h" + +#include <stdio.h> +#include <iostream> +#include <iomanip> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> +#include <pwd.h> + +#if HAVE_LOCALE_H +#include <locale.h> +#endif + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#include "mytime.h" +#include <sys/types.h> +#include "mywait.h" +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#include "rfc822.h" +#include "buffer.h" +#include "liblock/config.h" +#include "liblock/liblock.h" + +#if HAS_GETHOSTNAME +#else +extern "C" int gethostname(const char *, size_t); +#endif + + +static int inbody=0, addcrs=0, catenate=0; +static const char *(*append_more_headers)(); +static Buffer optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR; + +static Buffer add_from_filter_buf; +static const char *add_from_filter_buf_ptr; +static const char *cache_maxlen="", *cache_name=""; + +static const char *( *from_filter)(); +static Buffer current_line; + +void outofmem() +{ + std::cerr << "reformail: Out of memory." << std::endl; + exit(1); +} + +void help() +{ + std::cerr << "reformail: Invalid arguments." << std::endl; + exit(1); +} + +// Return next line from standard input. Trailing CRs are stripped + +const char *NextLine() +{ +static Buffer buf; +int c; + + buf.reset(); + while ((c=std::cin.get()) >= 0 && c != '\n') + buf.push(c); + if (c < 0 && buf.Length() == 0) return (0); + if (buf.Length()) // Strip CRs + { + c=buf.pop(); + if (c != '\r') buf.push(c); + } + buf.push('\n'); + buf.push('\0'); + return (buf); +} + +// from_filter is the initial filtering done on the message. +// from_filter adds or subtracts "From " quoting from the message. +// from_filter returns the next line from the message, after filtering. +// The line is always terminated by a newline character. +// When the header is being read, multiline headers are silently +// concatenated into a single return from from_filter() (separated by +// newlines, of course. +// +// from_filter is initialized to either from_filter(), add_from_filter() +// and rem_from_filter() respectively, to cause either addition, removal of, +// or no change to from quoting. The pointer is automatically updated. +// +// Also, the from_filter silently discards empty lines at the beginning of +// the message. + +// DO NOT CHANGE FROM QUOTING. + +const char *no_from_filter_header(); + +const char *no_from_filter() +{ +const char *p; + + while ((p=NextLine()) && *p == '\n') + ; + if (!p) return (0); + + current_line=p; + return ( no_from_filter_header() ); +} + +const char *read_blank(); + +const char *no_from_filter_header() +{ +const char *p; + +static Buffer buf; + + from_filter= &no_from_filter_header; + + while ((p=NextLine()) && *p && *p != '\n' && isspace((unsigned char)*p)) + current_line += p; + + buf=current_line; + buf+='\0'; + if (!p || *p == '\n') + { + from_filter= &read_blank; + return (buf); + } + current_line=p; + return (buf); +} + +const char *read_blank() +{ + from_filter= &NextLine; + return ("\n"); +} + +////////////////////////////////////////////////////////////////////////// +// +// Add 'From ' quoting. All headers are read into add_from_filter_buf, +// and a suitable return address is located. The 'From ' line is +// generated, and return. Subsequent calls fetch one header at a +// time from add_from_filter_buf, then resume reading the body of the +// message. +// +////////////////////////////////////////////////////////////////////////// + +const char *add_from_filter_header(); +const char *add_from_filter() +{ +const char *p; +int n; + while ((p=NextLine()) && *p == '\n') + ; + if (!p) return (0); + + current_line=p; + if (strncmp(p, "From ", 5) == 0) + return ( no_from_filter_header() ); + add_from_filter_buf.reset(); + while (p && *p != '\n') + { + add_from_filter_buf += p; + p=NextLine(); + } + + add_from_filter_buf += '\0'; + +static Buffer return_path; +static Buffer from_header; + + return_path.reset(); + from_header.reset(); + + for (p=add_from_filter_buf; *p; ) + { + Buffer header; + + while (*p && *p != ':' && *p != '\n') + { + int c= (unsigned char)*p++; + + c=tolower(c); + header.push(c); + } + for (;;) + { + while (*p && *p != '\n') + { + header.push(*p); + p++; + } + if (!*p) break; + ++p; + header.push('\n'); + if (!*p || !isspace((unsigned char)*p)) break; + } + header.push('\0'); + if (strncmp(header, "return-path:", 12) == 0 || + strncmp(header, ">return-path:", 13) == 0 || + strncmp(header, "errors-to:", 10) == 0 || + strncmp(header, ">errors-to:", 11) == 0) + { + for (p=header; *p != ':'; p++) + ; + return_path=p; + } + + if (strncmp(header, "from:", 5) == 0) + from_header=(const char *)header + 5; + } + if (return_path.Length() == 0) return_path=from_header; + return_path += '\0'; + + struct rfc822t *rfc=rfc822t_alloc_new( (const char *)return_path, + NULL, NULL); + + if (!rfc) outofmem(); + + struct rfc822a *rfca=rfc822a_alloc( rfc); + + if (!rfca) outofmem(); + + from_header.reset(); + + for (n=0; n<rfca->naddrs; ++n) + { + if (rfca->addrs[n].tokens) + { + char *p=rfc822_display_addr_tobuf(rfca, n, NULL); + + if (p) + { + try { + from_header=p; + } catch (...) + { + free(p); + throw; + } + free(p); + break; + } + } + } + + rfc822a_free(rfca); + rfc822t_free(rfc); + + if (from_header.Length() == 0) from_header="root"; + return_path="From "; + return_path += from_header; + return_path.push(' '); +time_t t; + + time(&t); + p=ctime(&t); + while (*p && *p != '\n') + { + return_path.push(*p); + p++; + } + return_path += '\n'; + return_path += '\0'; + from_filter=add_from_filter_header; + add_from_filter_buf_ptr=add_from_filter_buf; + return (return_path); +} + +const char *add_from_filter_body(); + +const char *add_from_filter_header() +{ +static Buffer buf; + + buf.reset(); + + if (*add_from_filter_buf_ptr == '\0') + { + from_filter= &add_from_filter_body; + return ("\n"); + } + + do + { + while (*add_from_filter_buf_ptr) + { + buf.push ( (unsigned char)*add_from_filter_buf_ptr ); + if ( *add_from_filter_buf_ptr++ == '\n') break; + } + } while ( *add_from_filter_buf_ptr && *add_from_filter_buf_ptr != '\n' + && isspace( (unsigned char)*add_from_filter_buf_ptr )); + buf += '\0'; + return (buf); +} + +const char *add_from_filter_body() +{ +const char *p=NextLine(); + + if (!p) return (p); + +const char *q; + + for (q=p; *q == '>'; q++) + ; + if (strncmp(q, "From ", 5)) return (p); + +static Buffer add_from_buf; + + add_from_buf=">"; + add_from_buf += p; + add_from_buf += '\0'; + return (add_from_buf); +} + +//////////////////////////////////////////////////////////////////////////// +// +// Strip From quoting. +// +//////////////////////////////////////////////////////////////////////////// + +const char *rem_from_filter_header(); + +const char *(*rem_from_filter_header_ptr)(); + +const char *rem_from_filter() +{ +const char *p; + + while ((p=NextLine()) && *p == '\n') + ; + if (!p) return (0); + + if (strncmp(p, "From ", 5)) + { + current_line=p; + return ( no_from_filter_header() ); + } + p=NextLine(); + if (!p) return (p); + current_line=p; + rem_from_filter_header_ptr= &no_from_filter_header; + return ( rem_from_filter_header() ); +} + +const char *rem_from_filter_body(); +const char *rem_from_filter_header() +{ +const char *p=(*rem_from_filter_header_ptr)(); + + rem_from_filter_header_ptr=from_filter; + from_filter=rem_from_filter_header; + if (!p || *p == '\n') + { + from_filter=&rem_from_filter_body; + p="\n"; + } + return (p); +} + +const char *rem_from_filter_body() +{ +const char *p=NextLine(); + + if (!p) return (p); + + if (*p == '>') + { + const char *q; + + for (q=p; *q == '>'; q++) + ; + if (strncmp(q, "From ", 5) == 0) ++p; + } + return (p); +} + +static const char *HostName() +{ +static char hostname_buf[256]; + + hostname_buf[0]=0; + hostname_buf[sizeof(hostname_buf)-1]=0; + gethostname(hostname_buf, sizeof(hostname_buf)-1); + return (hostname_buf); +} + +//////////////////////////////////////////////////////////////////////////// +// +// Return TRUE if header is already in a list of headers. +// +// hdrs: null separated list of headers (and header contents) +// hdr - header to check (must be lowercase and terminated by a colon) +// pos - offset into hdrs where it's found. +// + +static int has_hdr(const Buffer &hdrs, const char *hdr, unsigned &pos) +{ +const char *r=hdrs; +int l=hdrs.Length(); +Buffer buf2; +unsigned pos2=0; + + while (l) + { + buf2.reset(); + pos=pos2; + while (l) + { + --l; + ++pos2; + buf2.push( tolower(*r)); + if (*r++ == 0) break; + } + buf2.push('\0'); + if (strncmp(hdr, buf2, strlen(hdr)) == 0) return (1); + } + return (0); +} + +static int has_hdr(const Buffer &hdrs, const char *hdr) +{ +unsigned dummy; + + return (has_hdr(hdrs, hdr, dummy)); +} + +static void strip_empty_header(Buffer &buf) +{ +Buffer newbuf; +int l; +const char *p; + + for (p=buf, l=buf.Length(); l; ) + { + if (p[strlen(p)-1] == ':') + { + while (l) + { + --l; + if (*p++ == '\0') break; + } + continue; + } + while (l) + { + --l; + newbuf.push( *p ); + if (*p++ == '\0') break; + } + } + buf=newbuf; +} + +static void strip_header(Buffer &header, unsigned offset) +{ +Buffer buf1; +const char *p=header; +int l=header.Length(); + + while (l) + { + if (!offset) + { + while (l) + { + --l; + if (*p++ == '\0') break; + } + break; + } + buf1.push( *p++ ); + --l; + --offset; + } + while (l--) + buf1.push( *p++ ); + header=buf1; +} + +const char *ReadLineAddNewHeader(); + +const char *ReadLineAddHeader() +{ +Buffer buf1; +const char *q; +const char *p; +unsigned pos; +static Buffer oldbuf; + + for (;;) + { + p= (*from_filter)(); + + if (!p) return p; + if (*p == '\n') + { + strip_empty_header(opti); + strip_empty_header(optI); + return ( ReadLineAddNewHeader()); + } + buf1.reset(); + for (q=p; *q && *q != '\n'; q++) + { + buf1.push( tolower(*q) ); + if (*q == ':') break; + } + buf1 += '\0'; + + if (has_hdr(opti, buf1)) + { + oldbuf="old-"; + oldbuf += buf1; + buf1=oldbuf; + + Buffer tbuf; + + tbuf="Old-"; + tbuf += p; + oldbuf=tbuf; + oldbuf += '\0'; + p=oldbuf; + } + if (has_hdr(optR, buf1, pos)) + { + Buffer tbuf; + + q=optR; + q += pos + strlen(buf1); + tbuf=q; + + p += strlen(buf1); + tbuf += p; + oldbuf=tbuf; + oldbuf += '\0'; + p=oldbuf; + } + + if (has_hdr(optI, buf1)) + continue; + if (has_hdr(optu, buf1)) + { + if (!has_hdr(optubuf, buf1)) + { + q=p; + do + { + optubuf.push( *q ); + } while (*q++); + break; + } + continue; + } + + if (has_hdr(optU, buf1)) + { + if (has_hdr(optUbuf, buf1, pos)) + strip_header(optUbuf, pos); + while (*p) + { + optUbuf.push( *p ); + p++; + } + optUbuf.pop(); + optUbuf.push('\0'); + continue; + } + break; + } + +unsigned offset; + + if (has_hdr(opta, buf1, offset)) + strip_header(opta, offset); + return (p); +} + +const char *ReadLineAddNewHeaderDone(); + +const char *ReadLineAddNewHeader() +{ + append_more_headers= &ReadLineAddNewHeader; + +Buffer *bufptr; + + if (opta.Length()) bufptr= &opta; + else if (optA.Length()) bufptr= &optA; + else if (opti.Length()) bufptr= &opti; + else if (optI.Length()) bufptr= &optI; + else if (optUbuf.Length()) bufptr= &optUbuf; + else + { + append_more_headers=&ReadLineAddNewHeaderDone; + return ("\n"); + } + +static Buffer buf1; +Buffer buf2; + + buf1.reset(); + +const char *p= *bufptr; +int l= bufptr->Length(); + + while (l) + { + if ( !*p ) + { + p++; + l--; + break; + } + buf1.push( *p ); + p++; + l--; + } + buf1.push('\n'); + buf1.push('\0'); + + while (l--) + buf2.push (*p++); + *bufptr=buf2; + return (buf1); +} + +const char *ReadLineAddNewHeaderDone() +{ + return ( (*from_filter)() ); +} + +//////////////////////////////////////////////////////////////////////////// +const char *ReadLine() +{ +const char *p=(*append_more_headers)(); + + if (!p) return (p); + +static Buffer buf; + + if (*p == '\n') + inbody=1; + + if (catenate && !inbody) + { + const char *q; + + buf.reset(); + for (q=p; *q; q++) + { + if (*q != '\n') + { + buf.push(*q); + continue; + } + do + { + ++q; + } while (*q && isspace(*q)); + if (*q) + buf.push(' '); + --q; + } + if (addcrs) buf.push('\r'); + buf.push('\n'); + buf.push('\0'); + return (buf); + } + + if (addcrs) + { + buf=p; + buf.pop(); + buf += "\r\n"; + buf += '\0'; + return (buf); + } + return (p); +} + +///////////////////////////////////////////////////////////////////////// +// +// Default activity: just copy the message (let the low-level format +// filters do their job. +// +///////////////////////////////////////////////////////////////////////// + +void copy(int, char *[], int) +{ +const char *p; + + while ((p= ReadLine()) != 0) + std::cout << p; +} + +void cache(int, char *[], int) +{ +const char *p; +Buffer buf; +int found=0; + + addcrs=0; + while ((p= ReadLine()) != 0) + { + int c; + + if (inbody) break; + buf.reset(); + while (*p && *p != '\n') + { + c= (unsigned char)*p; + c=tolower(c); + buf.push(c); + if (*p++ == ':') break; + } + if (!(buf == "message-id:")) continue; + buf += '\0'; + while (*p && isspace( (unsigned char)*p)) p++; + buf.reset(); + while (*p) + { + buf.push(*p); + ++p; + } + + while ( (c=(unsigned char)buf.pop()) != 0 && isspace(c)) + ; + if (c) buf.push(c); + if (buf.Length() == 0) break; + buf.push('\0'); + + int fd=open(cache_name, O_RDWR | O_CREAT, 0600); + + if (fd < 0) + { + perror("open"); + exit(75); + } + + if (ll_lock_ex(fd) < 0) + { + perror("lock"); + exit(75); + } + + off_t pos=0; + + if (lseek(fd, 0L, SEEK_END) == -1 || + (pos=lseek(fd, 0L, SEEK_CUR)) == -1 || + lseek(fd, 0L, SEEK_SET) == -1) + { + perror("seek"); + exit(75); + } + + off_t maxlen_n=atol(cache_maxlen); + char *charbuf; + off_t newpos=maxlen_n; + + if (newpos < pos) newpos=pos; + + if ((charbuf=new char[newpos+buf.Length()+1]) == NULL) + outofmem(); + + off_t readcnt=read(fd, charbuf, newpos); + + if (readcnt < 0) perror("read"); + + char *q, *r; + + for (q=r=charbuf; q<charbuf+readcnt; ) + { + if (*q == '\0') break; // Double null terminator + if (strcmp( (const char *)buf, q) == 0) + { + found=1; + while (q < charbuf+readcnt) + if (*q++ == '\0') break; + } + else while (q < charbuf+readcnt) + if ( (*r++=*q++) == '\0') break; + } + memcpy(r, (const char *)buf, buf.Length()); + r += buf.Length(); + for (q=charbuf; q<r; ) + { + if (r - q < maxlen_n) + break; + while (q < r) + if (*q++ == '\0') break; + } + if (q == r) q=charbuf; + *r++ = '\0'; + if (lseek(fd, 0L, SEEK_SET) == -1) + { + perror("lseek"); + exit(1); + } + while (q < r) + { + readcnt=write(fd, q, r-q); + if (readcnt == -1) + { + perror("write"); + exit(1); + } + q += readcnt; + } + close(fd); + delete[] charbuf; + break; + } + while ((p= ReadLine()) != 0) + ; + exit(found ? 0:1); +} + +////////////////////////////////////////////////////////////////////////// +// +// Extract headers + +void extract_headers(int, char *[], int) +{ +const char *p, *q; +Buffer b; + + catenate=1; + while ((p=ReadLine()) && !inbody) + { + b.reset(); + for (q=p; *q && *q != '\n'; ) + { + int c= (unsigned char)*q; + + b.push( tolower(c) ); + if ( *q++ == ':') break; + } + b.push(0); + + if (has_hdr(optx, b)) + { + while (*q && *q != '\n' && isspace(*q)) + q++; + std::cout << q; + continue; + } + + if (has_hdr(optX, b)) + { + std::cout << p; + continue; + } + } + if (!std::cin.seekg(0, std::ios::end).fail()) + return; + std::cin.clear(); + + while ( ReadLine() ) + ; +} +////////////////////////////////////////////////////////////////////////// +// +// Split mbox file into messages. + +void split(int argc, char *argv[], int argn) +{ +const char *p; +Buffer buf; +int l; +int do_environ=1; +unsigned long environ=0; +unsigned environ_len=3; +const char *env; + + if (argn >= argc) help(); + + while ( (p=NextLine()) && *p == '\n') + ; + + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_IGN); + env=getenv("FILENO"); + if (env) + { + const char *q; + + for (q=env; *q; q++) + if (!isdigit(*q)) break; + if (*q) do_environ=0; + else + { + environ_len=strlen(env); + environ=atol(env); + } + } + + while (p) + { + int fds[2]; + + if (pipe(fds) < 0) + { + std::cerr << "reformail: pipe() failed." << std::endl; + exit(1); + } + + pid_t pid=fork(); + + if (pid == -1) + { + std::cerr << "reformail: fork() failed." << std::endl; + exit(1); + } + + if (pid == 0) + { + dup2(fds[0], 0); + close(fds[0]); + close(fds[1]); + + Buffer buf, buf2; + + if (do_environ) + { + char *s; + + while (environ || environ_len) + { + buf.push( "0123456789"[environ % 10]); + environ /= 10; + if (environ_len) --environ_len; + } + + buf2="FILENO="; + while (buf.Length()) + buf2.push(buf.pop()); + buf2 += '\0'; + s=strdup(buf2); + if (!s) + { + perror("strdup"); + exit (1); + } + putenv(s); + } + + execvp( argv[argn], argv+argn); + std::cerr << "reformail: exec() failed." << std::endl; + exit(1); + } + close(fds[0]); + environ++; + + do + { + buf=p; + p=ReadLine(); + if (!p || strncmp(p, "From ", 5) == 0) + buf.pop(); // Drop trailing newline + else + { + if (addcrs) + { + buf.pop(); + buf.push('\r'); + buf.push('\n'); + } + } + + const char *q=buf; + + l=buf.Length(); + while (l) + { + int n= ::write( fds[1], q, l); + if (n <= 0) + { + std::cerr + << "reformail: write() failed." + << std::endl; + exit(1); + } + q += n; + l -= n; + } + } while (p && strncmp(p, "From ", 5)); + close(fds[1]); + + int wait_stat; + + while ( wait(&wait_stat) != pid ) + ; + if (!WIFEXITED(wait_stat) || WEXITSTATUS(wait_stat)) + break; // Rely on diagnostic from child + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void add_bin64(Buffer &buf, unsigned long n) +{ +int i; + + for (i=0; i<16; i++) + { + buf.push ( "0123456789ABCDEF"[n % 16] ); + n /= 16; + } +} + +static void add_messageid(Buffer &buf) +{ +time_t t; + + buf.push('<'); + time(&t); + add_bin64(buf,t); + buf.push('.'); + add_bin64(buf, getpid() ); + buf += ".reformail@"; + buf += HostName(); + buf.push('>'); +} + +static void add_opta(Buffer &buf, const char *optarg) +{ +Buffer chk_buf; +const char *c; + + for (c=optarg; *c; c++) + chk_buf.push ( tolower( (unsigned char)*c )); + if (chk_buf == "message-id:" || chk_buf == "resent_message_id:") + { + chk_buf=optarg; + chk_buf += ' '; + add_messageid(chk_buf); + chk_buf += '\0'; + optarg=chk_buf; + } + + do + { + buf.push( *optarg ); + } while (*optarg++); +} + +int main(int argc, char *argv[]) +{ +int argn, done; +const char *optarg; +void (*function)(int, char *[], int)=0; + +#if HAVE_SETLOCALE + setlocale(LC_ALL, "C"); +#endif + + from_filter= &no_from_filter; + append_more_headers=&ReadLineAddHeader; + done=0; + for (argn=1; argn<argc; argn++) + { + if (strcmp(argv[argn], "--") == 0 || strcmp(argv[argn],"-")==0) + { + ++argn; + break; + } + if (argv[argn][0] != '-') break; + optarg=argv[argn]+2; + if (!*optarg) optarg=0; + switch ( argv[argn][1] ) { + case 'd': + if (!optarg || !*optarg) optarg="1"; + addcrs=atoi(optarg); + break; + case 'c': + catenate=1; + break; + case 'f': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || *optarg == '0') + from_filter=&rem_from_filter; + else + from_filter=&add_from_filter; + break; + case 'D': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || argn+1 >= argc) help(); + if (function) help(); + function=cache; + cache_maxlen=optarg; + cache_name=argv[++argn]; + break; + case 'a': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + add_opta(opta, optarg); + break; + case 'A': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + add_opta(optA, optarg); + break; + case 'i': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + do + { + opti.push( *optarg ); + } while (*optarg++); + break; + case 'I': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + do + { + optI.push( *optarg ); + } while (*optarg++); + break; + case 'R': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + while (*optarg) + optR.push(*optarg++); + if (argn+1 >= argc) help(); + optarg=argv[++argn]; + while (*optarg) + optR.push(*optarg++); + optR.push(0); + break; + case 'u': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + while (*optarg) + optu.push(*optarg++); + optu.push(0); + break; + case 'U': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + while (*optarg) + optU.push(*optarg++); + optU.push(0); + break; + case 'x': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + while (*optarg) + optx.push(*optarg++); + optx.push(0); + break; + case 'X': + if (!optarg && argn+1 < argc) optarg=argv[++argn]; + if (!optarg || !*optarg) help(); + if (function) help(); + while (*optarg) + optX.push(*optarg++); + optX.push(0); + break; + case 's': + if (function) help(); + function= &split; + ++argn; + done=1; + break; + default: + help(); + } + if (done) break; + } + if (optx.Length() || optX.Length()) + { + if (function) help(); + function=extract_headers; + } + + if (!function) function=copy; + (*function)(argc, argv, argn); + std::cout.flush(); + if (std::cout.fail()) + { + std::cerr << "reformail: error writing output." << std::endl; + exit(1); + } + exit(0); +} |
