]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules.cpp
Fix stripping of character prior to delimiter in StreamSocket::GetNextLine (not notic...
[user/henk/code/inspircd.git] / src / modules.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "xline.h"
16 #include "socket.h"
17 #include "socketengine.h"
18 #include "command_parse.h"
19 #include "dns.h"
20 #include "exitcodes.h"
21
22 #ifndef WIN32
23         #include <dirent.h>
24 #endif
25
26
27 // Version is a simple class for holding a modules version number
28 template<>
29 VersionBase<API_VERSION>::VersionBase(const std::string &modv, int flags)
30 : description(modv), Flags(flags)
31 {
32 }
33
34 template<>
35 bool VersionBase<API_VERSION>::CanLink(const std::string& other_data)
36 {
37         return link_data == other_data;
38 }
39
40 Request::Request(Module* src, Module* dst, const char* idstr)
41 : id(idstr), source(src), dest(dst)
42 {
43 }
44
45 void Request::Send()
46 {
47         if (dest)
48                 dest->OnRequest(*this);
49 }
50
51 Event::Event(Module* src, const std::string &eventid) : source(src), id(eventid) { }
52
53 void Event::Send()
54 {
55         FOREACH_MOD(I_OnEvent,OnEvent(*this));
56 }
57
58 // These declarations define the behavours of the base class Module (which does nothing at all)
59
60 Module::Module() { }
61 CullResult Module::cull()
62 {
63         return classbase::cull();
64 }
65 Module::~Module()
66 {
67 }
68
69 ModResult       Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { return MOD_RES_PASSTHRU; }
70 void            Module::OnUserConnect(LocalUser*) { }
71 void            Module::OnUserQuit(User*, const std::string&, const std::string&) { }
72 void            Module::OnUserDisconnect(LocalUser*) { }
73 void            Module::OnUserJoin(Membership*, bool, bool, CUList&) { }
74 void            Module::OnPostJoin(Membership*) { }
75 void            Module::OnUserPart(Membership*, std::string&, CUList&) { }
76 void            Module::OnPreRehash(User*, const std::string&) { }
77 void            Module::OnModuleRehash(User*, const std::string&) { }
78 void            Module::OnRehash(User*) { }
79 ModResult       Module::OnUserPreJoin(User*, Channel*, const char*, std::string&, const std::string&) { return MOD_RES_PASSTHRU; }
80 void            Module::OnMode(User*, void*, int, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
81 void            Module::OnOper(User*, const std::string&) { }
82 void            Module::OnPostOper(User*, const std::string&, const std::string &) { }
83 void            Module::OnInfo(User*) { }
84 void            Module::OnWhois(User*, User*) { }
85 ModResult       Module::OnUserPreInvite(User*, User*, Channel*, time_t) { return MOD_RES_PASSTHRU; }
86 ModResult       Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
87 ModResult       Module::OnUserPreNotice(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
88 ModResult       Module::OnUserPreNick(User*, const std::string&) { return MOD_RES_PASSTHRU; }
89 void            Module::OnUserPostNick(User*, const std::string&) { }
90 ModResult       Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { return MOD_RES_PASSTHRU; }
91 void            Module::On005Numeric(std::string&) { }
92 ModResult       Module::OnKill(User*, User*, const std::string&) { return MOD_RES_PASSTHRU; }
93 void            Module::OnLoadModule(Module*) { }
94 void            Module::OnUnloadModule(Module*) { }
95 void            Module::OnBackgroundTimer(time_t) { }
96 ModResult       Module::OnPreCommand(std::string&, std::vector<std::string>&, User *, bool, const std::string&) { return MOD_RES_PASSTHRU; }
97 void            Module::OnPostCommand(const std::string&, const std::vector<std::string>&, User *, CmdResult, const std::string&) { }
98 ModResult       Module::OnCheckReady(LocalUser*) { return MOD_RES_PASSTHRU; }
99 ModResult       Module::OnUserRegister(LocalUser*) { return MOD_RES_PASSTHRU; }
100 ModResult       Module::OnUserPreKick(User*, Membership*, const std::string&) { return MOD_RES_PASSTHRU; }
101 void            Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { }
102 ModResult       Module::OnRawMode(User*, Channel*, const char, const std::string &, bool, int) { return MOD_RES_PASSTHRU; }
103 ModResult       Module::OnCheckInvite(User*, Channel*) { return MOD_RES_PASSTHRU; }
104 ModResult       Module::OnCheckKey(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
105 ModResult       Module::OnCheckLimit(User*, Channel*) { return MOD_RES_PASSTHRU; }
106 ModResult       Module::OnCheckChannelBan(User*, Channel*) { return MOD_RES_PASSTHRU; }
107 ModResult       Module::OnCheckBan(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
108 ModResult       Module::OnExtBanCheck(User*, Channel*, char) { return MOD_RES_PASSTHRU; }
109 ModResult       Module::OnStats(char, User*, string_list&) { return MOD_RES_PASSTHRU; }
110 ModResult       Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
111 ModResult       Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
112 ModResult       Module::OnPreTopicChange(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
113 void            Module::OnEvent(Event&) { }
114 void            Module::OnRequest(Request&) { }
115 ModResult       Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { return MOD_RES_PASSTHRU; }
116 void            Module::OnGlobalOper(User*) { }
117 void            Module::OnPostConnect(User*) { }
118 ModResult       Module::OnAddBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
119 ModResult       Module::OnDelBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
120 void            Module::OnStreamSocketAccept(StreamSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { }
121 int             Module::OnStreamSocketWrite(StreamSocket*, std::string&) { return -1; }
122 void            Module::OnStreamSocketClose(StreamSocket*) { }
123 void            Module::OnStreamSocketConnect(StreamSocket*) { }
124 int             Module::OnStreamSocketRead(StreamSocket*, std::string&) { return -1; }
125 void            Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&) { }
126 void            Module::OnUserNotice(User*, void*, int, const std::string&, char, const CUList&) { }
127 void            Module::OnRemoteKill(User*, User*, const std::string&, const std::string&) { }
128 void            Module::OnUserInvite(User*, User*, Channel*, time_t) { }
129 void            Module::OnPostTopicChange(User*, Channel*, const std::string&) { }
130 void            Module::OnGetServerDescription(const std::string&, std::string&) { }
131 void            Module::OnSyncUser(User*, Module*, void*) { }
132 void            Module::OnSyncChannel(Channel*, Module*, void*) { }
133 void            Module::OnSyncNetwork(Module*, void*) { }
134 void            Module::ProtoSendMode(void*, TargetTypeFlags, void*, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
135 void            Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { }
136 void            Module::ProtoSendMetaData(void*, Extensible*, const std::string&, const std::string&) { }
137 void            Module::OnWallops(User*, const std::string&) { }
138 void            Module::OnChangeHost(User*, const std::string&) { }
139 void            Module::OnChangeName(User*, const std::string&) { }
140 void            Module::OnChangeIdent(User*, const std::string&) { }
141 void            Module::OnAddLine(User*, XLine*) { }
142 void            Module::OnDelLine(User*, XLine*) { }
143 void            Module::OnExpireLine(XLine*) { }
144 void            Module::OnCleanup(int, void*) { }
145 ModResult       Module::OnChannelPreDelete(Channel*) { return MOD_RES_PASSTHRU; }
146 void            Module::OnChannelDelete(Channel*) { }
147 ModResult       Module::OnSetAway(User*, const std::string &) { return MOD_RES_PASSTHRU; }
148 ModResult       Module::OnUserList(User*, Channel*) { return MOD_RES_PASSTHRU; }
149 ModResult       Module::OnWhoisLine(User*, User*, int&, std::string&) { return MOD_RES_PASSTHRU; }
150 void            Module::OnBuildNeighborList(User*, UserChanList&, std::map<User*,bool>&) { }
151 void            Module::OnGarbageCollect() { }
152 void            Module::OnText(User*, void*, int, const std::string&, char, CUList&) { }
153 void            Module::OnRunTestSuite() { }
154 void            Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { }
155 ModResult       Module::OnNumeric(User*, unsigned int, const std::string&) { return MOD_RES_PASSTHRU; }
156 void            Module::OnHookIO(StreamSocket*, ListenSocket*) { }
157 ModResult   Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { return MOD_RES_PASSTHRU; }
158 void            Module::OnSendWhoLine(User*, User*, Channel*, std::string&) { }
159 ModResult       Module::OnChannelRestrictionApply(User*, Channel*, const char*) { return MOD_RES_PASSTHRU; }
160
161 ModuleManager::ModuleManager() : ModCount(0)
162 {
163 }
164
165 ModuleManager::~ModuleManager()
166 {
167 }
168
169 bool ModuleManager::Attach(Implementation i, Module* mod)
170 {
171         if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end())
172                 return false;
173
174         EventHandlers[i].push_back(mod);
175         return true;
176 }
177
178 bool ModuleManager::Detach(Implementation i, Module* mod)
179 {
180         EventHandlerIter x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod);
181
182         if (x == EventHandlers[i].end())
183                 return false;
184
185         EventHandlers[i].erase(x);
186         return true;
187 }
188
189 void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
190 {
191         for (size_t n = 0; n < sz; ++n)
192                 Attach(i[n], mod);
193 }
194
195 void ModuleManager::DetachAll(Module* mod)
196 {
197         for (size_t n = I_BEGIN + 1; n != I_END; ++n)
198                 Detach((Implementation)n, mod);
199 }
200
201 bool ModuleManager::SetPriority(Module* mod, Priority s)
202 {
203         for (size_t n = I_BEGIN + 1; n != I_END; ++n)
204                 SetPriority(mod, (Implementation)n, s);
205
206         return true;
207 }
208
209 bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Module** modules, size_t sz)
210 {
211         /** To change the priority of a module, we first find its position in the vector,
212          * then we find the position of the other modules in the vector that this module
213          * wants to be before/after. We pick off either the first or last of these depending
214          * on which they want, and we make sure our module is *at least* before or after
215          * the first or last of this subset, depending again on the type of priority.
216          */
217         size_t swap_pos = 0;
218         size_t source = 0;
219         bool swap = true;
220         bool found = false;
221
222         /* Locate our module. This is O(n) but it only occurs on module load so we're
223          * not too bothered about it
224          */
225         for (size_t x = 0; x != EventHandlers[i].size(); ++x)
226         {
227                 if (EventHandlers[i][x] == mod)
228                 {
229                         source = x;
230                         found = true;
231                         break;
232                 }
233         }
234
235         /* Eh? this module doesnt exist, probably trying to set priority on an event
236          * theyre not attached to.
237          */
238         if (!found)
239                 return false;
240
241         switch (s)
242         {
243                 /* Dummy value */
244                 case PRIORITY_DONTCARE:
245                         swap = false;
246                 break;
247                 /* Module wants to be first, sod everything else */
248                 case PRIORITY_FIRST:
249                         if (prioritizationState != PRIO_STATE_FIRST)
250                                 swap = false;
251                         else
252                                 swap_pos = 0;
253                 break;
254                 /* Module wants to be last. */
255                 case PRIORITY_LAST:
256                         if (prioritizationState != PRIO_STATE_FIRST)
257                                 swap = false;
258                         else if (EventHandlers[i].empty())
259                                 swap_pos = 0;
260                         else
261                                 swap_pos = EventHandlers[i].size() - 1;
262                 break;
263                 /* Place this module after a set of other modules */
264                 case PRIORITY_AFTER:
265                 {
266                         /* Find the latest possible position */
267                         swap_pos = 0;
268                         swap = false;
269                         for (size_t x = 0; x != EventHandlers[i].size(); ++x)
270                         {
271                                 for (size_t n = 0; n < sz; ++n)
272                                 {
273                                         if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x >= swap_pos) && (source <= swap_pos))
274                                         {
275                                                 swap_pos = x;
276                                                 swap = true;
277                                         }
278                                 }
279                         }
280                 }
281                 break;
282                 /* Place this module before a set of other modules */
283                 case PRIORITY_BEFORE:
284                 {
285                         swap_pos = EventHandlers[i].size() - 1;
286                         swap = false;
287                         for (size_t x = 0; x != EventHandlers[i].size(); ++x)
288                         {
289                                 for (size_t n = 0; n < sz; ++n)
290                                 {
291                                         if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x <= swap_pos) && (source >= swap_pos))
292                                         {
293                                                 swap = true;
294                                                 swap_pos = x;
295                                         }
296                                 }
297                         }
298                 }
299                 break;
300         }
301
302         /* Do we need to swap? */
303         if (swap && (swap_pos != source))
304         {
305                 // We are going to change positions; we'll need to run again to verify all requirements
306                 if (prioritizationState == PRIO_STATE_LAST)
307                         prioritizationState = PRIO_STATE_AGAIN;
308                 /* Suggestion from Phoenix, "shuffle" the modules to better retain call order */
309                 int incrmnt = 1;
310
311                 if (source > swap_pos)
312                         incrmnt = -1;
313
314                 for (unsigned int j = source; j != swap_pos; j += incrmnt)
315                 {
316                         if (( j + incrmnt > EventHandlers[i].size() - 1) || (j + incrmnt < 0))
317                                 continue;
318
319                         std::swap(EventHandlers[i][j], EventHandlers[i][j+incrmnt]);
320                 }
321         }
322
323         return true;
324 }
325
326 std::string& ModuleManager::LastError()
327 {
328         return LastModuleError;
329 }
330
331 CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user)
332 {
333         return this->Parser->CallHandler(commandname, parameters, user);
334 }
335
336 bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, User* user)
337 {
338         return this->Parser->IsValidCommand(commandname, pcnt, user);
339 }
340
341 void InspIRCd::AddCommand(Command *f)
342 {
343         if (!this->Parser->AddCommand(f))
344         {
345                 throw ModuleException("Command "+std::string(f->name)+" already exists.");
346         }
347 }
348
349 void ModuleManager::AddService(ServiceProvider& item)
350 {
351         switch (item.service)
352         {
353                 case SERVICE_COMMAND:
354                         if (!ServerInstance->Parser->AddCommand(static_cast<Command*>(&item)))
355                                 throw ModuleException("Command "+std::string(item.name)+" already exists.");
356                         return;
357                 case SERVICE_CMODE:
358                 case SERVICE_UMODE:
359                         if (!ServerInstance->Modes->AddMode(static_cast<ModeHandler*>(&item)))
360                                 throw ModuleException("Mode "+std::string(item.name)+" already exists.");
361                         return;
362                 case SERVICE_METADATA:
363                         ServerInstance->Extensions.Register(static_cast<ExtensionItem*>(&item));
364                         return;
365                 case SERVICE_DATA:
366                 case SERVICE_IOHOOK:
367                 {
368                         DataProviders.insert(std::make_pair(item.name, &item));
369                         std::string::size_type slash = item.name.find('/');
370                         if (slash != std::string::npos)
371                         {
372                                 DataProviders.insert(std::make_pair(item.name.substr(0, slash), &item));
373                                 DataProviders.insert(std::make_pair(item.name.substr(slash + 1), &item));
374                         }
375                         return;
376                 }
377                 default:
378                         throw ModuleException("Cannot add unknown service type");
379         }
380 }
381
382 ServiceProvider* ModuleManager::FindService(ServiceType type, const std::string& name)
383 {
384         switch (type)
385         {
386                 case SERVICE_DATA:
387                 case SERVICE_IOHOOK:
388                 {
389                         std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.find(name);
390                         if (i != DataProviders.end() && i->second->service == type)
391                                 return i->second;
392                         return NULL;
393                 }
394                 // TODO implement finding of the other types
395                 default:
396                         throw ModuleException("Cannot find unknown service type");
397         }
398 }
399
400 dynamic_reference_base::dynamic_reference_base(Module* Creator, const std::string& Name)
401         : name(Name), value(NULL), creator(Creator)
402 {
403         ServerInstance->Modules->ActiveDynrefs.push_back(this);
404 }
405
406 dynamic_reference_base::~dynamic_reference_base()
407 {
408         for(unsigned int i = 0; i < ServerInstance->Modules->ActiveDynrefs.size(); i++)
409         {
410                 if (ServerInstance->Modules->ActiveDynrefs[i] == this)
411                 {
412                         unsigned int last = ServerInstance->Modules->ActiveDynrefs.size() - 1;
413                         if (i != last)
414                                 ServerInstance->Modules->ActiveDynrefs[i] = ServerInstance->Modules->ActiveDynrefs[last];
415                         ServerInstance->Modules->ActiveDynrefs.erase(ServerInstance->Modules->ActiveDynrefs.begin() + last);
416                         return;
417                 }
418         }
419 }
420
421 void dynamic_reference_base::SetProvider(const std::string& newname)
422 {
423         name = newname;
424         ClearCache();
425 }
426
427 void dynamic_reference_base::lookup()
428 {
429         if (!*this)
430                 throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
431 }
432
433 dynamic_reference_base::operator bool()
434 {
435         if (!value)
436         {
437                 std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
438                 if (i != ServerInstance->Modules->DataProviders.end())
439                         value = static_cast<DataProvider*>(i->second);
440         }
441         return value;
442 }
443
444 void InspIRCd::SendMode(const std::vector<std::string>& parameters, User *user)
445 {
446         this->Modes->Process(parameters, user);
447 }
448
449 bool InspIRCd::AddResolver(Resolver* r, bool cached)
450 {
451         if (!cached)
452                 return this->Res->AddResolverClass(r);
453         else
454         {
455                 r->TriggerCachedResult();
456                 delete r;
457                 return true;
458         }
459 }
460
461 Module* ModuleManager::Find(const std::string &name)
462 {
463         std::map<std::string, Module*>::iterator modfind = Modules.find(name);
464
465         if (modfind == Modules.end())
466                 return NULL;
467         else
468                 return modfind->second;
469 }
470
471 const std::vector<std::string> ModuleManager::GetAllModuleNames(int filter)
472 {
473         std::vector<std::string> retval;
474         for (std::map<std::string, Module*>::iterator x = Modules.begin(); x != Modules.end(); ++x)
475                 if (!filter || (x->second->GetVersion().Flags & filter))
476                         retval.push_back(x->first);
477         return retval;
478 }
479
480 ConfigReader::ConfigReader()
481 {
482         this->error = 0;
483 }
484
485
486 ConfigReader::~ConfigReader()
487 {
488 }
489
490 static ConfigTag* SlowGetTag(const std::string &tag, int index)
491 {
492         ConfigTagList tags = ServerInstance->Config->ConfTags(tag);
493         while (tags.first != tags.second)
494         {
495                 if (!index)
496                         return tags.first->second;
497                 tags.first++;
498                 index--;
499         }
500         return NULL;
501 }
502
503 std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
504 {
505         std::string result = default_value;
506         if (!SlowGetTag(tag, index)->readString(name, result, allow_linefeeds))
507         {
508                 this->error = CONF_VALUE_NOT_FOUND;
509         }
510         return result;
511 }
512
513 std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds)
514 {
515         return ReadValue(tag, name, "", index, allow_linefeeds);
516 }
517
518 bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
519 {
520         bool def = (default_value == "yes");
521         return SlowGetTag(tag, index)->getBool(name, def);
522 }
523
524 bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
525 {
526         return ReadFlag(tag, name, "", index);
527 }
528
529
530 int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive)
531 {
532         int v = atoi(default_value.c_str());
533         int result = SlowGetTag(tag, index)->getInt(name, v);
534
535         if ((need_positive) && (result < 0))
536         {
537                 this->error = CONF_INT_NEGATIVE;
538                 return 0;
539         }
540
541         return result;
542 }
543
544 int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive)
545 {
546         return ReadInteger(tag, name, "", index, need_positive);
547 }
548
549 long ConfigReader::GetError()
550 {
551         long olderr = this->error;
552         this->error = 0;
553         return olderr;
554 }
555
556 int ConfigReader::Enumerate(const std::string &tag)
557 {
558         ServerInstance->Logs->Log("MODULE", DEBUG, "Module is using ConfigReader::Enumerate on %s; this is slow!",
559                 tag.c_str());
560         int i=0;
561         while (SlowGetTag(tag, i)) i++;
562         return i;
563 }
564
565 FileReader::FileReader(const std::string &filename)
566 {
567         LoadFile(filename);
568 }
569
570 FileReader::FileReader()
571 {
572 }
573
574 std::string FileReader::Contents()
575 {
576         std::string x;
577         for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
578         {
579                 x.append(*a);
580                 x.append("\r\n");
581         }
582         return x;
583 }
584
585 unsigned long FileReader::ContentSize()
586 {
587         return this->contentsize;
588 }
589
590 void FileReader::CalcSize()
591 {
592         unsigned long n = 0;
593         for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
594                 n += (a->length() + 2);
595         this->contentsize = n;
596 }
597
598 void FileReader::LoadFile(const std::string &filename)
599 {
600         file_cache c;
601         c.clear();
602         if (ServerInstance->Config->ReadFile(c,filename.c_str()))
603         {
604                 this->fc = c;
605                 this->CalcSize();
606         }
607 }
608
609
610 FileReader::~FileReader()
611 {
612 }
613
614 bool FileReader::Exists()
615 {
616         return (!(fc.size() == 0));
617 }
618
619 std::string FileReader::GetLine(int x)
620 {
621         if ((x<0) || ((unsigned)x>fc.size()))
622                 return "";
623         return fc[x];
624 }
625
626 int FileReader::FileSize()
627 {
628         return fc.size();
629 }