Crossfire Server, Branch 1.12  R12190
porting.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_porting_c =
00003  *   "$Id: porting.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00038 #include <string.h>
00039 #ifdef WIN32 /* ---win32 exclude/include headers */
00040 #include "process.h"
00041 #define pid_t int  /* we include it non global, because there is a redefinition in python.h */
00042 #else
00043 #include <ctype.h>
00044 #include <errno.h>
00045 #include <sys/stat.h>
00046 #include <sys/wait.h>
00047 
00048 #include <sys/param.h>
00049 #include <stdio.h>
00050 
00051 /* Need to pull in the HAVE_... values somehow */
00052 /* win32 reminder: always put this in a ifndef win32 block */
00053 #include <autoconf.h>
00054 #endif
00055 
00056 #ifdef HAVE_STDLIB_H
00057 #include <stdlib.h>
00058 #endif
00059 
00060 #ifdef HAVE_UNISTD_H
00061 #include <unistd.h>
00062 #endif
00063 
00064 #include <stdarg.h>
00065 /* Has to be after above includes so we don't redefine some values */
00066 #include "global.h"
00067 
00069 static unsigned int curtmp = 0;
00070 
00071 /*****************************************************************************
00072  * File related functions
00073  ****************************************************************************/
00074 
00087 char *tempnam_local(const char *dir, const char *pfx) {
00088     char *name;
00089     pid_t pid = getpid();
00090 
00091 /* HURD does not have a hard limit, but we do */
00092 #ifndef MAXPATHLEN
00093 #define MAXPATHLEN 4096
00094 #endif
00095 
00096     if (!pfx)
00097         pfx = "cftmp.";
00098 
00099     /* This is a pretty simple method - put the pid as a hex digit and
00100      * just keep incrementing the last digit.  Check to see if the file
00101      * already exists - if so, we'll just keep looking - eventually we should
00102      * find one that is free.
00103      */
00104     if (dir != NULL) {
00105         if (!(name = (char *)malloc(MAXPATHLEN)))
00106             return(NULL);
00107         do {
00108 #ifdef HAVE_SNPRINTF
00109             (void)snprintf(name, MAXPATHLEN, "%s/%s%hx.%u", dir, pfx, pid, curtmp);
00110 #else
00111             (void)sprintf(name, "%s/%s%hx%u", dir, pfx, pid, curtmp);
00112 #endif
00113             curtmp++;
00114         } while (access(name, F_OK) != -1);
00115         return(name);
00116     }
00117     return(NULL);
00118 }
00119 
00143 FILE *tempnam_secure(const char *dir, const char *pfx, char **filename) {
00144     char *tempname = NULL;
00145     int fd;
00146     int i;
00147     FILE *file = NULL;
00148 #define MAXTMPRETRY 10
00149 
00150     /* Limit number of retries to MAXRETRY */
00151     for (i = 0; i < MAXTMPRETRY; i++) {
00152         tempname = tempnam_local(dir, pfx);
00153         /* tempnam_local only fails for really bad stuff, so lets bail out right
00154          * away then.
00155          */
00156         if (!tempname)
00157             return NULL;
00158 
00159         fd = open(tempname, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
00160         if (fd != -1)
00161             break;
00162         if (errno == EEXIST)
00163             LOG(llevError, "Created file detected in tempnam_secure. Someone hoping for a race condition?\n");
00164         free(tempname);
00165     }
00166     /* Check that we successfully got an fd. */
00167     if (fd == -1)
00168         return NULL;
00169 
00170     file = fdopen(fd, "w+");
00171     if (!file) {
00172         LOG(llevError, "fdopen() failed in tempnam_secure()!\n");
00173         free(tempname);
00174         return NULL;
00175     }
00176 
00177     *filename = tempname;
00178     return file;
00179 }
00180 
00192 void remove_directory(const char *path) {
00193     DIR *dirp;
00194     char buf[MAX_BUF];
00195     struct stat statbuf;
00196     int status;
00197 
00198     if ((dirp = opendir(path)) != NULL) {
00199         struct dirent *de;
00200 
00201         for (de = readdir(dirp); de; de = readdir(dirp)) {
00202             /* Don't remove '.' or '..'  In  theory we should do a better
00203              * check for .., but the directories we are removing are fairly
00204              * limited and should not have dot files in them.
00205              */
00206             if (de->d_name[0] == '.')
00207                 continue;
00208 
00209             /* Linux actually has a type field in the dirent structure,
00210              * but that is not portable - stat should be portable
00211              */
00212             status = stat(de->d_name, &statbuf);
00213             if ((status != -1) && (S_ISDIR(statbuf.st_mode))) {
00214                 snprintf(buf, sizeof(buf), "%s/%s", path, de->d_name);
00215                 remove_directory(buf);
00216                 continue;
00217             }
00218             snprintf(buf, sizeof(buf), "%s/%s", path, de->d_name);
00219             if (unlink(buf)) {
00220                 LOG(llevError, "Unable to remove %s\n", path);
00221             }
00222         }
00223         closedir(dirp);
00224     }
00225     if (rmdir(path)) {
00226         LOG(llevError, "Unable to remove directory %s\n", path);
00227     }
00228 }
00229 
00230 #if defined(sgi)
00231 
00232 #include <stdio.h>
00233 #include <stdlib.h>
00234 #include <string.h>
00235 
00236 #define popen fixed_popen
00237 
00253 FILE *popen_local(const char *command, const char *type) {
00254     int fd[2];
00255     int pd;
00256     FILE *ret;
00257     if (!strcmp(type, "r")) {
00258         pd = STDOUT_FILENO;
00259     } else if (!strcmp(type, "w")) {
00260         pd = STDIN_FILENO;
00261     } else {
00262         return NULL;
00263     }
00264     if (pipe(fd) != -1) {
00265         switch (fork()) {
00266         case -1:
00267             close(fd[0]);
00268             close(fd[1]);
00269             break;
00270 
00271         case 0:
00272             close(fd[0]);
00273             if ((fd[1] == pd) || (dup2(fd[1], pd) == pd)) {
00274                 if (fd[1] != pd) {
00275                     close(fd[1]);
00276                 }
00277                 execl("/bin/sh", "sh", "-c", command, NULL);
00278                 close(pd);
00279             }
00280             exit(1);
00281             break;
00282 
00283         default:
00284             close(fd[1]);
00285             if (ret = fdopen(fd[0], type)) {
00286                 return ret;
00287             }
00288             close(fd[0]);
00289             break;
00290         }
00291     }
00292     return NULL;
00293 }
00294 
00295 #endif /* defined(sgi) */
00296 
00297 /*****************************************************************************
00298  * String related function
00299  ****************************************************************************/
00300 
00310 char *strdup_local(const char *str) {
00311     char *c = (char *)malloc(strlen(str)+1);
00312     if (c != NULL)
00313         strcpy(c, str);
00314     return c;
00315 }
00316 
00318 #define DIGIT(x) (isdigit(x) ? (x)-'0' : \
00319 islower(x) ? (x)+10-'a' : (x)+10-'A')
00320 #define MBASE ('z'-'a'+1+10)
00321 
00322 #if !defined(HAVE_STRTOL)
00323 
00339 long strtol(register char *str, char **ptr, register int base) {
00340     register long val;
00341     register int c;
00342     int xx, neg = 0;
00343 
00344     if (ptr != (char **)0)
00345         *ptr = str;         /* in case no number is formed */
00346     if (base < 0 || base > MBASE)
00347         return (0);         /* base is invalid */
00348     if (!isalnum(c = *str)) {
00349         while (isspace(c))
00350             c = *++str;
00351         switch (c) {
00352         case '-':
00353             neg++;
00354         case '+':
00355             c = *++str;
00356         }
00357     }
00358     if (base == 0) {
00359         if (c != '0')
00360             base = 10;
00361         else {
00362             if (str[1] == 'x' || str[1] == 'X')
00363                 base = 16;
00364             else
00365                 base = 8;
00366         }
00367     }
00368     /*
00369      * For any base > 10, the digits incrementally following
00370      * 9 are assumed to be "abc...z" or "ABC...Z"
00371      */
00372     if (!isalnum(c) || (xx = DIGIT(c)) >= base)
00373         return 0;           /* no number formed */
00374     if (base == 16 && c == '0' && isxdigit(str[2]) && (str[1] == 'x' || str[1] == 'X'))
00375         c = *(str += 2);    /* skip over leading "0x" or "0X" */
00376     for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < base; )
00377         /* accumulate neg avoids surprises near
00378         MAXLONG */
00379         val = base*val-xx;
00380     if (ptr != (char **)0)
00381         *ptr = str;
00382     return (neg ? val : -val);
00383 }
00384 #endif
00385 
00401 #if !defined(HAVE_STRNCASECMP)
00402 int strncasecmp(const char *s1, const char *s2, int n) {
00403     register int c1, c2;
00404 
00405     while (*s1 && *s2 && n) {
00406         c1 = tolower(*s1);
00407         c2 = tolower(*s2);
00408         if (c1 != c2)
00409             return (c1-c2);
00410         s1++;
00411         s2++;
00412         n--;
00413     }
00414     if (!n)
00415         return (0);
00416     return (int)(*s1-*s2);
00417 }
00418 #endif
00419 
00420 #if !defined(HAVE_STRCASECMP)
00421 
00434 int strcasecmp(const char *s1, const char *s2) {
00435     register int c1, c2;
00436 
00437     while (*s1 && *s2) {
00438         c1 = tolower(*s1);
00439         c2 = tolower(*s2);
00440         if (c1 != c2)
00441             return (c1-c2);
00442         s1++;
00443         s2++;
00444     }
00445     if (*s1 == '\0' && *s2 == '\0')
00446         return 0;
00447     return (int)(*s1-*s2);
00448 }
00449 #endif
00450 
00461 const char *strcasestr_local(const char *s, const char *find) {
00462     char c, sc;
00463     size_t len;
00464 
00465     if ((c = *find++) != 0) {
00466         c = tolower(c);
00467         len = strlen(find);
00468         do {
00469             do {
00470                 if ((sc = *s++) == 0)
00471                     return NULL;
00472             } while (tolower(sc) != c);
00473         } while (strncasecmp(s, find, len) != 0);
00474         s--;
00475     }
00476     return s;
00477 }
00478 
00479 #if !defined(HAVE_SNPRINTF)
00480 
00498 int snprintf(char *dest, int max, const char *format, ...) {
00499     va_list var;
00500     int ret;
00501 
00502     va_start(var, format);
00503     ret = vsprintf(dest, format, var);
00504     va_end(var);
00505     if (ret > max)
00506         abort();
00507 
00508     return ret;
00509 }
00510 #endif
00511 
00525 char *strerror_local(int errnum, char *buf, size_t size) {
00526 #if defined(HAVE_STRERROR_R)
00527     /* Then what flavour of strerror_r... */
00528 # if defined(STRERROR_R_CHAR_P)
00529     char *bbuf;
00530 
00531     buf[0] = 0;
00532     bbuf = (char *)strerror_r(errnum, buf, size);
00533     if ((buf[0] == 0) && (bbuf != NULL))
00534         strncpy(buf, bbuf, size);
00535 # else
00536     if (strerror_r(errnum, buf, size) != 0) {
00537         /* EINVAL and ERANGE are possible errors from this strerror_r */
00538         if (errno == ERANGE) {
00539             strncat(buf, "Too small buffer.", size);
00540         } else if (errno == EINVAL) {
00541             strncat(buf, "Error number invalid.", size);
00542         }
00543     }
00544 # endif /* STRERROR_R_CHAR_P */
00545 
00546 #else /* HAVE_STRERROR_R */
00547 
00548 # if defined(HAVE_STRERROR)
00549     snprintf(buf, size, "%s", strerror(errnum));
00550 # else
00551 #  error If this is C89 the compiler should have strerror!;
00552 # endif
00553 #endif /* HAVE_STRERROR_R */
00554     return buf;
00555 }
00556 
00573 int isqrt(int n) {
00574     int result, sum, prev;
00575 
00576     result = 0;
00577     prev = sum = 1;
00578     while (sum <= n) {
00579         prev += 2;
00580         sum += prev;
00581         ++result;
00582     }
00583     return result;
00584 }
00585 
00593 const char *uncomp[NROF_COMPRESS_METHODS][3] = {
00594     { NULL, NULL, NULL },
00595     { ".Z", UNCOMPRESS, COMPRESS },
00596     { ".gz", GUNZIP, GZIP },
00597     { ".bz2", BUNZIP, BZIP }
00598 };
00599 
00621 static FILE *open_and_uncompress_file(const char *ext, const char *uncompressor, const char *name, int flag, int *compressed) {
00622     struct stat st;
00623     char buf[MAX_BUF];
00624     char buf2[MAX_BUF];
00625     int ret;
00626 
00627     if (ext == NULL) {
00628         ext = "";
00629     }
00630 
00631     if (strlen(name)+strlen(ext) >= sizeof(buf)) {
00632         errno = ENAMETOOLONG; /* File name too long */
00633         return NULL;
00634     }
00635     snprintf(buf, sizeof(buf), "%s%s", name, ext);
00636 
00637     if (stat(buf, &st) != 0) {
00638         return NULL;
00639     }
00640 
00641     if (!S_ISREG(st.st_mode)) {
00642         errno = EISDIR;         /* Not a regular file */
00643         return NULL;
00644     }
00645 
00646     if (uncompressor == NULL) {
00647         /* open without uncompression */
00648         return fopen(buf, "rb");
00649     }
00650 
00651     /* The file name buf (and its substring name) is passed as an argument to a
00652      * shell command, therefore check for characters that could confuse the
00653      * shell.
00654      */
00655     if (strpbrk(buf, "'\\\r\n") != NULL) {
00656         errno = ENOENT;         /* Pretend the file does not exist */
00657         return NULL;
00658     }
00659 
00660     if (!flag) {
00661         /* uncompress via pipe */
00662 
00663         if (strlen(uncompressor)+4+strlen(buf)+1 >= sizeof(buf2)) {
00664             errno = ENAMETOOLONG;       /* File name too long */
00665             return NULL;
00666         }
00667         snprintf(buf2, sizeof(buf2), "%s < '%s'", uncompressor, buf);
00668         return popen(buf2, "r");
00669     }
00670 
00671     /* remove compression from file, then open file */
00672 
00673     if (stat(name, &st) == 0 && !S_ISREG(st.st_mode)) {
00674         errno = EISDIR;
00675         return NULL;
00676     }
00677 
00678     if (strlen(uncompressor)+4+strlen(buf)+5+strlen(name)+1 >= sizeof(buf2)) {
00679         errno = ENAMETOOLONG;   /* File name too long */
00680         return NULL;
00681     }
00682     snprintf(buf2, sizeof(buf2), "%s < '%s' > '%s'", uncompressor, buf, name);
00683 
00684     ret = system(buf2);
00685     if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
00686         LOG(llevError, "system(%s) returned %d\n", buf2, ret);
00687         errno = ENOENT;
00688         return NULL;
00689     }
00690 
00691     unlink(buf);                /* Delete the original */
00692     *compressed = 0;            /* Change to "uncompressed file" */
00693     chmod(name, st.st_mode);    /* Copy access mode from compressed file */
00694 
00695     return fopen(name, "rb");
00696 }
00697 
00724 FILE *open_and_uncompress(const char *name, int flag, int *compressed) {
00725     size_t i;
00726     FILE *fp;
00727 
00728     for (i = 0; i < NROF_COMPRESS_METHODS; i++) {
00729         *compressed = i;
00730         fp = open_and_uncompress_file(uncomp[i][0], uncomp[i][1], name, flag, compressed);
00731         if (fp != NULL) {
00732             return fp;
00733         }
00734     }
00735 
00736     errno = ENOENT;
00737     return NULL;
00738 }
00739 
00748 void close_and_delete(FILE *fp, int compressed) {
00749     if (compressed)
00750         pclose(fp);
00751     else
00752         fclose(fp);
00753 }
00754 
00764 void make_path_to_file(const char *filename) {
00765     char buf[MAX_BUF], *cp = buf;
00766     struct stat statbuf;
00767 
00768     if (!filename || !*filename)
00769         return;
00770 
00771     strcpy(buf, filename);
00772     LOG(llevDebug, "make_path_tofile %s...\n", filename);
00773     while ((cp = strchr(cp+1, (int)'/'))) {
00774         *cp = '\0';
00775         if (stat(buf, &statbuf) || !S_ISDIR(statbuf.st_mode)) {
00776             LOG(llevDebug, "Was not dir: %s\n", buf);
00777             if (mkdir(buf, SAVE_DIR_MODE)) {
00778                 char err[MAX_BUF];
00779 
00780                 LOG(llevError, "Cannot mkdir %s: %s\n", buf, strerror_local(errno, err, sizeof(err)));
00781                 return;
00782             }
00783         }
00784         *cp = '/';
00785     }
00786 }