class CtagsUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(CtagsUtil.class);

    public static final String SYSTEM_CTAGS_PROPERTY = "org.opengrok.indexer.analysis.Ctags";

    /** Private to enforce static. */
    private CtagsUtil() {
    }

    /**
     * Check that {@code ctags} program exists and is working.
     * @param ctagsBinary name of the ctags program or path
     * @return true if the program works, false otherwise
     */
    public static boolean validate(String ctagsBinary) {
        if (!isUniversalCtags(ctagsBinary)) {
            return false;
        }

        return canProcessFiles(RuntimeEnvironment.getInstance().getSourceRootFile());
    }

    /**
     * Run ctags program on a known temporary file to be created under given path and see if it was possible
     * to get some symbols.
     * @param baseDir directory to use for storing the temporary file
     * @return true if at least one symbol was found, false otherwise
     */
    private static boolean canProcessFiles(File baseDir) {<FILL_FUNCTION_BODY>}

    @Nullable
    public static String getCtagsVersion(String ctagsBinary) {
        Executor executor = new Executor(new String[]{ctagsBinary, "--version"});
        executor.exec(false);
        String output = executor.getOutputString();
        if (output != null) {
            output = output.trim();
        }

        return output;
    }

    private static boolean isUniversalCtags(String ctagsBinary) {
        String version = getCtagsVersion(ctagsBinary);
        boolean isUniversalCtags = version != null && version.contains("Universal Ctags");
        if (version == null || !isUniversalCtags) {
            LOGGER.log(Level.SEVERE, "Error: No Universal Ctags found !\n"
                            + "(tried running " + "{0}" + ")\n"
                            + "Please use the -c option to specify path to a "
                            + "Universal Ctags program.\n"
                            + "Or set it in Java system property {1}",
                    new Object[]{ctagsBinary, SYSTEM_CTAGS_PROPERTY});
            return false;
        }

        return true;
    }

    /**
     * Gets the base set of languages by executing {@code --list-languages} for the specified binary.
     * @return empty list on failure to run, or a defined list
     */
    public static Set<String> getLanguages(String ctagsBinary) {
        Executor executor = new Executor(new String[]{ctagsBinary, "--list-languages"});
        int rc = executor.exec(false);
        String output = executor.getOutputString();
        if (output == null || rc != 0) {
            LOGGER.log(Level.WARNING, "Failed to get Ctags languages");
            return Collections.emptySet();
        }

        output = output.replaceAll("\\s+\\[disabled]", "");
        String[] split = output.split("(?m)$");
        Set<String> result = new HashSet<>();
        for (String lang : split) {
            lang = lang.trim();
            if (!lang.isEmpty()) {
                result.add(lang);
            }
        }
        return result;
    }

    /**
     * Deletes Ctags temporary files left over after terminating Ctags processes
     * in case of timeout or Ctags crash, @see Ctags#doCtags.
     */
    public static void deleteTempFiles() {
        Set<String> dirs = new HashSet<>(Arrays.asList(System.getProperty("java.io.tmpdir"),
                System.getenv("TMPDIR"), System.getenv("TMP")));

        if (SystemUtils.IS_OS_UNIX) {
            // hard-coded TMPDIR in Universal Ctags on Unix.
            dirs.add("/tmp");
        }
        dirs = dirs.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        for (String directoryName : dirs) {

            File directory = new File(directoryName);
            if (!directory.isDirectory()) {
                continue;
            }

            LOGGER.log(Level.FINER, "deleting Ctags temporary files in directory ''{0}''", directoryName);
            deleteTempFiles(directory);
        }
    }

    private static void deleteTempFiles(File directory) {
        final Pattern pattern = Pattern.compile("tags\\.\\S{6}"); // ctags uses this pattern to call mkstemp()

        File[] files = directory.listFiles((dir, name) -> {
            Matcher matcher = pattern.matcher(name);
            return matcher.find();
        });

        if (Objects.isNull(files)) {
            return;
        }

        for (File file : files) {
            if (file.isFile()) {
                try {
                    Files.delete(file.toPath());
                } catch (IOException exception) {
                    LOGGER.log(Level.WARNING, String.format("cannot delete file '%s'", file), exception);
                }
            }
        }
    }
}

        Path inputPath;
        try {
            inputPath = File.createTempFile("ctagsValidation", ".c", baseDir).toPath();
        } catch (IOException e) {
            LOGGER.log(Level.WARNING, "cannot create temporary file in ''{0}''", baseDir);
            return false;
        }
        final String resourceFileName = "sample.c";
        ClassLoader classLoader = CtagsUtil.class.getClassLoader();
        try (InputStream inputStream = classLoader.getResourceAsStream(resourceFileName)) {
            if (inputStream == null) {
                LOGGER.log(Level.SEVERE, "cannot get resource URL of ''{0}'' for ctags check",
                        resourceFileName);
                return false;
            }

            Files.copy(inputStream, inputPath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, "cannot copy ''{0}'' to ''{1}'' for ctags check: {2}",
                    new Object[]{resourceFileName, inputPath, e});
            return false;
        }

        Ctags ctags = new Ctags();
        try {
            Definitions definitions = ctags.doCtags(inputPath.toString());
            if (definitions != null && definitions.numberOfSymbols() > 1) {
                return true;
            }
        } catch (IOException | InterruptedException e) {
            LOGGER.log(Level.SEVERE, "cannot determine whether ctags can produce definitions", e);
        } finally {
            ctags.close();
            try {
                Files.delete(inputPath);
            } catch (IOException e) {
                LOGGER.log(Level.SEVERE, "cannot delete ''{0}''", inputPath);
            }
        }

        return false;
    
<no_super_class>