Skip to content

Commit 073c2fc

Browse files
committed
0.8.0.24: Implemented basic timestamped backup system with fixed default path at \Data\Backups\<dbname>. Improved default directory specification to also allow users to prefer storage cards when inserted, and choose different default paths on cards versus the in-memory filesystem.
1 parent 3c971fc commit 073c2fc

7 files changed

Lines changed: 229 additions & 28 deletions

File tree

src/sqlite-ce-edit/database.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ void UpdateDbSize(void) {
6767
wsprintfW(buf, L"%s", name);
6868
SetStatusDb(buf);
6969

70-
/* Enable/disable Close based on whether we have a real file */
70+
/* Enable/disable Close and Backup based on whether we have a real file */
7171
canClose = (g_szDbPath[0] && g_szDbPath[0] != ':');
7272
EnableMenuItem(g_hMenu, IDM_CLOSE, canClose ? MF_ENABLED : MF_GRAYED);
73+
EnableMenuItem(g_hMenu, IDM_BACKUP, canClose ? MF_ENABLED : MF_GRAYED);
7374
SendMessage(g_hwndCB, TB_ENABLEBUTTON, IDM_CLOSE, canClose);
7475
}
7576

@@ -113,8 +114,9 @@ void CloseDatabase(void) {
113114
SetWindowTextW(g_hwndResult, L"");
114115
SetStatusDb(L":memory:");
115116
SetStatusResult(L"");
116-
/* Disable Close for in-memory database */
117+
/* Disable Close and Backup for in-memory database */
117118
EnableMenuItem(g_hMenu, IDM_CLOSE, MF_GRAYED);
119+
EnableMenuItem(g_hMenu, IDM_BACKUP, MF_GRAYED);
118120
SendMessage(g_hwndCB, TB_ENABLEBUTTON, IDM_CLOSE, FALSE);
119121
}
120122

src/sqlite-ce-edit/dialogs.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,12 @@ int PromptForPath(const wchar_t *title, const wchar_t *defPath) {
149149
#define IDC_OPT_ERRORMSGBOX 1004
150150
#define IDC_OPT_DBPATH 1005
151151
#define IDC_OPT_CLEARREG 1006
152+
#define IDC_OPT_STORAGECARD 1007
153+
#define IDC_OPT_STORAGECARDDATA 1008
154+
#define IDC_OPT_DBPATHLABEL 1009
152155

153156
static int g_optClearExec, g_optExecAtCursor, g_optLineNums, g_optErrorMsgBox;
157+
static int g_optStorageCard, g_optStorageCardData;
154158
static wchar_t g_optDbPath[MAX_PATH];
155159
static HWND g_hwndOptions = NULL;
156160
static int g_optResult = 0;
@@ -160,10 +164,34 @@ static void ApplyOptions(HWND hwnd) {
160164
g_optExecAtCursor = SendMessage(GetDlgItem(hwnd, IDC_OPT_EXECATCURSOR), BM_GETCHECK, 0, 0);
161165
g_optLineNums = SendMessage(GetDlgItem(hwnd, IDC_OPT_LINENUMS), BM_GETCHECK, 0, 0);
162166
g_optErrorMsgBox = SendMessage(GetDlgItem(hwnd, IDC_OPT_ERRORMSGBOX), BM_GETCHECK, 0, 0);
167+
g_optStorageCardData = SendMessage(GetDlgItem(hwnd, IDC_OPT_STORAGECARDDATA), BM_GETCHECK, 0, 0);
168+
g_optStorageCard = SendMessage(GetDlgItem(hwnd, IDC_OPT_STORAGECARD), BM_GETCHECK, 0, 0);
163169
GetWindowTextW(GetDlgItem(hwnd, IDC_OPT_DBPATH), g_optDbPath, MAX_PATH);
164170
g_optResult = 1;
165171
}
166172

173+
static void UpdatePathDisplay(HWND hwnd) {
174+
wchar_t label[128];
175+
WIN32_FIND_DATAW fd;
176+
HANDLE hFind;
177+
int useCard = SendMessage(GetDlgItem(hwnd, IDC_OPT_STORAGECARDDATA), BM_GETCHECK, 0, 0);
178+
179+
if (useCard) {
180+
/* Try to detect actual storage card */
181+
hFind = FindFirstFileW(L"\\Storage Card*", &fd);
182+
if (hFind != INVALID_HANDLE_VALUE && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
183+
wsprintfW(label, L"Data path (in \\%s%s):", fd.cFileName, g_szCardBasePath);
184+
FindClose(hFind);
185+
} else {
186+
if (hFind != INVALID_HANDLE_VALUE) FindClose(hFind);
187+
lstrcpyW(label, L"Data path (no card found):");
188+
}
189+
} else {
190+
wsprintfW(label, L"Data path (in %s):", g_szLocalBasePath);
191+
}
192+
SetWindowTextW(GetDlgItem(hwnd, IDC_OPT_DBPATHLABEL), label);
193+
}
194+
167195
static LRESULT CALLBACK OptionsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
168196
switch (msg) {
169197
case WM_CREATE: {
@@ -180,24 +208,33 @@ static LRESULT CALLBACK OptionsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARA
180208
CreateWindowW(L"BUTTON", L"Message box on error",
181209
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
182210
10, 76, 165, 20, hwnd, (HMENU)IDC_OPT_ERRORMSGBOX, g_hInst, NULL);
211+
CreateWindowW(L"BUTTON", L"Use storage card for data",
212+
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
213+
10, 98, 190, 20, hwnd, (HMENU)IDC_OPT_STORAGECARDDATA, g_hInst, NULL);
214+
CreateWindowW(L"BUTTON", L"Use storage card for backups",
215+
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
216+
10, 120, 190, 20, hwnd, (HMENU)IDC_OPT_STORAGECARD, g_hInst, NULL);
183217

184218
/* Right column - paths */
185-
CreateWindowW(L"STATIC", L"Default database path:",
219+
CreateWindowW(L"STATIC", L"Data path:",
186220
WS_CHILD | WS_VISIBLE,
187-
185, 10, 150, 16, hwnd, NULL, g_hInst, NULL);
221+
210, 10, 230, 16, hwnd, (HMENU)IDC_OPT_DBPATHLABEL, g_hInst, NULL);
188222
CreateWindowW(L"EDIT", g_optDbPath,
189223
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
190-
185, 28, 180, 22, hwnd, (HMENU)IDC_OPT_DBPATH, g_hInst, NULL);
224+
210, 28, 230, 22, hwnd, (HMENU)IDC_OPT_DBPATH, g_hInst, NULL);
191225

192226
/* Clear settings button */
193227
CreateWindowW(L"BUTTON", L"Clear All Settings...",
194228
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
195-
185, 76, 130, 22, hwnd, (HMENU)IDC_OPT_CLEARREG, g_hInst, NULL);
229+
210, 98, 140, 22, hwnd, (HMENU)IDC_OPT_CLEARREG, g_hInst, NULL);
196230

197231
SendMessage(GetDlgItem(hwnd, IDC_OPT_CLEAREXEC), BM_SETCHECK, g_optClearExec, 0);
198232
SendMessage(GetDlgItem(hwnd, IDC_OPT_EXECATCURSOR), BM_SETCHECK, g_optExecAtCursor, 0);
199233
SendMessage(GetDlgItem(hwnd, IDC_OPT_LINENUMS), BM_SETCHECK, g_optLineNums, 0);
200234
SendMessage(GetDlgItem(hwnd, IDC_OPT_ERRORMSGBOX), BM_SETCHECK, g_optErrorMsgBox, 0);
235+
SendMessage(GetDlgItem(hwnd, IDC_OPT_STORAGECARDDATA), BM_SETCHECK, g_optStorageCardData, 0);
236+
SendMessage(GetDlgItem(hwnd, IDC_OPT_STORAGECARD), BM_SETCHECK, g_optStorageCard, 0);
237+
UpdatePathDisplay(hwnd);
201238
return 0;
202239
}
203240
case WM_COMMAND:
@@ -206,6 +243,10 @@ static LRESULT CALLBACK OptionsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARA
206243
DestroyWindow(hwnd);
207244
return 0;
208245
}
246+
if (LOWORD(wParam) == IDC_OPT_STORAGECARDDATA) {
247+
UpdatePathDisplay(hwnd);
248+
return 0;
249+
}
209250
if (LOWORD(wParam) == IDC_OPT_CLEARREG) {
210251
if (MessageBoxW(hwnd, L"Clear all saved settings and recent files?",
211252
L"Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
@@ -241,7 +282,9 @@ void DoOptions(void) {
241282
g_optExecAtCursor = g_execAtCursor;
242283
g_optLineNums = g_showLineNumbers;
243284
g_optErrorMsgBox = g_showErrorMsgBox;
244-
lstrcpyW(g_optDbPath, g_szDefaultDbPath);
285+
g_optStorageCard = g_useStorageCard;
286+
g_optStorageCardData = g_useStorageCardData;
287+
lstrcpyW(g_optDbPath, g_szDataRelPath);
245288
g_optResult = 0;
246289

247290
wc.lpfnWndProc = OptionsWndProc;
@@ -254,7 +297,7 @@ void DoOptions(void) {
254297
g_hwndOptions = CreateWindowExW(WS_EX_CAPTIONOKBTN,
255298
L"SQLiteCEOptions", L"Options",
256299
WS_POPUP | WS_CAPTION | WS_SYSMENU,
257-
rc.left + 20, rc.top + 30, 380, 125,
300+
rc.left + 20, rc.top + 30, 460, 170,
258301
g_hwndMain, NULL, g_hInst, NULL);
259302
ShowWindow(g_hwndOptions, SW_SHOW);
260303

@@ -272,7 +315,9 @@ void DoOptions(void) {
272315
g_clearOnExec = g_optClearExec;
273316
g_execAtCursor = g_optExecAtCursor;
274317
g_showErrorMsgBox = g_optErrorMsgBox;
275-
lstrcpyW(g_szDefaultDbPath, g_optDbPath);
318+
g_useStorageCard = g_optStorageCard;
319+
g_useStorageCardData = g_optStorageCardData;
320+
lstrcpyW(g_szDataRelPath, g_optDbPath);
276321

277322
/* Sync toolbar button state */
278323
SendMessage(g_hwndCB, TB_CHECKBUTTON, IDM_EXECATCURSOR, g_execAtCursor);

src/sqlite-ce-edit/fileops.c

Lines changed: 143 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,52 @@
44

55
#include "globals.h"
66

7+
/*============================================================================
8+
** Storage Card Detection Helper
9+
**============================================================================*/
10+
11+
static int FindStorageCard(wchar_t *cardPath, int maxLen) {
12+
WIN32_FIND_DATAW fd;
13+
HANDLE hFind;
14+
15+
hFind = FindFirstFileW(L"\\Storage Card*", &fd);
16+
if (hFind != INVALID_HANDLE_VALUE) {
17+
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
18+
wsprintfW(cardPath, L"\\%s", fd.cFileName);
19+
FindClose(hFind);
20+
return 1;
21+
}
22+
FindClose(hFind);
23+
}
24+
cardPath[0] = 0;
25+
return 0;
26+
}
27+
28+
/* Get effective data path based on storage card option */
29+
static void GetDataPath(wchar_t *path, int maxLen) {
30+
wchar_t cardPath[MAX_PATH];
31+
32+
if (g_useStorageCardData && FindStorageCard(cardPath, MAX_PATH)) {
33+
/* Card base path is appended after detected card path */
34+
wsprintfW(path, L"%s%s%s", cardPath, g_szCardBasePath, g_szDataRelPath);
35+
} else {
36+
wsprintfW(path, L"%s%s", g_szLocalBasePath, g_szDataRelPath);
37+
}
38+
CreateDirectoryW(path, NULL);
39+
}
40+
741
/*============================================================================
842
** File New/Open
943
**============================================================================*/
1044

1145
void DoFileNew(void) {
1246
CE_OPENFILENAME ofn;
1347
wchar_t szFile[MAX_PATH];
14-
int createdDir = 0;
15-
16-
/* Try to create default directory if it doesn't exist */
17-
if (g_szDefaultDbPath[0]) {
18-
DWORD attr = GetFileAttributesW(g_szDefaultDbPath);
19-
if (attr == 0xFFFFFFFF) {
20-
if (CreateDirectoryW(g_szDefaultDbPath, NULL))
21-
createdDir = 1;
22-
}
23-
lstrcpyW(szFile, L"new.db");
24-
} else {
25-
lstrcpyW(szFile, L"new.db");
26-
}
48+
wchar_t szDataPath[MAX_PATH];
49+
50+
/* Get data path (storage card or My Documents) */
51+
GetDataPath(szDataPath, MAX_PATH);
52+
lstrcpyW(szFile, L"new.db");
2753

2854
memset(&ofn, 0, sizeof(ofn));
2955
ofn.lStructSize = sizeof(ofn);
@@ -33,21 +59,22 @@ void DoFileNew(void) {
3359
ofn.lpstrFilter = L"Database Files (*.db)\0*.db\0All Files (*.*)\0*.*\0";
3460
ofn.lpstrDefExt = L"db";
3561
ofn.lpstrTitle = L"New Database";
36-
ofn.lpstrInitialDir = g_szDefaultDbPath[0] ? g_szDefaultDbPath : NULL;
62+
ofn.lpstrInitialDir = szDataPath;
3763
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
3864

3965
if (GetSaveFileNameW(&ofn)) {
4066
DeleteFileW(szFile);
4167
OpenDatabase(szFile);
42-
} else if (createdDir) {
43-
/* Remove directory if we created it and user cancelled */
44-
RemoveDirectoryW(g_szDefaultDbPath);
4568
}
4669
}
4770

4871
void DoFileOpen(void) {
4972
CE_OPENFILENAME ofn;
5073
wchar_t szFile[MAX_PATH] = L"";
74+
wchar_t szDataPath[MAX_PATH];
75+
76+
/* Get data path (storage card or My Documents) */
77+
GetDataPath(szDataPath, MAX_PATH);
5178

5279
memset(&ofn, 0, sizeof(ofn));
5380
ofn.lStructSize = sizeof(ofn);
@@ -57,7 +84,7 @@ void DoFileOpen(void) {
5784
ofn.lpstrFilter = L"Database Files (*.db)\0*.db\0All Files (*.*)\0*.*\0";
5885
ofn.lpstrTitle = L"Open Database";
5986
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
60-
if (g_szDefaultDbPath[0]) ofn.lpstrInitialDir = g_szDefaultDbPath;
87+
ofn.lpstrInitialDir = szDataPath;
6188

6289
if (GetOpenFileNameW(&ofn)) {
6390
OpenDatabase(szFile);
@@ -1450,3 +1477,101 @@ void DoExportDb(void) {
14501477

14511478
sqlite_close(destDb);
14521479
}
1480+
1481+
/*============================================================================
1482+
** Backup Database
1483+
**============================================================================*/
1484+
1485+
void DoBackupDatabase(void) {
1486+
wchar_t szBackup[MAX_PATH];
1487+
wchar_t szBackupDir[MAX_PATH];
1488+
wchar_t szCardPath[MAX_PATH];
1489+
wchar_t szDbName[64];
1490+
wchar_t szStatus[128];
1491+
SYSTEMTIME st;
1492+
const wchar_t *fn;
1493+
wchar_t *d;
1494+
HANDLE hSrc, hDst;
1495+
BYTE buf[4096];
1496+
DWORD dwRead, dwWritten;
1497+
int ok = 0;
1498+
1499+
/* Must have a file-based database */
1500+
if (!g_db || g_szDbPath[0] == ':' || g_szDbPath[0] == 0) {
1501+
MessageBoxW(g_hwndMain, L"No database file to backup", L"Backup", MB_OK | MB_ICONINFORMATION);
1502+
return;
1503+
}
1504+
1505+
/* Extract database name (without extension) */
1506+
fn = GetFilename(g_szDbPath);
1507+
d = szDbName;
1508+
while (*fn && *fn != '.' && d < szDbName + 60) *d++ = *fn++;
1509+
*d = 0;
1510+
1511+
/* Build backup directory path */
1512+
if (g_useStorageCard && FindStorageCard(szCardPath, MAX_PATH)) {
1513+
wsprintfW(szBackupDir, L"%s%s%s", szCardPath, g_szCardBasePath, g_szDataRelPath);
1514+
CreateDirectoryW(szBackupDir, NULL);
1515+
lstrcatW(szBackupDir, L"\\Backups");
1516+
} else {
1517+
wsprintfW(szBackupDir, L"%s%s", g_szLocalBasePath, g_szDataRelPath);
1518+
CreateDirectoryW(szBackupDir, NULL);
1519+
lstrcatW(szBackupDir, L"\\Backups");
1520+
}
1521+
1522+
/* Create directory structure: BackupDir\dbname\ */
1523+
CreateDirectoryW(szBackupDir, NULL);
1524+
lstrcatW(szBackupDir, L"\\");
1525+
lstrcatW(szBackupDir, szDbName);
1526+
CreateDirectoryW(szBackupDir, NULL);
1527+
1528+
/* Build backup filename: BackupDir\dbname\dbname_YYYYMMDD_HHMMSS.bak.db */
1529+
GetLocalTime(&st);
1530+
wsprintfW(szBackup, L"%s\\%s_%04d%02d%02d_%02d%02d%02d.bak.db",
1531+
szBackupDir, szDbName,
1532+
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
1533+
1534+
/* Show status during backup */
1535+
SendMessageW(g_hwndStatus, SB_SETTEXTW, 1, (LPARAM)L"Backing up...");
1536+
UpdateWindow(g_hwndStatus);
1537+
1538+
/* Close database to ensure file is flushed */
1539+
sqlite_close(g_db);
1540+
g_db = NULL;
1541+
1542+
/* Copy file */
1543+
hSrc = CreateFileW(g_szDbPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1544+
if (hSrc != INVALID_HANDLE_VALUE) {
1545+
hDst = CreateFileW(szBackup, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1546+
if (hDst != INVALID_HANDLE_VALUE) {
1547+
ok = 1;
1548+
while (ReadFile(hSrc, buf, sizeof(buf), &dwRead, NULL) && dwRead > 0) {
1549+
if (!WriteFile(hDst, buf, dwRead, &dwWritten, NULL) || dwWritten != dwRead) {
1550+
ok = 0;
1551+
break;
1552+
}
1553+
}
1554+
CloseHandle(hDst);
1555+
if (!ok) DeleteFileW(szBackup);
1556+
}
1557+
CloseHandle(hSrc);
1558+
}
1559+
1560+
/* Reopen database */
1561+
{
1562+
char szPath[MAX_PATH * 2];
1563+
WideCharToMultiByte(CP_ACP, 0, g_szDbPath, -1, szPath, sizeof(szPath), NULL, NULL);
1564+
g_db = sqlite_open(szPath, 0, NULL);
1565+
}
1566+
1567+
if (ok) {
1568+
fn = GetFilename(szBackup);
1569+
wsprintfW(szStatus, L"Backed up to %s", fn);
1570+
} else {
1571+
lstrcpyW(szStatus, L"Backup failed");
1572+
MessageBoxW(g_hwndMain, L"Backup failed", L"Error", MB_OK | MB_ICONERROR);
1573+
}
1574+
SendMessageW(g_hwndStatus, SB_SETTEXTW, 1, (LPARAM)szStatus);
1575+
1576+
RefreshSchema();
1577+
}

src/sqlite-ce-edit/globals.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ BOOL WINAPI GetSaveFileNameW(CE_OPENFILENAME*);
8282
** Version
8383
**============================================================================*/
8484

85-
#define SQLITECEDIT_VERSION L"0.8.0.10"
85+
#define SQLITECEDIT_VERSION L"0.8.0.24"
8686

8787
/*============================================================================
8888
** Menu IDs
@@ -103,6 +103,7 @@ BOOL WINAPI GetSaveFileNameW(CE_OPENFILENAME*);
103103
#define IDM_EXPORTHTMLRES 115
104104
#define IDM_IMPORTCSV 110
105105
#define IDM_IMPORTCEDB 111
106+
#define IDM_BACKUP 116
106107
#define IDM_EXECUTE 201
107108
#define IDM_FIND 202
108109
#define IDM_FINDNEXT 203
@@ -293,6 +294,13 @@ void ExportAllDDL(void);
293294
/* Schema options */
294295
extern int g_showSizes;
295296

297+
/* Backup options */
298+
extern int g_useStorageCard;
299+
extern int g_useStorageCardData;
300+
extern wchar_t g_szDataRelPath[MAX_PATH];
301+
extern wchar_t g_szLocalBasePath[MAX_PATH];
302+
extern wchar_t g_szCardBasePath[MAX_PATH];
303+
296304
/* Grid options */
297305
extern int g_gridAutoSize;
298306

@@ -330,6 +338,7 @@ void DoExportTable(void);
330338
void DoExportHTML(void);
331339
void DoExportHTMLResults(void);
332340
void DoExportDb(void);
341+
void DoBackupDatabase(void);
333342
void DoImportCSV(void);
334343
void DoImportCEDB(void);
335344
void AddRecentFile(const wchar_t *path);

0 commit comments

Comments
 (0)