/* * static char *rcsid_porting_c = * "$Id: porting.c,v 1.3 2006/07/20 03:57:03 root Exp $"; */ /* CrossFire, A Multiplayer game for X-windows Copyright (C) 2002 Mark Wedel & Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at crossfire-devel@real-time.com */ /* This file contains various functions that are not really unique for * crossfire, but rather provides what should be standard functions * for systems that do not have them. In this way, most of the * nasty system dependent stuff is contained here, with the program * calling these functions. */ #ifdef WIN32 /* ---win32 exclude/include headers */ #include "process.h" #define pid_t int /* we include it non global, because there is a redefinition in python.h */ #else #include #include #include #include #include /* Need to pull in the HAVE_... values somehow */ /* win32 reminder: always put this in a ifndef win32 block */ #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include /* Has to be after above includes so we don't redefine some values */ #include "global.h" static unsigned int curtmp = 0; /***************************************************************************** * File related functions ****************************************************************************/ /* * A replacement for the tempnam() function since it's not defined * at some unix variants. */ char *tempnam_local(const char *dir, const char *pfx) { char *name; pid_t pid=getpid(); /* HURD does not have a hard limit, but we do */ #ifndef MAXPATHLEN #define MAXPATHLEN 4096 #endif if (!(name = (char *) malloc(MAXPATHLEN))) return(NULL); if (!pfx) pfx = "cftmp."; /* This is a pretty simple method - put the pid as a hex digit and * just keep incrementing the last digit. Check to see if the file * already exists - if so, we'll just keep looking - eventually we should * find one that is free. */ if (dir!=NULL) { do { #ifdef HAVE_SNPRINTF (void)snprintf(name, MAXPATHLEN, "%s/%s%hx.%d", dir, pfx, pid, curtmp); #else (void)sprintf(name,"%s/%s%hx%d", dir, pfx, pid, curtmp); #endif curtmp++; } while (access(name, F_OK)!=-1); return(name); } return(NULL); } /* This function removes everything in the directory. */ void remove_directory(const char *path) { DIR *dirp; char buf[MAX_BUF]; struct stat statbuf; int status; if ((dirp=opendir(path))!=NULL) { struct dirent *de; for (de=readdir(dirp); de; de = readdir(dirp)) { /* Don't remove '.' or '..' In theory we should do a better * check for .., but the directories we are removing are fairly * limited and should not have dot files in them. */ if (de->d_name[0] == '.') continue; /* Linux actually has a type field in the dirent structure, * but that is not portable - stat should be portable */ status=stat(de->d_name, &statbuf); if ((status!=-1) && (S_ISDIR(statbuf.st_mode))) { sprintf(buf,"%s/%s", path, de->d_name); remove_directory(buf); continue; } sprintf(buf,"%s/%s", path, de->d_name); if (unlink(buf)) { LOG(llevError,"Unable to remove directory %s\n", path); } } closedir(dirp); } if (rmdir(path)) { LOG(llevError,"Unable to remove directory %s\n", path); } } #if defined(sgi) #include #include #include #define popen fixed_popen FILE *popen_local(const char *command, const char *type) { int fd[2]; int pd; FILE *ret; if (!strcmp(type,"r")) { pd=STDOUT_FILENO; } else if (!strcmp(type,"w")) { pd=STDIN_FILENO; } else { return NULL; } if (pipe(fd)!=-1) { switch (fork()) { case -1: close(fd[0]); close(fd[1]); break; case 0: close(fd[0]); if ((fd[1]==pd)||(dup2(fd[1],pd)==pd)) { if (fd[1]!=pd) { close(fd[1]); } execl("/bin/sh","sh","-c",command,NULL); close(pd); } exit(1); break; default: close(fd[1]); if (ret=fdopen(fd[0],type)) { return ret; } close(fd[0]); break; } } return NULL; } #endif /* defined(sgi) */ /***************************************************************************** * String related function ****************************************************************************/ /* * A replacement of strdup(), since it's not defined at some * unix variants. */ char *strdup_local(const char *str) { char *c=(char *)malloc(sizeof(char)*(strlen(str)+1)); strcpy(c,str); return c; } #define DIGIT(x) (isdigit(x) ? (x) - '0' : \ islower (x) ? (x) + 10 - 'a' : (x) + 10 - 'A') #define MBASE ('z' - 'a' + 1 + 10) /* * A replacement of strtol() since it's not defined at * many unix systems. */ long strtol_local(str, ptr, base) register char *str; char **ptr; register int base; { register long val; register int c; int xx, neg = 0; if (ptr != (char **) 0) *ptr = str; /* in case no number is formed */ if (base < 0 || base > MBASE) return (0); /* base is invalid */ if (!isalnum (c = *str)) { while (isspace (c)) c = *++str; switch (c) { case '-': neg++; case '+': c = *++str; } } if (base == 0) { if (c != '0') base = 10; else { if (str[1] == 'x' || str[1] == 'X') base = 16; else base = 8; } } /* ** For any base > 10, the digits incrementally following ** 9 are assumed to be "abc...z" or "ABC...Z" */ if (!isalnum (c) || (xx = DIGIT (c)) >= base) return 0; /* no number formed */ if (base == 16 && c == '0' && isxdigit (str[2]) && (str[1] == 'x' || str[1] == 'X')) c = *(str += 2); /* skip over leading "0x" or "0X" */ for (val = -DIGIT (c); isalnum (c = *++str) && (xx = DIGIT (c)) < base;) /* accumulate neg avoids surprises near MAXLONG */ val = base * val - xx; if (ptr != (char **) 0) *ptr = str; return (neg ? val : -val); } /* This seems to be lacking on some system */ #if !defined(HAVE_STRNCASECMP) int strncasecmp(const char *s1, const char *s2, int n) { register int c1, c2; while (*s1 && *s2 && n) { c1 = tolower(*s1); c2 = tolower(*s2); if (c1 != c2) return (c1 - c2); s1++; s2++; n--; } if (!n) return(0); return (int) (*s1 - *s2); } #endif #if !defined(HAVE_STRCASECMP) int strcasecmp(const char *s1, const char*s2) { register int c1, c2; while (*s1 && *s2) { c1 = tolower(*s1); c2 = tolower(*s2); if (c1 != c2) return (c1 - c2); s1++; s2++; } if (*s1=='\0' && *s2=='\0') return 0; return (int) (*s1 - *s2); } #endif char *strcasestr_local(const char *s, const char *find) { char c, sc; size_t len; if ((c = *find++) != 0) { c = tolower(c); len = strlen(find); do { do { if ((sc = *s++) == 0) return NULL; } while (tolower(sc) != c); } while (strncasecmp(s, find, len) != 0); s--; } return (char *)s; } #if !defined(HAVE_SNPRINTF) int snprintf(char *dest, int max, const char *format, ...) { va_list var; int ret; va_start(var, format); ret = vsprintf(dest, format, var); va_end(var); if (ret > max) abort(); return ret; } #endif /* This takes an err number and returns a string with a description of * the error. */ char *strerror_local(int errnum) { #if defined(HAVE_STRERROR) return(strerror(errnum)); #else return("strerror_local not implemented"); #endif } /* * Based on (n+1)^2 = n^2 + 2n + 1 * given that 1^2 = 1, then * 2^2 = 1 + (2 + 1) = 1 + 3 = 4 * 3^2 = 4 + (4 + 1) = 4 + 5 = 1 + 3 + 5 = 9 * 4^2 = 9 + (6 + 1) = 9 + 7 = 1 + 3 + 5 + 7 = 16 * ... * In other words, a square number can be express as the sum of the * series n^2 = 1 + 3 + ... + (2n-1) */ int isqrt(n) int n; { int result, sum, prev; result = 0; prev = sum = 1; while (sum <= n) { prev += 2; sum += prev; ++result; } return result; } /* * returns a char-pointer to a static array, in which a representation * of the decimal number given will be stored. */ char *ltostr10(signed long n) { static char buf[12]; /* maximum size is n=-2 billion, i.e. 11 characters+1 character for the trailing nul character */ snprintf(buf, sizeof(buf), "%ld", n); return buf; } char *doubletostr10(double v){ static char tbuf[200]; sprintf(tbuf,"%f",v); return tbuf; } /* * A fast routine which appends the name and decimal number specified * to the given buffer. * Could be faster, though, if the strcat()s at the end could be changed * into alternate strcat which returned a pointer to the _end_, not the * start! * * Hey good news, it IS faster now, according to changes in get_ob_diff * Completly redone prototype and made define in loader.l. See changes there. * Didn't touch those for speed reason (don't use them anymore) . * Tchize */ void save_long(char *buf, char *name, long n) { char buf2[MAX_BUF]; strcpy(buf2,name); strcat(buf2," "); strcat(buf2,ltostr10(n)); strcat(buf2,"\n"); strcat(buf,buf2); } void save_long_long(char *buf, char *name, sint64 n) { char buf2[MAX_BUF]; #ifndef WIN32 sprintf(buf2,"%s %lld\n", name, n); #else sprintf(buf2,"%s %I64d\n", name, n); #endif strcat(buf,buf2); } /* This is a list of the suffix, uncompress and compress functions. Thus, * if you have some other compress program you want to use, the only thing * that needs to be done is to extended this. * The first entry must be NULL - this is what is used for non * compressed files. */ char *uncomp[NROF_COMPRESS_METHODS][3] = { {NULL, NULL, NULL}, {".Z", UNCOMPRESS, COMPRESS}, {".gz", GUNZIP, GZIP}, {".bz2", BUNZIP, BZIP} }; /** * Open and possibly uncompress a file. * * @param ext the extension if the file is compressed. * * @param uncompressor the command to uncompress the file if the file is * compressed. * * @param name the base file name without compression extension * * @param flag only used for compressed files: if set, uncompress and open the * file; if unset, uncompress the file via pipe * * @param *compressed set to zero if the file was uncompressed */ static FILE *open_and_uncompress_file(const char *ext, const char *uncompressor, const char *name, int flag, int *compressed) { struct stat st; char buf[MAX_BUF]; char buf2[MAX_BUF]; int ret; if (ext == NULL) { ext = ""; } if (strlen(name)+strlen(ext) >= sizeof(buf)) { errno = ENAMETOOLONG; /* File name too long */ return NULL; } sprintf(buf, "%s%s", name, ext); if (stat(buf, &st) != 0) { return NULL; } if (!S_ISREG(st.st_mode)) { errno = EISDIR; /* Not a regular file */ return NULL; } if (uncompressor == NULL) { /* open without uncompression */ return fopen(buf, "r"); } /* The file name buf (and its substring name) is passed as an argument to a * shell command, therefore check for characters that could confuse the * shell. */ if (strpbrk(buf, "'\\\r\n") != NULL) { errno = ENOENT; /* Pretend the file does not exist */ return NULL; } if (!flag) { /* uncompress via pipe */ if (strlen(uncompressor)+4+strlen(buf)+1 >= sizeof(buf2)) { errno = ENAMETOOLONG; /* File name too long */ return NULL; } sprintf(buf2, "%s < '%s'", uncompressor, buf); return popen(buf2, "r"); } /* remove compression from file, then open file */ if (stat(name, &st) == 0 && !S_ISREG(st.st_mode)) { errno = EISDIR; return NULL; } if (strlen(uncompressor)+4+strlen(buf)+5+strlen(name)+1 >= sizeof(buf2)) { errno = ENAMETOOLONG; /* File name too long */ return NULL; } sprintf(buf2, "%s < '%s' > '%s'", uncompressor, buf, name); ret = system(buf2); if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { LOG(llevError, "system(%s) returned %d\n", buf2, ret); errno = ENOENT; return NULL; } unlink(buf); /* Delete the original */ *compressed = 0; /* Change to "uncompressed file" */ chmod(name, st.st_mode); /* Copy access mode from compressed file */ return fopen(name, "r"); } /** * open_and_uncompress() first searches for the original filename. If it exist, * then it opens it and returns the file-pointer. * * If not, it does two things depending on the flag. If the flag is set, it * tries to create the original file by appending a compression suffix to name * and uncompressing it. If the flag is not set, it creates a pipe that is used * for reading the file (NOTE - you can not use fseek on pipes). * * The compressed pointer is set to nonzero if the file is compressed (and * thus, fp is actually a pipe.) It returns 0 if it is a normal file. */ FILE *open_and_uncompress(const char *name, int flag, int *compressed) { size_t i; FILE *fp; for (i = 0; i < NROF_COMPRESS_METHODS; i++) { *compressed = i; fp = open_and_uncompress_file(uncomp[i][0], uncomp[i][1], name, flag, compressed); if (fp != NULL) { return fp; } } errno = ENOENT; return NULL; } /* * See open_and_uncompress(). */ void close_and_delete(FILE *fp, int compressed) { if (compressed) pclose(fp); else fclose(fp); } /* * If any directories in the given path doesn't exist, they are created. */ void make_path_to_file (char *filename) { char buf[MAX_BUF], *cp = buf; struct stat statbuf; if (!filename || !*filename) return; strcpy (buf, filename); while ((cp = strchr (cp + 1, (int) '/'))) { *cp = '\0'; if (stat(buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) { if (mkdir (buf, SAVE_DIR_MODE)) { LOG(llevError, "Cannot mkdir %s: %s\n", buf, strerror_local(errno)); return; } } *cp = '/'; } }