code: public static void nailMain(NGContext context)
        throws java.security.NoSuchAlgorithmException, java.io.IOException {
      String[] args = context.getArgs();
  
      if (args.length == 0) {
        // display available algorithms
        Set algs = getCryptoImpls("MessageDigest");
        for (Object alg : algs) {
          context.out.println(alg);
        }
        return;
      }
  
      // perform the actual hash.  throw any exceptions back to the user.
      MessageDigest md = MessageDigest.getInstance(args[0]);
  
      byte[] b = new byte[1024];
      int bytesRead = context.in.read(b);
      while (bytesRead != -1) {
        md.update(b, 0, bytesRead);
        bytesRead = System.in.read(b);
      }
      byte[] result = md.digest();
  
      // convert hash result to a string of hex characters and print it to the client.
      StringBuffer buf = new StringBuffer();
      for (int i = 0; i < result.length; ++i) {
        buf.append(HEXCHARS[(result[i] >> 4) & 0x0f]);
        buf.append(HEXCHARS[result[i] & 0x0f]);
      }
      context.out.println(buf);
    }
docs: Hashes client stdin, displays hash result to client stdout. Requires one command line
  parameter, either the name of the hash algorithm to use (e.g., "MD5") or "?" to request a list
  of available algorithms. Any exceptions become the problem of the user.