diff --git a/src/servers/registrar/Jamfile b/src/servers/registrar/Jamfile index d9c551fb33..d5398cf64e 100644 --- a/src/servers/registrar/Jamfile +++ b/src/servers/registrar/Jamfile @@ -18,6 +18,7 @@ Server obos_registrar : RecentEntries.cpp Registrar.cpp RosterAppInfo.cpp + RosterSettingsCharStream.cpp TRoster.cpp Watcher.cpp WatchingService.cpp diff --git a/src/servers/registrar/RecentApps.cpp b/src/servers/registrar/RecentApps.cpp index d1d3070829..355d6d756b 100644 --- a/src/servers/registrar/RecentApps.cpp +++ b/src/servers/registrar/RecentApps.cpp @@ -37,8 +37,6 @@ #include #include -#include - #define DBG(x) (x) //#define DBG(x) #define OUT printf @@ -189,6 +187,45 @@ RecentApps::Clear() return B_OK; } +// Print +/*! \brief Dumps the the current list of apps to stdout. +*/ +status_t +RecentApps::Print() +{ + std::list::iterator item; + int counter = 1; + for (item = fAppList.begin(); + item != fAppList.end(); + item++) + { + printf("%d: '%s'\n", counter++, item->c_str()); + } +} + +// Save +/*! \brief Outputs a textual representation of the current recent + apps list to the given file stream. + +*/ +status_t +RecentApps::Save(FILE* file) +{ + status_t error = file ? B_OK : B_BAD_VALUE; + if (!error) { + fprintf(file, "# Recent applications\n"); + std::list::iterator item; + for (item = fAppList.begin(); + item != fAppList.end(); + item++) + { + fprintf(file, "RecentApp %s\n", item->c_str()); + } + fprintf(file, "\n"); + } + return error; +} + // GetRefForApp /*! \brief Fetches an \c entry_ref for the application with the given signature. @@ -211,18 +248,3 @@ RecentApps::GetRefForApp(const char *appSig, entry_ref *result) return err; } -// Print -/*! \brief Dumps the the current list of apps to stdout. -*/ -status_t -RecentApps::Print() -{ - std::list::iterator item; - int counter = 1; - for (item = fAppList.begin(); - item != fAppList.end(); - item++) - { - printf("%d: '%s'\n", counter++, item->c_str()); - } -} diff --git a/src/servers/registrar/RecentApps.h b/src/servers/registrar/RecentApps.h index 1db9b20299..7037ad71cd 100644 --- a/src/servers/registrar/RecentApps.h +++ b/src/servers/registrar/RecentApps.h @@ -33,8 +33,13 @@ #include #include +#include #include +namespace BPrivate { + class TRoster; +} + struct entry_ref; class RecentApps { @@ -47,7 +52,11 @@ public: status_t Get(int32 maxCount, BMessage *list); status_t Clear(); status_t Print(); + status_t Save(FILE* file); private: + friend class BPrivate::TRoster; + // For loading from disk + static status_t GetRefForApp(const char *appSig, entry_ref *result); std::list fAppList; diff --git a/src/servers/registrar/RecentEntries.cpp b/src/servers/registrar/RecentEntries.cpp index 86b5846547..c57d689961 100644 --- a/src/servers/registrar/RecentEntries.cpp +++ b/src/servers/registrar/RecentEntries.cpp @@ -32,6 +32,7 @@ #include #include #include +#include // From the Storage Kit #include #include #include @@ -39,8 +40,7 @@ #include #include -#include -#include +#include #define DBG(x) (x) //#define DBG(x) @@ -50,20 +50,22 @@ // recent_entry //------------------------------------------------------------------------------ -/*! \brief An recent entry and the corresponding signature of the application - that launched/used/opened/viewed/whatevered it. -*/ -struct recent_entry { - recent_entry(const entry_ref *ref, const char *appSig); - entry_ref ref; - std::string sig; -private: - recent_entry(); -}; +/*! \struct recent_entry -recent_entry::recent_entry(const entry_ref *ref, const char *appSig) + \brief A recent entry, the corresponding signature of the application + that launched/used/opened/viewed/whatevered it, and an index used for + keeping track of orderings when loading/storing the recent entries list + from/to disk. + +*/ + +/*! \brief Creates a new recent_entry object. +*/ +recent_entry::recent_entry(const entry_ref *ref, const char *appSig, + uint32 index) : ref(ref ? *ref : entry_ref()) , sig(appSig) + , index(index) { } @@ -136,7 +138,7 @@ RecentEntries::Add(const entry_ref *ref, const char *appSig) } // Add this entry to the front of the list - recent_entry *entry = new(nothrow) recent_entry(ref, appSig); + recent_entry *entry = new(nothrow) recent_entry(ref, appSig, 0); error = entry ? B_OK : B_NO_MEMORY; if (!error) fEntryList.push_front(entry); @@ -229,13 +231,95 @@ RecentEntries::Print() item != fEntryList.end(); item++) { - printf("%d: device == '%ld', dir == '%lld', name == '%s', app == '%s'\n", + printf("%d: device == '%ld', dir == '%lld', name == '%s', app == '%s', index == %ld\n", counter++, (*item)->ref.device, (*item)->ref.directory, (*item)->ref.name, - (*item)->sig.c_str()); + (*item)->sig.c_str(), (*item)->index); } } -// RecentEntries +// Save +status_t +RecentEntries::Save(FILE* file, const char *description, const char *tag) +{ + status_t error = file ? B_OK : B_BAD_VALUE; + if (!error) { + fprintf(file, "# %s\n", description); + + /* In order to write our entries out in the format used by the + Roster settings file, we need to collect all the signatures + for each entry in one place, while at the same time updating + the index values for each entry/sig pair to reflect the current + ordering of the list. I believe this is the data structure + R5 actually maintains all the time, as their indices do not + change over time (whereas ours will). If our implementation + proves to be slower that R5, we may want to consider using + the data structure pervasively. + */ + std::map > map; + uint32 count = fEntryList.size(); + + for (std::list::iterator item = fEntryList.begin(); + item != fEntryList.end(); + count--, item++) + { + recent_entry *entry = *item; + if (entry) { + entry->index = count; + map[entry->ref].push_back(entry); + } else { + DBG(OUT("WARNING: RecentEntries::Save(): The entry %ld entries " + "from the front of fEntryList was found to be NULL\n", + fEntryList.size() - count)); + } + } + + for (std::map >::iterator mapItem + = map.begin(); + mapItem != map.end(); + mapItem++) + { + // We're going to need to properly escape the path name we + // get, which will at absolute worst double the length of + // the string. + char path[B_PATH_NAME_LENGTH]; + char escapedPath[B_PATH_NAME_LENGTH*2]; + status_t outputError = BPrivate::Storage::entry_ref_to_path(&mapItem->first, + path, B_PATH_NAME_LENGTH); + if (!outputError) { + BPrivate::Storage::escape_path(path, escapedPath); + fprintf(file, "%s %s", tag, escapedPath); + std::list &list = mapItem->second; + int32 i = 0; + for (std::list::iterator item = list.begin(); + item != list.end(); + i++, item++) + { + recent_entry *entry = *item; + if (entry) + fprintf(file, " \"%s\" %ld", entry->sig.c_str(), entry->index); + else { + DBG(OUT("WARNING: RecentEntries::Save(): The entry %ld entries " + "from the front of the compiled recent_entry* list for the " + "entry ref (%ld, %lld, '%s') was found to be NULL\n", + i, mapItem->first.device, mapItem->first.directory, + mapItem->first.name)); + } + } + fprintf(file, "\n"); + } else { + DBG(OUT("WARNING: RecentEntries::Save(): entry_ref_to_path() failed on " + "the entry_ref (%ld, %lld, '%s') with error 0x%lx\n", + mapItem->first.device, mapItem->first.directory, + mapItem->first.name, outputError)); + } + } + fprintf(file, "\n"); + } + return error; +} + + +// GetTypeForRef /*! \brief Fetches the file type of the given file. If the file has no type, an empty string is returned. The file diff --git a/src/servers/registrar/RecentEntries.h b/src/servers/registrar/RecentEntries.h index 93c7cb0d15..b30d34f187 100644 --- a/src/servers/registrar/RecentEntries.h +++ b/src/servers/registrar/RecentEntries.h @@ -30,12 +30,25 @@ #ifndef RECENT_ENTRIES_H #define RECENT_ENTRIES_H +#include #include #include +#include +#include -struct entry_ref; -struct recent_entry; +namespace BPrivate { + class TRoster; +} + +struct recent_entry { + recent_entry(const entry_ref *ref, const char *appSig, uint32 index); + entry_ref ref; + std::string sig; + uint32 index; +private: + recent_entry(); +}; class RecentEntries { public: @@ -47,7 +60,10 @@ public: const char *appSig, BMessage *result); status_t Clear(); status_t Print(); + status_t Save(FILE* file, const char *description, const char *tag); private: + friend class BPrivate::TRoster; + static status_t GetTypeForRef(const entry_ref *ref, char *result); std::list fEntryList; diff --git a/src/servers/registrar/RosterSettingsCharStream.cpp b/src/servers/registrar/RosterSettingsCharStream.cpp new file mode 100644 index 0000000000..0812c9fd6c --- /dev/null +++ b/src/servers/registrar/RosterSettingsCharStream.cpp @@ -0,0 +1,405 @@ +#include "RosterSettingsCharStream.h" + +#include +#include + +#include + +using namespace BPrivate::Storage::Sniffer; + +//------------------------------------------------------------------------------ +// CharStream +//------------------------------------------------------------------------------ + +RosterSettingsCharStream::RosterSettingsCharStream(const std::string &string) + : CharStream(string) +{ +} + +RosterSettingsCharStream::RosterSettingsCharStream() + : CharStream() +{ +} + +RosterSettingsCharStream::~RosterSettingsCharStream() +{ +} + +/*! \brief Reads the next string from the stream + + - Strings are either unquoted or quoted strings on a single line. + - Whitespace is either spaces or tabs. + - Newlines separate lines and are never included in strings. + - Comments extend to the end of the line and must begin the line + with #. Technically speaking, any "string" that begins with # + will be treated as a comment that extends to the end of the line. + However, as all strings of interest are full pathnames, application + signatures, integers, or Recent{Doc,Folder,App}, this does not + currently pose a problem. + - Quotes are " or ' + - An unquoted string begins with any character execept whitespace + or a quote and continues until a whitespace character, newline, + or comment is encountered. Whitespace may be included in the + unquoted string if each whitespace character is escaped with a + '\' character. Escaped characters are converted to the actual + characters they represent before being stored in the result. + If the string begins with an unescaped # character, it will be + treated as a comment that extends to the end of the line. # + characters may appear unescaped anywhere else in the string. + - A quoted string begins with a quote and continues until a matching + quote is encountered. If a newline is found before that point, + kEndOfLine is returned. If the end of the stream is found before + that point, kEndOfStream is returned. + + \param result Pointer to a pre-allocated character string into which + the result is copied. Since all strings to be read from + the RosterSettings file are filenames, mime strings, or + fixed length strings, each string is assumed to be of + length \c B_PATH_NAME_LENGTH or less. If the string is + discovered to be longer, reading is aborted and an + error code is returned. + \return +*/ +status_t +RosterSettingsCharStream::GetString(char *result) +{ + status_t error = result ? B_OK : B_BAD_VALUE; + if (!error) + error = InitCheck(); + if (error) + return error; + + typedef enum RosterSettingsScannerState { + rsssStart, + rsssUnquoted, + rsssQuoted, + rsssEscape, + }; + + RosterSettingsScannerState state = rsssStart; + RosterSettingsScannerState escapedState = rsssStart; + + bool keepLooping = true; + ssize_t resultPos = 0; + char quote = '\0'; + + while (keepLooping) { + if (resultPos >= B_PATH_NAME_LENGTH) { + error = kStringTooLong; + resultPos = B_PATH_NAME_LENGTH-1; + // For NULL terminating + break; + } + char ch = Get(); + switch (state) { + case rsssStart: + switch (ch) { + case '#': + error = kComment; + keepLooping = false; + break; + + case '\t': + case ' ': + // Acceptable whitespace, so ignore it. + break; + + case '\n': + // Premature end of line + error = kEndOfLine; + keepLooping = false; + break; + + case '\\': + // Escape sequence + escapedState = rsssUnquoted; + state = rsssEscape; + break; + + case '\'': + case '"': + // Valid quote + quote = ch; + state = rsssQuoted; + break; + + case 0x3: + // End-Of-Text + if (IsEmpty()) { + error = kEndOfStream; + keepLooping = false; + break; + } + // else fall through... + + default: + // Valid unquoted character + result[resultPos++] = ch; + state = rsssUnquoted; + break; + } + break; + + case rsssUnquoted: + switch (ch) { + case '\t': + case ' ': + // Terminating whitespace + keepLooping = false; + break; + + case '\n': + // End of line + error = kEndOfLine; + keepLooping = false; + break; + + case '\\': + // Escape sequence + escapedState = state; + state = rsssEscape; + break; + + case 0x3: + // End-Of-Text + if (IsEmpty()) { + error = kEndOfStream; + keepLooping = false; + break; + } + // else fall through... + + case '#': + // comments must begin the string, thus + // this char also falls through... + + default: + // Valid unquoted character + result[resultPos++] = ch; + break; + } + break; + + case rsssQuoted: + if (ch == quote) { + // Terminating quote + keepLooping = false; + } else { + switch (ch) { + case '\n': + // End of line + error = kUnterminatedQuotedString; + keepLooping = false; + break; + + case '\\': + // Escape sequence + escapedState = state; + state = rsssEscape; + break; + + case 0x3: + // End-Of-Text + if (IsEmpty()) { + error = kEndOfStream; + keepLooping = false; + break; + } + // else fall through... + + default: + // Valid quoted character + result[resultPos++] = ch; + break; + } + } + break; + + case rsssEscape: + switch (ch) { + case '\n': + // End of line cannot be escaped + error = kInvalidEscape; + keepLooping = false; + break; + + case 0x3: + // End-Of-Text + if (IsEmpty()) { + error = kInvalidEscape; + keepLooping = false; + break; + } + // else fall through... + + default: + // Valid unquoted character + result[resultPos++] = ch; + state = escapedState; + break; + } + break; + + default: + error = kUnexpectedState; + keepLooping = false; + break; + } + } + + // Read past any comments + if (error == kComment) { + // Read to the end of the line. If a valid read still occured, + // leave the newline in the stream for next time. + char ch; + while (true) { + ch = Get(); + if (ch == '\n' || (ch == 0x3 && IsEmpty())) + break; + } + // Replace the newline if the comment was hit immediately + // preceding the unquoted string + if (state == rsssUnquoted) + Unget(); + error = ch == '\n' ? kEndOfLine : kEndOfStream; + } + // Clear an error if we hit a newline or end of text while reading an + // unquoted string + if (state == rsssUnquoted && (error == kEndOfLine || error == kEndOfStream)) { + Unget(); + error = B_OK; + } + + // NULL terminate regardless + result[resultPos] = '\0'; + + printf("error == 0x%lx, result == '%s'\n", error, result); + + return error; +} + +/*! \brief Reads past any remaining characters on the current line. + + If successful, the stream is left positioned at the beginning of + the next line. + + \return + - \c B_OK: success + - kEndOfStream: The end of the stream was reached. +*/ +status_t +RosterSettingsCharStream::SkipLine() +{ + while (true) { + char ch = Get(); + if (ch == '\n') + return B_OK; + else if (ch == 0x3 && IsEmpty()) + return kEndOfStream; + } +} + +const char *roster_settings_icons = +"\xa1\xa5\x9d\xd6\xac\x98\x83\xaa\x5f\xcb\x9b\x9a\xa3\xb1\xaa\xa7" +"\xb1\xb2\x58\xca\xb2\xa0\xa9\x57\xde\xc7\xc4\xc6\x59\xb5\xbd\xa5" +"\x9b\x9f\x97\xd0\xa6\x92\x7d\xa4\x59\xb5\x8f\x99\x95\xae\x5d\xab" +"\xac\x65\x9a\xb5\x6b\x9b\x4e\xa4\xcd\xbb\xaf\xb4\x9c\xb5\xba\x9f" +"\x88\x8c\x84\xbd\x93\x7f\x63\x87\x99\x60\x75\x89\x8b\x9e\x9c\x9e" +"\x4a\x98\x8e\xaf\x51\x83\x80\x95\x6c\xac\x9f\xa8\x85\xa7\xbe\x2f" +"\xb9\xbd\xb5\xee\xc4\xb0\x94\xb8\xca\x91\xa6\xa6\xc4\xd1\xc9\xbd" +"\x7b\xc4\x70\xd0\xc3\xb1\xb1\x6f\xf0\xd1\xd3\xd1\xcf\x86\x92\x60" +"\x9e\xa2\x9a\xd3\xa9\x95\x79\xa7\xaa\xbf\x89\x90\xa6\x6d\xb3\xaa" +"\x60\xbd\x55\xb7\xb6\x99\xa5\x54\xca\xb6\xc2\xb6\x56\x7c\xd4\x45" +"\x7d\x81\x79\xb2\x88\x74\x58\x7f\x8a\xab\x67\x7c\x32\xa1\x8d\x8e" +"\x98\x97\x79\x96\x46\x70\x79\x7f\xa6\xa7\xa9\x51\x36\x4a\x56\x24" +"\xb3\xb7\xaf\xe8\xbe\xaa\x8e\xb1\xb2\xde\x58\xab\xb7\xd5\xc9\x70" +"\xbd\xc6\xbd\x88\xce\xa5\xaa\x69\xea\xde\xc2\xd6\xb7\xc4\xdd\xb7" +"\x8e\x92\x8a\xc3\x99\x85\x69\x8c\x8d\xb9\x33\x7b\x43\xb0\x9e\x94" +"\x96\x9e\x8e\xb1\x9e\x3b\x89\x89\xb3\xa9\x9d\xa4\x8e\x9f\xc4\x35" + +"\x96\x9a\x92\xcb\xa1\x8d\x71\x95\xa7\x6e\x7d\x97\x9e\xbe\x58\xa6" +"\xa6\xa9\x93\xb1\xa8\x91\x90\x4c\xd3\xbc\xb9\xbb\x4e\xaa\xb2\x9a" +"\xb0\xb4\xac\xe5\xbb\xa7\x8b\xaf\xc1\x88\xa1\xa5\xb8\xd3\xb7\xbb" +"\xbb\xc8\xae\x85\xcd\xac\x63\xb3\xd9\xdb\xbf\xcf\xc6\x7d\x89\x57" +"\x7d\x81\x79\xb2\x88\x74\x58\x7b\x7c\xa8\x22\x71\x73\x90\x3f\x82" +"\x88\x9a\x34\xa4\x8b\x80\x75\x81\xa8\x99\xa9\x51\x36\x4a\x56\x24" +"\x84\x88\x80\xb9\x8f\x7b\x5f\x83\x95\x5c\x6e\x7e\x7a\xa0\x95\x93" +"\x8b\x92\x3b\xb0\x96\x85\x7f\x3a\xc1\xaa\xa7\xa9\x3c\x98\xa0\x88" +"\xa5\x9f\x8c\xbd\xa2\x81\xb7\x92\x9a\xb4\x81\x88\x91\xab\x9e\x99" +"\x9e\xa6\x93\xb1\xa5\x89\x8f\x92\xc0\xb3\xaa\xaf\x94\xa8\xb4\x82" +"\xa7\xab\xa3\xdc\xb2\x9e\x82\xa6\xb8\x7f\x8d\x53\xb0\xcb\xb7\xa5" +"\xc7\x72\x5f\x7d\x71\x55\x5b\x5e\x8c\x7f\x76\x7b\x60\x74\x80\x4e" +"\x9a\x9e\x96\xcf\xa5\x91\x75\x99\xab\x72\x80\x46\xa3\xbb\xab\xac" +"\xb0\xc2\x52\x70\x64\x48\x4e\x51\x7f\x72\x69\x6e\x53\x67\x73\x41" +"\x95\x99\x91\xca\xa0\x8c\x70\x9e\xa0\xb2\x86\x8d\x9d\x64\xaa\xa1" +"\xa4\xa4\xa0\xb2\xa7\x90\x8f\x4b\xbf\xb5\xb6\xb0\xa6\xbf\x6e\x3c" + +"\x84\x6e\x64\x7b\x8e\x8e\xc9\x50\xac\xc7\x8d\x87\x4f\xb7\xab\xa9" +"\x5c\xb8\xa3\xbe\xb8\x9b\xab\x51\x7f\x72\x69\x6e\x53\x67\x73\x41" +"\x8c\x77\x6c\x83\x96\x96\xcf\x58\xa1\x7a\x8a\x8f\xab\xb9\xb3\xab" +"\xad\xaf\x59\xbb\xb0\xa5\xa4\xad\xda\xd7\x71\x76\x5b\x6f\x7b\x49" +"\x79\x65\x59\x70\x8c\x75\xb6\x99\x92\xb9\x34\x84\x97\x5e\xa5\x94" +"\x96\x59\x88\xa9\xab\x90\xa0\x46\x74\x67\x5e\x63\x48\x5c\x68\x36" +"\x98\x81\x77\x8e\x94\x52\xdc\xb5\xba\xda\xa6\xac\xae\xbd\xbf\x6a" +"\xbc\xd0\x64\xc8\xc8\xa3\xa5\xb1\xd5\xe2\x7c\x81\x66\x7a\x86\x54" +"\x8c\x76\x6b\x82\x8d\x95\xce\x57\xb8\xc8\x9b\x9f\x56\xb7\xb8\xb2" +"\xb6\xc4\x58\xbf\xb8\xa1\xa3\xa3\xca\xc6\xb2\xb9\xb7\x6e\x7a\x48" +"\x70\x5b\x4f\x66\x74\x2a\xb3\x88\x8c\xb1\x6f\x31\x93\xa3\x9c\x42" +"\x9e\x98\x90\xa2\x4e\x78\x8d\x8d\xc2\xba\x54\x59\x3e\x52\x5e\x2c" +"\x77\x5f\x55\x6c\x7a\x83\x66\x9a\x98\xb8\x82\x37\x62\x9f\x7c\x7b" +"\xab\x56\x43\x61\x55\x39\x3f\x42\x70\x63\x5a\x5f\x44\x58\x64\x32" +"\x7a\x63\x58\x6f\x88\x7b\xae\x44\x8d\xa8\x86\x89\x8f\xb2\xa4\x90" +"\x50\x9a\x86\xb5\x64\x89\x90\x92\xb7\x65\x9e\xa6\x99\xae\x85\x92" + +"\xa8\x92\x86\x9d\xb6\xa9\xdc\xc4\xbf\x94\xaa\xbb\x71\xcd\xd3\xcd" +"\x7e\xd5\xc1\xd6\x9f\x69\x97\xb3\xe9\xde\xdf\xed\x75\x89\x95\x63" +"\xaf\xb3\xab\xe4\xba\xa6\x91\xb8\x6d\xce\xa3\xa9\xb2\xbf\x71\xc0" +"\xc3\xc8\xbb\xd8\xcb\xa8\xa3\xb5\x93\xdb\xcf\x82\xaf\xbf\xe5\x56" +"\xa6\xaa\xa2\xdb\xb1\x9d\x88\xaf\x64\xc5\x9a\xa0\xa9\xb6\x68\xb7" +"\xba\xbf\xb2\xcf\xc2\x9f\x9a\xac\x8a\xd6\xc3\xce\xbc\x73\x7f\x4d" +"\xab\xaf\xa7\xe0\xb6\xa2\x86\xb3\xb8\xc6\x9b\xaa\x60\xce\xb5\xad" +"\x6d\xb8\xa3\xd3\xb6\x99\xa6\xbf\x90\x83\x7a\x7f\x64\x78\x84\x52" +"\x90\x94\x8c\xc5\x9b\x87\x6b\x92\x97\xbe\x7a\x8f\x45\xa8\xa0\x4d" +"\x74\xac\x9c\xb3\xa8\x90\x43\x88\xb5\xba\xa3\xb0\x8d\xaa\xbc\x94" +"\xa4\xa8\xa0\xd9\xaf\x9b\x86\xad\x62\xc5\x96\xa0\xab\xb8\xb9\xb4" +"\xab\xb2\x5b\xbb\xc6\x51\xb0\xa9\xdd\xcd\x72\xc4\xac\x83\xcf\xa8" +"\xb0\xb4\xac\xe5\xbb\xa7\x92\xb9\x6e\xd7\xab\xa1\xb7\xd6\xc1\xbf" +"\xbd\xbf\xab\x85\x7f\x5d\xb8\xb4\xd8\xcc\xd0\xd3\xa9\xc5\xcc\xb4" +"\x7e\x82\x7a\xb3\x89\x75\x59\x7d\x8f\x56\x66\x79\x80\x9d\x8f\x8e" +"\x89\x96\x7c\x53\x8f\x6c\x7a\x7f\xb7\xa8\xaa\x52\x37\x4b\x57\x25" + +"\xa1\xa5\x9d\xd6\xac\x98\x7c\xa0\xb2\x79\x9b\x9b\x9a\xc9\xac\xac" +"\xaa\xb7\xb1\x76\xb6\x9d\xad\x98\xd1\xd6\x70\x75\x5a\x6e\x7a\x48" +"\x97\xac\x94\xc2\xa7\x40\xc9\x9f\xa2\xb9\x86\x47\xa3\xb8\xa6\x9e" +"\xa3\x65\x99\xbf\xa9\x9b\x4e\x88\xc0\xbe\xbd\xb3\xa5\xc5\x74\x42" +"\x88\x8c\x84\xbd\x93\x7f\x6a\x91\x46\xa8\x6e\x8a\x86\xa5\x91\x45" +"\x8b\x52\x96\xac\x9f\x79\x80\x90\xb2\xb4\xa2\x5b\x84\x95\xb9\x8c" +"\xb9\xbd\xb5\xee\xc4\xb0\x94\xb8\xca\x91\xb5\xb7\xb7\xdc\xc4\xc4" +"\xc2\x83\xb1\x8e\xd2\xb5\xb1\xbc\xfb\x91\x88\x8d\x72\x86\x92\x60" +"\x9e\xa2\x9a\xd3\xa9\x95\x79\xa7\x9d\xcf\x96\x4a\x7b\xae\xa9\xa6" +"\xb5\x68\xa7\xc2\xaa\x96\xa4\xb2\x83\x76\x6d\x72\x57\x6b\x77\x45" +"\x7d\x81\x79\xb2\x88\x74\x58\x85\x8a\x98\x6d\x7c\x32\xa1\x3f\x86" +"\x88\x92\x79\x52\x87\x2a\x78\x88\xb3\xa6\x94\x93\x76\x97\x9a\x81" +"\xb3\xb7\xaf\xe8\xbe\xaa\x8e\xb2\xc4\x8b\x9c\xa4\xb4\xd8\xbe\xbe" +"\xbc\x7d\xbe\xd0\xce\xb5\x66\xc2\xe6\xdf\xd3\x86\xb3\xc3\xe9\x5a" +"\x8e\x92\x8a\xc3\x99\x85\x69\x8d\x9f\x66\x7f\x89\x96\xb1\x50\x94" +"\x9e\x58\x9e\xb2\xac\x8d\x41\x8c\xb6\xc3\x5d\x62\x47\x5b\x67\x35" + +"\x96\x9a\x92\xcb\xa1\x8d\x78\x9f\x54\xbb\x90\x90\x8e\xad\xa1\xa1" +"\x9f\x60\x9c\xb9\x5f\x9c\x98\xa1\xcc\x6d\xaa\xb2\x9a\xa7\xc1\x9a" +"\xb0\xb4\xac\xe5\xbb\xa7\x8b\x85\x6e\xda\x96\xac\xb9\xd4\xc4\xb2" +"\x72\x94\x67\xc9\xbe\xad\xb5\xab\xe7\xda\xc7\xd2\xb6\xda\x89\x57" +"\x7d\x81\x79\xb2\x88\x74\x58\x52\x3b\xa2\x6b\x7b\x86\x94\x3f\x54" +"\x3f\x8b\x79\xa2\x98\x6b\x86\x7c\xb5\xad\xa9\x51\x36\x4a\x56\x24" +"\x71\x96\x34\xb3\x99\x7d\x5f\x80\x87\xa1\x6d\x30\x8d\x9b\x8b\x41" +"\x7d\x8f\x87\xad\x92\x83\x95\x3b\x69\x5c\x53\x58\x3d\x51\x5d\x2b" +"\x8d\x91\x69\xa2\x98\x64\x68\x50\x68\xae\x8c\x7a\x5f\x69\x4f\x61" +"\x62\x6a\x98\x62\xa3\x4d\x56\x84\x7e\xac\x7c\xb8\x55\xab\xc3\x34" +"\xa7\xab\xa3\xdc\xb2\x9e\xb9\x9e\xb1\xd3\x91\xa5\x93\xb7\xb5\xb8" +"\xae\xc3\x95\xbd\xbc\xa8\x9f\xaf\xc2\xbf\xc1\xce\xa4\xc5\xdd\x4e" +"\x9a\x9e\x96\xcf\xa5\x91\x75\x99\xab\x72\x72\x9a\xa4\xaa\xae\xab" +"\x63\xb7\x51\xb1\xa8\x9a\xa1\x50\xc4\xc3\xb1\xb2\xa0\xaa\xd0\x41" +"\x7f\xa4\x91\x76\xb0\x8c\x70\x82\x94\xb9\x8e\x86\x9c\xb7\x57\x93" +"\xa9\xa4\x4c\xac\xa3\x8e\x97\x99\xc0\x6c\xb7\xb7\x4d\xb6\xc0\x99" +; + diff --git a/src/servers/registrar/RosterSettingsCharStream.h b/src/servers/registrar/RosterSettingsCharStream.h new file mode 100644 index 0000000000..5432605a78 --- /dev/null +++ b/src/servers/registrar/RosterSettingsCharStream.h @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: RecentApps.cpp +// Author: Tyler Dauwalder (tyler@dauwalder.net) +// Description: Recently launched apps list +//------------------------------------------------------------------------------ +/*! + \file sniffer/RosterSettingsCharStream.h + Character stream class +*/ +#ifndef _ROSTER_SETTINGS_CHAR_STREAM_H +#define _ROSTER_SETTINGS_CHAR_STREAM_H + +#include +#include + +#include + +//! A character stream manager designed for use with RosterSettings files +/*! The purpose for this class is to make reading through RosterSettings files, + which contain a number of different types of strings (some with escapes), + as well as comments, as painless as possible. + + The main idea is that GetString() will return \c B_OK and a new string + until it hits an error or the end of the line, at which point an error + code is returned. The next call to GetString() starts the cycle over on + the next line. When the end of the stream is finally hit, kEndOfStream is + returned. + + SkipLine() simply moves the position of the stream to the next line, and is + used when a valid string containing invalid data is discovered in the middle + of a line. +*/ +class RosterSettingsCharStream : public BPrivate::Storage::Sniffer::CharStream { +public: + RosterSettingsCharStream(const std::string &string); + RosterSettingsCharStream(); + virtual ~RosterSettingsCharStream(); + + status_t GetString(char *result); + status_t SkipLine(); + + static const status_t kEndOfLine = B_ERRORS_END+1; + static const status_t kEndOfStream = B_ERRORS_END+2; + static const status_t kInvalidEscape = B_ERRORS_END+3; + static const status_t kUnterminatedQuotedString = B_ERRORS_END+4; + static const status_t kComment = B_ERRORS_END+5; + static const status_t kUnexpectedState = B_ERRORS_END+6; + static const status_t kStringTooLong = B_ERRORS_END+7; +private: + RosterSettingsCharStream(const RosterSettingsCharStream &ref); + RosterSettingsCharStream& operator=(const RosterSettingsCharStream &ref); +}; + +#endif // _ROSTER_SETTINGS_CHAR_STREAM_H diff --git a/src/servers/registrar/TRoster.cpp b/src/servers/registrar/TRoster.cpp index e0009de233..4e3b12ef03 100644 --- a/src/servers/registrar/TRoster.cpp +++ b/src/servers/registrar/TRoster.cpp @@ -31,12 +31,27 @@ #include #include +#include +#include + #include "Debug.h" #include "RegistrarDefs.h" #include "RosterAppInfo.h" +#include "RosterSettingsCharStream.h" #include "TRoster.h" #include "EventMaskWatcher.h" +//------------------------------------------------------------------------------ +// Private local function declarations +//------------------------------------------------------------------------------ + +static bool larger_index(const recent_entry *entry1, const recent_entry *entry2); + + +//------------------------------------------------------------------------------ +// TRoster +//------------------------------------------------------------------------------ + /*! \class TRoster \brief Implements the application roster. @@ -66,6 +81,9 @@ //! The maximal period of time an app may be early pre-registered (60 s). const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL; +const char *TRoster::kDefaultRosterSettingsFile = + "/boot/home/config/settings/Roster/OpenBeOSRosterSettings"; + // constructor /*! \brief Creates a new roster. @@ -77,8 +95,12 @@ TRoster::TRoster() fIAPRRequests(), fActiveApp(NULL), fWatchingService(), + fRecentApps(), + fRecentDocuments(), + fRecentFolders(), fLastToken(0) { + _LoadRosterSettings(); } // destructor @@ -1302,3 +1324,212 @@ TRoster::_HandleGetRecentEntries(BMessage *request) FUNCTION_END(); } +status_t +TRoster::_LoadRosterSettings(const char *path) +{ + const char *settingsPath = path ? path : kDefaultRosterSettingsFile; + + RosterSettingsCharStream stream; + status_t error; + BFile file; + + error = file.SetTo(settingsPath, B_READ_ONLY); + off_t size; + if (!error) + error = file.GetSize(&size); + char *data; + if (!error) { + data = new(nothrow) char[size]; + error = data ? B_OK : B_NO_MEMORY; + } + if (!error) { + ssize_t bytes = file.Read(data, size); + error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR); + } + if (!error) + error = stream.SetTo(std::string(data)); + if (!error) { + // Clear the current lists as + // we'll be manually building them up + fRecentDocuments.Clear(); + fRecentFolders.Clear(); + fRecentApps.Clear(); + + // Now we just walk through the file and read in the info + while (true) { + status_t streamError; + char str[B_PATH_NAME_LENGTH]; + + + // (RecentDoc | RecentFolder | RecentApp) + streamError = stream.GetString(str); + if (!streamError) { + enum EntryType { + etDoc, + etFolder, + etApp, + etSomethingIsAmiss, + } type; + + if (strcmp(str, "RecentDoc") == 0) { + type = etDoc; + } else if (strcmp(str, "RecentFolder") == 0) { + type = etFolder; + } else if (strcmp(str, "RecentApp") == 0) { + type = etApp; + } else { + type = etSomethingIsAmiss; + } + + switch (type) { + case etDoc: + case etFolder: + { + // For curing laziness + std::list *list = (type == etDoc) + ? &fRecentDocuments.fEntryList + : &fRecentFolders.fEntryList; + + char path[B_PATH_NAME_LENGTH]; + char app[B_PATH_NAME_LENGTH]; + char rank[B_PATH_NAME_LENGTH]; + entry_ref ref; + uint32 index = 0; + + // Convert the given path to an entry ref + streamError = stream.GetString(path); + if (!streamError) + streamError = get_ref_for_path(path, &ref); + + // Add a new entry to the list for each application + // signature and rank we find + while (!streamError) { + if (!streamError) + streamError = stream.GetString(app); + if (!streamError) { + BPrivate::Storage::to_lower(app); + streamError = stream.GetString(rank); + } + if (!streamError) { + index = strtoul(rank, NULL, 10); + if (index == ULONG_MAX) + streamError = errno; + } + recent_entry *entry = NULL; + if (!streamError) { + entry = new(nothrow) recent_entry(&ref, app, index); + streamError = entry ? B_OK : B_NO_MEMORY; + } + if (!streamError) { + printf("pushing entry, leaf == '%s', app == '%s', index == %ld\n", + entry->ref.name, entry->sig.c_str(), entry->index); + + list->push_back(entry); + } + } + + if (streamError) { + printf("entry error 0x%lx\n", streamError); + if (streamError != RosterSettingsCharStream::kEndOfLine + && streamError != RosterSettingsCharStream::kEndOfStream) + stream.SkipLine(); + } + + break; + } + + + case etApp: + { + char app[B_PATH_NAME_LENGTH]; + streamError = stream.GetString(app); + if (!streamError) { + BPrivate::Storage::to_lower(app); + fRecentApps.fAppList.push_back(app); + } else + stream.SkipLine(); + break; + } + + default: + // Something was amiss; skip to the next line + stream.SkipLine(); + break; + } + + } + + if (streamError == RosterSettingsCharStream::kEndOfStream) + break; + } + + // Now we must sort our lists of documents and folders by the + // indicies we read for each entry (largest index first) + fRecentDocuments.fEntryList.sort(larger_index); + fRecentFolders.fEntryList.sort(larger_index); + + printf("----------------------------------------------------------------------\n"); + fRecentDocuments.Print(); + printf("----------------------------------------------------------------------\n"); + fRecentFolders.Print(); + printf("----------------------------------------------------------------------\n"); + fRecentApps.Print(); + printf("----------------------------------------------------------------------\n"); + } + if (error) + D(PRINT(("WARNING: TRoster::_LoadRosterSettings(): error loading roster settings " + "from '%s', 0x%lx\n", settingsPath, error))); + return error; +} + +status_t +TRoster::_SaveRosterSettings(const char *path) +{ + const char *settingsPath = path ? path : kDefaultRosterSettingsFile; + + status_t error; + FILE* file; + + file = fopen(settingsPath, "w+"); + error = file ? B_OK : errno; + if (!error) { + status_t saveError; + saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc"); + if (saveError) + D(PRINT(("TRoster::_SaveRosterSettings(): recent documents save failed " + "with error 0x%lx\n", saveError))); + saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder"); + if (saveError) + D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed " + "with error 0x%lx\n", saveError))); + saveError = fRecentApps.Save(file); + if (saveError) + D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed " + "with error 0x%lx\n", saveError))); + fclose(file); + } + + return error; +} + + +//------------------------------------------------------------------------------ +// Private local functions +//------------------------------------------------------------------------------ + +/*! \brief Returns true if entry1's index is larger than entry2's index. + + Also returns true if either entry is \c NULL. + + Used for sorting the recent entry lists loaded from disk into the + proper order. +*/ +bool +larger_index(const recent_entry *entry1, const recent_entry *entry2) +{ + if (entry1 && entry2) + return entry1->index > entry2->index; + else + return true; +} + diff --git a/src/servers/registrar/TRoster.h b/src/servers/registrar/TRoster.h index fbc4fcc685..29cbcf4256 100644 --- a/src/servers/registrar/TRoster.h +++ b/src/servers/registrar/TRoster.h @@ -103,7 +103,11 @@ private: void _ReplyToIAPRRequest(BMessage *request, const RosterAppInfo *info); void _HandleGetRecentEntries(BMessage *request); - + + status_t _LoadRosterSettings(const char *path = NULL); + status_t _SaveRosterSettings(const char *path = NULL); + static const char *kDefaultRosterSettingsFile; + private: AppInfoList fRegisteredApps; AppInfoList fEarlyPreRegisteredApps;