001/*
002 * JDrupes Builder
003 * Copyright (C) 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core.console;
020
021import java.io.InputStreamReader;
022
023/// Provides information about the terminal. This class relies on variables
024/// `LINES` and `COLUMNS` to be environment variables (exported).
025///
026public class TerminalInfo {
027    private int lines;
028    private int cols;
029    private final boolean supportsAnsi;
030
031    /// Initializes a new terminal info.
032    ///
033    public TerminalInfo() {
034        this.supportsAnsi = System.console() != null
035            && System.getenv("TERM") != null
036            && !"dumb".equals(System.getenv("TERM"));
037        refreshSize();
038    }
039
040    /// Returns whether the terminal supports ANSI.
041    ///
042    /// @return true, if successful
043    ///
044    public boolean supportsAnsi() {
045        return supportsAnsi;
046    }
047
048    /// Returns the number of lines.
049    ///
050    /// @return the lines
051    ///
052    public int lines() {
053        return lines;
054    }
055
056    /// Returns the number of columns.
057    ///
058    /// @return the columns
059    ///
060    public int columns() {
061        return cols;
062    }
063
064    @SuppressWarnings({ "PMD.RelianceOnDefaultCharset",
065        "PMD.AvoidCatchingGenericException", "PMD.ShortVariable",
066        "PMD.AvoidLiteralsInIfCondition", "PMD.UnusedAssignment" })
067    private void refreshSize() {
068        try {
069            Process proc
070                = new ProcessBuilder("sh", "-c", "stty size < /dev/tty")
071                    .redirectErrorStream(true).start();
072
073            try (java.io.BufferedReader in = new java.io.BufferedReader(
074                new InputStreamReader(proc.getInputStream()))) {
075                String line = in.readLine();
076                if (line != null) {
077                    String[] parts = line.trim().split("\\s+");
078                    if (parts.length == 2) {
079                        lines = Integer.parseInt(parts[0]);
080                        cols = Integer.parseInt(parts[1]);
081                        return;
082                    }
083                }
084            }
085        } catch (Exception ignored) {
086            // use fallback
087        }
088
089        // fallback
090        lines = 24;
091        cols = 80;
092    }
093}