got rid of all the absolute paths in the fitpc code

Previously, there were 2 places (BinaryLogReader and the HTTP file server) that
had "/home/driver/" hard coded. I changed both of those to use paths relative to
the location of the executable (retrieved from /proc/self/exe).

git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4162 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/aos/atom_code/core/BinaryLogReader.cpp b/aos/atom_code/core/BinaryLogReader.cpp
index 703a219..b673c26 100644
--- a/aos/atom_code/core/BinaryLogReader.cpp
+++ b/aos/atom_code/core/BinaryLogReader.cpp
@@ -13,25 +13,16 @@
 
 #include "aos/aos_core.h"
 #include "aos/atom_code/core/LogFileCommon.h"
+#include "aos/common/Configuration.h"
 
 static const char *const kCRIOName = "CRIO";
 
 int main() {
   aos::InitNRT();
 
-  char *folder_tmp;
-  if (asprintf(&folder_tmp, "%s/tmp/robot_logs", getpwuid(getuid())->pw_dir) == -1) {
-    LOG(ERROR, "couldn't figure out what folder to use because of %d (%s)\n",
-        errno, strerror(errno));
-    return EXIT_FAILURE;
-  }
-  std::string hack("/home/driver/tmp/robot_logs"); // TODO(brians) remove this hack
-  const char *folder = hack.c_str();
+  const char *folder = aos::configuration::GetLoggingDirectory();
   if (access(folder, R_OK | W_OK) == -1) {
-    fprintf(stderr,
-            "LogReader: error: folder '%s' does not exist. please create it\n",
-            folder);
-    return EXIT_FAILURE;
+    LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
   }
   LOG(INFO, "logging to folder '%s'\n", folder);
 
diff --git a/aos/atom_code/output/ctemplate_cache.cc b/aos/atom_code/output/ctemplate_cache.cc
new file mode 100644
index 0000000..a73e4ad
--- /dev/null
+++ b/aos/atom_code/output/ctemplate_cache.cc
@@ -0,0 +1,24 @@
+#include "aos/atom_code/output/ctemplate_cache.h"
+
+#include "aos/common/Configuration.h"
+#include "aos/common/once.h"
+
+namespace aos {
+namespace http {
+
+namespace {
+ctemplate::TemplateCache *CreateTemplateCache() {
+  ctemplate::TemplateCache *r = new ctemplate::TemplateCache();
+
+  r->SetTemplateRootDirectory(configuration::GetRootDirectory());
+
+  return r;
+}
+}  // namespace
+ctemplate::TemplateCache *get_template_cache() {
+  static Once<ctemplate::TemplateCache> once(CreateTemplateCache);
+  return once.Get();
+}
+
+}  // namespace http
+}  // namespace aos
diff --git a/aos/atom_code/output/ctemplate_cache.h b/aos/atom_code/output/ctemplate_cache.h
new file mode 100644
index 0000000..7e5dc3d
--- /dev/null
+++ b/aos/atom_code/output/ctemplate_cache.h
@@ -0,0 +1,12 @@
+#include "ctemplate/template_cache.h"
+
+namespace aos {
+namespace http {
+
+// Retrieves the cache used by all of the aos functions etc.
+// This cache will have its root directory set to the directory where the
+// executable is running from.
+ctemplate::TemplateCache *get_template_cache();
+
+}  // namespace http
+}  // namespace aos
diff --git a/aos/atom_code/output/output.gyp b/aos/atom_code/output/output.gyp
index 3e4abd4..d41fe7d 100644
--- a/aos/atom_code/output/output.gyp
+++ b/aos/atom_code/output/output.gyp
@@ -6,11 +6,13 @@
       'sources': [
         'HTTPServer.cpp',
         'evhttp_ctemplate_emitter.cc',
+        'ctemplate_cache.cc',
       ],
       'dependencies': [
         '<(AOS)/build/aos.gyp:libaos',
         '<(EXTERNALS):libevent',
         '<(EXTERNALS):ctemplate',
+        '<(AOS)/common/common.gyp:once',
       ],
       'export_dependent_settings': [
 # Our headers #include headers from both of these.
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index a65909d..d235f14 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -21,7 +21,7 @@
         #'../common/messages/messages.gyp:*', # TODO(brians) did this test ever exist?
         '../atom_code/logging/logging.gyp:*',
         '../common/common.gyp:die_test',
-        ':Common',
+        'Common',
       ],
     },
     {
@@ -29,7 +29,7 @@
       'type': 'none',
       'dependencies': [
         '../crio/googletest/googletest.gyp:*',
-        ':Common',
+        'Common',
       ],
     },
     {
@@ -42,6 +42,7 @@
         '<(AOS)/common/common.gyp:type_traits_test',
         '<(AOS)/common/common.gyp:time_test',
         '<(AOS)/common/common.gyp:mutex_test',
+        '<(AOS)/common/common.gyp:once_test',
       ],
     },
   ],
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index 2ed0478..796fdeb 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -81,11 +81,30 @@
       },
     },
     {
+# Dependents should only use the "gtest/gtest_prod.h" header.
+# This target is NOT the correct one for "aos/common/gtest_prod.h". That one is
+#   aos/common/common.gyp:gtest_prod. This target just deals with setting up to
+#   use the gtest header.
+      'target_name': 'gtest_prod',
+      'type': 'static_library',
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(externals)/gtest-<(gtest_version)/include'
+        ],
+      },
+    },
+    {
       'target_name': 'gtest',
       'type': 'static_library',
       'sources': [
         '<(externals)/gtest-<(gtest_version)/fused-src/gtest/gtest-all.cc',
       ],
+      'dependencies': [
+        'gtest_prod',
+      ],
+      'export_dependent_settings': [
+        'gtest_prod',
+      ],
       'conditions': [['OS=="crio"', {
             'defines': [
               'GTEST_HAS_TR1_TUPLE=0',
@@ -104,10 +123,6 @@
             '<(externals)/gtest-<(gtest_version)/fused-src/gtest/gtest_main.cc',
           ],
         }]],
-      'include_dirs': [
-        '<(externals)/gtest-<(gtest_version)',
-        '<(externals)/gtest-<(gtest_version)/include'
-      ],
       'cflags!': ['-Werror'],
       'direct_dependent_settings': {
         'include_dirs': ['<(externals)/gtest-<(gtest_version)/include'],
diff --git a/aos/common/Configuration.cpp b/aos/common/Configuration.cpp
index e0b56ea..90de05d 100644
--- a/aos/common/Configuration.cpp
+++ b/aos/common/Configuration.cpp
@@ -19,6 +19,7 @@
 #ifndef __VXWORKS__
 #include "aos/common/unique_malloc_ptr.h"
 #endif
+#include "aos/common/once.h"
 
 namespace aos {
 namespace configuration {
@@ -146,5 +147,59 @@
   return NULL;
 }
 
+namespace {
+const char *DoGetRootDirectory() {
+#ifdef __VXWORKS__
+  return "/";
+#else
+  ssize_t size = 0;
+  char *r = NULL;
+  while (true) {
+    if (r != NULL) delete r;
+    size += 256;
+    r = new char[size];
+
+    ssize_t ret = readlink("/proc/self/exe", r, size);
+    if (ret < 0) {
+      if (ret != -1) {
+        LOG(WARNING, "it returned %zd, not -1\n", ret);
+      }
+      LOG(FATAL, "readlink(\"/proc/self/exe\", %p, %zu) failed with %d: %s\n",
+          r, size, errno, strerror(errno));
+    }
+    if (ret < size) {
+      void *last_slash = memrchr(r, '/', size);
+      if (last_slash == NULL) {
+        r[ret] = '\0';
+        LOG(FATAL, "couldn't find a '/' in \"%s\"\n", r);
+      }
+      *static_cast<char *>(last_slash) = '\0';
+      LOG(INFO, "got a root dir of \"%s\"\n", r);
+      return r;
+    }
+  }
+#endif
+}
+
+const char *DoGetLoggingDirectory() {
+  static const char kSuffix[] = "/../../tmp/robot_logs";
+  const char *root = GetRootDirectory();
+  char *r = new char[strlen(root) + sizeof(kSuffix)];
+  strcpy(r, root);
+  strcat(r, kSuffix);
+  return r;
+}
+}  // namespace
+
+const char *GetRootDirectory() {
+  static aos::Once<const char> once(DoGetRootDirectory);
+  return once.Get();
+}
+
+const char *GetLoggingDirectory() {
+  static aos::Once<const char> once(DoGetLoggingDirectory);
+  return once.Get();
+}
+
 }  // namespace configuration
 }  // namespace aos
diff --git a/aos/common/Configuration.h b/aos/common/Configuration.h
index bf2dc80..2f7e777 100644
--- a/aos/common/Configuration.h
+++ b/aos/common/Configuration.h
@@ -22,8 +22,8 @@
   kCameraStreamer = 9714,
 };
 
-// Holds global configuration data. All of the public static functions are safe
-// to call concurrently (the ones that need to create locks on the cRIO).
+// Holds global configuration data. All of the functions are safe to call
+// from wherever (the ones that need to create locks on the cRIO).
 namespace configuration {
 
 // Constants indentifying various devices on the network.
@@ -36,6 +36,17 @@
 // The return value should be passed to free(3) if it is no longer needed.
 const char *GetIPAddress(NetworkDevice device);
 
+// Returns the "root directory" for this run. Under linux, this is the
+// directory where the executable is located (from /proc/self/exe) and under
+// vxworks it is just "/".
+// The return value will always be to a static string, so no freeing is
+// necessary.
+const char *GetRootDirectory();
+// Returns the directory where logs get written. Relative to GetRootDirectory().
+// The return value will always be to a static string, so no freeing is
+// necessary.
+const char *GetLoggingDirectory();
+
 }  // namespace configuration
 }  // namespace aos
 
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 6dca969..967eb51 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -45,6 +45,10 @@
       ],
       'dependencies': [
         '<(AOS)/build/aos.gyp:logging',
+        'once',
+      ],
+      'export_dependent_settings': [
+        'once',
       ],
       'conditions': [
         ['OS=="crio"', {
@@ -158,6 +162,38 @@
       'dependencies': [
         '<(EXTERNALS):gtest',
         '<(AOS)/build/aos.gyp:libaos',
+        ':common',
+      ],
+    },
+    {
+      'target_name': 'gtest_prod',
+      'type': 'static_library',
+      'dependencies': [
+        '<(EXTERNALS):gtest_prod',
+      ],
+      'export_dependent_settings': [
+        '<(EXTERNALS):gtest_prod',
+      ],
+    },
+    {
+      'target_name': 'once',
+      'type': 'static_library',
+      'dependencies': [
+        '<(EXTERNALS):gtest_prod',
+      ],
+      'export_dependent_settings': [
+        '<(EXTERNALS):gtest_prod',
+      ],
+    },
+    {
+      'target_name': 'once_test',
+      'type': '<(aos_target)',
+      'sources': [
+        'once_test.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):gtest',
+        '<(AOS)/build/aos.gyp:libaos',
       ],
     },
     {
diff --git a/aos/common/gtest_prod.h b/aos/common/gtest_prod.h
new file mode 100644
index 0000000..fe0b056
--- /dev/null
+++ b/aos/common/gtest_prod.h
@@ -0,0 +1,37 @@
+#ifndef AOS_COMMON_GTEST_PROD_H_
+#define AOS_COMMON_GTEST_PROD_H_
+
+#include "gtest/gtest_prod.h"
+
+// These macros replace gtest's FRIEND_TEST if the test is in a different
+// namespace than the code that needs to make it a friend.
+// Example:
+//  foo.h:
+//   namespace bla {
+//   namespace testing {
+//
+//   FORWARD_DECLARE_TEST_CASE(FooTest, Bar);
+//
+//   }  // namespace testing
+//
+//   class Foo {
+//     FRIEND_TEST_NAMESPACE(FooTest, Bar, testing);
+//   };
+//
+//   }  // namespace bla
+//  foo_test.cc:
+//   namespace bla {
+//   namespace testing {
+//
+//   TEST(FooTest, Bar) {
+//     access private members of Foo
+//   }
+//
+//   }  // namespace testing
+//   }  // namespace bla
+#define FORWARD_DECLARE_TEST_CASE(test_case_name, test_name) \
+    class test_case_name##_##test_name##_Test;
+#define FRIEND_TEST_NAMESPACE(test_case_name, test_name, namespace_name) \
+    friend class namespace_name::test_case_name##_##test_name##_Test
+
+#endif  // AOS_COMMON_GTEST_PROD_H_
diff --git a/aos/common/once-tmpl.h b/aos/common/once-tmpl.h
new file mode 100644
index 0000000..9b9c8c5
--- /dev/null
+++ b/aos/common/once-tmpl.h
@@ -0,0 +1,47 @@
+#ifdef __VXWORKS__
+#include <taskLib.h>
+#else
+#include <sched.h>
+#endif
+
+#include "aos/common/type_traits.h"
+
+// It doesn't use pthread_once, because Brian looked at the pthreads
+// implementation for vxworks and noticed that it is completely and entirely
+// broken for doing just about anything (including its pthread_once). It has the
+// same implementation on the atom for simplicity.
+
+namespace aos {
+
+// Setting function_ multiple times would be OK because it'll get set to the
+// same value each time.
+template<typename T>
+Once<T>::Once(Function function)
+    : function_(function) {
+  static_assert(shm_ok<Once<T>>::value, "Once should work in shared memory");
+}
+
+template<typename T>
+void Once<T>::Reset() {
+  done_ = false;
+  run_ = 0;
+}
+
+template<typename T>
+T *Once<T>::Get() {
+  if (__sync_lock_test_and_set(&run_, 1) == 0) {
+    result_ = function_();
+    done_ = true;
+  } else {
+    while (!done_) {
+#ifdef __VXWORKS__
+      taskDelay(1);
+#else
+      sched_yield();
+#endif
+    }
+  }
+  return result_;
+}
+
+}  // namespace aos
diff --git a/aos/common/once.h b/aos/common/once.h
new file mode 100644
index 0000000..026b1bd
--- /dev/null
+++ b/aos/common/once.h
@@ -0,0 +1,69 @@
+#ifndef AOS_COMMON_ONCE_H_
+#define AOS_COMMON_ONCE_H_
+
+#include <stdint.h>
+
+#include "aos/common/gtest_prod.h"
+
+namespace aos {
+namespace testing {
+
+FORWARD_DECLARE_TEST_CASE(OnceTest, MemoryClearing);
+
+}  // namespace testing
+
+// Designed for the same thing as pthread_once: to run something exactly 1 time.
+//
+// Intended use case:
+//   const char *CalculateSomethingCool() {
+//     static ::aos::Once<const char> once(DoCalculateSomethingCool);
+//     return once.Get();
+//   }
+//
+// IMPORTANT: Instances _must_ be placed in memory that gets 0-initialized
+// automatically or Reset() must be called exactly once!!
+// The expected use case is to use one of these as a static variable, and those
+// do get 0-initialized under the C++ standard. Global variables are treated the
+// same way by the C++ Standard.
+// Placing an instance in shared memory (and using Reset()) is also supported.
+// The constructor does not initialize all of the member variables!
+// This is because initializing them in the constructor creates a race condition
+// if initialization of static variables isn't thread safe.
+template<typename T>
+class Once {
+ public:
+  typedef T *(*Function)();
+  explicit Once(Function function);
+
+  // Returns the result of calling function_. The first call will actually run
+  // it and then any other ones will block (if necessary) until it's finished
+  // and then return the same thing.
+  T *Get();
+
+  // Will clear out all the member variables. If this is going to be used
+  // instead of creating an instance in 0-initialized memory, then this method
+  // must be called exactly once before Get() is called anywhere.
+  // This method can also be called to run the function again the next time
+  // Get() is called. However, calling it again is not thread safe.
+  void Reset();
+
+ private:
+  // The function to run to calculate result_.
+  Function function_;
+  // Whether or not it is running. Gets atomically swapped from 0 to 1 by the
+  // thread that actually runs function_.
+  volatile int run_;
+  // Whether or not it is done. Gets set to true after the thread that is
+  // running function_ finishes running it and storing the result in result_.
+  volatile bool done_;
+  // What function_ returned when it was executed.
+  T *result_;
+
+  FRIEND_TEST_NAMESPACE(OnceTest, MemoryClearing, testing);
+};
+
+}  // namespace aos
+
+#include "aos/common/once-tmpl.h"
+
+#endif  // AOS_COMMON_ONCE_H_
diff --git a/aos/common/once_test.cc b/aos/common/once_test.cc
new file mode 100644
index 0000000..ed21b37
--- /dev/null
+++ b/aos/common/once_test.cc
@@ -0,0 +1,128 @@
+#include "aos/common/once.h"
+
+#include "stdlib.h"
+#include "limits.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+class OnceTest : public ::testing::Test {
+ public:
+  static int *Function() {
+    ++times_run_;
+    value_ = rand() % INT_MAX;
+    return &value_;
+  }
+
+ protected:
+  void SetUp() {
+    value_ = 0;
+    times_run_ = 0;
+  }
+
+  static int value_;
+  static int times_run_;
+};
+int OnceTest::value_, OnceTest::times_run_;
+
+// Makes sure that it calls the function at the right time and that it correctly
+// passes the result out.
+TEST_F(OnceTest, Works) {
+  static Once<int> once(Function);
+
+  EXPECT_EQ(0, value_);
+  EXPECT_EQ(0, times_run_);
+
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_NE(0, value_);
+  EXPECT_NE(0, times_run_);
+  // Make sure it's not passing it through an assignment by value or something
+  // else weird.
+  EXPECT_EQ(&value_, once.Get());
+}
+
+// Makes sure that having a Once at namespace scope works correctly.
+namespace {
+
+Once<int> global_once(OnceTest::Function);
+
+}  // namespace
+
+TEST_F(OnceTest, Global) {
+  EXPECT_EQ(value_, *global_once.Get());
+  EXPECT_NE(0, value_);
+  EXPECT_NE(0, times_run_);
+}
+
+// Makes sure that an instance keeps returning the same value without running
+// the function again.
+TEST_F(OnceTest, MultipleGets) {
+  static Once<int> once(Function);
+
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(1, times_run_);
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(1, times_run_);
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(1, times_run_);
+}
+
+// Tests to make sure that the right methods clear out the instance variables at
+// the right times.
+TEST_F(OnceTest, MemoryClearing) {
+  Once<int> once(NULL);
+
+  once.run_ = 1;
+  once.done_ = true;
+  // Run the constructor again to make sure it doesn't touch the variables set
+  // above.
+  new (&once)Once<int>(Function);
+
+  // Should return a random, (potentially) uninitialized value.
+  once.Get();
+  EXPECT_EQ(0, times_run_);
+
+  once.Reset();
+  EXPECT_EQ(0, times_run_);
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(1, times_run_);
+}
+
+namespace {
+
+int second_result = 0;
+int *SecondFunction() {
+  second_result = rand() % INT_MAX;
+  return &second_result;
+}
+
+}  // namespace
+
+// Makes sure that multiple instances don't interfere with each other.
+TEST_F(OnceTest, MultipleInstances) {
+  static Once<int> once1(Function);
+  static Once<int> once2(SecondFunction);
+
+  EXPECT_EQ(&value_, once1.Get());
+  EXPECT_EQ(&second_result, once2.Get());
+  EXPECT_EQ(&value_, once1.Get());
+  EXPECT_EQ(&second_result, once2.Get());
+}
+
+// Tests calling Reset() to run the function a second time.
+TEST_F(OnceTest, Recalculate) {
+  Once<int> once(Function);
+
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(1, times_run_);
+
+  value_ = 0;
+  once.Reset();
+  EXPECT_EQ(value_, *once.Get());
+  EXPECT_EQ(2, times_run_);
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/frc971/output/CameraServer.cc b/frc971/output/CameraServer.cc
index 96f698d..d3c3bc8 100644
--- a/frc971/output/CameraServer.cc
+++ b/frc971/output/CameraServer.cc
@@ -3,6 +3,9 @@
 #include "aos/aos_core.h"
 #include "aos/atom_code/output/HTTPServer.h"
 #include "aos/atom_code/output/evhttp_ctemplate_emitter.h"
+#include "aos/atom_code/output/ctemplate_cache.h"
+#include "aos/common/Configuration.h"
+#include "aos/common/messages/RobotState.q.h"
 #include "ctemplate/template.h"
 
 #include "frc971/constants.h"
@@ -11,12 +14,10 @@
 
 namespace frc971 {
 
-const char *const kPath = "/home/driver/robot_code/bin/";
-//const char *const kPath = "/home/brians/Desktop/git_frc971/2012/trunk/src/frc971/output";
-
 class CameraServer : public aos::http::HTTPServer {
  public:
-  CameraServer() : HTTPServer(kPath, 8080), buf_(NULL) {
+  CameraServer() : HTTPServer(aos::configuration::GetRootDirectory(), 8080),
+      buf_(NULL) {
     AddPage<CameraServer>("/robot.html", &CameraServer::RobotHTML, this);
   }
 
@@ -53,6 +54,11 @@
     // after it.
     dict.SetValue("HOST", ctemplate::TemplateString(host, length));
 
+    if (!aos::robot_state.FetchLatest()) {
+      LOG(WARNING, "getting a RobotState message failed\n");
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return;
+    }
     int center;
     if (!constants::camera_center(&center)) {
       evhttp_send_error(request, HTTP_INTERNAL, NULL);
@@ -61,8 +67,9 @@
     dict.SetIntValue("CENTER", center);
 
     aos::http::EvhttpCtemplateEmitter emitter(buf_);
-    if (!ctemplate::ExpandTemplate(ROBOT_HTML, ctemplate::STRIP_WHITESPACE,
-                                   &dict, &emitter)) {
+    if (!aos::http::get_template_cache()->
+        ExpandWithData(ROBOT_HTML, ctemplate::STRIP_WHITESPACE,
+                       &dict, NULL, &emitter)) {
       LOG(ERROR, "expanding the template failed\n");
       evhttp_send_error(request, HTTP_INTERNAL, NULL);
       return;
@@ -78,4 +85,3 @@
 }  // namespace frc971
 
 AOS_RUN_NRT(frc971::CameraServer)
-