1 /********************************************************************
2 * $Author: jgoerzen $
3 * $Revision: 1.3 $
4 * $Date: 2002/01/28 03:38:58 $
5 * $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/object/util.c,v $
6 * $State: Exp $
7 *
8 * Paul Lindner, University of Minnesota CIS.
9 *
10 * Copyright 1991, 1992 by the Regents of the University of Minnesota
11 * see the file "Copyright" in the distribution for conditions of use.
12 *********************************************************************
13 * MODULE: util.c
14 * Various useful utilities for gopher clients and servers
15 *********************************************************************
16 * Revision History:
17 * $Log: util.c,v $
18 * Revision 1.3 2002/01/28 03:38:58 jgoerzen
19 * Patches for FreeBSD:
20 * * Test in configure for stdlib.h
21 * * Include stdlib.h if it's available in Malloc.h
22 * * Don't include malloc.h if it's missing in Malloc.h
23 * * Don't include strcasestr if it's present in util.c and util.h
24 *
25 * Revision 1.2 2000/12/20 01:19:20 jgoerzen
26 * Added patches from David Allen <s2mdalle@titan.vcu.edu>
27 *
28 * Revision 1.1.1.1 2000/08/19 00:28:56 jgoerzen
29 * Import from UMN Gopher 2.3.1 after GPLization
30 *
31 * Revision 3.25 1996/01/04 18:24:24 lindner
32 * Updates for autoconf
33 *
34 * Revision 3.24 1995/09/25 22:07:25 lindner
35 * Ansification
36 *
37 * Revision 3.23 1995/08/29 07:12:06 lindner
38 * Turn off annoying debug stmt
39 *
40 * Revision 3.22 1995/05/01 05:41:46 lindner
41 * compatibility fixes
42 *
43 * Revision 3.21 1995/04/15 07:07:39 lindner
44 * Add ControlC handling feature...
45 *
46 * Revision 3.20 1995/02/02 17:14:47 lindner
47 * Fix for memory leaks and accesses
48 *
49 * Revision 3.19 1994/10/19 03:34:39 lindner
50 * Dynamically allocate acceptable array
51 *
52 * Revision 3.18 1994/08/19 16:12:11 lindner
53 * Sanity check for Tohexstr
54 *
55 * Revision 3.17 1994/07/21 22:07:52 lindner
56 * none
57 *
58 * Revision 3.16 1994/04/25 03:37:01 lindner
59 * Modifications for Debug() and mismatched NULL arguments, added Debugmsg
60 *
61 * Revision 3.15 1994/03/31 21:04:36 lindner
62 * Mitra's 2.011 debug patch
63 *
64 * Revision 3.14 1994/03/08 15:56:22 lindner
65 * gcc -Wall fixes
66 *
67 * Revision 3.13 1994/02/20 16:25:58 lindner
68 * Optimize readline and friends to use fileio routines
69 *
70 * Revision 3.12 1993/12/27 16:14:54 lindner
71 * prettify debug output
72 *
73 * Revision 3.11 1993/09/21 01:52:54 lindner
74 * Fixes for NETLIB
75 *
76 * Revision 3.10 1993/08/16 19:41:23 lindner
77 * Fix for DECC/Alpha
78 *
79 * Revision 3.9 1993/08/09 20:17:13 lindner
80 * Fixes for CMULIB and NETLIB for VMS
81 *
82 * Revision 3.8 1993/08/05 03:23:40 lindner
83 * Changes for CMUIP and NETLIB
84 *
85 * Revision 3.7 1993/07/27 05:30:30 lindner
86 * Mondo Debug overhaul from Mitra
87 *
88 * Revision 3.6 1993/07/23 04:49:24 lindner
89 * none
90 *
91 * Revision 3.5 1993/06/22 05:49:39 lindner
92 * *** empty log message ***
93 *
94 * Revision 3.4 1993/04/23 20:11:56 lindner
95 * Fixed misdeclaration of DEBUG
96 *
97 * Revision 3.3 1993/04/15 17:53:12 lindner
98 * Debug stuff from Mitra
99 *
100 * Revision 3.2 1993/03/18 22:28:27 lindner
101 * changed hex routines around for admit1
102 *
103 * Revision 3.1.1.1 1993/02/11 18:03:05 lindner
104 * Gopher+1.2beta release
105 *
106 * Revision 2.5 1993/01/31 00:31:12 lindner
107 * New functions, readword() and readtotoken()
108 *
109 * Revision 2.4 1993/01/12 21:12:23 lindner
110 * Reverted to old readfield behavior() \n is now ignored again.
111 *
112 * Revision 2.3 1993/01/08 23:29:21 lindner
113 * More mods from jqj.
114 *
115 * Revision 2.2 1992/12/31 04:58:41 lindner
116 * merged 1.1.1.1 and 2.1
117 *
118 * Revision 2.1 1992/12/21 19:41:14 lindner
119 * Added check for null in writestring
120 * Added function skip_whitespace
121 *
122 * Revision 1.1.1.1 1992/12/31 04:52:01 lindner
123 * Changes for VMS
124 *
125 * Revision 1.1 1992/12/10 23:27:52 lindner
126 * gopher 1.1 release
127 *
128 *
129 *********************************************************************/
130
131 #include <ctype.h>
132 #include <stdio.h>
133 #include <signal.h>
134 #include <setjmp.h>
135
136 #include "boolean.h"
137 #include "util.h"
138 #include "String.h"
139 #include "Debug.h"
140 #include "fileio.h"
141
142 #if defined(VMS) && (defined(UCX) || defined(CMUIP) || defined(NETLIB))
143 #include <errno.h>
144 #endif
145
146
147 static FileIO *Gfio = NULL;
148 static int Oldsockfd = -1;
149
150 boolean ControlCpressed = 0;
151 jmp_buf Jmpenv; /* Save environment for jump from ctrlc */
152
153 RETSIGTYPE
controlc(int sig)154 controlc(int sig)
155 {
156 ControlCpressed = TRUE;
157 reprimeControlc();
158 }
159
160 RETSIGTYPE
controlcJmp(int sig)161 controlcJmp(int sig)
162 {
163 Debug("controlcJmp",NULL);
164 controlc(sig);
165 longjmp(Jmpenv,1);
166 }
167
168
interruptable_read(int fd,char * ptr,int nbytes)169 int interruptable_read(int fd, char *ptr, int nbytes)
170 {
171 int retval;
172
173 if (ControlCpressed) { /* Already interrupted */
174 Debug("interruptable_read:already interrupted\r\n",NULL);
175 return(1);
176 }
177
178 if (signal(SIGINT, controlcJmp) == SIG_ERR)
179 perror("signal died:\n"), exit(-1);
180
181 if (setjmp(Jmpenv)) {
182 /* Note controlcJmp will reprime controlc and set flag*/
183 Debug("interruptable_read triggered\r\n",NULL);
184 return(-1);
185 }
186 retval = read(fd, ptr, nbytes);
187 reprimeControlc();
188 return(retval);
189 }
190
191
192 /* Place this after the code to be interruptable*/
193 void
reprimeControlc()194 reprimeControlc()
195 {
196 if (signal(SIGINT, controlc) == SIG_ERR)
197 perror("signal died:\n"), exit(-1);
198 }
199
200
201 #if defined(VMS) && (defined(WOLLONGONG) || defined(MULTINET) ||defined(CMUIP)||defined(NETLIB))
202 /* Multinet and Wollongong,etc. (non UCX-emulation) use channel numbers */
203 /* for sockets, which are small multiples of 16. The first 5 */
204 /* channels can be assumed to be already used, so we assume that */
205 /* sockets start at 64, and that only 64 VAXC fds are simultaneously */
206 /* open in the program. Actually, the first socket is likely to be */
207 /* more like 176! */
208
209 #define IS_SOCKET(s) ((s)>=64)
210
211 /* Close a socket.
212 * Note that in old Wollongong and Multinet implementations close()
213 * works only on fds, not sockets.
214 * For UCX and Unix, closenet() is #defined to be close()
215 */
closenet(int s)216 int closenet(int s)
217 {
218 #ifdef DEBUGGING
219 if (s == stderr) {
220 fprintf(stderr, "YUK - closing stderr");
221 }
222 #endif
223
224 if (IS_SOCKET(s)) {
225 #ifdef MULTINET
226 return (socket_close(s));
227 #else /* WOLLONGONG, CMUIP, NETLIB */
228 return (netclose(s));
229 #endif
230 }
231 else
232 close(s); /* shouldn't be calling this routine */
233 }
234 #else /* WOLLANGONG or MULTINET */
235 #define IS_SOCKET(a) 1
236 #endif
237
CheckGfio(int fd)238 static void CheckGfio(int fd)
239 {
240
241 if (Gfio == NULL || Oldsockfd != fd) {
242 Oldsockfd = fd;
243 if (Gfio != NULL)
244 FIOdestroy(Gfio);
245 Gfio = FIOopenfd(fd, IS_SOCKET(fd));
246 }
247 }
248
249
250
251
252 /* Read "n" bytes from a descriptor.
253 * Use in place of read() when fd is a stream socket
254 *
255 * Returns the number of total bytes read.
256 */
257
readn(int fd,char * ptr,int nbytes)258 int readn(int fd, char *ptr, int nbytes)
259 {
260 int nleft, nread;
261
262 nleft = nbytes;
263 while (nleft > 0) {
264 #if defined(VMS) && (defined(WOLLONGONG) || defined(CMUIP) || defined(NETLIB))
265 nread = IS_SOCKET(fd) ? netread(fd, ptr, nleft) : read(fd, ptr, nleft);
266 #else
267 #if defined(VMS) && defined(MULTINET)
268 nread = IS_SOCKET(fd) ? socket_read(fd, ptr, nleft) : read(fd, ptr, nleft);
269 #else
270 nread = read(fd, ptr, nleft);
271 #endif
272 #endif
273 #if defined(VMS) && (defined(UCX) || defined(CMUIP) || defined(NETLIB))
274 if (nread < 0 && errno == EPIPE)
275 break;
276 #endif
277 if (nread < 0)
278 return(nread); /* error, return <0 */
279 else if (nread == 0) /* EOF */
280 break;
281
282 nleft -= nread;
283 ptr += nread;
284 }
285 return(nbytes - nleft); /* return >= 0) */
286 }
287
288
289
290 /*
291 * Write "n" bytes to a descriptor.
292 * Use in place of write() when fd is a stream socket
293 *
294 * We return the number of bytes written
295 */
296
297 int
writen(int fd,char * ptr,int nbytes)298 writen(int fd, char *ptr, int nbytes)
299 {
300 int nleft, nwritten;
301
302 nleft = nbytes;
303 while(nleft > 0) {
304 #if defined(VMS) && (defined(WOLLONGONG) || defined(CMUIP) || defined(NETLIB))
305 nwritten = IS_SOCKET(fd) ? netwrite(fd, ptr, nleft) : write(fd, ptr, nleft);
306 #else
307 #if defined(VMS) && defined(MULTINET)
308 nwritten = IS_SOCKET(fd) ? socket_write(fd, ptr, nleft) : write(fd, ptr, nleft);
309 #else
310 nwritten = write(fd, ptr, nleft);
311 #endif
312 #endif
313 if (nwritten <= 0)
314 return(nwritten); /* error */
315
316 nleft -= nwritten;
317 ptr += nwritten;
318 }
319 return(nbytes - nleft);
320 }
321
322
323 /*
324 * Writestring uses the writen and strlen calls to write a
325 * string to the file descriptor fd. If the write fails
326 * a -1 is returned. Otherwise zero is returned.
327 */
328
writestring(int fd,char * stringptr)329 int writestring(int fd, char *stringptr)
330 {
331 int length;
332
333 /* Debug("writing: %s\n",stringptr);*/
334
335 if (stringptr == NULL)
336 return(0);
337
338 length = strlen(stringptr);
339 if (writen(fd, stringptr, length) != length) {
340 Debugmsg("writestring: writen failed\n");
341 return(-1);
342 }
343 else
344 return(0);
345 }
346
347 /*
348 * Read from the socket into a buffer. Mucho more efficent in terms of
349 * system calls..
350 *
351 * returns bytes read, or <0 for an error
352 */
353
readrecvbuf(int sockfd,char * buf,int len)354 int readrecvbuf(int sockfd, char *buf, int len)
355 {
356 int bytesread;
357
358 CheckGfio(sockfd);
359 if (Gfio == NULL)
360 return(-1);
361
362 bytesread = FIOreadbuf(Gfio, buf, len);
363
364 return(bytesread);
365 }
366
367
368 /*
369 * Read a line from a descriptor. Read the line one byte at a time,
370 * looking for the newline. We store the newline in the buffer,
371 * then follow it with a null (the same as fgets(3)).
372 * We return the number of characters up to, but not including,
373 * the null (the same as strlen(3))
374 */
375
376 int
readline(int fd,char * ptr,int maxlen)377 readline(int fd, char *ptr, int maxlen)
378 {
379 int n;
380
381 CheckGfio(fd);
382
383 if (Gfio == NULL)
384 return(-1);
385
386 n = FIOreadline(Gfio, ptr, maxlen);
387 Debug("readline: %s\n", ptr);
388
389 return(n);
390 }
391
392
393 /*
394 * Read a line from the file/socket, Read the line one byte at a time,
395 * looking for the token. We nuke the token from the returned string.
396 * We return the number of characters up to, but not including,
397 * the null (the same as strlen(3))
398 */
399
400 int
readtoken(int fd,char * ptr,int maxlen,char zechar)401 readtoken(int fd, char *ptr, int maxlen, char zechar)
402 {
403 int bytesread;
404
405 CheckGfio(fd);
406 if (Gfio == NULL)
407 return(-1);
408
409 bytesread = FIOreadtoken(Gfio, ptr, maxlen, zechar);
410
411 Debug("readtoken: %s\n", (ptr-bytesread));
412 return(bytesread);
413 }
414
415
416 int
sreadword(char * input,char * output,int maxlen)417 sreadword(char *input, char *output, int maxlen)
418 {
419 int n;
420 char c;
421
422 for (n=0; n < maxlen; n++) {
423 c = *input++;
424 *output++ = c;
425 if (isspace(c)) {
426 *(output - 1) = '\0';
427 break;
428 }
429
430 if (c == '\0') {
431 break;
432 }
433 }
434
435 *output = '\0'; /* Tack a NULL on the end */
436 return(n);
437 }
438
439
440 /*
441 * ZapCRLF removes all carriage returns and linefeeds from the end of
442 * a C-string.
443 */
444
445 void
ZapCRLF(char * inputline)446 ZapCRLF(char *inputline)
447 {
448 int len;
449
450 len = strlen(inputline);
451
452 if (len == 0)
453 return;
454 inputline += len;
455 inputline--;
456
457 if (*inputline == '\n' || *inputline == '\r') {
458 *inputline = '\0';
459 if (len > 1) {
460 inputline--;
461 if (*inputline == '\n' || *inputline == '\r')
462 *inputline = '\0';
463 }
464 }
465 }
466
467 /*
468 * Utilities for dealing with HTML junk
469 */
470
471 static boolean *acceptable;
472 static boolean acceptable_inited = FALSE;
473
init_acceptable()474 static void init_acceptable()
475 {
476 unsigned int i;
477 char * good =
478 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
479
480 acceptable = (boolean*) malloc(sizeof(boolean) * 256);
481
482 for(i=0; i<256; i++)
483 acceptable[i] = FALSE;
484 for(;*good; good++)
485 acceptable[(unsigned int)*good] = TRUE;
486
487 acceptable_inited = TRUE;
488 }
489
490 static char hex[17] = "0123456789abcdef";
491
492 static char
from_hex(char c)493 from_hex(char c)
494 {
495 return (c>='0')&&(c<='9') ? c-'0'
496 : (c>='A')&&(c<='F') ? c-'A'+10
497 : (c>='a')&&(c<='f') ? c-'a'+10
498 : 0;
499 }
500
501
502 /*
503 * return a hex encoding of the char,
504 */
505
506 #if 0 /* unused */
507 static char *
508 to_hex(char c)
509 {
510 static char out[4];
511
512 out[0] = '\0';
513
514 out[0]='%';
515 out[1]=hex[c >> 4];
516 out[2]=hex[c & 15];
517 out[3]='\0';
518
519 return(out);
520 }
521 #endif /* 0 */
522
523 /*
524 * Replace hex escape sequences with the proper codes...
525 *
526 * input and output can be the same if you want
527 */
528
529 void
Fromhexstr(char * input,char * output)530 Fromhexstr(char *input, char *output)
531 {
532 char c;
533 unsigned int b;
534
535 while (*input) {
536 if (*input == '%') {
537 input++;
538 c = *input++;
539 b = from_hex(c);
540 c = *input++;
541 if (!c) break;
542 *output++ = (b<<4) + from_hex(c);
543 }
544 else
545 *output++ = *input++;
546 }
547 *output = '\0';
548 }
549
550 void
Tohexstr(char * input,char * output)551 Tohexstr(char *input, char *output)
552 {
553 if ( (input == NULL) || (output == NULL) )
554 return;
555
556 if (acceptable_inited == FALSE)
557 init_acceptable();
558
559 while (*input) {
560
561 if (acceptable[(int)*input] == TRUE) {
562 *output++ = *input++;
563 }
564 else {
565 *output++ = '%';
566 *output++ = hex[*input >> 4];
567 *output++ = hex[*input & 15];
568 input++;
569 }
570 }
571
572 *output = '\0';
573 }
574
575 /*
576 * This fcn hexifies everything
577 */
578
579 void
Hexall(char * input,char * output)580 Hexall(char *input, char *output)
581 {
582 while (*input) {
583
584 *output++ = hex[*input >> 4];
585 *output++ = hex[*input & 15];
586 input++;
587 }
588
589 *output = '\0';
590 }
591
592
593 /*
594 * String insensitive strstr
595 */
596
597 #ifndef HAVE_STRCASESTR
598
599 char *
strcasestr(char * inputline,char * match)600 strcasestr(char *inputline, char *match)
601 {
602 int matchlen=0;
603 int i, inlen;
604
605 matchlen = strlen(match);
606 inlen = strlen(inputline);
607
608 for(i=0; i<inlen; i++) {
609 if (strncasecmp(inputline+i, match, matchlen)==0)
610 return(inputline+i);
611 }
612
613 return(NULL);
614 }
615
616 #endif
617
618 /*
619 * Iterate over a string, return a pointer to the next character
620 * that isn't whitespace.
621 */
622
623 char *
skip_whitespace(char * str)624 skip_whitespace(char *str)
625 {
626 while (isspace(*str) && *str!='\0')
627 str++;
628
629 return(str);
630 }
631
632
633
634 #if defined(VMS) && defined(NETLIB)
635
636 /*
637 * netclose, netread, and netwrite for NETLIB
638 */
639
640 #include iodef
641 #include ssdef
642
643 static struct {
644 short status;
645 short size;
646 long xxx;
647 } netlib_iosb;
648
netlib_ast(param)649 void netlib_ast (param)
650 int param;
651 {
652 SYS$SETEF (0);
653 }
654
netclose(channel)655 int netclose (channel)
656 int channel;
657 {
658 NET_DEASSIGN (&channel);
659
660 return (0);
661 }
662
netread(channel,buffer,length)663 int netread (channel, buffer, length)
664 int channel;
665 char *buffer;
666 int length;
667 {
668 struct {
669 long len;
670 char *adr;
671 } buffer_desc;
672 int status;
673
674 if (length > 1500) length = 1500;
675 buffer_desc.len = length;
676 buffer_desc.adr = buffer;
677 SYS$CLREF (0);
678 status = TCP_RECEIVE (&channel, &buffer_desc, &netlib_iosb, netlib_ast, 0);
679 if ((status & 1) == 0)
680 return (-1);
681 SYS$WAITFR (0);
682 if ((netlib_iosb.status & 1) == 0) {
683 if ((netlib_iosb.status == SS$_ABORT &&
684 netlib_iosb.xxx == 0x086380da) /* CMUIP Connection Closing */ ||
685 netlib_iosb.status == SS$_LINKDISCON) /* UCX link disconnecting */
686 errno = EPIPE;
687 return (-1);
688 }
689 return (netlib_iosb.size);
690 }
691
netwrite(channel,buffer,length)692 int netwrite (channel, buffer, length)
693 int channel;
694 char *buffer;
695 int length;
696 {
697 struct {
698 long len;
699 char *adr;
700 } buffer_desc;
701 int status;
702
703 buffer_desc.len = length;
704 buffer_desc.adr = buffer;
705 SYS$CLREF (0);
706 status = TCP_SEND (&channel, &buffer_desc, 6, &netlib_iosb, netlib_ast, 0);
707 if ((status & 1) == 0)
708 return (-1);
709 SYS$WAITFR (0);
710 if ((netlib_iosb.status & 1) == 0) {
711 return (-1);
712 }
713 return (netlib_iosb.size);
714 }
715
716 #endif /* NETLIB */
717
718 #if defined(VMS) && defined(CMUIP)
719
720 /*
721 * netclose, netread, and netwrite for CMUIP
722 */
723
724 #include iodef
725 #include ssdef
726
727 static struct {
728 short status;
729 short size;
730 long xxx;
731 } cmu_iosb;
732 globalvalue NET$_CC; /* Connection Closing */
733
netclose(channel)734 int netclose (channel)
735 int channel;
736 {
737 int status;
738
739 status = SYS$QIOW (0, channel, IO$_DELETE, &cmu_iosb, 0, 0,
740 0, 0, 0, 0, 0, 0);
741 status = SYS$DASSGN (channel);
742
743 return (0);
744 }
745
netread(channel,buffer,length)746 int netread (channel, buffer, length)
747 int channel;
748 char *buffer;
749 int length;
750 {
751 int status;
752
753 if (length > 1500) length = 1500;
754 status = SYS$QIOW (0, channel, IO$_READVBLK, &cmu_iosb, 0, 0,
755 buffer, length, 0, 0, 0, 0);
756 if ((status & 1) == 0)
757 return (-1);
758 if ((cmu_iosb.status & 1) == 0) {
759 if (cmu_iosb.status == SS$_ABORT && cmu_iosb.xxx == NET$_CC)
760 errno = EPIPE;
761 return (-1);
762 }
763 return (cmu_iosb.size);
764 }
765
netwrite(channel,buffer,length)766 int netwrite (channel, buffer, length)
767 int channel;
768 char *buffer;
769 int length;
770 {
771 int status;
772
773 status = SYS$QIOW (0, channel, IO$_WRITEVBLK, &cmu_iosb, 0, 0,
774 buffer, length, 0, 1, 0, 0);
775 if ((status & 1) == 0)
776 return (-1);
777 if ((cmu_iosb.status & 1) == 0)
778 return (-1);
779 return (cmu_iosb.size);
780 }
781
782 #endif /* CMUIP */
783
784
785
786