OSDN Git Service

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