LCOV - code coverage report
Current view: top level - mogwai-tariff - main.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 182 264 68.9 %
Date: 2022-06-30 20:59:16 Functions: 13 16 81.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 69 117 59.0 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :            :  *
       3                 :            :  * Copyright © 2018 Endless Mobile, Inc.
       4                 :            :  *
       5                 :            :  * This library is free software; you can redistribute it and/or
       6                 :            :  * modify it under the terms of the GNU Lesser General Public
       7                 :            :  * License as published by the Free Software Foundation; either
       8                 :            :  * version 2.1 of the License, or (at your option) any later version.
       9                 :            :  *
      10                 :            :  * This library is distributed in the hope that it will be useful,
      11                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13                 :            :  * Lesser General Public License for more details.
      14                 :            :  *
      15                 :            :  * You should have received a copy of the GNU Lesser General Public
      16                 :            :  * License along with this library; if not, write to the Free Software
      17                 :            :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      18                 :            :  *
      19                 :            :  * Authors:
      20                 :            :  *  - Philip Withnall <withnall@endlessm.com>
      21                 :            :  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : 
      25                 :            : #include <gio/gio.h>
      26                 :            : #include <glib.h>
      27                 :            : #include <glib/gi18n.h>
      28                 :            : #include <glib-object.h>
      29                 :            : #include <glib-unix.h>
      30                 :            : #include <libmogwai-tariff/tariff-builder.h>
      31                 :            : #include <libmogwai-tariff/tariff-loader.h>
      32                 :            : #include <locale.h>
      33                 :            : #include <signal.h>
      34                 :            : #include <stdio.h>
      35                 :            : #include <string.h>
      36                 :            : #include <unistd.h>
      37                 :            : 
      38                 :            : 
      39                 :            : /* FIXME: Add tests for this client. */
      40                 :            : 
      41                 :            : /* Error handling. */
      42                 :            : typedef enum
      43                 :            : {
      44                 :            :   MWT_CLIENT_ERROR_INVALID_OPTIONS = 0,
      45                 :            :   MWT_CLIENT_ERROR_LOOKUP_FAILED,
      46                 :            : } MwtClientError;
      47                 :            : #define MWT_CLIENT_N_ERRORS (MWT_CLIENT_ERROR_INVALID_OPTIONS + 1)
      48                 :            : 
      49                 :            : GQuark mwt_client_error_quark (void);
      50                 :            : #define MWT_CLIENT_ERROR mwt_client_error_quark ()
      51                 :            : 
      52         [ +  + ]:          3 : G_DEFINE_QUARK (MwtClientError, mwt_client_error)
      53                 :            : 
      54                 :            : /* Exit statuses. */
      55                 :            : typedef enum
      56                 :            : {
      57                 :            :   /* Success. */
      58                 :            :   EXIT_OK = 0,
      59                 :            :   /* Error parsing command line options. */
      60                 :            :   EXIT_INVALID_OPTIONS = 1,
      61                 :            :   /* Specified period couldn’t be found (for ‘lookup’ command). */
      62                 :            :   EXIT_LOOKUP_FAILED = 2,
      63                 :            :   /* Command failed. */
      64                 :            :   EXIT_FAILED = 3,
      65                 :            : } ExitStatus;
      66                 :            : 
      67                 :            : /* Main function stuff. */
      68                 :            : typedef struct
      69                 :            : {
      70                 :            :   GCancellable *cancellable;  /* (owned) */
      71                 :            :   gint *signum_out;
      72                 :            :   guint handler_id;
      73                 :            : } SignalData;
      74                 :            : 
      75                 :            : static void
      76                 :         16 : signal_data_clear (SignalData *data)
      77                 :            : {
      78         [ +  - ]:         16 :   g_clear_object (&data->cancellable);
      79         [ +  - ]:         16 :   g_clear_handle_id (&data->handler_id, g_source_remove);
      80                 :         16 : }
      81                 :            : 
      82                 :         16 : G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (SignalData, signal_data_clear)
      83                 :            : 
      84                 :            : static gboolean
      85                 :          0 : handle_signal (SignalData *signal_data,
      86                 :            :                gint        signum)
      87                 :            : {
      88                 :          0 :   *signal_data->signum_out = signum;
      89                 :          0 :   g_cancellable_cancel (signal_data->cancellable);
      90                 :            : 
      91                 :            :   /* Remove the signal handler so when we raise again later, we don’t enter a
      92                 :            :    * loop. */
      93                 :          0 :   signal_data->handler_id = 0;
      94                 :          0 :   return G_SOURCE_REMOVE;
      95                 :            : }
      96                 :            : 
      97                 :            : static gboolean
      98                 :          0 : signal_sigint_cb (gpointer user_data)
      99                 :            : {
     100                 :          0 :   SignalData *signal_data = user_data;
     101                 :            : 
     102                 :          0 :   return handle_signal (signal_data, SIGINT);
     103                 :            : }
     104                 :            : 
     105                 :            : static gboolean
     106                 :          0 : signal_sigterm_cb (gpointer user_data)
     107                 :            : {
     108                 :          0 :   SignalData *signal_data = user_data;
     109                 :            : 
     110                 :          0 :   return handle_signal (signal_data, SIGTERM);
     111                 :            : }
     112                 :            : 
     113                 :            : /* Output handling functions. */
     114                 :            : static gchar *
     115                 :          5 : dump_period (MwtPeriod *period,
     116                 :            :              gboolean   use_colour)
     117                 :            : {
     118                 :         10 :   g_autoptr(GString) str = g_string_new ("");
     119                 :          5 :   GDateTime *start = mwt_period_get_start (period);
     120                 :          5 :   GDateTime *end = mwt_period_get_end (period);
     121                 :            : 
     122                 :         10 :   g_autofree gchar *start_str = g_date_time_format (start, "%Y-%m-%dT%H:%M:%S%:::z");
     123                 :         10 :   g_autofree gchar *end_str = g_date_time_format (end, "%Y-%m-%dT%H:%M:%S%:::z");
     124         [ -  + ]:          5 :   if (use_colour)
     125                 :          0 :     g_string_append (str, "\033[1m");  /* bold */
     126                 :          5 :   g_string_append_printf (str, _("Period %s – %s:"), start_str, end_str);
     127         [ -  + ]:          5 :   if (use_colour)
     128                 :          0 :     g_string_append (str, "\033[0m");  /* reset */
     129                 :          5 :   g_string_append_c (str, '\n');
     130                 :            : 
     131                 :          5 :   g_autofree gchar *repeat_str = NULL;
     132                 :          5 :   guint repeat_period = mwt_period_get_repeat_period (period);
     133   [ +  -  +  -  :          5 :   switch (mwt_period_get_repeat_type (period))
                -  +  - ]
     134                 :            :     {
     135                 :          2 :     case MWT_PERIOD_REPEAT_NONE:
     136                 :          2 :       repeat_str = g_strdup (_("Never repeats"));
     137                 :          2 :       break;
     138                 :          0 :     case MWT_PERIOD_REPEAT_HOUR:
     139                 :          0 :       repeat_str = g_strdup_printf (g_dngettext (NULL,
     140                 :            :                                                  "Repeats every %u hour",
     141                 :            :                                                  "Repeats every %u hours",
     142                 :            :                                                  repeat_period),
     143                 :            :                                     repeat_period);
     144                 :          0 :       break;
     145                 :          1 :     case MWT_PERIOD_REPEAT_DAY:
     146                 :          1 :       repeat_str = g_strdup_printf (g_dngettext (NULL,
     147                 :            :                                                  "Repeats every %u day",
     148                 :            :                                                  "Repeats every %u days",
     149                 :            :                                                  repeat_period),
     150                 :            :                                     repeat_period);
     151                 :          1 :       break;
     152                 :          0 :     case MWT_PERIOD_REPEAT_WEEK:
     153                 :          0 :       repeat_str = g_strdup_printf (g_dngettext (NULL,
     154                 :            :                                                  "Repeats every %u week",
     155                 :            :                                                  "Repeats every %u weeks",
     156                 :            :                                                  repeat_period),
     157                 :            :                                     repeat_period);
     158                 :          0 :       break;
     159                 :          0 :     case MWT_PERIOD_REPEAT_MONTH:
     160                 :          0 :       repeat_str = g_strdup_printf (g_dngettext (NULL,
     161                 :            :                                                  "Repeats every %u month",
     162                 :            :                                                  "Repeats every %u months",
     163                 :            :                                                  repeat_period),
     164                 :            :                                     repeat_period);
     165                 :          0 :       break;
     166                 :          2 :     case MWT_PERIOD_REPEAT_YEAR:
     167                 :          2 :       repeat_str = g_strdup_printf (g_dngettext (NULL,
     168                 :            :                                                  "Repeats every %u year",
     169                 :            :                                                  "Repeats every %u years",
     170                 :            :                                                  repeat_period),
     171                 :            :                                     repeat_period);
     172                 :          2 :       break;
     173                 :          0 :     default:
     174                 :          0 :       g_assert_not_reached ();
     175                 :            :     }
     176                 :            : 
     177                 :          5 :   g_string_append_printf (str, " • %s\n", repeat_str);
     178                 :            : 
     179                 :            :   /* Properties. */
     180                 :          5 :   guint64 capacity_limit = mwt_period_get_capacity_limit (period);
     181                 :          5 :   g_autofree gchar *capacity_limit_str = NULL;
     182                 :          5 :   g_autofree gchar *capacity_limit_value_str = NULL;
     183         [ +  + ]:          5 :   if (capacity_limit == G_MAXUINT64)
     184                 :          2 :     capacity_limit_value_str = g_strdup (_("unlimited"));
     185                 :            :   else
     186                 :          3 :     capacity_limit_value_str = g_format_size_full (capacity_limit,
     187                 :            :                                                    G_FORMAT_SIZE_LONG_FORMAT);
     188                 :          5 :   capacity_limit_str = g_strdup_printf (_("Capacity limit: %s"),
     189                 :            :                                         capacity_limit_value_str);
     190                 :          5 :   g_string_append_printf (str, " • %s\n", capacity_limit_str);
     191                 :            : 
     192                 :          5 :   return g_string_free (g_steal_pointer (&str), FALSE);
     193                 :            : }
     194                 :            : 
     195                 :            : static gchar *
     196                 :          3 : dump_tariff (MwtTariff *tariff,
     197                 :            :              gboolean   use_colour)
     198                 :            : {
     199                 :          6 :   g_autoptr(GString) str = g_string_new ("");
     200                 :            : 
     201                 :            :   /* Add a title and underline it. */
     202         [ -  + ]:          3 :   if (use_colour)
     203                 :          0 :     g_string_append (str, "\033[1;4m");  /* bold, underlined */
     204                 :            : 
     205                 :          6 :   g_autofree gchar *title = g_strdup_printf (_("Tariff ‘%s’"), mwt_tariff_get_name (tariff));
     206                 :          3 :   g_string_append (str, title);
     207                 :          3 :   g_string_append_c (str, '\n');
     208                 :            : 
     209         [ +  - ]:          3 :   if (!use_colour)
     210                 :            :     {
     211         [ +  + ]:         46 :       for (gsize i = 0; i < (gsize) g_utf8_strlen (title, -1); i++)
     212                 :         43 :         g_string_append_c (str, '-');
     213                 :          3 :       g_string_append_c (str, '\n');
     214                 :            :     }
     215                 :            : 
     216         [ -  + ]:          3 :   if (use_colour)
     217                 :          0 :     g_string_append (str, "\033[0m");  /* reset */
     218                 :            : 
     219                 :          3 :   g_string_append_c (str, '\n');
     220                 :            : 
     221                 :          3 :   GPtrArray *periods = mwt_tariff_get_periods (tariff);
     222                 :            : 
     223         [ +  + ]:          7 :   for (gsize i = 0; i < periods->len; i++)
     224                 :            :     {
     225                 :          4 :       MwtPeriod *period = g_ptr_array_index (periods, i);
     226                 :          8 :       g_autofree gchar *out = dump_period (period, use_colour);
     227                 :          4 :       g_string_append (str, out);
     228                 :            :     }
     229                 :            : 
     230                 :          3 :   return g_string_free (g_steal_pointer (&str), FALSE);
     231                 :            : }
     232                 :            : 
     233                 :            : /**
     234                 :            :  * load_tariff_from_file:
     235                 :            :  * @tariff_path: (type filename): tariff path, relative or absolute
     236                 :            :  *
     237                 :            :  * Returns: (transfer full): loaded tariff
     238                 :            :  */
     239                 :            : static MwtTariff *
     240                 :          5 : load_tariff_from_file (const gchar  *tariff_path,
     241                 :            :                        GError      **error)
     242                 :            : {
     243                 :         10 :   g_autofree gchar *tariff_path_utf8 = g_filename_display_name (tariff_path);
     244                 :            : 
     245                 :            :   /* Load the file. */
     246                 :          5 :   g_autoptr(GMappedFile) mmap = NULL;
     247                 :            : 
     248                 :          5 :   mmap = g_mapped_file_new (tariff_path, FALSE  /* read-only */, error);
     249         [ -  + ]:          5 :   if (mmap == NULL)
     250                 :            :     {
     251                 :          0 :       g_prefix_error (error, _("Error loading tariff file ‘%s’: "),
     252                 :            :                       tariff_path_utf8);
     253                 :          0 :       return NULL;
     254                 :            :     }
     255                 :            : 
     256                 :         10 :   g_autoptr(GBytes) bytes = g_mapped_file_get_bytes (mmap);
     257                 :            : 
     258                 :            :   /* Load the tariff. */
     259                 :         10 :   g_autoptr(MwtTariffLoader) loader = mwt_tariff_loader_new ();
     260                 :            : 
     261         [ -  + ]:          5 :   if (!mwt_tariff_loader_load_from_bytes (loader, bytes, error))
     262                 :            :     {
     263                 :          0 :       g_prefix_error (error, _("Error loading tariff file ‘%s’: "),
     264                 :            :                       tariff_path_utf8);
     265                 :          0 :       return NULL;
     266                 :            :     }
     267                 :            : 
     268                 :          5 :   MwtTariff *tariff = mwt_tariff_loader_get_tariff (loader);
     269         [ -  + ]:          5 :   g_assert (tariff != NULL);
     270                 :            : 
     271                 :          5 :   return g_object_ref (tariff);
     272                 :            : }
     273                 :            : 
     274                 :            : static GDateTime *
     275                 :         10 : date_time_from_string (const gchar  *str,
     276                 :            :                        GError      **error)
     277                 :            : {
     278                 :         10 :   g_autoptr(GDateTime) date_time = NULL;
     279                 :         10 :   date_time = g_date_time_new_from_iso8601 (str, NULL);
     280                 :            : 
     281         [ -  + ]:         10 :   if (date_time == NULL)
     282                 :            :     {
     283                 :          0 :       g_set_error (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_INVALID_OPTIONS,
     284                 :          0 :                    _("Invalid ISO 8601 date/time ‘%s’."), str);
     285                 :          0 :       return NULL;
     286                 :            :     }
     287                 :            : 
     288                 :         10 :   return g_steal_pointer (&date_time);
     289                 :            : }
     290                 :            : 
     291                 :            : static gboolean
     292                 :          4 : repeat_type_from_string (const gchar          *str,
     293                 :            :                          MwtPeriodRepeatType  *repeat_type_out,
     294                 :            :                          GError              **error)
     295                 :            : {
     296         [ -  + ]:          4 :   g_return_val_if_fail (repeat_type_out != NULL, FALSE);
     297                 :            : 
     298                 :            :   const struct
     299                 :            :     {
     300                 :            :       MwtPeriodRepeatType repeat_type;
     301                 :            :       const gchar *str;
     302                 :            :     }
     303                 :          4 :   repeat_types[] =
     304                 :            :     {
     305                 :            :       { MWT_PERIOD_REPEAT_NONE, "none" },
     306                 :            :       { MWT_PERIOD_REPEAT_HOUR, "hour" },
     307                 :            :       { MWT_PERIOD_REPEAT_DAY, "day" },
     308                 :            :       { MWT_PERIOD_REPEAT_WEEK, "week" },
     309                 :            :       { MWT_PERIOD_REPEAT_MONTH, "month" },
     310                 :            :       { MWT_PERIOD_REPEAT_YEAR, "year" },
     311                 :            :     };
     312                 :            : 
     313         [ -  + ]:          4 :   if (str == NULL)
     314                 :          0 :     str = "";
     315                 :            : 
     316         [ +  - ]:         11 :   for (gsize i = 0; i < G_N_ELEMENTS (repeat_types); i++)
     317                 :            :     {
     318         [ +  + ]:         11 :       if (g_str_equal (str, repeat_types[i].str))
     319                 :            :         {
     320                 :          4 :           *repeat_type_out = repeat_types[i].repeat_type;
     321                 :          4 :           return TRUE;
     322                 :            :         }
     323                 :            :     }
     324                 :            : 
     325                 :          0 :   g_set_error (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_INVALID_OPTIONS,
     326                 :          0 :                _("Unknown repeat type ‘%s’."), str);
     327                 :          0 :   return FALSE;
     328                 :            : }
     329                 :            : 
     330                 :            : /* Accept an unsigned integer (to a guint64) or ‘unlimited’. */
     331                 :            : static gboolean
     332                 :          4 : capacity_limit_from_string (const gchar  *str,
     333                 :            :                             guint64      *capacity_limit_out,
     334                 :            :                             GError      **error)
     335                 :            : {
     336         [ -  + ]:          4 :   g_return_val_if_fail (capacity_limit_out != NULL, FALSE);
     337                 :            : 
     338   [ +  -  +  + ]:          4 :   if (str != NULL && g_str_equal (str, "unlimited"))
     339                 :            :     {
     340                 :          2 :       *capacity_limit_out = G_MAXUINT64;
     341                 :          2 :       return TRUE;
     342                 :            :     }
     343                 :            : 
     344                 :          2 :   return g_ascii_string_to_unsigned (str, 10, 0, G_MAXUINT64,
     345                 :            :                                      capacity_limit_out, error);
     346                 :            : }
     347                 :            : 
     348                 :            : /* Handle a command like
     349                 :            :  *   mogwai-tariff lookup tariff.file 2018-10-02T10:15:00Z
     350                 :            :  * by looking up which period applies at that time, and dumping its properties.
     351                 :            :  */
     352                 :            : static gboolean
     353                 :          2 : handle_lookup (const gchar * const  *args,
     354                 :            :                gboolean              use_colour,
     355                 :            :                GError              **error)
     356                 :            : {
     357                 :            :   /* Parse arguments. */
     358   [ +  -  -  + ]:          2 :   if (args == NULL || g_strv_length ((gchar **) args) != 2)
     359                 :            :     {
     360                 :          0 :       g_set_error_literal (error, MWT_CLIENT_ERROR,
     361                 :            :                            MWT_CLIENT_ERROR_INVALID_OPTIONS,
     362                 :          0 :                            _("A TARIFF and LOOKUP-TIME are required."));
     363                 :          0 :       return FALSE;
     364                 :            :     }
     365                 :            : 
     366                 :            :   /* Note @tariff_path is (type filename) not (type utf8). */
     367                 :          2 :   const gchar *tariff_path = args[0];
     368                 :          2 :   const gchar *lookup_time_str = args[1];
     369                 :            : 
     370                 :          4 :   g_autoptr(GDateTime) lookup_time = date_time_from_string (lookup_time_str, error);
     371         [ -  + ]:          2 :   if (lookup_time == NULL)
     372                 :            :     {
     373                 :          0 :       g_prefix_error (error, _("Invalid LOOKUP-TIME: "));
     374                 :          0 :       return FALSE;
     375                 :            :     }
     376                 :            : 
     377                 :            :   /* Load the file. */
     378                 :          4 :   g_autoptr(MwtTariff) tariff = load_tariff_from_file (tariff_path, error);
     379         [ -  + ]:          2 :   if (tariff == NULL)
     380                 :          0 :     return FALSE;
     381                 :            : 
     382                 :          2 :   MwtPeriod *period = mwt_tariff_lookup_period (tariff, lookup_time);
     383                 :            : 
     384         [ +  + ]:          2 :   if (period == NULL)
     385                 :            :     {
     386                 :          1 :       g_set_error (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_LOOKUP_FAILED,
     387                 :          1 :                    _("No period matches the given date/time."));
     388                 :          1 :       return FALSE;
     389                 :            :     }
     390                 :            :   else
     391                 :            :     {
     392                 :          2 :       g_autofree gchar *out = dump_period (period, use_colour);
     393                 :          1 :       g_print ("%s", out);
     394                 :            :     }
     395                 :            : 
     396                 :          1 :   return TRUE;
     397                 :            : }
     398                 :            : 
     399                 :            : /* Handle a command like
     400                 :            :  *   mogwai-tariff dump tariff.file
     401                 :            :  * by dumping all the periods from that file, plus the file metadata.
     402                 :            :  */
     403                 :            : static gboolean
     404                 :          3 : handle_dump (const gchar * const  *args,
     405                 :            :              gboolean              use_colour,
     406                 :            :              GError              **error)
     407                 :            : {
     408                 :            :   /* Parse arguments. */
     409   [ +  -  -  + ]:          3 :   if (args == NULL || g_strv_length ((gchar **) args) != 1)
     410                 :            :     {
     411                 :          0 :       g_set_error_literal (error, MWT_CLIENT_ERROR,
     412                 :            :                            MWT_CLIENT_ERROR_INVALID_OPTIONS,
     413                 :          0 :                            _("A TARIFF is required."));
     414                 :          0 :       return FALSE;
     415                 :            :     }
     416                 :            : 
     417                 :          3 :   const gchar *tariff_path = args[0];
     418                 :            : 
     419                 :          6 :   g_autoptr(MwtTariff) tariff = load_tariff_from_file (tariff_path, error);
     420         [ -  + ]:          3 :   if (tariff == NULL)
     421                 :          0 :     return FALSE;
     422                 :            : 
     423                 :          3 :   g_autofree gchar *out = dump_tariff (tariff, use_colour);
     424                 :          3 :   g_print ("%s", out);
     425                 :            : 
     426                 :          3 :   return TRUE;
     427                 :            : }
     428                 :            : 
     429                 :            : /* Handle a command like
     430                 :            :  *   mogwai-tariff build tariff.file name \
     431                 :            :  *     [start end repeat-type repeat-period capacity-limit] \
     432                 :            :  *     …
     433                 :            :  * by building and saving the given tariff.
     434                 :            :  */
     435                 :            : static gboolean
     436                 :          3 : handle_build (const gchar * const  *args,
     437                 :            :               gboolean              use_colour,
     438                 :            :               GError              **error)
     439                 :            : {
     440                 :            :   /* Parse arguments. */
     441                 :          3 :   const guint n_args_per_period = 5;
     442         [ +  - ]:          3 :   guint n_args = (args != NULL) ? g_strv_length ((gchar **) args) : 0;
     443   [ +  -  +  - ]:          3 :   if (args == NULL || n_args < 2 + n_args_per_period ||
     444         [ -  + ]:          3 :       ((n_args - 2) % n_args_per_period) != 0)
     445                 :            :     {
     446                 :          0 :       g_set_error_literal (error, MWT_CLIENT_ERROR,
     447                 :            :                            MWT_CLIENT_ERROR_INVALID_OPTIONS,
     448                 :          0 :                            _("A TARIFF and NAME and at least one PERIOD are required."));
     449                 :          0 :       return FALSE;
     450                 :            :     }
     451                 :            : 
     452                 :          3 :   const gchar *tariff_path = args[0];
     453                 :          3 :   const gchar *tariff_name = args[1];
     454                 :            : 
     455                 :          6 :   g_autoptr(MwtTariffBuilder) builder = mwt_tariff_builder_new ();
     456                 :            : 
     457                 :          3 :   mwt_tariff_builder_set_name (builder, tariff_name);
     458                 :            : 
     459         [ +  + ]:          7 :   for (gsize i = 2; i < n_args; i += n_args_per_period)
     460                 :            :     {
     461                 :            :       /* Parse the arguments for this period. */
     462                 :          4 :       const gchar *start_str = args[i];
     463                 :          4 :       const gchar *end_str = args[i + 1];
     464                 :          4 :       const gchar *repeat_type_str = args[i + 2];
     465                 :          4 :       const gchar *repeat_period_str = args[i + 3];
     466                 :          4 :       const gchar *capacity_limit_str = args[i + 4];
     467                 :            : 
     468         [ +  - ]:          8 :       g_autoptr(GDateTime) start = date_time_from_string (start_str, error);
     469         [ -  + ]:          4 :       if (start == NULL)
     470                 :            :         {
     471                 :          0 :           g_prefix_error (error, _("Invalid START: "));
     472                 :          0 :           return FALSE;
     473                 :            :         }
     474                 :            : 
     475         [ +  - ]:          8 :       g_autoptr(GDateTime) end = date_time_from_string (end_str, error);
     476         [ -  + ]:          4 :       if (end == NULL)
     477                 :            :         {
     478                 :          0 :           g_prefix_error (error, _("Invalid END: "));
     479                 :          0 :           return FALSE;
     480                 :            :         }
     481                 :            : 
     482                 :            :       MwtPeriodRepeatType repeat_type;
     483         [ -  + ]:          4 :       if (!repeat_type_from_string (repeat_type_str, &repeat_type, error))
     484                 :            :         {
     485                 :          0 :           g_prefix_error (error, _("Invalid REPEAT-TYPE: "));
     486                 :          0 :           return FALSE;
     487                 :            :         }
     488                 :            : 
     489                 :            :       guint64 repeat_period64;
     490         [ -  + ]:          4 :       if (!g_ascii_string_to_unsigned (repeat_period_str, 10, 0, G_MAXUINT,
     491                 :            :                                        &repeat_period64, error))
     492                 :            :         {
     493                 :          0 :           g_prefix_error (error, _("Invalid REPEAT-PERIOD: "));
     494                 :          0 :           return FALSE;
     495                 :            :         }
     496         [ -  + ]:          4 :       g_assert (repeat_period64 <= G_MAXUINT);
     497                 :          4 :       guint repeat_period = repeat_period64;
     498                 :            : 
     499                 :            :       guint64 capacity_limit;
     500         [ -  + ]:          4 :       if (!capacity_limit_from_string (capacity_limit_str, &capacity_limit, error))
     501                 :            :         {
     502                 :          0 :           g_prefix_error (error, _("Invalid CAPACITY-LIMIT: "));
     503                 :          0 :           return FALSE;
     504                 :            :         }
     505                 :            : 
     506         [ -  + ]:          4 :       if (!mwt_period_validate (start, end, repeat_type, repeat_period, error))
     507                 :            :         {
     508                 :          0 :           g_prefix_error (error, _("Error validating period %" G_GSIZE_FORMAT ": "),
     509                 :          0 :                           (i - 2) / n_args_per_period);
     510                 :          0 :           return FALSE;
     511                 :            :         }
     512                 :            : 
     513                 :          4 :       g_autoptr(MwtPeriod) period = NULL;
     514                 :          4 :       period = mwt_period_new (start, end, repeat_type, repeat_period,
     515                 :            :                                "capacity-limit", capacity_limit,
     516                 :            :                                NULL);
     517                 :          4 :       mwt_tariff_builder_add_period (builder, period);
     518                 :            :     }
     519                 :            : 
     520                 :            :   /* Save the tariff. */
     521                 :          6 :   g_autoptr(GBytes) bytes = mwt_tariff_builder_get_tariff_as_bytes (builder);
     522         [ -  + ]:          3 :   if (bytes == NULL)
     523                 :            :     {
     524                 :            :       /* FIXME: Would be good to change the #MwtTariffBuilder API to expose
     525                 :            :        * the validation error here. */
     526                 :          0 :       g_set_error (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_INVALID_OPTIONS,
     527                 :          0 :                    _("Error building tariff: periods are invalid."));
     528                 :          0 :       return FALSE;
     529                 :            :     }
     530                 :            : 
     531         [ -  + ]:          3 :   if (!g_file_set_contents (tariff_path, g_bytes_get_data (bytes, NULL),
     532                 :          3 :                             g_bytes_get_size (bytes), error))
     533                 :            :     {
     534                 :          0 :       g_autofree gchar *tariff_path_utf8 = g_filename_display_name (tariff_path);
     535                 :          0 :       g_prefix_error (error, _("Error saving tariff file ‘%s’: "),
     536                 :            :                       tariff_path_utf8);
     537                 :          0 :       return FALSE;
     538                 :            :     }
     539                 :            : 
     540                 :          3 :   return TRUE;
     541                 :            : }
     542                 :            : 
     543                 :            : int
     544                 :          8 : main (int   argc,
     545                 :            :       char *argv[])
     546                 :            : {
     547                 :          8 :   g_autoptr(GError) error = NULL;
     548                 :            : 
     549                 :            :   /* Localisation */
     550                 :          8 :   setlocale (LC_ALL, "");
     551                 :          8 :   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
     552                 :          8 :   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
     553                 :          8 :   textdomain (GETTEXT_PACKAGE);
     554                 :            : 
     555                 :            :   /* Set up signal handlers. */
     556                 :         16 :   g_autoptr(GCancellable) cancellable = g_cancellable_new ();
     557                 :          8 :   gint signum = 0;
     558                 :            : 
     559                 :          8 :   g_auto(SignalData) sigint_data = { NULL, &signum, 0 };
     560                 :          8 :   sigint_data.cancellable = g_object_ref (cancellable);
     561                 :          8 :   sigint_data.handler_id = g_unix_signal_add (SIGINT, signal_sigint_cb, &sigint_data);
     562                 :            : 
     563                 :          8 :   g_auto(SignalData) sigterm_data = { NULL, &signum, 0 };
     564                 :          8 :   sigterm_data.cancellable = g_object_ref (cancellable);
     565                 :          8 :   sigterm_data.handler_id = g_unix_signal_add (SIGTERM, signal_sigterm_cb, &sigterm_data);
     566                 :            : 
     567                 :            :   /* Only valid for g_print() calls, not g_printerr(). */
     568                 :          8 :   gboolean use_colour = g_log_writer_supports_color (fileno (stdout));
     569                 :            : 
     570                 :            :   /* Handle command line parameters. */
     571                 :          8 :   g_auto (GStrv) args = NULL;
     572                 :            : 
     573                 :          8 :   const GOptionEntry entries[] =
     574                 :            :     {
     575                 :            :       { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY,
     576                 :            :         &args, NULL, NULL },
     577                 :            :       { NULL, },
     578                 :            :     };
     579                 :            : 
     580                 :          8 :   g_autoptr(GOptionContext) context = NULL;
     581                 :          8 :   context = g_option_context_new (_("COMMAND ARGS"));
     582                 :          8 :   g_option_context_set_summary (context, _("Create and view network connection tariffs"));
     583                 :          8 :   g_option_context_set_description (context,
     584                 :          8 :       _("Commands:\n"
     585                 :            :         "  build TARIFF NAME START END REPEAT-TYPE REPEAT-PERIOD CAPACITY-LIMIT […]\n"
     586                 :            :         "    Build a new tariff called NAME and save it to the TARIFF file.\n"
     587                 :            :         "    Add one or more periods using the given arguments.\n"
     588                 :            :         "  dump TARIFF\n"
     589                 :            :         "    Dump all periods from the given TARIFF file.\n"
     590                 :            :         "  lookup TARIFF LOOKUP-TIME\n"
     591                 :            :         "    Look up the period which covers LOOKUP-TIME in the given TARIFF file.\n"));
     592                 :          8 :   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
     593                 :            : 
     594         [ -  + ]:          8 :   if (!g_option_context_parse (context, &argc, &argv, &error))
     595                 :            :     {
     596                 :          0 :       g_autofree gchar *message = NULL;
     597                 :          0 :       message = g_strdup_printf (_("Option parsing failed: %s"),
     598                 :          0 :                                  error->message);
     599                 :          0 :       g_printerr ("%s: %s\n", argv[0], message);
     600                 :            : 
     601                 :          0 :       return EXIT_INVALID_OPTIONS;
     602                 :            :     }
     603                 :            : 
     604   [ +  -  -  + ]:          8 :   if (args == NULL || args[0] == NULL)
     605                 :            :     {
     606                 :          0 :       g_autofree gchar *message = NULL;
     607                 :          0 :       message = g_strdup_printf (_("Option parsing failed: %s"),
     608                 :            :                                  _("A COMMAND is required"));
     609                 :          0 :       g_printerr ("%s: %s\n", argv[0], message);
     610                 :            : 
     611                 :          0 :       return EXIT_INVALID_OPTIONS;
     612                 :            :     }
     613                 :            : 
     614                 :            :   /* Handle the different commands. */
     615         [ +  + ]:          8 :   if (g_str_equal (args[0], "build"))
     616                 :          3 :     handle_build ((const gchar * const *) args + 1, use_colour, &error);
     617         [ +  + ]:          5 :   else if (g_str_equal (args[0], "dump"))
     618                 :          3 :     handle_dump ((const gchar * const *) args + 1, use_colour, &error);
     619         [ +  - ]:          2 :   else if (g_str_equal (args[0], "lookup"))
     620                 :          2 :     handle_lookup ((const gchar * const *) args + 1, use_colour, &error);
     621                 :            :   else
     622                 :          0 :     g_set_error (&error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_INVALID_OPTIONS,
     623                 :          0 :                  _("Unrecognised command ‘%s’"), args[1]);
     624                 :            : 
     625         [ +  + ]:          8 :   if (error != NULL)
     626                 :            :     {
     627                 :          1 :       int exit_status = EXIT_FAILED;
     628                 :            : 
     629         [ -  + ]:          1 :       if (g_error_matches (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_INVALID_OPTIONS))
     630                 :            :         {
     631                 :          0 :           g_prefix_error (&error, _("Option parsing failed: "));
     632                 :          0 :           exit_status = EXIT_INVALID_OPTIONS;
     633                 :            :         }
     634         [ +  - ]:          1 :       else if (g_error_matches (error, MWT_CLIENT_ERROR, MWT_CLIENT_ERROR_LOOKUP_FAILED))
     635                 :            :         {
     636                 :          1 :           exit_status = EXIT_LOOKUP_FAILED;
     637                 :            :         }
     638                 :            : 
     639                 :          1 :       g_printerr ("%s: %s\n", argv[0], error->message);
     640                 :            : 
     641                 :          1 :       return exit_status;
     642                 :            :     }
     643                 :            : 
     644                 :          7 :   return EXIT_OK;
     645                 :            : }

Generated by: LCOV version 1.16