Bug Summary

File:obj-scan-build/libshouldbeinlibc/../../libshouldbeinlibc/timefmt.c
Location:line 341, column 8
Description:Value stored to 'end' is never read

Annotated Source Code

1/* Routines for formatting time
2
3 Copyright (C) 1995, 1996 Free Software Foundation, Inc.
4
5 Written by Miles Bader <miles@gnu.ai.mit.edu>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include <stdio.h>
22#include <string.h>
23#include <sys/time.h>
24#include <time.h>
25
26#include "timefmt.h"
27
28#define SECOND1 1
29#define MINUTE60 60
30#define HOUR(60*60) (60*MINUTE60)
31#define DAY(24*(60*60)) (24*HOUR(60*60))
32#define WEEK(7*(24*(60*60))) (7*DAY(24*(60*60)))
33#define MONTH(31*(24*(60*60))) (31*DAY(24*(60*60))) /* Not strictly accurate, but oh well. */
34#define YEAR(365*(24*(60*60))) (365*DAY(24*(60*60))) /* ditto */
35
36/* Returns the number of digits in the integer N. */
37static unsigned
38int_len (unsigned n)
39{
40 unsigned len = 1;
41 while (n >= 10)
42 {
43 n /= 10;
44 len++;
45 }
46 return len;
47}
48
49/* Returns TV1 divided by TV2. */
50static unsigned
51tv_div (struct timeval *tv1, struct timeval *tv2)
52{
53 return
54 tv2->tv_sec
55 ? tv1->tv_sec / tv2->tv_sec
56 : (tv1->tv_usec / tv2->tv_usec
57 + (tv1->tv_sec ? tv1->tv_sec * 1000000 / tv2->tv_usec : 0));
58}
59
60/* Returns true if TV is zero. */
61static inline int
62tv_is_zero (struct timeval *tv)
63{
64 return tv->tv_sec == 0 && tv->tv_usec == 0;
65}
66
67/* Returns true if TV1 >= TV2. */
68static inline int
69tv_is_ge (struct timeval *tv1, struct timeval *tv2)
70{
71 return
72 tv1->tv_sec > tv2->tv_sec
73 || (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec >= tv2->tv_usec);
74}
75
76/* Format into BUF & BUF_LEN the time interval represented by TV, trying to
77 make the result less than WIDTH characters wide. The number of characters
78 used is returned. */
79size_t
80fmt_named_interval (struct timeval *tv, size_t width,
81 char *buf, size_t buf_len)
82{
83 struct tscale
84 {
85 struct timeval thresh; /* Minimum time to use this scale. */
86 struct timeval unit; /* Unit this scale is based on. */
87 struct timeval frac_thresh; /* If a emitting a single digit of precision
88 will cause at least this much error, also
89 emit a single fraction digit. */
90 char *sfxs[5]; /* Names to use, in descending length. */
91 }
92 time_scales[] =
93 {
94 {{2*YEAR(365*(24*(60*60))), 0}, {YEAR(365*(24*(60*60))), 0}, {MONTH(31*(24*(60*60))), 0},{" years", "years", "yrs", "y", 0 }},
95 {{3*MONTH(31*(24*(60*60))), 0}, {MONTH(31*(24*(60*60))), 0}, {WEEK(7*(24*(60*60))), 0}, {" months","months","mo", 0 }},
96 {{2*WEEK(7*(24*(60*60))), 0}, {WEEK(7*(24*(60*60))), 0}, {DAY(24*(60*60)), 0}, {" weeks", "weeks", "wks", "w", 0 }},
97 {{2*DAY(24*(60*60)), 0}, {DAY(24*(60*60)), 0}, {HOUR(60*60), 0}, {" days", "days", "dys", "d", 0 }},
98 {{2*HOUR(60*60), 0}, {HOUR(60*60), 0}, {MINUTE60, 0},{" hours","hours", "hrs", "h", 0 }},
99 {{2*MINUTE60, 0},{MINUTE60, 0},{1, 0}, {" minutes","min", "mi", "m", 0 }},
100 {{1, 100000}, {1, 0}, {0, 100000},{" seconds", "sec", "s", 0 }},
101 {{1, 0}, {1, 0}, {0, 0}, {" second", "sec", "s", 0 }},
102 {{0, 1100}, {0, 1000}, {0, 100}, {" milliseconds", "ms", 0 }},
103 {{0, 1000}, {0, 1000}, {0, 0}, {" millisecond", "ms", 0 }},
104 {{0, 2}, {0, 1}, {0, 0}, {" microseconds", "us", 0 }},
105 {{0, 1}, {0, 1}, {0, 0}, {" microsecond", "us", 0 }},
106 {{0, 0} }
107 };
108 struct tscale *ts = time_scales;
109
110 if (width <= 0 || width >= buf_len)
111 width = buf_len - 1;
112
113 for (ts = time_scales; !tv_is_zero (&ts->thresh); ts++)
114 if (tv_is_ge (tv, &ts->thresh))
115 {
116 char **sfx;
117 struct timeval *u = &ts->unit;
118 unsigned num = tv_div (tv, u);
119 unsigned frac = 0;
120 unsigned num_len = int_len (num);
121
122 if (num < 10
123 && !tv_is_zero (&ts->frac_thresh)
124 && tv_is_ge (tv, &ts->frac_thresh))
125 /* Calculate another place of prec, but only for low numbers. */
126 {
127 /* TV times 10. */
128 struct timeval tv10 =
129 { tv->tv_sec * 10 + tv->tv_usec / 100000,
130 (tv->tv_usec % 100000) * 10 };
131 frac = tv_div (&tv10, u) - num * 10;
132 if (frac)
133 num_len += 2; /* Account for the extra `.' + DIGIT. */
134 }
135
136 /* While we have a choice, find a suffix that fits in WIDTH. */
137 for (sfx = ts->sfxs; sfx[1]; sfx++)
138 if (num_len + strlen (*sfx) <= width)
139 break;
140
141 if (!sfx[1] && frac)
142 /* We couldn't find a suffix that fits, and we're printing a
143 fraction digit. Sacrifice the fraction to make it fit. */
144 {
145 num_len -= 2;
146 frac = 0;
147 for (sfx = ts->sfxs; sfx[1]; sfx++)
148 if (num_len + strlen (*sfx) <= width)
149 break;
150 }
151
152 if (!sfx[1])
153 /* Still couldn't find a suffix that fits. Oh well, use the best. */
154 sfx--;
155
156 if (frac)
157 return snprintf (buf, buf_len, "%d.%d%s", num, frac, *sfx);
158 else
159 return snprintf (buf, buf_len, "%d%s", num, *sfx);
160 }
161
162 return sprintf (buf, "0"); /* Whatever */
163}
164
165/* Prints the number of units of size UNIT in *SECS, subtracting them from
166 *SECS, to BUF (the result *must* fit!), followed by SUFFIX; if the number
167 of units is zero, however, and *LEADING_ZEROS is false, print nothing (and
168 if something *is* printed, set *LEADING_ZEROS to true). MIN_WIDTH is the
169 minimum *total width* (including other fields) needed to print these
170 units. WIDTH is the amount of (total) space available. The number of
171 characters printed is returned. */
172static size_t
173add_field (int *secs, int unit, int *leading_zeros,
174 size_t min_width, char *suffix,
175 size_t width, char *buf)
176{
177 int units = *secs / unit;
178 if (units || (width >= min_width && *leading_zeros))
179 {
180 *secs -= units * unit;
181 *leading_zeros = 1;
182 return
183 sprintf (buf,
184 (width == min_width ? "%d%s"
185 : width == min_width + 1 ? "%2d%s"
186 : "%02d%s"),
187 units, suffix);
188 }
189 else
190 return 0;
191}
192
193/* Format into BUF & BUF_LEN the time interval represented by TV, using
194 HH:MM:SS notation where possible, with FRAC_PLACES digits after the
195 decimal point, and trying to make the result less than WIDTH characters
196 wide. If LEADING_ZEROS is true, then any fields that are zero-valued, but
197 would fit in the given width are printed. If FRAC_PLACES is negative,
198 then any space remaining after printing the time, up to WIDTH, is used for
199 the fraction. The number of characters used is returned. */
200size_t
201fmt_seconds (struct timeval *tv, int leading_zeros, int frac_places,
202 size_t width, char *buf, size_t buf_len)
203{
204 char *p = buf;
205 int secs = tv->tv_sec;
206
207 if (width <= 0 || width >= buf_len)
208 width = buf_len - 1;
209
210 if (tv->tv_sec > DAY(24*(60*60)))
211 return fmt_named_interval (tv, width, buf, buf_len);
212
213 if (frac_places > 0)
214 width -= frac_places + 1;
215
216 /* See if this time won't fit at all in fixed format. */
217 if ((secs > 10*HOUR(60*60) && width < 8)
218 || (secs > HOUR(60*60) && width < 7)
219 || (secs > 10*MINUTE60 && width < 5)
220 || (secs > MINUTE60 && width < 4)
221 || (secs > 10 && width < 2)
222 || width < 1)
223 return fmt_named_interval (tv, width, buf, buf_len);
224
225 p += add_field (&secs, HOUR(60*60), &leading_zeros, 7, ":", width, p);
226 p += add_field (&secs, MINUTE60, &leading_zeros, 4, ":", width, p);
227 p += add_field (&secs, SECOND1, &leading_zeros, 1, "", width, p);
228
229 if (frac_places < 0 && (p - buf) < (int) width - 2)
230 /* If FRAC_PLACES is < 0, then use any space remaining before WIDTH. */
231 frac_places = width - (p - buf) - 1;
232
233 if (frac_places > 0)
234 /* Print fractions of a second. */
235 {
236 int frac = tv->tv_usec, i;
237 for (i = 6; i > frac_places; i--)
238 frac /= 10;
239 return (p - buf) + sprintf (p, ".%0*d", frac_places, frac);
240 }
241 else
242 return (p - buf);
243}
244
245/* Format into BUF & BUF_LEN the time interval represented by TV, using HH:MM
246 notation where possible, and trying to make the result less than WIDTH
247 characters wide. If LEADING_ZEROS is true, then any fields that are
248 zero-valued, but would fit in the given width are printed. The number of
249 characters used is returned. */
250size_t
251fmt_minutes (struct timeval *tv, int leading_zeros,
252 size_t width, char *buf, size_t buf_len)
253{
254 char *p = buf;
255 int secs = tv->tv_sec;
256
257 if (width <= 0 || width >= buf_len)
258 width = buf_len - 1;
259
260 if (secs > DAY(24*(60*60)))
261 return fmt_named_interval (tv, width, buf, buf_len);
262
263 /* See if this time won't fit at all in fixed format. */
264 if ((secs > 10*HOUR(60*60) && width < 5)
265 || (secs > HOUR(60*60) && width < 4)
266 || (secs > 10*MINUTE60 && width < 2)
267 || width < 1)
268 return fmt_named_interval (tv, width, buf, buf_len);
269
270 p += add_field (&secs, HOUR(60*60), &leading_zeros, 4, ":", width, p);
271 p += add_field (&secs, MINUTE60, &leading_zeros, 1, "", width, p);
272
273 return p - buf;
274}
275
276/* Format into BUF & BUF_LEN the absolute time represented by TV. An attempt
277 is made to fit the result in less than WIDTH characters, by omitting
278 fields implied by the current time, NOW (if NOW is 0, then no assumption
279 is made, so the resulting times will be somewhat long). The number of
280 characters used is returned. */
281size_t
282fmt_past_time (struct timeval *tv, struct timeval *now,
283 size_t width, char *buf, size_t buf_len)
284{
285 static char *time_fmts[] = { "%-r", "%-l:%M%p", "%-l%p", 0 };
286 static char *week_fmts[] = { "%A", "%a", 0 };
287 static char *month_fmts[] = { "%A %-d", "%a %-d", "%a%-d", 0 };
288 static char *date_fmts[] =
289 { "%A, %-d %B", "%a, %-d %b", "%-d %B", "%-d %b", "%-d%b", 0 };
290 static char *year_fmts[] =
291 { "%A, %-d %B %Y", "%a, %-d %b %Y", "%a, %-d %b %y", "%-d %b %y", "%-d%b%y", 0 };
292 struct tm tm;
293 int used = 0; /* Number of characters generated. */
294 long diff = now ? (now->tv_sec - tv->tv_sec) : tv->tv_sec;
295
296 if (diff < 0)
297 diff = -diff; /* XXX */
298
299 bcopy (localtime ((time_t *) &tv->tv_sec), &tm, sizeof tm);
300
301 if (width <= 0 || width >= buf_len)
302 width = buf_len - 1;
303
304 if (diff < DAY(24*(60*60)))
305 {
306 char **fmt;
307 for (fmt = time_fmts; *fmt && !used; fmt++)
308 used = strftime (buf, width + 1, *fmt, &tm);
309 if (! used)
310 /* Nothing worked, perhaps WIDTH is too small, but BUF_LEN will work.
311 We know FMT is one past the end the array, so FMT[-1] should be
312 the shortest possible option. */
313 used = strftime (buf, buf_len, fmt[-1], &tm);
314 }
315 else
316 {
317 static char *seps[] = { ", ", " ", "", 0 };
318 char **fmt, **dfmt, **dfmts, **sep;
319
320 if (diff < WEEK(7*(24*(60*60))))
321 dfmts = week_fmts;
322 else if (diff < MONTH(31*(24*(60*60))))
323 dfmts = month_fmts;
324 else if (diff < YEAR(365*(24*(60*60))))
325 dfmts = date_fmts;
326 else
327 dfmts = year_fmts;
328
329 /* This ordering (date varying most quickly, then the separator, then
330 the time) preserves time detail as long as possible, and seems to
331 produce a graceful degradation of the result with decreasing widths. */
332 for (fmt = time_fmts; *fmt && !used; fmt++)
333 for (sep = seps; *sep && !used; sep++)
334 for (dfmt = dfmts; *dfmt && !used; dfmt++)
335 {
336 char whole_fmt[strlen (*dfmt) + strlen (*sep) + strlen (*fmt) + 1];
337 char *end = whole_fmt;
338
339 end = stpcpy (end, *dfmt);
340 end = stpcpy (end, *sep);
341 end = stpcpy (end, *fmt);
Value stored to 'end' is never read
342
343 used = strftime (buf, width + 1, whole_fmt, &tm);
344 }
345
346 if (! used)
347 /* No concatenated formats worked, try just date formats. */
348 for (dfmt = dfmts; *dfmt && !used; dfmt++)
349 used = strftime (buf, width + 1, *dfmt, &tm);
350
351 if (! used)
352 /* Absolutely nothing has worked, perhaps WIDTH is too small, but
353 BUF_LEN will work. We know DFMT is one past the end the array, so
354 DFMT[-1] should be the shortest possible option. */
355 used = strftime (buf, buf_len, dfmt[-1], &tm);
356 }
357
358 return used;
359}