/* $Id: sqpack.c,v 1.51.2.12 2003/05/30 08:37:47 sfpavel Exp $ */
/*****************************************************************************
* SqPack --- FTN messagebase packer (purger)
*****************************************************************************
* Copyright (C) 1997-1999 Matthias Tichy (mtt@tichy.de).
* Copyright (C) 1999-2002 Husky developers team
*
* This file is part of HUSKY Fidonet Software project.
*
* SQPACK is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* SQPACK is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HPT; see the file COPYING. If not, write to the Free
* Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************************
*/
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#ifdef UNIX
#include <unistd.h>
#else
#include <io.h>
#endif
#if defined( __TURBOC__ ) || defined(__EMX__) || defined(MSDOS) || defined(__DOS__) || defined ( __WATCOMC__ ) || defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <share.h>
#endif
#ifdef _MAKE_DLL_MVC_
#define SH_DENYNO _SH_DENYNO
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <smapi/msgapi.h>
#include <smapi/prog.h>
#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#define PROGRAM_NAME "sqpack v1.2.4-release"
unsigned long msgCopied, msgProcessed; // per Area
unsigned long totaloldMsg, totalmsgCopied;
void SqReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
HAREA area)
{
int fd;
struct stat st;
unsigned long i, temp;
unsigned char buffer[4];
char *name=NULL;
xstrscat( &name, fileName, ".sql", NULL );
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
fstat(fd, &st);
*lcountp = st.st_size / 4;
*lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32));
for (i = 0; i < *lcountp; i++) {
read(fd, &buffer, 4);
temp = buffer[0] + (((unsigned long)(buffer[1])) << 8) +
(((unsigned long)(buffer[2])) << 16) +
(((unsigned long)(buffer[3])) << 24);
(*lastreadp)[i] = MsgUidToMsgn(area, temp, UID_PREV);
}
close(fd);
} else {
*lastreadp = NULL;
*lcountp = 0;
};
free(name);
}
void SqWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
HAREA area)
{
char *name=NULL;
unsigned char buffer[4];
int fd;
unsigned long i, temp;
if (lastread) {
xstrscat( &name, fileName, ".sql", NULL );
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
lseek(fd, 0l, SEEK_SET);
for (i = 0; i < lcount; i++) {
temp = MsgMsgnToUid(area, lastread[i]);
buffer[0] = temp & 0xFF;
buffer[1] = (temp >> 8) & 0xFF;
buffer[2] = (temp >> 16) & 0xFF;
buffer[3] = (temp >> 24) & 0xFF;
write(fd, &buffer, 4);
}
close(fd);
} else printf("Could not write lastread file %s, error %u!\n", name, errno);
free(name);
}
}
/*
* get_dword
*
* Reads in a 4 byte word that is stored in little endian (Intel) notation
* and converts it to the local representation n an architecture-
* independent manner
*/
#define get_dword(ptr) \
((dword)((unsigned char)(ptr)[0]) | \
(((dword)((unsigned char)(ptr)[1])) << 8) | \
(((dword)((unsigned char)(ptr)[2])) << 16) | \
(((dword)((unsigned char)(ptr)[3])) << 24)) \
/*
* get_word
*
* Reads in a 2 byte word that is stored in little endian (Intel) notation
* and converts it to the local representation in an architecture-
* independent manner
*/
#define get_word(ptr) \
((word)((unsigned char)(ptr)[0]) | \
(((word)((unsigned char)(ptr)[1])) << 8 ))
typedef struct
{
unsigned long UserCRC; /* CRC-32 of user name (lowercase) */
unsigned long UserID; /* Unique UserID */
unsigned long LastReadMsg; /* Last read message number */
unsigned long HighReadMsg; /* Highest read message number */
}
JAMLREAD;
#define JAMLREAD_SIZE 16
int read_jamlread(int fd, JAMLREAD *plread)
{
unsigned char buf[JAMLREAD_SIZE];
if (read(fd, buf, JAMLREAD_SIZE) != JAMLREAD_SIZE)
return 0;
plread->UserCRC = get_dword(buf);
plread->UserID = get_dword(buf+4);
plread->LastReadMsg = get_dword(buf+8);
plread->HighReadMsg = get_dword(buf+12);
return 1;
}
int write_jamlread(int fd, JAMLREAD *plread)
{
unsigned char buf[JAMLREAD_SIZE];
put_dword(buf, plread->UserCRC);
put_dword(buf + 4, plread->UserID);
put_dword(buf + 8, plread->LastReadMsg);
put_dword(buf + 12, plread->HighReadMsg);
if (write(fd, buf, JAMLREAD_SIZE) != JAMLREAD_SIZE)
return 0;
return 1;
}
int write_partial_jamlread(int fd, JAMLREAD *plread)
{
unsigned char buf[JAMLREAD_SIZE/2];
put_dword(buf + 0, plread->LastReadMsg);
put_dword(buf + 4, plread->HighReadMsg);
if (write(fd, buf, JAMLREAD_SIZE/2) != JAMLREAD_SIZE/2)
return 0;
return 1;
}
void JamReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
HAREA area)
{
int fd;
struct stat st;
unsigned long i;
char *name = NULL;
JAMLREAD lread;
xstrscat( &name, fileName, ".jlr", NULL );
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
fstat(fd, &st);
*lcountp = st.st_size / JAMLREAD_SIZE;
*lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32) * 2);
for (i = 0; i < *lcountp; i++) {
read_jamlread(fd, &lread);
(*lastreadp)[i*2] = MsgUidToMsgn(area, lread.LastReadMsg, UID_PREV);
(*lastreadp)[i*2+1] = MsgUidToMsgn(area, lread.HighReadMsg, UID_PREV);
}
close(fd);
} else {
*lastreadp = NULL;
*lcountp = 0;
};
*lcountp = (*lcountp) << 1; /* rest of sqpack does not now of 2 lastread ptrs */
free(name);
}
void JamWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
HAREA area)
{
char *name = NULL;
int fd;
unsigned long i;
JAMLREAD lread;
if (lastread) {
xstrscat( &name, fileName, ".jlr", NULL );
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
for (i = 0; i < (lcount >> 1); i++) {
lread.LastReadMsg = MsgMsgnToUid(area, lastread[i*2]);
lread.HighReadMsg = MsgMsgnToUid(area, lastread[i*2+1]);
lseek(fd, i*JAMLREAD_SIZE + JAMLREAD_SIZE/2, SEEK_SET);
write_partial_jamlread(fd, &lread);
}
close(fd);
} else printf("Could not write lastread file %s, error %u!\n", name, errno);
free(name);
}
}
void SdmReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
HAREA area)
{
int fd;
struct stat st;
unsigned long i;
char *name;
UINT16 temp;
name = (char *) malloc(strlen(fileName)+9+1);
strcpy(name, fileName);
Add_Trailing(name, PATH_DELIM);
strcat(name, "lastread");
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
fstat(fd, &st);
*lcountp = st.st_size / 2; /*sizeof(UINT16)*/
*lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32));
for (i = 0; i < *lcountp; i++) {
read(fd, &temp, 2);
(*lastreadp)[i] = MsgUidToMsgn(area, temp, UID_PREV);
}
close(fd);
} else {
*lastreadp = NULL;
*lcountp = 0;
};
free(name);
}
void SdmWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
HAREA area)
{
char *name;
int fd;
unsigned long i;
unsigned char buf[2];
if (lastread) {
name = (char *) malloc(strlen(fileName)+9+1);
strcpy(name, fileName);
Add_Trailing(name, PATH_DELIM);
strcat(name, "lastread");
fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
if (fd != -1) {
lseek(fd, 0, SEEK_SET);
for (i = 0; i < lcount; i++) {
#if 0 /* Messages renumbered, MsgMsgnToUid() returns old value */
/* We should reopen area before write lastreads */
UINT16 temp = (UINT16)MsgMsgnToUid(area, lastread[i]);
put_word(buf, temp);
#else /* msgns are equial to uids after renumber */
put_word(buf, (UINT16)lastread[i]);
#endif
write(fd, buf, 2);
}
close(fd);
} else printf("Could not write lastread file %s, error %u!\n", name, errno);
free(name);
}
}
void readLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
HAREA area, int areaType)
{
if (areaType == MSGTYPE_SQUISH)
SqReadLastreadFile(fileName, lastreadp, lcountp, area);
else if (areaType == MSGTYPE_JAM)
JamReadLastreadFile(fileName, lastreadp, lcountp, area);
else if (areaType == MSGTYPE_SDM)
SdmReadLastreadFile(fileName, lastreadp, lcountp, area);
}
void writeLastreadFile(char *fileName, UINT32 *lastreadp, ULONG lcount,
HAREA area, int areaType)
{
if (areaType == MSGTYPE_SQUISH)
SqWriteLastreadFile(fileName, lastreadp, lcount, area);
else if (areaType == MSGTYPE_JAM)
JamWriteLastreadFile(fileName, lastreadp, lcount, area);
else if (areaType == MSGTYPE_SDM)
SdmWriteLastreadFile(fileName, lastreadp, lcount, area);
}
unsigned long getOffsetInLastread(UINT32 *lastread, ULONG lcount, dword msgnum)
{
unsigned long i;
for (i = 0; i < lcount; i++) {
if (lastread[i] == msgnum) return i;
}
return (-1);
}
/* returns zero if msg was killed, nonzero if it was copied */
int processMsg(dword msgNum, dword numMsg, HAREA oldArea, HAREA newArea,
s_area *area, UINT32 shift)
{
HMSG msg, newMsg;
XMSG xmsg;
struct tm tmTime;
time_t ttime, actualTime = time(NULL);
char *text, *ctrlText;
dword textLen, ctrlLen;
int unsent, i, rc = 0;
// unsigned long offset;
msg = MsgOpenMsg(oldArea, MOPEN_RW, msgNum);
if (msg == NULL) return rc;
if (MsgReadMsg(msg, &xmsg, 0, 0, NULL, 0, NULL)==(dword)-1L) {
MsgCloseMsg(msg);
msgProcessed++;
return rc;
}
unsent = ((xmsg.attr & MSGLOCAL) && !(xmsg.attr & MSGSENT)) || (xmsg.attr & MSGLOCKED);
if (unsent || (((area -> max == 0) || ((numMsg - msgProcessed + msgCopied) <= area -> max) ||
(area -> keepUnread && !(xmsg.attr & MSGREAD))) && !((xmsg.attr & MSGREAD) && area -> killRead))) {
//only max msgs should be in new area
if (xmsg.attr & MSGLOCAL) {
DosDate_to_TmDate((SCOMBO*)&(xmsg.date_written), &tmTime);
} else {
DosDate_to_TmDate((SCOMBO*)&(xmsg.date_arrived), &tmTime);
}
/* DosDate_to_TmDate(&(xmsg.attr & MSGLOCAL ? xmsg.date_written :
xmsg.date_arrived), &tmTime);*/
ttime = mktime(&tmTime);
if (ttime == 0xfffffffflu) ttime = 0; /* emx */
if (unsent || (area -> purge == 0) || ttime == 0 ||
(abs(actualTime - ttime) <= (area -> purge * 24 *60 * 60))) {
xmsg.replyto = MsgUidToMsgn(oldArea, xmsg.replyto, UID_EXACT) > shift ? MsgUidToMsgn(oldArea, xmsg.replyto, UID_EXACT) - shift : 0;
if ((area->msgbType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH){
for (i = 0; i < MAX_REPLY; i++)
xmsg.replies[i] = MsgUidToMsgn(oldArea, xmsg.replies[i], UID_EXACT) > shift ? MsgUidToMsgn(oldArea, xmsg.replies[i], UID_EXACT) - shift : 0;
}else {
xmsg.replies[0] = MsgUidToMsgn(oldArea, xmsg.replies[0], UID_EXACT) > shift ? MsgUidToMsgn(oldArea, xmsg.replies[0], UID_EXACT) - shift : 0;
xmsg.xmreplynext = MsgUidToMsgn(oldArea, xmsg.xmreplynext, UID_EXACT) > shift ? MsgUidToMsgn(oldArea, xmsg.xmreplynext, UID_EXACT) - shift : 0;
}
// copy msg
textLen = MsgGetTextLen(msg);
ctrlLen = MsgGetCtrlLen(msg);
text = (char *) malloc(textLen+1);
text[textLen] = '\0';
ctrlText = (char *) malloc(ctrlLen+1);
ctrlText[ctrlLen] = '\0';
MsgReadMsg(msg, NULL, 0, textLen, (byte*)text, ctrlLen, (byte*)ctrlText);
if (area->msgbType & MSGTYPE_SDM)
MsgWriteMsg(msg, 0, &xmsg, (byte*)text, textLen, textLen, ctrlLen, (byte*)ctrlText);
else {
newMsg = MsgOpenMsg(newArea, MOPEN_CREATE, 0);
MsgWriteMsg(newMsg, 0, &xmsg, (byte*)text, textLen, textLen, ctrlLen, (byte*)ctrlText);
MsgCloseMsg(newMsg);
}
msgCopied++;
free(text);
free(ctrlText);
rc = 1;
}
}
MsgCloseMsg(msg);
msgProcessed++;
return rc;
}
UINT32 getShiftedNum(UINT32 msgNum, UINT32 rmCount, UINT32 *rmMap)
{
UINT32 i, nMsgNum = msgNum;
msgNum += rmMap[1];
for (i = 0; i < rmCount; i+=2)
if (msgNum >= rmMap[i])
nMsgNum -= rmMap[i + 1];
else
break;
return msgNum > 0L ? msgNum : 0L;
}
void updateMsgLinks(UINT32 msgNum, HAREA area, UINT32 rmCount, UINT32 *rmMap, int areaType)
{
HMSG msg;
XMSG xmsg;
int i;
msg = MsgOpenMsg(area, MOPEN_RW, getShiftedNum(msgNum, rmCount, rmMap));
if (msg == NULL) return;
MsgReadMsg(msg, &xmsg, 0, 0, NULL, 0, NULL);
xmsg.replyto = getShiftedNum(xmsg.replyto, rmCount, rmMap);
if ((areaType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH)
for (i = 0; i < MAX_REPLY; i++)
xmsg.replies[i] = getShiftedNum(xmsg.replies[i], rmCount, rmMap);
else {
xmsg.replies[0] = getShiftedNum(xmsg.replies[0], rmCount, rmMap);
xmsg.xmreplynext = getShiftedNum(xmsg.xmreplynext, rmCount, rmMap);
}
MsgWriteMsg(msg, 0, &xmsg, NULL, 0, 0, 0, NULL);
MsgCloseMsg(msg);
}
void renameArea(int areaType, char *oldName, char *newName)
{
char *oldTmp=NULL, *newTmp=NULL;
xstrcat(&oldTmp, oldName);
xstrcat(&newTmp, newName);
if (areaType==MSGTYPE_SQUISH) {
xstrcat(&oldTmp, ".sqd");
xstrcat(&newTmp, ".sqd");
remove(oldTmp);
rename(newTmp, oldTmp);
oldTmp[strlen(oldTmp)-1] = 'i';
newTmp[strlen(newTmp)-1] = 'i';
remove(oldTmp);
rename(newTmp, oldTmp);
}
if (areaType==MSGTYPE_JAM) {
xstrcat(&oldTmp, ".jdt");
xstrcat(&newTmp, ".jdt");
remove(oldTmp);
rename(newTmp, oldTmp);
oldTmp[strlen(oldTmp)-1] = 'x';
newTmp[strlen(newTmp)-1] = 'x';
remove(oldTmp);
rename(newTmp, oldTmp);
oldTmp[strlen(oldTmp)-2] = 'h';
newTmp[strlen(newTmp)-2] = 'h';
oldTmp[strlen(oldTmp)-1] = 'r';
newTmp[strlen(newTmp)-1] = 'r';
remove(oldTmp);
rename(newTmp, oldTmp);
newTmp[strlen(newTmp)-2] = 'l';
#if 0
oldTmp[strlen(oldTmp)-2] = 'l';
remove(oldTmp);
rename(newTmp, oldTmp);
#endif
remove(newTmp); // erase new lastread file
}
free(oldTmp);
free(newTmp);
}
void purgeArea(s_area *area)
{
char *oldName = area -> fileName;
char *newName=NULL;
HAREA oldArea=NULL, newArea = NULL;
dword highMsg, i, j, numMsg, hw=0;
int areaType = area -> msgbType & (MSGTYPE_JAM | MSGTYPE_SQUISH | MSGTYPE_SDM);
UINT32 *oldLastread, *newLastread = NULL;
UINT32 *removeMap;
UINT32 rmIndex = 0;
if (area->nopack) {
printf(" No purging needed!\n");
return;
}
//generated tmp-FileName
xstrscat(&newName, oldName, "_tmp", NULL);
/*oldArea = MsgOpenArea((byte *) oldName, MSGAREA_NORMAL, -1, -1, -1, MSGTYPE_SQUISH);*/
oldArea = MsgOpenArea((byte *) oldName, MSGAREA_NORMAL, (word) areaType);
/*if (oldArea) newArea = MsgOpenArea((byte *) newName, MSGAREA_CREATE, area.fperm, area.uid, area.gid,MSGTYPE_SQUISH);*/
if (oldArea) {
if (areaType == MSGTYPE_SDM)
newArea = oldArea;
else
newArea = MsgOpenArea((byte *) newName, MSGAREA_CREATE, (word) areaType);
}
if ((oldArea != NULL) && (newArea != NULL)) {
ULONG lcount;
highMsg = MsgGetHighMsg(oldArea);
numMsg = MsgGetNumMsg(oldArea);
if (areaType != MSGTYPE_SDM) hw = MsgGetHighWater(oldArea);
readLastreadFile(oldName, &oldLastread, &lcount, oldArea, areaType);
if (oldLastread) {
newLastread = (UINT32 *) malloc(lcount * sizeof(UINT32));
memcpy(newLastread, oldLastread, lcount * sizeof(UINT32));
}
removeMap = (UINT32 *) calloc(2, sizeof(UINT32));
for (i = j = 1; i <= highMsg; i++, j++) {
if (!processMsg(j, numMsg, oldArea, newArea, area,
removeMap[1])) {
if (!(rmIndex & 1)) {
/* We started to delete new portion of */
removeMap = (UINT32 *) realloc(removeMap, (rmIndex + 2) * sizeof(UINT32));
removeMap[rmIndex++] = i;
removeMap[rmIndex] = 0;
};
removeMap[rmIndex]++; /* Anyway, update counter */
if (areaType == MSGTYPE_SDM)
MsgKillMsg(oldArea, j--);
} else {
/* We are copying msgs */
if (rmIndex & 1) rmIndex++;
};
};
if (rmIndex && areaType == MSGTYPE_SDM) {
/* renumber the area */
char oldmsgname[PATHLEN], newmsgname[PATHLEN];
for (i = j = 1; i <= highMsg; i++) {
strncpy(oldmsgname, oldName, PATHLEN);
Add_Trailing(oldmsgname, PATH_DELIM);
strncpy(newmsgname, oldmsgname, PATHLEN);
sprintf(oldmsgname+strlen(oldmsgname), "%u.msg", (unsigned int)i);
sprintf(newmsgname+strlen(newmsgname), "%u.msg", (unsigned int)j);
if (access(oldmsgname, 0))
continue;
if (i == j) {
j++;
continue;
}
if (rename(oldmsgname, newmsgname) == 0)
j++;
}
}
if (rmIndex > 2) { /* there were several areas with deleted msgs */
for (j = 1; j <= highMsg; j++)
updateMsgLinks(i, newArea, rmIndex + 1, removeMap, areaType);
}
if (rmIndex) { /* someting was removed, maybe need to update lastreadfile */
for (j = 0; j < lcount; j++) {
for (i=0; i<rmIndex; i+=2) {
if (oldLastread[j] >= removeMap[i]) {
if (oldLastread[j] >= removeMap[i] + removeMap[i+1]) {
newLastread[j] -= removeMap[i+1];
} else {
newLastread[j] -= oldLastread[j] - removeMap[i] + 1;
}
}
}
}
}
writeLastreadFile(oldName, newLastread, lcount, newArea, areaType);
MsgCloseArea(oldArea);
if (areaType != MSGTYPE_SDM) {
if ((numMsg - msgCopied) > hw) hw=0;
else hw -= (numMsg - msgCopied);
MsgSetHighWater(newArea, hw);
MsgCloseArea(newArea);
}
printf(" oldMsg: %lu newMsg: %lu\n", (unsigned long)numMsg, msgCopied);
totaloldMsg+=numMsg; totalmsgCopied+=msgCopied; // total
free(oldLastread);
free(newLastread);
//rename oldArea to newArea
renameArea(areaType, oldName, newName);
}
else {
if (oldArea) MsgCloseArea(oldArea);
printf("Could not open %s or create %s.\n", oldName, newName);
}
free(newName);
}
void handleArea(s_area *area)
{
if ((area -> msgbType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH ||
(area -> msgbType & MSGTYPE_JAM) == MSGTYPE_JAM ||
(area -> msgbType & MSGTYPE_SDM) == MSGTYPE_SDM) {
printf("%s\n", area -> areaName);
msgCopied = 0;
msgProcessed = 0;
purgeArea(area);
};
}
void doArea(s_area *area, char *cmp)
{
if (patimat(area->areaName,cmp)) handleArea(area);
}
int main(int argc, char **argv) {
s_fidoconfig *cfg;
unsigned int i;
struct _minf m;
printf( PROGRAM_NAME "\n");
if (argc!=2) {
if (argc>2) printf("too many arguments!\n");
printf ("usage: sqpack <areamask>\n");
} else {
setvar("module", "sqpack");
cfg = readConfig(NULL);
if (cfg != NULL ) {
m.req_version = 0;
m.def_zone = cfg->addr[0].zone;
if (MsgOpenApi(&m)!= 0) {
printf("MsgOpenApi Error.\n");
exit(1);
}
// purge dupe area
doArea(&(cfg->dupeArea), argv[1]);
// purge bad area
doArea(&(cfg->badArea), argv[1]);
for (i=0; i < cfg->netMailAreaCount; i++)
// purge netmail areas
doArea(&(cfg->netMailAreas[i]), argv[1]);
for (i=0; i < cfg->echoAreaCount; i++)
// purge echomail areas
doArea(&(cfg->echoAreas[i]), argv[1]);
for (i=0; i < cfg->localAreaCount; i++)
// purge local areas
doArea(&(cfg->localAreas[i]), argv[1]);
disposeConfig(cfg);
printf("\ntotal oldMsg: %lu total newMsg: %lu\n",
(unsigned long)totaloldMsg, (unsigned long)totalmsgCopied);
return 0;
} else {
printf("Could not read fido config\n");
return 1;
}
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1