this repo has no description

io: handle missing symlink targets. add more icons

rockorager.dev 2d242acf f7626887

verified
+144 -15
+2 -2
build.zig.zon
··· 37 37 // internet connectivity. 38 38 .dependencies = .{ 39 39 .ourio = .{ 40 - .url = "git+https://github.com/rockorager/ourio#6ed0e45a772f6a7241db1663085db3f813bf3932", 41 - .hash = "ourio-0.0.0-_s-z0agIAgD_Ih6DEjzDGxy5EP9K1i_xye_VacvLzgY5", 40 + .url = "git+https://github.com/rockorager/ourio#1afffffe2424f9c5923271ac764e950efbdde696", 41 + .hash = "ourio-0.0.0-_s-z0cALAgCdk4d-uZk0B07uX47Lf64fJDJ9L4Ejj3Rs", 42 42 }, 43 43 .zeit = .{ 44 44 .url = "git+https://github.com/rockorager/zeit#4496d1c40b2223c22a1341e175fc2ecd94cc0de9",
+142 -13
src/main.zig
··· 10 10 @"almost-all": bool = false, 11 11 color: enum { none, auto, always } = .auto, 12 12 @"group-directories-first": bool = true, 13 + icons: bool = true, 13 14 long: bool = false, 14 15 15 16 directory: [:0]const u8 = ".", ··· 21 22 reset: []const u8, 22 23 dir: []const u8, 23 24 executable: []const u8, 24 - sym_link: []const u8, 25 + symlink: []const u8, 26 + symlink_target: []const u8, 27 + symlink_missing: []const u8, 25 28 26 29 const none: Colors = .{ 27 30 .reset = "", 28 31 .dir = "", 29 32 .executable = "", 30 - .sym_link = "", 33 + .symlink = "", 34 + .symlink_target = "", 35 + .symlink_missing = "", 31 36 }; 32 37 33 38 const default: Colors = .{ 34 39 .reset = _reset, 35 40 .dir = bold ++ blue, 36 41 .executable = bold ++ green, 37 - .sym_link = bold ++ purple, 42 + .symlink = bold ++ purple, 43 + .symlink_target = bold ++ cyan, 44 + .symlink_missing = bold ++ red, 38 45 }; 39 46 40 47 const _reset = "\x1b[m"; ··· 192 199 fn printShort(cmd: Command, writer: anytype) !void { 193 200 const colors = cmd.opts.colors; 194 201 for (cmd.entries) |entry| { 195 - const mode = entry.modeStr(); 196 202 switch (entry.kind) { 197 203 .directory => try writer.writeAll(colors.dir), 198 - .sym_link => try writer.writeAll(colors.sym_link), 204 + .sym_link => try writer.writeAll(colors.symlink), 199 205 else => { 200 - if (isExecutable(mode)) { 206 + if (entry.isExecutable()) { 201 207 try writer.writeAll(colors.executable); 202 208 } 203 209 }, ··· 272 278 try writer.print("{d: >5} ", .{@as(u32, @intCast(time.year))}); 273 279 } 274 280 281 + if (cmd.opts.icons and cmd.opts.isatty) { 282 + const icon = Icon.get(entry); 283 + 284 + try writer.writeAll(icon.color); 285 + try writer.writeAll(icon.icon); 286 + try writer.writeAll(colors.reset); 287 + try writer.writeByte(' '); 288 + } 289 + 275 290 switch (entry.kind) { 276 291 .directory => try writer.writeAll(colors.dir), 277 - .sym_link => try writer.writeAll(colors.sym_link), 292 + .sym_link => try writer.writeAll(colors.symlink), 278 293 else => { 279 - if (isExecutable(mode)) { 294 + if (entry.isExecutable()) { 280 295 try writer.writeAll(colors.executable); 281 296 } 282 297 }, ··· 286 301 287 302 if (entry.kind == .sym_link) { 288 303 try writer.writeAll(" -> "); 304 + const color = if (entry.symlink_missing) 305 + colors.symlink_missing 306 + else 307 + colors.symlink_target; 308 + try writer.writeAll(color); 289 309 try writer.writeAll(entry.link_name); 310 + try writer.writeAll(colors.reset); 290 311 } 291 312 292 313 try writer.writeAll("\r\n"); ··· 352 373 kind: std.fs.File.Kind, 353 374 statx: ourio.Statx, 354 375 link_name: [:0]const u8 = "", 376 + symlink_missing: bool = false, 355 377 356 378 fn lessThan(opts: Options, lhs: Entry, rhs: Entry) bool { 357 379 if (opts.@"group-directories-first" and ··· 426 448 } 427 449 return std.fmt.bufPrint(out, "{d}", .{self.statx.size}); 428 450 } 451 + 452 + fn isExecutable(self: Entry) bool { 453 + return self.statx.mode & (posix.S.IXUSR | posix.S.IXGRP | posix.S.IXOTH) != 0; 454 + } 429 455 }; 430 456 431 457 fn onCompletion(io: *ourio.Ring, task: ourio.Task) anyerror!void { ··· 600 626 std.mem.sort(Group, cmd.groups.items, {}, Group.lessThan); 601 627 }, 602 628 603 - else => {}, 629 + .stat => { 630 + if (result.statx) |_| { 631 + return; 632 + } else |_| {} 633 + 634 + const entry: *Entry = @fieldParentPtr("statx", task.req.statx.result); 635 + if (entry.symlink_missing) { 636 + // we already got here. Just zero out the statx; 637 + entry.statx = std.mem.zeroInit(ourio.Statx, entry.statx); 638 + return; 639 + } 640 + 641 + entry.symlink_missing = true; 642 + _ = try io.lstat(task.req.statx.path, task.req.statx.result, .{ 643 + .cb = onCompletion, 644 + .ptr = cmd, 645 + .msg = @intFromEnum(Msg.stat), 646 + }); 647 + }, 604 648 } 605 649 } 606 650 651 + const Icon = struct { 652 + icon: []const u8, 653 + color: []const u8, 654 + 655 + // Entry types 656 + const directory: Icon = .{ .icon = "󰉋", .color = Options.Colors.blue }; 657 + const drive: Icon = .{ .icon = "󰋊", .color = Options.Colors.blue }; 658 + const file: Icon = .{ .icon = "󰈤", .color = Options.Colors.fg }; 659 + const file_hidden: Icon = .{ .icon = "󰘓", .color = Options.Colors.fg }; 660 + const pipe: Icon = .{ .icon = "󰟥", .color = Options.Colors.fg }; 661 + const socket: Icon = .{ .icon = "󰐧", .color = Options.Colors.fg }; 662 + const symlink: Icon = .{ .icon = "", .color = Options.Colors.fg }; 663 + const symlink_dir: Icon = .{ .icon = "", .color = Options.Colors.blue }; 664 + 665 + // Broad file types 666 + const executable: Icon = .{ .icon = "", .color = Options.Colors.green }; 667 + const image: Icon = .{ .icon = "", .color = Options.Colors.yellow }; 668 + const video: Icon = .{ .icon = "󰸬", .color = Options.Colors.yellow }; 669 + 670 + // Filetypes 671 + const css: Icon = .{ .icon = "", .color = "\x1b[38:2:50:167:220m" }; 672 + const go: Icon = .{ .icon = "󰟓", .color = Options.Colors.blue }; 673 + const html: Icon = .{ .icon = "", .color = "\x1b[38:2:229:76:33m" }; 674 + const javascript: Icon = .{ .icon = "", .color = "\x1b[38:2:233:212:77m" }; 675 + const json: Icon = .{ .icon = "", .color = Options.Colors.blue }; 676 + const lua: Icon = .{ .icon = "󰢱", .color = Options.Colors.blue }; 677 + const markdown: Icon = .{ .icon = "", .color = "" }; 678 + const python: Icon = .{ .icon = "", .color = Options.Colors.yellow }; 679 + const typescript: Icon = .{ .icon = "", .color = Options.Colors.blue }; 680 + const zig: Icon = .{ .icon = "", .color = "\x1b[38:2:247:164:29m" }; 681 + 682 + const by_name: std.StaticStringMap(Icon) = .initComptime(.{}); 683 + 684 + const by_extension: std.StaticStringMap(Icon) = .initComptime(.{ 685 + .{ "css", Icon.css }, 686 + .{ "gif", Icon.image }, 687 + .{ "go", Icon.go }, 688 + .{ "html", Icon.html }, 689 + .{ "jpeg", Icon.image }, 690 + .{ "jpg", Icon.image }, 691 + .{ "js", Icon.javascript }, 692 + .{ "json", Icon.json }, 693 + .{ "lua", Icon.lua }, 694 + .{ "md", Icon.markdown }, 695 + .{ "mkv", Icon.video }, 696 + .{ "mp4", Icon.video }, 697 + .{ "png", Icon.image }, 698 + .{ "py", Icon.python }, 699 + .{ "ts", Icon.typescript }, 700 + .{ "webp", Icon.image }, 701 + .{ "zig", Icon.zig }, 702 + .{ "zon", Icon.zig }, 703 + }); 704 + 705 + fn get(entry: Entry) Icon { 706 + // 1. By name 707 + // 2. By extension 708 + // 3. By type 709 + if (by_name.get(entry.name)) |icon| return icon; 710 + 711 + const ext = std.fs.path.extension(entry.name); 712 + if (ext.len > 0) { 713 + const ft = ext[1..]; 714 + if (by_extension.get(ft)) |icon| return icon; 715 + } 716 + 717 + switch (entry.kind) { 718 + .block_device => return drive, 719 + .character_device => return drive, 720 + .directory => return directory, 721 + .file => { 722 + if (entry.isExecutable()) { 723 + return executable; 724 + } 725 + return file; 726 + }, 727 + .named_pipe => return pipe, 728 + .sym_link => { 729 + if (posix.S.ISDIR(entry.statx.mode)) { 730 + return symlink_dir; 731 + } 732 + return symlink; 733 + }, 734 + .unix_domain_socket => return pipe, 735 + else => return file, 736 + } 737 + } 738 + }; 739 + 607 740 fn eql(a: []const u8, b: []const u8) bool { 608 741 return std.mem.eql(u8, a, b); 609 742 } ··· 613 746 if (std.mem.startsWith(u8, a, "-")) return .short; 614 747 return .positional; 615 748 } 616 - 617 - fn isExecutable(mode: [10]u8) bool { 618 - return mode[3] == 'x' or mode[6] == 'x' or mode[9] == 'x'; 619 - }
test.go

This is a binary file and will not be displayed.