Cleanup
[thomas_code.git] / cacti / win_perf / getlog_base.c
blob9ca9b6648518cc61f6374fa509c1a07593c30981
1 /*****************************************************************************
3 * Getlog base library
5 * License: GPL
6 * Copyright (c) 2009 Thomas Guyot-Sionnest <tguyot@gmail.com>
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *****************************************************************************/
23 #include "getlog.h"
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <limits.h>
29 #include <time.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
35 /* Get the first line of the file... easy stuff */
36 char *get_head(int log) {
37 char readbuf[READ_CHNK];
38 int read_sz;
39 char *buf = NULL;
40 int buf_sz = 0;
41 char *tmp = NULL;
43 /* Make sure we're at the beginning */
44 lseek(log, 0, SEEK_SET);
46 /* Loop until we get a newline */
47 while ((read_sz = read(log, readbuf, READ_CHNK)) > 0) {
48 if ((buf = realloc(buf, buf_sz+read_sz)) == NULL) {
49 fprintf(stderr, "malloc failed in get_head()\n");
50 exit(3);
52 memcpy(buf+buf_sz, readbuf, read_sz);
53 buf_sz += read_sz;
55 /* Terminate buf as a string if we reached end of line */
56 if ((tmp = memchr(buf, '\n', buf_sz)) != NULL) {
57 if (tmp[-1] == '\r') tmp--;
58 tmp[0] = '\0';
59 break;
61 if (buf_sz >= MAX_READ) break; /* endless line? */
63 if (read_sz == -1) {
64 perror("read");
65 exit(3);
68 /* return whatever line de got, or NULL */
69 if (tmp) {
70 #ifdef MALLOC_FREE
71 tmp = strdup(buf);
72 free(buf);
73 buf = tmp;
74 #endif
75 return strdup(buf);
77 return NULL;
80 /* Get the last line of the file, reading backwards to make this quick on
81 * large log files. */
82 char *get_tail(int log) {
83 char readbuf[READ_CHNK];
84 int read_sz;
85 char *buf = NULL;
86 int buf_sz = 0;
87 char *tmp1=NULL, *tmp2=NULL;
88 off_t length, start;
89 struct stat sb;
91 if (fstat(log, &sb) == -1) {
92 perror("fstat");
93 exit(3);
95 length = sb.st_size; /* Size in bytes */
97 /* Try to read up to READ_CHNK bytes at time, but make sure we read at
98 * 512-bytes boundaries. THIS IS TRICKY, don't change this unless you
99 * know what you're doing! */
100 start = (length / 512) * 512;
101 if (start >= READ_CHNK && start == length) {
102 start -= READ_CHNK;
103 } else if (start >= READ_CHNK) {
104 start -= READ_CHNK - 512;
105 } else {
106 start = 0;
109 /* Set our position and start reading */
110 lseek(log, start, SEEK_SET);
111 while ((read_sz = read(log, readbuf, READ_CHNK)) > 0) {
112 if ((buf = realloc(buf, buf_sz+read_sz)) == NULL) {
113 fprintf(stderr, "malloc failed in get_tail()\n");
114 exit(3);
117 /* Prepend to buffer - straight memcpy() if memory don't overlap */
118 if (buf_sz)
119 memmove(buf+read_sz, buf, buf_sz);
120 memcpy(buf, readbuf, read_sz);
121 buf_sz += read_sz;
123 /* Terminate buf as a string if we got a full line */
124 if ((tmp1 = memchr(buf, '\n', buf_sz)) != NULL && tmp1 != buf+buf_sz-1) {
126 /* Make sure we got the last line */
127 while ((tmp2 = memchr(tmp1+1, '\n', buf_sz-(tmp1-buf)-1)) != NULL) {
128 if (tmp2 != buf+buf_sz-1) {
129 tmp1 = tmp2;
130 continue;
132 /* terminate tmp2 such as tmp1 becomes a string */
133 break;
135 if (tmp2) {
136 if (tmp2[-1] == '\r') tmp2--;
137 tmp2[0] = '\0';
138 tmp1++; /* get past first '\n' */
139 break;
143 if (buf_sz >= MAX_READ) break; /* endless line? */
144 if ((start -= READ_CHNK) < 0) break;
145 lseek(log, start, SEEK_SET);
148 /* Return the last line if we got one */
149 if (tmp1 && tmp2 && tmp2 - tmp1 > 0) {
150 #ifdef MALLOC_FREE
151 tmp2 = strdup(tmp1);
152 free(buf);
153 tmp1 = tmp2;
154 #endif
155 return strdup(tmp1);
157 return NULL;
160 /* Return position of index `colname` on `line`. */
161 int find_index(const char *colname, char *line) {
162 char *col;
163 int i = 0;
165 while (line && (col = subst_col(0, &line)) != NULL) {
166 /* Skip over the server name (\\name) */
167 col = strchr(col+3, '\\');
168 if (strcmp(col, colname) == 0) return i;
169 i++;
171 return -1;
175 * Fetch the column indicated by colnum and remove the scanned part from
176 * lineref (this allow faster scanning by find_index() ).
177 * NOTE: CSV does not require delimiters on numeric values; but since Windows
178 * doesn't do that anyways it's not supported here. Could be easy to add
179 * though...
181 char *subst_col(int colnum, char **lineref) {
182 char *col=NULL;
183 int i=0, pos=0, c;
184 int quotestate = 0;
186 /* We reached last counter on previour call, return NULL */
187 if (*lineref == NULL) return NULL;
189 /* First column always start here */
190 if (colnum == 0) col = *lineref;
192 while ((c=lineref[0][pos++]) != '\0') {
193 switch (c){
194 case ',':
195 if (!quotestate) {
196 i++;
197 *lineref = *lineref + pos;
198 pos = 0;
199 break;
201 case '"':
202 quotestate ^= 1;
203 break;
204 default:
205 do {
206 c = lineref[0][pos++];
207 } while (c != '\0' && c != (quotestate ? '"' : ','));
208 pos--;
209 continue;
212 /* continue at field boundary */
213 if (c != ',') continue;
215 if (i == colnum) {
216 /* found start of column */
217 col = *lineref;
218 } else if (col) {
219 /* End of column, terminate */
220 lineref[0][pos-1] = '\0';
221 break;
225 /* Not sure if this is needed, can't be too careful :) */
226 if (c == '\0') *lineref = NULL;
228 if (col) {
229 /* We're done, remove quotes... */
230 if (col[0] == '"' && col[strlen(col)-1] == '"') {
231 col++;
232 col[strlen(col)-1] = '\0';
234 return col;
236 return NULL;
239 /* Date string to time diff. Ex. string: "01/22/2008 07:49:19.798" */
240 int datediff(const char *datestr) {
241 char *array[7];
242 char *tmp;
243 char dup[25];
244 int i, month, day, year, hour, min, sec;
245 time_t *now=NULL;
246 struct tm *tmnow;
248 if (strlen(datestr) != 23)
249 return -1;
251 strncpy(dup, datestr, 24);
252 dup[23] = dup[24] = '\0';
253 tmp = array[0] = dup;
255 /* Loop over the string and replace separators with NULLs */
256 for (i=1; i<=6; i++) {
257 if ((tmp = strpbrk(tmp, "/ :.")) == NULL)
258 return -1;
260 tmp[0] = '\0';
261 tmp++;
262 array[i] = tmp;
265 month = myatoi(array[0]);
266 day = myatoi(array[1]);
267 year = myatoi(array[2]);
268 hour = myatoi(array[3]);
269 min = myatoi(array[4]);
270 sec = myatoi(array[5]);
272 *now = time(NULL);
273 tmnow = localtime(now);
275 tmnow->tm_mon++;
276 tmnow->tm_year += 1900;
278 /* Seconds out from Google Calculator. Those are rounded averages; we
279 * don't care about precision as we really shouldn't need to exceed one
280 * day. We care about correctness though (i.e. same date last month is
281 * NOT correct). */
282 return (tmnow->tm_year-year)*31556926
283 +(tmnow->tm_mon-month)*2629744
284 +(tmnow->tm_mday-day)*86400
285 +(tmnow->tm_hour-hour)*3600
286 +(tmnow->tm_min-min)*60
287 +(tmnow->tm_sec-sec);
290 /* like atoi with error checking */
291 int myatoi(const char *num) {
292 char *endptr;
293 long val;
295 errno = 0;
296 val = strtol(num, &endptr, 10);
298 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
299 perror("strtol");
300 exit(3);
302 if (val >= INT_MAX || val < 0) {
303 fprintf(stderr, "Number our of bounds\n");
304 exit(3);
307 if (endptr == num) {
308 fprintf(stderr, "No digits were found\n");
309 exit(EXIT_FAILURE);
312 return (int)val;