Code View

mstring / mstring-1.3.1.2 / src / main.cpp
// SPDX-License-Identifier: MIT
#include <cstdlib>
#include <exception>
#include <filesystem>
#include <iostream>
#include <sstream>
#include <string>

#include <unistd.h>

#include "MException.hh"
#include "MString.hh"
#include "MStringParser.hh"

#ifndef MSTRING_VERSION
#define MSTRING_VERSION "unknown"
#endif

namespace
{
  void Help( std::ostream &stream )
  {
    stream << "usage:" << std::endl
           << "    mstring [-h] [-v] [-t] [-w] [-m <module>] [-O <folder>] [-I <folders>]... [<message file>]" << std::endl
           << std::endl
           << "  -h            display this page" << std::endl
           << "  -v            display the version" << std::endl
           << "  -t            trace the parsing (prints every $MESSAGE)" << std::endl
           << "  -w            do not print warnings" << std::endl
           << "  -m <module>   the module name of the output files (overrides $MODULE)" << std::endl
           << "  -O <folder>   write the output files into <folder> instead of the current folder" << std::endl
           << "  -I <folders>  folders searched for $IMPORT files, ':'-separated; may be repeated" << std::endl
           << std::endl
           << "  <message file>  the message definition file; standard input when omitted or '-'" << std::endl;
  }

  void AddFolders( std::vector<std::string> &folders, const std::string &list )
  {
    std::string::size_type begin = 0;

    while ( begin <= list.size( ) )
    {
      std::string::size_type const end = list.find( ':', begin );
      std::string const folder = list.substr( begin, end == std::string::npos ? std::string::npos : end - begin );

      if ( ! folder.empty( ) )
      {
        folders.push_back( folder );
      }

      if ( end == std::string::npos )
      {
        break;
      }

      begin = end + 1;
    }
  }

  void Report( const std::vector<fedem::parser::ParseError> &diagnostics, bool warnings )
  {
    for ( const auto &e : diagnostics )
    {
      if ( ! warnings && e.kind == fedem::parser::ParseError::Kind::Warning )
      {
        continue;
      }

      std::cerr << ( e.filename.empty( ) ? "mstring" : e.filename );

      if ( e.line != 0 )
      {
        std::cerr << ':' << e.line << ':' << e.col;
      }

      std::cerr << ": "
                << ( e.kind == fedem::parser::ParseError::Kind::Warning ? "warning" : "error" )
                << ": " << e.message << std::endl;
    }
  }
}

int main( int argc, char **argv )
try
{
  std::string moduleName;
  std::string outputFolder;
  bool warnings = true;
  Module module;
  ParseContext context( module );
  int c;

  opterr = 0;

  while ( ( c = getopt( argc, argv, "hvtwm:I:O:" ) ) != -1 )
  {
    switch ( c )
    {
      case 'm':
        moduleName = optarg;
        break;
      case 'O':
        outputFolder = optarg;
        break;
      case 'I':
        AddFolders( context.importFolders, optarg );
        break;
      case 'h':
        Help( std::cout );
        return EXIT_SUCCESS;
      case 'v':
        std::cout << "mstring " << MSTRING_VERSION << std::endl;
        return EXIT_SUCCESS;
      case 't':
        context.trace = true;
        break;
      case 'w':
        warnings = false;
        break;
      default:
        if ( optopt == 'm' || optopt == 'O' || optopt == 'I' )
          std::cerr << "mstring: option -" << static_cast<char>( optopt ) << " needs an argument" << std::endl;
        else
          std::cerr << "mstring: unknown option -" << static_cast<char>( optopt ) << std::endl;

        Help( std::cerr );
        return EXIT_FAILURE;
    }
  }

  if ( argc - optind > 1 )
  {
    std::cerr << "mstring: too many parameters" << std::endl;
    Help( std::cerr );
    return EXIT_FAILURE;
  }

  std::string const input = argc - optind == 1 ? argv[optind] : "-";
  MStringParser parser( context );
  bool const parsed = input == "-" ? parser.parseStream( "<stdin>", std::cin )
                                   : parser.parseFile( input );

  if ( parsed )
  {
    CheckTranslations( context );
  }

  Report( context.diagnostics, warnings );

  if ( ! parsed )
  {
    return EXIT_FAILURE;
  }

  if ( ! moduleName.empty( ) )
  {
    module.Name = moduleName;
  }

  ValidateModule( module );

  if ( ! outputFolder.empty( ) && ! std::filesystem::is_directory( outputFolder ) )
  {
    throw MException( "output folder '" + outputFolder + "' does not exist" );
  }

  std::filesystem::path const folder( outputFolder );
  std::string const banner = input == "-" ? "" : std::filesystem::path( input ).filename( ).string( );
  std::string const header = HeaderFileName( module );
  std::string const source = SourceFileName( module );

  if ( ! header.empty( ) )
  {
    std::ostringstream stream;
    WriteCppInterface( stream, module, banner );
    WriteIfChanged( ( folder / header ).string( ), stream.str( ) );
  }

  if ( ! source.empty( ) )
  {
    std::ostringstream stream;
    WriteCppImplementation( stream, module, banner );
    WriteIfChanged( ( folder / source ).string( ), stream.str( ) );
  }

  return EXIT_SUCCESS;
}
catch ( const MException &ex )
{
  std::cerr << "mstring: error: " << ex.what( ) << std::endl;
  return EXIT_FAILURE;
}
catch ( const std::exception &ex )
{
  std::cerr << "mstring: error: (std) " << ex.what( ) << std::endl;
  return EXIT_FAILURE;
}
catch ( ... )
{
  std::cerr << "mstring: error: unexpected exception" << std::endl;
  return EXIT_FAILURE;
}