www.pudn.com > MP3CORD.rar > beosprefs.cpp
/*____________________________________________________________________________
FreeAmp - The Free MP3 Player
Portions Copyright (C) 1998-1999 EMusic.com
Portions Copyright (C) 1999 Mark H. Weaver
This program 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 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: beosprefs.cpp,v 1.7 1999/12/10 04:25:35 elrod Exp $
____________________________________________________________________________*/
#include "config.h"
#ifdef HAVE_ERRNO_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "utility.h"
#include "beosprefs.h"
#include "prefixprefs.h"
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
// THis module uses the strdup function to pass strings to the lists class
// (among others) which will in turn use delete to reclaim the memory.
// This is NOT VALID! A strdup()ed string must be free()ed, not deleted!
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
// default values
const char* kDefaultLibraryPath = "/boot/home/config/add-ons/freeamp";
const char* kDefaultUI = "FreeAmp.ui";
const char* kDefaultTextUI = "FreeAmpCmd.ui";
const char* kDefaultPMO = "Soundcard.pmo";
class LibDirFindHandle {
public:
vector *m_pLibDirs;
int32 m_current;
};
static
Error
ScanQuotableString(const char *in, const char **endPtr,
char *out, int32 *lengthPtr, const char *delim)
{
bool quoted = false;
int32 length = 0;
// In case of error, clear return fields now
if (endPtr)
*endPtr = 0;
if (lengthPtr)
*lengthPtr = 0;
if (*in == '"')
{
in++;
quoted = true;
}
while (*in)
{
if (*in == '"' && quoted)
{
in++;
break;
}
else if ((isspace(*in) || strchr(delim, *in)) && !quoted)
break;
else if (*in == '\\' && quoted)
{
char ch;
in++;
switch (*in)
{
case '\\': ch = '\\'; in++; break;
case '"': ch = '"'; in++; break;
case 'n': ch = '\n'; in++; break;
case 't': ch = '\t'; in++; break;
default:
if (in[0] < '0' || in[0] > '3' ||
in[1] < '0' || in[1] > '7' ||
in[2] < '0' || in[2] > '7')
{
*endPtr = in;
return kError_SyntaxError;
}
ch = (char)(((in[0] - '0') << 6) |
((in[1] - '0') << 3) |
(in[2] - '0'));
in += 3;
break;
}
if (out)
*out++ = ch;
length++;
}
else if (*in == '\n')
{
if (quoted)
{
*endPtr = in;
return kError_SyntaxError;
}
break;
}
else
{
if (out)
*out++ = *in;
length++;
in++;
}
}
if (out)
*out++ = '\0';
if (lengthPtr)
*lengthPtr = length;
if (endPtr)
*endPtr = in;
return kError_NoErr;
}
static
char *
ReadQuotableString(const char *in, const char **endPtr, const char *delim)
{
Error error;
int32 length;
error = ScanQuotableString(in, endPtr, 0, &length, delim);
if (IsError(error))
return 0;
char *out = new char[length + 1];
ScanQuotableString(in, 0, out, 0, delim);
return out;
}
static
void
PrepareQuotableString(const char *str, bool *needQuotes,
char *out, int32 *lengthPtr, const char *delim)
{
int32 unquotedLength = 0;
int32 quotedLength = 2;
bool quoted = *needQuotes;
if (out && quoted)
*out++ = '"';
if (*str == '"')
*needQuotes = true;
for (; *str; str++)
{
if (strchr(delim, *str) || *str == ' ')
{
*needQuotes = true;
quotedLength++;
if (out)
*out++ = *str;
}
else if (*str == '\\' || *str == '"')
{
quotedLength += 2;
unquotedLength++;
if (out)
{
if (quoted)
*out++ = '\\';
*out++ = *str;
}
}
else if (*str == '\n' || *str == '\t')
{
*needQuotes = true;
quotedLength += 2;
if (out)
{
*out++ = '\\';
*out++ = (*str == '\n') ? 'n' : 't';
}
}
else if (isspace(*str) || !isgraph(*str))
{
*needQuotes = true;
quotedLength += 4;
if (out)
{
unsigned char ch = *str;
*out++ = '\\';
*out++ = '0' + (ch >> 6);
*out++ = '0' + ((ch >> 3) & 7);
*out++ = '0' + (ch & 7);
}
}
else
{
unquotedLength++;
quotedLength++;
if (out)
*out++ = *str;
}
}
if (out)
{
if (quoted)
*out++ = '"';
*out++ = '\0';
}
if (lengthPtr)
*lengthPtr = *needQuotes ? quotedLength : unquotedLength;
}
static
char *
WriteQuotableString(const char *str, const char *delim)
{
int32 length;
bool needQuotes = false;
PrepareQuotableString(str, &needQuotes, 0, &length, delim);
char *out = new char[length + 1];
PrepareQuotableString(str, &needQuotes, out, 0, delim);
return out;
}
static
void
AppendToString(char **destPtr, const char *src, int32 length)
{
char *oldStr = *destPtr;
int32 oldLen = oldStr ? strlen(oldStr) : 0;
int32 newLen = oldLen + length;
char *newStr = new char[newLen + 1];
if (oldStr)
{
memcpy(newStr, oldStr, oldLen);
delete[] oldStr;
}
memcpy(newStr + oldLen, src, length);
newStr[newLen] = '\0';
*destPtr = newStr;
}
#if 0
static
void
Test(BeOSPrefs *prefs)
{
const char *delim = "#";
static const char *tests[] = {
"# \nfoo\tbar\r\017blah\371\321woz\\top\"dog",
"FooBar",
"Foo Bar",
" Foo Bar",
"\"Foo",
"Foo\"",
"Fo\"o",
"Fo\\o",
"#Foo",
"Fo#o",
":Foo",
"Fo:o",
"Fo\no",
"Fo\to",
"Fo\ro",
"Fo\017o",
"Fo\371o",
"Fo\123o",
"Foo",
0
};
prefs->SetPrefString(tests[0], tests[0]);
int i;
char quotedStr[256], recoveredStr[256];
bool needQuotes;
int len1, len2;
const char *end;
for (i = 0; tests[i]; i++)
{
needQuotes = false;
PrepareQuotableString(tests[i], &needQuotes, 0, &len1, delim);
PrepareQuotableString(tests[i], &needQuotes, quotedStr,
&len2, delim);
if (len1 != len2 || len1 != strlen(quotedStr))
printf("ERROR: len1: %d, len2: %d, len: %d\n", len1, len2,
strlen(quotedStr));
puts(quotedStr);
ScanQuotableString(quotedStr, &end, recoveredStr, &len2, delim);
if (len2 != strlen(recoveredStr) || strcmp(tests[i], recoveredStr))
printf("ERROR: len2: %d, len: %d\n", len2,
strlen(recoveredStr));
}
}
#endif
BeOSPrefEntry::
~BeOSPrefEntry()
{
if (prefix) delete[] prefix;
if (key) delete[] key;
if (separator) delete[] separator;
if (value) delete[] value;
if (suffix) delete[] suffix;
}
BeOSPrefs::
BeOSPrefs()
: m_prefsFilePath(0),
m_saveEnable(true),
m_changed(false),
m_errorLineNumber(0)
{
BPath prefPath;
if ( find_directory( B_USER_SETTINGS_DIRECTORY, &prefPath ) < B_NO_ERROR )
{
m_saveEnable = false;
return;
}
prefPath.Append( "freeamp.org" );
create_directory( prefPath.Path(), 0755 );
prefPath.Append( "freeamp_prefs" );
// Compute pathname of preferences file
m_prefsFilePath = new char[ strlen( prefPath.Path() ) + 1 ];
strcpy( m_prefsFilePath, prefPath.Path() );
FILE *prefsFile = fopen(m_prefsFilePath, "r");
if (!prefsFile && errno != ENOENT)
{
m_saveEnable = false;
fprintf(stderr, "Error opening %s: %s\n",
m_prefsFilePath, strerror(errno));
}
if (prefsFile)
{
BeOSPrefEntry *entry = new BeOSPrefEntry;
char buffer[1024];
char *p;
int lineNumber = 0;
while (fgets(buffer, sizeof(buffer) - 1, prefsFile))
{
lineNumber++;
p = buffer;
while (*p && isspace(*p))
p++;
if (*p == '#')
{
// No data on this line, skip to the end of the comment
while (*p)
p++;
}
if (p > buffer)
AppendToString(&entry->prefix, buffer, p - buffer);
if (*p)
{
char *end;
// char *out;
int32 length;
entry->key = ReadQuotableString(p, (const char **)&end, ":#");
if (entry->key && !m_ht.Value(entry->key))
m_ht.Insert(entry->key, entry);
else if (!m_errorLineNumber)
m_errorLineNumber = lineNumber;
p = end;
while (*p && isspace(*p))
p++;
if (*p == ':')
p++;
else if (!m_errorLineNumber)
m_errorLineNumber = lineNumber;
while (*p && isspace(*p))
p++;
AppendToString(&entry->separator, end, p - end);
entry->value = ReadQuotableString(p, (const char **)&end, "#");
if (!entry->value && !m_errorLineNumber)
m_errorLineNumber = lineNumber;
p = end;
length = strlen(p);
if (p[length - 1] != '\n')
{
p[length++] = '\n';
p[length] = '\0';
}
AppendToString(&entry->suffix, p, length);
m_entries.push_back(entry);
entry = new BeOSPrefEntry;
}
}
if (entry->prefix)
m_entries.push_back(entry);
else
delete entry;
if (m_errorLineNumber)
m_saveEnable = false;
fclose(prefsFile);
}
SetDefaults();
Save();
// Test(this);
}
BeOSPrefs::
~BeOSPrefs()
{
Save();
delete[] m_prefsFilePath;
}
Error
BeOSPrefs::
SetDefaults()
{
char buf[1024];
uint32 size;
char cwd[_MAX_PATH]= {0x00};
getcwd(cwd, sizeof(cwd));
// set install directory value
size = sizeof(buf);
if (GetPrefString(kInstallDirPref, buf, &size) == kError_NoPrefValue)
SetPrefString(kInstallDirPref, cwd);
// set default freeamp library path value
size = sizeof(buf);
if (GetPrefString(kLibraryPathPref, buf, &size) == kError_NoPrefValue)
SetPrefString(kLibraryPathPref, kDefaultLibraryPath);
// set default ui value
size = sizeof(buf);
if (GetPrefString(kUIPref, buf, &size) == kError_NoPrefValue)
SetPrefString(kUIPref, kDefaultUI);
// set default text ui value
size = sizeof(buf);
if (GetPrefString(kTextUIPref, buf, &size) == kError_NoPrefValue)
SetPrefString(kTextUIPref, kDefaultTextUI);
// set default pmo value
size = sizeof(buf);
if (GetPrefString(kPMOPref, buf, &size) == kError_NoPrefValue)
SetPrefString(kPMOPref, kDefaultPMO);
size = sizeof(buf);
if (GetPrefString(kDatabaseDirPref, buf, &size) == kError_NoPrefValue) {
string tempdir = FreeampDir(NULL);
tempdir += "/db";
SetPrefString(kDatabaseDirPref, tempdir.c_str());
}
size = sizeof(buf);
if (GetPrefString(kSaveMusicDirPref, buf, &size) == kError_NoPrefValue) {
string tempdir = FreeampDir(NULL);
tempdir += "/MyMusic";
SetPrefString(kSaveMusicDirPref, tempdir.c_str());
}
Preferences::SetDefaults();
return kError_NoErr;
}
Error
BeOSPrefs::
Save()
{
if (!m_saveEnable || !m_changed)
return kError_NoErr;
// XXX: We should check the modification date of the file,
// and refuse to save if it's been modified since we
// read it.
m_changed = false;
const char *tmpSuffix = ".tmp";
char *tmpFilePath = new char[strlen(m_prefsFilePath) +
strlen(tmpSuffix) + 1];
strcpy(tmpFilePath, m_prefsFilePath);
strcat(tmpFilePath, tmpSuffix);
const char *bakSuffix = ".bak";
char *bakFilePath = new char[strlen(m_prefsFilePath) +
strlen(bakSuffix) + 1];
strcpy(bakFilePath, m_prefsFilePath);
strcat(bakFilePath, bakSuffix);
{
FILE *prefsFile = fopen(tmpFilePath, "w+");
if (!prefsFile)
{
delete[] tmpFilePath;
delete[] bakFilePath;
return kError_UnknownErr; // XXX: Be more informative
}
m_mutex.Acquire();
int32 numEntries = m_entries.size();
int32 i;
BeOSPrefEntry *entry;
for (i = 0; i < numEntries; i++)
{
entry = m_entries[i];
if (entry->prefix)
fputs(entry->prefix, prefsFile);
if (entry->key && entry->separator && entry->value)
{
char *outStr;
outStr = WriteQuotableString(entry->key, ":#");
fputs(outStr, prefsFile);
delete[] outStr;
fputs(entry->separator, prefsFile);
outStr = WriteQuotableString(entry->value, "#");
fputs(outStr, prefsFile);
delete[] outStr;
}
if (entry->suffix)
fputs(entry->suffix, prefsFile);
}
m_mutex.Release();
if (ferror(prefsFile))
{
fclose(prefsFile);
delete[] tmpFilePath;
delete[] bakFilePath;
return kError_UnknownErr; // XXX: Be more informative
}
fclose(prefsFile);
}
if (rename(m_prefsFilePath, bakFilePath) && errno != ENOENT)
{
delete[] tmpFilePath;
delete[] bakFilePath;
return kError_UnknownErr; // XXX: Be more informative
}
if (rename(tmpFilePath, m_prefsFilePath))
rename(bakFilePath, m_prefsFilePath);
delete[] tmpFilePath;
delete[] bakFilePath;
return kError_NoErr;
}
Preferences *
BeOSPrefs::
ComponentPrefs(const char *componentName)
{
return new PrefixPrefs(this, componentName);
}
Error
BeOSPrefs::
GetPrefString(const char* pref, char* buf, uint32* len)
{
BeOSPrefEntry *entry;
// int32 index;
m_mutex.Acquire();
buf[0] = '\0';
entry = m_ht.Value(pref);
if (!entry || !entry->value)
{
*len = 0;
m_mutex.Release();
return kError_NoPrefValue;
}
char *value = entry->value;
uint32 value_len = strlen(value) + 1;
if (value_len > *len)
{
m_mutex.Release();
return kError_BufferTooSmall;
}
memcpy(buf, value, value_len);
*len = value_len;
m_mutex.Release();
return kError_NoErr;
}
Error
BeOSPrefs::
SetPrefString(const char* pref, const char* buf)
{
BeOSPrefEntry *entry;
m_mutex.Acquire();
entry = m_ht.Value(pref);
if (entry)
{
delete[] entry->value;
entry->value = 0;
}
else
{
entry = new BeOSPrefEntry;
AppendToString(&entry->key, pref, strlen(pref));
AppendToString(&entry->separator, ": ", 2);
AppendToString(&entry->suffix, "\n", 1);
m_entries.push_back(entry);
m_ht.Insert(pref, entry);
}
AppendToString(&entry->value, buf, strlen(buf));
m_changed = true;
m_mutex.Release();
return kError_NoErr;
}
char *BeOSPrefs::m_libDirs = 0;
const char *
BeOSPrefs::
GetLibDirs()
{
if (!m_libDirs) {
uint32 size = 1024;
m_libDirs = new char[size];
GetPrefString(kLibraryPathPref, m_libDirs, &size);
}
return m_libDirs;
}
LibDirFindHandle *
BeOSPrefs::
GetFirstLibDir(char *path, uint32 *len)
{
// if no FREEAMP_PATH, use kLibraryPathPref
// if FREEAMP_PATH, then its FREEAMP_PATH
char *pEnv = getenv(FREEAMP_PATH_ENV);
char *pPath = NULL;
if (pEnv) {
// cout << "Using env: " << pEnv << endl;
pPath = strdup_new(pEnv);
} else {
pPath = strdup_new(GetLibDirs());
}
pEnv = pPath;
LibDirFindHandle *hLibDirFind = new LibDirFindHandle();
hLibDirFind->m_pLibDirs = new vector();
hLibDirFind->m_current = 0;
char *pCol = (char *)1;
char *pPart = pPath;
while (pCol) {
pCol = strchr(pPart,':');
if (pCol) *pCol = '\0';
char *pFoo = strdup_new(pPart);
hLibDirFind->m_pLibDirs->push_back(pFoo);
pPart = pCol + sizeof(char);
}
pPath = (*hLibDirFind->m_pLibDirs)[0];
if (pPath) {
strncpy(path,pPath,*len);
*len = strlen(pPath);
} else {
*path = '\0';
*len = 0;
delete hLibDirFind->m_pLibDirs;
delete hLibDirFind;
hLibDirFind = 0;
}
if (pEnv) delete pEnv;
//cout << "returning " << path << endl;
return hLibDirFind;
}
Error
BeOSPrefs::
GetNextLibDir(LibDirFindHandle *hLibDirFind, char *path, uint32 *len)
{
if (hLibDirFind) {
hLibDirFind->m_current++;
char *pPath = (*hLibDirFind->m_pLibDirs)[hLibDirFind->m_current];
if ( pPath &&
hLibDirFind->m_current < (int32)hLibDirFind->m_pLibDirs->size() )
{
strncpy(path,pPath,*len);
*len = strlen(pPath);
// cout << "returning next: " << path << endl;
return kError_NoErr;
} else {
*path = '\0';
*len = 0;
// cout << "returning no next " << path << endl;
return kError_NoMoreLibDirs;
}
}
return kError_NoMoreLibDirs;
}
Error
BeOSPrefs::
GetLibDirClose(LibDirFindHandle *hLibDirFind)
{
if (hLibDirFind) {
vector ::iterator i = hLibDirFind->m_pLibDirs->begin();
for (; i != hLibDirFind->m_pLibDirs->end(); i++)
delete *i;
delete hLibDirFind->m_pLibDirs;
delete hLibDirFind;
}
return kError_NoErr;
}