<commit_before>/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Part of the Processing project - http://processing.org

  Copyright (c) 2004-10 <NAME> and <NAME>
  Copyright (c) 2001-04 Massachusetts Institute of Technology

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License version 2
  as published by the Free Software Foundation.

  This program 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 General Public License for more details.

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

package processing.app;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.*;

import org.apache.commons.logging.impl.LogFactoryImpl;
import org.apache.commons.logging.impl.NoOpLog;

import cc.arduino.packages.DiscoveryManager;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException;
import processing.app.helpers.FileUtils;
import processing.app.helpers.PreferencesMap;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
import processing.app.javax.swing.filechooser.FileNameExtensionFilter;
import processing.app.packages.Library;
import processing.app.packages.LibraryList;
import processing.app.tools.MenuScroller;
import processing.app.tools.ZipDeflater;
import processing.core.*;
import static processing.app.I18n._;


/**
 * The base class for the main processing application.
 * Primary role of this class is for platform identification and
 * general interaction with the system (launching URLs, loading
 * files and images, etc) that comes from that.
 */
public class Base {
  public static final int REVISION = 157;
  /** This might be replaced by main() if there's a lib/version.txt file. */
  static String VERSION_NAME = "0157";
  /** Set true if this a proper release rather than a numbered revision. */
  static public boolean RELEASE = false;

  static Map<Integer, String> platformNames = new HashMap<Integer, String>();
  static {
    platformNames.put(PConstants.WINDOWS, "windows");
    platformNames.put(PConstants.MACOSX, "macosx");
    platformNames.put(PConstants.LINUX, "linux");
  }

  static HashMap<String, Integer> platformIndices = new HashMap<String, Integer>();
  static {
    platformIndices.put("windows", PConstants.WINDOWS);
    platformIndices.put("macosx", PConstants.MACOSX);
    platformIndices.put("linux", PConstants.LINUX);
  }
  static Platform platform;

  private static DiscoveryManager discoveryManager = new DiscoveryManager();
  
  static private boolean commandLine;

  // A single instance of the preferences window
  Preferences preferencesFrame;

  // set to true after the first time the menu is built.
  // so that the errors while building don't show up again.
  boolean builtOnce;

  static File buildFolder;

  // these are static because they're used by Sketch
  static private File examplesFolder;
  static private File toolsFolder;

  static private List<File> librariesFolders;

  // maps library name to their library folder
  static private LibraryList libraries;

  // maps #included files to their library folder
  static Map<String, Library> importToLibraryTable;

  // classpath for all known libraries for p5
  // (both those in the p5/libs folder and those with lib subfolders
  // found in the sketchbook)
  static public String librariesClassPath;

  static public Map<String, TargetPackage> packages;

  // Location for untitled items
  static File untitledFolder;

  // p5 icon for the window
//  static Image icon;

//  int editorCount;
  List<Editor> editors = Collections.synchronizedList(new ArrayList<Editor>());
  Editor activeEditor;
  private final Map<String, Map<String, Object>> boardsViaNetwork;

  static File portableFolder = null;
  static final String portableSketchbookFolder = "sketchbook";

  static public void main(String args[]) throws Exception {
    System.setProperty(LogFactoryImpl.LOG_PROPERTY, NoOpLog.class.getCanonicalName());
    Logger.getLogger("javax.jmdns").setLevel(Level.OFF);

    initPlatform();

    // Portable folder
    portableFolder = getContentFile("portable");
    if (!portableFolder.exists())
      portableFolder = null;

    // run static initialization that grabs all the prefs
    Preferences.init(args);

    try {
      File versionFile = getContentFile("lib/version.txt");
      if (versionFile.exists()) {
        String version = PApplet.loadStrings(versionFile)[0];
        if (!version.equals(VERSION_NAME) && !version.equals("${version}")) {
          VERSION_NAME = version;
          RELEASE = true;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // help 3rd party installers find the correct hardware path
    Preferences.set("last.ide." + VERSION_NAME + ".hardwarepath", getHardwarePath());
    Preferences.set("last.ide." + VERSION_NAME + ".daterun", "" + (new Date()).getTime() / 1000);

//    if (System.getProperty("mrj.version") != null) {
//      //String jv = System.getProperty("java.version");
//      String ov = System.getProperty("os.version");
//      if (ov.startsWith("10.5")) {
//        System.setProperty("apple.laf.useScreenMenuBar", "true");
//      }
//    }

    /*
    commandLine = false;
    if (args.length >= 2) {
      if (args[0].startsWith("--")) {
        commandLine = true;
      }
    }

    if (PApplet.javaVersion < 1.5f) {
      //System.err.println("no way man");
      Base.showError("Need to install Java 1.5",
                     "This version of Processing requires    \n" +
                     "Java 1.5 or later to run properly.\n" +
                     "Please visit java.com to upgrade.", null);
    }
    */

//    // Set the look and feel before opening the window
//    try {
//      platform.setLookAndFeel();
//    } catch (Exception e) {
//      System.err.println("Non-fatal error while setting the Look & Feel.");
//      System.err.println("The error message follows, however Processing should run fine.");
//      System.err.println(e.getMessage());
//      //e.printStackTrace();
//    }

    // Use native popups so they don't look so crappy on osx
    JPopupMenu.setDefaultLightWeightPopupEnabled(false);

    // Don't put anything above this line that might make GUI,
    // because the platform has to be inited properly first.

    // Make sure a full JDK is installed
    //initRequirements();

    // setup the theme coloring fun
    Theme.init();

    // Set the look and feel before opening the window
    try {
      platform.setLookAndFeel();
    } catch (Exception e) {
      String mess = e.getMessage();
      if (mess.indexOf("ch.randelshofer.quaqua.QuaquaLookAndFeel") == -1) {
        System.err.println(_("Non-fatal error while setting the Look & Feel."));
        System.err.println(_("The error message follows, however Arduino should run fine."));
        System.err.println(mess);
      }
    }

    // Create a location for untitled sketches
    untitledFolder = createTempFolder("untitled");
    untitledFolder.deleteOnExit();

    new Base(args);
  }


  static protected void setCommandLine() {
    commandLine = true;
  }


  static protected boolean isCommandLine() {
    return commandLine;
  }


  static protected void initPlatform() {
    try {
      Class<?> platformClass = Class.forName("processing.app.Platform");
      if (Base.isMacOS()) {
        platformClass = Class.forName("processing.app.macosx.Platform");
      } else if (Base.isWindows()) {
        platformClass = Class.forName("processing.app.windows.Platform");
      } else if (Base.isLinux()) {
        platformClass = Class.forName("processing.app.linux.Platform");
      }
      platform = (Platform) platformClass.newInstance();
    } catch (Exception e) {
      Base.showError(_("Problem Setting the Platform"),
                     _("An unknown error occurred while trying to load\n" +
                       "platform-specific code for your machine."), e);
    }
  }


  static protected void initRequirements() {
    try {
      Class.forName("com.sun.jdi.VirtualMachine");
    } catch (ClassNotFoundException cnfe) {
      Base.showPlatforms();
      Base.showError(_("Please install JDK 1.5 or later"),
                     _("Arduino requires a full JDK (not just a JRE)\n" +
                       "to run. Please install JDK 1.5 or later.\n" +
                       "More information can be found in the reference."), cnfe);
    }
  }


  protected static enum ACTION { GUI, VERIFY, UPLOAD };
  public Base(String[] args) throws Exception {
    platform.init(this);

    this.boardsViaNetwork = new ConcurrentHashMap<String, Map<String, Object>>();

    // Get the sketchbook path, and make sure it's set properly
    String sketchbookPath = Preferences.get("sketchbook.path");

    // If a value is at least set, first check to see if the folder exists.
    // If it doesn't, warn the user that the sketchbook folder is being reset.
    if (sketchbookPath != null) {
      File sketchbookFolder;
      if (portableFolder != null)
        sketchbookFolder = new File(portableFolder, sketchbookPath);
      else
        sketchbookFolder = new File(sketchbookPath);
      if (!sketchbookFolder.exists()) {
        Base.showWarning(_("Sketchbook folder disappeared"),
                _("The sketchbook folder no longer exists.\n" +
                        "Arduino will switch to the default sketchbook\n" +
                        "location, and create a new sketchbook folder if\n" +
                        "necessary. Arduino will then stop talking about\n" +
                        "himself in the third person."), null);
        sketchbookPath = null;
      }
    }

    // If no path is set, get the default sketchbook folder for this platform
    if (sketchbookPath == null) {
      File defaultFolder = getDefaultSketchbookFolder();
      if (portableFolder != null)
        Preferences.set("sketchbook.path", portableSketchbookFolder);
      else
        Preferences.set("sketchbook.path", defaultFolder.getAbsolutePath());
      if (!defaultFolder.exists()) {
        defaultFolder.mkdirs();
      }
    }

    packages = new HashMap<String, TargetPackage>();
    loadHardware(getHardwareFolder());
    loadHardware(getSketchbookHardwareFolder());
    if (packages.size() == 0) {
      System.out.println(_("No valid configured cores found! Exiting..."));
      System.exit(3);
    }
    
    // Setup board-dependent variables.
    onBoardOrPortChange();

    ACTION action = ACTION.GUI;
    boolean doVerboseBuild = false;
    boolean doVerboseUpload = false;;
    String selectBoard = null;
    String selectPort = null;
    String currentDirectory = System.getProperty("user.dir");
    List<String> filenames = new LinkedList<String>();

    // Map of possible actions and corresponding options
    final Map<String, ACTION> actions = new HashMap<String, ACTION>();
    actions.put("--verify", ACTION.VERIFY);
    actions.put("--upload", ACTION.UPLOAD);

    // Check if any files were passed in on the command line
    for (int i = 0; i < args.length; i++) {
      ACTION a = actions.get(args[i]);
      if (a != null) {
        if (action != ACTION.GUI) {
          String[] valid = actions.keySet().toArray(new String[0]);
          String mess = I18n.format(_("Can only pass one of: {0}"), PApplet.join(valid, ", "));
          showError(null, mess, 3);
        }
        action = a;
        continue;
      }
      if (args[i].equals("--verbose") || args[i].equals("-v")) {
        doVerboseBuild = true;
        doVerboseUpload = true;
        continue;
      }
      if (args[i].equals("--verbose-build")) {
        doVerboseBuild = true;
        continue;
      }
      if (args[i].equals("--verbose-upload")) {
        doVerboseUpload = true;
        continue;
      }
      if (args[i].equals("--board")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --board"), 3);
        processBoardArgument(args[i]);
        continue;
      }
      if (args[i].equals("--port")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --port"), 3);
        Base.selectSerialPort(args[i]);
        continue;
      }
      if (args[i].equals("--curdir")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --curdir"), 3);
        currentDirectory = args[i];
        continue;
      }
      if (args[i].equals("--pref")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --pref"), 3);
        processPrefArgument(args[i]);
        continue;
      }
      if (args[i].equals("--preferences-file")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --preferences-file"), 3);
        // Argument should be already processed by Preferences.init(...) 
        continue;
      }
      if (args[i].startsWith("--"))
        showError(null, I18n.format(_("unknown option: {0}"), args[i]), 3);

      filenames.add(args[i]);
    }

    if ((action == ACTION.UPLOAD || action == ACTION.VERIFY) && filenames.size() != 1)
      showError(null, _("Must specify exactly one sketch file"), 3);

    if ((action != ACTION.UPLOAD && action != ACTION.VERIFY) && (doVerboseBuild || doVerboseUpload))
      showError(null, _("--verbose, --verbose-upload and --verbose-build can only be used together with --verify or --upload"), 3);

    for (String path: filenames) {
      // Fix a problem with systems that use a non-ASCII languages. Paths are
      // being passed in with 8.3 syntax, which makes the sketch loader code
      // unhappy, since the sketch folder naming doesn't match up correctly.
      // http://dev.processing.org/bugs/show_bug.cgi?id=1089
      if (isWindows()) {
        try {
          File file = new File(path);
          path = file.getCanonicalPath();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }

      if (!new File(path).isAbsolute()) {
        path = new File(currentDirectory, path).getAbsolutePath();
      }

      boolean showEditor = (action == ACTION.GUI);
      if (handleOpen(path, nextEditorLocation(), showEditor) == null) {
        String mess = I18n.format(_("Failed to open sketch: \"{0}\""), path);
        // Open failure is fatal in upload/verify mode
        if (action == ACTION.VERIFY || action == ACTION.UPLOAD)
          showError(null, mess, 2);
        else
          showWarning(null, mess, null);
      }
    }

    switch (action) {
      case VERIFY:
      case UPLOAD:
        // Set verbosity for command line build
        Preferences.set("build.verbose", "" + doVerboseBuild);
        Preferences.set("upload.verbose", "" + doVerboseUpload);

        Editor editor = editors.get(0);

        if (action == ACTION.UPLOAD) {
          // Build and upload
          editor.exportHandler.run();
        } else {
          // Build only
          editor.runHandler.run();
        }

        // Error during build or upload
        int res = editor.status.mode;
        if (res == EditorStatus.ERR)
          System.exit(1);

        // No errors exit gracefully
        System.exit(0);
        break;
      case GUI:
        // Check if there were previously opened sketches to be restored
        restoreSketches();

        // Create a new empty window (will be replaced with any files to be opened)<commit_msg>Explicitely save preferences on startup<commit_after>/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Part of the Processing project - http://processing.org

  Copyright (c) 2004-10 <NAME> and <NAME>
  Copyright (c) 2001-04 Massachusetts Institute of Technology

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License version 2
  as published by the Free Software Foundation.

  This program 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 General Public License for more details.

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

package processing.app;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.*;

import org.apache.commons.logging.impl.LogFactoryImpl;
import org.apache.commons.logging.impl.NoOpLog;

import cc.arduino.packages.DiscoveryManager;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException;
import processing.app.helpers.FileUtils;
import processing.app.helpers.PreferencesMap;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
import processing.app.javax.swing.filechooser.FileNameExtensionFilter;
import processing.app.packages.Library;
import processing.app.packages.LibraryList;
import processing.app.tools.MenuScroller;
import processing.app.tools.ZipDeflater;
import processing.core.*;
import static processing.app.I18n._;


/**
 * The base class for the main processing application.
 * Primary role of this class is for platform identification and
 * general interaction with the system (launching URLs, loading
 * files and images, etc) that comes from that.
 */
public class Base {
  public static final int REVISION = 157;
  /** This might be replaced by main() if there's a lib/version.txt file. */
  static String VERSION_NAME = "0157";
  /** Set true if this a proper release rather than a numbered revision. */
  static public boolean RELEASE = false;

  static Map<Integer, String> platformNames = new HashMap<Integer, String>();
  static {
    platformNames.put(PConstants.WINDOWS, "windows");
    platformNames.put(PConstants.MACOSX, "macosx");
    platformNames.put(PConstants.LINUX, "linux");
  }

  static HashMap<String, Integer> platformIndices = new HashMap<String, Integer>();
  static {
    platformIndices.put("windows", PConstants.WINDOWS);
    platformIndices.put("macosx", PConstants.MACOSX);
    platformIndices.put("linux", PConstants.LINUX);
  }
  static Platform platform;

  private static DiscoveryManager discoveryManager = new DiscoveryManager();
  
  static private boolean commandLine;

  // A single instance of the preferences window
  Preferences preferencesFrame;

  // set to true after the first time the menu is built.
  // so that the errors while building don't show up again.
  boolean builtOnce;

  static File buildFolder;

  // these are static because they're used by Sketch
  static private File examplesFolder;
  static private File toolsFolder;

  static private List<File> librariesFolders;

  // maps library name to their library folder
  static private LibraryList libraries;

  // maps #included files to their library folder
  static Map<String, Library> importToLibraryTable;

  // classpath for all known libraries for p5
  // (both those in the p5/libs folder and those with lib subfolders
  // found in the sketchbook)
  static public String librariesClassPath;

  static public Map<String, TargetPackage> packages;

  // Location for untitled items
  static File untitledFolder;

  // p5 icon for the window
//  static Image icon;

//  int editorCount;
  List<Editor> editors = Collections.synchronizedList(new ArrayList<Editor>());
  Editor activeEditor;
  private final Map<String, Map<String, Object>> boardsViaNetwork;

  static File portableFolder = null;
  static final String portableSketchbookFolder = "sketchbook";

  static public void main(String args[]) throws Exception {
    System.setProperty(LogFactoryImpl.LOG_PROPERTY, NoOpLog.class.getCanonicalName());
    Logger.getLogger("javax.jmdns").setLevel(Level.OFF);

    initPlatform();

    // Portable folder
    portableFolder = getContentFile("portable");
    if (!portableFolder.exists())
      portableFolder = null;

    // run static initialization that grabs all the prefs
    Preferences.init(args);

    try {
      File versionFile = getContentFile("lib/version.txt");
      if (versionFile.exists()) {
        String version = PApplet.loadStrings(versionFile)[0];
        if (!version.equals(VERSION_NAME) && !version.equals("${version}")) {
          VERSION_NAME = version;
          RELEASE = true;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // help 3rd party installers find the correct hardware path
    Preferences.set("last.ide." + VERSION_NAME + ".hardwarepath", getHardwarePath());
    Preferences.set("last.ide." + VERSION_NAME + ".daterun", "" + (new Date()).getTime() / 1000);

//    if (System.getProperty("mrj.version") != null) {
//      //String jv = System.getProperty("java.version");
//      String ov = System.getProperty("os.version");
//      if (ov.startsWith("10.5")) {
//        System.setProperty("apple.laf.useScreenMenuBar", "true");
//      }
//    }

    /*
    commandLine = false;
    if (args.length >= 2) {
      if (args[0].startsWith("--")) {
        commandLine = true;
      }
    }

    if (PApplet.javaVersion < 1.5f) {
      //System.err.println("no way man");
      Base.showError("Need to install Java 1.5",
                     "This version of Processing requires    \n" +
                     "Java 1.5 or later to run properly.\n" +
                     "Please visit java.com to upgrade.", null);
    }
    */

//    // Set the look and feel before opening the window
//    try {
//      platform.setLookAndFeel();
//    } catch (Exception e) {
//      System.err.println("Non-fatal error while setting the Look & Feel.");
//      System.err.println("The error message follows, however Processing should run fine.");
//      System.err.println(e.getMessage());
//      //e.printStackTrace();
//    }

    // Use native popups so they don't look so crappy on osx
    JPopupMenu.setDefaultLightWeightPopupEnabled(false);

    // Don't put anything above this line that might make GUI,
    // because the platform has to be inited properly first.

    // Make sure a full JDK is installed
    //initRequirements();

    // setup the theme coloring fun
    Theme.init();

    // Set the look and feel before opening the window
    try {
      platform.setLookAndFeel();
    } catch (Exception e) {
      String mess = e.getMessage();
      if (mess.indexOf("ch.randelshofer.quaqua.QuaquaLookAndFeel") == -1) {
        System.err.println(_("Non-fatal error while setting the Look & Feel."));
        System.err.println(_("The error message follows, however Arduino should run fine."));
        System.err.println(mess);
      }
    }

    // Create a location for untitled sketches
    untitledFolder = createTempFolder("untitled");
    untitledFolder.deleteOnExit();

    new Base(args);
  }


  static protected void setCommandLine() {
    commandLine = true;
  }


  static protected boolean isCommandLine() {
    return commandLine;
  }


  static protected void initPlatform() {
    try {
      Class<?> platformClass = Class.forName("processing.app.Platform");
      if (Base.isMacOS()) {
        platformClass = Class.forName("processing.app.macosx.Platform");
      } else if (Base.isWindows()) {
        platformClass = Class.forName("processing.app.windows.Platform");
      } else if (Base.isLinux()) {
        platformClass = Class.forName("processing.app.linux.Platform");
      }
      platform = (Platform) platformClass.newInstance();
    } catch (Exception e) {
      Base.showError(_("Problem Setting the Platform"),
                     _("An unknown error occurred while trying to load\n" +
                       "platform-specific code for your machine."), e);
    }
  }


  static protected void initRequirements() {
    try {
      Class.forName("com.sun.jdi.VirtualMachine");
    } catch (ClassNotFoundException cnfe) {
      Base.showPlatforms();
      Base.showError(_("Please install JDK 1.5 or later"),
                     _("Arduino requires a full JDK (not just a JRE)\n" +
                       "to run. Please install JDK 1.5 or later.\n" +
                       "More information can be found in the reference."), cnfe);
    }
  }


  protected static enum ACTION { GUI, VERIFY, UPLOAD };
  public Base(String[] args) throws Exception {
    platform.init(this);

    this.boardsViaNetwork = new ConcurrentHashMap<String, Map<String, Object>>();

    // Get the sketchbook path, and make sure it's set properly
    String sketchbookPath = Preferences.get("sketchbook.path");

    // If a value is at least set, first check to see if the folder exists.
    // If it doesn't, warn the user that the sketchbook folder is being reset.
    if (sketchbookPath != null) {
      File sketchbookFolder;
      if (portableFolder != null)
        sketchbookFolder = new File(portableFolder, sketchbookPath);
      else
        sketchbookFolder = new File(sketchbookPath);
      if (!sketchbookFolder.exists()) {
        Base.showWarning(_("Sketchbook folder disappeared"),
                _("The sketchbook folder no longer exists.\n" +
                        "Arduino will switch to the default sketchbook\n" +
                        "location, and create a new sketchbook folder if\n" +
                        "necessary. Arduino will then stop talking about\n" +
                        "himself in the third person."), null);
        sketchbookPath = null;
      }
    }

    // If no path is set, get the default sketchbook folder for this platform
    if (sketchbookPath == null) {
      File defaultFolder = getDefaultSketchbookFolder();
      if (portableFolder != null)
        Preferences.set("sketchbook.path", portableSketchbookFolder);
      else
        Preferences.set("sketchbook.path", defaultFolder.getAbsolutePath());
      if (!defaultFolder.exists()) {
        defaultFolder.mkdirs();
      }
    }

    packages = new HashMap<String, TargetPackage>();
    loadHardware(getHardwareFolder());
    loadHardware(getSketchbookHardwareFolder());
    if (packages.size() == 0) {
      System.out.println(_("No valid configured cores found! Exiting..."));
      System.exit(3);
    }
    
    // Setup board-dependent variables.
    onBoardOrPortChange();

    ACTION action = ACTION.GUI;
    boolean doVerboseBuild = false;
    boolean doVerboseUpload = false;;
    String selectBoard = null;
    String selectPort = null;
    String currentDirectory = System.getProperty("user.dir");
    List<String> filenames = new LinkedList<String>();

    // Map of possible actions and corresponding options
    final Map<String, ACTION> actions = new HashMap<String, ACTION>();
    actions.put("--verify", ACTION.VERIFY);
    actions.put("--upload", ACTION.UPLOAD);

    // Check if any files were passed in on the command line
    for (int i = 0; i < args.length; i++) {
      ACTION a = actions.get(args[i]);
      if (a != null) {
        if (action != ACTION.GUI) {
          String[] valid = actions.keySet().toArray(new String[0]);
          String mess = I18n.format(_("Can only pass one of: {0}"), PApplet.join(valid, ", "));
          showError(null, mess, 3);
        }
        action = a;
        continue;
      }
      if (args[i].equals("--verbose") || args[i].equals("-v")) {
        doVerboseBuild = true;
        doVerboseUpload = true;
        continue;
      }
      if (args[i].equals("--verbose-build")) {
        doVerboseBuild = true;
        continue;
      }
      if (args[i].equals("--verbose-upload")) {
        doVerboseUpload = true;
        continue;
      }
      if (args[i].equals("--board")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --board"), 3);
        processBoardArgument(args[i]);
        continue;
      }
      if (args[i].equals("--port")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --port"), 3);
        Base.selectSerialPort(args[i]);
        continue;
      }
      if (args[i].equals("--curdir")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --curdir"), 3);
        currentDirectory = args[i];
        continue;
      }
      if (args[i].equals("--pref")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --pref"), 3);
        processPrefArgument(args[i]);
        continue;
      }
      if (args[i].equals("--preferences-file")) {
        i++;
        if (i >= args.length)
          showError(null, _("Argument required for --preferences-file"), 3);
        // Argument should be already processed by Preferences.init(...) 
        continue;
      }
      if (args[i].startsWith("--"))
        showError(null, I18n.format(_("unknown option: {0}"), args[i]), 3);

      filenames.add(args[i]);
    }

    if ((action == ACTION.UPLOAD || action == ACTION.VERIFY) && filenames.size() != 1)
      showError(null, _("Must specify exactly one sketch file"), 3);

    if ((action != ACTION.UPLOAD && action != ACTION.VERIFY) && (doVerboseBuild || doVerboseUpload))
      showError(null, _("--verbose, --verbose-upload and --verbose-build can only be used together with --verify or --upload"), 3);

    for (String path: filenames) {
      // Fix a problem with systems that use a non-ASCII languages. Paths are
      // being passed in with 8.3 syntax, which makes the sketch loader code
      // unhappy, since the sketch folder naming doesn't match up correctly.
      // http://dev.processing.org/bugs/show_bug.cgi?id=1089
      if (isWindows()) {
        try {
          File file = new File(path);
          path = file.getCanonicalPath();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }

      if (!new File(path).isAbsolute()) {
        path = new File(currentDirectory, path).getAbsolutePath();
      }

      boolean showEditor = (action == ACTION.GUI);
      if (handleOpen(path, nextEditorLocation(), showEditor) == null) {
        String mess = I18n.format(_("Failed to open sketch: \"{0}\""), path);
        // Open failure is fatal in upload/verify mode
        if (action == ACTION.VERIFY || action == ACTION.UPLOAD)
          showError(null, mess, 2);
        else
          showWarning(null, mess, null);
      }
    }

    // Save the preferences. For GUI mode, this happens in the quit
    // handler, but for other modes we should also make sure to save
    // them.
    Preferences.save();

    switch (action) {
      case VERIFY:
      case UPLOAD:
        // Set verbosity for command line build
        Preferences.set("build.verbose", "" + doVerboseBuild);
        Preferences.set("upload.verbose", "" + doVerboseUpload);

        Editor editor = editors.get(0);

        if (action == ACTION.UPLOAD) {
          // Build and upload
          editor.exportHandler.run();
        } else {
          // Build only
          editor.runHandler.run();
        }

        // Error during build or upload
        int res = editor.status.mode;
        if (res == EditorStatus.ERR)
          System.exit(1);

        // No errors exit gracefully
        System.exit(0);
        break;
      case GUI:
        // Check if there were previously opened sketches to be restored
        restoreSketches();

        // Create a new empty window (will be replaced with any files to be opened)
{'fraction_non_alphanumeric': 0.11848502678764981, 'fraction_numerical': 0.005883992443714967, 'mean_word_length': 2.68, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3375298', 'n_tokens_mistral': 9195, 'n_tokens_neox': 8892, 'n_words': 2736}