From b3e9de2a76ca45333594d7b54ba09f94307462b7 Mon Sep 17 00:00:00 2001 From: "bstell%ix.netcom.com" Date: Sat, 16 Feb 2002 08:11:25 +0000 Subject: [PATCH] bug 116149: simple name-value pair DB r=yokoyama@netscape.com, sr=brendan@mozilla.org --- gfx/public/Makefile.in | 1 + gfx/src/Makefile.in | 1 + gfx/src/nsNameValuePairDB.cpp | 547 ++++++++++++++++++++++++ gfx/src/x11shared/nsNameValuePairDB.cpp | 547 ++++++++++++++++++++++++ 4 files changed, 1096 insertions(+) create mode 100644 gfx/src/nsNameValuePairDB.cpp create mode 100644 gfx/src/x11shared/nsNameValuePairDB.cpp diff --git a/gfx/public/Makefile.in b/gfx/public/Makefile.in index f76708938f9..e82005aadf5 100644 --- a/gfx/public/Makefile.in +++ b/gfx/public/Makefile.in @@ -41,6 +41,7 @@ EXPORTS = \ nsPoint.h \ nsSize.h \ nsMargin.h \ + nsNameValuePairDB.h \ nsTransform2D.h \ nsIRenderingContext.h \ nsRenderingContextImpl.h \ diff --git a/gfx/src/Makefile.in b/gfx/src/Makefile.in index d7447afb9ed..cefbb3e986d 100644 --- a/gfx/src/Makefile.in +++ b/gfx/src/Makefile.in @@ -93,6 +93,7 @@ CPPSRCS = \ nsDeviceContext.cpp \ nsFont.cpp \ nsFontList.cpp \ + nsNameValuePairDB.cpp \ nsRenderingContextImpl.cpp \ nsRect.cpp \ nsTransform2D.cpp \ diff --git a/gfx/src/nsNameValuePairDB.cpp b/gfx/src/nsNameValuePairDB.cpp new file mode 100644 index 00000000000..37ec8413e1b --- /dev/null +++ b/gfx/src/nsNameValuePairDB.cpp @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Stell + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nspr.h" +#include "nsCOMPtr.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsIFileSpec.h" +#include "nsNameValuePairDB.h" +#include "nsLocalFileUnix.h" + +#define NVPDB_VERSION_MAJOR 1 +#define NVPDB_VERSION_MINOR 0 +#define NVPDB_VERSION_MAINTENANCE 0 + +#ifdef DEBUG +# define NVPDB_PRINTF(x) \ + PR_BEGIN_MACRO \ + printf x ; \ + printf(", %s %d\n", __FILE__, __LINE__); \ + PR_END_MACRO +#else +# define NVPDB_PRINTF(x) +#endif + +PRBool +nsNameValuePairDB::CheckHeader() +{ + const char *name, *value; + int num, major, minor, maintenance; + PRBool foundVersion = PR_FALSE; + + if (!mFile) + return PR_FALSE; + + if (fseek(mFile, 0L, SEEK_SET) != 0) + return PR_FALSE; + mCurrentGroup = 0; + mAtEndOfGroup = PR_FALSE; + while (GetNextElement(&name, &value) > 0) { + if (*name == '\0') // ignore comments + continue; + if (strcmp(name, "Version")==0) { + foundVersion = PR_TRUE; + num = sscanf(value, "%d.%d.%d", &major, &minor, &maintenance); + if (num != 3) { + NVPDB_PRINTF(("failed to parse version number (%s)", value)); + return PR_FALSE; + } + + // NVPDB_VERSION_MAJOR + // It is presumed that major versions are not backwards compatibile. + if (major != NVPDB_VERSION_MAJOR) { + NVPDB_PRINTF(("version major %d != %d", major, NVPDB_VERSION_MAJOR)); + return PR_FALSE; + } + + // NVPDB_VERSION_MINOR + // It is presumed that minor versions are backwards compatible + // but will have additional features. + // Put any tests related to minor versions here. + + // NVPDB_VERSION_MAINTENANCE + // It is presumed that maintenance versions are backwards compatible, + // have no new features, but can have bug fixes. + // Put any tests related to maintenance versions here. + + mMajorNum = major; + mMinorNum = minor; + mMaintenanceNum = maintenance; + } + } + + return foundVersion; +} + +// +// Re-get an element. Used if the element is bigger than +// the buffer that was first passed in +// +// PRInt32 GetCurrentElement(const char** aName, const char** aValue, +// char *aBuffer, PRUint32 aBufferLen); +// +// to implement this the GetNextElement methods need to save +// the file position so this routine can seek backward to it. +// + +PRInt32 +nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue) +{ + return GetNextElement(aName, aValue, mBuf, sizeof(mBuf)); +} + +// +// Get the next element +// +// returns 1 if complete element read +// return 0 on end of file +// returns a negative number on error +// if error < -NVPDB_MIN_BUFLEN +// then the value is the negative of the needed buffer len +// +// +PRInt32 +nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue, + char *aBuffer, PRUint32 aBufferLen) +{ + char *line, *name, *value; + unsigned int num; + int len; + unsigned int groupNum; + + *aName = ""; + *aValue = ""; + + if (aBufferLen < NVPDB_MIN_BUFLEN) { + return NVPDB_BUFFER_TOO_SMALL; + } + + if (mAtEndOfGroup) { + return NVPDB_END_OF_GROUP; + } + + // + // Get a line + // + line = fgets(aBuffer, aBufferLen, mFile); + if (!line) { + if (feof(mFile)) { // end of file + mAtEndOfGroup = PR_TRUE; + mAtEndOfCatalog = PR_TRUE; + return NVPDB_END_OF_FILE; + } + return NVPDB_FILE_IO_ERROR; + } + + // + // Check we got a complete line + // + len = strlen(line); + NS_ASSERTION(len!=0, "an empty string is invalid"); + if (len == 0) + return NVPDB_GARBLED_LINE; + if (line[len-1] != '\n') { + len++; // space for the line terminator + while (1) { + int val = getc(mFile); + if (val == EOF) + return -len; + len++; + if (val == '\n') + return -len; + } + } + len--; + line[len] = '\0'; + //NVPDB_PRINTF(("line = (%s)", line)); + + // + // Check the group number + // + num = sscanf(line, "%u", &groupNum); + if ((num != 1) || (groupNum != (unsigned)mCurrentGroup)) + return NVPDB_END_OF_GROUP; + + // + // Get the name + // + name = strchr(line, ' '); + if ((!name) || (name[1]=='\0')) + return NVPDB_GARBLED_LINE; + name++; + + // + // If it is a comment + // return a blank name (strlen(*aName)==0) + // return the comment in the value field + // + if (*name == '#') { + *aValue = name; + return 1; + } + + // + // Get the value + // + value = strchr(name, '='); + if (!value) + return NVPDB_GARBLED_LINE; + *value = '\0'; + value++; + + // + // Check for end of group + // + if (strcmp(name,"end")==0) { + mAtEndOfGroup = PR_TRUE; + return NVPDB_END_OF_GROUP; + } + + // + // Got the name and value + // + *aName = name; + *aValue = value; + return 1; +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType) +{ + return GetNextGroup(aType, nsnull, 0); +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName) +{ + return GetNextGroup(aType, aName, strlen(aName)); +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName, int aLen) +{ + const char *name, *value; + long pos = 0; + + *aType = ""; + + if (mAtEndOfCatalog) + return PR_FALSE; + + // + // Move to end of current Group + // + while (GetNextElement(&name, &value) > 0) + continue; + mCurrentGroup++; + mAtEndOfGroup = PR_FALSE; + // save current pos in case this in not the desired type + // and we need to backup + if (aName) + pos = ftell(mFile); + + // check if there are more Groups + if (GetNextElement(&name, &value) <= 0) { + mAtEndOfGroup = PR_TRUE; + mAtEndOfCatalog = PR_TRUE; + return PR_FALSE; + } + if (strcmp(name,"begin")) + goto GetNext_Error; + + // check if this is the desired type + if (aName) { + if (strncmp(value,aName,aLen)) { + fseek(mFile, pos, SEEK_SET); + mCurrentGroup--; + mAtEndOfGroup = PR_TRUE; + return PR_FALSE; + } + } + + *aType = value; + return PR_TRUE; + +GetNext_Error: + mError = PR_TRUE; + NVPDB_PRINTF(("GetNext_Error")); + return PR_FALSE; +} + +nsNameValuePairDB::nsNameValuePairDB() +{ + mFile = nsnull; + mBuf[0] = '\0'; + mMajorNum = 0; + mMinorNum = 0; + mMaintenanceNum = 0; + mCurrentGroup = 0; + mAtEndOfGroup = PR_FALSE; + mAtEndOfCatalog = PR_FALSE; + mError = PR_FALSE; +} + +nsNameValuePairDB::~nsNameValuePairDB() +{ + if (mFile) { + fclose(mFile); + mFile = nsnull; + } +} + +PRBool +nsNameValuePairDB::OpenForRead(const nsACString & aCatalogName) +{ + nsresult result; + + nsCOMPtr local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, + &result); + if (NS_FAILED(result)) + goto error_return; + + local_file->InitWithPath(PromiseFlatCString(aCatalogName).get()); + local_file->OpenANSIFileDesc("r", &mFile); + if (mFile && CheckHeader()) + return PR_TRUE; + +error_return: + mError = PR_TRUE; + NVPDB_PRINTF(("OpenForRead error")); + return PR_FALSE; +} + +PRBool +nsNameValuePairDB::OpenTmpForWrite(const nsACString& aCatalogName) +{ + nsCAutoString name(aCatalogName); + name.Append(".tmp"); + + nsresult result; + nsCOMPtr local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, + &result); + if (NS_FAILED(result)) + return PR_FALSE; + local_file->InitWithPath(name.get()); + local_file->OpenANSIFileDesc("w+", &mFile); + if (mFile == nsnull) + return PR_FALSE; + + // Write the header + mAtEndOfGroup = PR_TRUE; + mCurrentGroup = -1; + PutStartGroup("Header"); + char buf[64]; + PutElement("", "########################################"); + PutElement("", "# #"); + PutElement("", "# Name Value Pair DB #"); + PutElement("", "# #"); + PutElement("", "# This is a program generated file #"); + PutElement("", "# #"); + PutElement("", "# Do not edit #"); + PutElement("", "# #"); + PutElement("", "########################################"); + PR_snprintf(buf, sizeof(buf), "%d.%d.%d", NVPDB_VERSION_MAJOR, + NVPDB_VERSION_MINOR, NVPDB_VERSION_MAINTENANCE); + PutElement("Version", buf); + PutEndGroup("Header"); + + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutElement(const char* aName, const char* aValue) +{ + if (mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutElement_Error")); + return PR_FALSE; + } + + if ((!*aName) && (*aValue == '#')) + fprintf(mFile, "%u %s\n", mCurrentGroup, aValue); + else + fprintf(mFile, "%u %s=%s\n", mCurrentGroup, aName, aValue); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutEndGroup(const char* aType) +{ + if (mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutEndGroup_Error")); + return PR_FALSE; + } + + mAtEndOfGroup = PR_TRUE; + fprintf(mFile, "%u end=%s\n", mCurrentGroup, aType); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutStartGroup(const char* aType) +{ + if (!mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutStartGroup_Error")); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_FALSE; + } + + mAtEndOfGroup = PR_FALSE; + mCurrentGroup++; + fprintf(mFile, "%u begin=%s\n", mCurrentGroup, aType); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::RenameTmp(const char* aCatalogName) +{ + nsresult rv; + nsCOMPtr dir; + PRBool exists = PR_FALSE; + nsCAutoString old_name(aCatalogName); + nsDependentCString current_name(aCatalogName); + nsCAutoString tmp_name(aCatalogName); + nsCAutoString old_name_tail; + nsCAutoString current_name_tail; + nsCOMPtr old_file; + nsCOMPtr current_file; + nsCOMPtr tmp_file; + nsCAutoString parent_dir; + nsXPIDLCString parent_path; + nsXPIDLCString cur_path; + + // + // Split the parent dir and file name + // + PRInt32 slash = 0, last_slash = -1; + nsCAutoString fontDirName(aCatalogName); + // RFindChar not coded so do it by hand + while ((slash=fontDirName.FindChar('/', slash))>=0) { + last_slash = slash; + slash++; + } + if (last_slash < 0) + goto Rename_Error; + + fontDirName.Left(parent_dir, last_slash); + dir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + dir->InitWithPath(parent_dir.get()); + dir->GetPath(getter_Copies(parent_path)); + + if (!mAtEndOfGroup || mError) + goto Rename_Error; + + // + // check that we have a tmp copy + // + tmp_name.Append(".tmp"); + tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + tmp_file->InitWithPath(tmp_name.get()); + tmp_file->Exists(&exists); + if (!exists) + goto Rename_Error; + + // + // get rid of any old copy + // + old_name.Append(".old"); + old_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + old_file->InitWithPath(old_name.get()); + + // + // Check we have a current copy + // + current_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + current_file->InitWithPath(current_name.get()); + current_file->Exists(&exists); + if (exists) { + // + // Rename the current copy to old + // + current_file->GetPath(getter_Copies(cur_path)); + old_name.Right(old_name_tail, old_name.Length() - last_slash - 1); + rv = current_file->MoveTo(dir, old_name_tail.get()); + if (NS_FAILED(rv)) + goto Rename_Error; + } + + // + // Rename the tmp to current + // + current_name.Right(current_name_tail, current_name.Length() - last_slash - 1); + rv = tmp_file->MoveTo(dir, current_name_tail.get()); + if (NS_FAILED(rv)) + goto Rename_Error; + + // + // remove the previous copy + // + if (exists) { + old_file->Remove(PR_FALSE); + } + + return PR_TRUE; + +Rename_Error: + mError = PR_TRUE; + NVPDB_PRINTF(("Rename_Error")); + return PR_FALSE; +} + diff --git a/gfx/src/x11shared/nsNameValuePairDB.cpp b/gfx/src/x11shared/nsNameValuePairDB.cpp new file mode 100644 index 00000000000..37ec8413e1b --- /dev/null +++ b/gfx/src/x11shared/nsNameValuePairDB.cpp @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Stell + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nspr.h" +#include "nsCOMPtr.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsIFileSpec.h" +#include "nsNameValuePairDB.h" +#include "nsLocalFileUnix.h" + +#define NVPDB_VERSION_MAJOR 1 +#define NVPDB_VERSION_MINOR 0 +#define NVPDB_VERSION_MAINTENANCE 0 + +#ifdef DEBUG +# define NVPDB_PRINTF(x) \ + PR_BEGIN_MACRO \ + printf x ; \ + printf(", %s %d\n", __FILE__, __LINE__); \ + PR_END_MACRO +#else +# define NVPDB_PRINTF(x) +#endif + +PRBool +nsNameValuePairDB::CheckHeader() +{ + const char *name, *value; + int num, major, minor, maintenance; + PRBool foundVersion = PR_FALSE; + + if (!mFile) + return PR_FALSE; + + if (fseek(mFile, 0L, SEEK_SET) != 0) + return PR_FALSE; + mCurrentGroup = 0; + mAtEndOfGroup = PR_FALSE; + while (GetNextElement(&name, &value) > 0) { + if (*name == '\0') // ignore comments + continue; + if (strcmp(name, "Version")==0) { + foundVersion = PR_TRUE; + num = sscanf(value, "%d.%d.%d", &major, &minor, &maintenance); + if (num != 3) { + NVPDB_PRINTF(("failed to parse version number (%s)", value)); + return PR_FALSE; + } + + // NVPDB_VERSION_MAJOR + // It is presumed that major versions are not backwards compatibile. + if (major != NVPDB_VERSION_MAJOR) { + NVPDB_PRINTF(("version major %d != %d", major, NVPDB_VERSION_MAJOR)); + return PR_FALSE; + } + + // NVPDB_VERSION_MINOR + // It is presumed that minor versions are backwards compatible + // but will have additional features. + // Put any tests related to minor versions here. + + // NVPDB_VERSION_MAINTENANCE + // It is presumed that maintenance versions are backwards compatible, + // have no new features, but can have bug fixes. + // Put any tests related to maintenance versions here. + + mMajorNum = major; + mMinorNum = minor; + mMaintenanceNum = maintenance; + } + } + + return foundVersion; +} + +// +// Re-get an element. Used if the element is bigger than +// the buffer that was first passed in +// +// PRInt32 GetCurrentElement(const char** aName, const char** aValue, +// char *aBuffer, PRUint32 aBufferLen); +// +// to implement this the GetNextElement methods need to save +// the file position so this routine can seek backward to it. +// + +PRInt32 +nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue) +{ + return GetNextElement(aName, aValue, mBuf, sizeof(mBuf)); +} + +// +// Get the next element +// +// returns 1 if complete element read +// return 0 on end of file +// returns a negative number on error +// if error < -NVPDB_MIN_BUFLEN +// then the value is the negative of the needed buffer len +// +// +PRInt32 +nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue, + char *aBuffer, PRUint32 aBufferLen) +{ + char *line, *name, *value; + unsigned int num; + int len; + unsigned int groupNum; + + *aName = ""; + *aValue = ""; + + if (aBufferLen < NVPDB_MIN_BUFLEN) { + return NVPDB_BUFFER_TOO_SMALL; + } + + if (mAtEndOfGroup) { + return NVPDB_END_OF_GROUP; + } + + // + // Get a line + // + line = fgets(aBuffer, aBufferLen, mFile); + if (!line) { + if (feof(mFile)) { // end of file + mAtEndOfGroup = PR_TRUE; + mAtEndOfCatalog = PR_TRUE; + return NVPDB_END_OF_FILE; + } + return NVPDB_FILE_IO_ERROR; + } + + // + // Check we got a complete line + // + len = strlen(line); + NS_ASSERTION(len!=0, "an empty string is invalid"); + if (len == 0) + return NVPDB_GARBLED_LINE; + if (line[len-1] != '\n') { + len++; // space for the line terminator + while (1) { + int val = getc(mFile); + if (val == EOF) + return -len; + len++; + if (val == '\n') + return -len; + } + } + len--; + line[len] = '\0'; + //NVPDB_PRINTF(("line = (%s)", line)); + + // + // Check the group number + // + num = sscanf(line, "%u", &groupNum); + if ((num != 1) || (groupNum != (unsigned)mCurrentGroup)) + return NVPDB_END_OF_GROUP; + + // + // Get the name + // + name = strchr(line, ' '); + if ((!name) || (name[1]=='\0')) + return NVPDB_GARBLED_LINE; + name++; + + // + // If it is a comment + // return a blank name (strlen(*aName)==0) + // return the comment in the value field + // + if (*name == '#') { + *aValue = name; + return 1; + } + + // + // Get the value + // + value = strchr(name, '='); + if (!value) + return NVPDB_GARBLED_LINE; + *value = '\0'; + value++; + + // + // Check for end of group + // + if (strcmp(name,"end")==0) { + mAtEndOfGroup = PR_TRUE; + return NVPDB_END_OF_GROUP; + } + + // + // Got the name and value + // + *aName = name; + *aValue = value; + return 1; +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType) +{ + return GetNextGroup(aType, nsnull, 0); +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName) +{ + return GetNextGroup(aType, aName, strlen(aName)); +} + +PRBool +nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName, int aLen) +{ + const char *name, *value; + long pos = 0; + + *aType = ""; + + if (mAtEndOfCatalog) + return PR_FALSE; + + // + // Move to end of current Group + // + while (GetNextElement(&name, &value) > 0) + continue; + mCurrentGroup++; + mAtEndOfGroup = PR_FALSE; + // save current pos in case this in not the desired type + // and we need to backup + if (aName) + pos = ftell(mFile); + + // check if there are more Groups + if (GetNextElement(&name, &value) <= 0) { + mAtEndOfGroup = PR_TRUE; + mAtEndOfCatalog = PR_TRUE; + return PR_FALSE; + } + if (strcmp(name,"begin")) + goto GetNext_Error; + + // check if this is the desired type + if (aName) { + if (strncmp(value,aName,aLen)) { + fseek(mFile, pos, SEEK_SET); + mCurrentGroup--; + mAtEndOfGroup = PR_TRUE; + return PR_FALSE; + } + } + + *aType = value; + return PR_TRUE; + +GetNext_Error: + mError = PR_TRUE; + NVPDB_PRINTF(("GetNext_Error")); + return PR_FALSE; +} + +nsNameValuePairDB::nsNameValuePairDB() +{ + mFile = nsnull; + mBuf[0] = '\0'; + mMajorNum = 0; + mMinorNum = 0; + mMaintenanceNum = 0; + mCurrentGroup = 0; + mAtEndOfGroup = PR_FALSE; + mAtEndOfCatalog = PR_FALSE; + mError = PR_FALSE; +} + +nsNameValuePairDB::~nsNameValuePairDB() +{ + if (mFile) { + fclose(mFile); + mFile = nsnull; + } +} + +PRBool +nsNameValuePairDB::OpenForRead(const nsACString & aCatalogName) +{ + nsresult result; + + nsCOMPtr local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, + &result); + if (NS_FAILED(result)) + goto error_return; + + local_file->InitWithPath(PromiseFlatCString(aCatalogName).get()); + local_file->OpenANSIFileDesc("r", &mFile); + if (mFile && CheckHeader()) + return PR_TRUE; + +error_return: + mError = PR_TRUE; + NVPDB_PRINTF(("OpenForRead error")); + return PR_FALSE; +} + +PRBool +nsNameValuePairDB::OpenTmpForWrite(const nsACString& aCatalogName) +{ + nsCAutoString name(aCatalogName); + name.Append(".tmp"); + + nsresult result; + nsCOMPtr local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, + &result); + if (NS_FAILED(result)) + return PR_FALSE; + local_file->InitWithPath(name.get()); + local_file->OpenANSIFileDesc("w+", &mFile); + if (mFile == nsnull) + return PR_FALSE; + + // Write the header + mAtEndOfGroup = PR_TRUE; + mCurrentGroup = -1; + PutStartGroup("Header"); + char buf[64]; + PutElement("", "########################################"); + PutElement("", "# #"); + PutElement("", "# Name Value Pair DB #"); + PutElement("", "# #"); + PutElement("", "# This is a program generated file #"); + PutElement("", "# #"); + PutElement("", "# Do not edit #"); + PutElement("", "# #"); + PutElement("", "########################################"); + PR_snprintf(buf, sizeof(buf), "%d.%d.%d", NVPDB_VERSION_MAJOR, + NVPDB_VERSION_MINOR, NVPDB_VERSION_MAINTENANCE); + PutElement("Version", buf); + PutEndGroup("Header"); + + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutElement(const char* aName, const char* aValue) +{ + if (mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutElement_Error")); + return PR_FALSE; + } + + if ((!*aName) && (*aValue == '#')) + fprintf(mFile, "%u %s\n", mCurrentGroup, aValue); + else + fprintf(mFile, "%u %s=%s\n", mCurrentGroup, aName, aValue); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutEndGroup(const char* aType) +{ + if (mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutEndGroup_Error")); + return PR_FALSE; + } + + mAtEndOfGroup = PR_TRUE; + fprintf(mFile, "%u end=%s\n", mCurrentGroup, aType); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::PutStartGroup(const char* aType) +{ + if (!mAtEndOfGroup) { + mError = PR_TRUE; + NVPDB_PRINTF(("PutStartGroup_Error")); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_FALSE; + } + + mAtEndOfGroup = PR_FALSE; + mCurrentGroup++; + fprintf(mFile, "%u begin=%s\n", mCurrentGroup, aType); +#ifdef DEBUG + fflush(mFile); +#endif + return PR_TRUE; +} + +PRBool +nsNameValuePairDB::RenameTmp(const char* aCatalogName) +{ + nsresult rv; + nsCOMPtr dir; + PRBool exists = PR_FALSE; + nsCAutoString old_name(aCatalogName); + nsDependentCString current_name(aCatalogName); + nsCAutoString tmp_name(aCatalogName); + nsCAutoString old_name_tail; + nsCAutoString current_name_tail; + nsCOMPtr old_file; + nsCOMPtr current_file; + nsCOMPtr tmp_file; + nsCAutoString parent_dir; + nsXPIDLCString parent_path; + nsXPIDLCString cur_path; + + // + // Split the parent dir and file name + // + PRInt32 slash = 0, last_slash = -1; + nsCAutoString fontDirName(aCatalogName); + // RFindChar not coded so do it by hand + while ((slash=fontDirName.FindChar('/', slash))>=0) { + last_slash = slash; + slash++; + } + if (last_slash < 0) + goto Rename_Error; + + fontDirName.Left(parent_dir, last_slash); + dir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + dir->InitWithPath(parent_dir.get()); + dir->GetPath(getter_Copies(parent_path)); + + if (!mAtEndOfGroup || mError) + goto Rename_Error; + + // + // check that we have a tmp copy + // + tmp_name.Append(".tmp"); + tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + tmp_file->InitWithPath(tmp_name.get()); + tmp_file->Exists(&exists); + if (!exists) + goto Rename_Error; + + // + // get rid of any old copy + // + old_name.Append(".old"); + old_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + old_file->InitWithPath(old_name.get()); + + // + // Check we have a current copy + // + current_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto Rename_Error; + current_file->InitWithPath(current_name.get()); + current_file->Exists(&exists); + if (exists) { + // + // Rename the current copy to old + // + current_file->GetPath(getter_Copies(cur_path)); + old_name.Right(old_name_tail, old_name.Length() - last_slash - 1); + rv = current_file->MoveTo(dir, old_name_tail.get()); + if (NS_FAILED(rv)) + goto Rename_Error; + } + + // + // Rename the tmp to current + // + current_name.Right(current_name_tail, current_name.Length() - last_slash - 1); + rv = tmp_file->MoveTo(dir, current_name_tail.get()); + if (NS_FAILED(rv)) + goto Rename_Error; + + // + // remove the previous copy + // + if (exists) { + old_file->Remove(PR_FALSE); + } + + return PR_TRUE; + +Rename_Error: + mError = PR_TRUE; + NVPDB_PRINTF(("Rename_Error")); + return PR_FALSE; +} +