/* * * Conky, a system monitor, based on torsmo * * Any original torsmo code is licensed under the BSD license * * All code written since the fork of torsmo is licensed under the GPL * * Please see COPYING for details * * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al. * (see AUTHORS) * All rights reserved. * * 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 3 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, see . * */ #include "config.h" #include "timeinfo.h" #include #include #include #include #include #include "conky.h" #include "logging.h" #include "text_object.h" #include struct tztime_s { char *tz; /* timezone variable */ char *fmt; /* time display formatting */ }; conky::simple_config_setting times_in_seconds("times_in_seconds", false, false); void scan_time(struct text_object *obj, const char *arg) { obj->data.opaque = strndup(arg != nullptr ? arg : "%F %T", text_buffer_size.get(*state)); } void scan_tztime(struct text_object *obj, const char *arg) { char buf1[256], buf2[256], *fmt, *tz; struct tztime_s *ts; fmt = tz = nullptr; if (arg != nullptr) { int nArgs = sscanf(arg, "%255s %255[^\n]", buf1, buf2); if (nArgs == 2) { fmt = buf2; tz = buf1; } else if (nArgs == 1) { tz = buf1; } } ts = static_cast(malloc(sizeof(struct tztime_s))); memset(ts, 0, sizeof(struct tztime_s)); ts->fmt = strndup(fmt != nullptr ? fmt : "%F %T", text_buffer_size.get(*state)); ts->tz = tz != nullptr ? strndup(tz, text_buffer_size.get(*state)) : nullptr; obj->data.opaque = ts; } void print_time(struct text_object *obj, char *p, unsigned int p_max_size) { time_t t = time(nullptr); struct tm *tm = localtime(&t); setlocale(LC_TIME, ""); strftime(p, p_max_size, static_cast(obj->data.opaque), tm); } void print_utime(struct text_object *obj, char *p, unsigned int p_max_size) { time_t t = time(nullptr); struct tm *tm = gmtime(&t); setlocale(LC_TIME, ""); strftime(p, p_max_size, static_cast(obj->data.opaque), tm); } void print_tztime(struct text_object *obj, char *p, unsigned int p_max_size) { char *oldTZ = nullptr; time_t t; struct tm *tm; auto *ts = static_cast(obj->data.opaque); if (ts == nullptr) { return; } if (ts->tz != nullptr) { oldTZ = getenv("TZ"); setenv("TZ", ts->tz, 1); tzset(); } t = time(nullptr); tm = localtime(&t); setlocale(LC_TIME, ""); strftime(p, p_max_size, ts->fmt, tm); if (oldTZ != nullptr) { setenv("TZ", oldTZ, 1); tzset(); } else { unsetenv("TZ"); } // Needless to free oldTZ since getenv gives ptr to static data } void free_time(struct text_object *obj) { free_and_zero(obj->data.opaque); } void free_tztime(struct text_object *obj) { auto *ts = static_cast(obj->data.opaque); if (ts == nullptr) { return; } free_and_zero(ts->tz); free_and_zero(ts->fmt); free_and_zero(obj->data.opaque); } /* a safer asprintf() * - no need to check for errors * - exit conky on memory allocation failure * - XXX: no return value at all, otherwise this * could be used globally */ #define safe_asprintf(bufp, ...) \ { \ int __v; \ if ((__v = asprintf(bufp, __VA_ARGS__)) == -1) { \ fprintf(stderr, "%s: memory allocation failed\n", __func__); \ exit(__v); \ } \ } // all chars after the ending " and between the seconds and the starting " are // silently ignored, this is wanted behavior, not a bug, so don't "fix" this. static void do_format_time(struct text_object *obj, char *p, unsigned int p_max_size) { double seconds; char *currentchar, *temp; unsigned int output_length = 0; int minutes, hours, days, weeks; char show_minutes = 0, show_hours = 0, show_days = 0, show_weeks = 0, hidestring; if (!times_in_seconds.get(*state)) { NORM_ERR("Enable \"times_in_seconds\" to use $format_time"); return; } errno = 0; seconds = strtod(obj->data.s, ¤tchar); if (errno == 0 && obj->data.s != currentchar) { while (*currentchar != 0 && *currentchar != '"') { currentchar++; } if (*currentchar != 0) { currentchar++; minutes = seconds / 60; seconds -= minutes * 60; hours = minutes / 60; minutes %= 60; days = hours / 24; hours %= 24; weeks = days / 7; days %= 7; for (temp = currentchar; *temp != 0 && *temp != '"'; temp++) { if (*temp == '\\') { switch (*(temp + 1)) { case '\\': temp++; break; case 'w': show_weeks = 1; break; case 'd': show_days = 1; break; case 'h': show_hours = 1; break; case 'm': show_minutes = 1; break; } } } if (show_weeks == 0) { days += weeks * 7; } if (show_days == 0) { hours += days * 24; } if (show_hours == 0) { minutes += hours * 60; } if (show_minutes == 0) { seconds += minutes * 60; } hidestring = 0; while (output_length < p_max_size - 1) { if (*currentchar != 0 && *currentchar != '"') { temp = nullptr; if (*currentchar == '\\' && hidestring == 0) { currentchar++; switch (*currentchar) { case 'w': safe_asprintf(&temp, "%d", weeks); break; case 'd': safe_asprintf(&temp, "%d", days); break; case 'h': safe_asprintf(&temp, "%d", hours); break; case 'm': safe_asprintf(&temp, "%d", minutes); break; case 's': safe_asprintf(&temp, "%d", (int)seconds); break; case 'S': currentchar++; if (*currentchar >= '0' && *currentchar <= '9') { safe_asprintf(&temp, "%.*f", (*currentchar) - '0', seconds); } else if (*currentchar == 'x') { if (seconds == static_cast(seconds)) { safe_asprintf(&temp, "%d", (int)seconds); } else { safe_asprintf(&temp, "%.9f", seconds); while (*(temp + strlen(temp) - 1) == '0' || *(temp + strlen(temp) - 1) == '.') { *(temp + strlen(temp) - 1) = 0; } } } else { currentchar--; NORM_ERR( "$format_time needs a digit behind 'S' to specify " "precision"); } break; case '\\': case '(': case ')': p[output_length] = *currentchar; output_length++; break; default: NORM_ERR("$format_time doesn't have a special char '%c'", *currentchar); } } else if (*currentchar == '(') { for (temp = currentchar + 1; *temp != 0 && *temp != ')'; temp++) { if (*(temp - 1) == '\\') { switch (*temp) { case 'w': if (weeks == 0) { hidestring = 1; } break; case 'd': if (days == 0) { hidestring = 1; } break; case 'h': if (hours == 0) { hidestring = 1; } break; case 'm': if (minutes == 0) { hidestring = 1; } break; case 's': case 'S': if (seconds == 0) { hidestring = 1; } break; } } } temp = nullptr; } else if (*currentchar == ')') { hidestring = 0; } else if (hidestring == 0) { p[output_length] = *currentchar; output_length++; } if (temp != nullptr) { if (output_length + strlen(temp) < p_max_size - 1) { strncpy(p + output_length, temp, p_max_size - output_length + strlen(temp)); output_length += strlen(temp); } else { NORM_ERR("The format string for $format_time is too long"); } free(temp); } currentchar++; } else { break; } } p[output_length] = 0; } else { NORM_ERR( "$format_time needs a output-format starting with a \"-char as 2nd " "argument"); } } else { NORM_ERR("$format_time didn't receive a time in seconds as first argument"); } } void print_format_time(struct text_object *obj, char *p, unsigned int p_max_size) { std::unique_ptr buf(new char[max_user_text.get(*state)]); generate_text_internal(buf.get(), max_user_text.get(*state), *obj->sub); obj->data.s = buf.get(); do_format_time(obj, p, p_max_size); }