Commit b5e65900fea2ce1470a0cc4ecee132fe7056760f

Thomas de Grivel 2022-03-18T17:53:10

use port and size to pygmentize content

diff --git a/.gitignore b/.gitignore
index 9731313..a91e699 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,6 @@ npm-debug.log
 /priv/avatar/
 /priv/repo/dumps/
 /priv/static/
+
+/size
+/size.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..21d2b17
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+
+PROG = size
+SRC = size.c
+SRC_O = size.o
+
+all: ${PROG}
+
+${PROG}: ${SRC_O}
+	${CC} ${CFLAGS} ${LDFLAGS} ${SRC_O} ${LIBS} -o ${PROG}
+
+.c.o:
+	${CC} ${CPPFLAGS} ${CFLAGS} -c $< -o $@
+
+clean:
+	rm -f ${SRC_O} ${PROG}
+
+.PHONY: all clean
diff --git a/lib/pygmentize.ex b/lib/pygmentize.ex
index 85e506d..da39dd8 100644
--- a/lib/pygmentize.ex
+++ b/lib/pygmentize.ex
@@ -5,31 +5,41 @@ defmodule Pygmentize do
     |> Base.url_encode64()
   end
 
-  def html(content, filename) do
-    dir = "#{System.tmp_dir()}/#{random_string()}"
-    File.mkdir_p(dir)
-    path = "#{dir}/#{filename}"
-    File.write(path, content)
-    {out, status} = System.cmd("pygmentize", ["-f", "html", path], stderr_to_stdout: true)
-    output = case status do
-      0 -> out
-      _ -> nil
+  def lexer(filename) do
+    {out, status} = System.cmd("pygmentize", ["-N", filename |> String.replace(~r/ /, "_")])
+    case status do
+      0 -> out |> String.trim()
+      _ -> ""
     end
-    File.rm_rf(dir)
-    output
+  end
+    
+  def html(content, filename) do
+    lexer = lexer(filename)
+    cmd = "./size #{byte_size(content)} pygmentize -l #{lexer} -f html"
+    IO.inspect(cmd)
+    port = Port.open({:spawn, cmd}, [:binary, :use_stdio, :exit_status, :stderr_to_stdout])
+    Port.monitor(port)
+    send(port, {self(), {:command, content}})
+    html_port(content, port, [])
   end
 
-  def get_reply(port, state) do
-    ref = state.ref
+  def html_port(content, port, acc) do
     receive do
-      {^port, {:data, msg}} ->
-        state = Map.put(state, :content, [msg | state.content])
-        get_reply(port, state)
-      {:DOWN, ^ref, :port, _port, :normal} ->
-        Enum.reverse(state.output)
-      msg ->
-        IO.inspect([msg: msg])
-        get_reply(port, state)
+      {^port, {:exit_status, 0}} ->
+        acc |> Enum.reverse() |> Enum.join()
+      {^port, {:exit_status, status}} ->
+        IO.inspect("pygmentize exited with status #{status}")
+        content
+      {^port, {:data, data}} ->
+        html_port(content, port, [data | acc])
+      {:DOWN, _, :port, ^port, reason} ->
+        IO.inspect({:down, reason})
+        acc |> Enum.reverse() |> Enum.join()
+      x ->
+        IO.inspect(x)
+        html_port(content, port, acc)
+    after 1000 ->
+        html_port(content, port, acc)
     end
   end
 end
diff --git a/size.c b/size.c
new file mode 100644
index 0000000..37c0852
--- /dev/null
+++ b/size.c
@@ -0,0 +1,50 @@
+/* size - truncate standard input by size */
+
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUFSIZE 8192
+
+int usage(char *argv0)
+{
+  fprintf(stderr, "Usage: %s SIZE COMMAND [ARGS ...]\n", argv0);
+  return 1;
+}
+
+int main (int argc, char **argv)
+{
+  char *a;
+  char cmd[BUFSIZE];
+  char *c = cmd;
+  char buf[BUFSIZE];
+  int i = 0;
+  size_t pos = 0;
+  ssize_t r;
+  unsigned long size;
+  FILE *pipe;
+  size_t len;
+  if (argc < 3)
+    return usage(argv[0]);
+  size = strtoul(argv[1], NULL, 10);
+  for (i = 2; i < argc; i++) {
+    a = argv[i];
+    while ((*c++ = *a++))
+      ;
+    c--;
+    *c++ = ' ';
+  }
+  *c = 0;
+  pipe = popen(cmd, "w");
+  len = pos + BUFSIZE < size ? BUFSIZE : size - pos;
+  while (pos < size && (r = fread(buf, 1, len, stdin)) > 0) {
+    if (fwrite(buf, r, 1, pipe) != 1)
+      err(1, "fwrite");
+    pos += r;
+  }
+  if (r < 0)
+      err(1, "fread");
+  pclose(pipe);
+  return 0;
+}