6fd4bb32fb1cc6c9f3c72f8a193a1adf92cf73f3
[mmh] / uip / scansbr.c
1
2 /*
3  * scansbr.c -- routines to help scan along...
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <h/addrsbr.h>
10 #include <h/fmt_scan.h>
11 #include <h/scansbr.h>
12 #include <h/tws.h>
13
14 #ifdef _FSTDIO
15 # define _ptr _p                /* Gag    */
16 # define _cnt _w                /* Wretch */
17 #endif
18
19 #ifdef SCO_5_STDIO
20 # define _ptr  __ptr
21 # define _cnt  __cnt
22 # define _base __base
23 # define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
24 #endif
25
26 #define MAXSCANL 256            /* longest possible scan line */
27
28 /*
29  * Buffer size for content part of header fields.  We want this
30  * to be large enough so that we don't do a lot of extra FLDPLUS
31  * calls on m_getfld but small enough so that we don't snarf
32  * the entire message body when we're only going to display 30
33  * characters of it.
34  */
35 #define SBUFSIZ 512
36
37 static struct format *fmt;
38 #ifdef JLR
39 static struct format *fmt_top;
40 #endif /* JLR */
41
42 static struct comp *datecomp;           /* pntr to "date" comp             */
43 static struct comp *bodycomp;           /* pntr to "body" pseudo-comp      *
44                                          * (if referenced)                 */
45 static int ncomps = 0;                  /* # of interesting components     */
46 static char **compbuffers = 0;          /* buffers for component text      */
47 static struct comp **used_buf = 0;      /* stack for comp that use buffers */
48
49 static int dat[5];                      /* aux. data for format routine    */
50
51 char *scanl = 0;                        /* text of most recent scanline    */
52
53 #define DIEWRERR() adios (scnmsg, "write error on")
54
55 #define FPUTS(buf) {\
56                 if (mh_fputs(buf,scnout) == EOF)\
57                     DIEWRERR();\
58                 }
59
60 /*
61  * prototypes
62  */
63 int sc_width (void);                    /* from termsbr.c */
64 static int mh_fputs(char *, FILE *);
65
66
67 int
68 scan (FILE *inb, int innum, int outnum, char *nfs, int width, int curflg,
69       int unseen, char *folder, long size, int noisy)
70 {
71     int i, compnum, encrypted, state;
72     char *cp, *tmpbuf, **nxtbuf;
73     char *saved_c_text;
74     struct comp *cptr;
75     struct comp **savecomp;
76     char *scnmsg;
77     FILE *scnout;
78     char name[NAMESZ];
79     static int rlwidth, slwidth;
80
81 #ifdef RPATHS
82     char returnpath[BUFSIZ];
83     char deliverydate[BUFSIZ];
84 #endif
85
86     /* first-time only initialization */
87     if (!scanl) {
88         if (width == 0) {
89             if ((width = sc_width ()) < WIDTH/2)
90                 width = WIDTH/2;
91             else if (width > MAXSCANL)
92                 width = MAXSCANL;
93         }
94         dat[3] = slwidth = width;
95         if ((scanl = (char *) malloc((size_t) (slwidth + 2) )) == NULL)
96             adios (NULL, "unable to malloc scan line (%d bytes)", slwidth+2);
97         if (outnum)
98             umask(~m_gmprot());
99
100         /* Compile format string */
101         ncomps = fmt_compile (nfs, &fmt) + 1;
102
103 #ifdef JLR
104         fmt_top = fmt;
105 #endif  /* JLR */
106         FINDCOMP(bodycomp, "body");
107         FINDCOMP(datecomp, "date");
108         FINDCOMP(cptr, "folder");
109         if (cptr && folder)
110             cptr->c_text = folder;
111         FINDCOMP(cptr, "encrypted");
112         if (!cptr)
113             if ((cptr = (struct comp *) calloc (1, sizeof(*cptr)))) {
114                 cptr->c_name = "encrypted";
115                 cptr->c_next = wantcomp[i = CHASH (cptr->c_name)];
116                 wantcomp[i] = cptr;
117                 ncomps++;
118         }
119         FINDCOMP (cptr, "dtimenow");
120         if (cptr)
121             cptr->c_text = getcpy(dtimenow (0));
122         nxtbuf = compbuffers = (char **) calloc((size_t) ncomps, sizeof(char *));
123         if (nxtbuf == NULL)
124             adios (NULL, "unable to allocate component buffers");
125         used_buf = (struct comp **) calloc((size_t) (ncomps+1),
126             sizeof(struct comp *));
127         if (used_buf == NULL)
128             adios (NULL, "unable to allocate component buffer stack");
129         used_buf += ncomps+1; *--used_buf = 0;
130         rlwidth = bodycomp && (width > SBUFSIZ) ? width : SBUFSIZ;
131         for (i = ncomps; i--; )
132             if ((*nxtbuf++ = malloc(rlwidth)) == NULL)
133                 adios (NULL, "unable to allocate component buffer");
134     }
135
136     /*
137      * each-message initialization
138      */
139     nxtbuf = compbuffers;
140     savecomp = used_buf;
141     tmpbuf = *nxtbuf++;
142     dat[0] = innum ? innum : outnum;
143     dat[1] = curflg;
144     dat[4] = unseen;
145
146     /*
147      * Get the first field.  If the message is non-empty
148      * and we're doing an "inc", open the output file.
149      */
150     if ((state = m_getfld (FLD, name, tmpbuf, rlwidth, inb)) == FILEEOF) {
151         if (ferror(inb)) {
152             advise("read", "unable to"); /* "read error" */
153             return SCNFAT;
154         } else {
155             return SCNEOF;
156         }
157     }
158
159     if (outnum) {
160         if (outnum > 0) {
161             scnmsg = m_name (outnum);
162             if (*scnmsg == '?')         /* msg num out of range */
163                 return SCNNUM;
164         } else {
165             scnmsg = "/dev/null";
166         }
167         if ((scnout = fopen (scnmsg, "w")) == NULL)
168             adios (scnmsg, "unable to write");
169 #ifdef RPATHS
170         /*
171          * Add the Return-Path and Delivery-Date
172          * header fields to message.
173          */
174         if (get_returnpath (returnpath, sizeof(returnpath),
175                 deliverydate, sizeof(deliverydate))) {
176             FPUTS ("Return-Path: ");
177             FPUTS (returnpath);
178             FPUTS ("Delivery-Date: ");
179             FPUTS (deliverydate);
180         }
181 #endif /* RPATHS */
182     }
183
184     /* scan - main loop */
185     for (compnum = 1; ; state = m_getfld (state, name, tmpbuf, rlwidth, inb)) {
186         switch (state) {
187             case FLD: 
188             case FLDPLUS: 
189                 compnum++;
190                 if (outnum) {
191                     FPUTS (name);
192                     if ( putc (':', scnout) == EOF) DIEWRERR();
193                     FPUTS (tmpbuf);
194                 }
195                 /*
196                  * if we're interested in this component, save a pointer
197                  * to the component text, then start using our next free
198                  * buffer as the component temp buffer (buffer switching
199                  * saves an extra copy of the component text).
200                  */
201                 if ((cptr = wantcomp[CHASH(name)])) {
202                     do {
203                         if (!strcasecmp(name, cptr->c_name)) {
204                             if (! cptr->c_text) {
205                                 cptr->c_text = tmpbuf;
206                                 for (cp = tmpbuf + strlen (tmpbuf) - 1; 
207                                         cp >= tmpbuf; cp--)
208                                     if (isspace (*cp))
209                                         *cp = 0;
210                                     else
211                                         break;
212                                 *--savecomp = cptr;
213                                 tmpbuf = *nxtbuf++;
214                             }
215                             break;
216                         }
217                     } while ((cptr = cptr->c_next));
218                 }
219
220                 while (state == FLDPLUS) {
221                     state = m_getfld (state, name, tmpbuf, rlwidth, inb);
222                     if (outnum)
223                         FPUTS (tmpbuf);
224                 }
225                 break;
226
227             case BODY: 
228                 compnum = -1;
229                 if (! outnum) {
230                     state = FILEEOF; /* stop now if scan cmd */
231                     goto finished;
232                 }
233                 if (putc ('\n', scnout) == EOF) DIEWRERR();
234                 FPUTS (tmpbuf);
235                 /*
236                  * performance hack: some people like to run "inc" on
237                  * things like net.sources or large digests.  We do a
238                  * copy directly into the output buffer rather than
239                  * going through an intermediate buffer.
240                  *
241                  * We need the amount of data m_getfld found & don't
242                  * want to do a strlen on the long buffer so there's
243                  * a hack in m_getfld to save the amount of data it
244                  * returned in the global "msg_count".
245                  */
246 body:;
247                 while (state == BODY) {
248 #ifdef LINUX_STDIO
249                     if (scnout->_IO_write_ptr == scnout->_IO_write_end) {
250 #else
251                     if (scnout->_cnt <= 0) {
252 #endif
253                         if (fflush(scnout) == EOF)
254                             DIEWRERR ();
255                     }
256 #ifdef LINUX_STDIO
257                     state = m_getfld(state, name, scnout->_IO_write_ptr,
258                         (long)scnout->_IO_write_ptr-(long)scnout->_IO_write_end , inb);
259                     scnout->_IO_write_ptr += msg_count;
260 #else
261                     state = m_getfld( state, name, scnout->_ptr, -(scnout->_cnt), inb );
262                     scnout->_cnt -= msg_count;
263                     scnout->_ptr += msg_count;
264 #endif
265                 }
266                 goto finished;
267
268             case LENERR: 
269             case FMTERR: 
270                 fprintf (stderr, 
271                         innum ? "??Format error (message %d) in "
272                               : "??Format error in ",
273                         outnum ? outnum : innum);
274                 fprintf (stderr, "component %d\n", compnum);
275
276                 if (outnum) {
277                     FPUTS ("\n\nBAD MSG:\n");
278                     FPUTS (name);
279                     if (putc ('\n', scnout) == EOF) DIEWRERR();
280                     state = BODY;
281                     goto body;
282                 }
283                 /* fall through */
284
285             case FILEEOF:
286                 goto finished;
287
288             default: 
289                 adios (NULL, "getfld() returned %d", state);
290         }
291     }
292
293     /*
294      * format and output the scan line.
295      */
296 finished:
297     if (ferror(inb)) {
298         advise("read", "unable to"); /* "read error" */
299         return SCNFAT;
300     }
301
302     /* Save and restore buffer so we don't trash our dynamic pool! */
303     if (bodycomp) {
304         saved_c_text = bodycomp->c_text;
305         bodycomp->c_text = tmpbuf;
306     }
307
308     if (size)
309         dat[2] = size;
310     else if (outnum > 0)
311     {
312         dat[2] = ftell(scnout);
313         if (dat[2] == EOF) DIEWRERR();
314     }
315
316     if ((datecomp && !datecomp->c_text) || (!size && !outnum)) {
317         struct stat st;
318
319         fstat (fileno(inb), &st);
320         if (!size && !outnum)
321             dat[2] = st.st_size;
322         if (datecomp) {
323             if (! datecomp->c_text) {
324                 if (datecomp->c_tws == NULL)
325                     datecomp->c_tws = (struct tws *)
326                         calloc((size_t) 1, sizeof(*datecomp->c_tws));
327                 if (datecomp->c_tws == NULL)
328                     adios (NULL, "unable to allocate tws buffer");
329                 *datecomp->c_tws = *dlocaltime ((time_t *) &st.st_mtime);
330                 datecomp->c_flags = -1;
331             } else {
332                 datecomp->c_flags = 0;
333             }
334         }
335     }
336
337     fmt_scan (fmt, scanl, slwidth, dat);
338
339 #if 0
340     fmt = fmt_scan (fmt, scanl, slwidth, dat);
341     if (!fmt)
342         fmt = fmt_top;          /* reset for old format files */
343 #endif
344
345     if (bodycomp)
346         bodycomp->c_text = saved_c_text;
347
348     if (noisy)
349         fputs (scanl, stdout);
350
351     FINDCOMP (cptr, "encrypted");
352     encrypted = cptr && cptr->c_text;
353
354     /* return dynamically allocated buffers to pool */
355     while ((cptr = *savecomp++)) {
356         *--nxtbuf = cptr->c_text;
357         cptr->c_text = NULL;
358     }
359     *--nxtbuf = tmpbuf;
360
361     if (outnum && (ferror(scnout) || fclose (scnout) == EOF))
362         DIEWRERR();
363
364     return (state != FILEEOF ? SCNERR : encrypted ? SCNENC : SCNMSG);
365 }
366
367
368 /*
369  * Cheat:  we are loaded with adrparse, which wants a routine called
370  * OfficialName().  We call adrparse:getm() with the correct arguments
371  * to prevent OfficialName() from being called.  Hence, the following
372  * is to keep the loader happy.
373  */
374 char *
375 OfficialName (char *name)
376 {
377     return name;
378 }
379
380
381 static int
382 mh_fputs(char *s, FILE *stream)
383 {
384     char c;
385
386     while ((c = *s++)) 
387         if (putc (c,stream) == EOF )
388             return(EOF);
389     return (0);
390 }
391