Hacker News new | ask | show | jobs
by kazinator 1910 days ago

  $ find /etc/network | ./frangi.tl
  etc:
      network:
          if-post-down.d:
              wireless-tools wpasupplicant avahi-daemon 
          if-down.d:
              resolvconf wpasupplicant avahi-autoipd 
          interfaces.d interfaces if-pre-up.d:
              wireless-tools wpasupplicant ethtool 
          if-up.d:
              ntpdate wpasupplicant 000resolvconf openssh-server ethtool avahi-autoipd slrn avahi-daemon 

  $ find /etc/network | ./frangi-cheat.tl 
  /etc/network
              /if-post-down.d
                             /wireless-tools
                             /wpasupplicant
                             /avahi-daemon
              /if-down.d
                        /resolvconf
                        /wpasupplicant
                        /avahi-autoipd
              /interfaces.d
              /interfaces
              /if-pre-up.d
                          /wireless-tools
                          /wpasupplicant
                          /ethtool
              /if-up.d
                      /ntpdate
                      /wpasupplicant
                      /000resolvconf
                      /openssh-server
                      /ethtool
                      /avahi-autoipd
                      /slrn
                      /avahi-daemon

  $ cat frangi-cheat.tl
  #!/usr/bin/env txr
  (let (old-path)
    (whilet ((line (get-line)))
      (whenlet ((path (tok #/[^\/]*/ line))
                (canon `@{path "/"}`)
                (pos (mismatch path old-path)))
        (let ((cpos (max 0 (+ pos -1 [sum [path 0..pos] len]))))
          (put-line `@{"" cpos}@{canon [cpos..:]}`))
        (set old-path path))))


  $ cat frangi.tl
  #!/usr/bin/env txr

  (defstruct (node name) list-builder
    name

    (:method equal (me) me.name)

    (:method ensure-child (me child-name)
      (let ((children me.(get)))
        (or (find child-name me.(get))
            (let ((new-child (new (node child-name))))
              me.(add new-child)
              new-child))))

    (:method print (me stream : pretty-p)
      (let* ((old-im (set-indent-mode stream indent-code))
             (old-id (get-indent stream))
             (children me.(get))
             (is-bottom (none children .(get))))
        (unwind-protect
          (cond
            (children
              (put-line `@{me.name}:` stream)
              (set-indent stream (+ old-id 4))
              [mapdo (op print @1 stream) children]
              (when is-bottom
                (put-char #\newline stream)))
            (t (put-string `@{me.name} `) stream))
          (set-indent-mode stream old-im)
          (set-indent stream old-id)))))

  (let ((supernode (new (node :root))))
    (whilet ((line (get-line)))
      (let ((path (tok #/[^\/]+/ line))
            (node supernode))
        (each ((comp path))
          (set node node.(ensure-child comp)))))
    (each ((top-child supernode.(get)))
      (pprinl top-child)))
1 comments

C version. Maybe this logic should be built into GNU find as an option!

  $ find /etc/network | ./frangi-cheat 
  /etc/network
              /if-post-down.d
                             /wireless-tools
                             /wpasupplicant
                             /avahi-daemon
              /if-down.d
                        /resolvconf
                        /wpasupplicant
                        /avahi-autoipd
              /interfaces.d
              /interfaces
              /if-pre-up.d
                          /wireless-tools
                          /wpasupplicant
                          /ethtool
              /if-up.d
                      /ntpdate
                      /wpasupplicant
                      /000resolvconf
                      /openssh-server
                      /ethtool
                      /avahi-autoipd
                      /slrn
                      /avahi-daemon

  $ cat frangi-cheat.c
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  int main(void)
  {
    char old_line[FILENAME_MAX] = "", line[FILENAME_MAX];

    while (fgets(line, sizeof line, stdin)) {
      char *p = line, *o = old_line, *nl = strchr(line, '\n');

      if (nl)
        *nl = 0;

      while (*p && *o) {
        char *op = p;

        if (*o == '/' && *p == '/')
          o++, p++;

        size_t lp = strcspn(p, "/");
        size_t lo = strcspn(o, "/");

        if (lp == lo && !strncmp(p, o, lp)) {
          p += lp;
          o += lp;
          printf("%*s", (int) (p - op), "");
          continue;
        }

        p = op;
        break;
      }

      puts(p);
      strcpy(old_line, line);
    }

    return feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE;
  }
Note: yes, we could swap pointers between two buffers instead of strcpy.
New version:

- swap pointers to flip buffers instead of strcpy

- handle corner case of directory printed after contents (find -depth) by avoiding printing nothing but spaces, or nothing but spaces followed by a slash

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  int main(void)
  {
    typedef char buf_t[FILENAME_MAX];
    buf_t buf[2] = { "" };
    buf_t *pline = &buf[0], *line = &buf[1];

    while (fgets(*line, sizeof *line, stdin)) {
      buf_t *tmp;
      char *l = *line, *p = *pline, *nl = strchr(l, '\n');

      if (nl)
        *nl = 0;

      while (*l && *p) {
        char *op = l;

        if (*p == '/' && *l == '/')
          p++, l++;

        size_t lp = strcspn(l, "/");
        size_t lo = strcspn(p, "/");

        if (lp == lo && !strncmp(l, p, lp)) {
          l += lp;
          p += lp;
          continue;
        }

        l = op;
        break;
      }

      if (l[0] && (l[1] || l[0] != '/'))
        printf("%*s%s\n", (int) (l - *line), "", l);
      else
        puts(*line);

      tmp = pline, pline = line, line = tmp;
    }

    return feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE;
  }
I love your enthusiasm, and thanks for the code. But 1) the above only works with sorted input and 2) adding it to find that would be like adding '-v' to 'cat' which as we know is considered harmful. http://harmful.cat-v.org/cat-v/
Actually, what it works with is a partially sorted input: an input in some tree-order input, which is what any recursive file system traversal will always put out. It will work with depth first or breadth-first order, with usefully different results, and that order preserved.

All it is doing is hiding the redundancy with spaces to improve the human readability of find's output.

Here it is on the same data in breadth-first. Note the lack of any lexicographic sort: "interfaces" is flanked by "if-down.d" and "if-pre-up.d":

  /etc/network
              /if-post-down.d
              /if-down.d
              /interfaces.d
              /interfaces
              /if-pre-up.d
              /if-up.d
              /if-post-down.d/wireless-tools
                             /wpasupplicant
                             /avahi-daemon
              /if-down.d/resolvconf
                        /wpasupplicant
                        /avahi-autoipd
              /if-pre-up.d/wireless-tools
                          /wpasupplicant
                          /ethtool
              /if-up.d/ntpdate
                      /wpasupplicant
                      /000resolvconf
                      /openssh-server
                      /ethtool
                      /avahi-autoipd
                      /slrn
                      /avahi-daemon
My first program in TXR Lisp in the grandparent comment builds the tree structure from the paths in any order and then prints that, so the paths could be scrambled into random order, yet it will recover the tree structure.

However, not munging the the output in that way and just tweaking the original output for readability has some advantages