Skip to content

Commit 2e679b6

Browse files
Copilotjgarzik
andcommitted
Complete POSIX compliance audit for asa utility with enhanced tests and documentation
Co-authored-by: jgarzik <494411+jgarzik@users.noreply.github.com>
1 parent ba6d1ab commit 2e679b6

File tree

5 files changed

+437
-0
lines changed

5 files changed

+437
-0
lines changed

text/ASA_AUDIT_REPORT.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# ASA Utility - POSIX Compliance Audit Report
2+
3+
**Date:** 2025-12-16
4+
**Auditor:** GitHub Copilot
5+
**Standard:** POSIX.2024
6+
7+
## Executive Summary
8+
9+
The `asa` utility implementation in posixutils-rs has been audited for POSIX.2024 compliance. The implementation is **COMPLIANT** with all POSIX requirements and includes comprehensive test coverage. One non-POSIX extension (triple-spacing with '-') is clearly documented.
10+
11+
## POSIX Requirements Analysis
12+
13+
### 1. Command Syntax ✅ COMPLIANT
14+
15+
**POSIX Requirement:** `asa [file...]`
16+
17+
**Implementation Status:** ✅ Compliant
18+
- Accepts zero or more file arguments
19+
- Reads from stdin when no files specified
20+
- Supports "-" to explicitly read from stdin
21+
- Multiple files processed sequentially
22+
23+
**Test Coverage:**
24+
- `asa_single_file` - Single file argument
25+
- `asa_multiple_files` - Multiple file arguments
26+
- `asa_stdin_dash` - Explicit stdin with "-"
27+
- Default stdin tested via existing tests
28+
29+
### 2. Carriage-Control Characters ✅ COMPLIANT
30+
31+
**POSIX Requirement:** First character of each line is carriage-control character
32+
33+
#### 2.1 Space Character (' ') ✅ COMPLIANT
34+
35+
**Behavior:** Single spacing - advance one line before printing
36+
37+
**Implementation:** Lines 88-94 in asa.rs
38+
```rust
39+
_ => {
40+
// Space and other chars: normal single-spaced output
41+
if !first_line {
42+
println!();
43+
}
44+
print!("{}", line);
45+
}
46+
```
47+
48+
**Test Coverage:**
49+
- `asa_space_single_line`
50+
- `asa_space_multiple_lines`
51+
- `asa_space_empty_content`
52+
- Multiple other tests using space as control
53+
54+
#### 2.2 Zero Character ('0') ✅ COMPLIANT
55+
56+
**Behavior:** Double spacing - advance two lines (one blank line before printing)
57+
58+
**Implementation:** Lines 64-71 in asa.rs
59+
```rust
60+
'0' => {
61+
// Double-space: newline before content (blank line)
62+
if !first_line {
63+
println!();
64+
}
65+
println!();
66+
print!("{}", line);
67+
}
68+
```
69+
70+
**Test Coverage:**
71+
- `asa_zero_first_line`
72+
- `asa_zero_second_line`
73+
- `asa_zero_multiple`
74+
- `asa_zero_empty_content`
75+
- `asa_alternating_spacing`
76+
77+
#### 2.3 One Character ('1') ✅ COMPLIANT
78+
79+
**Behavior:** New page - advance to next page (form-feed)
80+
81+
**Implementation:** Lines 81-87 in asa.rs
82+
```rust
83+
'1' => {
84+
// New page: form-feed
85+
if !first_line {
86+
println!();
87+
}
88+
print!("\x0c{}", line);
89+
}
90+
```
91+
92+
**Test Coverage:**
93+
- `asa_one_first_line`
94+
- `asa_one_second_line`
95+
- `asa_fortran_style_report`
96+
- `asa_multiple_form_feeds`
97+
- `asa_file_independence`
98+
99+
#### 2.4 Plus Character ('+') ✅ COMPLIANT
100+
101+
**Behavior:** Overprint - carriage return without line advance
102+
103+
**Special Rule:** '+' as first character of first line treated as space
104+
105+
**Implementation:** Lines 56-63 in asa.rs
106+
```rust
107+
// POSIX: '+' as first character in input is equivalent to space
108+
let effective_ch = if first_line && ch == '+' { ' ' } else { ch };
109+
110+
match effective_ch {
111+
'+' => {
112+
// Overprint: return to column 1 of current line
113+
print!("\r{}", line);
114+
}
115+
// ...
116+
}
117+
```
118+
119+
**Test Coverage:**
120+
- `asa_plus_overprint`
121+
- `asa_plus_multiple_overprint`
122+
- `asa_plus_first_line` ✅ Tests special rule
123+
- `asa_plus_first_line_then_normal`
124+
- `asa_plus_no_trailing_newline`
125+
126+
#### 2.5 Other Characters ✅ COMPLIANT
127+
128+
**POSIX Requirement:** Unrecognized control characters treated as space
129+
130+
**Implementation:** Default case in match (lines 88-94)
131+
132+
**Test Coverage:**
133+
- `asa_other_char`
134+
- `asa_digit_as_control`
135+
- `asa_tab_control`
136+
- `asa_multibyte_control_char`
137+
- `asa_cjk_control_char`
138+
139+
### 3. File Processing ✅ COMPLIANT
140+
141+
**POSIX Requirement:** Process files sequentially, independently
142+
143+
**Implementation Status:** ✅ Compliant
144+
- Each file processed by separate `asa_file()` call
145+
- Each file has independent `first_line` state
146+
- Files processed in order specified
147+
148+
**Test Coverage:**
149+
- `asa_multiple_files` - Verifies sequential processing
150+
- `asa_file_independence` - Verifies independent state per file
151+
152+
### 4. Error Handling ✅ COMPLIANT
153+
154+
**POSIX Requirement:** Write diagnostic messages for errors to stderr
155+
156+
**Implementation Status:** ✅ Compliant
157+
- Errors written to stderr (line 126)
158+
- Exit code 1 on error (line 121)
159+
- Continues processing remaining files after error
160+
161+
**Manual Testing:**
162+
```bash
163+
$ ./asa /nonexistent/file.txt
164+
/nonexistent/file.txt: No such file or directory (os error 2)
165+
$ echo $?
166+
1
167+
```
168+
169+
### 5. Line Content Processing ✅ COMPLIANT
170+
171+
**POSIX Requirement:**
172+
- Remove first character from each line
173+
- Write remaining content to stdout
174+
- Preserve trailing newlines appropriately
175+
176+
**Implementation Status:** ✅ Compliant
177+
- First character extracted (lines 37-41)
178+
- Content extracted correctly handling multi-byte UTF-8 (lines 43-54)
179+
- Final newline added per file (lines 101-104)
180+
181+
**Test Coverage:**
182+
- All tests verify content extraction
183+
- `asa_utf8_content` - Multi-byte content
184+
- `asa_cjk_content` - CJK characters
185+
- `asa_no_trailing_newline` - EOF without newline
186+
- `asa_long_line` - Very long lines (5000 chars)
187+
188+
### 6. Edge Cases ✅ WELL-TESTED
189+
190+
Additional edge case testing beyond POSIX requirements:
191+
192+
**Test Coverage:**
193+
- `asa_empty` - Empty input
194+
- `asa_line_with_only_newline` - Line with only newline
195+
- `asa_newline_control` - Newline as control character
196+
- `asa_control_only_no_newline` - Control char only, no newline
197+
- `asa_embedded_carriage_return` - CR within content (not control)
198+
- `asa_embedded_form_feed` - FF within content (not control)
199+
- `asa_all_controls_sequence` - All controls in sequence
200+
201+
## Extensions and Non-POSIX Features
202+
203+
### Triple-Spacing ('-' character) ⚠️ NON-POSIX EXTENSION
204+
205+
**Status:** Clearly documented as "non-POSIX extension"
206+
207+
**Implementation:** Lines 72-80 in asa.rs
208+
```rust
209+
'-' => {
210+
// Triple-space (non-POSIX extension): two blank lines before
211+
if !first_line {
212+
println!();
213+
}
214+
println!();
215+
println!();
216+
print!("{}", line);
217+
}
218+
```
219+
220+
**Test Coverage:**
221+
- `asa_dash_first_line`
222+
- `asa_dash_second_line`
223+
224+
**Recommendation:** Acceptable as extension, properly documented. Many historical implementations support this.
225+
226+
## Test Coverage Summary
227+
228+
**Total Tests:** 40
229+
- Core functionality: 27 tests (original)
230+
- File handling: 4 tests (added)
231+
- Edge cases: 9 tests (added)
232+
233+
**Coverage by Category:**
234+
- Space control: 3 tests
235+
- Zero control: 5 tests
236+
- One control: 5 tests
237+
- Plus control: 5 tests
238+
- Other/unknown controls: 5 tests
239+
- File operations: 4 tests
240+
- Edge cases: 9 tests
241+
- Mixed scenarios: 4 tests
242+
243+
**All tests passing:** ✅ 40/40
244+
245+
## Identified Gaps and Remediation
246+
247+
### Original Gaps (Now Fixed)
248+
249+
1. **Gap:** No tests for file argument handling
250+
- **Remediation:** Added `asa_single_file`, `asa_multiple_files`, `asa_stdin_dash`, `asa_file_independence`
251+
252+
2. **Gap:** No tests for edge cases like very long lines
253+
- **Remediation:** Added `asa_long_line` (5000 character line)
254+
255+
3. **Gap:** No tests for embedded control characters in content
256+
- **Remediation:** Added `asa_embedded_carriage_return`, `asa_embedded_form_feed`
257+
258+
4. **Gap:** Limited testing of sequential file processing
259+
- **Remediation:** Added comprehensive multi-file tests
260+
261+
5. **Gap:** Documentation of POSIX compliance
262+
- **Remediation:** Added detailed doc comments in source code
263+
264+
### No Remaining Gaps
265+
266+
All POSIX requirements are implemented and tested.
267+
268+
## Known Limitations
269+
270+
None. The implementation fully complies with POSIX.2024 requirements.
271+
272+
## Recommendations
273+
274+
1.**Implementation:** No changes needed - fully POSIX compliant
275+
2.**Testing:** Comprehensive test coverage achieved (40 tests)
276+
3.**Documentation:** Added detailed POSIX compliance comments
277+
4.**Extensions:** Triple-spacing extension properly documented
278+
279+
## Conclusion
280+
281+
The `asa` utility implementation is **FULLY COMPLIANT** with POSIX.2024 specifications. All required features are correctly implemented, comprehensive test coverage has been added, and the code is well-documented. The one non-POSIX extension (triple-spacing) is clearly marked and does not interfere with POSIX compliance.
282+
283+
**Status:** ✅ READY FOR PRODUCTION
284+
285+
**Audit Complete:** The utility may proceed to "Stage 6 - Audited" status.

text/asa.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,33 @@ use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleC
1515
use plib::io::input_reader;
1616

1717
/// asa - interpret carriage-control characters
18+
///
19+
/// POSIX.2024 COMPLIANCE:
20+
/// - Interprets first character of each line as carriage-control character
21+
/// - Supported control characters per POSIX:
22+
/// - ' ' (space): Single spacing (advance one line)
23+
/// - '0': Double spacing (advance two lines with blank line before)
24+
/// - '1': New page (form-feed character)
25+
/// - '+': Overprint (carriage return without line advance)
26+
/// - Special rule: '+' as first character of first line treated as space
27+
/// - Extension: '-' for triple-spacing (non-POSIX)
28+
/// - Processes multiple files sequentially, each with independent state
29+
/// - Reads from stdin if no files specified or "-" given
1830
#[derive(Parser)]
1931
#[command(version, about = gettext("asa - interpret carriage-control characters"))]
2032
struct Args {
2133
/// Files to read as input.
2234
files: Vec<PathBuf>,
2335
}
2436

37+
/// Process a single file according to POSIX asa rules
38+
///
39+
/// POSIX.2024 Requirements:
40+
/// - Each line's first character is interpreted as a carriage-control character
41+
/// - The remainder of the line (after first character) is written to stdout
42+
/// - Control character determines line spacing/positioning
43+
/// - First line of file has special handling: '+' treated as space
44+
/// - Each file is processed independently with its own state
2545
fn asa_file(pathname: &PathBuf) -> io::Result<()> {
2646
let mut reader = input_reader(pathname, true)?;
2747
let mut first_line = true;

text/tests/asa/file1.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Line 1 from file1
2+
Line 2 from file1
3+
0Line 3 with double-space

text/tests/asa/file2.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1Page 1 from file2
2+
Normal line
3+
+Overprint this

0 commit comments

Comments
 (0)