LCOV - code coverage report
Current view: top level - src/util - fn.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 94 94 100.0 %
Date: 2025-05-23 10:42:01 Functions: 7 7 100.0 %
Branches: 38 50 76.0 %

           Branch data     Line data    Source code
       1                 :            : #include <mutable/util/fn.hpp>
       2                 :            : 
       3                 :            : #if __linux
       4                 :            : #include <sys/types.h>
       5                 :            : #include <sys/wait.h>
       6                 :            : #include <unistd.h>
       7                 :            : #elif __APPLE__
       8                 :            : #include <string.h>
       9                 :            : #include <sys/wait.h>
      10                 :            : #include <unistd.h>
      11                 :            : #endif
      12                 :            : 
      13                 :            : 
      14                 :            : using namespace m;
      15                 :            : 
      16                 :            : 
      17                 :          7 : std::string m::escape(const std::string &str, char esc, char quote)
      18                 :            : {
      19                 :          7 :     std::string res;
      20                 :          7 :     res.reserve(str.length());
      21                 :            : 
      22         [ +  + ]:        112 :     for (auto c : str) {
      23                 :        105 :         if (c == esc or c == quote) {
      24                 :          9 :             res += esc;
      25                 :          9 :             res += c;
      26         [ +  + ]:        105 :         } else if (c == '\n') {
      27                 :          5 :             res += esc;
      28                 :          5 :             res += 'n';
      29                 :          5 :         } else {
      30                 :         91 :             res += c;
      31                 :            :         }
      32                 :            :     }
      33                 :            : 
      34                 :          7 :     return res;
      35                 :          7 : }
      36                 :            : 
      37                 :        164 : std::string m::unescape(const std::string &str, char esc, char quote)
      38                 :            : {
      39                 :        164 :     std::string res;
      40                 :        164 :     res.reserve(str.length());
      41                 :            : 
      42         [ +  + ]:       1511 :     for (auto it = str.begin(), end = str.end(); it != end; ++it) {
      43         [ +  + ]:       1347 :         if (*it == esc) {
      44                 :         16 :             ++it;
      45                 :         16 :             if (*it == esc or *it == quote) {
      46                 :          9 :                 res += *it;
      47         [ +  + ]:         16 :             } else if (*it == 'n') {
      48                 :          5 :                 res += '\n';
      49                 :          5 :             } else {
      50                 :            :                 /* invalid escape sequence; do not unescape */
      51                 :          2 :                 res += esc;
      52                 :          2 :                 --it;
      53                 :            :             }
      54                 :         16 :         } else {
      55                 :       1331 :             res += *it;
      56                 :            :         }
      57                 :       1347 :     }
      58                 :            : 
      59                 :        164 :     return res;
      60                 :        164 : }
      61                 :            : 
      62                 :          4 : std::string m::html_escape(std::string str)
      63                 :            : {
      64   [ +  -  +  -  :          4 :     str = replace_all(str, "&", "&amp;");
                   +  - ]
      65   [ +  -  +  -  :          4 :     str = replace_all(str, "<", "&lt;");
                   +  - ]
      66   [ +  -  +  -  :          4 :     str = replace_all(str, ">", "&gt;");
                   +  - ]
      67                 :          4 :     return str;
      68                 :            : } // M_LCOV_EXCL_LINE
      69                 :            : 
      70                 :         76 : bool m::like(const std::string &str, const std::string &pattern, const char escape_char)
      71                 :            : {
      72                 :         76 :     M_insist('_' != escape_char and '%' != escape_char, "illegal escape character");
      73                 :            : 
      74                 :         77 :     bool dp[pattern.length() + 1][str.length() + 1]; // dp[i][j] == true iff pattern[:i] contains str[:j]
      75                 :            : 
      76                 :         76 :     dp[0][0] = true; // empty pattern contains empty string
      77         [ +  + ]:        349 :     for (std::size_t j = 1; j <= str.length(); ++j)
      78                 :        273 :         dp[0][j] = false; // empty pattern does not contain non-empty string
      79                 :         76 :     std::size_t escaped_row = 0;
      80         [ +  + ]:        352 :     for (std::size_t i = 1; i <= pattern.length(); ++i) {
      81                 :        281 :         const auto c = pattern[i - 1];
      82                 :        281 :         const auto escaped = i == escaped_row;
      83                 :        281 :         if (escaped and '_' != c and '%' != c and escape_char != c)
      84         [ +  - ]:         11 :             throw m::runtime_error("invalid escape sequence");
      85                 :        258 :         if (not escaped and escape_char == c)
      86                 :         29 :             escaped_row = i + 1; // next row is escaped
      87                 :        258 :         if (not escaped and '%' == c)
      88                 :         61 :             dp[i][0] = dp[i - 1][0]; // pattern `X%` contains empty string iff `X` contains empty string
      89                 :            :         else
      90                 :        215 :             dp[i][0] = false; // pattern without `%`-wildcard does not contain empty string
      91                 :        276 :     }
      92         [ +  + ]:         71 :     if (pattern.length() + 1 == escaped_row)
      93         [ +  - ]:          6 :         throw m::runtime_error("invalid escape sequence");
      94                 :            : 
      95                 :         65 :     escaped_row = 0;
      96         [ +  + ]:        324 :     for (std::size_t i = 1; i <= pattern.length(); ++i) {
      97                 :        259 :         const auto c = pattern[i - 1];
      98                 :        259 :         const auto escaped = i == escaped_row;
      99                 :        259 :         if (not escaped and escape_char == c) {
     100                 :            :             /* Set `escaped_row` and copy entire above row. */
     101                 :         17 :             escaped_row = i + 1;
     102         [ +  + ]:        121 :             for (std::size_t j = 0; j <= str.length(); ++j)
     103                 :        104 :                 dp[i][j] = dp[i - 1][j];
     104                 :         17 :             continue;
     105                 :            :         }
     106         [ +  + ]:       1831 :         for (std::size_t j = 1; j <= str.length(); ++j) {
     107                 :       1589 :             if (not escaped and '%' == c) {
     108                 :            :                 /* pattern `X%` contains string `c_0...c_n` iff either `X%` contains `c_0...c_{n-1}
     109                 :            :                  * or `X` contains `c_0...c_n` */
     110                 :        432 :                 dp[i][j] = dp[i][j - 1] or dp[i - 1][j];
     111                 :       1502 :             } else if ((not escaped and '_' == c) or str[j - 1] == c) {
     112                 :            :                 /* pattern `X_` contains string `c_0...c_n` iff `X` contains `c_0...c_{n-1}`,
     113                 :            :                  * pattern `Xa` contains string `c_0...c_{n-1}a` iff `X` contains `c_0...c_{n-1}` */
     114                 :        462 :                 dp[i][j] = dp[i - 1][j - 1];
     115                 :        462 :             } else {
     116                 :            :                 /* pattern `Xa` does not contain string `c_0...c_n` if a != c_n */
     117                 :        695 :                 dp[i][j] = false;
     118                 :            :             }
     119                 :       1589 :         }
     120                 :        242 :     }
     121                 :            : 
     122                 :         65 :     return dp[pattern.length()][str.length()];
     123                 :         65 : }
     124                 :            : 
     125                 :          3 : void m::exec(const char *executable, std::initializer_list<const char*> args)
     126                 :            : {
     127                 :            : #if __linux || __APPLE__
     128                 :          3 :     pid_t pid = fork();
     129         [ +  + ]:          3 :     if (pid > 0) {
     130                 :            :         /* parent: wait for child to finish */
     131                 :            :         int status;
     132                 :          3 :         waitpid(pid, &status, 0);
     133         [ +  - ]:          6 :     } else if (pid == 0) {
     134                 :            :         /* child */
     135                 :          3 :         char **c_args = new char*[args.size() + 2];
     136                 :          3 :         char **p = c_args;
     137                 :          3 :         *p++ = strdup(executable);
     138         [ +  + ]:          7 :         for (auto arg : args)
     139                 :          4 :             *p++ = strdup(arg);
     140                 :          3 :         *p = nullptr;
     141                 :          3 :         execv(executable, c_args);
     142                 :            :         M_unreachable("Invalid executable path or arguments"); // M_LCOV_EXCL_LINE
     143                 :            :     }
     144                 :            : #endif
     145                 :          3 : }
     146                 :            : 
     147                 :       9724 : std::size_t m::get_pagesize()
     148                 :            : {
     149                 :            :     static std::size_t pagesize(0);
     150                 :       9724 :     if (0 == pagesize)
     151                 :          1 :         pagesize = sysconf(_SC_PAGESIZE);
     152                 :       9724 :     return pagesize;
     153                 :            : }

Generated by: LCOV version 1.16