// FileDirDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "lib_acl.h"  // 注意：lib_acl.h 必须放在 stdafx.h 的后面，否则编译会出错:(
#include "stdlib/avl.h"
#include "FileDir.h"
#include "FileDirDlg.h"
#include "ScanDir.h"
#include ".\filedirdlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#ifdef WIN32
#define snprintf _snprintf
#endif

#include <direct.h>
#define __S_ISTYPE(mode, mask)  (((mode) & _S_IFMT) == (mask))
#ifndef	S_ISDIR
# define S_ISDIR(mode)    __S_ISTYPE((mode), _S_IFDIR)
#endif

typedef struct MY_TYPE
{
	char  name[256];
	char  value[256];
	avl_node_t  node;
} MY_TYPE;


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// 对话框数据
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CFileDirDlg 对话框

CFileDirDlg::CFileDirDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CFileDirDlg::IDD, pParent)
	, m_dirPath(_T(""))
	, m_nested(TRUE)
	, m_pScan(NULL)
	, m_avlName(_T(""))
	, m_avlValue(_T(""))
	, m_outName(FALSE)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	avl_create(&m_avlTree, compare_fn, sizeof(MY_TYPE), offsetof(MY_TYPE, node));
}

void CFileDirDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_PATH, m_dirPath);
	DDX_Check(pDX, IDC_CHECK_NETSTED, m_nested);
	DDX_Text(pDX, IDC_EDIT_AVL_NAME, m_avlName);
	DDX_Control(pDX, IDC_EDIT_AV_RESULT, m_debugWin);
	DDX_Text(pDX, IDC_EDIT_AVL_VALUE, m_avlValue);
	DDX_Check(pDX, IDC_CHECK_OUT_NAME, m_outName);
}

BEGIN_MESSAGE_MAP(CFileDirDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_TIMER()
	ON_EN_CHANGE(IDC_EDIT_PATH, OnEnChangeEditPath)
	ON_BN_CLICKED(IDC_CHECK_NETSTED, OnBnClickedCheckNetsted)
	ON_BN_CLICKED(IDC_BUTTON_SCAN, OnBnClickedButtonScan)
	ON_BN_CLICKED(IDC_BUTTON_DELETE, OnBnClickedButtonDelete)
	ON_BN_CLICKED(IDC_BUTTON_AVL_ADD, OnBnClickedButtonAvlAdd)
	ON_BN_CLICKED(IDC_BUTTON_AVL_FIND, OnBnClickedButtonAvlFind)
	ON_BN_CLICKED(IDC_BUTTON_WALK, OnBnClickedButtonWalk)
	ON_EN_CHANGE(IDC_EDIT_AVL_NAME, OnEnChangeEditAvlName)
	ON_BN_CLICKED(IDC_BUTTON_TEST, OnBnClickedButtonTest)
	ON_EN_CHANGE(IDC_EDIT_AVL_VALUE, OnEnChangeEditAvlValue)
	ON_BN_CLICKED(IDC_BUTTON_AVL_ADD_BAT, OnBnClickedButtonAvlAddBat)
	ON_BN_CLICKED(IDC_BUTTON_AVL_DEL, OnBnClickedButtonAvlDel)
	ON_BN_CLICKED(IDC_BUTTON_CLEAR, OnBnClickedButtonClear)
	ON_BN_CLICKED(IDC_CHECK_OUT_NAME, OnBnClickedCheckOutName)
	ON_BN_CLICKED(IDC_SVN_UPDATE, OnBnClickedSvnUpdate)
END_MESSAGE_MAP()


// CFileDirDlg 消息处理程序

BOOL CFileDirDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 将\“关于...\”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	CheckDlgButton(IDC_CHECK_NETSTED, BST_CHECKED);
	m_debugWin.SetLimitText(81920);
	acl_msg_open("svn.log", "svn_update");

	return TRUE;  // 除非设置了控件的焦点，否则返回 TRUE
}

void CFileDirDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CFileDirDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标显示。
HCURSOR CFileDirDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

int CFileDirDlg::GetDirPath(void)
{
	CString msg;

	if (m_dirPath.GetLength() == 0) {
		MessageBox(_T("Please input the dir path ..."), _T("Error"));
		return (-1);
	}

	struct stat sbuf;
	if (stat(m_dirPath.GetString(), &sbuf) < 0) {
		msg.Format("%s no exist!", m_dirPath.GetString());	
		MessageBox(msg, "Error");
		return (-1);
	} else if (!S_ISDIR(sbuf.st_mode)) {
		msg.Format("%s is not a directory!", m_dirPath.GetString());
		MessageBox(msg, "Error");
		return (-1);
	}

	return 0;
}

void CFileDirDlg::OnTimer(UINT nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CDialog::OnTimer(nIDEvent);
	ScanDir();
}

void CFileDirDlg::OnEnChangeEditPath()
{
	// TODO:  如果该控件是 RICHEDIT 控件，则它将不会
	// 发送该通知，除非重写 CDialog::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask()，
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
	UpdateData(TRUE);
}

int CFileDirDlg::ScanCallback(ACL_SCAN_DIR * scan, void * ctx)
{
	CFileDirDlg *pfdd = (CFileDirDlg *) ctx;
	const char *ptr;

	if (pfdd->m_outName) {
		ptr = acl_scan_dir_file(scan);
		if (ptr && *ptr) {
			CString msg;
			struct acl_stat sbuf;

			acl_scan_stat(scan, &sbuf);
			msg.Format(">>>file(%s), size=%I64d\r\n", ptr,
				(acl_int64) sbuf.st_size);
			pfdd->DebugWinAppend(msg);
		}
	}

	if ((acl_scan_dir_nfiles(pfdd->m_pScan)
		+ acl_scan_dir_ndirs(pfdd->m_pScan)) % 100 == 0) {
		pfdd->UpdateInfo();
		pfdd->SetTimer(TIMER_EVENT_USER_100, 10, NULL);
		return (-1);  // 暂时停止扫描
	}

	return 0;
}

void CFileDirDlg::ScanDir(void)
{
	if (acl_scan_dir_end(m_pScan)) {
		ScanClose();
		return;
	}

	if (acl_scan_dir_size2(m_pScan) < 0) {
		ScanClose();
		return;
	}
	UpdateInfo();
}

// 更新显示信息
void CFileDirDlg::UpdateInfo(void)
{
	if (m_pScan == NULL)
		return;

	CString msg;

	msg.Format(_T("%I64u MB"), acl_scan_dir_nsize(m_pScan) / MB);
	GetDlgItem(IDC_EDIT_SIZE)->SetWindowText(msg);

	msg.Format(_T("%d 个"), acl_scan_dir_nfiles(m_pScan));
	GetDlgItem(IDC_EDIT_NFILE)->SetWindowText(msg);

	msg.Format(_T("%d 个"), acl_scan_dir_ndirs(m_pScan));
	GetDlgItem(IDC_EDIT_NDIR)->SetWindowText(msg);
}

void CFileDirDlg::ScanClose(void)
{
	KillTimer(TIMER_EVENT_USER_100);
	UpdateInfo();
	acl_scan_dir_close(m_pScan);  // 关闭扫描目录对象
	m_pScan = NULL;
}

void CFileDirDlg::OnBnClickedCheckNetsted()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
}

void CFileDirDlg::OnBnClickedButtonScan()
{
	// TODO: 在此添加控件通知处理程序代码
	CString msg;

	if (GetDirPath() < 0)
		return;

	m_pScan = acl_scan_dir_open(m_dirPath.GetString(), m_nested);
	if (m_pScan == NULL) {
		msg.Format("open dir %s error", m_dirPath.GetString());
		MessageBox(msg, "Error");
		return;
	}

	// 设置回调函数及回调参数
	acl_scan_dir_ctl(m_pScan, ACL_SCAN_CTL_FN, this->ScanCallback,
		ACL_SCAN_CTL_CTX, this, ACL_SCAN_CTL_END);

	ScanDir();
}

void CFileDirDlg::OnBnClickedButtonDelete()
{
	// TODO: 在此添加控件通知处理程序代码
	CString msg;

	if (GetDirPath() < 0)
		return;

	msg.Format("Are you sure to delete %s ?", m_dirPath.GetString());
	if (MessageBox(msg, "Warning", MB_YESNO) != IDYES)
		return;

	CScanDir scan(m_dirPath.GetString(), m_nested);

	if (scan.BeginRemove() < 0) {
		msg.Format("%s remove error", m_dirPath.GetString());
		MessageBox(msg, "Error");
		return;
	}

	__int64 size = scan.TotalSize();

	msg.Format(_T("%I64u MB"), size / MB);
	GetDlgItem(IDC_EDIT_SIZE)->SetWindowText(msg);

	int  n = scan.FileCount();
	msg.Format(_T("%d 个"), n);
	GetDlgItem(IDC_EDIT_NFILE)->SetWindowText(msg);

	n = scan.DirCount();
	msg.Format(_T("%d 个"), n);
	GetDlgItem(IDC_EDIT_NDIR)->SetWindowText(msg);
}

void CFileDirDlg::OnBnClickedButtonAvlAdd()
{
	// TODO: 在此添加控件通知处理程序代码
	MY_TYPE *pm, m;

	if (m_avlName.GetLength() == 0) {
		MessageBox("Please input searching name!", "error");
		GetDlgItem(IDC_EDIT_AVL_NAME)->SetFocus();
		return;
	}
	if (m_avlValue.GetLength() == 0) {
		MessageBox("Please input value!", "error");
		GetDlgItem(IDC_EDIT_AVL_VALUE)->SetFocus();
		return;
	}

	snprintf(m.name, sizeof(m.name), "%s", m_avlName.GetString());
	pm = (MY_TYPE*) avl_find(&m_avlTree, &m, NULL);
	if (pm != NULL) {
		CString msg;

		msg.Format("key(%s) already exist, which value is %s",
			m_avlName.GetString(), pm->value);
		MessageBox(msg, "error");
		return;
	}

	pm = (MY_TYPE*) acl_mycalloc(1, sizeof(MY_TYPE));
	snprintf(pm->name, sizeof(pm->name), "%s", m_avlName.GetString());
	snprintf(pm->value, sizeof(pm->value), "%s", m_avlValue.GetString());
	avl_add(&m_avlTree, pm);
	MessageBox("Add ok!", "ok");
	// m_avlName.Empty();
	m_avlValue.Empty();
	UpdateData(FALSE);
}

void CFileDirDlg::OnBnClickedButtonAvlFind()
{
	// TODO: 在此添加控件通知处理程序代码
	MY_TYPE m, *ptr;
	CString msg;

	if (m_avlName.GetLength() == 0) {
		MessageBox("Please input searching name", "error");
		GetDlgItem(IDC_EDIT_AVL_NAME)->SetFocus();
		return;
	}

	snprintf(m.name, sizeof(m.name), m_avlName.GetString());

	ptr = (MY_TYPE*) avl_find(&m_avlTree, &m, NULL);
	if (ptr) {
		m_avlValue.Format("%s", ptr->value);
	} else {
		m_avlValue.Empty();
		msg.Format("not find it, name=%s", m.name);
		MessageBox(msg);
	}

	UpdateData(FALSE);	
}

int CFileDirDlg::compare_fn(const void* v1, const void* v2)
{
	MY_TYPE *m1 = (MY_TYPE*) v1, *m2 = (MY_TYPE*) v2;

	return (strcmp(m1->name, m2->name));
}

void CFileDirDlg::OnBnClickedButtonWalk()
{
	// TODO: 在此添加控件通知处理程序代码
	MY_TYPE *next;
	CString msg;
	int   n = 0;

	next = (MY_TYPE*) avl_first(&m_avlTree);
	while (next) {
		msg.Format(">>name(%s), value(%s)\r\n", next->name, next->value);
		DebugWinAppend(msg);
		next = (MY_TYPE*) AVL_NEXT(&m_avlTree, next);
		n++;
	}

	msg.Format(">>ok, total count is %d\r\n", n);
	DebugWinAppend(msg);
}

void CFileDirDlg::OnEnChangeEditAvlName()
{
	// TODO:  如果该控件是 RICHEDIT 控件，则它将不会
	// 发送该通知，除非重写 CDialog::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask()，
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
	UpdateData(TRUE);
}

void CFileDirDlg::OnBnClickedButtonTest()
{
	// TODO: 在此添加控件通知处理程序代码
	avl_node_t *node = (avl_node_t*) acl_mycalloc(1, sizeof(avl_node_t)), *pnode;
	avl_index_t where;

	where = AVL_MKINDEX(node, 1);
	pnode = AVL_INDEX2NODE(where);

	CString msg;

	msg.Format("addr=%x, %x, %d", pnode, node, pnode);
	if (pnode == node) {
		MessageBox("ok, ==");
		MessageBox(msg);
	} else {
		MessageBox("no");
	}
}

void CFileDirDlg::OnEnChangeEditAvlValue()
{
	// TODO:  如果该控件是 RICHEDIT 控件，则它将不会
	// 发送该通知，除非重写 CDialog::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask()，
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
	UpdateData(TRUE);
}

void CFileDirDlg::OnBnClickedButtonAvlAddBat()
{
	// TODO: 在此添加控件通知处理程序代码
	MY_TYPE *pm, m;
	int   i, n = 0;
	CString msg;

	for (i = 0; i < 100; i++) {
		snprintf(m.name, sizeof(m.name), "%d", i);
		pm = (MY_TYPE*) avl_find(&m_avlTree, &m, NULL);
		if (pm != NULL) {
			msg.Format(">>key(%s) already exist, value(%s)\r\n",
				pm->name, pm->value);
			DebugWinAppend(msg);
			continue;
		}

		pm = (MY_TYPE*) acl_mycalloc(1, sizeof(MY_TYPE));
		snprintf(pm->name, sizeof(pm->name), "%d", i);
		snprintf(pm->value, sizeof(pm->value), "value(%d)", i);
		avl_add(&m_avlTree, pm);
		msg.Format(">>add one, key(%s), value(%s)\r\n", pm->name, pm->value);
		DebugWinAppend(msg);
		n++;
	}
	
	msg.Format(">>ok, %d records were added\r\n", n);
	DebugWinAppend(msg);
}

void CFileDirDlg::OnBnClickedButtonAvlDel()
{
	// TODO: 在此添加控件通知处理程序代码
	MY_TYPE m, *pm;

	if (m_avlName.GetLength() == 0) {
		MessageBox("Please input searching name!", "error");
		GetDlgItem(IDC_EDIT_AVL_NAME)->SetFocus();
		return;
	}

	snprintf(m.name, sizeof(m.name), "%s", m_avlName.GetString());
	pm = (MY_TYPE*) avl_find(&m_avlTree, &m, NULL);
	if (!pm) {
		MessageBox("not find", "ok");
	} else {
		avl_remove(&m_avlTree, pm);
		MessageBox("Delete ok!", "ok");
	}
	m_avlName.Empty();
	m_avlValue.Empty();
	UpdateData(FALSE);
}

void CFileDirDlg::OnBnClickedButtonClear()
{
	// TODO: 在此添加控件通知处理程序代码
	m_avlName.Empty();
	m_avlValue.Empty();
	UpdateData(FALSE);
	m_debugWin.SetSel(0, m_debugWin.GetWindowTextLength());
	m_debugWin.ReplaceSel("");
}

void CFileDirDlg::DebugWinAppend(CString& msg)
{
	m_debugWin.SetSel(m_debugWin.GetWindowTextLength(),
		m_debugWin.GetWindowTextLength());
	m_debugWin.ReplaceSel(msg.GetString());
}

void CFileDirDlg::OnBnClickedCheckOutName()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
}

void CFileDirDlg::OnBnClickedSvnUpdate()
{
	// TODO: 在此添加控件通知处理程序代码
	CString msg;

	if (GetDirPath() < 0)
		return;

	m_pScan = acl_scan_dir_open(m_dirPath.GetString(), m_nested);
	if (m_pScan == NULL) {
		msg.Format("open dir %s error", m_dirPath.GetString());
		MessageBox(msg, "Error");
		return;
	}

	// 设置回调函数及回调参数
	acl_scan_dir_ctl(m_pScan, ACL_SCAN_CTL_FN, ScanSvnCallback,
		ACL_SCAN_CTL_CTX, this, ACL_SCAN_CTL_END);

	ScanSvnDir();
}

int CFileDirDlg::ScanSvnCallback(ACL_SCAN_DIR * scan, void * ctx)
{
	CFileDirDlg *pfdd = (CFileDirDlg *) ctx;

	if ((acl_scan_dir_nfiles(pfdd->m_pScan)
		+ acl_scan_dir_ndirs(pfdd->m_pScan)) % 100 == 0)
	{
		pfdd->UpdateInfo();
		pfdd->SetTimer(TIMER_EVENT_USER_100, 10, NULL);
		return (-1);  // 暂时停止扫描
	}

	if (!pfdd->m_outName)
		return 0;

	const char* filename = acl_scan_dir_file(scan);
	const char* path = acl_scan_dir_path(scan);
	if (!filename || strcasecmp(filename, "entries") || !path)
		return 0;

	CString filepath;
	filepath.Format("%s/%s", path, filename);

	if (acl_strrncasecmp(filepath.GetString(),
		".svn/entries", sizeof(".svn/entries") - 1) != 0)
	{
		return 0;
	}

	CString msg;
	struct acl_stat sbuf;

	acl_assert(path);
	acl_scan_stat(scan, &sbuf);
	msg.Format(">>>file(%s/%s), size=%I64d\r\n",
		path, filename, (acl_int64) sbuf.st_size);
	pfdd->DebugWinAppend(msg);
	return 0;
}

void CFileDirDlg::ScanSvnDir(void)
{
	if (acl_scan_dir_end(m_pScan)) {
		ScanClose();
		return;
	}

	if (acl_scan_dir_size2(m_pScan) < 0) {
		ScanClose();
		return;
	}
	UpdateInfo();
}

BOOL CFileDirDlg::UpdateSvn(const char* filepath)
{
	ssize_t file_size;
	char* buf = acl_vstream_loadfile2(filepath, &file_size);
	if (buf == NULL)
	{
		acl_msg_error("loadfile(%s) error(%s)",
			filepath, acl_last_serror());
		return FALSE;
	}

	ACL_VSTREAM* fp = acl_vstream_fopen(filepath,
		O_WRONLY | O_BINARY | O_TRUNC, 0700, 4096);
	if (fp == NULL)
	{
		acl_myfree(buf);
		acl_msg_error("fopen(%s) error(%s)",
			filepath, acl_last_serror());
		return FALSE;
	}

	ACL_VSTRING* vbuf = acl_vstring_alloc(2048);
	const char* ptr = buf;
	const char* old_svn = "svn://192.168.1.231";
	size_t old_npre = strlen(old_svn);
	const char* new_svn = "svn://122.49.0.188";
	size_t new_npre = strlen(new_svn);

#define STR	acl_vstring_str
#define LEN	ACL_VSTRING_LEN

	int   i = 0;
	while (TRUE)
	{
		// 只需要修改前 10 数据即可
		if (i++ > 10)
			break;

		// 从缓冲区中获得一行数据
		const ACL_VSTRING* vp = acl_buffer_gets(vbuf, &ptr, strlen(ptr));
		if (vp == NULL)
			break;

		// 比较前缀是否相等
		if (strncasecmp(old_svn, STR(vp), old_npre) != 0)
		{
			if (acl_vstream_writen(fp, STR(vp), LEN(vp)) == ACL_VSTREAM_EOF)
			{
				acl_msg_error("%s(%d): write error(%s)",
					__FUNCTION__, __LINE__,
					acl_last_serror());
				goto end;
			}
			ACL_VSTRING_RESET(vbuf);
			continue;
		}

		// 写入新的前缀
		if (acl_vstream_writen(fp, new_svn, new_npre) == ACL_VSTREAM_EOF)
		{
			acl_msg_error("%s(%d): write error(%s)",
				__FUNCTION__, __LINE__, acl_last_serror());
			goto end;
		}

		const char* pleft = STR(vp) + old_npre;
		if (*pleft == 0)
		{
			ACL_VSTRING_RESET(vbuf);
			continue;
		}

		// 写入该行数据中剩余部分
		if (acl_vstream_writen(fp, pleft, strlen(pleft)) == ACL_VSTREAM_EOF)
		{
			acl_msg_error("%s(%d): write error(%s)",
				__FUNCTION__, __LINE__, acl_last_serror());
			goto end;
		}
		ACL_VSTRING_RESET(vbuf);
	}

	ssize_t nleft = (ssize_t) (file_size - (ptr - buf));
	if (*ptr && nleft > 0)
	{
		if (acl_vstream_writen(fp, ptr, nleft) == ACL_VSTREAM_EOF)
			acl_msg_error("%s(%d): write nleft(%d) error(%s)",
				__FUNCTION__, __LINE__,
				(int) nleft, acl_last_serror());
	}

end:
	acl_vstring_free(vbuf);
	acl_vstream_fclose(fp);
	acl_myfree(buf);
	return TRUE;
}