rdvec doesn't need to be static, makes uucico fit on PDP11's
[unix-history] / usr / src / usr.bin / uucp / uucico / conn.c
CommitLineData
55a580e3 1#ifndef lint
50cde384 2static char sccsid[] = "@(#)conn.c 5.3 (Berkeley) %G%";
55a580e3
SL
3#endif
4
5#include "uucp.h"
6#include <signal.h>
7#include <setjmp.h>
8#include <ctype.h>
9#include <sys/types.h>
62ba4a08 10#include <sys/time.h>
55a580e3
SL
11#include <errno.h>
12#ifdef SYSIII
13#include <termio.h>
14#include <fcntl.h>
15#endif
16#ifndef SYSIII
17#include <sgtty.h>
18#endif
19
20#define MAXC 1000
21
22extern jmp_buf Sjbuf;
23extern int errno;
24
25/* Parity control during login procedure */
26#define P_ZERO 0
27#define P_ONE 1
28#define P_EVEN 2
29#define P_ODD 3
30char par_tab[128]; /* must be power of two */
31
32int next_fd = -1; /* predicted fd to close interrupted opens */
33 /* rti!trt, courtesy unc!smb */
34/***
35 * alarmtr() - catch alarm routine for "expect".
36 */
37alarmtr()
38{
39 signal(SIGALRM, alarmtr);
40 if (next_fd >= 0) {
41 if (close(next_fd))
42 logent("FAIL", "ACU LINE CLOSE");
43 next_fd = -1;
44 }
45 longjmp(Sjbuf, 1);
46}
47
48/*******
49 * conn(system)
50 * char *system;
51 *
52 * conn - place a telephone call to system and
53 * login, etc.
54 *
55 * return codes:
56 * CF_SYSTEM: don't know system
57 * CF_TIME: wrong time to call
58 * CF_DIAL: call failed
59 * CF_NODEV: no devices available to place call
60 * CF_LOGIN: login/password dialog failed
61 *
62 * >0 - file no. - connect ok
63 *
64 */
65
66int Dcf = -1;
67
68conn(system)
69char *system;
70{
71 int ret, nf;
72 register int fn, fnd;
73 char info[MAXC], *flds[MAXC/10];
74 register FILE *fsys;
75 int fcode = 0;
76
77 nf = 0;
78 fnd = 0;
79
80
81 fsys = fopen(SYSFILE, "r");
82 ASSERT(fsys != NULL, "CAN'T OPEN", SYSFILE, 0);
83
84 DEBUG(4, "finds %s\n", "called");
85 while((nf = finds(fsys, system, info, flds)) > 0) {
86 DEBUG(4, "getto %s\n", "called");
87 if ((fn = getto(flds)) > 0) {
88 fnd = 1;
89 Dcf = fn;
90 break;
91 }
92 fcode = (fn == FAIL ? CF_DIAL : fn);
93 }
94 fclose(fsys);
95
96 if (nf <= 0)
97 return(fcode ? fcode : nf);
98
99 DEBUG(4, "login %s\n", "called");
100 ret = login(nf, flds, fn);
101 if (ret < 0) {
102 clsacu();
103 return(CF_LOGIN);
104 }
105 /* rti!trt: avoid passing file to children */
106 fioclex(fn);
107 return(fn);
108}
109
110/***
111 * getto(flds) connect to remote machine
112 * char *flds[];
113 *
114 * return codes:
115 * >0 - file number - ok
116 * FAIL - failed
117 */
118
119getto(flds)
120register char *flds[];
121{
122 register struct condev *cd;
123 int nulldev(), diropn();
124
125 DEBUG(4, "call: no. %s ", flds[F_PHONE]);
126 DEBUG(4, "for sys %s\n", flds[F_NAME]);
127
128 CU_end = nulldev;
129 for (cd = condevs; cd->CU_meth != NULL; cd++) {
130 if (snccmp(cd->CU_meth, flds[F_LINE]) == SAME) {
131 DEBUG(4, "Using %s to call\n", cd->CU_meth);
132 return((*(cd->CU_gen))(flds));
133 }
134 }
135 logent(flds[F_LINE], "getto: Can't find, using DIR");
136 return(diropn(flds)); /* search failed, so use direct */
137 }
138
139/***
140 * clsacu() close call unit
141 *
142 * return codes: none
143 */
144
145int (*CU_end)() = nulldev;
146clsacu()
147{
148 (*(CU_end))(Dcf);
149 if (close(Dcf) == 0) {
150 DEBUG(4, "fd %d NOT CLOSED by CU_clos\n", Dcf);
151 logent("clsacu", "NOT CLOSED by CU_clos");
152 }
153 Dcf = -1;
154 CU_end = nulldev;
155}
156
157/***
158 * exphone - expand phone number for given prefix and number
159 *
160 * return code - none
161 */
162
163exphone(in, out)
164register char *in, *out;
165{
166 FILE *fn;
167 char pre[MAXPH], npart[MAXPH], tpre[MAXPH], p[MAXPH];
168 char buf[BUFSIZ];
169 register char *s1;
170
171 if (!isalpha(*in)) {
172 strcpy(out, in);
173 return;
174 }
175
176 s1=pre;
177 while (isalpha(*in))
178 *s1++ = *in++;
179 *s1 = '\0';
180 s1 = npart;
181 while (*in != '\0')
182 *s1++ = *in++;
183 *s1 = '\0';
184
185 tpre[0] = '\0';
186 if ((fn = fopen(DIALFILE, "r")) == NULL)
187 DEBUG(2, "CAN'T OPEN %s\n", DIALFILE);
188 else {
189 while (cfgets(buf, BUFSIZ, fn)) {
190 sscanf(buf, "%s%s", p, tpre);
191 if (strcmp(p, pre) == SAME)
192 goto found;
193 tpre[0] = '\0';
194 }
195 DEBUG(2, "CAN'T FIND dialcodes prefix '%s'\n", pre);
196 found:;
197 fclose(fn);
198 }
199
200 strcpy(out, tpre);
201 strcat(out, npart);
202 return;
203}
204
205/***
206 * rddev - read and decode a line from device file
207 *
208 * return code - FAIL at end-of file; 0 otherwise
209 */
210
211rddev(fp, dev)
212register struct Devices *dev;
213FILE *fp;
214{
215 char *fdig();
216 char buf[BUFSIZ];
217 int na;
218
219 if (!cfgets(buf, BUFSIZ, fp))
220 return(FAIL);
221
222 na = sscanf(buf, "%s%s%s%s%s", dev->D_type, dev->D_line,
223 dev->D_calldev, dev->D_class, dev->D_brand);
224 ASSERT(na >= 4, "BAD DEVICE ENTRY", buf, 0);
225 if (na != 5) dev->D_brand[0] = '\0';
226 dev->D_speed = atoi(fdig(dev->D_class));
227 return(0);
228}
229
230/***
231 * finds(fsys, sysnam, info, flds) set system attribute vector
232 *
233 * return codes:
234 * >0 - number of arguments in vector - succeeded
235 * CF_SYSTEM - system name not found
236 * CF_TIME - wrong time to call
237 */
238
239finds(fsys, sysnam, info, flds)
240char *sysnam, info[], *flds[];
241FILE *fsys;
242{
243 char sysn[8];
244 int na;
245 int fcode = 0;
246
247 /* format of fields
248 * 0 name;
249 * 1 time
250 * 2 acu/hardwired
251 * 3 speed
252 * etc
253 */
254 while (cfgets(info, MAXC, fsys) != NULL) {
255 na = getargs(info, flds);
256 sprintf(sysn, "%.7s", flds[F_NAME]);
257 if (strcmp(sysnam, sysn) != SAME)
258 continue;
259 if (ifdate(flds[F_TIME]))
260 /* found a good entry */
261 return(na);
262 DEBUG(2, "Wrong time ('%s') to call\n", flds[F_TIME]);
263 fcode = CF_TIME;
264 }
265 return(fcode ? fcode : CF_SYSTEM);
266}
267
268/***
269 * login(nf, flds, dcr) do login conversation
270 * char *flds[];
271 * int nf;
272 *
273 * return codes: 0 | FAIL
274 */
275
276login(nf, flds, fn)
277register char *flds[];
278int nf, fn;
279{
280 register char *want, *altern;
281 extern char *index();
282 int k, ok;
283
284 ASSERT(nf > 4, "TOO FEW LOG FIELDS", "", nf);
285 for (k = F_LOGIN; k < nf; k += 2) {
286 want = flds[k];
287 ok = FAIL;
288 while (ok != 0) {
289 altern = index(want, '-');
290 if (altern != NULL)
291 *altern++ = '\0';
292 DEBUG(4, "wanted %s ", want);
293 ok = expect(want, fn);
294 DEBUG(4, "got %s\n", ok ? "?" : "that");
295 if (ok == 0)
296 break;
297 if (altern == NULL) {
298 logent("LOGIN", "FAILED");
299 /* close *not* needed here. rti!trt */
300 return(FAIL);
301 }
302 want = index(altern, '-');
303 if (want != NULL)
304 *want++ = '\0';
305 sendthem(altern, fn);
306 }
307 sleep(2);
308 if (k+1 < nf)
309 sendthem(flds[k+1], fn);
310 }
311 return(0);
312}
313
314
315/* rti!trt: conditional table generation to support odd speeds */
316/* Suggested in n44a.139 by n44!dan (Dan Ts'o) */
317struct sg_spds {int sp_val, sp_name;} spds[] = {
318#ifdef B50
319 { 50, B50},
320#endif
321#ifdef B75
322 { 75, B75},
323#endif
324#ifdef B110
325 { 110, B110},
326#endif
327#ifdef B150
328 { 150, B150},
329#endif
330#ifdef B200
331 { 200, B200},
332#endif
333#ifdef B300
334 { 300, B300},
335#endif
336#ifdef B600
337 {600, B600},
338#endif
339#ifdef B1200
340 {1200, B1200},
341#endif
342#ifdef B1800
343 {1800, B1800},
344#endif
345#ifdef B2000
346 {2000, B2000},
347#endif
348#ifdef B2400
349 {2400, B2400},
350#endif
351#ifdef B3600
352 {3600, B3600},
353#endif
354#ifdef B4800
355 {4800, B4800},
356#endif
357#ifdef B7200
358 {7200, B7200},
359#endif
360#ifdef B9600
361 {9600, B9600},
362#endif
363#ifdef B19200
364 {19200,B19200},
365#endif
366 {0, 0}
367};
368
369/***
370 * fixline(tty, spwant) set speed/echo/mode...
371 * int tty, spwant;
372 *
373 * return codes: none
374 */
375
376fixline(tty, spwant)
377int tty, spwant;
378{
379#ifdef SYSIII
380 struct termio ttbuf;
381#endif
382#ifndef SYSIII
383 struct sgttyb ttbuf;
384#endif
385 register struct sg_spds *ps;
386 int speed = -1;
387 int ret;
388
389 for (ps = spds; ps->sp_val; ps++)
390 if (ps->sp_val == spwant)
391 speed = ps->sp_name;
392 ASSERT(speed >= 0, "BAD SPEED", "", speed);
393#ifdef SYSIII
394 ioctl(tty, TCGETA, &ttbuf);
395 /* ttbuf.sg_flags = (ANYP|RAW);
396 ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed; */
397 ttbuf.c_iflag = (ushort)0;
398 ttbuf.c_oflag = (ushort)0;
399 ttbuf.c_cflag = (speed|CS8|HUPCL|CREAD);
400 ttbuf.c_lflag = (ushort)0;
401 ttbuf.c_cc[VMIN] = 6;
402 ttbuf.c_cc[VTIME] = 1;
403 ret = ioctl(tty, TCSETA, &ttbuf);
404#endif
405#ifndef SYSIII
406 ioctl(tty, TIOCGETP, &ttbuf);
407 ttbuf.sg_flags = (ANYP|RAW);
408 ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed;
409 ret = ioctl(tty, TIOCSETP, &ttbuf);
410#endif
411 ASSERT(ret >= 0, "RETURN FROM STTY", "", ret);
412#ifndef SYSIII
413 ioctl(tty, TIOCHPCL, STBNULL);
414 ioctl(tty, TIOCEXCL, STBNULL);
415#endif
416 return;
417}
418
419
420/* Bill Shannon recommends MR 2000, but that takes too much space on PDPs */
421/* Actually, the 'expect' algorithm should be rewritten. */
422#define MR 1000
423
424
425/***
426 * expect(str, fn) look for expected string
427 * char *str;
428 *
429 * return codes:
430 * 0 - found
431 * FAIL - lost line or too many characters read
432 * some character - timed out
433 */
434
435expect(str, fn)
436register char *str;
437int fn;
438{
50cde384 439 char rdvec[MR];
55a580e3
SL
440 register char *rp = rdvec;
441 int kr;
442 char nextch;
443
444 if (strcmp(str, "\"\"") == SAME)
445 return(0);
446 *rp = 0;
447 if (setjmp(Sjbuf)) {
448 return(FAIL);
449 }
450 signal(SIGALRM, alarmtr);
451/* change MAXCHARTIME to MAXMSGTIME, outside while loop -- brl-bmd!dpk */
452 alarm(MAXMSGTIME);
453 while (notin(str, rdvec)) {
454 kr = read(fn, &nextch, 1);
455 if (kr <= 0) {
456 alarm(0);
457 DEBUG(4, "lost line kr - %d\n, ", kr);
458 logent("LOGIN", "LOST LINE");
459 return(FAIL);
460 }
461 {
462 int c;
463 c = nextch & 0177;
464 DEBUG(4, c >= 040 ? "%c" : "\\%03o", c);
465 }
466 if ((*rp = nextch & 0177) != '\0')
467 rp++;
468/* Check rdvec before null termination -- cmcl2!salkind */
469 if (rp >= rdvec + MR) {
470 alarm(0);
471 return(FAIL);
472 }
473 *rp = '\0';
474 }
475 alarm(0);
476 return(0);
477}
478
479
480/*
481 * Determine next file descriptor that would be allocated.
482 * This permits later closing of a file whose open was interrupted.
483 * It is a UNIX kernel problem, but it has to be handled.
484 * unc!smb (Steve Bellovin) probably first discovered it.
485 */
486getnextfd()
487{
488 close(next_fd = open("/", 0));
489}
490
491/***
492 * sendthem(str, fn) send line of login sequence
493 * char *str;
494 *
495 * return codes: none
496 */
497
498sendthem(str, fn)
499register char *str;
500int fn;
501{
502 register char *strptr;
503 int i, n, cr = 1;
504 static int p_init = 0;
505
506 /* Note: debugging authorized only for privileged users */
507 DEBUG(5, "send %s\n", str);
508
509 if (!p_init) {
510 p_init++;
511 bld_partab(P_EVEN);
512 }
513
514 if (prefix("BREAK", str)) {
515 sscanf(&str[5], "%1d", &i);
516 if (i <= 0 || i > 10)
517 i = 3;
518 /* send break */
519 genbrk(fn, i);
520 return;
521 }
522
523 if (prefix("PAUSE", str)) {
524 sscanf(&str[5], "%1d", &i);
525 if (i <= 0 || i > 10)
526 i = 3;
527 /* pause for a while */
528 sleep((unsigned)i);
529 return;
530 }
531
532 if (strcmp(str, "EOT") == SAME) {
533 p_chwrite(fn, '\04');
534 return;
535 }
536
537 /* LF, CR, and "" courtesy unc!smb */
538 /* Send a '\n' */
539 if (strcmp(str, "LF") == SAME)
540 str = "\\n\\c";
541
542 /* Send a '\r' */
543 if (strcmp(str, "CR") == SAME)
544 str = "\\r\\c";
545
546 /* Set parity as needed */
547 if (strcmp(str, "P_ZERO") == SAME) {
548 bld_partab(P_ZERO);
549 return;
550 }
551 if (strcmp(str, "P_ONE") == SAME) {
552 bld_partab(P_ONE);
553 return;
554 }
555 if (strcmp(str, "P_EVEN") == SAME) {
556 bld_partab(P_EVEN);
557 return;
558 }
559 if (strcmp(str, "P_ODD") == SAME) {
560 bld_partab(P_ODD);
561 return;
562 }
563
564 /* If "", just send '\r' */
565 if (strcmp(str, "\"\"") != SAME)
566 for (strptr = str; *strptr; strptr++) {
567 if (*strptr == '\\') switch(*++strptr) {
568 case 's':
569 DEBUG(5, "BLANK\n", "");
570 *strptr = ' ';
571 break;
572 case 'd':
573 DEBUG(5, "DELAY\n", "");
574 sleep(1);
575 continue;
576 case 'r':
577 DEBUG(5, "RETURN\n", "");
578 *strptr = '\r';
579 break;
580 case 'b':
581 if (isdigit(*(strptr+1))) {
582 i = (*++strptr - '0');
583 if (i <= 0 || i > 10)
584 i = 3;
585 } else
586 i = 3;
587 /* send break */
588 genbrk(fn, i);
589 continue;
590 case 'c':
591 if (*(strptr+1) == '\0') {
592 DEBUG(5, "NO CR\n", "");
593 cr = 0;
594 continue;
595 }
596 DEBUG(5, "NO CR - MIDDLE IGNORED\n", "");
597 continue;
598 default:
599 if (isdigit(strptr[1])) {
600 i = 0;
601 n = 0;
602 while (isdigit(strptr[1]) && ++n <= 3)
603 i = i*8 + (*++strptr - '0');
604 p_chwrite(fn, i);
605 continue;
606 }
607 DEBUG(5, "BACKSLASH\n", "");
608 strptr--;
609 }
610 p_chwrite(fn, *strptr);
611 }
612
613 /* '\n' changed to '\r'--a better default. rti!trt */
614 if (cr)
615 p_chwrite(fn, '\r');
616 return;
617}
618
619p_chwrite(fd, c)
620int fd;
621int c;
622{
623 char t[2];
624
625 t[0] = par_tab[c&0177];
626 t[1] = '\0';
627 ASSERT(write(fd, t, 1) == 1, "BAD WRITE", "", t[0]);
628}
629
630/*
631 * generate parity table for use by p_chwrite.
632 */
633bld_partab(type)
634int type;
635{
636 register int i, j, n;
637
638 for (i = 0; i < sizeof(par_tab); i++) {
639 n = 0;
640 for (j = i&(sizeof(par_tab)-1); j; j = (j-1)&j)
641 n++;
642 par_tab[i] = i;
643 if (type == P_ONE
644 || (type == P_EVEN && (n&01) != 0)
645 || (type == P_ODD && (n&01) == 0))
646 par_tab[i] |= sizeof(par_tab);
647 }
648}
649
650#define BSPEED B150
651
652/***
653 * genbrk send a break
654 *
655 * return codes; none
656 */
657
658genbrk(fn, bnulls)
659register int fn, bnulls;
660{
661 register int ret;
662#ifdef SYSIII
663 ret = ioctl(fn, TCSBRK, STBNULL);
664 DEBUG(5, "break ioctl ret %d\n", ret);
665#endif
666#ifndef SYSIII
667#ifdef TIOCSBRK
668 ret = ioctl(fn, TIOCSBRK, STBNULL);
669 DEBUG(5, "break ioctl ret %d\n", ret);
670#ifdef TIOCCBRK
671 ret = write(fn, "\0\0\0\0\0\0\0\0\0\0\0\0", bnulls);
672 ASSERT(ret > 0, "BAD WRITE genbrk", "", ret);
673 sleep(1);
674 ret = ioctl(fn, TIOCCBRK, STBNULL);
675 DEBUG(5, "break ioctl ret %d\n", ret);
676#endif
677 DEBUG(4, "ioctl 1 second break\n", STBNULL);
678#else
679 struct sgttyb ttbuf;
680 register int sospeed;
681
682 ret = ioctl(fn, TIOCGETP, &ttbuf);
683 sospeed = ttbuf.sg_ospeed;
684 ttbuf.sg_ospeed = BSPEED;
685 ret = ioctl(fn, TIOCSETP, &ttbuf);
686 ret = write(fn, "\0\0\0\0\0\0\0\0\0\0\0\0", bnulls);
687 ASSERT(ret > 0, "BAD WRITE genbrk", "", ret);
688 ttbuf.sg_ospeed = sospeed;
689 ret = ioctl(fn, TIOCSETP, &ttbuf);
690 ret = write(fn, "@", 1);
691 ASSERT(ret > 0, "BAD WRITE genbrk", "", ret);
692 DEBUG(4, "sent BREAK nulls - %d\n", bnulls);
693#endif
694#endif
695}
696
697
698/***
699 * notin(sh, lg) check for occurrence of substring "sh"
700 * char *sh, *lg;
701 *
702 * return codes:
703 * 0 - found the string
704 * 1 - not in the string
705 */
706
707notin(sh, lg)
708register char *sh, *lg;
709{
710 while (*lg != '\0') {
711 /* Dave Martingale: permit wild cards in 'expect' */
712 if (wprefix(sh, lg))
713 return(0);
714 else
715 lg++;
716 }
717 return(1);
718}
719
720
721/*******
722 * ifdate(s)
723 * char *s;
724 *
725 * ittvax!swatt
726 * Allow multiple date specifications separated by '|'.
727 * Calls ifadate, formerly "ifdate".
728 *
729 * return codes:
730 * see ifadate
731 */
732
733ifdate(s)
734char *s;
735{
736 register char *p;
737 register int ret;
738
739 for (p = s; p && (*p == '|' ? *++p : *p); p = index(p, '|'))
740 if (ret = ifadate(p))
741 return(ret);
742 return(0);
743}
744
745
746/*******
747 * ifadate(s)
748 * char *s;
749 *
750 * ifadate - this routine will check a string (s)
751 * like "MoTu0800-1730" to see if the present
752 * time is within the given limits.
753 * SIDE EFFECT - Retrytime is set
754 *
755 * String alternatives:
756 * Wk - Mo thru Fr
757 * zero or one time means all day
758 * Any - any day
759 *
760 * return codes:
761 * 0 - not within limits
762 * 1 - within limits
763 */
764
765ifadate(s)
766char *s;
767{
768 static char *days[]={
769 "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
770 };
771 time_t clock;
772 int rtime;
773 int i, tl, th, tn, flag, dayok=0;
774 struct tm *localtime();
775 struct tm *tp;
776 char *index();
777 char *p;
778
779 /* pick up retry time for failures */
780 /* global variable Retrytime is set here */
781 if ((p = index(s, ',')) == NULL) {
782 Retrytime = RETRYTIME;
783 }
784 else {
785 i = sscanf(p+1, "%d", &rtime);
786 if (i < 1 || rtime < 5)
787 rtime = 5;
788 Retrytime = rtime * 60;
789 }
790
791 time(&clock);
792 tp = localtime(&clock);
793 while (isalpha(*s)) {
794 for (i = 0; days[i]; i++) {
795 if (prefix(days[i], s))
796 if (tp->tm_wday == i)
797 dayok = 1;
798 }
799
800 if (prefix("Wk", s))
801 if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
802 dayok = 1;
803 if (prefix("Any", s))
804 dayok = 1;
805 s++;
806 }
807
808 if (dayok == 0)
809 return(0);
810 i = sscanf(s, "%d-%d", &tl, &th);
811 tn = tp->tm_hour * 100 + tp->tm_min;
812 if (i < 2)
813 return(1);
814 if (th < tl)
815 flag = 0; /* set up for crossover 2400 test */
816 else
817 flag = 1;
818 if ((tn >= tl && tn <= th)
819 || (tn >= th && tn <= tl)) /* test for crossover 2400 */
820 return(flag);
821 else
822 return(!flag);
823}
824
825
826/***
827 * char *
828 * lastc(s) return pointer to last character
829 * char *s;
830 *
831 */
832
833char *
834lastc(s)
835register char *s;
836{
837 while (*s != '\0') s++;
838 return(s);
839}
840
841
842/***
843 * char *
844 * fdig(cp) find first digit in string
845 *
846 * return - pointer to first digit in string or end of string
847 */
848
849char *
850fdig(cp)
851register char *cp;
852{
853 register char *c;
854
855 for (c = cp; *c; c++)
856 if (*c >= '0' && *c <= '9')
857 break;
858 return(c);
859}
860
861
862/*
863 * Compare strings: s1>s2: >0 s1==s2: 0 s1<s2: <0
864 * Strings are compared as if they contain all capital letters.
865 */
866
867snccmp(s1, s2)
868register char *s1, *s2;
869{
870 char c1, c2;
871
872 if (islower(*s1)) c1 = toupper(*s1);
873 else c1 = *s1;
874 if (islower(*s2)) c2 = toupper(*s2);
875 else c2 = *s2;
876
877 while (c1 == c2) {
878 if (*s1++=='\0')
879 return(0);
880 s2++;
881 if (islower(*s1)) c1 = toupper(*s1);
882 else c1 = *s1;
883 if (islower(*s2)) c2 = toupper(*s2);
884 else c2 = *s2;
885 }
886 return(c1 - c2);
887}