/*********************************************************************** * libwsmake - Core functionality library of wsmake * * Copyright (C) 1999,2000,2001 Michael Brownlow * * * * 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. * * * * For questions and comments, please email the author at: * * mike@wsmake.org * ***********************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UTIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "wsUtil.h" using namespace std; static int __wsmake_level = 0; static int __wsmake_quiet = 0; int std::isWhiteSpace(string *line) { unsigned i; // A quick check the first character test if(((*line)[0] == '#') || ((*line)[0] == '\n') || ((*line)[0] == 0)) { DBGPRINT(("%s: C: iws? -%s", PACKAGE, line->c_str())); return 1; } // Is this line whitespace? (or a line with whitespace before the hash?) for(i=0;ilength();i++) { if(!isspace(((*line)[i]))) { if((*line)[i] != '#') { DBGPRINT(("%s: C:iws? +%s", PACKAGE, line->c_str())); return 0; } else { DBGPRINT(("%s: C:iws? -%s", PACKAGE, line->c_str())); return 1; } } } // If we can't figure out what this line is, let's toss it DBGPRINT(("%s: C:iws? -%s", PACKAGE, line->c_str())); return 1; } // Returns line number on success, 0 on fail int std::getNextConfigLine(ifstream *file, string *line) { static int lineNum = 0; string buff; string::const_iterator i; int loc; int inquotes; int linesRead; if((linesRead = getNextLine(file,&buff)) == 0) { return 0; } lineNum += linesRead; // Walk up to next valid line while(isWhiteSpace(&buff)) { if((linesRead = getNextLine(file,&buff)) == 0) { return 0; } lineNum += linesRead; } // Find the first comment (if any) and delete everything after it // if it is outside of doublequotes i = buff.begin(); loc = inquotes = 0; while( i != buff.end() ) { assert(*i != 0); if( *i == '"' ) { if(loc==0) { inquotes = !inquotes; } else { if(*(--i) != '\\' ) { inquotes = !inquotes; } i++; } } else { if( *i == '#' ) { if((loc!=0) && ( *(--i) != '\\' )) { if(!inquotes) { buff.erase(loc+1); buff.replace(loc,1,"\n"); break; } i++; } } } i++; loc++; } line->assign(buff); return lineNum; } int std::getNextBinaryLine(ifstream *file, char bline[MAXLINELEN]) { if(file->eof() == 1) { return 0; } else { memset(bline, '\0', MAXLINELEN); file->read(bline, MAXLINELEN); return file->gcount(); } } int std::getNextLine(ifstream *file, string *line) { int c; int i; int lines = 1; char buff[MAXLINELEN]; memset(buff, '\0', MAXLINELEN); i = 0; if(file->peek() == EOF) { line->assign(buff); return 0; } while((i < MAXLINELEN) && ((c = file->get()) != EOF)) { if(c == '\\') { if(file->peek() == '\n') { c = file->get(); lines++; } else { if(i < MAXLINELEN) { buff[i] = '\\'; buff[i+1] = '\0'; } else { buff[i] = '\0'; line->assign(buff); return lines; } i++; } } else { if(c == '\n') { if(file->peek() != EOF) { // We want to treat \nEOF as EOF buff[i] = '\n'; buff[i+1] = '\0'; } else { buff[i] = '\0'; } line->assign(buff); return lines; } if(i < MAXLINELEN) { buff[i] = c; buff[i+1] = '\0'; } else { buff[i] = '\0'; line->assign(buff); return lines; } i++; } } buff[MAXLINELEN-1] = '\0'; line->assign(buff); return lines; } string *std::chopQuotes(string *buff) { unsigned stepper = 0; // Step up to first doublequote while(((isspace((*buff)[stepper]) || (*buff)[stepper] == '"')) && (stepper < buff->length())) { buff->replace(stepper,1,""); } // Get rid of ending doublequotes while(stepper < buff->length()) { if((*buff)[stepper] == '"') { if((*buff)[stepper-1] != '\\') { buff->erase(stepper); } } stepper++; } // Turn \" into " stepper = buff->find("\\\""); while(stepper != string::npos) { buff->replace(stepper,2,"\""); stepper = buff->find("\\\""); } return buff; } string *std::chopBackQuotes(string *buff) { unsigned stepper = 0; // Step up to first backquote while(((isspace((*buff)[stepper]) || (*buff)[stepper] == '`')) && (stepper < buff->length())) { buff->replace(stepper,1,""); } // Get rid of ending doublequotes while(stepper < buff->length()) { if((*buff)[stepper] == '`') { if((*buff)[stepper-1] != '\\') { buff->erase(stepper); } } stepper++; } // Turn \` into ` stepper = buff->find("\\`"); while(stepper != string::npos) { buff->replace(stepper,2,"`"); stepper = buff->find("\\`"); } return buff; } int std::splitString(string *front, string *back, const string *buff) { // Find first group of characters separated by whitespace in buff // and assign it to front. Assign the rest of buff to back. unsigned index; unsigned frontindex; unsigned backindex; int inquotes; index = frontindex = backindex = 0; // Step up to first non-space value while((index < buff->length()) && (isspace((*buff)[index]))) { index++; } if(index == buff->length()) { return 0; // No value found } frontindex = index; inquotes = 0; // Step up to next space value that isn't in doublequotes while((index < buff->length()) && ((inquotes) || (!isspace((*buff)[index])))) { if((*buff)[index]=='"') { if(index!=0) { if((*buff)[index-1]!='\\') { inquotes = !inquotes; } } else { inquotes = !inquotes; } } index++; } front->assign(buff->substr(frontindex,index-frontindex)); // Step up to second non-space value (if any) while((index < buff->length()) && (isspace((*buff)[index]))) { index++; } backindex = index; // If we only find the front, set back to no value and return if(index == buff->length()) { back->assign(""); return 1; } // Step back to first non-space value from the end index = buff->length()-1; while((isspace((*buff)[index])) || ((*buff)[index] == '\n')) { index--; } back->assign(buff->substr(backindex,(index+1)-backindex)); return 1; } // Puts contents of line into dest and returns pointer to dest string *std::getStringFromChar(string *dest, const char *line) { dest = new string(line); return dest; } // Gets the last mod date of the given file and returns it time_t std::getFileLastMod(string file) { struct stat statbuf; int err; err=stat(file.c_str(),&statbuf); if(!err) return statbuf.st_mtime; else return -1; } // Looks in 's' for tag and replaces it with value int std::searchAndReplace(string *s, string tag, string value) { unsigned x = s->find(tag); if(x < string::npos) { while(x < string::npos) { s->replace(x,tag.length(),value); x=s->find(tag,x+1); } return 1; } return 0; } char *std::getFullPath(char *buff) { return getcwd(buff, 512); } int std::applyStat(const char *file1, const char *file2) { struct stat statbuf; // struct utimbuf utimebuff; int err; err = stat(file1,&statbuf); if(err == -1) { if(errno == ENOENT) return errno; } err = chmod(file2, statbuf.st_mode); if(err == -1) { return errno; } // utimebuff.actime = statbuf.st_atime; // utimebuff.modtime = statbuf.st_mtime; // err = utime(file2, &utimebuff); // if(err == -1) { // return 0; // } return 0; } int std::applyTime(const char *file1, const char *file2) { struct stat statbuf; struct utimbuf utimebuff; int err; err = stat(file1,&statbuf); if(err == -1) { if(errno == ENOENT) return errno; } utimebuff.actime = statbuf.st_atime; utimebuff.modtime = statbuf.st_mtime; err = utime(file2, &utimebuff); if(err == -1) { return errno; } return 0; } int std::fileExists(const char *file) { struct stat statbuf; int err; err = stat(file,&statbuf); if(err == -1) { if(errno == ENOENT) return 0; } return 1; } int std::fileIsDir(const char *file) { struct stat statbuf; mode_t modes; int err; err = stat(file,&statbuf); if(!err) { modes = statbuf.st_mode; if(S_ISDIR(modes)) return 1; else return 0; } return 0; } int std::fileMkDir(string buff, mode_t mode) { char *dirs = NULL; char *work = NULL; char *tstdir = NULL; int err; size_t len; const char delim[] = "/\\"; work = strdup(buff.c_str()); dirs = strtok(work, delim); tstdir = (char *)malloc(strlen(dirs)+2); tstdir[0] = '/'; memcpy(tstdir+1, dirs, strlen(dirs)); tstdir[strlen(dirs)+1] = 0; while(dirs != NULL) { dirs = strtok(NULL, delim); if(dirs != NULL) { len = strlen(tstdir); tstdir = (char *)realloc(tstdir, strlen(tstdir)+strlen(dirs)+2); tstdir[len] = '/'; memcpy(tstdir+len+1, dirs, strlen(dirs)); tstdir[len+strlen(dirs)+1] = 0; if(mkdir(tstdir, mode) == -1) { err = errno; if(err != EEXIST) { return err; } } } } free(tstdir); if(mkdir(buff.c_str(), mode) == -1) { err = errno; return err; } else { return 0; } } /* The following function cleans up a path name: /example/ -> /example //example -> /example /./example -> /example /dir/../example -> /example /example/. -> /example example -> /cwd/example (or /example if cwd is NULL) Any combination of the above should be fixed on return. Path is not modified. */ #ifdef WIN32 #define SEP '\\' #else #define SEP '/' #endif #define DOT '.' #define DOTS ".." #define END 0 char *std::collapsePath(const char *path, const char *cwd) { return collapsePath(path, cwd, NULL); } char *std::collapsePath(const char *path, const char *cwd, const char *result) { char *start = NULL; char *i = NULL, *j = NULL; /* If path is NULL return cwd */ if(path == NULL) { /* If cwd is NULL, return SEP */ if(cwd == NULL) { start = (char *)malloc(2); start[0] = SEP; start[1] = 0; return start; } else { start = (char *)malloc(strlen(cwd)+1); memset(start,0,strlen(cwd)+1); memcpy(start, cwd, strlen(cwd)); return start; } } #ifdef WIN32 if(path[1] == ':') { work = (char *)malloc(strlen(path)-1); /* We know it's at least 2 long */ memcpy(work,path+2,strlen(path)-1); } else { work = (char *)malloc(strlen(path)+1); memcpy(work,path,strlen(path)+1); } #else start = (char *)malloc(strlen(path)+1); memcpy(start,path,strlen(path)+1); #endif if(result != NULL) { fprintf(stderr, "collapsing path: `%s' -> `%s'\n", start, result); } /* Fixing DOT's only this pass (either /. or /./) */ i = start; while(*i != 0) { if((*i == DOT) && (i!=start)) { if((*(i-1) == SEP) && ((*(i+1) == SEP)||(*(i+1) == END))) { memmove(i,i+1,strlen(i+1)+1); } } i++; } start = remExtraSlashes(start); /* Fixing DOTDOT's only this pass: ../ -> ../ /../ -> / /a/.. -> / */ i = start; while(*i != 0) { /* #ifdef DEBUG printf("`%s' %*s `%s'\n", i, (12-strlen(i)), "", start); #endif */ if(*i == DOT) { i++; if(*i == DOT) { if((*(i+1) == SEP)||(*(i+1) == END)) { if((i-4) < start) { if((i-1) == start) { i++; } else { if((*(i-2) == SEP)&&((i-2) == start)) { if(*(i+1) == SEP) { memmove(i-2,i+1,strlen(i+1)+1); i-=2; } else { memmove(i-1,i+1,strlen(i+1)+1); i--; } } else { if(*(i-3) != DOT) { if(*(i+1) == SEP) { memmove(i-3,i+2, strlen(i+2)+1); } else { memmove(i-3,i+1, strlen(i+1)+1); } i-=3; } else { i++; } } } } else { if( (strncmp((i-4), DOTS, 2)) || (((i-5) >= start) && (*(i-5) != SEP)) ) { j=i; while((*j != SEP)&&(j != start)) { j--; } if(*j == SEP) j--; while((*j != SEP)&&(j != start)) { j--; } if(*j == SEP) j++; if(*(i+1) == SEP) { memmove(j,i+2,strlen(i+2)+1); } else { memmove(j,i+1,strlen(i+1)+1); } i=j; } else { i++; } } } else { while((*i != SEP)&&(*i != END)) { i++; } if(*i == SEP) i++; } } else { while((*i != SEP)&&(*i != END)) { i++; } if(*i == SEP) i++; } } else { while((*i != SEP)&&(*i != END)) { i++; } if(*i == SEP) i++; } } start = remExtraSlashes(start); /* Remove beginning ./ or sole '.' if found */ i = start; if(*i != END) { if((*i == DOT) && (*(i+1) == SEP)) { memmove(i,i+2,strlen(i+2)+1); } else if((*i == DOT) && (*(i+1) == END)) { memmove(i,i+1,strlen(i+1)+1); } } /* #ifdef DEBUG printf("`%s'%*s=> `%s'%*s%s `%s'\n", path, (12-strlen(path)), "", start, (12-strlen(start)), "", (strcmp(start,result))?"!=":"==", result); #endif */ return start; } inline char *std::remExtraSlashes(char *path) { char *i; /* Fixing SEP's only this pass (multiple SEPs into one SEP //// -> /) */ i = path; while(*i != 0) { if(*i == SEP) { i++; if(*i == SEP) { memmove(i-1,i,strlen(i)+1); i--; } } else { i++; } } /* Remove trailing slash if not '/' */ if(i != path) { if((*(i-1) == SEP) && ((i-1) != path)) { memmove(i-1,i,1); } } return path; } void std::__wsmake_set_level(int level) { __wsmake_level = level; } int std::__wsmake_get_level(void) { return __wsmake_level; } void std::__wsmake_set_quiet(int quiet) { if(quiet!=0) { __wsmake_quiet = 1; } else { __wsmake_quiet = 0; } } void std::__wsmake_print_it(FILE *out, const char *output, va_list *ap) { if(__wsmake_quiet) return; vfprintf(out,output,*ap); va_end(*ap); } #ifdef DEBUG void std::__wsmake_print_debug(const char *output, ...) { va_list ap; fprintf(stderr,"%s debug: ", PACKAGE); va_start(ap, output); __wsmake_print_it(stderr,output,&ap); } #endif void std::__wsmake_print_warning(const char *output, ...) { va_list ap; if(__wsmake_quiet) return; fprintf(stdout,"%s warning: ", PACKAGE); va_start(ap, output); __wsmake_print_it(stdout,output,&ap); } void std::__wsmake_print_error(const char *output, ...) { va_list ap; if(__wsmake_quiet) return; fprintf(stderr,"%s error: ", PACKAGE); va_start(ap, output); __wsmake_print_it(stderr,output,&ap); } void std::__wsmake_print(const char *output, ...) { va_list ap; if(__wsmake_quiet) return; va_start(ap, output); __wsmake_print_it(stdout,output,&ap); } void std::__wsmake_print(int level, const char *output, ...) { va_list ap; if(__wsmake_quiet) return; if(__wsmake_level >= level) { va_start(ap, output); __wsmake_print_it(stdout,output,&ap); } }