OSDN Git Service

・新着の扱いを変更。
[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
18 type
19         //BBS\83^\83C\83v
20         TGikoBBSType = (gbt2ch);
21         //\83\8d\83O\83^\83C\83v
22         TGikoLogType = (glt2chNew, glt2chOld);
23         //\83\81\83b\83Z\81[\83W\83A\83C\83R\83\93
24         TGikoMessageIcon = (gmiOK, gmiSAD, gmiNG, gmiWhat, gmiNone);
25         //URL\83I\81[\83v\83\93\83u\83\89\83E\83U\83^\83C\83v
26         TGikoBrowserType = (gbtIE, gbtUserApp, gbtAuto);
27
28
29         TStrTokSeparator = set of Char;
30         TStrTokRec = record
31                 Str: string;
32                 Pos: Integer;
33         end;
34
35         //\83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b\83\8c\83R\81[\83h
36         TIndexRec = record
37                 FNo: Integer;
38                 FFileName: string;
39                 FTitle: string;
40                 FCount: Integer;
41                 FSize: Integer;
42 //              FRoundNo: Integer;
43                 FRoundDate: TDateTime;
44                 FLastModified: TDateTime;
45                 FKokomade: Integer;
46                 FNewReceive: Integer;
47                 FMishiyou: Boolean;     //\96¢\8eg\97p
48                 FUnRead: Boolean;
49                 FScrollTop: Integer;
50                 //Index Ver 1.01
51                 FAllResCount: Integer;
52                 FNewResCount: Integer;
53                 FAgeSage: TGikoAgeSage;
54         end;
55
56         //\83T\83u\83W\83F\83N\83g\83\8c\83R\81[\83h
57         TSubjectRec = record
58                 FFileName: string;
59                 FTitle: string;
60                 FCount: Integer;
61         end;
62
63         //\83\8c\83X\83\8c\83R\81[\83h
64         TResRec = record
65                 FTitle: string;
66                 FMailTo: string;
67                 FName: string;
68                 FDateTime: string;
69                 FBody: string;
70                 FType: TGikoLogType;
71         end;
72
73         //URLPath\83\8c\83R\81[\83h
74         TPathRec = record
75                 FBBS: string;                           //BBSID
76                 FKey: string;                           //ThreadID
77                 FSt: Integer;                           //\8aJ\8en\83\8c\83X\94Ô
78                 FTo: Integer;                           //\8fI\97¹\83\8c\83X\94Ô
79                 FFirst: Boolean;                //>>1\82Ì\95\\8e¦
80                 FStBegin: Boolean;      //1\81`\95\\8e¦
81                 FToEnd: Boolean;                //\81`\8dÅ\8cã\82Ü\82Å\95\\8e¦
82                 FDone: Boolean;                 //\90¬\8c÷
83         end;
84
85         TGikoSys = class(TObject)
86         private
87                 { Private \90é\8c¾ }
88                 FSetting: TSetting;
89                 FDolib: TDolib;
90                 FAWKStr: TAWKStr;
91 //              FExitWrite: TStringList;
92 //              function StrToFloatDef(s: string; Default: Double): Double;
93
94         public
95                 { Public \90é\8c¾ }
96                 FAbon : TAbon;
97                 FSelectResFilter : TAbon;
98                 constructor Create;
99
100                 destructor Destroy; override;
101
102
103 //              function MsgBox(Msg: string; Title: string; Flags: Longint): integer; overload;
104 //              function MsgBox(Handle: THandle; Msg: string; Title: string; Flags: Longint): integer; overload;
105                 function IsNumeric(s: string): boolean;
106                 function IsFloat(s: string): boolean;
107                 function DirectoryExistsEx(const Name: string): Boolean;
108                 function ForceDirectoriesEx(Dir: string): Boolean;
109 //              function GetVersion: string;
110
111                 function GetBoardFileName: string;
112                 function GetCustomBoardFileName: string;
113                 function GetHtmlTempFileName: string;
114                 function GetAppDir: string;
115                 function GetTempFolder: string;
116                 function GetSentFileName: string;
117                 function GetConfigDir: string;
118                 function GetSkinDir: string;
119                 function GetStyleSheetDir: string;
120                 function GetOutBoxFileName: string;
121                 function GetURL(BBSID: string; FileName: string): string;
122                 function GetUserAgent: string;
123
124                 procedure ReadSubjectFile(Board: TBoard);
125                 procedure CreateThreadDat(Board: TBoard);
126                 procedure WriteThreadDat(Board: TBoard);
127                 function ParseIndexLine(Line: string): TIndexRec;
128                 procedure GetFileList(Path: string; Mask: string; List: TStringList; SubDir: Boolean; IsPathAdd: Boolean);
129                 procedure GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
130
131                 procedure CreateHTML2(doc: Variant; ThreadItem: TThreadItem; var sTitle: string);
132                 function AddAnchorTag(s: string): string;
133
134                 function DivideSubject(Line: string): TSubjectRec;
135                 function DivideStrLine(Line: string): TResRec;
136
137                 property Setting: TSetting read FSetting write FSetting;
138                 property Dolib: TDolib read FDolib write FDolib;
139
140                 function UrlToID(url: string): string;
141                 function UrlToServer(url: string): string;
142
143                 function StrTokFirst(const s:string; const sep:TStrTokSeparator; var Rec:TStrTokRec):string;
144                 function StrTokNext(const sep:TStrTokSeparator; var Rec:TStrTokRec): string;
145
146                 function GetFileSize(FileName : string) : longint;
147                 function GetFileLineCount(FileName : string): longint;
148                 function Get2chDate(aDate: TDateTime): string;
149                 function IntToDateTime(val: Int64): TDateTime;
150                 function DateTimeToInt(ADate: TDateTime): Integer;
151
152                 function ReadThreadFile(FileName: string; Line: Integer): string;
153
154                 procedure MenuFont(Font: TFont);
155
156                 function RemoveToken(var s:string;delimiter:string):string;
157                 function GetTokenIndex(s: string; delimiter: string; index: Integer): string;
158
159                 function DeleteLink(const s: string): string;
160
161                 function GetShortName(const LongName: string; ALength: integer): string;
162                 function ConvRes(const Body, Bbs, Key,  ParamBBS, ParamKey, ParamStart, ParamTo, ParamNoFirst, ParamTrue : string): string;
163
164                 function ZenToHan(const s: string): string;
165                 function VaguePos(const Substr, S: string): Integer;
166                 function BoolToInt(b: Boolean): Integer;
167                 function IntToBool(i: Integer): Boolean;
168                 function GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
169                 procedure LoadKeySetting(ActionList: TActionList);
170                 procedure SaveKeySetting(ActionList: TActionList);
171                 procedure CreateProcess(const AppPath: string; const Param: string);
172                 procedure OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
173                 function HTMLDecode(const AStr: String): String;
174                 function GetHRefText(s: string): string;
175                 function Is2chHost(Host: string): Boolean;
176                 function Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
177                 function Parse2chURL2(URL: string): TPathRec;
178                 procedure ParseURI(var URL, Protocol, Host, Path, Document, Port, Bookmark: string);
179                 function GetVersionBuild: Integer;
180         end;
181
182 var
183         GikoSys: TGikoSys;
184 const
185         LENGTH_RESTITLE                 = 40;
186         ZERO_DATE: Integer      = 25569;
187         BETA_VERSION_NAME_E = 'beta';
188         BETA_VERSION_NAME_J = 'ÊÞÀ';
189         BETA_VERSION                            = 40;
190         BETA_VERSION_BUILD      = '';                           //debug\94Å\82È\82Ç
191
192 implementation
193
194 uses
195         Giko, RoundData;
196
197 const
198         BOARD_FILE_NAME                                                         = 'board.2ch';
199         CUSTOMBOARD_FILE_NAME                                   = 'custom.2ch';
200         KEY_SETTING_FILE_NAME                                   = 'key.ini';
201         TEMP_FOLDER                                                                             = 'Temp';
202         OUTBOX_FILE_NAME                                                        = 'outbox.ini';
203         SENT_FILE_NAME                                                          = 'sent.ini';
204         CONFIG_DIR_NAME                                                         = 'config';
205         CSS_DIR_NAME                                                                    = 'css';
206         SKIN_DIR_NAME                                                                   = 'skin';
207   SKIN_HEADER_FILE_NAME                                 = 'Header.html';
208   SKIN_FOOTER_FILE_NAME                                 = 'Footer.html';
209   SKIN_NEWRES_FILE_NAME                                 = 'NewRes.html';
210   SKIN_RES_FILE_NAME                                            = 'Res.html';
211         FOLDER_INDEX_VERSION                                    = '1.01';
212         USER_AGENT                                                                              = 'Monazilla';
213         APP_NAME                                                                                        = 'gikoNavi';
214         DEFAULT_NGWORD_FILE_NAME : String = 'NGword.txt';
215         NGWORDs_DIR_NAME : String               = 'NGwords';
216
217 (*************************************************************************
218  *GikoSys\83R\83\93\83X\83g\83\89\83N\83^
219  *************************************************************************)
220 constructor TGikoSys.Create;
221 begin
222         FSetting := TSetting.Create;
223         FDolib := TDolib.Create;
224         FAWKStr := TAWKStr.Create(nil);
225         if DirectoryExists(GetConfigDir) = false then begin
226                 CreateDir(GetConfigDir);
227         end;
228         FAbon := TAbon.Create;
229         FAbon.Setroot(GetConfigDir+NGWORDs_DIR_NAME);
230         //FAbon.SetNGwordpath(DEFAULT_NGWORD_FILE_NAME);
231         FAbon.GoHome;
232         FSelectResFilter := TAbon.Create;
233         // \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
234         FSelectResFilter.AbonString := '';
235 end;
236
237 (*************************************************************************
238  *GikoSys\83f\83X\83g\83\89\83N\83^
239  *************************************************************************)
240 destructor TGikoSys.Destroy;
241 var
242         i: Integer;
243         FileList: TStringList;
244 begin
245         //\83X\83\8c\83b\83h\83f\81[\83^\83t\83@\83C\83\8b\82ð\8dX\90V
246 //      FlashExitWrite;
247
248 //      FExitWrite.Free;
249         FAWKStr.Free;
250         FSetting.Free;
251         FDolib.Free;
252
253         //\83e\83\93\83|\83\89\83\8aHTML\82ð\8dí\8f\9c
254         FileList := TStringList.Create;
255         try
256                 GetFileList(GetTempFolder, '*.html', FileList, False, True);
257                 for i := 0 to FileList.Count - 1 do begin
258                         DeleteFile(FileList[i]);
259                 end;
260         finally
261                 FileList.Free;
262         end;
263         inherited;
264 end;
265
266 (*************************************************************************
267  *\95\8e\9a\97ñ\90\94\8e\9a\83`\83F\83b\83N
268  *************************************************************************)
269 {$HINTS OFF}
270 function TGikoSys.IsNumeric(s: string): boolean;
271 var
272         e: integer;
273         v: integer;
274 begin
275         Val(s, v, e);
276         Result := e = 0;
277 end;
278 {$HINTS ON}
279
280 (*************************************************************************
281  *\95\8e\9a\97ñ\95\82\93®\8f¬\90\94\93_\90\94\8e\9a\83`\83F\83b\83N
282  *************************************************************************)
283 function TGikoSys.IsFloat(s: string): boolean;
284 var
285         v: Extended;
286 begin
287         Result := TextToFloat(PChar(s), v, fvExtended);
288 end;
289
290 (*************************************************************************
291  *\83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
292  *************************************************************************)
293 function TGikoSys.GetBoardFileName: string;
294 begin
295         Result := GetAppDir + CONFIG_DIR_NAME + '\' + BOARD_FILE_NAME;
296 end;
297
298 (*************************************************************************
299  *\83{\81[\83h\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
300  *************************************************************************)
301 function TGikoSys.GetCustomBoardFileName: string;
302 begin
303         Result := GetAppDir + CONFIG_DIR_NAME + '\' + CUSTOMBOARD_FILE_NAME;
304 end;
305
306 (*************************************************************************
307  *\83e\83\93\83|\83\89\83\8a\83t\83H\83\8b\83_\81[\96¼\8eæ\93¾
308  *************************************************************************)
309 function TGikoSys.GetHtmlTempFileName: string;
310 begin
311         Result := TEMP_FOLDER;
312 end;
313
314
315 (*************************************************************************
316  *\8eÀ\8ds\83t\83@\83C\83\8b\83t\83H\83\8b\83_\8eæ\93¾
317  *************************************************************************)
318 function TGikoSys.GetAppDir: string;
319 begin
320         Result := ExtractFilePath(Application.ExeName);
321 end;
322
323 (*************************************************************************
324  *TempHtml\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
325  *************************************************************************)
326 function TGikoSys.GetTempFolder: string;
327 begin
328         Result := GetAppDir + TEMP_FOLDER;
329 end;
330
331 (*************************************************************************
332  *sent.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
333  *************************************************************************)
334 function TGikoSys.GetSentFileName: string;
335 begin
336         Result := GetAppDir + SENT_FILE_NAME;
337 end;
338
339 (*************************************************************************
340  *outbox.ini\83t\83@\83C\83\8b\96¼\8eæ\93¾\81i\83p\83X\81{\83t\83@\83C\83\8b\96¼\81j
341  *************************************************************************)
342 function TGikoSys.GetOutBoxFileName: string;
343 begin
344         Result := GetAppDir + OUTBOX_FILE_NAME;
345 end;
346
347 (*************************************************************************
348  *Config\83t\83H\83\8b\83_\8eæ\93¾
349  *************************************************************************)
350 function TGikoSys.GetConfigDir: string;
351 begin
352         Result := IncludeTrailingPathDelimiter(GetAppDir + CONFIG_DIR_NAME);
353 end;
354
355 function TGikoSys.GetStyleSheetDir: string;
356 begin
357         Result := IncludeTrailingPathDelimiter(GetConfigDir + CSS_DIR_NAME);
358 end;
359
360 function TGikoSys.GetSkinDir: string;
361 begin
362         Result := IncludeTrailingPathDelimiter(GetConfigDir + SKIN_DIR_NAME);
363 end;
364
365 (*************************************************************************
366  *URL\82ð\8dì\90¬(\83R\83s\83y\97p)
367  *************************************************************************)
368 function TGikoSys.GetURL(BBSID: string; FileName: string): string;
369 var
370         Board: TBoard;
371 begin
372         Board := BoardGroup.BBS2ch.GetBoardFromBBSID(BBSID);
373         Result := UrlToServer(Board.URL) + 'test/read.cgi/' + UrlToID(Board.URL) + '/' + ChangeFileExt(FileName, '') + '/l50';
374         //http://teri.2ch.net/test/read.cgi?bbs=accuse&key=974619522&ls=50
375         //http://pc.2ch.net/test/read.cgi/tech/1003664165/l50
376 end;
377
378 // UserAgent\8eæ\93¾
379 function TGikoSys.GetUserAgent: string;
380 begin
381         if Dolib.Connected then begin
382                 Result := Format('%s %s/%s%d%s', [
383                                                                 Dolib.UserAgent,
384                                                                 APP_NAME,
385                                                                 //MAJOR_VERSION,
386                                                                 //MINOR_VERSION,
387                                                                 BETA_VERSION_NAME_E,
388                                                                 BETA_VERSION,
389                                                                 BETA_VERSION_BUILD]);
390         end else begin
391                 Result := Format('%s/%s %s/%s%d%s', [
392                                                                 USER_AGENT,
393                                                                 Dolib.Version,
394                                                                 APP_NAME,
395                                                                 //MAJOR_VERSION,
396                                                                 //MINOR_VERSION,
397                                                                 BETA_VERSION_NAME_E,
398                                                                 BETA_VERSION,
399                                                                 BETA_VERSION_BUILD]);
400         end;
401 end;
402
403 (*************************************************************************
404  *\82Q\82¿\82á\82ñ\82Ë\82é\95û\8e®\8e\9e\8d\8f\8eæ\93¾
405  *************************************************************************)
406 function TGikoSys.Get2chDate(aDate: TDateTime): string;
407 var
408         d1: TDateTime;
409         d2: TDateTime;
410 begin
411         d1 := EncodeDate(1970, 1, 1);
412         d2 := aDate - EncodeTime(9, 0, 0, 0);
413         Result := FloatToStr(Trunc((d2 - d1) * 24 * 60 * 60));
414 end;
415
416
417 function TGikoSys.IntToDateTime(val: Int64): TDateTime;
418 var
419         d1: tdatetime;
420         d2: tdatetime;
421 begin
422         d1 := EncodeDate(1970, 1, 1);
423         d2 := (val * 1000) / (24 * 60 * 60 * 1000);
424         Result := d1 + d2;
425 end;
426
427 function TGikoSys.DateTimeToInt(ADate: TDateTime): Integer;
428 var
429         d: TDateTime;
430         c: Currency;
431 begin
432         d := EncodeDate(1970, 1, 1);
433         c := (ADate - d) * 24 * 60 * 60;
434         Result := Trunc(c);
435 end;
436
437
438 (*************************************************************************
439  *Subject\83t\83@\83C\83\8bRead
440  *************************************************************************)
441 procedure TGikoSys.ReadSubjectFile(Board: TBoard);
442 var
443         ThreadItem: TThreadItem;
444         FileName: string;
445         FileList: TStringList;
446         TmpFileList: TStringList;
447 //      SrchRec: TSearchRec;
448 //      R: integer;
449         Index: Integer;
450         sl: TStringList;
451         i: Integer;
452         Rec: TIndexRec;
453         UnRead: Integer;
454 //      TmpUpdate: Boolean;
455         ini: TMemIniFile;
456         ResRec: TResRec;
457         RoundItem: TRoundItem;
458         idx: Integer;
459 begin
460         Board.Clear;
461         UnRead := 0;
462 //      TmpUpdate := False;
463
464         FileName := Board.GetFolderIndexFileName;
465         if not FileExists(FileName) then CreateThreadDat(Board);
466 //      if not FileExists(FileName) then Exit;
467
468         //IsLogFile\97pDAT\83t\83@\83C\83\8b\83\8a\83X\83g
469         FileList := TStringList.Create;
470         FileList.Sorted := True;
471         GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.dat', FileList, False, False);
472
473         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\97pTmp\83t\83@\83C\83\8b\83\8a\83X\83g
474         TmpFileList := TStringList.Create;
475         TmpFileList.Sorted := True;
476         GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.tmp', TmpFileList, False, False);
477
478 {       R := FindFirst(ExtractFileDir(Board.GetFolderIndexFileName) + '\*.dat', 0, SrchRec);
479         while R = 0 do begin
480                 FileList.Add(SrchRec.Name);
481                 R := FindNext(SrchRec);
482         end;
483         FindClose(SrchRec);}
484
485         sl := TStringList.Create;
486         try
487                 if FileExists(FileName) then
488                         sl.LoadFromFile(FileName);
489
490                 //\82Q\8ds\96Ú\82©\82ç\81i\82P\8ds\96Ú\82Í\83o\81[\83W\83\87\83\93\81j
491                 for i := 1 to sl.Count - 1 do begin
492                         Rec := ParseIndexLine(sl[i]);
493
494                         ThreadItem := TThreadItem.Create;
495                         ThreadItem.BeginUpdate;
496                         ThreadItem.No := Rec.FNo;
497                         ThreadItem.FileName := Rec.FFileName;
498                         ThreadItem.Title := Rec.FTitle;
499                         ThreadItem.Count := Rec.FCount;
500                         ThreadItem.Size := Rec.FSize;
501 //                      ThreadItem.RoundNo := Rec.FRoundNo;
502                         ThreadItem.RoundDate := Rec.FRoundDate;
503                         ThreadItem.LastModified := Rec.FLastModified;
504                         ThreadItem.Kokomade := Rec.FKokomade;
505                         ThreadItem.NewReceive := Rec.FNewReceive;
506 //                      ThreadItem.Round := Rec.FRound;
507                         ThreadItem.UnRead := Rec.FUnRead;
508                         ThreadItem.ScrollTop := Rec.FScrollTop;
509                         ThreadItem.AllResCount := Rec.FAllResCount;
510                         ThreadItem.NewResCount := Rec.FNewResCount;
511                         ThreadItem.AgeSage := Rec.FAgeSage;
512                         ThreadItem.ParentBoard := Board;
513
514                         //IsLogFile\83`\83F\83b\83N
515                         ThreadItem.IsLogFile := False;
516                         if FileList.Count <> 0 then begin
517                                 if FileList.Find(ThreadItem.FileName, Index) then begin
518                                         ThreadItem.IsLogFile := True;
519                                         FileList.Delete(Index);
520                                 end;
521                         end;
522
523                         //\8f\84\89ñ\83\8a\83X\83g\82É\91\8dÝ\82µ\82½\82ç\8f\84\89ñ\83t\83\89\83O\83Z\83b\83g
524                         if ThreadItem.IsLogFile then begin
525                                 idx := RoundList.Find(ThreadItem);
526                                 if idx <> -1 then begin
527                                         RoundItem := RoundList.Items[idx, grtItem];
528                                         ThreadItem.RoundName := RoundItem.RoundName;
529                                         ThreadItem.Round := True;
530                                 end;
531                         end;
532
533                         //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\83`\83F\83b\83N
534                         if TmpFileList.Count <> 0 then begin
535                                 if TmpFileList.Find(ChangeFileExt(ThreadItem.FileName, '.tmp'), Index) then begin
536                                         ini := TMemIniFile.Create(ChangeFileExt(ThreadItem.GetThreadFileName, '.tmp'));
537                                         try
538                                                 ThreadItem.RoundDate := ini.ReadDateTime('Setting', 'RoundDate', ZERO_DATE);
539                                                 ThreadItem.LastModified := ini.ReadDateTime('Setting', 'LastModified', ZERO_DATE);
540                                                 ThreadItem.Size := ini.ReadInteger('Setting', 'Size', 0);
541                                                 ThreadItem.Count := ini.ReadInteger('Setting', 'Count', 0);
542                                                 ThreadItem.NewReceive := ini.ReadInteger('Setting', 'NewReceive', 0);
543                                                 ThreadItem.Round := ini.ReadBool('Setting', 'Round', False);
544                                                 ThreadItem.UnRead := False;//ini.ReadBool('Setting', 'UnRead', False);
545                                                 ThreadItem.ScrollTop := ini.ReadInteger('Setting', 'ScrollTop', 0);
546                                                 ThreadItem.AllResCount := ini.ReadInteger('Setting', 'AllResCount', 0);
547                                                 ThreadItem.NewResCount := ini.ReadInteger('Setting', 'NewResCount', 0);
548                                                 ThreadItem.AgeSage := TGikoAgeSage(ini.ReadInteger('Setting', 'AgeSage', Ord(gasNone)));
549                                         finally
550                                                 ini.Free;
551                                         end;
552                                         TmpFileList.Delete(Index);
553                                 end;
554                         end;
555
556                         ThreadItem.EndUpdate;
557                         Board.Add(ThreadItem);
558
559 //                      if (ThreadItem.IsLogFile) and (ThreadItem.Count > ThreadItem.Kokomade) then
560                         if (ThreadItem.IsLogFile) and (ThreadItem.UnRead) then
561                                 Inc(UnRead);
562                 end;
563                 if UnRead <> Board.UnRead then
564                         Board.UnRead := UnRead;
565
566                 //\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
567                 for i := 0 to FileList.Count - 1 do begin
568                         FileName := ExtractFileDir(Board.GetFolderIndexFileName) + '\' + FileList[i];
569
570                         ResRec := DivideStrLine(ReadThreadFile(FileName, 1));
571                         ThreadItem := TThreadItem.Create;
572                         ThreadItem.No := Board.Count + 1;
573                         ThreadItem.FileName := FileList[i];
574                         ThreadItem.Title := ResRec.FTitle;
575                         ThreadItem.Count := GetFileLineCount(FileName);
576                         ThreadItem.AllResCount := ThreadItem.Count;
577                         ThreadItem.NewResCount := 0;
578                         ThreadItem.Size := 0;
579                         ThreadItem.RoundDate := ZERO_DATE;
580                         ThreadItem.LastModified := ZERO_DATE;
581                         ThreadItem.Kokomade := -1;
582                         ThreadItem.NewReceive := 0;
583                         ThreadItem.ParentBoard := Board;
584                         ThreadItem.IsLogFile := True;
585                         ThreadItem.Round := False;
586                         ThreadItem.UnRead := False;
587                         ThreadItem.ScrollTop := 0;
588                         ThreadItem.AgeSage := gasNone;
589                         Board.Add(ThreadItem);
590                 end;
591         finally
592                 sl.Free;
593         end;
594         FileList.Free;
595         TmpFileList.Free;
596         Board.IsThreadDatRead := True;
597 end;
598
599 (*************************************************************************
600  *\83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X\83t\83@\83C\83\8b(Folder.idx)\8dì\90¬
601  *************************************************************************)
602 procedure TGikoSys.CreateThreadDat(Board: TBoard);
603 var
604         i: integer;
605         s: string;
606         SubjectList: TStringList;
607         sl: TStringList;
608         Rec: TSubjectRec;
609         FileName: string;
610         cnt: Integer;
611 begin
612         if not FileExists(Board.GetSubjectFileName) then Exit;
613         FileName := Board.GetFolderIndexFileName;
614
615         SubjectList := TStringList.Create;
616         try
617                 SubjectList.LoadFromFile(Board.GetSubjectFileName);
618                 sl := TStringList.Create;
619                 try
620                         cnt := 1;
621                         sl.Add(FOLDER_INDEX_VERSION);
622                         for i := 0 to SubjectList.Count - 1 do begin
623                                 Rec := DivideSubject(SubjectList[i]);
624
625                                 if (Trim(Rec.FFileName) = '') or (Trim(Rec.FTitle) = '') then
626                                         Continue;
627
628                                 s := Format('%x', [cnt]) + #1                                   //\94Ô\8d\86
629                                          + Rec.FFileName + #1                                                           //\83t\83@\83C\83\8b\96¼
630                                          + Rec.FTitle + #1                                                                      //\83^\83C\83g\83\8b
631                                          + Format('%x', [Rec.FCount]) + #1      //\83J\83E\83\93\83g
632                                          + Format('%x', [0]) + #1                                               //size
633                                          + Format('%x', [0]) + #1                                               //RoundDate
634                                          + Format('%x', [0]) + #1                                               //LastModified
635                                          + Format('%x', [0]) + #1                                               //Kokomade
636                                          + Format('%x', [0]) + #1                                               //NewReceive
637                                          + '0' + #1                                                                                             //\96¢\8eg\97p
638                                          + Format('%x', [0]) + #1                                               //UnRead
639                                          + Format('%x', [0]) + #1                                               //ScrollTop
640                                          + Format('%x', [Rec.FCount]) + #1      //AllResCount
641                                          + Format('%x', [0]) + #1                                               //NewResCount
642                                          + Format('%x', [0]);                                                           //AgeSage
643
644                                 sl.Add(s);
645                                 inc(cnt);
646                         end;
647                         sl.SaveToFile(FileName);
648                 finally
649                         sl.Free;
650                 end;
651         finally
652                 SubjectList.Free;
653         end;
654 end;
655
656 (*************************************************************************
657  *\83X\83\8c\83b\83h\83C\83\93\83f\83b\83N\83X(Thread.dat)\8f\91\82«\8d\9e\82Ý
658  *Public
659  *************************************************************************)
660 procedure TGikoSys.WriteThreadDat(Board: TBoard);
661 //const
662 //      Values: array[Boolean] of string = ('0', '1');
663 var
664         i: integer;
665         FileName: string;
666         sl: TStringList;
667         s: string;
668         FileList: TStringList;
669 begin
670         if not Board.IsThreadDatRead then
671                 Exit;
672         FileName := Board.GetFolderIndexFileName;
673         ForceDirectoriesEx(Board.ParentCategory.ParentBBS2ch.GetLogFolder + Board.BBSID);
674
675         sl := TStringList.Create;
676         try
677                 sl.Add(FOLDER_INDEX_VERSION);
678                 for i := 0 to Board.Count - 1 do begin
679                         if Board.Items[i].No = 0 then
680                                 Board.Items[i].No := i + 1;
681
682                         s := Format('%x', [Board.Items[i].No]) + #1
683                                  + Board.Items[i].FileName + #1
684                                  + Board.Items[i].Title + #1
685                                  + Format('%x', [Board.Items[i].Count]) + #1
686                                  + Format('%x', [Board.Items[i].Size]) + #1
687                                  + Format('%x', [DateTimeToInt(Board.Items[i].RoundDate)]) + #1
688                                  + Format('%x', [DateTimeToInt(Board.Items[i].LastModified)]) + #1
689                                  + Format('%x', [Board.Items[i].Kokomade]) + #1
690                                  + Format('%x', [Board.Items[i].NewReceive]) + #1
691                                  + '0' + #1     //\96¢\8eg\97p
692                                  + Format('%x', [BoolToInt(Board.Items[i].UnRead)]) + #1
693                                  + Format('%x', [Board.Items[i].ScrollTop]) + #1
694                                  + Format('%x', [Board.Items[i].AllResCount]) + #1
695                                  + Format('%x', [Board.Items[i].NewResCount]) + #1
696                                  + Format('%x', [Ord(Board.Items[i].AgeSage)]);
697
698                         sl.Add(s);
699                 end;
700
701                 sl.SaveToFile(FileName);
702
703                 FileList := TStringList.Create;
704                 try
705                         GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.tmp', FileList, False, True);
706                         for i := 0 to FileList.Count - 1 do begin
707                                 DeleteFile(FileList[i]);
708                         end;
709                 finally
710                         FileList.Free;
711                 end;
712         finally
713                 sl.Free;
714         end;
715 end;
716
717 function TGikoSys.ParseIndexLine(Line: string): TIndexRec;
718 var
719         s: string;
720         i: Integer;
721 begin
722         for i := 0 to 14 do begin
723                 s := GetTokenIndex(Line, #1, i);
724                 case i of
725                         0: Result.FNo := StrToIntDef('$' + s, 0);
726                         1: Result.FFileName := s;
727                         2: Result.FTitle := s;
728                         3: Result.FCount := StrToIntDef('$' + s, 0);
729                         4: Result.FSize := StrToIntDef('$' + s, 0);
730                         5: Result.FRoundDate := IntToDateTime(StrToIntDef('$' + s, ZERO_DATE));
731                         6: Result.FLastModified := IntToDateTime(StrToIntDef('$' + s, ZERO_DATE));
732                         7: Result.FKokomade := StrToIntDef('$' + s, -1);
733                         8: Result.FNewReceive := StrToIntDef('$' + s, 0);
734                         9: ;    //\96¢\8eg\97p
735                         10: Result.FUnRead := IntToBool(StrToIntDef('$' + s, 0));
736                         11: Result.FScrollTop := StrToIntDef('$' + s, 0);
737                         12: Result.FAllResCount := StrToIntDef('$' + s, 0);
738                         13: Result.FNewResCount := StrToIntDef('$' + s, 0);
739                         14: Result.FAgeSage := TGikoAgeSage(StrToIntDef('$' + s, 0));
740                 end;
741         end;
742 end;
743
744 //\8ew\92è\83t\83H\83\8b\83_\93à\82Ì\8ew\92è\83t\83@\83C\83\8b\88ê\97\97\82ð\8eæ\93¾\82·\82é
745 // ListFiles('c:\', '*.txt', list, True);
746 procedure TGikoSys.GetFileList(Path: string; Mask: string; List: TStringList; SubDir: Boolean; IsPathAdd: Boolean);
747 var
748         rc: Integer;
749         SearchRec : TSearchRec;
750         s: string;
751 begin
752         Path := IncludeTrailingPathDelimiter(Path);
753         rc := FindFirst(Path + '*.*', faAnyfile, SearchRec);
754         try
755                 while rc = 0 do begin
756                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
757                                 s := Path + SearchRec.Name;
758                                 //if (SearchRec.Attr and faDirectory > 0) then
759                                 //      s := IncludeTrailingPathDelimiter(s)
760
761                                 if (SearchRec.Attr and faDirectory = 0) and (MatchesMask(s, Mask)) then
762                                         if IsPathAdd then
763                                                 List.Add(s)
764                                         else
765                                                 List.Add(SearchRec.Name);
766                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
767                                         GetFileList(s, Mask, List, True, IsPathAdd);
768                         end;
769                         rc := FindNext(SearchRec);
770                 end;
771         finally
772                 SysUtils.FindClose(SearchRec);
773         end;
774 end;
775
776 //\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é
777 procedure TGikoSys.GetDirectoryList(Path: string; Mask: string; List: TStringList; SubDir: Boolean);
778 var
779         rc: Integer;
780         SearchRec : TSearchRec;
781         s: string;
782 begin
783         Path := IncludeTrailingPathDelimiter(Path);
784         rc := FindFirst(Path + '*.*', faDirectory, SearchRec);
785         try
786                 while rc = 0 do begin
787                         if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then begin
788                                 s := Path + SearchRec.Name;
789                                 //if (SearchRec.Attr and faDirectory > 0) then
790                                 //      s := IncludeTrailingPathDelimiter(s)
791
792                                 if (SearchRec.Attr and faDirectory > 0) and (MatchesMask(s, Mask)) then
793                                         List.Add( IncludeTrailingPathDelimiter( s ) );
794                                 if SubDir and (SearchRec.Attr and faDirectory > 0) then
795                                         GetDirectoryList(s, Mask, List, True);
796                         end;
797                         rc := FindNext(SearchRec);
798                 end;
799         finally
800                 SysUtils.FindClose(SearchRec);
801         end;
802 end;
803
804 procedure TGikoSys.CreateHTML2(doc: Variant; ThreadItem: TThreadItem; var sTitle: string);
805 var
806         i: integer;
807         No: string;
808                 //bufList : TStringList;
809         ReadList: TStringList;
810         SaveList: TStringList;
811         CSSFileName: string;
812         BBSID: string;
813         FileName: string;
814         NewReceiveNo: Integer;
815         Res: TResRec;
816         Skin: TStringList;
817   SkinHeaderFileName: string;
818   SkinNewRes: TStringList;
819   SkinRes: TStringList;
820   SizeByte: Integer;
821 begin
822         ShortDayNames[1] := '\93ú';               ShortDayNames[2] := '\8c\8e';
823         ShortDayNames[3] := '\89Î';               ShortDayNames[4] := '\90\85';
824         ShortDayNames[5] := '\96Ø';               ShortDayNames[6] := '\8bà';
825         ShortDayNames[7] := '\93y';
826         BBSID := ThreadItem.ParentBoard.BBSID;
827         FileName := ThreadItem.FileName;
828         NewReceiveNo := ThreadItem.NewReceive;
829         FileName := ThreadItem.GetThreadFileName;
830         ReadList := TStringList.Create;
831         FAbon.Deleterlo := FSetting.AbonDeleterlo;
832         FAbon.Replaceul := FSetting.AbonReplaceul;
833         FAbon.AbonPopupRes := FSetting.PopUpAbon;
834         try
835                 if ThreadItem.IsLogFile then begin
836                         ReadList.LoadFromFile(FileName);
837                         FAbon.IndividualAbon(ReadList, ChangeFileExt(FileName,'.NG'));
838                         FAbon.Execute(ReadList);                //       \82 \82Ú\81`\82ñ\82µ\82Ä
839                         FSelectResFilter.Execute(ReadList); //\83\8c\83X\82Ì\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\82·\82é
840                         Res := DivideStrLine(ReadList[0]);
841                         Res.FTitle := StringReplace(Res.FTitle, '\81\97\81M', ',', [rfReplaceAll]);
842                         sTitle := Res.FTitle;
843
844                 end else begin
845                         sTitle := StringReplace(ThreadItem.Title, '\81\97\81M', ',', [rfReplaceAll]);
846                 end;
847                 SaveList := TStringList.Create;
848                 try
849                         doc.open;
850                         doc.charset := 'Shift_JIS';
851
852                         CSSFileName := GetStyleSheetDir + Setting.CSSFileName;
853       SkinHeaderFileName := Setting.CSSFileName + SKIN_HEADER_FILE_NAME;
854       if GikoSys.Setting.UseCSS and
855         (Pos(GetSkinDir, Setting.CSSFileName) > 0) and
856         FileExists( SkinHeaderFileName ) then begin
857         // \83X\83L\83\93\8eg\97p
858
859                                 SaveList.Add('<html><head>');
860                                 SaveList.Add('<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">');
861                                 SaveList.Add('<title>' + sTitle + '</title>');
862
863                                 // CSS \82Ì\90Ý\92è
864                                 // SaveList.Add('<link rel="stylesheet" href="'+CSSFileName+'" type="text/css">');
865
866                                 // \83X\83L\83\93\82Ì\90Ý\92è
867         Skin := TStringList.Create;
868                                 try
869                                         Skin.LoadFromFile( SkinHeaderFileName );
870           Skin.Text := StringReplace( Skin.Text, '&GETRESCOUNT', IntToStr( NewReceiveNo ), [rfReplaceAll] );
871           Skin.Text := StringReplace( Skin.Text, '&SKINPATH', Setting.CSSFileName, [rfReplaceAll] );
872                                         SaveList.AddStrings( Skin );
873         finally
874                                         Skin.Free;
875                                 end;
876         SkinNewRes := TStringList.Create;
877         SkinRes := TStringList.Create;
878         try
879                 SkinNewRes.LoadFromFile( Setting.CSSFileName + SKIN_NEWRES_FILE_NAME );
880                 SkinRes.LoadFromFile( Setting.CSSFileName + SKIN_RES_FILE_NAME );
881         except
882         end;
883
884                                 //SaveList.Add('</head>');
885                                 //SaveList.Add('<body>');
886                                 //SaveList.Add('<dl id="dl">');
887                                 SaveList.Add('<a name="top"></a>');
888                                 //SaveList.Add('<div class="title">' + sTitle + '</div>');
889
890                                 for i := 0 to ReadList.Count - 1 do begin
891                                         if (Trim(ReadList[i]) <> '') then begin
892                                                 No := IntToStr(i + 1);
893                         Skin := TStringList.Create;
894
895                                                 if (NewReceiveNo = (i + 1)) or ((NewReceiveNo = 0) and (i = 0)) then
896                                                         SaveList.Add('<a name="new"></a>');
897
898                                                 if NewReceiveNo <= (i + 1) then
899                 Skin.AddStrings( SkinNewRes )
900             else
901                 Skin.AddStrings( SkinRes );
902
903                                                 Res := DivideStrLine(ReadList[i]);
904                                                 Res.FBody := ConvRes(Res.FBody, ThreadItem.ParentBoard.BBSID, ChangeFileExt(ThreadItem.FileName, ''), 'bbs', 'key', 'st', 'to', 'nofirst', 'true');
905
906                                                 if Res.FType = glt2chOld then begin
907                                                         Res.FMailTo := StringReplace(Res.FMailTo, '\81\97\81M', ',', [rfReplaceAll]);
908                                                         Res.FName := StringReplace(Res.FName, '\81\97\81M', ',', [rfReplaceAll]);
909                                                         Res.FBody := StringReplace(Res.FBody, '\81\97\81M', ',', [rfReplaceAll]);
910                                                 end;
911
912                                                 Res.FBody := AddAnchorTag(Res.FBody);
913                                                 if Res.FName = '' then
914                                                         Res.FName := '&nbsp;';
915
916             Skin.Text := StringReplace( Skin.Text, '&NUMBER', No, [rfReplaceAll] );
917             Skin.Text := StringReplace( Skin.Text, '&NAME', Res.FName, [rfReplaceAll] );
918             Skin.Text := StringReplace( Skin.Text, '&MAIL', Res.FMailTo, [rfReplaceAll] );
919             Skin.Text := StringReplace( Skin.Text, '&DATE', Res.FDateTime, [rfReplaceAll] );
920             Skin.Text := StringReplace( Skin.Text, '&MESSAGE', Res.FBody, [rfReplaceAll] );
921             SaveList.AddStrings( Skin );
922
923             Skin.Free;
924
925             if ThreadItem.Kokomade = (i + 1) then
926               SaveList.Add('<dt><a name="koko">\83R\83R\82Ü\82Å\93Ç\82ñ\82¾</a></dt>');
927                                         end;
928
929                                         //if SaveList.Count > 50 then begin
930                                         if i = 20 then begin
931                                                 //Sleep(1);
932                                                 //Application.ProcessMessages;
933
934                                                 doc.Write(SaveList.Text);
935                                                 //while GikoForm.Browser.Busy do begin
936                                                 //      Sleep(1);
937                                                 //      Application.ProcessMessages;
938                                                 //end;
939                                                 while (GikoForm.Browser.ReadyState <> READYSTATE_COMPLETE) and
940                                                                         (GikoForm.Browser.ReadyState <> READYSTATE_INTERACTIVE) do begin
941                                                         //Sleep(1);
942                                                         //Application.ProcessMessages;
943                                                 end;
944                                                 SaveList.Clear;
945                                         end;
946                                 end;
947
948                                 SaveList.Add('<a name="bottom"></a>');
949
950         // \83X\83L\83\93(\83t\83b\83^)
951         Skin := TStringList.Create;
952                                 try
953                                         Skin.LoadFromFile( Setting.CSSFileName + SKIN_FOOTER_FILE_NAME );
954           SizeByte := Length( SaveList.Text );
955           Skin.Text := StringReplace( Skin.Text, '&SIZEKB', IntToStr( Floor( SizeByte / 1024 ) ), [rfReplaceAll] );
956           Skin.Text := StringReplace( Skin.Text, '&SIZE', IntToStr( SizeByte ), [rfReplaceAll] );
957                                         SaveList.AddStrings( Skin );
958         finally
959                                         Skin.Free;
960                                 end;
961
962                                 SaveList.Add('<a name="last"></a>');
963                                 SaveList.Add('</body></html>');
964
965                                 doc.Write(SaveList.Text);
966
967         SkinNewRes.Free;
968         SkinRes.Free;
969                         end else if GikoSys.Setting.UseCSS and FileExists(CSSFileName) then begin
970                                 //CSS\8eg\97p
971                                 //CSSFileName := GetAppDir + CSS_FILE_NAME;
972 //                              SaveList.Add('<html lang="ja"><head>');
973                                 SaveList.Add('<html><head>');
974                                 SaveList.Add('<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">');
975                                 SaveList.Add('<title>' + sTitle + '</title>');
976                                 SaveList.Add('<link rel="stylesheet" href="'+CSSFileName+'" type="text/css">');
977                                 SaveList.Add('</head>');
978                                 SaveList.Add('<body>');
979                                 SaveList.Add('<a name="top"></a>');
980                                 SaveList.Add('<div class="title">' + sTitle + '</div>');
981                                 //doc.Write(SaveList.Text);
982                                 //SaveList.Clear;
983                                 //Application.ProcessMessages;
984                                 for i := 0 to ReadList.Count - 1 do begin
985                                         if (Trim(ReadList[i]) <> '') then begin
986                                                 No := IntToStr(i + 1);
987                                                 if (NewReceiveNo = (i + 1)) or ((NewReceiveNo = 0) and (i = 0)) then begin
988                                                         SaveList.Add('<a name="new"></a><div class="new">\90V\92\85\83\8c\83X <span class="newdate">' + FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate) + '</span></div>');
989                                                 end;
990                                                 Res := DivideStrLine(ReadList[i]);
991                                                 Res.FBody := ConvRes(Res.FBody, ThreadItem.ParentBoard.BBSID, ChangeFileExt(ThreadItem.FileName, ''), 'bbs', 'key', 'st', 'to', 'nofirst', 'true');
992
993                                                 if Res.FType = glt2chOld then begin
994                                                         Res.FMailTo := StringReplace(Res.FMailTo, '\81\97\81M', ',', [rfReplaceAll]);
995                                                         Res.FName := StringReplace(Res.FName, '\81\97\81M', ',', [rfReplaceAll]);
996                                                         Res.FBody := StringReplace(Res.FBody, '\81\97\81M', ',', [rfReplaceAll]);
997                                                 end;
998                                                 //Res.FBody := StringReplace(Res.FBody, '&amphearts;', '&hearts;', [rfReplaceAll]);
999                                                 //Res.FBody := StringReplace(Res.FBody, '&ampnbsp;', '&nbsp;', [rfReplaceAll]);
1000                                                 //Res.FBody := StringReplace(Res.FBody, '&amp#', '&#', [rfReplaceAll]);
1001                                                 //Res.FBody := StringReplace(Res.FBody, '&amp', '&amp;', [rfReplaceAll]);
1002                                                 Res.FBody := AddAnchorTag(Res.FBody);
1003                                                 if Res.FName = '' then
1004                                                         Res.FName := '&nbsp;';
1005                                                 if Res.FMailTo = '' then
1006                                                         SaveList.Add('<a name="' + No + '"></a>'
1007                                                                                                  + '<div class="header"><span class="no"><a href="giko://?no=' + No + '">' + No + '</a></span> '
1008                                                                                                  + '<span class="name_label">\96¼\91O\81F</span> '
1009                                                                                                  + '<span class="name"><b>' + Res.FName + '</b></span> '
1010                                                                                                  + '<span class="date_label">\93\8a\8de\93ú\81F</span> '
1011                                                                                                  + '<span class="date">' + Res.FDateTime+ '</span></div>'
1012                                                                                                  + '<div class="mes">' + Res.FBody + ' </div>')
1013                                                 else
1014                                                         if GikoSys.Setting.ShowMail then
1015                                                                 SaveList.Add('<a name="' + No + '"></a>'
1016                                                                                                          + '<div class="header"><span class="no"><a href="giko://?no=' + No + '">' + No + '</a></span>'
1017                                                                                                          + '<span class="name_label"> \96¼\91O\81F </span>'
1018                                                                                                          + '<a class="name_mail" href="mailto:' + Res.FMailTo + '">'
1019                                                                                                          + '<b>' + Res.FName + '</b></a><span class="mail"> [' + Res.FMailTo + ']</span>'
1020                                                                                                          + '<span class="date_label"> \93\8a\8de\93ú\81F</span>'
1021                                                                                                          + '<span class="date"> ' + Res.FDateTime+ '</span></div>'
1022                                                                                                          + '<div class="mes">' + Res.FBody + ' </div>')
1023                                                         else
1024                                                                 SaveList.Add('<a name="' + No + '"></a>'
1025                                                                                                          + '<div class="header"><span class="no"><a href="giko://?no=' + No + '">' + No + '</a></span>'
1026                                                                                                          + '<span class="name_label"> \96¼\91O\81F </span>'
1027                                                                                                          + '<a class="name_mail" href="mailto:' + Res.FMailTo + '">'
1028                                                                                                          + '<b>' + Res.FName + '</b></a>'
1029                                                                                                          + '<span class="date_label"> \93\8a\8de\93ú\81F</span>'
1030                                                                                                          + '<span class="date"> ' + Res.FDateTime+ '</span></div>'
1031                                                                                                          + '<div class="mes">' + Res.FBody + ' </div>');
1032                                                 if ThreadItem.Kokomade = (i + 1) then begin
1033                                                         SaveList.Add('<a name="koko"></a><div class="koko">\83R\83R\82Ü\82Å\93Ç\82ñ\82¾</div>');
1034                                                 end;
1035                                         end;
1036                                         //if SaveList.Count > 50 then begin
1037                                         if i = 20 then begin
1038                                                 //Sleep(1);
1039                                                 //Application.ProcessMessages;
1040
1041                                                 doc.Write(SaveList.Text);
1042                                                 //while GikoForm.Browser.Busy do begin
1043                                                 //      Sleep(1);
1044                                                 //      Application.ProcessMessages;
1045                                                 //end;
1046                                                 while (GikoForm.Browser.ReadyState <> READYSTATE_COMPLETE) and
1047                                                                         (GikoForm.Browser.ReadyState <> READYSTATE_INTERACTIVE) do begin
1048                                                         //Sleep(1);
1049                                                         //Application.ProcessMessages;
1050                                                 end;
1051                                                 SaveList.Clear;
1052                                         end;
1053                                 end;
1054                                 SaveList.Add('<a name="bottom"></a>');
1055                                 SaveList.Add('</body></html>');
1056                                 SaveList.Add('<a name="last"></a>');
1057                                 SaveList.Add('</body></html>');
1058
1059                                 doc.Write(SaveList.Text);
1060                         end else begin
1061                                 //CSS\94ñ\8eg\97p
1062 //                              SaveList.Add('<html lang="ja"><head>');
1063                                 SaveList.Add('<html><head>');
1064                                 SaveList.Add('<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">');
1065                                 SaveList.Add('<title>' + sTitle + '</title></head>');
1066                                 SaveList.Add('<body TEXT="#000000" BGCOLOR="#EFEFEF" link="#0000FF" alink="#FF0000" vlink="#660099">');
1067                                 SaveList.Add('<a name="top"></a>');
1068                                 SaveList.Add('<font size=+1 color="#FF0000">' + sTitle + '</font>');
1069                                 SaveList.Add('<dl>');
1070                                 doc.Write(SaveList.Text);
1071                                 SaveList.Clear;
1072                                 //Application.ProcessMessages;
1073                                 for i := 0 to ReadList.Count - 1 do begin
1074                                         if (Trim(ReadList[i]) <> '') then begin
1075                                                 No := IntToStr(i + 1);
1076
1077                                                 if (NewReceiveNo = (i + 1)) or ((NewReceiveNo = 0) and (i = 0)) then begin
1078                                                         SaveList.Add('</dl>');
1079                                                         SaveList.Add('<a name="new"></a>');
1080                                                         SaveList.Add('<table width="100%" bgcolor="#3333CC" cellpadding="0" cellspacing="1"><tr><td align="center" bgcolor="#6666FF" valign="middle"><font size="-1" color="#ffffff"><b>\90V\92\85\83\8c\83X ' + FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate) + '</b></font></td></tr></table>');
1081                                                         SaveList.Add('<dl>');
1082                                                 end;
1083                                                 Res := DivideStrLine(ReadList[i]);
1084                                                 Res.FBody := ConvRes(Res.FBody, ThreadItem.ParentBoard.BBSID, ChangeFileExt(ThreadItem.FileName, ''), 'bbs', 'key', 'st', 'to', 'nofirst', 'true');
1085                                                 if Res.FType = glt2chOld then begin
1086                                                         Res.FMailTo := StringReplace(Res.FMailTo, '\81\97\81M', ',', [rfReplaceAll]);
1087                                                         Res.FName := StringReplace(Res.FName, '\81\97\81M', ',', [rfReplaceAll]);
1088                                                         Res.FBody := StringReplace(Res.FBody, '\81\97\81M', ',', [rfReplaceAll]);
1089                                                 end;
1090                                                 //Res.FBody := StringReplace(Res.FBody, '&amphearts;', '&hearts;', [rfReplaceAll]);
1091                                                 //Res.FBody := StringReplace(Res.FBody, '&ampnbsp;', '&nbsp;', [rfReplaceAll]);
1092                                                 //Res.FBody := StringReplace(Res.FBody, '&amp#', '&#', [rfReplaceAll]);
1093                                                 //Res.FBody := StringReplace(Res.FBody, '&amp', '&amp;', [rfReplaceAll]);
1094                                                 Res.FBody := AddAnchorTag(Res.FBody);
1095                                                 if Res.FMailTo = '' then
1096                                                         SaveList.Add('<a name="' + No + '"></a><dt><a href="giko://?no=' + No + '">' + No + '</a> \96¼\91O\81F<font color="forestgreen"><b> ' + Res.FName + ' </b></font> \93\8a\8de\93ú\81F ' + Res.FDateTime+ '<br><dd>' + Res.Fbody + ' <br><br><br>')
1097                                                 else
1098                                                         if GikoSys.Setting.ShowMail then
1099                                                                 SaveList.Add('<a name="' + No + '"></a><dt><a href="giko://?no=' + No + '">' + No + '</a> \96¼\91O\81F<a href="mailto:' + Res.FMailTo + '"><b> ' + Res.FName + ' </B></a> [' + Res.FMailTo + '] \93\8a\8de\93ú\81F ' + Res.FDateTime+ '<br><dd>' + Res.Fbody + ' <br><br><br>')
1100                                                         else
1101                                                                 SaveList.Add('<a name="' + No + '"></a><dt><a href="giko://?no=' + No + '">' + No + '</a> \96¼\91O\81F<a href="mailto:' + Res.FMailTo + '"><b> ' + Res.FName + ' </B></a> \93\8a\8de\93ú\81F ' + Res.FDateTime+ '<br><dd>' + Res.Fbody + ' <br><br><br>');
1102                                                 if ThreadItem.Kokomade = (i + 1) then begin
1103                                                         SaveList.Add('</dl>');
1104                                                         SaveList.Add('<a name="koko"></a><table width="100%" bgcolor="#55AA55" cellpadding="0" cellspacing="1"><tr><td align="center" bgcolor="#77CC77" valign="middle"><font size="-1" color="#ffffff"><b>\83R\83R\82Ü\82Å\93Ç\82ñ\82¾</b></font></td></tr></table>');
1105                                                         SaveList.Add('<dl>');
1106                                                 end;
1107                                         end;
1108                                         if SaveList.Count > 50 then begin
1109                                                 doc.Write(SaveList.Text);
1110                                                 SaveList.Clear;
1111                                                 //Application.ProcessMessages;
1112                                         end;
1113                                 end;
1114                                 SaveList.Add('</dl>');
1115                                 SaveList.Add('<a name="bottom"></a>');
1116                                 SaveList.Add('</body></html>');
1117                                 doc.Write(SaveList.Text);
1118                         end;
1119                 finally
1120                         SaveList.Free;
1121                         doc.Close;
1122                 end;
1123         finally
1124                 ReadList.Free;
1125         end;
1126 end;
1127
1128 (*************************************************************************
1129  *http://\82Ì\95\8e\9a\97ñ\82ðanchor\83^\83O\95t\82«\82É\82·\82é\81B
1130  *************************************************************************)
1131 function TGikoSys.AddAnchorTag(s: string): string;
1132 const
1133         URL_CHAR: string = '0123456789'
1134                                                                          + 'abcdefghijklmnopqrstuvwxyz'
1135                                                                          + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
1136                                                                          + '#$%&()*+,-./:;=?@[]^_`{|}~!''\';
1137 var
1138         wkIdx: array[0..9] of Integer;
1139         url: string;
1140         href: string;
1141         i: Integer;
1142         idx: Integer;
1143 begin
1144         Result := '';
1145
1146         while True do begin
1147                 wkIdx[0] := AnsiPos('http://', s);
1148                 wkIdx[1] := AnsiPos('ttp://', s);
1149                 wkIdx[2] := AnsiPos('tp://', s);
1150                 wkIdx[3] := AnsiPos('ms-help://', s);
1151                 wkIdx[4] := AnsiPos('p://', s);
1152                 wkIdx[5] := AnsiPos('https://', s);
1153                 wkIdx[6] := AnsiPos('www.', s);
1154                 wkIdx[7] := AnsiPos('ftp://', s);
1155                 wkIdx[8] := AnsiPos('news://', s);
1156                 wkIdx[9] := AnsiPos('rtsp://', s);
1157
1158                 idx := MaxInt;
1159                 for i := 0 to 8 do
1160                         if wkIdx[i] <> 0 then idx := Min(wkIdx[i], idx);
1161
1162                 if idx = MaxInt then begin
1163                         //\83\8a\83\93\83N\82ª\96³\82¢\82æ\81B
1164                         Result := Result + s;
1165                         Break;
1166                 end;
1167
1168                 if (idx > 1) and (Copy(s, idx - 1, 1) = '"') then begin
1169                         //\8aù\82É\83\8a\83\93\83N\83^\83O\82ª\82Â\82¢\82Ä\82¢\82é\82Á\82Û\82¢\82Æ\82«\82Í\83\80\83V
1170                         Result := Result + Copy(s, 0, idx + Length('http://') - 1);
1171                         s := Copy(s, idx + Length('http://'), length(s));
1172                         Continue;
1173                 end;
1174
1175                 Result := Result + Copy(s, 0, idx - 1);
1176
1177                 s := Copy(s, idx, length(s));
1178
1179                 for i := 0 to Length(s) do begin
1180                         idx := AnsiPos(s[i + 1], URL_CHAR);
1181                         if (idx = 0) or (i = (Length(s))) then begin
1182                                 //URL\82\82á\82È\82¢\95\8e\9a\94­\8c©\81I\82Æ\82©\81A\95\8e\9a\82ª\82È\82­\82È\82Á\82½\81B
1183                                 url := Copy(s, 0, i);
1184
1185                                 if AnsiPos('ttp://', url) = 1 then
1186                                         href := 'h' + url
1187                                 else if AnsiPos('tp://', url) = 1 then
1188                                         href := 'ht' + url
1189                                 else if AnsiPos('p://', url) = 1 then
1190                                         href := 'htt' + url
1191                                 else if AnsiPos('www.', url) = 1 then
1192                                         href := 'http://' + url
1193                                 else
1194                                         href := url;
1195                                 Result := Result + '<a href="' + href + '" target="_blank">' + url + '</a>';
1196                                 s := Copy(s, i + 1, Length(s));
1197                                 Break;
1198                         end;
1199                 end;
1200         end;
1201 end;
1202
1203 (*************************************************************************
1204  *\83T\83u\83W\83F\83N\83g\88ê\8ds\82ð\95ª\8a\84
1205  *************************************************************************)
1206 function TGikoSys.DivideSubject(Line: string): TSubjectRec;
1207 var
1208         i: integer;
1209         ws: WideString;
1210         Delim: string;
1211         LeftK: string;
1212         RightK: string;
1213 begin
1214         Result.FCount := 0;
1215
1216         if Pos('<>', Line) = 0 then
1217                 Delim := ','
1218         else
1219                 Delim := '<>';
1220
1221         Result.FFileName := GetTokenIndex(Line, Delim, 0);
1222         Result.FTitle := GetTokenIndex(Line, Delim, 1);
1223
1224         ws := Trim(Result.FTitle);
1225
1226         if Copy(ws, Length(ws), 1) = ')' then begin
1227                 LeftK := '(';
1228                 RightK := ')';
1229         end else if Copy(ws, Length(ws), 1) = '\81j' then begin
1230                 LeftK := '\81i';
1231                 RightK := '\81j';
1232         end else if Copy(ws, Length(ws), 1) = '<' then begin
1233                 LeftK := '<';
1234                 RightK := '>';
1235         end;
1236
1237         for i := Length(ws) - 1 downto 1 do begin
1238                 if ws[i] = LeftK then begin
1239                         ws := Copy(ws, i + 1, Length(ws) - i - 1);
1240                         if IsNumeric(ws) then
1241                                 Result.FCount := StrToInt(ws);
1242                         Result.FTitle := Trim(StringReplace(Result.FTitle, LeftK + ws + RightK, '', [rfReplaceAll]));
1243                         break;
1244                 end;
1245         end;
1246 end;
1247
1248 (*************************************************************************
1249  * dat\83t\83@\83C\83\8b\82Ì\88ê\83\89\83C\83\93\82ð\95ª\89ð
1250  *************************************************************************)
1251 function TGikoSys.DivideStrLine(Line: string): TResRec;
1252 var
1253         Delim: string;
1254                 bufbody : String;
1255 begin
1256         if Pos('<>', Line) = 0 then begin
1257                 Delim := ',';
1258                 Result.FType := glt2chOld;
1259         end else begin
1260                 Delim := '<>';
1261                 Result.FType := glt2chNew;
1262         end;
1263         Result.FName := Trim(GetTokenIndex(Line, Delim, 0));
1264         Result.FMailTo := Trim(GetTokenIndex(Line, Delim, 1));
1265         Result.FDateTime := Trim(GetTokenIndex(Line, Delim, 2));
1266         bufBody := Trim(GetTokenIndex(Line, Delim, 3));
1267         if bufbody = '' then begin
1268                 Insert('&nbsp;',bufbody, 1);
1269         end;
1270         Result.FBody := bufBody;
1271         Result.FTitle := Trim(GetTokenIndex(Line, Delim, 4));
1272
1273 end;
1274
1275 (*************************************************************************
1276  * URL\82©\82çBBSID\82ð\8eæ\93¾
1277  *************************************************************************)
1278 function TGikoSys.UrlToID(url: string): string;
1279 var
1280         i: integer;
1281 begin
1282         Result := '';
1283         url := Trim(url);
1284
1285         if url = '' then Exit;
1286
1287         url := Copy(url, 0, Length(url) - 1);
1288         for i := Length(url) downto 0 do begin
1289                 if url[i] = '/' then begin
1290                         Result := Copy(url, i + 1, Length(url));
1291                         Break;
1292                 end;
1293         end;
1294 end;
1295
1296 (*************************************************************************
1297  *URL\82©\82çBBSID\88È\8aO\82Ì\95\94\95ª(http://teri.2ch.net/)\82ð\8eæ\93¾
1298  *************************************************************************)
1299 function TGikoSys.UrlToServer(url: string): string;
1300 var
1301         i: integer;
1302         wsURL: WideString;
1303 begin
1304         Result := '';
1305         wsURL := url;
1306         wsURL := Trim(wsURL);
1307
1308         if wsURL = '' then exit;
1309
1310         if Copy(wsURL, Length(wsURL), 1) = '/' then
1311                 wsURL := Copy(wsURL, 0, Length(wsURL) - 1);
1312
1313         for i := Length(wsURL) downto 0 do begin
1314                 if wsURL[i] = '/' then begin
1315                         Result := Copy(wsURL, 0, i);
1316                         break;
1317                 end;
1318         end;
1319 end;
1320
1321 (*************************************************************************
1322  *\83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
1323  *************************************************************************)
1324 function TGikoSys.DirectoryExistsEx(const Name: string): Boolean;
1325 var
1326         Code: Integer;
1327 begin
1328         Code := GetFileAttributes(PChar(Name));
1329         Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
1330 end;
1331
1332 (*************************************************************************
1333  *\83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
1334  *************************************************************************)
1335 function TGikoSys.ForceDirectoriesEx(Dir: string): Boolean;
1336 begin
1337         Result := True;
1338         if Length(Dir) = 0 then
1339                 raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
1340         Dir := ExcludeTrailingPathDelimiter(Dir);
1341         if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
1342                 or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
1343         Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
1344 end;
1345
1346 (*************************************************************************
1347  *\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
1348  *FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1349  *************************************************************************)
1350 function TGikoSys.StrTokFirst(const s:string; const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1351 begin
1352         Rec.Str := s;
1353         Rec.Pos := 1;
1354         Result := StrTokNext(sep, Rec);
1355 end;
1356
1357 (*************************************************************************
1358  *\95\8e\9a\97ñ\82©\82ç\83g\81[\83N\83\93\82Ì\90Ø\82è\8fo\82µ
1359  *FDelphi\82©\82ç\82Ì\83p\83N\83\8a
1360  *************************************************************************)
1361 function TGikoSys.StrTokNext(const sep: TStrTokSeparator; var Rec: TStrTokRec): string;
1362 var
1363         Len, I: Integer;
1364 begin
1365         with Rec do     begin
1366                 Len := Length(Str);
1367                 Result := '';
1368                 if Len >= Pos then begin
1369                         while (Pos <= Len) and (Str[Pos] in sep) do begin
1370                          Inc(Pos);
1371                         end;
1372                         I := Pos;
1373                         while (Pos<= Len) and not (Str[Pos] in sep) do begin
1374                                 if IsDBCSLeadByte(Byte(Str[Pos])) then begin
1375                                         Inc(Pos);
1376                                 end;
1377                                 Inc(Pos);
1378                         end;
1379                         Result := Copy(Str, I, Pos - I);
1380                         while (Pos <= Len) and (Str[Pos] in sep) do begin// \82±\82ê\82Í\82¨\8dD\82Ý
1381                                 Inc(Pos);
1382                         end;
1383                 end;
1384         end;
1385 end;
1386
1387 (*************************************************************************
1388  *\83t\83@\83C\83\8b\83T\83C\83Y\8eæ\93¾
1389  *************************************************************************)
1390 function TGikoSys.GetFileSize(FileName : string): longint;
1391 var
1392         F : File;
1393 begin
1394         try
1395                 if not FileExists(FileName) then begin
1396                         Result := 0;
1397                         Exit;
1398                 end;
1399                 Assign(F, FileName);
1400                 Reset(F, 1);
1401                 Result := FileSize(F);
1402                 CloseFile(F);
1403         except
1404                 Result := 0;
1405         end;
1406 end;
1407
1408 (*************************************************************************
1409  *\83t\83@\83C\83\8b\8ds\90\94\8eæ\93¾
1410  *************************************************************************)
1411 function TGikoSys.GetFileLineCount(FileName : string): longint;
1412 var
1413         sl: TStringList;
1414 begin
1415         sl := TStringList.Create;
1416         try
1417                 try
1418                         sl.LoadFromFile(FileName);
1419                         Result := sl.Count;
1420                 except
1421                         Result := 0;
1422                 end;
1423         finally
1424                 sl.Free;
1425         end;
1426
1427 end;
1428
1429 (*************************************************************************
1430  *\83X\83\8c\83b\83h\83t\83@\83C\83\8b\82©\82ç\8ew\92è\8ds\82ð\8eæ\93¾
1431  *************************************************************************)
1432 function TGikoSys.ReadThreadFile(FileName: string; Line: Integer): string;
1433 const
1434         BUFFER_SIZE = 1024;
1435 var
1436         f: TextFile;
1437         s: string;
1438         num: Integer;
1439         ArrBuff: array [1..BUFFER_SIZE] of Char;
1440 begin
1441         Result := '';
1442         if FileExists(FileName) then begin
1443                 AssignFile(f, FileName);
1444                 System.SetTextBuf(f, ArrBuff);
1445                 try
1446                         Reset(f);
1447                         num := 1;
1448                         while not Eof(f) do begin
1449                                 Readln(f, s);
1450                                 if Line = num then begin
1451                                         Result := s;
1452 //                                      CloseFile(f);
1453                                         Break;
1454                                 end;
1455                                 inc(num);
1456                         end;
1457                 finally
1458                         CloseFile(f);
1459                 end;
1460         end;
1461 end;
1462
1463 (*************************************************************************
1464  *\83V\83X\83e\83\80\83\81\83j\83\85\81[\83t\83H\83\93\83g\82Ì\91®\90«\82ð\8eæ\93¾
1465  *************************************************************************)
1466 procedure TGikoSys.MenuFont(Font: TFont);
1467 var
1468         lf: LOGFONT;
1469         nm: NONCLIENTMETRICS;
1470 begin
1471         nm.cbSize := sizeof(NONCLIENTMETRICS);
1472
1473         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @nm, 0);
1474         lf := nm.lfMenuFont;
1475
1476         Font.Name := lf.lfFaceName;
1477         Font.Height := lf.lfHeight;
1478         Font.Style := [];
1479         if lf.lfWeight >= 700 then
1480                 Font.Style := Font.Style + [fsBold];
1481         if lf.lfItalic = 1 then
1482                 Font.Style := Font.Style + [fsItalic];
1483 end;
1484
1485 (*************************************************************************
1486  *
1487  *\82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1488  *************************************************************************)
1489 function TGikoSys.RemoveToken(var s: string; delimiter: string): string;
1490 var
1491         p: Integer;
1492 begin
1493         p := AnsiPos(delimiter, s);
1494         if p = 0 then
1495                 Result := s
1496         else
1497                 Result := Copy(s, 1, p - 1);
1498         s := Copy(s, Length(Result) + Length(delimiter) + 1, Length(s));
1499 end;
1500
1501 (*************************************************************************
1502  *
1503  *\82Ç\82±\82©\82Ì\83T\83C\83g\82©\82ç\82Ì\83p\83N\83\8a
1504  *************************************************************************)
1505 function TGikoSys.GetTokenIndex(s: string; delimiter: string; index: Integer): string;
1506 var
1507         i: Integer;
1508 begin
1509         Result := '';
1510         for i := 0 to index do
1511                 Result := RemoveToken(s, delimiter);
1512 end;
1513
1514 (*************************************************************************
1515  *
1516  *************************************************************************)
1517 function TGikoSys.DeleteLink(const s: string): string;
1518 var
1519         s1: string;
1520         s2: string;
1521         idx: Integer;
1522         i: Integer;
1523 begin
1524         i := 0;
1525         Result := '';
1526         while True do begin
1527                 s1 := GetTokenIndex(s, '<a href="', i);
1528                 s2 := GetTokenIndex(s, '<a href="', i + 1);
1529
1530                 idx := Pos('">', s1);
1531                 if idx <> 0 then
1532                         Delete(s1, 1, idx + 1);
1533                 idx := Pos('">', s2);
1534                 if idx <> 0 then
1535                         Delete(s2, 1, idx + 1);
1536
1537                 Result := Result + s1 + s2;
1538
1539                 if s2 = '' then
1540                         Break;
1541
1542                 inc(i, 2);
1543         end;
1544 end;
1545
1546 //\83C\83\93\83f\83b\83N\83X\96¢\8dX\90V\83o\83b\83t\83@\82ð\83t\83\89\83b\83V\83\85\81I
1547 {procedure TGikoSys.FlashExitWrite;
1548 var
1549         i: Integer;
1550 begin
1551         //\83X\83\8c\83b\83h\83f\81[\83^\83t\83@\83C\83\8b\82ð\8dX\90V
1552         for i := 0 to FExitWrite.Count - 1 do
1553                 WriteThreadDat(FExitWrite[i]);
1554         FExitWrite.Clear;
1555 end;}
1556
1557 (*************************************************************************
1558  *\83X\83\8c\96¼\82È\82Ç\82ð\92Z\82¢\96¼\91O\82É\95Ï\8a·\82·\82é
1559  *from HotZonu
1560  *************************************************************************)
1561 function TGikoSys.GetShortName(const LongName: string; ALength: integer): string;
1562 const
1563         ERASECHAR : array [1..39] of string =
1564                 ('\81\99','\81\9a','\81¡','\81 ','\81\9f','\81\9e','\81Q','\81\94','\81£','\81¥',
1565                  '\81¢','\81¤','\81\9c','\81\9b','\81\9d','\81y','\81z','\81ô','\81s','\81t',
1566                  '\81g','\81h','\81k','\81l','\81e','\81f','\81\83','\81\84','\81á','\81â',
1567                  '\81o','\81p','\81q','\81r','\81w','\81x','\81¬','\81c', '\81@');
1568 var
1569         Chr : array [0..255]    of      char;
1570         S : string;
1571         i : integer;
1572 begin
1573         s := Trim(LongName);
1574         if (Length(s) <= ALength) then begin
1575                 Result := s;
1576         end else begin
1577                 S := s;
1578                 for i := Low(ERASECHAR) to      High(ERASECHAR) do      begin
1579                         S := StringReplace(S, ERASECHAR[i], '', [rfReplaceAll]);
1580                 end;
1581                 if (Length(S) <= ALength) then begin
1582                         Result := S;
1583                 end else begin
1584                         Windows.LCMapString(
1585                                         GetUserDefaultLCID(),
1586                                         LCMAP_HALFWIDTH,
1587                                         PChar(S),
1588                                         Length(S) + 1,
1589                                         chr,
1590                                         Sizeof(chr)
1591                                         );
1592                         S := Chr;
1593                         S := Copy(S,1,ALength);
1594                         while true do begin
1595                                 if (ByteType(S, Length(S)) = mbLeadByte ) then begin
1596                                         S := Copy(S, 1, Length(S) - 1);
1597                                 end else begin
1598                                         Break;
1599                                 end;
1600                         end;
1601                         Result := S;
1602                 end;
1603         end;
1604 end;
1605
1606 (*************************************************************************
1607  *
1608  * from HotZonu
1609  *************************************************************************)
1610 function TGikoSys.ConvRes(const Body, Bbs, Key,
1611         ParamBBS, ParamKey, ParamStart, ParamTo, ParamNoFirst, ParamTrue : string): string;
1612 type
1613         PIndex = ^TIndex;
1614         TIndex = record
1615                 FIndexFrom      : integer;
1616                 FIndexTo                : integer;
1617                 FNo                              : string;
1618         end;
1619 const
1620         GT      = '&gt;';
1621         SN      = '0123456789-';
1622         ZN      = '\82O\82P\82Q\82R\82S\82T\82U\82V\82W\82X\81|';
1623 var
1624         i : integer;
1625         s,r : string;
1626         b : TMbcsByteType;
1627         sw: boolean;
1628         sp: integer;
1629         No: string;
1630         sx: string;
1631         List: TList;
1632         oc      : string;
1633         st, et: string;
1634         chk : boolean;
1635         al : boolean;
1636         procedure Add(IndexFrom, IndexTo: integer; const No: string);
1637         var
1638                 FIndex : PIndex;
1639         begin
1640                 New(FIndex);
1641                 FIndex.FIndexFrom       := IndexFrom;
1642                 FIndex.FIndexTo         := IndexTo;
1643                 FIndex.FNo                               := No;
1644                 List.Add(FIndex);
1645         end;
1646         function ChooseString(const Text, Separator: string; Index: integer): string;
1647         var
1648                 S : string;
1649                 i, p : integer;
1650         begin
1651                 S :=    Text;
1652                 for i :=        0 to    Index - 1 do    begin
1653                         if      (AnsiPos(Separator, S) = 0) then        S :=    ''
1654                         else    S :=    Copy(S, AnsiPos(Separator, S) + Length(Separator), Length(S));
1655                 end;
1656                 p :=    AnsiPos(Separator, S);
1657                 if      (p > 0) then    Result  :=      Copy(S, 1, p - 1) else Result :=        S;
1658         end;
1659 begin
1660         { v1.0 b2 - 03 }
1661         s        :=     Body;
1662         r        :=     Body;
1663         i        :=     1;
1664         sw      :=      False;
1665         No      :=      '';
1666         List:=  TList.Create;
1667         oc      :=      '';
1668         sp      :=      0;
1669         chk :=  False;
1670         al      :=      False;
1671         while true      do      begin
1672                 b :=    ByteType(s, i);
1673                 case    b of
1674                         mbSingleByte    : begin
1675                                 if      (not sw) and (Copy(s,i,8) = GT + GT) then       begin
1676                                         if      (AnsiPos('<A HREF', AnsiUpperCase(oc)) = 0) then        begin
1677                                                 sw      :=      True;
1678                                                 sp      :=      i;
1679                                                 i :=    i + 7;
1680                                                 oc:='';
1681                                                 chk :=  True;
1682                                         end;
1683                                 end else
1684                                 if      (not sw) and (Copy(s,i,8) = GT + GT) then       begin
1685                                         if      (AnsiPos('<A HREF', AnsiUpperCase(oc)) = 1) then        begin
1686                                                 i :=    i + 7;
1687                                                 oc:='';
1688                                                 chk :=  True;
1689                                         end;
1690                                 end else
1691                                 if      (not sw) and (Copy(s,i,4) = GT) then    begin
1692                                         if      (AnsiPos('<A HREF', AnsiUpperCase(oc)) = 0) then        begin
1693                                                 sw      :=      True;
1694                                                 sp      :=      i;
1695                                                 i :=    i + 3;
1696                                                 oc:='';
1697                                                 chk :=  True;
1698                                         end;
1699                                 end else
1700                                 if      ((not sw) and (Copy(s,i,1) = ',')) or
1701                                                 ((not sw) and (Copy(s,i,1) = '=')) then begin
1702                                         if      ((not Chk) and (AnsiLowerCase(oc) = '</a>')) or
1703                                                         ((Chk) and      (oc = '')) or
1704                                                         ((not Chk) and (al)) then
1705                                         begin
1706                                                 sw      :=      True;
1707                                                 sp      :=      i;
1708                                                 //i :=  i + 1;
1709                                                 oc:='';
1710                                         end;
1711                                 end else
1712                                 if      (sw) then begin
1713                                         sx      :=      Copy(s,i,1);
1714                                         if      (AnsiPos(sx, SN) > 0)   then    begin
1715                                                 No      :=      No      + sx;
1716                                         end else begin
1717                                                 if      (No <> '') and (No <> '-')       then   begin
1718                                                         Add(sp, i, No);
1719                                                         al := True;
1720                                                 end;
1721                                                 sw      :=      False;
1722                                                 //
1723                                                 i := i - 1;
1724                                                 //
1725                                                 No      := '';
1726                                                 oc:='';
1727                                                 //chk :=        False;
1728                                         end;
1729                                 end else begin
1730                                         if      Copy(s,i,1) = '<' then  oc      :=      '';
1731                                         oc      :=      oc + Copy(s,i,1);
1732                                         chk :=  False;
1733                                         al      :=      False;
1734                                 end;
1735                         end;
1736                         mbLeadByte      : begin
1737                                 if      (not sw) and (Copy(s,i,4) = '\81\84\81\84') then        begin
1738                                         sw      :=      True;
1739                                         sp      :=      i;
1740                                         i :=    i + 3;
1741                                         chk :=  True;
1742                                 end else
1743                                 if      (not sw) and (Copy(s,i,2) = '\81\84') then  begin
1744                                         sw      :=      True;
1745                                         sp      :=      i;
1746                                         i :=    i + 1;
1747                                         chk :=  True;
1748                                 end else
1749                                 if      (sw) then begin
1750                                         sx      :=      Copy(s,i,2);
1751                                         if      (AnsiPos(sx, ZN) > 0)   then    begin
1752                                                 No      :=      No      + ZenToHan(sx);
1753                                         end else begin
1754                                                 if      (No <> '') and (No <> '-')      and (No <> '\81|') then   begin
1755                                                         Add(sp, i, No);
1756                                                 end;
1757                                                 sw      :=      False;
1758                                                 i := i - 1;
1759                                                 No      :=      '';
1760                                         end;
1761                                 end else begin
1762                                         oc      :=      '';
1763                                         chk :=  False;
1764                                 end;
1765                                 al      :=      False;
1766                         end;
1767                 end;
1768                 inc(i);
1769                 if      (i > Length(Body))      then    begin
1770                         if      (sw)    then    begin
1771                                 if      (No <> '')      then    Add(sp, i, No);
1772                         end;
1773                         Break;
1774                 end;
1775         end;
1776         for i :=        List.Count - 1  downto  0 do    begin
1777                 if      (AnsiPos('-', PIndex(List[i]).FNo) > 0) then    begin
1778                         st      :=      ChooseString(PIndex(List[i]).FNo, '-', 0);
1779                         et      :=      ChooseString(PIndex(List[i]).FNo, '-', 1);
1780                 end else begin
1781                         st      :=      PIndex(List[i]).FNo;
1782                         et      :=      PIndex(List[i]).FNo;
1783                 end;
1784                 r :=    Copy(r,0, PIndex(List[i]).FIndexFrom - 1) +
1785                                         Format('<a href="../test/read.cgi?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s" target="_blank">',
1786                                                                 [ParamBBS, Bbs, ParamKey, Key, ParamStart, st, ParamTo, et, ParamNoFirst, ParamTrue]) +
1787                                         Copy(r,PIndex(List[i]).FIndexFrom, PIndex(List[i]).FIndexTo - PIndex(List[i]).FIndexFrom) + '</A>' +
1788                                         Copy(r,PIndex(List[i]).FIndexTo,Length(r));
1789                 Dispose(PIndex(List[i]));
1790         end;
1791         List.Free;
1792         Result  :=      r;
1793 end;
1794
1795 (*************************************************************************
1796  * \91S\8ap\81¨\94¼\8ap
1797  * from HotZonu
1798  *************************************************************************)
1799 function TGikoSys.ZenToHan(const s: string): string;
1800 var
1801         Chr: array [0..255] of char;
1802 begin
1803         Windows.LCMapString(
1804                  GetUserDefaultLCID(),
1805 //               LCMAP_HALFWIDTH,
1806                  LCMAP_HALFWIDTH or LCMAP_KATAKANA or LCMAP_LOWERCASE,
1807                  PChar(s),
1808                  Length(s) + 1,
1809                  chr,
1810                  Sizeof(chr)
1811                  );
1812         Result := Chr;
1813 end;
1814
1815 (*************************************************************************
1816  * \91S\8ap\94¼\8ap\82Ð\82ç\82ª\82È\82©\82½\82©\82È\82ð\8bæ\95Ê\82µ\82È\82¢\90¦\82¢Pos
1817  *************************************************************************)
1818 function TGikoSys.VaguePos(const Substr, S: string): Integer;
1819 begin
1820         Result := Pos(ZenToHan(Substr), ZenToHan(S));
1821 end;
1822
1823 function TGikoSys.BoolToInt(b: Boolean): Integer;
1824 begin
1825         Result := IfThen(b, 1, 0);
1826 end;
1827
1828 function TGikoSys.IntToBool(i: Integer): Boolean;
1829 begin
1830         Result := i = 1;
1831 end;
1832
1833 //gzip\82Å\88³\8fk\82³\82ê\82½\82Ì\82ð\96ß\82·
1834 function TGikoSys.GzipDecompress(ResStream: TStream; ContentEncoding: string): string;
1835 const
1836         BUF_SIZE = 4096;
1837 var
1838         GZipStream: TGzipDecompressStream;
1839         TextStream: TStringStream;
1840         buf: array[0..BUF_SIZE - 1] of Byte;
1841         cnt: Integer;
1842         s: string;
1843         i: Integer;
1844 begin
1845         Result := '';
1846         TextStream := TStringStream.Create('');
1847         try
1848 //\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¢)
1849 //              if LowerCase(Trim(ContentEncoding)) = 'gzip' then begin
1850                 if AnsiPos('gzip', LowerCase(Trim(ContentEncoding))) > 0 then begin
1851                         ResStream.Position := 0;
1852                         GZipStream := TGzipDecompressStream.Create(TextStream);
1853                         try
1854                                 repeat
1855                                         FillChar(buf, BUF_SIZE, 0);
1856                                         cnt := ResStream.Read(buf, BUF_SIZE);
1857                                         if cnt > 0 then
1858                                                 GZipStream.Write(buf, BUF_SIZE);
1859                                 until cnt = 0;
1860                         finally
1861                                 GZipStream.Free;
1862                         end;
1863                 end else begin
1864                         ResStream.Position := 0;
1865                         repeat
1866                                 FillChar(buf, BUF_SIZE, 0);
1867                                 cnt := ResStream.Read(buf, BUF_SIZE);
1868                                 if cnt > 0 then
1869                                         TextStream.Write(buf, BUF_SIZE);
1870                         until cnt = 0;
1871                 end;
1872
1873                 //NULL\95\8e\9a\82ð"*"\82É\82·\82é
1874                 s := TextStream.DataString;
1875                 i := Length(s);
1876                 while (i > 0) and (s[i] = #0) do
1877                         Dec(i);
1878                 s := Copy(s, 1, i);
1879
1880                 i := Pos(#0, s);
1881                 while i <> 0 do begin
1882                         s[i] := '*';
1883                         i := Pos(#0, s);
1884                 end;
1885                 Result := s;
1886         finally
1887                 TextStream.Free;
1888         end;
1889 end;
1890
1891 procedure TGikoSys.LoadKeySetting(ActionList: TActionList);
1892 const
1893         STD_SEC = 'KeySetting';
1894 var
1895         i: Integer;
1896         ini: TMemIniFile;
1897         ActionName: string;
1898         ActionKey: Integer;
1899         SecList: TStringList;
1900         Component: TComponent;
1901 begin
1902         if not FileExists(GetConfigDir + KEY_SETTING_FILE_NAME) then
1903                 Exit;
1904         SecList := TStringList.Create;
1905         ini := TMemIniFile.Create(GetConfigDir + KEY_SETTING_FILE_NAME);
1906         try
1907                 ini.ReadSection(STD_SEC, SecList);
1908                 for i := 0 to SecList.Count - 1 do begin
1909                         ActionName := SecList[i];
1910                         ActionKey := ini.ReadInteger(STD_SEC, ActionName, -1);
1911                         if ActionKey <> -1 then begin
1912                                 Component := ActionList.Owner.FindComponent(ActionName);
1913                                 if TObject(Component) is TAction then begin
1914                                         TAction(Component).ShortCut := ActionKey;
1915                                 end;
1916                         end;
1917                 end;
1918         finally
1919                 ini.Free;
1920                 SecList.Free;
1921         end;
1922 end;
1923
1924 procedure TGikoSys.SaveKeySetting(ActionList: TActionList);
1925 const
1926         STD_SEC = 'KeySetting';
1927 var
1928         i: Integer;
1929         ini: TMemIniFile;
1930 begin
1931         ini := TMemIniFile.Create(GetConfigDir + KEY_SETTING_FILE_NAME);
1932         try
1933                 for i := 0 to ActionList.ActionCount - 1 do begin
1934                         if ActionList.Actions[i].Tag = -1 then
1935                                 Continue;
1936                         ini.WriteInteger(STD_SEC, ActionList.Actions[i].Name, TAction(ActionList.Actions[i]).ShortCut);
1937                 end;
1938                 ini.UpdateFile;
1939         finally
1940                 ini.Free;
1941         end;
1942 end;
1943
1944 //
1945 procedure TGikoSys.CreateProcess(const AppPath: string; const Param: string);
1946 var
1947         PI: TProcessInformation;
1948         SI: TStartupInfo;
1949         Path: string;
1950 begin
1951         Path := '"' + AppPath + '"';
1952         if Param <> '' then
1953                 Path := Path + ' ' + Param;
1954
1955         SI.Cb := SizeOf(Si);
1956         SI.lpReserved   := nil;
1957         SI.lpDesktop     := nil;
1958         SI.lpTitle               := nil;
1959         SI.dwFlags               := 0;
1960         SI.cbReserved2 := 0;
1961         SI.lpReserved2 := nil;
1962         SI.dwysize               := 0;
1963         Windows.CreateProcess(nil,
1964                                                                 PChar(Path),
1965                                                                 nil,
1966                                                                 nil,
1967                                                                 False,
1968                                                                 0,
1969                                                                 nil,
1970                                                                 nil,
1971                                                                 SI,
1972                                                                 PI);
1973 end;
1974
1975 procedure TGikoSys.OpenBrowser(URL: string; BrowserType: TGikoBrowserType);
1976 begin
1977         case BrowserType of
1978                 gbtIE:
1979                         HlinkNavigateString(nil, PWideChar(WideString(URL)));
1980                 gbtUserApp, gbtAuto:
1981                         if (Setting.URLApp) and (FileExists(Setting.URLAppFile)) then
1982                                 GikoSys.CreateProcess(Setting.URLAppFile, URL)
1983                         else
1984                                 HlinkNavigateString(nil, PWideChar(WideString(URL)));
1985         end;
1986 end;
1987
1988 function TGikoSys.HTMLDecode(const AStr: String): String;
1989 var
1990         Sp, Rp, Cp, Tp: PChar;
1991         S: String;
1992         I, Code: Integer;
1993         Num: Boolean;
1994 begin
1995         SetLength(Result, Length(AStr));
1996         Sp := PChar(AStr);
1997         Rp := PChar(Result);
1998         //Cp := Sp;
1999         try
2000                 while Sp^ <> #0 do begin
2001                         case Sp^ of
2002                                 '&': begin
2003                                                          //Cp := Sp;
2004                                                          Inc(Sp);
2005                                                          case Sp^ of
2006                                                                  'a': if AnsiStrPos(Sp, 'amp;') = Sp then
2007                                                                                         begin
2008                                                                                                 Inc(Sp, 3);
2009                                                                                                 Rp^ := '&';
2010                                                                                         end;
2011                                                                  'l',
2012                                                                  'g': if (AnsiStrPos(Sp, 'lt;') = Sp) or (AnsiStrPos(Sp, 'gt;') = Sp) then
2013                                                                                         begin
2014                                                                                                 Cp := Sp;
2015                                                                                                 Inc(Sp, 2);
2016                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do
2017                                                                                                         Inc(Sp);
2018                                                                                                 if Cp^ = 'l' then
2019                                                                                                         Rp^ := '<'
2020                                                                                                 else
2021                                                                                                         Rp^ := '>';
2022                                                                                         end;
2023                                                                  'q': if AnsiStrPos(Sp, 'quot;') = Sp then
2024                                                                                         begin
2025                                                                                                 Inc(Sp,4);
2026                                                                                                 Rp^ := '"';
2027                                                                                         end;
2028                                                                  '#': begin
2029                                                                                                 Tp := Sp;
2030                                                                                                 Inc(Tp);
2031                                                                                                 Num := IsNumeric(Copy(Tp, 1, 1));
2032                                                                                                 while (Sp^ <> ';') and (Sp^ <> #0) do begin
2033                                                                                                         if (Num) and (not IsNumeric(Copy(Sp, 1, 1))) then
2034                                                                                                                 Break;
2035                                                                                                         Inc(Sp);
2036                                                                                                 end;
2037                                                                                                 SetString(S, Tp, Sp - Tp);
2038                                                                                                 Val(S, I, Code);
2039                                                                                                 Rp^ := Chr((I));
2040                                                                                         end;
2041                                                          //      else
2042                                                                          //raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
2043                                                                                  //[Cp^ + Sp^, Cp - PChar(AStr)])
2044                                                          end;
2045                                          end
2046                         else
2047                                 Rp^ := Sp^;
2048                         end;
2049                         Inc(Rp);
2050                         Inc(Sp);
2051                 end;
2052         except
2053 //              on E:EConvertError do
2054 //                      raise EConvertError.CreateFmt(sInvalidHTMLEncodedChar,
2055 //                              [Cp^ + Sp^, Cp - PChar(AStr)])
2056         end;
2057         SetLength(Result, Rp - PChar(Result));
2058 end;
2059
2060 function TGikoSys.GetHRefText(s: string): string;
2061 var
2062         Index: Integer;
2063         Index2: Integer;
2064 begin
2065         Result := '';
2066         s := Trim(s);
2067         if s = '' then
2068                 Exit;
2069
2070         Index := AnsiPos('href', LowerCase(s));
2071         if Index = 0 then
2072                 Exit;
2073         s := Trim(Copy(s, Index + 4, Length(s)));
2074         s := Trim(Copy(s, 2, Length(s)));
2075
2076         //\8en\82ß\82Ì\95\8e\9a\82ª'"'\82È\82ç\8eæ\82è\8f\9c\82­
2077         if Copy(s, 1, 1) = '"' then begin
2078                 s := Trim(Copy(s, 2, Length(s)));
2079         end;
2080
2081         Index := AnsiPos('"', s);
2082         if Index <> 0 then begin
2083                 //'"'\82Ü\82ÅURL\82Æ\82·\82é
2084                 s := Copy(s, 1, Index - 1);
2085         end else begin
2086                 //'"'\82ª\96³\82¯\82ê\82Î\83X\83y\81[\83X\82©">"\82Ì\91\81\82¢\95û\82Ü\82Å\82ðURL\82Æ\82·\82é
2087                 Index := AnsiPos(' ', s);
2088                 Index2 := AnsiPos('>', s);
2089                 if Index = 0 then
2090                         Index := Index2;
2091                 if Index > Index2 then
2092                         Index := Index2;
2093                 if Index <> 0 then
2094                         s := Copy(s, 1, Index - 1)
2095                 else
2096                         //\82±\82ê\88È\8fã\82à\82¤\92m\82ç\82ñ\82Ê
2097                         ;
2098         end;
2099         Result := Trim(s);
2100 end;
2101
2102 //\83z\83X\83g\96¼\82ª\82Q\82\83\82\88\82©\82Ç\82¤\82©\83`\83F\83b\83N\82·\82é
2103 function TGikoSys.Is2chHost(Host: string): Boolean;
2104 const
2105         HOST_NAME: array[0..1] of string = ('2ch.net', 'bbspink.com');
2106 var
2107         i: Integer;
2108 //      Len: Integer;
2109 begin
2110         Result := False;
2111         OutputDebugString(pchar(HOST_NAME[0]));
2112         for i := 0 to Length(HOST_NAME) - 1 do begin
2113 //              Len := Length(HOST_NAME[i]);
2114                 if AnsiPos(HOST_NAME[i], Host) = (Length(Host) - Length(HOST_NAME[i]) + 1) then begin
2115                         Result := True;
2116                         Exit;
2117                 end;
2118         end;
2119 end;
2120
2121 function TGikoSys.Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
2122 const
2123         READ_PATH: string =                     '/test/read.cgi/';
2124         OLD_READ_PATH: string =         '/test/read.cgi?';
2125         KAKO_PATH: string =                     '/kako/';
2126 var
2127         Index: Integer;
2128         s: string;
2129         SList: TStringList;
2130 begin
2131         BBSID := '';
2132         BBSKey := '';
2133         Result := False;
2134
2135         Index := AnsiPos(READ_PATH, path);
2136         if Index <> 0 then begin
2137                 s := Copy(path, Length(READ_PATH) + 1, Length(path));
2138                 BBSID := GetTokenIndex(s, '/', 0);
2139                 BBSKey := GetTokenIndex(s, '/', 1);
2140                 if BBSKey = '' then
2141                         BBSKey := Document;
2142                 Result := (BBSID <> '') or (BBSKey <> '');
2143                 Exit;
2144         end;
2145         Index := AnsiPos(KAKO_PATH, path);
2146         if Index <> 0 then begin
2147                 s := Copy(path, 2, Length(path));
2148                 BBSID := GetTokenIndex(s, '/', 0);
2149                 if (BBSID = 'log') and (GetTokenIndex(s, '/', 2) = 'kako') then
2150                         BBSID := GetTokenIndex(s, '/', 1);
2151                 BBSKey := ChangeFileExt(Document, '');
2152                 Result := (BBSID <> '') or (BBSKey <> '');
2153                 Exit;
2154         end;
2155         Index := AnsiPos('read.cgi?', URL);
2156         if Index <> 0 then begin
2157                 SList := TStringList.Create;
2158                 try
2159                         try
2160 //                              s := HTMLDecode(Document);
2161                                 ExtractHTTPFields(['?', '&'], [], PChar(URL), SList, False);
2162                                 BBSID := SList.Values['bbs'];
2163                                 BBSKey := SList.Values['key'];
2164                                 Result := (BBSID <> '') or (BBSKey <> '');
2165                                 Exit;
2166                         except
2167                                 Exit;
2168                         end;
2169                 finally
2170                         SList.Free;
2171                 end;
2172         end;
2173 end;
2174
2175 function TGikoSys.Parse2chURL2(URL: string): TPathRec;
2176 var
2177         i: Integer;
2178         s: string;
2179         wk: string;
2180         wkMin: Integer;
2181         wkMax: Integer;
2182         wkInt: Integer;
2183         RStart: Integer;
2184         RLength: Integer;
2185         SList: TStringList;
2186 begin
2187         URL := Trim(LowerCase(URL));
2188         Result.FBBS := '';
2189         Result.FKey := '';
2190         Result.FSt := 0;
2191         Result.FTo := 0;
2192         Result.FFirst := False;
2193         Result.FStBegin := False;
2194         Result.FToEnd := False;
2195         Result.FDone := False;
2196
2197         wkMin := 0;
2198         wkMax := 1;
2199
2200         FAWKStr.RegExp := 'http://.+\.(2ch\.net|bbspink\.com)/';
2201         if FAWKStr.Match(FAWKStr.ProcessEscSeq(URL), RStart, RLength) = 0 then
2202                 Exit;
2203         s := Copy(URL, RStart + RLength - 1, Length(URL));
2204
2205         //\95W\8f\80\8f\91\8e®
2206         //\8dÅ\8cã\82Íl50, 10, 10-20, 10n, 10-20n, -10, 10-, 10n- \82È\82Ç
2207         //http://xxx.2ch.net/test/read.cgi/bbsid/1000000000/
2208         FAWKStr.RegExp := '/test/read.cgi/.+/[0-9]+/.*';
2209         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2210                 s := Copy(s, 15, Length(s));
2211
2212                 SList := TStringList.Create;
2213                 try
2214                         SList.Clear;
2215                         FAWKStr.RegExp := '/';
2216                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 2 then begin
2217                                 Result.FBBS := SList[1];
2218                                 Result.FKey := SList[2];
2219                                 if SList.Count >= 3 then
2220                                         s := SList[3]
2221                                 else
2222                                         s := '';
2223                         end else
2224                                 Exit;
2225
2226                         SList.Clear;
2227                         FAWKStr.LineSeparator := mcls_CRLF;
2228                         FAWKStr.RegExp := '-';
2229                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) = 0 then begin
2230                                 Result.FFirst := True;
2231                         end else begin
2232                                 FAWKStr.RegExp := 'l[0-9]+';
2233                                 if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2234                                         Result.FFirst := True;
2235                                 end else begin
2236                                         for i := 0 to SList.Count - 1 do begin
2237                                                 if Trim(SList[i]) = '' then begin
2238                                                         if i = 0 then
2239                                                                 Result.FStBegin := True;
2240                                                         if i = (SList.Count - 1) then
2241                                                                 Result.FToEnd := True;
2242                                                 end else if IsNumeric(SList[i]) then begin
2243                                                         wkInt := StrToInt(SList[i]);
2244                                                         wkMax := Max(wkMax, wkInt);
2245                                                         if wkMin = 0 then
2246                                                                 wkMin := wkInt
2247                                                         else
2248                                                                 wkMin := Min(wkMin, wkInt);
2249                                                 end else if Trim(SList[i]) = 'n' then begin
2250                                                         Result.FFirst := True;
2251                                                 end else begin
2252                                                         FAWKStr.RegExp := '^n[0-9]+$|^[0-9]+n$';
2253                                                         if FAWKStr.Match(FAWKStr.ProcessEscSeq(SList[i]), RStart, RLength) > 0 then begin
2254                                                                 if Copy(SList[i], 1, 1) = 'n' then
2255                                                                         wkInt := StrToInt(Copy(SList[i], 2, Length(SList[i])))
2256                                                                 else
2257                                                                         wkInt := StrToInt(Copy(SList[i], 1, Length(SList[i]) - 1));
2258                                                                 Result.FFirst := True;
2259                                                                 wkMax := Max(wkMax, wkInt);
2260                                                                 if wkMin = 1 then
2261                                                                         wkMin := wkInt
2262                                                                 else
2263                                                                         wkMin := Min(wkMin, wkInt);
2264                                                         end;
2265                                                 end;
2266                                         end;
2267                                         if Result.FStBegin and (not Result.FToEnd) then
2268                                                 Result.FSt := wkMin
2269                                         else if (not Result.FStBegin) and Result.FToEnd then
2270                                                 Result.FTo := wkMax
2271                                         else if (not Result.FStBegin) and (not Result.FToEnd) then begin
2272                                                 Result.FSt := wkMin;
2273                                                 Result.FTo := wkMax;
2274                                         end;
2275                                         //Result.FSt := wkMin;
2276                                         //Result.FTo := wkMax;
2277                                 end;
2278                         end;
2279                 finally
2280                         SList.Free;
2281                 end;
2282                 Result.FDone := True;
2283                 Exit;
2284         end;
2285
2286         //\90Vkako\8f\91\8e®
2287         //http://server.2ch.net/ITA_NAME/kako/1000/10000/1000000000.html
2288         FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+/[0-9]+\.html';
2289         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2290                 SList := TStringList.Create;
2291                 try
2292                         SList.Clear;
2293                         FAWKStr.RegExp := '/';
2294                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2295                                 Result.FBBS := SList[1];
2296                                 Result.FKey := ChangeFileExt(SList[5], '');
2297                                 Result.FFirst := True;
2298                         end else
2299                                 Exit;
2300                 finally
2301                         SList.Free;
2302                 end;
2303                 Result.FDone := True;
2304                 Exit;
2305         end;
2306
2307         //\8b\8ckako\8f\91\8e®
2308         //http://server.2ch.net/ITA_NAME/kako/999/999999999.html
2309         FAWKStr.RegExp := '/.+/kako/[0-9]+/[0-9]+\.html';
2310         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2311                 SList := TStringList.Create;
2312                 try
2313                         SList.Clear;
2314                         FAWKStr.RegExp := '/';
2315                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 5 then begin
2316                                 Result.FBBS := SList[1];
2317                                 Result.FKey := ChangeFileExt(SList[4], '');
2318                                 Result.FFirst := True;
2319                         end else
2320                                 Exit;
2321                 finally
2322                         SList.Free;
2323                 end;
2324                 Result.FDone := True;
2325                 Exit;
2326         end;
2327
2328         //log\8by\82Ñlog2\8f\91\8e®
2329         //http://server.2ch.net/log/ITA_NAME/kako/999/999999999.html
2330         //http://server.2ch.net/log2/ITA_NAME/kako/999/999999999.html
2331         FAWKStr.RegExp := '/log2?/.+/kako/[0-9]+/[0-9]+\.html';
2332         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2333                 SList := TStringList.Create;
2334                 try
2335                         SList.Clear;
2336                         FAWKStr.RegExp := '/';
2337                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 6 then begin
2338                                 Result.FBBS := SList[2];
2339                                 Result.FKey := ChangeFileExt(SList[5], '');
2340                                 Result.FFirst := True;
2341                         end else
2342                                 Exit;
2343                 finally
2344                         SList.Free;
2345                 end;
2346                 Result.FDone := True;
2347                 Exit;
2348         end;
2349
2350
2351         //\8b\8cURL\8f\91\8e®
2352         //http://server.2ch.net/test/read.cgi?bbs=ITA_NAME&key=1000000000&st=1&to=5&nofirst=true
2353         FAWKStr.RegExp := '/test/read\.cgi\?';
2354         if FAWKStr.Match(FAWKStr.ProcessEscSeq(s), RStart, RLength) > 0 then begin
2355                 s := Copy(s, 16, Length(s));
2356                 SList := TStringList.Create;
2357                 try
2358                         SList.Clear;
2359                         FAWKStr.RegExp := '&';
2360                         if FAWKStr.Split(FAWKStr.ProcessEscSeq(s), SList) >= 2 then begin
2361                                 Result.FFirst := True;
2362                                 for i := 0 to SList.Count - 1 do begin
2363                                         if Pos('bbs=', SList[i]) = 1 then begin
2364                                                 Result.FBBS := Copy(SList[i], 5, Length(SList[i]));
2365                                         end else if Pos('key=', SList[i]) = 1 then begin
2366                                                 Result.FKey := Copy(SList[i], 5, Length(SList[i]));
2367                                         end else if Pos('st=', SList[i]) = 1 then begin
2368                                                 wk := Copy(SList[i], 4, Length(SList[i]));
2369                                                 if IsNumeric(wk) then
2370                                                         Result.FSt := StrToInt(wk)
2371                                                 else if wk = '' then
2372                                                         Result.FStBegin := True;
2373                                         end else if Pos('to=', SList[i]) = 1 then begin
2374                                                 wk := Copy(SList[i], 4, Length(SList[i]));
2375                                                 if IsNumeric(wk) then
2376                                                         Result.FTo := StrToInt(wk)
2377                                                 else if wk = '' then
2378                                                         Result.FToEnd := True;
2379                                         end else if Pos('nofirst=', SList[i]) = 1 then begin
2380                                                 Result.FFirst := False;
2381                                         end;
2382                                 end;
2383                         end else
2384                                 Exit;
2385                 finally
2386                         SList.Free;
2387                 end;
2388
2389                 if (Result.FBBS <> '') and (Result.FKey <> '') then begin
2390                         Result.FDone := True;
2391                 end;
2392                 Exit;
2393         end;
2394 end;
2395
2396 procedure TGikoSys.ParseURI(var URL, Protocol, Host, Path, Document, Port, Bookmark: string);
2397 var
2398         URI: TIdURI;
2399 begin
2400         Protocol := '';
2401         Host := '';
2402         Path := '';
2403         Document := '';
2404         Port := '';
2405         Bookmark := '';
2406         URI := TIdURI.Create(URL);
2407         try
2408                 Protocol := URI.Protocol;
2409                 Host := URI.Host;
2410                 Path := URI.Path;
2411                 Document := URI.Document;
2412                 Port := URI.Port;
2413                 Bookmark := URI.Bookmark;
2414         finally
2415                 URI.Free;
2416         end;
2417 end;
2418
2419 function TGikoSys.GetVersionBuild: Integer;
2420 var
2421         FixedFileInfo: PVSFixedFileInfo;
2422         VersionHandle, VersionSize: DWORD;
2423         pVersionInfo: Pointer;
2424         ItemLen : UInt;
2425         AppFile: string;
2426 begin
2427         Result := 0;
2428         AppFile := Application.ExeName;
2429         VersionSize := GetFileVersionInfoSize(pChar(AppFile), VersionHandle);
2430         if VersionSize = 0 then
2431                 Exit;
2432         GetMem(pVersionInfo, VersionSize);
2433         try
2434                 if GetFileVersionInfo(PChar(AppFile),VersionHandle,VersionSize, pVersionInfo) then
2435                         if VerQueryValue(pVersionInfo, '\', Pointer(FixedFileInfo), ItemLen) then
2436                                 Result := LOWORD(FixedFileInfo^.dwFileVersionLS);
2437         finally
2438                 FreeMem(pVersionInfo, VersionSize);
2439         end;
2440 end;
2441
2442 initialization
2443         GikoSys := TGikoSys.Create;
2444
2445 finalization
2446         if GikoSys <> nil then begin
2447                 GikoSys.Free;
2448                 GikoSys := nil;
2449         end;
2450 end.