/*
* Copyright (c) 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: strexpmac.c,v 1.3 2005/07/05 17:20:49 ca Exp $")
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/ctype.h"
#include "sm/str-int.h"
#include "sm/strexp.h"
/*
** SM_FIND_MACRO -- Find macro in array macros
**
** Parameters:
** str -- str to expand (read)
** mac_begin -- begin of macro in str
** mac_end -- end of macro in str
** pidx -- (pointer to) index in macros if found (otput)
** nmacros -- number of valid elements in macros/repl
** macros -- array of macros
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_find_macro(const sm_str_P src, uint len, uint mac_begin, uint mac_end, uint *pidx, uint nmacros, sm_str_P *macros)
{
uint i, j, k, l;
bool match;
if (mac_begin >= mac_end ||
mac_begin >= len ||
mac_end > len)
return sm_err_perm(EINVAL);
SM_REQUIRE(pidx != NULL);
SM_REQUIRE(macros != NULL);
for (i = 0; i < nmacros; i++)
{
if (macros[i] == NULL)
continue;
l = sm_str_getlen(macros[i]);
if (l != mac_end - mac_begin + 1)
continue;
match = true;
for (k = 0, j = mac_begin;
match && k < l && j <= mac_end;
k++, j++)
{
match = sm_str_rd_elem(src, j) ==
sm_str_rd_elem(macros[i], k);
}
if (match && k == l && j == mac_end + 1)
{
*pidx = i;
return SM_SUCCESS;
}
}
return sm_err_perm(SM_E_NOTFOUND);
}
/*
** SM_STR_EXPMAC -- Expand ${macro} in string with repl[macro]
** \$ will be replaced by $ to suppress expansion of ${macro}
**
** Parameters:
** src -- str to expand (read)
** dst -- str into which the expanded sequence is written (output)
** flags -- flags (currently unused)
** macros -- array of macros
** repl -- array of replacement strings to use for expansion
**
** Returns:
** ==0: no macro in src, NOTHING written to dst!
** >=0: number of replacements
** <0: usual sm_error code
**
** Change API to use a callback to get a string for a macro?
*/
sm_ret_T
sm_str_expmac(const sm_str_P src, sm_str_P dst, uint flags, uint nmacros, sm_str_P *macros, sm_str_P *repl)
{
uint i, len, idx, replacements, mac_begin, mac_end;
uchar ch;
sm_ret_T ret;
bool backslash, found;
SM_IS_BUF(dst);
SM_IS_BUF(src);
if (nmacros == 0)
return 0;
SM_REQUIRE(macros != NULL);
SM_REQUIRE(repl != NULL);
#define MACPUTCH(ch) \
do { \
ret = sm_str_put(dst, (ch)); \
if (sm_is_err(ret)) \
goto error; \
} while (0)
len = sm_str_getlen(src);
backslash = false;
found = false;
for (i = 0; i < len; i++)
{
ch = sm_str_rd_elem(src, i);
/* need at least ${A} */
if (!backslash &&
ch == '$' && i + 3 < len &&
(ch = sm_str_rd_elem(src, i + 1)) == '{')
{
found = true;
break;
}
if (!backslash)
backslash = ch == '\\';
else
{
/* \$ will be replaced */
if (ch == '$')
{
found = true;
break;
}
backslash = false;
}
}
if (!found)
return 0;
replacements = 0;
backslash = false;
for (i = 0; i < len; i++)
{
ch = sm_str_rd_elem(src, i);
/* need at least ${A} */
if (!backslash &&
ch == '$' && i + 3 < len &&
(ch = sm_str_rd_elem(src, i + 1)) == '{')
{
i += 2;
mac_begin = i;
for (;
i < len && (ch = sm_str_rd_elem(src, i)) != '}';
i++)
;
if (i < len && ch == '}')
{
mac_end = i - 1;
ret = sm_find_macro(src, len, mac_begin,
mac_end, &idx, nmacros, macros);
++replacements;
/* on error: replace with empty string */
/* make this an option to preserve string?? */
if (sm_is_err(ret))
continue;
if (idx >= nmacros || repl[idx] == NULL)
continue;
ret = sm_str_cat(dst, repl[idx]);
if (sm_is_err(ret))
goto error;
}
else
{
/* copy "skipped" text over */
MACPUTCH('$');
MACPUTCH('{');
for (idx = mac_begin; idx < i; idx++)
{
ch = sm_str_rd_elem(src, idx);
MACPUTCH(ch);
}
}
}
else
{
if (!backslash)
{
backslash = ch == '\\';
if (backslash)
{
++replacements;
continue;
}
}
else
{
backslash = false;
if (ch != '$')
MACPUTCH('\\');
else
++replacements;
}
ret = sm_str_put(dst, ch);
if (sm_is_err(ret))
goto error;
}
}
if (backslash)
MACPUTCH('\\');
if (replacements > (uint)SM_RET_MAX)
replacements = SM_RET_MAX;
return (sm_ret_T) replacements;
error:
/* cleanup? */
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1