Initial rewrite of getlog in C
[thomas_code.git] / cacti / win_perf / getlog_base.c
blobd9003489a62c29945e7af34848deda189d93d06f
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;
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 char *tmpbuf;
87 int buf_sz = 0;
88 char *tmp1, *tmp2;
89 off_t length, start;
90 struct stat sb;
92 if (fstat(log, &sb) == -1) {
93 perror("fstat");
94 exit(3);
96 length = sb.st_size; /* Size in bytes */
98 /* Try to read up to READ_CHNK bytes at time, but make sure we read at
99 * 512-bytes boundaries. THIS IS TRICKY, don't change this unless you
100 * know what you're doing! */
101 start = (length / 512) * 512;
102 if (start >= READ_CHNK && start == length) {
103 start -= READ_CHNK;
104 } else if (start >= READ_CHNK) {
105 start -= READ_CHNK - 512;
106 } else {
107 start = 0;
110 /* Set our position and start reading */
111 lseek(log, start, SEEK_SET);
112 while (read_sz = read(log, readbuf, READ_CHNK)) {
113 if ((buf = realloc(buf, buf_sz+read_sz)) == NULL) {
114 fprintf(stderr, "malloc failed in get_head()\n");
115 exit(3);
118 /* Prepend to buffer - straight memcpy() if memory don't overlap */
119 if (read_sz == READ_CHNK)
120 memcpy(buf, buf+READ_CHNK, buf_sz);
121 else
122 memmove(buf, buf+read_sz, buf_sz);
123 memcpy(buf, readbuf, read_sz);
124 buf_sz += read_sz;
126 /* Terminate buf as a string if we got a full line */
127 if ((tmp1 = memchr(buf, '\n', buf_sz)) != NULL) {
128 if (tmp1 == buf+buf_sz) continue; /* last newline only */
130 /* Make sure we got the last line */
131 while ((tmp2 = memchr(tmp1+1, '\n', buf_sz-(tmp1-buf-1))) != NULL) {
132 if (tmp2 == buf+buf_sz) continue;
133 tmp1 = tmp2;
136 /* terminate tmp2 such as tmp1 becomes a string */
137 if (tmp2[-1] == '\r') tmp2--;
138 tmp2[0] = '\0';
139 break;
142 if (buf_sz >= MAX_READ) break; /* endless line? */
143 if ((start -= READ_CHNK) < 0) break;
144 lseek(log, start, SEEK_SET);
147 /* Return the last line if we got one */
148 if (tmp1 && tmp2 && tmp2 - tmp1 > 0) {
149 #ifdef MALLOC_FREE
150 tmp2 = strdup(tmp1);
151 free(buf);
152 tmp1 = tmp2;
153 #endif
154 return strdup(tmp1);
156 return NULL;
159 /* Return position of index `colname` on `line`. */
160 int find_index(const char *colname, char *line) {
161 char *col;
162 int i = 0;
164 while (line && (col = subst_col(0, &line)) != NULL) {
165 /* Skip over the server name (\\name) */
166 col = strchr(col+3, '\\');
167 if (strcmp(col, colname) == 0) return i;
168 i++;
170 return -1;
174 * Fetch the column indicated by colnum and remove the scanned part from
175 * lineref (this allow faster scanning by find_index() ).
176 * NOTE: CSV does not require delimiters on numeric values; but since Windows
177 * doesn't do that anyways it's not supported here. Could be easy to add
178 * though...
180 char *subst_col(int colnum, char **lineref) {
181 char *col, *delim;
182 int i;
184 for (i=0; i <= colnum; i++) {
185 delim = strchr(*lineref, ',');
187 if (delim == NULL) { /* this is the last column? */
188 col = strdup(*lineref);
189 /* Possible infinite loop is you leave data in there ?*/
190 //*lineref = NULL;
191 } else {
192 col = malloc(delim-*lineref+1);
193 strncpy(col, *lineref, delim-*lineref);
194 col[delim-*lineref] = '\0';
196 /* Look for starting and ending double-quotes... */
197 if (col[0] != '"' || col[strlen(col)] != '"') {
198 /* The field isn't properly delimited; try next comma and hope for the best */
199 if (!delim)
200 return NULL;
202 *lineref = delim + 1;
204 if (i == colnum) /* We're done, extract the current column */
205 break;
206 #ifdef MALLOC_FREE
207 free(col);
208 #else
209 col = NULL;
210 #endif
212 return col;
215 /* Date string to time diff. Ex. string: "01/22/2008 07:49:19.798" */
216 int datediff(const char *datestr) {
217 char *array[7];
218 char *tmp;
219 char dup[25];
220 int i, month, day, year, hour, min, sec;
221 time_t *now;
222 struct tm *tmnow;
224 if (strlen(datestr) != 23)
225 return -1;
227 strncpy(dup, datestr, 24);
228 dup[23] = dup[24] = '\0';
229 tmp = array[0] = dup;
231 /* Loop over the string and replace separators with NULLs */
232 for (i=1; i<=6; i++) {
233 if ((tmp = strpbrk(tmp, "/ :.")) == NULL)
234 return -1;
236 tmp[0] = '\0';
237 tmp++;
238 array[i] = tmp;
241 month = myatoi(array[0]);
242 day = myatoi(array[1]);
243 year = myatoi(array[2]);
244 hour = myatoi(array[3]);
245 min = myatoi(array[4]);
246 sec = myatoi(array[5]);
248 *now = time(NULL);
249 tmnow = localtime(now);
251 tmnow->tm_mon++;
252 tmnow->tm_year += 1900;
254 /* Seconds out from Google Calculator. Those are rounded averages; we
255 * don't care about precision as we really shouldn't need to exceed one
256 * day. We care about correctness though (i.e. same date last month is
257 * NOT correct). */
258 return (tmnow->tm_year-year)*31556926
259 +(tmnow->tm_mon-month)*2629744
260 +(tmnow->tm_mday-day)*86400
261 +(tmnow->tm_hour-hour)*3600
262 +(tmnow->tm_min-min)*60
263 +(tmnow->tm_sec-sec);
266 /* like atoi with error checking */
267 int myatoi(const char *num) {
268 char *endptr;
269 long val;
271 errno = 0;
272 val = strtol(num, &endptr, 10);
274 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
275 perror("strtol");
276 exit(3);
278 if (val >= INT_MAX || val < 0) {
279 fprintf(stderr, "Number our of bounds\n");
280 exit(3);
283 if (endptr == num) {
284 fprintf(stderr, "No digits were found\n");
285 exit(EXIT_FAILURE);
288 return (int)val;