text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/extensions/api/serial/serial_port_enumerator.h"
  
  #include "base/file_util.h"
  #include "base/files/file_enumerator.h"
  #include "base/files/file_path.h"
  #include "base/strings/string_util.h"
  #include "content/public/browser/browser_thread.h"
  
  using content::BrowserThread;
  
  namespace extensions {
  
  // static
  SerialPortEnumerator::StringSet SerialPortEnumerator::GenerateValidPatterns() {
    // TODO(miket): the set of patterns should be larger. See the rxtx project.
    //
    // TODO(miket): The list of patterns tested at runtime should also be
    // OS-dependent.
    const char* VALID_PATTERNS[] = {
      "/dev/*Bluetooth*",
      "/dev/*Modem*",
      "/dev/*bluetooth*",
      "/dev/*modem*",
      "/dev/*serial*",
      "/dev/ttyACM*",
      "/dev/ttyS*",
      "/dev/ttyUSB*",
      "/dev/tty.SLAB_*",
      "/dev/cu.SLAB_*",
    };
  
    StringSet valid_patterns;
    for (size_t i = 0; i < arraysize(VALID_PATTERNS); ++i)
      valid_patterns.insert(VALID_PATTERNS[i]);
  
    return valid_patterns;
  }
  
  // static
  //
  // TODO(miket): Investigate udev. Search for equivalent solutions on OSX.
  // Continue to examine rxtx code.
  //
  // On a fairly ordinary Linux machine, ls -l /dev | wc -l returned about 200
  // items. So we're doing about N(VALID_PATTERNS) * 200 = 1,600 MatchPattern
  // calls to find maybe a dozen serial ports in a small number of milliseconds
  // (a single trial of SerialPortEnumeratorTest.ValidPortNames took 6ms to run).
  // It's not cheap, but then again, we don't expect users of this API to be
  // enumerating too often (at worst on every click of a UI element that displays
  // the generated list).
  //
  // An upside-down approach would instead take each pattern and turn it into a
  // string generator (something like /dev/ttyS[0-9]{0,3}) and then expanding
  // that into a series of possible paths, perhaps early-outing if we knew that
  // port patterns were contiguous (e.g., /dev/ttyS1 can't exist if /dev/ttyS0
  // doesn't exist).
  //
  // Caching seems undesirable. Many devices can be dynamically added to and
  // removed from the system, so we really do want to regenerate the set each
  // time.
  //
  // TODO(miket): this might be refactorable into serial_connection.cc, if
  // Windows serial-port enumeration also entails looking through a directory.
  SerialPortEnumerator::StringSet
  SerialPortEnumerator::GenerateValidSerialPortNames() {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    const base::FilePath kDevRoot("/dev");
    const int kFilesAndSymLinks =
        base::FileEnumerator::FILES |
        base::FileEnumerator::SHOW_SYM_LINKS;
  
    StringSet valid_patterns = GenerateValidPatterns();
    StringSet name_set;
    base::FileEnumerator enumerator(kDevRoot, false, kFilesAndSymLinks);
    do {
      const base::FilePath next_device_path(enumerator.Next());
      const std::string next_device = next_device_path.value();
      if (next_device.empty())
        break;
  
      StringSet::const_iterator i = valid_patterns.begin();
      for (; i != valid_patterns.end(); ++i) {
        if (MatchPattern(next_device, *i)) {
          name_set.insert(next_device);
          break;
        }
      }
    } while (true);
  
    return name_set;
  }
  
  }  // namespace extensions
  