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
];
92 if (fstat(log
, &sb
) == -1) {
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
) {
104 } else if (start
>= READ_CHNK
) {
105 start
-= READ_CHNK
- 512;
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");
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
);
122 memmove(buf
, buf
+read_sz
, buf_sz
);
123 memcpy(buf
, readbuf
, 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;
136 /* terminate tmp2 such as tmp1 becomes a string */
137 if (tmp2
[-1] == '\r') tmp2
--;
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) {
159 /* Return position of index `colname` on `line`. */
160 int find_index(const char *colname
, char *line
) {
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
;
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
180 char *subst_col(int colnum
, char **lineref
) {
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 ?*/
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 */
202 *lineref
= delim
+ 1;
204 if (i
== colnum
) /* We're done, extract the current column */
215 /* Date string to time diff. Ex. string: "01/22/2008 07:49:19.798" */
216 int datediff(const char *datestr
) {
220 int i
, month
, day
, year
, hour
, min
, sec
;
224 if (strlen(datestr
) != 23)
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
)
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]);
249 tmnow
= localtime(now
);
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
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
) {
272 val
= strtol(num
, &endptr
, 10);
274 if ((errno
== ERANGE
&& (val
== LONG_MAX
|| val
== LONG_MIN
)) || (errno
!= 0 && val
== 0)) {
278 if (val
>= INT_MAX
|| val
< 0) {
279 fprintf(stderr
, "Number our of bounds\n");
284 fprintf(stderr
, "No digits were found\n");