~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

Possible solution to longjump clobber problem.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
#include <google/protobuf/io/zero_copy_stream.h>
33
33
#include <google/protobuf/io/zero_copy_stream_impl.h>
34
34
 
 
35
#include "drizzled/my_hash.h"
35
36
#include "drizzled/cached_directory.h"
36
37
 
37
38
#include <drizzled/definitions.h>
54
55
#include <drizzled/table_proto.h>
55
56
#include <drizzled/plugin/event_observer.h>
56
57
 
57
 
#include <drizzled/table/shell.h>
58
 
 
59
 
#include "drizzled/message/cache.h"
60
 
 
61
 
#include <boost/algorithm/string/compare.hpp>
62
 
 
63
58
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
64
59
 
 
60
using namespace std;
 
61
 
65
62
namespace drizzled
66
63
{
67
64
 
71
68
static EngineVector vector_of_engines;
72
69
static EngineVector vector_of_schema_engines;
73
70
 
74
 
const std::string DEFAULT_STRING("default");
75
71
const std::string UNKNOWN_STRING("UNKNOWN");
76
72
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
77
73
 
82
78
  return vector_of_schema_engines;
83
79
}
84
80
 
85
 
StorageEngine::StorageEngine(const std::string name_arg,
86
 
                             const std::bitset<HTON_BIT_SIZE> &flags_arg) :
 
81
StorageEngine::StorageEngine(const string name_arg,
 
82
                             const bitset<HTON_BIT_SIZE> &flags_arg) :
87
83
  Plugin(name_arg, "StorageEngine"),
88
84
  MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
89
85
  flags(flags_arg)
192
188
}
193
189
 
194
190
class FindEngineByName
195
 
  : public std::unary_function<StorageEngine *, bool>
 
191
  : public unary_function<StorageEngine *, bool>
196
192
{
197
 
  const std::string &predicate;
 
193
  const string &target;
198
194
 
199
195
public:
200
 
  explicit FindEngineByName(const std::string &target_arg) :
201
 
    predicate(target_arg)
 
196
  explicit FindEngineByName(const string &target_arg) :
 
197
    target(target_arg)
202
198
  {
203
199
  }
204
 
 
205
200
  result_type operator() (argument_type engine)
206
201
  {
207
 
    return boost::iequals(engine->getName(), predicate);
 
202
    string engine_name(engine->getName());
 
203
 
 
204
    transform(engine_name.begin(), engine_name.end(),
 
205
              engine_name.begin(), ::tolower);
 
206
    return engine_name == target;
208
207
  }
209
208
};
210
209
 
211
 
StorageEngine *StorageEngine::findByName(const std::string &predicate)
 
210
StorageEngine *StorageEngine::findByName(const string &find_str)
212
211
{
213
 
  EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
214
 
                                            vector_of_engines.end(),
215
 
                                            FindEngineByName(predicate));
 
212
  string search_string(find_str);
 
213
  transform(search_string.begin(), search_string.end(),
 
214
            search_string.begin(), ::tolower);
 
215
 
 
216
  
 
217
  EngineVector::iterator iter= find_if(vector_of_engines.begin(),
 
218
                                       vector_of_engines.end(),
 
219
                                       FindEngineByName(search_string));
216
220
  if (iter != vector_of_engines.end())
217
221
  {
218
222
    StorageEngine *engine= *iter;
223
227
  return NULL;
224
228
}
225
229
 
226
 
StorageEngine *StorageEngine::findByName(Session& session, const std::string &predicate)
 
230
StorageEngine *StorageEngine::findByName(Session& session, const string &find_str)
227
231
{
228
 
  if (boost::iequals(predicate, DEFAULT_STRING))
 
232
  string search_string(find_str);
 
233
  transform(search_string.begin(), search_string.end(),
 
234
            search_string.begin(), ::tolower);
 
235
 
 
236
  if (search_string.compare("default") == 0)
229
237
    return session.getDefaultStorageEngine();
230
238
 
231
 
  EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
232
 
                                            vector_of_engines.end(),
233
 
                                            FindEngineByName(predicate));
 
239
  EngineVector::iterator iter= find_if(vector_of_engines.begin(),
 
240
                                       vector_of_engines.end(),
 
241
                                       FindEngineByName(search_string));
234
242
  if (iter != vector_of_engines.end())
235
243
  {
236
244
    StorageEngine *engine= *iter;
241
249
  return NULL;
242
250
}
243
251
 
244
 
class StorageEngineCloseConnection : public std::unary_function<StorageEngine *, void>
 
252
class StorageEngineCloseConnection : public unary_function<StorageEngine *, void>
245
253
{
246
254
  Session *session;
247
255
public:
263
271
*/
264
272
void StorageEngine::closeConnection(Session* session)
265
273
{
266
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
267
 
                StorageEngineCloseConnection(session));
 
274
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
275
           StorageEngineCloseConnection(session));
268
276
}
269
277
 
270
278
bool StorageEngine::flushLogs(StorageEngine *engine)
271
279
{
272
280
  if (engine == NULL)
273
281
  {
274
 
    if (std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
275
 
                     std::mem_fun(&StorageEngine::flush_logs))
 
282
    if (find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
283
                mem_fun(&StorageEngine::flush_logs))
276
284
        != vector_of_engines.begin())
277
285
      return true;
278
286
  }
284
292
  return false;
285
293
}
286
294
 
287
 
class StorageEngineGetTableDefinition: public std::unary_function<StorageEngine *,bool>
 
295
class StorageEngineGetTableDefinition: public unary_function<StorageEngine *,bool>
288
296
{
289
297
  Session& session;
290
298
  const TableIdentifier &identifier;
312
320
  }
313
321
};
314
322
 
315
 
class StorageEngineDoesTableExist: public std::unary_function<StorageEngine *, bool>
 
323
class StorageEngineDoesTableExist: public unary_function<StorageEngine *, bool>
316
324
{
317
325
  Session& session;
318
326
  const TableIdentifier &identifier;
343
351
  }
344
352
 
345
353
  EngineVector::iterator iter=
346
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
347
 
                 StorageEngineDoesTableExist(session, identifier));
 
354
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
355
            StorageEngineDoesTableExist(session, identifier));
348
356
 
349
357
  if (iter == vector_of_engines.end())
350
358
  {
356
364
 
357
365
bool plugin::StorageEngine::doDoesTableExist(Session&, const drizzled::TableIdentifier&)
358
366
{
359
 
  std::cerr << " Engine was called for doDoesTableExist() and does not implement it: " << this->getName() << "\n";
 
367
  cerr << " Engine was called for doDoesTableExist() and does not implement it: " << this->getName() << "\n";
360
368
  assert(0);
361
369
  return false;
362
370
}
368
376
*/
369
377
int StorageEngine::getTableDefinition(Session& session,
370
378
                                      const TableIdentifier &identifier,
371
 
                                      message::table::shared_ptr &table_message,
 
379
                                      message::Table &table_message,
372
380
                                      bool include_temporary_tables)
373
381
{
374
382
  int err= ENOENT;
375
383
 
376
384
  if (include_temporary_tables)
377
385
  {
378
 
    Table *table= session.find_temporary_table(identifier);
379
 
    if (table)
380
 
    {
381
 
      table_message.reset(new message::Table(*table->getShare()->getTableProto()));
 
386
    if (session.doGetTableDefinition(identifier, table_message) == EEXIST)
382
387
      return EEXIST;
383
 
    }
384
 
  }
385
 
 
386
 
  drizzled::message::table::shared_ptr table_ptr;
387
 
  if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
388
 
  {
389
 
    table_message= table_ptr;
390
 
  }
391
 
 
392
 
  message::Table message;
 
388
  }
 
389
 
393
390
  EngineVector::iterator iter=
394
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
395
 
                 StorageEngineGetTableDefinition(session, identifier, message, err));
 
391
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
392
            StorageEngineGetTableDefinition(session, identifier, table_message, err));
396
393
 
397
394
  if (iter == vector_of_engines.end())
398
395
  {
399
396
    return ENOENT;
400
397
  }
401
 
  table_message.reset(new message::Table(message));
402
 
 
403
 
 drizzled::message::Cache::singleton().insert(identifier, table_message);
404
398
 
405
399
  return err;
406
400
}
443
437
{
444
438
  int error= 0;
445
439
  int error_proto;
446
 
  message::table::shared_ptr src_proto;
 
440
  message::Table src_proto;
447
441
  StorageEngine *engine;
448
442
 
449
443
  error_proto= StorageEngine::getTableDefinition(session, identifier, src_proto);
450
444
 
451
445
  if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
452
446
  {
453
 
    std::string error_message;
454
 
    identifier.getSQLPath(error_message);
 
447
    string error_message;
455
448
 
 
449
    error_message.append(const_cast<TableIdentifier &>(identifier).getSQLPath());
456
450
    error_message.append(" : ");
457
 
    error_message.append(src_proto->InitializationErrorString());
 
451
    error_message.append(src_proto.InitializationErrorString());
458
452
 
459
453
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
460
454
 
461
455
    return ER_CORRUPT_TABLE_DEFINITION;
462
456
  }
463
457
 
464
 
  if (src_proto)
465
 
    engine= StorageEngine::findByName(session, src_proto->engine().name());
466
 
  else
467
 
    engine= StorageEngine::findByName(session, "");
 
458
  engine= StorageEngine::findByName(session, src_proto.engine().name());
468
459
 
469
460
  if (not engine)
470
461
  {
471
 
    std::string error_message;
472
 
    identifier.getSQLPath(error_message);
473
 
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
 
462
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), const_cast<TableIdentifier &>(identifier).getSQLPath().c_str());
474
463
 
475
464
    return ER_CORRUPT_TABLE_DEFINITION;
476
465
  }
504
493
    }
505
494
  }
506
495
 
507
 
  drizzled::message::Cache::singleton().erase(identifier);
508
496
 
509
497
  return error;
510
498
}
523
511
                               message::Table& table_message)
524
512
{
525
513
  int error= 1;
 
514
  Table table;
526
515
  TableShare share(identifier);
527
 
  table::Shell table(share);
528
516
  message::Table tmp_proto;
529
517
 
530
 
  if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
 
518
  if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, "", 0, 0, table))
531
519
  { 
532
520
    // @note Error occured, we should probably do a little more here.
533
521
  }
556
544
 
557
545
    if (error)
558
546
    {
559
 
      std::string path;
560
 
      identifier.getSQLPath(path);
561
 
      my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), path.c_str(), error);
 
547
      my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), const_cast<TableIdentifier &>(identifier).getSQLPath().c_str(), error);
562
548
    }
563
549
 
564
 
    table.delete_table();
 
550
    table.delete_table(false);
565
551
  }
566
552
 
567
553
  return(error != 0);
568
554
}
569
555
 
570
 
Cursor *StorageEngine::getCursor(Table &arg)
 
556
Cursor *StorageEngine::getCursor(TableShare &share, memory::Root *alloc)
571
557
{
572
 
  return create(arg);
 
558
  return create(share, alloc);
573
559
}
574
560
 
 
561
class AddTableName : 
 
562
  public unary_function<StorageEngine *, void>
 
563
{
 
564
  CachedDirectory &directory;
 
565
  SchemaIdentifier &identifier;
 
566
  TableNameList &set_of_names;
 
567
 
 
568
public:
 
569
 
 
570
  AddTableName(CachedDirectory &directory_arg, SchemaIdentifier &identifier_arg, set<string>& of_names) :
 
571
    directory(directory_arg),
 
572
    identifier(identifier_arg),
 
573
    set_of_names(of_names)
 
574
  {
 
575
  }
 
576
 
 
577
  result_type operator() (argument_type engine)
 
578
  {
 
579
    engine->doGetTableNames(directory, identifier, set_of_names);
 
580
  }
 
581
};
 
582
 
575
583
class AddTableIdentifier : 
576
 
  public std::unary_function<StorageEngine *, void>
 
584
  public unary_function<StorageEngine *, void>
577
585
{
578
586
  CachedDirectory &directory;
579
 
  const SchemaIdentifier &identifier;
580
 
  TableIdentifier::vector &set_of_identifiers;
 
587
  SchemaIdentifier &identifier;
 
588
  TableIdentifiers &set_of_identifiers;
581
589
 
582
590
public:
583
591
 
584
 
  AddTableIdentifier(CachedDirectory &directory_arg, const SchemaIdentifier &identifier_arg, TableIdentifier::vector &of_names) :
 
592
  AddTableIdentifier(CachedDirectory &directory_arg, SchemaIdentifier &identifier_arg, TableIdentifiers &of_names) :
585
593
    directory(directory_arg),
586
594
    identifier(identifier_arg),
587
595
    set_of_identifiers(of_names)
595
603
};
596
604
 
597
605
 
598
 
void StorageEngine::getIdentifiers(Session &session, const SchemaIdentifier &schema_identifier, TableIdentifier::vector &set_of_identifiers)
599
 
{
600
 
  static SchemaIdentifier INFORMATION_SCHEMA_IDENTIFIER("information_schema");
601
 
  static SchemaIdentifier DATA_DICTIONARY_IDENTIFIER("data_dictionary");
602
 
 
603
 
  CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
604
 
 
605
 
  if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
606
 
  { }
607
 
  else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
608
 
  { }
609
 
  else
610
 
  {
611
 
    if (directory.fail())
612
 
    {
613
 
      errno= directory.getError();
614
 
      if (errno == ENOENT)
615
 
      {
616
 
        std::string path;
617
 
        schema_identifier.getSQLPath(path);
618
 
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), path.c_str());
619
 
      }
620
 
      else
621
 
      {
622
 
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
623
 
      }
624
 
 
625
 
      return;
626
 
    }
627
 
  }
628
 
 
629
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
630
 
                AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
 
606
static SchemaIdentifier INFORMATION_SCHEMA_IDENTIFIER("information_schema");
 
607
static SchemaIdentifier DATA_DICTIONARY_IDENTIFIER("data_dictionary");
 
608
 
 
609
void StorageEngine::getTableNames(Session &session, SchemaIdentifier &schema_identifier, TableNameList &set_of_names)
 
610
{
 
611
  CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
 
612
 
 
613
  if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
 
614
  { }
 
615
  else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
 
616
  { }
 
617
  else
 
618
  {
 
619
    if (directory.fail())
 
620
    {
 
621
      errno= directory.getError();
 
622
      if (errno == ENOENT)
 
623
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_identifier.getSQLPath().c_str());
 
624
      else
 
625
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
 
626
      return;
 
627
    }
 
628
  }
 
629
 
 
630
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
631
           AddTableName(directory, schema_identifier, set_of_names));
 
632
 
 
633
  session.doGetTableNames(directory, schema_identifier, set_of_names);
 
634
}
 
635
 
 
636
void StorageEngine::getTableIdentifiers(Session &session, SchemaIdentifier &schema_identifier, TableIdentifiers &set_of_identifiers)
 
637
{
 
638
  CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
 
639
 
 
640
  if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
 
641
  { }
 
642
  else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
 
643
  { }
 
644
  else
 
645
  {
 
646
    if (directory.fail())
 
647
    {
 
648
      errno= directory.getError();
 
649
      if (errno == ENOENT)
 
650
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_identifier.getSQLPath().c_str());
 
651
      else
 
652
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
 
653
      return;
 
654
    }
 
655
  }
 
656
 
 
657
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
658
           AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
631
659
 
632
660
  session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
633
661
}
634
662
 
635
 
class DropTable: public std::unary_function<TableIdentifier&, bool>
 
663
class DropTable: public unary_function<TableIdentifier&, bool>
636
664
{
637
665
  Session &session;
638
666
  StorageEngine *engine;
651
679
};
652
680
 
653
681
/* This will later be converted to TableIdentifiers */
654
 
class DropTables: public std::unary_function<StorageEngine *, void>
 
682
class DropTables: public unary_function<StorageEngine *, void>
655
683
{
656
684
  Session &session;
657
 
  TableIdentifier::vector &table_identifiers;
 
685
  TableIdentifierList &table_identifiers;
658
686
 
659
687
public:
660
688
 
661
 
  DropTables(Session &session_arg, TableIdentifier::vector &table_identifiers_arg) :
 
689
  DropTables(Session &session_arg, TableIdentifierList &table_identifiers_arg) :
662
690
    session(session_arg),
663
691
    table_identifiers(table_identifiers_arg)
664
692
  { }
667
695
  {
668
696
    // True returning from DropTable means the table has been successfully
669
697
    // deleted, so it should be removed from the list of tables to drop
670
 
    table_identifiers.erase(std::remove_if(table_identifiers.begin(),
671
 
                                           table_identifiers.end(),
672
 
                                           DropTable(session, engine)),
 
698
    table_identifiers.erase(remove_if(table_identifiers.begin(),
 
699
                                      table_identifiers.end(),
 
700
                                      DropTable(session, engine)),
673
701
                            table_identifiers.end());
674
702
  }
675
703
};
682
710
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
683
711
{
684
712
  CachedDirectory dir(directory, set_of_table_definition_ext);
685
 
  TableIdentifier::vector table_identifiers;
 
713
  TableIdentifierList table_identifiers;
686
714
 
687
715
  if (dir.fail())
688
716
  {
698
726
       fileIter != files.end(); fileIter++)
699
727
  {
700
728
    size_t length;
701
 
    std::string path;
 
729
    string path;
702
730
    CachedDirectory::Entry *entry= *fileIter;
703
731
 
704
732
    /* We remove the file extension. */
716
744
    }
717
745
  }
718
746
 
719
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
720
 
                DropTables(session, table_identifiers));
721
 
 
 
747
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
748
           DropTables(session, table_identifiers));
 
749
  
722
750
  /*
723
751
    Now we just clean up anything that might left over.
724
752
 
725
753
    We rescan because some of what might have been there should
726
754
    now be all nice and cleaned up.
727
755
  */
728
 
  std::set<std::string> all_exts= set_of_table_definition_ext;
 
756
  set<string> all_exts= set_of_table_definition_ext;
729
757
 
730
758
  for (EngineVector::iterator iter= vector_of_engines.begin();
731
759
       iter != vector_of_engines.end() ; iter++)
740
768
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
741
769
       fileIter != files.end(); fileIter++)
742
770
  {
743
 
    std::string path;
 
771
    string path;
744
772
    CachedDirectory::Entry *entry= *fileIter;
745
773
 
746
774
    path+= directory;
1007
1035
 
1008
1036
int StorageEngine::deleteDefinitionFromPath(const TableIdentifier &identifier)
1009
1037
{
1010
 
  std::string path(identifier.getPath());
 
1038
  string path(identifier.getPath());
1011
1039
 
1012
1040
  path.append(DEFAULT_DEFINITION_FILE_EXT);
1013
1041
 
1017
1045
int StorageEngine::renameDefinitionFromPath(const TableIdentifier &dest, const TableIdentifier &src)
1018
1046
{
1019
1047
  message::Table table_message;
1020
 
  std::string src_path(src.getPath());
1021
 
  std::string dest_path(dest.getPath());
 
1048
  string src_path(src.getPath());
 
1049
  string dest_path(dest.getPath());
1022
1050
 
1023
1051
  src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1024
1052
  dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1046
1074
int StorageEngine::writeDefinitionFromPath(const TableIdentifier &identifier, message::Table &table_message)
1047
1075
{
1048
1076
  char definition_file_tmp[FN_REFLEN];
1049
 
  std::string file_name(identifier.getPath());
 
1077
  string file_name(identifier.getPath());
1050
1078
 
1051
1079
  file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1052
1080
 
1065
1093
 
1066
1094
  bool success;
1067
1095
 
1068
 
  try
1069
 
  {
 
1096
  try {
1070
1097
    success= table_message.SerializeToZeroCopyStream(output);
1071
1098
  }
1072
1099
  catch (...)
1116
1143
  return 0;
1117
1144
}
1118
1145
 
1119
 
class CanCreateTable: public std::unary_function<StorageEngine *, bool>
 
1146
class CanCreateTable: public unary_function<StorageEngine *, bool>
1120
1147
{
1121
1148
  const TableIdentifier &identifier;
1122
1149
 
1138
1165
bool StorageEngine::canCreateTable(const TableIdentifier &identifier)
1139
1166
{
1140
1167
  EngineVector::iterator iter=
1141
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
1142
 
                 CanCreateTable(identifier));
 
1168
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
1169
            CanCreateTable(identifier));
1143
1170
 
1144
1171
  if (iter == vector_of_engines.end())
1145
1172
  {
1151
1178
 
1152
1179
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1153
1180
{
1154
 
  std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
 
1181
  fstream input(path.c_str(), ios::in | ios::binary);
1155
1182
 
1156
1183
  if (input.good())
1157
1184
  {