Back to index

lightning-sunbird  0.9+nobinonly
nsNameValuePairDB.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Brian Stell <bstell@ix.netcom.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nspr.h"
00040 #include "nsCOMPtr.h"
00041 #include "nsAppDirectoryServiceDefs.h"
00042 #include "nsNameValuePairDB.h"
00043 #include "nsILocalFile.h"
00044 
00045 #define NVPDB_VERSION_MAJOR 1
00046 #define NVPDB_VERSION_MINOR 0
00047 #define NVPDB_VERSION_MAINTENANCE   0
00048 
00049 #ifdef DEBUG
00050 # define NVPDB_PRINTF(x) \
00051             PR_BEGIN_MACRO \
00052               printf x ; \
00053               printf(", %s %d\n", __FILE__, __LINE__); \
00054             PR_END_MACRO 
00055 #else
00056 # define NVPDB_PRINTF(x)
00057 #endif
00058 
00059 PRBool
00060 nsNameValuePairDB::CheckHeader()
00061 {
00062   const char *name, *value;
00063   int num, major, minor, maintenance;
00064   PRBool foundVersion = PR_FALSE;
00065 
00066   if (!mFile)
00067     return PR_FALSE;
00068 
00069   if (fseek(mFile, 0L, SEEK_SET) != 0)
00070     return PR_FALSE;
00071   mCurrentGroup = 0;
00072   mAtEndOfGroup = PR_FALSE;
00073   while (GetNextElement(&name, &value) > 0) {
00074     if (*name == '\0') // ignore comments
00075       continue;
00076     if (strcmp(name, "Version")==0) {
00077       foundVersion = PR_TRUE;
00078       num = sscanf(value, "%d.%d.%d", &major, &minor, &maintenance);
00079       if (num != 3) {
00080         NVPDB_PRINTF(("failed to parse version number (%s)", value));
00081         return PR_FALSE;
00082       }
00083 
00084       // NVPDB_VERSION_MAJOR
00085       // It is presumed that major versions are not backwards compatibile.
00086       if (major != NVPDB_VERSION_MAJOR) {
00087         NVPDB_PRINTF(("version major %d != %d", major, NVPDB_VERSION_MAJOR));
00088         return PR_FALSE;
00089       }
00090 
00091       // NVPDB_VERSION_MINOR
00092       // It is presumed that minor versions are backwards compatible
00093       // but will have additional features.
00094       // Put any tests related to minor versions here.
00095 
00096       // NVPDB_VERSION_MAINTENANCE
00097       // It is presumed that maintenance versions are backwards compatible,
00098       // have no new features, but can have bug fixes.
00099       // Put any tests related to maintenance versions here.
00100 
00101       mMajorNum = major;
00102       mMinorNum = minor;
00103       mMaintenanceNum = maintenance;
00104     }
00105   }
00106 
00107   return foundVersion;
00108 }
00109 
00110 //
00111 // Re-get an element. Used if the element is bigger than
00112 // the buffer that was first passed in
00113 //
00114 // PRInt32 GetCurrentElement(const char** aName, const char** aValue,
00115 //                           char *aBuffer, PRUint32 aBufferLen);
00116 //
00117 // to implement this the GetNextElement methods need to save
00118 // the file position so this routine can seek backward to it.
00119 //
00120 
00121 PRInt32
00122 nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue)
00123 {
00124   return GetNextElement(aName, aValue, mBuf, sizeof(mBuf));
00125 }
00126 
00127 //
00128 // Get the next element
00129 //
00130 // returns 1 if complete element read
00131 // return  0 on end of file
00132 // returns a negative number on error
00133 //           if error < -NVPDB_MIN_BUFLEN
00134 //               then the value is the negative of the needed buffer len
00135 //
00136 //
00137 PRInt32
00138 nsNameValuePairDB::GetNextElement(const char** aName, const char** aValue,
00139                                   char *aBuffer, PRUint32 aBufferLen)
00140 {
00141   char *line, *name, *value;
00142   unsigned int num;
00143   int len;
00144   unsigned int groupNum;
00145 
00146   *aName  = "";
00147   *aValue = "";
00148 
00149   if (aBufferLen < NVPDB_MIN_BUFLEN) {
00150     return NVPDB_BUFFER_TOO_SMALL;
00151   }
00152 
00153   if (mAtEndOfGroup) {
00154     return NVPDB_END_OF_GROUP;
00155   }
00156 
00157   //
00158   // Get a line
00159   //
00160   line = fgets(aBuffer, aBufferLen, mFile);
00161   if (!line) {
00162     if (feof(mFile)) { // end of file
00163       mAtEndOfGroup = PR_TRUE;
00164       mAtEndOfCatalog = PR_TRUE;
00165       return NVPDB_END_OF_FILE;
00166     }
00167     return NVPDB_FILE_IO_ERROR;
00168   }
00169 
00170   //
00171   // Check we got a complete line
00172   //
00173   len = strlen(line);
00174   NS_ASSERTION(len!=0, "an empty string is invalid");
00175   if (len == 0)
00176     return NVPDB_GARBLED_LINE;
00177   if (line[len-1] != '\n') {
00178     len++; // space for the line terminator
00179     while (1) {
00180       int val = getc(mFile);
00181       if (val == EOF)
00182         return -len;
00183       len++;
00184       if (val == '\n')
00185         return -len;
00186     }
00187   }
00188   len--;
00189   line[len] = '\0';
00190   //NVPDB_PRINTF(("line = (%s)", line));
00191 
00192   //
00193   // Check the group number
00194   //
00195   num = sscanf(line, "%u", &groupNum);
00196   if ((num != 1) || (groupNum != (unsigned)mCurrentGroup))
00197     return NVPDB_END_OF_GROUP;
00198 
00199   //
00200   // Get the name
00201   //
00202   name = strchr(line, ' ');
00203   if ((!name) || (name[1]=='\0'))
00204     return NVPDB_GARBLED_LINE;
00205   name++;
00206 
00207   //
00208   // If it is a comment 
00209   //   return a blank name (strlen(*aName)==0)
00210   //   return the comment in the value field
00211   //
00212   if (*name == '#') {
00213     *aValue = name;
00214     return 1;
00215   }
00216 
00217   //
00218   // Get the value
00219   //
00220   value = strchr(name, '=');
00221   if (!value)
00222     return NVPDB_GARBLED_LINE;
00223   *value = '\0';
00224   value++;
00225 
00226   //
00227   // Check for end of group
00228   //
00229   if (strcmp(name,"end")==0) {
00230     mAtEndOfGroup = PR_TRUE;
00231     return NVPDB_END_OF_GROUP;
00232   }
00233 
00234   //
00235   // Got the name and value
00236   //
00237   *aName = name;
00238   *aValue = value;
00239   return 1;
00240 }
00241 
00242 PRBool
00243 nsNameValuePairDB::GetNextGroup(const char** aType)
00244 {
00245   return GetNextGroup(aType, nsnull, 0);
00246 }
00247 
00248 PRBool
00249 nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName)
00250 {
00251   return GetNextGroup(aType, aName, strlen(aName));
00252 }
00253 
00254 PRBool
00255 nsNameValuePairDB::GetNextGroup(const char** aType, const char* aName, int aLen)
00256 {
00257   const char *name, *value;
00258   long pos = 0;
00259 
00260   *aType = "";
00261 
00262   if (mAtEndOfCatalog)
00263     return PR_FALSE;
00264 
00265   //
00266   // Move to end of current Group
00267   //
00268   while (GetNextElement(&name, &value) > 0) 
00269     continue;
00270   mCurrentGroup++;
00271   mAtEndOfGroup = PR_FALSE;
00272   // save current pos in case this in not the desired type 
00273   // and we need to backup
00274   if (aName)
00275     pos = ftell(mFile);
00276 
00277   // check if there are more Groups
00278   if (GetNextElement(&name, &value) <= 0) {
00279     mAtEndOfGroup = PR_TRUE;
00280     mAtEndOfCatalog = PR_TRUE;
00281     return PR_FALSE;
00282   }
00283   if (strcmp(name,"begin"))
00284     goto GetNext_Error;
00285 
00286   // check if this is the desired type
00287   if (aName) {
00288     if (strncmp(value,aName,aLen)) {
00289       fseek(mFile, pos, SEEK_SET);
00290       mCurrentGroup--;
00291       mAtEndOfGroup = PR_TRUE;
00292       return PR_FALSE;
00293     }
00294   }
00295 
00296   *aType = value;
00297   return PR_TRUE;
00298 
00299 GetNext_Error:
00300   mError = PR_TRUE;
00301   NVPDB_PRINTF(("GetNext_Error"));
00302   return PR_FALSE;
00303 }
00304 
00305 nsNameValuePairDB::nsNameValuePairDB()
00306 {
00307   mFile = nsnull;
00308   mBuf[0] = '\0';
00309   mMajorNum = 0;
00310   mMinorNum = 0;
00311   mMaintenanceNum = 0;
00312   mCurrentGroup = 0;
00313   mAtEndOfGroup = PR_FALSE;
00314   mAtEndOfCatalog = PR_FALSE;
00315   mError = PR_FALSE;
00316 }
00317 
00318 nsNameValuePairDB::~nsNameValuePairDB()
00319 {
00320   if (mFile) {
00321     fclose(mFile);
00322     mFile = nsnull;
00323   }
00324 }
00325 
00326 PRBool
00327 nsNameValuePairDB::OpenForRead(const nsACString & aCatalogName) // native charset
00328 {
00329   nsresult result;
00330 
00331   nsCOMPtr<nsILocalFile> local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID,
00332                                                         &result);
00333   if (NS_FAILED(result))
00334     goto error_return;
00335 
00336   local_file->InitWithNativePath(aCatalogName);
00337   local_file->OpenANSIFileDesc("r", &mFile);
00338   if (mFile && CheckHeader())
00339     return PR_TRUE;
00340 
00341 error_return:
00342   mError = PR_TRUE;
00343   NVPDB_PRINTF(("OpenForRead error"));
00344   return PR_FALSE;
00345 }
00346 
00347 PRBool
00348 nsNameValuePairDB::OpenTmpForWrite(const nsACString& aCatalogName) // native charset
00349 {
00350   nsresult result;
00351   nsCOMPtr<nsILocalFile> local_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID,
00352                                                         &result);
00353   if (NS_FAILED(result))
00354     return PR_FALSE;
00355   local_file->InitWithNativePath(aCatalogName + NS_LITERAL_CSTRING(".tmp"));
00356   local_file->OpenANSIFileDesc("w+", &mFile);
00357   if (mFile == nsnull)
00358     return PR_FALSE;
00359 
00360   // Write the header
00361   mAtEndOfGroup = PR_TRUE;
00362   mCurrentGroup = -1;
00363   PutStartGroup("Header");
00364   char buf[64];
00365   PutElement("", "########################################");
00366   PutElement("", "#                                      #");
00367   PutElement("", "#          Name Value Pair DB          #");
00368   PutElement("", "#                                      #");
00369   PutElement("", "#   This is a program generated file   #");
00370   PutElement("", "#                                      #");
00371   PutElement("", "#             Do not edit              #");
00372   PutElement("", "#                                      #");
00373   PutElement("", "########################################");
00374   PR_snprintf(buf, sizeof(buf), "%d.%d.%d", NVPDB_VERSION_MAJOR,
00375                 NVPDB_VERSION_MINOR, NVPDB_VERSION_MAINTENANCE);
00376   PutElement("Version", buf);
00377   PutEndGroup("Header");
00378 
00379   return PR_TRUE;
00380 }
00381 
00382 PRBool
00383 nsNameValuePairDB::PutElement(const char* aName, const char* aValue)
00384 {
00385   if (mAtEndOfGroup) {
00386     mError = PR_TRUE;
00387     NVPDB_PRINTF(("PutElement_Error"));
00388     return PR_FALSE;
00389   }
00390 
00391   if ((!*aName) && (*aValue == '#'))
00392     fprintf(mFile, "%u %s\n", mCurrentGroup, aValue);
00393   else
00394     fprintf(mFile, "%u %s=%s\n", mCurrentGroup, aName, aValue);
00395 #ifdef DEBUG
00396   fflush(mFile);
00397 #endif
00398   return PR_TRUE;
00399 }
00400 
00401 PRBool
00402 nsNameValuePairDB::PutEndGroup(const char* aType)
00403 {
00404   if (mAtEndOfGroup) {
00405     mError = PR_TRUE;
00406     NVPDB_PRINTF(("PutEndGroup_Error"));
00407     return PR_FALSE;
00408   }
00409 
00410   mAtEndOfGroup = PR_TRUE;
00411   fprintf(mFile, "%u end=%s\n", mCurrentGroup, aType);
00412 #ifdef DEBUG
00413   fflush(mFile);
00414 #endif
00415   return PR_TRUE;
00416 }
00417 
00418 PRBool
00419 nsNameValuePairDB::PutStartGroup(const char* aType)
00420 {
00421   if (!mAtEndOfGroup) {
00422     mError = PR_TRUE;
00423     NVPDB_PRINTF(("PutStartGroup_Error"));
00424 #ifdef DEBUG
00425     fflush(mFile);
00426 #endif
00427     return PR_FALSE;
00428   }
00429 
00430   mAtEndOfGroup = PR_FALSE;
00431   mCurrentGroup++;
00432   fprintf(mFile, "%u begin=%s\n", mCurrentGroup, aType);
00433 #ifdef DEBUG
00434   fflush(mFile);
00435 #endif
00436   return PR_TRUE;
00437 }
00438 
00439 PRBool
00440 nsNameValuePairDB::RenameTmp(const char* aCatalogName)
00441 {
00442   nsresult rv;
00443   nsCOMPtr<nsILocalFile> dir;
00444   PRBool exists = PR_FALSE;
00445   nsCAutoString old_name(aCatalogName);
00446   nsDependentCString current_name(aCatalogName);
00447   nsCAutoString tmp_name(aCatalogName);
00448   nsCAutoString old_name_tail;
00449   nsCAutoString current_name_tail;
00450   nsCOMPtr<nsILocalFile> old_file;
00451   nsCOMPtr<nsILocalFile> current_file;
00452   nsCOMPtr<nsILocalFile> tmp_file;
00453   nsCAutoString parent_dir;
00454   nsCAutoString parent_path;
00455   nsCAutoString cur_path;
00456 
00457   //
00458   // Split the parent dir and file name
00459   //
00460   PRInt32 slash = 0, last_slash = -1;
00461   nsCAutoString fontDirName(aCatalogName);
00462   // RFindChar not coded so do it by hand
00463   while ((slash=fontDirName.FindChar('/', slash))>=0) {
00464     last_slash = slash;
00465     slash++;
00466   }
00467   if (last_slash < 0)
00468     goto Rename_Error;
00469 
00470   fontDirName.Left(parent_dir, last_slash);
00471   dir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
00472   if (NS_FAILED(rv))
00473     goto Rename_Error;
00474   dir->InitWithNativePath(parent_dir);
00475   dir->GetNativePath(parent_path);
00476 
00477   if (!mAtEndOfGroup || mError)
00478     goto Rename_Error;
00479 
00480   //
00481   // check that we have a tmp copy
00482   //
00483   tmp_name.Append(".tmp");
00484   tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
00485   if (NS_FAILED(rv))
00486     goto Rename_Error;
00487   tmp_file->InitWithNativePath(tmp_name);
00488   tmp_file->Exists(&exists);
00489   if (!exists)
00490     goto Rename_Error;
00491 
00492   //
00493   // get rid of any old copy
00494   //
00495   old_name.Append(".old");
00496   old_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
00497   if (NS_FAILED(rv))
00498     goto Rename_Error;
00499   old_file->InitWithNativePath(old_name);
00500 
00501   //
00502   // Check we have a current copy
00503   //
00504   current_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
00505   if (NS_FAILED(rv))
00506     goto Rename_Error;
00507   current_file->InitWithNativePath(current_name);
00508   current_file->Exists(&exists);
00509   if (exists) {
00510     //
00511     // Rename the current copy to old
00512     //
00513     current_file->GetNativePath(cur_path);
00514     old_name.Right(old_name_tail, old_name.Length() - last_slash - 1);
00515     rv = current_file->MoveToNative(dir, old_name_tail);
00516     if (NS_FAILED(rv))
00517       goto Rename_Error;
00518   }
00519 
00520   //
00521   // Rename the tmp to current
00522   //
00523   current_name_tail = Substring(current_name, last_slash+1,
00524                                 current_name.Length() - (last_slash + 1));
00525   rv = tmp_file->MoveToNative(dir, current_name_tail);
00526   if (NS_FAILED(rv))
00527     goto Rename_Error;
00528 
00529   //
00530   // remove the previous copy
00531   //
00532   if (exists) {
00533     old_file->Remove(PR_FALSE);
00534   }
00535 
00536   return PR_TRUE;
00537 
00538 Rename_Error:
00539   mError = PR_TRUE;
00540   NVPDB_PRINTF(("Rename_Error"));
00541   return PR_FALSE;
00542 }
00543