<commit_before>// Copyright 2017 Google Inc.
//
// 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
//
//      http://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.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_MSVC: &str = "shaderc_combined.lib";
static SPIRV_STATIC_LIB_FILE: &str = "libSPIRV.a";
static SPIRV_SHARED_LIB_FILE: &str = "libSPIRV.so";

fn build_shaderc(shaderc_dir: &PathBuf, use_ninja: bool) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib");
    if use_ninja {
        config.generator("Ninja");
    }
    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        // cmake-rs tries to be clever on Windows by injecting several
        // C/C++ flags, which causes problems. So I have to manually
        // define CMAKE_*_FLAGS_* here to suppress that.
        .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
        // prevent shaderc's cmake script messes with crt flags
        .define("SHADERC_ENABLE_SHARED_CRT", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .generator("Ninja");
    config.build()
}

fn main() {
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let explicit_lib_dir_set = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit libshaderc search directory first
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=Specified {} to search for shaderc libraries",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // If no explicit path is set and no explicit request is made to build from
    // source, check known locations before falling back to from-source-build
    if search_dir.is_none() && target_os == "linux" && !config_build_from_source {
        println!(
            "cargo:warning=Checking for system installed libraries.  \
             Use --features = build-from-source to disable this behavior"
        );
        search_dir = Some("/usr/lib/".to_owned());
    }

    // Try to build with the static lib if a path was received or chosen
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=Provided path {:?} is relative.  Path must be \
                 relative to shaderc-sys crate, possibly not your current \
                 working directory",
                &path
            );
        } else if path.is_dir() == false {
            println!("cargo:warning=Provided path {:?} is not a directory", &path);
        }
        if (cannonical.is_err()) && explicit_lib_dir_set {
            println!("cargo:warning={:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=Provided path {:?} could not be canonicallized",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();
        let combined_lib_path =
            search_dir.join(if target_os == "windows" && target_env == "msvc" {
                SHADERC_STATIC_LIB_FILE_MSVC
            } else {
                SHADERC_STATIC_LIB_FILE
            });
        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name.clone());

        if let Some((lib_dir, lib_name, kind)) = {
            if combined_lib_path.exists() {
                Some((&search_dir_str, SHADERC_STATIC_LIB, "static"))
            } else if dylib_path.exists() {
                Some((&search_dir_str, SHADERC_SHARED_LIB, "dylib"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    if let Some((spirv_lib_kind, spirv_lib_link)) = {
                        if search_dir.join(SPIRV_SHARED_LIB_FILE).exists() {
                            Some(("shared", ""))
                        } else if search_dir.join(SPIRV_STATIC_LIB_FILE).exists() {
                            Some(("static", "static="))
                        } else {
                            None
                        }
                    } {
                        println!(
                            "cargo:warning=Found and linking {} system installed SPIRV libraries.",
                            spirv_lib_kind
                        );
                        println!("cargo:rustc-link-lib={}SPIRV", spirv_lib_link);
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools-opt");
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools");
                        println!("cargo:rustc-link-lib=glslang");
                    } else {
                        println!(
                            "cargo:warning=Only shaderc library found.  \
                             Assuming SPIRV & glslang libraries built into provided \
                             shaderc library."
                        );
                    }
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=Windows MSVC static builds experimental");
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=Windows MinGW static builds experimental");
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=MacOS static builds experimental");
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!("cargo:warning=Platform unsupported for linking against system installed shaderc libraries");
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=Requested to build from source");
    } else {
        println!(
            "cargo:warning=System installed library not found.  Falling back \
             to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.must_have("python");

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc(&shaderc_dir, has_ninja)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static=shaderc_combined");

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<commit_msg>Probe to use shared libraries for all depedencies if possible<commit_after>// Copyright 2017 Google Inc.
//
// 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
//
//      http://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.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_MSVC: &str = "shaderc_combined.lib";

fn build_shaderc(shaderc_dir: &PathBuf, use_ninja: bool) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib");
    if use_ninja {
        config.generator("Ninja");
    }
    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        // cmake-rs tries to be clever on Windows by injecting several
        // C/C++ flags, which causes problems. So I have to manually
        // define CMAKE_*_FLAGS_* here to suppress that.
        .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
        // prevent shaderc's cmake script messes with crt flags
        .define("SHADERC_ENABLE_SHARED_CRT", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .generator("Ninja");
    config.build()
}

fn probe_linux_lib_kind(lib_name: &str, search_dir: &PathBuf) -> Option<&'static str> {
    if search_dir.join(&format!("lib{}.so", lib_name)).exists() {
        Some("dylib")
    } else if search_dir.join(&format!("lib{}.a", lib_name)).exists() {
        Some("static")
    } else {
        None
    }
}

fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let explicit_lib_dir_set = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit libshaderc search directory first
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=Specified {} to search for shaderc libraries",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // If no explicit path is set and no explicit request is made to build from
    // source, check known locations before falling back to from-source-build
    if search_dir.is_none() && target_os == "linux" && !config_build_from_source {
        println!(
            "cargo:warning=Checking for system installed libraries.  \
             Use --features = build-from-source to disable this behavior"
        );
        search_dir = Some("/usr/lib/".to_owned());
    }

    // Try to build with the static lib if a path was received or chosen
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=Provided path {:?} is relative.  Path must be \
                 relative to shaderc-sys crate, possibly not your current \
                 working directory",
                &path
            );
        } else if path.is_dir() == false {
            println!("cargo:warning=Provided path {:?} is not a directory", &path);
        }
        if (cannonical.is_err()) && explicit_lib_dir_set {
            println!("cargo:warning={:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=Provided path {:?} could not be canonicallized",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();

        let combined_lib_path =
            search_dir.join(if target_os == "windows" && target_env == "msvc" {
                SHADERC_STATIC_LIB_FILE_MSVC
            } else {
                SHADERC_STATIC_LIB_FILE
            });

        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name.clone());

        if let Some((lib_name, kind)) = {
            if combined_lib_path.exists() {
                Some((SHADERC_STATIC_LIB, "static"))
            } else if dylib_path.exists() {
                Some((SHADERC_SHARED_LIB, "dylib"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", search_dir_str);

                    // Libraries from the SPIRV-Tools project
                    let spirv_tools_lib = probe_linux_lib_kind("SPIRV-Tools", &search_dir);
                    let spirv_tools_opt_lib = probe_linux_lib_kind("SPIRV-Tools-opt", &search_dir);
                    // Libraries from the Glslang project
                    let spirv_lib = probe_linux_lib_kind("SPIRV", &search_dir);
                    let glslang_lib = probe_linux_lib_kind("glslang", &search_dir);

                    if spirv_tools_lib.is_none()
                        || spirv_tools_opt_lib.is_none()
                        || spirv_lib.is_none()
                        || glslang_lib.is_none()
                    {
                        println!(
                            "cargo:warning=Only shaderc library found.  \
                             Assuming libraries it depends on are built into \
                             the shaderc library."
                        );
                    } else {
                        println!("cargo:warning=Found and linking system installed Glslang and SPIRV-Tools libraries.");
                        println!("cargo:rustc-link-lib={}=glslang", glslang_lib.unwrap());
                        println!("cargo:rustc-link-lib={}=SPIRV", spirv_lib.unwrap());
                        println!(
                            "cargo:rustc-link-lib={}=SPIRV-Tools",
                            spirv_tools_lib.unwrap()
                        );
                        println!(
                            "cargo:rustc-link-lib={}=SPIRV-Tools-opt",
                            spirv_tools_opt_lib.unwrap()
                        );
                    }
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=Windows MSVC static builds experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=Windows MinGW static builds experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=MacOS static builds experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!("cargo:warning=Platform unsupported for linking against system installed shaderc libraries");
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=Requested to build from source");
    } else {
        println!(
            "cargo:warning=System installed library not found.  Falling back \
             to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.must_have("python");

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc(&shaderc_dir, has_ninja)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static=shaderc_combined");

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This file contains a helper macro includes all supported non-tree-structural
 * pseudo-classes.
 *

 * FIXME: Find a way to autogenerate this file.
 *
 * Expected usage is as follows:
 * ```
 * macro_rules! pseudo_class_macro{
 *     (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
 *      string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
 *      keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
 *         // do stuff
 *     }
 * }
 * apply_non_ts_list!(pseudo_class_macro)
 * ```
 *
 * The `string` and `keyword` variables will be applied to pseudoclasses that are of the form of
 * functions with string or keyword arguments.
 *
 * Pending pseudo-classes:
 *
 *  :-moz-lwtheme, :-moz-lwtheme-brighttext, :-moz-lwtheme-darktext,
 *  :-moz-window-inactive.
 *
 *  :scope -> <style scoped>, pending discussion.
 *
 * This follows the order defined in layout/style/nsCSSPseudoClassList.h when
 * possible.
 *
 * $gecko_type can be either "_" or an ident in Gecko's CSSPseudoClassType.
 * $state can be either "_" or an expression of type ElementState.  If present,
 *        the semantics are that the pseudo-class matches if any of the bits in
 *        $state are set on the element.
 * $flags can be either "_" or an expression of type NonTSPseudoClassFlag,
 * see selector_parser.rs for more details.
 */

macro_rules! apply_non_ts_list {
    ($apply_macro:ident) => {
        $apply_macro! {
            bare: [
                ("unresolved", Unresolved, unresolved, IN_UNRESOLVED_STATE, _),
                ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_INTERNAL),
                ("link", Link, link, IN_UNVISITED_STATE, _),
                ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _),
                ("visited", Visited, visited, IN_VISITED_STATE, _),
                ("active", Active, active, IN_ACTIVE_STATE, _),
                ("checked", Checked, checked, IN_CHECKED_STATE, _),
                ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
                ("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
                ("focus", Focus, focus, IN_FOCUS_STATE, _),
                ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _),
                ("hover", Hover, hover, IN_HOVER_STATE, _),
                ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _),
                ("target", Target, target, IN_TARGET_STATE, _),
                ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
                ("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, _),
                ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, _),
                // TODO(emilio): Needs pref check for
                // full-screen-api.unprefix.enabled!
                ("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _),
                // TODO(emilio): This is inconsistently named (the capital R).
                ("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _),
                ("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _),
                ("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _),
                ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, mozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),

                ("required", Required, required, IN_REQUIRED_STATE, _),
                ("optional", Optional, optional, IN_OPTIONAL_STATE, _),
                ("valid", Valid, valid, IN_VALID_STATE, _),
                ("invalid", Invalid, invalid, IN_INVALID_STATE, _),
                ("in-range", InRange, inRange, IN_INRANGE_STATE, _),
                ("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _),
                ("default", Default, defaultPseudo, IN_DEFAULT_STATE, _),
                ("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
                ("-moz-read-only", MozReadOnly, mozReadOnly, IN_MOZ_READONLY_STATE, _),
                ("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _),
                ("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
                ("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _),
                ("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
                ("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _),
                ("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
                ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _),

                ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-first-node", MozFirstNode, firstNode, _, _),
                ("-moz-last-node", MozLastNode, lastNode, _, _),
                ("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _),
                ("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-is-html", MozIsHTML, mozIsHTML, _, _),
                ("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _),
            ],
            string: [
                ("-moz-system-metric", MozSystemMetric, mozSystemMetric, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-empty-except-children-with-localname", MozEmptyExceptChildrenWithLocalname,
                 mozEmptyExceptChildrenWithLocalname, _, PSEUDO_CLASS_INTERNAL),
                ("lang", Lang, lang, _, _),
            ],
            keyword: [
                ("-moz-locale-dir", MozLocaleDir, mozLocaleDir, _, PSEUDO_CLASS_INTERNAL),
                ("dir", Dir, dir, _, _),
            ]
        }
    }
}
<commit_msg>Auto merge of #17234 - mbrubeck:dir, r=xidorn<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This file contains a helper macro includes all supported non-tree-structural
 * pseudo-classes.
 *

 * FIXME: Find a way to autogenerate this file.
 *
 * Expected usage is as follows:
 * ```
 * macro_rules! pseudo_class_macro{
 *     (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
 *      string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
 *      keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
 *         // do stuff
 *     }
 * }
 * apply_non_ts_list!(pseudo_class_macro)
 * ```
 *
 * The `string` and `keyword` variables will be applied to pseudoclasses that are of the form of
 * functions with string or keyword arguments.
 *
 * Pending pseudo-classes:
 *
 *  :-moz-lwtheme, :-moz-lwtheme-brighttext, :-moz-lwtheme-darktext,
 *  :-moz-window-inactive.
 *
 *  :scope -> <style scoped>, pending discussion.
 *
 * This follows the order defined in layout/style/nsCSSPseudoClassList.h when
 * possible.
 *
 * $gecko_type can be either "_" or an ident in Gecko's CSSPseudoClassType.
 * $state can be either "_" or an expression of type ElementState.  If present,
 *        the semantics are that the pseudo-class matches if any of the bits in
 *        $state are set on the element.
 * $flags can be either "_" or an expression of type NonTSPseudoClassFlag,
 * see selector_parser.rs for more details.
 */

macro_rules! apply_non_ts_list {
    ($apply_macro:ident) => {
        $apply_macro! {
            bare: [
                ("unresolved", Unresolved, unresolved, IN_UNRESOLVED_STATE, _),
                ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_INTERNAL),
                ("link", Link, link, IN_UNVISITED_STATE, _),
                ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _),
                ("visited", Visited, visited, IN_VISITED_STATE, _),
                ("active", Active, active, IN_ACTIVE_STATE, _),
                ("checked", Checked, checked, IN_CHECKED_STATE, _),
                ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
                ("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
                ("focus", Focus, focus, IN_FOCUS_STATE, _),
                ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _),
                ("hover", Hover, hover, IN_HOVER_STATE, _),
                ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _),
                ("target", Target, target, IN_TARGET_STATE, _),
                ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
                ("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, _),
                ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, _),
                // TODO(emilio): Needs pref check for
                // full-screen-api.unprefix.enabled!
                ("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _),
                // TODO(emilio): This is inconsistently named (the capital R).
                ("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _),
                ("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _),
                ("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _),
                ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_INTERNAL),
                ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, mozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),

                ("required", Required, required, IN_REQUIRED_STATE, _),
                ("optional", Optional, optional, IN_OPTIONAL_STATE, _),
                ("valid", Valid, valid, IN_VALID_STATE, _),
                ("invalid", Invalid, invalid, IN_INVALID_STATE, _),
                ("in-range", InRange, inRange, IN_INRANGE_STATE, _),
                ("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _),
                ("default", Default, defaultPseudo, IN_DEFAULT_STATE, _),
                ("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
                ("-moz-read-only", MozReadOnly, mozReadOnly, IN_MOZ_READONLY_STATE, _),
                ("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _),
                ("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
                ("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _),
                ("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
                ("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _),
                ("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
                ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _),

                ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_INTERNAL),

                ("-moz-first-node", MozFirstNode, firstNode, _, _),
                ("-moz-last-node", MozLastNode, lastNode, _, _),
                ("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _),
                ("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-is-html", MozIsHTML, mozIsHTML, _, _),
                ("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _),
            ],
            string: [
                ("-moz-system-metric", MozSystemMetric, mozSystemMetric, _, PSEUDO_CLASS_INTERNAL),
                ("-moz-empty-except-children-with-localname", MozEmptyExceptChildrenWithLocalname,
                 mozEmptyExceptChildrenWithLocalname, _, PSEUDO_CLASS_INTERNAL),
                ("lang", Lang, lang, _, _),
            ],
            keyword: [
                ("-moz-locale-dir", MozLocaleDir, mozLocaleDir, _, _),
                ("dir", Dir, dir, _, _),
            ]
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Color", inherited=True) %>

<%helpers:raw_longhand name="color" need_clone="True" animatable="True">
    use cssparser::Color as CSSParserColor;
    use cssparser::RGBA;
    use values::specified::{CSSColor, CSSRGBA};

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
            self.parsed
        }
    }

    pub type SpecifiedValue = CSSRGBA;
    pub mod computed_value {
        use cssparser;
        pub type T = cssparser::RGBA;
    }
    #[inline] pub fn get_initial_value() -> computed_value::T {
        RGBA { red: 0., green: 0., blue: 0., alpha: 1. }  /* black */
    }
    pub fn parse_specified(_context: &ParserContext, input: &mut Parser)
                           -> Result<DeclaredValue<SpecifiedValue>, ()> {
        let value = try!(CSSColor::parse(input));
        let rgba = match value.parsed {
            CSSParserColor::RGBA(rgba) => rgba,
            CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit)
        };
        Ok(DeclaredValue::Value(CSSRGBA {
            parsed: rgba,
            authored: value.authored,
        }))
    }
</%helpers:raw_longhand>
<commit_msg>style: stylistic nit. be consistent on how we write inline annotations.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Color", inherited=True) %>

<%helpers:raw_longhand name="color" need_clone="True" animatable="True">
    use cssparser::Color as CSSParserColor;
    use cssparser::RGBA;
    use values::specified::{CSSColor, CSSRGBA};

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
            self.parsed
        }
    }

    pub type SpecifiedValue = CSSRGBA;
    pub mod computed_value {
        use cssparser;
        pub type T = cssparser::RGBA;
    }
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        RGBA { red: 0., green: 0., blue: 0., alpha: 1. }  /* black */
    }
    pub fn parse_specified(_context: &ParserContext, input: &mut Parser)
                           -> Result<DeclaredValue<SpecifiedValue>, ()> {
        let value = try!(CSSColor::parse(input));
        let rgba = match value.parsed {
            CSSParserColor::RGBA(rgba) => rgba,
            CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit)
        };
        Ok(DeclaredValue::Value(CSSRGBA {
            parsed: rgba,
            authored: value.authored,
        }))
    }
</%helpers:raw_longhand>
<|endoftext|>


<commit_before>/// Figure 4.22: Recursively descend a directory hierarchy, counting file types
///
/// TODO: explain why this is better than the C program, object oriented and yada yada
/// TODO: doctest

extern crate clap;
extern crate libc;
extern crate apue;
extern crate errno;

use libc::{stat, S_IFMT, S_IFDIR, S_IFREG, S_IFBLK, S_IFCHR,
    S_IFIFO, S_IFLNK, S_IFSOCK, opendir, closedir, lstat};
use apue::{LibcResult, array_to_string};
use apue::my_libc::readdir;
use clap::App;
use std::ffi::CString;

struct Counter {
    nreg:u64,
    ndir:u64,
    nblk:u64,
    nchr:u64,
    nfifo:u64,
    nslink:u64,
    nsock:u64,
}

impl Counter {
    fn count_file(&mut self, path: &str, statbuf: &stat) {
        match statbuf.st_mode & S_IFMT {
            S_IFREG => self.nreg += 1,
            S_IFBLK => self.nblk += 1,
            S_IFCHR => self.nchr += 1,
            S_IFIFO => self.nfifo += 1,
            S_IFLNK => self.nslink += 1,
            S_IFSOCK => self.nsock += 1,
            // directories should have type = FTW_D
            S_IFDIR => panic!("found S_IFDIR for {}", path),
            _ => panic!("unknown stat: {}", statbuf.st_mode),
        }
    }
    fn count_other(&mut self, path: &str, typ: FileType) {
        match typ {
            FileType::Directory => self.ndir+= 1,
            FileType::DirectoryCannotRead => println!("cannot read dir: {}", path),
            FileType::FileCannotStat => println!("cannot stat file: {}", path),
        };
    }
}

impl std::fmt::Debug for Counter {
    // TODO: percentages
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, r#"
        {:8} regular files
        {:8} directories
        {:8} symbolic links
        {:8} block special
        {:8} char special
        {:8} FIFOs
        {:8} sockets"#,
               self.nreg, self.ndir, self.nslink, self.nblk, self.nchr, self.nfifo, self.nsock)
    }
}

enum FileType {
    Directory,
    DirectoryCannotRead,
    FileCannotStat,
}

unsafe fn myftw(path: &str, cnt: &mut Counter) {
    let p = CString::new(path).unwrap();
    let dp = match opendir(p.as_ptr()).to_option() {
        Some(dp) => dp,
        None => {
            println!("cannot open dir: {}", errno::errno());
            cnt.count_other(path, FileType::DirectoryCannotRead);
            return
        },
    };
    while let Some(dirp) = readdir(dp).to_option() {
        let filename = array_to_string(&(*dirp).d_name);
        if filename == "." || filename == ".." {
            continue;
        }
        let filename = format!("{}/{}", path, filename);
        let mut statbuf:stat = std::mem::uninitialized();
        if let None = lstat(CString::new(filename.to_owned()).unwrap().as_ptr(), &mut statbuf).to_option() {
            cnt.count_other(&filename, FileType::FileCannotStat);
            continue;
        }
        match statbuf.st_mode & S_IFMT {
            S_IFDIR => {
                cnt.count_other(&filename, FileType::Directory);
                myftw(&filename, cnt);
            },
            _ => cnt.count_file(&filename, &statbuf),
        }
    }
    if let None = closedir(dp).to_option() {
        panic!("can’t close directory {}", path);
    }
}

fn main() {
    unsafe {
        // put ndir=1 because we first start with the initial dir
        let mut c = Counter { nreg: 0, ndir: 1, nblk: 0, nchr: 0, nfifo: 0, nslink: 0, nsock: 0};
        let matches = App::new("tree-traversal")
            .args_from_usage("<path> beginning path for traversal").get_matches();
        let path = matches.value_of("path").unwrap();
        myftw(path, &mut c);
        println!("{:?}", c);
    }
}

<commit_msg>add documentation, doctest, reduce string-copying<commit_after>/// Figure 4.22: Recursively descend a directory hierarchy, counting file types
///
/// takeaways:
///
/// - calling the libc functions was straightforward (with the exception of readdir which
///   is missing in rusts libc (only readdir_r is there)
/// - first forgot to call `closedir` and ran into the "too many files open" limit
/// - the C code in the book suffers is hard to read. Rusts OOP features help a lot in making
///   the code easier to read:
///   + Counter struct instead of static variables and passing a function
///     pointer
///   + Enum instead of constants
///
/// timing wise this rust implementation is about 100% slower than the C program,
/// most probably due to string copying (solved some heavy issues already, but still some
/// are remaining)
///
/// $ rm -rf /tmp/f21
/// $ mkdir /tmp/f21
/// $ touch /tmp/f21/{a,b,c,d,e}
/// $ ln -s /tmp /tmp/f21/tmp
/// $ mkdir /tmp/f21/0
/// $ touch /tmp/f21/0/{f,g,e}
/// $ f22-path-traversal /tmp/f21
///                8 regular files    72.73%
///                2 directories      18.18%
///                1 symbolic links    9.09%
///                0 block special     0.00%
///                0 char special      0.00%
///                0 FIFOs             0.00%
///                0 sockets           0.00%
/// $ rm -rf /tmp/f21

extern crate clap;
extern crate libc;
#[macro_use(print_err)]
extern crate apue;
extern crate errno;

use libc::{stat, S_IFMT, S_IFDIR, S_IFREG, S_IFBLK, S_IFCHR,
    S_IFIFO, S_IFLNK, S_IFSOCK, opendir, closedir, lstat};
use apue::{LibcResult};
use apue::my_libc::readdir;
use clap::App;
use std::ffi::{CString, CStr};

struct Counter {
    nreg:u64,
    ndir:u64,
    nblk:u64,
    nchr:u64,
    nfifo:u64,
    nslink:u64,
    nsock:u64,
}

impl Counter {
    fn count_file(&mut self, path: &str, statbuf: &stat) {
        match statbuf.st_mode & S_IFMT {
            S_IFREG => self.nreg += 1,
            S_IFBLK => self.nblk += 1,
            S_IFCHR => self.nchr += 1,
            S_IFIFO => self.nfifo += 1,
            S_IFLNK => self.nslink += 1,
            S_IFSOCK => self.nsock += 1,
            // directories should have type = FTW_D
            S_IFDIR => panic!("found S_IFDIR for {}", path),
            _ => panic!("unknown stat: {}", statbuf.st_mode),
        }
    }
    fn count_other(&mut self, path: &str, typ: FileType) {
        match typ {
            FileType::Directory => self.ndir+= 1,
            FileType::DirectoryCannotRead => print_err!("cannot read dir: {}", path),
            FileType::FileCannotStat => print_err!("cannot stat file: {}", path),
        };
    }
}

impl std::fmt::Debug for Counter {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let total = (self.nsock+self.ndir+self.nslink+self.nfifo+self.nblk+self.nchr+self.nreg) as f32 / 100.0;
        write!(f, r#"        {0:8} regular files    {7:5.2}%
        {1:8} directories      {8:5.2}%
        {2:8} symbolic links   {9:5.2}%
        {3:8} block special    {10:5.2}%
        {4:8} char special     {11:5.2}%
        {5:8} FIFOs            {12:5.2}%
        {6:8} sockets          {13:5.2}%"#,
               self.nreg, self.ndir, self.nslink, self.nblk, self.nchr, self.nfifo, self.nsock,
               self.nreg as f32 / total, self.ndir as f32 / total, self.nslink as f32 / total,
               self.nblk as f32 / total, self.nchr as f32 / total, self.nfifo as f32 / total,
               self.nsock as f32 / total
        )
    }
}

enum FileType {
    Directory,
    DirectoryCannotRead,
    FileCannotStat,
}

unsafe fn myftw(path: &str, cnt: &mut Counter) {
    let p = CString::new(path).unwrap();
    let dp = match opendir(p.as_ptr()).to_option() {
        Some(dp) => dp,
        None => {
            print_err!("cannot open dir: {}", errno::errno());
            cnt.count_other(path, FileType::DirectoryCannotRead);
            return
        },
    };
    while let Some(dirp) = readdir(dp).to_option() {
        let filename = CStr::from_ptr((&(*dirp).d_name).as_ptr()).to_str().expect("invalid string");
        if filename == "." || filename == ".." {
            continue;
        }
        let filename = [path, &filename].join("/");
        let mut statbuf:stat = std::mem::uninitialized();
        if let None = lstat(CString::new(filename.to_owned()).unwrap().as_ptr(), &mut statbuf).to_option() {
            cnt.count_other(&filename, FileType::FileCannotStat);
            continue;
        }
        match statbuf.st_mode & S_IFMT {
            S_IFDIR => {
                cnt.count_other(&filename, FileType::Directory);
                myftw(&filename, cnt);
            },
            _ => cnt.count_file(&filename, &statbuf),
        }
    }
    if let None = closedir(dp).to_option() {
        panic!("can’t close directory {}", path);
    }
}

fn main() {
    unsafe {
        // put ndir=1 because we first start with the initial dir
        let mut c = Counter { nreg: 0, ndir: 1, nblk: 0, nchr: 0, nfifo: 0, nslink: 0, nsock: 0};
        let matches = App::new("tree-traversal")
            .args_from_usage("<path> beginning path for traversal").get_matches();
        let path = matches.value_of("path").unwrap();
        myftw(path, &mut c);
        println!("{:?}", c);
    }
}

<|endoftext|>


<commit_before>use log::{debug};
use goblin::{Object};
use failure::{Error};
use byteorder::{ByteOrder, LittleEndian};

use super::super::super::arch::{RVA};
use super::super::super::workspace::Workspace;
use super::super::{Analyzer};

pub struct ImportsAnalyzer {}

impl ImportsAnalyzer {
    pub fn new() -> ImportsAnalyzer {
        ImportsAnalyzer {}
    }
}

pub struct ImageImportDescriptor {
    pub original_first_thunk: RVA,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name: RVA,
    pub first_thunk: RVA,
}

impl ImageImportDescriptor {
    pub fn is_empty(&self) -> bool {
        self.original_first_thunk == RVA(0x0) &&
            self.time_date_stamp == 0x0 &&
            self.forwarder_chain == 0x0 &&
            self.name == RVA(0x0) &&
            self.first_thunk == RVA(0x0)
    }
}

impl std::fmt::Debug for ImageImportDescriptor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_DESCRIPTOR(FT: {} OFT: {} name: {})",
               self.first_thunk,
               self.original_first_thunk,
               self.name
        )
    }
}

pub fn read_image_import_descriptor(ws: &Workspace, rva: RVA) -> Result<ImageImportDescriptor, Error> {
    let buf = ws.read_bytes(rva, 5 * 4)?;

    // these fields are all u32, even on 64-bit
    let entries: Vec<u32> = buf
        .chunks_exact(0x4)
        .map(|b| LittleEndian::read_u32(b))
        .collect();

    Ok(ImageImportDescriptor {
        original_first_thunk: RVA::from(entries[0] as i64),
        time_date_stamp: entries[1],
        forwarder_chain: entries[2],
        name: RVA::from(entries[3] as i64),
        first_thunk: RVA::from(entries[4] as i64),
    })
}

pub enum ImageThunkData {
    Function(RVA),
    Ordinal(u32),
}

pub fn read_image_thunk_data(ws: &Workspace, rva: RVA) -> Result<ImageThunkData, Error> {
    // see: https://reverseengineering.stackexchange.com/a/13387/17194
    let thunk = ws.read_rva(rva)?;
    let v: i64 = thunk.into();
    if v & (1 << (ws.loader.get_arch().get_pointer_size() * 8 - 1)) > 0x0 {
        // MSB is set, this is an ordinal
        Ok(ImageThunkData::Ordinal((v & 0xFFFF) as u32))
    } else {
        Ok(ImageThunkData::Function(thunk))
    }
}

pub struct ImageImportByName {
    pub hint: u16,
    pub name: String,
}

impl std::fmt::Debug for ImageImportByName {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_BY_NAME(hint: {} name: {})",
               self.hint,
               self.name
        )
    }
}

pub fn read_image_import_by_name(ws: &Workspace, rva: RVA) -> Result<ImageImportByName, Error> {
    Ok(ImageImportByName {
        hint: ws.read_u16(rva)?,
        name: ws.read_utf8(rva + RVA::from(2))?,
    })
}

impl Analyzer for ImportsAnalyzer {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::new();
    /// anal.analyze(&mut ws).unwrap();
    /// assert_eq!(ws.get_symbol(RVA(0x77448)).unwrap(), "api-ms-win-core-appcompat-l1-1-1.dll!BaseReadAppCompatDataForProcess");
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let import_directory = {
            let pe = match Object::parse(&ws.buf) {
                Ok(Object::PE(pe)) => pe,
                _ => panic!("can't analyze unexpected format"),
            };

            let opt_header = match pe.header.optional_header {
                Some(opt_header) => opt_header,
                _ => return Ok(()),
            };

            let import_directory = match opt_header.data_directories.get_import_table() {
                Some(import_directory) => import_directory,
                _ => return Ok(()),
            };

            RVA::from(import_directory.virtual_address as i64)
        };

        debug!("found import directory: {}", import_directory);

        let mut symbols: Vec<(RVA, String)> = vec![];

        let psize: usize = ws.loader.get_arch().get_pointer_size() as usize;
        for i in 0..std::usize::MAX {
            //
            //  0x0                    0x14
            //  +-------------------------+           0x0          ptrsize
            //  | IMAGE_IMPORT_DESCRIPTOR | --------> +------------------+
            //  +-------------------------+ \         | IMAGE_THUNK_DATA | -------> +----------------------+
            //  | ...                     |  |        +------------------+          | IMAGE_IMPORT_BY_NAME |
            //  +-------------------------+  |        | ...              |          +----------------------+
            //  | 00 00 00 00 00 00 000   |  |        +------------------+          | hint u16             |
            //  +-------------------------+  |        | 00 00 00 00 00   |          | name ascii           |
            //                               |        +------------------+          +----------------------+
            //                               |
            //                               +> dll-name (ascii)
            let import_descriptor_rva = import_directory + RVA::from(i * 0x14);
            let import_descriptor = read_image_import_descriptor(ws, import_descriptor_rva)?;
            if import_descriptor.is_empty() {
                break;
            }

            let dll_name = ws.read_utf8(import_descriptor.name)?;
            debug!("found {:?} -> {}", import_descriptor, dll_name);

            for j in 0..std::usize::MAX {
                // the Original First Thunk (OFT) remains constant, and points to the IMAGE_IMPORT_BY_NAME.
                // FT and OFT are parallel arrays.
                let _original_first_thunk = import_descriptor.original_first_thunk + RVA::from(j * psize);

                // the First Thunk (FT) is the pointer that will be overwritten upon load.
                // entries here may not point to the IMAGE_IMPORT_BY_NAME.
                let first_thunk = import_descriptor.first_thunk + RVA::from(j * psize);

                match read_image_thunk_data(ws, first_thunk)? {
                    ImageThunkData::Function(rva) => {
                        if rva == RVA(0x0) {
                            break;
                        } else {
                            let imp = read_image_import_by_name(ws, rva)?;
                            debug!("found {:?}", imp);

                            symbols.push((first_thunk, format!("{}!{}", dll_name, imp.name)))
                        }
                    },
                    ImageThunkData::Ordinal(ord) => {
                        symbols.push((first_thunk, format!("{}!#{}", dll_name, ord)))
                    },
                };
            }
        }

        for (rva, name) in symbols.iter() {
            debug!("found import: {} -> {}", rva, name);
            ws.make_symbol(*rva, name)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<commit_msg>imports: prefer to use FT over OFT<commit_after>use log::{debug};
use goblin::{Object};
use failure::{Error};
use byteorder::{ByteOrder, LittleEndian};

use super::super::super::arch::{RVA};
use super::super::super::workspace::Workspace;
use super::super::{Analyzer};

pub struct ImportsAnalyzer {}

impl ImportsAnalyzer {
    pub fn new() -> ImportsAnalyzer {
        ImportsAnalyzer {}
    }
}

pub struct ImageImportDescriptor {
    pub original_first_thunk: RVA,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name: RVA,
    pub first_thunk: RVA,
}

impl ImageImportDescriptor {
    pub fn is_empty(&self) -> bool {
        self.original_first_thunk == RVA(0x0) &&
            self.time_date_stamp == 0x0 &&
            self.forwarder_chain == 0x0 &&
            self.name == RVA(0x0) &&
            self.first_thunk == RVA(0x0)
    }
}

impl std::fmt::Debug for ImageImportDescriptor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_DESCRIPTOR(FT: {} OFT: {} name: {})",
               self.first_thunk,
               self.original_first_thunk,
               self.name
        )
    }
}

pub fn read_image_import_descriptor(ws: &Workspace, rva: RVA) -> Result<ImageImportDescriptor, Error> {
    let buf = ws.read_bytes(rva, 5 * 4)?;

    // these fields are all u32, even on 64-bit
    let entries: Vec<u32> = buf
        .chunks_exact(0x4)
        .map(|b| LittleEndian::read_u32(b))
        .collect();

    Ok(ImageImportDescriptor {
        original_first_thunk: RVA::from(entries[0] as i64),
        time_date_stamp: entries[1],
        forwarder_chain: entries[2],
        name: RVA::from(entries[3] as i64),
        first_thunk: RVA::from(entries[4] as i64),
    })
}

pub enum ImageThunkData {
    Function(RVA),
    Ordinal(u32),
}

pub fn read_image_thunk_data(ws: &Workspace, rva: RVA) -> Result<ImageThunkData, Error> {
    // see: https://reverseengineering.stackexchange.com/a/13387/17194
    let thunk = ws.read_rva(rva)?;
    let v: i64 = thunk.into();
    if v & (1 << (ws.loader.get_arch().get_pointer_size() * 8 - 1)) > 0x0 {
        // MSB is set, this is an ordinal
        Ok(ImageThunkData::Ordinal((v & 0xFFFF) as u32))
    } else {
        Ok(ImageThunkData::Function(thunk))
    }
}

pub struct ImageImportByName {
    pub hint: u16,
    pub name: String,
}

impl std::fmt::Debug for ImageImportByName {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_BY_NAME(hint: {} name: {})",
               self.hint,
               self.name
        )
    }
}

pub fn read_image_import_by_name(ws: &Workspace, rva: RVA) -> Result<ImageImportByName, Error> {
    Ok(ImageImportByName {
        hint: ws.read_u16(rva)?,
        name: ws.read_utf8(rva + RVA::from(2))?,
    })
}

impl Analyzer for ImportsAnalyzer {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::new();
    /// anal.analyze(&mut ws).unwrap();
    /// assert_eq!(ws.get_symbol(RVA(0x77448)).unwrap(), "api-ms-win-core-appcompat-l1-1-1.dll!BaseReadAppCompatDataForProcess");
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let import_directory = {
            let pe = match Object::parse(&ws.buf) {
                Ok(Object::PE(pe)) => pe,
                _ => panic!("can't analyze unexpected format"),
            };

            let opt_header = match pe.header.optional_header {
                Some(opt_header) => opt_header,
                _ => return Ok(()),
            };

            let import_directory = match opt_header.data_directories.get_import_table() {
                Some(import_directory) => import_directory,
                _ => return Ok(()),
            };

            RVA::from(import_directory.virtual_address as i64)
        };

        debug!("found import directory: {}", import_directory);

        let mut symbols: Vec<(RVA, String)> = vec![];

        let psize: usize = ws.loader.get_arch().get_pointer_size() as usize;
        for i in 0..std::usize::MAX {
            //
            //  0x0                    0x14
            //  +-------------------------+           0x0          ptrsize
            //  | IMAGE_IMPORT_DESCRIPTOR | --------> +------------------+
            //  +-------------------------+ \         | IMAGE_THUNK_DATA | -------> +----------------------+
            //  | ...                     |  |        +------------------+          | IMAGE_IMPORT_BY_NAME |
            //  +-------------------------+  |        | ...              |          +----------------------+
            //  | 00 00 00 00 00 00 000   |  |        +------------------+          | hint u16             |
            //  +-------------------------+  |        | 00 00 00 00 00   |          | name ascii           |
            //                               |        +------------------+          +----------------------+
            //                               |
            //                               +> dll-name (ascii)
            let import_descriptor_rva = import_directory + RVA::from(i * 0x14);
            let import_descriptor = read_image_import_descriptor(ws, import_descriptor_rva)?;
            if import_descriptor.is_empty() {
                break;
            }

            let dll_name = ws.read_utf8(import_descriptor.name)?;
            debug!("found {:?} -> {}", import_descriptor, dll_name);

            for j in 0..std::usize::MAX {
                // the Original First Thunk (OFT) remains constant, and points to the IMAGE_IMPORT_BY_NAME.
                // FT and OFT are parallel arrays.
                let _original_first_thunk = import_descriptor.original_first_thunk + RVA::from(j * psize);

                // the First Thunk (FT) is the pointer that will be overwritten upon load.
                // entries here may not point to the IMAGE_IMPORT_BY_NAME.
                //
                // in practice, using this array works better, as some OFT entries may be empty.
                // see: be24e9d47cfe588a8ced0ac3e453d390 hotfix2.exe
                let first_thunk = import_descriptor.first_thunk + RVA::from(j * psize);

                match read_image_thunk_data(ws, first_thunk)? {
                    ImageThunkData::Function(rva) => {
                        if rva == RVA(0x0) {
                            break;
                        } else {
                            let imp = read_image_import_by_name(ws, rva)?;
                            debug!("found {:?}", imp);

                            symbols.push((first_thunk, format!("{}!{}", dll_name, imp.name)))
                        }
                    },
                    ImageThunkData::Ordinal(ord) => {
                        symbols.push((first_thunk, format!("{}!#{}", dll_name, ord)))
                    },
                };
            }
        }

        for (rva, name) in symbols.iter() {
            debug!("found import: {} -> {}", rva, name);
            ws.make_symbol(*rva, name)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Structs for creating and using a Resolver
use std::io;
use std::net::IpAddr;
use std::sync::Mutex;

use proto::rr::RecordType;
use tokio::runtime::{self, Runtime};

use config::{ResolverConfig, ResolverOpts};
use error::*;
use lookup;
use lookup::Lookup;
use lookup_ip::LookupIp;
use AsyncResolver;

/// The Resolver is used for performing DNS queries.
///
/// For forward (A) lookups, hostname -> IP address, see: `Resolver::lookup_ip`
///
/// Special note about resource consumption. The Resolver and all Trust-DNS software is built around the Tokio async-io library. This synchronous Resolver is intended to be a simpler wrapper for of the [`trust_dns_resolver::ResolverFuture`]. To allow the Resolver to be [`Send`] + [`Sync`], the construction of the `ResolverFuture` is lazy, this means some of the features of the `ResolverFuture`, like performance based resolution via the most efficient `NameServer` will be lost (the lookup cache is shared across invocations of the `Resolver`). If these other features of the Trust-DNS Resolver are desired, please use the tokio based `ResolverFuture`.
///
/// *Note: Threaded/Sync usage*: In multithreaded scenarios, the internal Tokio Runtime will block on an internal Mutex for the tokio Runtime in use. For higher performance, it's recommended to use the `AsyncResolver`.
pub struct Resolver {
    // TODO: Mutex allows this to be Sync, another option would be to instantiate a thread_local, but that has other
    //   drawbacks. One major issues, is if this Resolver is shared across threads, it will cause all to block on any
    //   query. A TLS on the other hand would not, at the cost of only allowing a Resolver to be configured once per Thread
    runtime: Mutex<Runtime>,
    async_resolver: AsyncResolver,
}

macro_rules! lookup_fn {
    ($p:ident, $l:ty) => {
/// Performs a lookup for the associated type.
///
/// *hint* queries that end with a '.' are fully qualified names and are cheaper lookups
///
/// # Arguments
///
/// * `query` - a str which parses to a domain name, failure to parse will return an error
pub fn $p(&self, query: &str) -> ResolveResult<$l> {
    let lookup = self.async_resolver.$p(query);
    self.runtime.lock()?.block_on(lookup)
}
    };
    ($p:ident, $l:ty, $t:ty) => {
/// Performs a lookup for the associated type.
///
/// # Arguments
///
/// * `query` - a type which can be converted to `Name` via `From`.
pub fn $p(&self, query: $t) -> ResolveResult<$l> {
    let lookup = self.async_resolver.$p(query);
    self.runtime.lock()?.block_on(lookup)
}
    };
}

impl Resolver {
    /// Constructs a new Resolver with the specified configuration.
    ///
    /// # Arguments
    /// * `config` - configuration for the resolver
    /// * `options` - resolver options for performing lookups
    /// * `client_connection` - ClientConnection for establishing the connection to the DNS server
    ///
    /// # Returns
    ///
    /// A new Resolver or an error if there was an error with the configuration.
    pub fn new(config: ResolverConfig, options: ResolverOpts) -> io::Result<Self> {
        let mut builder = runtime::Builder::new();
        builder.core_threads(1);

        let mut runtime = builder.build()?;
        let (async_resolver, bg) = AsyncResolver::new(config, options);

        runtime.spawn(bg);

        Ok(Resolver {
            runtime: Mutex::new(runtime),
            async_resolver,
        })
    }

    /// Constructs a new Resolver with default config and default options.
    ///
    /// See [`ResolverConfig::default`] and [`ResolverOpts::default`] for more information.
    ///
    /// # Returns
    ///
    /// A new Resolver or an error if there was an error with the configuration.
    pub fn default() -> io::Result<Self> {
        Self::new(ResolverConfig::default(), ResolverOpts::default())
    }

    /// Constructs a new Resolver with the system configuration.
    ///
    /// This will use `/etc/resolv.conf` on Unix OSes and the registry on Windows.
    #[cfg(any(unix, target_os = "windows"))]
    pub fn from_system_conf() -> io::Result<Self> {
        let (config, options) = super::system_conf::read_system_conf()?;
        Self::new(config, options)
    }

    /// Generic lookup for any RecordType
    ///
    /// *WARNING* This interface may change in the future, please use [`Self::lookup_ip`] or another variant for more stable interfaces.
    ///
    /// # Arguments
    ///
    /// * `name` - name of the record to lookup, if name is not a valid domain name, an error will be returned
    /// * `record_type` - type of record to lookup
    pub fn lookup(&self, name: &str, record_type: RecordType) -> ResolveResult<Lookup> {
        let lookup = self.async_resolver.lookup(name, record_type);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a dual-stack DNS lookup for the IP for the given hostname.
    ///
    /// See the configuration and options parameters for controlling the way in which A(Ipv4) and AAAA(Ipv6) lookups will be performed. For the least expensive query a fully-qualified-domain-name, FQDN, which ends in a final `.`, e.g. `www.example.com.`, will only issue one query. Anything else will always incur the cost of querying the `ResolverConfig::domain` and `ResolverConfig::search`.
    ///
    /// # Arguments
    ///
    /// * `host` - string hostname, if this is an invalid hostname, an error will be returned.
    pub fn lookup_ip(&self, host: &str) -> ResolveResult<LookupIp> {
        let lookup = self.async_resolver.lookup_ip(host);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a DNS lookup for an SRV record for the specified service type and protocol at the given name.
    ///
    /// This is a convenience method over `lookup_srv`, it combines the service, protocol and name into a single name: `_service._protocol.name`.
    ///
    /// # Arguments
    ///
    /// * `service` - service to lookup, e.g. ldap or http
    /// * `protocol` - wire protocol, e.g. udp or tcp
    /// * `name` - zone or other name at which the service is located.
    #[deprecated(note = "use lookup_srv instead, this interface is none ideal")]
    pub fn lookup_service(
        &self,
        service: &str,
        protocol: &str,
        name: &str,
    ) -> ResolveResult<lookup::SrvLookup> {
        #[allow(deprecated)]
        let lookup = self.async_resolver.lookup_service(service, protocol, name);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Lookup an SRV record.
    pub fn lookup_srv(&self, name: &str) -> ResolveResult<lookup::SrvLookup> {
        let lookup = self.async_resolver.lookup_srv(name);
        self.runtime.lock()?.block_on(lookup)
    }

    lookup_fn!(reverse_lookup, lookup::ReverseLookup, IpAddr);
    lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup);
    lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup);
    lookup_fn!(mx_lookup, lookup::MxLookup);
    lookup_fn!(srv_lookup, lookup::SrvLookup);
    lookup_fn!(txt_lookup, lookup::TxtLookup);
}

#[cfg(test)]
mod tests {
    use std::net::*;

    use super::*;

    fn require_send_sync<S: Send + Sync>() {}

    #[test]
    fn test_resolver_sendable() {
        require_send_sync::<Resolver>();
    }

    #[test]
    fn test_lookup() {
        let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }

    #[test]
    #[ignore]
    #[cfg(any(unix, target_os = "windows"))]
    fn test_system_lookup() {
        let resolver = Resolver::from_system_conf().unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }
}
<commit_msg>Deprecate Resolver::srv_lookup.<commit_after>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Structs for creating and using a Resolver
use std::io;
use std::net::IpAddr;
use std::sync::Mutex;

use proto::rr::RecordType;
use tokio::runtime::{self, Runtime};

use config::{ResolverConfig, ResolverOpts};
use error::*;
use lookup;
use lookup::Lookup;
use lookup_ip::LookupIp;
use AsyncResolver;

/// The Resolver is used for performing DNS queries.
///
/// For forward (A) lookups, hostname -> IP address, see: `Resolver::lookup_ip`
///
/// Special note about resource consumption. The Resolver and all Trust-DNS software is built around the Tokio async-io library. This synchronous Resolver is intended to be a simpler wrapper for of the [`trust_dns_resolver::ResolverFuture`]. To allow the Resolver to be [`Send`] + [`Sync`], the construction of the `ResolverFuture` is lazy, this means some of the features of the `ResolverFuture`, like performance based resolution via the most efficient `NameServer` will be lost (the lookup cache is shared across invocations of the `Resolver`). If these other features of the Trust-DNS Resolver are desired, please use the tokio based `ResolverFuture`.
///
/// *Note: Threaded/Sync usage*: In multithreaded scenarios, the internal Tokio Runtime will block on an internal Mutex for the tokio Runtime in use. For higher performance, it's recommended to use the `AsyncResolver`.
pub struct Resolver {
    // TODO: Mutex allows this to be Sync, another option would be to instantiate a thread_local, but that has other
    //   drawbacks. One major issues, is if this Resolver is shared across threads, it will cause all to block on any
    //   query. A TLS on the other hand would not, at the cost of only allowing a Resolver to be configured once per Thread
    runtime: Mutex<Runtime>,
    async_resolver: AsyncResolver,
}

macro_rules! lookup_fn {
    ($p:ident, $l:ty) => {
/// Performs a lookup for the associated type.
///
/// *hint* queries that end with a '.' are fully qualified names and are cheaper lookups
///
/// # Arguments
///
/// * `query` - a str which parses to a domain name, failure to parse will return an error
pub fn $p(&self, query: &str) -> ResolveResult<$l> {
    let lookup = self.async_resolver.$p(query);
    self.runtime.lock()?.block_on(lookup)
}
    };
    ($p:ident, $l:ty, $t:ty) => {
/// Performs a lookup for the associated type.
///
/// # Arguments
///
/// * `query` - a type which can be converted to `Name` via `From`.
pub fn $p(&self, query: $t) -> ResolveResult<$l> {
    let lookup = self.async_resolver.$p(query);
    self.runtime.lock()?.block_on(lookup)
}
    };
}

impl Resolver {
    /// Constructs a new Resolver with the specified configuration.
    ///
    /// # Arguments
    /// * `config` - configuration for the resolver
    /// * `options` - resolver options for performing lookups
    /// * `client_connection` - ClientConnection for establishing the connection to the DNS server
    ///
    /// # Returns
    ///
    /// A new Resolver or an error if there was an error with the configuration.
    pub fn new(config: ResolverConfig, options: ResolverOpts) -> io::Result<Self> {
        let mut builder = runtime::Builder::new();
        builder.core_threads(1);

        let mut runtime = builder.build()?;
        let (async_resolver, bg) = AsyncResolver::new(config, options);

        runtime.spawn(bg);

        Ok(Resolver {
            runtime: Mutex::new(runtime),
            async_resolver,
        })
    }

    /// Constructs a new Resolver with default config and default options.
    ///
    /// See [`ResolverConfig::default`] and [`ResolverOpts::default`] for more information.
    ///
    /// # Returns
    ///
    /// A new Resolver or an error if there was an error with the configuration.
    pub fn default() -> io::Result<Self> {
        Self::new(ResolverConfig::default(), ResolverOpts::default())
    }

    /// Constructs a new Resolver with the system configuration.
    ///
    /// This will use `/etc/resolv.conf` on Unix OSes and the registry on Windows.
    #[cfg(any(unix, target_os = "windows"))]
    pub fn from_system_conf() -> io::Result<Self> {
        let (config, options) = super::system_conf::read_system_conf()?;
        Self::new(config, options)
    }

    /// Generic lookup for any RecordType
    ///
    /// *WARNING* This interface may change in the future, please use [`Self::lookup_ip`] or another variant for more stable interfaces.
    ///
    /// # Arguments
    ///
    /// * `name` - name of the record to lookup, if name is not a valid domain name, an error will be returned
    /// * `record_type` - type of record to lookup
    pub fn lookup(&self, name: &str, record_type: RecordType) -> ResolveResult<Lookup> {
        let lookup = self.async_resolver.lookup(name, record_type);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a dual-stack DNS lookup for the IP for the given hostname.
    ///
    /// See the configuration and options parameters for controlling the way in which A(Ipv4) and AAAA(Ipv6) lookups will be performed. For the least expensive query a fully-qualified-domain-name, FQDN, which ends in a final `.`, e.g. `www.example.com.`, will only issue one query. Anything else will always incur the cost of querying the `ResolverConfig::domain` and `ResolverConfig::search`.
    ///
    /// # Arguments
    ///
    /// * `host` - string hostname, if this is an invalid hostname, an error will be returned.
    pub fn lookup_ip(&self, host: &str) -> ResolveResult<LookupIp> {
        let lookup = self.async_resolver.lookup_ip(host);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Performs a DNS lookup for an SRV record for the specified service type and protocol at the given name.
    ///
    /// This is a convenience method over `lookup_srv`, it combines the service, protocol and name into a single name: `_service._protocol.name`.
    ///
    /// # Arguments
    ///
    /// * `service` - service to lookup, e.g. ldap or http
    /// * `protocol` - wire protocol, e.g. udp or tcp
    /// * `name` - zone or other name at which the service is located.
    #[deprecated(note = "use lookup_srv instead, this interface is none ideal")]
    pub fn lookup_service(
        &self,
        service: &str,
        protocol: &str,
        name: &str,
    ) -> ResolveResult<lookup::SrvLookup> {
        #[allow(deprecated)]
        let lookup = self.async_resolver.lookup_service(service, protocol, name);
        self.runtime.lock()?.block_on(lookup)
    }

    /// Lookup an SRV record.
    pub fn lookup_srv(&self, name: &str) -> ResolveResult<lookup::SrvLookup> {
        let lookup = self.async_resolver.lookup_srv(name);
        self.runtime.lock()?.block_on(lookup)
    }

    lookup_fn!(reverse_lookup, lookup::ReverseLookup, IpAddr);
    lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup);
    lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup);
    lookup_fn!(mx_lookup, lookup::MxLookup);
    #[deprecated(note = "use lookup_srv instead, this interface is not ideal")]
    lookup_fn!(srv_lookup, lookup::SrvLookup);
    lookup_fn!(txt_lookup, lookup::TxtLookup);
}

#[cfg(test)]
mod tests {
    use std::net::*;

    use super::*;

    fn require_send_sync<S: Send + Sync>() {}

    #[test]
    fn test_resolver_sendable() {
        require_send_sync::<Resolver>();
    }

    #[test]
    fn test_lookup() {
        let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }

    #[test]
    #[ignore]
    #[cfg(any(unix, target_os = "windows"))]
    fn test_system_lookup() {
        let resolver = Resolver::from_system_conf().unwrap();

        let response = resolver.lookup_ip("www.example.com.").unwrap();
        println!("response records: {:?}", response);

        assert_eq!(response.iter().count(), 1);
        for address in response.iter() {
            if address.is_ipv4() {
                assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
            } else {
                assert_eq!(
                    address,
                    IpAddr::V6(Ipv6Addr::new(
                        0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
                    ))
                );
            }
        }
    }
}
<|endoftext|>