LCOV - code coverage report
Current view: top level - lib/dpkg - parsehelp.c (source / functions) Hit Total Coverage
Test: dpkg 1.21.11 C code coverage Lines: 109 146 74.7 %
Date: 2022-12-03 00:40:01 Functions: 11 14 78.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 88 118 74.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * libdpkg - Debian packaging suite library routines
       3                 :            :  * parsehelp.c - helpful routines for parsing and writing
       4                 :            :  *
       5                 :            :  * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
       6                 :            :  * Copyright © 2006-2012 Guillem Jover <guillem@debian.org>
       7                 :            :  *
       8                 :            :  * This is free software; you can redistribute it and/or modify
       9                 :            :  * it under the terms of the GNU General Public License as published by
      10                 :            :  * the Free Software Foundation; either version 2 of the License, or
      11                 :            :  * (at your option) any later version.
      12                 :            :  *
      13                 :            :  * This is distributed in the hope that it will be useful,
      14                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16                 :            :  * GNU General Public License for more details.
      17                 :            :  *
      18                 :            :  * You should have received a copy of the GNU General Public License
      19                 :            :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : #include <compat.h>
      24                 :            : 
      25                 :            : #include <errno.h>
      26                 :            : #include <limits.h>
      27                 :            : #include <string.h>
      28                 :            : #include <stdlib.h>
      29                 :            : #include <stdio.h>
      30                 :            : 
      31                 :            : #include <dpkg/i18n.h>
      32                 :            : #include <dpkg/c-ctype.h>
      33                 :            : #include <dpkg/dpkg.h>
      34                 :            : #include <dpkg/dpkg-db.h>
      35                 :            : #include <dpkg/string.h>
      36                 :            : #include <dpkg/error.h>
      37                 :            : #include <dpkg/parsedump.h>
      38                 :            : 
      39                 :            : static DPKG_ATTR_VPRINTF(2) const char *
      40                 :          8 : parse_error_msg(struct parsedb_state *ps, const char *fmt, va_list args)
      41                 :            : {
      42                 :          8 :   struct varbuf *vb = &ps->errmsg;
      43                 :            : 
      44                 :          8 :   varbuf_reset(vb);
      45                 :            : 
      46   [ +  -  +  - ]:          8 :   if (ps->pkg && ps->pkg->set->name)
      47                 :          8 :     varbuf_printf(vb, _("parsing file '%s' near line %d package '%s':\n "),
      48                 :            :                   ps->filename, ps->lno,
      49                 :            :                   pkgbin_name(ps->pkg, ps->pkgbin, pnaw_nonambig));
      50                 :            :   else
      51                 :          0 :     varbuf_printf(vb, _("parsing file '%.255s' near line %d:\n "),
      52                 :            :                   ps->filename, ps->lno);
      53                 :            : 
      54                 :          8 :   varbuf_vprintf(vb, fmt, args);
      55                 :            : 
      56                 :          8 :   return vb->buf;
      57                 :            : }
      58                 :            : 
      59                 :            : void
      60                 :          0 : parse_error(struct parsedb_state *ps, const char *fmt, ...)
      61                 :            : {
      62                 :            :   va_list args;
      63                 :            :   const char *str;
      64                 :            : 
      65                 :          0 :   va_start(args, fmt);
      66                 :          0 :   str = parse_error_msg(ps, fmt, args);
      67                 :          0 :   va_end(args);
      68                 :            : 
      69                 :          0 :   ohshit("%s", str);
      70                 :            : }
      71                 :            : 
      72                 :            : void
      73                 :          8 : parse_warn(struct parsedb_state *ps, const char *fmt, ...)
      74                 :            : {
      75                 :            :   va_list args;
      76                 :            : 
      77                 :          8 :   va_start(args, fmt);
      78                 :          8 :   warning("%s", parse_error_msg(ps, fmt, args));
      79                 :          8 :   va_end(args);
      80                 :          8 : }
      81                 :            : 
      82                 :            : void
      83                 :          0 : parse_problem(struct parsedb_state *ps, const char *fmt, ...)
      84                 :            : {
      85                 :            :   va_list args;
      86                 :            :   const char *str;
      87                 :            : 
      88                 :          0 :   va_start(args, fmt);
      89                 :          0 :   str = parse_error_msg(ps, fmt, args);
      90                 :          0 :   va_end(args);
      91                 :            : 
      92         [ #  # ]:          0 :   if (ps->err.type == DPKG_MSG_WARN)
      93                 :          0 :     warning("%s: %s", str, ps->err.str);
      94                 :            :   else
      95                 :          0 :     ohshit("%s: %s", str, ps->err.str);
      96                 :          0 : }
      97                 :            : 
      98                 :            : const struct fieldinfo *
      99                 :         29 : find_field_info(const struct fieldinfo *fields, const char *fieldname)
     100                 :            : {
     101                 :            :   const struct fieldinfo *field;
     102                 :            : 
     103         [ +  + ]:        646 :   for (field = fields; field->name; field++)
     104         [ +  + ]:        634 :     if (strcasecmp(field->name, fieldname) == 0)
     105                 :         17 :       return field;
     106                 :            : 
     107                 :         12 :   return NULL;
     108                 :            : }
     109                 :            : 
     110                 :            : const struct arbitraryfield *
     111                 :          2 : find_arbfield_info(const struct arbitraryfield *arbs, const char *fieldname)
     112                 :            : {
     113                 :            :   const struct arbitraryfield *arbfield;
     114                 :            : 
     115         [ +  + ]:          2 :   for (arbfield = arbs; arbfield; arbfield = arbfield->next)
     116         [ +  - ]:          1 :     if (strcasecmp(arbfield->name, fieldname) == 0)
     117                 :          1 :       return arbfield;
     118                 :            : 
     119                 :          1 :   return NULL;
     120                 :            : }
     121                 :            : 
     122                 :            : const char *
     123                 :         28 : pkg_name_is_illegal(const char *p)
     124                 :            : {
     125                 :            :   /* TODO: _ is deprecated, remove sometime. */
     126                 :            :   static const char alsoallowed[] = "-+._";
     127                 :            :   static char buf[150];
     128                 :            :   int c;
     129                 :            : 
     130         [ -  + ]:         28 :   if (!*p) return _("may not be empty string");
     131         [ -  + ]:         28 :   if (!c_isalnum(*p))
     132                 :          0 :     return _("must start with an alphanumeric character");
     133         [ +  + ]:        507 :   while ((c = *p++) != '\0')
     134   [ +  +  -  + ]:        479 :     if (!c_isalnum(c) && !strchr(alsoallowed, c))
     135                 :          0 :       break;
     136         [ +  - ]:         28 :   if (!c) return NULL;
     137                 :            : 
     138                 :          0 :   snprintf(buf, sizeof(buf), _(
     139                 :            :            "character '%c' not allowed (only letters, digits and characters '%s')"),
     140                 :            :            c, alsoallowed);
     141                 :          0 :   return buf;
     142                 :            : }
     143                 :            : 
     144                 :          6 : void varbufversion
     145                 :            : (struct varbuf *vb,
     146                 :            :  const struct dpkg_version *version,
     147                 :            :  enum versiondisplayepochwhen vdew)
     148                 :            : {
     149   [ -  +  -  - ]:          6 :   switch (vdew) {
     150                 :          0 :   case vdew_never:
     151                 :          0 :     break;
     152                 :          6 :   case vdew_nonambig:
     153         [ +  + ]:          6 :     if (!version->epoch &&
     154   [ +  -  +  - ]:          5 :         (!version->version || !strchr(version->version,':')) &&
     155   [ +  -  -  + ]:          5 :         (!version->revision || !strchr(version->revision,':'))) break;
     156                 :            :     /* Fall through. */
     157                 :            :   case vdew_always:
     158                 :          1 :     varbuf_printf(vb, "%u:", version->epoch);
     159                 :          1 :     break;
     160                 :          0 :   default:
     161                 :          0 :     internerr("unknown versiondisplayepochwhen '%d'", vdew);
     162                 :            :   }
     163         [ +  - ]:          6 :   if (version->version)
     164                 :          6 :     varbuf_add_str(vb, version->version);
     165         [ +  - ]:          6 :   if (str_is_set(version->revision)) {
     166                 :          6 :     varbuf_add_char(vb, '-');
     167                 :          6 :     varbuf_add_str(vb, version->revision);
     168                 :            :   }
     169                 :          6 : }
     170                 :            : 
     171                 :          1 : const char *versiondescribe
     172                 :            : (const struct dpkg_version *version,
     173                 :            :  enum versiondisplayepochwhen vdew)
     174                 :            : {
     175                 :            :   static struct varbuf bufs[10];
     176                 :            :   static int bufnum=0;
     177                 :            : 
     178                 :            :   struct varbuf *vb;
     179                 :            : 
     180         [ -  + ]:          1 :   if (!dpkg_version_is_informative(version))
     181                 :          0 :     return C_("version", "<none>");
     182                 :            : 
     183         [ -  + ]:          1 :   vb= &bufs[bufnum]; bufnum++; if (bufnum == 10) bufnum= 0;
     184                 :          1 :   varbuf_reset(vb);
     185                 :          1 :   varbufversion(vb,version,vdew);
     186                 :          1 :   varbuf_end_str(vb);
     187                 :            : 
     188                 :          1 :   return vb->buf;
     189                 :            : }
     190                 :            : 
     191                 :            : const char *
     192                 :          0 : versiondescribe_c(const struct dpkg_version *version,
     193                 :            :                   enum versiondisplayepochwhen vdew)
     194                 :            : {
     195                 :            :   struct dpkg_locale oldloc;
     196                 :            :   const char *str;
     197                 :            : 
     198                 :          0 :   oldloc = dpkg_locale_switch_C();
     199                 :          0 :   str = versiondescribe(version, vdew);
     200                 :          0 :   dpkg_locale_switch_back(oldloc);
     201                 :            : 
     202                 :          0 :   return str;
     203                 :            : }
     204                 :            : 
     205                 :            : /**
     206                 :            :  * Parse a version string and check for invalid syntax.
     207                 :            :  *
     208                 :            :  * Distinguish between lax (warnings) and strict (error) parsing.
     209                 :            :  *
     210                 :            :  * @param rversion The parsed version.
     211                 :            :  * @param string The version string to parse.
     212                 :            :  * @param err The warning or error message if any.
     213                 :            :  *
     214                 :            :  * @retval  0 On success.
     215                 :            :  * @retval -1 On failure, and err is set accordingly.
     216                 :            :  */
     217                 :            : int
     218                 :        106 : parseversion(struct dpkg_version *rversion, const char *string,
     219                 :            :              struct dpkg_error *err)
     220                 :            : {
     221                 :            :   char *hyphen, *colon, *eepochcolon;
     222                 :            :   const char *end, *ptr;
     223                 :            : 
     224                 :            :   /* Trim leading and trailing space. */
     225   [ +  +  +  + ]:        114 :   while (*string && c_isblank(*string))
     226                 :          8 :     string++;
     227                 :            : 
     228         [ +  + ]:        106 :   if (!*string)
     229                 :          2 :     return dpkg_put_error(err, _("version string is empty"));
     230                 :            : 
     231                 :            :   /* String now points to the first non-whitespace char. */
     232                 :        104 :   end = string;
     233                 :            :   /* Find either the end of the string, or a whitespace char. */
     234   [ +  +  +  + ]:        694 :   while (*end && !c_isblank(*end))
     235                 :        590 :     end++;
     236                 :            :   /* Check for extra chars after trailing space. */
     237                 :        104 :   ptr = end;
     238   [ +  +  +  + ]:        111 :   while (*ptr && c_isblank(*ptr))
     239                 :          7 :     ptr++;
     240         [ +  + ]:        104 :   if (*ptr)
     241                 :          1 :     return dpkg_put_error(err, _("version string has embedded spaces"));
     242                 :            : 
     243                 :        103 :   colon= strchr(string,':');
     244         [ +  + ]:        103 :   if (colon) {
     245                 :            :     long epoch;
     246                 :            : 
     247                 :         76 :     errno = 0;
     248                 :         76 :     epoch = strtol(string, &eepochcolon, 10);
     249         [ +  + ]:         76 :     if (string == eepochcolon)
     250                 :          3 :       return dpkg_put_error(err, _("epoch in version is empty"));
     251         [ -  + ]:         73 :     if (colon != eepochcolon)
     252                 :          0 :       return dpkg_put_error(err, _("epoch in version is not number"));
     253         [ +  + ]:         73 :     if (epoch < 0)
     254                 :          1 :       return dpkg_put_error(err, _("epoch in version is negative"));
     255   [ +  +  -  + ]:         72 :     if (epoch > INT_MAX || errno == ERANGE)
     256                 :          1 :       return dpkg_put_error(err, _("epoch in version is too big"));
     257         [ +  + ]:         71 :     if (!*++colon)
     258                 :          1 :       return dpkg_put_error(err, _("nothing after colon in version number"));
     259                 :         70 :     string= colon;
     260                 :         70 :     rversion->epoch= epoch;
     261                 :            :   } else {
     262                 :         27 :     rversion->epoch= 0;
     263                 :            :   }
     264                 :         97 :   rversion->version= nfstrnsave(string,end-string);
     265                 :         97 :   hyphen= strrchr(rversion->version,'-');
     266         [ +  + ]:         97 :   if (hyphen) {
     267                 :         90 :     *hyphen++ = '\0';
     268                 :            : 
     269         [ +  + ]:         90 :     if (*hyphen == '\0')
     270                 :          1 :       return dpkg_put_error(err, _("revision number is empty"));
     271                 :            :   }
     272         [ +  + ]:         96 :   rversion->revision= hyphen ? hyphen : "";
     273                 :            : 
     274                 :            :   /* XXX: Would be faster to use something like cisversion and cisrevision. */
     275                 :         96 :   ptr = rversion->version;
     276         [ +  + ]:         96 :   if (!*ptr)
     277                 :          2 :     return dpkg_put_error(err, _("version number is empty"));
     278   [ +  -  +  + ]:         94 :   if (*ptr && !c_isdigit(*ptr++))
     279                 :          1 :     return dpkg_put_warn(err, _("version number does not start with digit"));
     280         [ +  + ]:        175 :   for (; *ptr; ptr++) {
     281   [ +  +  +  +  :        106 :     if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".-+~:", *ptr) == NULL)
                   +  + ]
     282                 :         24 :       return dpkg_put_warn(err, _("invalid character in version number"));
     283                 :            :   }
     284         [ +  + ]:        116 :   for (ptr = rversion->revision; *ptr; ptr++) {
     285   [ +  +  +  +  :         73 :     if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".+~", *ptr) == NULL)
                   +  + ]
     286                 :         26 :       return dpkg_put_warn(err, _("invalid character in revision number"));
     287                 :            :   }
     288                 :            : 
     289                 :         43 :   return 0;
     290                 :            : }
     291                 :            : 
     292                 :            : /**
     293                 :            :  * Parse a version string coming from a database file.
     294                 :            :  *
     295                 :            :  * It parses a version string, and prints a warning or an error depending
     296                 :            :  * on the parse options.
     297                 :            :  *
     298                 :            :  * @param ps The parsedb state.
     299                 :            :  * @param version The version to parse into.
     300                 :            :  * @param value The version string to parse from.
     301                 :            :  *
     302                 :            :  * @retval  0 On success, and err is reset.
     303                 :            :  * @retval -1 On failure, and err is set accordingly.
     304                 :            :  */
     305                 :            : int
     306                 :         24 : parse_db_version(struct parsedb_state *ps, struct dpkg_version *version,
     307                 :            :                  const char *value)
     308                 :            : {
     309                 :         24 :   dpkg_error_destroy(&ps->err);
     310                 :            : 
     311         [ +  - ]:         24 :   if (parseversion(version, value, &ps->err) == 0)
     312                 :         24 :     return 0;
     313                 :            : 
     314                 :            :   /* If not in lax mode, turn everything into an error. */
     315         [ #  # ]:          0 :   if (!(ps->flags & pdb_lax_version_parser))
     316                 :          0 :     ps->err.type = DPKG_MSG_ERROR;
     317                 :            : 
     318                 :          0 :   return -1;
     319                 :            : }
     320                 :            : 
     321                 :            : void
     322                 :         48 : parse_must_have_field(struct parsedb_state *ps,
     323                 :            :                       const char *value, const char *what)
     324                 :            : {
     325         [ -  + ]:         48 :   if (!value)
     326                 :          0 :     parse_error(ps, _("missing '%s' field"), what);
     327         [ -  + ]:         48 :   else if (!*value)
     328                 :          0 :     parse_error(ps, _("empty value for '%s' field"), what);
     329                 :         48 : }
     330                 :            : 
     331                 :            : void
     332                 :         48 : parse_ensure_have_field(struct parsedb_state *ps,
     333                 :            :                         const char **value, const char *what)
     334                 :            : {
     335                 :            :   static const char empty[] = "";
     336                 :            : 
     337         [ -  + ]:         48 :   if (!*value) {
     338                 :          0 :     parse_warn(ps, _("missing '%s' field"), what);
     339                 :          0 :     *value = empty;
     340         [ -  + ]:         48 :   } else if (!**value) {
     341                 :          0 :     parse_warn(ps, _("empty value for '%s' field"), what);
     342                 :            :   }
     343                 :         48 : }

Generated by: LCOV version 1.16