const std = @import("std"); const c = @cImport({ @cInclude("cmark.h"); }); const endsWith = std.mem.endsWith; fn stripExtension(file_name: []const u8) ![]const u8 { const index = std.mem.lastIndexOfLinear(u8, file_name, "."); if (index) |i| { return file_name[0..i]; } else return error.NoExtension; } const HeaderOptions = struct { // Si esto es true, se muestra un botón para volver al index. ir_al_inicio: bool = true, // Si esto es true, se muestra un
. header: bool = true, }; fn header( writer: std.fs.File.Writer, title: []const u8, src_name: []const u8, options: HeaderOptions, ) !void { // TODO: deshardcodear base_uri try writer.print( \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{0s} \\ , .{ title, src_name }); // if test "$mirror" = true; then // echo "

Ojo: este sitio es un espejo (mirror). nulo.in es la fuente.

" // fi if (options.ir_al_inicio) { try writer.writeAll("☚ Volver al inicio\n"); } if (options.header) { try writer.print( \\
\\

{s}

\\Historial \\
\\ , .{ title, src_name }); } } pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var cwd = try std.fs.cwd().openDir(".", .{ .iterate = true }); defer cwd.close(); var cwd_iterator = cwd.iterate(); var build_dir = try cwd.makeOpenPath("build", .{}); defer build_dir.close(); while (try cwd_iterator.next()) |entry| { if (entry.kind != .File) continue; // Autocopiarnos :) if (endsWith(u8, entry.name, ".sh") or endsWith(u8, entry.name, ".md") or endsWith(u8, entry.name, ".css") or endsWith(u8, entry.name, ".png") or endsWith(u8, entry.name, ".jpg") or endsWith(u8, entry.name, ".mp4") or endsWith(u8, entry.name, ".svg") or endsWith(u8, entry.name, ".html")) { try cwd.copyFile(entry.name, build_dir, entry.name, .{}); } if (endsWith(u8, entry.name, ".md")) try generateMarkdown(allocator, cwd, entry.name, build_dir); if (endsWith(u8, entry.name, ".gen")) try generateExecutable(allocator, cwd, entry.name, build_dir); } } const Writer = std.io.BufferedWriter(4096, std.fs.File.Writer).Writer; fn hackilyTransformHtml(input: []const u8, writer: Writer) !void { var iter = std.mem.split(u8, input, "\n"); while (iter.next()) |line| { var index: usize = 0; while (index < line.len) : (index += 1) { const rest = line[index..]; const link_marker = "{0s} , .{name}); continue; } } try writer.writeByte(line[index]); } try writer.writeByte('\n'); } } fn generateMarkdown( allocator: std.mem.Allocator, cwd: std.fs.Dir, src_name: []const u8, build_dir: std.fs.Dir, ) !void { var file = try cwd.openFile(src_name, .{}); defer file.close(); const markdown = try file.readToEndAllocOptions( allocator, 69696969, null, @alignOf(u32), 0, ); defer allocator.free(markdown); const html = c.cmark_markdown_to_html( markdown.ptr, markdown.len, c.CMARK_OPT_UNSAFE, //| c.CMARK_OPT_SMART, ); defer std.c.free(html); const output_file_name = try std.fmt.allocPrint( allocator, "{s}.html", .{try stripExtension(src_name)}, ); defer allocator.free(output_file_name); const is_index = std.ascii.eqlIgnoreCase(src_name, "index.md"); const title = if (is_index) "nulo.in" else try stripExtension(src_name); var output = try build_dir.createFile(output_file_name, .{}); defer output.close(); try header(output.writer(), title, src_name, .{ .ir_al_inicio = !is_index, .header = !is_index, }); var buffered_writer = std.io.bufferedWriter(output.writer()); try hackilyTransformHtml( std.mem.span(html), buffered_writer.writer(), ); try buffered_writer.flush(); } fn generateExecutable( allocator: std.mem.Allocator, cwd: std.fs.Dir, src_name: []const u8, build_dir: std.fs.Dir, ) !void { _ = cwd; const output_file_name = try std.fmt.allocPrint( allocator, "{s}.html", .{try stripExtension(src_name)}, ); defer allocator.free(output_file_name); const title = try stripExtension(src_name); var output = try build_dir.createFile(output_file_name, .{}); defer output.close(); try header(output.writer(), title, src_name, .{}); const executable_name = try std.fmt.allocPrint(allocator, "./{s}", .{src_name}); defer allocator.free(executable_name); // const process = try std.ChildProcess.init(&.{executable_name}, allocator); // defer process.deinit(); // process.stdout_behavior = .Ignore; // process.stdout = output; // const term = try process.spawnAndWait(); const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{executable_name}, }); defer allocator.free(result.stdout); defer allocator.free(result.stderr); if (result.stderr.len > 0) { std.log.err("{s} stderr: {s}", .{ src_name, result.stderr }); } switch (result.term) { .Exited => |status| { if (status != 0) return error.ProcessFailed; }, else => unreachable, } try output.writeAll(result.stdout); }