/* ARISA - Utility Functions * Copyright (C) 2003, 2004 Carl Ritson * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "arisa.h" #include #include #include #include #include inline time_t xtime(void) { time_t t; /* FIXME: do drift calculation? */ t = time(NULL); return t; } /* strncpy doesn't quite do what I want */ inline char *xstrncpy(char *dst, const char *src, size_t n) { strncpy(dst,src,n); dst[n-1] = '\0'; return dst; } size_t xstrncpy_rpl(char *dst, const char *src, size_t len, const char *pattern, const char *replace) { const size_t p_len = strlen(pattern); const size_t r_len = strlen(replace); const char p0 = pattern[0]; size_t d_pos = 0; while(d_pos < (len-1) && *src != '\0') { if(*src == p0) { if(strncmp(src,pattern,p_len) == 0) { strncpy(dst+d_pos,replace, IMIN((len-1) + d_pos,r_len)); d_pos += r_len; src += p_len; } else { dst[d_pos++] = *(src++); } } else { dst[d_pos++] = *(src++); } } dst[d_pos] = '\0'; return d_pos; } inline int IMAX(int a, int b) { return a < b ? b : a; } inline int IMIN(int a, int b) { return a > b ? b : a; } char *readline_term(FILE *fp, char *buffer, size_t size) { int i,ret; for(i = 0; i < (size - 1); ++i) { ret = fgetc(fp); if(ret != EOF && ret != '\n') buffer[i] = (char) ret; else break; } buffer[i] = '\0'; if(i != 0) return buffer; else return NULL; } char *readline_file_nt(FILE *fp, char *buffer, size_t size, size_t *ret_size) { char *tmp; *ret_size = fread(buffer,1,size-1,fp); buffer[*ret_size] = '\0'; if((tmp = strchr(buffer,'\n')) != NULL) { // this is always possible due to earlier termination *(tmp+1) = '\0'; fseek(fp,-(*ret_size - (tmp - buffer) - 1),SEEK_CUR); *ret_size = tmp - buffer; } if(tmp != NULL || *ret_size > 0) return buffer; else return NULL; } char *readline_file(FILE *fp, char *buffer, size_t size) { size_t ret_size = 0; char *ret = readline_file_nt(fp,buffer,size,&ret_size); if(ret != NULL && ret_size > 0) { if(buffer[ret_size-1] == '\n') buffer[ret_size-1] = '\0'; } return ret; } void trim(char *str) { int i, start = 0, end = 0; assert(str != NULL); if(*str == '\0') return; for(i = 0; isspace(str[i]) && str[i] != '\0'; ++i) ; start = i; for(i = strlen(str) - 1; isspace(str[i]) && i > start; --i) ; end = i + 1; if(start == end) { *str = '\0'; return; } memmove(str, str + start, end - start); str[(end - start)] = '\0'; } char *readtline_term(FILE *fp, char *buffer, size_t size) { char *line = readline_term(fp,buffer,size); if(line != NULL) trim(line); return line; } char *readtline_file(FILE *fp, char *buffer, size_t size) { char *line = readline_file(fp,buffer,size); if(line != NULL) trim(line); return line; } void splitline(char *part1, char **part2) { int i; assert(part1 != NULL); for(i = 0; part1[i] != '\0'; ++i) { if(isspace(part1[i])) break; } if(part1[i] != '\0') { /* Space found, so split */ part1[i] = '\0'; for(i = i + 1; part1[i] != '\0' && isspace(part1[i]); ++i) ; *part2 = (part1 + i); } else { *part2 = NULL; } } int lrand(int limit) { return (int) (((float)limit)*rand()/(RAND_MAX+1.0)); } #define PTRARR_BLOCK_SIZE 4 static inline int no_blocks(int no_elem) { return no_elem == 0 ? 0 : (no_elem >> 2) + 1; } void ptrarr_add(void ***arr, int *no_elem, void *ptr) { int i; for(i = 0; i < *no_elem; ++i) { if((*arr)[i] == ptr) return; } if(no_blocks(*no_elem) != no_blocks((*no_elem)+1)) { (*arr) = xreloc(*arr,sizeof(void *) * (no_blocks((*no_elem)+1) * PTRARR_BLOCK_SIZE)); } (*no_elem)++; (*arr)[(*no_elem)-1] = ptr; } void ptrarr_del(void ***arr, int *no_elem, void *ptr) { int i,j,del; for(i = 0, j = 0, del = 0; j < *no_elem;) { if((*arr)[j] != ptr) { if(i != j) (*arr)[i] = (*arr)[j]; i++; j++; } else { j++; del++; } } if(no_blocks(*no_elem) != no_blocks((*no_elem)-del)) { (*arr) = xreloc(*arr,sizeof(void *) * (no_blocks((*no_elem)-del) * PTRARR_BLOCK_SIZE)); } (*no_elem) -= del; } void *ptrarr_dup(void **arr, int no_elem, int *ret_no_elem) { void **ret = NULL; if(arr != NULL) { ret = xalloc(sizeof(void *) * no_blocks(no_elem) * PTRARR_BLOCK_SIZE); memcpy(ret,arr,sizeof(void *) * no_elem); } (*ret_no_elem) = no_elem; return (void *)ret; } void ptrarr_alloc(void ***arr, int *no_elem, int size) { if(size != 0) { int i; (*arr) = xalloc(sizeof(void *) * no_blocks(size) * PTRARR_BLOCK_SIZE); for(i = 0; i < size; ++i) (*arr)[i] = NULL; } else { (*arr) = NULL; } (*no_elem) = size; } #if 0 void ptrarr_reduce(void ***arr, int *no_elem, int hint) { void **newarr = NULL; int i,j; if(hint <= 0) { for(i = 0, hint = 0; i < *no_elem; ++i) { if((*arr)[i] != NULL) hint++; } } newarr = xalloc(sizeof(void *) * hint); for(i = 0, j = 0; i < *no_elem; ++i) { if((*arr)[i] != NULL) newarr[j++] = (*arr)[i]; } xfree(*arr); *arr = newarr; *no_elem = hint; } #endif int ptrarr_swap(void ***arr, int *no_elem, int elem1, int elem2) { void *tmp; if(elem1 < 0 || elem2 < 0) return -1; if(elem1 >= *no_elem || elem2 >= *no_elem) return -1; tmp = (*arr)[elem1]; (*arr)[elem1] = (*arr)[elem2]; (*arr)[elem2] = tmp; return 0; } int ptrarr_move(void ***arr, int *no_elem, int from_pos, int to_pos) { void *tmp; int i; if(from_pos < 0 || to_pos < 0) return -1; else if(from_pos >= *no_elem || to_pos >= *no_elem) return -1; else if(from_pos == to_pos) return 0; tmp = (*arr)[from_pos]; if(from_pos < to_pos) { for(i = from_pos; i < to_pos; ++i) (*arr)[i] = (*arr)[i+1]; } else { for(i = from_pos; i > to_pos; --i) (*arr)[i] = (*arr)[i-1]; } (*arr)[to_pos] = tmp; return 0; } char **strarr_dup(char **in, int no_in, int *no_out) { char **arr = NULL; int i; arr = PTRARR_DUP(in,no_in,no_out); for(i = 0; i < *no_out; ++i) { if(arr[i] != NULL) arr[i] = xstrdup(arr[i]); } return arr; } void strarr_free(char ***arr, int *no_arr) { int i; if(*arr != NULL) { for(i = 0; i < *no_arr; ++i) { if((*arr)[i] != NULL) xfree((*arr)[i]); } xfree(*arr); *arr = NULL; } *no_arr = 0; } void pathtofn(const char *path, char *fn, const size_t fn_len) { int i,ls; for(i = 0, ls = 0; path[i] != '\0'; ++i) { if(path[i] == '/') ls = i + 1; } xstrncpy(fn,path+ls,fn_len); } char **split_opt(char *input, char *output, int *no_opt) { char **ret = NULL; int lpos,ppos,opt,q; if(output == NULL) { output = xstrdup(input); input = output; } else if(input != output) { strcpy(output,input); input = output; } retry: if(*no_opt > 0) ret = xalloc(sizeof(char *)*(*no_opt)); for(lpos = -1,ppos = opt = 0,q = -1; input[ppos] != '\0' && (*no_opt == 0 || opt < (*no_opt)-1); ++ppos) { if(lpos == -1 && !isspace(input[ppos])) { if(input[ppos] == '"') lpos = q = ppos; else lpos = ppos; } else if(lpos != -1 && isspace(input[ppos]) && q == -1) { if(ret != NULL) { ret[opt++] = &(input[lpos]); input[ppos] = '\0'; } else opt++; lpos = -1; } else if(q != -1 && input[ppos] == '"') { if(input[ppos-1] != '\\') { if(ret != NULL) { ret[opt++] = &(input[lpos+1]); input[lpos] = '\0'; input[ppos] = '\0'; } else opt++; lpos = -1; q = -1; } } } if(lpos != -1) { if(ret != NULL) { if(q == -1) ret[opt++] = &(input[lpos]); else ret[opt++] = &(input[lpos+1]); } else opt++; } else { for(; input[ppos] != '\0'; ++ppos) { if(!isspace(input[ppos])) { if(ret != NULL) ret[opt++] = &(input[ppos]); else opt++; break; } } } if(ret == NULL && opt != 0) { *no_opt = opt + 1; goto retry; } else *no_opt = opt; return ret; } size_t colourise(colourtype_t colour, char *buffer, int bufsize, const char *data) { char *ptr,*c; int i,j; if(colour == COLOUR_INT) { return (size_t) (strlen(xstrncpy(buffer,data,bufsize))); } else if(colour == COLOUR_NONE) { for(i = 0, j = 0; data[i] != '\0' && j < (bufsize-1); ++i) { if(data[i] == '%') { i++; switch(tolower(data[i])) { case 'k': case 'r': case 'g': case 'y': case 'b': case 'm': case 'c': case 'w': case 'n': case '9': case '_': case 'u': break; case '%': buffer[j++] = '%'; break; default: buffer[j++] = data[i-1]; buffer[j++] = data[i]; break; } } else buffer[j++] = data[i]; } if(buffer[j] != '\0') buffer[j] = '\0'; return j; } else { if(data == buffer) ptr = xstrdup(data); else ptr = (char *)data; for(i = 0, j = 0; ptr[i] != '\0' && j < (bufsize-1); ++i) { if(ptr[i] == '%') { i++; switch(ptr[i]) { case '_': case '9': c = IRC_BOLD; break; case 'k': c = IRC_BLACK; break; case 'K': c = IRC_GRAY; break; case 'r': c = IRC_RED; break; case 'R': c = IRC_LRED; break; case 'g': c = IRC_GREEN; break; case 'G': c = IRC_LGREEN; break; case 'y': c = IRC_YELLOW; break; case 'Y': c = IRC_LYELLOW; break; case 'b': c = IRC_BLUE; break; case 'B': c = IRC_LBLUE; break; case 'm': c = IRC_MAGENTA; break; case 'M': c = IRC_LMAGENTA; break; case 'c': c = IRC_CYAN; break; case 'C': c = IRC_LCYAN; break; case 'w': c = IRC_WHITE; break; case 'W': c = IRC_LWHITE; break; case 'U': c = IRC_UNDERLINE; break; case 'n': c = IRC_OFF; break; case '%': c = "%"; break; default: c = NULL; break; } if(c != NULL) { strncpy(buffer+j,c,bufsize-j); j += strlen(c); } else { buffer[j++] = ptr[i-1]; buffer[j++] = ptr[i]; } } else buffer[j++] = ptr[i]; } if(buffer[j] != '\0') buffer[j] = '\0'; if(ptr != data) xfree(ptr); return j; } } size_t quote_colours(char *buffer, size_t bufsize, const char *data) { int i,j; for(i = 0, j = 0; j < (bufsize-1) && data[i] != '\0'; ++i) { if(data[i] == '%') buffer[j++] = '%'; buffer[j++] = data[i]; } buffer[j] = '\0'; return j; } #if defined(HAVE_STRERROR_R) char *lstrerror_r(int errnum, char *buffer, size_t bufsize) { /* SuSv3 strerror_r returns int, the glibc man page acknowledges this * and says their strerror_r returns int, but in the headers it doesn't. * * So here is crude function that trys to handle int and char* versions * on the fly. */ if(strerror_r(EINVAL,buffer,bufsize) == 0) { /* int version */ if(strerror_r(errnum,buffer,bufsize) != 0) snprintf(buffer,bufsize,"Unknown: %d",errnum); return buffer; } else { char *ptr = (char *)strerror_r(errnum,buffer,bufsize); /* char* version */ if(ptr == 0) snprintf(buffer,bufsize,"Unknown: %d",errnum); else snprintf(buffer,bufsize,"%s",ptr); return buffer; } } #else /* defined(HAVE_STRERROR_R) */ char *lstrerror_r(int errnum, char *buffer, size_t bufsize) { /* In case a version of strerror_r is not found then use strerror * and hope that it is thread safe. SunOS is a good example. */ char *str = strerror(errnum); xstrncpy(buffer,str,bufsize); return buffer; } #endif char *lctime_r(const time_t *timep, char *buf, size_t bufsize) { #if !defined(HAVE_SUNOS) char ctime_buf[32]; ctime_r(timep,ctime_buf); xstrncpy(buf,ctime_buf,bufsize); return buf; #else return ctime_r(timep,buf,bufsize); #endif } char *timestr(time_t time, char *str, size_t strsize) { char buffer[32]; lctime_r(&time,buffer,sizeof(buffer)); buffer[strlen(buffer)-1] = '\0'; // trailing \n xstrncpy(str,buffer,strsize); return str; } char *etimestr(time_t sec, char *str, size_t strsize) { if(sec >= 24*60*60) snprintf(str,strsize,"%2ldd%2ldh", (long)(sec / (24*60*60)), (long)(sec - ((sec/(24*60*60))*24*60*60)) / (60*60)); else if(sec >= 60*60) snprintf(str,strsize,"%2ldh%2ldm", (long)(sec / (60*60)), (long)(sec - ((sec/(60*60))*60*60)) / 60); else snprintf(str,strsize,"%2ldm%2lds", (long)(sec / 60),(long)(sec - ((sec/60)*60))); return str; } /* this function cleans "./" and "../" from paths */ char *clean_ds_path(const char *path, char *buffer, size_t bufsize) { int i,j,k; if(buffer[0] == '/') i = 1; else i = 0; for(j = 0; j < (bufsize-1) && path[i] != '\0'; ++i) { if(path[i] == '.') { for(k = i; path[k] == '.'; ++k); if(path[k] == '/' && (k-i) <= 2) { i = k; continue; } } buffer[j++] = path[i]; } buffer[j] = '\0'; return buffer; } char *format_bytes(char *buffer, size_t bufsize, unsigned long long bytes, int multiplier) { unsigned long long b = bytes * (unsigned long long)multiplier; if(b < 1024ULL) snprintf(buffer,bufsize,"%dB", (int)b); else if(b < (768ULL*1024ULL)) snprintf(buffer,bufsize,"%.1fKiB", ((double)b) / 1024.0); else if(b < (768ULL*1024ULL*1024ULL)) snprintf(buffer,bufsize,"%.1fMiB", ((double)b) / (1024.0*1024.0)); else if(b < (768ULL*1024ULL*1024ULL*1024ULL)) snprintf(buffer,bufsize,"%.1fGiB", ((double)b) / (1024.0*1024.0*1024.0)); else snprintf(buffer,bufsize,"%.1fTiB", ((double)b) / (1024.0*1024.0*1024.0*1024.0)); return buffer; } int diff_tv_usec(struct timeval *ti, struct timeval *tj) { if(ti->tv_sec == tj->tv_sec) return IMAX(ti->tv_usec,tj->tv_usec) - IMIN(ti->tv_usec,tj->tv_usec); else { if(IMAX(ti->tv_sec,tj->tv_sec) == ti->tv_sec) { return ((ti->tv_sec - tj->tv_sec) * 1000000) + (ti->tv_usec - tj->tv_usec); } else { return ((tj->tv_sec - ti->tv_sec) * 1000000) + (tj->tv_usec - ti->tv_usec); } } } inline int nextspace(char *str, int pos) { int i; for(i = pos; str[i] != '\0' && !isspace(str[i]); ++i) ; return i; } inline int nextchar(char *str, int pos) { int i; for(i = pos; str[i] != '\0' && isspace(str[i]); ++i) ; return i; } /** max_sokbuf * attempt to determine the maximum socket buffer in using an os * dependent method. */ #if defined(HAVE_LINUX) int max_sokbuf(int r1s0) { char buffer[32]; int ret,fd; int val = 0; if(!r1s0) fd = open("/proc/sys/net/core/wmem_max",O_RDONLY); else fd = open("/proc/sys/net/core/rmem_max",O_RDONLY); if(fd != -1) { ret = read(fd,buffer,sizeof(buffer)-1); if(ret != -1) { buffer[ret] = '\0'; val = atoi(buffer); } //D("close: %d",fd); close(fd); } if(val <= 0) return 64*1024; else return val; } #elif defined(HAVE_FREEBSD) && defined(HAVE_SYSCTL) #include int max_sokbuf(int r1s0) { int mib[3]; int val,len; mib[0] = CTL_KERN; mib[1] = KERN_IPC; mib[2] = KIPC_MAXSOCKBUF; len = sizeof(val); if(sysctl(mib,3,&val,&len,NULL,0) != 0) val = 0; if(val <= 0) return 64*1024; else return val; } #else int max_sokbuf(int r1s0) { return 64*1024; } #endif int wild_match_cf(const char *str, const char *mask, int (*tl)(int)) { while(*mask != '\0') { if(*mask == '*') { if(tl(*str) == tl(*(mask+1))) { if(wild_match_cf(str,mask+1,tl)) return 1; } } else if(tl(*mask) != tl(*str)) { return 0; } else { mask++; } if(*str == '\0') break; str++; } if(*mask == '\0' && *str == '\0') return 1; else return 0; } int wild_match(const char *str, const char *mask) { while(*mask != '\0') { if(*mask == '*') { if(*str == *(mask+1)) { if(wild_match(str,mask+1)) return 1; } } else if(*mask != *str) { return 0; } else { mask++; } if(*str == '\0') break; str++; } if(*mask == '\0' && *str == '\0') return 1; else return 0; } void antispin_init(antispin_t *as) { as->count = 0; as->second = 0; } void antispin_test_and_sleep(antispin_t *as, unsigned int max_loop) { struct timeval now; gettimeofday(&now,NULL); if(as->second == now.tv_sec) { as->count++; if(as->count > max_loop) usleep((1000000 - now.tv_usec) + 5000); } else { as->second = now.tv_sec; as->count = 0; } } /* a decimal space counts as a digit, so this is really three_character_size */ char *three_digit_size(char *buffer, off_t size) { float f, s = (float) size; char c; int cnt; for(cnt = 0; cnt < 4; ++cnt) { if(s < 1000.0) break; s /= 1024.0; } if(cnt == 0) { sprintf(buffer,"%3dB",(int)size); return buffer; } else { switch(cnt) { case 1: c = 'K'; break; case 2: c = 'M'; break; case 3: c = 'G'; break; case 4: c = 'T'; break; default: c = ' '; break; } for(cnt = 0, f = 100.0; f > 0.1; f /= 10.0) { if(s >= f) cnt++; } // printf rounds snprintf(buffer,4,"%3.*f",(cnt <= 1 ? 1 : 0),s); buffer[3] = c; buffer[4] = '\0'; return buffer; } } off_t expand_text_size(const char *str, int si) { char *ptr; off_t b = si ? 1000 : 1024; off_t ret; ret = (off_t) strtoull(str,&ptr,0); if(ptr != NULL) { switch(*ptr) { case 'k': case 'K': ret *= b; break; case 'm': case 'M': ret *= b*b; break; case 'g': case 'G': ret *= b*b*b; break; case 't': case 'T': ret *= b*b*b*b; break; default: break; } } return ret; } int test_timer(time_t now, time_t last, uint32_t interval) { if(now == 0 && interval != 0) now = xtime(); if(interval != 0 && abs(now - last) >= interval) return 1; else return 0; } int test_update_timer(time_t now, time_t *last, uint32_t interval) { if(test_timer(now,*last,interval)) { *last = now != 0 ? now : xtime(); return 1; } else return 0; }