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