LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/base - expand_path.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 81 204 39.7 %
Date: 2024-04-21 15:09:00 Functions: 6 14 42.9 %

          Line data    Source code
       1             : 
       2             : /***********************************************************************
       3             :  * Copyright (c) 2009-2020, Secure Endpoints Inc.
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * - Redistributions of source code must retain the above copyright
      11             :  *   notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * - Redistributions in binary form must reproduce the above copyright
      14             :  *   notice, this list of conditions and the following disclaimer in
      15             :  *   the documentation and/or other materials provided with the
      16             :  *   distribution.
      17             :  *
      18             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      21             :  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      22             :  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      23             :  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      24             :  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      25             :  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      26             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      27             :  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      28             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      29             :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      30             :  *
      31             :  **********************************************************************/
      32             : 
      33             : #include "baselocl.h"
      34             : 
      35             : #include <stdarg.h>
      36             : 
      37             : typedef int PTYPE;
      38             : 
      39             : #ifdef _WIN32
      40             : #include <shlobj.h>
      41             : #include <sddl.h>
      42             : 
      43             : /*
      44             :  * Expand a %{TEMP} token
      45             :  *
      46             :  * The %{TEMP} token expands to the temporary path for the current
      47             :  * user as returned by GetTempPath().
      48             :  *
      49             :  * @note: Since the GetTempPath() function relies on the TMP or TEMP
      50             :  * environment variables, this function will failover to the system
      51             :  * temporary directory until the user profile is loaded.  In addition,
      52             :  * the returned path may or may not exist.
      53             :  */
      54             : static heim_error_code
      55             : expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
      56             :                    const char *arg, char **ret)
      57             : {
      58             :     TCHAR tpath[MAX_PATH];
      59             :     size_t len;
      60             : 
      61             :     if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
      62             :         heim_set_error_message(context, EINVAL,
      63             :                                "Failed to get temporary path (GLE=%d)",
      64             :                                GetLastError());
      65             :         return EINVAL;
      66             :     }
      67             : 
      68             :     len = strlen(tpath);
      69             : 
      70             :     if (len > 0 && tpath[len - 1] == '\\')
      71             :         tpath[len - 1] = '\0';
      72             : 
      73             :     *ret = strdup(tpath);
      74             : 
      75             :     if (*ret == NULL)
      76             :         return heim_enomem(context);
      77             : 
      78             :     return 0;
      79             : }
      80             : 
      81             : EXTERN_C IMAGE_DOS_HEADER __ImageBase;
      82             : 
      83             : /*
      84             :  * Expand a %{BINDIR} token
      85             :  *
      86             :  * This is also used to expand a few other tokens on Windows, since
      87             :  * most of the executable binaries end up in the same directory.  The
      88             :  * "bin" directory is considered to be the directory in which the
      89             :  * containing DLL is located.
      90             :  */
      91             : static heim_error_code
      92             : expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
      93             :                const char *arg, char **ret)
      94             : {
      95             :     TCHAR path[MAX_PATH];
      96             :     TCHAR *lastSlash;
      97             :     DWORD nc;
      98             : 
      99             :     nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
     100             :                            sizeof(path)/sizeof(path[0]));
     101             :     if (nc == 0 ||
     102             :         nc == sizeof(path)/sizeof(path[0])) {
     103             :         return EINVAL;
     104             :     }
     105             : 
     106             :     lastSlash = strrchr(path, '\\');
     107             :     if (lastSlash != NULL) {
     108             :         TCHAR *fslash = strrchr(lastSlash, '/');
     109             : 
     110             :         if (fslash != NULL)
     111             :             lastSlash = fslash;
     112             : 
     113             :         *lastSlash = '\0';
     114             :     }
     115             : 
     116             :     if (postfix) {
     117             :         if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
     118             :             return EINVAL;
     119             :     }
     120             : 
     121             :     *ret = strdup(path);
     122             :     if (*ret == NULL)
     123             :         return heim_enomem(context);
     124             : 
     125             :     return 0;
     126             : }
     127             : 
     128             : /*
     129             :  *  Expand a %{USERID} token
     130             :  *
     131             :  *  The %{USERID} token expands to the string representation of the
     132             :  *  user's SID.  The user account that will be used is the account
     133             :  *  corresponding to the current thread's security token.  This means
     134             :  *  that:
     135             :  *
     136             :  *  - If the current thread token has the anonymous impersonation
     137             :  *    level, the call will fail.
     138             :  *
     139             :  *  - If the current thread is impersonating a token at
     140             :  *    SecurityIdentification level the call will fail.
     141             :  *
     142             :  */
     143             : static heim_error_code
     144             : expand_userid(heim_context context, PTYPE param, const char *postfix,
     145             :               const char *arg, char **ret)
     146             : {
     147             :     int rv = EINVAL;
     148             :     HANDLE hThread = NULL;
     149             :     HANDLE hToken = NULL;
     150             :     PTOKEN_OWNER pOwner = NULL;
     151             :     DWORD len = 0;
     152             :     LPTSTR strSid = NULL;
     153             : 
     154             :     hThread = GetCurrentThread();
     155             : 
     156             :     if (!OpenThreadToken(hThread, TOKEN_QUERY,
     157             :                          FALSE, /* Open the thread token as the
     158             :                                    current thread user. */
     159             :                          &hToken)) {
     160             : 
     161             :         DWORD le = GetLastError();
     162             : 
     163             :         if (le == ERROR_NO_TOKEN) {
     164             :             HANDLE hProcess = GetCurrentProcess();
     165             : 
     166             :             le = 0;
     167             :             if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
     168             :                 le = GetLastError();
     169             :         }
     170             : 
     171             :         if (le != 0) {
     172             :             heim_set_error_message(context, rv,
     173             :                                    "Can't open thread token (GLE=%d)", le);
     174             :             goto _exit;
     175             :         }
     176             :     }
     177             : 
     178             :     if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
     179             :         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
     180             :             heim_set_error_message(context, rv,
     181             :                                    "Unexpected error reading token information (GLE=%d)",
     182             :                                    GetLastError());
     183             :             goto _exit;
     184             :         }
     185             : 
     186             :         if (len == 0) {
     187             :             heim_set_error_message(context, rv,
     188             :                                    "GetTokenInformation() returned truncated buffer");
     189             :             goto _exit;
     190             :         }
     191             : 
     192             :         pOwner = malloc(len);
     193             :         if (pOwner == NULL) {
     194             :             heim_set_error_message(context, rv, "Out of memory");
     195             :             goto _exit;
     196             :         }
     197             :     } else {
     198             :         heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
     199             :         goto _exit;
     200             :     }
     201             : 
     202             :     if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
     203             :         heim_set_error_message(context, rv,
     204             :                                "GetTokenInformation() failed. GLE=%d",
     205             :                                GetLastError());
     206             :         goto _exit;
     207             :     }
     208             : 
     209             :     if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
     210             :         heim_set_error_message(context, rv,
     211             :                                "Can't convert SID to string. GLE=%d",
     212             :                                GetLastError());
     213             :         goto _exit;
     214             :     }
     215             : 
     216             :     *ret = strdup(strSid);
     217             :     if (*ret == NULL)
     218             :         heim_set_error_message(context, rv, "Out of memory");
     219             : 
     220             :     rv = 0;
     221             : 
     222             :  _exit:
     223             :     if (hToken != NULL)
     224             :         CloseHandle(hToken);
     225             : 
     226             :     if (pOwner != NULL)
     227             :         free (pOwner);
     228             : 
     229             :     if (strSid != NULL)
     230             :         LocalFree(strSid);
     231             : 
     232             :     return rv;
     233             : }
     234             : 
     235             : /*
     236             :  * Expand a folder identified by a CSIDL
     237             :  */
     238             : 
     239             : static heim_error_code
     240             : expand_csidl(heim_context context, PTYPE folder, const char *postfix,
     241             :              const char *arg, char **ret)
     242             : {
     243             :     TCHAR path[MAX_PATH];
     244             :     size_t len;
     245             : 
     246             :     if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
     247             :         heim_set_error_message(context, EINVAL, "Unable to determine folder path");
     248             :         return EINVAL;
     249             :     }
     250             : 
     251             :     len = strlen(path);
     252             : 
     253             :     if (len > 0 && path[len - 1] == '\\')
     254             :         path[len - 1] = '\0';
     255             : 
     256             :     if (postfix &&
     257             :         strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
     258             :         return heim_enomem(context);
     259             : 
     260             :     *ret = strdup(path);
     261             :     if (*ret == NULL)
     262             :         return heim_enomem(context);
     263             :     return 0;
     264             : }
     265             : 
     266             : #else
     267             : 
     268             : static heim_error_code
     269           0 : expand_path(heim_context context, PTYPE param, const char *postfix,
     270             :             const char *arg, char **ret)
     271             : {
     272           0 :     *ret = strdup(postfix);
     273           0 :     if (*ret == NULL)
     274           0 :         return heim_enomem(context);
     275           0 :     return 0;
     276             : }
     277             : 
     278             : static heim_error_code
     279         172 : expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
     280             :                    const char *arg, char **ret)
     281             : {
     282         172 :     const char *p = NULL;
     283             : 
     284         172 :     p = secure_getenv("TEMP");
     285             : 
     286         172 :     if (p)
     287           0 :         *ret = strdup(p);
     288             :     else
     289         172 :         *ret = strdup("/tmp");
     290         172 :     if (*ret == NULL)
     291           0 :         return heim_enomem(context);
     292         172 :     return 0;
     293             : }
     294             : 
     295             : static heim_error_code
     296           0 : expand_userid(heim_context context, PTYPE param, const char *postfix,
     297             :               const char *arg, char **str)
     298             : {
     299           0 :     int ret = asprintf(str, "%ld", (unsigned long)getuid());
     300           0 :     if (ret < 0 || *str == NULL)
     301           0 :         return heim_enomem(context);
     302           0 :     return 0;
     303             : }
     304             : 
     305             : static heim_error_code
     306           0 : expand_euid(heim_context context, PTYPE param, const char *postfix,
     307             :             const char *arg, char **str)
     308             : {
     309           0 :     int ret = asprintf(str, "%ld", (unsigned long)geteuid());
     310           0 :     if (ret < 0 || *str == NULL)
     311           0 :         return heim_enomem(context);
     312           0 :     return 0;
     313             : }
     314             : #endif /* _WIN32 */
     315             : 
     316             : static heim_error_code
     317      792122 : expand_home(heim_context context, PTYPE param, const char *postfix,
     318             :             const char *arg, char **str)
     319             : {
     320       19522 :     char homedir[MAX_PATH];
     321       19522 :     int ret;
     322             : 
     323      792122 :     if (roken_get_homedir(homedir, sizeof(homedir)))
     324      792122 :         ret = asprintf(str, "%s", homedir);
     325             :     else
     326           0 :         ret = asprintf(str, "/unknown");
     327      792122 :     if (ret < 0 || *str == NULL)
     328           0 :         return heim_enomem(context);
     329      772600 :     return 0;
     330             : }
     331             : 
     332             : static heim_error_code
     333           0 : expand_username(heim_context context, PTYPE param, const char *postfix,
     334             :                 const char *arg, char **str)
     335             : {
     336           0 :     char user[128];
     337           0 :     const char *username = roken_get_username(user, sizeof(user));
     338             : 
     339           0 :     if (username == NULL) {
     340           0 :         heim_set_error_message(context, ENOTTY,
     341           0 :                                N_("unable to figure out current principal",
     342             :                                ""));
     343           0 :         return ENOTTY; /* XXX */
     344             :     }
     345             : 
     346           0 :     *str = strdup(username);
     347           0 :     if (*str == NULL)
     348           0 :         return heim_enomem(context);
     349             : 
     350           0 :     return 0;
     351             : }
     352             : 
     353             : static heim_error_code
     354           0 : expand_loginname(heim_context context, PTYPE param, const char *postfix,
     355             :                  const char *arg, char **str)
     356             : {
     357           0 :     char user[128];
     358           0 :     const char *username = roken_get_loginname(user, sizeof(user));
     359             : 
     360           0 :     if (username == NULL) {
     361           0 :         heim_set_error_message(context, ENOTTY,
     362           0 :                                N_("unable to figure out current principal",
     363             :                                ""));
     364           0 :         return ENOTTY; /* XXX */
     365             :     }
     366             : 
     367           0 :     *str = strdup(username);
     368           0 :     if (*str == NULL)
     369           0 :         return heim_enomem(context);
     370             : 
     371           0 :     return 0;
     372             : }
     373             : 
     374             : static heim_error_code
     375           0 : expand_strftime(heim_context context, PTYPE param, const char *postfix,
     376             :                 const char *arg, char **ret)
     377             : {
     378           0 :     size_t len;
     379           0 :     time_t t;
     380           0 :     char buf[1024];
     381             : 
     382           0 :     t = time(NULL);
     383           0 :     len = strftime(buf, sizeof(buf), arg, localtime(&t));
     384           0 :     if (len == 0 || len >= sizeof(buf))
     385           0 :         return heim_enomem(context);
     386           0 :     *ret = strdup(buf);
     387           0 :     return 0;
     388             : }
     389             : 
     390             : /**
     391             :  * Expand an extra token
     392             :  */
     393             : 
     394             : static heim_error_code
     395           0 : expand_extra_token(heim_context context, const char *value, char **ret)
     396             : {
     397           0 :     *ret = strdup(value);
     398           0 :     if (*ret == NULL)
     399           0 :         return heim_enomem(context);
     400           0 :     return 0;
     401             : }
     402             : 
     403             : /**
     404             :  * Expand a %{null} token
     405             :  *
     406             :  * The expansion of a %{null} token is always the empty string.
     407             :  */
     408             : 
     409             : static heim_error_code
     410           0 : expand_null(heim_context context, PTYPE param, const char *postfix,
     411             :             const char *arg, char **ret)
     412             : {
     413           0 :     *ret = strdup("");
     414           0 :     if (*ret == NULL)
     415           0 :         return heim_enomem(context);
     416           0 :     return 0;
     417             : }
     418             : 
     419             : 
     420             : static const struct {
     421             :     const char * tok;
     422             :     int ftype;
     423             : #define FTYPE_CSIDL 0
     424             : #define FTYPE_SPECIAL 1
     425             : 
     426             :     PTYPE param;
     427             :     const char * postfix;
     428             : 
     429             :     int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
     430             : 
     431             : #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
     432             : #define SPECIAL(f) SPECIALP(f, NULL)
     433             : 
     434             : } tokens[] = {
     435             : #ifdef _WIN32
     436             : #define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
     437             : #define CSIDL(C) CSIDLP(C, NULL)
     438             : 
     439             :     {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
     440             :     {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
     441             :     {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
     442             :     {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
     443             :     {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
     444             :     {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
     445             :     {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
     446             :     {"LIBDIR", SPECIAL(expand_bin_dir)},
     447             :     {"BINDIR", SPECIAL(expand_bin_dir)},
     448             :     {"LIBEXEC", SPECIAL(expand_bin_dir)},
     449             :     {"SBINDIR", SPECIAL(expand_bin_dir)},
     450             : #else
     451             :     {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
     452             :     {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
     453             :     {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
     454             :     {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
     455             :     {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
     456             :     {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
     457             :     {"euid", SPECIAL(expand_euid)},
     458             :     {"ruid", SPECIAL(expand_userid)},
     459             :     {"loginname", SPECIAL(expand_loginname)},
     460             : #endif
     461             :     {"username", SPECIAL(expand_username)},
     462             :     {"TEMP", SPECIAL(expand_temp_folder)},
     463             :     {"USERID", SPECIAL(expand_userid)},
     464             :     {"uid", SPECIAL(expand_userid)},
     465             :     {"null", SPECIAL(expand_null)},
     466             :     {"strftime", SPECIAL(expand_strftime)},
     467             :     {"HOME", SPECIAL(expand_home)},
     468             : };
     469             : 
     470             : static heim_error_code
     471      792294 : expand_token(heim_context context,
     472             :              const char *token,
     473             :              const char *token_end,
     474             :              char **extra_tokens,
     475             :              char **ret)
     476             : {
     477       19522 :     heim_error_code errcode;
     478       19522 :     size_t i;
     479       19522 :     char **p;
     480       19522 :     const char *colon;
     481             : 
     482      792294 :     *ret = NULL;
     483             : 
     484      792294 :     if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
     485      792294 :         token_end - token <= 2) {
     486           0 :         heim_set_error_message(context, EINVAL,"Invalid token.");
     487           0 :         return EINVAL;
     488             :     }
     489             : 
     490      792294 :     for (p = extra_tokens; p && p[0]; p += 2) {
     491           0 :         if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
     492           0 :             return expand_extra_token(context, p[1], ret);
     493             :     }
     494             : 
     495     8714202 :     for (colon=token+2; colon < token_end; colon++)
     496     7921908 :         if (*colon == ':')
     497           0 :             break;
     498             : 
     499     4754624 :     for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
     500     4754624 :         if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
     501      792294 :             char *arg = NULL;
     502             : 
     503      792294 :             errcode = 0;
     504      792294 :             if (*colon == ':') {
     505           0 :                 int asprintf_ret = asprintf(&arg, "%.*s",
     506           0 :                                             (int)(token_end - colon - 1),
     507             :                                             colon + 1);
     508           0 :                 if (asprintf_ret < 0 || !arg)
     509           0 :                     errcode = ENOMEM;
     510             :             }
     511      772772 :             if (!errcode)
     512      792294 :                 errcode = tokens[i].exp_func(context, tokens[i].param,
     513      792294 :                                              tokens[i].postfix, arg, ret);
     514      792294 :             free(arg);
     515      792294 :             return errcode;
     516             :         }
     517             : 
     518           0 :     heim_set_error_message(context, EINVAL, "Invalid token.");
     519           0 :     return EINVAL;
     520             : }
     521             : 
     522             : /**
     523             :  * Internal function to expand tokens in paths.
     524             :  *
     525             :  * Params:
     526             :  *
     527             :  * @context   A heim_context
     528             :  * @path_in   The path to expand tokens from
     529             :  * @filepath  True if this is a filesystem path (converts slashes to
     530             :  *            backslashes on Windows)
     531             :  * @ppath_out The expanded path
     532             :  * @...       Variable number of pairs of strings, the first of each
     533             :  *            being a token (e.g., "luser") and the second a string to
     534             :  *            replace it with.  The list is terminated by a NULL.
     535             :  */
     536             : heim_error_code
     537     3317074 : heim_expand_path_tokens(heim_context context,
     538             :                         const char *path_in,
     539             :                         int filepath,
     540             :                         char **ppath_out,
     541             :                         ...)
     542             : {
     543       77626 :     heim_error_code ret;
     544       77626 :     va_list ap;
     545             : 
     546     3317074 :     va_start(ap, ppath_out);
     547     3317074 :     ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
     548     3317074 :     va_end(ap);
     549             : 
     550     3317074 :     return ret;
     551             : }
     552             : 
     553             : static void
     554     3317074 : free_extra_tokens(char **extra_tokens)
     555             : {
     556       77626 :     char **p;
     557             : 
     558     3317074 :     for (p = extra_tokens; p && *p; p++)
     559           0 :         free(*p);
     560     3317074 :     free(extra_tokens);
     561     3317074 : }
     562             : 
     563             : /**
     564             :  * Internal function to expand tokens in paths.
     565             :  *
     566             :  * Inputs:
     567             :  *
     568             :  * @context   A heim_context
     569             :  * @path_in   The path to expand tokens from
     570             :  * @filepath  True if this is a filesystem path (converts slashes to
     571             :  *            backslashes on Windows)
     572             :  * @ppath_out The expanded path
     573             :  * @ap        A NULL-terminated va_list of pairs of strings, the first of each
     574             :  *            being a token (e.g., "luser") and the second a string to replace
     575             :  *            it with.
     576             :  * 
     577             :  * Outputs:
     578             :  *
     579             :  * @ppath_out Path with expanded tokens (caller must free() this)
     580             :  */
     581             : heim_error_code
     582     3317074 : heim_expand_path_tokensv(heim_context context,
     583             :                          const char *path_in,
     584             :                          int filepath,
     585             :                          char **ppath_out, va_list ap)
     586             : {
     587       77626 :     char *tok_begin, *tok_end, *append;
     588     3317074 :     char **extra_tokens = NULL;
     589       77626 :     const char *path_left;
     590     3317074 :     size_t nargs = 0;
     591     3317074 :     size_t len = 0;
     592       77626 :     va_list ap2;
     593             : 
     594     3317074 :     if (path_in == NULL || *path_in == '\0') {
     595           0 :         *ppath_out = strdup("");
     596           0 :         return 0;
     597             :     }
     598             : 
     599     3317074 :     *ppath_out = NULL;
     600             : 
     601             : #if defined(_MSC_VER)
     602             :     ap2 = ap;           /* Come on! See SO #558223 */
     603             : #else
     604     3317074 :     va_copy(ap2, ap);
     605             : #endif
     606     3317074 :     while (va_arg(ap2, const char *)) {
     607           0 :         nargs++;
     608           0 :         va_arg(ap2, const char *);
     609             :     }
     610     3317074 :     va_end(ap2);
     611     3317074 :     nargs *= 2;
     612             : 
     613             :     /* Get extra tokens */
     614     3317074 :     if (nargs) {
     615           0 :         size_t i;
     616             : 
     617           0 :         extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
     618           0 :         if (extra_tokens == NULL)
     619           0 :             return heim_enomem(context);
     620           0 :         for (i = 0; i < nargs; i++) {
     621           0 :             const char *s = va_arg(ap, const char *); /* token key */
     622           0 :             if (s == NULL)
     623           0 :                 break;
     624           0 :             extra_tokens[i] = strdup(s);
     625           0 :             if (extra_tokens[i++] == NULL) {
     626           0 :                 free_extra_tokens(extra_tokens);
     627           0 :                 return heim_enomem(context);
     628             :             }
     629           0 :             s = va_arg(ap, const char *); /* token value */
     630           0 :             if (s == NULL)
     631           0 :                 s = "";
     632           0 :             extra_tokens[i] = strdup(s);
     633           0 :             if (extra_tokens[i] == NULL) {
     634           0 :                 free_extra_tokens(extra_tokens);
     635           0 :                 return heim_enomem(context);
     636             :             }
     637             :         }
     638             :     }
     639             : 
     640     7426442 :     for (path_left = path_in; path_left && *path_left; ) {
     641             : 
     642     4109368 :         tok_begin = strstr(path_left, "%{");
     643             : 
     644     4109368 :         if (tok_begin && tok_begin != path_left) {
     645             : 
     646           0 :             append = malloc((tok_begin - path_left) + 1);
     647           0 :             if (append) {
     648           0 :                 memcpy(append, path_left, tok_begin - path_left);
     649           0 :                 append[tok_begin - path_left] = '\0';
     650             :             }
     651           0 :             path_left = tok_begin;
     652             : 
     653     4109368 :         } else if (tok_begin) {
     654             : 
     655      792294 :             tok_end = strchr(tok_begin, '}');
     656      792294 :             if (tok_end == NULL) {
     657           0 :                 free_extra_tokens(extra_tokens);
     658           0 :                 if (*ppath_out)
     659           0 :                     free(*ppath_out);
     660           0 :                 *ppath_out = NULL;
     661           0 :                 heim_set_error_message(context, EINVAL, "variable missing }");
     662           0 :                 return EINVAL;
     663             :             }
     664             : 
     665      792294 :             if (expand_token(context, tok_begin, tok_end, extra_tokens,
     666             :                              &append)) {
     667           0 :                 free_extra_tokens(extra_tokens);
     668           0 :                 if (*ppath_out)
     669           0 :                     free(*ppath_out);
     670           0 :                 *ppath_out = NULL;
     671           0 :                 return EINVAL;
     672             :             }
     673             : 
     674      792294 :             path_left = tok_end + 1;
     675             :         } else {
     676             : 
     677     3317074 :             append = strdup(path_left);
     678     3317074 :             path_left = NULL;
     679             : 
     680             :         }
     681             : 
     682     4109368 :         if (append == NULL) {
     683             : 
     684           0 :             free_extra_tokens(extra_tokens);
     685           0 :             if (*ppath_out)
     686           0 :                 free(*ppath_out);
     687           0 :             *ppath_out = NULL;
     688           0 :             return heim_enomem(context);
     689             : 
     690             :         }
     691             : 
     692             :         {
     693     4109368 :             size_t append_len = strlen(append);
     694     4109368 :             char * new_str = realloc(*ppath_out, len + append_len + 1);
     695             : 
     696     4109368 :             if (new_str == NULL) {
     697           0 :                 free_extra_tokens(extra_tokens);
     698           0 :                 free(append);
     699           0 :                 if (*ppath_out)
     700           0 :                     free(*ppath_out);
     701           0 :                 *ppath_out = NULL;
     702           0 :                 return heim_enomem(context);
     703             :             }
     704             : 
     705     4109368 :             *ppath_out = new_str;
     706     4109368 :             memcpy(*ppath_out + len, append, append_len + 1);
     707     4109368 :             len = len + append_len;
     708     4109368 :             free(append);
     709             :         }
     710             :     }
     711             : 
     712             : #ifdef _WIN32
     713             :     /* Also deal with slashes */
     714             :     if (filepath && *ppath_out) {
     715             :         char * c;
     716             : 
     717             :         for (c = *ppath_out; *c; c++)
     718             :             if (*c == '/')
     719             :                 *c = '\\';
     720             :     }
     721             : #endif
     722             : 
     723     3317074 :     free_extra_tokens(extra_tokens);
     724     3317074 :     return 0;
     725             : }

Generated by: LCOV version 1.14