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()