LCOV - code coverage report
Current view: top level - src/core - arg_parse.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 28.6 % 332 95
Test Date: 2026-04-20 19:54:24 Functions: 73.3 % 15 11

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file arg_parse.c
       6              :  * @brief Custom command-line argument parser for tg-cli.
       7              :  */
       8              : 
       9              : #include "arg_parse.h"
      10              : 
      11              : #include <stdio.h>
      12              : #include <string.h>
      13              : #include <stdlib.h>
      14              : 
      15              : #define TG_CLI_VERSION "0.1.0"
      16              : 
      17              : #ifndef TG_CLI_GIT_COMMIT
      18              : #  define TG_CLI_GIT_COMMIT "unknown"
      19              : #endif
      20              : 
      21              : /* ---- Internal helpers ---- */
      22              : 
      23              : /* Sentinels returned only by try_global_flag — never exposed to callers. */
      24              : #define TGF_HELP    -10
      25              : #define TGF_VERSION -11
      26              : 
      27          107 : static int str_eq(const char *a, const char *b) {
      28          107 :     return strcmp(a, b) == 0;
      29              : }
      30              : 
      31            1 : static int parse_int(const char *s, int *out) {
      32            1 :     if (!s || !*s) return -1;
      33            1 :     char *end = NULL;
      34            1 :     long v = strtol(s, &end, 10);
      35            1 :     if (*end != '\0') return -1;
      36            1 :     *out = (int)v;
      37            1 :     return 0;
      38              : }
      39              : 
      40              : /* ---- Global flag parsing (returns number of argv entries consumed) ----
      41              :  *
      42              :  * Called for each positional slot; returns 0 if argv[i] is not a global
      43              :  * flag, 1 for a boolean flag, 2 for a flag that takes a value.
      44              :  */
      45            0 : static int take_value_flag(int argc, char **argv, int i,
      46              :                             const char *name, const char **out) {
      47            0 :     if (i + 1 >= argc) {
      48            0 :         fprintf(stderr, "tg-cli: %s requires a value\n", name);
      49            0 :         return -1;
      50              :     }
      51            0 :     *out = argv[i + 1];
      52            0 :     return 2;
      53              : }
      54              : 
      55            0 : static int try_global_flag(int argc, char **argv, int i, ArgResult *out) {
      56            0 :     const char *a = argv[i];
      57              : 
      58            0 :     if (str_eq(a, "--json"))   { out->json   = 1; return 1; }
      59            0 :     if (str_eq(a, "--quiet"))  { out->quiet  = 1; return 1; }
      60            0 :     if (str_eq(a, "--help")   || str_eq(a, "-h")) return TGF_HELP;
      61            0 :     if (str_eq(a, "--version") || str_eq(a, "-v")) return TGF_VERSION;
      62              : 
      63            0 :     if (str_eq(a, "--config"))
      64            0 :         return take_value_flag(argc, argv, i, "--config", &out->config_path);
      65            0 :     if (str_eq(a, "--phone"))
      66            0 :         return take_value_flag(argc, argv, i, "--phone", &out->phone);
      67            0 :     if (str_eq(a, "--code"))
      68            0 :         return take_value_flag(argc, argv, i, "--code", &out->code);
      69            0 :     if (str_eq(a, "--password"))
      70            0 :         return take_value_flag(argc, argv, i, "--password", &out->password);
      71              : 
      72            0 :     return 0; /* not a global flag */
      73              : }
      74              : 
      75              : /* ---- Subcommand parsers ---- */
      76              : 
      77            1 : static int parse_dialogs(int argc, char **argv, int i, ArgResult *out) {
      78            1 :     out->command = CMD_DIALOGS;
      79            1 :     out->limit = 20; /* default */
      80              : 
      81            1 :     while (i < argc) {
      82            0 :         if (str_eq(argv[i], "--limit")) {
      83            0 :             if (i + 1 >= argc) {
      84            0 :                 fprintf(stderr, "tg-cli dialogs: --limit requires a number\n");
      85            0 :                 return ARG_ERROR;
      86              :             }
      87            0 :             if (parse_int(argv[i + 1], &out->limit) != 0) {
      88            0 :                 fprintf(stderr, "tg-cli dialogs: --limit value is not a number\n");
      89            0 :                 return ARG_ERROR;
      90              :             }
      91            0 :             if (out->limit < 1 || out->limit > 1000) {
      92            0 :                 fprintf(stderr,
      93              :                         "tg-cli dialogs: --limit %d out of range [1, 1000]\n",
      94              :                         out->limit);
      95            0 :                 return ARG_ERROR;
      96              :             }
      97            0 :             i += 2;
      98            0 :         } else if (str_eq(argv[i], "--archived")) {
      99            0 :             out->archived = 1;
     100            0 :             i++;
     101              :         } else {
     102            0 :             fprintf(stderr, "tg-cli dialogs: unknown option: %s\n", argv[i]);
     103            0 :             return ARG_ERROR;
     104              :         }
     105              :     }
     106            1 :     return ARG_OK;
     107              : }
     108              : 
     109            1 : static int parse_history(int argc, char **argv, int i, ArgResult *out) {
     110            1 :     out->command = CMD_HISTORY;
     111            1 :     out->limit  = 50; /* default */
     112            1 :     out->offset = 0;
     113              : 
     114            1 :     if (i >= argc) {
     115            0 :         fprintf(stderr, "tg-cli history: <peer> argument required\n");
     116            0 :         return ARG_ERROR;
     117              :     }
     118            1 :     if (argv[i][0] == '-') {
     119            0 :         fprintf(stderr, "tg-cli history: <peer> argument required before options\n");
     120            0 :         return ARG_ERROR;
     121              :     }
     122            1 :     out->peer = argv[i++];
     123              : 
     124            1 :     while (i < argc) {
     125            0 :         if (str_eq(argv[i], "--limit")) {
     126            0 :             if (i + 1 >= argc) {
     127            0 :                 fprintf(stderr, "tg-cli history: --limit requires a number\n");
     128            0 :                 return ARG_ERROR;
     129              :             }
     130            0 :             if (parse_int(argv[i + 1], &out->limit) != 0) {
     131            0 :                 fprintf(stderr, "tg-cli history: --limit value is not a number\n");
     132            0 :                 return ARG_ERROR;
     133              :             }
     134            0 :             if (out->limit < 1 || out->limit > 1000) {
     135            0 :                 fprintf(stderr,
     136              :                         "tg-cli history: --limit %d out of range [1, 1000]\n",
     137              :                         out->limit);
     138            0 :                 return ARG_ERROR;
     139              :             }
     140            0 :             i += 2;
     141            0 :         } else if (str_eq(argv[i], "--offset")) {
     142            0 :             if (i + 1 >= argc) {
     143            0 :                 fprintf(stderr, "tg-cli history: --offset requires a number\n");
     144            0 :                 return ARG_ERROR;
     145              :             }
     146            0 :             if (parse_int(argv[i + 1], &out->offset) != 0) {
     147            0 :                 fprintf(stderr, "tg-cli history: --offset value is not a number\n");
     148            0 :                 return ARG_ERROR;
     149              :             }
     150            0 :             i += 2;
     151            0 :         } else if (str_eq(argv[i], "--no-media")) {
     152            0 :             out->no_media = 1;
     153            0 :             i++;
     154              :         } else {
     155            0 :             fprintf(stderr, "tg-cli history: unknown option: %s\n", argv[i]);
     156            0 :             return ARG_ERROR;
     157              :         }
     158              :     }
     159            1 :     return ARG_OK;
     160              : }
     161              : 
     162            0 : static int parse_send(int argc, char **argv, int i, ArgResult *out) {
     163            0 :     out->command = CMD_SEND;
     164              : 
     165            0 :     if (i >= argc) {
     166            0 :         fprintf(stderr, "tg-cli send: <peer> argument required\n");
     167            0 :         return ARG_ERROR;
     168              :     }
     169            0 :     if (argv[i][0] == '-') {
     170            0 :         fprintf(stderr, "tg-cli send: <peer> argument required before options\n");
     171            0 :         return ARG_ERROR;
     172              :     }
     173            0 :     out->peer = argv[i++];
     174              : 
     175              :     /* Optional --reply <msg_id> before the message text. */
     176            0 :     while (i < argc && argv[i][0] == '-') {
     177            0 :         if (str_eq(argv[i], "--reply")) {
     178            0 :             if (i + 1 >= argc
     179            0 :                 || parse_int(argv[i + 1], &out->reply_to) != 0
     180            0 :                 || out->reply_to <= 0) {
     181            0 :                 fprintf(stderr,
     182              :                         "tg-cli send: --reply needs a positive message id\n");
     183            0 :                 return ARG_ERROR;
     184              :             }
     185            0 :             i += 2;
     186            0 :         } else if (str_eq(argv[i], "--stdin")) {
     187              :             /* Explicit pipe opt-in; tg_cli will detect anyway, but this
     188              :              * is convenient in scripts that redirect stdin. */
     189            0 :             i++;
     190              :         } else {
     191            0 :             fprintf(stderr, "tg-cli send: unknown option: %s\n", argv[i]);
     192            0 :             return ARG_ERROR;
     193              :         }
     194              :     }
     195              : 
     196            0 :     if (i >= argc) {
     197              :         /* message may be provided via stdin; tg_cli checks isatty later. */
     198            0 :         return ARG_OK;
     199              :     }
     200            0 :     out->message = argv[i];
     201            0 :     return ARG_OK;
     202              : }
     203              : 
     204            1 : static int parse_search(int argc, char **argv, int i, ArgResult *out) {
     205            1 :     out->command = CMD_SEARCH;
     206            1 :     out->limit   = 20; /* default */
     207              : 
     208            1 :     if (i >= argc) {
     209            0 :         fprintf(stderr, "tg-cli search: <query> argument required\n");
     210            0 :         return ARG_ERROR;
     211              :     }
     212              : 
     213              :     /* Optional peer before query: two consecutive non-flag positionals. */
     214            1 :     if (i + 1 < argc && argv[i][0] != '-' && argv[i + 1][0] != '-') {
     215            0 :         out->peer  = argv[i++];
     216            0 :         out->query = argv[i++];
     217            1 :     } else if (argv[i][0] != '-') {
     218            1 :         out->query = argv[i++];
     219              :     } else {
     220            0 :         fprintf(stderr, "tg-cli search: <query> argument required\n");
     221            0 :         return ARG_ERROR;
     222              :     }
     223              : 
     224              :     /* Optional flags after the positional arguments. */
     225            1 :     while (i < argc) {
     226            0 :         if (str_eq(argv[i], "--limit")) {
     227            0 :             if (i + 1 >= argc) {
     228            0 :                 fprintf(stderr, "tg-cli search: --limit requires a number\n");
     229            0 :                 return ARG_ERROR;
     230              :             }
     231            0 :             int val = 0;
     232            0 :             if (parse_int(argv[i + 1], &val) != 0) {
     233            0 :                 fprintf(stderr, "tg-cli search: --limit value is not a number\n");
     234            0 :                 return ARG_ERROR;
     235              :             }
     236            0 :             if (val < 1 || val > 100) {
     237            0 :                 fprintf(stderr,
     238              :                         "tg-cli search: --limit %d out of range [1, 100]\n",
     239              :                         val);
     240            0 :                 return ARG_ERROR;
     241              :             }
     242            0 :             out->limit = val;
     243            0 :             i += 2;
     244              :         } else {
     245            0 :             fprintf(stderr, "tg-cli search: unknown option: %s\n", argv[i]);
     246            0 :             return ARG_ERROR;
     247              :         }
     248              :     }
     249            1 :     return ARG_OK;
     250              : }
     251              : 
     252            1 : static int parse_contacts(int argc, char **argv, int i, ArgResult *out) {
     253              :     (void)argc; (void)argv; (void)i;
     254            1 :     out->command = CMD_CONTACTS;
     255            1 :     return ARG_OK;
     256              : }
     257              : 
     258            4 : static int parse_me(int argc, char **argv, int i, ArgResult *out) {
     259              :     (void)argc; (void)argv; (void)i;
     260            4 :     out->command = CMD_ME;
     261            4 :     return ARG_OK;
     262              : }
     263              : 
     264            1 : static int parse_watch(int argc, char **argv, int i, ArgResult *out) {
     265            1 :     out->command = CMD_WATCH;
     266            1 :     out->watch_interval = 30; /* default 30 s */
     267            1 :     while (i < argc) {
     268            0 :         if (str_eq(argv[i], "--peers")) {
     269            0 :             if (i + 1 >= argc) {
     270            0 :                 fprintf(stderr, "tg-cli watch: --peers requires a value\n");
     271            0 :                 return ARG_ERROR;
     272              :             }
     273            0 :             out->watch_peers = argv[i + 1]; /* comma-separated list stored as-is */
     274            0 :             i += 2;
     275            0 :         } else if (str_eq(argv[i], "--interval")) {
     276            0 :             if (i + 1 >= argc) {
     277            0 :                 fprintf(stderr, "tg-cli watch: --interval requires a number\n");
     278            0 :                 return ARG_ERROR;
     279              :             }
     280            0 :             int val = 0;
     281            0 :             if (parse_int(argv[i + 1], &val) != 0) {
     282            0 :                 fprintf(stderr, "tg-cli watch: --interval value is not a number\n");
     283            0 :                 return ARG_ERROR;
     284              :             }
     285            0 :             if (val < 2 || val > 3600) {
     286            0 :                 fprintf(stderr,
     287              :                         "tg-cli watch: --interval %d out of range [2, 3600]\n",
     288              :                         val);
     289            0 :                 return ARG_ERROR;
     290              :             }
     291            0 :             out->watch_interval = val;
     292            0 :             i += 2;
     293              :         } else {
     294            0 :             fprintf(stderr, "tg-cli watch: unknown option: %s\n", argv[i]);
     295            0 :             return ARG_ERROR;
     296              :         }
     297              :     }
     298            1 :     return ARG_OK;
     299              : }
     300              : 
     301            1 : static int parse_user_info(int argc, char **argv, int i, ArgResult *out) {
     302            1 :     out->command = CMD_USER_INFO;
     303              : 
     304            1 :     if (i >= argc) {
     305            0 :         fprintf(stderr, "tg-cli user-info: <peer> argument required\n");
     306            0 :         return ARG_ERROR;
     307              :     }
     308            1 :     out->peer = argv[i];
     309            1 :     return ARG_OK;
     310              : }
     311              : 
     312            1 : static int parse_download(int argc, char **argv, int i, ArgResult *out) {
     313            1 :     out->command = CMD_DOWNLOAD;
     314              : 
     315            1 :     if (i >= argc || argv[i][0] == '-') {
     316            0 :         fprintf(stderr, "tg-cli download: <peer> argument required\n");
     317            0 :         return ARG_ERROR;
     318              :     }
     319            1 :     out->peer = argv[i++];
     320              : 
     321            1 :     if (i >= argc || argv[i][0] == '-') {
     322            0 :         fprintf(stderr, "tg-cli download: <msg_id> argument required\n");
     323            0 :         return ARG_ERROR;
     324              :     }
     325            1 :     if (parse_int(argv[i], &out->msg_id) != 0 || out->msg_id <= 0) {
     326            0 :         fprintf(stderr, "tg-cli download: <msg_id> must be a positive integer\n");
     327            0 :         return ARG_ERROR;
     328              :     }
     329            1 :     i++;
     330              : 
     331            1 :     while (i < argc) {
     332            0 :         if (str_eq(argv[i], "--out")) {
     333            0 :             if (i + 1 >= argc) {
     334            0 :                 fprintf(stderr, "tg-cli download: --out requires a path\n");
     335            0 :                 return ARG_ERROR;
     336              :             }
     337            0 :             out->out_path = argv[i + 1];
     338            0 :             i += 2;
     339              :         } else {
     340            0 :             fprintf(stderr, "tg-cli download: unknown option: %s\n", argv[i]);
     341            0 :             return ARG_ERROR;
     342              :         }
     343              :     }
     344            1 :     return ARG_OK;
     345              : }
     346              : 
     347              : /* ---- Public API ---- */
     348              : 
     349           13 : int arg_parse(int argc, char **argv, ArgResult *out) {
     350           13 :     if (!argv || !out) return ARG_ERROR;
     351              : 
     352           13 :     memset(out, 0, sizeof(*out));
     353           13 :     out->limit = -1; /* -1 means "not set" (subcommand may supply default) */
     354              : 
     355           13 :     int i = 1; /* skip argv[0] (program name) */
     356              : 
     357              :     /* Collect global flags that may appear before the subcommand */
     358           13 :     while (i < argc && argv[i][0] == '-') {
     359            0 :         int r = try_global_flag(argc, argv, i, out);
     360            0 :         if (r == TGF_HELP)    return ARG_HELP;
     361            0 :         if (r == TGF_VERSION) return ARG_VERSION;
     362            0 :         if (r < 0)            return ARG_ERROR;
     363            0 :         if (r == 0)           break; /* unknown flag — let subcommand handle */
     364            0 :         i += r;
     365              :     }
     366              : 
     367           13 :     if (i >= argc) {
     368              :         /* No subcommand given */
     369            0 :         if (out->json) {
     370            0 :             fprintf(stderr, "tg-cli: subcommand required\n");
     371            0 :             return ARG_ERROR;
     372              :         }
     373              :         /* Interactive mode — no subcommand is fine */
     374            0 :         return ARG_OK;
     375              :     }
     376              : 
     377           13 :     const char *subcmd = argv[i++];
     378              : 
     379           13 :     if (str_eq(subcmd, "dialogs"))   return parse_dialogs  (argc, argv, i, out);
     380           12 :     if (str_eq(subcmd, "history"))   return parse_history  (argc, argv, i, out);
     381           11 :     if (str_eq(subcmd, "send"))      return parse_send     (argc, argv, i, out);
     382           11 :     if (str_eq(subcmd, "search"))    return parse_search   (argc, argv, i, out);
     383           10 :     if (str_eq(subcmd, "contacts"))  return parse_contacts (argc, argv, i, out);
     384            9 :     if (str_eq(subcmd, "user-info")) return parse_user_info(argc, argv, i, out);
     385            8 :     if (str_eq(subcmd, "me")   || str_eq(subcmd, "self"))
     386            4 :                                       return parse_me       (argc, argv, i, out);
     387            4 :     if (str_eq(subcmd, "watch"))     return parse_watch    (argc, argv, i, out);
     388            3 :     if (str_eq(subcmd, "download"))  return parse_download (argc, argv, i, out);
     389            2 :     if (str_eq(subcmd, "edit")) {
     390            0 :         out->command = CMD_EDIT;
     391            0 :         if (i >= argc || argv[i][0] == '-') {
     392            0 :             fprintf(stderr, "tg-cli edit: <peer> required\n"); return ARG_ERROR;
     393              :         }
     394            0 :         out->peer = argv[i++];
     395            0 :         if (i >= argc || parse_int(argv[i], &out->msg_id) != 0
     396            0 :             || out->msg_id <= 0) {
     397            0 :             fprintf(stderr, "tg-cli edit: positive <msg_id> required\n");
     398            0 :             return ARG_ERROR;
     399              :         }
     400            0 :         i++;
     401            0 :         if (i >= argc) {
     402            0 :             fprintf(stderr, "tg-cli edit: <new-text> required\n");
     403            0 :             return ARG_ERROR;
     404              :         }
     405            0 :         out->message = argv[i];
     406            0 :         return ARG_OK;
     407              :     }
     408            2 :     if (str_eq(subcmd, "delete")) {
     409            0 :         out->command = CMD_DELETE;
     410            0 :         if (i >= argc || argv[i][0] == '-') {
     411            0 :             fprintf(stderr, "tg-cli delete: <peer> required\n"); return ARG_ERROR;
     412              :         }
     413            0 :         out->peer = argv[i++];
     414            0 :         if (i >= argc || parse_int(argv[i], &out->msg_id) != 0
     415            0 :             || out->msg_id <= 0) {
     416            0 :             fprintf(stderr, "tg-cli delete: positive <msg_id> required\n");
     417            0 :             return ARG_ERROR;
     418              :         }
     419            0 :         i++;
     420            0 :         while (i < argc) {
     421            0 :             if (str_eq(argv[i], "--revoke")) { out->revoke = 1; i++; }
     422              :             else {
     423            0 :                 fprintf(stderr, "tg-cli delete: unknown option: %s\n", argv[i]);
     424            0 :                 return ARG_ERROR;
     425              :             }
     426              :         }
     427            0 :         return ARG_OK;
     428              :     }
     429            2 :     if (str_eq(subcmd, "send-file") || str_eq(subcmd, "upload")) {
     430            0 :         out->command = CMD_SEND_FILE;
     431            0 :         if (i + 1 >= argc) {
     432            0 :             fprintf(stderr, "tg-cli send-file: <peer> <path> required\n");
     433            0 :             return ARG_ERROR;
     434              :         }
     435            0 :         out->peer = argv[i++];
     436            0 :         out->out_path = argv[i++];          /* reuse out_path as local file */
     437            0 :         while (i < argc) {
     438            0 :             if (str_eq(argv[i], "--caption")) {
     439            0 :                 if (i + 1 >= argc) {
     440            0 :                     fprintf(stderr, "tg-cli send-file: --caption needs text\n");
     441            0 :                     return ARG_ERROR;
     442              :                 }
     443            0 :                 out->message = argv[i + 1];
     444            0 :                 i += 2;
     445              :             } else {
     446            0 :                 fprintf(stderr, "tg-cli send-file: unknown option: %s\n",
     447            0 :                         argv[i]);
     448            0 :                 return ARG_ERROR;
     449              :             }
     450              :         }
     451            0 :         return ARG_OK;
     452              :     }
     453            2 :     if (str_eq(subcmd, "forward")) {
     454            0 :         out->command = CMD_FORWARD;
     455            0 :         if (i + 2 >= argc) {
     456            0 :             fprintf(stderr,
     457              :                     "tg-cli forward: <from_peer> <to_peer> <msg_id> required\n");
     458            0 :             return ARG_ERROR;
     459              :         }
     460            0 :         out->peer  = argv[i++];
     461            0 :         out->peer2 = argv[i++];
     462            0 :         if (parse_int(argv[i], &out->msg_id) != 0 || out->msg_id <= 0) {
     463            0 :             fprintf(stderr, "tg-cli forward: positive <msg_id> required\n");
     464            0 :             return ARG_ERROR;
     465              :         }
     466            0 :         return ARG_OK;
     467              :     }
     468            2 :     if (str_eq(subcmd, "read")) {
     469            0 :         out->command = CMD_READ;
     470            0 :         if (i >= argc || argv[i][0] == '-') {
     471            0 :             fprintf(stderr, "tg-cli read: <peer> argument required\n");
     472            0 :             return ARG_ERROR;
     473              :         }
     474            0 :         out->peer = argv[i++];
     475            0 :         if (i < argc && str_eq(argv[i], "--max-id")) {
     476            0 :             if (i + 1 >= argc
     477            0 :                 || parse_int(argv[i + 1], &out->msg_id) != 0) {
     478            0 :                 fprintf(stderr, "tg-cli read: --max-id needs a number\n");
     479            0 :                 return ARG_ERROR;
     480              :             }
     481              :         }
     482            0 :         return ARG_OK;
     483              :     }
     484            2 :     if (str_eq(subcmd, "login")) {
     485            2 :         out->command = CMD_LOGIN;
     486            5 :         while (i < argc) {
     487            3 :             if (str_eq(argv[i], "--api-id")) {
     488            1 :                 if (i + 1 >= argc) {
     489            0 :                     fprintf(stderr, "tg-cli login: --api-id requires a value\n");
     490            0 :                     return ARG_ERROR;
     491              :                 }
     492            1 :                 out->api_id_str = argv[i + 1];
     493            1 :                 i += 2;
     494            2 :             } else if (str_eq(argv[i], "--api-hash")) {
     495            1 :                 if (i + 1 >= argc) {
     496            0 :                     fprintf(stderr, "tg-cli login: --api-hash requires a value\n");
     497            0 :                     return ARG_ERROR;
     498              :                 }
     499            1 :                 out->api_hash_str = argv[i + 1];
     500            1 :                 i += 2;
     501            1 :             } else if (str_eq(argv[i], "--force")) {
     502            1 :                 out->force = 1;
     503            1 :                 i++;
     504              :             } else {
     505            0 :                 fprintf(stderr, "tg-cli login: unknown option: %s\n", argv[i]);
     506            0 :                 return ARG_ERROR;
     507              :             }
     508              :         }
     509            2 :         return ARG_OK;
     510              :     }
     511            0 :     if (str_eq(subcmd, "help"))      return ARG_HELP;
     512            0 :     if (str_eq(subcmd, "version"))   return ARG_VERSION;
     513              : 
     514            0 :     fprintf(stderr, "tg-cli: unknown subcommand: %s\n", subcmd);
     515            0 :     return ARG_ERROR;
     516              : }
     517              : 
     518            0 : void arg_print_version(void) {
     519            0 :     printf("tg-cli %s (git %s)\n", TG_CLI_VERSION, TG_CLI_GIT_COMMIT);
     520            0 : }
        

Generated by: LCOV version 2.0-1