<commit_before>/*-----------------------------------------------------------------------------
  NeXus - Neutron & X-ray Common Data Format

  Utility to convert a NeXus file into HDF4/HDF5/XML/...

  Author: Freddie Akeroyd

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  For further information, see <http://www.neutron.anl.gov/NeXus/>

 $Id: nxconvert.c 991 2008-03-19 19:30:03Z Freddie Akeroyd $
-----------------------------------------------------------------------------*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unistd.h>
#include <vector>

#include "napi.h"
#include "napiconfig.h"
#include "nxconvert_common.h"
#include "tclap/CmdLine.h"

using namespace TCLAP;
using std::cout;
using std::cerr;
using std::endl;
using std::runtime_error;
using std::string;
using std::stringstream;
using std::vector;

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
#if HAVE_MKSTEMP
#else
static int mkstemp(char* template)
{
    if (mktemp(template) != NULL)
    {
	return open(template, O_RDWR | O_CREAT, 0600);
    }
    else
    {
	return -1;
    }
}
#endif

#ifdef _WIN32
#define NULL_DEVICE "NUL"
#define DIR_SEPARATOR "\\"
#define TMP_DIR getenv("TEMP")
#else
#define NULL_DEVICE "/dev/null"
#define DIR_SEPARATOR "/"
#define TMP_DIR P_tmpdir
#endif /* _WIN32 */

struct Definition {
  string name;
  string version;
  string nxdl_path;
};

struct NeXus {
  string raw;
  string reduced;
};

static int url_encode(char c, FILE* f)
{
	switch(c)
	{
	    case '+':
	    case '&':
	    case '?':
	    case '$':
	    case ',':
	    case '/':
	    case ':':
	    case '=':
	    case ';':
	    case '@':
	    case '%':
	    case '#':
	        fprintf(f, "%%%02X", c);
	  	break;

	    default:
		fputc(c, f);
		break;
	}
	return 0;
}

#define NXVALIDATE_ERROR_EXIT	exit(EXIT_FAILURE)

static const string DEFAULT_NXDL_PATH(".");
static const int STR_BUFFER_SIZE = 256;

static int quiet = 0;

/**
 * Generate a temporary file based on the name of a supplied file. This will
 * generate the file to make sure that it can be created.
 */
static string tempfile(const string &in)
{
  // get the name of the file without the path
  string separator(DIR_SEPARATOR);
  string inFile(in);
  size_t found;
  found = inFile.rfind(DIR_SEPARATOR);
  if (found != string::npos) {
    inFile.erase(0, found + separator.length());
  }

  // generate the temporary filename
  char outFile[STR_BUFFER_SIZE];
  sprintf(outFile, "%s%s%s.XXXXXX", TMP_DIR, DIR_SEPARATOR, inFile.c_str());

  // create the temporary file
  int fd;
   if ( (fd = mkstemp(outFile)) == -1 )
   {
     stringstream msg;
     msg << "error making temporary file " << outFile;
     throw runtime_error(msg.str());
   }
   close(fd);

   return string(outFile);
}

static void convertDFN(Definition &definition)
{
  cout << "name:" << definition.name << endl
       << "vers:" << definition.version << endl
       << "path:" << definition.nxdl_path << endl;
}

static void convertNXS(NeXus &nexus, Definition &definition)
{
   nexus.reduced = tempfile(nexus.raw);
   if (!quiet) {
     cout << "* Converting nexus file to reduced format" << endl;
   }
   if (convert_file(NX_DEFINITION, nexus.raw.c_str(), NXACC_READ,
                    nexus.reduced.c_str(), NXACC_CREATEXML,
                    definition.name.c_str()) != NX_OK)
   {
     stringstream msg;
     msg << "Error converting file " << nexus.raw
         << " to definiton XML format";
     throw runtime_error(msg.str());
   }
}

static int validate(const string& nxsfile, Definition &definition,
		    const int keep_temps, int use_web)
{
   // set up the validation file
   convertDFN(definition);

   // set up the reduced nexus file
   NeXus nexus;
   nexus.raw = nxsfile;
   convertNXS(nexus, definition);

   // let people know what is going on
   if (!quiet) {
     cout << "* Validating " << nexus.raw << " using definition "
          << (!definition.name.empty() ? definition.name : "<default>")
	  << " for all NXentry" << endl;
   }

   char outFile2[STR_BUFFER_SIZE], *strPtr;
   string command;
   const char* cStrPtr;
   int ret, opt, c, i, fd;
   FILE *fIn, *fOut2;

   string extra_xmllint_args;
   extra_xmllint_args.append(" --nowarning");

   // check without the web
   if (use_web == 0)
   {
       stringstream command_buff;
       command_buff << "xmllint " << extra_xmllint_args << " --noout --path \"" << definition.nxdl_path << "\" --schema \"" << NEXUS_SCHEMA_BASE << ".xsd\" \"" << nexus.reduced << "\"" << (quiet ? "> " NULL_DEVICE " 2>&1" : "");

	   command = command_buff.str();
       if (!quiet) {
         cout << "* Validating using locally installed \"xmllint\" program"
              << endl;
         cout << command << endl;
       }
       ret = system(command.c_str());
       if (ret != -1 && WIFEXITED(ret))
       {
	    if (!keep_temps)
	    {
		remove(nexus.reduced.c_str());
	    }
	    if (WEXITSTATUS(ret) != 0)
	    {
              cout << "* Validation with \"xmllint\" of " << nexus.raw
                   << " failed" << endl;
              cout << "* If the program was unable to find all the required "
                   << "schema from the web, check your \"http_proxy\" "
                   << "environment variable" << endl;
              NXVALIDATE_ERROR_EXIT;
	    }
	    else
	    {
	        if (!quiet) {
                  cout << "* Validation with \"xmllint\" of " << nexus.raw
                       << " OK" << endl;
                }
	        return 0;
	    }
       }
       cout << "* Unable to find \"xmllint\" to validate file - will try "
            << "web validation" << endl;
       cout << "* \"xmllint\" is installed as part of libxml2 from "
            << "xmlsoft.org" << endl;
       NXVALIDATE_ERROR_EXIT;
   } // this section returns whether or not it works

   // set up post request for post request
   sprintf(outFile2, "%s%s%s.XXXXXX", TMP_DIR, DIR_SEPARATOR, nexus.raw.c_str());
   if ( (fd = mkstemp(outFile2)) == -1 )
   {
     cerr << "Failed to make the temporary file " << outFile2 << endl;
     NXVALIDATE_ERROR_EXIT;
   }
   close(fd);
   fOut2 = fopen(outFile2, "wt");
   fIn = fopen(nexus.reduced.c_str(), "rt");
   fprintf(fOut2, "file_name=");
   for(cStrPtr = nexus.reduced.c_str(); *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&definition_name=");
   for(cStrPtr = definition.name.c_str(); cStrPtr != NULL && *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&definition_version=");
   for(cStrPtr = definition.version.c_str(); *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&file_data=");
   while( (c = fgetc(fIn)) != EOF )
   {
	url_encode(c, fOut2);
   }
   fclose(fIn);
   fclose(fOut2);

   // post the validation information to the web
   stringstream command_buff;
   command_buff << "wget --quiet -O " << (quiet ? NULL_DEVICE : "-")
                << " --post-file=\"" << outFile2
                << "\" http://definition.nexusformat.org/dovalidate/run";
   command = command_buff.str();

   if (!quiet) {
     cout << "* Validating via http://definition.nexusformat.org using "
          << "\"wget\"" << endl;
     cout << command << endl;
   }
   ret = system(command.c_str());
   if (!keep_temps)
   {
	remove(nexus.reduced.c_str());
	remove(outFile2);
   }
   if (ret == -1)
   {
        cout << "* Unable to find \"wget\" to validate the file " << nexus.raw
          << " over the web" << endl;
	NXVALIDATE_ERROR_EXIT;
   }

   // cleanup and return code
   if (WIFEXITED(ret) && (WEXITSTATUS(ret) != 0))
   {
        cerr << "* Validation via http://definition.nexusformat.org/ of "
          << nexus.raw << " failed" << endl;
        cerr << "* If \"wget\" was unable to load the schema files from the "
          << "web, check your \"http_proxy\" environment variable" << endl;
	NXVALIDATE_ERROR_EXIT;
   }
   else if (!quiet)
   {
        cout << "* Validation via http://definition.nexusformat.org/ of "
          << nexus.raw << " OK" << endl;
   }
   return 0;
}

int main(int argc, char *argv[])
{
   char *strPtr;
   int use_web = 0;
   int keep_temps = 0;
   int ignore_definition = 0;

   // set up the command line arguments
   CmdLine cmd("Validate a NeXus file", ' ', "1.1.0");

   std::ostringstream temp;
   temp << "Use specified definiton for NXentry (default: read from <definition> field in NXentry )";
   ValueArg<string> definition_name_arg("d", "def", temp.str(), false,
                                        "", "definition", cmd);
   temp.str("");
   temp << "specify schema version (default: " << NEXUS_SCHEMA_VERSION << ")";
   ValueArg<string> definition_version_arg("", "defver", temp.str(), false,
                                          NEXUS_SCHEMA_VERSION, "version", cmd);

   SwitchArg quiet_arg("q", "quiet", "Turn on quiet mode (only report errors)",
                       false, cmd);
   SwitchArg keep_arg("k", "keep", "Keep temporary files",
                       false, cmd);
   SwitchArg nodef_arg("n", "nodef", "Ignore <definition> field in NXentry",
                       false, cmd);

   temp.str("");
   temp << "Specify path to additional schema files (default: "
	<< DEFAULT_NXDL_PATH << ")";
   ValueArg<string> schema_path_arg("p", "path", temp.str(), false,
				    DEFAULT_NXDL_PATH, "path", cmd);

   temp.str("");
   temp << "Send file to NeXus web site (using wget) for validation "
        << "(default: try to run \"xmllint\" program locally";
   SwitchArg send_arg("w", "web", temp.str(), false, cmd);

   string sinfile;
   UnlabeledMultiArg<string> infiles_arg("filename",
                                         "Name of a file to be viewed",
                                         "filename",cmd);

   // parse the command line and turn it into variables
   cmd.parse(argc, argv);
   if (quiet_arg.getValue()) {
     quiet = 1;
   }
   if (send_arg.getValue()) {
     use_web = 1;
   }
   if (keep_arg.getValue()) {
     keep_temps = 1;
   }
   if (nodef_arg.getValue()) {
     ignore_definition = 1;
   }
   Definition definition;
   if (ignore_definition) {
     definition.name = "NXentry";
   }
   definition.version = definition_version_arg.getValue();
   definition.nxdl_path = schema_path_arg.getValue();
   vector<string> infiles = infiles_arg.getValue();
   if (infiles.empty()) {
     cerr << "Must supply at least one data file" << endl;
     NXVALIDATE_ERROR_EXIT;
   }

   // do the work
   int result = 0;
   for (size_t i = 0; i < infiles.size(); i++) {
     result += validate(infiles[i], definition, keep_temps, use_web);
   }
   return result;
}
<commit_msg>Move from ints to bools. Refs #179.<commit_after>/*-----------------------------------------------------------------------------
  NeXus - Neutron & X-ray Common Data Format

  Utility to convert a NeXus file into HDF4/HDF5/XML/...

  Author: Freddie Akeroyd

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  For further information, see <http://www.neutron.anl.gov/NeXus/>

 $Id: nxconvert.c 991 2008-03-19 19:30:03Z Freddie Akeroyd $
-----------------------------------------------------------------------------*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unistd.h>
#include <vector>

#include "napi.h"
#include "napiconfig.h"
#include "nxconvert_common.h"
#include "tclap/CmdLine.h"

using namespace TCLAP;
using std::cout;
using std::cerr;
using std::endl;
using std::runtime_error;
using std::string;
using std::stringstream;
using std::vector;

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
#if HAVE_MKSTEMP
#else
static int mkstemp(char* template)
{
    if (mktemp(template) != NULL)
    {
	return open(template, O_RDWR | O_CREAT, 0600);
    }
    else
    {
	return -1;
    }
}
#endif

#ifdef _WIN32
#define NULL_DEVICE "NUL"
#define DIR_SEPARATOR "\\"
#define TMP_DIR getenv("TEMP")
#else
#define NULL_DEVICE "/dev/null"
#define DIR_SEPARATOR "/"
#define TMP_DIR P_tmpdir
#endif /* _WIN32 */

struct Definition {
  string name;
  string version;
  string nxdl_path;
};

struct NeXus {
  string raw;
  string reduced;
};

static int url_encode(char c, FILE* f)
{
	switch(c)
	{
	    case '+':
	    case '&':
	    case '?':
	    case '$':
	    case ',':
	    case '/':
	    case ':':
	    case '=':
	    case ';':
	    case '@':
	    case '%':
	    case '#':
	        fprintf(f, "%%%02X", c);
	  	break;

	    default:
		fputc(c, f);
		break;
	}
	return 0;
}

#define NXVALIDATE_ERROR_EXIT	exit(EXIT_FAILURE)

static const string DEFAULT_NXDL_PATH(".");
static const int STR_BUFFER_SIZE = 256;

static int quiet = 0;

/**
 * Generate a temporary file based on the name of a supplied file. This will
 * generate the file to make sure that it can be created.
 */
static string tempfile(const string &in)
{
  // get the name of the file without the path
  string separator(DIR_SEPARATOR);
  string inFile(in);
  size_t found;
  found = inFile.rfind(DIR_SEPARATOR);
  if (found != string::npos) {
    inFile.erase(0, found + separator.length());
  }

  // generate the temporary filename
  char outFile[STR_BUFFER_SIZE];
  sprintf(outFile, "%s%s%s.XXXXXX", TMP_DIR, DIR_SEPARATOR, inFile.c_str());

  // create the temporary file
  int fd;
   if ( (fd = mkstemp(outFile)) == -1 )
   {
     stringstream msg;
     msg << "error making temporary file " << outFile;
     throw runtime_error(msg.str());
   }
   close(fd);

   return string(outFile);
}

static void convertDFN(Definition &definition)
{
  cout << "name:" << definition.name << endl
       << "vers:" << definition.version << endl
       << "path:" << definition.nxdl_path << endl;
}

static void convertNXS(NeXus &nexus, Definition &definition)
{
   nexus.reduced = tempfile(nexus.raw);
   if (!quiet) {
     cout << "* Converting nexus file to reduced format" << endl;
   }
   if (convert_file(NX_DEFINITION, nexus.raw.c_str(), NXACC_READ,
                    nexus.reduced.c_str(), NXACC_CREATEXML,
                    definition.name.c_str()) != NX_OK)
   {
     stringstream msg;
     msg << "Error converting file " << nexus.raw
         << " to definiton XML format";
     throw runtime_error(msg.str());
   }
}

static int validate(const string& nxsfile, Definition &definition,
		    const bool keep_temps, const bool use_web)
{
   // set up the validation file
   convertDFN(definition);

   // set up the reduced nexus file
   NeXus nexus;
   nexus.raw = nxsfile;
   convertNXS(nexus, definition);

   // let people know what is going on
   if (!quiet) {
     cout << "* Validating " << nexus.raw << " using definition "
          << (!definition.name.empty() ? definition.name : "<default>")
	  << " for all NXentry" << endl;
   }

   char outFile2[STR_BUFFER_SIZE], *strPtr;
   string command;
   const char* cStrPtr;
   int ret, opt, c, i, fd;
   FILE *fIn, *fOut2;

   string extra_xmllint_args;
   extra_xmllint_args.append(" --nowarning");

   // check without the web
   if (!use_web)
   {
       stringstream command_buff;
       command_buff << "xmllint " << extra_xmllint_args << " --noout --path \"" << definition.nxdl_path << "\" --schema \"" << NEXUS_SCHEMA_BASE << ".xsd\" \"" << nexus.reduced << "\"" << (quiet ? "> " NULL_DEVICE " 2>&1" : "");

	   command = command_buff.str();
       if (!quiet) {
         cout << "* Validating using locally installed \"xmllint\" program"
              << endl;
         cout << command << endl;
       }
       ret = system(command.c_str());
       if (ret != -1 && WIFEXITED(ret))
       {
	    if (!keep_temps)
	    {
		remove(nexus.reduced.c_str());
	    }
	    if (WEXITSTATUS(ret) != 0)
	    {
              cout << "* Validation with \"xmllint\" of " << nexus.raw
                   << " failed" << endl;
              cout << "* If the program was unable to find all the required "
                   << "schema from the web, check your \"http_proxy\" "
                   << "environment variable" << endl;
              NXVALIDATE_ERROR_EXIT;
	    }
	    else
	    {
	        if (!quiet) {
                  cout << "* Validation with \"xmllint\" of " << nexus.raw
                       << " OK" << endl;
                }
	        return 0;
	    }
       }
       cout << "* Unable to find \"xmllint\" to validate file - will try "
            << "web validation" << endl;
       cout << "* \"xmllint\" is installed as part of libxml2 from "
            << "xmlsoft.org" << endl;
       NXVALIDATE_ERROR_EXIT;
   } // this section returns whether or not it works

   // set up post request for post request
   sprintf(outFile2, "%s%s%s.XXXXXX", TMP_DIR, DIR_SEPARATOR, nexus.raw.c_str());
   if ( (fd = mkstemp(outFile2)) == -1 )
   {
     cerr << "Failed to make the temporary file " << outFile2 << endl;
     NXVALIDATE_ERROR_EXIT;
   }
   close(fd);
   fOut2 = fopen(outFile2, "wt");
   fIn = fopen(nexus.reduced.c_str(), "rt");
   fprintf(fOut2, "file_name=");
   for(cStrPtr = nexus.reduced.c_str(); *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&definition_name=");
   for(cStrPtr = definition.name.c_str(); cStrPtr != NULL && *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&definition_version=");
   for(cStrPtr = definition.version.c_str(); *cStrPtr != '\0'; ++cStrPtr)
   {
	url_encode(*cStrPtr, fOut2);
   }
   fprintf(fOut2, "&file_data=");
   while( (c = fgetc(fIn)) != EOF )
   {
	url_encode(c, fOut2);
   }
   fclose(fIn);
   fclose(fOut2);

   // post the validation information to the web
   stringstream command_buff;
   command_buff << "wget --quiet -O " << (quiet ? NULL_DEVICE : "-")
                << " --post-file=\"" << outFile2
                << "\" http://definition.nexusformat.org/dovalidate/run";
   command = command_buff.str();

   if (!quiet) {
     cout << "* Validating via http://definition.nexusformat.org using "
          << "\"wget\"" << endl;
     cout << command << endl;
   }
   ret = system(command.c_str());
   if (!keep_temps)
   {
	remove(nexus.reduced.c_str());
	remove(outFile2);
   }
   if (ret == -1)
   {
        cout << "* Unable to find \"wget\" to validate the file " << nexus.raw
          << " over the web" << endl;
	NXVALIDATE_ERROR_EXIT;
   }

   // cleanup and return code
   if (WIFEXITED(ret) && (WEXITSTATUS(ret) != 0))
   {
        cerr << "* Validation via http://definition.nexusformat.org/ of "
          << nexus.raw << " failed" << endl;
        cerr << "* If \"wget\" was unable to load the schema files from the "
          << "web, check your \"http_proxy\" environment variable" << endl;
	NXVALIDATE_ERROR_EXIT;
   }
   else if (!quiet)
   {
        cout << "* Validation via http://definition.nexusformat.org/ of "
          << nexus.raw << " OK" << endl;
   }
   return 0;
}

int main(int argc, char *argv[])
{
   bool use_web;
   bool keep_temps;
   bool ignore_definition;

   // set up the command line arguments
   CmdLine cmd("Validate a NeXus file", ' ', "1.1.0");

   std::ostringstream temp;
   temp << "Use specified definiton for NXentry (default: read from <definition> field in NXentry )";
   ValueArg<string> definition_name_arg("d", "def", temp.str(), false,
                                        "", "definition", cmd);
   temp.str("");
   temp << "specify schema version (default: " << NEXUS_SCHEMA_VERSION << ")";
   ValueArg<string> definition_version_arg("", "defver", temp.str(), false,
                                          NEXUS_SCHEMA_VERSION, "version", cmd);

   SwitchArg quiet_arg("q", "quiet", "Turn on quiet mode (only report errors)",
                       false, cmd);
   SwitchArg keep_arg("k", "keep", "Keep temporary files",
                       false, cmd);
   SwitchArg nodef_arg("n", "nodef", "Ignore <definition> field in NXentry",
                       false, cmd);

   temp.str("");
   temp << "Specify path to additional schema files (default: "
	<< DEFAULT_NXDL_PATH << ")";
   ValueArg<string> schema_path_arg("p", "path", temp.str(), false,
				    DEFAULT_NXDL_PATH, "path", cmd);

   temp.str("");
   temp << "Send file to NeXus web site (using wget) for validation "
        << "(default: try to run \"xmllint\" program locally";
   SwitchArg send_arg("w", "web", temp.str(), false, cmd);

   string sinfile;
   UnlabeledMultiArg<string> infiles_arg("filename",
                                         "Name of a file to be viewed",
                                         "filename",cmd);

   // parse the command line and turn it into variables
   cmd.parse(argc, argv);
   if (quiet_arg.getValue()) {
     quiet = 1;
   }
   use_web = send_arg.getValue();
   keep_temps = keep_arg.getValue();
   ignore_definition = nodef_arg.getValue();
   Definition definition;
   if (ignore_definition) {
     definition.name = "NXentry";
   }
   definition.version = definition_version_arg.getValue();
   definition.nxdl_path = schema_path_arg.getValue();
   vector<string> infiles = infiles_arg.getValue();
   if (infiles.empty()) {
     cerr << "Must supply at least one data file" << endl;
     NXVALIDATE_ERROR_EXIT;
   }

   // do the work
   int result = 0;
   for (size_t i = 0; i < infiles.size(); i++) {
     result += validate(infiles[i], definition, keep_temps, use_web);
   }
   return result;
}
<|endoftext|>


<commit_before>// Copyright 2017 Global Phasing Ltd.
//
// Read mmcif (PDBx/mmCIF) file into a Structure from model.hpp.

#ifndef GEMMI_MMCIF_HPP_
#define GEMMI_MMCIF_HPP_

#include <array>
#include <string>
#include <unordered_map>
#include "cifdoc.hpp"
#include "numb.hpp"
#include "model.hpp"

namespace gemmi {

namespace impl {

inline std::unordered_map<std::string, std::array<float,6>>
get_anisotropic_u(cif::Block& block) {
  cif::Table aniso_tab = block.find("_atom_site_anisotrop.",
                                    {"id", "U[1][1]", "U[2][2]", "U[3][3]",
                                     "U[1][2]", "U[1][3]", "U[2][3]"});
  std::unordered_map<std::string, std::array<float,6>> aniso_map;
  for (auto ani : aniso_tab)
    aniso_map[ani[0]] = {{(float) cif::as_number(ani[1]),
                          (float) cif::as_number(ani[2]),
                          (float) cif::as_number(ani[3]),
                          (float) cif::as_number(ani[4]),
                          (float) cif::as_number(ani[5]),
                          (float) cif::as_number(ani[6])}};
  return aniso_map;
}

inline
std::vector<std::string> transform_tags(std::string mstr, std::string vstr) {
  return {mstr + "[1][1]", mstr + "[2][1]", mstr + "[3][1]",
          mstr + "[1][2]", mstr + "[2][2]", mstr + "[3][2]",
          mstr + "[1][3]", mstr + "[2][3]", mstr + "[3][3]",
          vstr + "[1]",    vstr + "[2]",    vstr + "[3]"};
}

inline Mat4x4 get_transform_matrix(const cif::Table::Row& r) {
  auto num = [&r](int n) { return cif::as_number(r[n]); };
  return {{num(0), num(1),  num(2),  0},
          {num(3), num(4),  num(5),  0},
          {num(6), num(7),  num(8),  0},
          {num(9), num(10), num(11), 1}};
}

inline Mat4x4 Matrix33_to_Mat4x4(const Matrix33& m) {
  return {{m.a11, m.a21, m.a31, 0},
          {m.a12, m.a22, m.a32, 0},
          {m.a13, m.a23, m.a33, 0},
          {    0,     0,     0, 1}};
}

inline Structure structure_from_cif_block(cif::Block& block) {
  using cif::as_number;
  using cif::as_string;
  Structure st;
  st.name = block.name;

  // unit cell and symmetry
  cif::Table cell = block.find("_cell.",
                               {"length_a", "length_b", "length_c",
                                "angle_alpha", "angle_beta", "angle_gamma"});
  if (cell.ok()) {
    auto c = cell.one();
    st.cell.set(as_number(c[0]), as_number(c[1]), as_number(c[2]),
                as_number(c[3]), as_number(c[4]), as_number(c[5]));
  }
  st.sg_hm = as_string(block.find_value("_symmetry.space_group_name_H-M"));

  auto add_info = [&](std::string tag) {
    cif::Column col = block.find_values(tag);
    bool first = true;
    for (const std::string& v : col)
      if (!cif::is_null(v)) {
        if (first)
          st.info[tag] = as_string(v);
        else
          st.info[tag] += "; " + as_string(v);
        first = false;
      }
  };
  add_info("_entry.id");
  add_info("_cell.Z_PDB");
  add_info("_exptl.method");
  add_info("_struct.title");
  // in pdbx/mmcif v5 date_original was replaced with a much longer tag
  std::string old_date_tag = "_database_PDB_rev.date_original";
  std::string new_date_tag
                      = "_pdbx_database_status.recvd_initial_deposition_date";
  add_info(old_date_tag);
  add_info(new_date_tag);
  if (st.info.count(old_date_tag) == 1 && st.info.count(new_date_tag) == 0)
    st.info[new_date_tag] = st.info[old_date_tag];
  add_info("_struct_keywords.pdbx_keywords");
  add_info("_struct_keywords.text");

  std::vector<std::string> ncs_oper_tags = transform_tags("matrix", "vector");
  ncs_oper_tags.push_back("id");  // 12
  ncs_oper_tags.push_back("?code");  // 13
  cif::Table ncs_oper = block.find("_struct_ncs_oper.", ncs_oper_tags);
  for (auto op : ncs_oper) {
    bool given = op.has(13) && op.str(13) == "given";
    Mat4x4 mat = get_transform_matrix(op);
    if (mat != Mat4x4(linalg::identity))
      st.ncs.push_back({op.str(12), given, mat});
  }

  // PDBx/mmcif spec defines both _database_PDB_matrix.scale* and
  // _atom_sites.fract_transf_* as equivalent of pdb SCALE, but the former
  // is not used, so we ignore it.
  cif::Table fract_tv = block.find("_atom_sites.fract_transf_",
                                   transform_tags("matrix", "vector"));
  if (fract_tv.length() > 0) {
    Mat4x4 fract = get_transform_matrix(fract_tv[0]);
    st.cell.set_matrices_from_fract(fract);
  }

  // We store origx just for completeness. It may never be useful
  // for anything but writing it back to a file.
  cif::Table origx_tv = block.find("_database_PDB_matrix.",
                                   transform_tags("origx", "origx_vector"));
  if (origx_tv.length() > 0)
    st.origx = get_transform_matrix(origx_tv[0]);

  auto aniso_map = get_anisotropic_u(block);

  // atom list
  enum { kId=0, kSymbol, kAtomId, kAltId, kCompId, kAsymId, kSeqId, kInsCode,
         kX, kY, kZ, kOcc, kBiso, kCharge, kAuthSeqId, kAuthAsymId, kModelNum };
  cif::Table atom_table = block.find("_atom_site.",
                                     {"id",
                                      "type_symbol",
                                      "label_atom_id",
                                      "label_alt_id",
                                      "label_comp_id",
                                      "label_asym_id",
                                      "label_seq_id",
                                      "pdbx_PDB_ins_code",
                                      "Cartn_x",
                                      "Cartn_y",
                                      "Cartn_z",
                                      "occupancy",
                                      "B_iso_or_equiv",
                                      "pdbx_formal_charge",
                                      "auth_seq_id",
                                      "auth_asym_id",
                                      "pdbx_PDB_model_num"});
  Model *model = nullptr;
  Chain *chain = nullptr;
  Residue *resi = nullptr;
  for (auto row : atom_table) {
    if (!model || row[kModelNum] != model->name) {
      model = st.find_or_add_model(row[kModelNum]);
      chain = nullptr;
    }
    if (!chain || row[kAsymId] != chain->name) {
      chain = model->find_or_add_chain(row.str(kAsymId));
      chain->auth_name = row.str(kAuthAsymId);
      resi = nullptr;
    }
    ResidueId rid(cif::as_int(row[kSeqId], Residue::UnknownId),
                  ResidueId::SNIC{cif::as_int(row[kAuthSeqId],
                                              Residue::UnknownId),
                                  as_string(row[kInsCode])[0]},
                  as_string(row[kCompId]));
    if (!resi || !resi->matches(rid)) {
      // the insertion code happens to be always a single letter
      assert(row[kInsCode].size() == 1);
      resi = chain->find_or_add_residue(rid);
    } else {
      assert(resi->snic.seq_num == rid.snic.seq_num);
      assert(resi->snic.ins_code == rid.snic.ins_code);
    }
    Atom atom;
    atom.name = as_string(row[kAtomId]);
    atom.group = 0;
    atom.altloc = as_string(row[kAltId])[0];
    atom.charge = cif::is_null(row[kCharge]) ? 0 : cif::as_int(row[kCharge]);
    atom.element = gemmi::Element(as_string(row[kSymbol]));
    atom.pos.x = cif::as_number(row[kX]);
    atom.pos.y = cif::as_number(row[kY]);
    atom.pos.z = cif::as_number(row[kZ]);
    atom.occ = (float) cif::as_number(row[kOcc], 1.0);
    atom.b_iso = (float) cif::as_number(row[kBiso], 50.0);

    if (!aniso_map.empty()) {
      auto ani = aniso_map.find(row[kId]);
      if (ani != aniso_map.end()) {
        atom.u11 = ani->second[0];
        atom.u22 = ani->second[1];
        atom.u33 = ani->second[2];
        atom.u12 = ani->second[3];
        atom.u13 = ani->second[4];
        atom.u23 = ani->second[5];
      }
    }
    resi->atoms.emplace_back(atom);
  }

  cif::Table polymer_types = block.find("_entity_poly.", {"entity_id", "type"});
  for (auto row : block.find("_entity.", {"id", "type"})) {
    std::string id = row.str(0);
    EntityType etype = entity_type_from_string(row.str(1));
    PolymerType ptype = PolymerType::NA;
    try {
      ptype = polymer_type_from_string(polymer_types.find_row(id).str(1));
    } catch (std::runtime_error&) {}
    st.entities.emplace_back(new Entity{id, etype, ptype, {}});
  }

  for (auto row : block.find("_entity_poly_seq.",
                             {"entity_id", "num", "mon_id"})) {
    Entity *ent = st.find_or_add_entity(row.str(0));
    ent->sequence.push_back({cif::as_int(row[1], -1), row.str(2)});
  }

  auto chain_to_entity = block.find("_struct_asym.", {"id", "entity_id"});
  for (Model& mod : st.models)
    for (Chain& ch : mod.chains) {
      try {
        std::string ent_id = chain_to_entity.find_row(ch.name).str(1);
        ch.entity = st.find_or_add_entity(ent_id);
      } catch (std::runtime_error&) {  // maybe _struct_asym is missing
        ch.entity = nullptr;
      }
    }
  st.finish();

  for (auto row : block.find("_struct_mon_prot_cis.",
                             {"pdbx_PDB_model_num", "label_asym_id",
                              "label_seq_id", "label_comp_id"})) {
    if (row.has2(0) && row.has2(1) && row.has2(2) && row.has2(3))
      if (Model* model = st.find_model(row[0])) {
        int seq = cif::as_int(row[2]);
        if (Residue* res = model->find_residue_with_label(row[1], seq, row[3]))
          res->is_cis = true;
      }
  }

  for (auto row : block.find("_struct_conn.", {"id", "conn_type_id",
        "ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_label_comp_id",
        "ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_label_comp_id"})) {
    Connection c;
    c.id = row.str(0);
    std::string type = row.str(1);
    for (int i = 0; i != Connection::None; ++i)
      if (get_mmcif_connection_type_id(Connection::Type(i)) == type) {
        c.type = Connection::Type(i);
        break;
      }
    for (Model& model : st.models) {
      c.res1 = model.find_residue_with_label(row.str(2),
                                             cif::as_int(row[3]), row.str(4));
      c.res2 = model.find_residue_with_label(row.str(5),
                                             cif::as_int(row[6]), row.str(7));
      if (c.res1 && c.res2) {
        c.res1->conn.push_back(c.id);
        c.res2->conn.push_back(c.id);
        model.connections.push_back(c);
      }
      // TODO atom name
    }
  }

  return st;
}

} // namespace impl

inline Structure read_atoms(cif::Document doc) {
  return impl::structure_from_cif_block(doc.sole_block());
}

} // namespace gemmi
#endif
// vim:sw=2:ts=2:et
<commit_msg>bug fix: _struct_conn.ptnr1_label_seq_id can be null<commit_after>// Copyright 2017 Global Phasing Ltd.
//
// Read mmcif (PDBx/mmCIF) file into a Structure from model.hpp.

#ifndef GEMMI_MMCIF_HPP_
#define GEMMI_MMCIF_HPP_

#include <array>
#include <string>
#include <unordered_map>
#include "cifdoc.hpp"
#include "numb.hpp"
#include "model.hpp"

namespace gemmi {

namespace impl {

inline std::unordered_map<std::string, std::array<float,6>>
get_anisotropic_u(cif::Block& block) {
  cif::Table aniso_tab = block.find("_atom_site_anisotrop.",
                                    {"id", "U[1][1]", "U[2][2]", "U[3][3]",
                                     "U[1][2]", "U[1][3]", "U[2][3]"});
  std::unordered_map<std::string, std::array<float,6>> aniso_map;
  for (auto ani : aniso_tab)
    aniso_map[ani[0]] = {{(float) cif::as_number(ani[1]),
                          (float) cif::as_number(ani[2]),
                          (float) cif::as_number(ani[3]),
                          (float) cif::as_number(ani[4]),
                          (float) cif::as_number(ani[5]),
                          (float) cif::as_number(ani[6])}};
  return aniso_map;
}

inline
std::vector<std::string> transform_tags(std::string mstr, std::string vstr) {
  return {mstr + "[1][1]", mstr + "[2][1]", mstr + "[3][1]",
          mstr + "[1][2]", mstr + "[2][2]", mstr + "[3][2]",
          mstr + "[1][3]", mstr + "[2][3]", mstr + "[3][3]",
          vstr + "[1]",    vstr + "[2]",    vstr + "[3]"};
}

inline Mat4x4 get_transform_matrix(const cif::Table::Row& r) {
  auto num = [&r](int n) { return cif::as_number(r[n]); };
  return {{num(0), num(1),  num(2),  0},
          {num(3), num(4),  num(5),  0},
          {num(6), num(7),  num(8),  0},
          {num(9), num(10), num(11), 1}};
}

inline Mat4x4 Matrix33_to_Mat4x4(const Matrix33& m) {
  return {{m.a11, m.a21, m.a31, 0},
          {m.a12, m.a22, m.a32, 0},
          {m.a13, m.a23, m.a33, 0},
          {    0,     0,     0, 1}};
}

inline Structure structure_from_cif_block(cif::Block& block) {
  using cif::as_number;
  using cif::as_string;
  Structure st;
  st.name = block.name;

  // unit cell and symmetry
  cif::Table cell = block.find("_cell.",
                               {"length_a", "length_b", "length_c",
                                "angle_alpha", "angle_beta", "angle_gamma"});
  if (cell.ok()) {
    auto c = cell.one();
    st.cell.set(as_number(c[0]), as_number(c[1]), as_number(c[2]),
                as_number(c[3]), as_number(c[4]), as_number(c[5]));
  }
  st.sg_hm = as_string(block.find_value("_symmetry.space_group_name_H-M"));

  auto add_info = [&](std::string tag) {
    cif::Column col = block.find_values(tag);
    bool first = true;
    for (const std::string& v : col)
      if (!cif::is_null(v)) {
        if (first)
          st.info[tag] = as_string(v);
        else
          st.info[tag] += "; " + as_string(v);
        first = false;
      }
  };
  add_info("_entry.id");
  add_info("_cell.Z_PDB");
  add_info("_exptl.method");
  add_info("_struct.title");
  // in pdbx/mmcif v5 date_original was replaced with a much longer tag
  std::string old_date_tag = "_database_PDB_rev.date_original";
  std::string new_date_tag
                      = "_pdbx_database_status.recvd_initial_deposition_date";
  add_info(old_date_tag);
  add_info(new_date_tag);
  if (st.info.count(old_date_tag) == 1 && st.info.count(new_date_tag) == 0)
    st.info[new_date_tag] = st.info[old_date_tag];
  add_info("_struct_keywords.pdbx_keywords");
  add_info("_struct_keywords.text");

  std::vector<std::string> ncs_oper_tags = transform_tags("matrix", "vector");
  ncs_oper_tags.push_back("id");  // 12
  ncs_oper_tags.push_back("?code");  // 13
  cif::Table ncs_oper = block.find("_struct_ncs_oper.", ncs_oper_tags);
  for (auto op : ncs_oper) {
    bool given = op.has(13) && op.str(13) == "given";
    Mat4x4 mat = get_transform_matrix(op);
    if (mat != Mat4x4(linalg::identity))
      st.ncs.push_back({op.str(12), given, mat});
  }

  // PDBx/mmcif spec defines both _database_PDB_matrix.scale* and
  // _atom_sites.fract_transf_* as equivalent of pdb SCALE, but the former
  // is not used, so we ignore it.
  cif::Table fract_tv = block.find("_atom_sites.fract_transf_",
                                   transform_tags("matrix", "vector"));
  if (fract_tv.length() > 0) {
    Mat4x4 fract = get_transform_matrix(fract_tv[0]);
    st.cell.set_matrices_from_fract(fract);
  }

  // We store origx just for completeness. It may never be useful
  // for anything but writing it back to a file.
  cif::Table origx_tv = block.find("_database_PDB_matrix.",
                                   transform_tags("origx", "origx_vector"));
  if (origx_tv.length() > 0)
    st.origx = get_transform_matrix(origx_tv[0]);

  auto aniso_map = get_anisotropic_u(block);

  // atom list
  enum { kId=0, kSymbol, kAtomId, kAltId, kCompId, kAsymId, kSeqId, kInsCode,
         kX, kY, kZ, kOcc, kBiso, kCharge, kAuthSeqId, kAuthAsymId, kModelNum };
  cif::Table atom_table = block.find("_atom_site.",
                                     {"id",
                                      "type_symbol",
                                      "label_atom_id",
                                      "label_alt_id",
                                      "label_comp_id",
                                      "label_asym_id",
                                      "label_seq_id",
                                      "pdbx_PDB_ins_code",
                                      "Cartn_x",
                                      "Cartn_y",
                                      "Cartn_z",
                                      "occupancy",
                                      "B_iso_or_equiv",
                                      "pdbx_formal_charge",
                                      "auth_seq_id",
                                      "auth_asym_id",
                                      "pdbx_PDB_model_num"});
  Model *model = nullptr;
  Chain *chain = nullptr;
  Residue *resi = nullptr;
  for (auto row : atom_table) {
    if (!model || row[kModelNum] != model->name) {
      model = st.find_or_add_model(row[kModelNum]);
      chain = nullptr;
    }
    if (!chain || row[kAsymId] != chain->name) {
      chain = model->find_or_add_chain(row.str(kAsymId));
      chain->auth_name = row.str(kAuthAsymId);
      resi = nullptr;
    }
    ResidueId rid(cif::as_int(row[kSeqId], Residue::UnknownId),
                  ResidueId::SNIC{cif::as_int(row[kAuthSeqId],
                                              Residue::UnknownId),
                                  as_string(row[kInsCode])[0]},
                  as_string(row[kCompId]));
    if (!resi || !resi->matches(rid)) {
      // the insertion code happens to be always a single letter
      assert(row[kInsCode].size() == 1);
      resi = chain->find_or_add_residue(rid);
    } else {
      assert(resi->snic.seq_num == rid.snic.seq_num);
      assert(resi->snic.ins_code == rid.snic.ins_code);
    }
    Atom atom;
    atom.name = as_string(row[kAtomId]);
    atom.group = 0;
    atom.altloc = as_string(row[kAltId])[0];
    atom.charge = cif::is_null(row[kCharge]) ? 0 : cif::as_int(row[kCharge]);
    atom.element = gemmi::Element(as_string(row[kSymbol]));
    atom.pos.x = cif::as_number(row[kX]);
    atom.pos.y = cif::as_number(row[kY]);
    atom.pos.z = cif::as_number(row[kZ]);
    atom.occ = (float) cif::as_number(row[kOcc], 1.0);
    atom.b_iso = (float) cif::as_number(row[kBiso], 50.0);

    if (!aniso_map.empty()) {
      auto ani = aniso_map.find(row[kId]);
      if (ani != aniso_map.end()) {
        atom.u11 = ani->second[0];
        atom.u22 = ani->second[1];
        atom.u33 = ani->second[2];
        atom.u12 = ani->second[3];
        atom.u13 = ani->second[4];
        atom.u23 = ani->second[5];
      }
    }
    resi->atoms.emplace_back(atom);
  }

  cif::Table polymer_types = block.find("_entity_poly.", {"entity_id", "type"});
  for (auto row : block.find("_entity.", {"id", "type"})) {
    std::string id = row.str(0);
    EntityType etype = entity_type_from_string(row.str(1));
    PolymerType ptype = PolymerType::NA;
    try {
      ptype = polymer_type_from_string(polymer_types.find_row(id).str(1));
    } catch (std::runtime_error&) {}
    st.entities.emplace_back(new Entity{id, etype, ptype, {}});
  }

  for (auto row : block.find("_entity_poly_seq.",
                             {"entity_id", "num", "mon_id"})) {
    Entity *ent = st.find_or_add_entity(row.str(0));
    ent->sequence.push_back({cif::as_int(row[1], -1), row.str(2)});
  }

  auto chain_to_entity = block.find("_struct_asym.", {"id", "entity_id"});
  for (Model& mod : st.models)
    for (Chain& ch : mod.chains) {
      try {
        std::string ent_id = chain_to_entity.find_row(ch.name).str(1);
        ch.entity = st.find_or_add_entity(ent_id);
      } catch (std::runtime_error&) {  // maybe _struct_asym is missing
        ch.entity = nullptr;
      }
    }
  st.finish();

  for (auto row : block.find("_struct_mon_prot_cis.",
                             {"pdbx_PDB_model_num", "label_asym_id",
                              "label_seq_id", "label_comp_id"})) {
    if (row.has2(0) && row.has2(1) && row.has2(2) && row.has2(3))
      if (Model* model = st.find_model(row[0])) {
        int seq = cif::as_int(row[2]);
        if (Residue* res = model->find_residue_with_label(row[1], seq, row[3]))
          res->is_cis = true;
      }
  }

  for (auto row : block.find("_struct_conn.", {"id", "conn_type_id",
        "ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_label_comp_id",
        "ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_label_comp_id"})) {
    if (cif::is_null(row[3]) || cif::is_null(row[6]))
      continue;
    Connection c;
    c.id = row.str(0);
    std::string type = row.str(1);
    for (int i = 0; i != Connection::None; ++i)
      if (get_mmcif_connection_type_id(Connection::Type(i)) == type) {
        c.type = Connection::Type(i);
        break;
      }
    for (Model& model : st.models) {
      c.res1 = model.find_residue_with_label(row.str(2),
                                             cif::as_int(row[3]), row.str(4));
      c.res2 = model.find_residue_with_label(row.str(5),
                                             cif::as_int(row[6]), row.str(7));
      if (c.res1 && c.res2) {
        c.res1->conn.push_back(c.id);
        c.res2->conn.push_back(c.id);
        model.connections.push_back(c);
      }
      // TODO atom name
    }
  }

  return st;
}

} // namespace impl

inline Structure read_atoms(cif::Document doc) {
  return impl::structure_from_cif_block(doc.sole_block());
}

} // namespace gemmi
#endif
// vim:sw=2:ts=2:et
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Algorithms/Fields/MeshData/GetMeshQualityFieldAlgo.h>
#include <Core/Datatypes/Field.h>
#include <Core/Datatypes/FieldInformation.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Algorithms::Fields;

GetMeshQualityFieldAlgo::GetMeshQualityFieldAlgo()
{
  add_option("metric","scaled_jacobian","scaled_jacobian|jacobian|volume|insc_circ_ratio");
}

ALGORITHM_PARAMETER_DEF(Fields,Metric);

bool
GetMeshQualityFieldAlgo::run(FieldHandle input, FieldHandle& output)
{
  std::string metric = get_option("metric");

  if (!input)
  {
    error("No input field");
    return false;
  }

  FieldInformation fi(input);
  fi.make_double();
  fi.make_constantdata();

  output = CreateField(fi,input->mesh());

  if (!output)
  {
    error("Could not create output field");
    return false;
  }

  VField* ofield = output->vfield();
  VMesh*  imesh  = input->vmesh();

  if (metric == "scaled_jacobian")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->scaled_jacobian_metric(j),j);
    }
  }
  else if (metric == "jacobian")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->jacobian_metric(j),j);
    }
  }
  else if (metric == "volume")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->volume_metric(j),j);
    }
  }
  else if (metric == "insc_circ_ratio")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->inscribed_circumscribed_radius_metric(j),j);
    }
  }

    return true;
}

<commit_msg>UI File 1/3<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Algorithms/Fields/MeshData/GetMeshQualityFieldAlgo.h>
#include <Core/Datatypes/Field.h>
#include <Core/Datatypes/FieldInformation.h>
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Algorithms::Fields;

ALGORITHM_PARAMETER_DEF(Fields,Metric);

GetMeshQualityFieldAlgo::GetMeshQualityFieldAlgo()
{
  addOption(Metric,"scaled_jacobian","scaled_jacobian|jacobian|volume|insc_circ_ratio");
}

bool
GetMeshQualityFieldAlgo::run(FieldHandle input, FieldHandle& output)
{
  std::string Metric = get_option("Metric");

  if (!input)
  {
    error("No input field");
    return false;
  }

  FieldInformation fi(input);
  fi.make_double();
  fi.make_constantdata();

  output = CreateField(fi,input->mesh());

  if (!output)
  {
    error("Could not create output field");
    return false;
  }

  VField* ofield = output->vfield();
  VMesh*  imesh  = input->vmesh();

  if (Metric == "scaled_jacobian")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->scaled_jacobian_Metric(j),j);
    }
  }
  else if (Metric == "jacobian")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->jacobian_Metric(j),j);
    }
  }
  else if (Metric == "volume")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->volume_Metric(j),j);
    }
  }
  else if (Metric == "insc_circ_ratio")
  {
    VMesh::Elem::size_type num_values = imesh->num_elems();
    for (VMesh::Elem::index_type j=0; j<num_values; j++)
    {
      ofield->set_value(imesh->inscribed_circumscribed_radius_Metric(j),j);
    }
  }

    return true;
}

<|endoftext|>


<commit_before>/** @file
@brief Implementation

@date 2015

@author
Sensics, Inc.
<http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <osvr/ClientKit/Context.h>
#include <osvr/Common/ClientContext.h>
#include <osvr/Common/PathTreeFull.h>
#include <osvr/Common/PathNode.h>
#include <osvr/Common/ResolveTreeNode.h>
#include <osvr/Util/TreeTraversalVisitor.h>
#include <osvr/Common/PathElementTypes.h>
#include <osvr/Common/PathElementTools.h>
#include <osvr/Common/ParseAlias.h>
#include <osvr/Util/Verbosity.h>
#include "GraphOutputInterface.h"

// Library/third-party includes
#include <boost/program_options.hpp>
#include <boost/variant.hpp>

// Standard includes
#include <iostream>
#include <fstream>

struct Options {
    std::string graphOutputType;
    bool fullPaths = false;
    bool showAliases = false;
    bool showTree = true;
};
int osvrToStream(std::ostream &os, Options const &opts) {
    osvr::clientkit::ClientContext context("com.osvr.osvr2dot");

    /// Get a non-const copy of the path tree.
    osvr::common::PathTree pathTree;
    osvr::common::clonePathTree(context.get()->getPathTree(), pathTree);
    {
        auto graph = GraphOutputInterface::createGraphOutputInterface(
            os, opts.graphOutputType);
        using namespace osvr::common;
        /// First traverse to ensure all aliases are resolved
        osvr::util::traverseWith(
            pathTree.getRoot(),
            [&pathTree](osvr::common::PathNode const &node) {
                resolveTreeNode(pathTree, getFullPath(node));
            });

        /// Now traverse for tree node output
        bool fullPaths = opts.fullPaths;
        osvr::util::traverseWith(
            pathTree.getRoot(),
            [&graph, &pathTree, fullPaths](osvr::common::PathNode const &node) {
                auto parent = node.getParent();
                if (nullptr == parent) {
                    return;
                }
                auto fullPath = getFullPath(node);
                auto &graphNode = graph->addNode(
                    fullPaths ? fullPath : ("/" + node.getName()), fullPath,
                    getTypeName(node));
            });

        if (opts.showTree) {
            /// Now traverse for parent link output
            graph->enableTreeOrganization();
            osvr::util::traverseWith(
                pathTree.getRoot(), [&graph, &pathTree, fullPaths](
                                        osvr::common::PathNode const &node) {
                    auto parent = node.getParent();
                    if (nullptr == parent) {
                        return;
                    }
                    auto fullPath = getFullPath(node);
                    auto &graphNode = graph->getNode(getFullPath(node));
                    graph->addEdge(graph->getNode(getFullPath(*parent)),
                                   graphNode, "child");
                });
        }
        if (opts.showAliases) {
            /// Now traverse for aliases
            osvr::util::traverseWith(
                pathTree.getRoot(),
                [&graph, &pathTree](osvr::common::PathNode const &node) {
                    auto alias =
                        boost::get<elements::AliasElement>(&node.value());
                    if (nullptr == alias) {
                        return;
                    }
                    auto fullPath = getFullPath(node);
                    ParsedAlias parsed(alias->getSource());
                    auto &graphNode = graph->getNode(fullPath);

                    PathNode &source = pathTree.getNodeByPath(parsed.getLeaf());
                    graph->addEdge(
                        graphNode, graph->getNode(getFullPath(source)), "alias",
                        parsed.isSimple() ? std::string() : parsed.getAlias());
                });
        }
    }
    return 0;
}

int main(int argc, char *argv[]) {
    std::ostream *stream = &std::cout;
    Options opt;
    std::string type;
    {
        namespace po = boost::program_options;
        // clang-format off
        po::options_description desc("Options");
        desc.add_options()
            ("help", "produce help message")
            ("output,O", po::value<std::string>(), "output file (defaults to standard out)")
            ("type,T", po::value<std::string>(&opt.graphOutputType)->default_value("dot"), "output data format (defaults to graphviz/dot)")
            ("show-tree,t", po::value<bool>(&opt.showTree)->default_value(true), "Whether or not to show the path tree structure")
            ("show-aliases,a", po::value<bool>(&opt.showAliases)->default_value(false), "Whether or not to show the alias links")
            ("full-paths,p", po::value<bool>(&opt.fullPaths)->default_value(false), "Whether or not to use a node's full path as its label")
            ;
        // clang-format on
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
        po::notify(vm);
        if (vm.count("help")) {
            std::cerr << "Traverses the path tree and outputs it as input data "
                         "for a graph software package\n";
            std::cerr << "Usage: " << argv[0] << " [options]\n";
            std::cerr << desc << "\n";
            return 1;
        }
        std::ofstream file;
        if (vm.count("output")) {
            file.open(vm["output"].as<std::string>().c_str());
            if (!file) {
                std::cerr << "Error opening output file." << std::endl;
                return 1;
            }
            stream = &file;
        }
        return osvrToStream(*stream, opt);
    }
}<commit_msg>No sense in offering an option of output formats when the only one is dot<commit_after>/** @file
@brief Implementation

@date 2015

@author
Sensics, Inc.
<http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <osvr/ClientKit/Context.h>
#include <osvr/Common/ClientContext.h>
#include <osvr/Common/PathTreeFull.h>
#include <osvr/Common/PathNode.h>
#include <osvr/Common/ResolveTreeNode.h>
#include <osvr/Util/TreeTraversalVisitor.h>
#include <osvr/Common/PathElementTypes.h>
#include <osvr/Common/PathElementTools.h>
#include <osvr/Common/ParseAlias.h>
#include <osvr/Util/Verbosity.h>
#include "GraphOutputInterface.h"

// Library/third-party includes
#include <boost/program_options.hpp>
#include <boost/variant.hpp>

// Standard includes
#include <iostream>
#include <fstream>

struct Options {
    std::string graphOutputType = "dot";
    bool fullPaths = false;
    bool showAliases = false;
    bool showTree = true;
};
int osvrToStream(std::ostream &os, Options const &opts) {
    osvr::clientkit::ClientContext context("com.osvr.osvr2dot");

    /// Get a non-const copy of the path tree.
    osvr::common::PathTree pathTree;
    osvr::common::clonePathTree(context.get()->getPathTree(), pathTree);
    {
        auto graph = GraphOutputInterface::createGraphOutputInterface(
            os, opts.graphOutputType);
        using namespace osvr::common;
        /// First traverse to ensure all aliases are resolved
        osvr::util::traverseWith(
            pathTree.getRoot(),
            [&pathTree](osvr::common::PathNode const &node) {
                resolveTreeNode(pathTree, getFullPath(node));
            });

        /// Now traverse for tree node output
        bool fullPaths = opts.fullPaths;
        osvr::util::traverseWith(
            pathTree.getRoot(),
            [&graph, &pathTree, fullPaths](osvr::common::PathNode const &node) {
                auto parent = node.getParent();
                if (nullptr == parent) {
                    return;
                }
                auto fullPath = getFullPath(node);
                auto &graphNode = graph->addNode(
                    fullPaths ? fullPath : ("/" + node.getName()), fullPath,
                    getTypeName(node));
            });

        if (opts.showTree) {
            /// Now traverse for parent link output
            graph->enableTreeOrganization();
            osvr::util::traverseWith(
                pathTree.getRoot(), [&graph, &pathTree, fullPaths](
                                        osvr::common::PathNode const &node) {
                    auto parent = node.getParent();
                    if (nullptr == parent) {
                        return;
                    }
                    auto fullPath = getFullPath(node);
                    auto &graphNode = graph->getNode(getFullPath(node));
                    graph->addEdge(graph->getNode(getFullPath(*parent)),
                                   graphNode, "child");
                });
        }
        if (opts.showAliases) {
            /// Now traverse for aliases
            osvr::util::traverseWith(
                pathTree.getRoot(),
                [&graph, &pathTree](osvr::common::PathNode const &node) {
                    auto alias =
                        boost::get<elements::AliasElement>(&node.value());
                    if (nullptr == alias) {
                        return;
                    }
                    auto fullPath = getFullPath(node);
                    ParsedAlias parsed(alias->getSource());
                    auto &graphNode = graph->getNode(fullPath);

                    PathNode &source = pathTree.getNodeByPath(parsed.getLeaf());
                    graph->addEdge(
                        graphNode, graph->getNode(getFullPath(source)), "alias",
                        parsed.isSimple() ? std::string() : parsed.getAlias());
                });
        }
    }
    return 0;
}

int main(int argc, char *argv[]) {
    std::ostream *stream = &std::cout;
    Options opt;
    std::string type;
    {
        namespace po = boost::program_options;
        // clang-format off
        po::options_description desc("Options");
        desc.add_options()
            ("help", "produce help message")
            ("output,O", po::value<std::string>(), "output file (defaults to standard out)")
            //("type,T", po::value<std::string>(&opt.graphOutputType)->default_value("dot"), "output data format (defaults to graphviz/dot)")
            ("show-tree,t", po::value<bool>(&opt.showTree)->default_value(true), "Whether or not to show the path tree structure")
            ("show-aliases,a", po::value<bool>(&opt.showAliases)->default_value(false), "Whether or not to show the alias links")
            ("full-paths,p", po::value<bool>(&opt.fullPaths)->default_value(false), "Whether or not to use a node's full path as its label")
            ;
        // clang-format on
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
        po::notify(vm);
        if (vm.count("help")) {
            std::cerr << "Traverses the path tree and outputs it as input data "
                         "for a graph software package\n";
            std::cerr << "Usage: " << argv[0] << " [options]\n";
            std::cerr << desc << "\n";
            return 1;
        }
        std::ofstream file;
        if (vm.count("output")) {
            file.open(vm["output"].as<std::string>().c_str());
            if (!file) {
                std::cerr << "Error opening output file." << std::endl;
                return 1;
            }
            stream = &file;
        }
        return osvrToStream(*stream, opt);
    }
}<|endoftext|>


<commit_before>#ifndef LIBICS3_ICS3_EEPROM_H_
#define LIBICS3_ICS3_EEPROM_H_

#include <array>
#include <vector>

namespace ics {
  class Eeprom {
  public:
    Eeprom() noexcept;
    void set(EepParam) noexcept;
    EepParam get(const EepParam &) const noexcept;
    void copy(std::vector<unsigned char> &) const noexcept;

  private:
    std::array<unsigned char, 64> data;
  };
}

#endif // LIBICS3_ICS3_EEPROM_H_
<commit_msg>Fix EepParam forward declaration on Eeprom class<commit_after>#ifndef LIBICS3_ICS3_EEPROM_H_
#define LIBICS3_ICS3_EEPROM_H_

#include <array>
#include <vector>

namespace ics {
  class EepParam;
  class Eeprom {
  public:
    Eeprom() noexcept;
    void set(EepParam) noexcept;
    EepParam get(const EepParam &) const noexcept;
    void copy(std::vector<unsigned char> &) const noexcept;

  private:
    std::array<unsigned char, 64> data;
  };
}

#endif // LIBICS3_ICS3_EEPROM_H_
<|endoftext|>


<commit_before>/**
 * File    : E.cpp
 * Author  : Kazune Takahashi
 * Created : 2018-7-7 20:57:37
 * Powered by Visual Studio Code
 */

#include <iostream>
#include <iomanip>   // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set>
#include <functional>
#include <random> // auto rd = bind(uniform_int_distribution<int>(0, 9), mt19937(19920725));
#include <chrono> // std::chrono::system_clock::time_point start_time, end_time;
// start = std::chrono::system_clock::now();
// double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time).count();
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
const ll infty = 1000000000000007;

typedef tuple<bool, ll> I;
typedef tuple<int, ll> P;
int n, m;
vector<P> V[100010];
I info[100010];
I nil = I(true, infty);
set<ll> value;

int main()
{
  cin >> n >> m;
  for (auto i = 0; i < m; i++)
  {
    int u, v;
    ll s;
    cin >> u >> v >> s;
    u--;
    v--;
    V[u].push_back(P(v, s));
    V[v].push_back(P(u, s));
  }
  fill(info, info + 100010, nil);
  info[0] = I(true, 0);
  queue<int> Q;
  Q.push(0);
  bool visited[100010];
  fill(visited, visited + 100010, false);
  while (!Q.empty())
  {
    int x = Q.front();
    Q.pop();
    if (!visited[x])
    {
      visited[x] = true;
      bool pm = get<0>(info[x]);
      ll v = get<1>(info[x]);
      for (auto e : V[x])
      {
        int y = get<0>(e);
        ll w = get<1>(e);
        if (info[y] == nil)
        {
          info[y] = I(!pm, w - v);
          Q.push(y);
        }
        else
        {
          bool pm2 = get<0>(info[y]);
          ll v2 = get<1>(info[y]);
          if (pm == pm2)
          {
            ll tmp = w - (v + v2);
            if (tmp % 2 != 0)
            {
              cout << 0 << endl;
              return 0;
            }
            tmp = tmp / 2;
            if (!pm)
            {
              tmp = -tmp;
            }
            if (tmp > 0)
            {
              value.insert(tmp);
              if ((int)value.size() > 1)
              {
                cout << 0 << endl;
                return 0;
              }
            }
          }
          else
          {
            if (info[y] != I(!pm, w - v))
            {
              cout << 0 << endl;
              return 0;
            }
          }
        }
      }
    }
  }
  ll p_mini = infty;
  ll m_mini = infty;
  if ((int)value.size() == 1)
  {
    cout << 1 << endl;
    return 0;
  }
  for (auto i = 0; i < n; i++)
  {
    bool pm = get<0>(info[i]);
    ll v = get<1>(info[i]);
    cerr << "info[" << i << "] = (" << pm << ", " << v << ")" << endl;
    if (pm)
    {
      p_mini = min(p_mini, v);
    }
    else
    {
      m_mini = min(m_mini, v);
    }
  }
  ll ans;
  if (p_mini >= 0)
  {
    ans = m_mini;
  }
  else
  {
    ans = m_mini + p_mini + 1;
  }
  cout << max(0ll, ans) << endl;
}<commit_msg>tried E.cpp to 'E'<commit_after>/**
 * File    : E.cpp
 * Author  : Kazune Takahashi
 * Created : 2018-7-7 20:57:37
 * Powered by Visual Studio Code
 */

#include <iostream>
#include <iomanip>   // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set>
#include <functional>
#include <random> // auto rd = bind(uniform_int_distribution<int>(0, 9), mt19937(19920725));
#include <chrono> // std::chrono::system_clock::time_point start_time, end_time;
// start = std::chrono::system_clock::now();
// double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time).count();
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
const ll infty = 1000000000000007;

typedef tuple<bool, ll> I;
typedef tuple<int, ll> P;
int n, m;
vector<P> V[100010];
I info[100010];
I nil = I(true, infty);
set<ll> value;

int main()
{
  cin >> n >> m;
  for (auto i = 0; i < m; i++)
  {
    int u, v;
    ll s;
    cin >> u >> v >> s;
    u--;
    v--;
    V[u].push_back(P(v, s));
    V[v].push_back(P(u, s));
  }
  fill(info, info + 100010, nil);
  info[0] = I(true, 0);
  queue<int> Q;
  Q.push(0);
  bool visited[100010];
  fill(visited, visited + 100010, false);
  while (!Q.empty())
  {
    int x = Q.front();
    Q.pop();
    if (!visited[x])
    {
      visited[x] = true;
      bool pm = get<0>(info[x]);
      ll v = get<1>(info[x]);
      for (auto e : V[x])
      {
        int y = get<0>(e);
        ll w = get<1>(e);
        if (info[y] == nil)
        {
          info[y] = I(!pm, w - v);
          Q.push(y);
        }
        else
        {
          bool pm2 = get<0>(info[y]);
          ll v2 = get<1>(info[y]);
          if (pm == pm2)
          {
            ll tmp = w - (v + v2);
            if (tmp % 2 != 0)
            {
              cout << 0 << endl;
              return 0;
            }
            tmp = tmp / 2;
            if (!pm)
            {
              tmp = -tmp;
            }
            if (tmp > 0)
            {
              value.insert(tmp);
              if ((int)value.size() > 1)
              {
                cout << 0 << endl;
                return 0;
              }
            }
          }
          else
          {
            if (info[y] != I(!pm, w - v))
            {
              cout << 0 << endl;
              return 0;
            }
          }
        }
      }
    }
  }
  ll p_mini = infty;
  ll m_mini = infty;
  if ((int)value.size() == 1)
  {
    cout << 1 << endl;
    return 0;
  }
  for (auto i = 0; i < n; i++)
  {
    bool pm = get<0>(info[i]);
    ll v = get<1>(info[i]);
    // cerr << "info[" << i << "] = (" << pm << ", " << v << ")" << endl;
    if (pm)
    {
      p_mini = min(p_mini, v);
    }
    else
    {
      m_mini = min(m_mini, v);
    }
  }
  cerr << p_mini << " " << m_mini << endl;
  ll ans;
  if (p_mini >= 0)
  {
    ans = m_mini;
  }
  else
  {
    ans = m_mini + p_mini + 1;
  }
  cout << max(0ll, ans) << endl;
}<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2001 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "multicast.h"
#include "ErrorHandler.h"

int			openBroadcast(int port, const char* service,
					struct sockaddr_in* addr)
{
#if defined(_WIN32)
  const BOOL optOn = TRUE;
  BOOL opt = optOn;
#else
  const int optOn = 1;
  int opt = optOn;
#endif
  int fd;

  /* check parameters */
  if (!addr) {
    printError("openBroadcast: Must supply a return address structure!");
    return -1;
  }
  memset(addr, 0, sizeof(*addr));

  /* lookup service and check port */
  if (service) {
    struct servent *sp = getservbyname(service, "udp");
    if (!sp) {
      if (port <= 0) {
	printError("openBroadcast: No udp service %s", service);
	return -1;
      }
    }
    else {
      port = sp->s_port;
    }
  }
  if (port <= 0) {
    printError("openBroadcast: Invalid port %s", port);
    return -1;
  }

  /* open socket */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    nerror("openBroadcast: socket");
    return -1;
  }

  /* set address info */
  addr->sin_family = AF_INET;
  addr->sin_addr.s_addr = htonl(INADDR_ANY);
  addr->sin_port = htons(port);

#if defined(SO_REUSEPORT)
  /* set reuse port */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_REUSEPORT");
    close(fd);
    return -1;
  }
#else if defined(SO_REUSEADDR)
  /* set reuse address */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_REUSEADDR");
    close(fd);
    return -1;
  }
#endif

  /* bind address */
  if (bind(fd, (const struct sockaddr*)addr, sizeof(*addr)) < 0) {
    nerror("openBroadcast: bind");
    close(fd);
    return -1;
  }

  /* make broadcast */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_BROADCAST");
    close(fd);
    return -1;
  }

  // address to send to is the broadcast address
  addr->sin_addr.s_addr = INADDR_BROADCAST;

#if defined(__linux__)
  // linux doesn't seem to like INADDR_BROADCAST, but it's okay
  // with the broadcast address on the local network.  get the
  // broadcast address on the first interface that's not the
  // loopback interface.  (check the first numInterfaces
  // interfaces.)
  const int numInterfaces = 5;
  int i;
  struct ifconf conf;
  struct ifreq req[numInterfaces];

  // get the list of interface names
  conf.ifc_len = sizeof(req);
  conf.ifc_ifcu.ifcu_req = req;
  for (i = 0; i < numInterfaces; ++i)
    req[i].ifr_ifrn.ifrn_name[0] = 0;
  if (ioctl(fd, SIOCGIFCONF, &conf) < 0) {
    nerror("openBroadcast: getting interface list");
    close(fd);
    return(-1);
  }

  // get the broadcast address on each interface
  for (i = 0; i < numInterfaces; ++i) {
    // if no name then we're done
    if (req[i].ifr_ifrn.ifrn_name[0] == 0)
      break;

    // if we can't get the address then skip this interface
    if (ioctl(fd, SIOCGIFBRDADDR, req + i) < 0)
      continue;

    // if the address is the loopback broadcast address then skip it
    const sockaddr_in* ifbaddr = (const sockaddr_in*)
					&req[i].ifr_ifru.ifru_broadaddr;
    if (ntohl(ifbaddr->sin_addr.s_addr) == 0x7ffffffflu)
      continue;

    // got the broadcast address on the interface
    addr->sin_addr.s_addr = ifbaddr->sin_addr.s_addr;
    break;
  }

  // failed if no broadcast addresses found
  if (i == numInterfaces) {
    nerror("openBroadcast: getting broadcast address");
    close(fd);
    return(-1);
  }
#endif

  /* turn on non-blocking io */
  if (BzfNetwork::setNonBlocking(fd) < 0) {
    nerror("openBroadcast: setNonBlocking");
    close(fd);
    return(-1);
  }

  return fd;
}

int			openMulticast(const Address& address, int port,
					const char* service, int ttl,
					const char* net_interface,
					const char* mode,
					struct sockaddr_in* addr)
{
  struct ip_mreq mreq;
  struct in_addr ifaddr;
  int fd;

  /* check parameters */
  if (!addr) {
    printError("openMulticast: Must supply a return address structure!");
    return -1;
  }
  memset(addr, 0, sizeof(*addr));

  /* check mode */
  if (!mode || (mode[0] != 'r' && mode[0] != 'w')) {
    printError("openMulticast: Invalid mode");
    return -1;
  }

  /* lookup service and check port */
  if (service) {
    struct servent *sp = getservbyname(service, "udp");
    if (!sp) {
      if (port <= 0) {
	printError("openMulticast: No udp service %s", service);
	return -1;
      }
    }
    else {
      port = sp->s_port;
    }
  }
  if (port <= 0) {
    printError("openMulticast: Invalid port %s", port);
    return -1;
  }

  /* check group address */
  mreq.imr_multiaddr = InAddr(address);
  if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
    printError("openMulticast: Invalid multicast address");
    return -1;
  }

  /* open socket */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    nerror("openMulticast: socket");
    return -1;
  }

  /* check interface */
  if (net_interface && net_interface[0]) {
    ifaddr.s_addr = inet_addr(net_interface);
    if (ifaddr.s_addr == (unsigned long)-1) {
      struct hostent* hp = gethostbyname(net_interface);
      if (!hp) {
	printError("openMulticast: Can't get address of %s", net_interface);
	close(fd);
	return -1;
      }
      memcpy(&ifaddr, hp->h_addr, hp->h_length);
    }
    mreq.imr_interface = ifaddr;
  }
  else {
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

#if defined(__linux__)
    // linux doesn't seem to like INADDR_ANY as the interface.
    // find the first non-loopback interface and use that.
    // (only check the first numInterfaces interfaces.)
    const int numInterfaces = 5;
    int i;
    struct ifconf conf;
    struct ifreq req[numInterfaces];

    // get the list of interface names
    conf.ifc_len = sizeof(req);
    conf.ifc_ifcu.ifcu_req = req;
    for (i = 0; i < numInterfaces; ++i)
      req[i].ifr_ifrn.ifrn_name[0] = 0;
    if (ioctl(fd, SIOCGIFCONF, &conf) < 0) {
      nerror("openMulticast: getting interface list");
      close(fd);
      return(-1);
    }

    // get the address of each interface
    for (i = 0; i < numInterfaces; ++i) {
      // if no name then we're done
      if (req[i].ifr_ifrn.ifrn_name[0] == 0)
	break;

      // if we can't get the address then skip this interface
      if (ioctl(fd, SIOCGIFADDR, req + i) < 0)
	continue;

      // if the address is the loopback address then skip it
      const sockaddr_in* ifaddr = (const sockaddr_in*)
					&req[i].ifr_ifru.ifru_addr;
      if (ntohl(ifaddr->sin_addr.s_addr) == INADDR_LOOPBACK)
	continue;

      // got the address on the interface
      if (!net_interface || !net_interface[0])
	mreq.imr_interface.s_addr = ifaddr->sin_addr.s_addr;
      break;
    }
#endif
  }

  /* set address info */
  addr->sin_family = AF_INET;
  addr->sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
  addr->sin_port = htons(port);

  /* set options on socket */
  if (mode[0] == 'w') {
#if defined(_WIN32) || defined(__linux__)
    /* windows requires socket to be bound before using multicast sockopts */
    /* linux requires that we bind to an interface's address to send */
    struct sockaddr_in tmpAddr;
    memset(&tmpAddr, 0, sizeof(tmpAddr));
    tmpAddr.sin_family = AF_INET;
    tmpAddr.sin_addr.s_addr = mreq.imr_interface.s_addr;
    tmpAddr.sin_port = 0;
    if (bind(fd, (const struct sockaddr*)&tmpAddr, sizeof(tmpAddr)) < 0) {
      nerror("openMulticast: bind");
      close(fd);
      return -1;
    }
#endif

    /* set ttl */
    if (setMulticastTTL(fd, ttl) < 0) {
      nerror("WARNING: openMulticast: setsockopt IP_MULTICAST_TTL");
      close(fd);
      return -1;
    }

    if (net_interface && net_interface[0] &&
		setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
		(SSOType)&ifaddr, sizeof(ifaddr)) < 0) {
      nerror("WARNING: openMulticast: Can't multicast on interface");
      close(fd);
      return -1;
    }
  }

  else {
#if defined(SO_REUSEPORT)
    /* set reuse port */
    int opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
				(SSOType)&opt, sizeof(opt)) < 0) {
      nerror("WARNING: openMulticast: setsockopt SO_REUSEPORT");
      close(fd);
      return -1;
    }
#else if defined(SO_REUSEADDR)
    /* set reuse address */
#if defined(_WIN32)
    BOOL opt = TRUE;
#else
    int opt = 1;
#endif
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
				(SSOType)&opt, sizeof(opt)) < 0) {
      nerror("WARNING: openMulticast: setsockopt SO_REUSEADDR");
      close(fd);
      return -1;
    }
#endif

    /* bind address */
#if defined(_WIN32)
    /* unlike linux, win32 requires that we bind to the ANY address */
    addr->sin_addr.s_addr = htonl(INADDR_ANY);
#endif
    if (bind(fd, (const struct sockaddr*)addr, sizeof(*addr)) < 0) {
      nerror("openMulticast: bind");
      close(fd);
      return -1;
    }

    /* join multicast group */
    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
				(SSOType)&mreq, sizeof(mreq)) < 0) {
      nerror("WARNING: openMulticast: Can't join multicast group");
      close(fd);
      return -1;
    }
  }

  /* turn on non-blocking io */
  if (BzfNetwork::setNonBlocking(fd) < 0) {
    nerror("openBroadcast: setNonBlocking");
    close(fd);
    return(-1);
  }

  return fd;
}

int			closeMulticast(int fd)
{
  if (fd == -1) return 0;
  return close(fd);
}

int			setMulticastTTL(int fd, int ttl)
{
#if defined(_WIN32)
  unsigned int opt = (unsigned int)ttl;
#else /* defined(_WIN32) */
  unsigned char opt = (unsigned char)ttl;
#endif /* defined(_WIN32) */
  return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
				(SSOType)&opt, sizeof(opt));
}

int			sendMulticast(int fd, const void* buffer,
					int bufferLength,
					const struct sockaddr_in* addr)
{
  return sendto(fd, (const char*)buffer, bufferLength, 0,
				(const struct sockaddr*)addr, sizeof(*addr));
}

#if !defined(AddrLen)
#define AddrLen		int
#endif

int			recvMulticast(int fd, void* buffer, int bufferLength,
					 struct sockaddr_in* addr)
{
  struct sockaddr_in from;
  AddrLen fromLength = sizeof(from);

  int byteCount = recvfrom(fd, (char*)buffer, bufferLength, 0,
				(struct sockaddr*)&from, &fromLength);
  if (byteCount < 0) {
    if (getErrno() == EWOULDBLOCK) {
      return 0;
    }
    else {
      nerror("recvMulticast");
      return -1;
    }
  }
  if (addr) *addr = from;
  return byteCount;
}
<commit_msg>try both SO_REUSEADDR and SO_REUSEPORT if available<commit_after>/* bzflag
 * Copyright (c) 1993 - 2001 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "multicast.h"
#include "ErrorHandler.h"

int			openBroadcast(int port, const char* service,
					struct sockaddr_in* addr)
{
#if defined(_WIN32)
  const BOOL optOn = TRUE;
  BOOL opt = optOn;
#else
  const int optOn = 1;
  int opt = optOn;
#endif
  int fd;

  /* check parameters */
  if (!addr) {
    printError("openBroadcast: Must supply a return address structure!");
    return -1;
  }
  memset(addr, 0, sizeof(*addr));

  /* lookup service and check port */
  if (service) {
    struct servent *sp = getservbyname(service, "udp");
    if (!sp) {
      if (port <= 0) {
	printError("openBroadcast: No udp service %s", service);
	return -1;
      }
    }
    else {
      port = sp->s_port;
    }
  }
  if (port <= 0) {
    printError("openBroadcast: Invalid port %s", port);
    return -1;
  }

  /* open socket */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    nerror("openBroadcast: socket");
    return -1;
  }

  /* set address info */
  addr->sin_family = AF_INET;
  addr->sin_addr.s_addr = htonl(INADDR_ANY);
  addr->sin_port = htons(port);

#if defined(SO_REUSEPORT)
  /* set reuse port */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_REUSEPORT");
    close(fd);
    return -1;
  }
#else if defined(SO_REUSEADDR)
  /* set reuse address */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_REUSEADDR");
    close(fd);
    return -1;
  }
#endif

  /* bind address */
  if (bind(fd, (const struct sockaddr*)addr, sizeof(*addr)) < 0) {
    nerror("openBroadcast: bind");
    close(fd);
    return -1;
  }

  /* make broadcast */
  opt = optOn;
  if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
				(SSOType)&opt, sizeof(opt)) < 0) {
    nerror("openBroadcast: setsockopt SO_BROADCAST");
    close(fd);
    return -1;
  }

  // address to send to is the broadcast address
  addr->sin_addr.s_addr = INADDR_BROADCAST;

#if defined(__linux__)
  // linux doesn't seem to like INADDR_BROADCAST, but it's okay
  // with the broadcast address on the local network.  get the
  // broadcast address on the first interface that's not the
  // loopback interface.  (check the first numInterfaces
  // interfaces.)
  const int numInterfaces = 5;
  int i;
  struct ifconf conf;
  struct ifreq req[numInterfaces];

  // get the list of interface names
  conf.ifc_len = sizeof(req);
  conf.ifc_ifcu.ifcu_req = req;
  for (i = 0; i < numInterfaces; ++i)
    req[i].ifr_ifrn.ifrn_name[0] = 0;
  if (ioctl(fd, SIOCGIFCONF, &conf) < 0) {
    nerror("openBroadcast: getting interface list");
    close(fd);
    return(-1);
  }

  // get the broadcast address on each interface
  for (i = 0; i < numInterfaces; ++i) {
    // if no name then we're done
    if (req[i].ifr_ifrn.ifrn_name[0] == 0)
      break;

    // if we can't get the address then skip this interface
    if (ioctl(fd, SIOCGIFBRDADDR, req + i) < 0)
      continue;

    // if the address is the loopback broadcast address then skip it
    const sockaddr_in* ifbaddr = (const sockaddr_in*)
					&req[i].ifr_ifru.ifru_broadaddr;
    if (ntohl(ifbaddr->sin_addr.s_addr) == 0x7ffffffflu)
      continue;

    // got the broadcast address on the interface
    addr->sin_addr.s_addr = ifbaddr->sin_addr.s_addr;
    break;
  }

  // failed if no broadcast addresses found
  if (i == numInterfaces) {
    nerror("openBroadcast: getting broadcast address");
    close(fd);
    return(-1);
  }
#endif

  /* turn on non-blocking io */
  if (BzfNetwork::setNonBlocking(fd) < 0) {
    nerror("openBroadcast: setNonBlocking");
    close(fd);
    return(-1);
  }

  return fd;
}

int			openMulticast(const Address& address, int port,
					const char* service, int ttl,
					const char* net_interface,
					const char* mode,
					struct sockaddr_in* addr)
{
  struct ip_mreq mreq;
  struct in_addr ifaddr;
  int fd;

  /* check parameters */
  if (!addr) {
    printError("openMulticast: Must supply a return address structure!");
    return -1;
  }
  memset(addr, 0, sizeof(*addr));

  /* check mode */
  if (!mode || (mode[0] != 'r' && mode[0] != 'w')) {
    printError("openMulticast: Invalid mode");
    return -1;
  }

  /* lookup service and check port */
  if (service) {
    struct servent *sp = getservbyname(service, "udp");
    if (!sp) {
      if (port <= 0) {
	printError("openMulticast: No udp service %s", service);
	return -1;
      }
    }
    else {
      port = sp->s_port;
    }
  }
  if (port <= 0) {
    printError("openMulticast: Invalid port %s", port);
    return -1;
  }

  /* check group address */
  mreq.imr_multiaddr = InAddr(address);
  if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
    printError("openMulticast: Invalid multicast address");
    return -1;
  }

  /* open socket */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    nerror("openMulticast: socket");
    return -1;
  }

  /* check interface */
  if (net_interface && net_interface[0]) {
    ifaddr.s_addr = inet_addr(net_interface);
    if (ifaddr.s_addr == (unsigned long)-1) {
      struct hostent* hp = gethostbyname(net_interface);
      if (!hp) {
	printError("openMulticast: Can't get address of %s", net_interface);
	close(fd);
	return -1;
      }
      memcpy(&ifaddr, hp->h_addr, hp->h_length);
    }
    mreq.imr_interface = ifaddr;
  }
  else {
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

#if defined(__linux__)
    // linux doesn't seem to like INADDR_ANY as the interface.
    // find the first non-loopback interface and use that.
    // (only check the first numInterfaces interfaces.)
    const int numInterfaces = 5;
    int i;
    struct ifconf conf;
    struct ifreq req[numInterfaces];

    // get the list of interface names
    conf.ifc_len = sizeof(req);
    conf.ifc_ifcu.ifcu_req = req;
    for (i = 0; i < numInterfaces; ++i)
      req[i].ifr_ifrn.ifrn_name[0] = 0;
    if (ioctl(fd, SIOCGIFCONF, &conf) < 0) {
      nerror("openMulticast: getting interface list");
      close(fd);
      return(-1);
    }

    // get the address of each interface
    for (i = 0; i < numInterfaces; ++i) {
      // if no name then we're done
      if (req[i].ifr_ifrn.ifrn_name[0] == 0)
	break;

      // if we can't get the address then skip this interface
      if (ioctl(fd, SIOCGIFADDR, req + i) < 0)
	continue;

      // if the address is the loopback address then skip it
      const sockaddr_in* ifaddr = (const sockaddr_in*)
					&req[i].ifr_ifru.ifru_addr;
      if (ntohl(ifaddr->sin_addr.s_addr) == INADDR_LOOPBACK)
	continue;

      // got the address on the interface
      if (!net_interface || !net_interface[0])
	mreq.imr_interface.s_addr = ifaddr->sin_addr.s_addr;
      break;
    }
#endif
  }

  /* set address info */
  addr->sin_family = AF_INET;
  addr->sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
  addr->sin_port = htons(port);

  /* set options on socket */
  if (mode[0] == 'w') {
#if defined(_WIN32) || defined(__linux__)
    /* windows requires socket to be bound before using multicast sockopts */
    /* linux requires that we bind to an interface's address to send */
    struct sockaddr_in tmpAddr;
    memset(&tmpAddr, 0, sizeof(tmpAddr));
    tmpAddr.sin_family = AF_INET;
    tmpAddr.sin_addr.s_addr = mreq.imr_interface.s_addr;
    tmpAddr.sin_port = 0;
    if (bind(fd, (const struct sockaddr*)&tmpAddr, sizeof(tmpAddr)) < 0) {
      nerror("openMulticast: bind");
      close(fd);
      return -1;
    }
#endif

    /* set ttl */
    if (setMulticastTTL(fd, ttl) < 0) {
      nerror("WARNING: openMulticast: setsockopt IP_MULTICAST_TTL");
      close(fd);
      return -1;
    }

    if (net_interface && net_interface[0] &&
		setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
		(SSOType)&ifaddr, sizeof(ifaddr)) < 0) {
      nerror("WARNING: openMulticast: Can't multicast on interface");
      close(fd);
      return -1;
    }
  }

  else {
#if defined(SO_REUSEPORT)
    /* set reuse port */
    int opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
				(SSOType)&opt, sizeof(opt)) < 0) {
      nerror("WARNING: openMulticast: setsockopt SO_REUSEPORT");
      close(fd);
      return -1;
    }
#endif
#if defined(SO_REUSEADDR)
    /* set reuse address */
#if defined(_WIN32)
    BOOL opt = TRUE;
#else
    int opt = 1;
#endif
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
				(SSOType)&opt, sizeof(opt)) < 0) {
      nerror("WARNING: openMulticast: setsockopt SO_REUSEADDR");
      close(fd);
      return -1;
    }
#endif

    /* bind address */
#if defined(_WIN32)
    /* unlike linux, win32 requires that we bind to the ANY address */
    addr->sin_addr.s_addr = htonl(INADDR_ANY);
#endif
    if (bind(fd, (const struct sockaddr*)addr, sizeof(*addr)) < 0) {
      nerror("openMulticast: bind");
      close(fd);
      return -1;
    }

    /* join multicast group */
    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
				(SSOType)&mreq, sizeof(mreq)) < 0) {
      nerror("WARNING: openMulticast: Can't join multicast group");
      close(fd);
      return -1;
    }
  }

  /* turn on non-blocking io */
  if (BzfNetwork::setNonBlocking(fd) < 0) {
    nerror("openBroadcast: setNonBlocking");
    close(fd);
    return(-1);
  }

  return fd;
}

int			closeMulticast(int fd)
{
  if (fd == -1) return 0;
  return close(fd);
}

int			setMulticastTTL(int fd, int ttl)
{
#if defined(_WIN32)
  unsigned int opt = (unsigned int)ttl;
#else /* defined(_WIN32) */
  unsigned char opt = (unsigned char)ttl;
#endif /* defined(_WIN32) */
  return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
				(SSOType)&opt, sizeof(opt));
}

int			sendMulticast(int fd, const void* buffer,
					int bufferLength,
					const struct sockaddr_in* addr)
{
  return sendto(fd, (const char*)buffer, bufferLength, 0,
				(const struct sockaddr*)addr, sizeof(*addr));
}

#if !defined(AddrLen)
#define AddrLen		int
#endif

int			recvMulticast(int fd, void* buffer, int bufferLength,
					 struct sockaddr_in* addr)
{
  struct sockaddr_in from;
  AddrLen fromLength = sizeof(from);

  int byteCount = recvfrom(fd, (char*)buffer, bufferLength, 0,
				(struct sockaddr*)&from, &fromLength);
  if (byteCount < 0) {
    if (getErrno() == EWOULDBLOCK) {
      return 0;
    }
    else {
      nerror("recvMulticast");
      return -1;
    }
  }
  if (addr) *addr = from;
  return byteCount;
}
<|endoftext|>