// The contents of this file are subject to the Mozilla Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is the Berkeley Open Infrastructure for Network Computing. // // The Initial Developer of the Original Code is the SETI@home project. // Portions created by the SETI@home project are Copyright (C) 2002, 2003 // University of California at Berkeley. All Rights Reserved. // // Contributor(s): // #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILE_H #include #endif #include #include using namespace std; #ifdef _WIN32 #include #include #else #include #include #endif #include "error_numbers.h" #include "util.h" // Converts a double precision time (where the value of 1 represents // a day) into a string. smallest_timescale determines the smallest // unit of time division used // smallest_timescale: 0=seconds, 1=minutes, 2=hours, 3=days, 4=years // int double_to_ydhms (double x, int smallest_timescale, char *buf) { double years, days, hours, minutes, seconds; char year_buf[64], day_buf[16], hour_buf[16], min_buf[16], sec_buf[16]; if (x < 0 || buf == NULL) return -1; years = x / 365.25; days = fmod(x, 365.25); hours = fmod(x*24, 24); minutes = fmod(x*24*60, 60); seconds = fmod(x*24*60*60, 60); if (smallest_timescale==4) { sprintf( year_buf, "%.3f yr ", years ); } else if (years > 1 && smallest_timescale < 4) { sprintf( year_buf, "%d yr ", (int)years ); } else { strcpy( year_buf, "" ); } if (smallest_timescale==3) { sprintf( day_buf, "%.2f day%s ", days, (days>1?"s":"") ); } else if (days > 1 && smallest_timescale < 3) { sprintf( day_buf, "%d day%s ", (int)days, (days>1?"s":"") ); } else { strcpy( day_buf, "" ); } if (smallest_timescale==2) { sprintf( hour_buf, "%.2f hr ", hours ); } else if (hours > 1 && smallest_timescale < 2) { sprintf( hour_buf, "%d hr ", (int)hours ); } else { strcpy( hour_buf, "" ); } if (smallest_timescale==1) { sprintf( min_buf, "%.2f min ", minutes ); } else if (minutes > 1 && smallest_timescale < 1) { sprintf( min_buf, "%d min ", (int)minutes ); } else { strcpy( min_buf, "" ); } if (smallest_timescale==0) { sprintf( sec_buf, "%.2f sec ", seconds ); } else if (seconds > 1 && smallest_timescale < 0) { sprintf( sec_buf, "%d sec ", (int)seconds ); } else { strcpy( sec_buf, "" ); } // the "-0.05" below is to prevent it from printing 60.0 sec // when the real value is e.g. 59.91 // sprintf(buf, "%s%s%s%s%s", year_buf, day_buf, hour_buf, min_buf, sec_buf); return 0; } // Convert nbytes into a string. If total_bytes is non-zero, // convert the two into a fractional display (i.e. 4/16 KB) // void get_byte_string(double nbytes, double total_bytes, char* str, int len) { char buf[256]; double xTera = (1024.0*1024.0*1024.0*1024.0); double xGiga = (1024.0*1024.0*1024.0); double xMega = (1024.0*1024.0); double xKilo = (1024.0); if (total_bytes != 0) { if (total_bytes >= xTera) { sprintf(buf, "%0.2f/%0.2f TB", nbytes/xTera, total_bytes/xTera); } else if (total_bytes >= xGiga) { sprintf(buf, "%0.2f/%0.2f GB", nbytes/xGiga, total_bytes/xGiga); } else if (total_bytes >= xMega) { sprintf(buf, "%0.2f/%0.2f MB", nbytes/xMega, total_bytes/xMega); } else if (total_bytes >= xKilo) { sprintf(buf, "%0.2f/%0.2f KB", nbytes/xKilo, total_bytes/xKilo); } else { sprintf(buf, "%0.0f/%0.0f bytes", nbytes, total_bytes); } } else { if (nbytes >= xTera) { sprintf(buf, "%0.2f TB", nbytes/xTera); } else if (nbytes >= xGiga) { sprintf(buf, "%0.2f GB", nbytes/xGiga); } else if (nbytes >= xMega) { sprintf(buf, "%0.2f MB", nbytes/xMega); } else if (nbytes >= xKilo) { sprintf(buf, "%0.2f KB", nbytes/xKilo); } else { sprintf(buf, "%0.0f bytes", nbytes); } } safe_strncpy(str, buf, len); } // return time of day as a double // Not necessarily in terms of UNIX time (especially on Windows) // double dtime() { #ifdef _WIN32 LARGE_INTEGER time; FILETIME sysTime; GetSystemTimeAsFileTime(&sysTime); time.LowPart = sysTime.dwLowDateTime; time.HighPart = sysTime.dwHighDateTime; // Time is in 100 ns units return (double)time.QuadPart/10000000; // Convert to 1 s units #else struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec + (tv.tv_usec/1.e6); #endif } // sleep for a specified number of seconds // void boinc_sleep(double seconds) { #ifdef _WIN32 ::Sleep((int)(1000*seconds)); #else sleep((int)seconds); usleep((int)fmod(seconds*1000000,1000000)); #endif } // take a string containing some space separated words. // return an array of pointers to the null-terminated words. // Modifies the string arg. // Returns argc // TODO: use strtok here #define NOT_IN_TOKEN 0 #define IN_SINGLE_QUOTED_TOKEN 1 #define IN_DOUBLE_QUOTED_TOKEN 2 #define IN_UNQUOTED_TOKEN 3 int parse_command_line(char* p, char** argv) { int state = NOT_IN_TOKEN; int argc=0; while (*p) { switch(state) { case NOT_IN_TOKEN: if (isspace(*p)) { } else if (*p == '\'') { p++; argv[argc++] = p; state = IN_SINGLE_QUOTED_TOKEN; break; } else if (*p == '\"') { p++; argv[argc++] = p; state = IN_DOUBLE_QUOTED_TOKEN; break; } else { argv[argc++] = p; state = IN_UNQUOTED_TOKEN; } break; case IN_SINGLE_QUOTED_TOKEN: if (*p == '\'') { *p = 0; state = NOT_IN_TOKEN; } break; case IN_DOUBLE_QUOTED_TOKEN: if (*p == '\"') { *p = 0; state = NOT_IN_TOKEN; } break; case IN_UNQUOTED_TOKEN: if (isspace(*p)) { *p = 0; state = NOT_IN_TOKEN; } break; } p++; } argv[argc] = 0; return argc; } int lock_file(char* filename) { int retval; // some systems have both! #ifdef HAVE_FLOCK int lock = open(filename, O_WRONLY|O_CREAT, 0644); retval = flock(lock, LOCK_EX|LOCK_NB); #elif HAVE_LOCKF int lock = open(filename, O_WRONLY|O_CREAT, 0644); retval = lockf(lock, F_TLOCK, 1); // must leave fd open #endif #ifdef _WIN32 HANDLE hfile = CreateFile( filename, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); if (hfile == INVALID_HANDLE_VALUE) retval = 1; else retval = 0; #endif return retval; } static char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } void c2x(char *what) { char buf[3]; char num = atoi(what); char d1 = num / 16; char d2 = num % 16; int abase1, abase2; if (d1 < 10) abase1 = 48; else abase1 = 55; if (d2 < 10) abase2 = 48; else abase2 = 55; buf[0] = d1+abase1; buf[1] = d2+abase2; buf[2] = 0; strcpy(what, buf); } void strip_whitespace(char *str) { int read_pos=0, write_pos=0; while (str[read_pos]) { if (!isspace(str[read_pos])) { str[write_pos++] = str[read_pos]; } read_pos++; } str[write_pos] = 0; } void unescape_url(char *url) { register int x,y; for(x=0,y=0;url[y];++x,++y) { if((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } void escape_url(char *in, char*out) { int x, y; for (x=0, y=0; in[x]; ++x) { if (isalnum(in[x])) { out[y] = in[x]; ++y; } else { out[y] = '%'; ++y; out[y] = 0; char buf[256]; sprintf(buf, "%d", (char)in[x]); c2x(buf); strcat(out, buf); y += 2; } } out[y] = 0; } // Escape a URL for the project directory, cutting off the "http://", // converting '\' '/' and ' ' to '_', // and converting the non alphanumeric characters to %XY // where XY is their hexadecimal equivalent // void escape_url_readable(char *in, char* out) { int x, y; char *temp; char buf[256]; temp = strstr(in,"://"); if (temp) { in = temp + strlen("://"); } for (x=0, y=0; in[x]; ++x) { if (isalnum(in[x]) || in[x]=='.' || in[x]=='-' || in[x]=='_') { out[y] = in[x]; ++y; } else if (in[x] == '/' || in[x] == '\\' || in[x] == ' ') { out[y] = '_'; ++y; } else { out[y] = '%'; ++y; out[y] = 0; sprintf(buf, "%d", (char)in[x]); c2x(buf); strcat(out, buf); y += 2; } } out[y] = 0; } inline void replace_string(string& s, string const& src, string const& dest, string::size_type start=0) { string::size_type p; while ( (p=s.find(src, start)) != string::npos ) { s.replace(p, src.length(), dest); start = p + dest.length(); } } // Canonicalize a master url. // - Convert the first part of a URL (before the "://") to lowercase // - Remove double slashes // - Add a trailing slash if necessary // void canonicalize_master_url(string& url) { string::size_type p = url.find("://"); // lowercase http:// if (p != string::npos) { transform(url.begin(), url.begin()+p, url.begin(), tolower); p += 3; } else { p = 0; } // remove double slashes replace_string(url, "//", "/", p); // ensure trailing slash if (url[url.length()-1] != '/') { url += '/'; } } void canonicalize_master_url(char *xurl) { string url = xurl; canonicalize_master_url(url); strcpy(xurl, url.c_str()); } void safe_strncpy(char* dst, char* src, int len) { strncpy(dst, src, len); dst[len-1]=0; } // return current time of day as ASCII string, no CR // char* timestamp() { time_t now = time(0); char* p = ctime(&now); *(strchr(p, '\n')) = 0; return p; } // set by command line bool debug_fake_exponential_backoff = false; double debug_total_exponential_backoff = 0; // return a random integer in the range [MIN,min(e^n,MAX)) int calculate_exponential_backoff(const char* debug_descr, int n, double MIN, double MAX, double factor /*=1.0*/) { double rmax = min(MAX, factor*exp((double)n)); if (debug_fake_exponential_backoff) { // For debugging/testing purposes, fake exponential back-off by // returning 0 seconds; report arguments so we can tell what we would // have done (this doesn't test the rand_range() functions but is // very useful for testing backoff/retry policies). double expected_backoff = (MIN > rmax) ? MIN : (rmax-MIN)/2.0; debug_total_exponential_backoff += expected_backoff; fprintf(stderr, "## calculate_exponential_backoff(): descr=\"%s\", n=%d, MIN=%.1f, MAX=%.1f, factor=%.1f; rand_range [%.1f,%.1f); total expected backoff=%.1f\n", debug_descr, n, MIN, MAX, factor, MIN, rmax, debug_total_exponential_backoff); return 0; } return (int) rand_range(MIN, rmax); }