Fix buffer overrun during loadding opening names
[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 struct t_tree {
651   struct t_tree *left, *right;
652   char name;                    /* Not just 1 char - space for whole name */
653 };                              /* is allocated.  Maybe a little cheesy? */
654
655 struct t_dirs {
656   struct t_dirs *left, *right;
657   time_t mtime;                 /* dir's modification time */
658   struct t_tree *files;
659   char name;                    /* ditto */
660 };
661
662 static char **t_buffer = NULL; /* pointer to next space in return buffer */
663 static int t_buffersize = 0;    /* size of return buffer */
664
665 /* fill t_buffer with anything matching "want*" in file tree t_tree */
666 static void t_sft(const char *want, struct t_tree *t)
667 {
668   if (t) {
669     int cmp = strncmp(want, &t->name, strlen(want));
670     if (cmp <= 0)               /* if want <= this one, look left */
671       t_sft(want, t->left);
672     if (t_buffersize && (cmp == 0)) {   /* if a match, add it to buffer */
673       t_buffersize--;
674       *t_buffer++ = &(t->name);
675     }
676     if (cmp >= 0)               /* if want >= this one, look right */
677       t_sft(want, t->right);
678   }
679 }
680
681 /* delete file tree t_tree */
682 static void t_cft(struct t_tree **t)
683 {
684   if (*t) {
685     t_cft(&(*t)->left);
686     t_cft(&(*t)->right);
687     free(*t);
688     *t = NULL;
689   }
690 }
691
692 /* make file tree for dir d */
693 static void t_mft(struct t_dirs *d)
694 {
695   DIR *dirp;
696   struct dirent *dp;
697   struct t_tree **t;
698
699   if ((dirp = opendir(&(d->name))) == NULL) {
700     d_printf( "CHESSD:mft() couldn't opendir.\n");
701   } else {
702     while ((dp = readdir(dirp))) {
703       t = &d->files;
704       if (dp->d_name[0] != '.') {       /* skip anything starting with . */
705         while (*t) {
706           if (strcmp(dp->d_name, &(*t)->name) < 0) {
707             t = &(*t)->left;
708           } else {
709             t = &(*t)->right;
710           }
711         }
712         *t = malloc(sizeof(struct t_tree) + strlen(dp->d_name));
713         (*t)->right = (*t)->left = NULL;
714         strcpy(&(*t)->name, dp->d_name);
715       }
716     }
717     closedir(dirp);
718   }
719 }
720
721 int search_directory(const char *dir, const char *filter, char **buffer, int buffersize)
722 {
723 /* dir = directory to search
724    filter = what to search for
725    buffer = where to store pointers to matches
726    buffersize = how many pointers will fit inside buffer */
727
728   static struct t_dirs *ramdirs = NULL;
729   struct t_dirs **i;
730   int cmp;
731   static char nullify = '\0';
732   struct stat statbuf;
733
734   t_buffer = buffer;
735   t_buffersize = buffersize;
736
737   if (!stat(dir, &statbuf)) {
738     if (filter == NULL)         /* NULL becomes pointer to null string */
739       filter = &nullify;
740     i = &ramdirs;
741     while (*i) {                        /* find dir in dir tree */
742       cmp = strcmp(dir, &(*i)->name);
743       if (cmp == 0)
744         break;
745       else if (cmp < 0)
746         i = &(*i)->left;
747       else
748         i = &(*i)->right;
749     }
750     if (!*i) {                          /* if dir isn't in dir tree, add him */
751       *i = malloc(sizeof(struct t_dirs) + strlen(dir));
752       (*i)->left = (*i)->right = NULL;
753       (*i)->files = NULL;
754       strcpy(&(*i)->name, dir);
755     }
756     if ((*i)->files) {                  /* delete any obsolete file tree */
757       if ((*i)->mtime != statbuf.st_mtime) {
758         t_cft(&(*i)->files);
759       }
760     }
761     if ((*i)->files == NULL) {          /* if no file tree for him, make one */
762       (*i)->mtime = statbuf.st_mtime;
763       t_mft(*i);
764     }
765     t_sft(filter, (*i)->files);         /* finally, search for matches */
766   }
767   return (buffersize - t_buffersize);
768 }
769
770 int display_directory(int p, char **buffer, int count)
771 /* buffer contains 'count' string pointers */
772 {
773         struct player *pp = &player_globals.parray[p];
774         int i;
775         multicol *m = multicol_start(count);
776
777         for (i = 0; (i < count); i++)
778                 multicol_store(m, *buffer++);
779         multicol_pprint(m, p, pp->d_width, 1);
780         multicol_end(m);
781         return (i);
782 }
783
784 void CenterText (char *target, const char *text, int width, int pad)
785 {
786   int left, len;
787   char *format;
788
789   len = strlen(text);
790   left = (width + 1 - len) / 2;    /* leading blank space. */
791
792   if (pad)
793     asprintf (&format, "%%%ds%%-%ds", left, width-left);  /* e.g. "%5s%-10s" */
794   else
795     asprintf (&format, "%%%ds%%s", left);    /* e.g. "%5s%s" */
796   sprintf (target, format, "", text);
797
798   free(format);
799
800   return;
801 }
802
803 /* block a signal */
804 void block_signal(int signum)
805 {
806         sigset_t set;
807         sigemptyset(&set);
808         sigaddset(&set,signum);
809         sigprocmask(SIG_BLOCK,&set,NULL);
810 }
811
812 /* unblock a signal */
813 void unblock_signal(int signum)
814 {
815         sigset_t set;
816         sigemptyset(&set);
817         sigaddset(&set,signum);
818         sigprocmask(SIG_UNBLOCK,&set,NULL);
819 }
820
821
822 int file_copy(const char *src, const char *dest)
823 {
824   int fd1, fd2, n;
825   char buf[1024];
826
827   fd1 = open(src, O_RDONLY);
828   if (fd1 == -1) return -1;
829
830   unlink(dest);
831   fd2 = open(dest, O_WRONLY|O_CREAT|O_EXCL, 0644);
832   if (fd2 == -1) {
833     close(fd1);
834     return -1;
835   }
836
837   while ((n = read(fd1, buf, sizeof(buf))) > 0) {
838     if (write(fd2, buf, n) != n) {
839       close(fd2);
840       close(fd1);
841       unlink(dest);
842       return -1;
843     }
844   }
845
846   close(fd1);
847   close(fd2);
848   return 0;
849 }
850
851
852 #ifndef HAVE_DPRINTF
853  int dprintf(int fd, const char *format, ...)
854 {
855         va_list ap;
856         char *ptr = NULL;
857         int ret = 0;
858
859         va_start(ap, format);
860         vasprintf(&ptr, format, ap);
861         va_end(ap);
862
863         if (ptr) {
864                 ret = write(fd, ptr, strlen(ptr));
865                 free(ptr);
866         }
867
868         return ret;
869 }
870 #endif
871
872
873 #ifndef HAVE_STRNLEN_X
874 /*
875   some platforms don't have strnlen
876 */
877 size_t strnlen(const char *s, size_t n)
878 {
879         int i;
880         for (i=0; s[i] && i<n; i++) /* noop */ ;
881         return i;
882 }
883 #endif
884
885 /* day as a string */
886 const char *strday(time_t *t)
887 {
888         struct tm *stm = localtime(t);
889         static char tstr[100];
890
891         strftime(tstr, sizeof(tstr), "%a %b %e", stm);
892         return tstr;
893 }
894
895
896 static const char *strtime(struct tm * stm, short gmt)
897 {
898         static char tstr[100];
899
900         if (gmt)
901                 strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M GMT %Y", stm);
902         else
903                 strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M %Z %Y", stm);
904         return (tstr);
905 }
906
907 const char *strltime(time_t *clock)
908 {
909         struct tm *stm = localtime(clock);
910         return strtime(stm, 0);
911 }
912
913 const char *strgtime(time_t *clock)
914 {
915         struct tm *stm = gmtime(clock);
916         return strtime(stm, 1);
917 }
918
919 /* useful debug utility */
920 void d_printf(const char *fmt, ...)
921 {
922         va_list ap;
923         time_t t = time(NULL);
924         fprintf(stderr,"%s ", strltime(&t));
925         va_start(ap, fmt);
926         vfprintf(stderr,fmt,ap);
927         va_end(ap);
928 }
929
930
931 /* append something to admin.log */
932 static void admin_printf(const char *fmt, ...)
933 {
934         int fd;
935         va_list ap;
936         time_t t = time(NULL);
937
938         fd = open("admin.log", O_APPEND | O_CREAT | O_RDWR, 0600);
939         if (fd == -1) {
940                 d_printf("Failed to open admin.log - %s\n", strerror(errno));
941                 return;
942         }
943
944         dprintf(fd,"%s ", strltime(&t));
945         va_start(ap, fmt);
946         vdprintf(fd,fmt,ap);
947         va_end(ap);
948
949         close(fd);
950 }
951
952 /*
953   log an admin command 
954 */
955 void admin_log(struct player *pp, const char *command, param_list params)
956 {
957         char *s;
958         char *s2;
959         int i;
960
961         asprintf(&s, "%s: %s", pp->login, command);
962
963         if (!s) return;
964         for (i=0; params[i].type != TYPE_NULL; i++) {
965                 switch (params[i].type) {
966                 case TYPE_INT:
967                         asprintf(&s2, "%s %d", s, params[i].val.integer);
968                         break;
969                 case TYPE_STRING:
970                         asprintf(&s2, "%s %s", s, params[i].val.string);
971                         break;
972                 case TYPE_WORD:
973                         asprintf(&s2, "%s %s", s, params[i].val.word);
974                         break;
975                 }
976
977                 free(s);
978                 s = s2;
979                 if (!s) return;
980         }
981
982         admin_printf("%s\n", s);
983         free(s);
984 }
985
986 /*
987   save a lump of data into a file. 
988   return 0 on success
989 */
990 int file_save(const char *fname, void *data, size_t length)
991 {
992         int fd;
993         unlink(fname);
994         fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
995         if (fd == -1) {
996                 return -1;
997         }
998         if (write(fd, data, length) != length) {
999                 close(fd);
1000                 unlink(fname);
1001                 return -1;
1002         }
1003         close(fd);
1004         return 0;
1005 }
1006
1007 /*
1008   load a file into memory from a fd.
1009 */
1010 char *fd_load(int fd, size_t *size)
1011 {
1012         struct stat sbuf;
1013         char *p;
1014
1015         if (fstat(fd, &sbuf) != 0) return NULL;
1016
1017         p = (char *)malloc(sbuf.st_size+1);
1018         if (!p) return NULL;
1019
1020         if (pread(fd, p, sbuf.st_size, 0) != sbuf.st_size) {
1021                 free(p);
1022                 return NULL;
1023         }
1024         p[sbuf.st_size] = 0;
1025
1026         if (size) *size = sbuf.st_size;
1027
1028         return p;
1029 }
1030
1031 /*
1032   load a file into memory
1033 */
1034 char *file_load(const char *fname, size_t *size)
1035 {
1036         int fd;
1037         char *p;
1038
1039         if (!fname || !*fname) return NULL;
1040         
1041         fd = open(fname,O_RDONLY);
1042         if (fd == -1) return NULL;
1043
1044         p = fd_load(fd, size);
1045         close(fd);
1046
1047         return p;
1048 }
1049
1050 /*
1051   this is like fgets() but operates on a file descriptor
1052 */
1053 char *fd_gets(char *line, size_t maxsize, int fd)
1054 {
1055         char c;
1056         int n = 0;
1057         while (n < (maxsize-1) && read(fd, &c, 1) == 1) {
1058                 line[n++] = c;
1059                 if (c == '\n') break;
1060         }
1061         line[n] = 0;
1062         return n?line:NULL;
1063 }
1064
1065
1066 /*
1067   like fopen() but uses printf style arguments for the file name
1068 */
1069 FILE *fopen_p(const char *fmt, const char *mode, ...)
1070 {
1071         char *s = NULL;
1072         FILE *f;
1073         va_list ap;
1074         va_start(ap, mode);
1075         vasprintf(&s, fmt, ap);
1076         va_end(ap);
1077         if (!s) return NULL;
1078         if (strstr(s, "..")) {
1079                 d_printf("Invalid filename [%s]\n", s);
1080                 free(s);
1081                 return NULL;
1082         }
1083         f = fopen(s, mode);
1084         free(s);
1085         return f;
1086 }
1087
1088 /*
1089   like fopen() but doesn't allow opening of filenames containing '..'
1090 */
1091 FILE *fopen_s(const char *fname, const char *mode)
1092 {
1093         return fopen_p("%s", mode, fname);
1094 }
1095
1096
1097 #ifndef HAVE_STRLCPY
1098 /**
1099  * Like strncpy but does not 0 fill the buffer and always null 
1100  * terminates.
1101  *
1102  * @param bufsize is the size of the destination buffer.
1103  *
1104  * @return index of the terminating byte.
1105  **/
1106  size_t strlcpy(char *d, const char *s, size_t bufsize)
1107 {
1108         size_t len = strlen(s);
1109         size_t ret = len;
1110         if (bufsize <= 0) return 0;
1111         if (len >= bufsize) len = bufsize-1;
1112         memcpy(d, s, len);
1113         d[len] = 0;
1114         return ret;
1115 }
1116 #endif
1117
1118
1119 /*
1120   set an integer value in a TDB
1121 */
1122 int tdb_set_int(TDB_CONTEXT *tdb, const char *name, int value)
1123 {
1124         TDB_DATA key, data;
1125         int ret;
1126
1127         key.dptr = strdup(name);
1128         key.dsize = strlen(name)+1;
1129
1130         data.dptr = (char *)&value;
1131         data.dsize = sizeof(value);
1132
1133         ret = tdb_store(tdb, key, data, TDB_REPLACE);
1134         free(key.dptr);
1135         return ret;
1136 }
1137
1138 /*
1139   get an integer value from a TDB. Return def_value if its not there
1140 */
1141 int tdb_get_int(TDB_CONTEXT *tdb, const char *name, int def_value)
1142 {
1143         TDB_DATA key, data;
1144         int ret;
1145
1146         key.dptr = strdup(name);
1147         key.dsize = strlen(name)+1;
1148
1149         data = tdb_fetch(tdb, key);
1150         free(key.dptr);
1151
1152         if (!data.dptr) {
1153                 return def_value;
1154         }
1155
1156         ret = *(int *)data.dptr;
1157         free(data.dptr);
1158
1159         return ret;
1160 }
1161
1162
1163 /*
1164   set a string value in a TDB
1165 */
1166 int tdb_set_string(TDB_CONTEXT *tdb, const char *name, const char *value)
1167 {
1168         TDB_DATA key, data;
1169         int ret;
1170
1171         key.dptr = strdup(name);
1172         key.dsize = strlen(name)+1;
1173
1174         data.dptr = strdup(value);
1175         data.dsize = strlen(value)+1;
1176
1177         ret = tdb_store(tdb, key, data, TDB_REPLACE);
1178         free(key.dptr); 
1179         free(data.dptr);
1180         return ret;
1181 }
1182
1183 /*
1184   get a string value from a TDB. Caller frees.
1185 */
1186 const char *tdb_get_string(TDB_CONTEXT *tdb, const char *name)
1187 {
1188         TDB_DATA key, data;
1189
1190         key.dptr = strdup(name);
1191         key.dsize = strlen(name)+1;
1192
1193         data = tdb_fetch(tdb, key);
1194         free(key.dptr); 
1195         if (!data.dptr) {
1196                 return NULL;
1197         }
1198         return data.dptr;
1199 }
1200
1201 /*
1202   delete a value from a TDB by string
1203 */
1204 int tdb_delete_string(TDB_CONTEXT *tdb, const char *name)
1205 {
1206         TDB_DATA key;
1207         int ret;
1208
1209         key.dptr = strdup(name);
1210         key.dsize = strlen(name)+1;
1211
1212         ret = tdb_delete(tdb, key);
1213         free(key.dptr);
1214         return ret;
1215 }