File: paths.rs

package info (click to toggle)
rust-apr 0.3.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 492 kB
  • sloc: makefile: 4
file content (167 lines) | stat: -rw-r--r-- 4,987 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Path handling utilities for cross-platform operations.
//!
//! This module provides utilities for handling paths in a cross-platform way,
//! particularly for converting between Rust Path types and C string representations
//! that APR and related libraries expect.

use crate::pool::Pool;
use crate::status::{apr_result, Status};
use crate::strings::{pstrdup, BStr, PoolString};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

/// Convert a Rust Path to a pool-allocated C string suitable for APR functions
///
/// This handles platform-specific path encoding:
/// - On Unix: paths are typically UTF-8 bytes
/// - On Windows: converts from UTF-16 to the appropriate byte encoding
pub fn path_to_cstring<P: AsRef<Path>>(path: P, pool: &Pool) -> Result<PoolString, Status> {
    let path = path.as_ref();

    #[cfg(unix)]
    {
        use std::os::unix::ffi::OsStrExt;
        let bytes = path.as_os_str().as_bytes();
        let path_str = String::from_utf8_lossy(bytes);
        pstrdup(&path_str, pool).map_err(|_| Status::BadArgument)
    }

    #[cfg(windows)]
    {
        let path_str = path.to_string_lossy();
        pstrdup(&path_str, pool).map_err(|_| Status::BadArgument)
    }
}

/// Convert a C string back to a Rust PathBuf
///
/// # Safety
/// The ptr must be a valid null-terminated C string
pub unsafe fn cstring_to_pathbuf(ptr: *const std::ffi::c_char) -> PathBuf {
    if ptr.is_null() {
        return PathBuf::new();
    }

    let bstr = BStr::from_ptr(ptr);

    #[cfg(unix)]
    {
        use std::os::unix::ffi::OsStrExt;
        let os_str = OsStr::from_bytes(bstr.as_bytes());
        PathBuf::from(os_str)
    }

    #[cfg(windows)]
    {
        let path_str = bstr.to_string_lossy();
        PathBuf::from(path_str.as_ref())
    }
}

/// Normalize a path using APR's path normalization
pub fn normalize_path<P: AsRef<Path>>(path: P, pool: &Pool) -> Result<PathBuf, Status> {
    let path_cstr = path_to_cstring(path, pool)?;

    unsafe {
        let mut normalized_ptr: *const std::ffi::c_char = std::ptr::null();
        let status = apr_sys::apr_filepath_merge(
            &mut normalized_ptr as *mut _ as *mut *mut std::ffi::c_char,
            std::ptr::null(), // No root path
            path_cstr.as_ptr(),
            apr_sys::APR_FILEPATH_SECUREROOT as i32,
            pool.as_mut_ptr(),
        );

        apr_result(status)?;
        Ok(cstring_to_pathbuf(normalized_ptr))
    }
}

/// Check if a path is absolute using APR's path checking
pub fn is_absolute<P: AsRef<Path>>(_path: P, pool: &Pool) -> Result<bool, Status> {
    // Note: apr_filepath_root has a different signature than expected
    // For now, use Rust's built-in path checking
    // TODO: Investigate proper APR usage for this
    let _pool = pool; // Suppress warning
    Ok(_path.as_ref().is_absolute())
}

// Note: apr_filepath_name_get doesn't exist in APR
// Libraries using APR will need to implement their own basename/dirname

/// Join two paths
pub fn join_paths<P1: AsRef<Path>, P2: AsRef<Path>>(
    base: P1,
    path: P2,
    pool: &Pool,
) -> Result<PathBuf, Status> {
    let base_cstr = path_to_cstring(base, pool)?;
    let path_cstr = path_to_cstring(path, pool)?;

    unsafe {
        let mut joined_ptr: *const std::ffi::c_char = std::ptr::null();
        let status = apr_sys::apr_filepath_merge(
            &mut joined_ptr as *mut _ as *mut *mut std::ffi::c_char,
            base_cstr.as_ptr(),
            path_cstr.as_ptr(),
            0, // No special flags
            pool.as_mut_ptr(),
        );

        apr_result(status)?;
        Ok(cstring_to_pathbuf(joined_ptr))
    }
}

/// Get the current working directory
pub fn get_cwd(pool: &Pool) -> Result<PathBuf, Status> {
    unsafe {
        let mut cwd_ptr: *mut std::ffi::c_char = std::ptr::null_mut();
        let status = apr_sys::apr_filepath_get(
            &mut cwd_ptr,
            0, // No special flags
            pool.as_mut_ptr(),
        );

        apr_result(status)?;
        Ok(cstring_to_pathbuf(cwd_ptr))
    }
}

/// Set the current working directory
pub fn set_cwd<P: AsRef<Path>>(path: P, pool: &Pool) -> Result<(), Status> {
    let path_cstr = path_to_cstring(path, pool)?;

    unsafe {
        let status = apr_sys::apr_filepath_set(path_cstr.as_ptr(), pool.as_mut_ptr());

        apr_result(status)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::Path;

    #[test]
    fn test_path_conversion() {
        let pool = Pool::new();
        let path = Path::new("/tmp/test/file.txt");

        let pool_string = path_to_cstring(path, &pool).unwrap();

        // Should contain the path
        assert!(pool_string.as_str().unwrap().contains("tmp"));
        assert!(pool_string.as_str().unwrap().contains("file.txt"));
    }

    #[test]
    fn test_get_cwd() {
        let pool = Pool::new();
        let cwd = get_cwd(&pool).unwrap();

        // Should get some path
        assert!(!cwd.as_os_str().is_empty());
    }
}