aboutsummaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..959cda0
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,117 @@
+// Copyright 2021 Benjamin Barenblat
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <getopt.h>
+#include <locale.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+
+#include "src/ui/stream.h"
+#include "src/ui/terminal.h"
+#include "src/util.h"
+#include "third_party/abseil/absl/strings/string_view.h"
+
+namespace {
+
+constexpr absl::string_view kShortUsage = "Usage: ec [OPTION...] [FILENAME]\n";
+
+constexpr absl::string_view kHelp =
+ R"(
+A Reverse Polish scientific calculator. Invoked directly, ec launches an
+interactive interpreter that displays the stack and accepts input. Invoked on a
+file, ec processes the entire file; if the result stack is nonempty, ec prints
+the stack in space-separated form.
+
+Options:
+ --help display this help and exit
+ --version display version information and exit
+)";
+
+constexpr absl::string_view kAskForHelp =
+ "Try 'ec --help' for more information.\n";
+
+constexpr absl::string_view kVersionInfo = "ec development build\n";
+
+enum {
+ kHelpLongOption = 128,
+ kVersionLongOption = 129,
+};
+
+int TerminalUi() {
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+ return ec::TerminalUi().Main();
+ } else {
+ return ec::LineUi().Main();
+ }
+}
+
+int ProcessFile(const char* path) {
+ std::ifstream file(path);
+ if (!file.is_open()) {
+ std::cerr << "ec: failed to open file " << path << '\n';
+ return 1;
+ }
+ return ec::StreamUi(file).Main();
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ setlocale(LC_ALL, "");
+
+ static option long_options[] = {
+ {"help", no_argument, nullptr, kHelpLongOption},
+ {"version", no_argument, nullptr, kVersionLongOption},
+ {nullptr, 0, nullptr, 0},
+ };
+ while (true) {
+ int c = getopt_long(argc, argv, "", long_options, /*longindex=*/nullptr);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case kHelpLongOption:
+ std::cout << kShortUsage << kHelp;
+ return 0;
+ case kVersionLongOption:
+ std::cout << kVersionInfo;
+ return 0;
+ case '?':
+ std::cerr << kAskForHelp;
+ return 1;
+ default:
+ std::cerr << "ec: internal error: unhandled getopt switch\nThis is a "
+ "bug! Please report it.\n";
+ return 1;
+ }
+ }
+
+ try {
+ if (optind == argc) {
+ TerminalUi();
+ } else if (optind == argc - 1) {
+ return ProcessFile(argv[optind]);
+ } else {
+ std::cerr << kShortUsage << kAskForHelp;
+ return 1;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << "ec: internal error: " << DemangleIfPossible(typeid(e).name())
+ << ": " << e.what() << "\nThis is a bug! Please report it.\n";
+ return 1;
+ }
+}