OSDN Git Service

This commit was manufactured by cvs2svn to create branch 'Bb55'.
[gikonavigoeson/gikonavi.git] / GikoSystem.pas
1 unit GikoSystem;
2
3 interface
4
5 uses
6         Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
7         ComCtrls, {IniFiles,} ShellAPI, ActnList, Math,
8 {$IF Defined(DELPRO) }
9         SHDocVw,
10         MSHTML,
11 {$ELSE}
12         SHDocVw_TLB,
13         MSHTML_TLB,
14 {$IFEND}
15         {HttpApp,} URLMon, IdGlobal, IdURI, {Masks,}
16         Setting, BoardGroup, gzip, Dolib, bmRegExp, AbonUnit,
17         ExternalBoardManager, ExternalBoardPlugInMain,
18         GikoBayesian, GikoMessage;
19
20 type
21         TVerResourceKey = (
22                         vrComments,         //!< \83R\83\81\83\93\83g
23                         vrCompanyName,      //!< \89ï\8eÐ\96¼
24                         vrFileDescription,  //!< \90à\96¾
25                         vrFileVersion,      //!< \83t\83@\83C\83\8b\83o\81[\83W\83\87\83\93
26                         vrInternalName,     //!< \93à\95\94\96¼
27                         vrLegalCopyright,   //!< \92\98\8dì\8c 
28                         vrLegalTrademarks,  //!< \8f¤\95W
29                         vrOriginalFilename, //!< \90³\8e®\83t\83@\83C\83\8b\96¼
30                         vrPrivateBuild,     //!< \83v\83\89\83C\83x\81[\83g\83r\83\8b\83h\8fî\95ñ
31                         vrProductName,      //!< \90»\95i\96¼
32                         vrProductVersion,   //!< \90»\95i\83o\81[\83W\83\87\83\93
33                         vrSpecialBuild);    //!< \83X\83y\83V\83\83\83\8b\83r\83\8b\83h\8fî\95ñ
34
35         //! BBS\83^\83C\83v
36         TGikoBBSType = (gbt2ch);
37         //! \83\8d\83O\83^\83C\83v
38         TGikoLogType = (glt2chNew, glt2chOld);
39         //! \83\81\83b\83Z\81[\83W\83A\83C\83R\83\93
40         TGikoMessageIcon = (gmiOK, gmiSAD, gmiNG, gmiWhat, gmiNone);
41         //! URL\83I\81[\83v\83\93\83u\83\89\83E\83U\83^\83C\83v
42         TGikoBrowserType = (gbtIE, gbtUserApp, gbtAuto);
43
44
45         TStrTokSeparator = set of Char;
46         TStrTokRec = record
47                 Str: string;
48                 Pos: Integer;
49         end;
50
51         //! \83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b\83\8c\83R\81[\83h
52         TIndexRec = record
53                 FNo: Integer;
54                 FFileName: string;
55                 FTitle: string;
56                 FCount: Integer;
57                 FSize: Integer;
58 //              FRoundNo: Integer;
59                 FRoundDate: TDateTime;
60                 FLastModified: TDateTime;
61                 FKokomade: Integer;
62                 FNewReceive: Integer;
63                 FMishiyou: Boolean;     //!< \96¢\8eg\97p
64                 FUnRead: Boolean;
65                 FScrollTop: Integer;
66                 //Index Ver 1.01
67                 FAllResCount: Integer;
68                 FNewResCount: Integer;
69                 FAgeSage: TGikoAgeSage;
70         end;
71
72         //! \83T\83u\83W\83F\83N\83g\83\8c\83R\81[\83h
73         TSubjectRec = record
74                 FFileName: string;
75                 FTitle: string;
76                 FCount: Integer;
77         end;
78
79         //! \83\8c\83X\83\8c\83R\81[\83h\82Ö\82Ì\83|\83C\83\93\83^
80         PResRec = ^TResRec;
81
82         //! \83\8c\83X\83\8c\83R\81[\83h
83         TResRec = record
84                 FTitle: string;
85                 FMailTo: string;
86                 FName: string;
87                 FDateTime: string;
88                 FBody: string;
89                 FType: TGikoLogType;
90         end;
91
92         //! URLPath\83\8c\83R\81[\83h
93         TPathRec = record
94                 FBBS: string;                           //!< BBSID
95                 FKey: string;                           //!< ThreadID
96                 FSt: Int64;                               //!< \8aJ\8en\83\8c\83X\94Ô
97                 FTo: Int64;                               //!< \8fI\97¹\83\8c\83X\94Ô
98                 FFirst: Boolean;                //!< >>1\82Ì\95\\8e¦
99                 FStBegin: Boolean;      //!< 1\81`\95\\8e¦
100                 FToEnd: Boolean;                //!< \81`\8dÅ\8cã\82Ü\82Å\95\\8e¦
101                 FDone: Boolean;                 //!< \90¬\8c÷
102                 FNoParam: Boolean;  //!< \83\8c\83X\94Ô\83p\83\89\83\81\81[\83^\82È\82µ
103         end;
104
105         TGikoSys = class(TObject)
106         private
107                 { Private \90é\8c¾ }
108                 FSetting: TSetting;
109                 FDolib: TDolib;
110                 FAWKStr: TAWKStr;
111                 FResRange : Longint;
112                 FBayesian       : TGikoBayesian;        //!< \83x\83C\83W\83A\83\93\83t\83B\83\8b\83^
113                 FVersion : String;                    //!< \83t\83@\83C\83\8b\83o\81[\83W\83\87\83\93
114                 FGikoMessage: TGikoMessage;
115                 //! \82 \82é\83Z\83p\83\8c\81[\83^\82Å\8bæ\90Ø\82ç\82ê\82½\95\8e\9a\97ñ\82©\82ç\82\8e\94Ô\96Ú\82Ì\95\8e\9a\97ñ\82ð\8eæ\82è\8fo\82·
116                 function ChooseString(const Text, Separator: string; Index: integer): string;
117         //! \88ê\8e\9e\83t\83@\83C\83\8b\82©\82ç\82Ì\95\9c\8b\8c
118         procedure RestoreThreadData(Board: TBoard);
119         public
120                 { Public \90é\8c¾ }
121                 FAbon : TAbon;
122                 FSelectResFilter : TAbon;
123                 //FBoardURLList: TStringList;
124                 constructor Create;
125
126                 destructor Destroy; override;
127                 property ResRange : Longint read FResRange write FResRange;
128                 //! \83o\81[\83W\83\87\83\93\8fî\95ñ
129                 property Version : String read FVersion;
130 //              function MsgBox(Msg: string; Title: string; Flags: Longint): integer; overload;
131 //              function MsgBox(Handle: THandle; Msg: string; Title: string; Flags: Longint): integer; overload;
132                 function IsNumeric(s: string): boolean;
133                 function IsFloat(s: string): boolean;
134                 function DirectoryExistsEx(const Name: string): Boolean;
135                 function ForceDirectoriesEx(Dir: string): Boolean;
136 //              function GetVersion: string;
137
138                 function GetBoardFileName: string;
139                 function GetCustomBoardFileName: string;
140                 function GetHtmlTempFileName: string;
141                 function GetAppDir: string;
142                 function GetTempFolder: string;
143                 function GetSentFileName: string;
144                 function GetConfigDir: string;
145                 function GetSkinDir: string;
146                 function GetSkinHeaderFileName: string;
147                 function GetSkinFooterFileName: string;
148                 function GetSkinResFileName: string;
149                 function GetSkinNewResFileName: string;
150                 function GetSkinBookmarkFileName: string;
151                 function GetSkinNewmarkFileName: string;
152                 function GetStyleSheetDir: string;
153                 function GetOutBoxFileName: string;
154                 function GetUserAgent: string;
155                                 function GetSambaFileName : string;
156
157                 function GetMainKeyFileName : String;
158                 function GetEditorKeyFileName: String;
159                 function GetInputAssistFileName: String;
160                 procedure ReadSubjectFile(Board: TBoard);
161                 procedure CreateThreadDat(Board: TBoard);
162                 procedure WriteThreadDat(Board: TBoard);
163                 function ParseIndexLine(Line: string): TIndexRec;
164                 procedure GetFileList(Path: string; Mask: string; var List: TStringList; SubDir: Boolean; IsPathAdd: Boolean); overload;
165                 procedure GetFileList(Path: string; Mask: string; var List: TStringList; IsPathAdd: Boolean); overload;//\83T\83u\83t\83H\83\8b\83_\82Í\8c\9f\8dõ\82µ\82È\82¢
166                 procedure GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
167
168                 function DivideSubject(Line: string): TSubjectRec;
169                 property Setting: TSetting read FSetting write FSetting;
170                 property Dolib: TDolib read FDolib write FDolib;
171
172                 function UrlToID(url: string): string;
173                 function UrlToServer(url: string): string;
174
175                 function StrTokFirst(const s:string; const sep:TStrTokSeparator; var Rec:TStrTokRec):string;
176                 function StrTokNext(const sep:TStrTokSeparator; var Rec:TStrTokRec): string;
177
178                 function GetFileSize(FileName : string) : longint;
179                 function GetFileLineCount(FileName : string): longint;
180                 function IntToDateTime(val: Int64): TDateTime;
181                 function DateTimeToInt(ADate: TDateTime): Int64;
182
183                 function ReadThreadFile(FileName: string; Line: Integer): string;
184
185                 procedure MenuFont(Font: TFont);
186
187                 function RemoveToken(var s:string; const delimiter:string):string;
188                 function GetTokenIndex(s: string; delimiter: string; index: Integer): string;
189
190                 function GetShortName(const LongName: string; ALength: integer): string;
191                 function BoolToInt(b: Boolean): Integer;
192                 function IntToBool(i: Integer): Boolean;
193                 function GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
194                 procedure LoadKeySetting(ActionList: TActionList; FileName: String);
195                 procedure SaveKeySetting(ActionList: TActionList; FileName: String);
196                 procedure CreateProcess(const AppPath: string; const Param: string);
197                 procedure OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
198                 function HTMLDecode(const AStr: String): String;
199                 function GetHRefText(s: string): string;
200                 function Is2chHost(Host: string): Boolean;
201                 function Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
202                 function Parse2chURL2(URL: string): TPathRec;
203                 procedure ParseURI(const URL : string; var Protocol, Host, Path, Document, Port, Bookmark: string);
204                 function GetVersionBuild: Integer;
205                 function        GetBrowsableThreadURL( inURL : string ) : string;
206                 function        GetThreadURL2BoardURL( inURL : string ) : string;
207                 function        Get2chThreadURL2BoardURL( inURL : string ) : string;
208                 function        Get2chBrowsableThreadURL( inURL : string ) : string;
209                 function        Get2chBoard2ThreadURL( inBoard : TBoard; inKey : string ) : string;
210                 procedure ListBoardFile;
211                 procedure ReadBoardFile( bbs : TBBS );
212
213                 function        GetUnknownCategory : TCategory;
214                 function        GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
215
216                 procedure GetPopupResNumber(URL : string; var stRes, endRes : Int64);
217
218                 property Bayesian : TGikoBayesian read FBayesian write FBayesian;
219                 function GetSameIDResAnchor(const AID : string; ThreadItem: TThreadItem; limited: Integer):string; overload;
220                 function GetSameIDResAnchor(AIDNum : Integer; ThreadItem: TThreadItem; limited: Integer):string; overload;
221                 procedure GetSameIDRes(const AID : string; ThreadItem: TThreadItem;var body: TStringList); overload;
222                 procedure GetSameIDRes(AIDNum : Integer; ThreadItem: TThreadItem;var body: TStringList); overload;
223                 function GetSameIDResCount(const AID : string; ThreadItem: TThreadItem):Integer; overload;
224                 function GetSameIDResCount(AIDNum : Integer; ThreadItem: TThreadItem):Integer; overload;
225
226                 //! \92P\8cê\89ð\90Í
227                 procedure SpamCountWord( const text : string; wordCount : TWordCount );
228                 //! \8aw\8fK\83N\83\8a\83A
229                 procedure SpamForget( wordCount : TWordCount; isSpam : Boolean );
230                 //! \83X\83p\83\80\8aw\8fK
231                 procedure SpamLearn( wordCount : TWordCount; isSpam : Boolean );
232                 //! \83X\83p\83\80\93x\90\94
233                 function SpamParse( const text : string; wordCount : TWordCount ) : Extended;
234                 //\88ø\90\94\82ª\81A\93ú\95t\82Å\82à\8e\9e\8d\8f\82Å\82à\82È\82¢\82±\82Æ\82ð\92²\82×\82é
235                 function NotDateorTimeString(const AStr : string): boolean;
236
237                 //! \88ø\90\94\82É\91\97\82ç\82ê\82Ä\82«\82½\93ú\95t/ID\95\94\82ÉBE\82Ì\95\8e\9a\97ñ\82ª\82 \82Á\82½\82ç\81A\83v\83\8d\83t\83@\83C\83\8b\82Ö\82Ì\83\8a\83\93\83N\82ð\92Ç\89Á
238                 function AddBeProfileLink(AID : string; ANum: Integer): string;
239                 //! \83o\81[\83W\83\87\83\93\8fî\95ñ\82Ì\8eæ\93¾
240                 function GetVersionInfo(KeyWord: TVerResourceKey): string;
241                 //! Plugin\82Ì\8fî\95ñ\82Ì\8eæ\93¾
242                 function GetPluginsInfo(): string;
243                 //! IE\82Ì\83o\81[\83W\83\87\83\93\8fî\95ñ\82Ì\8eæ\93¾
244                 function GetIEVersion: string;
245                 function SetUserOptionalStyle(): string;
246                 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\90Ý\92è\82·\82é
247                 procedure SetGikoMessage;
248                 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\8eæ\93¾\82·\82é
249                 function GetGikoMessage(MesType: TGikoMessageListType): String;
250                 //! GMT\82Ì\8e\9e\8d\8f\82ðTDateTime\82É\95Ï\8a·\82·\82é
251                 function  DateStrToDateTime(const DateStr: string): TDateTime;
252         //! User32.dll\82ª\97\98\97p\82Å\82«\82é\82©
253         function CanUser32DLL: Boolean;
254         //! OE\88ø\97p\95\84\8eæ\93¾
255         function GetOEIndentChar : string;
256         //! \92u\8a·\90Ý\92è\83t\83@\83C\83\8b\8eæ\93¾
257         function GetReplaceFileName: String;
258         end;
259
260 var
261         GikoSys: TGikoSys;
262 const
263         //LENGTH_RESTITLE                       = 40;
264         ZERO_DATE: Integer      = 25569;
265         BETA_VERSION_NAME_E = 'beta';
266         BETA_VERSION_NAME_J = 'ÊÞÀ';
267         BETA_VERSION                            = 55;
268         BETA_VERSION_BUILD      = '';                           //!< debug\94Å\82È\82Ç
269         APP_NAME                                                = 'gikoNavi';
270         BE_PHP_URL = 'http://be.2ch.net/test/p.php?i=';
271
272
273 implementation
274
275 uses
276         Giko, RoundData, Favorite, Registry, HTMLCreate, MojuUtils, Sort, YofUtils,
277         IniFiles, DateUtils;
278
279 const
280         FOLDER_INDEX_VERSION                                    = '1.01';
281         USER_AGENT                                                                              = 'Monazilla';
282         DEFAULT_NGWORD_FILE_NAME : String = 'NGword.txt';
283         NGWORDs_DIR_NAME : String               = 'NGwords';
284
285         READ_PATH: string =                     '/test/read.cgi/';
286         OLD_READ_PATH: string =         '/test/read.cgi?';
287         KAKO_PATH: string =                     '/kako/';
288
289         KeyWordStr: array [TVerResourceKey] of String = (
290                   'Comments',
291                   'CompanyName',
292                   'FileDescription',
293                   'FileVersion',
294                   'InternalName',
295                   'LegalCopyright',
296                   'LegalTrademarks',
297                   'OriginalFilename',
298                   'PrivateBuild',
299                   'ProductName',
300                   'ProductVersion',
301                   'SpecialBuild');
302
303 // *************************************************************************
304 //! GikoSys\83R\83\93\83X\83g\83\89\83N\83^
305 // *************************************************************************
306 constructor TGikoSys.Create;
307 begin
308         FSetting := TSetting.Create;
309         FDolib := TDolib.Create;
310         FAWKStr := TAWKStr.Create(nil);
311         if DirectoryExists(GetConfigDir) = false then begin
312                 CreateDir(GetConfigDir);
313         end;
314         FAbon := TAbon.Create;
315     FAbon.IgnoreKana := FSetting.IgnoreKana;
316         FAbon.Setroot(GetConfigDir+NGWORDs_DIR_NAME);
317         FAbon.GoHome;
318         FAbon.ReturnNGwordLineNum := FSetting.ShowNGLinesNum;
319         FAbon.SetNGResAnchor := FSetting.AddResAnchor;
320         FAbon.DeleteSyria := FSetting.DeleteSyria;
321         FAbon.Deleterlo := FSetting.AbonDeleterlo;
322         FAbon.Replaceul := FSetting.AbonReplaceul;
323         FAbon.AbonPopupRes := FSetting.PopUpAbon;
324
325         FSelectResFilter := TAbon.Create;
326     FSelectResFilter.IgnoreKana := True;
327         // \8di\82è\8d\9e\82Þ\82Æ\82«\82Í\8bÉ\97Í\88ê\97\97\82ª\8c©\82ç\82ê\82é\82Ù\82¤\82ª\82¢\82¢\82Ì\82Å\91¼\82Í\8a®\91S\82É\8dí\8f\9c
328         FSelectResFilter.AbonString := '';
329     //
330         ResRange := FSetting.ResRange;
331         FVersion := Trim(GetVersionInfo(vrFileVersion));
332         FBayesian := TGikoBayesian.Create;
333         //FBoardURLList := TStringList.Create;
334         //\83\81\83b\83Z\81[\83W\82Ì\8dì\90¬
335         FGikoMessage := TGikoMessage.Create;
336 end;
337
338 // *************************************************************************
339 //! GikoSys\83f\83X\83g\83\89\83N\83^
340 // *************************************************************************
341 destructor TGikoSys.Destroy;
342 var
343         i: Integer;
344         FileList: TStringList;
345 begin
346         //\83X\83\8c\83b\83h\83f\81[\83^\83t\83@\83C\83\8b\82ð\8dX\90V
347 //      FlashExitWrite;
348
349 //      FExitWrite.Free;
350         FBayesian.Free;
351         FAWKStr.Free;
352         FSetting.Free;
353         FDolib.Free;
354         FAbon.Free;
355         FSelectResFilter.Free;
356     FGikoMessage.Free;
357         //FBoardURLList.Free;
358         //\83e\83\93\83|\83\89\83\8aHTML\82ð\8dí\8f\9c
359         FileList := TStringList.Create;
360         try
361         FileList.BeginUpdate;
362                 GetFileList(GetTempFolder, '*.html', FileList, False, True);
363         FileList.EndUpdate;
364                 for i := 0 to FileList.Count - 1 do begin
365                         DeleteFile(FileList[i]);
366                 end;
367         finally
368                 FileList.Free;
369         end;
370         inherited;
371 end;
372
373 {!
374 \brief \95\8e\9a\97ñ\90\94\8e\9a\83`\83F\83b\83N
375 \param s \83`\83F\83b\83N\82·\82é\95\8e\9a\97ñ
376 \return s \82ª\95\84\8d\86\95t\82«\90®\90\94\82Æ\82µ\82Ä\94F\8e¯\89Â\94\\82È\82ç True
377 }
378 {$HINTS OFF}
379 function TGikoSys.IsNumeric(s: string): boolean;
380 var
381         e: integer;
382         v: integer;
383 begin
384         Val(s, v, e);
385         Result := e = 0;
386 end;
387 {$HINTS ON}
388
389 {!
390 \brief \95\8e\9a\97ñ\95\82\93®\8f¬\90\94\93_\90\94\8e\9a\83`\83F\83b\83N
391 \param s \83`\83F\83b\83N\82·\82é\95\8e\9a\97ñ
392 \return s \82ª\95\84\8d\86\95t\82«\95\82\93®\8f¬\90\94\82Æ\82µ\82Ä\94F\8e¯\89Â\94\\82È\82ç True
393 }
394 function TGikoSys.IsFloat(s: string): boolean;
395 var
396         v: Extended;
397 begin
398         Result := TextToFloat(PChar(s), v, fvExtended);
399 end;
400
401 // *************************************************************************
402 //! \83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
403 // *************************************************************************
404 function TGikoSys.GetBoardFileName: string;
405 begin
406         Result := Setting.GetBoardFileName;
407 end;
408
409 // *************************************************************************
410 //! \83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
411 // *************************************************************************
412 function TGikoSys.GetCustomBoardFileName: string;
413 begin
414         Result := Setting.GetCustomBoardFileName;
415 end;
416
417 // *************************************************************************
418 //! \83e\83\93\83|\83\89\83\8a\83t\83H\83\8b\83_\81[\96¼\8eæ\93¾
419 // *************************************************************************
420 function TGikoSys.GetHtmlTempFileName: string;
421 begin
422         Result := Setting.GetHtmlTempFileName;
423 end;
424
425
426 // *************************************************************************
427 //! \8eÀ\8ds\83t\83@\83C\83\8b\83t\83H\83\8b\83_\8eæ\93¾
428 // *************************************************************************
429 function TGikoSys.GetAppDir: string;
430 begin
431         Result := Setting.GetAppDir;
432 end;
433
434 // *************************************************************************
435 //! TempHtml\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
436 // *************************************************************************
437 function TGikoSys.GetTempFolder: string;
438 begin
439         Result := Setting.GetTempFolder;
440 end;
441
442 // *************************************************************************
443 //! sent.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
444 // *************************************************************************)
445 function TGikoSys.GetSentFileName: string;
446 begin
447         Result := Setting.GetSentFileName;
448 end;
449
450 // *************************************************************************
451 //! outbox.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
452 // *************************************************************************
453 function TGikoSys.GetOutBoxFileName: string;
454 begin
455         Result := Setting.GetOutBoxFileName;
456 end;
457
458 // *************************************************************************
459 //! Config\83t\83H\83\8b\83_\8eæ\93¾
460 // *************************************************************************
461 function TGikoSys.GetConfigDir: string;
462 begin
463         Result := Setting.GetConfigDir;
464 end;
465
466 //! \83X\83^\83C\83\8b\83V\81[\83g\83t\83H\83\8b\83_
467 function TGikoSys.GetStyleSheetDir: string;
468 begin
469         Result := Setting.GetStyleSheetDir;
470 end;
471
472 //! \83X\83L\83\93\83t\83H\83\8b\83_
473 function TGikoSys.GetSkinDir: string;
474 begin
475         Result := Setting.GetSkinDir;
476 end;
477
478 //! Skin:\83w\83b\83_\82Ì\83t\83@\83C\83\8b\96¼
479 function TGikoSys.GetSkinHeaderFileName: string;
480 begin
481         Result := Setting.GetSkinHeaderFileName;
482 end;
483
484 //! Skin:\83t\83b\83^\82Ì\83t\83@\83C\83\8b\96¼
485 function TGikoSys.GetSkinFooterFileName: string;
486 begin
487         Result := Setting.GetSkinFooterFileName;
488 end;
489
490 //! Skin:\90V\92\85\83\8c\83X\82Ì\83t\83@\83C\83\8b\96¼
491 function TGikoSys.GetSkinNewResFileName: string;
492 begin
493         Result := Setting.GetSkinNewResFileName;
494 end;
495
496 //! Skin:\94ñ\90V\92\85\83\8c\83X\82Ì\83t\83@\83C\83\8b\96¼
497 function TGikoSys.GetSkinResFileName: string;
498 begin
499         Result := Setting.GetSkinResFileName;
500 end;
501
502 //! Skin:\82µ\82¨\82è(\82±\82±\82Ü\82Å\93Ç\82ñ\82¾)\82Ì\83t\83@\83C\83\8b\96¼
503 function TGikoSys.GetSkinBookmarkFileName: string;
504 begin
505         Result := Setting.GetSkinBookmarkFileName;
506 end;
507
508 //! Skin:\82µ\82¨\82è(\90V\92\85\83\8c\83X)\82Ì\83t\83@\83C\83\8b\96¼
509 function TGikoSys.GetSkinNewmarkFileName: string;
510 begin
511         Result := Setting.GetSkinNewmarkFileName;
512 end;
513
514 //! UserAgent\8eæ\93¾
515 function TGikoSys.GetUserAgent: string;
516 begin
517         if Dolib.Connected then begin
518                 Result := Format('%s %s/%s%d/%s', [
519                                                                 Dolib.UserAgent,
520                                                                 APP_NAME,
521                                                                 BETA_VERSION_NAME_E,
522                                                                 BETA_VERSION,
523                                                                 Version]);
524         end else begin
525                 Result := Format('%s/%s %s/%s%d/%s', [
526                                                                 USER_AGENT,
527                                                                 Dolib.Version,
528                                                                 APP_NAME,
529                                                                 BETA_VERSION_NAME_E,
530                                                                 BETA_VERSION,
531                                                                 Version]);
532         end;
533 end;
534
535 {!
536 \brief \8co\89ß\95b\82ð TDateTime \82É\95Ï\8a·
537 \param val 1970/1/1/ 00:00:00 \82©\82ç\82Ì\8co\89ß\95b
538 \return val \82ð\8e¦\82· TDateTime
539 }
540 function TGikoSys.IntToDateTime(val: Int64): TDateTime;
541 begin
542         Result := ZERO_DATE + val / 86400.0;
543 end;
544
545 {!
546 \brief TDateTime \82ð\8co\89ß\95b\82É\95Ï\8a·
547 \param ADate \95Ï\8a·\82·\82é\8e\9e\8d\8f
548 \return 1970/1/1/ 00:00:00 \82©\82ç\82Ì\8co\89ß\95b
549 }
550 function TGikoSys.DateTimeToInt(ADate: TDateTime): Int64;
551 begin
552         Result := Trunc((ADate - ZERO_DATE) * 86400);
553 end;
554
555
556 {!
557 \brief Subject\83t\83@\83C\83\8bRead
558 \param Board \83X\83\8c\88ê\97\97\82ð\8eæ\93¾\82·\82é\94Â
559 }
560 procedure TGikoSys.ReadSubjectFile(Board: TBoard);
561 var
562         ThreadItem: TThreadItem;
563         FileName: string;
564         FileList: TStringList;
565         Index: Integer;
566         sl: TStringList;
567         i: Integer;
568         Rec: TIndexRec;
569         UnRead: Integer;
570         ResRec: TResRec;
571         usePlugIn : Boolean;
572         BoardPath : String;
573         server : String;
574         islog : Boolean;
575     urlHead: String;
576         {*
577         FavoThreadItem : TFavoriteThreadItem;
578         Node: TTreeNode;
579         *}
580 begin
581         if Board.IsThreadDatRead then
582                 Exit;
583         Board.Clear;
584         UnRead := 0;
585         usePlugIn := Board.IsBoardPlugInAvailable;
586         server :=  UrlToServer( Board.URL );
587
588         FileName := Board.GetFolderIndexFileName;
589
590         FileList := TStringList.Create;
591         FileList.Sorted := True;
592         FileList.BeginUpdate;
593         //IsLogFile\97pDAT\83t\83@\83C\83\8b\83\8a\83X\83g
594         GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.dat', FileList, False);
595         FileList.EndUpdate;
596
597         // \8fd\95¡\82ð\96h\82®
598         Board.BeginUpdate;
599         Board.Sorted := True;
600
601         sl := TStringList.Create;
602         try
603                 if FileExists(FileName) then begin
604                         sl.LoadFromFile(FileName);
605             // \83X\83\8c\83b\83h\82Å\8b¤\92Ê\82ÌURL\95\94
606             if Board.is2ch then begin
607                 urlHead := server + 'test/read.cgi/' + Board.BBSID + '/';
608             end else begin
609                 urlHead := server + 'test/read.cgi?bbs=' + Board.BBSID + '&key=';
610             end;
611                         //\82Q\8ds\96Ú\82©\82ç\81i\82P\8ds\96Ú\82Í\83o\81[\83W\83\87\83\93\81j
612                         for i := sl.Count - 1 downto 1 do begin
613                                 Rec := ParseIndexLine(sl[i]);
614                                 islog := FileList.Find( Rec.FFileName, Index );
615                                 if usePlugIn then
616                                         ThreadItem := TThreadItem.Create(
617                                                         Board.BoardPlugIn,
618                                                         Board,
619                                                         Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), Rec.FFileName ) )
620                                 else begin
621                                         if Board.is2ch then begin
622                                                 ThreadItem := TThreadItem.Create(
623                                                         nil,
624                                                         Board,
625                                                         urlHead + ChangeFileExt( Rec.FFileName, '' ) + '/l50',
626                                                         islog,
627                                                         Rec.FFileName
628                                                         );
629                                         end else begin
630                                                 ThreadItem := TThreadItem.Create(
631                                                         nil,
632                                                         Board,
633                                                         urlHead + ChangeFileExt( Rec.FFileName, '' ) + '&ls=50',
634                                                         islog,
635                                                         Rec.FFileName
636                                                         );
637                                         end;
638                                 end;
639
640                                 ThreadItem.BeginUpdate;
641                                 if islog then
642                                         FileList.Delete( Index );
643
644                                 ThreadItem.No := Rec.FNo;
645                                 ThreadItem.FileName := Rec.FFileName;
646                                 ThreadItem.Title := MojuUtils.UnSanitize(Rec.FTitle);
647                                 ThreadItem.Count := Rec.FCount;
648                                 ThreadItem.Size := Rec.FSize;
649                                 ThreadItem.RoundDate := Rec.FRoundDate;
650                                 ThreadItem.LastModified := Rec.FLastModified;
651                                 ThreadItem.Kokomade := Rec.FKokomade;
652                                 ThreadItem.NewReceive := Rec.FNewReceive;
653                                 ThreadItem.UnRead := Rec.FUnRead;
654                                 ThreadItem.ScrollTop := Rec.FScrollTop;
655                                 ThreadItem.AllResCount := Rec.FAllResCount;
656                                 ThreadItem.NewResCount := Rec.FNewResCount;
657                                 ThreadItem.AgeSage := Rec.FAgeSage;
658                                 ThreadItem.ParentBoard := Board;
659                                 {* \82¨\8bC\82É\93ü\82è\91å\97Ê\90\90¬\83R\81[\83h *}
660                                 {*
661                                 FavoThreadItem := TFavoriteThreadItem.Create( ThreadItem.URL, ThreadItem.Title, ThreadItem );
662                                 Node := FavoriteDM.TreeView.Items.AddChildObject( FavoriteDM.TreeView.Items.Item[0], ThreadItem.Title, FavoThreadItem);
663                                 *}
664
665                                 ThreadItem.EndUpdate;
666                                 Board.Add(ThreadItem);
667
668                                 if (ThreadItem.IsLogFile) and (ThreadItem.UnRead) then
669                                         Inc(UnRead);
670                         end;
671                 end;
672
673                 if UnRead <> Board.UnRead then
674                         Board.UnRead := UnRead;
675
676                 Boardpath := ExtractFilePath(Board.GetFolderIndexFileName);
677                 //\83C\83\93\83f\83b\83N\83X\82É\96³\82©\82Á\82½\83\8d\83O\82ð\92Ç\89Á\81i\95\85\82ê\83C\83\93\83f\83b\83N\83X\91Î\89\9e\81j
678                 for i := 0 to FileList.Count - 1 do begin
679                         FileName := Boardpath + FileList[i];
680
681                         //ResRec := DivideStrLine(ReadThreadFile(FileName, 1));
682                         if usePlugIn then begin
683                                 ThreadItem := TThreadItem.Create(
684                                         Board.BoardPlugIn,
685                                         Board,
686                                         Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), FileList[i] ) );
687                                 THTMLCreate.DivideStrLine(Board.BoardPlugIn.GetDat( DWORD( ThreadItem ), 1 ), @ResRec);
688                         end else begin
689                                 ThreadItem := TThreadItem.Create(
690                                         nil,
691                                         Board,
692                                         Get2chBoard2ThreadURL( Board, ChangeFileExt( FileList[i], '' ) ) );
693                                 THTMLCreate.DivideStrLine(ReadThreadFile(FileName, 1), @ResRec);
694                         end;
695
696                         ThreadItem.BeginUpdate;
697                         ThreadItem.FileName := FileList[i];
698                         //ThreadItem.FilePath := FileName;
699                         ThreadItem.No := Board.Count + 1;
700                         ThreadItem.Title := ResRec.FTitle;
701                         ThreadItem.Count := GetFileLineCount(FileName);
702                         ThreadItem.AllResCount := ThreadItem.Count;
703                         ThreadItem.NewResCount := ThreadItem.Count;
704                         ThreadItem.Size := GetFileSize(FileName) - ThreadItem.Count;//1byte\82¸\82ê\82é\82Æ\82«\82ª\82 \82é\82¯\82Ç\82»\82ê\82Í\82 \82«\82ç\82ß\82é
705                         ThreadItem.RoundDate := FileDateToDateTime( FileAge( FileName ) );
706                         ThreadItem.LastModified := ThreadItem.RoundDate;
707                         ThreadItem.Kokomade := -1;
708                         ThreadItem.NewReceive := 0;
709                         ThreadItem.ParentBoard := Board;
710                         ThreadItem.IsLogFile := True;
711                         ThreadItem.Round := False;
712                         ThreadItem.UnRead := False;
713                         ThreadItem.ScrollTop := 0;
714                         ThreadItem.AgeSage := gasNone;
715                         ThreadItem.EndUpdate;
716                         Board.Add(ThreadItem);
717                 end;
718                 Board.EndUpdate;
719         
720         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\83`\83F\83b\83N
721         RestoreThreadData( Board );
722         finally
723                 sl.Free;
724                 FileList.Free;
725                 Board.Sorted := False;
726         end;
727         Board.IsThreadDatRead := True;
728 end;
729
730 {!
731 \brief \83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b(Folder.idx)\8dì\90¬
732 \param Board Folder.idx \82ð\8dì\90¬\82·\82é\94Â
733 }
734 procedure TGikoSys.CreateThreadDat(Board: TBoard);
735 var
736         i: integer;
737         s: string;
738         SubjectList: TStringList;
739         sl: TStringList;
740         Rec: TSubjectRec;
741         FileName: string;
742         cnt: Integer;
743 begin
744         if not FileExists(Board.GetSubjectFileName) then Exit;
745         FileName := Board.GetFolderIndexFileName;
746
747         SubjectList := TStringList.Create;
748         try
749                 SubjectList.LoadFromFile(Board.GetSubjectFileName);
750                 sl := TStringList.Create;
751                 try
752                         cnt := 1;
753                         sl.BeginUpdate;
754                         sl.Add(FOLDER_INDEX_VERSION);
755                         for i := 0 to SubjectList.Count - 1 do begin
756                                 Rec := DivideSubject(SubjectList[i]);
757
758                                 if (Trim(Rec.FFileName) = '') or (Trim(Rec.FTitle) = '') then
759                                         Continue;
760
761                                 {s := Format('%x', [cnt]) + #1                                  //\94Ô\8d\86
762                                          + Rec.FFileName + #1                                                           //\83t\83@\83C\83\8b\96¼
763                                          + Rec.FTitle + #1                                                                      //\83^\83C\83g\83\8b
764                                          + Format('%x', [Rec.FCount]) + #1      //\83J\83E\83\93\83g
765                                          + Format('%x', [0]) + #1                                               //size
766                                          + Format('%x', [0]) + #1                                               //RoundDate
767                                          + Format('%x', [0]) + #1                                               //LastModified
768                                          + Format('%x', [0]) + #1                                               //Kokomade
769                                          + Format('%x', [0]) + #1                                               //NewReceive
770                                          + '0' + #1                                                                                             //\96¢\8eg\97p
771                                          + Format('%x', [0]) + #1                                               //UnRead
772                                          + Format('%x', [0]) + #1                                               //ScrollTop
773                                          + Format('%x', [Rec.FCount]) + #1      //AllResCount
774                                          + Format('%x', [0]) + #1                                               //NewResCount
775                                          + Format('%x', [0]);                                                           //AgeSage
776                                 }
777                                 s := Format('%x'#1'%s'#1'%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1 + 
778                                                         '%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x',
779                                         [cnt,                   //\94Ô\8d\86
780                                          Rec.FFileName, //\83t\83@\83C\83\8b\96¼
781                                          MojuUtils.Sanitize(Rec.FTitle),    //\83^\83C\83g\83\8b
782                                          Rec.FCount,     //\83J\83E\83\93\83g
783                                          0,             //size
784                                          0,             //RoundDate
785                                          0,                             //LastModified
786                                          0,                             //Kokomade
787                                          0,                             //NewReceive
788                                          '0',                   //\96¢\8eg\97p
789                                          0,                             //UnRead
790                                          0,                             //ScrollTop
791                                          Rec.FCount,    //AllResCount
792                                          0,                             //NewResCount
793                                          0]             //AgeSage
794                                         );
795
796                                 sl.Add(s);
797                                 inc(cnt);
798                         end;
799                         sl.EndUpdate;
800                         sl.SaveToFile(FileName);
801                 finally
802                         sl.Free;
803                 end;
804         finally
805                 SubjectList.Free;
806         end;
807 end;
808
809 {!
810 \brief \83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X(Thread.dat)\8f\91\82«\8d\9e\82Ý
811 \param Thread.dat \82ð\8dì\90¬\82·\82é\94Â
812 }
813 procedure TGikoSys.WriteThreadDat(Board: TBoard);
814 //const
815 //      Values: array[Boolean] of string = ('0', '1');
816 var
817         i: integer;
818         FileName: string;
819         sl: TStringList;
820         s: string;
821         TmpFileList: TStringList;
822 begin
823         if not Board.IsThreadDatRead then
824                 Exit;
825         FileName := Board.GetFolderIndexFileName;
826         ForceDirectoriesEx( ExtractFilePath( FileName ) );
827
828         sl := TStringList.Create;
829         TmpFileList := TStringList.Create;
830         TmpFileList.Sorted := true;
831         try
832         TmpFileList.BeginUpdate;
833                 GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.tmp', TmpFileList, false);
834         TmpFileList.EndUpdate;
835                 sl.BeginUpdate;
836                 sl.Add(FOLDER_INDEX_VERSION);
837
838                 // \83X\83\8c\94Ô\8d\86\95Û\91\82Ì\82½\82ß\83\\81[\83g
839                 Sort.SetSortNoFlag(true);
840                 Sort.SetSortOrder(true);
841                 Sort.SetSortIndex(0);
842                 //Sort.SortNonAcquiredCountFlag := GikoSys.Setting.NonAcquiredCount;
843                 Board.CustomSort(ThreadItemSortProc);
844
845                 for i := 0 to Board.Count - 1 do begin
846                         Board.Items[i].No := i + 1;
847                         s := Format('%x'#1'%s'#1'%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x'#1 +
848                                                         '%s'#1'%x'#1'%x'#1'%x'#1'%x'#1'%x',
849                                         [Board.Items[i].No,                     //\94Ô\8d\86
850                                          Board.Items[i].FileName, //\83t\83@\83C\83\8b\96¼
851                      MojuUtils.Sanitize(Board.Items[i].Title),    //\83^\83C\83g\83\8b
852                                          Board.Items[i].Count,     //\83J\83E\83\93\83g
853                                          Board.Items[i].Size,             //size
854                                          DateTimeToInt(Board.Items[i].RoundDate),             //RoundDate
855                                          DateTimeToInt(Board.Items[i].LastModified),                            //LastModified
856                                          Board.Items[i].Kokomade,                               //Kokomade
857                                          Board.Items[i].NewReceive,                             //NewReceive
858                                          '0',                   //\96¢\8eg\97p
859                                          BoolToInt(Board.Items[i].UnRead),                              //UnRead
860                                          Board.Items[i].ScrollTop,                              //ScrollTop
861                                          Board.Items[i].AllResCount,    //AllResCount
862                                          Board.Items[i].NewResCount,                            //NewResCount
863                                          Ord(Board.Items[i].AgeSage)]             //AgeSage
864                                         );
865
866                         sl.Add(s);
867                 end;
868                 sl.EndUpdate;
869                 sl.SaveToFile(FileName);
870
871                 for i := 0 to TmpFileList.Count - 1 do begin
872                         DeleteFile(ExtractFilePath(Board.GetFolderIndexFileName) + TmpFileList[i]);
873                 end;
874
875         finally
876                 TmpFileList.Free;
877                 sl.Free;
878         end;
879 end;
880
881 {!
882 \brief Folder.idx \82ð 1 \8ds\89ð\8eß
883 \param Line Folder.idx \82ð\8d\\90¬\82·\82é 1 \8ds
884 \return \83X\83\8c\83b\83h\8fî\95ñ
885 }
886 function TGikoSys.ParseIndexLine(Line: string): TIndexRec;
887 begin
888         Result.FNo := StrToIntDef('$' + RemoveToken(Line, #1), 0);
889         Result.FFileName := RemoveToken(Line, #1);
890         Result.FTitle := MojuUtils.UnSanitize(RemoveToken(Line, #1));
891         Result.FCount := StrToIntDef('$' + RemoveToken(Line, #1), 0);
892         Result.FSize := StrToIntDef('$' + RemoveToken(Line, #1), 0);
893         Result.FRoundDate := IntToDateTime(StrToIntDef('$' + RemoveToken(Line, #1), ZERO_DATE));
894         Result.FLastModified := IntToDateTime(StrToIntDef('$' + RemoveToken(Line, #1), ZERO_DATE));
895         Result.FKokomade := StrToIntDef('$' + RemoveToken(Line, #1), -1);
896         Result.FNewReceive := StrToIntDef('$' + RemoveToken(Line, #1), 0);
897         RemoveToken(Line, #1);//9: ;    //\96¢\8eg\97p
898         Result.FUnRead := IntToBool(StrToIntDef('$' + RemoveToken(Line, #1), 0));
899         Result.FScrollTop := StrToIntDef('$' + RemoveToken(Line, #1), 0);
900         Result.FAllResCount := StrToIntDef('$' + RemoveToken(Line, #1), 0);
901         Result.FNewResCount := StrToIntDef('$' + RemoveToken(Line, #1), 0);
902         Result.FAgeSage := TGikoAgeSage(StrToIntDef('$' + RemoveToken(Line, #1), 0));
903
904 end;
905
906 {!
907 \brief \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\8ew\92è\83t\83@\83C\83\8b\88ê\97\97\82ð\8eæ\93¾\82·\82é
908 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
909 \param Mask      \83t\83@\83C\83\8b\96¼\82Ì\83}\83X\83N
910 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83@\83C\83\8b\96¼\88ê\97\97\82ª\95Ô\82é
911 \param SubDir    \92\86\82Ì\83t\83H\83\8b\83_\82Ü\82Å\8dÄ\8bA\93I\82É\83\8a\83X\83g\82·\82é\8fê\8d\87\82Í True
912 \param IsPathAdd \83p\83X\95t\82«\82Å\83\8a\83X\83g\83A\83b\83v\82·\82é\8fê\8d\87\82Í True
913
914 Mask \82ð '*.txt' \82Ì\82æ\82¤\82É\8ew\92è\82·\82é\82±\82Æ\82Å\81A
915 \93Á\92è\82Ì\83t\83@\83C\83\8b\96¼\82â\93Á\92è\82Ì\8ag\92£\8eq\82É\8di\82Á\82½\83\8a\83X\83g\83A\83b\83v\82ª\89Â\94\\82Å\82·\81B
916
917 \par \97á:
918 \code
919 GetFileList('c:\', '*.txt', list, True, True);
920 \endcode
921 }
922 procedure TGikoSys.GetFileList(Path: string; Mask: string; var List: TStringList; SubDir: Boolean; IsPathAdd: Boolean);
923 var
924         rc: Integer;
925         SearchRec : TSearchRec;
926         s: string;
927 begin
928         Path := IncludeTrailingPathDelimiter(Path);
929         rc := FindFirst(Path + '*.*', faAnyfile, SearchRec);
930         try
931                 while rc = 0 do begin
932                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
933                                 s := Path + SearchRec.Name;
934
935                                 if (SearchRec.Attr and faDirectory = 0) and (MatchesMask(s, Mask)) then
936                                                 if IsPathAdd then
937                                                         List.Add(s)
938                                                 else
939                                                         List.Add(SearchRec.Name);
940                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
941                                         GetFileList(s, Mask, List, True, IsPathAdd);
942                         end;
943                         rc := FindNext(SearchRec);
944                 end;
945         finally
946                 SysUtils.FindClose(SearchRec);
947         end;
948         List.Sort;
949 end;
950
951 {!
952 \breif \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\8ew\92è\83t\83@\83C\83\8b\88ê\97\97\82ð\8eæ\93¾\82·\82é\81B
953                          \83T\83u\83t\83H\83\8b\83_\82Í\8c\9f\8dõ\82µ\82È\82¢
954 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
955 \param Mask      \83t\83@\83C\83\8b\96¼\82Ì\83}\83X\83N
956 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83@\83C\83\8b\96¼\88ê\97\97\82ª\95Ô\82é
957 \param IsPathAdd \83p\83X\95t\82«\82Å\83\8a\83X\83g\83A\83b\83v\82·\82é\8fê\8d\87\82Í True
958 \note \8dÄ\8bN\8ew\92è\89Â\94\\82È GetFileList() \82ª\82 \82é\82Ì\82Å\82±\82Ì\8aÖ\90\94\82Í\95s\97v?
959 \par \97á
960 \code
961 GetFileList('c:\', '*.txt', list, True);
962 \endcode
963 }
964 procedure TGikoSys.GetFileList(Path: string; Mask: string; var List: TStringList; IsPathAdd: Boolean);
965 var
966         rc: Integer;
967         SearchRec : TSearchRec;
968 begin
969         Path := IncludeTrailingPathDelimiter(Path);
970         rc := FindFirst(Path + Mask, faAnyfile, SearchRec);
971         try
972                 while rc = 0 do begin
973                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
974                                 if (SearchRec.Attr and faDirectory = 0) then begin
975                     if IsPathAdd then begin
976                         List.Add(Path + SearchRec.Name)
977                     end else begin
978                         List.Add(SearchRec.Name);
979                     end;
980                 end;
981                         end;
982                         rc := FindNext(SearchRec);
983                 end;
984         finally
985                 SysUtils.FindClose(SearchRec);
986         end;
987         List.Sort;
988 end;
989
990 {!
991 \brief \8ew\92è\83t\83H\83\8b\83_\93à\82Ì\83f\83B\83\8c\83N\83g\83\8a\88ê\97\97\82ð\8eæ\93¾\82·\82é
992 \param Path      \8bN\93_\82Æ\82È\82é\83t\83H\83\8b\83_\83p\83X
993 \param Mask      \83t\83H\83\8b\83_\96¼\82Ì\83}\83X\83N
994 \param List      OUT:\8eæ\93¾\82³\82ê\82½\83t\83H\83\8b\83_\96¼\88ê\97\97\82ª\95Ô\82é
995 \param SubDir    \92\86\82Ì\83t\83H\83\8b\83_\82Ü\82Å\8dÄ\8bA\93I\82É\83\8a\83X\83g\82·\82é\8fê\8d\87\82Í True
996
997 Mask \82ð '*.txt' \82Ì\82æ\82¤\82É\8ew\92è\82·\82é\82±\82Æ\82Å\81A
998 \93Á\92è\82Ì\83t\83@\83C\83\8b\96¼\82â\93Á\92è\82Ì\8ag\92£\8eq\82É\8di\82Á\82½\83\8a\83X\83g\83A\83b\83v\82ª\89Â\94\\82Å\82·\81B
999
1000 \par \97á:
1001 \code
1002 GetDirectoryList('c:\', '*.txt', list, True);
1003 \endcode
1004 }
1005 procedure TGikoSys.GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
1006 var
1007         rc: Integer;
1008         SearchRec : TSearchRec;
1009         s: string;
1010 begin
1011         Path := IncludeTrailingPathDelimiter(Path);
1012         rc := FindFirst(Path + '*.*', faDirectory, SearchRec);
1013         try
1014                 while rc = 0 do begin
1015                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
1016                                 s := Path + SearchRec.Name;
1017                                 //if (SearchRec.Attr and faDirectory > 0) then
1018                                 //      s := IncludeTrailingPathDelimiter(s)
1019
1020                                 if (SearchRec.Attr and faDirectory > 0) and (MatchesMask(s, Mask)) then
1021                                         List.Add( IncludeTrailingPathDelimiter( s ) );
1022                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
1023                                         GetDirectoryList(s, Mask, List, True);
1024                         end;
1025                         rc := FindNext(SearchRec);
1026                 end;
1027         finally
1028                 SysUtils.FindClose(SearchRec);
1029         end;
1030 end;
1031
1032
1033 {!
1034 \brief Subject.txt \88ê\8ds\82ð\89ð\8eß
1035 \param Line Subject.txt \82ð\8d\\90¬\82·\82é 1 \8ds
1036 \return     \83X\83\8c\83b\83h\8fî\95ñ
1037 }
1038 function TGikoSys.DivideSubject(Line: string): TSubjectRec;
1039 var
1040         i: integer;
1041         ws: WideString;
1042         Delim: string;
1043         LeftK: string;
1044         RightK: string;
1045 begin
1046         Result.FCount := 0;
1047
1048         if AnsiPos('<>', Line) = 0 then
1049                 Delim := ','
1050         else
1051                 Delim := '<>';
1052         Result.FFileName := RemoveToken(Line, Delim);
1053         Result.FTitle := Trim(RemoveToken(Line, Delim));
1054
1055         ws := Result.FTitle;
1056         if Copy(ws, Length(ws), 1) = ')' then begin
1057                 LeftK := '(';
1058                 RightK := ')';
1059         end else if Copy(ws, Length(ws)-1, 2) = '\81j' then begin
1060                 LeftK := '\81i';
1061                 RightK := '\81j';
1062         end else if Copy(ws, Length(ws), 1) = '>' then begin
1063                 LeftK := '<';
1064                 RightK := '>';
1065         end;
1066         for i := Length(ws) - 1 downto 1 do begin
1067                 if Copy(ws, i, Length(LeftK)) = LeftK then begin
1068                         Result.FTitle := TrimRight(Copy(ws, 1, i - 1));
1069                         ws := Copy(ws, i + Length(LeftK), Length(ws) - i - Length(RightK));
1070                         if IsNumeric(ws) then
1071                                 Result.FCount := StrToInt(ws);
1072                         //Delete(Result.FTitle, i, Length(LeftK) + Length(ws) + Length(RightK));
1073                         break;
1074                 end;
1075         end;
1076 end;
1077
1078 {!
1079 \brief URL\82©\82çBBSID\82ð\8eæ\93¾
1080 \param url BBSID \82ð\8eæ\93¾\82·\82é URL
1081 \return    BBSID
1082 }
1083 function TGikoSys.UrlToID(url: string): string;
1084 var
1085         i: integer;
1086 begin
1087         Result := '';
1088         url := Trim(url);
1089
1090         if url = '' then Exit;
1091         try
1092                 url := Copy(url, 0, Length(url) - 1);
1093                 for i := Length(url) downto 0 do begin
1094                         if url[i] = '/' then begin
1095                                 Result := Copy(url, i + 1, Length(url));
1096                                 Break;
1097                         end;
1098                 end;
1099         except
1100                 Result := '';
1101         end;
1102 end;
1103
1104 {!
1105 \brief URL\82©\82ç\8dÅ\8cã\82Ì\97v\91f\82ð\8dí\8f\9c
1106 \param url \89ð\8eß\82·\82é URL
1107 \return    \90Ø\82è\8eæ\82ç\82ê\82½\8cã\82Ì URL
1108
1109 URL \82©\82ç BBSID\88È\8aO\82Ì\95\94\95ª\82ð\8eæ\93¾\82·\82é\82Ì\82É\8eg\97p\82µ\82Ü\82·\81B
1110 }
1111 function TGikoSys.UrlToServer(url: string): string;
1112 var
1113         i: integer;
1114         wsURL: WideString;
1115 begin
1116         Result := '';
1117         wsURL := url;
1118         wsURL := Trim(wsURL);
1119
1120         if wsURL = '' then exit;
1121
1122         if Copy(wsURL, Length(wsURL), 1) = '/' then
1123                 wsURL := Copy(wsURL, 0, Length(wsURL) - 1);
1124
1125         for i := Length(wsURL) downto 0 do begin
1126                 if wsURL[i] = '/' then begin
1127                         Result := Copy(wsURL, 0, i);
1128                         break;
1129                 end;
1130         end;
1131 end;
1132
1133 {!
1134 \brief \83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
1135 \param Name \91\8dÝ\82ð\8am\94F\82·\82é\83t\83H\83\8b\83_\83p\83X
1136 \return     \83t\83H\83\8b\83_\82ª\91\8dÝ\82·\82é\82È\82ç True
1137 }
1138 function TGikoSys.DirectoryExistsEx(const Name: string): Boolean;
1139 var
1140         Code: Integer;
1141 begin
1142         Code := GetFileAttributes(PChar(Name));
1143         Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
1144 end;
1145
1146 {!
1147 \brief \83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
1148 \param Dir \8dì\90¬\82·\82é\83p\83X
1149 \return    \8dì\90¬\82É\90¬\8c÷\82µ\82½\8fê\8d\87\82Í True
1150 }
1151 function TGikoSys.ForceDirectoriesEx(Dir: string): Boolean;
1152 begin
1153         Result := True;
1154         if Length(Dir) = 0 then
1155                 raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
1156         Dir := ExcludeTrailingPathDelimiter(Dir);
1157         if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
1158                 or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
1159         Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
1160 end;
1161
1162 {!
1163 \brief \95\8e\9a\97ñ\82©\82ç\83g\81[\83N\83\93\82Ì\90Ø\82è\8fo\82µ\81i\8f\89\8aú\8f\88\97\9d\81j
1164                          FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1165 \param s   \8c³\82É\82È\82é\83L\83\83\83\89\83N\83^
1166 \param sep \8bæ\90Ø\82è\82É\82È\82é\95\8e\9a\97ñ
1167 \param Rec OUT:\95\8e\9a\97ñ\91\96\8d¸\8fî\95ñ\82ª\95Ô\82é
1168 \return    \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1169 \todo Split, RemoveToken, GetTokenIndex, NthField \8ds\82«
1170 }
1171 function TGikoSys.StrTokFirst(const s:string; const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1172 begin
1173         Rec.Str := s;
1174         Rec.Pos := 1;
1175         Result := StrTokNext(sep, Rec);
1176 end;
1177
1178 {!
1179 \brief \95\8e\9a\97ñ\82©\82ç\83g\81[\83N\83\93\82Ì\90Ø\82è\8fo\82µ
1180                          FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1181 \param sep \8bæ\90Ø\82è\82É\82È\82é\83L\83\83\83\89\83N\83^
1182 \param Rec IN/OUT:StrTokFirst\82Å\8dì\90¬\82³\82ê\82½\95\8e\9a\97ñ\91\96\8d¸\8fî\95ñ
1183 \return    \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1184 \todo Split, RemoveToken, GetTokenIndex, NthField \8ds\82«
1185 }
1186 function TGikoSys.StrTokNext(const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1187 var
1188         Len, I: Integer;
1189 begin
1190         with Rec do     begin
1191                 Len := Length(Str);
1192                 Result := '';
1193                 if Len >= Pos then begin
1194                         while (Pos <= Len) and (Str[Pos] in sep) do begin
1195                          Inc(Pos);
1196                         end;
1197                         I := Pos;
1198                         while (Pos<= Len) and not (Str[Pos] in sep) do begin
1199                                 if IsDBCSLeadByte(Byte(Str[Pos])) then begin
1200                                         Inc(Pos);
1201                                 end;
1202                                 Inc(Pos);
1203                         end;
1204                         Result := Copy(Str, I, Pos - I);
1205                         while (Pos <= Len) and (Str[Pos] in sep) do begin// \82±\82ê\82Í\82¨\8dD\82Ý
1206                                 Inc(Pos);
1207                         end;
1208                 end;
1209         end;
1210 end;
1211
1212 {!
1213 \brief \83t\83@\83C\83\8b\83T\83C\83Y\8eæ\93¾
1214 \param FileName \83t\83@\83C\83\8b\83T\83C\83Y\82ð\8eæ\93¾\82·\82é\83t\83@\83C\83\8b\83p\83X
1215 \return         \83t\83@\83C\83\8b\83T\83C\83Y(bytes)
1216 }
1217 function TGikoSys.GetFileSize(FileName : string): longint;
1218 var
1219         F : File;
1220 begin
1221         try
1222                 if not FileExists(FileName) then begin
1223                         Result := 0;
1224                         Exit;
1225                 end;
1226                 Assign(F, FileName);
1227                 Reset(F, 1);
1228                 Result := FileSize(F);
1229                 CloseFile(F);
1230         except
1231                 Result := 0;
1232         end;
1233 end;
1234
1235 {!
1236 \brief \83e\83L\83X\83g\83t\83@\83C\83\8b\82Ì\8ds\90\94\82ð\8eæ\93¾
1237 \param FileName \8ds\90\94\82ð\8eæ\93¾\82·\82é\83t\83@\83C\83\8b\83p\83X
1238 \return         \8ds\90\94
1239 \todo \83\81\83\82\83\8a\83}\83b\83v\83h\83t\83@\83C\83\8b\8ds\82«
1240 }
1241 function TGikoSys.GetFileLineCount(FileName : string): longint;
1242 var
1243         sl: TStringList;
1244 begin
1245         sl := TStringList.Create;
1246         try
1247                 try
1248                         sl.LoadFromFile(FileName);
1249                         Result := sl.Count;
1250                 except
1251                         Result := 0;
1252                 end;
1253         finally
1254                 sl.Free;
1255         end;
1256
1257 end;
1258
1259 {!
1260 \brief \83t\83@\83C\83\8b\82©\82ç\8ew\92è\8ds\82ð\8eæ\93¾
1261 \param FileName \83t\83@\83C\83\8b\82Ì\83p\83X
1262 \param Line     \8ew\92è\8ds
1263 \return         \8ew\92è\82³\82ê\82½ 1 \8ds
1264 \todo \83\81\83\82\83\8a\83}\83b\83v\83h\83t\83@\83C\83\8b\8ds\82«
1265 }
1266 function TGikoSys.ReadThreadFile(FileName: string; Line: Integer): string;
1267 var
1268         fileTmp : TStringList;
1269 begin
1270         Result := '';
1271         if FileExists(FileName) then begin
1272                 fileTmp := TStringList.Create;
1273                 try
1274                         try
1275                                 fileTmp.LoadFromFile( FileName );
1276                                 if ( Line       >= 1 ) and ( Line       < fileTmp.Count + 1 ) then begin
1277                                         Result := fileTmp.Strings[ Line-1 ];
1278                                 end;
1279                         except
1280                                 //on EFOpenError do Result := '';
1281                         end;
1282                 finally
1283                         fileTmp.Free;
1284                 end;
1285         end;
1286 end;
1287
1288 {!
1289 \brief \83V\83X\83e\83\80\83\81\83j\83\85\81[\83t\83H\83\93\83g\82Ì\91®\90«\82ð\8eæ\93¾
1290 \param Font OUT:\8eæ\93¾\82µ\82½\83t\83H\83\93\83g\91®\90«\82ª\95Ô\82é
1291 }
1292 procedure TGikoSys.MenuFont(Font: TFont);
1293 var
1294         lf: LOGFONT;
1295         nm: NONCLIENTMETRICS;
1296 begin
1297         nm.cbSize := sizeof(NONCLIENTMETRICS);
1298
1299         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @nm, 0);
1300         lf := nm.lfMenuFont;
1301
1302         Font.Name := lf.lfFaceName;
1303         Font.Height := lf.lfHeight;
1304         Font.Style := [];
1305         if lf.lfWeight >= 700 then
1306                 Font.Style := Font.Style + [fsBold];
1307         if lf.lfItalic = 1 then
1308                 Font.Style := Font.Style + [fsItalic];
1309 end;
1310
1311 {!
1312 \brief \90æ\93ª\82Ì\83g\81[\83N\83\93\82ð\90Ø\82è\8fo\82µ
1313 \param s         IN/OUT:\8c³\82É\82È\82é\95\8e\9a\97ñ\81A\90Ø\82è\8fo\82µ\82½\8cã\82Ì\8ec\82è\82Ì\95\8e\9a\97ñ
1314 \param delimiter \8bæ\90Ø\82è\82É\82È\82é\95\8e\9a\97ñ
1315 \return          \90Ø\82è\8fo\82µ\82½\95\8e\9a\97ñ
1316
1317 \82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1318 }
1319 function TGikoSys.RemoveToken(var s: string;const delimiter: string): string;
1320 var
1321         p: Integer;
1322 begin
1323         p := AnsiPos(delimiter, s);
1324         if p = 0 then
1325                 Result := s
1326         else
1327                 Result := Copy(s, 1, p - 1);
1328         Delete(s, 1, Length(Result) + Length(delimiter));
1329 end;
1330
1331
1332 {!
1333 \brief n \8cÂ\96Ú\82Ì\83g\81[\83N\83\93\82ð\90Ø\82è\8fo\82µ
1334 \param s     \8c³\82É\82È\82é\95\8e\9a\97ñ
1335 \param index 0 \82©\82ç\8en\82Ü\82é\83C\83\93\83f\83b\83N\83X(n \8cÂ\96Ú\82Ì n)
1336 \return \90Ø\82è\8fo\82µ\82½\83g\81[\83N\83\93
1337
1338 \82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1339 }
1340 function TGikoSys.GetTokenIndex(s: string; delimiter: string; index: Integer): string;
1341 var
1342         i: Integer;
1343 begin
1344         Result := '';
1345         for i := 0 to index do
1346                 Result := RemoveToken(s, delimiter);
1347 end;
1348
1349
1350 //\83C\83\93\83f\83b\83N\83X\96¢\8dX\90V\83o\83b\83t\83@\82ð\83t\83\89\83b\83V\83\85\81I
1351 {procedure TGikoSys.FlashExitWrite;
1352 var
1353         i: Integer;
1354 begin
1355         //\83X\83\8c\83b\83h\83f\81[\83^\83t\83@\83C\83\8b\82ð\8dX\90V
1356         for i := 0 to FExitWrite.Count - 1 do
1357                 WriteThreadDat(FExitWrite[i]);
1358         FExitWrite.Clear;
1359 end;}
1360
1361 {!
1362 \brief \83X\83\8c\96¼\82È\82Ç\82ð\92Z\82¢\96¼\91O\82É\95Ï\8a·\82·\82é
1363 \param LongName \8c³\82É\82È\82é\95\8e\9a\97ñ
1364 \param ALength  \8eû\82ß\82é\95\8e\9a\97ñ\92·(bytes)
1365 \return         \95Ï\8a·\82³\82ê\82½\95\8e\9a\97ñ
1366
1367 from HotZonu
1368 }
1369 function TGikoSys.GetShortName(const LongName: string; ALength: integer): string;
1370 const
1371         ERASECHAR : array [1..39] of string =
1372                 ('\81\99','\81\9a','\81¡','\81 ','\81\9f','\81\9e','\81Q','\81\94','\81£','\81¥',
1373                  '\81¢','\81¤','\81\9c','\81\9b','\81\9d','\81y','\81z','\81ô','\81s','\81t',
1374                  '\81g','\81h','\81k','\81l','\81e','\81f','\81\83','\81\84','\81á','\81â',
1375                  '\81o','\81p','\81q','\81r','\81w','\81x','\81¬','\81c', '\81@');
1376 var
1377         Chr : array [0..255]    of      char;
1378         S : string;
1379         i : integer;
1380 begin
1381         s := Trim(LongName);
1382         if (Length(s) <= ALength) then begin
1383                 Result := s;
1384         end else begin
1385                 S := s;
1386                 for i := Low(ERASECHAR) to      High(ERASECHAR) do      begin
1387                         S := CustomStringReplace(S, ERASECHAR[i], '');
1388                 end;
1389                 if (Length(S) <= ALength) then begin
1390                         Result := S;
1391                 end else begin
1392                         Windows.LCMapString(
1393                                         GetUserDefaultLCID(),
1394                                         LCMAP_HALFWIDTH,
1395                                         PChar(S),
1396                                         Length(S) + 1,
1397                                         chr,
1398                                         Sizeof(chr)
1399                                         );
1400                         S := Chr;
1401                         S := Copy(S,1,ALength);
1402                         while true do begin
1403                                 if (ByteType(S, Length(S)) = mbLeadByte ) then begin
1404                                         S := Copy(S, 1, Length(S) - 1);
1405                                 end else begin
1406                                         Break;
1407                                 end;
1408                         end;
1409                         Result := S;
1410                 end;
1411         end;
1412 end;
1413
1414 {!
1415 \brief Boolean \82ð Integer \82É\95Ï\8a·
1416 \return False..0, True..1
1417 }
1418 function TGikoSys.BoolToInt(b: Boolean): Integer;
1419 begin
1420         Result := IfThen(b, 1, 0);
1421 end;
1422
1423 {!
1424 \brief Integer \82ð Boolean \82É\95Ï\8a·
1425 \return 1..True, other..False
1426 \todo 0..False, other..True \82Ì\95û\82ª\82¢\82¢\82Ì\82Å\82Í?
1427                         (\82±\82Ì\8ed\97l\82É\88Ë\91\82µ\82Ä\82¢\82é\82©\82à\82µ\82ê\82È\82¢\82Ì\82Å\96¢\8fC\90³)
1428 }
1429 function TGikoSys.IntToBool(i: Integer): Boolean;
1430 begin
1431         Result := i = 1;
1432 end;
1433
1434 {!
1435 \brief gzip\82Å\88³\8fk\82³\82ê\82½\82Ì\82ð\96ß\82·
1436 \param ResStream       \93Ç\82Ý\8d\9e\82Þ\83X\83g\83\8a\81[\83\80
1437 \param ContentEncoding \83G\83\93\83R\81[\83f\83B\83\93\83O
1438 \return                \93W\8aJ\82³\82ê\82½\95\8e\9a\97ñ
1439 }
1440 function TGikoSys.GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
1441 const
1442         BUF_SIZE = 4096;
1443 var
1444         GZipStream: TGzipDecompressStream;
1445         TextStream: TStringStream;
1446         buf: array[0..BUF_SIZE - 1] of Byte;
1447         cnt: Integer;
1448         s: string;
1449         i, ln: Integer;
1450 begin
1451         Result := '';
1452         TextStream := TStringStream.Create('');
1453         try
1454 //\83m\81[\83g\83\93\83E\83\93\83`\83E\83B\83\8b\83X2003\91Î\8dô(x-gzip\82Æ\82©\82É\82È\82é\82Ý\82½\82¢)
1455 //              if LowerCase(Trim(ContentEncoding)) = 'gzip' then begin
1456                 if AnsiPos('gzip', LowerCase(Trim(ContentEncoding))) > 0 then begin
1457                         ResStream.Position := 0;
1458                         GZipStream := TGzipDecompressStream.Create(TextStream);
1459                         try
1460                                 repeat
1461                                         FillChar(buf, BUF_SIZE, 0);
1462                                         cnt := ResStream.Read(buf, BUF_SIZE);
1463                                         if cnt > 0 then
1464                                                 GZipStream.Write(buf, BUF_SIZE);
1465                                 until cnt = 0;
1466                         finally
1467                                 GZipStream.Free;
1468                         end;
1469                 end else begin
1470                         ResStream.Position := 0;
1471                         repeat
1472                                 FillChar(buf, BUF_SIZE, 0);
1473                                 cnt := ResStream.Read(buf, BUF_SIZE);
1474                                 if cnt > 0 then
1475                                         TextStream.Write(buf, BUF_SIZE);
1476                         until cnt = 0;
1477                 end;
1478
1479                 //NULL\95\8e\9a\82ð"*"\82É\82·\82é
1480                 s := TextStream.DataString;
1481                 i := Length(s);
1482                 ln := i;
1483                 while (i > 0) and (s[i] = #0) do
1484                         Dec(i);
1485                 Delete(s, i + 1, ln - i);
1486
1487                 i := Pos(#0, s);
1488                 while i <> 0 do begin
1489                         s[i] := '*';
1490                         i := Pos(#0, s);
1491                 end;
1492
1493                 Result := s;
1494         finally
1495                 TextStream.Free;
1496         end;
1497 end;
1498
1499 {!
1500 \brief \83A\83N\83V\83\87\83\93\82É\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð\90Ý\92è
1501 \param ActionList \90Ý\92è\82·\82é\83A\83N\83V\83\87\83\93\88ê\97\97
1502 \param FileName Ini\83t\83@\83C\83\8b\82Ì\96¼\91O
1503 }
1504 procedure TGikoSys.LoadKeySetting(ActionList: TActionList; FileName: String);
1505 const
1506         STD_SEC = 'KeySetting';
1507 var
1508         i: Integer;
1509         ini: TMemIniFile;
1510         ActionName: string;
1511         ActionKey: Integer;
1512         SecList: TStringList;
1513         Component: TComponent;
1514 begin
1515         if not FileExists(fileName) then
1516                 Exit;
1517         SecList := TStringList.Create;
1518         ini := TMemIniFile.Create(fileName);
1519         try
1520                 ini.ReadSection(STD_SEC, SecList);
1521                 for i := 0 to SecList.Count - 1 do begin
1522                         ActionName := SecList[i];
1523                         ActionKey := ini.ReadInteger(STD_SEC, ActionName, -1);
1524                         if ActionKey <> -1 then begin
1525                                 Component := ActionList.Owner.FindComponent(ActionName);
1526                                 if TObject(Component) is TAction then begin
1527                                         TAction(Component).ShortCut := ActionKey;
1528                                 end;
1529                         end;
1530                 end;
1531         finally
1532                 ini.Free;
1533                 SecList.Free;
1534         end;
1535 end;
1536
1537 {!
1538 \brief \83A\83N\83V\83\87\83\93\82É\90Ý\92è\82³\82ê\82Ä\82¢\82é\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð\83t\83@\83C\83\8b\82É\95Û\91
1539 \param ActionList \95Û\91\82·\82é\83A\83N\83V\83\87\83\93\88ê\97\97
1540 \param FileName Ini\83t\83@\83C\83\8b\96¼
1541
1542 ActionList \82É\90Ý\92è\82³\82ê\82Ä\82¢\82é\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ð FileName \82É\95Û\91\82µ\82Ü\82·\81B
1543 }
1544 procedure TGikoSys.SaveKeySetting(ActionList: TActionList; FileName: String);
1545 const
1546         STD_SEC = 'KeySetting';
1547 var
1548         i: Integer;
1549         ini: TMemIniFile;
1550 begin
1551         ini := TMemIniFile.Create(GetConfigDir + FileName);
1552         try
1553                 for i := 0 to ActionList.ActionCount - 1 do begin
1554                         if ActionList.Actions[i].Tag = -1 then
1555                                 Continue;
1556                         ini.WriteInteger(STD_SEC, ActionList.Actions[i].Name, TAction(ActionList.Actions[i]).ShortCut);
1557                 end;
1558                 ini.UpdateFile;
1559         finally
1560                 ini.Free;
1561         end;
1562 end;
1563
1564
1565 {!
1566 \brief \83v\83\8d\83Z\83X\82Ì\90\90¬
1567 \param AppPath \8bN\93®\82·\82é\83v\83\8d\83Z\83X\82Ì\83t\83@\83C\83\8b\83p\83X
1568 \param Param   AppPath \82É\93n\82·\88ø\90\94
1569 }
1570 procedure TGikoSys.CreateProcess(const AppPath: string; const Param: string);
1571 var
1572         PI: TProcessInformation;
1573         SI: TStartupInfo;
1574         Path: string;
1575 begin
1576         Path := '"' + AppPath + '"';
1577         if Param <> '' then
1578                 Path := Path + ' ' + Param;
1579
1580         SI.Cb := SizeOf(Si);
1581         SI.lpReserved   := nil;
1582         SI.lpDesktop     := nil;
1583         SI.lpTitle               := nil;
1584         SI.dwFlags               := 0;
1585         SI.cbReserved2 := 0;
1586         SI.lpReserved2 := nil;
1587         SI.dwysize               := 0;
1588         Windows.CreateProcess(nil,
1589                                                                 PChar(Path),
1590                                                                 nil,
1591                                                                 nil,
1592                                                                 False,
1593                                                                 0,
1594                                                                 nil,
1595                                                                 nil,
1596                                                                 SI,
1597                                                                 PI);
1598 end;
1599
1600 {!
1601 \brief Web \83u\83\89\83E\83U\82ð\8bN\93®
1602 \param URL         Web \83u\83\89\83E\83U\82Å\95\\8e¦\82·\82é URL
1603 \param BrowserType \83u\83\89\83E\83U\82Ì\83^\83C\83v(IE \82©\82Ç\82¤\82©)
1604 }
1605 procedure TGikoSys.OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
1606 begin
1607         case BrowserType of
1608                 gbtIE:
1609                         HlinkNavigateString(nil, PWideChar(WideString(URL)));
1610                 gbtUserApp, gbtAuto:
1611                         if (Setting.URLApp) and (FileExists(Setting.URLAppFile)) then
1612                                 GikoSys.CreateProcess(Setting.URLAppFile, URL)
1613                         else
1614                                 HlinkNavigateString(nil, PWideChar(WideString(URL)));
1615         end;
1616 end;
1617
1618 {!
1619 \brief \95\8e\9a\8eÀ\91Ì\8eQ\8fÆ\82ð\83f\83R\81[\83h
1620 \param AStr \83f\83R\81[\83h\82·\82é\95\8e\9a\97ñ
1621 \return     \83f\83R\81[\83h\82³\82ê\82½\95\8e\9a\97ñ
1622 }
1623 function TGikoSys.HTMLDecode(const AStr: String): String;
1624 var
1625         Sp, Rp, Cp, Tp: PChar;
1626         S: String;
1627         I, Code: Integer;
1628         Num: Boolean;
1629 begin
1630         SetLength(Result, Length(AStr));
1631         Sp := PChar(AStr);
1632         Rp := PChar(Result);
1633         //Cp := Sp;
1634         try
1635                 while Sp^ <> #0 do begin
1636                         case Sp^ of
1637                                 '&': begin
1638                                                          //Cp := Sp;
1639                                                          Inc(Sp);
1640                                                          case Sp^ of
1641                                                                  'a': if AnsiStrPos(Sp, 'amp;') = Sp then
1642                                                                                         begin
1643                                                                                                 Inc(Sp, 3);
1644                                                                                                 Rp^ := '&';
1645                                                                                         end;
1646                                                                  'l',
1647                                                                  'g': if (AnsiStrPos(Sp, 'lt;') = Sp) or (AnsiStrPos(Sp, 'gt;') = Sp) then
1648                                                                                         begin
1649                                                                                                 Cp := Sp;
1650                                                                                                 Inc(Sp, 2);
1651                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do
1652                                                                                                         Inc(Sp);
1653                                                                                                 if Cp^ = 'l' then
1654                                                                                                         Rp^ := '<'
1655                                                                                                 else
1656                                                                                                         Rp^ := '>';
1657                                                                                         end;
1658                                                                  'q': if AnsiStrPos(Sp, 'quot;') = Sp then
1659                                                                                         begin
1660                                                                                                 Inc(Sp,4);
1661                                                                                                 Rp^ := '"';
1662                                                                                         end;
1663                                                                  '#': begin
1664                                                                                                 Tp := Sp;
1665                                                                                                 Inc(Tp);
1666                                                                                                 Num := IsNumeric(Copy(Tp, 1, 1));
1667                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do begin
1668                                                                                                         if (Num) and (not IsNumeric(Copy(Sp, 1, 1))) then
1669                                                                                                                 Break;
1670                                                                                                         Inc(Sp);
1671                                                                                                 end;
1672                                                                                                 SetString(S, Tp, Sp - Tp);
1673                                                                                                 Val(S, I, Code);
1674                                                                                                 Rp^ := Chr((I));
1675                                                                                         end;
1676                                                          //      else
1677                                                                          //raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
1678                                                                                  //[Cp^ + Sp^, Cp - PChar(AStr)])
1679                                                          end;
1680                                          end
1681                         else
1682                                 Rp^ := Sp^;
1683                         end;
1684                         Inc(Rp);
1685                         Inc(Sp);
1686                 end;
1687         except
1688 //              on E:EConvertError do
1689 //                      raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
1690 //                              [Cp^ + Sp^, Cp - PChar(AStr)])
1691         end;
1692         SetLength(Result, Rp - PChar(Result));
1693 end;
1694
1695 {!
1696 \brief HTML \82Ì\83A\83\93\83J\81[\83^\83O\82©\82ç URL \82ð\8eæ\93¾
1697 \param s URL \82ð\8eæ\93¾\82·\82é HTML
1698 \return  \8eæ\93¾\82µ\82½ URL
1699 }
1700 function TGikoSys.GetHRefText(s: string): string;
1701 var
1702         Index: Integer;
1703         Index2: Integer;
1704 begin
1705         Result := '';
1706         s := Trim(s);
1707         if s = '' then
1708                 Exit;
1709
1710         Index := AnsiPos('href', LowerCase(s));
1711         if Index = 0 then
1712                 Exit;
1713         s := Trim(Copy(s, Index + 4, Length(s)));
1714         s := Trim(Copy(s, 2, Length(s)));
1715
1716         //\8en\82ß\82Ì\95\8e\9a\82ª'"'\82È\82ç\8eæ\82è\8f\9c\82­
1717         //if Copy(s, 1, 1) = '"' then begin
1718     if s[1]  = '"' then begin
1719                 s := Trim(Copy(s, 2, Length(s)));
1720         end;
1721
1722         Index := AnsiPos('"', s);
1723         if Index <> 0 then begin
1724                 //'"'\82Ü\82ÅURL\82Æ\82·\82é
1725                 s := Copy(s, 1, Index - 1);
1726         end else begin
1727                 //'"'\82ª\96³\82¯\82ê\82Î\83X\83y\81[\83X\82©">"\82Ì\91\81\82¢\95û\82Ü\82Å\82ðURL\82Æ\82·\82é
1728                 Index := AnsiPos(' ', s);
1729                 Index2 := AnsiPos('>', s);
1730                 if Index = 0 then
1731                         Index := Index2;
1732                 if Index > Index2 then
1733                         Index := Index2;
1734                 if Index <> 0 then
1735                         s := Copy(s, 1, Index - 1)
1736                 else
1737                         //\82±\82ê\88È\8fã\82à\82¤\92m\82ç\82ñ\82Ê
1738                         ;
1739         end;
1740         Result := Trim(s);
1741 end;
1742
1743 {!
1744 \brief \83z\83X\83g\96¼\82ª\82Q\82\83\82\88\82©\82Ç\82¤\82©\83`\83F\83b\83N\82·\82é
1745 \param Host \83`\83F\83b\83N\82·\82é\83z\83X\83g\96¼
1746 \return     2\82¿\82á\82ñ\82Ë\82é\82Ì\83z\83X\83g\96¼\82È\82ç True
1747 }
1748 function TGikoSys.Is2chHost(Host: string): Boolean;
1749 const
1750         HOST_NAME: array[0..1] of string = ('2ch.net', 'bbspink.com');
1751 var
1752         i: Integer;
1753 //      Len: Integer;
1754 begin
1755         Result := False;
1756         if RightStr( Host, 1 ) = '/' then
1757                 Host := Copy( Host, 1, Length( Host ) - 1 );
1758         OutputDebugString(pchar(HOST_NAME[0]));
1759         for i := 0 to Length(HOST_NAME) - 1 do begin
1760 //              Len := Length(HOST_NAME[i]);
1761                 if (AnsiPos(HOST_NAME[i], Host) > 0) and
1762                         (AnsiPos(HOST_NAME[i], Host) = (Length(Host) - Length(HOST_NAME[i]) + 1)) then begin
1763                         Result := True;
1764                         Exit;
1765                 end;
1766         end;
1767 end;
1768
1769 {!
1770 \brief 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL \82ð\95ª\89ð
1771 \param url      2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
1772 \param path     test/read.cgi \82È\82Ç\82Ì\92\86\8aÔ\83p\83X(ParseURI \82©\82ç\93¾\82é)
1773 \param document index.html \82È\82Ç\82Ì\83h\83L\83\85\83\81\83\93\83g\96¼(ParseURI \82©\82ç\93¾\82é)
1774 \param BBSID    OUT:BBSID \82ª\95Ô\82é(ex. giko)
1775 \param BBSKey   OUT:\83X\83\8c\83b\83h\83L\81[\82ª\95Ô\82é(ex. 10000000000)
1776 \return 2\82¿\82á\82ñ\82Ë\82é\82Ì URL \82Æ\82µ\82Ä\95ª\89ð\82Å\82«\82½\82È\82ç True
1777 }
1778 function TGikoSys.Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
1779 var
1780         Index: Integer;
1781         s: string;
1782         SList: TStringList;
1783 begin
1784         BBSID := '';
1785         BBSKey := '';
1786         Result := False;
1787
1788         Index := AnsiPos(READ_PATH, path);
1789         if Index <> 0 then begin
1790                 s := Copy(path, Index + Length(READ_PATH), Length(path));
1791
1792                 if (Length(s) > 0) and (s[1] = '/') then
1793                         Delete(s, 1, 1);
1794                 BBSID := GetTokenIndex(s, '/', 0);
1795                 BBSKey := GetTokenIndex(s, '/', 1);
1796                 if BBSKey = '' then
1797                         BBSKey := Document;
1798                 Result := (BBSID <> '') or (BBSKey <> '');
1799                 Exit;
1800         end;
1801         Index := AnsiPos(KAKO_PATH, path);
1802         if Index <> 0 then begin
1803                 s := Copy(path, 2, Length(path));
1804                 BBSID := GetTokenIndex(s, '/', 0);
1805                 if (BBSID = 'log') and (GetTokenIndex(s, '/', 2) = 'kako') then
1806                         BBSID := GetTokenIndex(s, '/', 1);
1807                 BBSKey := ChangeFileExt(Document, '');
1808                 Result := (BBSID <> '') or (BBSKey <> '');
1809                 Exit;
1810         end;
1811         Index := AnsiPos('read.cgi?', URL);
1812         if Index <> 0 then begin
1813                 SList := TStringList.Create;
1814                 try
1815                         try
1816 //                              s := HTMLDecode(Document);
1817                                 ExtractHTTPFields(['?', '&'], [], PChar(URL), SList, False);
1818                                 BBSID := SList.Values['bbs'];
1819                                 BBSKey := SList.Values['key'];
1820                                 Result := (BBSID <> '') or (BBSKey <> '');
1821                                 Exit;
1822                         except
1823                                 Exit;
1824                         end;
1825                 finally
1826                         SList.Free;
1827                 end;
1828         end;
1829 end;
1830
1831 {!
1832 \brief 2ch \8c`\8e®\82Ì URL \82©\82ç\83\8c\83X\94Ô\82ð\8eæ\93¾
1833 \param URL    2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
1834 \param stRes  OUT:\8aJ\8en\83\8c\83X\94Ô\82ª\95Ô\82é
1835 \param endRes OUT:\8fI\97¹\83\8c\83X\94Ô\82ª\95Ô\82é
1836
1837 http://2ch.net/\92\86\97ª/32-50 \n
1838 \82Ì\8fê\8d\87 stRef = 32, endRes = 50 \82É\82È\82é
1839 }
1840 procedure TGikoSys.GetPopupResNumber(URL : string; var stRes, endRes : Int64);
1841 var
1842         buf : String;
1843         convBuf : String;
1844         ps : Int64;
1845         pch : PChar;
1846 begin
1847         URL := Trim(LowerCase(URL));
1848         if (AnsiPos('&st=', URL ) <> 0) and ( AnsiPos( '&to=',URL) <> 0 ) then begin
1849                 stRes := 0;
1850                 endRes := 0;
1851                 try
1852                         buf := Copy( URL, AnsiPos('&st=', URL ) + 4, AnsiPos( '&to=',URL) - AnsiPos('&st=', URL ) - 4 );
1853                         if buf <> '' then
1854                                 stRes := StrToInt64( buf );
1855                         if AnsiPos( '&nofirst=',URL) <> 0 then begin
1856                                 buf := Copy( URL, AnsiPos('&to=', URL ) + 4, AnsiPos( '&nofirst=',URL) - AnsiPos('&to=', URL ) - 4);
1857                         end else begin
1858                                 buf := Copy( URL, AnsiPos('&to=', URL ) + 4, Length( URL ) - AnsiPos('&to=', URL ) - 4 + 1 );
1859                                 ps := 0;
1860                                 pch := PChar(buf);
1861                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1862                                 buf := Copy( buf, 1, ps );
1863                         end;
1864                         try
1865                                 if buf <> '' then
1866                                         endRes := StrToInt64(buf)
1867                         except
1868                                 endRes := 0;
1869                         end;
1870                 except
1871                         stRes := 0;
1872                 end;
1873                 if (stRes <> 0) and (endRes = 0) then
1874                         endRes := stRes + MAX_POPUP_RES
1875                 else if (stRes = 0) and (endRes <> 0) then begin
1876                         stRes := endRes - MAX_POPUP_RES;
1877                         if stRes < 1 then
1878                                 stRes := 1;
1879                 end;
1880                 GikoSys.GetBrowsableThreadURL( URL );
1881         end else if( AnsiPos('&res=', URL ) <> 0 ) then begin
1882                 endRes := 0;
1883                 buf := Copy( URL, AnsiPos('&res=', URL ) + 5, Length( URL ) - AnsiPos('&res=', URL ) - 5 + 1 );
1884                 ps := 0;
1885                 pch := PChar(buf);
1886                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1887                 buf := Copy( buf, 1, ps );
1888                 try
1889                         if buf <> '' then
1890                                 stRes := StrToInt(buf)
1891                         else begin
1892                                 stRes := 0;
1893                         end;
1894                 except
1895                         stRes := 0;
1896                 end;
1897         end else if (AnsiPos('&start=', URL ) <> 0) and ( AnsiPos( '&end=',URL) <> 0 ) then begin
1898                 try
1899                         stRes := StrToInt64( Copy( URL, AnsiPos('&start=', URL ) + 7, AnsiPos( '&end=',URL) - AnsiPos('&start=', URL ) - 7 ) );
1900                         if AnsiPos( '&nofirst=',URL) <> 0 then begin
1901                                 buf := Copy( URL, AnsiPos('&end=', URL ) + 5, AnsiPos( '&nofirst=',URL) - AnsiPos('&end=', URL ) - 5);
1902                         end else begin
1903                                 buf := Copy( URL, AnsiPos('&end=', URL ) + 5, Length( URL ) - AnsiPos('&to=', URL ) - 5 + 1 );
1904                                 ps := 0;
1905                                 pch := PChar(buf);
1906                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1907                                 buf := Copy( buf, 1, ps );
1908                         end;
1909                         try
1910                                 if buf <> '' then
1911                                         endRes := StrToInt64(buf);
1912                         except
1913                                 endRes := 0;
1914                         end;
1915                 except
1916                         stRes := 0;
1917                 end;
1918         end else if ( AnsiPos('.html',URL) <> Length(URL) -4 ) and ( AnsiPos('.htm',URL) <> Length(URL) -3 ) then begin
1919                 buf := Copy(URL, LastDelimiter('/',URL)+1,Length(URL)-LastDelimiter('/',URL)+1);
1920                 if  Length(buf) > 0 then begin
1921                         if AnsiPos('-', buf) = 1 then begin
1922                                 stRes := 0;
1923                                 Delete(buf,1,1);
1924                                 ps := 0;
1925                                 pch := PChar(buf);
1926                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1927                                 try
1928                                         convBuf := Copy( buf, 1, ps );
1929                                         if convBuf <> '' then
1930                                                 endRes := StrToInt64(convBuf)
1931                                         else
1932                                                 endRes := 0;
1933                                 except
1934                                         endRes := 0;
1935                                 end;
1936                                 if endRes <> 0 then begin
1937                                         stRes := endRes - MAX_POPUP_RES;
1938                                         if stRes < 1 then
1939                                                 stRes := 1;
1940                                 end;
1941                         end else begin
1942                                 ps := 0;
1943                                 pch := PChar(buf);
1944                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1945                                 try
1946                                         convBuf := Copy( buf, 1, ps );
1947                                         if convBuf <> '' then begin
1948                                                 stRes := StrToInt64(convBuf);
1949                                                 Delete(buf,1,ps+1);
1950                                                 ps := 0;
1951                                                 pch := PChar(buf);
1952                                                 while  ( ps < Length(buf) )and ( pch[ps] >= '0' ) and ( pch[ps] <= '9' ) do Inc(ps);
1953                                                 try
1954                                                         convBuf := Copy( buf, 1, ps );
1955                                                         if convBuf <> '' then
1956                                                                 endRes := StrToInt64(convBuf)
1957                                                         else
1958                                                                 endRes := 0;
1959                                                 except
1960                                                         endRes := 0;
1961                                                 end;
1962                                         end else begin
1963                                                 stRes := 0;
1964                                         end;
1965                                 except
1966                                         stRes := 0;
1967                                         endRes := 0;
1968                                 end;
1969                         end;
1970                 end;
1971         end else begin
1972                 //stRes := 0;
1973                 //endRes := 0;
1974         end;
1975 end;
1976
1977 {!
1978 \brief 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL \82ð\95ª\89ð
1979 \param URL 2\82¿\82á\82ñ\82Ë\82é\8c`\8e®\82Ì URL
1980 \return    \95ª\89ð\82³\82ê\82½\97v\91f
1981 }
1982 function TGikoSys.Parse2chURL2(URL: string): TPathRec;
1983 var
1984         i: Integer;
1985         s: string;
1986 //      buf : String;
1987 //      convBuf : String;
1988         wk: string;
1989         wkMin: Integer;
1990         wkMax: Integer;
1991         wkInt: Integer;
1992         RStart: Integer;
1993         RLength: Integer;
1994 //      ps : Integer;
1995 //      pch : PChar;
1996         SList: TStringList;
1997 begin
1998         URL := Trim(LowerCase(URL));
1999         Result.FBBS := '';
2000         Result.FKey := '';
2001         Result.FSt := 0;
2002         Result.FTo := 0;
2003         Result.FFirst := False;
2004         Result.FStBegin := False;
2005         Result.FToEnd := False;
2006         Result.FDone := False;
2007         Result.FNoParam := False;
2008
2009         wkMin := 0;
2010         wkMax := 1;
2011         if URL[length(URL)] = '\' then
2012                 URL := URL + 'n';
2013         FAWKStr.RegExp := 'http://.+\.(2ch\.net|bbspink\.com)/';
2014         if FAWKStr.Match(FAWKStr.ProcessEscSeq(URL), RStart, RLength) <> 0 then begin
2015                 s := Copy(URL, RStart + RLength - 1, Length(URL));
2016
2017                 //\95W\8f\80\8f\91\8e®
2018                 //\8dÅ\8cã\82Íl50, 10, 10-20, 10n, 10-20n, -10, 10-, 10n- \82È\82Ç
2019                 //http://xxx.2ch.net/test/read.cgi/bbsid/1000000000/
2020                 FAWKStr.RegExp := '/test/read.cgi/.+/[0-9]+/?.*';
2021                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2022                         s := Copy(s, 15, Length(s));
2023
2024                         SList := TStringList.Create;
2025                         try
2026                                 SList.Clear;
2027                                 FAWKStr.RegExp := '/';
2028                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 3 then begin
2029                                         Result.FBBS := SList[1];
2030                                         Result.FKey := SList[2];
2031                                         if SList.Count >= 4 then
2032                                                 s := SList[3]
2033                                         else begin
2034                                                 s := '';
2035                                                 Result.FNoParam := true;
2036                                         end;
2037                                 end else
2038                                         Exit;
2039
2040                                 SList.Clear;
2041                                 FAWKStr.LineSeparator := mcls_CRLF;
2042                                 FAWKStr.RegExp := '-';
2043                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) = 0 then begin
2044                                         Result.FFirst := True;
2045                                 end else begin
2046                                         FAWKStr.RegExp := 'l[0-9]+';
2047                                         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2048                                                 Result.FFirst := True;
2049                                         end else begin
2050                                                 for i := 0 to SList.Count - 1 do begin
2051                                                         if Trim(SList[i]) = '' then begin
2052                                                                 if i = 0 then
2053                                                                         Result.FStBegin := True;
2054                                                                 if i = (SList.Count - 1) then
2055                                                                         Result.FToEnd := True;
2056                                                         end else if IsNumeric(SList[i]) then begin
2057                                                                 wkInt := StrToInt(SList[i]);
2058                                                                 wkMax := Max(wkMax, wkInt);
2059                                                                 if wkMin = 0 then
2060                                                                         wkMin := wkInt
2061                                                                 else
2062                                                                         wkMin := Min(wkMin, wkInt);
2063                                                         end else if Trim(SList[i]) = 'n' then begin
2064                                                                 Result.FFirst := True;
2065                                                         end else begin
2066                                                                 FAWKStr.RegExp := '^n[0-9]+$|^[0-9]+n$';
2067                                                                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(SList[i]), RStart, RLength) > 0 then begin
2068                                                                         if Copy(SList[i], 1, 1) = 'n' then
2069                                                                                 wkInt := StrToInt(Copy(SList[i], 2, Length(SList[i])))
2070                                                                         else
2071                                                                                 wkInt := StrToInt(Copy(SList[i], 1, Length(SList[i]) - 1));
2072                                                                         Result.FFirst := True;
2073                                                                         wkMax := Max(wkMax, wkInt);
2074                                                                         if wkMin = 1 then
2075                                                                                 wkMin := wkInt
2076                                                                         else
2077                                                                                 wkMin := Min(wkMin, wkInt);
2078                                                                 end;
2079                                                         end;
2080                                                 end;
2081                                                 if Result.FStBegin and (not Result.FToEnd) then
2082                                                         Result.FSt := wkMin
2083                                                 else if (not Result.FStBegin) and Result.FToEnd then
2084                                                         Result.FTo := wkMax
2085                                                 else if (not Result.FStBegin) and (not Result.FToEnd) then begin
2086                                                         Result.FSt := wkMin;
2087                                                         Result.FTo := wkMax;
2088                                                 end;
2089                                                 //Result.FSt := wkMin;
2090                                                 //Result.FTo := wkMax;
2091                                         end;
2092                                 end;
2093                         finally
2094                                 SList.Free;
2095                         end;
2096                         Result.FDone := True;
2097                         Exit;
2098                 end;
2099
2100                 //\90Vkako\8f\91\8e®
2101                 //http://server.2ch.net/ITA_NAME/kako/1000/10000/1000000000.html
2102                 FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+/[0-9]+\.html';
2103                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2104                         SList := TStringList.Create;
2105                         try
2106                                 SList.Clear;
2107                                 FAWKStr.RegExp := '/';
2108                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2109                                         Result.FBBS := SList[1];
2110                                         Result.FKey := ChangeFileExt(SList[5], '');
2111                                         Result.FFirst := True;
2112                                 end else
2113                                         Exit;
2114                         finally
2115                                 SList.Free;
2116                         end;
2117                         Result.FDone := True;
2118                         Exit;
2119                 end;
2120
2121                 //\8b\8ckako\8f\91\8e®
2122                 //http://server.2ch.net/ITA_NAME/kako/999/999999999.html
2123                 FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+\.html';
2124                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2125                         SList := TStringList.Create;
2126                         try
2127                                 SList.Clear;
2128                                 FAWKStr.RegExp := '/';
2129                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 5 then begin
2130                                         Result.FBBS := SList[1];
2131                                         Result.FKey := ChangeFileExt(SList[4], '');
2132                                         Result.FFirst := True;
2133                                 end else
2134                                         Exit;
2135                         finally
2136                                 SList.Free;
2137                         end;
2138                         Result.FDone := True;
2139                         Exit;
2140                 end;
2141
2142                 //log\8by\82Ñlog2\8f\91\8e®
2143                 //http://server.2ch.net/log/ITA_NAME/kako/999/999999999.html
2144                 //http://server.2ch.net/log2/ITA_NAME/kako/999/999999999.html
2145                 FAWKStr.RegExp := '/log2?/.+/kako/[0-9]+/[0-9]+\.html';
2146                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2147                         SList := TStringList.Create;
2148                         try
2149                                 SList.Clear;
2150                                 FAWKStr.RegExp := '/';
2151                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2152                                         Result.FBBS := SList[2];
2153                                         Result.FKey := ChangeFileExt(SList[5], '');
2154                                         Result.FFirst := True;
2155                                 end else
2156                                         Exit;
2157                         finally
2158                                 SList.Free;
2159                         end;
2160                         Result.FDone := True;
2161                         Exit;
2162                 end;
2163
2164
2165                 //\8b\8cURL\8f\91\8e®
2166                 //http://server.2ch.net/test/read.cgi?bbs=ITA_NAME&key=1000000000&st=1&to=5&nofirst=true
2167                 FAWKStr.RegExp := '/test/read\.cgi\?';
2168                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2169                         s := Copy(s, 16, Length(s));
2170                         SList := TStringList.Create;
2171                         try
2172                                 SList.Clear;
2173                                 FAWKStr.RegExp := '&';
2174                                 if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 2 then begin
2175                                         Result.FFirst := True;
2176                                         for i := 0 to SList.Count - 1 do begin
2177                                                 if Pos('bbs=', SList[i]) = 1 then begin
2178                                                         Result.FBBS := Copy(SList[i], 5, Length(SList[i]));
2179                                                 end else if Pos('key=', SList[i]) = 1 then begin
2180                                                         Result.FKey := Copy(SList[i], 5, Length(SList[i]));
2181                                                 end else if Pos('st=', SList[i]) = 1 then begin
2182                                                         wk := Copy(SList[i], 4, Length(SList[i]));
2183                                                         if IsNumeric(wk) then
2184                                                                 Result.FSt := StrToInt(wk)
2185                                                         else if wk = '' then
2186                                                                 Result.FStBegin := True;
2187                                                 end else if Pos('to=', SList[i]) = 1 then begin
2188                                                         wk := Copy(SList[i], 4, Length(SList[i]));
2189                                                         if IsNumeric(wk) then
2190                                                                 Result.FTo := StrToInt(wk)
2191                                                         else if wk = '' then
2192                                                                 Result.FToEnd := True;
2193                                                 end else if Pos('nofirst=', SList[i]) = 1 then begin
2194                                                         Result.FFirst := False;
2195                                                 end;
2196                                         end;
2197                                 end else
2198                                         Exit;
2199                         finally
2200                                 SList.Free;
2201                         end;
2202
2203                         if (Result.FBBS <> '') and (Result.FKey <> '') then begin
2204                                 Result.FDone := True;
2205                         end;
2206                         Exit;
2207                 end;
2208         end;
2209 end;
2210
2211 {!
2212 \brief URI \82ð\95ª\89ð
2213 \param URL      \95ª\89ð\82·\82é URI
2214 \param Protocol OUT:\83v\83\8d\83g\83R\83\8b\82ª\95Ô\82é(ex. http)
2215 \param Host     OUT:\83z\83X\83g\82ª\95Ô\82é(ex. hoge.com)
2216 \param Path     OUT:\92\86\8aÔ\83p\83X\82ª\95Ô\82é(ex. test/read.cgi)
2217 \param Document OUT:\83h\83L\83\85\83\81\83\93\83g\96¼\82ª\95Ô\82é(ex. index.html)
2218 \param Port     OUT:\83|\81[\83g\82ª\95Ô\82é(ex. 8080)
2219 \param Bookmark OUT:\83u\83b\83N\83}\81[\83N(?)\82ª\95Ô\82é
2220 }
2221 procedure TGikoSys.ParseURI(const URL : string; var Protocol, Host, Path, Document, Port, Bookmark: string);
2222 var
2223         URI: TIdURI;
2224 begin
2225         Protocol := '';
2226         Host := '';
2227         Path := '';
2228         Document := '';
2229         Port := '';
2230         Bookmark := '';
2231         URI := TIdURI.Create(URL);
2232         try
2233                 Protocol := URI.Protocol;
2234                 Host := URI.Host;
2235                 Path := URI.Path;
2236                 Document := URI.Document;
2237                 Port := URI.Port;
2238                 Bookmark := URI.Bookmark;
2239         finally
2240                 URI.Free;
2241         end;
2242 end;
2243
2244 {!
2245 \brief \83M\83R\83i\83r\82Ì\83o\81[\83W\83\87\83\93\82ð\8eæ\93¾
2246 \return \83o\81[\83W\83\87\83\93\82Ì\89º 2 \8c\85(dwFileVersionLS)
2247 }
2248 function TGikoSys.GetVersionBuild: Integer;
2249 var
2250         FixedFileInfo: PVSFixedFileInfo;
2251         VersionHandle, VersionSize: DWORD;
2252         pVersionInfo: Pointer;
2253         ItemLen : UInt;
2254         AppFile: string;
2255 begin
2256         Result := 0;
2257         AppFile := Application.ExeName;
2258         VersionSize := GetFileVersionInfoSize(pChar(AppFile), VersionHandle);
2259         if VersionSize = 0 then
2260                 Exit;
2261         GetMem(pVersionInfo, VersionSize);
2262         try
2263                 if GetFileVersionInfo(PChar(AppFile),VersionHandle,VersionSize, pVersionInfo) then
2264                         if VerQueryValue(pVersionInfo, '\', Pointer(FixedFileInfo), ItemLen) then
2265                                 Result := LOWORD(FixedFileInfo^.dwFileVersionLS);
2266         finally
2267                 FreeMem(pVersionInfo, VersionSize);
2268         end;
2269 end;
2270
2271 {!
2272 \brief \83X\83\8c\83b\83h URL \82Ì\90³\8bK\89»
2273 \param inURL \90³\8bK\89»\82·\82é\83X\83\8c\83b\83h URL
2274 \return      \90³\8bK\89»\82³\82ê\82½\83X\83\8c\83b\83h URL
2275
2276 \83X\83\8c\83b\83h URL \82ð\83M\83R\83i\83r\82Ì\92\86\82Å\88ê\88Ó\82È\82à\82Ì\82É\90³\8bK\89»\82µ\82Ü\82·\81B
2277 \88ê\88Ó\82È URL \82É\82·\82é\8e\96\82Å\81AURL \82©\82ç\83X\83\8c\83b\83h\82ð\93±\82«\8fo\82·\8dì\8bÆ\82ð\8dÅ\93K\89»\82µ\82Ü\82·\81B\n
2278 \90³\8bK\89»\82Ì\95û\90j\82Æ\82µ\82Ä\81A\83T\83C\83g\82ª\90\84\8f§\82·\82é\83f\83t\83H\83\8b\83g\82Ì URL \82É\82È\82é\82æ\82¤\82É\90S\82ª\82¯\82Ü\82·\81B
2279 (1-1000 \82Ì\82æ\82¤\82È\95\89\89×\82ð\82©\82¯\82é\82à\82Ì\82É\82Í\82µ\82È\82¢\82±\82Æ)
2280
2281 \97á(\90³\8bK\89»\91O):\n
2282 http://\92\86\97ª/ \n
2283 http://\92\86\97ª/20-100
2284
2285 (\90³\8bK\89»\8cã):\n
2286 http://\92\86\97ª/l50
2287 }
2288 function        TGikoSys.GetBrowsableThreadURL(
2289         inURL : string
2290 ) : string;
2291 var
2292         threadItem      : TThreadItem;
2293         boardPlugIn     : TBoardPlugIn;
2294     board               : TBoard;
2295         i                                               : Integer;
2296 begin
2297
2298         //===== \83v\83\89\83O\83C\83\93
2299         try
2300                 for i := Length( BoardGroups ) - 1 downto 1 do begin
2301                         if Assigned( Pointer( BoardGroups[i].BoardPlugIn.Module ) ) then begin
2302                                 if BoardGroups[i].BoardPlugIn.AcceptURL( inURL ) = atThread then begin
2303                     board := BBSsFindBoardFromURL( BoardGroups[i].BoardPlugIn.ExtractBoardURL(inURL) );
2304                                         if board <> nil then begin
2305                                                 boardPlugIn := BoardGroups[i].BoardPlugIn;
2306                                                 threadItem      := TThreadItem.Create( boardPlugIn, board, inURL );
2307                                                 Result                  := threadItem.URL;
2308                                                 threadItem.Free;
2309
2310                                         end;
2311                                         Exit;
2312                                 end;
2313                         end;
2314                 end;
2315         except
2316                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2317         end;
2318
2319         if Length( Result ) = 0 then
2320                 Result := GikoSys.Get2chBrowsableThreadURL( inURL );
2321
2322 end;
2323
2324 {!
2325 \brief \83X\83\8c\83b\83h URL \82ð\94 URL \82É\95Ï\8a·
2326 \param inURL \83X\83\8c\83b\83h URL
2327 \return      \94 URL
2328 }
2329 function        TGikoSys.GetThreadURL2BoardURL(
2330         inURL : string
2331 ) : string;
2332 var
2333         threadItem      : TThreadItem;
2334         boardPlugIn     : TBoardPlugIn;
2335     board               : TBoard;
2336         i                                               : Integer;
2337 begin
2338
2339         //===== \83v\83\89\83O\83C\83\93
2340         try
2341                 for i := Length( BoardGroups ) - 1 downto 1 do begin
2342                         if Assigned( Pointer( BoardGroups[i].BoardPlugIn.Module ) ) then begin
2343                                 if BoardGroups[i].BoardPlugIn.AcceptURL( inURL ) = atThread then begin
2344                     board               := BBSsFindBoardFromURL(BoardGroups[i].BoardPlugIn.ExtractBoardURL(inURL));
2345                                         boardPlugIn := BoardGroups[i].BoardPlugIn;
2346                                         threadItem      := TThreadItem.Create( boardPlugIn, board, inURL );
2347                                         Result                  := BoardGroups[i].BoardPlugIn.GetBoardURL( Longword( threadItem ) );
2348                                         threadItem.Free;
2349
2350                                         Break;
2351                                 end;
2352                         end;
2353                 end;
2354         except
2355                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2356         end;
2357
2358         if Length( Result ) = 0 then
2359                 Result := GikoSys.Get2chThreadURL2BoardURL( inURL );
2360
2361 end;
2362
2363 {!
2364 \brief 2ch\97p:\83X\83\8c\83b\83h URL \82ð\94 URL \82É\95Ï\8a·
2365 \param inURL \83X\83\8c\83b\83h URL
2366 \return      \94 URL
2367 \see TGikoSys.GetThreadURL2BoardURL
2368 }
2369 function        TGikoSys.Get2chThreadURL2BoardURL(
2370         inURL : string
2371 ) : string;
2372 var
2373         Protocol, Host, Path, Document, Port, Bookmark : string;
2374         BBSID, BBSKey : string;
2375         foundPos                        : Integer;
2376 begin
2377
2378         ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
2379         Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
2380
2381         foundPos := Pos( '/test/read.cgi', inURL );
2382         if {(Is2chHost(Host)) and} (foundPos > 0) then
2383                 Result := Copy( inURL, 1, foundPos ) + BBSID + '/'
2384         else
2385                 Result := Protocol + '://' + Host + '/' + BBSID + '/';
2386
2387 end;
2388
2389 {!
2390 \brief 2ch\97p:\83X\83\8c\83b\83h URL \82Ì\90³\8bK\89»
2391 \param inURL \90³\8bK\89»\82·\82é\83X\83\8c\83b\83h URL
2392 \return      \90³\8bK\89»\82³\82ê\82½\83X\83\8c\83b\83h URL
2393 \see TGikoSys.GetBrowsableThreadURL
2394 }
2395 function        TGikoSys.Get2chBrowsableThreadURL(
2396         inURL                   : string
2397 ) : string;
2398 var
2399         Protocol, Host, Path, Document, Port, Bookmark : string;
2400         BBSID, BBSKey : string;
2401         foundPos        : Integer;
2402 begin
2403
2404 //      if Pos( KAKO_PATH, inURL ) > 0 then begin
2405 //              Result := inURL;
2406 //      end else begin
2407                 ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
2408                 Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
2409                 foundPos := Pos( '/test/read.cgi', inURL ) - 1;
2410
2411                 if Is2chHost( Host ) then begin
2412                         Result := Protocol + '://' + Host +
2413                                 READ_PATH + BBSID + '/' + BBSKey + '/l50';
2414                 end else begin
2415                         if foundPos > 0 then
2416                                 Result := Copy( inURL, 1, foundPos ) +
2417                                         OLD_READ_PATH + 'bbs=' + BBSID + '&key=' + BBSKey + '&ls=50'
2418                         else
2419                                 Result := Protocol + '://' + Host +
2420                                         OLD_READ_PATH + 'bbs=' + BBSID + '&key=' + BBSKey + '&ls=50';
2421                 end;
2422 //      end;
2423
2424 end;
2425
2426 {!
2427 \brief 2ch\97p:\94 URL \82©\82ç\83X\83\8c\83b\83h URL \82ð\8dì\90¬
2428 \param inBoard \94 URL
2429 \param inKey   \83X\83\8c\83b\83h\83L\81[(ex. 1000000000)
2430 \return        \83X\83\8c\83b\83h URL
2431 }
2432 function        TGikoSys.Get2chBoard2ThreadURL(
2433         inBoard : TBoard;
2434         inKey           : string
2435 ) : string;
2436 var
2437         server  : string;
2438 begin
2439
2440         server := UrlToServer( inBoard.URL );
2441         //if Is2chHost( server ) then
2442         if inBoard.Is2ch then
2443                 Result := server + 'test/read.cgi/' + inBoard.BBSID + '/' + inKey + '/l50'
2444         else
2445                 Result := server + 'test/read.cgi?bbs=' + inBoard.BBSID + '&key=' + inKey + '&ls=50';
2446
2447 end;
2448
2449 {!
2450 \brief \83{\81[\83h\83t\83@\83C\83\8b\97ñ\8b\93
2451
2452 \97ñ\8b\93\82³\82ê\82½ BBS(\83{\81[\83h) \82Í BBSs \82É\93ü\82è\82Ü\82·\81B
2453 }
2454 procedure TGikoSys.ListBoardFile;
2455 var
2456         boardFileList   : TStringList;
2457         i, l                    : Integer;
2458 begin
2459         // BBS \82Ì\8aJ\95ú
2460         try
2461           for i := 0 to Length( BBSs ) - 1 do
2462                 BBSs[ i ].Free;
2463         except
2464         end;
2465         SetLength( BBSs, 0 );
2466
2467         l := 0;
2468         // \94Â\83\8a\83X\83g\82Ì\97ñ\8b\93
2469         if FileExists( GikoSys.GetBoardFileName ) then begin
2470           SetLength( BBSs, l + 1 );
2471           BBSs[ l ]                             := TBBS.Create( GikoSys.GetBoardFileName );
2472           BBSs[ l ].Title       := '\82Q\82¿\82á\82ñ\82Ë\82é';
2473                   Inc( l );
2474         end;
2475
2476         if FileExists( GikoSys.GetCustomBoardFileName ) then begin
2477           SetLength( BBSs, l + 1 );
2478           BBSs[ l ]                             := TBBS.Create( GikoSys.GetCustomBoardFileName );
2479           BBSs[ l ].Title       := '\82»\82Ì\91¼';
2480                   Inc( l );
2481         end;
2482
2483         // Board \83t\83H\83\8b\83_
2484         if DirectoryExists( GikoSys.Setting.GetBoardDir ) then begin
2485           BoardFileList := TStringList.Create;
2486           try
2487         BoardFileList.BeginUpdate;
2488                 GikoSys.GetFileList( GikoSys.Setting.GetBoardDir, '*.txt', BoardFileList, True, True );
2489         BoardFileList.EndUpdate;
2490         SetLength( BBSs, l + BoardFileList.Count );
2491                 for i := BoardFileList.Count - 1 downto 0 do begin
2492                   BBSs[ l ]                             := TBBS.Create( BoardFileList[ i ] );
2493                   BBSs[ l ].Title       := ChangeFileExt( ExtractFileName( BoardFileList[ i ] ), '' );
2494                   Inc( l );
2495                 end;
2496           finally
2497                 BoardFileList.Free;
2498           end;
2499         end;
2500 end;
2501
2502 {!
2503 \brief \83{\81[\83h\83t\83@\83C\83\8b\93Ç\82Ý\8d\9e\82Ý
2504 \param bbs \83{\81[\83h\83t\83@\83C\83\8b\82ð\93Ç\82Ý\8d\9e\82Þ BBS
2505 }
2506 procedure TGikoSys.ReadBoardFile( bbs : TBBS );
2507 var
2508 //      idx                                             : Integer;
2509         ini                                             : TMemIniFile;
2510         p : Integer;
2511         boardFile                       : TStringList;
2512         CategoryList    : TStringList;
2513         BoardList                       : TStringList;
2514         Category                        : TCategory;
2515         Board                                   : TBoard;
2516         inistr                          : string;
2517         tmpstring                       : string;
2518 //      RoundItem                       : TRoundItem;
2519
2520         i, iBound                       : Integer;
2521         j, jBound                       : Integer;
2522         k, kBound                       : Integer;
2523 begin
2524
2525         if not FileExists( bbs.FilePath ) then
2526                 Exit;
2527
2528         bbs.Clear;
2529         ini := TMemIniFile.Create('');
2530         boardFile := TStringList.Create;
2531
2532         try
2533                 boardFile.LoadFromFile( bbs.FilePath );
2534
2535                 ini.SetStrings( boardFile );
2536                 CategoryList    := TStringList.Create;
2537                 BoardList                       := TStringList.Create;
2538                 try
2539                         ini.ReadSections( CategoryList );
2540
2541                         iBound := CategoryList.Count - 1;
2542                         for i := 0 to iBound do begin
2543                                 ini.ReadSection( CategoryList[i], BoardList );
2544                                 Category                                := TCategory.Create;
2545                                 Category.No                     := i + 1;
2546                                 Category.Title  := CategoryList[i];
2547
2548                                 jBound := BoardList.Count - 1;
2549                                 for j := 0 to jBound do begin
2550                                         Board := nil;
2551                                         inistr := ini.ReadString(CategoryList[i], BoardList[j], '');
2552                                         //'http://'\82ð\8aÜ\82Ü\82È\82¢\95\8e\9a\97ñ\82Ì\8e\9e\82Í\96³\8e\8b\82·\82é
2553                                         if (AnsiPos('http://', AnsiLowerCase(inistr)) = 0) then Continue;
2554                                         //===== \83v\83\89\83O\83C\83\93
2555                                         try
2556                                                 kBound := Length(BoardGroups) - 1;
2557                                                 for k := 1 to kBound do begin  //0\82Í\81A2\82¿\82á\82ñ
2558                                                         if Assigned( Pointer( BoardGroups[k].BoardPlugIn.Module ) ) then begin
2559                                                                 if BoardGroups[k].BoardPlugIn.AcceptURL( inistr ) = atBoard then begin
2560                                                                         if not BoardGroups[k].Find(inistr, p) then begin
2561                                                                                 tmpstring := BoardGroups[k].BoardPlugIn.ExtractBoardURL( inistr );
2562                                                                                 if AnsiCompareStr(tmpString, inistr) <> 0 then begin
2563                                                                                         if not BoardGroups[k].Find(tmpstring, p) then begin
2564                                                                                                 try
2565                                                                                                         Board := TBoard.Create( BoardGroups[k].BoardPlugIn, tmpstring );
2566                                                                                                         BoardGroups[k].AddObject(tmpstring, Board);
2567                                                                                                         Category.Add(Board);
2568                                                                                                 except
2569                                                                                                         //\82±\82±\82É\97\88\82é\82Æ\82µ\82½\82çBoard\82Ì\8dì\90¬\82É\8e¸\94s\82µ\82½\82Æ\82«\82¾\82©\82çBoard\82ðnil\82É\82·\82é
2570                                                                                                         Board := nil;
2571                                                                                                 end;
2572                                                                                         end else begin
2573                                                                                                 Board := TBoard(BoardGroups[k].Objects[p]);
2574                                                                                                 if Board.ParentCategory <> Category then
2575                                                                                                         Category.Add(Board);
2576                                                                                         end;
2577                                                                                 end else begin
2578                                                                                         try
2579                                                                                                 Board := TBoard.Create( BoardGroups[k].BoardPlugIn, tmpstring );
2580                                                                                                 BoardGroups[k].AddObject(tmpstring, Board);
2581                                                                                                 Category.Add(Board);
2582                                                                                         except
2583                                                                                                 //\82±\82±\82É\97\88\82é\82Æ\82µ\82½\82çBoard\82Ì\8dì\90¬\82É\8e¸\94s\82µ\82½\82Æ\82«\82¾\82©\82çBoard\82ðnil\82É\82·\82é
2584                                                                                                 Board := nil;
2585                                                                                         end;
2586                                                                                 end;
2587                                                                         end else begin
2588                                                                                 Board := TBoard(BoardGroups[k].Objects[p]);
2589                                                                                 if Board.ParentCategory <> Category then
2590                                                                                         Category.Add(Board);
2591                                                                         end;
2592                                                                         Break;
2593                                                                 end;
2594                                                         end;
2595                                                 end;
2596                                         except
2597                                                 // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
2598                                         end;
2599                                         try
2600                                                 if (Board = nil) then begin
2601                                                         if not BoardGroups[0].Find(inistr,p) then begin
2602                                                                 Board := TBoard.Create( nil, inistr );
2603                                                                 BoardGroups[0].AddObject(inistr, Board);
2604                                                                 Category.Add(Board);
2605                                                         end else begin
2606                                                                 Board := TBoard(BoardGroups[0].Objects[p]);
2607                                                                 if Board.ParentCategory <> Category then
2608                                                                         Category.Add(Board);
2609                                                         end;
2610                                                 end;
2611
2612                                                 if (Board.Multiplicity = 0) then begin
2613                                                         Board.BeginUpdate;
2614                                                         Board.No := j + 1;
2615                             Board.Multiplicity := 1;
2616                                                         Board.Title := BoardList[j];
2617                                                         Board.RoundDate := ZERO_DATE;
2618                                                         Board.LoadSettings;
2619                                                         Board.EndUpdate;
2620                                                 end else begin
2621                                                         Board.No := j + 1;
2622                                                         Board.Multiplicity := Board.Multiplicity + 1;
2623                                                 end;
2624                                         except
2625                                         end;
2626                                 end;
2627                                 bbs.Add( Category );
2628                         end;
2629
2630
2631                   //end;
2632                   bbs.IsBoardFileRead := True;
2633           finally
2634                 BoardList.Free;
2635                 CategoryList.Free;
2636           end;
2637   finally
2638         boardFile.Free;
2639         ini.Free;
2640   end;
2641
2642 end;
2643
2644 {!
2645 \brief \96¼\8fÌ\82ª\95s\96¾\82È\83J\83e\83S\83\8a\82Ì\90\90¬
2646 \return \90\90¬\82³\82ê\82½\83J\83e\83S\83\8a
2647 }
2648 function        TGikoSys.GetUnknownCategory : TCategory;
2649 const
2650         UNKNOWN_CATEGORY = '(\96¼\8fÌ\95s\96¾)';
2651 begin
2652
2653         if Length( BBSs ) < 2 then begin
2654                 Result := nil;
2655                 Exit;
2656         end;
2657
2658         Result := BBSs[ 1 ].FindCategoryFromTitle( UNKNOWN_CATEGORY );
2659         if Result = nil then begin
2660                 Result                          := TCategory.Create;
2661                 Result.Title    := UNKNOWN_CATEGORY;
2662                 BBSs[ 1 ].Add( Result );
2663         end;
2664
2665 end;
2666
2667 {!
2668 \brief \96¼\8fÌ\82ª\95s\96¾\82È BBS \82Ì\90\90¬
2669 \return \90\90¬\82³\82ê\82½ BBS
2670 }
2671 function        TGikoSys.GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
2672 var
2673         category : TCategory;
2674 const
2675         UNKNOWN_BOARD = '(\96¼\8fÌ\95s\96¾)';
2676 begin
2677
2678         category := GetUnknownCategory;
2679         if category = nil then begin
2680                 Result := nil;
2681         end else begin
2682                 Result := category.FindBoardFromTitle( UNKNOWN_BOARD );
2683                 if Result = nil then begin
2684                         Result                          := TBoard.Create( inPlugIn, inURL );
2685                         Result.Title    := UNKNOWN_BOARD;
2686                         category.Add( Result );
2687                 end;
2688         end;
2689
2690 end;
2691
2692 //! Samba.ini
2693 function TGikoSys.GetSambaFileName : string;
2694 begin
2695         Result := Setting.GetSambaFileName;
2696 end;
2697
2698 {!
2699 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\83A\83\93\83J\81[\82É\82µ\82Ä\97ñ\8b\93
2700 \param AID        \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2701 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2702 \param limited    \97ñ\8b\93\82·\82é\90\94\82ð\90§\8cÀ\82·\82é\82È\82ç1\88È\8fã
2703 \return           \97ñ\8b\93\82³\82ê\82½\83\8c\83X\83A\83\93\83J\81[
2704 }
2705 function TGikoSys.GetSameIDResAnchor(
2706     const AID : string; ThreadItem: TThreadItem;
2707     limited: Integer):string;
2708 var
2709         i: integer;
2710         body: TStringList;
2711     Res: TResRec;
2712     ResLink : TResLinkRec;
2713 begin
2714     // body\88È\8aO\82Í\8eg\97p\82µ\82È\82¢\82Ì\82Å\8f\89\8aú\89»\82µ\82È\82¢
2715     Res.FBody := '';
2716     Res.FType := glt2chNew;
2717
2718                 Result := '';
2719                 if (not IsNoValidID(AID)) and
2720                         (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2721                                 body := TStringList.Create;
2722                                 try
2723                                                 GetSameIDRes(AID, ThreadItem, body);
2724                                                 if (limited > 0) and (body.Count > limited) then begin
2725                                                                 for i := body.Count - limited to body.Count - 1 do begin
2726                                                                                 Res.FBody := Res.FBody + '&gt;' + body[i] + ' ';
2727                                                                 end;
2728                                                 end else begin
2729                                                                 for i := 0 to body.Count - 1 do begin
2730                                                                                 Res.FBody := Res.FBody + '&gt;' + body[i] + ' ';
2731                                                                 end;
2732                                                 end;
2733                                 finally
2734                                                 body.Free;
2735                                 end;
2736         ResLink.FBbs := ThreadItem.ParentBoard.BBSID;
2737         ResLink.FKey := ChangeFileExt(ThreadItem.FileName, '');
2738         HTMLCreater.ConvRes(@Res, @ResLink, false);
2739         Result := Res.FBody;
2740                 end;
2741 end;
2742
2743 {!
2744 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\97ñ\8b\93
2745 \param AID        \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2746 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2747 \param body       OUT:\97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86\82ª\95Ô\82é
2748 }
2749 procedure TGikoSys.GetSameIDRes(const AID : string; ThreadItem: TThreadItem;var body: TStringList);
2750 var
2751         i: integer;
2752         ReadList: TStringList;
2753         Res: TResRec;
2754         boardPlugIn : TBoardPlugIn;
2755 begin
2756         if (not IsNoValidID(AID)) and
2757         (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2758                 //if ThreadItem.IsBoardPlugInAvailable then begin
2759         if ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
2760                         //===== \83v\83\89\83O\83C\83\93\82É\82æ\82é\95\\8e¦
2761                         //boardPlugIn           := ThreadItem.BoardPlugIn;
2762             boardPlugIn         := ThreadItem.ParentBoard.BoardPlugIn;
2763
2764                         for i := 0 to threadItem.Count - 1 do begin
2765                                 // \83\8c\83X
2766                                 THTMLCreate.DivideStrLine(boardPlugIn.GetDat(DWORD( threadItem ), i + 1), @Res);
2767                                 if(AnsiPos(AID, Res.FDateTime) > 0) then begin
2768                                         body.Add(IntToStr(i+1));
2769                                 end;
2770                         end;
2771                 end else begin
2772                         ReadList := TStringList.Create;
2773                         try
2774                                 ReadList.LoadFromFile(ThreadItem.GetThreadFileName);
2775                                 for i := 0 to ReadList.Count - 1 do begin
2776                                         THTMLCreate.DivideStrLine(ReadList[i], @Res);
2777                                         if AnsiPos(AID, Res.FDateTime) > 0 then begin
2778                                                 body.Add(IntToStr(i+1));
2779                                         end;
2780                                 end;
2781                         finally
2782                                 ReadList.Free;
2783                         end;
2784                 end;
2785         end;
2786 end;
2787
2788 {!
2789 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\97ñ\8b\93
2790 \param AIDNum     \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2791 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2792 \param limited    \97ñ\8b\93\82·\82é\90\94\82ð\90§\8cÀ\82·\82é\82È\82ç1\88È\8fã
2793 \return
2794 }
2795 function TGikoSys.GetSameIDResAnchor(AIDNum : Integer;
2796      ThreadItem: TThreadItem;
2797      limited: Integer):string;
2798 var
2799         i: integer;
2800         body: TStringList;
2801     Res: TResRec;
2802     ResLink : TResLinkRec;
2803 begin
2804     // body\88È\8aO\82Í\8eg\97p\82µ\82È\82¢\82Ì\82Å\8f\89\8aú\89»\82µ\82È\82¢
2805     Res.FBody := '';
2806     Res.FType := glt2chNew;
2807
2808         Result := '';
2809         if (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2810                 body := TStringList.Create;
2811                 try
2812                         GetSameIDRes(AIDNum, ThreadItem, body);
2813             if (limited > 0) and ( body.Count > limited) then begin
2814                 for i := body.Count - 20 to body.Count - 1 do begin
2815                     Res.FBody := Res.FBody + '&gt;' + body[i] + ' ';
2816                 end;
2817             end else begin
2818                         for i := 0 to body.Count - 1 do begin
2819                                 Res.FBody := Res.FBody + '&gt;' + body[i] + ' ';
2820                         end;
2821             end;
2822                 finally
2823                         body.Free;
2824                 end;
2825         ResLink.FBbs := ThreadItem.ParentBoard.BBSID;
2826         ResLink.FKey := ChangeFileExt(ThreadItem.FileName, '');
2827         HTMLCreater.ConvRes(@Res, @ResLink, false);
2828         Result := Res.FBody;
2829         end;
2830 end;
2831
2832 {!
2833 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\97ñ\8b\93
2834 \param AIDNum     \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2835 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2836 \param body       OUT:\97ñ\8b\93\82³\82ê\82½\83\8c\83X\94Ô\8d\86\82ª\95Ô\82é
2837 }
2838 procedure TGikoSys.GetSameIDRes(AIDNum : Integer; ThreadItem: TThreadItem;var body: TStringList);
2839 var
2840         Res: TResRec;
2841         boardPlugIn : TBoardPlugIn;
2842         AID : String;
2843         stList: TStringList;
2844         i : Integer;
2845 begin
2846         if (ThreadItem <> nil) and (ThreadItem.IsLogFile)
2847                 and (AIDNum > 0) and (AIDNum <= ThreadItem.Count) then begin
2848                 //if ThreadItem.IsBoardPlugInAvailable then begin
2849         if ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
2850                         //===== \83v\83\89\83O\83C\83\93\82É\82æ\82é\95\\8e¦
2851                         //boardPlugIn           := ThreadItem.BoardPlugIn;
2852             boardPlugIn         := ThreadItem.ParentBoard.BoardPlugIn;
2853                         THTMLCreate.DivideStrLine(boardPlugIn.GetDat(DWORD( threadItem ), AIDNum), @Res);
2854                 end else begin
2855                         THTMLCreate.DivideStrLine( ReadThreadFile(ThreadItem.GetThreadFileName, AIDNum), @Res);
2856                 end;
2857                 AID := Res.FDateTime;
2858                 if AnsiPos('id', AnsiLowerCase(AID)) > 0 then begin
2859                         AID := Copy(AID, AnsiPos('id', AnsiLowerCase(AID)) - 1, 11);
2860             if AnsiPos(' be:', AnsiLowerCase(AID)) > 0 then begin
2861                 AID := Copy(AID, 1, AnsiPos(' BE:', AnsiLowerCase(AID)) - 1)
2862             end;
2863                 end else begin
2864                         stlist := TStringList.Create;
2865                         try
2866                                 stList.DelimitedText := AID;
2867                 AID := '';
2868                                 for i := 0 to stList.Count - 1 do
2869                                         if Length(WideString(stList[i])) = 8 then begin
2870                                                 if NotDateorTimeString(stList[i]) then begin
2871                                                         AID := stList[i];
2872                                                         break;
2873                                                 end;
2874                                         end;
2875                         finally
2876                                 stList.Free;
2877                         end;
2878                 end;
2879         if not IsNoValidID(AID) then
2880                         GetSameIDRes(AID, ThreadItem, body);
2881         end;
2882 end;
2883
2884 {!
2885 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\83J\83E\83\93\83g
2886 \param AID        \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2887 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2888 \return           \93¯\82¶ ID \82ð\8e\9d\82Â\83\8c\83X\82Ì\90\94
2889 }
2890 function TGikoSys.GetSameIDResCount(const AID : string; ThreadItem: TThreadItem):Integer;
2891 var
2892         body: TStringList;
2893 begin
2894     Result := 0;
2895         if (not IsNoValidID(AID))
2896      and (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2897                 body := TStringList.Create;
2898                 try
2899                         GetSameIDRes(AID, ThreadItem, body);
2900                         Result := body.Count;
2901                 finally
2902                         body.Free;
2903                 end;
2904         end;
2905
2906 end;
2907
2908 {!
2909 \brief \93¯\82\93\8a\8de ID \82ð\8e\9d\82Â\83\8c\83X\82ð\83J\83E\83\93\83g
2910 \param AIDNum     \8cÂ\90l\82ð\93Á\92è\82·\82é\93\8a\8de ID
2911 \param ThreadItem \97ñ\8b\93\82·\82é\83X\83\8c\83b\83h
2912 \return           \93¯\82¶ ID \82ð\8e\9d\82Â\83\8c\83X\82Ì\90\94
2913 }
2914 function TGikoSys.GetSameIDResCount(AIDNum : Integer; ThreadItem: TThreadItem):Integer;
2915 var
2916         body: TStringList;
2917 begin
2918         Result := 0;
2919         if (ThreadItem <> nil) and (ThreadItem.IsLogFile) then begin
2920                 body := TStringList.Create;
2921                 try
2922                         GetSameIDRes(AIDNum, ThreadItem, body);
2923             Result := body.Count;
2924                 finally
2925                         body.Free;
2926                 end;
2927         end;
2928 end;
2929
2930 {!
2931 \brief \8e\9e\8d\8f\82ð\8e¦\82·\95\8e\9a\97ñ\82Å\82Í\96³\82¢\82©\82Ç\82¤\82©
2932 \param AStr \92²\82×\82é\95\8e\9a\97ñ
2933 \return     \8e\9e\8d\8f\82Å\82Í\96³\82¢\82È\82ç True
2934 \todo \94Û\92è\8c`(Not)\82æ\82è\8dm\92è\8cn(Is)
2935 }
2936 function TGikoSys.NotDateorTimeString(const AStr : string): boolean;
2937 begin
2938         Result := false;
2939         try
2940                 StrToDate(AStr);
2941         except
2942                 try
2943                         StrToTime(AStr);
2944                         Result := false;
2945                 except
2946                         Result := true;
2947                 end;
2948         end;
2949
2950 end;
2951
2952 {!
2953 \brief \83X\83p\83\80:\8cê\90\94\82ð\83J\83E\83\93\83g
2954 \param text      \8c³\82É\82È\82é\95\8fÍ
2955 \param wordCount OUT:\83J\83E\83\93\83g\82³\82ê\82½\92P\8cê\82Ì\88ê\97\97\82ª\95Ô\82é
2956 }
2957 procedure TGikoSys.SpamCountWord( const text : string; wordCount : TWordCount );
2958 begin
2959
2960         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2961         Bayesian.CountWord( text, wordCount );
2962
2963 end;
2964
2965 {!
2966 \brief \83X\83p\83\80:\8aw\8fK\8c\8b\89Ê\82ð\95ú\8aü
2967 \param wordCount \95ú\8aü\82·\82é\92P\8cê\82Ì\88ê\97\97
2968 \param isSpam    wordCount \82ª\83X\83p\83\80\82Æ\82µ\82Ä\8aw\8fK\82³\82ê\82Ä\82¢\82½\82È\82ç True
2969 \warning        \8aw\8fK\8dÏ\82Ý\82Ì\95\8fÍ\82©\82Ç\82¤\82©\82Í\8am\94F\8fo\97\88\82Ü\82¹\82ñ\81B\n
2970                                         Learn \82µ\82Ä\82¢\82È\82¢\95\8fÍ\82â isSpam \82ð\8aÔ\88á\82¦\82Ä\8ew\92è\82·\82é\82Æ
2971                                         \83f\81[\83^\83x\81[\83X\82ª\94j\91¹\82µ\82Ü\82·\81B\n
2972                                         \8aw\8fK\8dÏ\82Ý\82©\82Ç\82¤\82©\82Í\93Æ\8e©\82É\8aÇ\97\9d\82µ\82Ä\82­\82¾\82³\82¢\81B
2973
2974 \91S\82Ä\82Ì\8aw\8fK\8c\8b\89Ê\82ð\83N\83\8a\83A\82·\82é\82í\82¯\82Å\82Í\82 \82è\82Ü\82¹\82ñ\81B\n
2975 wordCount \82ð\93¾\82½\95\8fÍ\82Ì\8aw\8fK\8c\8b\89Ê\82Ì\82Ý\83N\83\8a\83A\82µ\82Ü\82·\81B
2976
2977 \8eå\82É\83X\83p\83\80\82Æ\83n\83\80\82ð\90Ø\82è\91Ö\82¦\82é\82½\82ß\82É Forget -> Learn \82Ì\8f\87\82Å\8eg\97p\82µ\82Ü\82·\81B
2978 }
2979 procedure TGikoSys.SpamForget( wordCount : TWordCount; isSpam : Boolean );
2980 begin
2981
2982         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2983         Bayesian.Forget( wordCount, isSpam );
2984
2985 end;
2986
2987 {!
2988 \brief \83X\83p\83\80:\8aw\8fK
2989 \param wordCount \8aw\8fK\82·\82é\92P\8cê\82Ì\88ê\97\97
2990 \param isSpam    \83X\83p\83\80\82Æ\82µ\82Ä\8aw\8fK\82·\82é\82È\82ç True
2991 }
2992 procedure TGikoSys.SpamLearn( wordCount : TWordCount; isSpam : Boolean );
2993 begin
2994
2995         if Setting.SpamFilterAlgorithm = gsfaNone then Exit;
2996         Bayesian.Learn( wordCount, isSpam );
2997
2998 end;
2999
3000 {!
3001 \brief \83X\83p\83\80:\95\8fÍ\82ð\89ð\90Í\82µ\81A\83X\83p\83\80\93x\90\94\82ð\93¾\82é
3002 \param text      \8c³\82É\82È\82é\95\8fÍ
3003 \param wordCount OUT:\83J\83E\83\93\83g\82³\82ê\82½\92P\8cê\82Ì\88ê\97\97\82ª\95Ô\82é(SpamCountWord \82Æ\93¯\93\99)
3004 \return          0\81`1 \82Ì\83X\83p\83\80\93x\90\94
3005 }
3006 function TGikoSys.SpamParse( const text : string; wordCount : TWordCount ) : Extended;
3007 begin
3008
3009         case Setting.SpamFilterAlgorithm of
3010         gsfaNone:                                                               Result := 0;
3011         gsfaPaulGraham:                                 Result := Bayesian.Parse( text, wordCount, gbaPaulGraham );
3012         gsfaGaryRobinson:                               Result := Bayesian.Parse( text, wordCount, gbaGaryRobinson );
3013         gsfaGaryRobinsonFisher: Result := Bayesian.Parse( text, wordCount, gbaGaryRobinsonFisher );
3014         else                                                                            Result := 0;
3015         end;
3016
3017 end;
3018
3019 {!
3020 \brief \83\86\81[\83U\90Ý\92è\82Ì CSS \82ð\90\90¬
3021 \return \90\90¬\82³\82ê\82½ CSS
3022
3023 [\83c\81[\83\8b]\83\81\83j\83\85\81[-[\83I\83v\83V\83\87\83\93]-[CSS \82Æ\83X\83L\83\93]\83^\83u\82Ì
3024 [\83t\83H\83\93\83g\82ð\8ew\92è], [\94w\8ci\90F\82ð\8ew\92è] \82É\89\88\82Á\82½ CSS \82ð\90\90¬\82µ\82Ü\82·\81B
3025 }
3026 function TGikoSys.SetUserOptionalStyle(): string;
3027 begin
3028                 Result := '';
3029         if Length( GikoSys.Setting.BrowserFontName ) > 0 then
3030                 Result := 'font-family:"' + GikoSys.Setting.BrowserFontName + '";';
3031         if GikoSys.Setting.BrowserFontSize <> 0 then
3032                 Result := Result + 'font-size:' + IntToStr( GikoSys.Setting.BrowserFontSize ) + 'pt;';
3033         if GikoSys.Setting.BrowserFontColor <> -1 then
3034                 Result := Result + 'color:#' + IntToHex( GikoSys.Setting.BrowserFontColor, 6 ) + ';';
3035         if GikoSys.Setting.BrowserBackColor <> -1 then
3036                 Result := Result + 'background-color:#' + IntToHex( GikoSys.Setting.BrowserBackColor, 6 ) + ';';
3037         case GikoSys.Setting.BrowserFontBold of
3038                 -1: Result := Result + 'font-weight:normal;';
3039                 1:  Result := Result + 'font-weight:bold;';
3040         end;
3041         case GikoSys.Setting.BrowserFontItalic of
3042                 -1: Result := Result + 'font-style:normal;';
3043                 1:  Result := Result + 'font-style:italic;';
3044         end;
3045 end;
3046
3047 {!
3048 \brief Be \83v\83\8d\83t\83@\83C\83\8b\82Ö\82Ì\83A\83\93\83J\81[\83^\83O\82ð\90\90¬
3049 \param AID  \91Î\8fÛ\82Æ\82È\82é\93ú\95tID\95\8e\9a\97ñ
3050 \param ANum \83\8c\83X\94Ô
3051 \param AURL \82»\82Ì\83X\83\8c\83b\83h\82ÌURL
3052 \return     \90\90¬\82³\82ê\82½\83A\83\93\83J\81[\83^\83O
3053 }
3054 function TGikoSys.AddBeProfileLink(AID : string; ANum: Integer):string ;
3055 var
3056         p : integer;
3057         BNum, BMark : string;
3058 begin
3059         p := AnsiPos('BE:', AnsiUpperCase(AID));
3060         if p > 0 then begin
3061                 BNum := Copy(AID, p, Length(AID));
3062                 AID := Copy(AID, 1, p - 1);
3063                 p := AnsiPos('-', BNum);
3064                 if p > 0 then begin
3065                         BMark := '?' + Trim(Copy(BNum, p + 1, Length(BNum)));
3066                         BNum := Copy(BNum, 1, p - 1);
3067                 end;
3068                 BNum := Trim(BNum);
3069                 Result := AID + ' <a href="'  + BNum + '/' + IntToStr(ANum)
3070                         + '" target=_blank>' + BMark + '</a>';
3071         end else
3072                 Result := AID;
3073 end;
3074
3075 {!
3076 \brief \83o\81[\83W\83\87\83\93\8fî\95ñ\82ð\8eæ\93¾
3077 \param KeyWord \8eæ\93¾\82·\82é\8d\80\96Ú
3078 \return        \83o\81[\83W\83\87\83\93\95\8e\9a\97ñ
3079 }
3080 function TGikoSys.GetVersionInfo(KeyWord: TVerResourceKey): string;
3081 const
3082         Translation = '\VarFileInfo\Translation';
3083         FileInfo = '\StringFileInfo\%0.4s%0.4s\';
3084 var
3085         BufSize, HWnd: DWORD;
3086         VerInfoBuf: Pointer;
3087         VerData: Pointer;
3088         VerDataLen: Longword;
3089         PathLocale: String;
3090 begin
3091         // \95K\97v\82È\83o\83b\83t\83@\82Ì\83T\83C\83Y\82ð\8eæ\93¾
3092         BufSize := GetFileVersionInfoSize(PChar(Application.ExeName), HWnd);
3093         if BufSize <> 0 then begin
3094                 // \83\81\83\82\83\8a\82ð\8am\95Û
3095                 GetMem(VerInfoBuf, BufSize);
3096                 try
3097                         GetFileVersionInfo(PChar(Application.ExeName), 0, BufSize, VerInfoBuf);
3098                         // \95Ï\90\94\8fî\95ñ\83u\83\8d\83b\83N\93à\82Ì\95Ï\8a·\83e\81[\83u\83\8b\82ð\8ew\92è
3099                         VerQueryValue(VerInfoBuf, PChar(Translation), VerData, VerDataLen);
3100
3101                         if not (VerDataLen > 0) then
3102                                 raise Exception.Create('\8fî\95ñ\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½');
3103
3104                         // 8\8c\85\82Ì\82P\82U\90i\90\94\82É\95Ï\8a·
3105                         // \81¨'\StringFileInfo\027382\FileDescription'
3106                         PathLocale := Format(FileInfo + KeyWordStr[KeyWord],
3107                         [IntToHex(Integer(VerData^) and $FFFF, 4),
3108                         IntToHex((Integer(VerData^) shr 16) and $FFFF, 4)]);
3109                         VerQueryValue(VerInfoBuf, PChar(PathLocale), VerData, VerDataLen);
3110
3111                         if VerDataLen > 0 then begin
3112                                 // VerData\82Í\83[\83\8d\82Å\8fI\82í\82é\95\8e\9a\97ñ\82Å\82Í\82È\82¢\82±\82Æ\82É\92\8d\88Ó
3113                                 result := '';
3114                                 SetLength(result, VerDataLen);
3115                                 StrLCopy(PChar(result), VerData, VerDataLen);
3116                         end;
3117                 finally
3118                         // \89ð\95ú
3119                         FreeMem(VerInfoBuf);
3120                 end;
3121         end;
3122 end;
3123
3124 {!
3125 \brief Load \82³\82ê\82Ä\82¢\82é\83v\83\89\83O\83C\83\93\82Ì\83o\81[\83W\83\87\83\93\82ð\97ñ\8b\93
3126 \return 1\8ds1plugin
3127 }
3128 function TGikoSys.GetPluginsInfo(): String;
3129 var
3130         i : Integer;
3131         major, minor, revision : Cardinal;
3132         agent, release : String;
3133 begin
3134         //\8c\8b\89Ê\82ð\83N\83\8a\83A\82µ\82Ä\82¨\82­
3135         Result := '';
3136
3137         //BoardGroups\8co\97R\82ÅPlugin\82É\83A\83N\83Z\83X\82·\82é
3138         for  i := 0 to Length(BoardGroups) - 1 do begin
3139                 //BoardGroups\82Ì\92\86\82É\82Í\81APlugin\82ð\8e\9d\82Á\82Ä\82¢\82È\82¢\82Ì\81i2\82¿\82á\82ñ\81j\82ª
3140                 //\82¢\82é\82Ì\82Å\82»\82ê\82ð\8f\9c\82­
3141                 if BoardGroups[i].BoardPlugIn <> nil then begin
3142                         BoardGroups[i].BoardPlugIn.VersionInfo(agent, major, minor, release, revision);
3143
3144
3145                         //"Plugin\82Ì\96¼\91O(major.minor.revision)"
3146                         Result := Result +
3147                                 Format('%s(%d.%d.%d)', [agent, major, minor, revision]) + #13#10;
3148                 end;
3149         end;
3150 end;
3151
3152
3153 //! IE\82Ì\83o\81[\83W\83\87\83\93\82ð\8eæ\93¾\82·\82é
3154 function TGikoSys.GetIEVersion: string;
3155 var
3156         R: TRegistry;
3157 begin
3158         R := TRegistry.Create;
3159         try
3160                 //\93Ç\82Ý\8eæ\82è\90ê\97p\82É\82µ\82È\82¢\82Æ\81A\90§\8cÀUSER\82Æ\82©\82Ì\8fê\8d\87\81A\8aJ\82¯\82È\82¢\82Ý\82½\82¢
3161                 R.Access := KEY_EXECUTE;
3162                 R.RootKey := HKEY_LOCAL_MACHINE;
3163                 R.OpenKey('Software\Microsoft\Internet Explorer', False);
3164                 try
3165                         Result := R.ReadString('version');
3166                 except
3167                         Result := '\83o\81[\83W\83\87\83\93\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½\81B';
3168                 end;
3169                 R.CloseKey;
3170         finally
3171                 R.Free;
3172         end;
3173 end;
3174 //! main\83t\83H\81[\83\80\82Ì\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ÌIni\83t\83@\83C\83\8b\96¼
3175 function TGikoSys.GetMainKeyFileName : String;
3176 begin
3177         Result := Setting.GetMainKeyFileName;
3178 end;
3179 //! Editor\83t\83H\81[\83\80\82Ì\83V\83\87\81[\83g\83J\83b\83g\83L\81[\82ÌIni\83t\83@\83C\83\8b\96¼
3180 function TGikoSys.GetEditorKeyFileName: String;
3181 begin
3182         Result := Setting.GetEditorKeyFileName;
3183 end;
3184 //! \93ü\97Í\83A\83V\83X\83g\82Ì\90Ý\92è\83t\83@\83C\83\8b\96¼
3185 function TGikoSys.GetInputAssistFileName: String;
3186 begin
3187         Result := Setting.GetInputAssistFileName;
3188 end;
3189 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\90Ý\92è\82·\82é
3190 procedure TGikoSys.SetGikoMessage;
3191 begin
3192         if FGikoMessage = nil then begin
3193                 FGikoMessage := TGikoMessage.Create;
3194         end else begin
3195                 FGikoMessage.Clear;
3196         end;
3197
3198         if (Setting.GengoSupport) then begin
3199                 try
3200                         if (FileExists(Setting.GetLanguageFileName)) then begin
3201                                 FGikoMessage.LoadFromFile(Setting.GetLanguageFileName);
3202                         end;
3203                 except
3204                         FGikoMessage.Clear;
3205                 end;
3206         end;
3207 end;
3208 //! \83M\83R\83i\83r\82Ì\83\81\83b\83Z\81[\83W\82ð\8eæ\93¾\82·\82é
3209 function TGikoSys.GetGikoMessage(MesType: TGikoMessageListType): String;
3210 begin
3211     Result := '';
3212         if FGikoMessage <> nil then begin
3213                 Result := FGikoMessage.GetMessage(MesType);
3214         end;
3215 end;
3216
3217 //Tue, 17 Dec 2002 12:18:07 GMT \81¨ TDateTime\82Ö
3218 //MonaUtils\82©\82ç\88Ú\93®
3219 function  TGikoSys.DateStrToDateTime(const DateStr: string): TDateTime;
3220         function  StrMonthToMonth(const s: string): integer;
3221         const
3222                 m: array[1..12] of string = ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec');
3223         var
3224                 i: integer;
3225         begin
3226                 Result  :=  -1;
3227                 for i :=  Low(m)  to  High(m) do  begin
3228                         if  (SameText(s, m[i]))  then  begin
3229                                 Result  :=  i;
3230                                 Break;
3231                         end;
3232                 end;
3233         end;
3234 var
3235         wDay, wMonth, wYear: word;
3236         wHour, wMinute, wSecond: word;
3237         sTime: string;
3238         d: TDateTime;
3239 begin
3240         wDay    :=  StrToIntDef(ChooseString(DateStr, ' ', 1), 0);
3241         wMonth  :=  StrMonthToMonth(ChooseString(DateStr, ' ', 2));
3242         wYear   :=  StrToIntDef(ChooseString(DateStr, ' ', 3), 0);
3243         sTime   :=  ChooseString(DateStr, ' ', 4);
3244         wHour   :=  StrToIntDef(ChooseString(sTime, ':', 0), 0);
3245         wMinute :=  StrToIntDef(ChooseString(sTime, ':', 1), 0);
3246         wSecond :=  StrToIntDef(ChooseString(sTime, ':', 2), 0);
3247         d :=  EncodeDateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond, 0);
3248         Result  :=  d;
3249 end;
3250 //MonaUtils\82©\82ç\88Ú\93®
3251 //! \82 \82é\83Z\83p\83\8c\81[\83^\82Å\8bæ\90Ø\82ç\82ê\82½\95\8e\9a\97ñ\82©\82ç\82\8e\94Ô\96Ú\82Ì\95\8e\9a\97ñ\82ð\8eæ\82è\8fo\82·
3252 function TGikoSys.ChooseString(const Text, Separator: string; Index: integer): string;
3253 var
3254         S : string;
3255         i, p : integer;
3256 begin
3257         S :=  Text;
3258         for i :=  0 to  Index - 1 do  begin
3259                 if  (AnsiPos(Separator, S) = 0) then  S :=  ''
3260                 else  S :=  Copy(S, AnsiPos(Separator, S) + Length(Separator), Length(S));
3261         end;
3262         p :=  AnsiPos(Separator, S);
3263         if  (p > 0) then  Result  :=  Copy(S, 1, p - 1) else Result :=  S;
3264 end;
3265 //! \88ê\8e\9e\83t\83@\83C\83\8b\82©\82ç\82Ì\95\9c\8b\8c
3266 procedure TGikoSys.RestoreThreadData(Board : TBoard);
3267 const
3268     SECTION = 'Setting';
3269 var
3270     TmpFileList : TStringList;
3271     i : Integer;
3272     ini : TMemIniFile;
3273     ThreadItem : TThreadItem;
3274     Boardpath, tmpStr : string;
3275 begin
3276     Boardpath := ExtractFilePath(Board.GetFolderIndexFileName);
3277
3278         TmpFileList := TStringList.Create;
3279         TmpFileList.Sorted := True;
3280         TmpFileList.BeginUpdate;
3281     try
3282         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\97pTmp\83t\83@\83C\83\8b\83\8a\83X\83g
3283             GetFileList(Boardpath, '*.tmp', TmpFileList, False);
3284             TmpFileList.EndUpdate;
3285                 //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\83`\83F\83b\83N
3286                 for i := TmpFileList.Count - 1 downto 0 do begin
3287                         ThreadItem := Board.FindThreadFromFileName(ChangeFileExt(TmpFileList[i], '.dat'));
3288                         if ThreadItem <> nil then begin
3289                                 ini := TMemIniFile.Create(Boardpath + TmpFileList[i]);
3290                                 try
3291                                         tmpStr := ini.ReadString(SECTION, 'RoundDate', DateTimeToStr(ZERO_DATE));
3292                                         ThreadItem.RoundDate := ConvertDateTimeString(tmpStr);
3293
3294                                         tmpStr := ini.ReadString(SECTION, 'LastModified', DateTimeToStr(ZERO_DATE));
3295                                         ThreadItem.LastModified := ConvertDateTimeString(tmpStr);
3296                                         ThreadItem.Count := ini.ReadInteger(SECTION, 'Count', 0);
3297                                         ThreadItem.NewReceive := ini.ReadInteger(SECTION, 'NewReceive', 0);
3298
3299                                         ThreadItem.Size := ini.ReadInteger(SECTION, 'Size', 0);
3300                                         if(ThreadItem.Size = 0) and (FileExists(ThreadItem.GetThreadFileName)) then begin
3301                                                 try
3302                                                         ThreadItem.Size := GetFileSize(ThreadItem.GetThreadFileName) - ThreadItem.Count;
3303                                                 except
3304                                                 end;
3305                                         end;
3306
3307                     //\8f\84\89ñ\82Ì\90Ý\92è\82ÍRoundData\82Ì\95û\82ª\82â\82é\82©\82ç\8f\9f\8eè\82É\90Ý\92è\82µ\82Ä\82Í\83_\83\81\81I\81@by \82à\82\82ã
3308                                         //ThreadItem.Round := ini.ReadBool('Setting', 'Round', False);
3309                                         //ThreadItem.RoundName := ini.ReadString('Setting', 'RoundName', ThreadItem.RoundName);
3310                                         ThreadItem.UnRead := False;//ini.ReadBool('Setting', 'UnRead', False);
3311                                         ThreadItem.ScrollTop := ini.ReadInteger(SECTION, 'ScrollTop', 0);
3312                                         ThreadItem.AllResCount := ini.ReadInteger(SECTION, 'AllResCount', ThreadItem.Count);
3313                                         ThreadItem.NewResCount := ini.ReadInteger(SECTION, 'NewResCount', 0);
3314                                         ThreadItem.AgeSage := TGikoAgeSage(ini.ReadInteger(SECTION, 'AgeSage', Ord(gasNone)));
3315                                 finally
3316                                         ini.Free;
3317                                 end;
3318                                 DeleteFile(Boardpath + TmpFileList[i]);
3319                         end;
3320                 end;
3321     finally
3322         TmpFileList.Clear;
3323         TmpFileList.Free;
3324     end;
3325 end;
3326 {
3327 \brief User32.dll\82ª\97\98\97p\82Å\82«\82é\82©
3328 \return Boolean \97\98\97p\82Å\82«\82é\8fê\8d\87\82ÍTrue
3329 }
3330 function TGikoSys.CanUser32DLL: Boolean;
3331 var
3332     hUser32 : HINST;
3333 begin
3334     Result := False;
3335         hUser32 := 0;
3336         try
3337                 try
3338                         hUser32 := LoadLibrary('User32.dll');
3339                         if hUser32 <> 0 then begin
3340                                 Result := True;
3341             end;
3342                 except
3343                 Result := false;
3344                 end;
3345         finally
3346                 FreeLibrary(hUser32);
3347         end;
3348 end;
3349 {
3350 \brief  OE\88ø\97p\95\84\8eæ\93¾
3351 \return OE\82Ì\88ø\97p\95\84\81i\90Ý\92è\82³\82ê\82Ä\82¢\82È\82¢\8fê\8d\87\82Í'>')
3352 }
3353 function TGikoSys.GetOEIndentChar : string;
3354 var
3355         regKey                  : TRegistry;
3356         Identities      : string;
3357         IndentChar      : DWORD;
3358 const
3359         DEFAULT_CHAR    = '> ';
3360         OE_MAIL_PATH    = '\Software\Microsoft\Outlook Express\5.0\Mail';
3361         INDENT_CHAR             = 'Indent Char';
3362 begin
3363
3364         Result  := DEFAULT_CHAR;
3365         regKey  := TRegistry.Create;
3366         try
3367                 try
3368                         regKey.RootKey  := HKEY_CURRENT_USER;
3369                         if not regKey.OpenKey( 'Identities', False ) then
3370                                 Exit;
3371                         Identities                      := regKey.ReadString( 'Default User ID' );
3372                         if Identities = '' then
3373                                 Exit;
3374                         if not regKey.OpenKey( Identities + OE_MAIL_PATH, False ) then
3375                                 Exit;
3376                         IndentChar := regKey.ReadInteger( INDENT_CHAR );
3377                         Result := Char( IndentChar ) + ' ';
3378                 except
3379                 end;
3380         finally
3381                 regKey.Free;
3382         end;
3383
3384 end;
3385 //! \92u\8a·\90Ý\92è\83t\83@\83C\83\8b\8eæ\93¾
3386 function TGikoSys.GetReplaceFileName: String;
3387 begin
3388     Result := Setting.GetReplaceFileName;
3389 end;
3390
3391 initialization
3392         GikoSys := TGikoSys.Create;
3393
3394 finalization
3395         if GikoSys <> nil then begin
3396                 GikoSys.Free;
3397                 GikoSys := nil;
3398         end;
3399 end.