/*
* Copyright (c) 2003-2006 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: t-mm.c,v 1.40 2006/10/05 04:27:39 ca Exp $")
#include "sm/error.h"
#include "sm/sysexits.h"
#include "sm/ctype.h"
#include "sm/heap.h"
#include "sm/memops.h"
#include "sm/maps.h"
#include "sm/mapc.h"
#include "sm/map.h"
#include "sm/mapclasses.h"
#include "sm/bdb.h"
#include "sm/cdb_map.h"
#include "sm/io.h"
static int Verbose = 0;
#define SEP ':'
#define SEP_WSPC (-1)
#define MAPC_TYPE "hash"
#define MAPC_NAME "aliases"
#define MAPC_FILE "aliases.db"
#define MAX_IN_LEN (256 * 1024)
#define SM_COMM_CHAR '#'
#define SMM_FL_LWR_KEY 0x00000001u /* change key to lower case */
#define SMM_FL_SKIP_LEAD_SP 0x00000002u /* skip leading whitespace on RHS */
#define SMM_FL_WHSP_DELIM 0x00000004u /* use whitespace as delimiter */
#define SMM_FL_EMPTY_KEY 0x00000008u /* allow empty keys */
#define SMM_FL_SKIP_COMM 0x00000010u /* skip comments */
#ifndef SM_ALLOW_ROOT
# define SM_ALLOW_ROOT 0
#endif
static char buf[MAX_IN_LEN];
#if MAX_IN_LEN >= INT_MAX
ERROR _MAX_IN_LEN >= _INT_MAX
#endif
static int
sm_makemap(sm_map_P map, int sep, uint32_t flags)
{
sm_ret_T ret;
size_t len;
sm_map_key_T key;
sm_map_data_T data;
char *delim, *rhs;
int c;
uint u, line;
line = 0;
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
++line;
if (SM_IS_FLAG(flags, SMM_FL_SKIP_COMM))
{
char *p;
if (*buf == '\0' || *buf == SM_COMM_CHAR)
continue;
p = buf;
while (*p != '\0' && ISSPACE(*p))
p++;
if (*p == '\0')
continue;
}
/* find key/value separator */
if (SM_IS_FLAG(flags, SMM_FL_WHSP_DELIM))
delim = strpbrk(buf, " \t");
else
delim = strchr(buf, sep);
if (delim == NULL)
{
/* complain about missing delim */
fprintf(stderr,
"no key/value separator found; line=%u\n",
line);
continue;
}
SM_ASSERT(delim < buf + sizeof(buf));
SM_ASSERT(delim >= buf);
u = (delim - buf) + 1;
*delim = '\0';
c = '\0';
/* skip over leading space; make this optional? */
if (SM_IS_FLAG(flags, SMM_FL_SKIP_LEAD_SP))
{
while (u < sizeof(buf) && (c = buf[u]) != '\0'
&& isspace(c) && c != '\n')
++u;
}
if (u >= sizeof(buf) || c == '\0' || c == '\n')
{
fprintf(stderr, "rhs is empty, line=%u, u=%u\n",
line, u);
continue;
}
rhs = buf + u;
while (u < sizeof(buf) && (c = buf[u]) != '\0' && c != '\n')
++u;
if (u >= sizeof(buf) || c != '\n')
{
fprintf(stderr, "can't find end of rhs; line=%u\n",
line);
continue;
}
buf[u] = '\0';
len = strlen(buf);
if (SM_IS_FLAG(flags, SMM_FL_LWR_KEY))
{
uint j;
for (j = 0; j < len; j++)
{
c = buf[j];
if (ISUPPER(c))
buf[j] = TOLOWER(c);
}
}
if (!SM_IS_FLAG(flags, SMM_FL_EMPTY_KEY) && len == 0)
{
fprintf(stderr,"empty key; line=%u\n", line);
continue;
}
sm_str_assign(key, NULL, (uchar *)buf, len, len);
sm_str_assign(data, NULL, (uchar *)rhs, strlen(rhs),
strlen(rhs));
if (Verbose > 0)
{
fprintf(stderr, "lhs='%s' [%d], rhs='%s' [%d]\n"
, (char *)sm_str_data(&key)
, sm_str_getlen(&key)
, (char *)sm_str_data(&data)
, sm_str_getlen(&data)
);
}
ret = sm_map_add(map, &key, &data, SMMAP_AFL_UNIQUE);
if (sm_is_err(ret))
{
fprintf(stderr, "sm_map_add=failed, error=%s; line=%u\n"
, smerr2txt(ret), line);
sm_map_close(map, 0);
return ret;
}
}
ret = sm_map_close(map, 0);
return ret;
}
static int
sm_openmap(const char *mapname, const char *mapfile, const char *maptype, int sep, uint32_t flags)
{
sm_ret_T ret;
sm_maps_P maps;
sm_map_P map;
sm_cstr_P mtype, mname;
maps = NULL;
mtype = mname = NULL;
ret = sm_maps_init(&maps);
if (maps == NULL)
return ret;
mtype = sm_cstr_scpyn0((const uchar *)maptype, strlen(maptype));
if (mtype == NULL)
goto error;
mname = sm_cstr_scpyn0((const uchar *)mapname, strlen(mapname));
if (mname == NULL)
goto error;
ret = sm_bdb_class_create(maps);
#if MTA_USE_TINYCDB
ret = sm_cdb_class_create(maps);
#endif
#if 0
/* "delete" the file to make sure the result won't contain garbage? */
ret = truncate(mapfile, 0);
#endif /* 0 */
map = NULL;
ret = sm_map_open(maps, mname, mtype, SMAP_MODE_CREATE, mapfile,
SMAP_MODE_RDWR, &map, SMPO_END);
if (!sm_is_success(ret))
goto error;
ret = sm_makemap(map, sep, flags);
ret = sm_maps_term(maps);
SM_CSTR_FREE(mtype);
SM_CSTR_FREE(mname);
return ret;
error:
sm_maps_term(maps);
return ret;
}
static void
usage(const char *prg)
{
fprintf(stderr, "usage: %s [options]\n"
"-E allow empty key\n"
"-f do not convert keys to lower case\n"
"-F file name of db file [%s]\n"
"-l n list available map classes (n: verbosity)\n"
"-n name name of map [%s]\n"
"-s do not ignore comment or empty lines\n"
"-T type map type [%s]\n"
"-t c separator [%c]\n"
"-V increase verbosity\n"
"-w use whitespace as separator\n"
"Options -t and -w are mutually exclusive.\n"
"\n%s creates a %s map from the data provided via stdin.\n"
"Entries must be of the form\n"
"lhs%crhs\n"
"(where '%c' is the default separator)\n"
"Entries cannot span multiple lines.\n"
, prg
, MAPC_FILE
, MAPC_NAME
, MAPC_TYPE
, SEP
, prg
, MAPC_TYPE
, SEP
, SEP
);
exit(EX_USAGE);
}
int
main(int argc, char *argv[])
{
int c, sep;
uint32_t flags;
int list_mapc;
char *mapname, *mapfile, *maptype, *prg;
prg = argv[0];
sep = (int) SEP;
mapname = MAPC_NAME;
mapfile = MAPC_FILE;
maptype = MAPC_TYPE;
list_mapc = -1;
flags = SMM_FL_LWR_KEY|SMM_FL_SKIP_LEAD_SP|SMM_FL_SKIP_COMM;
#if !SM_ALLOW_ROOT
if (getuid() == 0 || geteuid() == 0)
{
fprintf(stderr,
"%s: ERROR: do not run this as super-user!\n",
prg);
exit(EX_USAGE);
}
#endif
while ((c = getopt(argc, argv, "EfF:hl:n:SsT:t:Vw")) != -1)
{
switch (c)
{
case 'E':
flags |= SMM_FL_EMPTY_KEY;
break;
case 'f':
flags &= ~SMM_FL_LWR_KEY;
break;
case 'F':
SM_STRDUP_OPT(mapfile, optarg);
break;
case 'l':
list_mapc = (int) strtol(optarg, NULL, 0);
break;
case 'n':
SM_STRDUP_OPT(mapname, optarg);
break;
case 's':
flags &= ~SMM_FL_SKIP_COMM;
break;
case 'S':
flags &= ~SMM_FL_SKIP_LEAD_SP;
break;
case 'T':
SM_STRDUP_OPT(maptype, optarg);
break;
case 't':
if (SM_IS_FLAG(flags, SMM_FL_WHSP_DELIM))
{
usage(prg);
/* NOTREACHED */
return EX_USAGE;
}
sep = (int) (*optarg);
break;
case 'V':
++Verbose;
break;
case 'w':
if (sep != (int) SEP)
{
usage(prg);
/* NOTREACHED */
return EX_USAGE;
}
flags |= SMM_FL_WHSP_DELIM;
break;
case 'h':
default:
usage(prg);
/* NOTREACHED */
return EX_USAGE;
}
}
if (list_mapc >= 0)
{
sm_ret_T ret;
sm_maps_P maps;
maps = NULL;
ret = sm_maps_init(&maps);
if (NULL == maps)
exit(EX_OSERR);
ret = sm_maps_create(maps);
if (sm_is_err(ret))
exit(EX_OSERR);
c = sm_mapc_list(smioout, maps, list_mapc,
SMMAPC_FL_FILE|SMMAPC_FL_ADD);
}
else
c = sm_openmap(mapname, mapfile, maptype, sep, flags);
return c;
}
syntax highlighted by Code2HTML, v. 0.9.1