Use explicit casts.
[metze/wireshark/wip.git] / ui / cli / tap-iostat.c
1 /* tap-iostat.c
2  * iostat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <stdio.h>
28
29 #include <string.h>
30 #include <epan/epan_dissect.h>
31 #include <epan/packet_info.h>
32 #include <epan/tap.h>
33 #include <epan/timestamp.h>
34 #include <epan/stat_cmd_args.h>
35 #include <epan/strutil.h>
36 #include "globals.h"
37
38 #define CALC_TYPE_FRAMES 0
39 #define CALC_TYPE_BYTES  1
40 #define CALC_TYPE_FRAMES_AND_BYTES 2
41 #define CALC_TYPE_COUNT  3
42 #define CALC_TYPE_SUM    4
43 #define CALC_TYPE_MIN    5
44 #define CALC_TYPE_MAX    6
45 #define CALC_TYPE_AVG    7
46 #define CALC_TYPE_LOAD   8
47
48 typedef struct {
49     const char *func_name;
50     int calc_type;
51 } calc_type_ent_t;
52
53 static calc_type_ent_t calc_type_table[] = {
54     { "FRAMES", CALC_TYPE_FRAMES },
55     { "BYTES", CALC_TYPE_BYTES },
56     { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
57     { "COUNT", CALC_TYPE_COUNT },
58     { "SUM", CALC_TYPE_SUM },
59     { "MIN", CALC_TYPE_MIN },
60     { "MAX", CALC_TYPE_MAX },
61     { "AVG", CALC_TYPE_AVG },
62     { "LOAD", CALC_TYPE_LOAD },
63     { NULL, 0 }
64 };
65
66 typedef struct _io_stat_t {
67     guint64 interval;     /* The user-specified time interval (us) */
68     guint invl_prec;      /* Decimal precision of the time interval (1=10s, 2=100s etc) */
69     guint32 num_cols;     /* The number of columns of statistics in the table */
70     struct _io_stat_item_t *items;  /* Each item is a single cell in the table */
71     time_t start_time;    /* Time of first frame matching the filter */
72     const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
73     guint64 *max_vals;    /* The max value sans the decimal or nsecs portion in each stat column */
74     guint32 *max_frame;   /* The max frame number displayed in each stat column */
75 } io_stat_t;
76
77 typedef struct _io_stat_item_t {
78     io_stat_t *parent;
79     struct _io_stat_item_t *next;
80     struct _io_stat_item_t *prev;
81     guint64 time;         /* Time since start of capture (us)*/
82     int calc_type;        /* The statistic type */
83     int colnum;           /* Column number of this stat (0 to n) */
84     int hf_index;
85     guint32 frames;
86     guint32 num;          /* The sample size of a given statistic (only needed for AVG) */
87     guint64 counter;      /* The accumulated data for the calculation of that statistic */
88     gfloat float_counter;
89     gdouble double_counter;
90 } io_stat_item_t;
91
92 #define NANOSECS_PER_SEC 1000000000
93
94 static int
95 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
96 {
97     io_stat_t *parent;
98     io_stat_item_t *mit;
99     io_stat_item_t *it;
100     guint64 relative_time, rt;
101     nstime_t *new_time;
102     GPtrArray *gp;
103     guint i;
104     int ftype;
105
106     mit = (io_stat_item_t *) arg;
107     parent = mit->parent;
108     relative_time = (guint64)((pinfo->fd->rel_ts.secs*1000000) + ((pinfo->fd->rel_ts.nsecs+500)/1000));
109     if (mit->parent->start_time == 0) {
110         mit->parent->start_time = pinfo->fd->abs_ts.secs - pinfo->fd->rel_ts.secs;
111     }
112
113     /* The prev item before the main one is always the last interval we saw packets for */
114     it = mit->prev;
115
116     /* XXX for the time being, just ignore all frames that are in the past.
117        should be fixed in the future but hopefully it is uncommon */
118     if(relative_time < it->time){
119         return FALSE;
120     }
121
122     /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
123     *  between the last struct and this one. If an item was not found in a previous interval, an empty
124     *  struct will be created for it. */
125     rt = relative_time;
126     while (rt >= it->time + parent->interval) {
127         it->next = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t));
128         it->next->prev = it;
129         it->next->next = NULL;
130         it = it->next;
131         mit->prev = it;
132
133         it->time = it->prev->time + parent->interval;
134         it->frames = 0;
135         it->counter = 0;
136         it->float_counter = 0;
137         it->double_counter = 0;
138         it->num = 0;
139         it->calc_type = it->prev->calc_type;
140         it->hf_index = it->prev->hf_index;
141         it->colnum = it->prev->colnum;
142     }
143
144     /* Store info in the current structure */
145     it->frames++;
146
147     switch(it->calc_type) {
148     case CALC_TYPE_FRAMES:
149     case CALC_TYPE_BYTES:
150     case CALC_TYPE_FRAMES_AND_BYTES:
151         it->counter += pinfo->fd->pkt_len;
152         break;
153     case CALC_TYPE_COUNT:
154         gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
155         if(gp){
156             it->counter += gp->len;
157         }
158         break;
159     case CALC_TYPE_SUM:
160         gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
161         if(gp){
162             guint64 val;
163
164             for(i=0;i<gp->len;i++){
165                 switch(proto_registrar_get_ftype(it->hf_index)){
166                 case FT_UINT8:
167                 case FT_UINT16:
168                 case FT_UINT24:
169                 case FT_UINT32:
170                     it->counter += fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
171                     break;
172                 case FT_UINT64:
173                     it->counter += fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
174                     break;
175                 case FT_INT8:
176                 case FT_INT16:
177                 case FT_INT24:
178                 case FT_INT32:
179                     it->counter += fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
180                     break;
181                 case FT_INT64:
182                     it->counter += (gint64)fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
183                     break;
184                 case FT_FLOAT:
185                     it->float_counter +=
186                         (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
187                     break;
188                 case FT_DOUBLE:
189                     it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
190                     break;
191                 case FT_RELATIVE_TIME:
192                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
193                     val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
194                     it->counter  +=  val;
195                     break;
196                 default:
197                     /*
198                      * "Can't happen"; see the checks
199                      * in register_io_tap().
200                      */
201                     g_assert_not_reached();
202                     break;
203                 }
204             }
205         }
206         break;
207     case CALC_TYPE_MIN:
208         gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
209         if(gp){
210             guint64 val;
211             gfloat float_val;
212             gdouble double_val;
213
214             ftype=proto_registrar_get_ftype(it->hf_index);
215             for(i=0;i<gp->len;i++){
216                 switch(ftype){
217                 case FT_UINT8:
218                 case FT_UINT16:
219                 case FT_UINT24:
220                 case FT_UINT32:
221                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
222                     if ((it->frames==1 && i==0) || (val < it->counter)) {
223                         it->counter=val;
224                     }
225                     break;
226                 case FT_UINT64:
227                     val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
228                     if((it->frames==1 && i==0) || (val < it->counter)){
229                         it->counter=val;
230                     }
231                     break;
232                 case FT_INT8:
233                 case FT_INT16:
234                 case FT_INT24:
235                 case FT_INT32:
236                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
237                     if((it->frames==1 && i==0) || ((gint32)val < (gint32)it->counter)) {
238                         it->counter=val;
239                     }
240                     break;
241                 case FT_INT64:
242                     val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
243                     if((it->frames==1 && i==0) || ((gint64)val < (gint64)it->counter)) {
244                         it->counter=val;
245                     }
246                     break;
247                 case FT_FLOAT:
248                     float_val=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
249                     if((it->frames==1 && i==0) || (float_val < it->float_counter)) {
250                         it->float_counter=float_val;
251                     }
252                     break;
253                 case FT_DOUBLE:
254                     double_val=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
255                     if((it->frames==1 && i==0) || (double_val < it->double_counter)) {
256                         it->double_counter=double_val;
257                     }
258                     break;
259                 case FT_RELATIVE_TIME:
260                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
261                     val = (guint64)new_time->secs * NANOSECS_PER_SEC + new_time->nsecs;
262                     if((it->frames==1 && i==0) || (val < it->counter)) {
263                         it->counter=val;
264                     }
265                     break;
266                 default:
267                     /*
268                      * "Can't happen"; see the checks
269                      * in register_io_tap().
270                      */
271                     g_assert_not_reached();
272                     break;
273                 }
274             }
275         }
276         break;
277     case CALC_TYPE_MAX:
278         gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
279         if(gp){
280             guint64 val;
281             gfloat float_val;
282             gdouble double_val;
283
284             ftype=proto_registrar_get_ftype(it->hf_index);
285             for(i=0;i<gp->len;i++){
286                 switch(ftype){
287                 case FT_UINT8:
288                 case FT_UINT16:
289                 case FT_UINT24:
290                 case FT_UINT32:
291                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
292                     if(val > it->counter)
293                         it->counter=val;
294                     break;
295                 case FT_UINT64:
296                     val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
297                     if(val > it->counter)
298                         it->counter=val;
299                     break;
300                 case FT_INT8:
301                 case FT_INT16:
302                 case FT_INT24:
303                 case FT_INT32:
304                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
305                     if((gint32)val > (gint32)it->counter)
306                         it->counter=val;
307                     break;
308                 case FT_INT64:
309                     val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
310                     if ((gint64)val > (gint64)it->counter)
311                         it->counter=val;
312                     break;
313                 case FT_FLOAT:
314                     float_val = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
315                     if(float_val > it->float_counter)
316                         it->float_counter=float_val;
317                     break;
318                 case FT_DOUBLE:
319                     double_val = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
320                     if(double_val > it->double_counter)
321                         it->double_counter=double_val;
322                     break;
323                 case FT_RELATIVE_TIME:
324                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
325                     val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
326                     if (val>it->counter)
327                         it->counter=val;
328                     break;
329                 default:
330                     /*
331                      * "Can't happen"; see the checks
332                      * in register_io_tap().
333                      */
334                     g_assert_not_reached();
335                     break;
336                 }
337             }
338         }
339         break;
340     case CALC_TYPE_AVG:
341         gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
342         if(gp){
343             guint64 val;
344
345             ftype=proto_registrar_get_ftype(it->hf_index);
346             for(i=0;i<gp->len;i++){
347                 it->num++;
348                 switch(ftype) {
349                 case FT_UINT8:
350                 case FT_UINT16:
351                 case FT_UINT24:
352                 case FT_UINT32:
353                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
354                     it->counter += val;
355                     break;
356                 case FT_UINT64:
357                 case FT_INT64:
358                     val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
359                     it->counter += val;
360                     break;
361                 case FT_INT8:
362                 case FT_INT16:
363                 case FT_INT24:
364                 case FT_INT32:
365                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
366                     it->counter += val;
367                     break;
368                 case FT_FLOAT:
369                     it->float_counter += (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
370                     break;
371                 case FT_DOUBLE:
372                     it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
373                     break;
374                 case FT_RELATIVE_TIME:
375                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
376                     val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
377                     it->counter += val;
378                     break;
379                 default:
380                     /*
381                      * "Can't happen"; see the checks
382                      * in register_io_tap().
383                      */
384                     g_assert_not_reached();
385                     break;
386                 }
387             }
388         }
389         break;
390     case CALC_TYPE_LOAD:
391         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
392         if (gp) {
393             ftype = proto_registrar_get_ftype(it->hf_index);
394             if (ftype != FT_RELATIVE_TIME) {
395                 fprintf(stderr,
396                     "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
397                 exit(10);
398             }
399             for(i=0;i<gp->len;i++){
400                 guint64 val;
401                 int tival;
402                 io_stat_item_t *pit;
403
404                 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
405                 val = (guint64)((new_time->secs*1000000) + (new_time->nsecs/1000));
406                 tival = (int)(val % parent->interval);
407                 it->counter += tival;
408                 val -= tival;
409                 pit = it->prev;
410                 while (val > 0) {
411                     if (val < (guint64)parent->interval) {
412                         pit->counter += val;
413                         break;
414                     }
415                     pit->counter += parent->interval;
416                     val -= parent->interval;
417                     pit = pit->prev;
418                 }
419             }
420         }
421         break;
422     }
423     /* Store the highest value for this item in order to determine the width of each stat column.
424     *  For real numbers we only need to know its magnitude (the value to the left of the decimal point
425     *  so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
426     *  calc the average, round it to the next second and store the seconds. For all other calc types
427     *  of RELATIVE_TIME fields, store the counters without modification.
428     *  fields. */
429     switch(it->calc_type) {
430         case CALC_TYPE_FRAMES:
431         case CALC_TYPE_FRAMES_AND_BYTES:
432             parent->max_frame[it->colnum] =
433                 MAX(parent->max_frame[it->colnum], it->frames);
434             if (it->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
435                 parent->max_vals[it->colnum] =
436                     MAX(parent->max_vals[it->colnum], it->counter);
437
438         case CALC_TYPE_BYTES:
439         case CALC_TYPE_COUNT:
440         case CALC_TYPE_LOAD:
441             parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
442             break;
443         case CALC_TYPE_SUM:
444         case CALC_TYPE_MIN:
445         case CALC_TYPE_MAX:
446             ftype=proto_registrar_get_ftype(it->hf_index);
447             switch(ftype) {
448                 case FT_FLOAT:
449                     parent->max_vals[it->colnum] =
450                         MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
451                     break;
452                 case FT_DOUBLE:
453                     parent->max_vals[it->colnum] =
454                         MAX(parent->max_vals[it->colnum],(guint64)(it->double_counter+0.5));
455                     break;
456                 case FT_RELATIVE_TIME:
457                     parent->max_vals[it->colnum] =
458                         MAX(parent->max_vals[it->colnum], it->counter);
459                     break;
460                 default:
461                     /* UINT16-64 and INT8-64 */
462                     parent->max_vals[it->colnum] =
463                         MAX(parent->max_vals[it->colnum], it->counter);
464                     break;
465             }
466             break;
467         case CALC_TYPE_AVG:
468             if (it->num==0) /* avoid division by zero */
469                break;
470             ftype=proto_registrar_get_ftype(it->hf_index);
471             switch(ftype) {
472                 case FT_FLOAT:
473                     parent->max_vals[it->colnum] =
474                         MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
475                     break;
476                 case FT_DOUBLE:
477                     parent->max_vals[it->colnum] =
478                         MAX(parent->max_vals[it->colnum],(guint64)it->double_counter/it->num);
479                     break;
480                 case FT_RELATIVE_TIME:
481                     parent->max_vals[it->colnum] =
482                         MAX(parent->max_vals[it->colnum], ((it->counter/it->num) + 500000000) / NANOSECS_PER_SEC);
483                     break;
484                 default:
485                     /* UINT16-64 and INT8-64 */
486                     parent->max_vals[it->colnum] =
487                         MAX(parent->max_vals[it->colnum], it->counter/it->num);
488                     break;
489             }
490     }
491     return TRUE;
492 }
493
494 static int
495 magnitude (guint64 val, int max_w)
496 {
497     int i, mag=0;
498
499     for (i=0; i<max_w; i++) {
500         mag++;
501         if ((val /= 10)==0)
502             break;
503     }
504     return(mag);
505 }
506
507 /*
508 *  Print the calc_type_table[] function label centered in the column header.
509 */
510 static void
511 printcenter (const char *label, int lenval, int numpad)
512 {
513     int lenlab = (int) strlen(label), len;
514     const char spaces[]="      ", *spaces_ptr;
515
516     len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
517     if (len > 0 && len < 6) {
518         spaces_ptr = &spaces[len];
519         if ((lenval-lenlab)%2==0) {
520             printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
521         } else {
522             printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
523         }
524     } else if (len > 0 && len <= 15) {
525         printf("%s|", label);
526     }
527 }
528
529 typedef struct {
530     int fr;  /* Width of this FRAMES column sans padding and border chars */
531     int val; /* Width of this non-FRAMES column sans padding and border chars */
532 } column_width;
533
534 static void
535 iostat_draw(void *arg)
536 {
537     guint32 num;
538     guint64 interval, duration, t, invl_end;
539     int i, j, k, num_cols, num_rows, dv, dur_secs, dur_mag, invl_mag, invl_prec, tabrow_w,
540         borderlen, invl_col_w, numpad=1, namelen, len_filt, type, maxfltr_w, ftype;
541     int fr_mag;    /* The magnitude of the max frame number in this column */
542     int val_mag;   /* The magnitude of the max value in this column */
543     char *spaces, *spaces_s, *filler_s=NULL, **fmts, *fmt=NULL;
544     const char *filter;
545     static gchar dur_mag_s[3], invl_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
546     io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
547     gboolean last_row=FALSE;
548     io_stat_t *iot;
549     column_width *col_w;
550     struct tm * tm_time;
551     time_t the_time;
552
553     mit = (io_stat_item_t *)arg;
554     iot = mit->parent;
555     num_cols = iot->num_cols;
556     col_w = (column_width *)g_malloc(sizeof(column_width) * num_cols);
557     fmts = (char **)g_malloc(sizeof(char *) * num_cols);
558     duration = (guint64)((cfile.elapsed_time.secs*1000000) + ((cfile.elapsed_time.nsecs+500)/1000));
559
560     /* Store the pointer to each stat column */
561     stat_cols = (io_stat_item_t **) g_malloc(sizeof(io_stat_item_t *) * num_cols);
562     for (j=0; j<num_cols; j++)
563         stat_cols[j] = &iot->items[j];
564
565     /* The following prevents gross inaccuracies when the user specifies an interval that is greater
566     *  than the capture duration. */
567     if (iot->interval > duration || iot->interval==G_MAXINT32) {
568         interval = duration;
569         iot->interval = G_MAXINT32;
570     } else {
571         interval = iot->interval;
572     }
573
574     /* Calc the capture duration's magnitude (dur_mag) */
575     dur_secs = (int)duration/1000000;
576     dur_mag = magnitude((guint64)dur_secs, 5);
577     g_snprintf(dur_mag_s, 3, "%u", dur_mag);
578
579     /* Calc the interval's magnitude */
580     invl_mag = magnitude((guint64)interval/1000000, 5);
581
582     /* Set or get the interval precision */
583     if (interval==duration) {
584         /*
585         * An interval arg of 0 or an interval size exceeding the capture duration was specified.
586         * Set the decimal precision of duration based on its magnitude. */
587         if (dur_mag >= 2)
588             invl_prec = 1;
589         else if (dur_mag==1)
590             invl_prec = 3;
591         else
592             invl_prec = 6;
593
594         borderlen = 30 + dur_mag + (invl_prec==0 ? 0 : invl_prec+1);
595     } else {
596         invl_prec = iot->invl_prec;
597         borderlen = 24 + invl_mag + (invl_prec==0 ? 0 : invl_prec+1);
598     }
599
600     /* Round the duration according to invl_prec */
601     dv=1000000;
602     for (i=0; i<invl_prec; i++)
603         dv /= 10;
604     duration = duration + (5*(dv/10));
605     if (iot->interval==G_MAXINT32)
606         interval = duration;
607
608     /* Recalc the dur_mag in case rounding has increased its magnitude */
609     dur_secs = (int)duration/1000000;
610     dur_mag = magnitude((guint64)dur_secs, 5);
611
612     /* Calc the width of the time interval column (incl borders and padding). */
613     if (invl_prec==0)
614         invl_col_w = (2*dur_mag) + 8;
615     else
616         invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
617
618     /* Update the width of the time interval column for "-t ad" */
619     if (timestamp_get_type()==TS_ABSOLUTE_WITH_DATE)
620         invl_col_w = MAX(invl_col_w, 23);
621     else
622         invl_col_w = MAX(invl_col_w, 12);
623
624     borderlen = MAX(borderlen, invl_col_w);
625
626     /* Calc the total width of each row in the stats table and build the printf format string for each
627     *  column based on its field type, width, and name length.
628     *  NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
629     *        is an *integer*. */
630     tabrow_w = invl_col_w;
631     for (j=0; j<num_cols; j++) {
632         type = iot->items[j].calc_type;
633         if (type==CALC_TYPE_FRAMES_AND_BYTES) {
634             namelen = 5;
635         } else {
636             namelen = (int) strlen(calc_type_table[type].func_name);
637         }
638         if(type==CALC_TYPE_FRAMES
639         || type==CALC_TYPE_FRAMES_AND_BYTES) {
640
641             fr_mag = magnitude(iot->max_frame[j], 15);
642             fr_mag = MAX(6, fr_mag);
643             col_w[j].fr = fr_mag;
644             tabrow_w += col_w[j].fr + 3;
645             g_snprintf(fr_mag_s, 3, "%u", fr_mag);
646
647             if (type==CALC_TYPE_FRAMES) {
648                 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
649             } else {
650                 /* CALC_TYPE_FRAMES_AND_BYTES
651                 */
652                 val_mag = magnitude(iot->max_vals[j], 15);
653                 val_mag = MAX(5, val_mag);
654                 col_w[j].val = val_mag;
655                 tabrow_w += (col_w[j].val + 3);
656                 g_snprintf(val_mag_s, 3, "%u", val_mag);
657                 fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
658             }
659             if (fmt)
660                 fmts[j] = fmt;
661             continue;
662         }
663         switch(type) {
664         case CALC_TYPE_BYTES:
665         case CALC_TYPE_COUNT:
666
667             val_mag = magnitude(iot->max_vals[j], 15);
668             val_mag = MAX(5, val_mag);
669             col_w[j].val = val_mag;
670             g_snprintf(val_mag_s, 3, "%u", val_mag);
671             fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
672             break;
673
674         default:
675             ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
676             switch (ftype) {
677                 case FT_FLOAT:
678                 case FT_DOUBLE:
679                     val_mag = magnitude(iot->max_vals[j], 15);
680                     g_snprintf(val_mag_s, 3, "%u", val_mag);
681                     fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
682                     col_w[j].val = val_mag + 7;
683                     break;
684                 case FT_RELATIVE_TIME:
685                     /* Convert FT_RELATIVE_TIME field to seconds
686                     *  CALC_TYPE_LOAD was already converted in iostat_packet() ) */
687                     if (type==CALC_TYPE_LOAD) {
688                         iot->max_vals[j] /= interval;
689                     } else if (type != CALC_TYPE_AVG) {
690                         iot->max_vals[j] = (iot->max_vals[j] + 500000000) / NANOSECS_PER_SEC;
691                     }
692                     val_mag = magnitude(iot->max_vals[j], 15);
693                     g_snprintf(val_mag_s, 3, "%u", val_mag);
694                     fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
695                     col_w[j].val = val_mag + 7;
696                    break;
697
698                 default:
699                     val_mag = magnitude(iot->max_vals[j], 15);
700                     val_mag = MAX(namelen, val_mag);
701                     col_w[j].val = val_mag;
702                     g_snprintf(val_mag_s, 3, "%u", val_mag);
703
704                     switch (ftype) {
705                     case FT_UINT8:
706                     case FT_UINT16:
707                     case FT_UINT24:
708                     case FT_UINT32:
709                     case FT_UINT64:
710                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
711                         break;
712                     case FT_INT8:
713                     case FT_INT16:
714                     case FT_INT24:
715                     case FT_INT32:
716                     case FT_INT64:
717                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "d |", NULL);
718                         break;
719                     }
720             } /* End of ftype switch */
721         } /* End of calc_type switch */
722         tabrow_w += col_w[j].val + 3;
723         if (fmt)
724             fmts[j] = fmt;
725     } /* End of for loop (columns) */
726
727     borderlen = MAX(borderlen, tabrow_w);
728
729     /* Calc the max width of the list of filters. */
730     maxfltr_w = 0;
731     for(j=0; j<num_cols; j++) {
732         if (iot->filters[j]) {
733             k = (int) (strlen(iot->filters[j]) + 11);
734             maxfltr_w = MAX(maxfltr_w, k);
735         } else {
736             maxfltr_w = MAX(maxfltr_w, 26);
737         }
738     }
739     /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
740     *  (which currently = borderlen); however, if the filter width exceeds the table width and the
741     *  table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
742     *  The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
743     *  NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
744     *  XXX: A pref could be added to change the max width from the default size of 102. */
745     if (maxfltr_w > borderlen && borderlen < 102)
746             borderlen = MIN(maxfltr_w, 102);
747
748     /* Prevent double right border by adding a space */
749     if (borderlen-tabrow_w==1)
750         borderlen++;
751
752     /* Display the top border */
753     printf("\n");
754     for (i=0; i<borderlen; i++)
755         printf("=");
756
757     spaces = (char*) g_malloc(borderlen+1);
758     for (i=0; i<borderlen; i++)
759         spaces[i] = ' ';
760     spaces[borderlen] = '\0';
761
762     spaces_s = &spaces[16];
763     printf("\n| IO Statistics%s|\n", spaces_s);
764     spaces_s = &spaces[2];
765     printf("|%s|\n", spaces_s);
766
767     g_snprintf(invl_mag_s, 3, "%u", invl_mag);
768     if (invl_prec > 0) {
769         g_snprintf(invl_prec_s, 3, "%u", invl_prec);
770         invl_fmt = g_strconcat("%", invl_mag_s, "u.%0", invl_prec_s, "u", NULL);
771         if (interval==duration) {
772             full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs (dur)%s", NULL);
773             spaces_s = &spaces[30+invl_mag+invl_prec];
774         } else {
775             full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
776             spaces_s = &spaces[24+invl_mag+invl_prec];
777         }
778         printf(full_fmt, (guint32)interval/1000000,
779                             (guint32)((interval%1000000)/dv), spaces_s);
780     } else {
781         invl_fmt = g_strconcat("%", invl_mag_s, "u", NULL);
782         full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
783         spaces_s = &spaces[23 + invl_mag];
784         printf(full_fmt, (guint32)interval/1000000, spaces_s);
785     }
786     g_free(invl_fmt);
787     g_free(full_fmt);
788
789     if (invl_prec > 0)
790         invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
791     else
792         invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
793
794     /* Display the list of filters and their column numbers vertically */
795     printf("|\n| Col");
796     for(j=0; j<num_cols; j++){
797         printf((j==0 ? "%2u: " : "|    %2u: "), j+1);
798         if (!iot->filters[j] || (iot->filters[j]==0)) {
799             /*
800             * An empty (no filter) comma field was specified */
801             spaces_s = &spaces[16 + 10];
802             printf("Frames and bytes%s|\n", spaces_s);
803         } else {
804             filter = iot->filters[j];
805             len_filt = (int) strlen(filter);
806
807             /* If the width of the widest filter exceeds the width of the stat table, borderlen has
808             *  been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
809             if (len_filt+11 <= borderlen) {
810                 printf("%s", filter);
811                 if (len_filt+11 <= borderlen) {
812                     spaces_s = &spaces[len_filt + 10];
813                     printf("%s", spaces_s);
814                 }
815                 printf("|\n");
816             } else {
817                 gchar *sfilter1, *sfilter2;
818                 const gchar *pos;
819                 gsize len;
820                 int next_start, max_w=borderlen-11;
821
822                 do {
823                     if (len_filt > max_w) {
824                         sfilter1 = g_strndup( (gchar *) filter, (gsize) max_w);
825                         /*
826                         * Find the pos of the last space in sfilter1. If a space is found, set
827                         * sfilter2 to the string prior to that space and print it; otherwise, wrap
828                         * the filter at max_w. */
829                         pos = g_strrstr(sfilter1, " ");
830                         if (pos) {
831                             len = (gsize)(pos-sfilter1);
832                             next_start = (int) len+1;
833                         } else {
834                             len = (gsize) strlen(sfilter1);
835                             next_start = (int)len;
836                         }
837                         sfilter2 = g_strndup(sfilter1, len);
838                         printf("%s%s|\n", sfilter2, &spaces[len+10]);
839                         g_free(sfilter1);
840                         g_free(sfilter2);
841
842                         printf("|        ");
843                         filter = &filter[next_start];
844                         len_filt = (int) strlen(filter);
845                     } else {
846                         printf("%s%s|\n", filter, &spaces[((int)strlen(filter))+10]);
847                         break;
848                     }
849                 } while (1);
850             }
851         }
852     }
853
854     printf("|-");
855     for(i=0;i<borderlen-3;i++){
856         printf("-");
857     }
858     printf("|\n");
859
860     /* Display spaces above "Interval (s)" label */
861     spaces_s = &spaces[borderlen-(invl_col_w-2)];
862     printf("|%s|", spaces_s);
863
864     /* Display column number headers */
865     for(j=0; j<num_cols; j++) {
866         item = stat_cols[j];
867         if(item->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
868             spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
869         else if (item->calc_type==CALC_TYPE_FRAMES)
870             spaces_s = &spaces[borderlen - col_w[j].fr];
871         else
872             spaces_s = &spaces[borderlen - col_w[j].val];
873
874         printf("%-2u%s|", j+1, spaces_s);
875     }
876     if (tabrow_w < borderlen) {
877         filler_s = &spaces[tabrow_w+1];
878         printf("%s|", filler_s);
879     }
880
881     k = 11;
882     switch (timestamp_get_type()) {
883     case TS_ABSOLUTE:
884         printf("\n| Time    ");
885         break;
886     case TS_ABSOLUTE_WITH_DATE:
887         printf("\n| Date and time");
888         k = 16;
889         break;
890     case TS_RELATIVE:
891     case TS_NOT_SET:
892         printf("\n| Interval");
893         break;
894     default:
895         break;
896     }
897
898     spaces_s = &spaces[borderlen-(invl_col_w-k)];
899     printf("%s|", spaces_s);
900
901     /* Display the stat label in each column */
902     for(j=0; j<num_cols; j++) {
903         type = stat_cols[j]->calc_type;
904         if(type==CALC_TYPE_FRAMES) {
905             printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
906         } else if (type==CALC_TYPE_FRAMES_AND_BYTES) {
907             printcenter ("Frames", col_w[j].fr, numpad);
908             printcenter ("Bytes", col_w[j].val, numpad);
909         } else {
910             printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
911         }
912     }
913     if (filler_s)
914         printf("%s|", filler_s);
915     printf("\n|-");
916
917     for(i=0; i<tabrow_w-3; i++)
918         printf("-");
919     printf("|");
920
921     if (tabrow_w < borderlen)
922         printf("%s|", &spaces[tabrow_w+1]);
923
924     printf("\n");
925     t=0;
926     full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
927     num_rows = (int)(duration/interval) + (((duration%interval+500000)/1000000) > 0 ? 1 : 0);
928
929     /* Load item_in_column with the first item in each column */
930     item_in_column = (io_stat_item_t **) g_malloc(sizeof(io_stat_item_t) * num_cols);
931     for (j=0; j<num_cols; j++) {
932         item_in_column[j] = stat_cols[j];
933     }
934
935     /* Display the table values
936     *
937     * The outer loop is for time interval rows and the inner loop is for stat column items.*/
938     for (i=0; i<num_rows; i++) {
939
940         if (i==num_rows-1)
941             last_row = TRUE;
942
943         /* Compute the interval for this row */
944         if (!last_row) {
945             invl_end = t + interval;
946         } else {
947             invl_end = duration;
948         }
949
950         /* Patch for Absolute Time */
951         the_time=iot->start_time+(guint32)(t/1000000);
952         tm_time = localtime(&the_time);
953
954         /* Display the interval for this row */
955         switch (timestamp_get_type()) {
956         case TS_ABSOLUTE:
957           printf("| %02d:%02d:%02d |",
958              tm_time->tm_hour,
959              tm_time->tm_min,
960              tm_time->tm_sec);
961           break;
962
963         case TS_ABSOLUTE_WITH_DATE:
964           printf("| %04d-%02d-%02d %02d:%02d:%02d |",
965              tm_time->tm_year + 1900,
966              tm_time->tm_mon + 1,
967              tm_time->tm_mday,
968              tm_time->tm_hour,
969              tm_time->tm_min,
970              tm_time->tm_sec);
971           break;
972
973         case TS_RELATIVE:
974         case TS_NOT_SET:
975
976           if (invl_prec==0) {
977               printf(full_fmt, (guint32)(t/1000000),
978                                (guint32)(invl_end/1000000));
979           } else {
980               printf(full_fmt, (guint32)(t/1000000),
981                                (guint32)(t%1000000) / dv,
982                                (guint32) (invl_end/1000000),
983                                (guint32)((invl_end%1000000) / dv));
984           }
985           break;
986      /* case TS_DELTA:
987         case TS_DELTA_DIS:
988         case TS_EPOCH:
989         case TS_UTC:
990         case TS_UTC_WITH_DATE:
991             are not implemented */
992         default:
993           break;
994         }
995
996         /* Display stat values in each column for this row */
997         for (j=0; j<num_cols; j++) {
998             fmt = fmts[j];
999             item = item_in_column[j];
1000
1001             if (item) {
1002                 switch(item->calc_type) {
1003                 case CALC_TYPE_FRAMES:
1004                     printf(fmt, item->frames);
1005                     break;
1006                 case CALC_TYPE_BYTES:
1007                 case CALC_TYPE_COUNT:
1008                     printf(fmt, item->counter);
1009                     break;
1010                 case CALC_TYPE_FRAMES_AND_BYTES:
1011                     printf(fmt, item->frames, item->counter);
1012                     break;
1013
1014                 case CALC_TYPE_SUM:
1015                 case CALC_TYPE_MIN:
1016                 case CALC_TYPE_MAX:
1017                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1018                     switch(ftype){
1019                     case FT_FLOAT:
1020                         printf(fmt, item->float_counter);
1021                         break;
1022                     case FT_DOUBLE:
1023                         printf(fmt, item->double_counter);
1024                         break;
1025                     case FT_RELATIVE_TIME:
1026                         item->counter = (item->counter + 500) / 1000;
1027                         printf(fmt, (int)(item->counter/1000000), (int)(item->counter%1000000));
1028                         break;
1029                     default:
1030                         printf(fmt, item->counter);
1031                         break;
1032                     }
1033                     break;
1034
1035                 case CALC_TYPE_AVG:
1036                     num = item->num;
1037                     if(num==0)
1038                         num=1;
1039                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1040                     switch(ftype){
1041                     case FT_FLOAT:
1042                         printf(fmt, item->float_counter/num);
1043                         break;
1044                     case FT_DOUBLE:
1045                         printf(fmt, item->double_counter/num);
1046                         break;
1047                     case FT_RELATIVE_TIME:
1048                         item->counter = ((item->counter/num) + 500) / 1000;
1049                         printf(fmt,
1050                             (int)(item->counter/1000000), (int)(item->counter%1000000));
1051                         break;
1052                     default:
1053                         printf(fmt, item->counter/num);
1054                         break;
1055                     }
1056                     break;
1057
1058                 case CALC_TYPE_LOAD:
1059                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1060                     switch(ftype){
1061                     case FT_RELATIVE_TIME:
1062                         if (!last_row) {
1063                             printf(fmt,
1064                                 (int) (item->counter/interval),
1065                                 (int)((item->counter%interval)*1000000 / interval));
1066                         } else {
1067                             printf(fmt,
1068                                 (int) (item->counter/(invl_end-t)),
1069                                 (int)((item->counter%(invl_end-t))*1000000 / (invl_end-t)));
1070                         }
1071                         break;
1072                     }
1073                     break;
1074                 }
1075
1076                 if (last_row) {
1077                     if (fmt)
1078                         g_free(fmt);
1079                 } else {
1080                     item_in_column[j] = item_in_column[j]->next;
1081                 }
1082             } else {
1083                 printf(fmt, (guint64)0);
1084             }
1085         }
1086         if (filler_s)
1087             printf("%s|", filler_s);
1088         printf("\n");
1089         t += interval;
1090
1091     }
1092     for(i=0;i<borderlen;i++){
1093         printf("=");
1094     }
1095     printf("\n");
1096     g_free(iot->items);
1097     g_free(iot->max_vals);
1098     g_free(iot->max_frame);
1099     g_free(iot);
1100     g_free(col_w);
1101     g_free(invl_fmt);
1102     g_free(full_fmt);
1103     g_free(fmts);
1104     g_free(spaces);
1105     g_free(stat_cols);
1106     g_free(item_in_column);
1107 }
1108
1109
1110 static void
1111 register_io_tap(io_stat_t *io, int i, const char *filter)
1112 {
1113     GString *error_string;
1114     const char *flt;
1115     int j;
1116     size_t namelen;
1117     const char *p, *parenp;
1118     char *field;
1119     header_field_info *hfi;
1120
1121     io->items[i].prev=&io->items[i];
1122     io->items[i].next=NULL;
1123     io->items[i].parent=io;
1124     io->items[i].time=0;
1125     io->items[i].calc_type=CALC_TYPE_FRAMES_AND_BYTES;
1126     io->items[i].frames=0;
1127     io->items[i].counter=0;
1128     io->items[i].num=0;
1129
1130     io->filters[i]=filter;
1131     flt=filter;
1132
1133     field=NULL;
1134     hfi=NULL;
1135     for(j=0; calc_type_table[j].func_name; j++){
1136         namelen=strlen(calc_type_table[j].func_name);
1137         if(filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1138             io->items[i].calc_type=calc_type_table[j].calc_type;
1139             io->items[i].colnum = i;
1140             if(*(filter+namelen)=='(') {
1141                 p=filter+namelen+1;
1142                 parenp=strchr(p, ')');
1143                 if(!parenp){
1144                     fprintf(stderr,
1145                         "\ntshark: Closing parenthesis missing from calculated expression.\n");
1146                     exit(10);
1147                 }
1148
1149                 if(io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES){
1150                     if(parenp!=p) {
1151                         fprintf(stderr,
1152                             "\ntshark: %s does not require or allow a field name within the parens.\n",
1153                             calc_type_table[j].func_name);
1154                         exit(10);
1155                     }
1156                 } else {
1157                     if(parenp==p) {
1158                             /* bail out if a field name was not specified */
1159                             fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
1160                                 calc_type_table[j].func_name);
1161                             exit(10);
1162                     }
1163                 }
1164
1165                 field = (char *) g_malloc(parenp-p+1);
1166                 memcpy(field, p, parenp-p);
1167                 field[parenp-p] = '\0';
1168                 flt=parenp + 1;
1169                 if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1170                     break;
1171                 hfi=proto_registrar_get_byname(field);
1172                 if(!hfi){
1173                     fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1174                         field);
1175                     g_free(field);
1176                     exit(10);
1177                 }
1178
1179                 io->items[i].hf_index=hfi->id;
1180                 break;
1181             }
1182         } else {
1183             if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1184                 flt="";
1185             io->items[i].colnum = i;
1186         }
1187     }
1188     if(hfi && !(io->items[i].calc_type==CALC_TYPE_BYTES ||
1189                 io->items[i].calc_type==CALC_TYPE_FRAMES ||
1190                 io->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES)){
1191         /* check that the type is compatible */
1192         switch(hfi->type){
1193         case FT_UINT8:
1194         case FT_UINT16:
1195         case FT_UINT24:
1196         case FT_UINT32:
1197         case FT_UINT64:
1198         case FT_INT8:
1199         case FT_INT16:
1200         case FT_INT24:
1201         case FT_INT32:
1202         case FT_INT64:
1203             /* these types support all calculations */
1204             break;
1205         case FT_FLOAT:
1206         case FT_DOUBLE:
1207             /* these types only support SUM, COUNT, MAX, MIN, AVG */
1208             switch(io->items[i].calc_type){
1209             case CALC_TYPE_SUM:
1210             case CALC_TYPE_COUNT:
1211             case CALC_TYPE_MAX:
1212             case CALC_TYPE_MIN:
1213             case CALC_TYPE_AVG:
1214                 break;
1215             default:
1216                 fprintf(stderr,
1217                     "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1218                     field,
1219                     calc_type_table[j].func_name);
1220                 exit(10);
1221             }
1222             break;
1223         case FT_RELATIVE_TIME:
1224             /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1225             switch(io->items[i].calc_type){
1226             case CALC_TYPE_SUM:
1227             case CALC_TYPE_COUNT:
1228             case CALC_TYPE_MAX:
1229             case CALC_TYPE_MIN:
1230             case CALC_TYPE_AVG:
1231             case CALC_TYPE_LOAD:
1232                 break;
1233             default:
1234                 fprintf(stderr,
1235                     "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1236                     field,
1237                     calc_type_table[j].func_name);
1238                 exit(10);
1239             }
1240             break;
1241         default:
1242             /*
1243              * XXX - support all operations on floating-point
1244              * numbers?
1245              */
1246             if(io->items[i].calc_type!=CALC_TYPE_COUNT){
1247                 fprintf(stderr,
1248                     "\ntshark: %s doesn't have integral values, so %s(*) "
1249                     "calculations are not supported on it.\n",
1250                     field,
1251                     calc_type_table[j].func_name);
1252                 exit(10);
1253             }
1254             break;
1255         }
1256         g_free(field);
1257     }
1258
1259     error_string=register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1260                                        iostat_packet, i?NULL:iostat_draw);
1261     if(error_string){
1262         g_free(io->items);
1263         g_free(io);
1264         fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1265             error_string->str);
1266         g_string_free(error_string, TRUE);
1267         exit(1);
1268     }
1269 }
1270
1271 static void
1272 iostat_init(const char *optarg, void* userdata _U_)
1273 {
1274     gdouble interval_float;
1275     guint32 idx=0, i;
1276     io_stat_t *io;
1277     const gchar *filters, *str, *pos;
1278
1279     if ((*(optarg+(strlen(optarg)-1)) == ',') ||
1280         (sscanf(optarg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
1281         (idx < 8)) {
1282         fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1283         exit(1);
1284     }
1285
1286     filters=optarg+idx;
1287     if (*filters) {
1288         if (*filters != ',') {
1289             /* For locale's that use ',' instead of '.', the comma might
1290              * have been consumed during the floating point conversion. */
1291             --filters;
1292             if (*filters != ',') {
1293                 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1294                 exit(1);
1295             }
1296         }
1297     } else
1298         filters=NULL;
1299
1300     switch (timestamp_get_type()) {
1301     case TS_DELTA:
1302     case TS_DELTA_DIS:
1303     case TS_EPOCH:
1304     case TS_UTC:
1305     case TS_UTC_WITH_DATE:
1306         fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad>\n");
1307         exit(1);
1308     default:
1309         break;
1310     }
1311
1312     io = (io_stat_t *) g_malloc(sizeof(io_stat_t));
1313
1314     /* If interval is 0, calculate statistics over the whole file by setting the interval to
1315     *  G_MAXINT32 */
1316     if (interval_float==0) {
1317         io->interval = G_MAXINT32;
1318         io->invl_prec = 0;
1319     } else {
1320         /* Set interval to the number of us rounded to the nearest integer */
1321         io->interval = (gint64)(interval_float*1000000.0+0.5);
1322         /*
1323         * Determine what interval precision the user has specified */
1324         io->invl_prec = 6;
1325         for (i=10; i<10000000; i*=10) {
1326             if (io->interval%i > 0)
1327                 break;
1328             io->invl_prec--;
1329         }
1330     }
1331     if (io->interval < 1){
1332         fprintf(stderr,
1333             "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1334         exit(10);
1335     }
1336
1337     /* Find how many ',' separated filters we have */
1338     io->num_cols = 1;
1339     io->start_time=0;
1340
1341     if (filters && (*filters != '\0')) {
1342         str = filters;
1343         while((str = strchr(str, ','))) {
1344             io->num_cols++;
1345             str++;
1346         }
1347     }
1348
1349     io->items = (io_stat_item_t *) g_malloc(sizeof(io_stat_item_t) * io->num_cols);
1350     io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
1351     io->max_vals = (guint64 *) g_malloc(sizeof(guint64) * io->num_cols);
1352     io->max_frame = (guint32 *) g_malloc(sizeof(guint32) * io->num_cols);
1353
1354     for (i=0; i<io->num_cols; i++) {
1355         io->max_vals[i] = 0;
1356         io->max_frame[i] = 0;
1357     }
1358
1359     /* Register a tap listener for each filter */
1360     if((!filters) || (filters[0]==0)) {
1361         register_io_tap(io, 0, NULL);
1362     } else {
1363         gchar *filter;
1364         i = 0;
1365         str = filters;
1366         do {
1367             pos = (gchar*) strchr(str, ',');
1368             if(pos==str){
1369                 register_io_tap(io, i, NULL);
1370             } else if (pos==NULL) {
1371                 str = (const char*) g_strstrip((gchar*)str);
1372                 filter = g_strdup((gchar*) str);
1373                 if (*filter)
1374                     register_io_tap(io, i, filter);
1375                 else
1376                     register_io_tap(io, i, NULL);
1377             } else {
1378                 filter = (gchar *)g_malloc((pos-str)+1);
1379                 g_strlcpy( filter, str, (gsize) ((pos-str)+1));
1380                 filter = g_strstrip(filter);
1381                 register_io_tap(io, i, (char *) filter);
1382             }
1383             str = pos+1;
1384             i++;
1385         } while(pos);
1386     }
1387 }
1388
1389 void
1390 register_tap_listener_iostat(void)
1391 {
1392     register_stat_cmd_arg("io,stat,", iostat_init, NULL);
1393 }