Deleted Added
full compact
msgs.c (8874) msgs.c (12390)
1/*-
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1980, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/*
45 * msgs - a user bulletin board program
46 *
47 * usage:
48 * msgs [fhlopq] [[-]number] to read messages
49 * msgs -s to place messages
50 * msgs -c [-days] to clean up the bulletin board
51 *
52 * prompt commands are:
53 * y print message
54 * n flush message, go to next message
55 * q flush message, quit
56 * p print message, turn on 'pipe thru more' mode
57 * P print message, turn off 'pipe thru more' mode
58 * - reprint last message
59 * s[-][<num>] [<filename>] save message
60 * m[-][<num>] mail with message in temp mbox
61 * x exit without flushing this message
62 * <num> print message number <num>
63 */
64
65#define V7 /* will look for TERM in the environment */
66#define OBJECT /* will object to messages without Subjects */
67/* #define REJECT */ /* will reject messages without Subjects
68 (OBJECT must be defined also) */
69/* #define UNBUFFERED *//* use unbuffered output */
70
71#include <sys/param.h>
72#include <sys/dir.h>
73#include <sys/stat.h>
74#include <ctype.h>
75#include <errno.h>
76#include <pwd.h>
77#include <setjmp.h>
78#include <termios.h>
79#include <signal.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <time.h>
84#include <unistd.h>
85#include "pathnames.h"
86
1/*-
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1980, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/*
45 * msgs - a user bulletin board program
46 *
47 * usage:
48 * msgs [fhlopq] [[-]number] to read messages
49 * msgs -s to place messages
50 * msgs -c [-days] to clean up the bulletin board
51 *
52 * prompt commands are:
53 * y print message
54 * n flush message, go to next message
55 * q flush message, quit
56 * p print message, turn on 'pipe thru more' mode
57 * P print message, turn off 'pipe thru more' mode
58 * - reprint last message
59 * s[-][<num>] [<filename>] save message
60 * m[-][<num>] mail with message in temp mbox
61 * x exit without flushing this message
62 * <num> print message number <num>
63 */
64
65#define V7 /* will look for TERM in the environment */
66#define OBJECT /* will object to messages without Subjects */
67/* #define REJECT */ /* will reject messages without Subjects
68 (OBJECT must be defined also) */
69/* #define UNBUFFERED *//* use unbuffered output */
70
71#include <sys/param.h>
72#include <sys/dir.h>
73#include <sys/stat.h>
74#include <ctype.h>
75#include <errno.h>
76#include <pwd.h>
77#include <setjmp.h>
78#include <termios.h>
79#include <signal.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <time.h>
84#include <unistd.h>
85#include "pathnames.h"
86
87#define CMODE 0666 /* bounds file creation mode */
87#define CMODE 0644 /* bounds file creation mode */
88#define NO 0
89#define YES 1
90#define SUPERUSER 0 /* superuser uid */
91#define DAEMON 1 /* daemon uid */
92#define NLINES 24 /* default number of lines/crt screen */
93#define NDAYS 21 /* default keep time for messages */
94#define DAYS *24*60*60 /* seconds/day */
95#define MSGSRC ".msgsrc" /* user's rc file */
96#define BOUNDS "bounds" /* message bounds file */
97#define NEXT "Next message? [yq]"
98#define MORE "More? [ynq]"
99#define NOMORE "(No more) [q] ?"
100
101typedef char bool;
102
103FILE *msgsrc;
104FILE *newmsg;
105char *sep = "-";
106char inbuf[BUFSIZ];
107char fname[128];
108char cmdbuf[128];
109char subj[128];
110char from[128];
111char date[128];
112char *ptr;
113char *in;
114bool local;
115bool ruptible;
116bool totty;
117bool seenfrom;
118bool seensubj;
119bool blankline;
120bool printing = NO;
121bool mailing = NO;
122bool quitit = NO;
123bool sending = NO;
124bool intrpflg = NO;
125int uid;
126int msg;
127int prevmsg;
128int lct;
129int nlines;
130int Lpp = 0;
131time_t t;
132time_t keep;
133
134char *mktemp();
135char *nxtfld();
136void onintr();
137void onsusp();
138
139/* option initialization */
140bool hdrs = NO;
141bool qopt = NO;
142bool hush = NO;
143bool send_msg = NO;
144bool locomode = NO;
145bool use_pager = NO;
146bool clean = NO;
147bool lastcmd = NO;
148jmp_buf tstpbuf;
149
150main(argc, argv)
151int argc; char *argv[];
152{
153 bool newrc, already;
154 int rcfirst = 0; /* first message to print (from .rc) */
155 int rcback = 0; /* amount to back off of rcfirst */
156 int firstmsg, nextmsg, lastmsg = 0;
157 int blast = 0;
158 FILE *bounds;
159
160#ifdef UNBUFFERED
161 setbuf(stdout, NULL);
162#endif
163
164
165 time(&t);
166 setuid(uid = getuid());
167 ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
168 if (ruptible)
169 signal(SIGINT, SIG_DFL);
170
171 argc--, argv++;
172 while (argc > 0) {
173 if (isdigit(argv[0][0])) { /* starting message # */
174 rcfirst = atoi(argv[0]);
175 }
176 else if (isdigit(argv[0][1])) { /* backward offset */
177 rcback = atoi( &( argv[0][1] ) );
178 }
179 else {
180 ptr = *argv;
181 while (*ptr) switch (*ptr++) {
182
183 case '-':
184 break;
185
186 case 'c':
187 if (uid != SUPERUSER && uid != DAEMON) {
188 fprintf(stderr, "Sorry\n");
189 exit(1);
190 }
191 clean = YES;
192 break;
193
194 case 'f': /* silently */
195 hush = YES;
196 break;
197
198 case 'h': /* headers only */
199 hdrs = YES;
200 break;
201
202 case 'l': /* local msgs only */
203 locomode = YES;
204 break;
205
206 case 'o': /* option to save last message */
207 lastcmd = YES;
208 break;
209
210 case 'p': /* pipe thru 'more' during long msgs */
211 use_pager = YES;
212 break;
213
214 case 'q': /* query only */
215 qopt = YES;
216 break;
217
218 case 's': /* sending TO msgs */
219 send_msg = YES;
220 break;
221
222 default:
223 fprintf(stderr,
224 "usage: msgs [fhlopq] [[-]number]\n");
225 exit(1);
226 }
227 }
228 argc--, argv++;
229 }
230
231 /*
232 * determine current message bounds
233 */
234 sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS);
235 bounds = fopen(fname, "r");
236
237 if (bounds != NULL) {
238 fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
239 fclose(bounds);
240 blast = lastmsg; /* save upper bound */
241 }
242
243 if (clean)
244 keep = t - (rcback? rcback : NDAYS) DAYS;
245
246 if (clean || bounds == NULL) { /* relocate message bounds */
247 struct direct *dp;
248 struct stat stbuf;
249 bool seenany = NO;
250 DIR *dirp;
251
252 dirp = opendir(_PATH_MSGS);
253 if (dirp == NULL) {
254 perror(_PATH_MSGS);
255 exit(errno);
256 }
257
258 firstmsg = 32767;
259 lastmsg = 0;
260
261 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
262 register char *cp = dp->d_name;
263 register int i = 0;
264
265 if (dp->d_ino == 0)
266 continue;
267 if (dp->d_namlen == 0)
268 continue;
269
270 if (clean)
271 sprintf(inbuf, "%s/%s", _PATH_MSGS, cp);
272
273 while (isdigit(*cp))
274 i = i * 10 + *cp++ - '0';
275 if (*cp)
276 continue; /* not a message! */
277
278 if (clean) {
279 if (stat(inbuf, &stbuf) != 0)
280 continue;
281 if (stbuf.st_mtime < keep
282 && stbuf.st_mode&S_IWRITE) {
283 unlink(inbuf);
284 continue;
285 }
286 }
287
288 if (i > lastmsg)
289 lastmsg = i;
290 if (i < firstmsg)
291 firstmsg = i;
292 seenany = YES;
293 }
294 closedir(dirp);
295
296 if (!seenany) {
297 if (blast != 0) /* never lower the upper bound! */
298 lastmsg = blast;
299 firstmsg = lastmsg + 1;
300 }
301 else if (blast > lastmsg)
302 lastmsg = blast;
303
304 if (!send_msg) {
305 bounds = fopen(fname, "w");
306 if (bounds == NULL) {
307 perror(fname);
308 exit(errno);
309 }
310 chmod(fname, CMODE);
311 fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
312 fclose(bounds);
313 }
314 }
315
316 if (send_msg) {
317 /*
318 * Send mode - place msgs in _PATH_MSGS
319 */
320 bounds = fopen(fname, "w");
321 if (bounds == NULL) {
322 perror(fname);
323 exit(errno);
324 }
325
326 nextmsg = lastmsg + 1;
327 sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg);
328 newmsg = fopen(fname, "w");
329 if (newmsg == NULL) {
330 perror(fname);
331 exit(errno);
332 }
88#define NO 0
89#define YES 1
90#define SUPERUSER 0 /* superuser uid */
91#define DAEMON 1 /* daemon uid */
92#define NLINES 24 /* default number of lines/crt screen */
93#define NDAYS 21 /* default keep time for messages */
94#define DAYS *24*60*60 /* seconds/day */
95#define MSGSRC ".msgsrc" /* user's rc file */
96#define BOUNDS "bounds" /* message bounds file */
97#define NEXT "Next message? [yq]"
98#define MORE "More? [ynq]"
99#define NOMORE "(No more) [q] ?"
100
101typedef char bool;
102
103FILE *msgsrc;
104FILE *newmsg;
105char *sep = "-";
106char inbuf[BUFSIZ];
107char fname[128];
108char cmdbuf[128];
109char subj[128];
110char from[128];
111char date[128];
112char *ptr;
113char *in;
114bool local;
115bool ruptible;
116bool totty;
117bool seenfrom;
118bool seensubj;
119bool blankline;
120bool printing = NO;
121bool mailing = NO;
122bool quitit = NO;
123bool sending = NO;
124bool intrpflg = NO;
125int uid;
126int msg;
127int prevmsg;
128int lct;
129int nlines;
130int Lpp = 0;
131time_t t;
132time_t keep;
133
134char *mktemp();
135char *nxtfld();
136void onintr();
137void onsusp();
138
139/* option initialization */
140bool hdrs = NO;
141bool qopt = NO;
142bool hush = NO;
143bool send_msg = NO;
144bool locomode = NO;
145bool use_pager = NO;
146bool clean = NO;
147bool lastcmd = NO;
148jmp_buf tstpbuf;
149
150main(argc, argv)
151int argc; char *argv[];
152{
153 bool newrc, already;
154 int rcfirst = 0; /* first message to print (from .rc) */
155 int rcback = 0; /* amount to back off of rcfirst */
156 int firstmsg, nextmsg, lastmsg = 0;
157 int blast = 0;
158 FILE *bounds;
159
160#ifdef UNBUFFERED
161 setbuf(stdout, NULL);
162#endif
163
164
165 time(&t);
166 setuid(uid = getuid());
167 ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
168 if (ruptible)
169 signal(SIGINT, SIG_DFL);
170
171 argc--, argv++;
172 while (argc > 0) {
173 if (isdigit(argv[0][0])) { /* starting message # */
174 rcfirst = atoi(argv[0]);
175 }
176 else if (isdigit(argv[0][1])) { /* backward offset */
177 rcback = atoi( &( argv[0][1] ) );
178 }
179 else {
180 ptr = *argv;
181 while (*ptr) switch (*ptr++) {
182
183 case '-':
184 break;
185
186 case 'c':
187 if (uid != SUPERUSER && uid != DAEMON) {
188 fprintf(stderr, "Sorry\n");
189 exit(1);
190 }
191 clean = YES;
192 break;
193
194 case 'f': /* silently */
195 hush = YES;
196 break;
197
198 case 'h': /* headers only */
199 hdrs = YES;
200 break;
201
202 case 'l': /* local msgs only */
203 locomode = YES;
204 break;
205
206 case 'o': /* option to save last message */
207 lastcmd = YES;
208 break;
209
210 case 'p': /* pipe thru 'more' during long msgs */
211 use_pager = YES;
212 break;
213
214 case 'q': /* query only */
215 qopt = YES;
216 break;
217
218 case 's': /* sending TO msgs */
219 send_msg = YES;
220 break;
221
222 default:
223 fprintf(stderr,
224 "usage: msgs [fhlopq] [[-]number]\n");
225 exit(1);
226 }
227 }
228 argc--, argv++;
229 }
230
231 /*
232 * determine current message bounds
233 */
234 sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS);
235 bounds = fopen(fname, "r");
236
237 if (bounds != NULL) {
238 fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
239 fclose(bounds);
240 blast = lastmsg; /* save upper bound */
241 }
242
243 if (clean)
244 keep = t - (rcback? rcback : NDAYS) DAYS;
245
246 if (clean || bounds == NULL) { /* relocate message bounds */
247 struct direct *dp;
248 struct stat stbuf;
249 bool seenany = NO;
250 DIR *dirp;
251
252 dirp = opendir(_PATH_MSGS);
253 if (dirp == NULL) {
254 perror(_PATH_MSGS);
255 exit(errno);
256 }
257
258 firstmsg = 32767;
259 lastmsg = 0;
260
261 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
262 register char *cp = dp->d_name;
263 register int i = 0;
264
265 if (dp->d_ino == 0)
266 continue;
267 if (dp->d_namlen == 0)
268 continue;
269
270 if (clean)
271 sprintf(inbuf, "%s/%s", _PATH_MSGS, cp);
272
273 while (isdigit(*cp))
274 i = i * 10 + *cp++ - '0';
275 if (*cp)
276 continue; /* not a message! */
277
278 if (clean) {
279 if (stat(inbuf, &stbuf) != 0)
280 continue;
281 if (stbuf.st_mtime < keep
282 && stbuf.st_mode&S_IWRITE) {
283 unlink(inbuf);
284 continue;
285 }
286 }
287
288 if (i > lastmsg)
289 lastmsg = i;
290 if (i < firstmsg)
291 firstmsg = i;
292 seenany = YES;
293 }
294 closedir(dirp);
295
296 if (!seenany) {
297 if (blast != 0) /* never lower the upper bound! */
298 lastmsg = blast;
299 firstmsg = lastmsg + 1;
300 }
301 else if (blast > lastmsg)
302 lastmsg = blast;
303
304 if (!send_msg) {
305 bounds = fopen(fname, "w");
306 if (bounds == NULL) {
307 perror(fname);
308 exit(errno);
309 }
310 chmod(fname, CMODE);
311 fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
312 fclose(bounds);
313 }
314 }
315
316 if (send_msg) {
317 /*
318 * Send mode - place msgs in _PATH_MSGS
319 */
320 bounds = fopen(fname, "w");
321 if (bounds == NULL) {
322 perror(fname);
323 exit(errno);
324 }
325
326 nextmsg = lastmsg + 1;
327 sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg);
328 newmsg = fopen(fname, "w");
329 if (newmsg == NULL) {
330 perror(fname);
331 exit(errno);
332 }
333 chmod(fname, 0644);
333 chmod(fname, CMODE);
334
335 fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
336 fclose(bounds);
337
338 sending = YES;
339 if (ruptible)
340 signal(SIGINT, onintr);
341
342 if (isatty(fileno(stdin))) {
343 ptr = getpwuid(uid)->pw_name;
344 printf("Message %d:\nFrom %s %sSubject: ",
345 nextmsg, ptr, ctime(&t));
346 fflush(stdout);
347 fgets(inbuf, sizeof inbuf, stdin);
348 putchar('\n');
349 fflush(stdout);
350 fprintf(newmsg, "From %s %sSubject: %s\n",
351 ptr, ctime(&t), inbuf);
352 blankline = seensubj = YES;
353 }
354 else
355 blankline = seensubj = NO;
356 for (;;) {
357 fgets(inbuf, sizeof inbuf, stdin);
358 if (feof(stdin) || ferror(stdin))
359 break;
360 blankline = (blankline || (inbuf[0] == '\n'));
361 seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
362 fputs(inbuf, newmsg);
363 }
364#ifdef OBJECT
365 if (!seensubj) {
366 printf("NOTICE: Messages should have a Subject field!\n");
367#ifdef REJECT
368 unlink(fname);
369#endif
370 exit(1);
371 }
372#endif
373 exit(ferror(stdin));
374 }
375 if (clean)
376 exit(0);
377
378 /*
379 * prepare to display messages
380 */
381 totty = (isatty(fileno(stdout)) != 0);
382 use_pager = use_pager && totty;
383
384 sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC);
385 msgsrc = fopen(fname, "r");
386 if (msgsrc) {
387 newrc = NO;
388 fscanf(msgsrc, "%d\n", &nextmsg);
389 fclose(msgsrc);
390 if (nextmsg > lastmsg+1) {
391 printf("Warning: bounds have been reset (%d, %d)\n",
392 firstmsg, lastmsg);
393 truncate(fname, (off_t)0);
394 newrc = YES;
395 }
396 else if (!rcfirst)
397 rcfirst = nextmsg - rcback;
398 }
399 else
400 newrc = YES;
401 msgsrc = fopen(fname, "r+");
402 if (msgsrc == NULL)
403 msgsrc = fopen(fname, "w");
404 if (msgsrc == NULL) {
405 perror(fname);
406 exit(errno);
407 }
408 if (rcfirst) {
409 if (rcfirst > lastmsg+1) {
410 printf("Warning: the last message is number %d.\n",
411 lastmsg);
412 rcfirst = nextmsg;
413 }
414 if (rcfirst > firstmsg)
415 firstmsg = rcfirst; /* don't set below first msg */
416 }
417 if (newrc) {
418 nextmsg = firstmsg;
419 fseek(msgsrc, 0L, 0);
420 fprintf(msgsrc, "%d\n", nextmsg);
421 fflush(msgsrc);
422 }
423
424#ifdef V7
425 if (totty) {
426 struct winsize win;
427 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
428 Lpp = win.ws_row;
429 if (Lpp <= 0) {
430 if (tgetent(inbuf, getenv("TERM")) <= 0
431 || (Lpp = tgetnum("li")) <= 0) {
432 Lpp = NLINES;
433 }
434 }
435 }
436#endif
437 Lpp -= 6; /* for headers, etc. */
438
439 already = NO;
440 prevmsg = firstmsg;
441 printing = YES;
442 if (ruptible)
443 signal(SIGINT, onintr);
444
445 /*
446 * Main program loop
447 */
448 for (msg = firstmsg; msg <= lastmsg; msg++) {
449
450 sprintf(fname, "%s/%d", _PATH_MSGS, msg);
451 newmsg = fopen(fname, "r");
452 if (newmsg == NULL)
453 continue;
454
455 gfrsub(newmsg); /* get From and Subject fields */
456 if (locomode && !local) {
457 fclose(newmsg);
458 continue;
459 }
460
461 if (qopt) { /* This has to be located here */
462 printf("There are new messages.\n");
463 exit(0);
464 }
465
466 if (already && !hdrs)
467 putchar('\n');
468
469 /*
470 * Print header
471 */
472 if (totty)
473 signal(SIGTSTP, onsusp);
474 (void) setjmp(tstpbuf);
475 already = YES;
476 nlines = 2;
477 if (seenfrom) {
478 printf("Message %d:\nFrom %s %s", msg, from, date);
479 nlines++;
480 }
481 if (seensubj) {
482 printf("Subject: %s", subj);
483 nlines++;
484 }
485 else {
486 if (seenfrom) {
487 putchar('\n');
488 nlines++;
489 }
490 while (nlines < 6
491 && fgets(inbuf, sizeof inbuf, newmsg)
492 && inbuf[0] != '\n') {
493 fputs(inbuf, stdout);
494 nlines++;
495 }
496 }
497
498 lct = linecnt(newmsg);
499 if (lct)
500 printf("(%d%slines) ", lct, seensubj? " " : " more ");
501
502 if (hdrs) {
503 printf("\n-----\n");
504 fclose(newmsg);
505 continue;
506 }
507
508 /*
509 * Ask user for command
510 */
511 if (totty)
512 ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
513 else
514 inbuf[0] = 'y';
515 if (totty)
516 signal(SIGTSTP, SIG_DFL);
517cmnd:
518 in = inbuf;
519 switch (*in) {
520 case 'x':
521 case 'X':
522 exit(0);
523
524 case 'q':
525 case 'Q':
526 quitit = YES;
527 printf("--Postponed--\n");
528 exit(0);
529 /* intentional fall-thru */
530 case 'n':
531 case 'N':
532 if (msg >= nextmsg) sep = "Flushed";
533 prevmsg = msg;
534 break;
535
536 case 'p':
537 case 'P':
538 use_pager = (*in++ == 'p');
539 /* intentional fallthru */
540 case '\n':
541 case 'y':
542 default:
543 if (*in == '-') {
544 msg = prevmsg-1;
545 sep = "replay";
546 break;
547 }
548 if (isdigit(*in)) {
549 msg = next(in);
550 sep = in;
551 break;
552 }
553
554 prmesg(nlines + lct + (seensubj? 1 : 0));
555 prevmsg = msg;
556
557 }
558
559 printf("--%s--\n", sep);
560 sep = "-";
561 if (msg >= nextmsg) {
562 nextmsg = msg + 1;
563 fseek(msgsrc, 0L, 0);
564 fprintf(msgsrc, "%d\n", nextmsg);
565 fflush(msgsrc);
566 }
567 if (newmsg)
568 fclose(newmsg);
569 if (quitit)
570 break;
571 }
572
573 /*
574 * Make sure .rc file gets updated
575 */
576 if (--msg >= nextmsg) {
577 nextmsg = msg + 1;
578 fseek(msgsrc, 0L, 0);
579 fprintf(msgsrc, "%d\n", nextmsg);
580 fflush(msgsrc);
581 }
582 if (already && !quitit && lastcmd && totty) {
583 /*
584 * save or reply to last message?
585 */
586 msg = prevmsg;
587 ask(NOMORE);
588 if (inbuf[0] == '-' || isdigit(inbuf[0]))
589 goto cmnd;
590 }
591 if (!(already || hush || qopt))
592 printf("No new messages.\n");
593 exit(0);
594}
595
596prmesg(length)
597int length;
598{
599 FILE *outf;
600
601 if (use_pager && length > Lpp) {
602 signal(SIGPIPE, SIG_IGN);
603 signal(SIGQUIT, SIG_IGN);
604 sprintf(cmdbuf, _PATH_PAGER, Lpp);
605 outf = popen(cmdbuf, "w");
606 if (!outf)
607 outf = stdout;
608 else
609 setbuf(outf, (char *)NULL);
610 }
611 else
612 outf = stdout;
613
614 if (seensubj)
615 putc('\n', outf);
616
617 while (fgets(inbuf, sizeof inbuf, newmsg)) {
618 fputs(inbuf, outf);
619 if (ferror(outf)) {
620 clearerr(outf);
621 break;
622 }
623 }
624
625 if (outf != stdout) {
626 pclose(outf);
627 signal(SIGPIPE, SIG_DFL);
628 signal(SIGQUIT, SIG_DFL);
629 }
630 else {
631 fflush(stdout);
632 }
633
634 /* force wait on output */
635 tcdrain(fileno(stdout));
636}
637
638void
639onintr()
640{
641 signal(SIGINT, onintr);
642 if (mailing)
643 unlink(fname);
644 if (sending) {
645 unlink(fname);
646 puts("--Killed--");
647 exit(1);
648 }
649 if (printing) {
650 putchar('\n');
651 if (hdrs)
652 exit(0);
653 sep = "Interrupt";
654 if (newmsg)
655 fseek(newmsg, 0L, 2);
656 intrpflg = YES;
657 }
658}
659
660/*
661 * We have just gotten a susp. Suspend and prepare to resume.
662 */
663void
664onsusp()
665{
666
667 signal(SIGTSTP, SIG_DFL);
668 sigsetmask(0);
669 kill(0, SIGTSTP);
670 signal(SIGTSTP, onsusp);
671 if (!mailing)
672 longjmp(tstpbuf, 0);
673}
674
675linecnt(f)
676FILE *f;
677{
678 off_t oldpos = ftell(f);
679 int l = 0;
680 char lbuf[BUFSIZ];
681
682 while (fgets(lbuf, sizeof lbuf, f))
683 l++;
684 clearerr(f);
685 fseek(f, oldpos, 0);
686 return (l);
687}
688
689next(buf)
690char *buf;
691{
692 int i;
693 sscanf(buf, "%d", &i);
694 sprintf(buf, "Goto %d", i);
695 return(--i);
696}
697
698ask(prompt)
699char *prompt;
700{
701 char inch;
702 int n, cmsg;
703 off_t oldpos;
704 FILE *cpfrom, *cpto;
705
706 printf("%s ", prompt);
707 fflush(stdout);
708 intrpflg = NO;
709 (void) fgets(inbuf, sizeof inbuf, stdin);
710 if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
711 inbuf[n - 1] = '\0';
712 if (intrpflg)
713 inbuf[0] = 'x';
714
715 /*
716 * Handle 'mail' and 'save' here.
717 */
718 if ((inch = inbuf[0]) == 's' || inch == 'm') {
719 if (inbuf[1] == '-')
720 cmsg = prevmsg;
721 else if (isdigit(inbuf[1]))
722 cmsg = atoi(&inbuf[1]);
723 else
724 cmsg = msg;
725 sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
726
727 oldpos = ftell(newmsg);
728
729 cpfrom = fopen(fname, "r");
730 if (!cpfrom) {
731 printf("Message %d not found\n", cmsg);
732 ask (prompt);
733 return;
734 }
735
736 if (inch == 's') {
737 in = nxtfld(inbuf);
738 if (*in) {
739 for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
740 fname[n] = in[n];
741 }
742 fname[n] = NULL;
743 }
744 else
745 strcpy(fname, "Messages");
746 }
747 else {
748 strcpy(fname, _PATH_TMP);
749 mktemp(fname);
750 sprintf(cmdbuf, _PATH_MAIL, fname);
751 mailing = YES;
752 }
753 cpto = fopen(fname, "a");
754 if (!cpto) {
755 perror(fname);
756 mailing = NO;
757 fseek(newmsg, oldpos, 0);
758 ask(prompt);
759 return;
760 }
761
762 while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
763 fwrite(inbuf, 1, n, cpto);
764
765 fclose(cpfrom);
766 fclose(cpto);
767 fseek(newmsg, oldpos, 0); /* reposition current message */
768 if (inch == 's')
769 printf("Message %d saved in \"%s\"\n", cmsg, fname);
770 else {
771 system(cmdbuf);
772 unlink(fname);
773 mailing = NO;
774 }
775 ask(prompt);
776 }
777}
778
779gfrsub(infile)
780FILE *infile;
781{
782 off_t frompos;
783
784 seensubj = seenfrom = NO;
785 local = YES;
786 subj[0] = from[0] = date[0] = NULL;
787
788 /*
789 * Is this a normal message?
790 */
791 if (fgets(inbuf, sizeof inbuf, infile)) {
792 if (strncmp(inbuf, "From", 4)==0) {
793 /*
794 * expected form starts with From
795 */
796 seenfrom = YES;
797 frompos = ftell(infile);
798 ptr = from;
799 in = nxtfld(inbuf);
800 if (*in) while (*in && *in > ' ') {
801 if (*in == ':' || *in == '@' || *in == '!')
802 local = NO;
803 *ptr++ = *in++;
804 /* what about sizeof from ? */
805 }
806 *ptr = NULL;
807 if (*(in = nxtfld(in)))
808 strncpy(date, in, sizeof date);
809 else {
810 date[0] = '\n';
811 date[1] = NULL;
812 }
813 }
814 else {
815 /*
816 * not the expected form
817 */
818 fseek(infile, 0L, 0);
819 return;
820 }
821 }
822 else
823 /*
824 * empty file ?
825 */
826 return;
827
828 /*
829 * look for Subject line until EOF or a blank line
830 */
831 while (fgets(inbuf, sizeof inbuf, infile)
832 && !(blankline = (inbuf[0] == '\n'))) {
833 /*
834 * extract Subject line
835 */
836 if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
837 seensubj = YES;
838 frompos = ftell(infile);
839 strncpy(subj, nxtfld(inbuf), sizeof subj);
840 }
841 }
842 if (!blankline)
843 /*
844 * ran into EOF
845 */
846 fseek(infile, frompos, 0);
847
848 if (!seensubj)
849 /*
850 * for possible use with Mail
851 */
852 strncpy(subj, "(No Subject)\n", sizeof subj);
853}
854
855char *
856nxtfld(s)
857char *s;
858{
859 if (*s) while (*s && *s > ' ') s++; /* skip over this field */
860 if (*s) while (*s && *s <= ' ') s++; /* find start of next field */
861 return (s);
862}
334
335 fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
336 fclose(bounds);
337
338 sending = YES;
339 if (ruptible)
340 signal(SIGINT, onintr);
341
342 if (isatty(fileno(stdin))) {
343 ptr = getpwuid(uid)->pw_name;
344 printf("Message %d:\nFrom %s %sSubject: ",
345 nextmsg, ptr, ctime(&t));
346 fflush(stdout);
347 fgets(inbuf, sizeof inbuf, stdin);
348 putchar('\n');
349 fflush(stdout);
350 fprintf(newmsg, "From %s %sSubject: %s\n",
351 ptr, ctime(&t), inbuf);
352 blankline = seensubj = YES;
353 }
354 else
355 blankline = seensubj = NO;
356 for (;;) {
357 fgets(inbuf, sizeof inbuf, stdin);
358 if (feof(stdin) || ferror(stdin))
359 break;
360 blankline = (blankline || (inbuf[0] == '\n'));
361 seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
362 fputs(inbuf, newmsg);
363 }
364#ifdef OBJECT
365 if (!seensubj) {
366 printf("NOTICE: Messages should have a Subject field!\n");
367#ifdef REJECT
368 unlink(fname);
369#endif
370 exit(1);
371 }
372#endif
373 exit(ferror(stdin));
374 }
375 if (clean)
376 exit(0);
377
378 /*
379 * prepare to display messages
380 */
381 totty = (isatty(fileno(stdout)) != 0);
382 use_pager = use_pager && totty;
383
384 sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC);
385 msgsrc = fopen(fname, "r");
386 if (msgsrc) {
387 newrc = NO;
388 fscanf(msgsrc, "%d\n", &nextmsg);
389 fclose(msgsrc);
390 if (nextmsg > lastmsg+1) {
391 printf("Warning: bounds have been reset (%d, %d)\n",
392 firstmsg, lastmsg);
393 truncate(fname, (off_t)0);
394 newrc = YES;
395 }
396 else if (!rcfirst)
397 rcfirst = nextmsg - rcback;
398 }
399 else
400 newrc = YES;
401 msgsrc = fopen(fname, "r+");
402 if (msgsrc == NULL)
403 msgsrc = fopen(fname, "w");
404 if (msgsrc == NULL) {
405 perror(fname);
406 exit(errno);
407 }
408 if (rcfirst) {
409 if (rcfirst > lastmsg+1) {
410 printf("Warning: the last message is number %d.\n",
411 lastmsg);
412 rcfirst = nextmsg;
413 }
414 if (rcfirst > firstmsg)
415 firstmsg = rcfirst; /* don't set below first msg */
416 }
417 if (newrc) {
418 nextmsg = firstmsg;
419 fseek(msgsrc, 0L, 0);
420 fprintf(msgsrc, "%d\n", nextmsg);
421 fflush(msgsrc);
422 }
423
424#ifdef V7
425 if (totty) {
426 struct winsize win;
427 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
428 Lpp = win.ws_row;
429 if (Lpp <= 0) {
430 if (tgetent(inbuf, getenv("TERM")) <= 0
431 || (Lpp = tgetnum("li")) <= 0) {
432 Lpp = NLINES;
433 }
434 }
435 }
436#endif
437 Lpp -= 6; /* for headers, etc. */
438
439 already = NO;
440 prevmsg = firstmsg;
441 printing = YES;
442 if (ruptible)
443 signal(SIGINT, onintr);
444
445 /*
446 * Main program loop
447 */
448 for (msg = firstmsg; msg <= lastmsg; msg++) {
449
450 sprintf(fname, "%s/%d", _PATH_MSGS, msg);
451 newmsg = fopen(fname, "r");
452 if (newmsg == NULL)
453 continue;
454
455 gfrsub(newmsg); /* get From and Subject fields */
456 if (locomode && !local) {
457 fclose(newmsg);
458 continue;
459 }
460
461 if (qopt) { /* This has to be located here */
462 printf("There are new messages.\n");
463 exit(0);
464 }
465
466 if (already && !hdrs)
467 putchar('\n');
468
469 /*
470 * Print header
471 */
472 if (totty)
473 signal(SIGTSTP, onsusp);
474 (void) setjmp(tstpbuf);
475 already = YES;
476 nlines = 2;
477 if (seenfrom) {
478 printf("Message %d:\nFrom %s %s", msg, from, date);
479 nlines++;
480 }
481 if (seensubj) {
482 printf("Subject: %s", subj);
483 nlines++;
484 }
485 else {
486 if (seenfrom) {
487 putchar('\n');
488 nlines++;
489 }
490 while (nlines < 6
491 && fgets(inbuf, sizeof inbuf, newmsg)
492 && inbuf[0] != '\n') {
493 fputs(inbuf, stdout);
494 nlines++;
495 }
496 }
497
498 lct = linecnt(newmsg);
499 if (lct)
500 printf("(%d%slines) ", lct, seensubj? " " : " more ");
501
502 if (hdrs) {
503 printf("\n-----\n");
504 fclose(newmsg);
505 continue;
506 }
507
508 /*
509 * Ask user for command
510 */
511 if (totty)
512 ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
513 else
514 inbuf[0] = 'y';
515 if (totty)
516 signal(SIGTSTP, SIG_DFL);
517cmnd:
518 in = inbuf;
519 switch (*in) {
520 case 'x':
521 case 'X':
522 exit(0);
523
524 case 'q':
525 case 'Q':
526 quitit = YES;
527 printf("--Postponed--\n");
528 exit(0);
529 /* intentional fall-thru */
530 case 'n':
531 case 'N':
532 if (msg >= nextmsg) sep = "Flushed";
533 prevmsg = msg;
534 break;
535
536 case 'p':
537 case 'P':
538 use_pager = (*in++ == 'p');
539 /* intentional fallthru */
540 case '\n':
541 case 'y':
542 default:
543 if (*in == '-') {
544 msg = prevmsg-1;
545 sep = "replay";
546 break;
547 }
548 if (isdigit(*in)) {
549 msg = next(in);
550 sep = in;
551 break;
552 }
553
554 prmesg(nlines + lct + (seensubj? 1 : 0));
555 prevmsg = msg;
556
557 }
558
559 printf("--%s--\n", sep);
560 sep = "-";
561 if (msg >= nextmsg) {
562 nextmsg = msg + 1;
563 fseek(msgsrc, 0L, 0);
564 fprintf(msgsrc, "%d\n", nextmsg);
565 fflush(msgsrc);
566 }
567 if (newmsg)
568 fclose(newmsg);
569 if (quitit)
570 break;
571 }
572
573 /*
574 * Make sure .rc file gets updated
575 */
576 if (--msg >= nextmsg) {
577 nextmsg = msg + 1;
578 fseek(msgsrc, 0L, 0);
579 fprintf(msgsrc, "%d\n", nextmsg);
580 fflush(msgsrc);
581 }
582 if (already && !quitit && lastcmd && totty) {
583 /*
584 * save or reply to last message?
585 */
586 msg = prevmsg;
587 ask(NOMORE);
588 if (inbuf[0] == '-' || isdigit(inbuf[0]))
589 goto cmnd;
590 }
591 if (!(already || hush || qopt))
592 printf("No new messages.\n");
593 exit(0);
594}
595
596prmesg(length)
597int length;
598{
599 FILE *outf;
600
601 if (use_pager && length > Lpp) {
602 signal(SIGPIPE, SIG_IGN);
603 signal(SIGQUIT, SIG_IGN);
604 sprintf(cmdbuf, _PATH_PAGER, Lpp);
605 outf = popen(cmdbuf, "w");
606 if (!outf)
607 outf = stdout;
608 else
609 setbuf(outf, (char *)NULL);
610 }
611 else
612 outf = stdout;
613
614 if (seensubj)
615 putc('\n', outf);
616
617 while (fgets(inbuf, sizeof inbuf, newmsg)) {
618 fputs(inbuf, outf);
619 if (ferror(outf)) {
620 clearerr(outf);
621 break;
622 }
623 }
624
625 if (outf != stdout) {
626 pclose(outf);
627 signal(SIGPIPE, SIG_DFL);
628 signal(SIGQUIT, SIG_DFL);
629 }
630 else {
631 fflush(stdout);
632 }
633
634 /* force wait on output */
635 tcdrain(fileno(stdout));
636}
637
638void
639onintr()
640{
641 signal(SIGINT, onintr);
642 if (mailing)
643 unlink(fname);
644 if (sending) {
645 unlink(fname);
646 puts("--Killed--");
647 exit(1);
648 }
649 if (printing) {
650 putchar('\n');
651 if (hdrs)
652 exit(0);
653 sep = "Interrupt";
654 if (newmsg)
655 fseek(newmsg, 0L, 2);
656 intrpflg = YES;
657 }
658}
659
660/*
661 * We have just gotten a susp. Suspend and prepare to resume.
662 */
663void
664onsusp()
665{
666
667 signal(SIGTSTP, SIG_DFL);
668 sigsetmask(0);
669 kill(0, SIGTSTP);
670 signal(SIGTSTP, onsusp);
671 if (!mailing)
672 longjmp(tstpbuf, 0);
673}
674
675linecnt(f)
676FILE *f;
677{
678 off_t oldpos = ftell(f);
679 int l = 0;
680 char lbuf[BUFSIZ];
681
682 while (fgets(lbuf, sizeof lbuf, f))
683 l++;
684 clearerr(f);
685 fseek(f, oldpos, 0);
686 return (l);
687}
688
689next(buf)
690char *buf;
691{
692 int i;
693 sscanf(buf, "%d", &i);
694 sprintf(buf, "Goto %d", i);
695 return(--i);
696}
697
698ask(prompt)
699char *prompt;
700{
701 char inch;
702 int n, cmsg;
703 off_t oldpos;
704 FILE *cpfrom, *cpto;
705
706 printf("%s ", prompt);
707 fflush(stdout);
708 intrpflg = NO;
709 (void) fgets(inbuf, sizeof inbuf, stdin);
710 if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
711 inbuf[n - 1] = '\0';
712 if (intrpflg)
713 inbuf[0] = 'x';
714
715 /*
716 * Handle 'mail' and 'save' here.
717 */
718 if ((inch = inbuf[0]) == 's' || inch == 'm') {
719 if (inbuf[1] == '-')
720 cmsg = prevmsg;
721 else if (isdigit(inbuf[1]))
722 cmsg = atoi(&inbuf[1]);
723 else
724 cmsg = msg;
725 sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
726
727 oldpos = ftell(newmsg);
728
729 cpfrom = fopen(fname, "r");
730 if (!cpfrom) {
731 printf("Message %d not found\n", cmsg);
732 ask (prompt);
733 return;
734 }
735
736 if (inch == 's') {
737 in = nxtfld(inbuf);
738 if (*in) {
739 for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
740 fname[n] = in[n];
741 }
742 fname[n] = NULL;
743 }
744 else
745 strcpy(fname, "Messages");
746 }
747 else {
748 strcpy(fname, _PATH_TMP);
749 mktemp(fname);
750 sprintf(cmdbuf, _PATH_MAIL, fname);
751 mailing = YES;
752 }
753 cpto = fopen(fname, "a");
754 if (!cpto) {
755 perror(fname);
756 mailing = NO;
757 fseek(newmsg, oldpos, 0);
758 ask(prompt);
759 return;
760 }
761
762 while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
763 fwrite(inbuf, 1, n, cpto);
764
765 fclose(cpfrom);
766 fclose(cpto);
767 fseek(newmsg, oldpos, 0); /* reposition current message */
768 if (inch == 's')
769 printf("Message %d saved in \"%s\"\n", cmsg, fname);
770 else {
771 system(cmdbuf);
772 unlink(fname);
773 mailing = NO;
774 }
775 ask(prompt);
776 }
777}
778
779gfrsub(infile)
780FILE *infile;
781{
782 off_t frompos;
783
784 seensubj = seenfrom = NO;
785 local = YES;
786 subj[0] = from[0] = date[0] = NULL;
787
788 /*
789 * Is this a normal message?
790 */
791 if (fgets(inbuf, sizeof inbuf, infile)) {
792 if (strncmp(inbuf, "From", 4)==0) {
793 /*
794 * expected form starts with From
795 */
796 seenfrom = YES;
797 frompos = ftell(infile);
798 ptr = from;
799 in = nxtfld(inbuf);
800 if (*in) while (*in && *in > ' ') {
801 if (*in == ':' || *in == '@' || *in == '!')
802 local = NO;
803 *ptr++ = *in++;
804 /* what about sizeof from ? */
805 }
806 *ptr = NULL;
807 if (*(in = nxtfld(in)))
808 strncpy(date, in, sizeof date);
809 else {
810 date[0] = '\n';
811 date[1] = NULL;
812 }
813 }
814 else {
815 /*
816 * not the expected form
817 */
818 fseek(infile, 0L, 0);
819 return;
820 }
821 }
822 else
823 /*
824 * empty file ?
825 */
826 return;
827
828 /*
829 * look for Subject line until EOF or a blank line
830 */
831 while (fgets(inbuf, sizeof inbuf, infile)
832 && !(blankline = (inbuf[0] == '\n'))) {
833 /*
834 * extract Subject line
835 */
836 if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
837 seensubj = YES;
838 frompos = ftell(infile);
839 strncpy(subj, nxtfld(inbuf), sizeof subj);
840 }
841 }
842 if (!blankline)
843 /*
844 * ran into EOF
845 */
846 fseek(infile, frompos, 0);
847
848 if (!seensubj)
849 /*
850 * for possible use with Mail
851 */
852 strncpy(subj, "(No Subject)\n", sizeof subj);
853}
854
855char *
856nxtfld(s)
857char *s;
858{
859 if (*s) while (*s && *s > ' ') s++; /* skip over this field */
860 if (*s) while (*s && *s <= ' ') s++; /* find start of next field */
861 return (s);
862}