master ohmu/fastfetch-openbsd / scripts / gen-man.py
ohmu  ·  2026-10-15
  1#!/usr/bin/env python3
  2
  3"""
  4Python script to generate a comprehensive man page for the command `fastfetch`.
  5
  6The generated man page content will be printed to stdout,
  7so you will need to pipe it to a file if you want to save it.
  8Example: python3 gen-man.py > fastfetch.1
  9
 10The command options are generated using a JSON file.
 11For the JSON file format, see:
 12https://github.com/fastfetch-cli/fastfetch/blob/dev/src/data/help.json
 13"""
 14
 15from json import load
 16from datetime import datetime, timezone
 17from time import time
 18from re import search
 19from os import environ, path
 20
 21
 22###### Text Decorations Tags ######
 23
 24startUnderline = r"\fI" # start underline text tag
 25endUnderline = r"\fR" # end underline text tag
 26
 27startBold = r"\fB" # start bold text tag
 28endBold = r"\fR" # end bold text tag
 29
 30
 31###### Parameters ######
 32
 33# path to the current directory
 34pathToCurrentDir = path.dirname(__file__)
 35# path to the JSON option file
 36pathToHelpFile = path.join(pathToCurrentDir, "../doc/help.json")
 37# man page section
 38manSection = 1
 39# title (center header)
 40titlePage = "FASTFETCH"
 41# date (center footer)
 42# format : "Month (abbreviation) Day Year"
 43todayDate = datetime.fromtimestamp(
 44    int(environ.get("SOURCE_DATE_EPOCH", time())),
 45    tz=timezone.utc,
 46).strftime("%b %d %Y")
 47# file to fastfetch version (left footer)
 48pathToVersionFile = path.join(pathToCurrentDir, "../CMakeLists.txt")
 49
 50
 51###### Sections Text ######
 52
 53# text displayed in the "NAME" section
 54nameSection = r"fastfetch \- A fast and feature-rich system information tool similar to neofetch"
 55
 56# text displayed in the "DESCRIPTION" section
 57descriptionSection = r"""
 58Fastfetch is a tool for displaying system information in a visually appealing way. Written primarily in C, it focuses on performance and customizability while providing functionality similar to neofetch.
 59It supports Linux, Android, FreeBSD, macOS, and Windows 7 or newer.
 60"""
 61
 62# text displayed at the beginning of the "OPTIONS" section
 63optionSection = r"""
 64Options are parsed in a case-insensitive manner. For example, \fB--logo-type\fR and \fB--LOGO-TYPE\fR are treated identically.
 65
 66Arguments in square brackets are optional. Optional boolean arguments default to 'true' when specified without a value.
 67
 68For more detailed information about a specific option, use:
 69\fBfastfetch -h <option_name_without_dashes>\fR
 70
 71Any combination of options can be made permanent by generating a configuration file:
 72\fBfastfetch <options> --gen-config\fR
 73"""
 74
 75# text displayed in the "CONFIGURATION"
 76configurationSection = f"""
 77.SS Fetch Structure
 78
 79The structure defines which modules to display and in what order. It consists of module names separated by colons (:).
 80For example: {startBold}title:separator:os:kernel:uptime{endBold}
 81
 82To list all available modules, use {startBold}--list-modules{endBold}
 83
 84
 85.SS Config Files
 86
 87Fastfetch uses JSONC (JSON with Comments) for configuration files. These files must have the .jsonc extension.
 88
 89You can generate a default config file using {startBold}--gen-config{endBold}. By default, the config file is saved at {startBold}~/.config/fastfetch/config.jsonc{endBold}.
 90
 91The configuration/preset files are searched in the following locations (in order):
 92
 93{startBold}1.{endBold} Relative to the current working directory
 94
 95{startBold}2.{endBold} Relative to ~/.local/share/fastfetch/presets/
 96
 97{startBold}3.{endBold} Relative to /usr/share/fastfetch/presets/
 98
 99For detailed information on logo options, module configuration, and formatting, visit:
100{startBold}https://github.com/fastfetch-cli/fastfetch/wiki/Configuration{endBold}
101
102Fastfetch provides several built-in presets. List them with {startBold}--list-presets{endBold}.
103
104.SS JSON Schema
105A JSON schema is available for editor intelligence when editing the configuration file. Add the following line at the beginning of your config file:
106
107{startBold}"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json"{endBold}
108"""
109
110# text displayed in the "EXAMPLE" section
111exampleSection = f"""
112.SS Basic Usage
113{startBold}fastfetch{endBold}
114
115.SS Use a specific logo
116{startBold}fastfetch --logo arch{endBold}
117
118.SS Custom structure
119{startBold}fastfetch --structure title:os:kernel:uptime:memory{endBold}
120
121.SS Generate a config file
122{startBold}fastfetch --gen-config{endBold}
123
124.SS Use a preset
125{startBold}fastfetch --config neofetch{endBold}
126
127.SS Config File Example
128.nf
129// ~/.config/fastfetch/config.jsonc
130{{
131    "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
132    "logo": {{
133        "type": "auto",
134        "source": "arch"
135    }},
136    "display": {{
137        "separator": ": ",
138        "color": {{
139            "keys": "blue",
140            "title": "red"
141        }},
142        "key": {{
143            "width": 12
144        }}
145    }},
146    "modules": [
147        "title",
148        "separator",
149        "os",
150        "kernel",
151        "uptime",
152        {{
153            "type": "memory",
154            "format": "{{used}}/{{total}} ({{used_percent}}%)"
155        }}
156    ]
157}}
158.fi
159"""
160
161# text displayed in the "BUGS" section
162bugSection = "Please report bugs to: https://github.com/fastfetch-cli/fastfetch/issues"
163
164# text displayed in the "AUTHORS" section
165authorsSection = "Fastfetch is developed by a team of contributors on GitHub.\nVisit https://github.com/fastfetch-cli/fastfetch for more information."
166
167
168###### Argument decoration ######
169
170### optional arguments tags ###
171
172# if an optional argument is displayed as [?optArg] (with "optArg" underlined)
173# this value should be f"[?{startUnderline}"
174startOptionalArgument = f"[{startUnderline}"
175# if an optional argument is displayed as [?optArg] (with "optArg underlined")
176# this value should be f"{endUnderline}]"
177endOptionalArgument = f"{endUnderline}]"
178
179### mandatory arguments tags ###
180startMandatoryArgument = f"{startUnderline}"
181endMandatoryArgument = f"{endUnderline}"
182
183def main():
184
185    # importing the JSON file
186    with open(pathToHelpFile, 'r') as jsonFile:
187        helpFileData = load(jsonFile) # json.load
188
189
190    ######## Start printing the generated .1 file ########
191
192
193    ###### header, footer & config #####
194
195    print(f".TH FASTFETCH {manSection} ", end=" ")
196    print(f"\"{todayDate}\"", end=" ")
197
198    # version number
199    with open(pathToVersionFile, 'r') as versionFile:
200
201        # research version number in file with regex
202        for line in versionFile:
203            researchVersion = search(r"^\s*VERSION (\d+\.\d+\.\d+)$", line)
204            if (researchVersion):
205                print(f"\"Fastfetch {researchVersion.group(1)}\"", end=" ")
206                break
207
208    print(f"\"{titlePage}\"")
209
210
211    ###### Name ######
212
213    print(".SH NAME")
214    print(nameSection)
215
216
217    ##### Synopsis ######
218
219    print(".SH SYNOPSIS")
220    print(".B fastfetch")
221    print(f"[{startUnderline}OPTIONS{endUnderline}...]")
222
223
224    ###### Description ######
225
226    print(".SH DESCRIPTION")
227    print(descriptionSection)
228
229
230    ###### Configuration ######
231
232    print(".SH CONFIGURATION")
233    print(configurationSection)
234
235
236    ###### Options ######
237
238    print(".SH OPTIONS")
239    print(optionSection)
240    print()
241
242    # loop through every options sections
243    for key, value in helpFileData.items():
244
245        # print new subsection
246        print(f".SS {key}")
247
248        # loop through every option in a section
249        for option in value:
250            # list of existing keys for this option
251            keyList = option.keys()
252
253            # start a new "option" entry
254            print(".TP")
255            print(startBold, end="")
256
257            # short option (-opt)
258            if "short" in keyList:
259                print(fr"\-{ option['short'] }", end="")
260                # if also have a long option, print a comma
261                if "long" in keyList:
262                    print(", ", end="")
263
264            # long option (--option)
265            if "long" in keyList:
266                print(fr"\-\-{ option['long'] }", end="")
267
268            print(endBold, end=" ")
269
270            # arguments
271            if "arg" in keyList:
272                # if argument is optional, print "[arg]"
273                if "optional" in option["arg"].keys() and option["arg"]["optional"]:
274                    print(startOptionalArgument + option['arg']['type'] + endOptionalArgument, end="")
275
276                # if argument is mandatory, print "arg"
277                else:
278                    print(startMandatoryArgument + option['arg']['type'] + endMandatoryArgument, end="")
279
280            # description
281            print()
282
283            # If desc is a list, join with newlines and proper spacing
284            if isinstance(option['desc'], list):
285                desc_text = "\n ".join(option['desc'])
286                print(f" {desc_text}")
287            else:
288                print(f" {option['desc']}")
289
290            # Add remarks if available
291            if "remark" in keyList:
292                print()
293                if isinstance(option['remark'], list):
294                    for remark in option['remark']:
295                        print(f" {remark}")
296                else:
297                    print(f" {option['remark']}")
298
299            print()
300
301
302    ###### Examples ######
303
304    print(".SH EXAMPLES")
305    print(exampleSection)
306
307
308    ###### See Also ######
309
310    print(".SH \"SEE ALSO\"")
311    print(".BR neofetch (1)")
312
313
314    ###### Bugs ######
315
316    print(".SH BUGS")
317    print(bugSection)
318
319
320    ###### Authors ######
321
322    print(".SH AUTHORS")
323    print(authorsSection)
324
325
326if __name__ == "__main__":
327    main()