#include "ttail_search_files.h" int _ttail_search_closest_files(ttail_t* t, const struct tm *tm) { int ret; size_t i, prev; struct tm **ftm; /* t->logfile_sz len of struct tm[2] */ ret = _ttail_search_closest_files_init(t); if(ret) { return ret; } /* Storing min & max of each files in ftm and checking that files are * sorted well */ ftm = malloc(sizeof(struct tm*) * t->logfile_sz); if(!ftm) { perror("Unable to allocate memory"); goto _ttail_search_closest_files_alloc_err; } for(i=0; ilogfile_sz; i++) { ftm[i] = malloc(sizeof(struct tm)*2); if(!ftm[i]) { perror("Unable to allocate memory for time"); goto _ttail_search_closest_files_alloc_loop_err; } if(!t->logfile[i]) { continue; } ret = _ttail_file_minmax(t, 0, ftm[i]); if(ret < 0) { fprintf(stderr, "Minmax error\n"); goto _ttail_search_closest_files_loop_err; } else if (ret == 1) { if(t->verbose) { fprintf(stderr, "Warning : unable to find \ a valid date in '%s'\n", t->logfile_name[i]); } free(ftm[i]); ftm[i] = NULL; fclose(t->logfile[i]); t->logfile[i] = NULL; } prev = i; if(i && prev && ttail_tm_cmp(&(ftm[prev][1]), &(ftm[i][0])) > 0) { /* files are not sorted */ fprintf(stderr, "Files do not seems to be sorted. \ File sorting not implemented yet\n"); goto _ttail_search_closest_files_loop_err; } } /* TODO begining binary search of date_min */ ret = _ttail_search_files_binary_search(t, tm, (const struct tm**)ftm); return 0; goto _ttail_search_closest_files_err; _ttail_search_closest_files_err: i = t->logfile_sz; do { _ttail_search_closest_files_alloc_loop_err: i--; _ttail_search_closest_files_loop_err: if(ftm[i]) { free(ftm[i]); } }while(i); free(ftm); _ttail_search_closest_files_alloc_err: return -1; } int _ttail_search_files_binary_search(ttail_t* t, const struct tm* in, const struct tm** ftm) { int cmin, cmax; size_t valid; off_t *off; size_t *id; off = &(t->session->file.off); id = &(t->session->file.id); *id = *off = 0; valid = 0; if(ttail_tm_cmp(&(ftm[0][0]), in) > 0) { return 1; } while(1) { if(!ftm[*id]) { (*id)++; continue; } valid++; cmin = ttail_tm_cmp(&(ftm[*id][0]), in); cmax = ttail_tm_cmp(&(ftm[*id][1]), in); if(!cmin) { /* found at the begining of the file */ return 0; } else if (!cmax) { /* found at EOF */ *off = _ttail_from_search_from_end(t, *id, in); if(*off < 0) { *off = 0; return -1; } return 0; } else if(cmin > 0) { /* not found */ *id=0; return 1; } else if(*id == t->logfile_sz - 1 || cmax > 0) { /* somewhere in current file */ break; } (*id)++; } if(!valid) { fprintf(stderr, "No files to scan"); } /* the answer is somewhere in *id file */ *off = _ttail_from_search_from_end(t, *id, in); return 0; } inline int _ttail_search_file_binary_search(ttail_t* t, const struct tm* in, const struct tm** ftm) { off_t cur, sz, d, prev; int ret, cmpres; off_t *off; size_t id; off = &(t->session->file.off); id = t->session->file.id; sz = t->session->file.file_sz[id]; d = cur = sz / 2; prev = 0; cmpres = 0; while(1) { cur = _ttail_file_next_line(t->logfile[id]); if(cur < -1) { cur = _ttail_file_start_line(t->logfile[id]); if(cur < 0) { return -1; } } if(cur == prev) { *off = cur; return 0; } prev = cur; ret = _ttail_file_cur_cmp(t, id, in, &cmpres); if(ret < 0) { return -1; } else if(cmpres == 0) { *off = cur; break; } else if(cmpres < 0) { ret = _ttail_file_cur_cmp(t, id, in, &cmpres); if(cmpres >=0) { *off = cur; break; } d/=2; cur += d; cur %= t->session->file.file_sz[id]; } else { d/=2; cur -= d; } } return 0; } int _ttail_search_closest_files_init(ttail_t* t) { struct stat stat; FILE *fp; int fd; size_t i; off_t *file_sz; t->session = (ttail_search_t*)malloc(sizeof(ttail_search_file_t)); if(!t->session) { perror("Unable to allocate memory for search session"); goto _ttail_search_closest_files_alloc_session_err; } memset(t->session, 0, sizeof(ttail_search_file_t)); file_sz = malloc(sizeof(off_t)*t->logfile_sz); if(!file_sz) { perror("Unable to allocate memory for file sizes"); goto _ttail_search_closest_files_alloc_err; } t->session->file.file_sz = file_sz; #ifdef HUGEFILE t->session->file.sz_div = 0; #endif for(i=0;ilogfile_sz;i++) { fp = t->logfile[i]; if(!fp) { file_sz[i] = 0; continue; } if((fd = fileno(fp)) < 0) { perror("Unable to get fp"); goto _ttail_search_closest_files_err; } if(fstat(fileno(fp), &stat)) { perror("Unable to get file size"); goto _ttail_search_closest_files_err; } file_sz[i] = stat.st_size; } /* we got all real size, determining if we need a divisor */ /* * not implemented */ if(_ttail_search_closest_files_set_fsizes(t)) { goto _ttail_search_closest_files_err; } t->session->file.buf_sz = 128; t->session->file.buf = malloc(t->session->file.buf_sz); if(!t->session->file.buf) { goto _ttail_search_closest_files_err; } return 0; _ttail_search_closest_files_err: free(file_sz); t->session->file.file_sz = NULL; _ttail_search_closest_files_alloc_err: free(t->session); _ttail_search_closest_files_alloc_session_err: return -1; } int _ttail_search_closest_files_set_fsizes(ttail_t* t) { size_t i; off_t *vfile, *vsz; vfile = malloc(sizeof(off_t)*t->logfile_sz); if(!vfile) { perror("Unable to allocate memory for file size sum"); return -1; } t->session->file.vfile = vfile; vsz = &(t->session->file.vsz); *vsz = 0; for(i=0; ilogfile_sz;i++) { vfile[i] = *vsz; #ifdef HUGEFILE *vsz += t->session->file.file_sz[i] >> t->session->file.sz_div; #else *vsz += t->session->file.file_sz[i]; #endif } t->session->file.vpos = 0; return 0; } inline int _ttail_file_minmax(ttail_t* t, size_t id, struct tm tm[2]) { FILE *fp; long cur; memset(tm, 0, sizeof(struct tm)*2); fp = t->logfile[id]; if(!fp) { return 1; } if(fseek(fp, 0, SEEK_SET) < 0) { perror("Unable to manipulate fp"); return -1; } while(1) { if(ttail_getline(t, id) < 0) { return 1; } if(!ttail_logline2date(t, ttail_getline_buf(t), tm)) { break; } } if(fseek(fp, -1, SEEK_END) < 0) { perror("Unable to manipulate fp"); return -1; } while(1) { if((cur = _ttail_file_start_line(fp)) < 0) { return -1; } if(ttail_getline(t, id) < 0) { return 1; } if(!ttail_logline2date(t, ttail_getline_buf(t), tm+1)) { break; } if(!cur) { return 1; } if(fseek(fp, cur-1, SEEK_SET) < 0) { perror("Unable to manipulate fp"); return -1; } } return 0; } int _ttail_file_reopen(ttail_t* t, size_t id) { if(t->logfile[id]) { fclose(t->logfile[id]); } t->logfile[id] = fopen(t->logfile_name[id], "r"); if(!t->logfile[id] && t->verbose > 2) { fprintf(stderr, "Unable to reopen '%s'\n", t->logfile_name[id]); } return t->logfile[id]?0:-1; } inline long _ttail_file_next_line(FILE* f) { ssize_t s; size_t r; char *buff; long res; int c; r=0; buff = NULL; s = getline(&buff, &r, f); if(s == -1) { goto _ttail_file_next_line_err; } while(1) { c = getc(f); if(c == EOF) { return 0; } else if(c!='\n') { if(fseek(f, -1, SEEK_CUR)<0) { goto _ttail_file_next_line_err; } break; } } res = ftell(f); free(buff); return res; _ttail_file_next_line_err: free(buff); return -1; } inline long _ttail_file_start_line(FILE* f) { #define _STARTLN_BUFFLEN 32 long res; /* function result */ long read_beg, cur, last, start; int read_sz; int c; if((start = ftell(f)) < 0) { return -1; } res = 0; read_beg = start; while(!res && start) { if(fseek(f, read_beg, SEEK_SET) < 0) { return -1; } start = read_beg; read_sz = start <= _STARTLN_BUFFLEN?start:_STARTLN_BUFFLEN; read_beg = start - read_sz; if(fseek(f, read_beg, SEEK_SET) < 0) { return -1; } last = -1; /* last pos we saw a '\n' */ cur = read_beg; while(cur <= start) { c = getc(f); if(c == EOF) { if(!res) { return 0; } break; } else if (c =='\n') { last = cur; } else if(last >= 0) { res = cur; last = -1; } cur++; } if(!read_beg) { break; } } if(fseek(f, res, SEEK_SET) < 0) { return -1; } return res; } inline off_t _ttail_from_search_from_end(ttail_t* t , size_t id , const struct tm* tm) { FILE *f; struct tm curtm; off_t last; int ret; f = t->logfile[id]; if(fseek(f, -1, SEEK_END) < 0) { return -1; } while(1) { last = _ttail_file_start_line(f); if(last < 0) { goto _ttail_from_search_from_end_err; } if(ttail_getline(t, id) < 0) { goto _ttail_from_search_from_end_err; } ret = ttail_logline2date(t, ttail_getline_buf(t), &curtm); if(ret < 0) { goto _ttail_from_search_from_end_err; } if(!ret && !ttail_tm_cmp(&curtm, tm)) { /* found */ break; } if(last == 0) { /* considere the begining of the file as the answer */ return 0; } if(fseek(f, last-1, SEEK_CUR)) { goto _ttail_from_search_from_end_err; } } return last; _ttail_from_search_from_end_err: return -1; } inline int _ttail_file_off_cmp(ttail_t* t, size_t id, off_t off, const struct tm* tm, int *res) { if(fseek(t->logfile[id], off, SEEK_CUR)) { return -1; } if(ttail_getline(t, id) < 0) { return -1; } return _ttail_file_cur_cmp(t, id, tm, res); } inline int _ttail_file_cur_cmp(ttail_t* t, size_t id, const struct tm* tm , int* res) { int ret; struct tm ctm; ret = ttail_logline2date(t, ttail_getline_buf(t), &ctm); if(ret < 0) { return -1; } else if(ret == 1) { return 1; } *res = ttail_tm_cmp(&ctm, tm); return 0; } void _ttail_search_file_free(ttail_t* t) { if(!t->session) { return; } if(t->session->file.buf) { free(t->session->file.buf); } if(t->session->file.file_sz) { free(t->session->file.file_sz); } if(t->session->file.vfile) { free(t->session->file.vfile); } free(t->session); }