| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <ctype.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/cdefs.h> |
| |
| #include <log/log_read.h> |
| |
| const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q"; |
| const timespec log_time::EPOCH = { 0, 0 }; |
| |
| // Add %#q for fractional seconds to standard strptime function |
| |
| char *log_time::strptime(const char *s, const char *format) { |
| time_t now; |
| #ifdef __linux__ |
| *this = log_time(CLOCK_REALTIME); |
| now = tv_sec; |
| #else |
| time(&now); |
| tv_sec = now; |
| tv_nsec = 0; |
| #endif |
| |
| struct tm *ptm; |
| #if !defined(_WIN32) |
| struct tm tmBuf; |
| ptm = localtime_r(&now, &tmBuf); |
| #else |
| ptm = localtime(&now); |
| #endif |
| |
| char fmt[strlen(format) + 1]; |
| strcpy(fmt, format); |
| |
| char *ret = const_cast<char *> (s); |
| char *cp; |
| for (char *f = cp = fmt; ; ++cp) { |
| if (!*cp) { |
| if (f != cp) { |
| ret = ::strptime(ret, f, ptm); |
| } |
| break; |
| } |
| if (*cp != '%') { |
| continue; |
| } |
| char *e = cp; |
| ++e; |
| #if (defined(__BIONIC__)) |
| if (*e == 's') { |
| *cp = '\0'; |
| if (*f) { |
| ret = ::strptime(ret, f, ptm); |
| if (!ret) { |
| break; |
| } |
| } |
| tv_sec = 0; |
| while (isdigit(*ret)) { |
| tv_sec = tv_sec * 10 + *ret - '0'; |
| ++ret; |
| } |
| now = tv_sec; |
| #if !defined(_WIN32) |
| ptm = localtime_r(&now, &tmBuf); |
| #else |
| ptm = localtime(&now); |
| #endif |
| } else |
| #endif |
| { |
| unsigned num = 0; |
| while (isdigit(*e)) { |
| num = num * 10 + *e - '0'; |
| ++e; |
| } |
| if (*e != 'q') { |
| continue; |
| } |
| *cp = '\0'; |
| if (*f) { |
| ret = ::strptime(ret, f, ptm); |
| if (!ret) { |
| break; |
| } |
| } |
| unsigned long mul = NS_PER_SEC; |
| if (num == 0) { |
| num = INT_MAX; |
| } |
| tv_nsec = 0; |
| while (isdigit(*ret) && num && (mul > 1)) { |
| --num; |
| mul /= 10; |
| tv_nsec = tv_nsec + (*ret - '0') * mul; |
| ++ret; |
| } |
| } |
| f = cp = e; |
| ++f; |
| } |
| |
| if (ret) { |
| tv_sec = mktime(ptm); |
| return ret; |
| } |
| |
| // Upon error, place a known value into the class, the current time. |
| #ifdef __linux__ |
| *this = log_time(CLOCK_REALTIME); |
| #else |
| time(&now); |
| tv_sec = now; |
| tv_nsec = 0; |
| #endif |
| return ret; |
| } |
| |
| log_time log_time::operator-= (const timespec &T) { |
| // No concept of negative time, clamp to EPOCH |
| if (*this <= T) { |
| return *this = EPOCH; |
| } |
| |
| if (this->tv_nsec < (unsigned long int)T.tv_nsec) { |
| --this->tv_sec; |
| this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec; |
| } else { |
| this->tv_nsec -= T.tv_nsec; |
| } |
| this->tv_sec -= T.tv_sec; |
| |
| return *this; |
| } |
| |
| log_time log_time::operator+= (const timespec &T) { |
| this->tv_nsec += (unsigned long int)T.tv_nsec; |
| if (this->tv_nsec >= NS_PER_SEC) { |
| this->tv_nsec -= NS_PER_SEC; |
| ++this->tv_sec; |
| } |
| this->tv_sec += T.tv_sec; |
| |
| return *this; |
| } |
| |
| log_time log_time::operator-= (const log_time &T) { |
| // No concept of negative time, clamp to EPOCH |
| if (*this <= T) { |
| return *this = EPOCH; |
| } |
| |
| if (this->tv_nsec < T.tv_nsec) { |
| --this->tv_sec; |
| this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec; |
| } else { |
| this->tv_nsec -= T.tv_nsec; |
| } |
| this->tv_sec -= T.tv_sec; |
| |
| return *this; |
| } |
| |
| log_time log_time::operator+= (const log_time &T) { |
| this->tv_nsec += T.tv_nsec; |
| if (this->tv_nsec >= NS_PER_SEC) { |
| this->tv_nsec -= NS_PER_SEC; |
| ++this->tv_sec; |
| } |
| this->tv_sec += T.tv_sec; |
| |
| return *this; |
| } |