summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--epan/CMakeLists.txt1
-rw-r--r--epan/dissectors/Makefile.common1
-rw-r--r--epan/dissectors/packet-logcat-text.c394
-rw-r--r--epan/dissectors/packet-logcat.c2
-rw-r--r--wiretap/CMakeLists.txt1
-rw-r--r--wiretap/Makefile.common2
-rw-r--r--wiretap/file_access.c20
-rw-r--r--wiretap/logcat.c362
-rw-r--r--wiretap/logcat.h40
-rw-r--r--wiretap/logcat_text.c593
-rw-r--r--wiretap/logcat_text.h59
11 files changed, 1098 insertions, 377 deletions
diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt
index 876165f384..69670faa54 100644
--- a/epan/CMakeLists.txt
+++ b/epan/CMakeLists.txt
@@ -907,6 +907,7 @@ set(DISSECTOR_SRC
dissectors/packet-lmi.c
dissectors/packet-lmp.c
dissectors/packet-logcat.c
+ dissectors/packet-logcat-text.c
dissectors/packet-lon.c
dissectors/packet-loop.c
dissectors/packet-lpd.c
diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common
index 758fab6654..7c4a7925d8 100644
--- a/epan/dissectors/Makefile.common
+++ b/epan/dissectors/Makefile.common
@@ -833,6 +833,7 @@ DISSECTOR_SRC = \
packet-lmi.c \
packet-lmp.c \
packet-logcat.c \
+ packet-logcat-text.c \
packet-lon.c \
packet-loop.c \
packet-lpd.c \
diff --git a/epan/dissectors/packet-logcat-text.c b/epan/dissectors/packet-logcat-text.c
new file mode 100644
index 0000000000..cc1638735c
--- /dev/null
+++ b/epan/dissectors/packet-logcat-text.c
@@ -0,0 +1,394 @@
+/* packet-logcat-text.c
+ * Routines for Android Logcat text formats
+ *
+ * Copyright 2014, Michal Orynicz for Tieto Corporation
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include "epan/packet.h"
+#include "epan/expert.h"
+#include "wiretap/wtap.h"
+
+#include <stdio.h>
+
+/* Basically the same regexes are present in wiretap/logcat_text.c */
+#define SPECIAL_STRING "[-]+ (beginning of \\/.+)"
+#define BRIEF_STRING "([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
+#define TAG_STRING "([IVDWEF])/(.*?): (.*)"
+#define TIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) ([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
+#define THREAD_STRING "([IVDWEF])\\( *(\\d+): *(\\d+)\\) (.*)"
+#define PROCESS_STRING "([IVDWEF])\\( *(\\d+)\\) (.*)"
+#define THREADTIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+) +(\\d+) ([IVDWEF]) (.*?): (.*)"
+#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]\\R(.*)"
+
+extern const value_string priority_vals[];
+
+static int proto_logcat_text = -1;
+
+static int hf_logcat_text_pid = -1;
+static int hf_logcat_text_tid = -1;
+static int hf_logcat_text_timestamp = -1;
+static int hf_logcat_text_priority = -1;
+static int hf_logcat_text_tag = -1;
+static int hf_logcat_text_log = -1;
+
+static gint ett_logcat = -1;
+
+static expert_field ei_malformed_time = EI_INIT;
+static expert_field ei_malformed_token = EI_INIT;
+
+static dissector_handle_t logcat_text_brief_handle;
+static dissector_handle_t logcat_text_tag_handle;
+static dissector_handle_t logcat_text_process_handle;
+static dissector_handle_t logcat_text_time_handle;
+static dissector_handle_t logcat_text_thread_handle;
+static dissector_handle_t logcat_text_threadtime_handle;
+static dissector_handle_t logcat_text_long_handle;
+
+static GRegex *special_regex = NULL;
+static GRegex *brief_regex = NULL;
+static GRegex *tag_regex = NULL;
+static GRegex *time_regex = NULL;
+static GRegex *process_regex = NULL;
+static GRegex *thread_regex = NULL;
+static GRegex *threadtime_regex = NULL;
+static GRegex *long_regex = NULL;
+
+static const gchar dissector_name[] = "Logcat Text";
+
+typedef int (*tGETTER) (const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo);
+
+typedef struct {
+ GRegex *regex;
+ const tGETTER *getters;
+ guint no_of_getters;
+} dissect_info_t;
+
+void proto_register_logcat_text(void);
+void proto_reg_handoff_logcat_text(void);
+
+static int get_priority(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
+ int prio;
+ gchar *p = g_strstr_len(frame + start_offset, -1, token);
+ int offset = p - frame;
+
+ switch (*p) {
+ case 'I':
+ prio = 4;
+ break;
+ case 'V':
+ prio = 2;
+ break;
+ case 'D':
+ prio = 3;
+ break;
+ case 'W':
+ prio = 5;
+ break;
+ case 'E':
+ prio = 6;
+ break;
+ case 'F':
+ prio = 7;
+ break;
+ default:
+ prio = 0;
+ }
+
+ proto_tree_add_uint(maintree, hf_logcat_text_priority, tvb, offset, 1, prio);
+ return offset + 1;
+}
+
+static int get_tag(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo) {
+ gchar *p = g_strstr_len(frame + start_offset, -1, token);
+ int offset = p - frame;
+ guint8 *src_addr = wmem_strdup(wmem_packet_scope(), token);
+ gint tok_len = strlen(token);
+
+ proto_tree_add_string(maintree, hf_logcat_text_tag, tvb, offset, tok_len,
+ token);
+ SET_ADDRESS(&pinfo->src, AT_STRINGZ, tok_len + 1, src_addr);
+ SET_ADDRESS(&pinfo->dst, AT_STRINGZ, sizeof(dissector_name), dissector_name);
+ return offset + tok_len;
+}
+
+static int get_ptid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint header_field, gint start_offset) {
+ gchar *p = g_strstr_len(frame + start_offset, -1, token);
+ int offset = p - frame;
+
+ proto_tree_add_uint(maintree, header_field, tvb, offset, strlen(token),
+ g_ascii_strtoull(token, NULL, 10));
+ return offset + strlen(token);
+}
+
+static int get_pid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
+ return get_ptid(frame, token, tvb, maintree, hf_logcat_text_pid, start_offset);
+}
+
+static int get_tid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
+ return get_ptid(frame, token, tvb, maintree, hf_logcat_text_tid, start_offset);
+}
+
+static int get_log(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo) {
+ gchar *p = g_strstr_len(frame + start_offset, -1, token);
+ int offset = p - frame;
+
+ proto_tree_add_string(maintree, hf_logcat_text_log, tvb, offset,
+ strlen(token), token);
+ col_add_str(pinfo->cinfo, COL_INFO, token);
+ return offset + strlen(token);
+}
+
+static int get_time(const gchar *frame, const gchar *token, tvbuff_t *tvb,
+ proto_tree *maintree, gint start_offset, packet_info *pinfo) {
+ gint offset;
+ gchar *p;
+ gint day, month, hrs, min, sec, ms;
+ GDateTime *date;
+ gint64 seconds;
+ nstime_t ts;
+
+ p = g_strstr_len(frame + start_offset, -1, token);
+ offset = p - frame;
+
+ if (6 == sscanf(token, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) {
+ date = g_date_time_new_local(1970, month, day, hrs, min,
+ (gdouble) sec + ((gdouble) ms) * 0.001);
+ seconds = g_date_time_to_unix(date);
+ ts.secs = (time_t) seconds;
+ ts.nsecs = (int) ms * 1e6;
+ proto_tree_add_time(maintree, hf_logcat_text_timestamp, tvb, offset,
+ strlen(token), &ts);
+ g_date_time_unref(date);
+ } else {
+ proto_tree_add_expert(maintree, pinfo, &ei_malformed_time, tvb, offset, -1);
+ }
+ return offset + strlen(token);
+}
+
+static int dissect_logcat_text(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo,
+ const dissect_info_t *dinfo) {
+ gchar **tokens;
+ guint i;
+ gchar *frame = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, tvb_captured_length(tvb),
+ ENC_ASCII);
+ proto_item *mainitem = proto_tree_add_item(tree, proto_logcat_text, tvb, 0, -1, ENC_NA);
+ proto_tree *maintree = proto_item_add_subtree(mainitem, ett_logcat);
+ gint offset = 0;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, dissector_name);
+
+ if (!g_regex_match(special_regex, frame, G_REGEX_MATCH_NOTEMPTY, NULL)) {
+
+ tokens = g_regex_split(dinfo->regex, frame, G_REGEX_MATCH_NOTEMPTY);
+ if (NULL == tokens) return 0;
+ if (g_strv_length(tokens) != dinfo->no_of_getters + 2) {
+ proto_tree_add_expert(maintree, pinfo, &ei_malformed_token, tvb, offset, -1);
+ g_strfreev(tokens);
+ return 0;
+ }
+
+ for (i = 0; i < dinfo->no_of_getters; ++i) {
+ offset = ((*dinfo->getters[i])(frame, tokens[i + 1], tvb, maintree, offset, pinfo));
+ }
+ } else {
+ tokens = g_regex_split(special_regex, frame, G_REGEX_MATCH_NOTEMPTY);
+ if (NULL == tokens) return 0;
+ get_log(frame, tokens[1], tvb, maintree, 0, pinfo);
+ }
+ g_strfreev(tokens);
+ return offset;
+}
+
+static int dissect_logcat_text_brief(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_priority, get_tag, get_pid, get_log };
+ dissect_info_t dinfo = { brief_regex, getters, array_length(getters) };
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_priority, get_tag, get_log };
+ dissect_info_t dinfo = { tag_regex, getters, array_length(getters) };
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_process(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_priority, get_pid, get_log };
+ dissect_info_t dinfo = { process_regex, getters, array_length(getters) };
+
+ SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, "");
+ SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, "");
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_time(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_time, get_priority, get_tag, get_pid, get_log };
+ dissect_info_t dinfo = { time_regex, getters, array_length(getters) };
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_thread(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_priority, get_pid, get_tid, get_log };
+ dissect_info_t dinfo = { thread_regex, getters, array_length(getters) };
+
+ SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, "");
+ SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, "");
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_threadtime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
+ dissect_info_t dinfo = { threadtime_regex, getters, array_length(getters) };
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+static int dissect_logcat_text_long(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_) {
+ static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
+ dissect_info_t dinfo = { long_regex, getters, array_length(getters) };
+
+ return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
+}
+
+void proto_register_logcat_text(void) {
+ expert_module_t *expert_module;
+ static hf_register_info hf[] = {
+ { &hf_logcat_text_timestamp,
+ { "Timestamp", "logcat_text.timestamp",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_logcat_text_tag,
+ { "Tag", "logcat_text.tag",
+ FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_logcat_text_log,
+ { "Log", "logcat_text.log",
+ FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_logcat_text_priority,
+ { "Priority", "logcat_text.priority",
+ FT_UINT8, BASE_DEC, VALS(priority_vals), 0x00, NULL, HFILL
+ }
+ },
+ { &hf_logcat_text_pid,
+ { "PID", "logcat_text.pid",
+ FT_UINT32, BASE_DEC, NULL, 0x00, "Process ID", HFILL
+ }
+ },
+ { &hf_logcat_text_tid,
+ { "TID", "logcat_text.tid",
+ FT_UINT32, BASE_DEC, NULL, 0x00, "Thread ID", HFILL
+ }
+ }
+ };
+
+ static ei_register_info ei[] = {
+ { &ei_malformed_time, { "logcat_text.malformed_time", PI_PROTOCOL, PI_ERROR, "Malformed time data", EXPFILL }},
+ { &ei_malformed_token, { "logcat_text.malformed_token", PI_PROTOCOL, PI_ERROR, "Failed to decode one or more tokens", EXPFILL }},
+ };
+
+ static gint *ett[] = { &ett_logcat};
+
+ proto_logcat_text = proto_register_protocol("Android Logcat Text", dissector_name,
+ "logcat_text");
+ proto_register_field_array(proto_logcat_text, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ logcat_text_brief_handle = new_register_dissector("logcat_text_brief",
+ dissect_logcat_text_brief, proto_logcat_text);
+ logcat_text_tag_handle = new_register_dissector("logcat_text_tag",
+ dissect_logcat_text_tag, proto_logcat_text);
+ logcat_text_time_handle = new_register_dissector("logcat_text_time",
+ dissect_logcat_text_time, proto_logcat_text);
+ logcat_text_process_handle = new_register_dissector("logcat_text_process",
+ dissect_logcat_text_process, proto_logcat_text);
+ logcat_text_thread_handle = new_register_dissector("logcat_text_thread",
+ dissect_logcat_text_thread, proto_logcat_text);
+ logcat_text_threadtime_handle = new_register_dissector("logcat_text_threadtime",
+ dissect_logcat_text_threadtime, proto_logcat_text);
+ logcat_text_long_handle = new_register_dissector("logcat_text_long",
+ dissect_logcat_text_long, proto_logcat_text);
+
+ special_regex = g_regex_new(SPECIAL_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ brief_regex = g_regex_new(BRIEF_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ tag_regex = g_regex_new(TAG_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ time_regex = g_regex_new(TIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ thread_regex = g_regex_new(THREAD_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ threadtime_regex = g_regex_new(THREADTIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ process_regex = g_regex_new(PROCESS_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
+ long_regex = g_regex_new(LONG_STRING, G_REGEX_MULTILINE, G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ expert_module = expert_register_protocol(proto_logcat_text);
+ expert_register_field_array(expert_module, ei, array_length(ei));
+}
+
+void proto_reg_handoff_logcat_text(void) {
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_BRIEF,
+ logcat_text_brief_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TAG,
+ logcat_text_tag_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TIME,
+ logcat_text_time_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREAD,
+ logcat_text_thread_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREADTIME,
+ logcat_text_threadtime_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_PROCESS,
+ logcat_text_process_handle);
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_LONG,
+ logcat_text_long_handle);
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/dissectors/packet-logcat.c b/epan/dissectors/packet-logcat.c
index 4a5141a907..6515477716 100644
--- a/epan/dissectors/packet-logcat.c
+++ b/epan/dissectors/packet-logcat.c
@@ -57,7 +57,7 @@ static gint exported_pdu_tap = -1;
static expert_field ei_invalid_payload_length = EI_INIT;
-static const value_string priority_vals[] = {
+const value_string priority_vals[] = {
{ 0x00, "Unknown" },
{ 0x01, "Default" },
{ 0x02, "Verbose" },
diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt
index 319e64a72f..b2687f1300 100644
--- a/wiretap/CMakeLists.txt
+++ b/wiretap/CMakeLists.txt
@@ -49,6 +49,7 @@ set(CLEAN_FILES
lanalyzer.c
libpcap.c
logcat.c
+ logcat_text.c
merge.c
mpeg.c
mime_file.c
diff --git a/wiretap/Makefile.common b/wiretap/Makefile.common
index e36949bbbd..abd2392cc7 100644
--- a/wiretap/Makefile.common
+++ b/wiretap/Makefile.common
@@ -53,6 +53,7 @@ NONGENERATED_C_FILES = \
mime_file.c \
k12.c \
lanalyzer.c \
+ logcat_text.c \
logcat.c \
libpcap.c \
merge.c \
@@ -112,6 +113,7 @@ NONGENERATED_HEADER_FILES = \
lanalyzer.h \
libpcap.h \
logcat.h \
+ logcat_text.h \
merge.h \
mpeg.h \
mp2t.h \
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
index 740108da33..a9c02d7341 100644
--- a/wiretap/file_access.c
+++ b/wiretap/file_access.c
@@ -65,6 +65,7 @@
#include "erf.h"
#include "hcidump.h"
#include "logcat.h"
+#include "logcat_text.h"
#include "network_instruments.h"
#include "k12.h"
#include "ber.h"
@@ -362,6 +363,7 @@ static struct open_info open_info_base[] = {
{ "Commview", OPEN_INFO_HEURISTIC, commview_open, "ncf", NULL, NULL },
{ "Nstrace", OPEN_INFO_HEURISTIC, nstrace_open, "txt", NULL, NULL },
{ "Logcat ", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL },
+ { "Logcat Text", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },
/* ASCII trace files from Telnet sessions. */
{ "Ascend", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL },
{ "Toshiba", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL },
@@ -1441,25 +1443,25 @@ static const struct file_type_subtype_info dump_open_table_base[] = {
logcat_dump_can_write_encap, logcat_binary_dump_open, NULL },
{ "Android Logcat Brief text format", "logcat-brief", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_brief_dump_open, NULL },
+ logcat_text_brief_dump_can_write_encap, logcat_text_brief_dump_open, NULL },
{ "Android Logcat Process text format", "logcat-process", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_process_dump_open, NULL },
+ logcat_text_process_dump_can_write_encap, logcat_text_process_dump_open, NULL },
{ "Android Logcat Tag text format", "logcat-tag", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_tag_dump_open, NULL },
- { "Android Logcat Time text format", "logcat-time", NULL, NULL,
- FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_time_dump_open, NULL },
+ logcat_text_tag_dump_can_write_encap, logcat_text_tag_dump_open, NULL },
{ "Android Logcat Thread text format", "logcat-thread", NULL, NULL,
+ FALSE, FALSE, 0,
+ logcat_text_thread_dump_can_write_encap, logcat_text_thread_dump_open, NULL },
+ { "Android Logcat Time text format", "logcat-time", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_thread_dump_open, NULL },
+ logcat_text_time_dump_can_write_encap, logcat_text_time_dump_open, NULL },
{ "Android Logcat Threadtime text format", "logcat-threadtime", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL },
+ logcat_text_threadtime_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL },
{ "Android Logcat Long text format", "logcat-long", NULL, NULL,
FALSE, FALSE, 0,
- logcat_dump_can_write_encap, logcat_text_long_dump_open, NULL }
+ logcat_text_long_dump_can_write_encap, logcat_text_long_dump_open, NULL }
};
diff --git a/wiretap/logcat.c b/wiretap/logcat.c
index 5ac6e383aa..ff80f99a39 100644
--- a/wiretap/logcat.c
+++ b/wiretap/logcat.c
@@ -28,53 +28,6 @@
#include "logcat.h"
-enum dump_type_t {
- DUMP_BINARY,
- DUMP_BRIEF,
- DUMP_PROCESS,
- DUMP_TAG,
- DUMP_TIME,
- DUMP_THREAD,
- DUMP_THREADTIME,
- DUMP_LONG
-};
-
-struct dumper_t {
- enum dump_type_t type;
-};
-
-/* The log format can be found on:
- * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h
- * Log format is assumed to be little-endian (Android platform).
- */
-/* maximum size of a message payload in a log entry */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4076
-
-struct logger_entry {
- guint16 len; /* length of the payload */
- guint16 __pad; /* no matter what, we get 2 bytes of padding */
- gint32 pid; /* generating process's pid */
- gint32 tid; /* generating process's tid */
- gint32 sec; /* seconds since Epoch */
- gint32 nsec; /* nanoseconds */
- char msg[0]; /* the entry's payload */
-};
-
-struct logger_entry_v2 {
- guint16 len; /* length of the payload */
- guint16 hdr_size; /* sizeof(struct logger_entry_v2) */
- gint32 pid; /* generating process's pid */
- gint32 tid; /* generating process's tid */
- gint32 sec; /* seconds since Epoch */
- gint32 nsec; /* nanoseconds */
- union {
- /* v1: not present */
- guint32 euid; /* v2: effective UID of logger */
- guint32 lid; /* v3: log id of the payload */
- };
- char msg[0]; /* the entry's payload */
-};
-
/* Returns '?' for invalid priorities */
static gchar get_priority(const guint8 priority) {
static gchar priorities[] = "??VDIWEFS";
@@ -85,51 +38,6 @@ static gchar get_priority(const guint8 priority) {
return priorities[priority];
}
-static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
- gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
- const gchar *log)
-{
- gchar time_buffer[15];
- time_t datetime;
-
- datetime = (time_t) seconds;
-
- switch (dumper->type) {
- case DUMP_BRIEF:
- return g_strdup_printf("%c/%-8s(%5i): %s\n",
- priority, tag, pid, log);
- case DUMP_PROCESS:
- /* NOTE: Last parameter should be "process name", not tag;
- Unfortunately, we do not have process name */
- return g_strdup_printf("%c(%5i) %s (%s)\n",
- priority, pid, log, "");
- case DUMP_TAG:
- return g_strdup_printf("%c/%-8s: %s\n",
- priority, tag, log);
- case DUMP_THREAD:
- return g_strdup_printf("%c(%5i:0x%02x) %s\n",
- priority, pid, tid, log);
- case DUMP_TIME:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
- time_buffer, milliseconds, priority, tag, pid, log);
- case DUMP_THREADTIME:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
- time_buffer, milliseconds, pid, tid, priority, tag, log);
- case DUMP_LONG:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("[ %s.%03i %5i:0x%02x %c/%s ]\n%s\n\n",
- time_buffer, milliseconds, pid, tid, priority, tag, log);
- default:
- return NULL;
- }
-
-}
-
static gint detect_version(wtap *wth, int *err, gchar **err_info)
{
gint bytes_read;
@@ -228,63 +136,7 @@ static gint detect_version(wtap *wth, int *err, gchar **err_info)
return -1;
}
-static gint buffered_detect_version(const guint8 *pd)
-{
- struct logger_entry *log_entry;
- struct logger_entry_v2 *log_entry_v2;
- gint version;
- guint8 *msg_payload = NULL;
- guint8 *msg_part;
- guint8 *msg_end;
- guint16 msg_len;
-
- log_entry_v2 = (struct logger_entry_v2 *) pd;
- log_entry = (struct logger_entry *) pd;
-
- /* must contain at least priority and two nulls as separator */
- if (log_entry->len < 3)
- return -1;
-
- /* payload length may not exceed the maximum payload size */
- if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
- return -1;
-
- /* cannot rely on __pad being 0 for v1, use heuristics to find out what
- * version is in use. First assume the smallest msg. */
- for (version = 1; version <= 2; ++version) {
- if (version == 1) {
- msg_payload = log_entry->msg;
- } else if (version == 2) {
- /* v2 is 4 bytes longer */
- msg_payload = log_entry_v2->msg;
- if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
- continue;
- }
-
- /* A v2 msg has a 32-bit userid instead of v1 priority */
- if (get_priority(msg_payload[0]) == '?')
- continue;
-
- /* Is there a terminating '\0' for the tag? */
- msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
- if (msg_part == NULL)
- continue;
-
- /* if msg is '\0'-terminated, is it equal to the payload len? */
- ++msg_part;
- msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
- msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
- /* is the end of the buffer (-1) equal to the end of msg? */
- if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
- continue;
-
- return version;
- }
-
- return -1;
-}
-
-static gint exported_pdu_length(const guint8 *pd) {
+gint logcat_exported_pdu_length(const guint8 *pd) {
guint16 *tag;
guint16 *tag_length;
gint length = 0;
@@ -457,7 +309,7 @@ static gboolean logcat_binary_dump(wtap_dumper *wdh,
if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
gint skipped_length;
- skipped_length = exported_pdu_length(pd);
+ skipped_length = logcat_exported_pdu_length(pd);
pd += skipped_length;
caplen -= skipped_length;
}
@@ -489,216 +341,6 @@ gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err)
return TRUE;
}
-static gboolean logcat_dump_text(wtap_dumper *wdh,
- const struct wtap_pkthdr *phdr,
- const guint8 *pd, int *err)
-{
- gchar *buf;
- gint length;
- gchar priority;
- const struct logger_entry *log_entry;
- const struct logger_entry_v2 *log_entry_v2;
- gint payload_length;
- const gchar *tag;
- gint32 pid;
- gint32 tid;
- gint32 seconds;
- gint32 milliseconds;
- const gchar *msg_begin;
- gint msg_pre_skip;
- gchar *log;
- gchar *log_part;
- gchar *log_next;
- gint logcat_version;
- const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
-
- /* We can only write packet records. */
- if (phdr->rec_type != REC_TYPE_PACKET) {
- *err = WTAP_ERR_REC_TYPE_UNSUPPORTED;
- return FALSE;
- }
-
- /* Skip EXPORTED_PDU*/
- if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
- gint skipped_length;
-
- skipped_length = exported_pdu_length(pd);
- pd += skipped_length;
-
- logcat_version = buffered_detect_version(pd);
- } else {
- const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
-
- logcat_version = pseudo_header->logcat.version;
- }
-
- log_entry = (struct logger_entry *) pd;
- log_entry_v2 = (struct logger_entry_v2 *) pd;
-
- payload_length = GINT32_FROM_LE(log_entry->len);
- pid = GINT32_FROM_LE(log_entry->pid);
- tid = GINT32_FROM_LE(log_entry->tid);
- seconds = GINT32_FROM_LE(log_entry->sec);
- milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
-
- /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
- if (logcat_version == 1) {
- priority = get_priority(log_entry->msg[0]);
- tag = log_entry->msg + 1;
- msg_pre_skip = 1 + (gint) strlen(tag) + 1;
- msg_begin = log_entry->msg + msg_pre_skip;
- } else if (logcat_version == 2) {
- priority = get_priority(log_entry_v2->msg[0]);
- tag = log_entry_v2->msg + 1;
- msg_pre_skip = 1 + (gint) strlen(tag) + 1;
- msg_begin = log_entry_v2->msg + msg_pre_skip;
- } else {
- *err = WTAP_ERR_UNSUPPORTED;
- return FALSE;
- }
-
- /* copy the message part. If a nul byte was missing, it will be added. */
- log = g_strndup(msg_begin, payload_length - msg_pre_skip);
-
- /* long format: display one header followed by the whole message (which may
- * contain new lines). Other formats: include tag, etc. with each line */
- log_next = log;
- do {
- log_part = log_next;
- if (dumper->type == DUMP_LONG) {
- /* read until end, there is no next string */
- log_next = NULL;
- } else {
- /* read until next newline */
- log_next = strchr(log_part, '\n');
- if (log_next != NULL) {
- *log_next = '\0';
- ++log_next;
- /* ignore trailing newline */
- if (*log_next == '\0') {
- log_next = NULL;
- }
- }
- }
-
- buf = logcat_log(dumper, seconds, milliseconds, pid, tid,
- priority, tag, log_part);
- if (!buf) {
- g_free(log);
- return FALSE;
- }
- length = (guint32)strlen(buf);
-
- if (!wtap_dump_file_write(wdh, buf, length, err)) {
- g_free(log);
- return FALSE;
- }
-
- wdh->bytes_dumped += length;
- } while (log_next != NULL);
-
- g_free(log);
- return TRUE;
-}
-
-gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_BRIEF;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_PROCESS;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_TAG;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_TIME;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_THREAD;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_THREADTIME;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_LONG;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
diff --git a/wiretap/logcat.h b/wiretap/logcat.h
index 4167bc21fb..9b2ea5cc25 100644
--- a/wiretap/logcat.h
+++ b/wiretap/logcat.h
@@ -25,19 +25,45 @@
#include "wtap.h"
+/* The log format can be found on:
+ * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h
+ * Log format is assumed to be little-endian (Android platform).
+ */
+/* maximum size of a message payload in a log entry */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4076
+
+struct logger_entry {
+ guint16 len; /* length of the payload */
+ guint16 __pad; /* no matter what, we get 2 bytes of padding */
+ gint32 pid; /* generating process's pid */
+ gint32 tid; /* generating process's tid */
+ gint32 sec; /* seconds since Epoch */
+ gint32 nsec; /* nanoseconds */
+ char msg[0]; /* the entry's payload */
+};
+
+struct logger_entry_v2 {
+ guint16 len; /* length of the payload */
+ guint16 hdr_size; /* sizeof(struct logger_entry_v2) */
+ gint32 pid; /* generating process's pid */
+ gint32 tid; /* generating process's tid */
+ gint32 sec; /* seconds since Epoch */
+ gint32 nsec; /* nanoseconds */
+ union {
+ /* v1: not present */
+ guint32 euid; /* v2: effective UID of logger */
+ guint32 lid; /* v3: log id of the payload */
+ };
+ char msg[0]; /* the entry's payload */
+};
+
int logcat_open(wtap *wth, int *err, gchar **err_info);
gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err);
-gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err);
int logcat_dump_can_write_encap(int encap);
+gint logcat_exported_pdu_length(const guint8 *pd);
#endif
/*
diff --git a/wiretap/logcat_text.c b/wiretap/logcat_text.c
new file mode 100644
index 0000000000..a24a853f6d
--- /dev/null
+++ b/wiretap/logcat_text.c
@@ -0,0 +1,593 @@
+/* logcat_text.c
+ *
+ * Copyright 2014, Michal Orynicz for Tieto Corporation
+ * Copyright 2014, Michal Labedzki for Tieto Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <time.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "wsutil/buffer.h"
+
+#include "logcat_text.h"
+#include "logcat.h"
+
+/* Basically the same regexes are present in epan/packet-logcat-text.c */
+#define SPECIAL_STRING "[-]+ beginning of \\/"
+#define BRIEF_STRING "[IVDWEF]/.*\\( *\\d*\\): .*"
+#define TAG_STRING "[IVDWEF]/.*: .*"
+#define TIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} [IVDWE]/.*\\( *\\d*\\): .*"
+#define THREAD_STRING "[IVDWEF]\\( *\\d+: *\\d+\\) .*"
+#define PROCESS_STRING "[IVDWEF]\\( *\\d+\\) .*"
+#define THREADTIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} *\\d+ *\\d+ [IVDWEF] .+: +"
+#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]"
+
+struct dumper_t {
+ int type;
+};
+
+/* Returns '?' for invalid priorities */
+static gchar get_priority(const guint8 priority) {
+ static gchar priorities[] = "??VDIWEFS";
+
+ if (priority >= (guint8) sizeof(priorities))
+ return '?';
+
+ return priorities[priority];
+}
+
+static gint buffered_detect_version(const guint8 *pd)
+{
+ struct logger_entry *log_entry;
+ struct logger_entry_v2 *log_entry_v2;
+ gint version;
+ guint8 *msg_payload = NULL;
+ guint8 *msg_part;
+ guint8 *msg_end;
+ guint16 msg_len;
+
+ log_entry_v2 = (struct logger_entry_v2 *) pd;
+ log_entry = (struct logger_entry *) pd;
+
+ /* must contain at least priority and two nulls as separator */
+ if (log_entry->len < 3)
+ return -1;
+
+ /* payload length may not exceed the maximum payload size */
+ if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
+ return -1;
+
+ /* cannot rely on __pad being 0 for v1, use heuristics to find out what
+ * version is in use. First assume the smallest msg. */
+ for (version = 1; version <= 2; ++version) {
+ if (version == 1) {
+ msg_payload = log_entry->msg;
+ } else if (version == 2) {
+ /* v2 is 4 bytes longer */
+ msg_payload = log_entry_v2->msg;
+ if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
+ continue;
+ }
+
+ /* A v2 msg has a 32-bit userid instead of v1 priority */
+ if (get_priority(msg_payload[0]) == '?')
+ continue;
+
+ /* Is there a terminating '\0' for the tag? */
+ msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
+ if (msg_part == NULL)
+ continue;
+
+ /* if msg is '\0'-terminated, is it equal to the payload len? */
+ ++msg_part;
+ msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
+ msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
+ /* is the end of the buffer (-1) equal to the end of msg? */
+ if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
+ continue;
+
+ return version;
+ }
+
+ return -1;
+}
+
+static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
+ gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
+ const gchar *log)
+{
+ gchar time_buffer[15];
+ time_t datetime;
+
+ datetime = (time_t) seconds;
+
+ switch (dumper->type) {
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ return g_strdup_printf("%c/%-8s(%5i): %s\n",
+ priority, tag, pid, log);
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ /* NOTE: Last parameter should be "process name", not tag;
+ Unfortunately, we do not have process name */
+ return g_strdup_printf("%c(%5i) %s (%s)\n",
+ priority, pid, log, "");
+ case WTAP_ENCAP_LOGCAT_TAG:
+ return g_strdup_printf("%c/%-8s: %s\n",
+ priority, tag, log);
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ return g_strdup_printf("%c(%5i:%5i) %s\n",
+ priority, pid, tid, log);
+ case WTAP_ENCAP_LOGCAT_TIME:
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ gmtime(&datetime));
+ return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
+ time_buffer, milliseconds, priority, tag, pid, log);
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ gmtime(&datetime));
+ return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
+ time_buffer, milliseconds, pid, tid, priority, tag, log);
+ case WTAP_ENCAP_LOGCAT_LONG:
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ gmtime(&datetime));
+ return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
+ time_buffer, milliseconds, pid, tid, priority, tag, log);
+ default:
+ return NULL;
+ }
+
+}
+
+static void get_time(gchar *string, struct wtap_pkthdr *phdr) {
+ gint day, month, hrs, min, sec, ms;
+ GDateTime *date = NULL;
+ gint64 seconds;
+
+ if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) {
+ date = g_date_time_new_local(1970, month, day, hrs, min,
+ (gdouble) sec + ((gdouble) ms) * 0.001);
+ seconds = g_date_time_to_unix(date);
+ phdr->ts.secs = (time_t) seconds;
+ phdr->ts.nsecs = (int) ms * 1e6;
+ phdr->presence_flags = WTAP_HAS_TS;
+ g_date_time_unref(date);
+ } else {
+ phdr->presence_flags = 0;
+ phdr->ts.secs = (time_t) 0;
+ phdr->ts.nsecs = (int) 0;
+ }
+}
+
+static gboolean logcat_text_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
+ Buffer *buf, gint file_type) {
+ gint8 *pd;
+ gchar cbuff[WTAP_MAX_PACKET_SIZE];
+ gchar *ret = NULL;
+
+ do {
+ ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, fh);
+ } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
+
+ if (NULL == ret || 3 > strlen(cbuff)) {
+ return FALSE;
+ }
+
+ if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type &&
+ !g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY)) {
+ gint64 file_off = 0;
+ gchar lbuff[WTAP_MAX_PACKET_SIZE];
+ int err;
+ gchar *ret2 = NULL;
+
+ file_off = file_tell(fh);
+ ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh);
+ while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
+ g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE);
+ file_off = file_tell(fh);
+ ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh);
+ }
+
+ if(NULL == ret2 || 2 < strlen(lbuff)) {
+ return FALSE;
+ }
+
+ file_seek(fh,file_off,SEEK_SET,&err);
+ }
+
+ phdr->rec_type = REC_TYPE_PACKET;
+ phdr->caplen = strlen(cbuff);
+ phdr->len = phdr->caplen;
+
+ ws_buffer_assure_space(buf, phdr->caplen + 1);
+ pd = ws_buffer_start_ptr(buf);
+ if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type
+ || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type
+ || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type)
+ && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
+ if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) {
+ get_time(cbuff+2, phdr);
+ } else {
+ get_time(cbuff, phdr);
+ }
+ } else {
+ phdr->presence_flags = 0;
+ phdr->ts.secs = (time_t) 0;
+ phdr->ts.nsecs = (int) 0;
+ }
+ memcpy(pd, cbuff, phdr->caplen + 1);
+ return TRUE;
+}
+
+static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_,
+ gint64 *data_offset _U_) {
+ *data_offset = file_tell(wth->fh);
+
+ return logcat_text_read_packet(wth->fh, &wth->phdr, wth->frame_buffer,
+ wth->file_type_subtype);
+}
+
+static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
+ struct wtap_pkthdr *phdr, Buffer *buf, int *err _U_, gchar **err_info _U_) {
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!logcat_text_read_packet(wth->random_fh, phdr, buf,
+ wth->file_type_subtype)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
+ gchar cbuff[WTAP_MAX_PACKET_SIZE];
+ gchar *ret = NULL;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return -1;
+
+ do {
+ ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, wth->fh);
+ } while (NULL != ret && !file_eof(wth->fh)
+ && ((3 > strlen(cbuff))
+ || g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)));
+
+ if (g_regex_match_simple(BRIEF_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
+ } else if (g_regex_match_simple(TAG_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
+ } else if (g_regex_match_simple(PROCESS_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
+ } else if (g_regex_match_simple(TIME_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
+ } else if (g_regex_match_simple(THREAD_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
+ } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
+ } else if (g_regex_match_simple(LONG_STRING, cbuff, G_REGEX_ANCHORED,
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
+ } else {
+ return -1;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return -1;
+
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = logcat_text_read;
+ wth->subtype_seek_read = logcat_text_seek_read;
+ wth->tsprecision = WTAP_FILE_TSPREC_USEC;
+ return 1;
+}
+
+int logcat_text_brief_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_process_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_tag_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_TAG:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_time_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_TIME:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_thread_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_threadtime_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+int logcat_text_long_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_LONG:
+ return 0;
+ default:
+ return WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+}
+
+static gboolean logcat_text_dump_text(wtap_dumper *wdh,
+ const struct wtap_pkthdr *phdr,
+ const guint8 *pd, int *err)
+{
+ gchar *buf;
+ gint length;
+ gchar priority;
+ const struct logger_entry *log_entry;
+ const struct logger_entry_v2 *log_entry_v2;
+ gint payload_length;
+ const gchar *tag;
+ gint32 pid;
+ gint32 tid;
+ gint32 seconds;
+ gint32 milliseconds;
+ const gchar *msg_begin;
+ gint msg_pre_skip;
+ gchar *log;
+ gchar *log_part;
+ gchar *log_next;
+ gint logcat_version;
+ const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
+
+ /* We can only write packet records. */
+ if (phdr->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_REC_TYPE_UNSUPPORTED;
+ return FALSE;
+ }
+
+ switch (wdh->encap) {
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ case WTAP_ENCAP_LOGCAT:
+ /* Skip EXPORTED_PDU*/
+ if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
+ gint skipped_length;
+
+ skipped_length = logcat_exported_pdu_length(pd);
+ pd += skipped_length;
+
+ logcat_version = buffered_detect_version(pd);
+ } else {
+ const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
+
+ logcat_version = pseudo_header->logcat.version;
+ }
+
+ log_entry = (struct logger_entry *) pd;
+ log_entry_v2 = (struct logger_entry_v2 *) pd;
+
+ payload_length = GINT32_FROM_LE(log_entry->len);
+ pid = GINT32_FROM_LE(log_entry->pid);
+ tid = GINT32_FROM_LE(log_entry->tid);
+ seconds = GINT32_FROM_LE(log_entry->sec);
+ milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
+
+ /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
+ if (logcat_version == 1) {
+ priority = get_priority(log_entry->msg[0]);
+ tag = log_entry->msg + 1;
+ msg_pre_skip = 1 + (gint) strlen(tag) + 1;
+ msg_begin = log_entry->msg + msg_pre_skip;
+ } else if (logcat_version == 2) {
+ priority = get_priority(log_entry_v2->msg[0]);
+ tag = log_entry_v2->msg + 1;
+ msg_pre_skip = 1 + (gint) strlen(tag) + 1;
+ msg_begin = log_entry_v2->msg + msg_pre_skip;
+ } else {
+ *err = WTAP_ERR_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* copy the message part. If a nul byte was missing, it will be added. */
+ log = g_strndup(msg_begin, payload_length - msg_pre_skip);
+
+ /* long format: display one header followed by the whole message (which may
+ * contain new lines). Other formats: include tag, etc. with each line */
+ log_next = log;
+ do {
+ log_part = log_next;
+ if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) {
+ /* read until end, there is no next string */
+ log_next = NULL;
+ } else {
+ /* read until next newline */
+ log_next = strchr(log_part, '\n');
+ if (log_next != NULL) {
+ *log_next = '\0';
+ ++log_next;
+ /* ignore trailing newline */
+ if (*log_next == '\0') {
+ log_next = NULL;
+ }
+ }
+ }
+
+ buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part);
+ if (!buf) {
+ g_free(log);
+ return FALSE;
+ }
+ length = (guint32) strlen(buf);
+
+ if (!wtap_dump_file_write(wdh, buf, length, err)) {
+ g_free(log);
+ return FALSE;
+ }
+
+ wdh->bytes_dumped += length;
+ } while (log_next != NULL );
+
+ g_free(log);
+
+ break;
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ case WTAP_ENCAP_LOGCAT_TAG:
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ case WTAP_ENCAP_LOGCAT_TIME:
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ case WTAP_ENCAP_LOGCAT_LONG:
+ if (dumper->type == wdh->encap) {
+ if (!wtap_dump_file_write(wdh, (gchar*) pd, phdr->caplen, err)) {
+ return FALSE;
+ }
+ } else {
+ *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) {
+ struct dumper_t *dumper;
+
+ dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
+ dumper->type = dump_type;
+
+ wdh->priv = dumper;
+ wdh->subtype_write = logcat_text_dump_text;
+ wdh->subtype_close = NULL;
+
+ return TRUE;
+}
+
+gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err);
+}
+
+gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err);
+}
+
+gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err);
+}
+
+gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err);
+}
+
+gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err);
+}
+
+gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err);
+}
+
+gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err);
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/logcat_text.h b/wiretap/logcat_text.h
new file mode 100644
index 0000000000..90f88ec0f1
--- /dev/null
+++ b/wiretap/logcat_text.h
@@ -0,0 +1,59 @@
+/* logcat_text.h
+ *
+ * Copyright 2014, Michal Orynicz for Tieto Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __LOGCAT_TEXT_H__
+#define __LOGCAT_TEXT_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+int logcat_text_open(wtap *wth, int *err, gchar **err_info);
+
+gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err);
+gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err);
+
+int logcat_text_brief_dump_can_write_encap(int encap);
+int logcat_text_tag_dump_can_write_encap(int encap);
+int logcat_text_process_dump_can_write_encap(int encap);
+int logcat_text_thread_dump_can_write_encap(int encap);
+int logcat_text_time_dump_can_write_encap(int encap);
+int logcat_text_threadtime_dump_can_write_encap(int encap);
+int logcat_text_long_dump_can_write_encap(int encap);
+
+#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */