Allow bigger snapshot lengths for D-Bus captures.
[metze/wireshark/wip.git] / wiretap / logcat_text.c
1 /* logcat_text.c
2  *
3  * Copyright 2014, Michal Orynicz for Tieto Corporation
4  * Copyright 2014, Michal Labedzki for Tieto Corporation
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include "wtap-int.h"
26 #include "file_wrappers.h"
27
28 #include "logcat_text.h"
29 #include "logcat.h"
30
31 struct dumper_t {
32     int type;
33 };
34
35 /* Returns '?' for invalid priorities */
36 static gchar get_priority(const guint8 priority) {
37     static gchar priorities[] = "??VDIWEFS";
38
39     if (priority >= (guint8) sizeof(priorities))
40         return '?';
41
42     return priorities[priority];
43 }
44
45 static gint buffered_detect_version(const guint8 *pd)
46 {
47     const struct logger_entry     *log_entry;
48     const struct logger_entry_v2  *log_entry_v2;
49     gint                     version;
50     const guint8            *msg_payload = NULL;
51     guint8                  *msg_part;
52     guint8                  *msg_end;
53     guint16                  msg_len;
54
55     log_entry    = (const struct logger_entry *)(const void *) pd;
56     log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
57
58     /* must contain at least priority and two nulls as separator */
59     if (log_entry->len < 3)
60         return -1;
61
62     /* payload length may not exceed the maximum payload size */
63     if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
64         return -1;
65
66     /* cannot rely on __pad being 0 for v1, use heuristics to find out what
67      * version is in use. First assume the smallest msg. */
68     for (version = 1; version <= 2; ++version) {
69         if (version == 1) {
70             msg_payload = (const guint8 *) (log_entry + 1);
71         } else if (version == 2) {
72             /* v2 is 4 bytes longer */
73             msg_payload = (const guint8 *) (log_entry_v2 + 1);
74             if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
75                 continue;
76         }
77
78         /* A v2 msg has a 32-bit userid instead of v1 priority */
79         if (get_priority(msg_payload[0]) == '?')
80             continue;
81
82         /* Is there a terminating '\0' for the tag? */
83         msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
84         if (msg_part == NULL)
85             continue;
86
87         /* if msg is '\0'-terminated, is it equal to the payload len? */
88         ++msg_part;
89         msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
90         msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
91         /* is the end of the buffer (-1) equal to the end of msg? */
92         if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
93             continue;
94
95         return version;
96     }
97
98     return -1;
99 }
100
101 static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
102         gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
103         const gchar *log)
104 {
105     gchar  time_buffer[15];
106     time_t datetime;
107     struct tm *tm;
108
109     datetime = (time_t) seconds;
110
111     switch (dumper->type) {
112         case WTAP_ENCAP_LOGCAT_BRIEF:
113             return g_strdup_printf("%c/%-8s(%5i): %s\n",
114                     priority, tag, pid, log);
115         case WTAP_ENCAP_LOGCAT_PROCESS:
116             /* NOTE: Last parameter should be "process name", not tag;
117                      Unfortunately, we do not have process name */
118             return g_strdup_printf("%c(%5i) %s  (%s)\n",
119                     priority, pid, log, "");
120         case WTAP_ENCAP_LOGCAT_TAG:
121             return g_strdup_printf("%c/%-8s: %s\n",
122                    priority, tag, log);
123         case WTAP_ENCAP_LOGCAT_THREAD:
124             return g_strdup_printf("%c(%5i:%5i) %s\n",
125                     priority, pid, tid, log);
126         case WTAP_ENCAP_LOGCAT_TIME:
127             tm = gmtime(&datetime);
128             if (tm != NULL) {
129                 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
130                         tm);
131                 return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
132                         time_buffer, milliseconds, priority, tag, pid, log);
133             } else {
134                 return g_strdup_printf("Not representable %c/%-8s(%5i): %s\n",
135                         priority, tag, pid, log);
136             }
137         case WTAP_ENCAP_LOGCAT_THREADTIME:
138             tm = gmtime(&datetime);
139             if (tm != NULL) {
140                 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
141                         tm);
142                 return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
143                         time_buffer, milliseconds, pid, tid, priority, tag, log);
144             } else {
145                 return g_strdup_printf("Not representable %5i %5i %c %-8s: %s\n",
146                         pid, tid, priority, tag, log);
147             }
148         case WTAP_ENCAP_LOGCAT_LONG:
149             tm = gmtime(&datetime);
150             if (tm != NULL) {
151                 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
152                         tm);
153                 return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
154                         time_buffer, milliseconds, pid, tid, priority, tag, log);
155             } else {
156                 return g_strdup_printf("[ Not representable %5i:%5i %c/%-8s ]\n%s\n\n",
157                         pid, tid, priority, tag, log);
158             }
159         default:
160             return NULL;
161     }
162
163 }
164
165 static void get_time(gchar *string, struct wtap_pkthdr *phdr) {
166     gint ms;
167     struct tm date;
168     time_t seconds;
169
170     if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday, &date.tm_hour,
171                     &date.tm_min, &date.tm_sec, &ms)) {
172         date.tm_year = 70;
173         date.tm_mon -= 1;
174         seconds = mktime(&date);
175         phdr->ts.secs = (time_t) seconds;
176         phdr->ts.nsecs = (int) (ms * 1e6);
177         phdr->presence_flags = WTAP_HAS_TS;
178     } else {
179         phdr->presence_flags = 0;
180         phdr->ts.secs = (time_t) 0;
181         phdr->ts.nsecs = (int) 0;
182     }
183 }
184
185 static gboolean logcat_text_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
186         Buffer *buf, gint file_type) {
187     gint8 *pd;
188     gchar *cbuff;
189     gchar *ret = NULL;
190
191     cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
192     do {
193         ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, fh);
194     } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
195
196     if (NULL == ret || 3 > strlen(cbuff)) {
197         g_free(cbuff);
198         return FALSE;
199     }
200
201     if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type &&
202             !g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY)) {
203         gint64 file_off = 0;
204         gchar *lbuff;
205         int err;
206         gchar *ret2 = NULL;
207
208         lbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
209         file_off = file_tell(fh);
210         ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
211         while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
212             g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE_STANDARD);
213             file_off = file_tell(fh);
214             ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
215         }
216
217         if(NULL == ret2 || 2 < strlen(lbuff)) {
218             g_free(cbuff);
219             g_free(lbuff);
220             return FALSE;
221         }
222
223         file_seek(fh,file_off,SEEK_SET,&err);
224         g_free(lbuff);
225     }
226
227     phdr->rec_type = REC_TYPE_PACKET;
228     phdr->caplen = (guint32)strlen(cbuff);
229     phdr->len = phdr->caplen;
230
231     ws_buffer_assure_space(buf, phdr->caplen + 1);
232     pd = ws_buffer_start_ptr(buf);
233     if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type
234             || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type
235             || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type)
236             && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
237         if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) {
238             get_time(cbuff+2, phdr);
239         } else {
240             get_time(cbuff, phdr);
241         }
242     } else {
243         phdr->presence_flags = 0;
244         phdr->ts.secs = (time_t) 0;
245         phdr->ts.nsecs = (int) 0;
246     }
247     memcpy(pd, cbuff, phdr->caplen + 1);
248     g_free(cbuff);
249     return TRUE;
250 }
251
252 static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_,
253         gint64 *data_offset) {
254     *data_offset = file_tell(wth->fh);
255
256     return logcat_text_read_packet(wth->fh, &wth->phdr, wth->frame_buffer,
257             wth->file_type_subtype);
258 }
259
260 static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
261         struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info _U_) {
262     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
263         return FALSE;
264
265     if (!logcat_text_read_packet(wth->random_fh, phdr, buf,
266             wth->file_type_subtype)) {
267         if (*err == 0)
268             *err = WTAP_ERR_SHORT_READ;
269         return FALSE;
270     }
271     return TRUE;
272 }
273
274 wtap_open_return_val logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
275     gchar *cbuff;
276     gchar *ret = NULL;
277
278     if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
279         return WTAP_OPEN_ERROR;
280
281     cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
282     do {
283         ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, wth->fh);
284     } while (NULL != ret && !file_eof(wth->fh)
285             && ((3 > strlen(cbuff))
286                     || g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
287                             G_REGEX_MATCH_NOTEMPTY)));
288
289     if (g_regex_match_simple(BRIEF_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
290             G_REGEX_MATCH_NOTEMPTY)) {
291         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF;
292         wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
293     } else if (g_regex_match_simple(TAG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
294             G_REGEX_MATCH_NOTEMPTY)) {
295         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG;
296         wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
297     } else if (g_regex_match_simple(PROCESS_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
298             G_REGEX_MATCH_NOTEMPTY)) {
299         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS;
300         wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
301     } else if (g_regex_match_simple(TIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
302             G_REGEX_MATCH_NOTEMPTY)) {
303         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME;
304         wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
305     } else if (g_regex_match_simple(THREAD_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
306             G_REGEX_MATCH_NOTEMPTY)) {
307         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD;
308         wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
309     } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
310             G_REGEX_MATCH_NOTEMPTY)) {
311         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME;
312         wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
313     } else if (g_regex_match_simple(LONG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
314             G_REGEX_MATCH_NOTEMPTY)) {
315         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG;
316         wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
317     } else {
318         g_free(cbuff);
319         return WTAP_OPEN_NOT_MINE;
320     }
321
322     if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
323         g_free(cbuff);
324         return WTAP_OPEN_ERROR;
325     }
326     wth->snapshot_length = 0;
327
328     wth->subtype_read = logcat_text_read;
329     wth->subtype_seek_read = logcat_text_seek_read;
330     wth->file_tsprec = WTAP_TSPREC_USEC;
331     g_free(cbuff);
332     return WTAP_OPEN_MINE;
333 }
334
335 int logcat_text_brief_dump_can_write_encap(int encap) {
336     if (encap == WTAP_ENCAP_PER_PACKET)
337         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
338
339     switch (encap) {
340     case WTAP_ENCAP_LOGCAT:
341     case WTAP_ENCAP_LOGCAT_BRIEF:
342     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
343         return 0;
344     default:
345         return WTAP_ERR_UNWRITABLE_ENCAP;
346     }
347 }
348
349 int logcat_text_process_dump_can_write_encap(int encap) {
350     if (encap == WTAP_ENCAP_PER_PACKET)
351         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
352
353     switch (encap) {
354     case WTAP_ENCAP_LOGCAT:
355     case WTAP_ENCAP_LOGCAT_PROCESS:
356     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
357         return 0;
358     default:
359         return WTAP_ERR_UNWRITABLE_ENCAP;
360     }
361 }
362
363 int logcat_text_tag_dump_can_write_encap(int encap) {
364     if (encap == WTAP_ENCAP_PER_PACKET)
365         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
366
367     switch (encap) {
368     case WTAP_ENCAP_LOGCAT:
369     case WTAP_ENCAP_LOGCAT_TAG:
370     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
371         return 0;
372     default:
373         return WTAP_ERR_UNWRITABLE_ENCAP;
374     }
375 }
376
377 int logcat_text_time_dump_can_write_encap(int encap) {
378     if (encap == WTAP_ENCAP_PER_PACKET)
379         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
380
381     switch (encap) {
382     case WTAP_ENCAP_LOGCAT:
383     case WTAP_ENCAP_LOGCAT_TIME:
384     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
385         return 0;
386     default:
387         return WTAP_ERR_UNWRITABLE_ENCAP;
388     }
389 }
390
391 int logcat_text_thread_dump_can_write_encap(int encap) {
392     if (encap == WTAP_ENCAP_PER_PACKET)
393         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
394
395     switch (encap) {
396     case WTAP_ENCAP_LOGCAT:
397     case WTAP_ENCAP_LOGCAT_THREAD:
398     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
399         return 0;
400     default:
401         return WTAP_ERR_UNWRITABLE_ENCAP;
402     }
403 }
404
405 int logcat_text_threadtime_dump_can_write_encap(int encap) {
406     if (encap == WTAP_ENCAP_PER_PACKET)
407         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
408
409     switch (encap) {
410     case WTAP_ENCAP_LOGCAT:
411     case WTAP_ENCAP_LOGCAT_THREADTIME:
412     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
413         return 0;
414     default:
415         return WTAP_ERR_UNWRITABLE_ENCAP;
416     }
417 }
418
419 int logcat_text_long_dump_can_write_encap(int encap) {
420     if (encap == WTAP_ENCAP_PER_PACKET)
421         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
422
423     switch (encap) {
424     case WTAP_ENCAP_LOGCAT:
425     case WTAP_ENCAP_LOGCAT_LONG:
426     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
427         return 0;
428     default:
429         return WTAP_ERR_UNWRITABLE_ENCAP;
430     }
431 }
432
433 static gboolean logcat_text_dump_text(wtap_dumper *wdh,
434     const struct wtap_pkthdr *phdr,
435     const guint8 *pd, int *err, gchar **err_info)
436 {
437     gchar                          *buf;
438     gint                            length;
439     gchar                           priority;
440     const struct logger_entry      *log_entry;
441     const struct logger_entry_v2   *log_entry_v2;
442     gint                            payload_length;
443     const gchar                    *tag;
444     gint32                          pid;
445     gint32                          tid;
446     gint32                          seconds;
447     gint32                          milliseconds;
448     const guint8                   *msg_payload = NULL;
449     const gchar                    *msg_begin;
450     gint                            msg_pre_skip;
451     gchar                          *log;
452     gchar                          *log_part;
453     gchar                          *log_next;
454     gint                           logcat_version;
455     const struct dumper_t          *dumper        = (const struct dumper_t *) wdh->priv;
456
457     /* We can only write packet records. */
458     if (phdr->rec_type != REC_TYPE_PACKET) {
459         *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
460         return FALSE;
461     }
462
463     switch (wdh->encap) {
464     case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
465         {
466             gint skipped_length;
467
468             skipped_length = logcat_exported_pdu_length(pd);
469             pd += skipped_length;
470
471             if (!wtap_dump_file_write(wdh, (const gchar*) pd, phdr->caplen - skipped_length, err)) {
472                 return FALSE;
473             }
474         }
475         break;
476     case WTAP_ENCAP_LOGCAT:
477         /* Skip EXPORTED_PDU*/
478         if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
479             gint skipped_length;
480
481             skipped_length = logcat_exported_pdu_length(pd);
482             pd += skipped_length;
483
484             logcat_version = buffered_detect_version(pd);
485         } else {
486             const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
487
488             logcat_version = pseudo_header->logcat.version;
489         }
490
491         log_entry    = (const struct logger_entry *)(const void *) pd;
492         log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
493
494         payload_length = GINT32_FROM_LE(log_entry->len);
495         pid = GINT32_FROM_LE(log_entry->pid);
496         tid = GINT32_FROM_LE(log_entry->tid);
497         seconds = GINT32_FROM_LE(log_entry->sec);
498         milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
499
500         /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
501         if (logcat_version == 1) {
502             msg_payload = (const guint8 *) (log_entry + 1);
503
504             priority = get_priority(msg_payload[0]);
505             tag = msg_payload + 1;
506             msg_pre_skip = 1 + (gint) strlen(tag) + 1;
507             msg_begin = msg_payload + msg_pre_skip;
508         } else if (logcat_version == 2) {
509             msg_payload = (const guint8 *) (log_entry_v2 + 1);
510
511             priority = get_priority(msg_payload[0]);
512             tag = msg_payload + 1;
513             msg_pre_skip = 1 + (gint) strlen(tag) + 1;
514             msg_begin = msg_payload + msg_pre_skip;
515         } else {
516             *err = WTAP_ERR_UNWRITABLE_REC_DATA;
517             *err_info = g_strdup_printf("logcat: version %d isn't supported",
518                                         logcat_version);
519             return FALSE;
520         }
521
522         /* copy the message part. If a nul byte was missing, it will be added. */
523         log = g_strndup(msg_begin, payload_length - msg_pre_skip);
524
525         /* long format: display one header followed by the whole message (which may
526          * contain new lines). Other formats: include tag, etc. with each line */
527         log_next = log;
528         do {
529             log_part = log_next;
530             if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) {
531                 /* read until end, there is no next string */
532                 log_next = NULL;
533             } else {
534                 /* read until next newline */
535                 log_next = strchr(log_part, '\n');
536                 if (log_next != NULL) {
537                     *log_next = '\0';
538                     ++log_next;
539                     /* ignore trailing newline */
540                     if (*log_next == '\0') {
541                         log_next = NULL;
542                     }
543                 }
544             }
545
546             buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part);
547             if (!buf) {
548                 g_free(log);
549                 return FALSE;
550             }
551             length = (guint32) strlen(buf);
552
553             if (!wtap_dump_file_write(wdh, buf, length, err)) {
554                 g_free(log);
555                 return FALSE;
556             }
557
558             wdh->bytes_dumped += length;
559         } while (log_next != NULL );
560
561         g_free(log);
562
563         break;
564     case WTAP_ENCAP_LOGCAT_BRIEF:
565     case WTAP_ENCAP_LOGCAT_TAG:
566     case WTAP_ENCAP_LOGCAT_PROCESS:
567     case WTAP_ENCAP_LOGCAT_TIME:
568     case WTAP_ENCAP_LOGCAT_THREAD:
569     case WTAP_ENCAP_LOGCAT_THREADTIME:
570     case WTAP_ENCAP_LOGCAT_LONG:
571         if (dumper->type == wdh->encap) {
572             if (!wtap_dump_file_write(wdh, (const gchar*) pd, phdr->caplen, err)) {
573                 return FALSE;
574             }
575         } else {
576             *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
577             return FALSE;
578         }
579     }
580
581     return TRUE;
582 }
583
584 static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) {
585     struct dumper_t *dumper;
586
587     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
588     dumper->type = dump_type;
589
590     wdh->priv = dumper;
591     wdh->subtype_write = logcat_text_dump_text;
592
593     return TRUE;
594 }
595
596 gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) {
597     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err);
598 }
599
600 gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) {
601     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err);
602 }
603
604 gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) {
605     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err);
606 }
607
608 gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) {
609     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err);
610 }
611
612 gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) {
613     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err);
614 }
615
616 gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) {
617     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err);
618 }
619
620 gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) {
621     return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err);
622 }
623
624 /*
625  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
626  *
627  * Local variables:
628  * c-basic-offset: 4
629  * tab-width: 8
630  * indent-tabs-mode: nil
631  * End:
632  *
633  * vi: set shiftwidth=4 tabstop=8 expandtab:
634  * :indentSize=4:tabSize=8:noTabs=true:
635  */