Skip to content

Commit ffcbaad

Browse files
add pkgbuild, cleanup. add more options
1 parent 0e003cb commit ffcbaad

File tree

7 files changed

+334
-153
lines changed

7 files changed

+334
-153
lines changed

PKGBUILD

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Maintainer: viable <hi@viable.gg>
2+
pkgname=lifetch
3+
pkgver=0.1.0
4+
pkgrel=1
5+
pkgdesc="Fast system information fetcher written in zig"
6+
arch=('x86_64')
7+
url="https://github.com/nuiipointerexception/lifetch"
8+
license=('MIT')
9+
depends=()
10+
makedepends=('zig')
11+
source=("git+${url}.git")
12+
sha256sums=('SKIP')
13+
14+
build() {
15+
cd "$pkgname"
16+
zig build -Doptimize=ReleaseSafe
17+
}
18+
19+
package() {
20+
cd "$pkgname"
21+
install -Dm755 zig-out/bin/lifetch "$pkgdir/usr/bin/lifetch"
22+
install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
23+
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
24+
}

src/color.zig

Lines changed: 150 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,162 @@
11
const std = @import("std");
22
const builtin = @import("builtin");
33
const os = std.os;
4+
const fs = std.fs;
45

56
pub const ColorSupport = struct {
67
truecolor: bool = false,
78
color256: bool = false,
89
basic: bool = false,
910

11+
const TermInfo = struct {
12+
fn readTermInfo(term: []const u8) !bool {
13+
if (term.len == 0) return false;
14+
15+
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
16+
const path = std.fmt.bufPrint(&path_buf, "/usr/share/terminfo/{c}/{s}", .{ term[0], term[1..] }) catch return false;
17+
18+
const file = fs.openFileAbsolute(path, .{ .mode = .read_only }) catch |err| switch (err) {
19+
error.FileNotFound => return false,
20+
else => |e| return e,
21+
};
22+
defer file.close();
23+
24+
var header_buf: [12]u8 = undefined;
25+
if ((try file.readAll(&header_buf)) < 12) return false;
26+
if (header_buf[0] != 0x1a and header_buf[1] != 0x01) return false;
27+
28+
return (@as(u16, @intCast(header_buf[10])) | (@as(u16, @intCast(header_buf[11])) << 8)) > 0;
29+
}
30+
};
31+
32+
fn parseColorTag(tag: []const u8) ?Color {
33+
return switch (tag[0]) {
34+
'b' => if (std.mem.eql(u8, tag, "black")) .black else if (std.mem.eql(u8, tag, "blue")) .blue else null,
35+
'r' => if (std.mem.eql(u8, tag, "red")) .red else null,
36+
'g' => if (std.mem.eql(u8, tag, "green")) .green else null,
37+
'y' => if (std.mem.eql(u8, tag, "yellow")) .yellow else null,
38+
'm' => if (std.mem.eql(u8, tag, "magenta")) .magenta else null,
39+
'c' => if (std.mem.eql(u8, tag, "cyan")) .cyan else null,
40+
'w' => if (std.mem.eql(u8, tag, "white")) .white else null,
41+
else => null,
42+
};
43+
}
44+
45+
fn parseStyleTag(tag: []const u8) ?[]const u8 {
46+
return switch (tag[0]) {
47+
'b' => if (std.mem.eql(u8, tag, "bold")) Style.bold else if (std.mem.eql(u8, tag, "blink")) Style.blink else null,
48+
'd' => if (std.mem.eql(u8, tag, "dim")) Style.dim else null,
49+
'i' => if (std.mem.eql(u8, tag, "italic")) Style.italic else null,
50+
'u' => if (std.mem.eql(u8, tag, "underline")) Style.underline else null,
51+
'r' => if (std.mem.eql(u8, tag, "reverse")) Style.reverse else null,
52+
'h' => if (std.mem.eql(u8, tag, "hidden")) Style.hidden else null,
53+
's' => if (std.mem.eql(u8, tag, "strike")) Style.strike else null,
54+
else => null,
55+
};
56+
}
57+
58+
fn parseRgbTag(tag: []const u8) !Rgb {
59+
if (!std.mem.startsWith(u8, tag, "rgb(") or !std.mem.endsWith(u8, tag, ")")) {
60+
return error.InvalidColorFormat;
61+
}
62+
63+
const rgb_content = tag[4 .. tag.len - 1];
64+
var values: [3]u8 = undefined;
65+
var value_idx: usize = 0;
66+
var num_start: usize = 0;
67+
68+
for (rgb_content, 0..) |c, i| {
69+
if (c == ',' or i == rgb_content.len - 1) {
70+
const num_end = if (i == rgb_content.len - 1) i + 1 else i;
71+
const num_str = std.mem.trim(u8, rgb_content[num_start..num_end], &std.ascii.whitespace);
72+
values[value_idx] = std.fmt.parseInt(u8, num_str, 10) catch return error.InvalidColorFormat;
73+
value_idx += 1;
74+
if (value_idx > 2) break;
75+
num_start = i + 1;
76+
}
77+
}
78+
79+
if (value_idx != 3) return error.InvalidColorFormat;
80+
return Rgb.init(values[0], values[1], values[2]);
81+
}
82+
1083
pub fn init() ColorSupport {
1184
var self = ColorSupport{};
85+
const env = os.environ;
86+
87+
for (env) |entry| {
88+
const entry_str = std.mem.span(entry);
89+
if (std.mem.startsWith(u8, entry_str, "NO_COLOR=")) return self;
90+
}
91+
92+
for (env) |entry| {
93+
const entry_str = std.mem.span(entry);
94+
if (std.mem.startsWith(u8, entry_str, "COLORTERM=")) {
95+
const value = entry_str["COLORTERM=".len..];
96+
if (std.mem.eql(u8, value, "truecolor") or std.mem.eql(u8, value, "24bit")) {
97+
self.truecolor = true;
98+
self.basic = true;
99+
}
100+
break;
101+
}
102+
}
12103

13-
if (std.process.getEnvVarOwned(std.heap.page_allocator, "COLORTERM")) |colorterm| {
14-
defer std.heap.page_allocator.free(colorterm);
15-
self.truecolor = std.mem.eql(u8, colorterm, "truecolor") or
16-
std.mem.eql(u8, colorterm, "24bit");
17-
} else |_| {}
18-
19-
if (std.process.getEnvVarOwned(std.heap.page_allocator, "TERM")) |term| {
20-
defer std.heap.page_allocator.free(term);
21-
self.color256 = std.mem.indexOf(u8, term, "256color") != null;
22-
self.basic = std.mem.indexOf(u8, term, "color") != null or
23-
self.color256 or self.truecolor;
24-
} else |_| {}
25-
26-
if (std.process.getEnvVarOwned(std.heap.page_allocator, "NO_COLOR")) |_| {
27-
self.truecolor = false;
28-
self.color256 = false;
29-
self.basic = false;
30-
} else |_| {}
104+
for (env) |entry| {
105+
const entry_str = std.mem.span(entry);
106+
if (std.mem.startsWith(u8, entry_str, "TERM=")) {
107+
const term = entry_str["TERM=".len..];
108+
if (TermInfo.readTermInfo(term)) |has_colors| {
109+
if (has_colors) {
110+
self.basic = true;
111+
self.color256 = std.mem.indexOf(u8, term, "256color") != null;
112+
}
113+
} else |_| {
114+
self.color256 = std.mem.indexOf(u8, term, "256color") != null;
115+
if (self.color256) self.basic = true;
116+
if (!self.basic) {
117+
self.basic = std.mem.indexOf(u8, term, "color") != null or self.truecolor;
118+
}
119+
}
120+
break;
121+
}
122+
}
31123

32124
return self;
33125
}
126+
127+
pub fn formatText(self: *const ColorSupport, text: []const u8, writer: anytype) !void {
128+
var i: usize = 0;
129+
while (i < text.len) : (i += 1) {
130+
if (text[i] == '{') {
131+
const end_idx = std.mem.indexOfScalarPos(u8, text, i, '}') orelse break;
132+
const tag = text[i + 1 .. end_idx];
133+
134+
if (std.mem.startsWith(u8, tag, "/")) {
135+
try writer.writeAll("\x1b[0m");
136+
i = end_idx;
137+
continue;
138+
}
139+
140+
if (std.mem.startsWith(u8, tag, "rgb(")) {
141+
if (self.truecolor) {
142+
const rgb = ColorSupport.parseRgbTag(tag) catch continue;
143+
try writer.print("\x1b[38;2;{};{};{}m", .{ rgb.r, rgb.g, rgb.b });
144+
}
145+
} else if (ColorSupport.parseColorTag(tag)) |c| {
146+
if (self.basic) {
147+
try writer.writeAll(c.ansiSequence());
148+
}
149+
} else if (ColorSupport.parseStyleTag(tag)) |s| {
150+
try writer.writeAll(s);
151+
} else {
152+
try writer.writeAll(text[i .. end_idx + 1]);
153+
}
154+
i = end_idx;
155+
} else {
156+
try writer.writeByte(text[i]);
157+
}
158+
}
159+
}
34160
};
35161

36162
pub const Rgb = struct {
@@ -70,14 +196,7 @@ pub const Color = enum(u8) {
70196
white = 37,
71197
reset = 0,
72198

73-
pub inline fn bright(self: Color) u8 {
74-
return switch (self) {
75-
.reset => 0,
76-
else => @intFromEnum(self) + 60,
77-
};
78-
}
79-
80-
pub inline fn ansiSequence(self: Color) []const u8 {
199+
pub fn ansiSequence(self: Color) []const u8 {
81200
return switch (self) {
82201
.black => "\x1b[30m",
83202
.red => "\x1b[31m",
@@ -90,33 +209,6 @@ pub const Color = enum(u8) {
90209
.reset => "\x1b[0m",
91210
};
92211
}
93-
94-
pub inline fn format(
95-
self: Color,
96-
comptime fmt: []const u8,
97-
options: std.fmt.FormatOptions,
98-
writer: anytype,
99-
) !void {
100-
_ = fmt;
101-
_ = options;
102-
try writer.writeAll(self.ansiSequence());
103-
}
104-
105-
pub inline fn fg(self: Color) Formatter {
106-
return .{ .kind = .basic_fg, .code = @intFromEnum(self) };
107-
}
108-
109-
pub inline fn bg(self: Color) Formatter {
110-
return .{ .kind = .basic_bg, .code = @intFromEnum(self) + 10 };
111-
}
112-
113-
pub inline fn bright_fg(self: Color) Formatter {
114-
return .{ .kind = .basic_fg, .code = self.bright() };
115-
}
116-
117-
pub inline fn bright_bg(self: Color) Formatter {
118-
return .{ .kind = .basic_bg, .code = self.bright() + 10 };
119-
}
120212
};
121213

122214
const AnsiBuffer = struct {
@@ -213,12 +305,12 @@ pub const Style = struct {
213305
}
214306
};
215307

308+
// Initialize color support at runtime
216309
var color_support: ?ColorSupport = null;
217310

218311
pub fn getColorSupport() ColorSupport {
219-
if (color_support) |cs| {
220-
return cs;
221-
}
222-
color_support = ColorSupport.init();
223-
return color_support.?;
312+
return color_support orelse {
313+
color_support = ColorSupport.init();
314+
return color_support.?;
315+
};
224316
}

0 commit comments

Comments
 (0)