URI: 
       use a binary search for checking the gopher type file extensions - gopher-validator - Simple gopher menu validator.
  HTML git clone git://bitreich.org/gopher-validator git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/gopher-validator
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit a230aa92f5f19ff3160fd7cdc7acc2810a02839a
   DIR parent d2d0e95268db4d42a56e0b8967af42b146a3a7e3
  HTML Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Fri, 18 Feb 2022 14:27:52 +0100
       
       use a binary search for checking the gopher type file extensions
       
       ... and improve a check which shouldve been outside the loop.
       
       Diffstat:
         M gopher-validator.c                  |      35 ++++++++++++++++++-------------
       
       1 file changed, 20 insertions(+), 15 deletions(-)
       ---
   DIR diff --git a/gopher-validator.c b/gopher-validator.c
       @@ -133,6 +133,13 @@ warning(const char *fmt, ...)
        }
        
        int
       +gophertypecmp(const void *v1, const void *v2)
       +{
       +        return strcasecmp(((struct gophertype *)v1)->ext,
       +               ((struct gophertype *)v2)->ext);
       +}
       +
       +int
        isvalidhost(const char *s)
        {
                int colons;
       @@ -220,6 +227,7 @@ edial(const char *host, const char *port)
        void
        checkdir(FILE *fp)
        {
       +        struct gophertype gt, *rgt;
                struct visited v;
                char line[1024], *end, *s;
                size_t linenr;
       @@ -333,21 +341,18 @@ checkdir(FILE *fp)
                                }
                        }
        
       -                /* check type with file extension */
       -                if ((s = strrchr(v.path, '.')) && !strchr(s, '/')) {
       -                        s++;
       -                        for (i = 0; i < sizeof(types) / sizeof(*types); i++) {
       -                                if (primarytype == 'h' &&
       -                                    !strncmp(v.path, "URL:", sizeof("URL:") - 1))
       -                                        continue;
       -
       -                                if (!strcasecmp(s, types[i].ext) &&
       -                                    !strchr(types[i].allow, primarytype)) {
       -                                        warning("%zu: invalid type '%c' for extension '%s', valid types: '%s'\n",
       -                                                linenr, primarytype, types[i].ext, types[i].allow);
       -                                        break;
       -                                }
       -                        }
       +                /* check type with file extension, unless it is the HTML 'h'
       +                   type with a "URL:" prefix */
       +                if ((s = strrchr(v.path, '.')) && !strchr(s, '/') &&
       +                    !(primarytype == 'h' && !strncmp(v.path, "URL:", sizeof("URL:") - 1))) {
       +                        gt.ext = ++s;
       +                        if (!(rgt = bsearch(&gt, &types, sizeof(types) / sizeof(types[0]),
       +                                sizeof(types[0]), &gophertypecmp)))
       +                                continue;
       +
       +                        if (!strchr(rgt->allow, primarytype))
       +                                warning("%zu: invalid type '%c' for extension '%s', valid types: '%s'\n",
       +                                        linenr, primarytype, rgt->ext, rgt->allow);
                        }
        
                        if (!isvalidhost(v.host))