2 #include <sys/socket.h>
4 #include <netinet/in.h>
19 int defaultTraceSync = TRUE;
21 /*-----------------------------------------------------------------*/
23 DiffTimeVal(const struct timeval *start, const struct timeval *end)
28 gettimeofday(&temp, NULL);
30 return(end->tv_sec - start->tv_sec +
31 1e-6*(end->tv_usec - start->tv_usec));
33 /*-----------------------------------------------------------------*/
35 CreatePrivateAcceptSocketEx(int portNum, int nonBlocking, int loopbackOnly)
38 struct linger doLinger;
40 struct sockaddr_in sa;
43 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
46 /* don't linger on close */
47 doLinger.l_onoff = doLinger.l_linger = 0;
48 if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
49 &doLinger, sizeof(doLinger)) == -1) {
55 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
56 &doReuse, sizeof(doReuse)) == -1) {
62 /* make listen socket nonblocking */
63 if (fcntl(sock, F_SETFL, O_NDELAY) == -1) {
69 /* set up info for binding listen */
70 memset(&sa, 0, sizeof(sa));
71 sa.sin_family = AF_INET;
72 sa.sin_addr.s_addr = (loopbackOnly) ? htonl(INADDR_LOOPBACK)
74 sa.sin_port = htons(portNum);
77 if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
83 if (listen(sock, 32) == -1) {
90 /*-----------------------------------------------------------------*/
92 CreatePrivateAcceptSocket(int portNum, int nonBlocking)
94 return CreatePrivateAcceptSocketEx(portNum, nonBlocking, FALSE);
96 /*-----------------------------------------------------------------*/
98 CreatePublicUDPSocket(int portNum)
100 struct sockaddr_in hb_sin;
103 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
106 memset(&hb_sin, 0, sizeof(hb_sin));
107 hb_sin.sin_family = AF_INET;
108 hb_sin.sin_addr.s_addr = INADDR_ANY;
109 hb_sin.sin_port = htons(portNum);
111 if (bind(sock, (struct sockaddr *) &hb_sin, sizeof(hb_sin)) < 0) {
117 /*-----------------------------------------------------------------*/
119 MakeConnection(char *name, in_addr_t netAddr, int portNum, int nonBlocking)
121 struct sockaddr_in saddr;
126 if ((ent = gethostbyname(name)) == NULL) {
128 TRACE("failed in name lookup - %s\n", name);
131 memcpy(&netAddr, ent->h_addr, sizeof(netAddr));
134 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
136 TRACE("failed creating socket - %d\n", errno);
141 if (fcntl(fd, F_SETFL, O_NDELAY) < 0) {
143 TRACE("failed fcntl'ing socket - %d\n", errno);
149 saddr.sin_family = AF_INET;
150 saddr.sin_addr.s_addr = netAddr;
151 saddr.sin_port = htons(portNum);
153 if (connect(fd, (struct sockaddr *) &saddr,
154 sizeof(struct sockaddr_in)) < 0) {
155 if (errno == EINPROGRESS)
158 TRACE("failed connecting socket - %d\n", errno);
165 /*-----------------------------------------------------------------*/
167 MakeLoopbackConnection(int portNum, int nonBlocking)
169 return(MakeConnection(NULL, htonl(INADDR_LOOPBACK),
170 portNum, nonBlocking));
172 /*-----------------------------------------------------------------*/
174 GetField(const unsigned char *start, int whichField)
178 /* move to first non-blank char */
179 while (isspace(*start))
185 for (currField = 0; currField < whichField; currField++) {
186 /* move over this field */
187 while (*start != '\0' && (!isspace(*start)))
189 /* move over blanks before next field */
190 while (isspace(*start))
195 return((char *) start);
197 /* ---------------------------------------------------------------- */
199 GetWord(const unsigned char *start, int whichWord)
201 /* returns a newly allocated string containing the desired word,
202 or NULL if there was a problem */
207 temp = (unsigned char *) GetField(start, whichWord);
210 while (!(temp[len] == '\0' || isspace(temp[len])))
213 NiceExit(-1, "internal error");
214 res = (char *)xcalloc(1, len+1);
216 NiceExit(-1, "out of memory");
217 memcpy(res, temp, len);
220 /* ---------------------------------------------------------------- */
222 GetWordEx(const unsigned char *start, int whichWord,
225 /* returns a newly allocated string containing the desired word,
226 or NULL if there was a problem */
230 memset(dest, 0, max);
231 temp = (unsigned char *) GetField(start, whichWord);
234 while (!(temp[len] == '\0' || isspace(temp[len])))
237 NiceExit(-1, "internal error");
240 memcpy(dest, temp, len);
243 /*-----------------------------------------------------------------*/
255 /*-----------------------------------------------------------------*/
259 if (a >= '0' && a <= '9')
261 if (a >= 'a' && a <= 'z')
262 return(10 + a - 'a');
263 if (a >= 'A' && a <= 'Z')
264 return(10 + a - 'A');
267 /*-----------------------------------------------------------------*/
274 for (i = 0; i < (int)sizeof(int) * 8; i++) {
280 /*-----------------------------------------------------------------*/
282 PopCountChar(int val)
287 for (i = 0; i < (int)sizeof(int) * 8; i++) {
291 return(Base36Digit(count));
293 /*-----------------------------------------------------------------*/
299 for (i = 0; i < 32; i++) {
301 return(Base36Digit(i));
303 return(Base36Digit(32));
305 /*-----------------------------------------------------------------*/
307 StringOrNull(const char *s)
313 /*-----------------------------------------------------------------*/
315 strchars(const char *string, const char *list)
317 /* acts like strchr, but works with multiple characters */
318 int numChars = strlen(list);
325 for (walk = string; *walk; walk++) {
326 for (i = 0; i < numChars; i++) {
327 if (*walk == list[i])
328 return (char *)(walk);
333 /*-----------------------------------------------------------------*/
335 strnchars(const char *string, int length, const char *list)
337 /* acts like strchr, but works with multiple characters, and
338 reads exactly length characters from string. */
339 int searchingfor[256] = {0};
345 for (; *list; list++) {
347 * Be careful with this cast.
348 * If *list == (char) -98 (extended ascii), then
349 * (unsigned)*list == (unsigned int)*list == 4294967198
350 * (int)(unsigned char)*list == (unsigned char)*list == 158
351 * The compiler automatically casts the character to an int before
352 * doing the array index.
354 searchingfor[(int)(unsigned char)*list] = 1;
357 for (walk = string; walk - string < length; walk++) {
359 if (searchingfor[(int)(unsigned char)*walk]) {
360 return (char *)(walk);
365 /*-----------------------------------------------------------------*/
367 strncspn(const char* string, int length, const char* reject)
368 /* like strcspn but reads to length characters */
371 int searchingfor[256] = {0};
374 for (; *reject; reject++) {
376 * Be careful with this cast.
377 * If *list == (char) -98 (extended ascii), then
378 * (unsigned)*list == (unsigned int)*list == 4294967198
379 * (int)(unsigned char)*list == (unsigned char)*list == 158
380 * The compiler automatically casts the character to an int before
381 * doing the array index.
383 searchingfor[(int)(unsigned char)*reject] = 1;
386 for(walk = string; walk - string < length; walk++) {
388 if (searchingfor[(int)(unsigned char)*walk]) {
396 /*-----------------------------------------------------------------*/
398 strnchr(const char *string, int length, const char needle)
400 /* acts like strchr, but
401 reads exactly length characters from string. */
404 for (walk = string; walk - string < length; walk++) {
405 if (needle == *walk) {
406 return (char *)(walk);
412 /*-----------------------------------------------------------------*/
414 /* same as strstr, except that si doesn't have to end at '\0' */
415 char * strnstr(const char * s1, int s1_len, const char * s2)
426 if (!memcmp(s1,s2,l2))
433 /*-----------------------------------------------------------------*/
434 /* same as strnstr, except case insensitive */
435 char * strncasestr(const char * s1, int s1_len, const char * s2)
446 if (!strncasecmp(s1,s2,l2))
453 /*-----------------------------------------------------------------*/
455 StrdupLower(const char *orig)
460 if ((temp = xstrdup(orig)) == NULL)
461 NiceExit(-1, "no memory in strduplower");
462 for (i = 0; temp[i]; i++) {
463 if (isupper((int) temp[i]))
464 temp[i] = tolower(temp[i]);
469 /*-----------------------------------------------------------------*/
471 StrcpyLower(char *dest, const char *src)
473 /* copy 'src' to 'dest' in lower cases.
474 'dest' should have enough free space to hold src */
477 for (i = 0; src[i]; i++) {
478 dest[i] = (isupper((int) src[i])) ? tolower(src[i]) : src[i];
481 /* mark it as NULL */
484 /*-----------------------------------------------------------------*/
486 StrcpyLowerExcept(char *dest, int dest_max, const char* src, const char* except)
488 /* copy 'src' to 'dest' in lower cases, skipping the chars in except.
489 'dest' should have enough free space to hold src */
495 for (i = 0, j= 0; src[i]; i++) {
496 if (strchr(except, src[i]))
499 if (j == dest_max - 1)
501 dest[j++] = (isupper((int) src[i])) ? tolower(src[i]) : src[i];
504 /* mark it as NULL */
508 /*-----------------------------------------------------------------*/
510 GetNextLineBack(FILE *file, int lower, int stripComments)
512 /* reads the next non-blank line of the file. strips off any leading
513 space, any comments, and any trailing space. returns a lowercase
514 version of the line that has been malloc'd */
517 while (fgets(line, sizeof(line), file) != NULL) {
521 /* strip off any comments, leading and trailing whitespace */
523 if ((temp = strchr(line, '#')) != NULL)
527 while (len > 0 && isspace((int) line[len-1])) {
532 while (isspace((int) *temp))
535 continue; /* blank line, move on */
538 return(StrdupLower(temp));
539 return(xstrdup(temp));
544 /*-----------------------------------------------------------------*/
546 GetLowerNextLine(FILE *file)
548 return(GetNextLineBack(file, TRUE, TRUE));
550 /*-----------------------------------------------------------------*/
552 GetNextLine(FILE *file)
554 return(GetNextLineBack(file, FALSE, TRUE));
556 /*-----------------------------------------------------------------*/
558 GetNextLineNoCommStrip(FILE *file)
560 return(GetNextLineBack(file, FALSE, FALSE));
562 /*-----------------------------------------------------------------*/
564 DoesSuffixMatch(char *start, int len, char *suffix)
566 int sufLen = strlen(suffix);
572 if (strncasecmp(start+len-sufLen, suffix, sufLen))
576 /*-----------------------------------------------------------------*/
578 DoesDotlessSuffixMatch(char *start, int len, char *suffix)
580 /* ignores any dots on end of start, suffix */
581 int sufLen = strlen(suffix);
586 while (len > 1 && start[len-1] == '.')
588 while (sufLen > 1 && suffix[sufLen-1] == '.')
593 if (strncasecmp(start+len-sufLen, suffix, sufLen))
598 /*-----------------------------------------------------------------*/
600 /* Bit Vector Implementation */
602 /*-----------------------------------------------------------------*/
603 #define BIT_INDEX (0x0000001F)
606 SetBits(int* bits, int idx, int maxNum)
608 if (idx > (maxNum << 5)) {
609 TRACE("Invalid index: %d", idx);
612 bits[(idx >> 5)] |= (1 << (idx & BIT_INDEX));
614 /*-----------------------------------------------------------------*/
616 GetBits(int* bits, int idx, int maxNum)
618 if (idx > (maxNum << 5)) {
619 TRACE("Invalid index: %d", idx);
622 return (bits[(idx >> 5)] & (1 << (idx & BIT_INDEX)));
625 /*-----------------------------------------------------------------*/
627 GetNumBits_I(int bitvec)
631 for (i = 0, count = 0; i < 32; i++)
632 if (bitvec & (1 << i)) count++;
636 /*-----------------------------------------------------------------*/
638 GetNumBits(int* bitvecs, int maxNum)
642 /* get the number of bits that have been set to 1 */
643 for (i = 0, count = 0; i < maxNum; i++)
644 count += GetNumBits_I(bitvecs[i]);
648 /*-----------------------------------------------------------------*/
650 /* Logging & Trace support */
652 /* buffer threshold : when the size hits this value, it flushes its content
654 #define LOG_BYTES_THRESHOLD (32*1024)
656 /* this flag indicates that it preserves the base file name for the current
657 one, and changes its name when it actually closes it off */
658 #define CHANGE_FILE_NAME_ON_SAVE 0x01
660 /* size of the actual log buffer */
661 #define LOG_BYTES_MAX (2*LOG_BYTES_THRESHOLD)
663 /* log/trace book keeping information */
664 typedef struct ExtendedLog {
665 char buffer[LOG_BYTES_MAX]; /* 64 KB */
666 int bytes; /* number of bytes written */
667 int filesize; /* number of bytes written into this file so far */
668 int fd; /* file descriptor */
669 char* sig; /* base file name */
670 int flags; /* flags */
672 } ExtendedLog, *PExtendedLog;
674 /* maximum single file size */
675 int maxSingleLogSize = 100 * (1024*1024);
678 GetNextLogFileName(char* file, int size, const char* sig)
680 #define COMPRESS_EXT1 ".bz2"
681 #define COMPRESS_EXT2 ".gz"
687 cur_t = timeex(NULL);
688 cur_tm = *gmtime(&cur_t);
691 /* check if .bz2 exists */
692 snprintf(file, size, "%s.%04d%02d%02d_%03d%s",
693 sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday,
696 if (access(file, F_OK) == 0) {
701 /* check if .gz exists */
702 snprintf(file, size, "%s.%04d%02d%02d_%03d%s",
703 sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday,
704 idx++, COMPRESS_EXT2);
706 if (access(file, F_OK) == 0)
709 /* strip the extension and see if the (uncompressed) file exists */
710 file[strlen(file) - sizeof(COMPRESS_EXT2) + 1] = 0;
711 if (access(file, F_OK) != 0)
715 /* calculate & return the next day */
716 cur_t -= (3600*cur_tm.tm_hour + 60*cur_tm.tm_min + cur_tm.tm_sec);
717 return cur_t + 60*60*24;
723 /*-----------------------------------------------------------------*/
725 FlushBuffer(HANDLE file)
727 /* write data into the file */
728 ExtendedLog* pel = (ExtendedLog *)file;
731 if (pel == NULL || pel->fd < 0)
734 if ((written = write(pel->fd, pel->buffer, pel->bytes)) > 0) {
735 pel->bytes -= written;
737 /* if it hasn't written all data, then we need to move memory */
739 memmove(pel->buffer, pel->buffer + written, pel->bytes);
740 pel->buffer[pel->bytes] = 0;
741 pel->filesize += written;
744 /* if the filesize is bigger than maxSignleLogSize, then close it off */
745 if (pel->filesize >= maxSingleLogSize)
749 /*-----------------------------------------------------------------*/
751 CreateLogFHandle(const char* signature, int change_file_name_on_save)
755 if ((pel = (ExtendedLog *)xcalloc(1, sizeof(ExtendedLog))) == NULL)
756 NiceExit(-1, "failed");
759 pel->sig = xstrdup(signature);
760 if (pel->sig == NULL)
761 NiceExit(-1, "signature copying failed");
762 if (change_file_name_on_save)
763 pel->flags |= CHANGE_FILE_NAME_ON_SAVE;
769 /*-----------------------------------------------------------------*/
771 OpenLogF(HANDLE file)
774 ExtendedLog* pel = (ExtendedLog *)file;
782 pel->nextday = GetNextLogFileName(filename, sizeof(filename), pel->sig);
784 /* change the file name only at saving time
785 use pel->sig as current file name */
786 if (pel->flags & CHANGE_FILE_NAME_ON_SAVE) {
787 if (access(pel->sig, F_OK) == 0)
788 rename(pel->sig, filename);
789 strcpy(filename, pel->sig);
793 if ((pel->fd = open(filename, O_RDWR | O_CREAT | O_APPEND,
794 S_IRUSR | S_IWUSR)) == -1) {
795 char errMessage[2048];
796 sprintf(errMessage, "couldn't open the extended log file %s\n", filename);
797 NiceExit(-1, errMessage);
800 /* reset the file size */
805 /*-----------------------------------------------------------------*/
807 WriteLog(HANDLE file, const char* data, int size, int forceFlush)
809 ExtendedLog* pel = (ExtendedLog *)file;
811 /* if an error might occur, then stop here */
812 if (pel == NULL || pel->fd < 0 || size > LOG_BYTES_MAX)
816 /* flush the previous data, if this data would overfill the buffer */
817 if (pel->bytes + size >= LOG_BYTES_MAX)
820 /* write into the buffer */
821 memcpy(pel->buffer + pel->bytes, data, size);
825 /* need to flush ? */
826 if ((forceFlush && (pel->bytes > 0)) || (pel->bytes >= LOG_BYTES_THRESHOLD))
832 /*-----------------------------------------------------------------*/
834 DailyReopenLogF(HANDLE file)
836 /* check if current file is a day long,
837 opens another for today's file */
838 ExtendedLog* pel = (ExtendedLog *)file;
840 if (pel && (timeex(NULL) >= pel->nextday)) {
841 FlushLogF(file); /* flush */
842 OpenLogF(file); /* close previous one & reopen today's */
845 /*-----------------------------------------------------------------*/
847 HandleToFileNo(HANDLE file)
849 ExtendedLog* pel = (ExtendedLog *)file;
851 return (pel != NULL) ? pel->fd : -1;
853 /*-----------------------------------------------------------------*/
854 #define TO_HOST_L(x) x = ntohl(x);
855 #define TO_NETWORK_L(x) x = htonl(x);
857 HtoN_LocalQueryInfo(LocalQueryInfo *p)
859 TO_NETWORK_L(p->lqi_size);
860 TO_NETWORK_L(p->lqi_id);
861 TO_NETWORK_L(p->lqi_cache);
863 /*----------------------------------------------------------------*/
865 NtoH_LocalQueryResult(LocalQueryResult* p)
868 TO_HOST_L(p->lq_ttl);
870 /*----------------------------------------------------------------*/
872 CoDNSGetHostByNameSync(const char *name, struct in_addr *res)
877 LocalQueryResult result;
879 /* create a connection to CoDNS */
880 if (fd == -1 && (fd = MakeLoopbackConnection(CODNS_PORT, 0)) < 0) {
881 TRACE("CoDNS connection try has failed!\n");
883 /* try to resolve names using gethostbyname() */
886 if ((hp = gethostbyname(name)) == NULL)
887 NiceExit(-1, "gethostbyname also failed!");
889 *res = *(struct in_addr *)hp->h_addr;
894 memset(&query, 0, sizeof(query));
895 size = strlen(name) + 1;
896 query.lq_info.lqi_size = size; /* length of name */
897 query.lq_info.lqi_cache = TRUE;
898 strcpy(query.lq_name, name);
899 size += sizeof(query.lq_info) + sizeof(query.lq_zero);
902 HtoN_LocalQueryInfo(&query.lq_info);
903 if (write(fd, &query, size) != size) {
911 size = read(fd, &result, sizeof(result));
912 } while (size == -1 && errno == EINTR);
914 NtoH_LocalQueryResult(&result);
916 if (size != sizeof(result))
919 *res = result.lq_address[0];
922 /*-----------------------------------------------------------------*/
924 FeedbackDelay(struct timeval *start, float minSec, float maxSec)
926 float diff = DiffTimeVal(start, NULL);
931 usleep((unsigned int)(diff * 1e6));
933 /*-----------------------------------------------------------------*/
934 static int niceExitLogFD = -1;
936 NiceExitOpenLog(char *logName)
938 /* log file to record exit reasons */
939 if (niceExitLogFD >= 0)
940 close(niceExitLogFD);
942 niceExitLogFD = open(logName, O_WRONLY | O_APPEND | O_CREAT,
944 return((niceExitLogFD >= 0) ? SUCCESS : FAILURE);
946 /*-----------------------------------------------------------------*/
948 NiceExitBack(int val, char *reason, char *file, int line)
951 time_t currT = time(NULL);
953 sprintf(buf, "[%s, %d] %.24s %s\n", file, line, ctime(&currT), reason);
956 FlushLogF(hdebugLog);
959 fprintf(stderr, "%s", buf);
961 if (niceExitLogFD >= 0)
962 write(niceExitLogFD, buf, strlen(buf));
965 /*-----------------------------------------------------------------*/
972 while (*buf != '\0') {
973 int isSpace = isspace(*buf);
974 if (wasSpace && (!isSpace))
981 /*-----------------------------------------------------------------*/
983 ReadFile(const char *filename)
986 return(ReadFileEx(filename, &dummySize));
988 /*-----------------------------------------------------------------*/
990 ReadFileEx(const char *filename, int *size)
992 /* allocate a buffer, read the file into it and
994 char *content = NULL;
999 if (access(filename, R_OK) < 0
1000 || stat(filename, &buf) < 0
1001 || (fd = open(filename, O_RDONLY)) < 0) {
1002 TRACE("opening captcha file %s failed\n", filename);
1006 if ((content = (char *)xmalloc(buf.st_size + 1)) == NULL) {
1007 TRACE("memory alloc failed\n");
1011 if (read(fd, content, buf.st_size) != buf.st_size) {
1012 TRACE("opening captcha test file failed\n");
1016 content[buf.st_size] = 0;
1017 *size = buf.st_size;
1020 /*-----------------------------------------------------------------*/
1022 MmapFile(const char *filename, int *size)
1024 /* allocate a buffer, read the file into it and
1025 return the buffer */
1026 char *content = NULL;
1031 if (access(filename, R_OK) < 0
1032 || stat(filename, &buf) < 0
1033 || (fd = open(filename, O_RDONLY)) < 0) {
1034 TRACE("opening captcha file %s failed\n", filename);
1038 content = (char *)mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1040 if (content != MAP_FAILED) {
1041 *size = buf.st_size;
1046 /*-----------------------------------------------------------------*/
1048 HashString(const char *name, unsigned int hash, int endOnQuery,
1051 /* if endOnQuery, we stop the hashing when we hit the question mark.
1052 if skipLastIfDot, we check the last component of the path to see
1053 if it includes a dot, and it so, we skip it. if both are specified,
1054 we first try the query, and if that exists, we don't trim the path */
1064 if (endOnQuery && (temp = strchr(name, '?')) != NULL)
1066 else if (skipLastIfDot) {
1067 /* first, find last component by searching backward */
1068 if ((temp = strrchr(name, '/')) != NULL) {
1069 /* now search forward for the dot */
1070 if (strchr(temp, '.') != NULL)
1075 for (i = 0; i < len; i ++)
1076 hash += (_rotl(hash, 19) + name[i]);
1080 /*-------------------------------------------------------------*/
1082 CalcAgentHash(const char* agent)
1084 char p[strlen(agent)+1];
1090 /* we remove all spaces */
1091 for (i = 0; *agent; agent++) {
1092 if (isspace(*agent))
1098 return HashString(p, 0, FALSE, FALSE);
1100 /*-----------------------------------------------------------------*/
1102 ZapSpacesAndZeros(char *src)
1104 /* get rid of excess spaces between words, and remove any trailing
1105 (post-decimal) zeros from floating point numbers */
1106 static char smallLine[4096];
1107 char *dst = smallLine;
1109 int addSpace = FALSE;
1111 while (src != NULL &&
1112 (word = GetWord((const unsigned char*)src, 0)) != NULL) {
1114 int isDotNumber = TRUE;
1116 src = GetField((const unsigned char*)src, 1); /* advance to next */
1118 /* check to make sure it has exactly one decimal point */
1119 if ((temp = strchr(word, '.')) != NULL &&
1120 (temp = strchr(temp+1, '.')) == NULL) {
1121 /* make sure it's all digits or the dot */
1122 for (temp = word; *temp != '\0'; temp++) {
1123 if (!(isdigit(*temp) || *temp == '.')) {
1124 isDotNumber = FALSE;
1130 isDotNumber = FALSE;
1133 /* strip off any trailing zeros and possibly the decimal point */
1134 int len = strlen(word) - 1;
1136 while (word[len] == '0') {
1140 if (word[len] == '.')
1145 sprintf(dst, " %s", word);
1147 sprintf(dst, "%s", word);
1156 /*-----------------------------------------------------------------*/