$format_time treats ( and ) as special chars
[monky] / src / timeinfo.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30
31 #include "conky.h"
32 #include "text_object.h"
33 #include <locale.h>
34 #include <string.h>
35 #include <time.h>
36 #include <errno.h>
37 #include <logging.h>
38
39 struct tztime_s {
40         char *tz;       /* timezone variable */
41         char *fmt;      /* time display formatting */
42 };
43
44 void scan_time(struct text_object *obj, const char *arg)
45 {
46         obj->data.opaque = strndup(arg ? arg : "%F %T", text_buffer_size);
47 }
48
49 void scan_tztime(struct text_object *obj, const char *arg)
50 {
51         char buf1[256], buf2[256], *fmt, *tz;
52         struct tztime_s *ts;
53
54         fmt = tz = NULL;
55         if (arg) {
56                 int nArgs = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
57
58                 switch (nArgs) {
59                         case 2:
60                                 fmt = buf2;
61                         case 1:
62                                 tz = buf1;
63                 }
64         }
65
66         ts = malloc(sizeof(struct tztime_s));
67         memset(ts, 0, sizeof(struct tztime_s));
68         ts->fmt = strndup(fmt ? fmt : "%F %T", text_buffer_size);
69         ts->tz = tz ? strndup(tz, text_buffer_size) : NULL;
70         obj->data.opaque = ts;
71 }
72
73 void print_time(struct text_object *obj, char *p, int p_max_size)
74 {
75         time_t t = time(NULL);
76         struct tm *tm = localtime(&t);
77
78         setlocale(LC_TIME, "");
79         strftime(p, p_max_size, (char *)obj->data.opaque, tm);
80 }
81
82 void print_utime(struct text_object *obj, char *p, int p_max_size)
83 {
84         time_t t = time(NULL);
85         struct tm *tm = gmtime(&t);
86
87         setlocale(LC_TIME, "");
88         strftime(p, p_max_size, (char *)obj->data.opaque, tm);
89 }
90
91 void print_tztime(struct text_object *obj, char *p, int p_max_size)
92 {
93         char *oldTZ = NULL;
94         time_t t;
95         struct tm *tm;
96         struct tztime_s *ts = obj->data.opaque;
97
98         if (!ts)
99                 return;
100
101         if (ts->tz) {
102                 oldTZ = getenv("TZ");
103                 setenv("TZ", ts->tz, 1);
104                 tzset();
105         }
106         t = time(NULL);
107         tm = localtime(&t);
108
109         setlocale(LC_TIME, "");
110         strftime(p, p_max_size, ts->fmt, tm);
111         if (oldTZ) {
112                 setenv("TZ", oldTZ, 1);
113                 tzset();
114         } else {
115                 unsetenv("TZ");
116         }
117         // Needless to free oldTZ since getenv gives ptr to static data
118 }
119
120 void free_time(struct text_object *obj)
121 {
122         if (!obj->data.opaque)
123                 return;
124         free(obj->data.opaque);
125         obj->data.opaque = NULL;
126 }
127
128 void free_tztime(struct text_object *obj)
129 {
130         struct tztime_s *ts = obj->data.opaque;
131
132         if (!ts)
133                 return;
134
135         if (ts->tz)
136                 free(ts->tz);
137         if (ts->fmt)
138                 free(ts->fmt);
139
140         free(obj->data.opaque);
141         obj->data.opaque = NULL;
142 }
143
144 //all chars after the ending " and between the seconds and the starting " are silently
145 //ignored, this is wanted behavior, not a bug, so don't "fix" this.
146 void print_format_time(struct text_object *obj, char *p, unsigned int p_max_size) {
147         double seconds;
148         char *currentchar, *temp;
149         unsigned int output_length = 0;
150         int minutes, hours, days, weeks;
151         char show_minutes = 0, show_hours = 0, show_days = 0, show_weeks = 0, hidestring;
152
153         errno = 0;
154         seconds = strtod(obj->data.s, &currentchar);
155         if(errno == 0 && obj->data.s != currentchar) {
156                 while(*currentchar != 0 && *currentchar != '"') {
157                         currentchar++;
158                 }
159                 if(*currentchar != 0) {
160                         currentchar++;
161                         minutes = seconds / 60;
162                         seconds -= minutes * 60;
163                         hours = minutes / 60;
164                         minutes %= 60;
165                         days = hours / 24;
166                         hours %= 24;
167                         weeks = days / 7;
168                         days %= 7;
169                         for(temp = currentchar; *temp != 0 && *temp != '"'; temp++) {
170                                 if(*temp=='\\') {
171                                         switch(*(temp+1)) {
172                                         case '\\':
173                                                 temp++;
174                                                 break;
175                                         case 'w':
176                                                 show_weeks = 1;
177                                                 break;
178                                         case 'd':
179                                                 show_days = 1;
180                                                 break;
181                                         case 'h':
182                                                 show_hours = 1;
183                                                 break;
184                                         case 'm':
185                                                 show_minutes = 1;
186                                                 break;
187                                         }
188                                 }
189                         }
190                         if(show_weeks == 0) days += weeks * 7;
191                         if(show_days == 0) hours += days * 24;
192                         if(show_hours == 0) minutes += hours * 60;
193                         if(show_minutes == 0) seconds += minutes * 60;
194                         hidestring = 0;
195                         while(output_length < p_max_size - 1) {
196                                 if(*currentchar != 0 && *currentchar != '"') {
197                                         temp = NULL;
198                                         if(*currentchar == '\\' && hidestring == 0) {
199                                                 currentchar++;
200                                                 switch(*currentchar){
201                                                 case 'w':
202                                                         asprintf(&temp, "%d", weeks);
203                                                         break;
204                                                 case 'd':
205                                                         asprintf(&temp, "%d", days);
206                                                         break;
207                                                 case 'h':
208                                                         asprintf(&temp, "%d", hours);
209                                                         break;
210                                                 case 'm':
211                                                         asprintf(&temp, "%d", minutes);
212                                                         break;
213                                                 case 's':
214                                                         asprintf(&temp, "%d", (int) seconds);
215                                                         break;
216                                                 case 'S':
217                                                         currentchar++;
218                                                         if(*currentchar >= '0' && *currentchar <= '9') {
219                                                                 asprintf(&temp, "%.*f", (*currentchar) - '0', seconds);
220                                                         } else if(*currentchar == 'x') {
221                                                                 asprintf(&temp, "%.9f", seconds);
222                                                                 while(*(temp + strlen(temp) - 1) == '0' || *(temp + strlen(temp) - 1) == '.') {
223                                                                         *(temp + strlen(temp) - 1) = 0;
224                                                                 }
225                                                                 if(*temp == 0) *temp = '0';
226                                                         }else{
227                                                                 currentchar--;
228                                                                 NORM_ERR("$format_time needs a digit behind 'S' to specify precision")
229                                                         }
230                                                         break;
231                                                 case '\\':
232                                                 case '(':
233                                                 case ')':
234                                                         p[output_length] = *currentchar;
235                                                         output_length++;
236                                                         break;
237                                                 default:
238                                                         NORM_ERR("$format_time doesn't have a special char '%c'", *currentchar)
239                                                 }
240                                         } else if(*currentchar == '(') {
241                                                 for(temp = currentchar + 1; *temp != 0 && *temp != ')'; temp++) {
242                                                         if(*(temp-1) == '\\') {
243                                                                 switch(*temp) {
244                                                                 case 'w':
245                                                                         if(weeks == 0) hidestring = 1;
246                                                                         break;
247                                                                 case 'd':
248                                                                         if(days == 0) hidestring = 1;
249                                                                         break;
250                                                                 case 'h':
251                                                                         if(hours == 0) hidestring = 1;
252                                                                         break;
253                                                                 case 'm':
254                                                                         if(minutes == 0) hidestring = 1;
255                                                                         break;
256                                                                 case 's':
257                                                                 case 'S':
258                                                                         if(seconds == 0) hidestring = 1;
259                                                                         break;
260                                                                 }
261                                                         }
262                                                 }
263                                                 temp = NULL;
264                                         } else if(*currentchar == ')') {
265                                                 hidestring = 0;
266                                         } else if(hidestring == 0) {
267                                                 p[output_length] = *currentchar;
268                                                 output_length++;
269                                         }
270                                         if(temp) {
271                                                 if(output_length + strlen(temp) < p_max_size - 1) {
272                                                         strcpy(p + output_length, temp);
273                                                         output_length += strlen(temp);
274                                                 } else NORM_ERR("The format string for $format_time is too long")
275                                                 free(temp);
276                                         }
277                                         currentchar++;
278                                 } else break;
279                         }
280                         p[output_length] = 0;
281                 } else {
282                         NORM_ERR("$format_time needs a output-format starting with a \"-char as 2nd argument")
283                 }
284         } else {
285                 NORM_ERR("$format_time didn't receive a time in seconds as first argument")
286         }
287 }