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