gchar *err_info, gboolean for_writing,
int file_type);
static const char *file_rename_error_message(int err);
-static void cf_write_failure_alert_box(const char *filename, int err);
static void cf_close_failure_alert_box(const char *filename, int err);
static void ref_time_packets(capture_file *cf);
/* Update the progress bar this many times when reading a file. */
/* The open succeeded. Close whatever capture file we had open,
and fill in the information for this file. */
- cf_reset_state(cf);
+ cf_close(cf);
/* Cleanup all data structures used for dissection. */
cleanup_dissection();
} else
cf->has_snap = TRUE;
+ /* Allocate a frame_data_sequence for the frames in this file */
+ cf->frames = new_frame_data_sequence();
+
nstime_set_zero(&cf->elapsed_time);
nstime_set_unset(&first_ts);
nstime_set_unset(&prev_dis_ts);
nstime_set_unset(&prev_cap_ts);
cum_bytes = 0;
-#if GLIB_CHECK_VERSION(2,10,0)
-#else
- /* memory chunks have been deprecated in favor of the slice allocator,
- * which has been added in 2.10
- */
- cf->plist_chunk = g_mem_chunk_new("frame_data_chunk",
- sizeof(frame_data),
- FRAME_DATA_CHUNK_SIZE * sizeof(frame_data),
- G_ALLOC_AND_FREE);
- g_assert(cf->plist_chunk);
-#endif
-
/* Adjust timestamp precision if auto is selected, col width will be adjusted */
cf_timestamp_auto_precision(cf);
/* XXX needed ? */
dfilter_free(cf->rfcode);
cf->rfcode = NULL;
- cap_file_free_frames(cf);
+ if (cf->frames != NULL) {
+ free_frame_data_sequence(cf->frames);
+ cf->frames = NULL;
+ }
+#ifdef WANT_PACKET_EDITOR
+ if (cf->edited_frames) {
+ g_tree_destroy(cf->edited_frames);
+ cf->edited_frames = NULL;
+ }
+#endif
cf_unselect_packet(cf); /* nothing to select */
cf->first_displayed = 0;
cf->last_displayed = 0;
- /* No frame selected, no field in that frame selected. */
+ /* No frames, no frame selected, no field in that frame selected. */
+ cf->count = 0;
cf->current_frame = 0;
cf->current_row = 0;
cf->finfo_selected = NULL;
*/
size = wtap_file_size(cf->wth, NULL);
- /* Another possibility is that we're reading a compressed file and we've
- * read more (uncompressed) data from the file than exists in the
- * (compressed) file. So check how much data we've actually read.
- *
- * This is inside this "if val > 1.0" check to avoid the lseek() when
- * reading uncompressed files. Testing has (thus far) shown no progress
- * bar weirdness resulting from switching from the data offset (when
- * reading the first part of the file) to the real file position.
- */
- file_pos = wtap_read_so_far(cf->wth, NULL);
-
if (size >= 0)
progbar_val = (gfloat) file_pos / (gfloat) size;
}
g_snprintf(status_str, status_size,
- "%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
- file_pos / 1024, size / 1024);
+ "%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
+ file_pos / 1024, size / 1024);
return progbar_val;
}
const char *errmsg;
char errmsg_errno[1024+1];
gint64 data_offset;
+ gint64 file_pos;
progdlg_t *volatile progbar = NULL;
gboolean stop_flag;
volatile gint64 size;
/* Progress so far. */
progbar_val = 0.0f;
+ /* The packet list window will be empty untill the file is completly loaded */
new_packet_list_freeze();
stop_flag = FALSE;
while ((wtap_read(cf->wth, &err, &err_info, &data_offset))) {
if (size >= 0) {
count++;
+ file_pos = wtap_read_so_far(cf->wth);
+
/* Create the progress bar if necessary.
* Check whether it should be created or not every MIN_NUMBER_OF_PACKET
*/
if ((progbar == NULL) && !(count % MIN_NUMBER_OF_PACKET)){
- progbar_val = calc_progbar_val(cf, size, data_offset, status_str, sizeof(status_str));
+ progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
if (from_save == FALSE)
progbar = delayed_create_progress_dlg("Loading", name_ptr,
TRUE, &stop_flag, &start_time, progbar_val);
to repaint what's pending, and doing so may involve an "ioctl()"
to see if there's any pending input from an X server, and doing
that for every packet can be costly, especially on a big file. */
- if (data_offset >= progbar_nextstep) {
+ if (file_pos >= progbar_nextstep) {
if (progbar != NULL) {
- progbar_val = calc_progbar_val(cf, size, data_offset, status_str, sizeof(status_str));
- /* update the packet lists content on the first run or frequently on very large files */
- /* (on smaller files the display update takes longer than reading the file) */
+ progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
+ /* update the packet bar content on the first run or frequently on very large files */
#ifdef HAVE_LIBPCAP
if (progbar_quantum > 500000 || displayed_once == 0) {
if ((auto_scroll_live || displayed_once == 0 || cf->displayed_count < 1000) && cf->count != 0) {
displayed_once = 1;
- new_packet_list_thaw();
- if (auto_scroll_live)
- new_packet_list_moveto_end();
- new_packet_list_freeze();
+ packets_bar_update();
}
}
#endif /* HAVE_LIBPCAP */
WTAP_ENCAP_PER_PACKET). */
cf->lnk_t = wtap_file_encap(cf->wth);
- cf->current_frame = cap_file_find_fdata(cf, cf->first_displayed);
+ cf->current_frame = frame_data_sequence_find(cf->frames, cf->first_displayed);
cf->current_row = 0;
new_packet_list_thaw();
" in the middle of a packet.";
break;
- case WTAP_ERR_BAD_RECORD:
+ case WTAP_ERR_BAD_FILE:
g_snprintf(errmsg_errno, sizeof(errmsg_errno),
"The capture file appears to be damaged or corrupt.\n(%s)",
err_info);
return row;
}
-/*
- * Initialize the col_text and col_text_len arrays.
- */
-static void
-init_col_text(frame_data *fdata, gint num_cols)
-{
- fdata->col_text_len = se_alloc0(sizeof(*fdata->col_text_len) * num_cols);
- fdata->col_text = se_alloc0(sizeof(*fdata->col_text) * num_cols);
-}
-
/* read in a new packet */
/* returns the row of the new packet in the packet list or -1 if not displayed */
static int
framenum = cf->count + 1;
frame_data_init(&fdlocal, framenum, phdr, offset, cum_bytes);
- /* Note - if the packet doesn't pass the read filter, and is thus
- not added to the capture_file's collection of packets, the
- column text arrays aren't free; they're alocated with
- se_alloc0(), so they eventually get freed when we close the
- file. */
- init_col_text(&fdlocal, cf->cinfo.num_cols);
passed = TRUE;
if (cf->rfcode) {
if (passed) {
/* This does a shallow copy of fdlocal, which is good enough. */
- fdata = cap_file_add_fdata(cf, &fdlocal);
+ fdata = frame_data_sequence_add(cf->frames, &fdlocal);
+ cf->count++;
cf->f_datalen = offset + fdlocal.cap_len;
if (!cf->redissecting) {
cf_merge_files(char **out_filenamep, int in_file_count,
char *const *in_filenames, int file_type, gboolean do_append)
{
- merge_in_file_t *in_files;
- wtap *wth;
+ merge_in_file_t *in_files, *in_file;
char *out_filename;
char *tmpname;
int out_fd;
/* do the merge (or append) */
for (;;) {
if (do_append)
- wth = merge_append_read_packet(in_file_count, in_files, &read_err,
- &err_info);
+ in_file = merge_append_read_packet(in_file_count, in_files, &read_err,
+ &err_info);
else
- wth = merge_read_packet(in_file_count, in_files, &read_err,
- &err_info);
- if (wth == NULL) {
- if (read_err != 0)
- got_read_error = TRUE;
+ in_file = merge_read_packet(in_file_count, in_files, &read_err,
+ &err_info);
+ if (in_file == NULL) {
+ /* EOF */
+ break;
+ }
+
+ if (read_err != 0) {
+ /* I/O error reading from in_file */
+ got_read_error = TRUE;
break;
}
/* Get the sum of the seek positions in all of the files. */
file_pos = 0;
for (i = 0; i < in_file_count; i++)
- file_pos += wtap_read_so_far(in_files[i].wth, NULL);
+ file_pos += wtap_read_so_far(in_files[i].wth);
progbar_val = (gfloat) file_pos / (gfloat) f_len;
if (progbar_val > 1.0f) {
/* Some file probably grew while we were reading it.
break;
}
- if (!wtap_dump(pdh, wtap_phdr(wth), wtap_pseudoheader(wth),
- wtap_buf_ptr(wth), &write_err)) {
+ if (!wtap_dump(pdh, wtap_phdr(in_file->wth), wtap_pseudoheader(in_file->wth),
+ wtap_buf_ptr(in_file->wth), &write_err)) {
got_write_error = TRUE;
break;
}
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == GOT_ERROR) {
- /* Put up a message box noting that a read failed somewhere along
- the line. */
- switch (read_err) {
-
- case WTAP_ERR_UNSUPPORTED_ENCAP:
- g_snprintf(errmsg_errno, sizeof(errmsg_errno),
- "The capture file %%s has a packet with a network type that Wireshark doesn't support.\n(%s)",
- err_info);
- g_free(err_info);
- errmsg = errmsg_errno;
- break;
+ /* Put up a message box noting that a read failed somewhere along
+ the line. */
+ switch (read_err) {
+
+ case WTAP_ERR_UNSUPPORTED_ENCAP:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The capture file %%s has a packet with a network type that Wireshark doesn't support.\n(%s)",
+ err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
- case WTAP_ERR_CANT_READ:
- errmsg = "An attempt to read from the capture file %s failed for"
- " some unknown reason.";
- break;
+ case WTAP_ERR_CANT_READ:
+ errmsg = "An attempt to read from the capture file %s failed for"
+ " some unknown reason.";
+ break;
- case WTAP_ERR_SHORT_READ:
- errmsg = "The capture file %s appears to have been cut short"
- " in the middle of a packet.";
- break;
+ case WTAP_ERR_SHORT_READ:
+ errmsg = "The capture file %s appears to have been cut short"
+ " in the middle of a packet.";
+ break;
- case WTAP_ERR_BAD_RECORD:
- g_snprintf(errmsg_errno, sizeof(errmsg_errno),
- "The capture file %%s appears to be damaged or corrupt.\n(%s)",
- err_info);
- g_free(err_info);
- errmsg = errmsg_errno;
- break;
+ case WTAP_ERR_BAD_FILE:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The capture file %%s appears to be damaged or corrupt.\n(%s)",
+ err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
- case WTAP_ERR_DECOMPRESS:
- g_snprintf(errmsg_errno, sizeof(errmsg_errno),
- "The compressed capture file %%s appears to be damaged or corrupt.\n"
- "(%s)", err_info);
- g_free(err_info);
- errmsg = errmsg_errno;
- break;
+ case WTAP_ERR_DECOMPRESS:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The compressed capture file %%s appears to be damaged or corrupt.\n"
+ "(%s)", err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
- default:
- g_snprintf(errmsg_errno, sizeof(errmsg_errno),
- "An error occurred while reading the"
- " capture file %%s: %s.", wtap_strerror(read_err));
- errmsg = errmsg_errno;
- break;
- }
+ default:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "An error occurred while reading the"
+ " capture file %%s: %s.", wtap_strerror(read_err));
+ errmsg = errmsg_errno;
+ break;
+ }
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, errmsg, in_files[i].filename);
}
}
if (got_write_error) {
/* Put up an alert box for the write error. */
- cf_write_failure_alert_box(out_filename, write_err);
+ if (write_err < 0) {
+ /* Wiretap error. */
+ switch (write_err) {
+
+ case WTAP_ERR_UNSUPPORTED_ENCAP:
+ /*
+ * This is a problem with the particular frame we're writing;
+ * note that, and give the frame number.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.",
+ in_file->packet_num, in_file->filename,
+ wtap_file_type_string(file_type));
+ break;
+
+ default:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "An error occurred while writing to the file \"%s\": %s.",
+ out_filename, wtap_strerror(write_err));
+ break;
+ }
+ } else {
+ /* OS error. */
+ write_failure_alert_box(out_filename, write_err);
+ }
}
if (got_read_error || got_write_error || stop_flag) {
gchar *err_info;
char errmsg_errno[1024+1];
+#ifdef WANT_PACKET_EDITOR
+ /* if fdata->file_off == -1 it means packet was edited, and we must find data inside edited_frames tree */
+ if (G_UNLIKELY(fdata->file_off == -1)) {
+ const modified_frame_data *frame = (const modified_frame_data *) g_tree_lookup(cf->edited_frames, GINT_TO_POINTER(fdata->num));
+
+ if (!frame) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "fdata->file_off == -1, but can't find modified frame!");
+ return FALSE;
+ }
+
+ *pseudo_header = frame->ph;
+ memcpy(pd, frame->pd, fdata->cap_len);
+ return TRUE;
+ }
+#endif
+
if (!wtap_seek_read(cf->wth, fdata->file_off, pseudo_header, pd,
fdata->cap_len, &err, &err_info)) {
switch (err) {
g_free(err_info);
break;
- case WTAP_ERR_BAD_RECORD:
+ case WTAP_ERR_BAD_FILE:
g_snprintf(errmsg_errno, sizeof(errmsg_errno),
"An error occurred while reading from the file \"%%s\": %s.\n(%s)",
wtap_strerror(err), err_info);
selected_frame_seen = FALSE;
for (framenum = 1; framenum <= cf->count; framenum++) {
- fdata = cap_file_find_fdata(cf, framenum);
+ fdata = frame_data_sequence_find(cf->frames, framenum);
/* Create the progress bar if necessary.
We check on every iteration of the loop, so that it takes no
* "init_dissection()"), and null out the GSList pointer. */
fdata->flags.visited = 0;
frame_data_cleanup(fdata);
-
- /* cleanup_dissection() calls se_free_all();
- * And after that fdata->col_text (which is allocated using se_alloc0())
- * no longer points to valid memory.
- */
- init_col_text(fdata, cf->cinfo.num_cols);
}
if (!cf_read_frame(cf, fdata))
would leave the user stuck with an Wireshark grinding on
until it finishes. Should we just stick them with that? */
for (; framenum <= cf->count; framenum++) {
- fdata = cap_file_find_fdata(cf, framenum);
+ fdata = frame_data_sequence_find(cf->frames, framenum);
fdata->flags.visited = 0;
frame_data_cleanup(fdata);
}
cum_bytes = 0;
for (framenum = 1; framenum <= cf->count; framenum++) {
- fdata = cap_file_find_fdata(cf, framenum);
+ fdata = frame_data_sequence_find(cf->frames, framenum);
/* just add some value here until we know if it is being displayed or not */
fdata->cum_bytes = cum_bytes + fdata->pkt_len;
cf->elapsed_time = fdata->rel_ts;
}
- /* Get the time elapsed between the previous displayed packet and
- this packet. */
- nstime_delta(&fdata->del_dis_ts, &fdata->abs_ts, &prev_dis_ts);
-
- prev_dis_ts = fdata->abs_ts;
+ /* If this frame is displayed, get the time elapsed between the
+ previous displayed packet and this packet. */
+ if( fdata->flags.passed_dfilter ) {
+ nstime_delta(&fdata->del_dis_ts, &fdata->abs_ts, &prev_dis_ts);
+ prev_dis_ts = fdata->abs_ts;
+ }
/*
* Byte counts
/* Iterate through all the packets, printing the packets that
were selected by the current display filter. */
for (framenum = 1; framenum <= cf->count; framenum++) {
- fdata = cap_file_find_fdata(cf, framenum);
+ fdata = frame_data_sequence_find(cf->frames, framenum);
/* Create the progress bar if necessary.
We check on every iteration of the loop, so that it takes no
if (fh == NULL)
return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
- write_pdml_preamble(fh);
+ write_pdml_preamble(fh, cf->filename);
if (ferror(fh)) {
fclose(fh);
return CF_PRINT_WRITE_ERROR;
static gboolean
write_carrays_packet(capture_file *cf _U_, frame_data *fdata,
- union wtap_pseudo_header *pseudo_header _U_,
+ union wtap_pseudo_header *pseudo_header,
const guint8 *pd, void *argsp)
{
FILE *fh = argsp;
+ epan_dissect_t edt;
+
+ epan_dissect_init(&edt, TRUE, TRUE);
+ epan_dissect_run(&edt, pseudo_header, pd, fdata, NULL);
+ proto_tree_write_carrays(fdata->num, fh, &edt);
+ epan_dissect_cleanup(&edt);
- proto_tree_write_carrays(pd, fdata->cap_len, fdata->num, fh);
return !ferror(fh);
}
mdata->cf = cf;
/* Iterate through all the nodes looking for matching text */
proto_tree_children_foreach(tree, match_subtree_text, mdata);
- return mdata->frame_matched ? MR_MATCHED : MR_NOTMATCHED;
+ return mdata->frame_matched ? MR_MATCHED : MR_NOTMATCHED;
}
static match_result
result = MR_NOTMATCHED;
buf_len = fdata->pkt_len;
- for (i = 0; i < buf_len; i++) {
+ i = 0;
+ while (i < buf_len) {
c_char = cf->pd[i];
if (cf->case_type)
c_char = toupper(c_char);
- if (c_char != 0) {
+ if (c_char != '\0') {
if (c_char == ascii_text[c_match]) {
- c_match++;
+ c_match += 1;
if (c_match == textlen) {
result = MR_MATCHED;
cf->search_pos = i; /* Save the position of the last character
for highlighting the field. */
break;
}
- } else
+ }
+ else {
+ g_assert(i>=c_match);
+ i -= (guint32)c_match;
c_match = 0;
+ }
}
+ i += 1;
}
return result;
}
result = MR_NOTMATCHED;
buf_len = fdata->pkt_len;
- for (i = 0; i < buf_len; i++) {
+ i = 0;
+ while (i < buf_len) {
c_char = cf->pd[i];
if (cf->case_type)
c_char = toupper(c_char);
if (c_char == ascii_text[c_match]) {
- c_match++;
+ c_match += 1;
if (c_match == textlen) {
result = MR_MATCHED;
cf->search_pos = i; /* Save the position of the last character
for highlighting the field. */
break;
}
- } else
+ }
+ else {
+ g_assert(i>=c_match);
+ i -= (guint32)c_match;
c_match = 0;
+ }
+ i += 1;
}
+
return result;
}
result = MR_NOTMATCHED;
buf_len = fdata->pkt_len;
- for (i = 0; i < buf_len; i++) {
+ i = 0;
+ while (i < buf_len) {
c_char = cf->pd[i];
if (cf->case_type)
c_char = toupper(c_char);
if (c_char == ascii_text[c_match]) {
- c_match++;
- i++;
+ c_match += 1;
if (c_match == textlen) {
result = MR_MATCHED;
cf->search_pos = i; /* Save the position of the last character
for highlighting the field. */
break;
}
- } else
+ i += 1;
+ }
+ else {
+ g_assert(i>=(c_match*2));
+ i -= (guint32)c_match*2;
c_match = 0;
+ }
+ i += 1;
}
return result;
}
result = MR_NOTMATCHED;
buf_len = fdata->pkt_len;
- for (i = 0; i < buf_len; i++) {
+ i = 0;
+ while (i < buf_len) {
if (cf->pd[i] == binary_data[c_match]) {
- c_match++;
+ c_match += 1;
if (c_match == datalen) {
result = MR_MATCHED;
cf->search_pos = i; /* Save the position of the last character
for highlighting the field. */
break;
}
- } else
+ }
+ else {
+ g_assert(i>=c_match);
+ i -= (guint32)c_match;
c_match = 0;
+ }
+ i += 1;
}
return result;
}
} else
framenum++;
}
- fdata = cap_file_find_fdata(cf, framenum);
+ fdata = frame_data_sequence_find(cf->frames, framenum);
count++;
{
frame_data *fdata;
- fdata = cap_file_find_fdata(cf, fnumber);
+ fdata = frame_data_sequence_find(cf->frames, fnumber);
if (fdata == NULL) {
/* we didn't find a packet with that packet number */
}
gboolean
-cf_goto_top_frame()
+cf_goto_top_frame(void)
{
/* Find and select */
new_packet_list_select_first_row();
}
gboolean
-cf_goto_bottom_frame()
+cf_goto_bottom_frame(void)
{
/* Find and select */
new_packet_list_select_last_row();
GtkCList; see the comment in "add_packet_to_packet_list()". */
if (row == 0 && cf->first_displayed == cf->last_displayed)
- fdata = cap_file_find_fdata(cf, cf->first_displayed);
+ fdata = frame_data_sequence_find(cf->frames, cf->first_displayed);
}
/* If fdata _still_ isn't set simply give up. */
typedef struct {
wtap_dumper *pdh;
const char *fname;
+ int file_type;
} save_callback_args_t;
/*
/* and save the packet */
if (!wtap_dump(args->pdh, &hdr, pseudo_header, pd, &err)) {
- cf_write_failure_alert_box(args->fname, err);
+ if (err < 0) {
+ /* Wiretap error. */
+ switch (err) {
+
+ case WTAP_ERR_UNSUPPORTED_ENCAP:
+ /*
+ * This is a problem with the particular frame we're writing;
+ * note that, and give the frame number.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Frame %u has a network type that can't be saved in a \"%s\" file.",
+ fdata->num, wtap_file_type_string(args->file_type));
+ break;
+
+ default:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "An error occurred while writing to the file \"%s\": %s.",
+ args->fname, wtap_strerror(err));
+ break;
+ }
+ } else {
+ /* OS error. */
+ write_failure_alert_box(args->fname, err);
+ }
return FALSE;
}
return TRUE;
if (do_copy) {
/* Copy the file, if we haven't moved it. */
if (!copy_file_binary_mode(from_filename, fname))
- goto fail;
+ goto fail;
}
} else {
/* Either we're filtering packets, or we're saving in a different
"range" since we initialized it. */
callback_args.pdh = pdh;
callback_args.fname = fname;
+ callback_args.file_type = save_format;
switch (process_specified_packets(cf, range, "Saving", "selected packets",
TRUE, save_packet, &callback_args)) {
}
break;
- case WTAP_ERR_BAD_RECORD:
+ case WTAP_ERR_BAD_FILE:
/* Seen only when opening a capture file for reading. */
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"The file \"%s\" appears to be damaged or corrupt.\n"
return errmsg;
}
-static void
-cf_write_failure_alert_box(const char *filename, int err)
-{
- if (err < 0) {
- /* Wiretap error. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "An error occurred while writing to the file \"%s\": %s.",
- filename, wtap_strerror(err));
- } else {
- /* OS error. */
- write_failure_alert_box(filename, err);
- }
-}
-
/* Check for write errors - if the file is being written to an NFS server,
a write error may not show up until the file is closed, as NFS clients
might not send writes to the server until the "write()" call finishes,
* indent-tabs-mode: nil
* End:
*
- * ex: set shiftwidth=2 tabstop=8 expandtab
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
* :indentSize=2:tabSize=8:noTabs=true:
*/