386BSD 0.1 development
[unix-history] / usr / src / usr.bin / msgs / msgs.c
CommitLineData
9c422e1e
WJ
1/*-
2 * Copyright (c) 1980 The Regents of the University of California.
3 * 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
35char copyright[] =
36"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
37 All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)msgs.c 5.8 (Berkeley) 2/4/91";
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 <sgtty.h>
79#include <signal.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <time.h>
84#include "pathnames.h"
85
86#define CMODE 0666 /* bounds file creation mode */
87#define NO 0
88#define YES 1
89#define SUPERUSER 0 /* superuser uid */
90#define DAEMON 1 /* daemon uid */
91#define NLINES 24 /* default number of lines/crt screen */
92#define NDAYS 21 /* default keep time for messages */
93#define DAYS *24*60*60 /* seconds/day */
94#define MSGSRC ".msgsrc" /* user's rc file */
95#define BOUNDS "bounds" /* message bounds file */
96#define NEXT "Next message? [yq]"
97#define MORE "More? [ynq]"
98#define NOMORE "(No more) [q] ?"
99
100typedef char bool;
101
102FILE *msgsrc;
103FILE *newmsg;
104char *sep = "-";
105char inbuf[BUFSIZ];
106char fname[128];
107char cmdbuf[128];
108char subj[128];
109char from[128];
110char date[128];
111char *ptr;
112char *in;
113bool local;
114bool ruptible;
115bool totty;
116bool seenfrom;
117bool seensubj;
118bool blankline;
119bool printing = NO;
120bool mailing = NO;
121bool quitit = NO;
122bool sending = NO;
123bool intrpflg = NO;
124int uid;
125int msg;
126int prevmsg;
127int lct;
128int nlines;
129int Lpp = 0;
130time_t t;
131time_t keep;
132struct sgttyb otty;
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 gtty(fileno(stdout), &otty);
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);
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, "a");
402 if (msgsrc == NULL) {
403 perror(fname);
404 exit(errno);
405 }
406 if (rcfirst) {
407 if (rcfirst > lastmsg+1) {
408 printf("Warning: the last message is number %d.\n",
409 lastmsg);
410 rcfirst = nextmsg;
411 }
412 if (rcfirst > firstmsg)
413 firstmsg = rcfirst; /* don't set below first msg */
414 }
415 if (newrc) {
416 nextmsg = firstmsg;
417 fseek(msgsrc, 0L, 0);
418 fprintf(msgsrc, "%d\n", nextmsg);
419 fflush(msgsrc);
420 }
421
422#ifdef V7
423 if (totty) {
424 struct winsize win;
425 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
426 Lpp = win.ws_row;
427 if (Lpp <= 0) {
428 if (tgetent(inbuf, getenv("TERM")) <= 0
429 || (Lpp = tgetnum("li")) <= 0) {
430 Lpp = NLINES;
431 }
432 }
433 }
434#endif
435 Lpp -= 6; /* for headers, etc. */
436
437 already = NO;
438 prevmsg = firstmsg;
439 printing = YES;
440 if (ruptible)
441 signal(SIGINT, onintr);
442
443 /*
444 * Main program loop
445 */
446 for (msg = firstmsg; msg <= lastmsg; msg++) {
447
448 sprintf(fname, "%s/%d", _PATH_MSGS, msg);
449 newmsg = fopen(fname, "r");
450 if (newmsg == NULL)
451 continue;
452
453 gfrsub(newmsg); /* get From and Subject fields */
454 if (locomode && !local) {
455 fclose(newmsg);
456 continue;
457 }
458
459 if (qopt) { /* This has to be located here */
460 printf("There are new messages.\n");
461 exit(0);
462 }
463
464 if (already && !hdrs)
465 putchar('\n');
466
467 /*
468 * Print header
469 */
470 if (totty)
471 signal(SIGTSTP, onsusp);
472 (void) setjmp(tstpbuf);
473 already = YES;
474 nlines = 2;
475 if (seenfrom) {
476 printf("Message %d:\nFrom %s %s", msg, from, date);
477 nlines++;
478 }
479 if (seensubj) {
480 printf("Subject: %s", subj);
481 nlines++;
482 }
483 else {
484 if (seenfrom) {
485 putchar('\n');
486 nlines++;
487 }
488 while (nlines < 6
489 && fgets(inbuf, sizeof inbuf, newmsg)
490 && inbuf[0] != '\n') {
491 fputs(inbuf, stdout);
492 nlines++;
493 }
494 }
495
496 lct = linecnt(newmsg);
497 if (lct)
498 printf("(%d%slines) ", lct, seensubj? " " : " more ");
499
500 if (hdrs) {
501 printf("\n-----\n");
502 fclose(newmsg);
503 continue;
504 }
505
506 /*
507 * Ask user for command
508 */
509 if (totty)
510 ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
511 else
512 inbuf[0] = 'y';
513 if (totty)
514 signal(SIGTSTP, SIG_DFL);
515cmnd:
516 in = inbuf;
517 switch (*in) {
518 case 'x':
519 case 'X':
520 exit(0);
521
522 case 'q':
523 case 'Q':
524 quitit = YES;
525 printf("--Postponed--\n");
526 exit(0);
527 /* intentional fall-thru */
528 case 'n':
529 case 'N':
530 if (msg >= nextmsg) sep = "Flushed";
531 prevmsg = msg;
532 break;
533
534 case 'p':
535 case 'P':
536 use_pager = (*in++ == 'p');
537 /* intentional fallthru */
538 case '\n':
539 case 'y':
540 default:
541 if (*in == '-') {
542 msg = prevmsg-1;
543 sep = "replay";
544 break;
545 }
546 if (isdigit(*in)) {
547 msg = next(in);
548 sep = in;
549 break;
550 }
551
552 prmesg(nlines + lct + (seensubj? 1 : 0));
553 prevmsg = msg;
554
555 }
556
557 printf("--%s--\n", sep);
558 sep = "-";
559 if (msg >= nextmsg) {
560 nextmsg = msg + 1;
561 fseek(msgsrc, 0L, 0);
562 fprintf(msgsrc, "%d\n", nextmsg);
563 fflush(msgsrc);
564 }
565 if (newmsg)
566 fclose(newmsg);
567 if (quitit)
568 break;
569 }
570
571 /*
572 * Make sure .rc file gets updated
573 */
574 if (--msg >= nextmsg) {
575 nextmsg = msg + 1;
576 fseek(msgsrc, 0L, 0);
577 fprintf(msgsrc, "%d\n", nextmsg);
578 fflush(msgsrc);
579 }
580 if (already && !quitit && lastcmd && totty) {
581 /*
582 * save or reply to last message?
583 */
584 msg = prevmsg;
585 ask(NOMORE);
586 if (inbuf[0] == '-' || isdigit(inbuf[0]))
587 goto cmnd;
588 }
589 if (!(already || hush || qopt))
590 printf("No new messages.\n");
591 exit(0);
592}
593
594prmesg(length)
595int length;
596{
597 FILE *outf;
598
599 if (use_pager && length > Lpp) {
600 signal(SIGPIPE, SIG_IGN);
601 signal(SIGQUIT, SIG_IGN);
602 sprintf(cmdbuf, _PATH_PAGER, Lpp);
603 outf = popen(cmdbuf, "w");
604 if (!outf)
605 outf = stdout;
606 else
607 setbuf(outf, (char *)NULL);
608 }
609 else
610 outf = stdout;
611
612 if (seensubj)
613 putc('\n', outf);
614
615 while (fgets(inbuf, sizeof inbuf, newmsg)) {
616 fputs(inbuf, outf);
617 if (ferror(outf)) {
618 clearerr(outf);
619 break;
620 }
621 }
622
623 if (outf != stdout) {
624 pclose(outf);
625 signal(SIGPIPE, SIG_DFL);
626 signal(SIGQUIT, SIG_DFL);
627 }
628 else {
629 fflush(stdout);
630 }
631
632 /* trick to force wait on output */
633 stty(fileno(stdout), &otty);
634}
635
636void
637onintr()
638{
639 signal(SIGINT, onintr);
640 if (mailing)
641 unlink(fname);
642 if (sending) {
643 unlink(fname);
644 puts("--Killed--");
645 exit(1);
646 }
647 if (printing) {
648 putchar('\n');
649 if (hdrs)
650 exit(0);
651 sep = "Interrupt";
652 if (newmsg)
653 fseek(newmsg, 0L, 2);
654 intrpflg = YES;
655 }
656}
657
658/*
659 * We have just gotten a susp. Suspend and prepare to resume.
660 */
661void
662onsusp()
663{
664
665 signal(SIGTSTP, SIG_DFL);
666 sigsetmask(0);
667 kill(0, SIGTSTP);
668 signal(SIGTSTP, onsusp);
669 if (!mailing)
670 longjmp(tstpbuf, 0);
671}
672
673linecnt(f)
674FILE *f;
675{
676 off_t oldpos = ftell(f);
677 int l = 0;
678 char lbuf[BUFSIZ];
679
680 while (fgets(lbuf, sizeof lbuf, f))
681 l++;
682 clearerr(f);
683 fseek(f, oldpos, 0);
684 return (l);
685}
686
687next(buf)
688char *buf;
689{
690 int i;
691 sscanf(buf, "%d", &i);
692 sprintf(buf, "Goto %d", i);
693 return(--i);
694}
695
696ask(prompt)
697char *prompt;
698{
699 char inch;
700 int n, cmsg;
701 off_t oldpos;
702 FILE *cpfrom, *cpto;
703
704 printf("%s ", prompt);
705 fflush(stdout);
706 intrpflg = NO;
707 (void) fgets(inbuf, sizeof inbuf, stdin);
708 if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
709 inbuf[n - 1] = '\0';
710 if (intrpflg)
711 inbuf[0] = 'x';
712
713 /*
714 * Handle 'mail' and 'save' here.
715 */
716 if ((inch = inbuf[0]) == 's' || inch == 'm') {
717 if (inbuf[1] == '-')
718 cmsg = prevmsg;
719 else if (isdigit(inbuf[1]))
720 cmsg = atoi(&inbuf[1]);
721 else
722 cmsg = msg;
723 sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
724
725 oldpos = ftell(newmsg);
726
727 cpfrom = fopen(fname, "r");
728 if (!cpfrom) {
729 printf("Message %d not found\n", cmsg);
730 ask (prompt);
731 return;
732 }
733
734 if (inch == 's') {
735 in = nxtfld(inbuf);
736 if (*in) {
737 for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
738 fname[n] = in[n];
739 }
740 fname[n] = NULL;
741 }
742 else
743 strcpy(fname, "Messages");
744 }
745 else {
746 strcpy(fname, _PATH_TMP);
747 mktemp(fname);
748 sprintf(cmdbuf, _PATH_MAIL, fname);
749 mailing = YES;
750 }
751 cpto = fopen(fname, "a");
752 if (!cpto) {
753 perror(fname);
754 mailing = NO;
755 fseek(newmsg, oldpos, 0);
756 ask(prompt);
757 return;
758 }
759
760 while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
761 fwrite(inbuf, 1, n, cpto);
762
763 fclose(cpfrom);
764 fclose(cpto);
765 fseek(newmsg, oldpos, 0); /* reposition current message */
766 if (inch == 's')
767 printf("Message %d saved in \"%s\"\n", cmsg, fname);
768 else {
769 system(cmdbuf);
770 unlink(fname);
771 mailing = NO;
772 }
773 ask(prompt);
774 }
775}
776
777gfrsub(infile)
778FILE *infile;
779{
780 off_t frompos;
781
782 seensubj = seenfrom = NO;
783 local = YES;
784 subj[0] = from[0] = date[0] = NULL;
785
786 /*
787 * Is this a normal message?
788 */
789 if (fgets(inbuf, sizeof inbuf, infile)) {
790 if (strncmp(inbuf, "From", 4)==0) {
791 /*
792 * expected form starts with From
793 */
794 seenfrom = YES;
795 frompos = ftell(infile);
796 ptr = from;
797 in = nxtfld(inbuf);
798 if (*in) while (*in && *in > ' ') {
799 if (*in == ':' || *in == '@' || *in == '!')
800 local = NO;
801 *ptr++ = *in++;
802 /* what about sizeof from ? */
803 }
804 *ptr = NULL;
805 if (*(in = nxtfld(in)))
806 strncpy(date, in, sizeof date);
807 else {
808 date[0] = '\n';
809 date[1] = NULL;
810 }
811 }
812 else {
813 /*
814 * not the expected form
815 */
816 fseek(infile, 0L, 0);
817 return;
818 }
819 }
820 else
821 /*
822 * empty file ?
823 */
824 return;
825
826 /*
827 * look for Subject line until EOF or a blank line
828 */
829 while (fgets(inbuf, sizeof inbuf, infile)
830 && !(blankline = (inbuf[0] == '\n'))) {
831 /*
832 * extract Subject line
833 */
834 if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
835 seensubj = YES;
836 frompos = ftell(infile);
837 strncpy(subj, nxtfld(inbuf), sizeof subj);
838 }
839 }
840 if (!blankline)
841 /*
842 * ran into EOF
843 */
844 fseek(infile, frompos, 0);
845
846 if (!seensubj)
847 /*
848 * for possible use with Mail
849 */
850 strncpy(subj, "(No Subject)\n", sizeof subj);
851}
852
853char *
854nxtfld(s)
855char *s;
856{
857 if (*s) while (*s && *s > ' ') s++; /* skip over this field */
858 if (*s) while (*s && *s <= ' ') s++; /* find start of next field */
859 return (s);
860}