Lesson 2: PWD
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
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, TCHAR* argv[]) {
// (1) Probe for Buffer Size: Compiler injects call
// to GetCurrentDirectory, returning required buffer
// size. Triggers kernel API interaction, marking
// CPU cycles for syscall handling. DWORD integral
// type maps to CPU register efficiency.
DWORD requiredSize = GetCurrentDirectory(0, NULL);
if (requiredSize == 0) { // (2) Handle Probe Failure
// GetLastError interacts with thread-local
// storage, fetching last-error code. Calls
// context-switch to retrieve specific error.
// Low-level formatted I/O via _ftprintf,
// exploiting buffered I/O optimizations.
_ftprintf(stderr, _T("Error 0x%lx: Buffer size probe failed\n"), GetLastError());
return 1; // Non-zero exit triggers OS-level
// failure signal.
}
// (3) Allocate Memory Buffer: malloc triggers heap
// allocation in process virtual address space. Size
// calculation uses sizeof(TCHAR) to ensure proper
// byte-alignment per character. TCHAR abstracts
// character width, enabling single code path for
// Unicode support.
TCHAR* buffer = (TCHAR*)malloc(requiredSize * sizeof(TCHAR));
if (!buffer) { // (4) Handle Allocation Failure
// Buffer allocation failure traps memory
// constraints; error message via _ftprintf.
// Aligns with system I/O operations.
_ftprintf(stderr, _T("Error: Alloc %lu chars failed\n"), requiredSize);
return 1; // Propagates failure state to OS.
}
// (5) Retrieve Directory Path: GetCurrentDirectory
// writes to allocated buffer, interacting with
// kernel-mode, transferring data from file system
// to user space. charsWritten holds count of
// characters written, internal buffer data alignment
// optimized for sequential memory access.
DWORD charsWritten = GetCurrentDirectory(requiredSize, buffer);
if (charsWritten == 0) { // (6) Handle Path Retrieval Failure
// Error handling engages GetLastError, buffering
// I/O through _ftprintf. Prevents memory leak
// via free(), deallocating heap space.
_ftprintf(stderr, _T("Error 0x%lx: Path read failed\n"), GetLastError());
free(buffer); // Critical to avoid resource leak.
return 1; // Non-zero exit signals failure to OS.
}
// (7) Validate Path Length: Ensures charsWritten
// matches requiredSize - 1, detecting inconsistencies.
// charsWritten reflects accurate character count.
// Discrepancy here suggests potential issues in file
// system or path changes during execution.
if (charsWritten != requiredSize - 1) {
_ftprintf(stderr, _T("Error: Size mismatch (%lu vs %lu)\n"), requiredSize - 1, charsWritten);
free(buffer); // Ensure memory release.
return 1; // Exit signals resource validation fail.
}
// (8) Output Directory Path: _tprintf interacts with
// stdout, leveraging terminal I/O subsystems.
// Buffer data remains intact due to prior validation,
// ensuring correct output.
_tprintf(_T("%s\n"), _T("The dir is \n"));
_tprintf(_T("%s\n"), buffer);
free(buffer); // Deallocates dynamic memory,
// releases heap space, crucial for avoiding leaks.
return 0; // Zero exit status signals success,
// concluding process cycle efficiently.
}
Rust Implementation
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
use std::env; // (1) `std::env` acts as a bridge between Rust
// and the underlying OS, using syscalls
// like `GetCurrentDirectory` (Windows) or
// `getcwd` (POSIX). These rely on syscall
// error handling, mapping to Rust's Result.
use std::io; // (2) IO imports connect to platform-specific
// stdio operations, heavily relying on libc.
// Provides abstractions for syscall-safe
// I/O, preventing UB caused by direct OS calls.
use std::path::PathBuf; // (3) `PathBuf` is an owned, mutable
// representation of filesystem paths,
// internally handling path separators
// for cross-platform compatibility.
fn main() {
// (4) `current_dir` syscall-like abstraction
// retrieves current working directory, translating
// OS-specific errors (e.g., errno) into Rust enums.
let path = env::current_dir().unwrap_or_else(|e| {
// (5) Error recovery logic uses lambdas (closures).
// `raw_os_error` extracts OS-level error codes,
// stored in platform-dependent formats (e.g.,
// Windows NTSTATUS or POSIX errno values).
match e.raw_os_error() {
Some(code) => eprintln!(
"Error 0x{:x}: Path resolution failed",
code
), // (6) `eprintln!` directly interacts with
// stderr using `libc`-backed syscalls like
// `write(2)`. Optimized for debug contexts.
None => eprintln!("Unknown error occurred"),
}
std::process::exit(1); // (7) `exit` bypasses stack
// unwinding, triggering immediate
// OS termination via `exit_group`
// syscall (Linux) or `_exit`
// (Windows).
});
// (8) `to_str` converts the internal byte buffer in `PathBuf`
// to a UTF-8 `&str`, ensuring safe memory reads. If path
// contains invalid UTF-8, returns `None`, preventing UB
// common in direct C-style string manipulations.
let path_str = path.to_str().unwrap_or_else(|| {
eprintln!("Error: Invalid UTF-8 in path");
std::process::exit(1);
});
// (9) `ends_with` inspects path buffer tail bytes without
// unnecessary allocations, leveraging slice comparisons.
// `trim_end_matches` avoids reallocation unless necessary.
let formatted_path = if path_str.ends_with('\\') && path_str.len() == 3 {
// (10) Edge case logic to handle root directories.
// Special cases for paths like `C:\` are preserved
// explicitly to mirror Windows behavior. Rust runtime
// avoids reallocating unchanged `&str` slices.
path_str
} else {
path_str.trim_end_matches('\\') // (11) Optimized trim
// avoids intermediate
// memory copies by
// working directly
// on slices.
};
println!("{}", formatted_path); // (12) `println!` invokes
// buffered stdout syscalls
// (via `write` or `WriteFile`),
// ensuring atomic writes.
}
Exercise
Let us implement this in C++ using closures and compile time tricks.