/* Convert other programs' databases to XML.
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.org>
* Parts written by Andrew Kempe and others.
* This program is free but copyrighted software; see the file COPYING for
* details.
*/
#define CONVERT_DB_MAIN
#define STANDALONE_NICKSERV
#define STANDALONE_CHANSERV
#include "convert-db.h"
#include "language.h"
#include "modules/misc/xml.h"
/*************************************************************************/
/* Data read in. */
NickGroupInfo *ngi_list;
NickInfo *ni_list;
ChannelInfo *ci_list;
NewsItem *news_list;
MaskData *md_list[256];
ServerStats *ss_list;
int32 maxusercnt;
time_t maxusertime;
char supass[PASSMAX];
int no_supass = 1;
/*************************************************************************/
/* NULL-terminated list of supported database types. */
static DBTypeInfo *dbtypes[] = {
&dbtype_anope,
&dbtype_auspice,
&dbtype_bolivia,
&dbtype_cygnus,
&dbtype_daylight,
&dbtype_epona,
&dbtype_hybserv,
&dbtype_ircs_1_2,
&dbtype_magick_14b2,
&dbtype_ptlink,
&dbtype_sirv,
&dbtype_trircd_4_26,
&dbtype_wrecked_1_2,
NULL
};
/*************************************************************************/
/*************************** Exported routines ***************************/
/*************************************************************************/
/* Safe memory allocation. We also clear the memory just to be clean, and
* to make scalloc() easy to implement (it's a macro defined in
* convert-db.h).
*/
void *smalloc(long size)
{
void *ptr;
if (!size)
size = 1;
ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
memset(ptr, 0, size);
return ptr;
}
/*************************************************************************/
/* Safe memory reallocation. If the buffer is grown, any extra memory is
* _not_ cleared.
*/
void *srealloc(void *ptr, long size)
{
if (!size)
size = 1;
ptr = realloc(ptr, size);
if (!ptr) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
return ptr;
}
/*************************************************************************/
/* Safe string copying/duplication (from misc.c/memory.c). */
char *strscpy(char *d, const char *s, size_t len)
{
strncpy(d, s, len-1);
d[len-1] = 0;
return d;
}
char *sstrdup(const char *s)
{
int len = strlen(s)+1;
char *d = smalloc(len);
memcpy(d, s, len);
return d;
}
/*************************************************************************/
/* NickInfo/NickGroupInfo allocation (from modules/nickserv/util.c). The
* NickInfo and NickGroupInfo (if appropriate) are added into their
* respective hash tables. Always succeeds (if the memory allocations
* fail, aborts the program).
*/
#include "modules/nickserv/util.c"
NickInfo *makenick(const char *nick, NickGroupInfo **nickgroup_ret)
{
NickInfo *ni = new_nickinfo();
strscpy(ni->nick, nick, sizeof(ni->nick));
if (nickgroup_ret) {
NickGroupInfo *ngi = new_nickgroupinfo(ni->nick);
while (get_nickgroupinfo(ngi->id)) {
/* We assume that eventually we'll find one that's not in use */
ngi->id = rand() + rand();
if (ngi->id == 0)
ngi->id = 1;
}
ni->nickgroup = ngi->id;
ARRAY_EXTEND(ngi->nicks);
strscpy(ngi->nicks[0], nick, sizeof(ngi->nicks[0]));
add_nickgroupinfo(ngi);
*nickgroup_ret = ngi;
}
add_nickinfo(ni);
return ni;
}
/*************************************************************************/
/* ChannelInfo allocation. The channel is NOT added to its hash tables. */
#include "modules/chanserv/util.c"
ChannelInfo *makechan(const char *name)
{
ChannelInfo *ci = new_channelinfo();
strscpy(ci->name, name, sizeof(ci->name));
return ci;
}
/*************************************************************************/
/* Open a (Services pre-5.0 style) data file and check the version number.
* Prints an error message and exits with 1 if either the file cannot be
* opened or the version number is wrong. If `version_ret' is non-NULL,
* the version number is stored there.
*/
dbFILE *open_db_ver(const char *dir, const char *name, int32 min_version,
int32 max_version, int32 *version_ret)
{
char filename[PATH_MAX+1];
dbFILE *f;
int32 ver;
snprintf(filename, sizeof(filename), "%s/%s", dir, name);
f = open_db(filename, "r", 0);
if (!f) {
fprintf(stderr, "Can't open %s for reading: %s\n", filename,
strerror(errno));
exit(1);
}
if (read_int32(&ver, f) < 0) {
fprintf(stderr, "Error reading version number on %s\n", filename);
exit(1);
}
if (ver < min_version || ver > max_version) {
fprintf(stderr, "Wrong version number on %s\n", filename);
exit(1);
}
if (version_ret)
*version_ret = ver;
return f;
}
/*************************************************************************/
/* Retrieve the NickGroupInfo structure for the given nick. Returns NULL
* if the nick does not exist or is forbidden (i.e. has no NickGroupInfo).
*/
NickGroupInfo *get_nickgroupinfo_by_nick(const char *nick)
{
NickInfo *ni = get_nickinfo(nick);
return ni ? get_nickgroupinfo(ni->nickgroup) : NULL;
}
/*************************************************************************/
/* Set the OperServ privilege level (os_priv) for the nickgroup associated
* with `nick' to `level', if it is not already greater than `level'. Does
* nothing if `nick' is NULL or does not have an associated nickgroup.
*/
void set_os_priv(const char *nick, int16 level)
{
NickGroupInfo *ngi;
if (!nick)
return;
ngi = get_nickgroupinfo_by_nick(nick);
if (ngi && ngi->os_priv < level)
ngi->os_priv = level;
}
/*************************************************************************/
/* Return the Services 5.0 channel access level that corresponds to the
* given pre-5.0 level. Code taken from modules/database/version4.c
* (convert_old_level()).
*/
int16 convert_acclev(int16 old)
{
if (old < 0)
return -convert_acclev(-old); /* avoid negative division */
else if (old <= 25)
return old*10; /* 0.. 25 -> 0..250 (10x) */
else if (old <= 50)
return 200 + old*2; /* 25.. 50 -> 250..300 ( 2x) */
else if (old <= 100)
return 280 + old*2/5; /* 50.. 100 -> 300..320 ( 0.4x) */
else if (old <= 1000)
return 300 + old/5; /* 100..1000 -> 320..500 ( 0.2x) */
else if (old <= 2000)
return 400 + old/10; /* 1000..2000 -> 500..600 ( 0.1x) */
else
return 500 + old/20; /* 2000..9999 -> 600..999 ( 0.05x) */
}
/*************************************************************************/
/*************************************************************************/
/* Replacements for database functions. add_*() functions are defined as
* macros in convert-db.h. */
/*************************************************************************/
static NickGroupInfo *ngi_iter;
NickGroupInfo *get_nickgroupinfo(uint32 id)
{
NickGroupInfo *ngi;
if (!id)
return NULL;
LIST_SEARCH_SCALAR(ngi_list, id, id, ngi);
return ngi;
}
NickGroupInfo *first_nickgroupinfo(void)
{
ngi_iter = ngi_list;
return next_nickgroupinfo();
}
NickGroupInfo *next_nickgroupinfo(void)
{
NickGroupInfo *retval = ngi_iter;
if (ngi_iter)
ngi_iter = ngi_iter->next;
return retval;
}
/*************************************************************************/
static NickInfo *ni_iter;
NickInfo *get_nickinfo(const char *nick)
{
NickInfo *ni;
LIST_SEARCH(ni_list, nick, nick, stricmp, ni);
return ni;
}
NickInfo *first_nickinfo(void)
{
ni_iter = ni_list;
return next_nickinfo();
}
NickInfo *next_nickinfo(void)
{
NickInfo *retval = ni_iter;
if (ni_iter)
ni_iter = ni_iter->next;
return retval;
}
/*************************************************************************/
static ChannelInfo *ci_iter;
ChannelInfo *get_channelinfo(const char *channel)
{
ChannelInfo *ci;
LIST_SEARCH(ci_list, name, channel, stricmp, ci);
return ci;
}
ChannelInfo *first_channelinfo(void)
{
ci_iter = ci_list;
return next_channelinfo();
}
ChannelInfo *next_channelinfo(void)
{
ChannelInfo *retval = ci_iter;
if (ci_iter)
ci_iter = ci_iter->next;
return retval;
}
/*************************************************************************/
static NewsItem *news_iter;
NewsItem *first_news(void)
{
news_iter = news_list;
return next_news();
}
NewsItem *next_news(void)
{
NewsItem *retval = news_iter;
if (news_iter)
news_iter = news_iter->next;
return retval;
}
/*************************************************************************/
static MaskData *md_iter[256];
MaskData *first_maskdata(uint8 type)
{
md_iter[type] = md_list[type];
return next_maskdata(type);
}
MaskData *next_maskdata(uint8 type)
{
MaskData *retval = md_iter[type];
if (md_iter[type])
md_iter[type] = md_iter[type]->next;
return retval;
}
/*************************************************************************/
static ServerStats *ss_iter;
ServerStats *first_serverstats(void)
{
ss_iter = ss_list;
return next_serverstats();
}
ServerStats *next_serverstats(void)
{
ServerStats *retval = ss_iter;
if (ss_iter)
ss_iter = ss_iter->next;
return retval;
}
/*************************************************************************/
int get_operserv_data(int what, void *ptr)
{
switch (what) {
case OSDATA_MAXUSERCNT:
*(int32 *)ptr = maxusercnt;
break;
case OSDATA_MAXUSERTIME:
*(time_t *)ptr = maxusertime;
break;
case OSDATA_SUPASS:
*(char **)ptr = no_supass ? NULL : supass;
break;
default:
return 0;
}
return 1;
}
/*************************************************************************/
/* Perform sanity checks on the data after it's been read in. */
static void sanity_check_nicks(void);
static void sanity_check_nickgroups(void);
static void sanity_check_channels(void);
static void sanity_check_maskdata(void);
static void sanity_checks(void)
{
sanity_check_nicks();
sanity_check_nickgroups();
sanity_check_channels();
sanity_check_maskdata();
}
static void sanity_check_nicks(void)
{
NickInfo *ni;
NickGroupInfo *ngi;
char *s;
int i;
for (ni = first_nickinfo(); ni; ni = next_nickinfo()) {
/* Forbidden nicks should have no other data associated with them. */
if (ni->status & NS_VERBOTEN) {
ni->status &= ~(NS_VERBOTEN);
ni->last_usermask = NULL;
ni->last_realmask = NULL;
ni->last_realname = NULL;
ni->last_quit = NULL;
ni->last_seen = 0;
if (ni->nickgroup) {
fprintf(stderr, "BUG: Forbidden nickname %s associated with"
" nickgroup %u! Clearing nickgroup field.\n",
ni->nick, ni->nickgroup);
ni->nickgroup = 0;
}
ni->id_stamp = 0;
continue;
}
/* The nick isn't a forbidden nick. First make sure it has the
* right nickgroup. */
if (!ni->nickgroup) {
fprintf(stderr, "BUG: Nickname %s has no nickgroup! Deleting.\n",
ni->nick);
del_nickinfo(ni);
continue;
}
ngi = get_nickgroupinfo(ni->nickgroup);
if (!ngi) {
fprintf(stderr, "BUG: Nickname %s points to nonexistent nickgroup"
" %u! Deleting.\n", ni->nick, ni->nickgroup);
del_nickinfo(ni);
} else {
/* Nicknames in the nicks[] array should match those in the
* NickInfo structure, so use strcmp() (this keeps us from
* having to worry about irc_stricmp() idiosyncrasies). */
ARRAY_SEARCH_PLAIN(ngi->nicks, ni->nick, strcmp, i);
if (i >= ngi->nicks_count) {
fprintf(stderr, "BUG: Nickname %s points to nickgroup %u,"
" but the nickgroup doesn't contain the nickname!"
" Deleting.\n", ni->nick, ni->nickgroup);
del_nickinfo(ni);
}
}
/* Clear unknown flags. */
ni->status &= NS_PERMANENT;
/* Make sure usermasks actually have non-empty user and host parts. */
if (ni->last_usermask) {
s = strchr(ni->last_usermask, '@');
if (!s || s==ni->last_usermask || !s[1]) {
fprintf(stderr, "Last usermask for nickname %s isn't a valid"
" user@host mask, clearing.\n", ni->nick);
ni->last_usermask = NULL;
}
}
if (ni->last_realmask) {
s = strchr(ni->last_realmask, '@');
if (!s || s==ni->last_realmask || !s[1]) {
fprintf(stderr, "Last real usermask for nickname %s isn't"
" a valid user@host mask, clearing.\n", ni->nick);
ni->last_realmask = NULL;
}
}
/* Make sure last-seen time is not earlier than registered time.
* Allow zero, to accommodate cases where the nickname was
* registered by an outside entity without the user actually
* logging on. */
if (ni->last_seen && ni->last_seen < ni->time_registered) {
fprintf(stderr, "Last-seen time for nickname %s is earlier than"
" registration time! Setting last-seen time to"
" registration time.\n", ni->nick);
ni->last_seen = ni->time_registered;
}
} /* for all nicks */
}
static void sanity_check_nickgroups(void)
{
NickInfo *ni;
NickGroupInfo *ngi;
char *s;
int i;
for (ngi = first_nickgroupinfo(); ngi; ngi = next_nickgroupinfo()) {
/* Make sure nickgroups don't contain extra nicks. */
ARRAY_FOREACH(i, ngi->nicks) {
ni = get_nickinfo(ngi->nicks[i]);
if (!ni) {
fprintf(stderr, "BUG: Nickgroup %u contains nonexistent"
" nickname %s! Removing nickname from group.\n",
ngi->id, ngi->nicks[i]);
ARRAY_REMOVE(ngi->nicks, i);
if (ngi->mainnick >= i)
ngi->mainnick--;
i--; /* to make sure we don't skip any */
}
}
/* Check for empty nickgroups (possibly from extraneous nickname
* removal above). */
if (ngi->nicks_count == 0) {
fprintf(stderr, "Removing empty nickgroup %u.\n", ngi->id);
del_nickgroupinfo(ngi);
continue;
}
/* Make sure main nick is a valid index. */
if (ngi->mainnick >= ngi->nicks_count) {
fprintf(stderr, "Invalid main nick index in nickgroup %u,"
" resetting.\n", ngi->id);
ngi->mainnick = 0;
}
/* If an authcode is set, make sure the reason is valid. If not,
* clear the auth-related fields. */
if (ngi->authcode && (ngi->authreason < NICKAUTH_MIN
|| ngi->authreason > NICKAUTH_MAX)) {
fprintf(stderr, "Authentication code set for nickgroup %u but"
" reason code invalid, setting to SETAUTH.\n", ngi->id);
ngi->authreason = NICKAUTH_SETAUTH;
} else {
ngi->authset = 0;
ngi->authreason = 0;
}
/* Clear all unknown flags. */
ngi->flags &= NF_ALLFLAGS;
/* Make sure language setting is in range. */
if ((ngi->language < 0 || ngi->language >= NUM_LANGS)
&& ngi->language != LANG_DEFAULT
) {
fprintf(stderr, "Invalid language set for nickgroup %u,"
" resetting to default.\n", ngi->id);
ngi->language = LANG_DEFAULT;
}
/* Make sure access, autojoin, and ignore counts are non-negative. */
if (ngi->access_count < 0) {
fprintf(stderr, "BUG: Access entry count for nickgroup %u is"
" negative! Clearing.\n", ngi->id);
ngi->access = NULL;
ngi->access_count = 0;
}
if (ngi->ajoin_count < 0) {
fprintf(stderr, "BUG: Autojoin entry count for nickgroup %u is"
" negative! Clearing.\n", ngi->id);
ngi->ajoin = NULL;
ngi->ajoin_count = 0;
}
if (ngi->ignore_count < 0) {
fprintf(stderr, "BUG: Ignore entry count for nickgroup %u is"
" negative! Clearing.\n", ngi->id);
ngi->ignore = NULL;
ngi->ignore_count = 0;
}
/* Make sure all access entries have non-empty user and host parts. */
ARRAY_FOREACH (i, ngi->access) {
if (!ngi->access[i]
|| !(s = strchr(ngi->access[i], '@'))
|| s == ngi->access[i]
|| !s[1]
) {
fprintf(stderr, "Access entry %d for nickgroup %u %s,"
" deleting.\n", i, ngi->id,
!ngi->access[i] ? "is empty"
: "isn't a valid user@host mask");
ARRAY_REMOVE(ngi->access, i);
i--;
}
}
/* Make sure memo count is non-negative. */
if (ngi->memos.memos_count < 0) {
fprintf(stderr, "BUG: Memo count for nickgroup %u is negative!"
" Clearing.\n", ngi->id);
ngi->memos.memos = NULL;
ngi->memos.memos_count = 0;
}
/* Clear unknown flags from memos. */
ARRAY_FOREACH (i, ngi->memos.memos)
ngi->memos.memos[i].flags &= MF_ALLFLAGS;
} /* for all nickgroups */
}
static void sanity_check_channels(void)
{
ChannelInfo *ci;
char *s;
int i;
for (ci = first_channelinfo(); ci; ci = next_channelinfo()) {
/* Forbidden channels should have no other data associated with
* them. */
if (ci->flags & CI_VERBOTEN) {
ci->founder = 0;
ci->successor = 0;
memset(ci->founderpass, 0, sizeof(ci->founderpass));
ci->desc = NULL;
ci->url = NULL;
ci->email = NULL;
ci->last_used = 0;
ci->last_topic = NULL;
memset(ci->last_topic_setter, 0, sizeof(ci->last_topic_setter));
ci->last_topic_time = 0;
ci->flags &= ~(CI_VERBOTEN);
ci->suspendinfo = NULL;
ci->levels = NULL;
ci->access = NULL;
ci->access_count = 0;
ci->akick = NULL;
ci->akick_count = 0;
ci->mlock_on = NULL;
ci->mlock_off = NULL;
ci->mlock_limit = 0;
ci->mlock_key = NULL;
ci->mlock_link = NULL;
ci->mlock_flood = NULL;
ci->mlock_joindelay = 0;
ci->mlock_joinrate1 = 0;
ci->mlock_joinrate2 = 0;
ci->entry_message = NULL;
ci->memos.memos = NULL;
ci->memos.memos_count = 0;
ci->memos.memomax = MEMOMAX_DEFAULT;
continue;
}
/* Channel is not forbidden. First make sure it has a (valid)
* founder. */
if (!ci->founder) {
fprintf(stderr, "BUG: Channel %s has no founder! Deleting.\n",
ci->name);
del_channelinfo(ci);
} else if (!get_nickgroupinfo(ci->founder)) {
fprintf(stderr, "BUG: Channel %s founder nickgroup %u is"
" missing! Deleting channel.\n", ci->name, ci->founder);
del_channelinfo(ci);
}
/* Make sure that the successor is valid if set. */
if (ci->successor && !get_nickgroupinfo(ci->successor)) {
fprintf(stderr, "BUG: Channel %s successor nickgroup %u is"
" missing! Clearing.", ci->name, ci->successor);
ci->successor = 0;
}
/* Make sure last-used time is not earlier than registered time.
* Allow zero, to accommodate cases where the channel was
* registered by an outside entity. */
if (ci->last_used && ci->last_used < ci->time_registered) {
fprintf(stderr, "Last-used time for channel %s is earlier than"
" registration time! Setting last-used time to"
" registration time.\n", ci->name);
ci->last_used = ci->time_registered;
}
/* If there is no saved topic, clear the topic setter and time. */
if (!ci->last_topic) {
memset(ci->last_topic_setter, 0, sizeof(ci->last_topic_setter));
ci->last_topic_time = 0;
}
/* Clear unknown flags. */
ci->flags &= CI_ALLFLAGS;
/* Check privilege level settings for vaility. */
if (ci->levels) {
for (i = 0; i < CA_SIZE; i++) {
if (ci->levels[i] < ACCLEV_INVALID
|| ci->levels[i] > ACCLEV_FOUNDER
) {
fprintf(stderr, "Privilege level %d on channel %s is"
"invalid; resetting all levels to defaults.\n",
i, ci->name);
ci->levels = NULL;
break;
}
}
}
/* Check access level nickgroups and levels for validity. */
ARRAY_FOREACH (i, ci->access) {
if (!ci->access[i].nickgroup)
continue;
if (!get_nickgroupinfo(ci->access[i].nickgroup)) {
fprintf(stderr, "BUG: Channel %s access entry %d has an"
" invalid nickgroup! Clearing.\n", ci->name, i);
ci->access[i].nickgroup = 0;
ci->access[i].level = 0;
} else if (ci->access[i].level <= ACCLEV_INVALID
|| ci->access[i].level >= ACCLEV_FOUNDER) {
fprintf(stderr, "BUG: Channel %s access entry %d has an"
"out-of-range level (%d)! Clearing.\n",
ci->name, i, ci->access[i].level);
ci->access[i].nickgroup = 0;
ci->access[i].level = 0;
}
}
/* Make sure all autokick entries have non-empty user and host
* parts. */
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
s = strchr(ci->akick[i].mask, '@');
if (!s || s==ci->akick[i].mask || !s[1]) {
fprintf(stderr, "Autokick entry %d for channel %s isn't a"
" valid user@host mask, deleting.\n", i, ci->name);
ci->akick[i].mask = NULL;
}
}
/* Make sure memo count is non-negative. */
if (ci->memos.memos_count < 0) {
fprintf(stderr, "BUG: Memo count for channel %s is negative!"
" Clearing.\n", ci->name);
ci->memos.memos = NULL;
ci->memos.memos_count = 0;
}
/* Clear unknown flags from memos. */
ARRAY_FOREACH (i, ci->memos.memos)
ci->memos.memos[i].flags &= MF_ALLFLAGS;
} /* for all channels */
/* Check that channel successors and access list entries have valid
* nickgroup IDs, and that access levels are in range */
for (ci = first_channelinfo(); ci; ci = next_channelinfo()) {
if (ci->flags & CI_VERBOTEN)
continue;
}
}
static void sanity_check_maskdata(void)
{
MaskData *md;
char *s;
int i;
/* Make sure all MaskData entries actually have masks allocated. */
for (i = 0; i < 256; i++) {
for (md = first_maskdata(i); md; md = next_maskdata(i)) {
if (!md->mask)
del_maskdata(i, md);
}
}
/* Make sure every autokill has a valid user@host mask and a reason. */
for (md = first_maskdata(MD_AKILL); md; md = next_maskdata(MD_AKILL)) {
s = strchr(md->mask, '@');
if (!s || s==md->mask || !s[1]) {
fprintf(stderr, "Autokill %s isn't a valid user@host mask,"
" deleting.\n", md->mask);
del_maskdata(MD_AKILL, md);
continue;
}
if (!md->reason)
md->reason = "Reason unknown";
}
}
/*************************************************************************/
/****************************** Main program *****************************/
/*************************************************************************/
void usage(const char *progname)
{
int i;
fprintf(stderr, "Usage: %s [-v] [+program-name] [options...] sourcedir\n"
"The following program names are known:\n", progname);
for (i = 0; dbtypes[i]; i++)
fprintf(stderr, " %s\n", dbtypes[i]->id);
fprintf(stderr,
"See the manual for options available for each database type.\n");
exit(1);
}
/*************************************************************************/
int main(int ac, char **av)
{
int newac = 0; /* For passing to processing function */
char **newav;
char *sourcedir = NULL; /* Source data file directory */
int verbose = 0; /* Verbose output? */
void (*load)(const char *, int, int, char **) = NULL;
int i;
char oldpath[PATH_MAX+1], newpath[PATH_MAX+1];
#ifdef CLEAN_COMPILE
free_nickinfo(NULL);
free_nickgroupinfo(NULL);
free_channelinfo(NULL);
#endif
/* Parse command-line parameters */
newac = 1;
newav = malloc(sizeof(*newav) * ac);
newav[0] = av[0];
for (i = 1; i < ac; i++) {
if (strcmp(av[i],"-v") == 0) {
verbose++;
} else if (strcmp(av[i],"-h") == 0
|| strcmp(av[i],"-?") == 0
|| strcmp(av[i],"-help") == 0
|| strcmp(av[i],"--help") == 0) {
usage(av[0]);
} else if (av[i][0] == '+') {
int j;
for (j = 0; dbtypes[j]; j++) {
if (stricmp(av[i]+1, dbtypes[j]->id) == 0) {
load = dbtypes[j]->load;
break;
}
}
if (!load) {
fprintf(stderr, "Unknown database type `%s'\n", av[i]+1);
usage(av[0]);
}
} else if (av[i][0] == '-') {
newav[newac++] = av[i];
} else {
if (sourcedir) {
fprintf(stderr, "Only one source directory may be specified\n");
usage(av[0]);
}
sourcedir = av[i];
}
}
/* Make sure that we have a source directory and that it's valid */
if (!sourcedir) {
fprintf(stderr, "Directory name must be specified\n");
usage(av[0]);
}
if (access(sourcedir, R_OK) < 0) {
perror(sourcedir);
exit(1);
}
/* If the source directory name is relative, make it absolute */
if (*sourcedir != '/') {
if (!getcwd(oldpath, sizeof(oldpath))) {
perror("Unable to read current directory name");
fprintf(stderr, "Try using an absolute pathname for the source directory.\n");
return 1;
}
if (strlen(oldpath) + 1 + strlen(sourcedir) + 1 > sizeof(newpath)) {
fprintf(stderr, "Source directory pathname too long\n");
return 1;
}
sprintf(newpath, "%s/%s", oldpath, sourcedir);
sourcedir = newpath;
}
/* If we weren't given a database type, try to figure one out */
if (!load) {
for (i = 0; dbtypes[i]; i++) {
const char *s;
if ((s = dbtypes[i]->check(sourcedir)) != NULL) {
fprintf(stderr, "Found %s databases\n", s);
load = dbtypes[i]->load;
break;
}
}
if (!load) {
fprintf(stderr, "Can't determine database type; use +name option\n");
usage(av[0]);
}
}
/* Actually load the databases. If an error occurs, load() will abort the
* program for us */
load(sourcedir, verbose, newac, newav);
if (verbose)
fprintf(stderr, "Data files successfully loaded.\n");
/* Do sanity checks. */
if (verbose)
fprintf(stderr, "Checking data integrity...\n");
sanity_checks();
/* Write the database to standard output in XML format */
if (verbose)
fprintf(stderr, "Writing converted data...\n");
xml_export((xml_writefunc_t)fprintf, stdout);
/* Success */
if (verbose)
fprintf(stderr, "Done.\n");
return 0;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1