Home | History | Annotate | Line # | Download | only in acpixtract
acpixtract.c revision 1.2
      1 /******************************************************************************
      2  *
      3  * Module Name: acpixtract - convert ascii ACPI tables to binary
      4  *
      5  *****************************************************************************/
      6 
      7 /*
      8  * Copyright (C) 2000 - 2013, Intel Corp.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions, and the following disclaimer,
     16  *    without modification.
     17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
     18  *    substantially similar to the "NO WARRANTY" disclaimer below
     19  *    ("Disclaimer") and any redistribution must be conditioned upon
     20  *    including a substantially similar Disclaimer requirement for further
     21  *    binary redistribution.
     22  * 3. Neither the names of the above-listed copyright holders nor the names
     23  *    of any contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * Alternatively, this software may be distributed under the terms of the
     27  * GNU General Public License ("GPL") version 2 as published by the Free
     28  * Software Foundation.
     29  *
     30  * NO WARRANTY
     31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
     34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     41  * POSSIBILITY OF SUCH DAMAGES.
     42  */
     43 
     44 #include "acpi.h"
     45 #include "accommon.h"
     46 #include "acapps.h"
     47 
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <ctype.h>
     52 
     53 /* Local prototypes */
     54 
     55 static void
     56 AxStrlwr (
     57     char                    *String);
     58 
     59 static void
     60 AxCheckAscii (
     61     char                    *Name,
     62     int                     Count);
     63 
     64 static void
     65 AxNormalizeSignature (
     66     char                    *Signature);
     67 
     68 static unsigned int
     69 AxGetNextInstance (
     70     char                    *InputPathname,
     71     char                    *Signature);
     72 
     73 static size_t
     74 AxGetTableHeader (
     75     FILE                    *InputFile,
     76     unsigned char           *OutputData);
     77 
     78 static unsigned int
     79 AxCountTableInstances (
     80     char                    *InputPathname,
     81     char                    *Signature);
     82 
     83 int
     84 AxExtractTables (
     85     char                    *InputPathname,
     86     char                    *Signature,
     87     unsigned int            MinimumInstances);
     88 
     89 int
     90 AxListTables (
     91     char                    *InputPathname);
     92 
     93 static size_t
     94 AxConvertLine (
     95     char                    *InputLine,
     96     unsigned char           *OutputData);
     97 
     98 static int
     99 AxIsEmptyLine (
    100     char                    *Buffer);
    101 
    102 typedef struct AxTableInfo
    103 {
    104     UINT32                  Signature;
    105     unsigned int            Instances;
    106     unsigned int            NextInstance;
    107     struct AxTableInfo      *Next;
    108 
    109 } AX_TABLE_INFO;
    110 
    111 /* Extraction states */
    112 
    113 #define AX_STATE_FIND_HEADER        0
    114 #define AX_STATE_EXTRACT_DATA       1
    115 
    116 /* Miscellaneous constants */
    117 
    118 #define AX_LINE_BUFFER_SIZE         256
    119 #define AX_MIN_TABLE_NAME_LENGTH    6   /* strlen ("DSDT @") */
    120 
    121 
    122 static AX_TABLE_INFO        *AxTableListHead = NULL;
    123 static char                 Filename[16];
    124 static unsigned char        Data[16];
    125 static char                 LineBuffer[AX_LINE_BUFFER_SIZE];
    126 static char                 HeaderBuffer[AX_LINE_BUFFER_SIZE];
    127 static char                 InstanceBuffer[AX_LINE_BUFFER_SIZE];
    128 
    129 
    130 /*******************************************************************************
    131  *
    132  * FUNCTION:    AxStrlwr
    133  *
    134  * PARAMETERS:  String              - Ascii string
    135  *
    136  * RETURN:      None
    137  *
    138  * DESCRIPTION: String lowercase function.
    139  *
    140  ******************************************************************************/
    141 
    142 static void
    143 AxStrlwr (
    144     char                    *String)
    145 {
    146 
    147     while (*String)
    148     {
    149         *String = (char) tolower ((int) *String);
    150         String++;
    151     }
    152 }
    153 
    154 
    155 /*******************************************************************************
    156  *
    157  * FUNCTION:    AxCheckAscii
    158  *
    159  * PARAMETERS:  Name                - Ascii string, at least as long as Count
    160  *              Count               - Number of characters to check
    161  *
    162  * RETURN:      None
    163  *
    164  * DESCRIPTION: Ensure that the requested number of characters are printable
    165  *              Ascii characters. Sets non-printable and null chars to <space>.
    166  *
    167  ******************************************************************************/
    168 
    169 static void
    170 AxCheckAscii (
    171     char                    *Name,
    172     int                     Count)
    173 {
    174     int                     i;
    175 
    176 
    177     for (i = 0; i < Count; i++)
    178     {
    179         if (!Name[i] || !isprint ((int) Name[i]))
    180         {
    181             Name[i] = ' ';
    182         }
    183     }
    184 }
    185 
    186 
    187 /******************************************************************************
    188  *
    189  * FUNCTION:    AxIsEmptyLine
    190  *
    191  * PARAMETERS:  Buffer              - Line from input file
    192  *
    193  * RETURN:      TRUE if line is empty (zero or more blanks only)
    194  *
    195  * DESCRIPTION: Determine if an input line is empty.
    196  *
    197  ******************************************************************************/
    198 
    199 static int
    200 AxIsEmptyLine (
    201     char                    *Buffer)
    202 {
    203 
    204     /* Skip all spaces */
    205 
    206     while (*Buffer == ' ')
    207     {
    208         Buffer++;
    209     }
    210 
    211     /* If end-of-line, this line is empty */
    212 
    213     if (*Buffer == '\n')
    214     {
    215         return (1);
    216     }
    217 
    218     return (0);
    219 }
    220 
    221 
    222 /*******************************************************************************
    223  *
    224  * FUNCTION:    AxNormalizeSignature
    225  *
    226  * PARAMETERS:  Name                - Ascii string containing an ACPI signature
    227  *
    228  * RETURN:      None
    229  *
    230  * DESCRIPTION: Change "RSD PTR" to "RSDP"
    231  *
    232  ******************************************************************************/
    233 
    234 static void
    235 AxNormalizeSignature (
    236     char                    *Signature)
    237 {
    238 
    239     if (!strncmp (Signature, "RSD ", 4))
    240     {
    241         Signature[3] = 'P';
    242     }
    243 }
    244 
    245 
    246 /******************************************************************************
    247  *
    248  * FUNCTION:    AxConvertLine
    249  *
    250  * PARAMETERS:  InputLine           - One line from the input acpidump file
    251  *              OutputData          - Where the converted data is returned
    252  *
    253  * RETURN:      The number of bytes actually converted
    254  *
    255  * DESCRIPTION: Convert one line of ascii text binary (up to 16 bytes)
    256  *
    257  ******************************************************************************/
    258 
    259 static size_t
    260 AxConvertLine (
    261     char                    *InputLine,
    262     unsigned char           *OutputData)
    263 {
    264     char                    *End;
    265     int                     BytesConverted;
    266     int                     Converted[16];
    267     int                     i;
    268 
    269 
    270     /* Terminate the input line at the end of the actual data (for sscanf) */
    271 
    272     End = strstr (InputLine + 2, "  ");
    273     if (!End)
    274     {
    275         return (0); /* Don't understand the format */
    276     }
    277     *End = 0;
    278 
    279     /*
    280      * Convert one line of table data, of the form:
    281      * <offset>: <up to 16 bytes of hex data> <ASCII representation> <newline>
    282      *
    283      * Example:
    284      * 02C0: 5F 53 42 5F 4C 4E 4B 44 00 12 13 04 0C FF FF 08  _SB_LNKD........
    285      */
    286     BytesConverted = sscanf (InputLine,
    287         "%*s %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
    288         &Converted[0],  &Converted[1],  &Converted[2],  &Converted[3],
    289         &Converted[4],  &Converted[5],  &Converted[6],  &Converted[7],
    290         &Converted[8],  &Converted[9],  &Converted[10], &Converted[11],
    291         &Converted[12], &Converted[13], &Converted[14], &Converted[15]);
    292 
    293     /* Pack converted data into a byte array */
    294 
    295     for (i = 0; i < BytesConverted; i++)
    296     {
    297         OutputData[i] = (unsigned char) Converted[i];
    298     }
    299 
    300     return ((size_t) BytesConverted);
    301 }
    302 
    303 
    304 /******************************************************************************
    305  *
    306  * FUNCTION:    AxGetTableHeader
    307  *
    308  * PARAMETERS:  InputFile           - Handle for the input acpidump file
    309  *              OutputData          - Where the table header is returned
    310  *
    311  * RETURN:      The actual number of bytes converted
    312  *
    313  * DESCRIPTION: Extract and convert an ACPI table header
    314  *
    315  ******************************************************************************/
    316 
    317 static size_t
    318 AxGetTableHeader (
    319     FILE                    *InputFile,
    320     unsigned char           *OutputData)
    321 {
    322     size_t                  BytesConverted;
    323     size_t                  TotalConverted = 0;
    324     int                     i;
    325 
    326 
    327     /* Get the full 36 byte ACPI table header, requires 3 input text lines */
    328 
    329     for (i = 0; i < 3; i++)
    330     {
    331         if (!fgets (HeaderBuffer, AX_LINE_BUFFER_SIZE, InputFile))
    332         {
    333             return (TotalConverted);
    334         }
    335 
    336         BytesConverted = AxConvertLine (HeaderBuffer, OutputData);
    337         TotalConverted += BytesConverted;
    338         OutputData += 16;
    339 
    340         if (BytesConverted != 16)
    341         {
    342             return (TotalConverted);
    343         }
    344     }
    345 
    346     return (TotalConverted);
    347 }
    348 
    349 
    350 /******************************************************************************
    351  *
    352  * FUNCTION:    AxCountTableInstances
    353  *
    354  * PARAMETERS:  InputPathname       - Filename for acpidump file
    355  *              Signature           - Requested signature to count
    356  *
    357  * RETURN:      The number of instances of the signature
    358  *
    359  * DESCRIPTION: Count the instances of tables with the given signature within
    360  *              the input acpidump file.
    361  *
    362  ******************************************************************************/
    363 
    364 static unsigned int
    365 AxCountTableInstances (
    366     char                    *InputPathname,
    367     char                    *Signature)
    368 {
    369     FILE                    *InputFile;
    370     unsigned int            Instances = 0;
    371 
    372 
    373     InputFile = fopen (InputPathname, "rt");
    374     if (!InputFile)
    375     {
    376         printf ("Could not open file %s\n", InputPathname);
    377         return (0);
    378     }
    379 
    380     /* Count the number of instances of this signature */
    381 
    382     while (fgets (InstanceBuffer, AX_LINE_BUFFER_SIZE, InputFile))
    383     {
    384         /* Ignore empty lines and lines that start with a space */
    385 
    386         if (AxIsEmptyLine (InstanceBuffer) ||
    387             (InstanceBuffer[0] == ' '))
    388         {
    389             continue;
    390         }
    391 
    392         AxNormalizeSignature (InstanceBuffer);
    393         if (ACPI_COMPARE_NAME (InstanceBuffer, Signature))
    394         {
    395             Instances++;
    396         }
    397     }
    398 
    399     fclose (InputFile);
    400     return (Instances);
    401 }
    402 
    403 
    404 /******************************************************************************
    405  *
    406  * FUNCTION:    AxGetNextInstance
    407  *
    408  * PARAMETERS:  InputPathname       - Filename for acpidump file
    409  *              Signature           - Requested ACPI signature
    410  *
    411  * RETURN:      The next instance number for this signature. Zero if this
    412  *              is the first instance of this signature.
    413  *
    414  * DESCRIPTION: Get the next instance number of the specified table. If this
    415  *              is the first instance of the table, create a new instance
    416  *              block. Note: only SSDT and PSDT tables can have multiple
    417  *              instances.
    418  *
    419  ******************************************************************************/
    420 
    421 static unsigned int
    422 AxGetNextInstance (
    423     char                    *InputPathname,
    424     char                    *Signature)
    425 {
    426     AX_TABLE_INFO           *Info;
    427 
    428 
    429     Info = AxTableListHead;
    430     while (Info)
    431     {
    432         if (*(UINT32 *) Signature == Info->Signature)
    433         {
    434             break;
    435         }
    436 
    437         Info = Info->Next;
    438     }
    439 
    440     if (!Info)
    441     {
    442         /* Signature not found, create new table info block */
    443 
    444         Info = malloc (sizeof (AX_TABLE_INFO));
    445         if (!Info)
    446         {
    447             printf ("Could not allocate memory\n");
    448             exit (0);
    449         }
    450 
    451         Info->Signature = *(UINT32 *) Signature;
    452         Info->Instances = AxCountTableInstances (InputPathname, Signature);
    453         Info->NextInstance = 1;
    454         Info->Next = AxTableListHead;
    455         AxTableListHead = Info;
    456     }
    457 
    458     if (Info->Instances > 1)
    459     {
    460         return (Info->NextInstance++);
    461     }
    462 
    463     return (0);
    464 }
    465 
    466 
    467 /******************************************************************************
    468  *
    469  * FUNCTION:    AxExtractTables
    470  *
    471  * PARAMETERS:  InputPathname       - Filename for acpidump file
    472  *              Signature           - Requested ACPI signature to extract.
    473  *                                    NULL means extract ALL tables.
    474  *              MinimumInstances    - Min instances that are acceptable
    475  *
    476  * RETURN:      Status
    477  *
    478  * DESCRIPTION: Convert text ACPI tables to binary
    479  *
    480  ******************************************************************************/
    481 
    482 int
    483 AxExtractTables (
    484     char                    *InputPathname,
    485     char                    *Signature,
    486     unsigned int            MinimumInstances)
    487 {
    488     FILE                    *InputFile;
    489     FILE                    *OutputFile = NULL;
    490     size_t                  BytesWritten;
    491     size_t                  TotalBytesWritten = 0;
    492     size_t                  BytesConverted;
    493     unsigned int            State = AX_STATE_FIND_HEADER;
    494     unsigned int            FoundTable = 0;
    495     unsigned int            Instances = 0;
    496     unsigned int            ThisInstance;
    497     char                    ThisSignature[4];
    498     int                     Status = 0;
    499 
    500 
    501     /* Open input in text mode, output is in binary mode */
    502 
    503     InputFile = fopen (InputPathname, "rt");
    504     if (!InputFile)
    505     {
    506         printf ("Could not open file %s\n", InputPathname);
    507         return (-1);
    508     }
    509 
    510     if (Signature)
    511     {
    512         /* Are there enough instances of the table to continue? */
    513 
    514         AxNormalizeSignature (Signature);
    515 
    516         Instances = AxCountTableInstances (InputPathname, Signature);
    517         if (Instances < MinimumInstances)
    518         {
    519             printf ("Table %s was not found in %s\n", Signature, InputPathname);
    520             Status = -1;
    521             goto CleanupAndExit;
    522         }
    523 
    524         if (Instances == 0)
    525         {
    526             goto CleanupAndExit;
    527         }
    528     }
    529 
    530     /* Convert all instances of the table to binary */
    531 
    532     while (fgets (LineBuffer, AX_LINE_BUFFER_SIZE, InputFile))
    533     {
    534         switch (State)
    535         {
    536         case AX_STATE_FIND_HEADER:
    537 
    538             /* Ignore lines that are too short to be header lines */
    539 
    540             if (strlen (LineBuffer) < AX_MIN_TABLE_NAME_LENGTH)
    541             {
    542                 continue;
    543             }
    544 
    545             /* Ignore empty lines and lines that start with a space */
    546 
    547             if (AxIsEmptyLine (LineBuffer) ||
    548                 (LineBuffer[0] == ' '))
    549             {
    550                 continue;
    551             }
    552 
    553             /*
    554              * Ignore lines that are not of the form <sig> @ <addr>.
    555              * Examples of lines that must be supported:
    556              *
    557              * DSDT @ 0x737e4000
    558              * XSDT @ 0x737f2fff
    559              * RSD PTR @ 0xf6cd0
    560              * SSDT @ (nil)
    561              */
    562             if (!strstr (LineBuffer, " @ "))
    563             {
    564                 continue;
    565             }
    566 
    567             AxNormalizeSignature (LineBuffer);
    568             ACPI_MOVE_NAME (ThisSignature, LineBuffer);
    569 
    570             if (Signature)
    571             {
    572                 /* Ignore signatures that don't match */
    573 
    574                 if (!ACPI_COMPARE_NAME (ThisSignature, Signature))
    575                 {
    576                     continue;
    577                 }
    578             }
    579 
    580             /*
    581              * Get the instance number for this signature. Only the
    582              * SSDT and PSDT tables can have multiple instances.
    583              */
    584             ThisInstance = AxGetNextInstance (InputPathname, ThisSignature);
    585 
    586             /* Build an output filename and create/open the output file */
    587 
    588             if (ThisInstance > 0)
    589             {
    590                 snprintf (Filename, sizeof(Filename), "%4.4s%u.dat", ThisSignature, ThisInstance);
    591             }
    592             else
    593             {
    594                 snprintf (Filename, sizeof(Filename), "%4.4s.dat", ThisSignature);
    595             }
    596 
    597             AxStrlwr (Filename);
    598             OutputFile = fopen (Filename, "w+b");
    599             if (!OutputFile)
    600             {
    601                 printf ("Could not open file %s\n", Filename);
    602                 Status = -1;
    603                 goto CleanupAndExit;
    604             }
    605 
    606             State = AX_STATE_EXTRACT_DATA;
    607             TotalBytesWritten = 0;
    608             FoundTable = 1;
    609             continue;
    610 
    611         case AX_STATE_EXTRACT_DATA:
    612 
    613             /* Empty line or non-data line terminates the data */
    614 
    615             if (AxIsEmptyLine (LineBuffer) ||
    616                 (LineBuffer[0] != ' '))
    617             {
    618                 fclose (OutputFile);
    619                 OutputFile = NULL;
    620                 State = AX_STATE_FIND_HEADER;
    621 
    622                 printf ("Acpi table [%4.4s] - %u bytes written to %s\n",
    623                     ThisSignature, (unsigned int) TotalBytesWritten, Filename);
    624                 continue;
    625             }
    626 
    627             /* Convert the ascii data (one line of text) to binary */
    628 
    629             BytesConverted = AxConvertLine (LineBuffer, Data);
    630 
    631             /* Write the binary data */
    632 
    633             BytesWritten = fwrite (Data, 1, BytesConverted, OutputFile);
    634             if (BytesWritten != BytesConverted)
    635             {
    636                 printf ("Error when writing file %s\n", Filename);
    637                 fclose (OutputFile);
    638                 OutputFile = NULL;
    639                 Status = -1;
    640                 goto CleanupAndExit;
    641             }
    642 
    643             TotalBytesWritten += BytesConverted;
    644             continue;
    645 
    646         default:
    647 
    648             Status = -1;
    649             goto CleanupAndExit;
    650         }
    651     }
    652 
    653     if (!FoundTable)
    654     {
    655         printf ("Table %s was not found in %s\n", Signature, InputPathname);
    656     }
    657 
    658 
    659 CleanupAndExit:
    660 
    661     if (OutputFile)
    662     {
    663         fclose (OutputFile);
    664         if (State == AX_STATE_EXTRACT_DATA)
    665         {
    666             /* Received an EOF while extracting data */
    667 
    668             printf ("Acpi table [%4.4s] - %u bytes written to %s\n",
    669                 ThisSignature, (unsigned int) TotalBytesWritten, Filename);
    670         }
    671     }
    672 
    673     fclose (InputFile);
    674     return (Status);
    675 }
    676 
    677 
    678 /******************************************************************************
    679  *
    680  * FUNCTION:    AxListTables
    681  *
    682  * PARAMETERS:  InputPathname       - Filename for acpidump file
    683  *
    684  * RETURN:      Status
    685  *
    686  * DESCRIPTION: Display info for all ACPI tables found in input. Does not
    687  *              perform an actual extraction of the tables.
    688  *
    689  ******************************************************************************/
    690 
    691 int
    692 AxListTables (
    693     char                    *InputPathname)
    694 {
    695     FILE                    *InputFile;
    696     size_t                  HeaderSize;
    697     unsigned char           Header[48];
    698     int                     TableCount = 0;
    699     ACPI_TABLE_HEADER       *TableHeader = (ACPI_TABLE_HEADER *) (void *) Header;
    700 
    701 
    702     /* Open input in text mode, output is in binary mode */
    703 
    704     InputFile = fopen (InputPathname, "rt");
    705     if (!InputFile)
    706     {
    707         printf ("Could not open file %s\n", InputPathname);
    708         return (-1);
    709     }
    710 
    711     /* Dump the headers for all tables found in the input file */
    712 
    713     printf ("\nSignature  Length      Revision   OemId    OemTableId"
    714             "   OemRevision CompilerId CompilerRevision\n\n");
    715 
    716     while (fgets (LineBuffer, AX_LINE_BUFFER_SIZE, InputFile))
    717     {
    718         /* Ignore empty lines and lines that start with a space */
    719 
    720         if (AxIsEmptyLine (LineBuffer) ||
    721             (LineBuffer[0] == ' '))
    722         {
    723             continue;
    724         }
    725 
    726         /* Get the 36 byte header and display the fields */
    727 
    728         HeaderSize = AxGetTableHeader (InputFile, Header);
    729         if (HeaderSize < 16)
    730         {
    731             continue;
    732         }
    733 
    734         /* RSDP has an oddball signature and header */
    735 
    736         if (!strncmp (TableHeader->Signature, "RSD PTR ", 8))
    737         {
    738             AxCheckAscii ((char *) &Header[9], 6);
    739             printf ("%7.4s                          \"%6.6s\"\n", "RSDP", &Header[9]);
    740             TableCount++;
    741             continue;
    742         }
    743 
    744         /* Minimum size for table with standard header */
    745 
    746         if (HeaderSize < sizeof (ACPI_TABLE_HEADER))
    747         {
    748             continue;
    749         }
    750 
    751         /* Signature and Table length */
    752 
    753         TableCount++;
    754         printf ("%7.4s   0x%8.8X", TableHeader->Signature, TableHeader->Length);
    755 
    756         /* FACS has only signature and length */
    757 
    758         if (ACPI_COMPARE_NAME (TableHeader->Signature, "FACS"))
    759         {
    760             printf ("\n");
    761             continue;
    762         }
    763 
    764         /* OEM IDs and Compiler IDs */
    765 
    766         AxCheckAscii (TableHeader->OemId, 6);
    767         AxCheckAscii (TableHeader->OemTableId, 8);
    768         AxCheckAscii (TableHeader->AslCompilerId, 4);
    769 
    770         printf ("     0x%2.2X    \"%6.6s\"  \"%8.8s\"   0x%8.8X    \"%4.4s\"     0x%8.8X\n",
    771             TableHeader->Revision, TableHeader->OemId,
    772             TableHeader->OemTableId, TableHeader->OemRevision,
    773             TableHeader->AslCompilerId, TableHeader->AslCompilerRevision);
    774     }
    775 
    776     printf ("\nFound %u ACPI tables\n", TableCount);
    777     fclose (InputFile);
    778     return (0);
    779 }
    780