<commit_before>/*********** <VRJ VNC auto-copyright.pl BEGIN do not edit this line> **********
 *
 * VRJ VNC is (C) Copyright 2003-2005 by Iowa State University
 *
 * Original Authors:
 *   Patrick Hartling, Allen Bierbaum
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this application; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ************ <VRJ VNC auto-copyright.pl END do not edit this line> **********/

#include <vrj/vrjConfig.h>

#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <gmtl/Matrix.h>
#include <gmtl/Vec.h>
#include <gmtl/Coord.h>
#include <gmtl/Generate.h>

#include <VNCDesktop.h>
#include <vncApp.h>

namespace vrjvnc
{

bool vncApp::configCanHandle(jccl::ConfigElementPtr element)
{
   static const std::string my_type("vrj_vnc_app");

   return (my_type == element->getID());
}

bool vncApp::configAdd(jccl::ConfigElementPtr element)
{
   vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "vncApp::configAdd()\n"
                                             << vprDEBUG_FLUSH;

   if ( element->getProperty<bool>("embedded_gui") )
   {
      std::string vnc_host = element->getProperty<std::string>("vnc_host");
      vpr::Uint16 vnc_port = element->getProperty<vpr::Uint16>("vnc_port");
      std::string vnc_pass = element->getProperty<std::string>("vnc_password");
      float       vnc_size =
         element->getProperty<float>("vnc_desktop_side_length");

      try
      {
         mDesktop = new VNCDesktop(vnc_host, vnc_port, vnc_pass, vnc_size);
         mDesktop->init("VJWand", "VJButton0", "VJButton1", "VJButton2");
         mEmbeddedGUI = true;
      }
      catch (...)
      {
         vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
            << clrOutNORM(clrRED, "ERROR:")
            << " Failed to initialize VNC desktop\n" << vprDEBUG_FLUSH;
         mEmbeddedGUI = false;
      }
   }

   return true;
}

void vncApp::preFrame()
{
   bool we_have_focus(true);

   mFrameCount++;

   vpr::Interval frame_time = vpr::Interval::now() - mPrevFrameStartTime;
   mPrevFrameStartTime.setNow();
   mFrameRate.addSample(frame_time.secf());

   if ( (mFrameCount % 50) == 1 )
   {
      mDesktop->printStats();

      // fps = 1.0f / time_per_frame
      float fps = 1.0f / mFrameRate.getSTA();
      std::cout << "fps: " << fps << std::endl;
   }

   // Update embedded VNC desktop
   if ( mEmbeddedGUI )
   {
      VNCDesktop::Focus focus = mDesktop->update(mNavMat);
      we_have_focus = (focus == VNCDesktop::NOT_IN_FOCUS);
   }

   // Put your pre frame computations here.

/*
   std::cout << "Wand Buttons:"
             << " 0: " << mButton0->getData()
             << " 1: " << mButton1->getData()
             << " 2: " << mButton2->getData()
             << " 3: " << mButton3->getData()
             << " 4: " << mButton4->getData()
             << " 5: " << mButton5->getData()
             << std::endl;
*/
   if ( we_have_focus )
   {
      gmtl::Matrix44f wandMatrix;
      wandMatrix = mWand->getData();      // Get the wand matrix

      // Update navigation
      // - Find forward direction of wand
      // - Translate along that direction
      float velocity(0.0f);
      float rotation(0.0f);
      if ( mButton0->getData() )
      {
         velocity = 0.05f;
      }

      if ( mButton1->getData() )
      {
         const float rot_scale(0.01f);
         float y_rot = gmtl::makeYRot<float>(wandMatrix);
         rotation = -1.0f * y_rot * rot_scale;
         gmtl::preMult(mNavMat,
                       gmtl::makeRot<gmtl::Matrix44f>(gmtl::EulerAngleXYZf(0.0f,rotation,0.0f)));
      }

      if ( velocity > 0.0f )
      {
         gmtl::Vec3f Zdir = gmtl::Vec3f(0.0f, 0.0f, velocity);
         gmtl::Vec3f direction(wandMatrix * Zdir);
         gmtl::preMult(mNavMat, gmtl::makeTrans<gmtl::Matrix44f>(direction));
      }

      // Check logger play button
      if ( mLoggerPlayButton->getData() == gadget::Digital::TOGGLE_ON )
      {
         std::cout << "\n\n------ Log Play Button hit ----\n" << std::flush;
         gadget::InputManager* input_mgr = gadget::InputManager::instance();
         gadget::InputLoggerPtr logger = input_mgr->getInputLogger();

         vprASSERT(logger.get() != NULL);
         logger->load("test_logging.xml");
         logger->play();
      }
   }
}

void vncApp::bufferPreDraw()
{
   glClearColor(0.0, 0.0, 0.0, 0.0);
   glClear(GL_COLOR_BUFFER_BIT);
}

void vncApp::contextPreDraw()
{
   if ( mEmbeddedGUI )
   {
      mDesktop->contextPreDraw();
   }
}

//----------------------------------------------
//  Draw the scene.  A box on the end of the wand
//----------------------------------------------

void vncApp::draw()
{
   glClear(GL_DEPTH_BUFFER_BIT);
   //std::cout << "\n--- myDraw() ---\n";

   //std::cout << "HeadPos:" << vrj::Coord(*mHead->getData()).pos << "\t"
   //          << "WandPos:" << vrj::Coord(*mWand->getData()).pos << std::endl;

   glMatrixMode(GL_MODELVIEW);

   // -- Draw box on wand --- //
   gmtl::Matrix44f wand_world = mWand->getData();
   gmtl::Matrix44f wand_mat;
   gmtl::Matrix44f inv_nav;
   gmtl::invert(inv_nav, mNavMat);
   wand_mat = inv_nav * wand_world;

   glPushMatrix();
   {
      // Move world for navigation
      glMultMatrixf(mNavMat.mData);

      // cout << "wand:\n" << *wandMatrix << endl;
      glPushMatrix();
         glMultMatrixf(wand_mat.mData);  // Push the wand matrix on the stack

         float wand_color[3];
         wand_color[0] = wand_color[1] = wand_color[2] = 0.0f;
         if ( mButton0->getData() == gadget::Digital::ON )
         {
            wand_color[0] += 0.5f;
         }

         if ( mButton1->getData() == gadget::Digital::ON )
         {
            wand_color[1] += 0.5f;
         }

         if ( mButton2->getData() == gadget::Digital::ON )
         {
            wand_color[2] += 0.5f;
         }

         if ( mButton3->getData() == gadget::Digital::ON )
         {
            wand_color[0] += 0.5f;
         }

         if ( mButton4->getData() == gadget::Digital::ON )
         {
            wand_color[1] += 0.5f;
         }

         if ( mButton5->getData() == gadget::Digital::ON )
         {
            wand_color[2] += 0.5f;
         }

         glColor3fv(wand_color);
         drawCube();
      glPopMatrix();

      // Draw a floor
      drawFloor();

      // VNC GUI
      if ( mEmbeddedGUI )
      {
         mDesktop->draw();
      }
   }
   glPopMatrix();
}

void vncApp::initGLState()
{
   GLfloat light0_ambient[]  = { 0.2f,  0.2f,  0.2f, 1.0f };
   GLfloat light0_diffuse[]  = { 0.8f,  0.8f,  0.8f, 1.0f };
   GLfloat light0_specular[] = { 1.0f,  1.0f,  1.0f, 1.0f };
   GLfloat light0_position[] = { 0.5f, 0.75f, 0.75f, 0.0f };

   GLfloat mat_ambient[]   = { 0.7f, 0.7f, 0.7f, 1.0f };
   GLfloat mat_diffuse[]   = { 1.0f, 0.5f, 0.8f, 1.0f };
   GLfloat mat_specular[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
   GLfloat mat_shininess[] = { 50.0f };
   GLfloat no_mat[]        = { 0.0f, 0.0f, 0.0f, 1.0f };

   glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
   glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
   glLightfv(GL_LIGHT0, GL_POSITION, light0_position);

   glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
   glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);

   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_COLOR_MATERIAL);
   glShadeModel(GL_SMOOTH);
}

//: Utility function for drawing a cube
void drawbox(GLdouble x0, GLdouble x1, GLdouble y0, GLdouble y1,
             GLdouble z0, GLdouble z1, GLenum type)
{
   static GLdouble n[6][3] =
   {
      { -1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 1.0, 0.0, 0.0 },
      { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 }
   };
   static GLint faces[6][4] =
   {
      { 0, 1, 2, 3 }, { 3, 2, 6, 7 }, { 7, 6, 5, 4 },
      { 4, 5, 1, 0 }, { 5, 6, 2, 1 }, { 7, 4, 0, 3 }
   };
   GLdouble v[8][3], tmp;

   if ( x0 > x1 )
   {
      tmp = x0; x0 = x1; x1 = tmp;
   }
   if ( y0 > y1 )
   {
      tmp = y0; y0 = y1; y1 = tmp;
   }
   if ( z0 > z1 )
   {
      tmp = z0; z0 = z1; z1 = tmp;
   }
   v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0;
   v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1;
   v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0;
   v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1;
   v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0;
   v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1;

   for ( GLint i = 0; i < 6; ++i )
   {
      glBegin(type);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][0]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][1]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][2]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][3]][0]);
      glEnd();
   }
}

void vncApp::drawFloor()
{
   glPolygonMode(GL_FRONT, GL_LINE);
   glLineWidth(2);
   glColor3f(0.30f, 0.87f, 0.22f);     // Nice matrix green

   glBegin(GL_LINES);
      for ( int z = -25; z < 25; ++z )
      {
         for ( int x = -25; x < 25; ++x )
         {
            // Draw the line along the Z axis.
            glVertex3f((float) x, 0.01f, (float) z);
            glVertex3f((float) x, 0.01f, (float) z + 1.0f);

            // Draw the line along the X axis.
            glVertex3f((float) x, 0.01f, (float) z);
            glVertex3f((float) x + 1.0f, 0.01f, (float) z);
         }
      }
   glEnd();

   glPolygonMode(GL_FRONT, GL_FILL);
}

} // namespace vrjvnc

<commit_msg>Bug fixed:	mDesktop could be accessed in preFrame() before it was 		ever assigned a valid address.<commit_after>/*********** <VRJ VNC auto-copyright.pl BEGIN do not edit this line> **********
 *
 * VRJ VNC is (C) Copyright 2003-2005 by Iowa State University
 *
 * Original Authors:
 *   Patrick Hartling, Allen Bierbaum
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this application; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ************ <VRJ VNC auto-copyright.pl END do not edit this line> **********/

#include <vrj/vrjConfig.h>

#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <gmtl/Matrix.h>
#include <gmtl/Vec.h>
#include <gmtl/Coord.h>
#include <gmtl/Generate.h>

#include <VNCDesktop.h>
#include <vncApp.h>

namespace vrjvnc
{

bool vncApp::configCanHandle(jccl::ConfigElementPtr element)
{
   static const std::string my_type("vrj_vnc_app");

   return (my_type == element->getID());
}

bool vncApp::configAdd(jccl::ConfigElementPtr element)
{
   vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "vncApp::configAdd()\n"
                                             << vprDEBUG_FLUSH;

   if ( element->getProperty<bool>("embedded_gui") )
   {
      std::string vnc_host = element->getProperty<std::string>("vnc_host");
      vpr::Uint16 vnc_port = element->getProperty<vpr::Uint16>("vnc_port");
      std::string vnc_pass = element->getProperty<std::string>("vnc_password");
      float       vnc_size =
         element->getProperty<float>("vnc_desktop_side_length");

      try
      {
         mDesktop = new VNCDesktop(vnc_host, vnc_port, vnc_pass, vnc_size);
         mDesktop->init("VJWand", "VJButton0", "VJButton1", "VJButton2");
         mEmbeddedGUI = true;
      }
      catch (...)
      {
         vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
            << clrOutNORM(clrRED, "ERROR:")
            << " Failed to initialize VNC desktop\n" << vprDEBUG_FLUSH;
         mEmbeddedGUI = false;
      }
   }

   return true;
}

void vncApp::preFrame()
{
   bool we_have_focus(true);

   mFrameCount++;

   vpr::Interval frame_time = vpr::Interval::now() - mPrevFrameStartTime;
   mPrevFrameStartTime.setNow();
   mFrameRate.addSample(frame_time.secf());

   if ( (mFrameCount % 50) == 1 && mEmbeddedGUI )
   {
      mDesktop->printStats();

      // fps = 1.0f / time_per_frame
      float fps = 1.0f / mFrameRate.getSTA();
      std::cout << "fps: " << fps << std::endl;
   }

   // Update embedded VNC desktop
   if ( mEmbeddedGUI )
   {
      VNCDesktop::Focus focus = mDesktop->update(mNavMat);
      we_have_focus = (focus == VNCDesktop::NOT_IN_FOCUS);
   }

   // Put your pre frame computations here.

/*
   std::cout << "Wand Buttons:"
             << " 0: " << mButton0->getData()
             << " 1: " << mButton1->getData()
             << " 2: " << mButton2->getData()
             << " 3: " << mButton3->getData()
             << " 4: " << mButton4->getData()
             << " 5: " << mButton5->getData()
             << std::endl;
*/
   if ( we_have_focus )
   {
      gmtl::Matrix44f wandMatrix;
      wandMatrix = mWand->getData();      // Get the wand matrix

      // Update navigation
      // - Find forward direction of wand
      // - Translate along that direction
      float velocity(0.0f);
      float rotation(0.0f);
      if ( mButton0->getData() )
      {
         velocity = 0.05f;
      }

      if ( mButton1->getData() )
      {
         const float rot_scale(0.01f);
         float y_rot = gmtl::makeYRot<float>(wandMatrix);
         rotation = -1.0f * y_rot * rot_scale;
         gmtl::preMult(mNavMat,
                       gmtl::makeRot<gmtl::Matrix44f>(gmtl::EulerAngleXYZf(0.0f,rotation,0.0f)));
      }

      if ( velocity > 0.0f )
      {
         gmtl::Vec3f Zdir = gmtl::Vec3f(0.0f, 0.0f, velocity);
         gmtl::Vec3f direction(wandMatrix * Zdir);
         gmtl::preMult(mNavMat, gmtl::makeTrans<gmtl::Matrix44f>(direction));
      }

      // Check logger play button
      if ( mLoggerPlayButton->getData() == gadget::Digital::TOGGLE_ON )
      {
         std::cout << "\n\n------ Log Play Button hit ----\n" << std::flush;
         gadget::InputManager* input_mgr = gadget::InputManager::instance();
         gadget::InputLoggerPtr logger = input_mgr->getInputLogger();

         vprASSERT(logger.get() != NULL);
         logger->load("test_logging.xml");
         logger->play();
      }
   }
}

void vncApp::bufferPreDraw()
{
   glClearColor(0.0, 0.0, 0.0, 0.0);
   glClear(GL_COLOR_BUFFER_BIT);
}

void vncApp::contextPreDraw()
{
   if ( mEmbeddedGUI )
   {
      mDesktop->contextPreDraw();
   }
}

//----------------------------------------------
//  Draw the scene.  A box on the end of the wand
//----------------------------------------------

void vncApp::draw()
{
   glClear(GL_DEPTH_BUFFER_BIT);
   //std::cout << "\n--- myDraw() ---\n";

   //std::cout << "HeadPos:" << vrj::Coord(*mHead->getData()).pos << "\t"
   //          << "WandPos:" << vrj::Coord(*mWand->getData()).pos << std::endl;

   glMatrixMode(GL_MODELVIEW);

   // -- Draw box on wand --- //
   gmtl::Matrix44f wand_world = mWand->getData();
   gmtl::Matrix44f wand_mat;
   gmtl::Matrix44f inv_nav;
   gmtl::invert(inv_nav, mNavMat);
   wand_mat = inv_nav * wand_world;

   glPushMatrix();
   {
      // Move world for navigation
      glMultMatrixf(mNavMat.mData);

      // cout << "wand:\n" << *wandMatrix << endl;
      glPushMatrix();
         glMultMatrixf(wand_mat.mData);  // Push the wand matrix on the stack

         float wand_color[3];
         wand_color[0] = wand_color[1] = wand_color[2] = 0.0f;
         if ( mButton0->getData() == gadget::Digital::ON )
         {
            wand_color[0] += 0.5f;
         }

         if ( mButton1->getData() == gadget::Digital::ON )
         {
            wand_color[1] += 0.5f;
         }

         if ( mButton2->getData() == gadget::Digital::ON )
         {
            wand_color[2] += 0.5f;
         }

         if ( mButton3->getData() == gadget::Digital::ON )
         {
            wand_color[0] += 0.5f;
         }

         if ( mButton4->getData() == gadget::Digital::ON )
         {
            wand_color[1] += 0.5f;
         }

         if ( mButton5->getData() == gadget::Digital::ON )
         {
            wand_color[2] += 0.5f;
         }

         glColor3fv(wand_color);
         drawCube();
      glPopMatrix();

      // Draw a floor
      drawFloor();

      // VNC GUI
      if ( mEmbeddedGUI )
      {
         mDesktop->draw();
      }
   }
   glPopMatrix();
}

void vncApp::initGLState()
{
   GLfloat light0_ambient[]  = { 0.2f,  0.2f,  0.2f, 1.0f };
   GLfloat light0_diffuse[]  = { 0.8f,  0.8f,  0.8f, 1.0f };
   GLfloat light0_specular[] = { 1.0f,  1.0f,  1.0f, 1.0f };
   GLfloat light0_position[] = { 0.5f, 0.75f, 0.75f, 0.0f };

   GLfloat mat_ambient[]   = { 0.7f, 0.7f, 0.7f, 1.0f };
   GLfloat mat_diffuse[]   = { 1.0f, 0.5f, 0.8f, 1.0f };
   GLfloat mat_specular[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
   GLfloat mat_shininess[] = { 50.0f };
   GLfloat no_mat[]        = { 0.0f, 0.0f, 0.0f, 1.0f };

   glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
   glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
   glLightfv(GL_LIGHT0, GL_POSITION, light0_position);

   glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
   glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);

   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_COLOR_MATERIAL);
   glShadeModel(GL_SMOOTH);
}

//: Utility function for drawing a cube
void drawbox(GLdouble x0, GLdouble x1, GLdouble y0, GLdouble y1,
             GLdouble z0, GLdouble z1, GLenum type)
{
   static GLdouble n[6][3] =
   {
      { -1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 1.0, 0.0, 0.0 },
      { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 }
   };
   static GLint faces[6][4] =
   {
      { 0, 1, 2, 3 }, { 3, 2, 6, 7 }, { 7, 6, 5, 4 },
      { 4, 5, 1, 0 }, { 5, 6, 2, 1 }, { 7, 4, 0, 3 }
   };
   GLdouble v[8][3], tmp;

   if ( x0 > x1 )
   {
      tmp = x0; x0 = x1; x1 = tmp;
   }
   if ( y0 > y1 )
   {
      tmp = y0; y0 = y1; y1 = tmp;
   }
   if ( z0 > z1 )
   {
      tmp = z0; z0 = z1; z1 = tmp;
   }
   v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0;
   v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1;
   v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0;
   v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1;
   v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0;
   v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1;

   for ( GLint i = 0; i < 6; ++i )
   {
      glBegin(type);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][0]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][1]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][2]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][3]][0]);
      glEnd();
   }
}

void vncApp::drawFloor()
{
   glPolygonMode(GL_FRONT, GL_LINE);
   glLineWidth(2);
   glColor3f(0.30f, 0.87f, 0.22f);     // Nice matrix green

   glBegin(GL_LINES);
      for ( int z = -25; z < 25; ++z )
      {
         for ( int x = -25; x < 25; ++x )
         {
            // Draw the line along the Z axis.
            glVertex3f((float) x, 0.01f, (float) z);
            glVertex3f((float) x, 0.01f, (float) z + 1.0f);

            // Draw the line along the X axis.
            glVertex3f((float) x, 0.01f, (float) z);
            glVertex3f((float) x + 1.0f, 0.01f, (float) z);
         }
      }
   glEnd();

   glPolygonMode(GL_FRONT, GL_FILL);
}

} // namespace vrjvnc

<|endoftext|>


<commit_before>#ifdef WIN32
#include <Io.h>
#include <Windows.h>
#include <Winbase.h>
#elif !defined(macintosh) // UNIX
#include <unistd.h>
#include <dlfcn.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include <osg/Notify>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>

#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>

using namespace osg;
using namespace osgDB;

// follows is definition of strdup for compatibility under mac,
// However, I'd prefer to migrate all the findFindInPath tools to use
// std::string's and then be able to remove the following definition.
// My objective is to minimize the number of platform #ifdef's as they
// are source of potential bugs and developer confusion.
#ifdef macintosh
#ifndef strdup
inline static char* strdup(const char *src);

inline static char* strdup(const char *src)
{
	char *ret = (char *) std::malloc(std::strlen(src) +1);
	if(!ret) return NULL;

	return std::strcpy(ret, src);
}
#endif
#endif

#ifdef WIN32
char *PathDelimitor = ";";
static const char *s_default_file_path = ".;";
static const char *s_default_dso_path = "C:/Windows/System/";
static char *s_filePath = ".;";
#elif macintosh
char *PathDelimitor = " ";
static const char *s_default_file_path = ":";
static const char *s_default_dso_path = ":";
static char *s_filePath = ":";
#elif __sgi
char *PathDelimitor = ":";
static const char *s_default_file_path = ".:";
static const char *s_default_dso_path = "/usr/lib32/osgPlugins/";
static char *s_filePath = ".:";
#else
char *PathDelimitor = ":";
static const char *s_default_file_path = ".:";
static const char *s_default_dso_path = "/usr/lib/osgPlugins/";
static char *s_filePath = ".:";
#endif

static bool s_filePathInitialized = false;

void osgDB::initFilePath( void )
{
    char *ptr;
    if( (ptr = getenv( "OSGFILEPATH" ))  )
    {
        notify(DEBUG_INFO) << "osgDB::Init("<<ptr<<")"<<endl;
        setFilePath( ptr );
    }
    else
    {
        notify(DEBUG_INFO) << "osgDB::Init(NULL)"<<endl;
    }
    s_filePathInitialized = true;
}

void osgDB::setFilePath( const char *_path )
{
    char buff[1024];

    notify(DEBUG_INFO) << "In osgDB::setFilePath("<<_path<<")"<<endl;

    buff[0] = 0;

    if( s_filePath != s_default_file_path )
    {
        strcpy( buff, s_filePath );
    }

    strcat( buff, PathDelimitor );
    strcat( buff, _path );

    s_filePath = strdup( buff );

    s_filePathInitialized = true;
}

const char* osgDB::getFilePath()
{
    return s_filePath;
}

char *osgDB::findFileInPath( const char *_file, const char * filePath )
{
#ifdef macintosh
    return (char *)_file;
#else

    #if defined (WIN32)
        #define F_OK 4
    #endif

    notify(DEBUG_INFO) << "FindFileInPath() : trying " << _file << " ...\n";
    if( access( _file, F_OK ) == 0 ) return (char *)_file;

    char pathbuff[1024];
    char *tptr, *tmppath;
    char *path = 0L;

    tptr    = strdup( filePath );
    tmppath = strtok(  tptr, PathDelimitor );

    do
    {
        sprintf( pathbuff, "%s/%s", tmppath, _file );
        notify(DEBUG_INFO) << "FindFileInPath() : trying " << pathbuff << " ...\n";
        if( access( pathbuff, F_OK ) == 0 ) break;
    } while( (tmppath = strtok( 0, PathDelimitor )) );

    if( tmppath != (char *)0L )
        path = strdup( pathbuff );

    ::free(tptr);

    if (path) notify( DEBUG_INFO ) << "FindFileInPath() : returning " << path << endl;
	else notify( DEBUG_INFO ) << "FindFileInPath() : returning NULL" << endl;
    #ifdef WIN32
        #undef F_OK
    #endif

    return path;
#endif
}

char *osgDB::findFile( const char *file )
{
    if (!file) return NULL;

    if (!s_filePathInitialized) initFilePath();

    char* newFileName = findFileInPath( file, s_filePath );
    if (newFileName) return newFileName;

    // need to check here to see if file has a path on it.

    // now strip the file of an previous path if one exists.
    std::string simpleFileName = getSimpleFileName(file);
    newFileName = findFileInPath( simpleFileName.c_str(), s_filePath );
    return newFileName;
}

/*

Order of precedence for

Under UNIX.

  ./
  OSG_LD_LIBRARY_PATH
  s_default_dso_path
  LD_LIBRARY*_PATH

Under Windows

  ./
  OSG_LD_LIBRARY_PATH
  s_default_dso_path
  PATH

*/

char *osgDB::findDSO( const char *name )
{
#ifndef macintosh
    char path[1024];
    char *ptr;

    strcpy( path, "./" );

    if((ptr = getenv( "OSG_LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    strcat( path, PathDelimitor );
    strcat( path, s_default_dso_path );

    #ifdef __sgi

    // bloody mess see rld(1) man page
    #if (_MIPS_SIM == _MIPS_SIM_ABI32)

    if( (ptr = getenv( "LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #elif (_MIPS_SIM == _MIPS_SIM_NABI32)

    if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
        ptr = getenv( "LD_LIBRARY_PATH" );

    if( ptr )
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #elif (_MIPS_SIM == _MIPS_SIM_ABI64)

    if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
        ptr = getenv( "LD_LIBRARY_PATH" );

    if( ptr )
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }
    #endif

#elif WIN32

    if ((ptr = getenv( "PATH" )))
    {
        notify(DEBUG_INFO) << "PATH = "<<ptr<<endl;
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #else
    if( (ptr = getenv( "LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }
    #endif

    // check existance of dso assembled direct paths.
    char* fileFound = NULL;
    fileFound = findFileInPath( name , path );
    if (fileFound) return fileFound;

    // failed with direct paths,
    // now try prepending the filename with "osgPlugins/"
    char* prependosgPlugins = new char[strlen(name)+12];
    strcpy(prependosgPlugins,"osgPlugins/");
    strcat(prependosgPlugins,name);

    fileFound = findFileInPath( prependosgPlugins , path );
    delete prependosgPlugins;

    return fileFound;
#else // defined macintosh
    return NULL;
#endif
}

std::string osgDB::findFileInDirectory(const std::string& fileName,const std::string& dirName,bool caseInsensitive)
{
    bool needFollowingBackslash = false;
    bool needDirectoryName = true;
    osgDB::DirectoryContents dc;

    if (dirName.empty())
    {
        dc = osgDB::getDirectoryContents(".");
        needFollowingBackslash = false;
        needDirectoryName = false;
    }
    else if (dirName=="." || dirName=="./" || dirName==".\\")
    {
        dc = osgDB::getDirectoryContents(".");
        needFollowingBackslash = false;
        needDirectoryName = false;
    }
    else
    {
        dc = osgDB::getDirectoryContents(dirName);
        char lastChar = dirName[dirName.size()-1];
        if (lastChar=='/') needFollowingBackslash = false;
        else if (lastChar=='\\') needFollowingBackslash = false;
        else needFollowingBackslash = true;
        needDirectoryName = true;
    }

    for(osgDB::DirectoryContents::iterator itr=dc.begin();
        itr!=dc.end();
        ++itr)
    {
        if ((caseInsensitive && osgDB::equalCaseInsensitive(fileName,*itr)) ||
            (fileName==*itr))
        {
            if (!needDirectoryName) return *itr;
            else if (needFollowingBackslash) return dirName+'/'+*itr;
            else return dirName+*itr;
        }
    }
    return "";
}

#ifdef WIN32

#include <io.h>
#include <direct.h>

osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
{
    osgDB::DirectoryContents contents;

    struct _finddata_t data;
    long handle = _findfirst((dirName + "\\*").c_str(), &data);
    if (handle != -1)
    {
        do
        {
            contents.push_back(data.name);
        }
        while (_findnext(handle, &data) != -1);

        _findclose(handle);
    }

    return contents;
}

#elif !defined(macintosh) // UNIX

#include <dirent.h>

osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
{
    osgDB::DirectoryContents contents;

    DIR *handle = opendir(dirName.c_str());
    if (handle)
    {
        dirent *rc;
        while((rc = readdir(handle))!=NULL)
        {
            contents.push_back(rc->d_name);
        }
        closedir(handle);
    }

    return contents;
}
#endif
<commit_msg>Fixed findFileInPath to prepend ./ if file is in local directory.  This was needed by dlopen() for dso's in the current directory<commit_after>#ifdef WIN32
#include <Io.h>
#include <Windows.h>
#include <Winbase.h>
#elif !defined(macintosh) // UNIX
#include <unistd.h>
#include <dlfcn.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include <osg/Notify>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>

#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>

using namespace osg;
using namespace osgDB;

// follows is definition of strdup for compatibility under mac,
// However, I'd prefer to migrate all the findFindInPath tools to use
// std::string's and then be able to remove the following definition.
// My objective is to minimize the number of platform #ifdef's as they
// are source of potential bugs and developer confusion.
#ifdef macintosh
#ifndef strdup
inline static char* strdup(const char *src);

inline static char* strdup(const char *src)
{
	char *ret = (char *) std::malloc(std::strlen(src) +1);
	if(!ret) return NULL;

	return std::strcpy(ret, src);
}
#endif
#endif

#ifdef WIN32
char *PathDelimitor = ";";
static const char *s_default_file_path = ".;";
static const char *s_default_dso_path = "C:/Windows/System/";
static char *s_filePath = ".;";
#elif macintosh
char *PathDelimitor = " ";
static const char *s_default_file_path = ":";
static const char *s_default_dso_path = ":";
static char *s_filePath = ":";
#elif __sgi
char *PathDelimitor = ":";
static const char *s_default_file_path = ".:";
static const char *s_default_dso_path = "/usr/lib32/osgPlugins/";
static char *s_filePath = ".:";
#else
char *PathDelimitor = ":";
static const char *s_default_file_path = ".:";
static const char *s_default_dso_path = "/usr/lib/osgPlugins/";
static char *s_filePath = ".:";
#endif

static bool s_filePathInitialized = false;

void osgDB::initFilePath( void )
{
    char *ptr;
    if( (ptr = getenv( "OSGFILEPATH" ))  )
    {
        notify(DEBUG_INFO) << "osgDB::Init("<<ptr<<")"<<endl;
        setFilePath( ptr );
    }
    else
    {
        notify(DEBUG_INFO) << "osgDB::Init(NULL)"<<endl;
    }
    s_filePathInitialized = true;
}

void osgDB::setFilePath( const char *_path )
{
    char buff[1024];

    notify(DEBUG_INFO) << "In osgDB::setFilePath("<<_path<<")"<<endl;

    buff[0] = 0;

    if( s_filePath != s_default_file_path )
    {
        strcpy( buff, s_filePath );
    }

    strcat( buff, PathDelimitor );
    strcat( buff, _path );

    s_filePath = strdup( buff );

    s_filePathInitialized = true;
}

const char* osgDB::getFilePath()
{
    return s_filePath;
}

char *osgDB::findFileInPath( const char *_file, const char * filePath )
{
#ifdef macintosh
    return (char *)_file;
#else

    #if defined (WIN32)
        #define F_OK 4
    #endif

    char pathbuff[1024];
    char *tptr, *tmppath;
    char *path = 0L;

    notify(DEBUG_INFO) << "FindFileInPath() : trying ./" << _file << " ...\n";
    if( access( _file, F_OK ) == 0 )
    {
        sprintf( pathbuff,"./%s", _file );
        return (char *)strdup(pathbuff);
    }

    tptr    = strdup( filePath );
    tmppath = strtok(  tptr, PathDelimitor );

    do
    {
        sprintf( pathbuff, "%s/%s", tmppath, _file );
        notify(DEBUG_INFO) << "FindFileInPath() : trying " << pathbuff << " ...\n";
        if( access( pathbuff, F_OK ) == 0 ) break;
    } while( (tmppath = strtok( 0, PathDelimitor )) );

    if( tmppath != (char *)0L )
        path = strdup( pathbuff );

    ::free(tptr);

    if (path) notify( DEBUG_INFO ) << "FindFileInPath() : returning " << path << endl;
	else notify( DEBUG_INFO ) << "FindFileInPath() : returning NULL" << endl;
    #ifdef WIN32
        #undef F_OK
    #endif

    return path;
#endif
}

char *osgDB::findFile( const char *file )
{
    if (!file) return NULL;

    if (!s_filePathInitialized) initFilePath();

    char* newFileName = findFileInPath( file, s_filePath );
    if (newFileName) return newFileName;

    // need to check here to see if file has a path on it.

    // now strip the file of an previous path if one exists.
    std::string simpleFileName = getSimpleFileName(file);
    newFileName = findFileInPath( simpleFileName.c_str(), s_filePath );
    return newFileName;
}

/*

Order of precedence for

Under UNIX.

  ./
  OSG_LD_LIBRARY_PATH
  s_default_dso_path
  LD_LIBRARY*_PATH

Under Windows

  ./
  OSG_LD_LIBRARY_PATH
  s_default_dso_path
  PATH

*/

char *osgDB::findDSO( const char *name )
{
#ifndef macintosh
    char path[1024];
    char *ptr;

    strcpy( path, "./" );

    if((ptr = getenv( "OSG_LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    strcat( path, PathDelimitor );
    strcat( path, s_default_dso_path );

    #ifdef __sgi

    // bloody mess see rld(1) man page
    #if (_MIPS_SIM == _MIPS_SIM_ABI32)

    if( (ptr = getenv( "LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #elif (_MIPS_SIM == _MIPS_SIM_NABI32)

    if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
        ptr = getenv( "LD_LIBRARY_PATH" );

    if( ptr )
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #elif (_MIPS_SIM == _MIPS_SIM_ABI64)

    if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
        ptr = getenv( "LD_LIBRARY_PATH" );

    if( ptr )
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }
    #endif

#elif WIN32

    if ((ptr = getenv( "PATH" )))
    {
        notify(DEBUG_INFO) << "PATH = "<<ptr<<endl;
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }

    #else
    if( (ptr = getenv( "LD_LIBRARY_PATH" )))
    {
        strcat( path, PathDelimitor );
        strcat( path, ptr );
    }
    #endif

    // check existance of dso assembled direct paths.
    char* fileFound = NULL;
    fileFound = findFileInPath( name , path );
    if (fileFound) return fileFound;

    // failed with direct paths,
    // now try prepending the filename with "osgPlugins/"
    char* prependosgPlugins = new char[strlen(name)+12];
    strcpy(prependosgPlugins,"osgPlugins/");
    strcat(prependosgPlugins,name);

    fileFound = findFileInPath( prependosgPlugins , path );
    delete prependosgPlugins;

    return fileFound;
#else // defined macintosh
    return NULL;
#endif
}

std::string osgDB::findFileInDirectory(const std::string& fileName,const std::string& dirName,bool caseInsensitive)
{
    bool needFollowingBackslash = false;
    bool needDirectoryName = true;
    osgDB::DirectoryContents dc;

    if (dirName.empty())
    {
        dc = osgDB::getDirectoryContents(".");
        needFollowingBackslash = false;
        needDirectoryName = false;
    }
    else if (dirName=="." || dirName=="./" || dirName==".\\")
    {
        dc = osgDB::getDirectoryContents(".");
        needFollowingBackslash = false;
        needDirectoryName = false;
    }
    else
    {
        dc = osgDB::getDirectoryContents(dirName);
        char lastChar = dirName[dirName.size()-1];
        if (lastChar=='/') needFollowingBackslash = false;
        else if (lastChar=='\\') needFollowingBackslash = false;
        else needFollowingBackslash = true;
        needDirectoryName = true;
    }

    for(osgDB::DirectoryContents::iterator itr=dc.begin();
        itr!=dc.end();
        ++itr)
    {
        if ((caseInsensitive && osgDB::equalCaseInsensitive(fileName,*itr)) ||
            (fileName==*itr))
        {
            if (!needDirectoryName) return *itr;
            else if (needFollowingBackslash) return dirName+'/'+*itr;
            else return dirName+*itr;
        }
    }
    return "";
}

#ifdef WIN32

#include <io.h>
#include <direct.h>

osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
{
    osgDB::DirectoryContents contents;

    struct _finddata_t data;
    long handle = _findfirst((dirName + "\\*").c_str(), &data);
    if (handle != -1)
    {
        do
        {
            contents.push_back(data.name);
        }
        while (_findnext(handle, &data) != -1);

        _findclose(handle);
    }

    return contents;
}

#elif !defined(macintosh) // UNIX

#include <dirent.h>

osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
{
    osgDB::DirectoryContents contents;

    DIR *handle = opendir(dirName.c_str());
    if (handle)
    {
        dirent *rc;
        while((rc = readdir(handle))!=NULL)
        {
            contents.push_back(rc->d_name);
        }
        closedir(handle);
    }

    return contents;
}
#endif
<|endoftext|>


<commit_before>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <malloc.h>

#include "Engine.h"
#include "Tools/File.h"
#include "Tools/Directory.h"
#include "Tools/Error.h"
#ifdef _WIN32
#include "Tools/Win32/getopt.h"
#else
#include <getopt.h>
#endif

#include "Mare.h"
#include "Vcxproj.h"

static const char* VERSION   = "0.3";
static const char* COPYRIGHT = "Copyright (C) 2011 Colin Graf";

static void errorHandler(void* userData, const String& file, int line, const String& message)
{
  if(line < 0)
    fprintf(stderr, "%s: %s\n", (const char*)userData, message.getData());
  else
  {
    if(line > 0)
      fprintf(stderr, "%s:%u: error: %s\n", file.getData(), line, message.getData());
    else
      fprintf(stderr, "%s: %s\n", file.getData(), message.getData());
  }
}

static void showVersion(bool andExit)
{
  printf("mare %s, the lightweight build system\n", VERSION);
  puts(COPYRIGHT);
  //printf("Author(s): %s\n", AUTHORS);
  puts("This program comes with ABSOLUTELY NO WARRANTY.");
  puts("This is free software, and you are welcome to redistribute it under certain");
  puts("conditions.");
  if(andExit)
    exit(EXIT_SUCCESS);
}

static void showUsage(const char* executable)
{
  String basename = File::getBasename(String(executable, -1));
  showVersion(false);
  puts("");
  printf("Usage: %s [ -f <file> ] [ <options> ] [ config=<config> ] [ <target> ]\n", basename.getData());
  puts("        [ platform=<platform> ] [ <variable>=<value> ] [ clean | rebuild ]");
  puts("");
  puts("Options:");
  puts("");
  puts("    -f <file>, --file=<file>");
  puts("        Use <file> as input marefile.");
  puts("        The default value of <file> is \"Marefile\".");
  puts("");
  puts("    --vcxproj[=<version>]");
  puts("        Convert the marefile into a .sln and .vcxproj files for Visual Studio.");
  puts("        <version> can be set to 2010 or 2012.");
  puts("");
  puts("    config=<config>, --config=<config>");
  puts("        Build using configuration <config> as declared in the marefile (Debug");
  puts("        and Release by default). Multiple configurations can be used by adding");
  puts("        config=<config> multiple times.");
  puts("");
  puts("    <target>, target=<target>, --target=<target>");
  puts("        Build <target> as declared in the marefile. Multiple targets can be");
  puts("        used.");
  puts("");
  puts("    platform=<config>, --platform=<config>");
  puts("        Build the target for platform <platform>. By default the native");
  puts("        build platform of the mare executable or the native platform of a");
  puts("        generator (e.g. \"Win32\" for vcxproj) will be used. Multiple platforms");
  puts("        can be used by adding platform=<platform> multiple times. Available");
  puts("        platforms can be declared in the mare.");
  puts("");
  puts("    <variable>=<value>, --<variable>[=<value>]");
  puts("        Set any variable <variable> to <value>. This can be used to set");
  puts("        various options with a meaning defined by the marefile.");
  puts("");
  puts("    clean, --clean");
  puts("        Delete all output files instead of building the selected target.");
  puts("");
  puts("    rebuild, --rebuild");
  puts("        Rebuild all output files of the selected target.");
  puts("");
  puts("    -C <dir>, --directory=<dir>");
  puts("        Change working directory to <dir> before doing anything else.");
  puts("");
  puts("    -d");
  puts("        Print debugging information while processing normally.");
  puts("");
  puts("    -j <jobs>");
  puts("        Use <jobs> processes in parallel for building alle targets. The default");
  puts("        value for <jobs> is the number of processors on the host system.");
  puts("");
  puts("    --ignore-dependencies");
  puts("        Do not respect dependencies between build targets.");
  puts("");
  puts("    -h, --help");
  puts("        Display this help message or a help message declared in the marefile.");
  puts("");
  puts("    -v, --version");
  puts("        Display version information.");
  puts("");
  exit(EXIT_SUCCESS);
}

static void showHelp(const char* executable)
{
  fprintf(stderr, "Type '%s --help' for help\n", executable);
  exit(EXIT_FAILURE);
}

int main(int argc, char* argv[])
{
  Map<String, String> userArgs;
  List<String> inputPlatforms, inputConfigs, inputTargets;
  String inputFile("Marefile"), inputDir;
  bool showHelp = false;
  bool showDebug = false;
  bool clean = false;
  bool rebuild = false;
  bool ignoreDependencies = false;
  int jobs = 0;
  int generateVcxproj = 0;

  // parse args
  {
    int c, option_index;
    static struct option long_options[] = {
      {"file", required_argument , 0, 'f'},
      {"help", no_argument , 0, 'h'},
      {"version", no_argument , 0, 'v'},
      {"directory", required_argument , 0, 'C'},
      {"clean", no_argument , 0, 0},
      {"rebuild", no_argument , 0, 0},
      {"ignore-dependencies", no_argument , 0, 0},
      {"vcxproj", optional_argument , 0, 0},
      {0, 0, 0, 0}
    };

    // find and remove all user arguments
    char** nargv = (char**)alloca(sizeof(char*) * argc);
    int nargc = 1;
    nargv[0] = argv[0];
    for(int i = 1; i < argc; ++i)
    {
      if(strncmp(argv[i], "--", 2) != 0)
        nargv[nargc++] = argv[i];
      else
      {
        const char* arg = argv[i] + 2;
        int arglen = strlen(arg);
        const char* argarg = strchr(arg, '=');
        if(argarg)
          arglen = argarg - arg;
        for(struct option* opt = long_options; opt->name; ++opt)
          if(strncmp(arg, opt->name, arglen) == 0 && strlen(opt->name) == arglen)
          {
            nargv[nargc++] = argv[i];
            goto nextarg;
          }
        const char* sep = strchr(arg, '=');
        if(sep)
        {
          String key(arg, sep - arg);
          String val(sep + 1, -1);
          if(key == "platform")
            inputPlatforms.append(val);
          else if(key == "config")
            inputConfigs.append(val);
          else if(key == "target")
            inputTargets.append(val);
          else
            userArgs.append(key, val);
        }
        else
          userArgs.append(String(arg, -1), String());
      }
    nextarg:;
    }
    argc = nargc;
    argv = nargv;

    // parse normal arguments
    while((c = getopt_long(argc, argv, "C:df:hj:v", long_options, &option_index)) != -1)
      switch(c)
      {
      case 0:
        {
          String opt(long_options[option_index].name, -1);
          if(opt == "vcxproj")
          {
            generateVcxproj = 2010;
            if(optarg)
            {
              if(strcmp(optarg, "2010") == 0)
                generateVcxproj = 2010;
              else if(strcmp(optarg, "2012") == 0)
                generateVcxproj = 2012;
              else // unknown version
                ::showHelp(argv[0]);
            }
          }
          else if(opt == "clean")
            clean = true;
          else if(opt == "rebuild")
            rebuild = true;
          else if(opt == "ignore-dependencies")
            ignoreDependencies = true;
        }
        break;
      case 'C':
        inputDir = String(optarg, -1);
        break;
      case 'd':
        showDebug = true;
        break;
      case 'f':
        inputFile = String(optarg, -1);
        break;
      case 'h':
        showHelp = true;
        break;
      case 'j':
        jobs = atoi(optarg);
        break;
      case 'v':
        showVersion(true);
        break;
      default:
        ::showHelp(argv[0]);
        break;
      }
    while(optind < argc)
    {
      const char* arg = argv[optind++];
      const char* sep = strchr(arg, '=');
      if(sep)
      {
        String key(arg, sep - arg);
        String val(sep + 1, -1);
        if(key == "platform")
          inputPlatforms.append(val);
        else if(key == "config")
          inputConfigs.append(val);
        else if(key == "target")
          inputTargets.append(val);
        else
          userArgs.append(key, val);
      }
      else
      {
        String target(arg, -1);
        if(target == "clean")
          clean = true;
        else if(target == "rebuild")
          rebuild = true;
        else
          inputTargets.append(target);
      }
    }
  }

  // change working directory?
  if(!inputDir.isEmpty())
  {
    if(!Directory::change(inputDir))
    {
      // TODO: error message
      return EXIT_FAILURE;
    }
  }

  // start the engine
  {
    Engine engine(errorHandler, argv[0]);
    if(!engine.load(inputFile))
    {
      if(showHelp)
        showUsage(argv[0]);

      return EXIT_FAILURE;
    }

    // show help only?
    if(showHelp)
    {
      engine.enterRootKey();
      if(engine.enterKey("help"))
      {
        List<String> help;
        engine.getText(help);
        for(List<String>::Node* node = help.getFirst(); node; node = node->getNext())
          puts(node->data.getData());
        return EXIT_SUCCESS;
      }
      else
        showUsage(argv[0]);
    }

    // generate vcxproj mode?
    if(generateVcxproj)
    {
      Vcxproj vcxprog(engine, generateVcxproj);
      if(!vcxprog.generate(userArgs))
        return EXIT_FAILURE;
      return EXIT_SUCCESS;
    }

    // direct build
    {
      Mare mare(engine, inputPlatforms, inputConfigs, inputTargets, showDebug, clean, rebuild, jobs, ignoreDependencies);
      if(!mare.build(userArgs))
        return EXIT_FAILURE;
      return EXIT_SUCCESS;
    }
  }
}
<commit_msg>Add "--codelite" command line argument<commit_after>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <malloc.h>

#include "Engine.h"
#include "Tools/File.h"
#include "Tools/Directory.h"
#include "Tools/Error.h"
#ifdef _WIN32
#include "Tools/Win32/getopt.h"
#else
#include <getopt.h>
#endif

#include "Mare.h"
#include "Vcxproj.h"
#include "CodeLite.h"

static const char* VERSION   = "0.3";
static const char* COPYRIGHT = "Copyright (C) 2011 Colin Graf";

static void errorHandler(void* userData, const String& file, int line, const String& message)
{
  if(line < 0)
    fprintf(stderr, "%s: %s\n", (const char*)userData, message.getData());
  else
  {
    if(line > 0)
      fprintf(stderr, "%s:%u: error: %s\n", file.getData(), line, message.getData());
    else
      fprintf(stderr, "%s: %s\n", file.getData(), message.getData());
  }
}

static void showVersion(bool andExit)
{
  printf("mare %s, the lightweight build system\n", VERSION);
  puts(COPYRIGHT);
  //printf("Author(s): %s\n", AUTHORS);
  puts("This program comes with ABSOLUTELY NO WARRANTY.");
  puts("This is free software, and you are welcome to redistribute it under certain");
  puts("conditions.");
  if(andExit)
    exit(EXIT_SUCCESS);
}

static void showUsage(const char* executable)
{
  String basename = File::getBasename(String(executable, -1));
  showVersion(false);
  puts("");
  printf("Usage: %s [ -f <file> ] [ <options> ] [ config=<config> ] [ <target> ]\n", basename.getData());
  puts("        [ platform=<platform> ] [ <variable>=<value> ] [ clean | rebuild ]");
  puts("");
  puts("Options:");
  puts("");
  puts("    -f <file>, --file=<file>");
  puts("        Use <file> as input marefile.");
  puts("        The default value of <file> is \"Marefile\".");
  puts("");
  puts("    --vcxproj[=<version>]");
  puts("        Convert the marefile into a .sln and .vcxproj files for Visual Studio.");
  puts("        <version> can be set to 2010 or 2012.");
  puts("");
  puts("    --codelite");
  puts("        Generate a .workspace and .project files for CodeLite from the");
  puts("        marefile.");
  puts("");
  puts("    config=<config>, --config=<config>");
  puts("        Build using configuration <config> as declared in the marefile (Debug");
  puts("        and Release by default). Multiple configurations can be used by adding");
  puts("        config=<config> multiple times.");
  puts("");
  puts("    <target>, target=<target>, --target=<target>");
  puts("        Build <target> as declared in the marefile. Multiple targets can be");
  puts("        used.");
  puts("");
  puts("    platform=<config>, --platform=<config>");
  puts("        Build the target for platform <platform>. By default the native");
  puts("        build platform of the mare executable or the native platform of a");
  puts("        generator (e.g. \"Win32\" for vcxproj) will be used. Multiple platforms");
  puts("        can be used by adding platform=<platform> multiple times. Available");
  puts("        platforms can be declared in the mare.");
  puts("");
  puts("    <variable>=<value>, --<variable>[=<value>]");
  puts("        Set any variable <variable> to <value>. This can be used to set");
  puts("        various options with a meaning defined by the marefile.");
  puts("");
  puts("    clean, --clean");
  puts("        Delete all output files instead of building the selected target.");
  puts("");
  puts("    rebuild, --rebuild");
  puts("        Rebuild all output files of the selected target.");
  puts("");
  puts("    -C <dir>, --directory=<dir>");
  puts("        Change working directory to <dir> before doing anything else.");
  puts("");
  puts("    -d");
  puts("        Print debugging information while processing normally.");
  puts("");
  puts("    -j <jobs>");
  puts("        Use <jobs> processes in parallel for building alle targets. The default");
  puts("        value for <jobs> is the number of processors on the host system.");
  puts("");
  puts("    --ignore-dependencies");
  puts("        Do not respect dependencies between build targets.");
  puts("");
  puts("    -h, --help");
  puts("        Display this help message or a help message declared in the marefile.");
  puts("");
  puts("    -v, --version");
  puts("        Display version information.");
  puts("");
  exit(EXIT_SUCCESS);
}

static void showHelp(const char* executable)
{
  fprintf(stderr, "Type '%s --help' for help\n", executable);
  exit(EXIT_FAILURE);
}

int main(int argc, char* argv[])
{
  Map<String, String> userArgs;
  List<String> inputPlatforms, inputConfigs, inputTargets;
  String inputFile("Marefile"), inputDir;
  bool showHelp = false;
  bool showDebug = false;
  bool clean = false;
  bool rebuild = false;
  bool ignoreDependencies = false;
  int jobs = 0;
  int generateVcxproj = 0;
  bool generateCodeLite = false;

  // parse args
  {
    int c, option_index;
    static struct option long_options[] = {
      {"file", required_argument , 0, 'f'},
      {"help", no_argument , 0, 'h'},
      {"version", no_argument , 0, 'v'},
      {"directory", required_argument , 0, 'C'},
      {"clean", no_argument , 0, 0},
      {"rebuild", no_argument , 0, 0},
      {"ignore-dependencies", no_argument , 0, 0},
      {"vcxproj", optional_argument , 0, 0},
      {"codelite", no_argument , 0, 0},
      {0, 0, 0, 0}
    };

    // find and remove all user arguments
    char** nargv = (char**)alloca(sizeof(char*) * argc);
    int nargc = 1;
    nargv[0] = argv[0];
    for(int i = 1; i < argc; ++i)
    {
      if(strncmp(argv[i], "--", 2) != 0)
        nargv[nargc++] = argv[i];
      else
      {
        const char* arg = argv[i] + 2;
        int arglen = strlen(arg);
        const char* argarg = strchr(arg, '=');
        if(argarg)
          arglen = argarg - arg;
        for(struct option* opt = long_options; opt->name; ++opt)
          if(strncmp(arg, opt->name, arglen) == 0 && strlen(opt->name) == arglen)
          {
            nargv[nargc++] = argv[i];
            goto nextarg;
          }
        const char* sep = strchr(arg, '=');
        if(sep)
        {
          String key(arg, sep - arg);
          String val(sep + 1, -1);
          if(key == "platform")
            inputPlatforms.append(val);
          else if(key == "config")
            inputConfigs.append(val);
          else if(key == "target")
            inputTargets.append(val);
          else
            userArgs.append(key, val);
        }
        else
          userArgs.append(String(arg, -1), String());
      }
    nextarg:;
    }
    argc = nargc;
    argv = nargv;

    // parse normal arguments
    while((c = getopt_long(argc, argv, "C:df:hj:v", long_options, &option_index)) != -1)
      switch(c)
      {
      case 0:
        {
          String opt(long_options[option_index].name, -1);
          if(opt == "vcxproj")
          {
            generateVcxproj = 2010;
            if(optarg)
            {
              if(strcmp(optarg, "2010") == 0)
                generateVcxproj = 2010;
              else if(strcmp(optarg, "2012") == 0)
                generateVcxproj = 2012;
              else // unknown version
                ::showHelp(argv[0]);
            }
          }
          else if(opt == "codelite")
            generateCodeLite = true;
          else if(opt == "clean")
            clean = true;
          else if(opt == "rebuild")
            rebuild = true;
          else if(opt == "ignore-dependencies")
            ignoreDependencies = true;
        }
        break;
      case 'C':
        inputDir = String(optarg, -1);
        break;
      case 'd':
        showDebug = true;
        break;
      case 'f':
        inputFile = String(optarg, -1);
        break;
      case 'h':
        showHelp = true;
        break;
      case 'j':
        jobs = atoi(optarg);
        break;
      case 'v':
        showVersion(true);
        break;
      default:
        ::showHelp(argv[0]);
        break;
      }
    while(optind < argc)
    {
      const char* arg = argv[optind++];
      const char* sep = strchr(arg, '=');
      if(sep)
      {
        String key(arg, sep - arg);
        String val(sep + 1, -1);
        if(key == "platform")
          inputPlatforms.append(val);
        else if(key == "config")
          inputConfigs.append(val);
        else if(key == "target")
          inputTargets.append(val);
        else
          userArgs.append(key, val);
      }
      else
      {
        String target(arg, -1);
        if(target == "clean")
          clean = true;
        else if(target == "rebuild")
          rebuild = true;
        else
          inputTargets.append(target);
      }
    }
  }

  // change working directory?
  if(!inputDir.isEmpty())
  {
    if(!Directory::change(inputDir))
    {
      // TODO: error message
      return EXIT_FAILURE;
    }
  }

  // start the engine
  {
    Engine engine(errorHandler, argv[0]);
    if(!engine.load(inputFile))
    {
      if(showHelp)
        showUsage(argv[0]);

      return EXIT_FAILURE;
    }

    // show help only?
    if(showHelp)
    {
      engine.enterRootKey();
      if(engine.enterKey("help"))
      {
        List<String> help;
        engine.getText(help);
        for(List<String>::Node* node = help.getFirst(); node; node = node->getNext())
          puts(node->data.getData());
        return EXIT_SUCCESS;
      }
      else
        showUsage(argv[0]);
    }

    // generate vcxproj mode?
    if(generateVcxproj)
    {
      Vcxproj vcxprog(engine, generateVcxproj);
      if(!vcxprog.generate(userArgs))
        return EXIT_FAILURE;
      return EXIT_SUCCESS;
    }

    // generate codelite mode?
    if(generateCodeLite)
    {
      CodeLite codeLite(engine);
      if(!codeLite.generate(userArgs))
        return EXIT_FAILURE;
      return EXIT_SUCCESS;
    }

    // direct build
    {
      Mare mare(engine, inputPlatforms, inputConfigs, inputTargets, showDebug, clean, rebuild, jobs, ignoreDependencies);
      if(!mare.build(userArgs))
        return EXIT_FAILURE;
      return EXIT_SUCCESS;
    }
  }
}
<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2016 Vaclav Slavik
 *
 *  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 "http_client.h"

#include "version.h"
#include "str_helpers.h"

#include <boost/algorithm/string/predicate.hpp>

#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>

#include <cpprest/asyncrt_utils.h>
#include <cpprest/http_client.h>
#include <cpprest/http_msg.h>
#include <cpprest/filestream.h>

#ifdef _WIN32
    #include <windows.h>
    #include <wininet.h>
    #include <netlistmgr.h>
    #pragma comment(lib, "ole32.lib")
#endif

using namespace web;

namespace
{

using utility::string_t;
using utility::conversions::to_string_t;

#ifndef _UTF16_STRINGS
inline string_t to_string_t(const std::wstring& s) { return str::to_utf8(s); }
#endif

class gzip_compression_support : public http::http_pipeline_stage
{
public:
    pplx::task<http::http_response> propagate(http::http_request request) override
    {
        request.headers().add(http::header_names::accept_encoding, L"gzip");

        return next_stage()->propagate(request).then([](http::http_response response) -> pplx::task<http::http_response>
        {
            string_t encoding;
            if (response.headers().match(http::header_names::content_encoding, encoding) && encoding == _XPLATSTR("gzip"))
            {
                return response.extract_vector().then([response](std::vector<unsigned char> compressed) mutable -> http::http_response
                {
                    namespace io = boost::iostreams;

                    io::array_source source(reinterpret_cast<char*>(compressed.data()), compressed.size());
                    io::filtering_istream in;
                    in.push(io::gzip_decompressor());
                    in.push(source);

                    std::vector<char> decompressed;
                    io::back_insert_device<std::vector<char>> sink(decompressed);
                    io::copy(in, sink);

                    response.set_body(concurrency::streams::bytestream::open_istream(std::move(decompressed)), decompressed.size());
                    return response;
                });
            }
            else
            {
                return pplx::task_from_result(response);
            }
        });
    }
};

} // anonymous namespace

struct json_dict::native
{
    native(const web::json::value& x) : val(x) {}

    web::json::value val;

    const web::json::value& get(const std::string& key)
    {
        return val.at(to_string_t(key));
    }
};

static inline json_dict make_json_dict(const web::json::value& x)
{
    return std::make_shared<json_dict::native>(x);
}

bool json_dict::is_null(const char *name) const
{
    return m_native->get(name).is_null();
}

json_dict json_dict::subdict(const char *name) const
{
    return make_json_dict(m_native->get(name));
}

std::string json_dict::utf8_string(const char *name) const
{
    return str::to_utf8(m_native->get(name).as_string());
}

std::wstring json_dict::wstring(const char *name) const
{
    return str::to_wstring(m_native->get(name).as_string());
}

int json_dict::number(const char *name) const
{
    return m_native->get(name).as_integer();
}

double json_dict::double_number(const char *name) const
{
    return m_native->get(name).as_double();
}

void json_dict::iterate_array(const char *name, std::function<void(const json_dict&)> on_item) const
{
    auto& val = m_native->get(name);
    if (!val.is_array())
        return;
    auto size = val.size();
    for (size_t i = 0; i < size; ++i)
    {
        on_item(make_json_dict(val.at(i)));
    }
}

class http_client::impl
{
public:
    impl(http_client& owner, const std::string& url_prefix, int flags)
        : m_owner(owner), m_native(sanitize_url(url_prefix, flags))
    {
        #define make_wide_str(x) make_wide_str_(x)
        #define make_wide_str_(x) L ## x

        #if defined(_WIN32)
            #define USER_AGENT_PLATFORM L" (Windows NT " + windows_version() + L")"
        #elif defined(__unix__)
            #define USER_AGENT_PLATFORM L" (Unix)"
        #else
            #define USER_AGENT_PLATFORM
        #endif
        m_userAgent = L"Poedit/" make_wide_str(POEDIT_VERSION) USER_AGENT_PLATFORM;

        std::shared_ptr<http::http_pipeline_stage> gzip_stage = std::make_shared<gzip_compression_support>();
        m_native.add_handler(gzip_stage);

    #ifdef _WIN32
        m_networkListManager = nullptr;
        CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_INetworkListManager, (LPVOID *)&m_networkListManager);
    #endif
    }

    ~impl()
    {
    #ifdef _WIN32
        if (m_networkListManager)
            m_networkListManager->Release();
    #endif
    }

    bool is_reachable() const
    {
    #ifdef _WIN32
        if (m_networkListManager)
        {
            NLM_CONNECTIVITY result;
            HRESULT hr = m_networkListManager->GetConnectivity(&result);
            if (SUCCEEDED(hr))
                return result & (NLM_CONNECTIVITY_IPV4_INTERNET|NLM_CONNECTIVITY_IPV6_INTERNET);
        }
        // XP or manager fallback (IPv6 ignorant):
        DWORD flags;
        return ::InternetGetConnectedState(&flags, 0);
    #else
        return true; // TODO
    #endif
    }

    void set_authorization(const std::string& auth)
    {
        m_auth = std::wstring(auth.begin(), auth.end());
    }

    void get(const std::string& url,
             std::function<void(const http_response&)> handler)
    {
        http::http_request req(http::methods::GET);
        req.headers().add(http::header_names::accept,     L"application/json");
        req.headers().add(http::header_names::user_agent, m_userAgent);
        if (!m_auth.empty())
            req.headers().add(http::header_names::authorization, m_auth);
        req.set_request_uri(to_string_t(url));

        m_native.request(req)
        .then([=](http::http_response response)
        {
            handle_error(response);
            handler(make_json_dict(response.extract_json().get()));
        })
        .then([=](pplx::task<void> t)
        {
            try { t.get(); }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

    void download(const std::string& url, const std::wstring& output_file, response_func_t handler)
    {
        using namespace concurrency::streams;
        auto fileStream = std::make_shared<ostream>();

        fstream::open_ostream(to_string_t(output_file)).then([=](ostream outFile)
        {
            *fileStream = outFile;
            http::http_request req(http::methods::GET);
            req.headers().add(http::header_names::user_agent, m_userAgent);
            if (!m_auth.empty())
                req.headers().add(http::header_names::authorization, m_auth);
            req.set_request_uri(to_string_t(url));
            return m_native.request(req);
        })
        .then([=](http::http_response response)
        {
            handle_error(response);
            return response.body().read_to_end(fileStream->streambuf());
        })
        .then([=](size_t)
        {
            return fileStream->close();
        })
        .then([=](pplx::task<void> t)
        {
            try
            {
                t.get();
                handler(json_dict());
            }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

    void post(const std::string& url, const http_body_data& data, response_func_t handler)
    {
        http::http_request req(http::methods::POST);
        req.headers().add(http::header_names::accept,     L"application/json");
        req.headers().add(http::header_names::user_agent, m_userAgent);
        if (!m_auth.empty())
            req.headers().add(http::header_names::authorization, m_auth);
        req.set_request_uri(to_string_t(url));

        auto body = data.body();
        req.set_body(body, data.content_type());
        req.headers().set_content_length(body.size());

        m_native.request(req)
        .then([=](http::http_response response)
        {
            handle_error(response);
            handler(make_json_dict(response.extract_json().get()));
        })
        .then([=](pplx::task<void> t)
        {
            try { t.get(); }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

private:
    // handle non-OK responses:
    void handle_error(http::http_response r)
    {
        if (r.status_code() == http::status_codes::OK)
            return; // not an error

        int status_code = r.status_code();
        std::string msg;
        if (r.headers().content_type() == L"application/json")
        {
            try
            {
                auto json = make_json_dict(r.extract_json().get());
                msg = m_owner.parse_json_error(json);
            }
            catch (...) {} // report original error if parsing broken
        }
        if (msg.empty())
            msg = str::to_utf8(r.reason_phrase());
        m_owner.on_error_response(status_code, msg);
        throw http::http_exception(status_code, msg);
    }

    // convert to wstring and make WinXP ready
    static string_t sanitize_url(const std::string& url, int flags)
    {
        (void)flags;
    #ifdef _WIN32
        if (flags & http_client::uses_sni)
        {
            // Windows XP doesn't support SNI and so can't connect over SSL to
            // hosts that use it. The use of SNI is increasingly common and some
            // APIs Poedit needs to connect to use it. To keep things simple, just
            // disable SSL on Windows XP.
            if (is_windows_xp())
            {
                if (boost::starts_with(url, "https://"))
                    return U("http://") + string_t(url.begin() + 8, url.end());
            }
        }
    #endif
        return to_string_t(url);
    }

private:
#ifdef _WIN32
    static bool is_windows_xp()
    {
        OSVERSIONINFOEX info;
        ZeroMemory(&info, sizeof(info));
        info.dwOSVersionInfoSize = sizeof(info);
        GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
        return (info.dwMajorVersion < 6); // XP
    }

    static std::wstring windows_version()
    {
        OSVERSIONINFOEX info;
        ZeroMemory(&info, sizeof(info));
        info.dwOSVersionInfoSize = sizeof(info);
        GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
        return std::to_wstring(info.dwMajorVersion) + L"." + std::to_wstring(info.dwMinorVersion);
    }

    INetworkListManager *m_networkListManager;
#endif

    http_client& m_owner;
    http::client::http_client m_native;
    std::wstring m_userAgent;
    std::wstring m_auth;
};

http_client::http_client(const std::string& url_prefix, int flags)
    : m_impl(new impl(*this, url_prefix, flags))
{
}

http_client::~http_client()
{
}

bool http_client::is_reachable() const
{
    return m_impl->is_reachable();
}

void http_client::set_authorization(const std::string& auth)
{
    m_impl->set_authorization(auth);
}

void http_client::get(const std::string& url,
                      std::function<void(const http_response&)> handler)
{
    m_impl->get(url, handler);
}

void http_client::download(const std::string& url, const std::wstring& output_file, response_func_t handler)
{
    m_impl->download(url, output_file, handler);
}

void http_client::post(const std::string& url, const http_body_data& data, response_func_t handler)
{
    m_impl->post(url, data, handler);
}
<commit_msg>Use MSIE’s proxy settings on Windows<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2016 Vaclav Slavik
 *
 *  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 "http_client.h"

#include "version.h"
#include "str_helpers.h"

#include <boost/algorithm/string/predicate.hpp>

#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>

#define CPPREST_TARGET_XP
#include <cpprest/asyncrt_utils.h>
#include <cpprest/http_client.h>
#include <cpprest/http_msg.h>
#include <cpprest/filestream.h>

#ifdef _WIN32
    #include <windows.h>
    #include <winhttp.h>
    #include <netlistmgr.h>
    #pragma comment(lib, "ole32.lib")

    // can't include both winhttp.h and wininet.h, so put a declaration here
    //#include <wininet.h>
    EXTERN_C DECLSPEC_IMPORT BOOL STDAPICALLTYPE InternetGetConnectedState(__out LPDWORD lpdwFlags, __reserved DWORD dwReserved);
#endif

using namespace web;

namespace
{

using utility::string_t;
using utility::conversions::to_string_t;

#ifndef _UTF16_STRINGS
inline string_t to_string_t(const std::wstring& s) { return str::to_utf8(s); }
#endif

class gzip_compression_support : public http::http_pipeline_stage
{
public:
    pplx::task<http::http_response> propagate(http::http_request request) override
    {
        request.headers().add(http::header_names::accept_encoding, L"gzip");

        return next_stage()->propagate(request).then([](http::http_response response) -> pplx::task<http::http_response>
        {
            string_t encoding;
            if (response.headers().match(http::header_names::content_encoding, encoding) && encoding == _XPLATSTR("gzip"))
            {
                return response.extract_vector().then([response](std::vector<unsigned char> compressed) mutable -> http::http_response
                {
                    namespace io = boost::iostreams;

                    io::array_source source(reinterpret_cast<char*>(compressed.data()), compressed.size());
                    io::filtering_istream in;
                    in.push(io::gzip_decompressor());
                    in.push(source);

                    std::vector<char> decompressed;
                    io::back_insert_device<std::vector<char>> sink(decompressed);
                    io::copy(in, sink);

                    response.set_body(concurrency::streams::bytestream::open_istream(std::move(decompressed)), decompressed.size());
                    return response;
                });
            }
            else
            {
                return pplx::task_from_result(response);
            }
        });
    }
};

} // anonymous namespace

struct json_dict::native
{
    native(const web::json::value& x) : val(x) {}

    web::json::value val;

    const web::json::value& get(const std::string& key)
    {
        return val.at(to_string_t(key));
    }
};

static inline json_dict make_json_dict(const web::json::value& x)
{
    return std::make_shared<json_dict::native>(x);
}

bool json_dict::is_null(const char *name) const
{
    return m_native->get(name).is_null();
}

json_dict json_dict::subdict(const char *name) const
{
    return make_json_dict(m_native->get(name));
}

std::string json_dict::utf8_string(const char *name) const
{
    return str::to_utf8(m_native->get(name).as_string());
}

std::wstring json_dict::wstring(const char *name) const
{
    return str::to_wstring(m_native->get(name).as_string());
}

int json_dict::number(const char *name) const
{
    return m_native->get(name).as_integer();
}

double json_dict::double_number(const char *name) const
{
    return m_native->get(name).as_double();
}

void json_dict::iterate_array(const char *name, std::function<void(const json_dict&)> on_item) const
{
    auto& val = m_native->get(name);
    if (!val.is_array())
        return;
    auto size = val.size();
    for (size_t i = 0; i < size; ++i)
    {
        on_item(make_json_dict(val.at(i)));
    }
}

class http_client::impl
{
public:
    impl(http_client& owner, const std::string& url_prefix, int flags)
        : m_owner(owner), m_native(sanitize_url(url_prefix, flags), get_client_config())
    {
        #define make_wide_str(x) make_wide_str_(x)
        #define make_wide_str_(x) L ## x

        #if defined(_WIN32)
            #define USER_AGENT_PLATFORM L" (Windows NT " + windows_version() + L")"
        #elif defined(__unix__)
            #define USER_AGENT_PLATFORM L" (Unix)"
        #else
            #define USER_AGENT_PLATFORM
        #endif
        m_userAgent = L"Poedit/" make_wide_str(POEDIT_VERSION) USER_AGENT_PLATFORM;

        std::shared_ptr<http::http_pipeline_stage> gzip_stage = std::make_shared<gzip_compression_support>();
        m_native.add_handler(gzip_stage);

    #ifdef _WIN32
        m_networkListManager = nullptr;
        CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_INetworkListManager, (LPVOID *)&m_networkListManager);
    #endif
    }

    ~impl()
    {
    #ifdef _WIN32
        if (m_networkListManager)
            m_networkListManager->Release();
    #endif
    }

    bool is_reachable() const
    {
    #ifdef _WIN32
        if (m_networkListManager)
        {
            NLM_CONNECTIVITY result;
            HRESULT hr = m_networkListManager->GetConnectivity(&result);
            if (SUCCEEDED(hr))
                return result & (NLM_CONNECTIVITY_IPV4_INTERNET|NLM_CONNECTIVITY_IPV6_INTERNET);
        }
        // XP or manager fallback (IPv6 ignorant):
        DWORD flags;
        return ::InternetGetConnectedState(&flags, 0);
    #else
        return true; // TODO
    #endif
    }

    void set_authorization(const std::string& auth)
    {
        m_auth = std::wstring(auth.begin(), auth.end());
    }

    void get(const std::string& url,
             std::function<void(const http_response&)> handler)
    {
        http::http_request req(http::methods::GET);
        req.headers().add(http::header_names::accept,     L"application/json");
        req.headers().add(http::header_names::user_agent, m_userAgent);
        if (!m_auth.empty())
            req.headers().add(http::header_names::authorization, m_auth);
        req.set_request_uri(to_string_t(url));

        m_native.request(req)
        .then([=](http::http_response response)
        {
            handle_error(response);
            handler(make_json_dict(response.extract_json().get()));
        })
        .then([=](pplx::task<void> t)
        {
            try { t.get(); }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

    void download(const std::string& url, const std::wstring& output_file, response_func_t handler)
    {
        using namespace concurrency::streams;
        auto fileStream = std::make_shared<ostream>();

        fstream::open_ostream(to_string_t(output_file)).then([=](ostream outFile)
        {
            *fileStream = outFile;
            http::http_request req(http::methods::GET);
            req.headers().add(http::header_names::user_agent, m_userAgent);
            if (!m_auth.empty())
                req.headers().add(http::header_names::authorization, m_auth);
            req.set_request_uri(to_string_t(url));
            return m_native.request(req);
        })
        .then([=](http::http_response response)
        {
            handle_error(response);
            return response.body().read_to_end(fileStream->streambuf());
        })
        .then([=](size_t)
        {
            return fileStream->close();
        })
        .then([=](pplx::task<void> t)
        {
            try
            {
                t.get();
                handler(json_dict());
            }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

    void post(const std::string& url, const http_body_data& data, response_func_t handler)
    {
        http::http_request req(http::methods::POST);
        req.headers().add(http::header_names::accept,     L"application/json");
        req.headers().add(http::header_names::user_agent, m_userAgent);
        if (!m_auth.empty())
            req.headers().add(http::header_names::authorization, m_auth);
        req.set_request_uri(to_string_t(url));

        auto body = data.body();
        req.set_body(body, data.content_type());
        req.headers().set_content_length(body.size());

        m_native.request(req)
        .then([=](http::http_response response)
        {
            handle_error(response);
            handler(make_json_dict(response.extract_json().get()));
        })
        .then([=](pplx::task<void> t)
        {
            try { t.get(); }
            catch (...)
            {
                handler(std::current_exception());
            }
        });
    }

private:
    // handle non-OK responses:
    void handle_error(http::http_response r)
    {
        if (r.status_code() == http::status_codes::OK)
            return; // not an error

        int status_code = r.status_code();
        std::string msg;
        if (r.headers().content_type() == L"application/json")
        {
            try
            {
                auto json = make_json_dict(r.extract_json().get());
                msg = m_owner.parse_json_error(json);
            }
            catch (...) {} // report original error if parsing broken
        }
        if (msg.empty())
            msg = str::to_utf8(r.reason_phrase());
        m_owner.on_error_response(status_code, msg);
        throw http::http_exception(status_code, msg);
    }

    // convert to wstring and make WinXP ready
    static string_t sanitize_url(const std::string& url, int flags)
    {
        (void)flags;
    #ifdef _WIN32
        if (flags & http_client::uses_sni)
        {
            // Windows XP doesn't support SNI and so can't connect over SSL to
            // hosts that use it. The use of SNI is increasingly common and some
            // APIs Poedit needs to connect to use it. To keep things simple, just
            // disable SSL on Windows XP.
            if (is_windows_xp())
            {
                if (boost::starts_with(url, "https://"))
                    return U("http://") + string_t(url.begin() + 8, url.end());
            }
        }
    #endif
        return to_string_t(url);
    }

    // prepare WinHttp configuration
    static http::client::http_client_config get_client_config()
    {
        http::client::http_client_config c;
    #ifdef _WIN32
        // WinHttp doesn't share WinInet/MSIE's proxy settings and has its own,
        // but many users don't have properly configured both. Adopting proxy
        // settings like this in desktop software is recommended behavior, see
        // https ://blogs.msdn.microsoft.com/ieinternals/2013/10/11/understanding-web-proxy-configuration/
        WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieConfig = { 0 };
        if (WinHttpGetIEProxyConfigForCurrentUser(&ieConfig))
        {
            if (ieConfig.fAutoDetect)
            {
                c.set_proxy(web_proxy::use_auto_discovery);
            }
            if (ieConfig.lpszProxy)
            {
                // Explicitly add // to the URL to work around a bug in C++ REST SDK's
                // parsing of proxies with port number in their address
                // (see https://github.com/Microsoft/cpprestsdk/issues/57)
                c.set_proxy(uri(L"//" + std::wstring(ieConfig.lpszProxy)));
            }
        }
    #endif
        return c;
    }

private:
#ifdef _WIN32
    static bool is_windows_xp()
    {
        OSVERSIONINFOEX info;
        ZeroMemory(&info, sizeof(info));
        info.dwOSVersionInfoSize = sizeof(info);
        GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
        return (info.dwMajorVersion < 6); // XP
    }

    static std::wstring windows_version()
    {
        OSVERSIONINFOEX info;
        ZeroMemory(&info, sizeof(info));
        info.dwOSVersionInfoSize = sizeof(info);
        GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
        return std::to_wstring(info.dwMajorVersion) + L"." + std::to_wstring(info.dwMinorVersion);
    }

    INetworkListManager *m_networkListManager;
#endif

    http_client& m_owner;
    http::client::http_client m_native;
    std::wstring m_userAgent;
    std::wstring m_auth;
};

http_client::http_client(const std::string& url_prefix, int flags)
    : m_impl(new impl(*this, url_prefix, flags))
{
}

http_client::~http_client()
{
}

bool http_client::is_reachable() const
{
    return m_impl->is_reachable();
}

void http_client::set_authorization(const std::string& auth)
{
    m_impl->set_authorization(auth);
}

void http_client::get(const std::string& url,
                      std::function<void(const http_response&)> handler)
{
    m_impl->get(url, handler);
}

void http_client::download(const std::string& url, const std::wstring& output_file, response_func_t handler)
{
    m_impl->download(url, output_file, handler);
}

void http_client::post(const std::string& url, const http_body_data& data, response_func_t handler)
{
    m_impl->post(url, data, handler);
}
<|endoftext|>