1 /*****************************************************************************
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 *****************************************************************************/
24 #include <sys/types.h>
35 /* Get the first line of the file... easy stuff */
36 char *get_head(int log
) {
37 char readbuf
[READ_CHNK
];
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");
52 memcpy(buf
+buf_sz
, readbuf
, 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
--;
61 if (buf_sz
>= MAX_READ
) break; /* endless line? */
68 /* return whatever line de got, or NULL */
80 /* Get the last line of the file, reading backwards to make this quick on
82 char *get_tail(int log
) {
83 char readbuf
[READ_CHNK
];
87 char *tmp1
=NULL
, *tmp2
=NULL
;
91 if (fstat(log
, &sb
) == -1) {
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
) {
103 } else if (start
>= READ_CHNK
) {
104 start
-= READ_CHNK
- 512;
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");
117 /* Prepend to buffer - straight memcpy() if memory don't overlap */
119 memmove(buf
+read_sz
, buf
, buf_sz
);
120 memcpy(buf
, readbuf
, 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) {
132 /* terminate tmp2 such as tmp1 becomes a string */
136 if (tmp2
[-1] == '\r') tmp2
--;
138 tmp1
++; /* get past first '\n' */
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) {
160 /* Return position of index `colname` on `line`. */
161 int find_index(const char *colname
, char *line
) {
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
;
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
181 char *subst_col(int colnum
, char **lineref
) {
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') {
197 *lineref
= *lineref
+ pos
;
206 c
= lineref
[0][pos
++];
207 } while (c
!= '\0' && c
!= (quotestate
? '"' : ','));
212 /* continue at field boundary */
213 if (c
!= ',') continue;
216 /* found start of column */
219 /* End of column, terminate */
220 lineref
[0][pos
-1] = '\0';
225 /* Not sure if this is needed, can't be too careful :) */
226 if (c
== '\0') *lineref
= NULL
;
229 /* We're done, remove quotes... */
230 if (col
[0] == '"' && col
[strlen(col
)-1] == '"') {
232 col
[strlen(col
)-1] = '\0';
239 /* Date string to time diff. Ex. string: "01/22/2008 07:49:19.798" */
240 int datediff(const char *datestr
) {
244 int i
, month
, day
, year
, hour
, min
, sec
;
248 if (strlen(datestr
) != 23)
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
)
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]);
273 tmnow
= localtime(now
);
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
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
) {
296 val
= strtol(num
, &endptr
, 10);
298 if ((errno
== ERANGE
&& (val
== LONG_MAX
|| val
== LONG_MIN
)) || (errno
!= 0 && val
== 0)) {
302 if (val
>= INT_MAX
|| val
< 0) {
303 fprintf(stderr
, "Number our of bounds\n");
308 fprintf(stderr
, "No digits were found\n");