char *android_log_formatLogLine ( AndroidLogFormat *p_format, char *defaultBuffer, size_t defaultBufferSize, const AndroidLogEntry *entry, size_t *p_outLength) { #if defined(HAVE_LOCALTIME_R) struct tm tmBuf; #endif struct tm* ptm; char timeBuf[32]; char nsInfoBuf[32]; char prefixBuf[128], suffixBuf[128]; char priChar; int prefixSuffixIsHeaderFooter = 0; char * ret = NULL; priChar = filterPriToChar(entry->priority); /* * Get the current date/time in pretty form * * It's often useful when examining a log with "less" to jump to * a specific point in the file by searching for the date/time stamp. * For this reason it's very annoying to have regexp meta characters * in the time stamp. Don't use forward slashes, parenthesis, * brackets, asterisks, or other special chars here. */ #if defined(HAVE_LOCALTIME_R) ptm = localtime_r(&(entry->tv_sec), &tmBuf); #else ptm = localtime(&(entry->tv_sec)); #endif //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); /* * Construct a buffer containing the log header and log message. */ size_t nsInfoLen, prefixLen, suffixLen; nsInfoLen = 0; if (entry->ns_initpid) { nsInfoLen = snprintf(nsInfoBuf, sizeof(nsInfoBuf), "|%*s(%5d)| ", DEV_NS_TAG_LEN, !entry->ns_tag[0] ? "<init>" : entry->ns_tag, entry->ns_initpid); } if (nsInfoLen >= sizeof(nsInfoBuf)) nsInfoLen = sizeof(nsInfoBuf) - 1; size_t prefixColorLen = 0; char * prefixBufTmp = prefixBuf; size_t prefixBufTmpRemainLen = sizeof(prefixBuf); if (p_format->colored_output == OUTPUT_COLOR_ON) { prefixColorLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c[%d;%d;%dm", 0x1B, 38, 5, colorFromPri(entry->priority)); if(prefixColorLen >= prefixBufTmpRemainLen) prefixColorLen = prefixBufTmpRemainLen - 1; prefixBufTmp += prefixColorLen; prefixBufTmpRemainLen -= prefixColorLen; } switch (p_format->format) { case FORMAT_TAG: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c/%-8s: ", priChar, entry->tag); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_PROCESS: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c(%5d) ", priChar, entry->pid); suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), " (%s)\n", entry->tag); break; case FORMAT_THREAD: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_RAW: prefixBuf[0] = 0; prefixLen = 0; strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_TIME: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, priChar, entry->tag, entry->pid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_THREADTIME: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_LONG: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "[ %s.%03ld %5d:%5d %c/%-8s ]\n", timeBuf, entry->tv_nsec / 1000000, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf, "\n\n"); suffixLen = 2; prefixSuffixIsHeaderFooter = 1; break; case FORMAT_BRIEF: default: prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; } /* snprintf has a weird return value. It returns what would have been * written given a large enough buffer. In the case that the prefix is * longer then our buffer(128), it messes up the calculations below * possibly causing heap corruption. To avoid this we double check and * set the length at the maximum (size minus null byte) */ if(prefixLen >= prefixBufTmpRemainLen) prefixLen = prefixBufTmpRemainLen - 1; if(suffixLen >= sizeof(suffixBuf)) suffixLen = sizeof(suffixBuf) - 1; size_t suffixColorLen = 0; char * suffixBufTmp = suffixBuf + suffixLen; size_t suffixBufTmpRemainLen = sizeof(suffixBuf) - suffixLen; if (p_format->colored_output == OUTPUT_COLOR_ON) { suffixColorLen = snprintf(suffixBufTmp, suffixBufTmpRemainLen, "%c[%dm", 0x1B, 0); if(suffixColorLen >= suffixBufTmpRemainLen) suffixColorLen = suffixBufTmpRemainLen - 1; } /* the following code is tragically unreadable */ size_t numLines; size_t i; char *p; size_t bufferSize; const char *pm; if (prefixSuffixIsHeaderFooter) { // we're just wrapping message with a header/footer numLines = 1; } else { pm = entry->message; numLines = 0; // The line-end finding here must match the line-end finding // in for ( ... numLines...) loop below while (pm < (entry->message + entry->messageLen)) { if (*pm++ == '\n') numLines++; } // plus one line for anything not newline-terminated at the end if (pm > entry->message && *(pm-1) != '\n') numLines++; } // this is an upper bound--newlines in message may be counted // extraneously bufferSize = (numLines * (nsInfoLen + prefixColorLen + prefixLen + suffixLen + suffixColorLen)) + entry->messageLen + 1; if (defaultBufferSize >= bufferSize) { ret = defaultBuffer; } else { ret = (char *)malloc(bufferSize); if (ret == NULL) { return ret; } } ret[0] = '\0'; /* to start strcat off */ p = ret; pm = entry->message; if (prefixSuffixIsHeaderFooter) { if (nsInfoLen) { strcat(p, nsInfoBuf); p += nsInfoLen; } strcat(p, prefixBuf); p += prefixColorLen + prefixLen; strncat(p, entry->message, entry->messageLen); p += entry->messageLen; strcat(p, suffixBuf); p += suffixLen + suffixColorLen; } else { while(pm < (entry->message + entry->messageLen)) { const char *lineStart; size_t lineLen; lineStart = pm; // Find the next end-of-line in message while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++; lineLen = pm - lineStart; if (nsInfoLen) { strcat(p, nsInfoBuf); p += nsInfoLen; } strcat(p, prefixBuf); p += prefixColorLen + prefixLen; strncat(p, lineStart, lineLen); p += lineLen; strcat(p, suffixBuf); p += suffixLen + suffixColorLen; if (*pm == '\n') pm++; } } if (p_outLength != NULL) { *p_outLength = p - ret; } return ret; }
char *android_log_formatLogLine ( AndroidLogFormat *p_format, char *defaultBuffer, size_t defaultBufferSize, const AndroidLogEntry *entry, size_t *p_outLength) { #if !defined(_WIN32) struct tm tmBuf; #endif struct tm* ptm; char timeBuf[64]; /* good margin, 23+nul for msec, 26+nul for usec */ char prefixBuf[128], suffixBuf[128]; char priChar; int prefixSuffixIsHeaderFooter = 0; char * ret = NULL; priChar = filterPriToChar(entry->priority); size_t prefixLen = 0, suffixLen = 0; size_t len; /* * Get the current date/time in pretty form * * It's often useful when examining a log with "less" to jump to * a specific point in the file by searching for the date/time stamp. * For this reason it's very annoying to have regexp meta characters * in the time stamp. Don't use forward slashes, parenthesis, * brackets, asterisks, or other special chars here. * * The caller may have affected the timezone environment, this is * expected to be sensitive to that. */ #if !defined(_WIN32) ptm = localtime_r(&(entry->tv_sec), &tmBuf); #else ptm = localtime(&(entry->tv_sec)); #endif strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm); len = strlen(timeBuf); if (p_format->usec_time_output) { len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", entry->tv_nsec / 1000); } else { len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", entry->tv_nsec / 1000000); } if (p_format->zone_output) { strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm); } /* * Construct a buffer containing the log header and log message. */ if (p_format->colored_output) { prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority)); prefixLen = MIN(prefixLen, sizeof(prefixBuf)); suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m"); suffixLen = MIN(suffixLen, sizeof(suffixBuf)); } switch (p_format->format) { case FORMAT_TAG: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8s: ", priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_PROCESS: len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, " (%s)\n", entry->tag); suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen); len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%5d) ", priChar, entry->pid); break; case FORMAT_THREAD: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_RAW: prefixBuf[prefixLen] = 0; len = 0; strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_TIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_THREADTIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%s %5d %5d %c %-8s: ", timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_LONG: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n\n"); suffixLen += 2; prefixSuffixIsHeaderFooter = 1; break; case FORMAT_BRIEF: default: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; } /* snprintf has a weird return value. It returns what would have been * written given a large enough buffer. In the case that the prefix is * longer then our buffer(128), it messes up the calculations below * possibly causing heap corruption. To avoid this we double check and * set the length at the maximum (size minus null byte) */ prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen); suffixLen = MIN(suffixLen, sizeof(suffixBuf)); /* the following code is tragically unreadable */ size_t numLines; char *p; size_t bufferSize; const char *pm; if (prefixSuffixIsHeaderFooter) { /* we're just wrapping message with a header/footer */ numLines = 1; } else { pm = entry->message; numLines = 0; /* * The line-end finding here must match the line-end finding * in for ( ... numLines...) loop below */ while (pm < (entry->message + entry->messageLen)) { if (*pm++ == '\n') numLines++; } /* plus one line for anything not newline-terminated at the end */ if (pm > entry->message && *(pm-1) != '\n') numLines++; } /* * this is an upper bound--newlines in message may be counted * extraneously */ bufferSize = (numLines * (prefixLen + suffixLen)) + 1; if (p_format->printable_output) { /* Calculate extra length to convert non-printable to printable */ bufferSize += convertPrintable(NULL, entry->message, entry->messageLen); } else { bufferSize += entry->messageLen; } if (defaultBufferSize >= bufferSize) { ret = defaultBuffer; } else { ret = (char *)malloc(bufferSize); if (ret == NULL) { return ret; } } ret[0] = '\0'; /* to start strcat off */ p = ret; pm = entry->message; if (prefixSuffixIsHeaderFooter) { strcat(p, prefixBuf); p += prefixLen; if (p_format->printable_output) { p += convertPrintable(p, entry->message, entry->messageLen); } else { strncat(p, entry->message, entry->messageLen); p += entry->messageLen; } strcat(p, suffixBuf); p += suffixLen; } else { while(pm < (entry->message + entry->messageLen)) { const char *lineStart; size_t lineLen; lineStart = pm; /* Find the next end-of-line in message */ while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++; lineLen = pm - lineStart; strcat(p, prefixBuf); p += prefixLen; if (p_format->printable_output) { p += convertPrintable(p, lineStart, lineLen); } else { strncat(p, lineStart, lineLen); p += lineLen; } strcat(p, suffixBuf); p += suffixLen; if (*pm == '\n') pm++; } } if (p_outLength != NULL) { *p_outLength = p - ret; } return ret; }