[aquaskk-changes 137] CVS update: AquaSKK

Tomotaka SUWA t-suw****@users*****
2006年 2月 18日 (土) 02:20:38 JST


Index: AquaSKK/AquaSKKServer.h
diff -u /dev/null AquaSKK/AquaSKKServer.h:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/AquaSKKServer.h	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,31 @@
+/*
+  $Id: AquaSKKServer.h,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002 phonohawk
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#pragma once
+
+ @ interface AquaSKKServer: NSObject
+{
+}
+- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
+ @ end
Index: AquaSKK/AquaSKKServer.mm
diff -u /dev/null AquaSKK/AquaSKKServer.mm:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/AquaSKKServer.mm	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,57 @@
+/* -*- objc -*-
+  $Id: AquaSKKServer.mm,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002 phonohawk
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <AppKit/AppKit.h>
+#include "AquaSKKServer.h"
+#include "PreferencesController.h"
+#include "DictionarySet.h"
+#include "BIMClientServer.h"
+#include "ServerMessageReceiver.h"
+#include "skkserv.h"
+
+ @ implementation AquaSKKServer
+
+- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
+    // ƒvƒŠƒtƒ@ƒŒƒ“ƒX‚ðŽæ“¾
+    PreferencesController* pref = [PreferencesController sharedController];
+
+    // Ž«‘ƒT[ƒo[‚ð‹N“®‚·‚é
+    DictionarySet::theInstance().initialize();
+
+    // ƒƒbƒZ[ƒWƒnƒ“ƒhƒ‰‚ð‹N“®‚·‚é
+    ServerMessageReceiver::start(kAquaSKKServerRunLoopMode);
+
+    // skkserv ƒGƒ~ƒ…ƒŒ[ƒVƒ‡ƒ“‚Ì‹N“®
+    if([pref isSkkservEnabled]) {
+	skkserv::theInstance().start([pref skkservPort], [pref isSkkservLocalOnly]);
+    }
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+    // Ž«‘ƒT[ƒo[‚ð—Ž‚Æ‚·
+    DictionarySet::theInstance().terminate();
+
+    return NSTerminateNow;
+}
+
+ @ end
Index: AquaSKK/BS.mm
diff -u AquaSKK/BS.mm:1.2.2.1 AquaSKK/BS.mm:1.2
--- AquaSKK/BS.mm:1.2.2.1	Mon Jan  9 11:11:05 2006
+++ AquaSKK/BS.mm	Sat Oct  8 00:08:36 2005
@@ -1,29 +1,28 @@
-/*
-   $Id: BS.mm,v 1.2.2.1 2006/01/09 02:11:05 t-suwa Exp $
-
-   MacOS X implementation of the SKK input method.
-
-   Copyright (C) 2002 phonohawk
-   Copyright (C) 2006 Tomotaka SUWA <t.suw****@mac*****>
-
-   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
-   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+/* -*- objc -*-
+	$Id: BS.mm,v 1.2 2005/10/07 15:08:36 t-suwa Exp $
+	---------
+	
+    MacOS X implementation of the SKK input method.
+    Copyright (C) 2002 phonohawk
+
+    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
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #import <Cocoa/Cocoa.h>
 
-// AquaSKKServer のエントリポイント
-int main(int argc, const char *argv[]) {
+int main(int argc, const char *argv[])
+{
     return NSApplicationMain(argc, argv);
 }
Index: AquaSKK/ChangeLog
diff -u AquaSKK/ChangeLog:1.20.2.4 AquaSKK/ChangeLog:1.20.2.5
--- AquaSKK/ChangeLog:1.20.2.4	Sat Jan 14 20:01:58 2006
+++ AquaSKK/ChangeLog	Sat Feb 18 02:20:38 2006
@@ -1,3 +1,31 @@
+2006-02-18  Tomotaka SUWA  <t.suw****@mac*****>
+
+	* skkserv.*: socketstream ‚ðŽg‚¤‚悤‚ɏC³B
+
+	* socketstream.h: V‹K’ljÁB
+
+	* Dictionary.h: Ž«‘‚̃Cƒ“ƒ^ƒtƒF[ƒX‚ð•ÏXB
+
+	* ƒtƒ@ƒCƒ‹–¼‚ð•ÏX
+	BS.mm ¨ main.m
+	DMDictionary.* ¨ KotoeriDictionary.*
+	Foundation.* ¨ AquaSKKServer.*
+	SKKServer.* ¨ DictionarySet.*
+	Skkserv.* ¨ skksev.*
+
+2006-02-15  Tomotaka SUWA  <t.suw****@mac*****>
+
+	* SKKServer.*: •¡”Ž«‘‚ɑΉžB
+
+	* PreferencesController.*: V‹K‚ɁuŽ«‘v‚ð’ljÁ‚µAuSKK Ž«‘v‚Æ
+	u‚±‚Æ‚¦‚莫‘v‚ðíœB
+
+	* Foundation.mm: ‰Šú‰»•”•ª‚ðC³B
+
+	* DictArrayController.*: V‹K’ljÁBNSArrayController ‚̃TƒuƒNƒ‰ƒXB
+
+	* DMDictionary.*: 1 ƒNƒ‰ƒX 1 Ž«‘‚ÌŠÖŒW‚ɏC³B
+
 2006-01-14  Tomotaka SUWA  <t.suw****@mac*****>
 
 	* ‘½”‚̃tƒ@ƒCƒ‹: ‘S‘Ì“I‚ɃRƒXƒƒeƒBƒbƒN‚ȏC³B
Index: AquaSKK/DMDictionary.cpp
diff -u AquaSKK/DMDictionary.cpp:1.4.2.1 AquaSKK/DMDictionary.cpp:1.4
--- AquaSKK/DMDictionary.cpp:1.4.2.1	Wed Feb 15 00:12:44 2006
+++ AquaSKK/DMDictionary.cpp	Tue Nov 15 00:37:13 2005
@@ -1,10 +1,10 @@
 /*
-  $Id: DMDictionary.cpp,v 1.4.2.1 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: DMDictionary.cpp,v 1.4 2005/11/14 15:37:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002 phonohawk
-  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -35,8 +35,9 @@
 
 static OSErr FilePathToFSSpec(CFStringRef filePath, FSSpec* apSpec);
 
-KotoeriDictionary::KotoeriDictionary(const std::string path) : path_(0), isRegistered_(false), id_(0) {
-    path_ = CFStringCreateWithCString(kCFAllocatorDefault, path.c_str(), kCFStringEncodingEUC_JP);
+DMDictionary::KotoeriDictionary::KotoeriDictionary(const CFStringRef path)
+    : path_(0), isRegistered_(false), id_(0) {
+    path_ = CFStringCreateCopy(kCFAllocatorDefault, path);
     if(!path_) {
 	std::cerr << "KotoeriDictionary: CFStringCreateCopy() failed" << std::endl;
 	return;
@@ -55,16 +56,9 @@
 	}
 	isRegistered_ = true;
     }
-
-    char buf[1024];
-    CFStringGetCString(path_, buf, sizeof(buf), kCFStringEncodingUTF8);
-    std::cerr << "Kotoeri Dictionary(" << buf << ")" << std::endl
-	      << "  okuri-ari: 0 entries." << std::endl
-	      << "  okuri-nasi: " << countOkuriNasi() << " entries."
-	      << std::endl << std::endl;
 }
 
-KotoeriDictionary::~KotoeriDictionary() {
+DMDictionary::KotoeriDictionary::~KotoeriDictionary() {
     if(isRegistered_) {
 	DCMUnregisterDictionary(id_);
     }
@@ -74,11 +68,38 @@
     }
 }
 
-int KotoeriDictionary::countOkuriAri() {
-    return 0;
+bool DMDictionary::KotoeriDictionary::Find(const CFStringRef key,
+					   DCMFoundRecordIterator* iterator) {
+    DCMDictionaryRef ref;
+    if(DCMOpenDictionary(id_, 0, NULL, &ref) != noErr) {
+	std::cerr << "DCMOpenDictionary() failed" << std::endl;
+	return false;
+    }
+
+    // ƒŒƒR[ƒh‚ðŒŸõ
+    OSStatus status;
+    DCMFieldTag	dataFieldTagList[] = { kDCMJapaneseHyokiTag };
+    ByteCount length = CFStringGetLength(key) * sizeof(UniChar);
+    status = DCMFindRecords(ref, // Dictionary reference
+			    kDCMJapaneseYomiTag, // key field tag
+			    length,		 // key data length
+			    CFStringGetCharactersPtr(key), // key data
+			    kDCMFindMethodExactMatch, // find method
+			    1,		      // number of data field
+			    dataFieldTagList, // data field tag list
+			    0, 0, // search all records
+			    iterator); // found result
+
+    DCMCloseDictionary(ref);
+
+    return (status == noErr);
 }
 
-int KotoeriDictionary::countOkuriNasi() {
+const bool DMDictionary::KotoeriDictionary::IsGood() const {
+    return (id_ != 0);
+}
+
+const int DMDictionary::KotoeriDictionary::Count() const {
     ItemCount numItems;
     if(DCMCountRecord(id_, &numItems) == noErr) {
 	return numItems;
@@ -87,96 +108,145 @@
     return 0;
 }
 
-std::vector<OkuriganaEntry> KotoeriDictionary::findOkuriAri(const CppCFString& str) {
-    // ‚±‚Æ‚¦‚莫‘‚ɂ́u‘—‚è‚ ‚èv‚Í‘¶Ý‚µ‚È‚¢
-    return std::vector<OkuriganaEntry>();
+const void DMDictionary::KotoeriDictionary::Description() const {
+    char buf[1024];
+    CFStringGetCString(path_, buf, sizeof(buf), kCFStringEncodingUTF8);
+    std::cerr << "Kotoeri Dictionary(" << buf << ")" << std::endl
+	      << "  okuri-ari: 0 entries." << std::endl
+	      << "  okuri-nasi: " << Count() << " entries."
+	      << std::endl << std::endl;
 }
 
-std::vector<CppCFString> KotoeriDictionary::findOkuriNasi(const CppCFString& str) {
-    OSStatus status;
-    std::vector<CppCFString> result;
+// ------------------------------------------------------------------
+
+DMDictionary::DMDictionary(const std::vector<CppCFString>& kotoeri_dic_file) {
+    load(kotoeri_dic_file);
+}
+
+DMDictionary::~DMDictionary() {
+    unload();
+}
 
-    DCMFoundRecordIterator iterator;
-    if(!find(str.getString(), &iterator)) {
-	return result;
-    }
-
-    while(true) {
-	ByteCount keySize;
-	char foundKeyStr[kMaxKanjiLengthInAppleJapaneseDictionary];
-	DCMUniqueID uniqueID;
-	AERecord dataList;
-
-	// Get one record from result list
-	status = DCMIterateFoundRecord(iterator, // found result
-				       kMaxKanjiLengthInAppleJapaneseDictionary, // key buffer size
-				       &keySize, // actual found key size
-				       foundKeyStr, // found key data
-				       &uniqueID,   // ƒ†ƒj[ƒNID
-				       &dataList);  // AERecordƒf[ƒ^
-
-	if(status != noErr) break;
-
-	DescType actualType;
-	char dataBuffer[kMaxKanjiLengthInAppleJapaneseDictionary];
-	Size actualSize;
-
-	// Get one data from AERecord
-	status = AEGetKeyPtr(&dataList, 
-			     kDCMJapaneseHyokiTag,
-			     'utxt',
-			     &actualType,
-			     dataBuffer,
-			     kMaxKanjiLengthInAppleJapaneseDictionary,
-			     &actualSize);
-
-	// Dispose data AERecord
-	AEDisposeDesc(&dataList);
+int DMDictionary::countOkuriAri() {
+    return 0;
+}
 
-	if(status != noErr) break;
+int DMDictionary::countOkuriNasi() {
+    int result = 0;
 
-	// Œ‹‰Ê‚ɒljÁ‚·‚é
-	result.push_back(CppCFData(dataBuffer, actualSize).getData());
+    for(KotoeriDictionaryIterator i = dics_.begin(); i != dics_.end(); ++ i) {
+	result += (*i)->Count();
     }
-    DCMDisposeRecordIterator(iterator);
 
     return result;
 }
 
-bool KotoeriDictionary::find(const CFStringRef key, DCMFoundRecordIterator* iterator) {
-    DCMDictionaryRef ref;
-    if(DCMOpenDictionary(id_, 0, NULL, &ref) != noErr) {
-	std::cerr << "DCMOpenDictionary() failed" << std::endl;
-	return false;
-    }
+std::vector<OkuriganaEntry> DMDictionary::findOkuriAri(const CppCFString& str) {
+    // ‚±‚Æ‚¦‚莫‘‚ɂ́u‘—‚è‚ ‚èv‚Í‘¶Ý‚µ‚È‚¢
+    return std::vector<OkuriganaEntry>();
+}
 
-    // ƒŒƒR[ƒh‚ðŒŸõ
+std::vector<CppCFString> DMDictionary::findOkuriNasi(const CppCFString& str) {
     OSStatus status;
-    DCMFieldTag	dataFieldTagList[] = { kDCMJapaneseHyokiTag };
-    ByteCount length = CFStringGetLength(key) * sizeof(UniChar);
-    status = DCMFindRecords(ref, // Dictionary reference
-			    kDCMJapaneseYomiTag, // key field tag
-			    length,		 // key data length
-			    CFStringGetCharactersPtr(key), // key data
-			    kDCMFindMethodExactMatch, // find method
-			    1,		      // number of data field
-			    dataFieldTagList, // data field tag list
-			    0, 0, // search all records
-			    iterator); // found result
+    std::vector<CppCFString> result;
 
-    DCMCloseDictionary(ref);
+    for(KotoeriDictionaryIterator i = dics_.begin(); i != dics_.end(); ++ i) {
+	DCMFoundRecordIterator iterator;
+	if(!(*i)->Find(str.getString(), &iterator)) continue;
+
+        while(true) {
+            ByteCount keySize;
+            char foundKeyStr[kMaxKanjiLengthInAppleJapaneseDictionary];
+            DCMUniqueID uniqueID;
+            AERecord dataList;
+            
+            // Get one record from result list
+            status = DCMIterateFoundRecord(iterator, // found result
+					   kMaxKanjiLengthInAppleJapaneseDictionary, // key buffer size
+					   &keySize, // actual found key size
+					   foundKeyStr, // found key data
+					   &uniqueID,	 // ƒ†ƒj[ƒNID
+					   &dataList);	// AERecordƒf[ƒ^
+
+            if(status != noErr) break;
+    
+            DescType actualType;
+            char dataBuffer[kMaxKanjiLengthInAppleJapaneseDictionary];
+            Size actualSize;
+
+            // Get one data from AERecord
+            status = AEGetKeyPtr(&dataList, 
+				 kDCMJapaneseHyokiTag,
+				 'utxt',
+				 &actualType,
+				 dataBuffer,
+				 kMaxKanjiLengthInAppleJapaneseDictionary,
+				 &actualSize);
+
+            // Dispose data AERecord
+            AEDisposeDesc(&dataList);
+
+	    if(status != noErr) break;
+
+	    // Œ‹‰Ê‚ɒljÁ‚·‚é
+	    result.push_back(CppCFData(dataBuffer, actualSize).getData());
+        }
+	DCMDisposeRecordIterator(iterator);
+    }
 
-    return (status == noErr);
+    return result;
+}
+
+void DMDictionary::changeDictionaryFile(const std::vector<CppCFString>& dics) {
+    unload();
+    load(dics);
+}
+
+bool DMDictionary::checkDictionary(const CFStringRef path) {
+    return KotoeriDictionary(path).IsGood();
+}
+
+// ------------------------------------------------------------------
+
+void DMDictionary::load(const std::vector<CppCFString>& dics) {
+    for(std::vector<CppCFString>::const_iterator ite = dics.begin();
+	ite != dics.end(); ++ ite) {
+	KotoeriDictionary* dic = new KotoeriDictionary(ite->getString());
+        if(dic->IsGood()) {
+	    dics_.push_back(dic);
+	    dic->Description();
+	} else {
+	    delete dic;
+	}
+    }
+}
+
+void DMDictionary::unload() {
+    struct local {
+	static void DeleteObject(const KotoeriDictionary* ptr) {
+	    delete ptr;
+	}
+    };
+    std::for_each(dics_.begin(), dics_.end(), local::DeleteObject);
+    dics_.clear();
 }
 
 static OSErr FilePathToFSSpec(CFStringRef filePath, FSSpec* apSpec) {
     FSRef fileRef;
     OSErr err = !noErr;
-    CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
-    if(CFURLGetFSRef(url, &fileRef)) {
-        err = FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, apSpec, NULL);
+    CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 
+						   filePath,
+						   kCFURLPOSIXPathStyle,
+						   false);
+    if(CFURLGetFSRef(cfUrl, &fileRef)) {
+        err = FSGetCatalogInfo(&fileRef,
+			       kFSCatInfoNone,
+			       NULL,
+			       NULL,
+			       apSpec,
+			       NULL);
     }
-    if(url) CFRelease(url);
+    CFRelease(cfUrl);
 
     return err;
 }
Index: AquaSKK/DMDictionary.h
diff -u AquaSKK/DMDictionary.h:1.4.2.1 AquaSKK/DMDictionary.h:1.4
--- AquaSKK/DMDictionary.h:1.4.2.1	Wed Feb 15 00:12:44 2006
+++ AquaSKK/DMDictionary.h	Tue Nov 15 00:37:13 2005
@@ -1,10 +1,10 @@
 /*
-  $Id: DMDictionary.h,v 1.4.2.1 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: DMDictionary.h,v 1.4 2005/11/14 15:37:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002 phonohawk
-  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -32,19 +32,37 @@
 class OkuriganaEntry;
 class CppCFString;
 
-class KotoeriDictionary: public Dictionary {
-    CFStringRef path_;
-    bool isRegistered_;
-    DCMDictionaryID id_;
+class DMDictionary: public Dictionary {
+    class KotoeriDictionary {
+	CFStringRef path_;
+	bool isRegistered_;
+	DCMDictionaryID id_;
+
+    public:
+	KotoeriDictionary(const CFStringRef path);
+	~KotoeriDictionary();
+
+	bool Find(const CFStringRef key, DCMFoundRecordIterator* iterator);
+	const bool IsGood() const;
+	const int Count() const;
+	const void Description() const;
+    };
+    typedef std::vector<KotoeriDictionary*> KotoeriDictionaryContainer;
+    typedef KotoeriDictionaryContainer::iterator KotoeriDictionaryIterator;
+    KotoeriDictionaryContainer dics_;
 
-    bool find(const CFStringRef key, DCMFoundRecordIterator* iterator);
+    void load(const std::vector<CppCFString>& dicFiles);
+    void unload();
 
 public:
-    KotoeriDictionary(const std::string path);
-    virtual ~KotoeriDictionary();
+    DMDictionary(const std::vector<CppCFString>& kotoeri_dic_file);
+    virtual ~DMDictionary();
 
     virtual int countOkuriAri();
     virtual int countOkuriNasi();
-    virtual std::vector<OkuriganaEntry> findOkuriAri(const CppCFString& str);
-    virtual std::vector<CppCFString> findOkuriNasi(const CppCFString& str);
+    virtual std::vector<OkuriganaEntry> findOkuriAri(const CppCFString& root);
+    virtual std::vector<CppCFString> findOkuriNasi(const CppCFString& query);
+
+    void changeDictionaryFile(const std::vector<CppCFString>& kotoeri_dic_file);
+    bool checkDictionary(const CFStringRef fpath);
 };
Index: AquaSKK/Dictionary.h
diff -u AquaSKK/Dictionary.h:1.3.2.2 AquaSKK/Dictionary.h:1.3.2.3
--- AquaSKK/Dictionary.h:1.3.2.2	Wed Feb 15 00:12:44 2006
+++ AquaSKK/Dictionary.h	Sat Feb 18 02:20:38 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: Dictionary.h,v 1.3.2.2 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: Dictionary.h,v 1.3.2.3 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -35,6 +35,9 @@
 public:
     virtual ~Dictionary() {};
 
+    // ‰Šú‰»(ˆø”‚Í EUC •¶Žš—ñ)
+    virtual void load(const std::string& location) = 0;
+
     // Ž«‘‚̏î•ñ
     virtual int countOkuriAri() = 0;
     virtual int countOkuriNasi() = 0;
Index: AquaSKK/DictionarySet.cpp
diff -u /dev/null AquaSKK/DictionarySet.cpp:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/DictionarySet.cpp	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,500 @@
+/*
+  $Id: DictionarySet.cpp,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002-2004 phonohawk
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <iostream>
+#include <sstream>
+#include "CppCFString.h"
+#include "OkuriganaEntry.h"
+#include "Dictionary.h"
+#include "SKKDictionary.h"
+#include "KotoeriDictionary.h"
+#include "DictionarySet.h"
+#include "SkkConfig.h"
+
+#if 0
+#	define D_PRINTF printf
+#else
+#	define D_PRINTF if(0)printf
+#endif
+
+// ’萔‚È‚Ç
+const char* SKK_USER_DICT_PATH = "/Library/AquaSKK/skk-user-dic";
+
+// ƒ†[ƒeƒBƒŠƒeƒB
+static void removeRedundantItems(std::vector<CppCFString>& candidates);
+
+// Null Ž«‘
+class NullDictionary: public Dictionary {
+public:
+    NullDictionary() {
+	// empty
+    }
+    virtual ~NullDictionary() {
+	// empty
+    }
+    virtual void load(const std::string& location) {
+	// empty
+    }
+    virtual int countOkuriAri() {
+	return 0;
+    }
+    virtual int countOkuriNasi() {
+	return 0;
+    }
+    virtual std::vector<OkuriganaEntry> findOkuriAri(const CppCFString& query) {
+	return std::vector<OkuriganaEntry>();
+    }
+    virtual std::vector<CppCFString> findOkuriNasi(const CppCFString& query) {
+	return std::vector<CppCFString>();
+    }
+};
+
+// ƒtƒ@ƒNƒgƒŠƒƒ\ƒbƒh
+Dictionary* DictionarySet::createDictionary(const DictionaryPref& pref) const {
+    Dictionary* dict;
+
+    // Ž«‘‚̐¶¬
+    switch(pref.type) {
+    case SKKDictionaryType:
+	dict = new SKKDictionary();
+	break;
+    case SKKAutoUpdateDictionaryType: // –¢ŽÀ‘•
+	dict = new NullDictionary();
+	break;
+    case KotoeriDictionaryType:
+	dict = new KotoeriDictionary();
+	break;
+    case ProxyDictionaryType:	// –¢ŽÀ‘•
+	dict = new NullDictionary();
+	break;
+    case GroupingDictionaryType:
+	dict = new NullDictionary();
+	break;
+    default:
+	std::cerr << "DictionarySet::createDictionary(): unknown type[" << pref.type << "]" << std::endl;
+	dict = new NullDictionary();
+	break;
+    }
+
+    // ‰Šú‰»
+    dict->load(pref.location);
+
+    return dict;
+}
+
+// map ƒL[¶¬
+std::string DictionarySet::generateID(const DictionaryPref& pref) const {
+    std::stringstream id;
+
+    // Ž«‘‚Ì ID ‚𐶐¬‚·‚é
+    id << pref.type << ":" << pref.location;
+
+    return id.str();
+}
+
+void DictionarySet::load() {
+    CFArrayRef arrayRef = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("DictionaryInfo"),
+								kCFPreferencesCurrentApplication);
+    if(arrayRef == NULL) {
+	return;
+    }
+
+    // ŒÃ‚¢î•ñ‚ðƒNƒŠƒA‚·‚é
+    prefs_.clear();
+
+    // ŒÂlŽ«‘‚ðæ“ª‚É“ü‚ê‚é
+    DictionaryPref entry;
+    entry.active = true;
+    entry.type = SKKDictionaryType;
+    entry.location = SkkConfig::home() + SKK_USER_DICT_PATH;
+    prefs_.push_back(entry);
+    if(dicts_.find(generateID(entry)) == dicts_.end()) {
+	dicts_[generateID(entry)] = userdict_;
+    }
+
+    // —LŒø‚ÈŽ«‘‚ð‘S‚㍁[ƒh‚·‚é
+    for(CFIndex index = 0; index < CFArrayGetCount(arrayRef); ++ index) {
+	CFDictionaryRef dictRef = (CFDictionaryRef)CFRetain(CFArrayGetValueAtIndex(arrayRef, index));
+
+	// DictionaryPref ‚ðì‚é
+	entry.active = ((CFBooleanRef)CFDictionaryGetValue(dictRef, CFSTR("active")) == kCFBooleanTrue);
+	CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(dictRef, CFSTR("type")), kCFNumberIntType, &entry.type);
+	CFStringRef str = (CFStringRef)CFDictionaryGetValue(dictRef, CFSTR("location"));
+	if(str) {
+	    char buf[2048];
+	    CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingEUC_JP);
+	    entry.location = buf;
+	} else {
+	    entry.location = "";
+	}
+
+	// ì¬Ï‚Ý‚ÌŽ«‘‚ª‚ ‚ê‚΁AŽæ“¾‚µ‚Ä‚¨‚­
+	DictionaryIterator iter = dicts_.find(generateID(entry));
+
+	// —LŒø‚©H
+	if(entry.active) {
+	    prefs_.push_back(entry);
+
+	    // Œ©‚‚©‚ç‚È‚¯‚ê‚΁A’ljÁ‚µ‚Ä‚¨‚­
+	    if(iter == dicts_.end()) {
+		dicts_[generateID(entry)] = createDictionary(entry);
+	    }
+	} else {
+	    // ì¬Ï‚Ý‚Å–³Œø‚É‚³‚ꂽŽ«‘‚͏Á‚·
+	    if(iter != dicts_.end()) {
+		delete iter->second;
+		dicts_.erase(generateID(entry));
+	    }
+	}
+	CFRelease(dictRef);
+    }
+    CFRelease(arrayRef);
+}
+
+DictionarySet::DictionarySet() {
+    // ƒ†[ƒU[Ž«‘‚ðƒ[ƒh‚·‚é
+    userdict_ = new SKKUserDictionary();
+    userdict_->load(SkkConfig::home() + SKK_USER_DICT_PATH);
+}
+
+DictionarySet::~DictionarySet() {
+    terminate();
+}
+
+DictionarySet& DictionarySet::theInstance() {
+    static DictionarySet obj;
+    return obj;
+}
+
+void DictionarySet::initialize() {
+    // Ž«‘‚ðƒ[ƒh‚·‚é
+    load();
+}
+
+void DictionarySet::terminate() {
+    struct local {
+	static void DeleteDictionary(std::pair<std::string, Dictionary*> entry) {
+	    delete entry.second;
+	    entry.second = NULL;
+	}
+    };
+
+    // ‘S‚Ä‚ÌŽ«‘‚ðíœ‚·‚é
+    std::for_each(dicts_.begin(), dicts_.end(), local::DeleteDictionary);
+    dicts_.clear();
+}
+
+CppCFString DictionarySet::skkserv_style_search(const CppCFString& query) {
+    // skkservƒXƒ^ƒCƒ‹‚Å‚Ìžûõ‚ðs‚ӁB
+    // ƒNƒGƒŠ‚ÌŒ`Ž®‚́Au‚¹‚ñ‚Æ‚¤vu‚¯sv‚̂₤‚È‚à‚́B––”ö‚É‹ó”’•¶Žš‚Í•t‚¯‚È‚¢B
+    // ƒŠƒvƒ‰ƒC‚ÌŒ`Ž®‚́Au/æ“ª/í“¬/‘K“’/v‚̂₤‚ɁA熏‘‚ÌŒ`Ž®‚»‚̂܂܁B–³‚¯‚ê‚΋󕶎š—ñB
+    if(query.length() == 0) {
+	return CppCFString();
+    }
+
+    // ‘—‚è˜ï–¼‚Í—L‚é‚©H
+    if(query[0] > 0xff && query[query.length() - 1] >= 'a' && query[query.length() - 1] <= 'z') {
+	// OkuriganaEntry.kana => OkuriganaEntry
+	std::map<CppCFString, OkuriganaEntry> cands;
+	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
+	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
+		break;
+	    }
+
+	    Dictionary* dict = dicts_[generateID(*iter)];
+	    std::vector<OkuriganaEntry> okuriEntries = dict->findOkuriAri(query);
+	    if(okuriEntries.empty()) {
+		continue;
+	    }
+
+	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
+		OkuriganaEntry& entry = cands[e->getKana()];
+		entry.setKana(e->getKana());
+		entry.getCandidates().insert(entry.getCandidates().begin(),
+					     e->getCandidates().begin(), e->getCandidates().end());
+	    }
+	}
+
+	// ‘S‚ẴGƒ“ƒgƒŠ‚ɂ‚¢‚āAd•¡‚µ‚Ä‚î‚é‚à‚Ì‚ðíœ‚µ‚È‚ª‚çAƒŠƒv
+	// ƒ‰ƒC‚ð‘g—§‚Ä‚éB
+	CppCFString reply;
+	for(std::map<CppCFString, OkuriganaEntry>::iterator e = cands.begin(); e != cands.end(); ++ e) {
+	    OkuriganaEntry& entry = e->second;
+	    entry.removeRedundantItems();
+	    if(entry.isWild()) {
+		reply.append(join('/', entry.getCandidates())).append('/');
+	    } else {
+		reply.append('[').append(entry.getKana()).append('/').
+		    append(join('/', entry.getCandidates())).append("/]/");
+	    }
+	}
+	if(reply.length() > 0) {
+	    reply.insert(0, '/');
+	}
+	return reply;
+    } else {
+	// ‘—‚è˜ï–¼‚ª–³‚¢B
+	std::vector<CppCFString> cands;
+
+	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
+	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
+		break;
+	    }
+
+	    Dictionary* dict = dicts_[generateID(*iter)];
+	    std::vector<CppCFString> result = dict->findOkuriNasi(query);
+	    if(!result.empty()) {
+		cands.insert(cands.end(), result.begin(), result.end());
+	    }
+	}
+
+	// d•¡‚µ‚Ä‚î‚é‚à‚Ì‚ðíœB
+	removeRedundantItems(cands);
+
+	if(cands.size() > 0) {
+	    return CppCFString('/').append(join('/', cands)).append('/');
+	} else {
+	    return CppCFString();
+	}
+    }
+}
+
+CppCFString DictionarySet::search(const CppCFString& query) {
+    CppCFString space(" ");
+    CppCFString nbsp("[20]");
+    bool has_okuri = query[0] == '+';
+    CppCFString query_str(query.substring(1)); // æ“ª‚Ì+,-‚ðÁ‚·B
+
+    D_PRINTF("QUERY: %s\n",query_str.toCString(kCFStringEncodingEUC_JP));
+
+    if(has_okuri) {
+	int pos_space = query_str.indexOf(' ');
+	CppCFString root = query_str.substring(0,pos_space).replace(nbsp,space); // u+‚È‚År ‚év‚È‚çu‚È‚Årv
+	CppCFString okuri = query_str.substring(pos_space+1).replace(nbsp,space); // u+‚È‚År ‚év‚È‚çu‚év
+	
+	std::vector<CppCFString> cand_strictly_matched;
+	std::vector<CppCFString> cand_unstrictly_matched;
+
+	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
+	    if(iter->type == GroupingDictionaryType &&
+	       (!cand_strictly_matched.empty() || !cand_unstrictly_matched.empty())) {
+		break;
+	    }
+
+	    Dictionary* dict = dicts_[generateID(*iter)];
+	    std::vector<OkuriganaEntry> okuriEntries = dict->findOkuriAri(root);
+	    if(okuriEntries.empty()) {
+		continue;
+	    }
+
+	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
+		if(e->getKana() == okuri) {
+		    cand_strictly_matched.insert(cand_strictly_matched.end(),
+						 e->getCandidates().begin(), e->getCandidates().end());
+		} else {
+		    cand_unstrictly_matched.insert(cand_unstrictly_matched.end(),
+						   e->getCandidates().begin(), e->getCandidates().end());
+		}
+	    }
+	}
+
+	std::vector<CppCFString> candidates = cand_strictly_matched;
+	candidates.insert(candidates.end(), cand_unstrictly_matched.begin(), cand_unstrictly_matched.end());
+
+	// candidates‚̏d•¡‚ðƒ`ƒFƒbƒNBd•¡‚µ‚Ä‚¢‚½‚çŒã‚É‚ ‚é‚à‚Ì‚ðíœB
+	removeRedundantItems(candidates);
+
+	// ‚±‚ÌŽž“_‚Åcandidates‚É“ü‚Á‚Ä‚¢‚é‚Ì‚ÍŠ¿Žš‚Ì•”•ª‚¾‚¯‚Ȃ̂ŁA
+	// ‘S‚Ä‚Ì—v‘f‚É‘—‚艼–¼‚ð•t‚¯‚éB“¯Žž‚ɃXƒy[ƒX‚ð[20]‚É•ÏŠ·B
+	// ’Žß‚ª•t‚¢‚Ä‚¢‚ê‚΁Aíœ‚·‚éB(Žb’è)
+	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
+	    const int semicolon_pos = ite->indexOf(';');
+	    if(semicolon_pos != -1) {
+		ite->erase(semicolon_pos, ite->length());
+	    }
+	    ite->append(okuri).replace(space,nbsp);
+	}
+
+	D_PRINTF("REPLY: %s\n",join(' ',candidates).toCString(kCFStringEncodingEUC_JP));
+	return join(' ', candidates);
+    } else {
+	std::vector<CppCFString> candidates;
+
+	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
+	    if(iter->type == GroupingDictionaryType && !candidates.empty()) {
+		break;
+	    }
+
+	    Dictionary* dict = dicts_[generateID(*iter)];
+	    std::vector<CppCFString> result = dict->findOkuriNasi(query_str);
+	    if(result.empty()) {
+		continue;
+	    }
+
+	    candidates.insert(candidates.end(), result.begin(), result.end());
+	}
+
+	// candidates‚̏d•¡‚ðƒ`ƒFƒbƒNBd•¡‚µ‚Ä‚¢‚½‚çŒã‚É‚ ‚é‚à‚Ì‚ðíœB
+	removeRedundantItems(candidates);
+
+	// candidates‚Ì‘S‚Ă̍€–ڂ̃Xƒy[ƒX‚ð[20]‚É•ÏŠ·B
+	// ’Žß‚ª•t‚¢‚Ä‚¢‚ê‚΁Aíœ‚·‚éB(Žb’è)
+	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
+	    const int semicolon_pos = ite->indexOf(';');
+	    if(semicolon_pos != -1) {
+		ite->erase(semicolon_pos, ite->length());
+	    }
+	    ite->replace(space,nbsp);
+	}
+
+	D_PRINTF("REPLY: %s\n",join(' ',candidates).toCString(kCFStringEncodingEUC_JP));
+	return join(' ',candidates);
+    }
+}
+
+void DictionarySet::registerToUserDic(const CppCFString& query) {
+    CppCFString space(" ");
+    CppCFString nbsp("[20]");
+
+    D_PRINTF("REGISTER: %s\n",query.toCString(kCFStringEncodingEUC_JP));
+
+    // ‘—‚艼–¼‚ ‚èHu+‚¨‚­r ‚è ‘–v
+    if(query[0] == '+') {
+	int pos_first_space = query.indexOf(' ');
+	int pos_second_space = query.indexOf(' ',pos_first_space+1);
+
+	CppCFString index(query.substring(1,pos_first_space));
+	index.replace(nbsp,space); // ‚¨‚­r
+	CppCFString okuri(query.substring(pos_first_space+1,pos_second_space));
+	okuri.replace(nbsp,space); // ‚è
+	CppCFString kanji(query.substring(pos_second_space+1));
+	kanji.replace(nbsp,space); // ‘–
+
+	if(index.length() == 0 || okuri.length() == 0 || kanji.length() == 0) {
+	    std::cerr << "AquaSKK: Invalid registration received; index:"
+		      << index.length() << " okuri:" << okuri.length()
+		      << " kanji:" << kanji.length() << " (len)" << std::endl;
+	} else {
+	    userdict_->registerOkuriAri(index, okuri, kanji);
+	}
+    } else {
+	// ‘—‚艼–¼–³‚µBu-‚©‚È ‰¼–¼v
+	int pos_space = query.indexOf(' ');
+	CppCFString index(query.substring(1,pos_space));
+	index.replace(nbsp,space);
+	CppCFString kanji(query.substring(pos_space+1));
+	kanji.replace(nbsp,space);
+
+	if(index.length() == 0 || kanji.length() == 0) {
+	    std::cerr << "AquaSKK: Invalid registration received; index:"
+		      << index.length() << " kanji:" << kanji.length()
+		      << " (len)" << std::endl;
+	} else {
+	    userdict_->registerOkuriNasi(index, kanji);
+	}
+    }
+}
+
+void DictionarySet::removeFromUserDic(const CppCFString& query) {
+    CppCFString space(" ");
+    CppCFString nbsp("[20]");
+
+    D_PRINTF("REMOVE: %s\n",query.toCString(kCFStringEncodingEUC_JP));
+
+    // ‘—‚艼–¼‚ ‚èHu+‚¨‚­r ‚è ‘–v
+    if(query[0] == '+') {
+	int pos_first_space = query.indexOf(' ');
+	int pos_second_space = query.indexOf(' ',pos_first_space+1);
+		
+	CppCFString index(query.substring(1,pos_first_space));
+	index.replace(nbsp,space); // ‚¨‚­r
+	CppCFString okuri(query.substring(pos_first_space+1,pos_second_space));
+	okuri.replace(nbsp,space); // ‚è
+	CppCFString kanji(query.substring(pos_second_space+1));
+	kanji.replace(nbsp,space); // ‘–
+
+	if(index.length() == 0 || okuri.length() == 0 || kanji.length() == 0) {
+	    std::cerr << "AquaSKK: Invalid removal received; index:"
+		      << index.length() << " okuri:" << okuri.length()
+		      << " kanji:" << kanji.length() << " (len)" << std::endl;
+	} else {
+	    userdict_->removeOkuriAri(index, kanji);
+	}
+    } else {
+	// ‘—‚艼–¼–³‚µBu-‚©‚È ‰¼–¼v
+	int pos_space = query.indexOf(' ');
+	CppCFString index(query.substring(1,pos_space));
+	index.replace(nbsp,space);
+	CppCFString kanji(query.substring(pos_space+1));
+	kanji.replace(nbsp,space);
+
+	if(index.length() == 0 || kanji.length() == 0) {
+	    std::cerr << "AquaSKK: Invalid removal received; index:"
+		      << index.length() << " kanji:" << kanji.length()
+		      << " (len)" << std::endl;
+	} else {
+	    userdict_->removeOkuriNasi(index, kanji);
+	}
+    }
+}
+
+CppCFString DictionarySet::searchCompletions(const CppCFString& query) {
+    CppCFString space(" ");
+    CppCFString nbsp("[20]");
+
+    D_PRINTF("COMPLETE: %s\n", query.toCString(kCFStringEncodingEUC_JP));
+
+    CppCFString head = query.replaceClone(nbsp, space);
+    std::vector<CppCFString> result = userdict_->findCompletions(head);
+
+    D_PRINTF("REPLY: %s\n",
+	     join(' ',result).toCString(kCFStringEncodingEUC_JP));
+
+    return join(' ', result);
+}
+
+static void removeRedundantItems(std::vector<CppCFString>& candidates) {
+    // ‚±‚̏ˆ—‚Ícandidates‚̐”‚ª‘‚¦‚é‚Ù‚ÇŽžŠÔ‚ª‚©‚©‚è‚Ü‚·B
+    // Žd•û‚È‚¢‚Ì‚¾‚낤‚¯‚ǁB
+    int size = candidates.size();
+    for(int i = 0; i < size; ++ i) {
+	CppCFString& cand_i = candidates[i];
+	bool is_redundant = false;
+	for(int j = 0; j < i; ++ j) {
+	    if(cand_i == candidates[j]) {
+		is_redundant = true;
+		break;
+	    }
+	}
+		
+	if(is_redundant) {
+	    candidates.erase(candidates.begin() + i);
+	    -- i; // ˆê‚Á‚µ‚½‚̂ŁA‚»‚ÌŒã‚É‘±‚­€–Ú‚ªˆê‚‚¸‚ê‚éB
+	    -- size; // ˆê‚Á‚µ‚½‚̂ŁB
+	}
+    }
+}
Index: AquaSKK/DictionarySet.h
diff -u /dev/null AquaSKK/DictionarySet.h:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/DictionarySet.h	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,79 @@
+/* -*- c++ -*-
+  $Id: DictionarySet.h,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002 phonohawk
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+
+class CppCFString;
+class Dictionary;
+class UserDictionary;
+
+class DictionarySet {
+    // Ž«‘‚ÌŽí—Þ
+    enum DictionaryTypes {
+	SKKDictionaryType = 10,
+	SKKAutoUpdateDictionaryType = 11,
+	KotoeriDictionaryType = 20,
+	ProxyDictionaryType = 30,
+	GroupingDictionaryType = 90,
+    };
+
+    // Ž«‘‚̐ݒè
+    struct DictionaryPref {
+	bool active;
+	int type;
+	std::string location;
+    };
+    typedef std::vector<DictionaryPref> DictionaryPrefContainer;
+    typedef DictionaryPrefContainer::iterator DictionaryPrefIterator;
+    typedef std::map<std::string, Dictionary*> DictionaryContainer;
+    typedef DictionaryContainer::iterator DictionaryIterator;
+
+    // ƒ†[ƒU[Ž«‘
+    UserDictionary* userdict_;
+
+    DictionaryPrefContainer prefs_;
+    DictionaryContainer dicts_;
+
+    Dictionary* createDictionary(const DictionaryPref& pref) const;
+    std::string generateID(const DictionaryPref& pref) const;
+    void load();
+
+    DictionarySet();
+    ~DictionarySet();
+
+public:
+    static DictionarySet& theInstance();
+
+    void initialize();
+    void terminate();
+
+    CppCFString skkserv_style_search(const CppCFString& query);
+    CppCFString search(const CppCFString& query);
+    void registerToUserDic(const CppCFString& query);
+    void removeFromUserDic(const CppCFString& query);
+    CppCFString searchCompletions(const CppCFString& query);
+};
Index: AquaSKK/Foundation.mm
diff -u AquaSKK/Foundation.mm:1.3.2.2 AquaSKK/Foundation.mm:1.3
--- AquaSKK/Foundation.mm:1.3.2.2	Wed Feb 15 00:12:44 2006
+++ AquaSKK/Foundation.mm	Wed Nov  9 00:02:24 2005
@@ -1,10 +1,10 @@
 /* -*- objc -*-
-  $Id: Foundation.mm,v 1.3.2.2 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: Foundation.mm,v 1.3 2005/11/08 15:02:24 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002 phonohawk
-  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -21,22 +21,37 @@
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#include <AppKit/AppKit.h>
-#include "Foundation.h"
-#include "PreferencesController.h"
-#include "SKKServer.h"
+#import <AppKit/AppKit.h>
+#import "Foundation.h"
+#import "PreferencesController.h"
+
+#include <Carbon/Carbon.h>
+#include <vector>
 #include "BIMClientServer.h"
+#include "CppCFString.h"
+#include "CppCFData.h"
+#include "PrivateRunLoop.h"
+#include "CppMessagePortServer.h"
 #include "ServerMessageReceiver.h"
+#include "SKKServer.h"
+#include "net/Socket.h"
 #include "Skkserv.h"
 
 @implementation Foundation
 
 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
-    // ƒvƒŠƒtƒ@ƒŒƒ“ƒX‚ðŽæ“¾
+    // ‚±‚Æ‚¦‚莫‘‚̃pƒX‚ðŽæ“¾
     PreferencesController* pref = [PreferencesController sharedController];
+    std::vector<CppCFString> vec;
+    for(unsigned i = 0 ; i < [[pref path] count] ; ++ i) {
+	vec.push_back((CFStringRef)[[pref path] objectAtIndex:i]);
+    }
 
     // Ž«‘ƒT[ƒo[‚ð‹N“®‚·‚é
-    SKKServer::sharedServer();
+    SKKServer::sharedServer([[pref getPathToMainDic] cString],
+			    [[pref getPathToSubDic] cString],
+			    [[pref getPathToUserDic] cString],
+			    vec);
 
     // ƒƒbƒZ[ƒWƒnƒ“ƒhƒ‰‚ð‹N“®‚·‚é
     ServerMessageReceiver::start(kAquaSKKServerRunLoopMode);
Index: AquaSKK/KotoeriDictionary.cpp
diff -u /dev/null AquaSKK/KotoeriDictionary.cpp:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/KotoeriDictionary.cpp	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,179 @@
+/*
+  $Id: KotoeriDictionary.cpp,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002 phonohawk
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*
+    Directory Maneger‘Ήž@2002.09.25 Shin_ichi Abe.
+*/
+
+#include <Carbon/Carbon.h>
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include "CppCFData.h"
+#include "CppCFString.h"
+#include "OkuriganaEntry.h"
+#include "KotoeriDictionary.h"
+
+KotoeriDictionary::KotoeriDictionary() : path_(0), isRegistered_(false), id_(0) {
+    // empty
+}
+
+KotoeriDictionary::~KotoeriDictionary() {
+    if(isRegistered_) {
+	DCMUnregisterDictionary(id_);
+    }
+
+    if(path_) {
+	CFRelease(path_);
+    }
+}
+
+void KotoeriDictionary::load(const std::string& path) {
+    path_ = CFStringCreateWithCString(kCFAllocatorDefault, path.c_str(), kCFStringEncodingEUC_JP);
+    if(!path_) {
+	std::cerr << "KotoeriDictionary: CFStringCreateCopy() failed" << std::endl;
+	return;
+    }
+
+    FSSpec spec;
+    FSRef fileRef;
+    CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_, kCFURLPOSIXPathStyle, false);
+    OSErr err = !noErr;
+    if(CFURLGetFSRef(url, &fileRef)) {
+        err = FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &spec, NULL);
+    }
+    if(url) CFRelease(url);
+    if(err != noErr) {
+	return;
+    }
+
+    if(DCMGetDictionaryIDFromFile(&spec, &id_) != noErr) {
+	std::cerr << "DCMGetDictionaryIDFromFile() failed" << std::endl;
+	if(DCMRegisterDictionaryFile(&spec, &id_) != noErr) {
+	    std::cerr << "DCMRegisterDictionaryFile() failed" << std::endl;
+	    return;
+	}
+	isRegistered_ = true;
+    }
+
+    char buf[1024];
+    CFStringGetCString(path_, buf, sizeof(buf), kCFStringEncodingUTF8);
+    std::cerr << "Kotoeri Dictionary(" << buf << ")" << std::endl
+	      << "  okuri-ari: 0 entries." << std::endl
+	      << "  okuri-nasi: " << countOkuriNasi() << " entries."
+	      << std::endl << std::endl;
+}
+
+int KotoeriDictionary::countOkuriAri() {
+    return 0;
+}
+
+int KotoeriDictionary::countOkuriNasi() {
+    ItemCount numItems;
+    if(DCMCountRecord(id_, &numItems) == noErr) {
+	return numItems;
+    }
+
+    return 0;
+}
+
+std::vector<OkuriganaEntry> KotoeriDictionary::findOkuriAri(const CppCFString& str) {
+    // ‚±‚Æ‚¦‚莫‘‚ɂ́u‘—‚è‚ ‚èv‚Í‘¶Ý‚µ‚È‚¢
+    return std::vector<OkuriganaEntry>();
+}
+
+std::vector<CppCFString> KotoeriDictionary::findOkuriNasi(const CppCFString& str) {
+    OSStatus status;
+    std::vector<CppCFString> result;
+
+    DCMFoundRecordIterator iterator;
+    if(!find(str.getString(), &iterator)) {
+	return result;
+    }
+
+    while(true) {
+	ByteCount keySize;
+	char foundKeyStr[kMaxKanjiLengthInAppleJapaneseDictionary];
+	DCMUniqueID uniqueID;
+	AERecord dataList;
+
+	// Get one record from result list
+	status = DCMIterateFoundRecord(iterator, // found result
+				       kMaxKanjiLengthInAppleJapaneseDictionary, // key buffer size
+				       &keySize, // actual found key size
+				       foundKeyStr, // found key data
+				       &uniqueID,   // ƒ†ƒj[ƒNID
+				       &dataList);  // AERecordƒf[ƒ^
+
+	if(status != noErr) break;
+
+	DescType actualType;
+	char dataBuffer[kMaxKanjiLengthInAppleJapaneseDictionary];
+	Size actualSize;
+
+	// Get one data from AERecord
+	status = AEGetKeyPtr(&dataList, 
+			     kDCMJapaneseHyokiTag,
+			     'utxt',
+			     &actualType,
+			     dataBuffer,
+			     kMaxKanjiLengthInAppleJapaneseDictionary,
+			     &actualSize);
+
+	// Dispose data AERecord
+	AEDisposeDesc(&dataList);
+
+	if(status != noErr) break;
+
+	// Œ‹‰Ê‚ɒljÁ‚·‚é
+	result.push_back(CppCFData(dataBuffer, actualSize).getData());
+    }
+    DCMDisposeRecordIterator(iterator);
+
+    return result;
+}
+
+bool KotoeriDictionary::find(const CFStringRef key, DCMFoundRecordIterator* iterator) {
+    DCMDictionaryRef ref;
+    if(DCMOpenDictionary(id_, 0, NULL, &ref) != noErr) {
+	std::cerr << "DCMOpenDictionary() failed" << std::endl;
+	return false;
+    }
+
+    // ƒŒƒR[ƒh‚ðŒŸõ
+    OSStatus status;
+    DCMFieldTag	dataFieldTagList[] = { kDCMJapaneseHyokiTag };
+    ByteCount length = CFStringGetLength(key) * sizeof(UniChar);
+    status = DCMFindRecords(ref, // Dictionary reference
+			    kDCMJapaneseYomiTag, // key field tag
+			    length,		 // key data length
+			    CFStringGetCharactersPtr(key), // key data
+			    kDCMFindMethodExactMatch, // find method
+			    1,		      // number of data field
+			    dataFieldTagList, // data field tag list
+			    0, 0, // search all records
+			    iterator); // found result
+
+    DCMCloseDictionary(ref);
+
+    return (status == noErr);
+}
Index: AquaSKK/KotoeriDictionary.h
diff -u /dev/null AquaSKK/KotoeriDictionary.h:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/KotoeriDictionary.h	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,52 @@
+/*
+  $Id: KotoeriDictionary.h,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2002 phonohawk
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*
+    Directory Maneger‘Ήž@2002.09.25 Shin_ichi Abe.
+*/
+
+#pragma once
+
+#include "Dictionary.h"
+
+class OkuriganaEntry;
+class CppCFString;
+
+class KotoeriDictionary: public Dictionary {
+    CFStringRef path_;
+    bool isRegistered_;
+    DCMDictionaryID id_;
+
+    bool find(const CFStringRef key, DCMFoundRecordIterator* iterator);
+
+public:
+    KotoeriDictionary();
+    virtual ~KotoeriDictionary();
+
+    virtual void load(const std::string& path);
+
+    virtual int countOkuriAri();
+    virtual int countOkuriNasi();
+
+    virtual std::vector<OkuriganaEntry> findOkuriAri(const CppCFString& str);
+    virtual std::vector<CppCFString> findOkuriNasi(const CppCFString& str);
+};
Index: AquaSKK/PreferencesController.h
diff -u AquaSKK/PreferencesController.h:1.6.2.3 AquaSKK/PreferencesController.h:1.6.2.4
--- AquaSKK/PreferencesController.h:1.6.2.3	Wed Feb 15 00:12:44 2006
+++ AquaSKK/PreferencesController.h	Sat Feb 18 02:20:38 2006
@@ -1,5 +1,5 @@
 /* -*- objc -*-
-  $Id: PreferencesController.h,v 1.6.2.3 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: PreferencesController.h,v 1.6.2.4 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -71,7 +71,7 @@
 - (IBAction)saveUserDefault:(id)sender;
 
 - (BOOL)isSkkservEnabled;
-- (int)skkservPort;
+- (unsigned short)skkservPort;
 - (BOOL)isSkkservLocalOnly;
 
 @end
Index: AquaSKK/PreferencesController.mm
diff -u AquaSKK/PreferencesController.mm:1.6.2.5 AquaSKK/PreferencesController.mm:1.6.2.6
--- AquaSKK/PreferencesController.mm:1.6.2.5	Wed Feb 15 00:12:44 2006
+++ AquaSKK/PreferencesController.mm	Sat Feb 18 02:20:38 2006
@@ -1,5 +1,5 @@
 /*  -*- objc -*-
-  $Id: PreferencesController.mm,v 1.6.2.5 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: PreferencesController.mm,v 1.6.2.6 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -21,18 +21,14 @@
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#import <AppKit/AppKit.h>
-#import "PreferencesController.h"
+#include <AppKit/AppKit.h>
+#include "PreferencesController.h"
 
-#include <Carbon/Carbon.h>
-#include <vector>
 #include "BIMClientServer.h"
 #include "CppCFString.h"
 #include "CppCFData.h"
-#include "DMDictionary.h"
-#include "SKKDictionary.h"
-#include "SKKServer.h"
-#include "Skkserv.h"
+#include "DictionarySet.h"
+#include "skkserv.h"
 #include "ClientConnectionFactory.h"
 
 // ƒ†[ƒU[ƒfƒtƒHƒ‹ƒg—p
@@ -294,31 +290,16 @@
 }
 
 - (BOOL)windowShouldClose:(NSNotification *)aNotification {
-    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-
-    // Ž«‘ƒT[ƒo[‚ðÄ‰Šú‰»‚·‚é
-    SKKServer::sharedServer().reload();
+    // Ä“xAŽ«‘ƒT[ƒo[‚ð‰Šú‰»‚·‚é
+    DictionarySet::theInstance().initialize();
 
-    Skkserv& skkserv = Skkserv::sharedServer();
-    skkserv.setLocalOnly([skkserv_local_only state] == YES ? true : false);
-    if(skkserv.isRunning()) {
-	if([skkserv_enabled state] == NO) {
-	    // ’âŽ~
-	    skkserv.stop();
-	} else {
-	    if([skkserv_port intValue] != skkserv.port()) {
-		// ƒ|[ƒgÌX
-		skkserv.stop();
-		skkserv.start([skkserv_port intValue]);
-	    }
-	}
-    } else {
-	if([skkserv_enabled state] == YES) {
-	    // ŠJŽn
-	    skkserv.start([skkserv_port intValue]);
-	}
+    // skkserv ƒGƒ~ƒ…ƒŒ[ƒVƒ‡ƒ“‚̍ċN“®
+    skkserv& skkserv = skkserv::theInstance();
+    skkserv.stop();
+    if([self isSkkservEnabled]) {
+	skkserv.start([self skkservPort], [self isSkkservLocalOnly]);
     }
-    
+
     [fontPanel close];
 	
     [self saveUserDefault:self];
@@ -330,7 +311,7 @@
     return [skkserv_enabled state];
 }
 
-- (int)skkservPort {
+- (unsigned short)skkservPort {
     return [skkserv_port intValue];
 }
 
Index: AquaSKK/SKKDictionary.cpp
diff -u AquaSKK/SKKDictionary.cpp:1.6.2.1 AquaSKK/SKKDictionary.cpp:1.6.2.2
--- AquaSKK/SKKDictionary.cpp:1.6.2.1	Sun Jan  8 16:15:30 2006
+++ AquaSKK/SKKDictionary.cpp	Sat Feb 18 02:20:38 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: SKKDictionary.cpp,v 1.6.2.1 2006/01/08 07:15:30 t-suwa Exp $
+  $Id: SKKDictionary.cpp,v 1.6.2.2 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -150,14 +150,56 @@
 
 #pragma mark --- SKKDictionary ---
 
-SKKDictionary::SKKDictionary(const std::string& path) : path_(path) {
-    load();
+SKKDictionary::SKKDictionary() {
+    // empty
 }
 
 SKKDictionary::~SKKDictionary() {
     // empty
 }
 
+void SKKDictionary::load(const std::string& path) {
+    std::string line;
+    std::string index;
+    std::string entry;
+    std::ifstream dic;
+
+    path_ = path;
+
+    if(!skkdic::OpenAndSeek(path_, dic)) {
+	return;
+    }
+
+    while(1) {
+	dic >> index;
+	if(dic.eof()) break;
+	dic.ignore(1, ' ');
+	std::getline(dic, line);
+	if(skkdic::OkuriNasiMark.find(line) != std::string::npos) break;
+
+	// ‘—‚è‚È‚µƒGƒ“ƒgƒŠ‚͍~‡‚Ń\[ƒg‚³‚ê‚Ä‚¢‚é‚Í‚¸
+	okuriAri_.push_front(SKKPair(index, line));
+    }
+
+    while(1) {
+	dic >> index;
+	if(dic.eof()) break;
+	dic.ignore(1, ' ');
+	std::getline(dic, line);
+
+	// ‘—‚è‚ ‚èƒGƒ“ƒgƒŠ‚͏¸‡‚Ń\[ƒg‚³‚ê‚Ä‚¢‚é‚Í‚¸
+	okuriNasi_.push_back(SKKPair(index, line));
+    }
+
+    // ƒGƒ“ƒgƒŠ‚ðƒ\[ƒg‚·‚é
+    std::sort(okuriAri_.begin(), okuriAri_.end(), skkdic::EntryCompare());
+    std::sort(okuriNasi_.begin(), okuriNasi_.end(), skkdic::EntryCompare());
+
+    std::cerr << "SKK Dictionary(" << path_ << ")" << std::endl
+	      << "  okuri-ari: " << countOkuriAri() << " entries." << std::endl
+	      << "  okuri-nasi: " << countOkuriNasi() << " entries." << std::endl << std::endl;
+}
+
 int SKKDictionary::countOkuriAri() {
     return okuriAri_.size();
 }
@@ -192,21 +234,25 @@
     return skkdic::ConvertOkuriNasi(SKKEntry::ParseOkuriNasi(i->first, i->second));
 }
 
-void SKKDictionary::changeDictionaryFile(const std::string& path) {
-    EntryContainer().swap(okuriAri_);
-    EntryContainer().swap(okuriNasi_);
+#pragma mark --- SKKUserDictionary ---
 
-    path_ = path;
+SKKUserDictionary::SKKUserDictionary() {
+    // empty
+}
 
-    load();
+SKKUserDictionary::~SKKUserDictionary() {
+    // ‹­§“I‚ÉŽ«‘‚ð•Û‘¶‚·‚é
+    save(true);
 }
 
-void SKKDictionary::load() {
+void SKKUserDictionary::load(const std::string& path) {
     std::string line;
     std::string index;
     std::string entry;
     std::ifstream dic;
 
+    path_ = path;
+
     if(!skkdic::OpenAndSeek(path_, dic)) {
 	return;
     }
@@ -217,9 +263,7 @@
 	dic.ignore(1, ' ');
 	std::getline(dic, line);
 	if(skkdic::OkuriNasiMark.find(line) != std::string::npos) break;
-
-	// ‘—‚è‚È‚µƒGƒ“ƒgƒŠ‚͍~‡‚Ń\[ƒg‚³‚ê‚Ä‚¢‚é‚Í‚¸
-	okuriAri_.push_front(SKKPair(index, line));
+	okuriAri_.push_back(SKKPair(index, line));
     }
 
     while(1) {
@@ -227,31 +271,14 @@
 	if(dic.eof()) break;
 	dic.ignore(1, ' ');
 	std::getline(dic, line);
-
-	// ‘—‚è‚ ‚èƒGƒ“ƒgƒŠ‚͏¸‡‚Ń\[ƒg‚³‚ê‚Ä‚¢‚é‚Í‚¸
 	okuriNasi_.push_back(SKKPair(index, line));
     }
 
-    // ƒGƒ“ƒgƒŠ‚ðƒ\[ƒg‚·‚é
-    std::sort(okuriAri_.begin(), okuriAri_.end(), skkdic::EntryCompare());
-    std::sort(okuriNasi_.begin(), okuriNasi_.end(), skkdic::EntryCompare());
-
-    std::cerr << "SKK Dictionary(" << path_ << ")" << std::endl
+    std::cerr << "SKK User Dictionary(" << path_ << ")" << std::endl
 	      << "  okuri-ari: " << countOkuriAri() << " entries." << std::endl
 	      << "  okuri-nasi: " << countOkuriNasi() << " entries." << std::endl << std::endl;
 }
 
-#pragma mark --- SKKUserDictionary ---
-
-SKKUserDictionary::SKKUserDictionary(const std::string& path) : path_(path) {
-    load();
-}
-
-SKKUserDictionary::~SKKUserDictionary() {
-    // ‹­§“I‚ÉŽ«‘‚ð•Û‘¶‚·‚é
-    save(true);
-}
-
 int SKKUserDictionary::countOkuriAri() {
     return okuriAri_.size();
 }
@@ -380,38 +407,6 @@
 
 // ------------------------------------------------------------------
 
-void SKKUserDictionary::load() {
-    std::string line;
-    std::string index;
-    std::string entry;
-    std::ifstream dic;
-
-    if(!skkdic::OpenAndSeek(path_, dic)) {
-	return;
-    }
-
-    while(1) {
-	dic >> index;
-	if(dic.eof()) break;
-	dic.ignore(1, ' ');
-	std::getline(dic, line);
-	if(skkdic::OkuriNasiMark.find(line) != std::string::npos) break;
-	okuriAri_.push_back(SKKPair(index, line));
-    }
-
-    while(1) {
-	dic >> index;
-	if(dic.eof()) break;
-	dic.ignore(1, ' ');
-	std::getline(dic, line);
-	okuriNasi_.push_back(SKKPair(index, line));
-    }
-
-    std::cerr << "SKK User Dictionary(" << path_ << ")" << std::endl
-	      << "  okuri-ari: " << countOkuriAri() << " entries." << std::endl
-	      << "  okuri-nasi: " << countOkuriNasi() << " entries." << std::endl << std::endl;
-}
-
 void SKKUserDictionary::save(bool force) {
     static int updateCount = 0;
     static std::time_t startOfUpdate = std::time(0);
Index: AquaSKK/SKKDictionary.h
diff -u AquaSKK/SKKDictionary.h:1.5.2.1 AquaSKK/SKKDictionary.h:1.5.2.2
--- AquaSKK/SKKDictionary.h:1.5.2.1	Sun Jan  8 16:15:30 2006
+++ AquaSKK/SKKDictionary.h	Sat Feb 18 02:20:38 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: SKKDictionary.h,v 1.5.2.1 2006/01/08 07:15:30 t-suwa Exp $
+  $Id: SKKDictionary.h,v 1.5.2.2 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -23,9 +23,6 @@
 
 #pragma once
 
-#include <iostream>
-#include <string>
-#include <vector>
 #include <deque>
 #include "Dictionary.h"
 
@@ -45,12 +42,12 @@
     EntryContainer okuriAri_;
     EntryContainer okuriNasi_;
 
-    void load();
-
 public:
-    SKKDictionary(const std::string& path);
+    SKKDictionary();
     virtual ~SKKDictionary();
 
+    virtual void load(const std::string& path);
+
     virtual int countOkuriAri();
     virtual int countOkuriNasi();
 
@@ -66,13 +63,14 @@
     EntryContainer okuriAri_;
     EntryContainer okuriNasi_;
 
-    void load();
     void save(bool force = false);
 
 public:
-    SKKUserDictionary(const std::string& path);
+    SKKUserDictionary();
     virtual ~SKKUserDictionary();
 
+    virtual void load(const std::string& path);
+
     virtual int countOkuriAri();
     virtual int countOkuriNasi();
 
Index: AquaSKK/SKKServer.cpp
diff -u AquaSKK/SKKServer.cpp:1.4.2.2 AquaSKK/SKKServer.cpp:1.4
--- AquaSKK/SKKServer.cpp:1.4.2.2	Wed Feb 15 00:12:44 2006
+++ AquaSKK/SKKServer.cpp	Wed Nov  9 00:02:24 2005
@@ -1,10 +1,10 @@
 /*
-  $Id: SKKServer.cpp,v 1.4.2.2 2006/02/14 15:12:44 t-suwa Exp $
+  $Id: SKKServer.cpp,v 1.4 2005/11/08 15:02:24 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002-2004 phonohawk
-  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -21,15 +21,17 @@
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#include <Carbon/Carbon.h>
 #include <iostream>
-#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
 #include "CppCFString.h"
 #include "OkuriganaEntry.h"
 #include "Dictionary.h"
-#include "SKKDictionary.h"
 #include "DMDictionary.h"
+#include "SKKDictionary.h"
 #include "SKKServer.h"
-#include "SkkConfig.h"
 
 #if 0
 #	define D_PRINTF printf
@@ -37,160 +39,69 @@
 #	define D_PRINTF if(0)printf
 #endif
 
-// ’萔‚È‚Ç
-const char* SKK_USER_DICT_PATH = "/Library/AquaSKK/skk-user-dic";
-
-// ƒ†[ƒeƒBƒŠƒeƒB
 static void removeRedundantItems(std::vector<CppCFString>& candidates);
 
-// Null Ž«‘
-class NullDictionary: public Dictionary {
-public:
-    NullDictionary(const std::string& location) {
-	// empty
-    }
-    virtual ~NullDictionary() {
-	// empty
-    }
-    virtual int countOkuriAri() {
-	return 0;
-    }
-    virtual int countOkuriNasi() {
-	return 0;
-    }
-    virtual std::vector<OkuriganaEntry> findOkuriAri(const CppCFString& query) {
-	return std::vector<OkuriganaEntry>();
-    }
-    virtual std::vector<CppCFString> findOkuriNasi(const CppCFString& query) {
-	return std::vector<CppCFString>();
-    }
-};
-
-// ƒtƒ@ƒNƒgƒŠƒƒ\ƒbƒh
-Dictionary* SKKServer::createDictionary(const DictionaryPref& pref) const {
-    switch(pref.type) {
-    case SKKDictionaryType:
-	return new SKKDictionary(pref.location);
-	break;
-    case SKKAutoUpdateDictionaryType:
-	return new NullDictionary(pref.location);
-	break;
-    case KotoeriDictionaryType:
-	return new KotoeriDictionary(pref.location);
-	break;
-    case ProxyDictionaryType:
-	return new NullDictionary(pref.location);
-	break;
-    case GroupingDictionaryType:
-	return new NullDictionary(pref.location);
-	break;
-    default:
-	std::cerr << "SKKServer::createDictionary(): unknown type[" << pref.type << "]" << std::endl;
-	break;
-    }
-
-    return new NullDictionary(pref.location);
-}
-
-// map ƒL[¶¬
-std::string SKKServer::generateID(const DictionaryPref& pref) const {
-    std::stringstream id;
-
-    // Ž«‘‚Ì ID ‚𐶐¬‚·‚é
-    id << pref.type << ":" << pref.location;
-
-    return id.str();
-}
+static SKKServer* _shared_instance = NULL;
 
-void SKKServer::load() {
-    CFArrayRef arrayRef = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("DictionaryInfo"),
-								kCFPreferencesCurrentApplication);
-    if(arrayRef == NULL) {
-	return;
-    }
-
-    // ŒÃ‚¢î•ñ‚ðƒNƒŠƒA‚·‚é
-    prefs_.clear();
-
-    // ŒÂlŽ«‘‚ðæ“ª‚É“ü‚ê‚é
-    DictionaryPref entry;
-    entry.active = true;
-    entry.type = SKKDictionaryType;
-    entry.location = SkkConfig::home() + SKK_USER_DICT_PATH;
-    prefs_.push_back(entry);
-    if(dicts_.find(generateID(entry)) == dicts_.end()) {
-	dicts_[generateID(entry)] = userdict_;
-    }
-
-    // —LŒø‚ÈŽ«‘‚ð‘S‚㍁[ƒh‚·‚é
-    for(CFIndex index = 0; index < CFArrayGetCount(arrayRef); ++ index) {
-	CFDictionaryRef dictRef = (CFDictionaryRef)CFRetain(CFArrayGetValueAtIndex(arrayRef, index));
-
-	// DictionaryPref ‚ðì‚é
-	entry.active = ((CFBooleanRef)CFDictionaryGetValue(dictRef, CFSTR("active")) == kCFBooleanTrue);
-	CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(dictRef, CFSTR("type")), kCFNumberIntType, &entry.type);
-	CFStringRef str = (CFStringRef)CFDictionaryGetValue(dictRef, CFSTR("location"));
-	if(str) {
-	    char buf[2048];
-	    CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingEUC_JP);
-	    entry.location = buf;
-	} else {
-	    entry.location = "";
-	}
-
-	// ì¬Ï‚Ý‚ÌŽ«‘‚ª‚ ‚ê‚΁AŽæ“¾‚µ‚Ä‚¨‚­
-	DictionaryIterator iter = dicts_.find(generateID(entry));
-
-	// —LŒø‚©H
-	if(entry.active) {
-	    prefs_.push_back(entry);
-
-	    // Œ©‚‚©‚ç‚È‚¯‚ê‚΁A’ljÁ‚µ‚Ä‚¨‚­
-	    if(iter == dicts_.end()) {
-		dicts_[generateID(entry)] = createDictionary(entry);
-	    }
-	} else {
-	    // ì¬Ï‚Ý‚Å–³Œø‚É‚³‚ꂽŽ«‘‚͏Á‚·
-	    if(iter != dicts_.end()) {
-		delete iter->second;
-		dicts_.erase(generateID(entry));
-	    }
-	}
-	CFRelease(dictRef);
-    }
-    CFRelease(arrayRef);
+SKKServer& SKKServer::sharedServer() {
+    return *_shared_instance;
 }
 
-SKKServer::SKKServer() {
-    userdict_ = new SKKUserDictionary(SkkConfig::home() + SKK_USER_DICT_PATH);
-    load();
+SKKServer&
+SKKServer::sharedServer(const std::string& base_dic_file,
+			const std::string& sub_dic_file,
+			const std::string& user_dic_file,
+			const std::vector<CppCFString>& kotoeri_dic_file) {
+    if(_shared_instance == NULL) {
+	_shared_instance = new SKKServer(base_dic_file, sub_dic_file,
+					 user_dic_file, kotoeri_dic_file);
+    }
+
+    return *_shared_instance;
+}
+
+SKKServer::SKKServer(const std::string& base_dic_file,
+		     const std::string& sub_dic_file,
+		     const std::string& user_dic_file,
+		     const std::vector<CppCFString>& kotoeri_dic_file) {
+    base_dic = new SKKDictionary(base_dic_file);
+    sub_dic = new SKKDictionary(sub_dic_file);
+    user_dic = new SKKUserDictionary(user_dic_file);
+    kotoeri_dic = new DMDictionary(kotoeri_dic_file);
+
+    all_dics.push_back(user_dic);
+    all_dics.push_back(sub_dic);
+    all_dics.push_back(base_dic);
+    all_dics.push_back(kotoeri_dic);
 }
 
 SKKServer::~SKKServer() {
     terminate();
 }
 
-SKKServer& SKKServer::sharedServer() {
-    static SKKServer obj;
-    return obj;
-}
-
-void SKKServer::reload() {
-    // Ž«‘‚ðƒ[ƒh‚µ’¼‚·
-    load();
-}
-
 void SKKServer::terminate() {
     struct local {
-	static void DeleteDictionary(std::pair<std::string, Dictionary*> entry) {
-	    delete entry.second;
-	    entry.second = NULL;
+	static void DeleteDictionary(Dictionary* ptr) {
+	    delete ptr;
+	    ptr = NULL;
 	}
     };
 
     // ‘S‚Ä‚ÌŽ«‘‚ðíœ‚·‚é
-    std::for_each(dicts_.begin(), dicts_.end(), local::DeleteDictionary);
-    dicts_.clear();
+    std::for_each(all_dics.begin(), all_dics.end(), local::DeleteDictionary);
+    all_dics.clear();
+}
+
+SKKDictionary& SKKServer::getBaseDic() {
+    return *dynamic_cast<SKKDictionary*>(base_dic);
+}
+
+SKKDictionary& SKKServer::getSubDic() {
+    return *dynamic_cast<SKKDictionary*>(sub_dic);
+}
+
+DMDictionary& SKKServer::getKotoeriDic() {
+    return *dynamic_cast<DMDictionary*>(kotoeri_dic);
 }
 
 CppCFString SKKServer::skkserv_style_search(const CppCFString& query) {
@@ -202,33 +113,34 @@
     }
 
     // ‘—‚è˜ï–¼‚Í—L‚é‚©H
-    if(query[0] > 0xff && query[query.length() - 1] >= 'a' && query[query.length() - 1] <= 'z') {
+    if(query[0] > 0xff &&
+       query[query.length()-1] >= 'a' && query[query.length()-1] <= 'z') {
 	// OkuriganaEntry.kana => OkuriganaEntry
 	std::map<CppCFString, OkuriganaEntry> cands;
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
-	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
-		break;
-	    }
+	for(std::vector<Dictionary*>::const_iterator dic = all_dics.begin();
+	    dic != all_dics.end(); ++ dic) {
 
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::vector<OkuriganaEntry> okuriEntries = dict->findOkuriAri(query);
-	    if(okuriEntries.empty()) {
+	    std::vector<OkuriganaEntry> vec_okuri = (*dic)->findOkuriAri(query);
+	    if(vec_okuri.empty()) {
 		continue;
 	    }
 
-	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
+	    for(std::vector<OkuriganaEntry>::iterator e = vec_okuri.begin();
+		 e != vec_okuri.end(); ++ e) {
 		OkuriganaEntry& entry = cands[e->getKana()];
 		entry.setKana(e->getKana());
-		entry.getCandidates().insert(entry.getCandidates().begin(),
-					     e->getCandidates().begin(), e->getCandidates().end());
+		entry.getCandidates().insert(
+		    entry.getCandidates().begin(),
+		    e->getCandidates().begin(),
+		    e->getCandidates().end());
 	    }
 	}
 
 	// ‘S‚ẴGƒ“ƒgƒŠ‚ɂ‚¢‚āAd•¡‚µ‚Ä‚î‚é‚à‚Ì‚ðíœ‚µ‚È‚ª‚çAƒŠƒv
 	// ƒ‰ƒC‚ð‘g—§‚Ä‚éB
 	CppCFString reply;
-	for(std::map<CppCFString, OkuriganaEntry>::iterator e = cands.begin(); e != cands.end(); ++ e) {
+	for(std::map<CppCFString, OkuriganaEntry>::iterator e = cands.begin();
+	    e != cands.end(); ++ e) {
 	    OkuriganaEntry& entry = e->second;
 	    entry.removeRedundantItems();
 	    if(entry.isWild()) {
@@ -246,16 +158,14 @@
 	// ‘—‚è˜ï–¼‚ª–³‚¢B
 	std::vector<CppCFString> cands;
 
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
-	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
-		break;
-	    }
+	for(std::vector<Dictionary*>::const_iterator ite = all_dics.begin();
+	    ite != all_dics.end(); ++ ite) {
 
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::vector<CppCFString> result = dict->findOkuriNasi(query);
-	    if(!result.empty()) {
-		cands.insert(cands.end(), result.begin(), result.end());
+	    std::vector<CppCFString> cand_for_one = (*ite)->findOkuriNasi(query);
+	    if(cand_for_one.size() > 0) {
+		cands.insert(cands.end(),
+			     cand_for_one.begin(),
+			     cand_for_one.end());
 	    }
 	}
 
@@ -285,33 +195,36 @@
 	
 	std::vector<CppCFString> cand_strictly_matched;
 	std::vector<CppCFString> cand_unstrictly_matched;
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
-	    if(iter->type == GroupingDictionaryType &&
-	       (!cand_strictly_matched.empty() || !cand_unstrictly_matched.empty())) {
-		break;
-	    }
-
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::vector<OkuriganaEntry> okuriEntries = dict->findOkuriAri(root);
-	    if(okuriEntries.empty()) {
-		continue;
-	    }
-
-	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
-		if(e->getKana() == okuri) {
-		    cand_strictly_matched.insert(cand_strictly_matched.end(),
-						 e->getCandidates().begin(), e->getCandidates().end());
+	for(std::vector<Dictionary*>::const_iterator dic = all_dics.begin();
+	    dic != all_dics.end(); ++ dic) {
+	    std::vector<OkuriganaEntry> vec_okuri = (*dic)->findOkuriAri(root);
+	    if(vec_okuri.empty()) continue;
+
+	    for(std::vector<OkuriganaEntry>::const_iterator
+		    okuri_entry = vec_okuri.begin();
+		okuri_entry != vec_okuri.end(); ++ okuri_entry) {
+		// ‚±‚ÌOkuriganaEntry‚ÌŒ©o‚µ‚Æokuri‚ªˆê’v‚µ‚Ä‚¢‚½‚ç
+		// cand_strictly_matched‚É“ü‚êAˆê’v‚µ‚Ä‚¢‚È‚¯‚ê‚Î
+		// cand_unstrictly_matched‚É“ü‚ê‚éB
+		if(okuri_entry->getKana() == okuri) {
+		    cand_strictly_matched.insert(
+			cand_strictly_matched.end(),
+			okuri_entry->getCandidates().begin(),
+			okuri_entry->getCandidates().end());
 		} else {
-		    cand_unstrictly_matched.insert(cand_unstrictly_matched.end(),
-						   e->getCandidates().begin(), e->getCandidates().end());
+		    cand_unstrictly_matched.insert(
+			cand_unstrictly_matched.end(),
+			okuri_entry->getCandidates().begin(),
+			okuri_entry->getCandidates().end());
 		}
 	    }
 	}
 
 	std::vector<CppCFString> candidates = cand_strictly_matched;
-	candidates.insert(candidates.end(), cand_unstrictly_matched.begin(), cand_unstrictly_matched.end());
+	candidates.insert(
+	    candidates.end(),
+	    cand_unstrictly_matched.begin(),
+	    cand_unstrictly_matched.end());
 
 	// candidates‚̏d•¡‚ðƒ`ƒFƒbƒNBd•¡‚µ‚Ä‚¢‚½‚çŒã‚É‚ ‚é‚à‚Ì‚ðíœB
 	removeRedundantItems(candidates);
@@ -319,7 +232,8 @@
 	// ‚±‚ÌŽž“_‚Åcandidates‚É“ü‚Á‚Ä‚¢‚é‚Ì‚ÍŠ¿Žš‚Ì•”•ª‚¾‚¯‚Ȃ̂ŁA
 	// ‘S‚Ä‚Ì—v‘f‚É‘—‚艼–¼‚ð•t‚¯‚éB“¯Žž‚ɃXƒy[ƒX‚ð[20]‚É•ÏŠ·B
 	// ’Žß‚ª•t‚¢‚Ä‚¢‚ê‚΁Aíœ‚·‚éB(Žb’è)
-	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
+	for(std::vector<CppCFString>::iterator ite = candidates.begin();
+	    ite != candidates.end(); ++ ite) {
 	    const int semicolon_pos = ite->indexOf(';');
 	    if(semicolon_pos != -1) {
 		ite->erase(semicolon_pos, ite->length());
@@ -331,20 +245,12 @@
 	return join(' ', candidates);
     } else {
 	std::vector<CppCFString> candidates;
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // ƒOƒ‹[ƒvŽ«‘‚̏ꍇAŠù‚ÉŒó•â‚ªŒ©‚‚©‚Á‚Ä‚¢‚ê‚ÎŒŸõ‚ð‚â‚ß‚é
-	    if(iter->type == GroupingDictionaryType && !candidates.empty()) {
-		break;
-	    }
-
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::vector<CppCFString> result = dict->findOkuriNasi(query_str);
-	    if(result.empty()) {
-		continue;
-	    }
-
-	    candidates.insert(candidates.end(), result.begin(), result.end());
+	for(std::vector<Dictionary*>::const_iterator ite = all_dics.begin();
+	    ite != all_dics.end(); ++ ite) {
+	    std::vector<CppCFString> cand_for_one = (*ite)->findOkuriNasi(query_str);
+	    if(cand_for_one.size() > 0)
+		candidates.insert(candidates.end(),
+				  cand_for_one.begin(), cand_for_one.end());
 	}
 
 	// candidates‚̏d•¡‚ðƒ`ƒFƒbƒNBd•¡‚µ‚Ä‚¢‚½‚çŒã‚É‚ ‚é‚à‚Ì‚ðíœB
@@ -352,7 +258,8 @@
 
 	// candidates‚Ì‘S‚Ă̍€–ڂ̃Xƒy[ƒX‚ð[20]‚É•ÏŠ·B
 	// ’Žß‚ª•t‚¢‚Ä‚¢‚ê‚΁Aíœ‚·‚éB(Žb’è)
-	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
+	for(std::vector<CppCFString>::iterator ite = candidates.begin();
+	    ite != candidates.end(); ++ ite) {
 	    const int semicolon_pos = ite->indexOf(';');
 	    if(semicolon_pos != -1) {
 		ite->erase(semicolon_pos, ite->length());
@@ -388,7 +295,7 @@
 		      << index.length() << " okuri:" << okuri.length()
 		      << " kanji:" << kanji.length() << " (len)" << std::endl;
 	} else {
-	    userdict_->registerOkuriAri(index, okuri, kanji);
+	    user_dic->registerOkuriAri(index, okuri, kanji);
 	}
     } else {
 	// ‘—‚艼–¼–³‚µBu-‚©‚È ‰¼–¼v
@@ -403,7 +310,7 @@
 		      << index.length() << " kanji:" << kanji.length()
 		      << " (len)" << std::endl;
 	} else {
-	    userdict_->registerOkuriNasi(index, kanji);
+	    user_dic->registerOkuriNasi(index, kanji);
 	}
     }
 }
@@ -431,7 +338,7 @@
 		      << index.length() << " okuri:" << okuri.length()
 		      << " kanji:" << kanji.length() << " (len)" << std::endl;
 	} else {
-	    userdict_->removeOkuriAri(index, kanji);
+	    user_dic->removeOkuriAri(index, kanji);
 	}
     } else {
 	// ‘—‚艼–¼–³‚µBu-‚©‚È ‰¼–¼v
@@ -446,19 +353,24 @@
 		      << index.length() << " kanji:" << kanji.length()
 		      << " (len)" << std::endl;
 	} else {
-	    userdict_->removeOkuriNasi(index, kanji);
+	    user_dic->removeOkuriNasi(index, kanji);
 	}
     }
 }
 
 CppCFString SKKServer::searchCompletions(const CppCFString& query) {
+    // Žb’è‘Ήž(#7387)
+    if(query.length() < 1) {
+	return CppCFString();
+    }
+
     CppCFString space(" ");
     CppCFString nbsp("[20]");
 
     D_PRINTF("COMPLETE: %s\n", query.toCString(kCFStringEncodingEUC_JP));
 
     CppCFString head = query.replaceClone(nbsp, space);
-    std::vector<CppCFString> result = userdict_->findCompletions(head);
+    std::vector<CppCFString> result = user_dic->findCompletions(head);
 
     D_PRINTF("REPLY: %s\n",
 	     join(' ',result).toCString(kCFStringEncodingEUC_JP));
Index: AquaSKK/SKKServer.h
diff -u AquaSKK/SKKServer.h:1.3.2.1 AquaSKK/SKKServer.h:1.3
--- AquaSKK/SKKServer.h:1.3.2.1	Wed Feb 15 00:12:44 2006
+++ AquaSKK/SKKServer.h	Wed Nov  9 00:02:24 2005
@@ -1,10 +1,11 @@
 /* -*- c++ -*-
-  $Id: SKKServer.h,v 1.3.2.1 2006/02/14 15:12:44 t-suwa Exp $
-
+  $Id: SKKServer.h,v 1.3 2005/11/08 15:02:24 t-suwa Exp $
+  ---------
+	
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002 phonohawk
-  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -25,52 +26,45 @@
 
 #include <string>
 #include <vector>
-#include <map>
 
 class CppCFString;
 class Dictionary;
 class UserDictionary;
+class SKKDictionary;
+class DMDictionary;
 
-class SKKServer {
-    // Ž«‘‚ÌŽí—Þ
-    enum DictionaryTypes {
-	SKKDictionaryType = 10,
-	SKKAutoUpdateDictionaryType = 11,
-	KotoeriDictionaryType = 20,
-	ProxyDictionaryType = 30,
-	GroupingDictionaryType = 90,
-    };
-
-    // Ž«‘‚̐ݒè
-    struct DictionaryPref {
-	bool active;
-	int type;
-	std::string location;
-    };
-    typedef std::vector<DictionaryPref> DictionaryPrefContainer;
-    typedef DictionaryPrefContainer::iterator DictionaryPrefIterator;
-    typedef std::map<std::string, Dictionary*> DictionaryContainer;
-    typedef DictionaryContainer::iterator DictionaryIterator;
-
-    // ƒ†[ƒU[Ž«‘
-    UserDictionary* userdict_;
-
-    DictionaryPrefContainer prefs_;
-    DictionaryContainer dicts_;
-
-    Dictionary* createDictionary(const DictionaryPref& pref) const;
-    std::string generateID(const DictionaryPref& pref) const;
-    void load();
+/*
+	‰Šú‰»‚ð•K—v‚Æ‚·‚éSingleton‚Å‚ ‚éB
+	Žg—p‚·‚é‘O‚ÉsharedServer(string,string,string)‚ðŒÄ‚΂Ȃ­‚Ä‚Í‚È‚ç‚È‚¢B
+	
+	c‚»‚Ì‚¤‚¿C³‚µ‚Ü‚·BŽ©—Í‚ÅŽ«‘‚̏ꏊ‚ðŽæ“¾‚·‚é‚Ì‚ª³‚µ‚¢‚ÆŽv‚¤c
+*/
 
-    SKKServer();
-    ~SKKServer();
+class SKKServer {
+    Dictionary* base_dic;
+    Dictionary* sub_dic;
+    UserDictionary* user_dic;
+    Dictionary* kotoeri_dic;
+    std::vector<Dictionary*> all_dics;
+
+    SKKServer(const std::string& base_dic_file,
+	      const std::string& sub_dic_file,
+	      const std::string& user_dic_file,
+	      const std::vector<CppCFString>& kotoeri_dic_file);
+    virtual ~SKKServer();
 
 public:
+    static SKKServer& sharedServer(const std::string& base_dic_file,
+				   const std::string& sub_dic_file,
+				   const std::string& user_dic_file,
+				   const std::vector<CppCFString>& kotoeri_dic_file);
     static SKKServer& sharedServer();
-
-    void reload();
     void terminate();
 
+    SKKDictionary& getBaseDic();
+    SKKDictionary& getSubDic();
+    DMDictionary& getKotoeriDic();
+
     CppCFString skkserv_style_search(const CppCFString& query); // NOT FOUND‚È‚ç‹ó‚Ì•¶Žš—ñ‚ð•Ô‚·B
     CppCFString search(const CppCFString& query); // NOT FOUND‚È‚ç‹ó‚Ì•¶Žš—ñ‚ð•Ô‚·B
     void registerToUserDic(const CppCFString& query);
Index: AquaSKK/ServerMessageReceiver.mm
diff -u AquaSKK/ServerMessageReceiver.mm:1.4.2.1 AquaSKK/ServerMessageReceiver.mm:1.4.2.2
--- AquaSKK/ServerMessageReceiver.mm:1.4.2.1	Sat Jan  7 16:22:29 2006
+++ AquaSKK/ServerMessageReceiver.mm	Sat Feb 18 02:20:38 2006
@@ -1,10 +1,10 @@
 /* -*- objc -*-
-  $Id: ServerMessageReceiver.mm,v 1.4.2.1 2006/01/07 07:22:29 t-suwa Exp $
+  $Id: ServerMessageReceiver.mm,v 1.4.2.2 2006/02/17 17:20:38 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
   Copyright (C) 2002 phonohawk
-  Copyright (C) 2005 Tomotaka SUWA <t.suw****@mac*****>
+  Copyright (C) 2005-2006 Tomotaka SUWA <t.suw****@mac*****>
 
   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
@@ -21,19 +21,17 @@
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#import <AppKit/AppKit.h>
-#import "CandidatesView.h"
-#import "CandidatesWindowController.h"
-#import "PreferencesController.h"
-#import "AboutBoxController.h"
-
-#include <Carbon/Carbon.h>
+#include <AppKit/AppKit.h>
+#include "CandidatesView.h"
+#include "CandidatesWindowController.h"
+#include "PreferencesController.h"
+#include "AboutBoxController.h"
 #include "BIMClientServer.h"
 #include "CppCFString.h"
 #include "CppCFData.h"
 #include "ServerMessageReceiver.h"
 #include "ClientConnectionFactory.h"
-#include "SKKServer.h"
+#include "DictionarySet.h"
 #include "CandidatesManager.h"
 
 static ServerMessageReceiver* _shared_instance = NULL;
@@ -131,7 +129,7 @@
 CppCFData ServerMessageReceiver::searchWord(const CppCFData& attachment) {
     // ’ljÁƒf[ƒ^‚Æ‚µ‚ÄUniChar‚Ì”z—ñ‚ðŽæ‚éB
     CppCFString query(attachment.getData());
-    CppCFString reply(SKKServer::sharedServer().search(query));
+    CppCFString reply(DictionarySet::theInstance().search(query));
 
     return CppCFData().own(reply.toCFData());
 }
@@ -193,13 +191,13 @@
 void ServerMessageReceiver::registerThisToUserDic(const CppCFData& attachment) {
     CppCFString query(attachment.getData());
 
-    SKKServer::sharedServer().registerToUserDic(query);
+    DictionarySet::theInstance().registerToUserDic(query);
 }
 
 void ServerMessageReceiver::removeThisFromUserDic(const CppCFData& attachment) {
     CppCFString query(attachment.getData());
 
-    SKKServer::sharedServer().removeFromUserDic(query);
+    DictionarySet::theInstance().removeFromUserDic(query);
 }
 
 void ServerMessageReceiver::showAboutBox() {
@@ -212,7 +210,7 @@
 
 CppCFData ServerMessageReceiver::fetchCompletions(const CppCFData& attachment) {
     CppCFString query(attachment.getData());
-    CppCFString reply(SKKServer::sharedServer().searchCompletions(query));
+    CppCFString reply(DictionarySet::theInstance().searchCompletions(query));
 
     return CppCFData().own(reply.toCFData());
 }
Index: AquaSKK/Skkserv.cpp
diff -u AquaSKK/Skkserv.cpp:1.2.2.1 AquaSKK/Skkserv.cpp:1.2
--- AquaSKK/Skkserv.cpp:1.2.2.1	Sat Jan 14 20:01:58 2006
+++ AquaSKK/Skkserv.cpp	Sat Oct  8 00:08:36 2005
@@ -1,74 +1,80 @@
 /*  -*- c++ -*-
-  $Id: Skkserv.cpp,v 1.2.2.1 2006/01/14 11:01:58 t-suwa Exp $
-
-  MacOS X implementation of the SKK input method.
-
-  Copyright (C) 2002-2004 phonohawk
-  Copyright (C) 2006 Tomotaka SUWA <t.suw****@mac*****>
+    $Id: Skkserv.cpp,v 1.2 2005/10/07 15:08:36 t-suwa Exp $
+	
+    MacOS X implementation of the SKK input method.
+    Copyright (C) 2002-2004 phonohawk
 
-  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
-  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    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
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include <Carbon/Carbon.h>
 #include <iostream>
 #include <string>
 #include <vector>
+#include <map>
 #include <pthread.h>
 #include <unistd.h>
 #include "CppCFString.h"
+#include "OkuriganaEntry.h"
+#include "Dictionary.h"
+#include "DMDictionary.h"
+#include "SKKDictionary.h"
 #include "SKKServer.h"
 #include "net/Socket.h"
 #include "Skkserv.h"
+//using namespace std;
 
 static Skkserv* _shared = NULL;
 static void listening_thread_begin(Skkserv* serv); // ”ñƒƒ“ƒo
-static void client_thread_begin(net::Socket* sock); // ”ñƒƒ“ƒo
+static void client_thread_begin(Socket* sock); // ”ñƒƒ“ƒo
 
 Skkserv& Skkserv::sharedServer() {
-    if(_shared == NULL) {
-	_shared = new Skkserv();
-    }
-    return *_shared;
+  if (_shared == NULL) {
+    _shared = new Skkserv();
+  }
+  return *_shared;
 }
 
-Skkserv::Skkserv() :listening(NULL), local_only(false), flag_term(false) {
-    // empty
+Skkserv::Skkserv()
+  :listening(NULL),local_only(false),flag_term(false) {
+
 }
 
 Skkserv::~Skkserv() {
-    stop();
+  stop();
 }
 
 Skkserv& Skkserv::setLocalOnly(bool b) {
-    local_only = b;
-    return *this;
+  local_only = b;
+  return *this;
 }
 
-Skkserv& Skkserv::start(int newPort) {
-    if(listening != NULL) {
+Skkserv& Skkserv::start(int port) {
+    if (listening != NULL) {
 	stop();
     }
 
     try {
-	listening = new net::Socket("", newPort, true, true, 20);
+	listening = new Socket("", port, true, true, 20);
 	
 	pthread_t pth;
-	pthread_create(&pth, NULL, (void*(*)(void*))listening_thread_begin, this);
+	pthread_create(&pth,NULL,(void*(*)(void*))listening_thread_begin,this);
 	pthread_detach(pth);
-    } catch(std::string& exception) {
-	std::cerr << exception << std::endl;
+    }
+    catch (string& exception) {
+	cerr << exception << endl;
 	
 	listening = NULL;
     }
@@ -77,17 +83,17 @@
 }
 
 Skkserv& Skkserv::stop() {
-    if(listening != NULL) {
-	flag_term = true;
-	while(flag_term) {
-	    usleep(300);
-	}
-    
-	delete listening;
-	listening = NULL;
+  if (listening != NULL) {
+    flag_term = true;
+    while (flag_term) {
+      usleep(300);
     }
+    
+    delete listening;
+    listening = NULL;
+  }
 
-    return *this;
+  return *this;
 }
 
 bool Skkserv::isRunning() const {
@@ -104,40 +110,39 @@
 }
 
 static void listening_thread_begin(Skkserv* obj) {
-    obj->listening_thread_routine();
+  obj->listening_thread_routine();
 }
 void Skkserv::listening_thread_routine() {
-    while(!flag_term) {
-	bool ready_to_accept;
-	listening->poll(&ready_to_accept, NULL, NULL, 500);
-	if(ready_to_accept) {
-	    net::Socket* client = listening->accept();
-
-	    if (local_only && client->getpeername() != "127.0.0.1") {
-		// Ú㔋‘”Û
-		delete client;
-	    }
-	    else {
-		pthread_t pth;
-		pthread_create(&pth,NULL,(void*(*)(void*))client_thread_begin,client);
-		pthread_detach(pth);
-	    }
-	}
+  while (!flag_term) {
+    bool ready_to_accept;
+    listening->poll(&ready_to_accept, NULL, NULL, 500);
+    if (ready_to_accept) {
+      Socket* client = listening->accept();
+
+      if (local_only && client->getpeername() != "127.0.0.1") {
+	// Ú㔋‘”Û
+	delete client;
+      }
+      else {
+	pthread_t pth;
+	pthread_create(&pth,NULL,(void*(*)(void*))client_thread_begin,client);
+	pthread_detach(pth);
+      }
     }
-    flag_term = false;
+  }
+  flag_term = false;
 }
 
-static void client_thread_begin(net::Socket* sock) {
-    SkkservSession session(sock);
-    session.run();
+static void client_thread_begin(Socket* sock) {
+  SkkservSession session(sock);
+  session.run();
 }
 
-SkkservSession::SkkservSession(net::Socket* src) : sock(src) {
-    // empty
-}
+SkkservSession::SkkservSession(Socket* sock)
+  : sock(sock) {}
 
 SkkservSession::~SkkservSession() {
-    delete sock;
+  delete sock;
 }
 
 void SkkservSession::run() {
@@ -154,8 +159,8 @@
 	}
 	else if (command == '1') {
 	    // žûõ
-	    std::string word = sock->readUntil(' ');
-	    CppCFString query(word.c_str(), kCFStringEncodingEUC_JP);
+	    string word = sock->readUntil(' ');
+	    CppCFString query(word.c_str(),kCFStringEncodingEUC_JP);
 	    CppCFString reply(SKKServer::sharedServer().skkserv_style_search(query.trim()));
 
 	    if (reply.length() == 0) {
Index: AquaSKK/Skkserv.h
diff -u AquaSKK/Skkserv.h:1.2.2.1 AquaSKK/Skkserv.h:1.2
--- AquaSKK/Skkserv.h:1.2.2.1	Sat Jan 14 20:01:58 2006
+++ AquaSKK/Skkserv.h	Sat Oct  8 00:08:36 2005
@@ -1,40 +1,38 @@
 /*  -*- c++ -*-
-  $Id: Skkserv.h,v 1.2.2.1 2006/01/14 11:01:58 t-suwa Exp $
-
-  MacOS X implementation of the SKK input method.
-
-  Copyright (C) 2002 phonohawk
-  Copyright (C) 2006 Tomotaka SUWA <t.suw****@mac*****>
-
-  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
-  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    $Id: Skkserv.h,v 1.2 2005/10/07 15:08:36 t-suwa Exp $
+    ---------
+	
+    MacOS X implementation of the SKK input method.
+    Copyright (C) 2002 phonohawk
+
+    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
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #pragma once
-
-namespace net {
-    class Socket;
-};
+using namespace net;
+using namespace std;
 
 class Skkserv {
-    net::Socket* listening;
+private:
+    Socket* listening;
     bool local_only;
     bool flag_term;
-
+    
     Skkserv();
     virtual ~Skkserv();
-
+    
 public:
     static Skkserv& sharedServer();
     virtual Skkserv& setLocalOnly(bool b);
@@ -42,14 +40,17 @@
     virtual Skkserv& stop();
     virtual bool isRunning() const;
     virtual int port() const;
+    
     virtual void listening_thread_routine();
 };
 
 class SkkservSession {
-    net::Socket* sock;
+private:
+    Socket* sock;
 
 public:
-    SkkservSession(net::Socket* sock);
+    SkkservSession(Socket* sock);
     virtual ~SkkservSession();
+    
     virtual void run();
 };
Index: AquaSKK/main.m
diff -u /dev/null AquaSKK/main.m:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/main.m	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,29 @@
+/*
+   $Id: main.m,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+   MacOS X implementation of the SKK input method.
+
+   Copyright (C) 2002 phonohawk
+   Copyright (C) 2006 Tomotaka SUWA <t.suw****@mac*****>
+
+   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
+   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#import <Cocoa/Cocoa.h>
+
+// AquaSKKServer ‚̃Gƒ“ƒgƒŠƒ|ƒCƒ“ƒg
+int main(int argc, const char* argv[]) {
+    return NSApplicationMain(argc, argv);
+}
Index: AquaSKK/socketstream.h
diff -u /dev/null AquaSKK/socketstream.h:1.1.2.1
--- /dev/null	Sat Feb 18 02:20:38 2006
+++ AquaSKK/socketstream.h	Sat Feb 18 02:20:38 2006
@@ -0,0 +1,470 @@
+/*  -*- c++ -*-
+  $Id: socketstream.h,v 1.1.2.1 2006/02/17 17:20:38 t-suwa Exp $
+
+  MacOS X implementation of the SKK input method.
+
+  Copyright (C) 2006 Tomotaka SUWA <t.suw****@mac*****>
+
+  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
+  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <iostream>
+#include <ios>
+#include <streambuf>
+#include <string>
+#include <vector>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+
+/*
+  
+このソースは、http://gimite.ddo.jp/gimite/cppmess.htm で提供されている
+socket14 を改変したものです。
+
+*/
+
+// IP アドレス
+class ip_address {
+    in_addr addr_;
+  
+    friend bool operator<(const ip_address& lhs, const ip_address& rhs) {
+        return (unsigned int)lhs < (unsigned int)rhs;
+    }
+    friend bool operator>(const ip_address& lhs, const ip_address& rhs) {
+	return rhs < lhs;
+    }
+    friend bool operator<=(const ip_address& lhs, const ip_address& rhs) {
+	return !(lhs > rhs);
+    }
+    friend bool operator>=(const ip_address& lhs, const ip_address& rhs) {
+	return !(lhs < rhs);
+    }
+    friend bool operator==(const ip_address& lhs, const ip_address& rhs) {
+	return lhs >= rhs && lhs <= rhs;
+    }
+    friend bool operator!=(const ip_address& lhs, const ip_address& rhs) {
+	return !(lhs == rhs);
+    }
+
+    friend std::ostream& operator<<(std::ostream& os, const ip_address& addr) {
+	return os << inet_ntoa(addr);
+    }
+
+public:
+    explicit ip_address(in_addr_t addr = INADDR_NONE) {
+        addr_.s_addr= addr;
+    }
+    explicit ip_address(const in_addr& addr) : addr_(addr) {
+        // empty
+    }
+    ip_address(const char* host) {
+        in_addr_t uaddr = inet_addr(host);
+        if(uaddr == INADDR_NONE) {
+            hostent* ent= gethostbyname(host);
+            if(ent && ent->h_addr_list[0]) {
+                uaddr= *(in_addr_t*)ent->h_addr_list[0];
+            }
+        }
+        addr_.s_addr= uaddr;
+    }
+    operator in_addr() const {
+        return addr_;
+    }
+    operator unsigned int() const {
+        return addr_.s_addr;
+    }
+    static ip_address getpeername(int fd) {
+        struct sockaddr_in sa;
+        socklen_t length = sizeof(sa);
+        ::getpeername(fd, (struct sockaddr*)&sa, &length);
+        return ip_address(sa.sin_addr);
+    }
+    static ip_address getsockname(int fd) {
+        struct sockaddr_in sa;
+        socklen_t length = sizeof(sa);
+        ::getsockname(fd, (struct sockaddr*)&sa, &length);
+        return ip_address(sa.sin_addr);
+    }
+};
+
+// ソケットアドレス
+struct socket_address {
+    ip_address ip;
+    unsigned short port;
+
+    explicit socket_address(const ip_address& i = ip_address(), unsigned short p = 0) : ip(i), port(p) {
+        // empty
+    }
+
+    friend bool operator<(const socket_address& lhs, const socket_address& rhs) {
+        if(lhs.ip < rhs.ip) return true;
+        if(lhs.ip > rhs.ip) return false;
+        return lhs.port < rhs.port;
+    }
+    friend bool operator>(const socket_address& lhs, const socket_address& rhs) {
+	return rhs < lhs;
+    }
+    friend bool operator<=(const socket_address& lhs, const socket_address& rhs) {
+	return !(lhs > rhs);
+    }
+    friend bool operator>=(const socket_address& lhs, const socket_address& rhs) {
+	return !(lhs < rhs);
+    }
+    friend bool operator==(const socket_address& lhs, const socket_address& rhs) {
+	return lhs >= rhs && lhs <= rhs;
+    }
+    friend bool operator!=(const socket_address& lhs, const socket_address& rhs) {
+	return !(lhs == rhs);
+    }
+  
+    friend std::ostream& operator<<(std::ostream& os, const socket_address& saddr) {
+        os << saddr.ip << ":" << saddr.port;
+        return os;
+    }
+};
+
+// ソケットストリームバッファ
+class socket_streambuf : public std::streambuf { 
+    typedef std::streambuf::int_type int_type;
+    typedef std::streambuf::traits_type traits_type;
+    int sock_;
+    char* egbuf_;
+
+    inline char* egbuf() {
+        return egbuf_;
+    }
+
+    inline void initialize(int bufsize = 4096) {
+	char* buf;
+	buf = new char[bufsize];
+	setg(buf, buf, buf);
+	egbuf_ = buf + bufsize;
+	buf = new char[bufsize];
+	setp(buf, buf + bufsize);
+    }
+
+protected:
+    virtual int_type overflow(int_type c = traits_type::eof()) {
+	if(traits_type::eq_int_type(traits_type::eof(), c))
+	    return traits_type::not_eof(c);
+
+	// バッファに溜まったデータを流す
+	if(sync() == -1)
+	    return traits_type::eof();
+
+        if(pptr() < epptr()) {
+	    *pptr() = traits_type::to_char_type(c);
+	    pbump(1);
+        } else {
+	    if(send((void*)&c, 1) != 1) {
+		return traits_type::eof();
+	    }
+	}
+        return c;
+    }
+
+    virtual int_type pbackfail(int_type c = traits_type::eof()) {
+	if(eback() < gptr()) {
+	    gbump(-1);
+	    if(!traits_type::eq_int_type(traits_type::eof(), c)) {
+		*gptr() = c;
+	    }
+	    return traits_type::not_eof(c);
+	}
+	return traits_type::eof();
+    }
+
+    virtual int_type underflow() {
+	if(gptr() < egptr()) {
+	    return traits_type::to_int_type(*gptr());
+	}
+
+	setg(eback(), eback(), eback());
+	int r = recv((void*)gptr(), egbuf() - gptr());
+	if(r == 0) {
+	    return traits_type::eof();
+	} else {
+	    if(r <0) {
+		r = 0;
+	    }
+	}
+	setg(eback(), gptr(), gptr() + r);
+	return *gptr();
+    }
+
+    virtual int sync() {
+	int len = pptr() - pbase();
+	if(send((void*)pbase(), len) != len) {
+	    return -1;
+	}
+
+	setp(pbase(), epptr());
+	return 0;
+    }
+
+public:
+    explicit socket_streambuf(int sock = -1) : sock_(sock) {
+	initialize();
+    }
+    socket_streambuf(const ip_address& host, unsigned short port) : sock_(-1) {
+	initialize();
+        open(host, port);
+    }
+    virtual ~socket_streambuf() {
+	close();
+    }
+
+    bool open(const ip_address& host, unsigned short port) {
+	if(is_open()) {
+	    close();
+	}
+	if(host == INADDR_NONE) {
+	    return false;
+	}
+	sock_ = ::socket(AF_INET, SOCK_STREAM, 0);
+	if(sock_ == -1) {
+	    return false;
+	}
+	sockaddr_in addr;
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	addr.sin_addr = host;
+	if(connect(sock_, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
+	    close();
+	}
+	return is_open();
+    }
+    void close() {
+	if(sock_ == -1) {
+	    return;
+	}
+	::close(sock_);
+	sock_= -1;
+    }
+    int release() {
+	int sock = sock_;
+	sock_ = -1;
+	return sock;
+    }
+    int send(const void* buffer, int size) {
+	return ::send(sock_, (const char*)buffer, size, 0);
+    }
+    int recv(void* buffer, int size) {
+	return ::recv(sock_, (char*)buffer, size, 0);
+    }
+    bool is_open() const {
+	return sock_ != -1;
+    }
+    int socket() const {
+	return sock_;
+    }
+};
+
+// ソケットストリーム
+class socket_stream: public std::iostream {
+    socket_streambuf sbuf_;
+
+public:
+    socket_stream() : std::iostream(0) {
+        rdbuf(&sbuf_);
+        setstate(std::ios::badbit);
+    }
+    socket_stream(const ip_address& host, unsigned short port) : std::iostream(0), sbuf_(host, port) {
+        rdbuf(&sbuf_);
+        if(!sbuf_.is_open()) {
+	    setstate(std::ios::badbit);
+	}
+    }
+    explicit socket_stream(int sock) : std::iostream(0), sbuf_(sock) {
+        rdbuf(&sbuf_);
+        if(!sbuf_.is_open()) {
+	    setstate(std::ios::badbit);
+	}
+    }
+    virtual ~socket_stream() {
+        rdbuf(0);
+    }
+    void open(const ip_address& host, unsigned short port) {
+        clear();
+        sbuf_.open(host, port);
+        if(!sbuf_.is_open()) {
+	    setstate(std::ios::badbit);
+	}
+    }
+    void close() {
+        setstate(std::ios::badbit);
+        sbuf_.close();
+    }
+    int release() {
+        setstate(std::ios::badbit);
+        return sbuf_.release();
+    }
+    int send(const void* buffer, int size) {
+        return sbuf_.send(buffer, size);
+    }
+    int recv(void* buffer, int size) {
+        return sbuf_.recv(buffer, size);
+    }
+    int socket() const {
+        return sbuf_.socket();
+    }
+};
+
+// バインドされたソケット
+class bound_socket {
+    const int type_;
+    int sock_;
+    bool is_bound_;
+
+protected:
+    bool bind(unsigned short port) {
+        if(is_bound_) {
+	    return false;
+	}
+        if(sock_ == -1) {
+            sock_ = ::socket(AF_INET, type_, 0);
+            if(sock_ == -1) {
+		return false;
+	    }
+        }
+        sockaddr_in addr;
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(port);
+        addr.sin_addr = ip_address(INADDR_ANY);
+        if(::bind(sock_, (const sockaddr*)&addr, sizeof(addr)) == -1) {
+            return false;
+        }
+        is_bound_ = true;
+        return true;
+    }
+
+public:
+    explicit bound_socket(int type) : type_(type), sock_(-1), is_bound_(false) {
+        // empty
+    }
+    bound_socket(int type, int sock, bool is_bound) : type_(type), sock_(sock), is_bound_(is_bound) {
+        // empty
+    }
+    virtual ~bound_socket() {
+        close();
+    }
+    void close() {
+        is_bound_ = false;
+        if(sock_ == -1) {
+	    return;
+	}
+        ::close(sock_);
+        sock_ = -1;
+    }
+    int release() {
+        int sock = sock_;
+        sock_ = -1;
+        is_bound_ = false;
+        return sock;
+    }
+    void socket(int sock, bool is_bound) {
+        close();
+        sock_ = sock;
+        is_bound_ = is_bound;
+    }
+    int socket() const {
+	return sock_;
+    }
+    operator const void*() const {
+	return is_bound_? this : NULL;
+    }
+    bool operator!() const {
+	return !is_bound_;
+    }
+};
+
+// サーバーソケット
+class server_stream_socket: public bound_socket {
+    bool ready(int timeout_ms) {
+	fd_set rfds;
+	timeval tv;
+	tv.tv_sec = 0;
+	tv.tv_usec = timeout_ms;
+
+	FD_ZERO(&rfds);
+	FD_SET(socket(), &rfds);
+
+	return (select(socket() + 1, &rfds, NULL, NULL, (timeout_ms == -1 ? NULL : &tv)) > 0);
+    }
+
+public:
+    server_stream_socket() : bound_socket(SOCK_STREAM) {
+        // empty
+    }
+    explicit server_stream_socket(int port, int backlog = 5) : bound_socket(SOCK_STREAM) {
+        bind(port, backlog);  
+    }
+    server_stream_socket(int sock, bool is_bound) : bound_socket(SOCK_STREAM, sock, is_bound) {
+        // empty
+    }
+    bool bind(int port, int backlog = 5) {
+        if(!bound_socket::bind(port)) {
+	    return false;
+	}
+        if(listen(socket(), backlog) != -1) {
+	    return true;
+	}
+        close();
+        return false;
+    }
+    int accept(int timeout_ms = 1000) {
+	if(ready(timeout_ms)) {
+	    return ::accept(socket(), 0, 0);
+	}
+	return -1;
+    }
+};
+
+// データグラムソケット
+class datagram_socket: public bound_socket {
+public:
+    datagram_socket() : bound_socket(SOCK_DGRAM) {
+        // empty
+    }
+    explicit datagram_socket(int port) : bound_socket(SOCK_DGRAM) {
+        bind(port);
+    }
+    datagram_socket(int sock, bool is_bound) : bound_socket(SOCK_DGRAM, sock, is_bound) {
+        // empty
+    }
+    bool bind(int port) {
+        return bound_socket::bind(port);
+    }
+    int recvfrom(void* buffer, int size, socket_address* addr = 0, int flags = 0) {
+        sockaddr_in saddr;
+        socklen_t addr_len = sizeof(saddr);
+        int result = ::recvfrom(socket(), (char*)buffer, size, flags, (sockaddr*)&saddr, &addr_len);
+        if(addr) {
+            addr->ip = ip_address(saddr.sin_addr);
+            addr->port = ntohs(saddr.sin_port);
+        }
+        return result;
+    }
+    int sendto(const void* buffer, int size, const socket_address* addr, int flags = 0) {
+        sockaddr_in saddr;
+        saddr.sin_family = AF_INET;
+        saddr.sin_addr = addr->ip;
+        saddr.sin_port = htons(addr->port);
+	return ::sendto(socket(), (const char*)buffer, size, flags, (const sockaddr*)&saddr, sizeof(saddr));
+    }
+};


aquaskk-changes メーリングリストの案内