@@ -7,50 +7,181 @@ const dim = @import("../../dim.zig");
7
7
8
8
pub const FsOperation = union (enum ) {
9
9
copy_file : struct {
10
- path : []const u8 ,
10
+ path : [: 0 ]const u8 ,
11
11
source : dim.FileName ,
12
12
},
13
13
14
14
copy_dir : struct {
15
- path : []const u8 ,
15
+ path : [: 0 ]const u8 ,
16
16
source : dim.FileName ,
17
17
},
18
18
19
19
make_dir : struct {
20
- path : []const u8 ,
20
+ path : [: 0 ]const u8 ,
21
21
},
22
22
23
23
create_file : struct {
24
- path : []const u8 ,
24
+ path : [: 0 ]const u8 ,
25
25
size : u64 ,
26
26
contents : dim.Content ,
27
27
},
28
28
29
29
pub fn execute (op : FsOperation , executor : anytype ) ! void {
30
- _ = executor ;
31
- switch (op ) {
32
- .copy_file = > | data | {
33
- _ = data ;
34
- },
35
- .copy_dir = > | data | {
36
- _ = data ;
37
- },
38
- .make_dir = > | data | {
39
- _ = data ;
40
- },
41
- .create_file = > | data | {
42
- _ = data ;
43
- },
44
- }
30
+ const exec : Executor (@TypeOf (executor )) = .init (executor );
31
+
32
+ try exec .execute (op );
45
33
}
46
34
};
47
35
48
- fn parse_path (ctx : dim.Context ) ! []const u8 {
36
+ fn Executor (comptime T : type ) type {
37
+ return struct {
38
+ const Exec = @This ();
39
+
40
+ inner : T ,
41
+
42
+ fn init (wrapped : T ) Exec {
43
+ return .{ .inner = wrapped };
44
+ }
45
+
46
+ fn execute (exec : Exec , op : FsOperation ) dim.Content.RenderError ! void {
47
+ switch (op ) {
48
+ .make_dir = > | data | {
49
+ try exec .recursive_mkdir (data .path );
50
+ },
51
+
52
+ .copy_file = > | data | {
53
+ var handle = data .source .open () catch | err | switch (err ) {
54
+ error .FileNotFound = > return , // open() already reporeted the error
55
+ else = > | e | return e ,
56
+ };
57
+ defer handle .close ();
58
+
59
+ try exec .add_file (data .path , handle .reader ());
60
+ },
61
+ .copy_dir = > | data | {
62
+ var iter_dir = data .source .open_dir () catch | err | switch (err ) {
63
+ error .FileNotFound = > return , // open() already reporeted the error
64
+ else = > | e | return e ,
65
+ };
66
+ defer iter_dir .close ();
67
+
68
+ var walker_memory : [16384 ]u8 = undefined ;
69
+ var temp_allocator : std.heap.FixedBufferAllocator = .init (& walker_memory );
70
+
71
+ var path_memory : [8192 ]u8 = undefined ;
72
+
73
+ var walker = try iter_dir .walk (temp_allocator .allocator ());
74
+ defer walker .deinit ();
75
+
76
+ while (walker .next () catch | err | return walk_err (err )) | entry | {
77
+ const path = std .fmt .bufPrintZ (& path_memory , "{s}/{s}" , .{
78
+ data .path ,
79
+ entry .path ,
80
+ }) catch @panic ("buffer too small!" );
81
+
82
+ // std.log.debug("- {s}", .{path_buffer.items});
83
+
84
+ switch (entry .kind ) {
85
+ .file = > {
86
+ const fname : dim.FileName = .{
87
+ .root_dir = entry .dir ,
88
+ .rel_path = entry .basename ,
89
+ };
90
+
91
+ var file = try fname .open ();
92
+ defer file .close ();
93
+
94
+ try exec .add_file (path , file .reader ());
95
+ },
96
+
97
+ .directory = > {
98
+ try exec .recursive_mkdir (path );
99
+ },
100
+
101
+ else = > {
102
+ var realpath_buffer : [std .fs .max_path_bytes ]u8 = undefined ;
103
+ std .log .warn ("cannot copy file {!s}: {s} is not a supported file type!" , .{
104
+ entry .dir .realpath (entry .path , & realpath_buffer ),
105
+ @tagName (entry .kind ),
106
+ });
107
+ },
108
+ }
109
+ }
110
+ },
111
+
112
+ .create_file = > | data | {
113
+ const buffer = try std .heap .page_allocator .alloc (u8 , data .size );
114
+ defer std .heap .page_allocator .free (buffer );
115
+
116
+ var bs : dim.BinaryStream = .init_buffer (buffer );
117
+
118
+ try data .contents .render (& bs );
119
+
120
+ var fbs : std .io .FixedBufferStream ([]u8 ) = .{ .buffer = buffer , .pos = 0 };
121
+
122
+ try exec .add_file (data .path , fbs .reader ());
123
+ },
124
+ }
125
+ }
126
+
127
+ fn add_file (exec : Exec , path : [:0 ]const u8 , reader : anytype ) ! void {
128
+ if (std .fs .path .dirnamePosix (path )) | dir | {
129
+ try exec .recursive_mkdir (dir );
130
+ }
131
+
132
+ try exec .inner_mkfile (path , reader );
133
+ }
134
+
135
+ fn recursive_mkdir (exec : Exec , path : []const u8 ) ! void {
136
+ var i : usize = 0 ;
137
+
138
+ while (std .mem .indexOfScalarPos (u8 , path , i , '/' )) | index | {
139
+ try exec .inner_mkdir (path [0.. index ]);
140
+ i = index + 1 ;
141
+ }
142
+
143
+ try exec .inner_mkdir (path );
144
+ }
145
+
146
+ fn inner_mkfile (exec : Exec , path : []const u8 , reader : anytype ) dim.Content.RenderError ! void {
147
+ try exec .inner .mkfile (path , reader );
148
+ }
149
+
150
+ fn inner_mkdir (exec : Exec , path : []const u8 ) dim.Content.RenderError ! void {
151
+ try exec .inner .mkdir (path );
152
+ }
153
+
154
+ fn walk_err (err : (std .fs .Dir .OpenError || std.mem.Allocator.Error )) dim.Content.RenderError {
155
+ return switch (err ) {
156
+ error .InvalidUtf8 = > error .InvalidPath ,
157
+ error .InvalidWtf8 = > error .InvalidPath ,
158
+ error .BadPathName = > error .InvalidPath ,
159
+ error .NameTooLong = > error .InvalidPath ,
160
+
161
+ error .OutOfMemory = > error .OutOfMemory ,
162
+ error .FileNotFound = > error .FileNotFound ,
163
+
164
+ error .DeviceBusy = > error .IoError ,
165
+ error .AccessDenied = > error .IoError ,
166
+ error .SystemResources = > error .IoError ,
167
+ error .NoDevice = > error .IoError ,
168
+ error .Unexpected = > error .IoError ,
169
+ error .NetworkNotFound = > error .IoError ,
170
+ error .SymLinkLoop = > error .IoError ,
171
+ error .ProcessFdQuotaExceeded = > error .IoError ,
172
+ error .SystemFdQuotaExceeded = > error .IoError ,
173
+ error .NotDir = > error .IoError ,
174
+ };
175
+ }
176
+ };
177
+ }
178
+
179
+ fn parse_path (ctx : dim.Context ) ! [:0 ]const u8 {
49
180
const path = try ctx .parse_string ();
50
181
51
182
if (path .len == 0 ) {
52
183
try ctx .report_nonfatal_error ("Path cannot be empty!" , .{});
53
- return path ;
184
+ return "" ;
54
185
}
55
186
56
187
if (! std .mem .startsWith (u8 , path , "/" )) {
@@ -75,7 +206,7 @@ fn parse_path(ctx: dim.Context) ![]const u8 {
75
206
});
76
207
};
77
208
78
- return path ;
209
+ return try normalize ( ctx . get_arena (), path ) ;
79
210
}
80
211
81
212
pub fn parse_ops (ctx : dim.Context , end_seq : []const u8 , handler : anytype ) ! void {
@@ -116,3 +247,25 @@ pub fn parse_ops(ctx: dim.Context, end_seq: []const u8, handler: anytype) !void
116
247
}
117
248
}
118
249
}
250
+
251
+ fn normalize (allocator : std.mem.Allocator , src_path : []const u8 ) ! [:0 ]const u8 {
252
+ var list = std .ArrayList ([]const u8 ).init (allocator );
253
+ defer list .deinit ();
254
+
255
+ var parts = std .mem .tokenizeAny (u8 , src_path , "\\ /" );
256
+
257
+ while (parts .next ()) | part | {
258
+ if (std .mem .eql (u8 , part , "." )) {
259
+ // "cd same" is a no-op, we can remove it
260
+ continue ;
261
+ } else if (std .mem .eql (u8 , part , ".." )) {
262
+ // "cd up" is basically just removing the last pushed part
263
+ _ = list .pop ();
264
+ } else {
265
+ // this is an actual "descend"
266
+ try list .append (part );
267
+ }
268
+ }
269
+
270
+ return try std .mem .joinZ (allocator , "/" , list .items );
271
+ }
0 commit comments