xref: /csrg-svn/usr.bin/mail/aux.c (revision 1219)
1*1219Skas #
2*1219Skas 
3*1219Skas #include "rcv.h"
4*1219Skas #include <sys/stat.h>
5*1219Skas #include <sgtty.h>
6*1219Skas #include <ctype.h>
7*1219Skas 
8*1219Skas /*
9*1219Skas  * Mail -- a mail program
10*1219Skas  *
11*1219Skas  * Auxiliary functions.
12*1219Skas  */
13*1219Skas 
14*1219Skas static char *SccsId = "@(#)aux.c	1.1 10/08/80";
15*1219Skas 
16*1219Skas /*
17*1219Skas  * Return a pointer to a dynamic copy of the argument.
18*1219Skas  */
19*1219Skas 
20*1219Skas char *
21*1219Skas savestr(str)
22*1219Skas 	char *str;
23*1219Skas {
24*1219Skas 	register char *cp, *cp2, *top;
25*1219Skas 
26*1219Skas 	for (cp = str; *cp; cp++)
27*1219Skas 		;
28*1219Skas 	top = salloc(cp-str + 1);
29*1219Skas 	if (top == NOSTR)
30*1219Skas 		return(NOSTR);
31*1219Skas 	for (cp = str, cp2 = top; *cp; cp++)
32*1219Skas 		*cp2++ = *cp;
33*1219Skas 	*cp2 = 0;
34*1219Skas 	return(top);
35*1219Skas }
36*1219Skas 
37*1219Skas /*
38*1219Skas  * Copy the name from the passed header line into the passed
39*1219Skas  * name buffer.  Null pad the name buffer.
40*1219Skas  */
41*1219Skas 
42*1219Skas copyname(linebuf, nbuf)
43*1219Skas 	char *linebuf, *nbuf;
44*1219Skas {
45*1219Skas 	register char *cp, *cp2;
46*1219Skas 
47*1219Skas 	for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
48*1219Skas 		*cp2++ = *cp;
49*1219Skas 	while (cp2-nbuf < 8)
50*1219Skas 		*cp2++ = 0;
51*1219Skas }
52*1219Skas 
53*1219Skas /*
54*1219Skas  * Announce a fatal error and die.
55*1219Skas  */
56*1219Skas 
57*1219Skas panic(str)
58*1219Skas 	char *str;
59*1219Skas {
60*1219Skas 	prs("panic: ");
61*1219Skas 	prs(str);
62*1219Skas 	prs("\n");
63*1219Skas 	exit(1);
64*1219Skas }
65*1219Skas 
66*1219Skas /*
67*1219Skas  * Catch stdio errors and report them more nicely.
68*1219Skas  */
69*1219Skas 
70*1219Skas _error(str)
71*1219Skas 	char *str;
72*1219Skas {
73*1219Skas 	prs("Stdio Error: ");
74*1219Skas 	prs(str);
75*1219Skas 	prs("\n");
76*1219Skas 	abort();
77*1219Skas }
78*1219Skas 
79*1219Skas /*
80*1219Skas  * Print a string on diagnostic output.
81*1219Skas  */
82*1219Skas 
83*1219Skas prs(str)
84*1219Skas 	char *str;
85*1219Skas {
86*1219Skas 	register char *s;
87*1219Skas 
88*1219Skas 	for (s = str; *s; s++)
89*1219Skas 		;
90*1219Skas 	write(2, str, s-str);
91*1219Skas }
92*1219Skas 
93*1219Skas /*
94*1219Skas  * Touch the named message by setting its MTOUCH flag.
95*1219Skas  * Touched messages have the effect of not being sent
96*1219Skas  * back to the system mailbox on exit.
97*1219Skas  */
98*1219Skas 
99*1219Skas touch(mesg)
100*1219Skas {
101*1219Skas 	if (mesg >= 1 && mesg <= msgCount)
102*1219Skas 		message[mesg-1].m_flag |= MTOUCH;
103*1219Skas }
104*1219Skas 
105*1219Skas /*
106*1219Skas  * Test to see if the passed file name is a directory.
107*1219Skas  * Return true if it is.
108*1219Skas  */
109*1219Skas 
110*1219Skas isdir(name)
111*1219Skas 	char name[];
112*1219Skas {
113*1219Skas 	struct stat sbuf;
114*1219Skas 
115*1219Skas 	if (stat(name, &sbuf) < 0)
116*1219Skas 		return(0);
117*1219Skas 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
118*1219Skas }
119*1219Skas 
120*1219Skas /*
121*1219Skas  * Compute the size in characters of the passed message
122*1219Skas  */
123*1219Skas 
124*1219Skas unsigned int
125*1219Skas msize(messp)
126*1219Skas 	struct message *messp;
127*1219Skas {
128*1219Skas 	register struct message *mp;
129*1219Skas 
130*1219Skas 	mp = messp;
131*1219Skas 	return(mp->m_size);
132*1219Skas }
133*1219Skas 
134*1219Skas /*
135*1219Skas  * Count the number of arguments in the given string raw list.
136*1219Skas  */
137*1219Skas 
138*1219Skas argcount(argv)
139*1219Skas 	char **argv;
140*1219Skas {
141*1219Skas 	register char **ap;
142*1219Skas 
143*1219Skas 	for (ap = argv; *ap != NOSTR; ap++)
144*1219Skas 		;
145*1219Skas 	return(ap-argv);
146*1219Skas }
147*1219Skas 
148*1219Skas /*
149*1219Skas  * Given a file address, determine the
150*1219Skas  * block number it represents.
151*1219Skas  */
152*1219Skas 
153*1219Skas blockof(off)
154*1219Skas 	off_t off;
155*1219Skas {
156*1219Skas 	off_t a;
157*1219Skas 
158*1219Skas 	a = off >> 9;
159*1219Skas 	a &= 077777;
160*1219Skas 	return((int) a);
161*1219Skas }
162*1219Skas 
163*1219Skas /*
164*1219Skas  * Take a file address, and determine
165*1219Skas  * its offset in the current block.
166*1219Skas  */
167*1219Skas 
168*1219Skas offsetof(off)
169*1219Skas 	off_t off;
170*1219Skas {
171*1219Skas 	off_t a;
172*1219Skas 
173*1219Skas 	a = off & 0777;
174*1219Skas 	return((int) a);
175*1219Skas }
176*1219Skas 
177*1219Skas /*
178*1219Skas  * Determine if the passed file is actually a tty, via a call to
179*1219Skas  * gtty.  This is not totally reliable, but . . .
180*1219Skas  */
181*1219Skas 
182*1219Skas isatty(f)
183*1219Skas {
184*1219Skas 	struct sgttyb buf;
185*1219Skas 
186*1219Skas 	if (gtty(f, &buf) < 0)
187*1219Skas 		return(0);
188*1219Skas 	return(1);
189*1219Skas }
190*1219Skas 
191*1219Skas /*
192*1219Skas  * Return the desired header line from the passed message
193*1219Skas  * pointer (or NOSTR if the desired header field is not available).
194*1219Skas  */
195*1219Skas 
196*1219Skas char *
197*1219Skas hfield(field, mp)
198*1219Skas 	char field[];
199*1219Skas 	struct message *mp;
200*1219Skas {
201*1219Skas 	register FILE *ibuf;
202*1219Skas 	char linebuf[LINESIZE];
203*1219Skas 	register int lc;
204*1219Skas 
205*1219Skas 	ibuf = setinput(mp);
206*1219Skas 	if ((lc = mp->m_lines) <= 0)
207*1219Skas 		return(NOSTR);
208*1219Skas 	if (readline(ibuf, linebuf) < 0)
209*1219Skas 		return(NOSTR);
210*1219Skas 	lc--;
211*1219Skas 	do {
212*1219Skas 		lc = gethfield(ibuf, linebuf, lc);
213*1219Skas 		if (lc == -1)
214*1219Skas 			return(NOSTR);
215*1219Skas 		if (ishfield(linebuf, field))
216*1219Skas 			return(savestr(hcontents(linebuf)));
217*1219Skas 	} while (lc > 0);
218*1219Skas 	return(NOSTR);
219*1219Skas }
220*1219Skas 
221*1219Skas /*
222*1219Skas  * Return the next header field found in the given message.
223*1219Skas  * Return > 0 if something found, <= 0 elsewise.
224*1219Skas  * Must deal with \ continuations & other such fraud.
225*1219Skas  */
226*1219Skas 
227*1219Skas gethfield(f, linebuf, rem)
228*1219Skas 	register FILE *f;
229*1219Skas 	char linebuf[];
230*1219Skas 	register int rem;
231*1219Skas {
232*1219Skas 	char line2[LINESIZE];
233*1219Skas 	long loc;
234*1219Skas 	register char *cp, *cp2;
235*1219Skas 	register int c;
236*1219Skas 
237*1219Skas 
238*1219Skas 	for (;;) {
239*1219Skas 		if (rem <= 0)
240*1219Skas 			return(-1);
241*1219Skas 		if (readline(f, linebuf) < 0)
242*1219Skas 			return(-1);
243*1219Skas 		rem--;
244*1219Skas 		if (strlen(linebuf) == 0)
245*1219Skas 			return(-1);
246*1219Skas 		if (isspace(linebuf[0]))
247*1219Skas 			continue;
248*1219Skas 		if (linebuf[0] == '>')
249*1219Skas 			continue;
250*1219Skas 		cp = index(linebuf, ':');
251*1219Skas 		if (cp == NOSTR)
252*1219Skas 			continue;
253*1219Skas 		for (cp2 = linebuf; cp2 < cp; cp2++)
254*1219Skas 			if (isdigit(*cp2))
255*1219Skas 				continue;
256*1219Skas 
257*1219Skas 		/*
258*1219Skas 		 * I guess we got a headline.
259*1219Skas 		 * Handle wraparounding
260*1219Skas 		 */
261*1219Skas 
262*1219Skas 		for (;;) {
263*1219Skas 			if (rem <= 0)
264*1219Skas 				break;
265*1219Skas #ifdef CANTELL
266*1219Skas 			loc = ftell(f);
267*1219Skas 			if (readline(f, line2) < 0)
268*1219Skas 				break;
269*1219Skas 			rem--;
270*1219Skas 			if (!isspace(line2[0])) {
271*1219Skas 				fseek(f, loc, 0);
272*1219Skas 				rem++;
273*1219Skas 				break;
274*1219Skas 			}
275*1219Skas #else
276*1219Skas 			c = getc(f);
277*1219Skas 			ungetc(c, f);
278*1219Skas 			if (!isspace(c) || c == '\n')
279*1219Skas 				break;
280*1219Skas 			if (readline(f, line2) < 0)
281*1219Skas 				break;
282*1219Skas 			rem--;
283*1219Skas #endif
284*1219Skas 			cp2 = line2;
285*1219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
286*1219Skas 				;
287*1219Skas 			if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
288*1219Skas 				break;
289*1219Skas 			cp = &linebuf[strlen(linebuf)];
290*1219Skas 			while (cp > linebuf &&
291*1219Skas 			    (isspace(cp[-1]) || cp[-1] == '\\'))
292*1219Skas 				cp--;
293*1219Skas 			*cp++ = ' ';
294*1219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
295*1219Skas 				;
296*1219Skas 			strcpy(cp, cp2);
297*1219Skas 		}
298*1219Skas 		if ((c = strlen(linebuf)) > 0) {
299*1219Skas 			cp = &linebuf[c-1];
300*1219Skas 			while (cp > linebuf && isspace(*cp))
301*1219Skas 				cp--;
302*1219Skas 			*++cp = 0;
303*1219Skas 		}
304*1219Skas 		return(rem);
305*1219Skas 	}
306*1219Skas 	/* NOTREACHED */
307*1219Skas }
308*1219Skas 
309*1219Skas /*
310*1219Skas  * Check whether the passed line is a header line of
311*1219Skas  * the desired breed.
312*1219Skas  */
313*1219Skas 
314*1219Skas ishfield(linebuf, field)
315*1219Skas 	char linebuf[], field[];
316*1219Skas {
317*1219Skas 	register char *cp;
318*1219Skas 	register int c;
319*1219Skas 
320*1219Skas 	if ((cp = index(linebuf, ':')) == NOSTR)
321*1219Skas 		return(0);
322*1219Skas 	if (cp == linebuf)
323*1219Skas 		return(0);
324*1219Skas 	cp--;
325*1219Skas 	while (cp > linebuf && isspace(*cp))
326*1219Skas 		cp--;
327*1219Skas 	c = *++cp;
328*1219Skas 	*cp = 0;
329*1219Skas 	if (icequal(linebuf ,field)) {
330*1219Skas 		*cp = c;
331*1219Skas 		return(1);
332*1219Skas 	}
333*1219Skas 	*cp = c;
334*1219Skas 	return(0);
335*1219Skas }
336*1219Skas 
337*1219Skas /*
338*1219Skas  * Extract the non label information from the given header field
339*1219Skas  * and return it.
340*1219Skas  */
341*1219Skas 
342*1219Skas char *
343*1219Skas hcontents(hfield)
344*1219Skas 	char hfield[];
345*1219Skas {
346*1219Skas 	register char *cp;
347*1219Skas 
348*1219Skas 	if ((cp = index(hfield, ':')) == NOSTR)
349*1219Skas 		return(NOSTR);
350*1219Skas 	cp++;
351*1219Skas 	while (*cp && isspace(*cp))
352*1219Skas 		cp++;
353*1219Skas 	return(cp);
354*1219Skas }
355*1219Skas 
356*1219Skas /*
357*1219Skas  * Compare two strings, ignoring case.
358*1219Skas  */
359*1219Skas 
360*1219Skas icequal(s1, s2)
361*1219Skas 	register char *s1, *s2;
362*1219Skas {
363*1219Skas 
364*1219Skas 	while (raise(*s1++) == raise(*s2))
365*1219Skas 		if (*s2++ == 0)
366*1219Skas 			return(1);
367*1219Skas 	return(0);
368*1219Skas }
369*1219Skas 
370*1219Skas /*
371*1219Skas  * The following code deals with input stacking to do source
372*1219Skas  * commands.  All but the current file pointer are saved on
373*1219Skas  * the stack.
374*1219Skas  */
375*1219Skas 
376*1219Skas static	int	ssp = -1;		/* Top of file stack */
377*1219Skas static	FILE	*sstack[_NFILE];	/* Saved input files */
378*1219Skas 
379*1219Skas /*
380*1219Skas  * Pushdown current input file and switch to a new one.
381*1219Skas  * Set the global flag "sourcing" so that others will realize
382*1219Skas  * that they are no longer reading from a tty (in all probability).
383*1219Skas  */
384*1219Skas 
385*1219Skas source(name)
386*1219Skas 	char name[];
387*1219Skas {
388*1219Skas 	register FILE *fi;
389*1219Skas 
390*1219Skas 	if ((fi = fopen(name, "r")) == NULL) {
391*1219Skas 		perror(name);
392*1219Skas 		return(1);
393*1219Skas 	}
394*1219Skas 	if (ssp >= _NFILE-2) {
395*1219Skas 		printf("Too much \"sourcing\" going on.\n");
396*1219Skas 		fclose(fi);
397*1219Skas 		return(1);
398*1219Skas 	}
399*1219Skas 	sstack[++ssp] = input;
400*1219Skas 	input = fi;
401*1219Skas 	sourcing++;
402*1219Skas 	return(0);
403*1219Skas }
404*1219Skas 
405*1219Skas /*
406*1219Skas  * Source a file, but do nothing if the file cannot be opened.
407*1219Skas  */
408*1219Skas 
409*1219Skas source1(name)
410*1219Skas 	char name[];
411*1219Skas {
412*1219Skas 	register int f;
413*1219Skas 
414*1219Skas 	if ((f = open(name, 0)) < 0)
415*1219Skas 		return(0);
416*1219Skas 	close(f);
417*1219Skas 	source(name);
418*1219Skas }
419*1219Skas 
420*1219Skas /*
421*1219Skas  * Pop the current input back to the previous level.
422*1219Skas  * Update the "sourcing" flag as appropriate.
423*1219Skas  */
424*1219Skas 
425*1219Skas unstack()
426*1219Skas {
427*1219Skas 	if (ssp < 0) {
428*1219Skas 		printf("\"Source\" stack over-pop.\n");
429*1219Skas 		sourcing = 0;
430*1219Skas 		return(1);
431*1219Skas 	}
432*1219Skas 	fclose(input);
433*1219Skas 	input = sstack[ssp--];
434*1219Skas 	if (ssp < 0)
435*1219Skas 		sourcing = 0;
436*1219Skas 	return(0);
437*1219Skas }
438*1219Skas 
439*1219Skas /*
440*1219Skas  * Touch the indicated file.
441*1219Skas  * This is nifty for the shell.
442*1219Skas  * If we have the utime() system call, this is better served
443*1219Skas  * by using that, since it will work for empty files.
444*1219Skas  * On non-utime systems, we must sleep a second, then read.
445*1219Skas  */
446*1219Skas 
447*1219Skas alter(name)
448*1219Skas 	char name[];
449*1219Skas {
450*1219Skas #ifdef UTIME
451*1219Skas 	struct stat statb;
452*1219Skas 	long time();
453*1219Skas 	time_t time_p[2];
454*1219Skas #else
455*1219Skas 	register int pid, f;
456*1219Skas 	char w;
457*1219Skas #endif UTIME
458*1219Skas 
459*1219Skas #ifdef UTIME
460*1219Skas 	if (stat(name, &statb) < 0)
461*1219Skas 		return;
462*1219Skas 	time_p[0] = time((long *) 0) + 1;
463*1219Skas 	time_p[1] = statb.st_mtime;
464*1219Skas 	utime(name, time_p);
465*1219Skas #else
466*1219Skas 	if ((pid = fork()) != 0)
467*1219Skas 		return;
468*1219Skas 	clrbuf(stdout);
469*1219Skas 	clrbuf(stderr);
470*1219Skas 	clrbuf(stdin);
471*1219Skas 	sleep(1);
472*1219Skas 	if ((f = open(name, 0)) < 0)
473*1219Skas 		exit(1);
474*1219Skas 	read(f, &w, 1);
475*1219Skas 	exit(0);
476*1219Skas #endif
477*1219Skas }
478*1219Skas 
479*1219Skas /*
480*1219Skas  * Examine the passed line buffer and
481*1219Skas  * return true if it is all blanks and tabs.
482*1219Skas  */
483*1219Skas 
484*1219Skas blankline(linebuf)
485*1219Skas 	char linebuf[];
486*1219Skas {
487*1219Skas 	register char *cp;
488*1219Skas 
489*1219Skas 	for (cp = linebuf; *cp; cp++)
490*1219Skas 		if (!any(*cp, " \t"))
491*1219Skas 			return(0);
492*1219Skas 	return(1);
493*1219Skas }
494*1219Skas 
495*1219Skas /*
496*1219Skas  * Fetch the sender's name from the passed message.
497*1219Skas  */
498*1219Skas 
499*1219Skas char *
500*1219Skas nameof(mp)
501*1219Skas 	register struct message *mp;
502*1219Skas {
503*1219Skas 	char namebuf[LINESIZE];
504*1219Skas 	char linebuf[LINESIZE];
505*1219Skas 	register char *cp, *cp2;
506*1219Skas 	register FILE *ibuf;
507*1219Skas 	int first = 1;
508*1219Skas 
509*1219Skas 	if ((cp = hfield("reply-to", mp)) != NOSTR) {
510*1219Skas 		strcpy(namebuf, cp);
511*1219Skas 		return(namebuf);
512*1219Skas 	}
513*1219Skas 	ibuf = setinput(mp);
514*1219Skas 	copy("", namebuf);
515*1219Skas 	if (readline(ibuf, linebuf) <= 0)
516*1219Skas 		return(savestr(namebuf));
517*1219Skas newname:
518*1219Skas 	for (cp = linebuf; *cp != ' '; cp++)
519*1219Skas 		;
520*1219Skas 	while (any(*cp, " \t"))
521*1219Skas 		cp++;
522*1219Skas 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
523*1219Skas 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
524*1219Skas 		;
525*1219Skas 	*cp2 = '\0';
526*1219Skas 	if (readline(ibuf, linebuf) <= 0)
527*1219Skas 		return(savestr(namebuf));
528*1219Skas 	if ((cp = index(linebuf, 'F')) == NULL)
529*1219Skas 		return(savestr(namebuf));
530*1219Skas 	if (strncmp(cp, "From", 4) != 0)
531*1219Skas 		return(savestr(namebuf));
532*1219Skas 	while ((cp = index(cp, 'r')) != NULL) {
533*1219Skas 		if (strncmp(cp, "remote", 6) == 0) {
534*1219Skas 			if ((cp = index(cp, 'f')) == NULL)
535*1219Skas 				break;
536*1219Skas 			if (strncmp(cp, "from", 4) != 0)
537*1219Skas 				break;
538*1219Skas 			if ((cp = index(cp, ' ')) == NULL)
539*1219Skas 				break;
540*1219Skas 			cp++;
541*1219Skas 			if (first) {
542*1219Skas 				copy(cp, namebuf);
543*1219Skas 				first = 0;
544*1219Skas 			} else
545*1219Skas 				strcpy(rindex(namebuf, '!')+1, cp);
546*1219Skas 			strcat(namebuf, "!");
547*1219Skas 			goto newname;
548*1219Skas 		}
549*1219Skas 		cp++;
550*1219Skas 	}
551*1219Skas 	return(savestr(namebuf));
552*1219Skas }
553*1219Skas 
554*1219Skas /*
555*1219Skas  * Find the rightmost pointer to an instance of the
556*1219Skas  * character in the string and return it.
557*1219Skas  */
558*1219Skas 
559*1219Skas char *
560*1219Skas rindex(str, c)
561*1219Skas 	char str[];
562*1219Skas 	register int c;
563*1219Skas {
564*1219Skas 	register char *cp, *cp2;
565*1219Skas 
566*1219Skas 	for (cp = str, cp2 = NOSTR; *cp; cp++)
567*1219Skas 		if (c == *cp)
568*1219Skas 			cp2 = cp;
569*1219Skas 	return(cp2);
570*1219Skas }
571*1219Skas 
572*1219Skas /*
573*1219Skas  * See if the string is a number.
574*1219Skas  */
575*1219Skas 
576*1219Skas numeric(str)
577*1219Skas 	char str[];
578*1219Skas {
579*1219Skas 	register char *cp = str;
580*1219Skas 
581*1219Skas 	while (*cp)
582*1219Skas 		if (!isdigit(*cp++))
583*1219Skas 			return(0);
584*1219Skas 	return(1);
585*1219Skas }
586*1219Skas 
587*1219Skas /*
588*1219Skas  * Are any of the characters in the two strings the same?
589*1219Skas  */
590*1219Skas 
591*1219Skas anyof(s1, s2)
592*1219Skas 	register char *s1, *s2;
593*1219Skas {
594*1219Skas 	register int c;
595*1219Skas 
596*1219Skas 	while (c = *s1++)
597*1219Skas 		if (any(c, s2))
598*1219Skas 			return(1);
599*1219Skas 	return(0);
600*1219Skas }
601*1219Skas 
602*1219Skas /*
603*1219Skas  * Determine the leftmost index of the character
604*1219Skas  * in the string.
605*1219Skas  */
606*1219Skas 
607*1219Skas char *
608*1219Skas index(str, ch)
609*1219Skas 	char *str;
610*1219Skas {
611*1219Skas 	register char *cp;
612*1219Skas 	register int c;
613*1219Skas 
614*1219Skas 	for (c = ch, cp = str; *cp; cp++)
615*1219Skas 		if (*cp == c)
616*1219Skas 			return(cp);
617*1219Skas 	return(NOSTR);
618*1219Skas }
619*1219Skas 
620*1219Skas /*
621*1219Skas  * String compare two strings of bounded length.
622*1219Skas  */
623*1219Skas 
624*1219Skas strncmp(as1, as2, an)
625*1219Skas 	char *as1, *as2;
626*1219Skas {
627*1219Skas 	register char *s1, *s2;
628*1219Skas 	register int n;
629*1219Skas 
630*1219Skas 	s1 = as1;
631*1219Skas 	s2 = as2;
632*1219Skas 	n = an;
633*1219Skas 	while (--n >= 0 && *s1 == *s2++)
634*1219Skas 		if (*s1++ == '\0')
635*1219Skas 			return(0);
636*1219Skas 	return(n<0 ? 0 : *s1 - *--s2);
637*1219Skas }
638*1219Skas 
639