libexplain  1.4.D001
libexplain/buffer/timespec.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2009-2013 Peter Miller
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU Lesser General Public License as
00007  * published by the Free Software Foundation; either version 3 of the
00008  * License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <libexplain/ac/limits.h>
00020 #include <libexplain/ac/math.h>
00021 #include <libexplain/ac/stdlib.h>
00022 #include <libexplain/ac/string.h>
00023 #include <libexplain/ac/sys/stat.h> /* for UTIME_NOW, etc */
00024 
00025 #include <libexplain/buffer/long.h>
00026 #include <libexplain/buffer/pointer.h>
00027 #include <libexplain/buffer/time_t.h>
00028 #include <libexplain/buffer/timespec.h>
00029 #include <libexplain/is_efault.h>
00030 #include <libexplain/option.h>
00031 
00032 
00038 static void
00039 print_longish(explain_string_buffer_t *sb, long x)
00040 {
00041     if (x == UTIME_NOW)
00042     {
00043         explain_string_buffer_puts(sb, "UTIME_NOW");
00044         return;
00045     }
00046     if (x == UTIME_OMIT)
00047     {
00048         explain_string_buffer_puts(sb, "UTIME_OMIT");
00049         return;
00050     }
00051     explain_buffer_long(sb, x);
00052 }
00053 
00054 
00055 static void
00056 print(explain_string_buffer_t *sb, const struct timespec *data)
00057 {
00067     if (data->tv_nsec < 0 || data->tv_nsec >= 1000000000L)
00068     {
00069         explain_string_buffer_puts(sb, "{ tv_sec = ");
00070         if
00071         (
00072             data->tv_sec == 0
00073         &&
00074             (data->tv_nsec == UTIME_OMIT || data->tv_nsec == UTIME_NOW)
00075         )
00076             explain_string_buffer_puts(sb, "0");
00077         else
00078             explain_buffer_time_t(sb, data->tv_sec);
00079         explain_string_buffer_puts(sb, ", tv_nsec = ");
00080         print_longish(sb, data->tv_nsec);
00081         explain_string_buffer_puts(sb, " }");
00082     }
00083     else
00084     {
00085         /*
00086          * This is a heuristic to distinguish between event dalta times and
00087          * high-precision system time and date usage.  It may need tuning.
00088          */
00089         if (data->tv_sec < 1000)
00090         {
00091             explain_string_buffer_printf
00092             (
00093                 sb,
00094                 "{ %.11g seconds }",
00095                 data->tv_sec + 1e-9 * data->tv_nsec
00096             );
00097         }
00098         else
00099         {
00100             explain_string_buffer_printf
00101             (
00102                 sb,
00103                 "{ %11.9f seconds",
00104                 data->tv_sec + 1e-9 * data->tv_nsec
00105             );
00106             if (explain_option_dialect_specific())
00107             {
00108                 struct tm       *tmp;
00109 
00110                 tmp = localtime(&data->tv_sec);
00111                 if (tmp)
00112                 {
00113                     char            buffer[200];
00114 
00115                     strftime
00116                     (
00117                         buffer,
00118                         sizeof(buffer),
00119                         " \"%a, %Y-%b-%d %H:%M:%S %z\"",
00120                         tmp
00121                     );
00122                     explain_string_buffer_puts(sb, buffer);
00123                 }
00124             }
00125             explain_string_buffer_puts(sb, " }");
00126         }
00127     }
00128 }
00129 
00130 
00131 void
00132 explain_buffer_timespec(explain_string_buffer_t *sb,
00133     const struct timespec *data)
00134 {
00135     if (explain_is_efault_pointer(data, sizeof(*data)))
00136         explain_buffer_pointer(sb, data);
00137     else
00138         print(sb, data);
00139 }
00140 
00141 
00142 void
00143 explain_buffer_timespec_array(explain_string_buffer_t *sb,
00144     const struct timespec *data, unsigned data_size)
00145 {
00146     unsigned        j;
00147 
00148     if (explain_is_efault_pointer(data, sizeof(*data) * data_size))
00149     {
00150         explain_buffer_pointer(sb, data);
00151         return;
00152     }
00153     explain_string_buffer_putc(sb, '{');
00154     for (j = 0; j < data_size; ++j)
00155     {
00156         if (j)
00157             explain_string_buffer_puts(sb, ", ");
00158         print(sb, data + j);
00159     }
00160     explain_string_buffer_putc(sb, '}');
00161 }
00162 
00163 
00164 void
00165 explain_parse_timespec_or_die(const char *text, const char *caption,
00166     struct timespec *result)
00167 {
00168     if (0 == strcmp(text, "UTIME_NOW"))
00169     {
00170         result->tv_sec = 0;
00171         result->tv_nsec = UTIME_NOW;
00172         return;
00173     }
00174     if (0 == strcmp(text, "UTIME_OMIT"))
00175     {
00176         result->tv_sec = 0;
00177         result->tv_nsec = UTIME_OMIT;
00178         return;
00179     }
00180 
00181     {
00182         char *ep = 0;
00183         double n = strtod(text, &ep);
00184         if (ep != text && !*ep)
00185         {
00186             /* cope with overflow */
00187             if (n + 0.5 >= MAX_TIME_T)
00188             {
00189                 result->tv_sec = MAX_TIME_T;
00190                 result->tv_nsec = 0;
00191                 return;
00192             }
00193 
00194             /* cope with underflow */
00195             if (n - 0.5 <= MIN_TIME_T)
00196             {
00197                 result->tv_sec = MIN_TIME_T;
00198                 result->tv_nsec = 0;
00199                 return;
00200             }
00201 
00202             /*
00203              * Note: tv_sec is signed, but tv_nsec is required to be in
00204              * the range 0..<1e9, because negative values mean something
00205              * else.  The floor() function will push negative tv_sec
00206              * values more negative, which means adding the positive
00207              * tv_nsec part brings them back up to the appropriate
00208              * floating point value.
00209              */
00210             result->tv_sec = floor(n);
00211             result->tv_nsec = floor((n - result->tv_sec) * 1e9 + 0.5);
00212             if (result->tv_nsec >= 1000000000)
00213             {
00214                 /* could have overflowed on rounding */
00215                 result->tv_sec++;
00216                 result->tv_nsec = 0;
00217             }
00218             return;
00219         }
00220     }
00221 
00222     {
00223         time_t n = explain_parse_time_t_or_die(text, caption);
00224         result->tv_sec = n;
00225         result->tv_nsec = 0;
00226     }
00227 }
00228 
00229 
00230 /* vim: set ts=8 sw=4 et : */