Ameliorate name-length hack
[capablanca.git] / lasker-2.2.3 / src / utils.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 #include "includes.h"
23
24 int safestring(char *str);
25
26 char *eatword(char *str)
27 {
28   while (*str && !isspace(*str))
29     str++;
30   return str;
31 }
32
33 char *eatwhite(char *str)
34 {
35   while (*str && isspace(*str))
36     str++;
37   return str;
38 }
39
40 char *eattailwhite(char *str)
41 {
42   int len;
43   if (str == NULL)
44     return NULL;
45
46   len = strlen(str);
47   while (len > 0 && isspace(str[len - 1]))
48     len--;
49   str[len] = '\0';
50   return (str);
51 }
52
53 char *nextword(char *str)
54 {
55   return eatwhite(eatword(str));
56 }
57
58 int mail_string_to_address(char *addr, char *subj, char *str)
59 {
60   int fd;
61   char template[] = SPOOL_DIR;
62
63   fd = mkstemp(template);
64   if (fd == -1) {
65     d_printf("Failed to create spool file\n");
66     return -1;
67   }
68
69   dprintf(fd, "To: %s\nSubject: %s\n\n%s\n", addr, subj, str);
70   close(fd);
71
72   return 0;
73 }
74
75 int mail_string_to_user(int p, char *subj, char *str)
76 {
77         struct player *pp = &player_globals.parray[p];
78         if (pp->emailAddress &&
79             pp->emailAddress[0] &&
80             safestring(pp->emailAddress)) {
81                 return mail_string_to_address(pp->emailAddress, subj, str);
82         }
83         return -1;
84 }
85
86
87 /* Process a command for a user */
88 int pcommand(int p, char *comstr,...)
89 {
90         static int recurse = 0;
91         struct player *pp = &player_globals.parray[p];
92         char *tmp;
93         int retval;
94         int current_socket = pp->socket;
95         va_list ap;
96         
97         if(recurse > 10) return COM_BADPARAMETERS; /* [HGM] guard against infinite recursion through bad aliasing */
98
99         va_start(ap, comstr);
100         vasprintf(&tmp, comstr, ap);
101         va_end(ap);
102         recurse++;
103         retval = process_input(current_socket, tmp);
104         recurse--;
105         free(tmp);
106         if (retval == COM_LOGOUT) {
107                 process_disconnection(current_socket);
108                 net_close_connection(current_socket);
109         }
110         return retval;
111 }
112
113 static int vpprintf(int p, int do_formatting, char *format,va_list ap)
114 {
115         struct player *pp = &player_globals.parray[p];
116         char *tmp = NULL;
117         int retval;
118
119         retval = vasprintf(&tmp, format, ap);
120         if (retval != 0) {
121                 net_send_string(pp->socket, 
122                                 tmp, 
123                                 do_formatting, 
124                                 pp->d_width + 1);
125         }
126         if (tmp) {
127                 free(tmp);
128         }
129
130         return retval;
131 }
132
133 int pprintf(int p, char *format,...)
134 {
135         int retval;
136         va_list ap;
137         va_start(ap, format);
138         retval = vpprintf(p, 1, format, ap);
139         va_end(ap);
140         return retval;
141 }
142
143 static void pprintf_dohightlight(int p)
144 {
145         struct player *pp = &player_globals.parray[p];
146         if (pp->highlight & 0x01)
147                 pprintf(p, "\033[7m");
148         if (pp->highlight & 0x02)
149                 pprintf(p, "\033[1m");
150         if (pp->highlight & 0x04)
151                 pprintf(p, "\033[4m");
152         if (pp->highlight & 0x08)
153                 pprintf(p, "\033[2m");
154 }
155
156 int pprintf_highlight(int p, char *format,...)
157 {
158         struct player *pp = &player_globals.parray[p];
159         int retval;
160         va_list ap;
161
162         pprintf_dohightlight(p);
163         va_start(ap, format);
164         retval = vpprintf(p, 1, format, ap);
165         va_end(ap);
166         if (pp->highlight)
167                 pprintf(p, "\033[0m");
168         return retval;
169 }
170
171 static void sprintf_dohightlight(int p, char *s)
172 {
173         struct player *pp = &player_globals.parray[p];
174         if (pp->highlight & 0x01)
175                 strcat(s, "\033[7m");
176         if (pp->highlight & 0x02)
177                 strcat(s, "\033[1m");
178         if (pp->highlight & 0x04)
179                 strcat(s, "\033[4m");
180         if (pp->highlight & 0x08)
181                 strcat(s, "\033[2m");
182 }
183
184 /*
185   like pprintf() but with paging for long messages
186 */
187 int pprintf_more(int p, char *format,...)
188 {
189         struct player *pp = &player_globals.parray[p];
190         char *s = NULL;
191         va_list ap;
192         va_start(ap, format);
193         vasprintf(&s, format, ap);
194         va_end(ap);
195
196         FREE(pp->more_text);
197         pp->more_text = s;
198
199         return pmore_text(p);
200 }
201
202 int psprintf_highlight(int p, char *s, char *format,...)
203 {
204         struct player *pp = &player_globals.parray[p];
205         int retval;
206         va_list ap;
207         
208         va_start(ap, format);
209         if (pp->highlight) {
210                 sprintf_dohightlight(p, s);
211                 retval = vsprintf(s + strlen(s), format, ap);
212                 strcat(s, "\033[0m");
213         } else {
214                 retval = vsprintf(s, format, ap);
215         }
216         va_end(ap);
217         return retval;
218 }
219
220 int pprintf_prompt(int p, char *format,...)
221 {
222         int retval;
223         va_list ap;
224         va_start(ap, format);
225         retval = vpprintf(p, 1, format, ap);
226         va_end(ap);
227         send_prompt(p);
228         return retval;
229 }
230
231 /* send a prompt to p */
232 void send_prompt(int p) 
233 {
234         struct player *pp = &player_globals.parray[p];
235         const char *prompt = pp->prompt;
236         pprintf(p, "%s%s", 
237                 prompt,
238                 isspace(prompt[strlen(prompt)-1])?"":" ");
239 }
240
241 int pprintf_noformat(int p, char *format,...)
242 {
243         int retval;
244         va_list ap;
245         va_start(ap, format);
246         retval = vpprintf(p, 0, format, ap);
247         va_end(ap);
248         return retval;
249 }
250
251 void Bell (int p)
252 {
253         if (CheckPFlag(p, PFLAG_BELL))
254                 pprintf (p, "\a");
255         return;
256 }
257
258 int psend_raw_file(int p, char *dir, char *file)
259 {
260         struct player *pp = &player_globals.parray[p];
261         FILE *fp;
262         char tmp[MAX_LINE_SIZE];
263         int num;
264         
265         fp = fopen_p("%s/%s", "r", dir, file);
266         
267         if (!fp)
268                 return -1;
269         while ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp)) > 0) {
270                 tmp[num] = '\0';
271                 net_send_string(pp->socket, tmp, 1, pp->d_width + 1);
272         }
273         fclose(fp);
274         return 0;
275 }
276
277 /*
278   send a file a page at a time
279 */
280 int psend_file(int p, const char *dir, const char *file)
281 {
282         struct player *pp = &player_globals.parray[p];
283         char *fname;
284
285         if (strstr(file, "..")) {
286                 pprintf(p,"Trying to be tricky, are we?\n");
287                 return 0;
288         }
289
290         if (dir) {
291                 asprintf(&fname,"%s/%s",dir,file);
292         } else {
293                 fname = strdup(file);
294         }
295
296         FREE(pp->more_text);
297         pp->more_text = file_load(fname, NULL);
298         if (!pp->more_text) {
299                 return -1;
300         }
301         
302         free(fname);
303
304         return pmore_text(p);
305 }
306
307 /* 
308  * Marsalis added on 8/27/95 so that [next] does not
309  * appear in the logout process for those that have
310  * a short screen height.  (Fixed due to complaint 
311  * in Bug's messages).
312  */
313 int psend_logoutfile(int p, char *dir, char *file)
314 {
315         return psend_file(p, dir, file);
316 }
317
318 /* 
319    continue with text from a previous command
320 */
321 int pmore_text(int p)
322 {
323         struct player *pp = &player_globals.parray[p];
324         int lcount = pp->d_height - 1;
325
326         if (!pp->more_text) {
327                 pprintf(p, "There is no more.\n");
328                 return -1;
329         }
330
331         while (pp->more_text && lcount--) {
332                 char *s = strndup(pp->more_text, strcspn(pp->more_text, "\n")+1);
333                 int len = strlen(s);
334                 net_send_string(pp->socket, s, 1, pp->d_width + 1);
335                 s = strdup(pp->more_text+len);
336                 FREE(pp->more_text);
337                 if (s[0]) {
338                         pp->more_text = s;
339                 } else {
340                         free(s);
341                 }
342         }
343         
344         if (pp->more_text) {
345                 pprintf(p, "Type [next] to see next page.\n");
346         }
347
348         return 0;
349 }
350
351 char *stolower(char *str)
352 {
353         int i;
354         
355         if (!str)
356                 return NULL;
357         for (i = 0; str[i]; i++) {
358                 if (isupper(str[i])) {
359                         str[i] = tolower(str[i]);
360                 }
361         }
362         return str;
363 }
364
365 static int safechar(int c)
366 {
367         return (isprint(c) && !strchr(">!&*?/<|`$;", c));
368 }
369
370 int safestring(char *str)
371 {
372         int i;
373
374         if (!str)
375                 return 1;
376         for (i = 0; str[i]; i++) {
377                 if (!safechar(str[i]))
378                         return 0;
379         }
380         return 1;
381 }
382
383 int alphastring(char *str)
384 {
385         int i;
386
387         if (!str)
388                 return 1;
389         for (i = 0; str[i]; i++) {
390                 if (!isalpha(str[i])) {
391                         return 0;
392                 }
393         }
394         return 1;
395 }
396
397 int printablestring(const char *str)
398 {
399         int i;
400         
401         if (!str)
402                 return 1;
403         for (i = 0; str[i]; i++) {
404                 if ((!isprint(str[i])) && (str[i] != '\t') && (str[i] != '\n'))
405                         return 0;
406         }
407         return 1;
408 }
409
410 char *hms_desc(int t)
411 {
412 static char tstr[80];
413 int days, hours, mins, secs;
414
415     days  = (t / (60*60*24));
416     hours = ((t % (60*60*24)) / (60*60));
417     mins  = (((t % (60*60*24)) % (60*60)) / 60);
418     secs  = (((t % (60*60*24)) % (60*60)) % 60);
419     if ((days==0) && (hours==0) && (mins==0)) {
420       sprintf(tstr, "%d sec%s",
421                  secs, (secs==1) ? "" : "s");
422     } else if ((days==0) && (hours==0)) {
423 /*      sprintf(tstr, "%d min%s, %d sec%s",
424                  mins, (mins==1) ? "" : "s",
425                  secs, (secs==1) ? "" : "s");  */
426       sprintf(tstr, "%d min%s",
427                  mins, (mins==1) ? "" : "s");
428     } else if (days==0) {
429       sprintf(tstr, "%d hr%s, %d min%s, %d "
430                  "sec%s",
431                  hours, (hours==1) ? "" : "s",
432                  mins, (mins==1) ? "" : "s",
433                  secs, (secs==1) ? "" : "s");
434     } else {
435       sprintf(tstr, "%d day%s, %d hour%s, %d minute%s "
436                  "and %d second%s",
437                  days, (days==1) ? "" : "s",
438                  hours, (hours==1) ? "" : "s",
439                  mins, (mins==1) ? "" : "s",
440                  secs, (secs==1) ? "" : "s");
441     }
442     return tstr;
443 }
444
445 char *hms(int t, int showhour, int showseconds, int spaces)
446 {
447   static char tstr[20];
448   char tmp[10];
449   int h, m, s;
450
451   h = t / 3600;
452   t = t % 3600;
453   m = t / 60;
454   s = t % 60;
455   if (h || showhour) {
456     if (spaces)
457       sprintf(tstr, "%d : %02d", h, m);
458     else
459       sprintf(tstr, "%d:%02d", h, m);
460   } else {
461     sprintf(tstr, "%d", m);
462   }
463   if (showseconds) {
464     if (spaces)
465       sprintf(tmp, " : %02d", s);
466     else
467       sprintf(tmp, ":%02d", s);
468     strcat(tstr, tmp);
469   }
470   return tstr;
471 }
472
473 /* This is used only for relative timeing since it reports seconds since
474  * about 5:00pm on Feb 16, 1994
475  */
476 unsigned tenth_secs(void)
477 {
478   struct timeval tp;
479   struct timezone tzp;
480
481   gettimeofday(&tp, &tzp);
482 /* .1 seconds since 1970 almost fills a 32 bit int! So lets subtract off
483  * the time right now */
484   return ((tp.tv_sec - 331939277) * 10L) + (tp.tv_usec / 100000);
485 }
486
487 /* This is to translate tenths-secs time back into 1/1/70 time in full
488  * seconds, because vek didn't read utils.c when he programmed new ratings.
489    1 sec since 1970 fits into a 32 bit int OK.
490 */
491 int untenths(unsigned tenths)
492 {
493   return (tenths / 10 + 331939277 + 4*((1<<30) / 5) + 1);
494 }
495
496 char *tenth_str(unsigned t, int spaces)
497 {
498   return hms((t + 5) / 10, 0, 1, spaces);       /* Round it */
499 }
500
501 #define MAX_TRUNC_SIZE 100
502
503 /* Warning, if lines in the file are greater than 1024 bytes in length, this
504    won't work! */
505 /* nasty bug fixed by mann, 3-12-95 */
506 int truncate_file(char *file, int lines)
507 {
508   FILE *fp;
509   int bptr = 0, trunc = 0, i;
510   char tBuf[MAX_TRUNC_SIZE][MAX_LINE_SIZE];
511
512   if (lines > MAX_TRUNC_SIZE)
513     lines = MAX_TRUNC_SIZE;
514   fp = fopen_s(file, "r");
515   if (!fp)
516     return 1;
517   while (!feof(fp)) {
518     fgets(tBuf[bptr], MAX_LINE_SIZE, fp);
519     if (feof(fp))
520       break;
521     if (tBuf[bptr][strlen(tBuf[bptr]) - 1] != '\n') {   /* Line too long */
522       fclose(fp);
523       return -1;
524     }
525     bptr++;
526     if (bptr == lines) {
527       trunc = 1;
528       bptr = 0;
529     }
530   }
531   fclose(fp);
532   if (trunc) {
533     fp = fopen_s(file, "w");
534     for (i = 0; i < lines; i++) {
535       fputs(tBuf[bptr], fp);
536       bptr++;
537       if (bptr == lines) {
538         bptr = 0;
539       }
540     }
541     fclose(fp);
542   }
543   return 0;
544 }
545
546 /* Warning, if lines in the file are greater than 1024 bytes in length, this
547    won't work! */
548 int lines_file(char *file)
549 {
550   FILE *fp;
551   int lcount = 0;
552   char tmp[MAX_LINE_SIZE];
553
554   fp = fopen_s(file, "r");
555   if (!fp)
556     return 0;
557   while (!feof(fp)) {
558     if (fgets(tmp, MAX_LINE_SIZE, fp))
559       lcount++;
560   }
561   fclose(fp);
562   return lcount;
563 }
564
565 int file_has_pname(char *fname, char *plogin)
566 {
567   if (!strcmp(file_wplayer(fname), plogin))
568     return 1;
569   if (!strcmp(file_bplayer(fname), plogin))
570     return 1;
571   return 0;
572 }
573
574 char *file_wplayer(char *fname)
575 {
576   static char tmp[MAX_FILENAME_SIZE];
577   char *ptr;
578   strcpy(tmp, fname);
579   ptr = rindex(tmp, '-');
580   if (!ptr)
581     return "";
582   *ptr = '\0';
583   return tmp;
584 }
585
586 char *file_bplayer(char *fname)
587 {
588   char *ptr;
589
590   ptr = rindex(fname, '-');
591   if (!ptr)
592     return "";
593   return ptr + 1;
594 }
595
596 /*
597   make a human readable IP
598 */
599 char *dotQuad(struct in_addr a)
600 {
601         return inet_ntoa(a);
602 }
603
604 int file_exists(char *fname)
605 {
606   FILE *fp;
607
608   fp = fopen_s(fname, "r");
609   if (!fp)
610     return 0;
611   fclose(fp);
612   return 1;
613 }
614
615 char *ratstr(int rat)
616 {
617   static int on = 0;
618   static char tmp[20][10];
619
620   if (on == 20)
621     on = 0;
622   if (rat) {
623     sprintf(tmp[on], "%4d", rat);
624   } else {
625     sprintf(tmp[on], "----");
626
627   }
628   on++;
629   return tmp[on - 1];
630 }
631
632 char *ratstrii(int rat, int p)
633 {
634   static int on = 0;
635   static char tmp[20][10];
636
637   if (on == 20)
638     on = 0;
639   if (rat) {
640     sprintf(tmp[on], "%4d", rat);
641   } else if (CheckPFlag(p, PFLAG_REG)) {
642     sprintf(tmp[on], "----");
643   } else {
644     sprintf(tmp[on], "++++");
645   }
646   on++;
647   return tmp[on - 1];
648 }
649
650 #define OVERRUN 99
651
652 struct t_tree {
653   struct t_tree *left, *right;
654   char name[OVERRUN+1];         // [HGM] hack below caused crashing on some compilers
655 //  char name;                  /* Not just 1 char - space for whole name */
656 };                              /* is allocated.  Maybe a little cheesy? */
657
658 struct t_dirs {
659   struct t_dirs *left, *right;
660   time_t mtime;                 /* dir's modification time */
661   struct t_tree *files;
662   char name[OVERRUN+1];         // [HGM] ditto
663 //  char name;                  /* ditto */
664 };
665
666 static char **t_buffer = NULL; /* pointer to next space in return buffer */
667 static int t_buffersize = 0;    /* size of return buffer */
668
669 /* fill t_buffer with anything matching "want*" in file tree t_tree */
670 static void t_sft(const char *want, struct t_tree *t)
671 {
672   if (t) {
673     int cmp = strncmp(want, &t->name, strlen(want));
674     if (cmp <= 0)               /* if want <= this one, look left */
675       t_sft(want, t->left);
676     if (t_buffersize && (cmp == 0)) {   /* if a match, add it to buffer */
677       t_buffersize--;
678       *t_buffer++ = &(t->name);
679     }
680     if (cmp >= 0)               /* if want >= this one, look right */
681       t_sft(want, t->right);
682   }
683 }
684
685 /* delete file tree t_tree */
686 static void t_cft(struct t_tree **t)
687 {
688   if (*t) {
689     t_cft(&(*t)->left);
690     t_cft(&(*t)->right);
691     free(*t);
692     *t = NULL;
693   }
694 }
695
696 /* make file tree for dir d */
697 static void t_mft(struct t_dirs *d)
698 {
699   DIR *dirp;
700   struct dirent *dp;
701   struct t_tree **t;
702
703   if ((dirp = opendir(&(d->name))) == NULL) {
704     d_printf( "CHESSD:mft() couldn't opendir.\n");
705   } else {
706     while ((dp = readdir(dirp))) {
707       t = &d->files;
708       if (dp->d_name[0] != '.') {       /* skip anything starting with . */
709         while (*t) {
710           if (strcmp(dp->d_name, &(*t)->name) < 0) {
711             t = &(*t)->left;
712           } else {
713             t = &(*t)->right;
714           }
715         }
716         *t = malloc(sizeof(struct t_tree) - OVERRUN + strlen(dp->d_name));
717         (*t)->right = (*t)->left = NULL;
718         strcpy(&(*t)->name, dp->d_name);
719       }
720     }
721     closedir(dirp);
722   }
723 }
724
725 int search_directory(const char *dir, const char *filter, char **buffer, int buffersize)
726 {
727 /* dir = directory to search
728    filter = what to search for
729    buffer = where to store pointers to matches
730    buffersize = how many pointers will fit inside buffer */
731
732   static struct t_dirs *ramdirs = NULL;
733   struct t_dirs **i;
734   int cmp;
735   static char nullify = '\0';
736   struct stat statbuf;
737
738   t_buffer = buffer;
739   t_buffersize = buffersize;
740
741   if (!stat(dir, &statbuf)) {
742     if (filter == NULL)         /* NULL becomes pointer to null string */
743       filter = &nullify;
744     i = &ramdirs;
745     while (*i) {                        /* find dir in dir tree */
746       cmp = strcmp(dir, &(*i)->name);
747       if (cmp == 0)
748         break;
749       else if (cmp < 0)
750         i = &(*i)->left;
751       else
752         i = &(*i)->right;
753     }
754     if (!*i) {                          /* if dir isn't in dir tree, add him */
755       *i = malloc(sizeof(struct t_dirs) - OVERRUN + strlen(dir)); // [HGM] allocate just enough, even though struct promoised more
756       (*i)->left = (*i)->right = NULL;
757       (*i)->files = NULL;
758       strcpy(&(*i)->name, dir);
759     }
760     if ((*i)->files) {                  /* delete any obsolete file tree */
761       if ((*i)->mtime != statbuf.st_mtime) {
762         t_cft(&(*i)->files);
763       }
764     }
765     if ((*i)->files == NULL) {          /* if no file tree for him, make one */
766       (*i)->mtime = statbuf.st_mtime;
767       t_mft(*i);
768     }
769     t_sft(filter, (*i)->files);         /* finally, search for matches */
770   }
771   return (buffersize - t_buffersize);
772 }
773
774 int display_directory(int p, char **buffer, int count)
775 /* buffer contains 'count' string pointers */
776 {
777         struct player *pp = &player_globals.parray[p];
778         int i;
779         multicol *m = multicol_start(count);
780
781         for (i = 0; (i < count); i++)
782                 multicol_store(m, *buffer++);
783         multicol_pprint(m, p, pp->d_width, 1);
784         multicol_end(m);
785         return (i);
786 }
787
788 void CenterText (char *target, const char *text, int width, int pad)
789 {
790   int left, len;
791   char *format;
792
793   len = strlen(text);
794   left = (width + 1 - len) / 2;    /* leading blank space. */
795
796   if (pad)
797     asprintf (&format, "%%%ds%%-%ds", left, width-left);  /* e.g. "%5s%-10s" */
798   else
799     asprintf (&format, "%%%ds%%s", left);    /* e.g. "%5s%s" */
800   sprintf (target, format, "", text);
801
802   free(format);
803
804   return;
805 }
806
807 /* block a signal */
808 void block_signal(int signum)
809 {
810         sigset_t set;
811         sigemptyset(&set);
812         sigaddset(&set,signum);
813         sigprocmask(SIG_BLOCK,&set,NULL);
814 }
815
816 /* unblock a signal */
817 void unblock_signal(int signum)
818 {
819         sigset_t set;
820         sigemptyset(&set);
821         sigaddset(&set,signum);
822         sigprocmask(SIG_UNBLOCK,&set,NULL);
823 }
824
825
826 int file_copy(const char *src, const char *dest)
827 {
828   int fd1, fd2, n;
829   char buf[1024];
830
831   fd1 = open(src, O_RDONLY);
832   if (fd1 == -1) return -1;
833
834   unlink(dest);
835   fd2 = open(dest, O_WRONLY|O_CREAT|O_EXCL, 0644);
836   if (fd2 == -1) {
837     close(fd1);
838     return -1;
839   }
840
841   while ((n = read(fd1, buf, sizeof(buf))) > 0) {
842     if (write(fd2, buf, n) != n) {
843       close(fd2);
844       close(fd1);
845       unlink(dest);
846       return -1;
847     }
848   }
849
850   close(fd1);
851   close(fd2);
852   return 0;
853 }
854
855
856 #ifndef HAVE_DPRINTF
857  int dprintf(int fd, const char *format, ...)
858 {
859         va_list ap;
860         char *ptr = NULL;
861         int ret = 0;
862
863         va_start(ap, format);
864         vasprintf(&ptr, format, ap);
865         va_end(ap);
866
867         if (ptr) {
868                 ret = write(fd, ptr, strlen(ptr));
869                 free(ptr);
870         }
871
872         return ret;
873 }
874 #endif
875
876
877 #ifndef HAVE_STRNLEN_X
878 /*
879   some platforms don't have strnlen
880 */
881 size_t strnlen(const char *s, size_t n)
882 {
883         int i;
884         for (i=0; s[i] && i<n; i++) /* noop */ ;
885         return i;
886 }
887 #endif
888
889 /* day as a string */
890 const char *strday(time_t *t)
891 {
892         struct tm *stm = localtime(t);
893         static char tstr[100];
894
895         strftime(tstr, sizeof(tstr), "%a %b %e", stm);
896         return tstr;
897 }
898
899
900 static const char *strtime(struct tm * stm, short gmt)
901 {
902         static char tstr[100];
903
904         if (gmt)
905                 strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M GMT %Y", stm);
906         else
907                 strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M %Z %Y", stm);
908         return (tstr);
909 }
910
911 const char *strltime(time_t *clock)
912 {
913         struct tm *stm = localtime(clock);
914         return strtime(stm, 0);
915 }
916
917 const char *strgtime(time_t *clock)
918 {
919         struct tm *stm = gmtime(clock);
920         return strtime(stm, 1);
921 }
922
923 /* useful debug utility */
924 void d_printf(const char *fmt, ...)
925 {
926         va_list ap;
927         time_t t = time(NULL);
928         fprintf(stderr,"%s ", strltime(&t));
929         va_start(ap, fmt);
930         vfprintf(stderr,fmt,ap);
931         va_end(ap);
932 }
933
934
935 /* append something to admin.log */
936 static void admin_printf(const char *fmt, ...)
937 {
938         int fd;
939         va_list ap;
940         time_t t = time(NULL);
941
942         fd = open("admin.log", O_APPEND | O_CREAT | O_RDWR, 0600);
943         if (fd == -1) {
944                 d_printf("Failed to open admin.log - %s\n", strerror(errno));
945                 return;
946         }
947
948         dprintf(fd,"%s ", strltime(&t));
949         va_start(ap, fmt);
950         vdprintf(fd,fmt,ap);
951         va_end(ap);
952
953         close(fd);
954 }
955
956 /*
957   log an admin command 
958 */
959 void admin_log(struct player *pp, const char *command, param_list params)
960 {
961         char *s;
962         char *s2;
963         int i;
964
965         asprintf(&s, "%s: %s", pp->login, command);
966
967         if (!s) return;
968         for (i=0; params[i].type != TYPE_NULL; i++) {
969                 switch (params[i].type) {
970                 case TYPE_INT:
971                         asprintf(&s2, "%s %d", s, params[i].val.integer);
972                         break;
973                 case TYPE_STRING:
974                         asprintf(&s2, "%s %s", s, params[i].val.string);
975                         break;
976                 case TYPE_WORD:
977                         asprintf(&s2, "%s %s", s, params[i].val.word);
978                         break;
979                 }
980
981                 free(s);
982                 s = s2;
983                 if (!s) return;
984         }
985
986         admin_printf("%s\n", s);
987         free(s);
988 }
989
990 /*
991   save a lump of data into a file. 
992   return 0 on success
993 */
994 int file_save(const char *fname, void *data, size_t length)
995 {
996         int fd;
997         unlink(fname);
998         fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
999         if (fd == -1) {
1000                 return -1;
1001         }
1002         if (write(fd, data, length) != length) {
1003                 close(fd);
1004                 unlink(fname);
1005                 return -1;
1006         }
1007         close(fd);
1008         return 0;
1009 }
1010
1011 /*
1012   load a file into memory from a fd.
1013 */
1014 char *fd_load(int fd, size_t *size)
1015 {
1016         struct stat sbuf;
1017         char *p;
1018
1019         if (fstat(fd, &sbuf) != 0) return NULL;
1020
1021         p = (char *)malloc(sbuf.st_size+1);
1022         if (!p) return NULL;
1023
1024         if (pread(fd, p, sbuf.st_size, 0) != sbuf.st_size) {
1025                 free(p);
1026                 return NULL;
1027         }
1028         p[sbuf.st_size] = 0;
1029
1030         if (size) *size = sbuf.st_size;
1031
1032         return p;
1033 }
1034
1035 /*
1036   load a file into memory
1037 */
1038 char *file_load(const char *fname, size_t *size)
1039 {
1040         int fd;
1041         char *p;
1042
1043         if (!fname || !*fname) return NULL;
1044         
1045         fd = open(fname,O_RDONLY);
1046         if (fd == -1) return NULL;
1047
1048         p = fd_load(fd, size);
1049         close(fd);
1050
1051         return p;
1052 }
1053
1054 /*
1055   this is like fgets() but operates on a file descriptor
1056 */
1057 char *fd_gets(char *line, size_t maxsize, int fd)
1058 {
1059         char c;
1060         int n = 0;
1061         while (n < (maxsize-1) && read(fd, &c, 1) == 1) {
1062                 line[n++] = c;
1063                 if (c == '\n') break;
1064         }
1065         line[n] = 0;
1066         return n?line:NULL;
1067 }
1068
1069
1070 /*
1071   like fopen() but uses printf style arguments for the file name
1072 */
1073 FILE *fopen_p(const char *fmt, const char *mode, ...)
1074 {
1075         char *s = NULL;
1076         FILE *f;
1077         va_list ap;
1078         va_start(ap, mode);
1079         vasprintf(&s, fmt, ap);
1080         va_end(ap);
1081         if (!s) return NULL;
1082         if (strstr(s, "..")) {
1083                 d_printf("Invalid filename [%s]\n", s);
1084                 free(s);
1085                 return NULL;
1086         }
1087         f = fopen(s, mode);
1088         free(s);
1089         return f;
1090 }
1091
1092 /*
1093   like fopen() but doesn't allow opening of filenames containing '..'
1094 */
1095 FILE *fopen_s(const char *fname, const char *mode)
1096 {
1097         return fopen_p("%s", mode, fname);
1098 }
1099
1100
1101 #ifndef HAVE_STRLCPY
1102 /**
1103  * Like strncpy but does not 0 fill the buffer and always null 
1104  * terminates.
1105  *
1106  * @param bufsize is the size of the destination buffer.
1107  *
1108  * @return index of the terminating byte.
1109  **/
1110  size_t strlcpy(char *d, const char *s, size_t bufsize)
1111 {
1112         size_t len = strlen(s);
1113         size_t ret = len;
1114         if (bufsize <= 0) return 0;
1115         if (len >= bufsize) len = bufsize-1;
1116         memcpy(d, s, len);
1117         d[len] = 0;
1118         return ret;
1119 }
1120 #endif
1121
1122
1123 /*
1124   set an integer value in a TDB
1125 */
1126 int tdb_set_int(TDB_CONTEXT *tdb, const char *name, int value)
1127 {
1128         TDB_DATA key, data;
1129         int ret;
1130
1131         key.dptr = strdup(name);
1132         key.dsize = strlen(name)+1;
1133
1134         data.dptr = (char *)&value;
1135         data.dsize = sizeof(value);
1136
1137         ret = tdb_store(tdb, key, data, TDB_REPLACE);
1138         free(key.dptr);
1139         return ret;
1140 }
1141
1142 /*
1143   get an integer value from a TDB. Return def_value if its not there
1144 */
1145 int tdb_get_int(TDB_CONTEXT *tdb, const char *name, int def_value)
1146 {
1147         TDB_DATA key, data;
1148         int ret;
1149
1150         key.dptr = strdup(name);
1151         key.dsize = strlen(name)+1;
1152
1153         data = tdb_fetch(tdb, key);
1154         free(key.dptr);
1155
1156         if (!data.dptr) {
1157                 return def_value;
1158         }
1159
1160         ret = *(int *)data.dptr;
1161         free(data.dptr);
1162
1163         return ret;
1164 }
1165
1166
1167 /*
1168   set a string value in a TDB
1169 */
1170 int tdb_set_string(TDB_CONTEXT *tdb, const char *name, const char *value)
1171 {
1172         TDB_DATA key, data;
1173         int ret;
1174
1175         key.dptr = strdup(name);
1176         key.dsize = strlen(name)+1;
1177
1178         data.dptr = strdup(value);
1179         data.dsize = strlen(value)+1;
1180
1181         ret = tdb_store(tdb, key, data, TDB_REPLACE);
1182         free(key.dptr); 
1183         free(data.dptr);
1184         return ret;
1185 }
1186
1187 /*
1188   get a string value from a TDB. Caller frees.
1189 */
1190 const char *tdb_get_string(TDB_CONTEXT *tdb, const char *name)
1191 {
1192         TDB_DATA key, data;
1193
1194         key.dptr = strdup(name);
1195         key.dsize = strlen(name)+1;
1196
1197         data = tdb_fetch(tdb, key);
1198         free(key.dptr); 
1199         if (!data.dptr) {
1200                 return NULL;
1201         }
1202         return data.dptr;
1203 }
1204
1205 /*
1206   delete a value from a TDB by string
1207 */
1208 int tdb_delete_string(TDB_CONTEXT *tdb, const char *name)
1209 {
1210         TDB_DATA key;
1211         int ret;
1212
1213         key.dptr = strdup(name);
1214         key.dsize = strlen(name)+1;
1215
1216         ret = tdb_delete(tdb, key);
1217         free(key.dptr);
1218         return ret;
1219 }