LCOV - code coverage report
Current view: top level - tools - as_rc.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 88 201 43.8 %
Date: 2023-07-29 22:00:24 Functions: 4 5 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2005-2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/as2js
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : // snapdev
      20             : //
      21             : #include    <snapdev/file_contents.h>
      22             : #include    <snapdev/hexadecimal_string.h>
      23             : #include    <snapdev/pathinfo.h>
      24             : #include    <snapdev/string_replace_many.h>
      25             : 
      26             : 
      27             : // C
      28             : //
      29             : #include    <string.h>
      30             : 
      31             : 
      32             : 
      33             : /** \file
      34             :  * \brief Tool used to convert text files to a C string.
      35             :  *
      36             :  * Often, I would like to have a "resource" built from an external text file
      37             :  * which gets compiled so the resulting library or tool has the resource
      38             :  * within its .DATA section instead of having to load a file.
      39             :  *
      40             :  * This tool coverts such text files to a .ci (C include) file with a string
      41             :  * composed of the input file converted to lines, "\\n", and also a length
      42             :  * for the string. The length is useful to create an std::string or when the
      43             :  * input may include "\\0" characters.
      44             :  *
      45             :  * If the input may include binary, use the --binary command line option and
      46             :  * all the bytes that are not ASCII will be transformed to the `\xXX` syntax.
      47             :  */
      48             : 
      49             : 
      50             : namespace
      51             : {
      52             : 
      53             : 
      54             : class as_rc
      55             : {
      56             : public:
      57             :                                 as_rc(int argc, char * argv[]);
      58             :                                 as_rc(as_rc const & rhs) = delete;
      59             :     as_rc &                     operator = (as_rc const & rhs) = delete;
      60             : 
      61             :     int                         init();
      62             :     int                         run();
      63             : 
      64             : private:
      65             :     void                        usage();
      66             : 
      67             :     int                         f_argc = 0;
      68             :     char **                     f_argv = nullptr;
      69             :     std::vector<std::string>    f_filenames = {};
      70             :     std::string                 f_output = std::string();
      71             :     std::string                 f_header = std::string();
      72             :     std::string                 f_name = std::string();
      73             :     std::string                 f_namespace = std::string();
      74             :     bool                        f_binary = false;
      75             :     bool                        f_verbose = false;
      76             : };
      77             : 
      78             : 
      79           0 : void as_rc::usage()
      80             : {
      81           0 :     std::cout << "Usage: as-rc [--opts] [--] <in1> <in2> ... <inN>\n";
      82           0 :     std::cout << "where [--opts] is one of more of the following:\n";
      83           0 :     std::cout << "   -h | --help                 print out this help screen.\n";
      84           0 :     std::cout << "   -o | --output <filename>    specify the output filename.\n";
      85           0 :     std::cout << "   -n | --name <name>          name of the final string variable.\n";
      86           0 :     std::cout << "   -n | --namespace <name>     place variables in a C++ namespace.\n";
      87           0 :     std::cout << "   -b | --binary               input is binary, not text.\n";
      88           0 :     std::cout << "   -v | --verbose              display messages.\n";
      89           0 :     std::cout << "   --                          anything after this are input filenames.\n";
      90           0 : }
      91             : 
      92             : 
      93           4 : as_rc::as_rc(int argc, char * argv[])
      94           4 :     : f_argc(argc)
      95           4 :     , f_argv(argv)
      96             : {
      97           4 : }
      98             : 
      99             : 
     100           4 : int as_rc::init()
     101             : {
     102           4 :     bool more_options(true);
     103          21 :     for(int i(1); i < f_argc; ++i)
     104             :     {
     105          17 :         if(more_options
     106          17 :         && f_argv[i][0] == '-')
     107             :         {
     108          13 :             if(f_argv[i][1] == '-')
     109             :             {
     110             :                 // long form
     111             :                 //
     112          13 :                 if(f_argv[i][2] == '\0')
     113             :                 {
     114           0 :                     more_options = false;
     115             :                 }
     116          13 :                 else if(strcmp(f_argv[i] + 2, "help") == 0)
     117             :                 {
     118           0 :                     usage();
     119           0 :                     return 1;
     120             :                 }
     121          13 :                 else if(strcmp(f_argv[i] + 2, "name") == 0)
     122             :                 {
     123           4 :                     ++i;
     124           4 :                     if(i >= f_argc)
     125             :                     {
     126           0 :                         std::cerr << "error:as-rc: --name expect a parameter.\n";
     127           0 :                         return 1;
     128             :                     }
     129           4 :                     if(!f_name.empty())
     130             :                     {
     131           0 :                         std::cerr << "error:as-rc: --name already defined.\n";
     132           0 :                         return 1;
     133             :                     }
     134           4 :                     f_name = f_argv[i];
     135             :                 }
     136           9 :                 else if(strcmp(f_argv[i] + 2, "namespace") == 0)
     137             :                 {
     138           4 :                     ++i;
     139           4 :                     if(i >= f_argc)
     140             :                     {
     141           0 :                         std::cerr << "error:as-rc: --namespace expect a parameter.\n";
     142           0 :                         return 1;
     143             :                     }
     144           4 :                     if(!f_namespace.empty())
     145             :                     {
     146           0 :                         std::cerr << "error:as-rc: --namespace already defined.\n";
     147           0 :                         return 1;
     148             :                     }
     149           4 :                     f_namespace = f_argv[i];
     150             :                 }
     151           5 :                 else if(strcmp(f_argv[i] + 2, "output") == 0)
     152             :                 {
     153           4 :                     ++i;
     154           4 :                     if(i >= f_argc)
     155             :                     {
     156           0 :                         std::cerr << "error:as-rc: --output expect a parameter.\n";
     157           0 :                         return 1;
     158             :                     }
     159           4 :                     if(!f_output.empty())
     160             :                     {
     161           0 :                         std::cerr << "error:as-rc: --output already defined.\n";
     162           0 :                         return 1;
     163             :                     }
     164           4 :                     f_output = f_argv[i];
     165             :                 }
     166           1 :                 else if(strcmp(f_argv[i] + 2, "verbose") == 0)
     167             :                 {
     168           1 :                     f_verbose = true;
     169             :                 }
     170             :                 else
     171             :                 {
     172             :                     std::cerr << "error:as-rc: unknown command line option \""
     173           0 :                         << f_argv[i]
     174           0 :                         << "\".\n";
     175           0 :                     return 1;
     176             :                 }
     177             :             }
     178             :             else
     179             :             {
     180           0 :                 std::size_t const len(strlen(f_argv[i]));
     181           0 :                 for(std::size_t j(1); j < len; ++j)
     182             :                 {
     183             :                     // short form
     184             :                     //
     185           0 :                     switch(f_argv[i][j])
     186             :                     {
     187           0 :                     case 'h':
     188           0 :                         usage();
     189           0 :                         return 1;
     190             :                         break;
     191             : 
     192           0 :                     case 'n':
     193           0 :                         if(i + 1 >= f_argc)
     194             :                         {
     195           0 :                             std::cerr << "error:as-rc: -o expect a parameter.\n";
     196           0 :                             return 1;
     197             :                         }
     198           0 :                         if(!f_name.empty())
     199             :                         {
     200           0 :                             std::cerr << "error:as-rc: -n already defined.\n";
     201           0 :                             return 1;
     202             :                         }
     203           0 :                         ++i;
     204           0 :                         f_name = f_argv[i];
     205           0 :                         break;
     206             : 
     207           0 :                     case 'o':
     208           0 :                         if(i + 1 >= f_argc)
     209             :                         {
     210           0 :                             std::cerr << "error:as-rc: -o expect a parameter.\n";
     211           0 :                             return 1;
     212             :                         }
     213           0 :                         if(!f_output.empty())
     214             :                         {
     215           0 :                             std::cerr << "error:as-rc: -o already defined.\n";
     216           0 :                             return 1;
     217             :                         }
     218           0 :                         ++i;
     219           0 :                         f_output = f_argv[i];
     220           0 :                         break;
     221             : 
     222           0 :                     case 'v':
     223           0 :                         f_verbose = true;
     224           0 :                         break;
     225             : 
     226             :                     }
     227             :                 }
     228             :             }
     229          13 :         }
     230             :         else
     231             :         {
     232           4 :            f_filenames.push_back(f_argv[i]);
     233             :         }
     234             :     }
     235             : 
     236           4 :     if(f_filenames.empty())
     237             :     {
     238           0 :         std::cerr << "error:as-rc: at least one input filename must be specified.\n";
     239           0 :         return 1;
     240             :     }
     241             : 
     242           4 :     if(f_output.empty())
     243             :     {
     244           0 :         if(f_filenames.size() == 1)
     245             :         {
     246           0 :             f_output = snapdev::pathinfo::replace_suffix(f_filenames[0], ".ci");
     247           0 :             if(f_filenames[0] == f_output)
     248             :             {
     249           0 :                 std::cerr << "error:as-rc: your input file is a .ci file, you must specify a --output in this case.\n";
     250           0 :                 return 1;
     251             :             }
     252             :         }
     253             :         else
     254             :         {
     255             :             // default when not specified
     256             :             //
     257             :             //f_output = "-"; -- not supported because we generate a header as well
     258           0 :             std::cerr << "error:as-rc: an output file name is required.\n";
     259           0 :             return 1;
     260             :         }
     261             :     }
     262             : 
     263           4 :     if(std::find(f_filenames.begin(), f_filenames.end(), f_output) != f_filenames.end())
     264             :     {
     265             :         std::cerr
     266             :             << "error:as-rc: one of your input filename is the same as the output filename: \""
     267           0 :             << f_output
     268           0 :             << "\".\n";
     269           0 :         return 1;
     270             :     }
     271             : 
     272           4 :     if(f_output == "-")
     273             :     {
     274           0 :         f_verbose = false;
     275             :     }
     276             : 
     277           4 :     f_header = snapdev::pathinfo::replace_suffix(f_output, ".*", ".h");
     278             : 
     279           4 :     if(f_name.empty())
     280             :     {
     281           0 :         if(f_filenames.size() != 1)
     282             :         {
     283             :             std::cerr << "error:as-rc: when you have more than one filename,"
     284           0 :                          " you must specify a --name to define the string name.\n";
     285           0 :             return 1;
     286             :         }
     287           0 :         f_name = snapdev::pathinfo::basename(f_filenames[0]);
     288           0 :         if(f_name.empty())
     289             :         {
     290             :             std::cerr << "error:as-rc: could not auto-define a string name,"
     291           0 :                          " try again with the --name command line option.\n";
     292           0 :             return 1;
     293             :         }
     294             :     }
     295             : 
     296           4 :     return 0;
     297             : }
     298             : 
     299             : 
     300           4 : int as_rc::run()
     301             : {
     302           4 :     std::string input;
     303           8 :     for(auto const & f : f_filenames)
     304             :     {
     305           4 :         if(f_verbose)
     306             :         {
     307             :             std::cout
     308             :                 << "as-rc:info: reading \""
     309             :                 << f
     310           1 :                 << "\".\n";
     311             :         }
     312             : 
     313           4 :         snapdev::file_contents in(f);
     314           4 :         if(!in.read_all())
     315             :         {
     316           0 :             int const e(errno);
     317             :             std::cerr << "error:as-rc: could not open \""
     318             :                 << f
     319           0 :                 << "\" for reading: "
     320             :                 << e
     321             :                 << " -- "
     322           0 :                 << strerror(e)
     323           0 :                 << "\n";
     324           0 :             return 1;
     325             :         }
     326           4 :         input += in.contents();
     327           4 :     }
     328             : 
     329             :     // we've got all the contents in memory, convert to a C literal string
     330             :     //
     331           4 :     std::string output(
     332             :           "/* AUTO-GENERATED FILE -- DO NOT EDIT -- see as-rc(1) for details */\n"
     333          12 :           "#include \"" + f_header + "\"\n"
     334          20 :         + (f_namespace.empty() ? "" : "namespace " + f_namespace + "{\n")
     335          32 :         + "size_t const " + f_name + "_size=" + std::to_string(input.length()) + ";\n"
     336          12 :           "char const * " + f_name + "=\n");
     337             : 
     338           4 :     if(f_binary)
     339             :     {
     340           0 :         unsigned pos(0);
     341           0 :         for(auto const & byte : input)
     342             :         {
     343           0 :             if((pos & 16) == 0)
     344             :             {
     345           0 :                 if(pos > 0)
     346             :                 {
     347           0 :                     output += "\"\n\"";
     348             :                 }
     349             :                 else
     350             :                 {
     351           0 :                     output += '"';
     352             :                 }
     353             :             }
     354           0 :             ++pos;
     355             : 
     356           0 :             if(byte == '"')
     357             :             {
     358           0 :                 output += "\\\"";
     359             :             }
     360           0 :             else if(static_cast<std::uint8_t>(byte) >= ' '
     361           0 :                  && static_cast<std::uint8_t>(byte) <= 0x7F)
     362             :             {
     363           0 :                 output += byte;
     364             :             }
     365             :             else
     366             :             {
     367           0 :                 output += "\\x";
     368           0 :                 output += snapdev::int_to_hex(byte, false, 2);
     369             :             }
     370             :         }
     371           0 :         if(pos == 0)
     372             :         {
     373             :             // case where the input is completely empty
     374             :             //
     375           0 :             output += '"';
     376             :         }
     377           0 :         output += "\";\n";
     378             :     }
     379             :     else
     380             :     {
     381           4 :         output += '"';
     382          16 :         output += snapdev::string_replace_many(input, {
     383             :                         // WARNING: the order is important
     384             :                         {"\"", "\\\""},
     385             :                         {"\n", "\\n\"\n\""},
     386           4 :                     });
     387           4 :         output += "\";\n";
     388             :     }
     389           4 :     if(!f_namespace.empty())
     390             :     {
     391           4 :         output += "}\n";
     392             :     }
     393             : 
     394           4 :     if(f_verbose)
     395             :     {
     396             :         std::cout
     397             :             << "as-rc:info: writing to \""
     398           1 :             << f_output
     399           1 :             << "\".\n";
     400             :     }
     401             : 
     402           4 :     std::ofstream out;
     403           4 :     out.open(f_output);
     404           4 :     if(!out)
     405             :     {
     406             :         std::cerr << "error:as-rc: could not open \""
     407           0 :             << f_output
     408           0 :             << "\" for writing the output.\n";
     409           0 :         return 1;
     410             :     }
     411           4 :     out << output;
     412           4 :     if(!out)
     413             :     {
     414             :         std::cerr << "error:as-rc: errors happened while writing to \""
     415           0 :             << f_output
     416           0 :             << "\".\n";
     417           0 :         return 1;
     418             :     }
     419             : 
     420           4 :     if(f_verbose)
     421             :     {
     422             :         std::cout
     423             :             << "as-rc:info: writing to \""
     424           1 :             << f_header
     425           1 :             << "\".\n";
     426             :     }
     427             : 
     428           4 :     std::ofstream header;
     429           4 :     header.open(f_header);
     430           4 :     if(!header)
     431             :     {
     432             :         std::cerr
     433             :             << "error:as-rc: could not open \""
     434           0 :             << f_header
     435           0 :             << "\" for writing the header.\n";
     436           0 :         return 1;
     437             :     }
     438             :     header
     439             :         << "/* AUTO-GENERATED FILE -- DO NOT EDIT -- see as-rc(1) for details */\n"
     440             :            "#include <stddef.h>\n"
     441           8 :         << (f_namespace.empty() ? "" : "namespace " + f_namespace + "{\n")
     442           4 :         << "extern size_t const " << f_name << "_size;\n"
     443           4 :            "extern char const * " << f_name << ";\n"
     444           8 :         << (f_namespace.empty() ? "" : "}\n");
     445           4 :     if(!header)
     446             :     {
     447             :         std::cerr << "error:as-rc: errors happened while writing to \""
     448           0 :             << f_header
     449           0 :             << "\".\n";
     450           0 :         return 1;
     451             :     }
     452             : 
     453           4 :     if(f_verbose)
     454             :     {
     455             :         std::cout
     456           1 :             << "as-rc:info: success.\n";
     457             :     }
     458             : 
     459           4 :     return 0;
     460           4 : }
     461             : 
     462             : 
     463             : } // no name namespace
     464             : 
     465           4 : int main(int argc, char * argv[])
     466             : {
     467           4 :     as_rc rc(argc, argv);
     468           4 :     int r(rc.init());
     469           4 :     if(r != 0)
     470             :     {
     471           0 :         return r;
     472             :     }
     473           4 :     return rc.run();
     474           4 : }
     475             : 
     476             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14