From eef69eed9a67f85d1f3724ccda2c804861885bb8 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 21 Dec 2025 15:42:57 +0200 Subject: [PATCH] Remove dependency on bash, switch to new module system and simplify code --- README.md | 2 +- config/ascii/arch | 8 +- config/ascii/artix | 2 +- config/ascii/bedrock | 4 +- config/ascii/cachyos | 42 ++-- config/ascii/debian | 14 +- config/ascii/fedora | 26 +-- config/ascii/gentoo | 34 +-- config/ascii/linuxmint | 38 ++-- config/ascii/opensuse | 24 +-- config/ascii/opensuse-tumbleweed | 2 +- config/ascii/pop | 32 +-- config/ascii/tide | 28 +-- config/ascii/ubuntu | 32 +-- config/ascii/void | 36 ++-- config/config.yaml | 21 +- config/fetch_script.sh | 46 ----- src/color.go | 21 +- src/config.go | 46 +++++ src/hardware.go | 28 ++- src/main.go | 319 +++++++++------------------- src/modules.go | 345 +++++++++++++++++++++++++++++++ src/partitions.go | 2 +- src/system.go | 40 +++- src/user.go | 4 +- 25 files changed, 737 insertions(+), 459 deletions(-) delete mode 100644 config/fetch_script.sh create mode 100644 src/config.go create mode 100644 src/modules.go diff --git a/README.md b/README.md index abded8b..451a254 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Stormfetch -## A simple linux fetch program written in go and bash +## A simple linux fetch program written in go ### Project Information Stormfetch is a program that can read your system's information and display it in the terminal along with the ASCII art of the Linux distribution you are running. diff --git a/config/ascii/arch b/config/ascii/arch index 071e58b..0df9d6a 100644 --- a/config/ascii/arch +++ b/config/ascii/arch @@ -1,5 +1,5 @@ #/4;27;4;11 -${C1} -` +%1 -` .o+` `ooo/ `+oooo: @@ -8,9 +8,9 @@ ${C1} -` `/:-:++oooo+: `/++++/+++++++: `/++++++++++++++: - `/+++o${C2}oooooooo${C1}oooo/` -${C2} ${C1}./${C2}ooosssso++osssssso${C1}+` -${C2} .oossssso-````/ossssss+` + `/+++o%2oooooooo%1oooo/` +%2 %1./%2ooosssso++osssssso%1+` +%2 .oossssso-````/ossssss+` -osssssso. :ssssssso. :osssssss/ osssso+++. /ossssssss/ +ssssooo/- diff --git a/config/ascii/artix b/config/ascii/artix index 1ff55e1..a5cfb71 100644 --- a/config/ascii/artix +++ b/config/ascii/artix @@ -1,5 +1,5 @@ #/45;27;45;7 -${C1} 'o' +%1 'o' 'ooo' 'ooxoo' 'ooxxxoo' diff --git a/config/ascii/bedrock b/config/ascii/bedrock index 2a54957..84a2df5 100644 --- a/config/ascii/bedrock +++ b/config/ascii/bedrock @@ -1,11 +1,11 @@ #/7;8;8;7 -${C1} \\\\\\\\\\\\ +%1 \\\\\\\\\\\\ \\\ \\\ \\\ \\\ \\\ \\\\\\\\\\\\\\\\\ \\\ \\\ \\\ \\\ - \\\ ${C2}______ \\\ + \\\ %2______ \\\ \\\ /// \\\ /// \\\ /// diff --git a/config/ascii/cachyos b/config/ascii/cachyos index 2daca93..44ac134 100644 --- a/config/ascii/cachyos +++ b/config/ascii/cachyos @@ -1,23 +1,23 @@ #/43;45;45;15 - ${C3}.${C1}-------------------------: - .${C2}+=${C1}========================. - :${C2}++${C1}===${C2}++===${C1}===============- :${C2}++${C1}- - :${C2}*++${C1}====${C2}+++++==${C1}===========- .==: - -${C2}*+++${C1}=====${C2}+***++=${C1}=========: - =${C2}*++++=${C1}=======------------: - =${C2}*+++++=${C1}====- ${C3}...${C1} - .${C2}+*+++++${C1}=-===: .${C2}=+++=${C1}: - :${C2}++++${C1}=====-==: -***${C2}**${C1}+ - :${C2}++=${C1}=======-=. .=+**+${C3}.${C1} -.${C2}+${C1}==========-. ${C3}.${C1} - :${C2}+++++++${C1}====- ${C3}.${C1}--==-${C3}.${C1} - :${C2}++${C1}==========. ${C3}:${C2}+++++++${C1}${C3}: - ${C1}.-===========. =*****+*+ - ${C1}.-===========: .+*****+: - ${C1}-=======${C2}++++${C1}:::::::::::::::::::::::::-: ${C3}.${C1}---: - :======${C2}++++${C1}====${C2}+++******************=. - ${C1}:=====${C2}+++${C1}==========${C2}++++++++++++++*- - ${C1}.====${C2}++${C1}==============${C2}++++++++++*- - ${C1}.===${C2}+${C1}==================${C2}+++++++: - ${C1}.-=======================${C2}+++: + ${C3}.%1-------------------------: + .%2+=%1========================. + :%2++%1===%2++===%1===============- :%2++%1- + :%2*++%1====%2+++++==%1===========- .==: + -%2*+++%1=====%2+***++=%1=========: + =%2*++++=%1=======------------: + =%2*+++++=%1====- ${C3}...%1 + .%2+*+++++%1=-===: .%2=+++=%1: + :%2++++%1=====-==: -***%2**%1+ + :%2++=%1=======-=. .=+**+${C3}.%1 +.%2+%1==========-. ${C3}.%1 + :%2+++++++%1====- ${C3}.%1--==-${C3}.%1 + :%2++%1==========. ${C3}:%2+++++++%1${C3}: + %1.-===========. =*****+*+ + %1.-===========: .+*****+: + %1-=======%2++++%1:::::::::::::::::::::::::-: ${C3}.%1---: + :======%2++++%1====%2+++******************=. + %1:=====%2+++%1==========%2++++++++++++++*- + %1.====%2++%1==============%2++++++++++*- + %1.===%2+%1==================%2+++++++: + %1.-=======================%2+++: ${C3}.......................... diff --git a/config/ascii/debian b/config/ascii/debian index 7fe68d2..a580519 100644 --- a/config/ascii/debian +++ b/config/ascii/debian @@ -1,16 +1,16 @@ #/1;7;9;15 -${C2} _,met&&&&&gg. +%2 _,met&&&&&gg. ,g&&&&&&&&&&&&&&&P. ,g&&P" """Y&&.". ,&&P' `&&&. ',&&P ,ggs. `&&b: -`d&&' ,&P"' ${C1}.${C2} &&& - &&P d&' ${C1},${C2} &&P - &&: &&. ${C1}-${C2} ,d&&' +`d&&' ,&P"' %1.%2 &&& + &&P d&' %1,%2 &&P + &&: &&. %1-%2 ,d&&' &&; Y&b._ _,d&P' - Y&&. ${C1}`.${C2}`"Y&&&&P"' -${C2} `&&b ${C1}"-.__ -${C2} `Y&& + Y&&. %1`.%2`"Y&&&&P"' +%2 `&&b %1"-.__ +%2 `Y&& `Y&&. `&&b. `Y&&b. diff --git a/config/ascii/fedora b/config/ascii/fedora index d4ba0e0..f0d5776 100644 --- a/config/ascii/fedora +++ b/config/ascii/fedora @@ -1,20 +1,20 @@ #/12;7;4;15 -${C1} .',;::::;,'. +%1 .',;::::;,'. .';:cccccccccccc:;,. .;cccccccccccccccccccccc;. .:cccccccccccccccccccccccccc:. - .;ccccccccccccc;${C2}.:dddl:.${C1};ccccccc;. - .:ccccccccccccc;${C2}OWMKOOXMWd${C1};ccccccc:. -.:ccccccccccccc;${C2}KMMc${C1};cc;${C2}xMMc${C1};ccccccc:. -,cccccccccccccc;${C2}MMM.${C1};cc;${C2};WW:${C1};cccccccc, -:cccccccccccccc;${C2}MMM.${C1};cccccccccccccccc: -:ccccccc;${C2}oxOOOo${C1};${C2}MMM0OOk.${C1};cccccccccccc: -cccccc;${C2}0MMKxdd:${C1};${C2}MMMkddc.${C1};cccccccccccc; -ccccc;${C2}XM0'${C1};cccc;${C2}MMM.${C1};cccccccccccccccc' -ccccc;${C2}MMo${C1};ccccc;${C2}MMW.${C1};ccccccccccccccc; -ccccc;${C2}0MNc.${C1}ccc${C2}.xMMd${C1};ccccccccccccccc; -cccccc;${C2}dNMWXXXWM0:${C1};cccccccccccccc:, -cccccccc;${C2}.:odl:.${C1};cccccccccccccc:,. + .;ccccccccccccc;%2.:dddl:.%1;ccccccc;. + .:ccccccccccccc;%2OWMKOOXMWd%1;ccccccc:. +.:ccccccccccccc;%2KMMc%1;cc;%2xMMc%1;ccccccc:. +,cccccccccccccc;%2MMM.%1;cc;%2;WW:%1;cccccccc, +:cccccccccccccc;%2MMM.%1;cccccccccccccccc: +:ccccccc;%2oxOOOo%1;%2MMM0OOk.%1;cccccccccccc: +cccccc;%20MMKxdd:%1;%2MMMkddc.%1;cccccccccccc; +ccccc;%2XM0'%1;cccc;%2MMM.%1;cccccccccccccccc' +ccccc;%2MMo%1;ccccc;%2MMW.%1;ccccccccccccccc; +ccccc;%20MNc.%1ccc%2.xMMd%1;ccccccccccccccc; +cccccc;%2dNMWXXXWM0:%1;cccccccccccccc:, +cccccccc;%2.:odl:.%1;cccccccccccccc:,. :cccccccccccccccccccccccccccc:'. .:cccccccccccccccccccccc:;,.. '::cccccccccccccc::;,. diff --git a/config/ascii/gentoo b/config/ascii/gentoo index 013f8b4..6cc1806 100644 --- a/config/ascii/gentoo +++ b/config/ascii/gentoo @@ -1,19 +1,19 @@ #/5;7;13;69 -${C1} -/oyddmdhs+:. - -o${C2}dNMMMMMMMMNNmhy+${C1}-` - -y${C2}NMMMMMMMMMMMNNNmmdhy${C1}+- - `o${C2}mMMMMMMMMMMMMNmdmmmmddhhy${C1}/` - om${C2}MMMMMMMMMMMN${C1}hhyyyo${C2}hmdddhhhd${C1}o` -.y${C2}dMMMMMMMMMMd${C1}hs++so/s${C2}mdddhhhhdm${C1}+` - oy${C2}hdmNMMMMMMMN${C1}dyooy${C2}dmddddhhhhyhN${C1}d. - :o${C2}yhhdNNMMMMMMMNNNmmdddhhhhhyym${C1}Mh - .:${C2}+sydNMMMMMNNNmmmdddhhhhhhmM${C1}my - /m${C2}MMMMMMNNNmmmdddhhhhhmMNh${C1}s: - `o${C2}NMMMMMMMNNNmmmddddhhdmMNhs${C1}+` - `s${C2}NMMMMMMMMNNNmmmdddddmNMmhs${C1}/. - /N${C2}MMMMMMMMNNNNmmmdddmNMNdso${C1}:` -+M${C2}MMMMMMNNNNNmmmmdmNMNdso${C1}/- -yM${C2}MNNNNNNNmmmmmNNMmhs+/${C1}-` -/h${C2}MMNNNNNNNNMNdhs++/${C1}-` -`/${C2}ohdmmddhys+++/:${C1}.` +%1 -/oyddmdhs+:. + -o%2dNMMMMMMMMNNmhy+%1-` + -y%2NMMMMMMMMMMMNNNmmdhy%1+- + `o%2mMMMMMMMMMMMMNmdmmmmddhhy%1/` + om%2MMMMMMMMMMMN%1hhyyyo%2hmdddhhhd%1o` +.y%2dMMMMMMMMMMd%1hs++so/s%2mdddhhhhdm%1+` + oy%2hdmNMMMMMMMN%1dyooy%2dmddddhhhhyhN%1d. + :o%2yhhdNNMMMMMMMNNNmmdddhhhhhyym%1Mh + .:%2+sydNMMMMMNNNmmmdddhhhhhhmM%1my + /m%2MMMMMMNNNmmmdddhhhhhmMNh%1s: + `o%2NMMMMMMMNNNmmmddddhhdmMNhs%1+` + `s%2NMMMMMMMMNNNmmmdddddmNMmhs%1/. + /N%2MMMMMMMMNNNNmmmdddmNMNdso%1:` ++M%2MMMMMMNNNNNmmmmdmNMNdso%1/- +yM%2MNNNNNNNmmmmmNNMmhs+/%1-` +/h%2MMNNNNNNNNMNdhs++/%1-` +`/%2ohdmmddhys+++/:%1.` `-//////:--. diff --git a/config/ascii/linuxmint b/config/ascii/linuxmint index bd52afd..dce3d53 100644 --- a/config/ascii/linuxmint +++ b/config/ascii/linuxmint @@ -1,20 +1,20 @@ #/34;15;46;252 -${C2} ...-:::::-... -${C2} .-MMMMMMMMMMMMMMM-. - .-MMMM${C1}`..-:::::::-..`${C2}MMMM-. - .:MMMM${C1}.:MMMMMMMMMMMMMMM:.${C2}MMMM:. - -MMM${C1}-M---MMMMMMMMMMMMMMMMMMM.${C2}MMM- - `:MMM${C1}:MM` :MMMM:....::-...-MMMM:${C2}MMM:` - :MMM${C1}:MMM` :MM:` `` `` `:MMM:${C2}MMM: -.MMM${C1}.MMMM` :MM. -MM. .MM- `MMMM.${C2}MMM. -:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM-${C2}MMM: -:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM:${C2}MMM: -:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM-${C2}MMM: -.MMM${C1}.MMMM` :MM:--:MM:--:MM: `MMMM.${C2}MMM. - :MMM${C1}:MMM- `-MMMMMMMMMMMM-` -MMM-${C2}MMM: - :MMM${C1}:MMM:` `:MMM:${C2}MMM: - .MMM${C1}.MMMM:--------------:MMMM.${C2}MMM. - '-MMMM${C1}.-MMMMMMMMMMMMMMM-.${C2}MMMM-' - '.-MMMM${C1}``--:::::--``${C2}MMMM-.' -${C2} '-MMMMMMMMMMMMM-' -${C2} ``-:::::-`` +%2 ...-:::::-... +%2 .-MMMMMMMMMMMMMMM-. + .-MMMM%1`..-:::::::-..`%2MMMM-. + .:MMMM%1.:MMMMMMMMMMMMMMM:.%2MMMM:. + -MMM%1-M---MMMMMMMMMMMMMMMMMMM.%2MMM- + `:MMM%1:MM` :MMMM:....::-...-MMMM:%2MMM:` + :MMM%1:MMM` :MM:` `` `` `:MMM:%2MMM: +.MMM%1.MMMM` :MM. -MM. .MM- `MMMM.%2MMM. +:MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM: +:MMM%1:MMMM` :MM. -MM- .MM: `MMMM:%2MMM: +:MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM: +.MMM%1.MMMM` :MM:--:MM:--:MM: `MMMM.%2MMM. + :MMM%1:MMM- `-MMMMMMMMMMMM-` -MMM-%2MMM: + :MMM%1:MMM:` `:MMM:%2MMM: + .MMM%1.MMMM:--------------:MMMM.%2MMM. + '-MMMM%1.-MMMMMMMMMMMMMMM-.%2MMMM-' + '.-MMMM%1``--:::::--``%2MMMM-.' +%2 '-MMMMMMMMMMMMM-' +%2 ``-:::::-`` diff --git a/config/ascii/opensuse b/config/ascii/opensuse index 3519a40..e3d283d 100644 --- a/config/ascii/opensuse +++ b/config/ascii/opensuse @@ -1,18 +1,18 @@ #/2;7;10;15 -${C2} .;ldkO0000Okdl;. +%2 .;ldkO0000Okdl;. .;d00xl:^''''''^:ok00d;. .d00l' 'o00d. - .d0Kd'${C1} Okxol:;,. ${C2}:O0d. - .OK${C1}KKK0kOKKKKKKKKKKOxo:, ${C2}lKO. - ,0K${C1}KKKKKKKKKKKKKKK0P^${C2},,,${C1}^dx:${C2} ;00, -.OK${C1}KKKKKKKKKKKKKKKk'${C2}.oOPPb.${C1}'0k.${C2} cKO. -:KK${C1}KKKKKKKKKKKKKKK: ${C2}kKx..dd ${C1}lKd${C2} 'OK: -dKK${C1}KKKKKKKKKOx0KKKd ${C2}^0KKKO' ${C1}kKKc${C2} dKd -dKK${C1}KKKKKKKKKK;.;oOKx,..${C2}^${C1}..;kKKK0.${C2} dKd -:KK${C1}KKKKKKKKKK0o;...^cdxxOK0O/^^' ${C2}.0K: - kKK${C1}KKKKKKKKKKKKK0x;,,......,;od ${C2}lKk - '0K${C1}KKKKKKKKKKKKKKKKKKKK00KKOo^ ${C2}c00' - 'kK${C1}KKOxddxkOO00000Okxoc;'' ${C2}.dKk' + .d0Kd'%1 Okxol:;,. %2:O0d. + .OK%1KKK0kOKKKKKKKKKKOxo:, %2lKO. + ,0K%1KKKKKKKKKKKKKKK0P^%2,,,%1^dx:%2 ;00, +.OK%1KKKKKKKKKKKKKKKk'%2.oOPPb.%1'0k.%2 cKO. +:KK%1KKKKKKKKKKKKKKK: %2kKx..dd %1lKd%2 'OK: +dKK%1KKKKKKKKKOx0KKKd %2^0KKKO' %1kKKc%2 dKd +dKK%1KKKKKKKKKK;.;oOKx,..%2^%1..;kKKK0.%2 dKd +:KK%1KKKKKKKKKK0o;...^cdxxOK0O/^^' %2.0K: + kKK%1KKKKKKKKKKKKK0x;,,......,;od %2lKk + '0K%1KKKKKKKKKKKKKKKKKKKK00KKOo^ %2c00' + 'kK%1KKOxddxkOO00000Okxoc;'' %2.dKk' l0Ko. .c00l' 'l0Kk:. .;xK0l' 'lkK0xl:;,,,,;:ldO0kl' diff --git a/config/ascii/opensuse-tumbleweed b/config/ascii/opensuse-tumbleweed index 9e315cd..6b03b5e 100644 --- a/config/ascii/opensuse-tumbleweed +++ b/config/ascii/opensuse-tumbleweed @@ -1,5 +1,5 @@ #/2;7;10;15 -${C1} ...... +%1 ...... .,cdxxxoc,. .:kKMMMNWMMMNk:. cKMMN0OOOKWMMXo. ; ;0MWk:. .:OMMk. ;WMK;. .lKMMNM, :NMK, .OMW; diff --git a/config/ascii/pop b/config/ascii/pop index 8bd7070..e53522e 100644 --- a/config/ascii/pop +++ b/config/ascii/pop @@ -1,21 +1,21 @@ #/33;7;11;15 -${C1} ///////////// +%1 ///////////// ///////////////////// - ///////${C2}*767${C1}//////////////// - //////${C2}7676767676*${C1}////////////// - /////${C2}76767${C1}//${C2}7676767${C1}////////////// - /////${C2}767676${C1}///${C2}*76767${C1}/////////////// - ///////${C2}767676${C1}///${C2}76767${C1}.///${C2}7676*${C1}/////// -/////////${C2}767676${C1}//${C2}76767${C1}///${C2}767676${C1}//////// -//////////${C2}76767676767${C1}////${C2}76767${C1}///////// -///////////${C2}76767676${C1}//////${C2}7676${C1}////////// -////////////,${C2}7676${C1},///////${C2}767${C1}/////////// -/////////////*${C2}7676${C1}///////${C2}76${C1}//////////// -///////////////${C2}7676${C1}//////////////////// - ///////////////${C2}7676${C1}///${C2}767${C1}//////////// - //////////////////////${C2}'${C1}//////////// - //////${C2}.7676767676767676767,${C1}////// - /////${C2}767676767676767676767${C1}///// + ///////%2*767%1//////////////// + //////%27676767676*%1////////////// + /////%276767%1//%27676767%1////////////// + /////%2767676%1///%2*76767%1/////////////// + ///////%2767676%1///%276767%1.///%27676*%1/////// +/////////%2767676%1//%276767%1///%2767676%1//////// +//////////%276767676767%1////%276767%1///////// +///////////%276767676%1//////%27676%1////////// +////////////,%27676%1,///////%2767%1/////////// +/////////////*%27676%1///////%276%1//////////// +///////////////%27676%1//////////////////// + ///////////////%27676%1///%2767%1//////////// + //////////////////////%2'%1//////////// + //////%2.7676767676767676767,%1////// + /////%2767676767676767676767%1///// /////////////////////////// ///////////////////// ///////////// diff --git a/config/ascii/tide b/config/ascii/tide index 097d39f..f6b4065 100644 --- a/config/ascii/tide +++ b/config/ascii/tide @@ -1,15 +1,15 @@ #/27;39;5;12 - ${C1}@@@@ ${C1}@@@${C2}* - ${C1}@@${C2}~~~~${C1}@@ ${C1}@@${C2}~~~ - ${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ - ${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ - ${C2}*${C1}@@${C2}~~ ${C1}@@@@ ${C2}~${C1}@@@@${C2}~~~ ${C1}@@@${C2}* - ${C2}~~ ${C1}@@${C2}~~~~${C1}@@ ${C2}~~~~ ${C1}@@${C2}~~~ - ${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ - ${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ - ${C2}*${C1}@@${C2}~~ ${C1}@@@@ ${C2}~${C1}@@@@${C2}~~~ ${C1}@@@${C2}* - ${C2}~~ ${C1}@@${C2}~~~~${C1}@@ ${C2}~~~~ ${C1}@@${C2}~~~ - ${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ - ${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ - ${C2}*${C1}@@${C2}~~ ${C2}~${C1}@@@@${C2}~~~ - ${C2}~~ ${C2}~~~~ + %1@@@@ %1@@@%2* + %1@@%2~~~~%1@@ %1@@%2~~~ + %1@@%2~~ %2~~%1@ %1@@%2~~ + %1@@%2~~ %2~%1@ %1@@@%2~~ + %2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2* + %2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~ + %1@@%2~~ %2~~%1@ %1@@%2~~ + %1@@%2~~ %2~%1@ %1@@@%2~~ + %2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2* + %2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~ + %1@@%2~~ %2~~%1@ %1@@%2~~ + %1@@%2~~ %2~%1@ %1@@@%2~~ + %2*%1@@%2~~ %2~%1@@@@%2~~~ + %2~~ %2~~~~ diff --git a/config/ascii/ubuntu b/config/ascii/ubuntu index 5c7c33c..d70eafb 100644 --- a/config/ascii/ubuntu +++ b/config/ascii/ubuntu @@ -1,21 +1,21 @@ #/1;7;3;15 -${C1} .-/+oossssoo+\-. +%1 .-/+oossssoo+\-. ':+ssssssssssssssssss+:' -+ssssssssssssssssssyyssss+- - .ossssssssssssssssss${C2}dMMMNy${C1}sssso. - /sssssssssss${C2}hdmmNNmmyNMMMMh${C1}ssssss\ - +sssssssss${C2}hm${C1}yd${C2}MMMMMMMNddddy${C1}ssssssss+ - /ssssssss${C2}hNMMM${C1}yh${C2}hyyyyhmNMMMNh${C1}ssssssss\ -.ssssssss${C2}dMMMNh${C1}ssssssssss${C2}hNMMMd${C1}ssssssss. -+ssss${C2}hhhyNMMNy${C1}ssssssssssss${C2}yNMMMy${C1}sssssss+ -oss${C2}yNMMMNyMMh${C1}ssssssssssssss${C2}hmmmh${C1}ssssssso -oss${C2}yNMMMNyMMh${C1}sssssssssssssshmmmh${C1}ssssssso -+ssss${C2}hhhyNMMNy${C1}ssssssssssss${C2}yNMMMy${C1}sssssss+ -.ssssssss${C2}dMMMNh${C1}ssssssssss${C2}hNMMMd${C1}ssssssss. - \ssssssss${C2}hNMMM${C1}yh${C2}hyyyyhdNMMMNh${C1}ssssssss/ - +sssssssss${C2}dm${C1}yd${C2}MMMMMMMMddddy${C1}ssssssss+ - \sssssssssss${C2}hdmNNNNmyNMMMMh${C1}ssssss/ - .ossssssssssssssssss${C2}dMMMNy${C1}sssso. - -+sssssssssssssssss${C2}yyy${C1}ssss+- + .ossssssssssssssssss%2dMMMNy%1sssso. + /sssssssssss%2hdmmNNmmyNMMMMh%1ssssss\ + +sssssssss%2hm%1yd%2MMMMMMMNddddy%1ssssssss+ + /ssssssss%2hNMMM%1yh%2hyyyyhmNMMMNh%1ssssssss\ +.ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss. ++ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+ +oss%2yNMMMNyMMh%1ssssssssssssss%2hmmmh%1ssssssso +oss%2yNMMMNyMMh%1sssssssssssssshmmmh%1ssssssso ++ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+ +.ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss. + \ssssssss%2hNMMM%1yh%2hyyyyhdNMMMNh%1ssssssss/ + +sssssssss%2dm%1yd%2MMMMMMMMddddy%1ssssssss+ + \sssssssssss%2hdmNNNNmyNMMMMh%1ssssss/ + .ossssssssssssssssss%2dMMMNy%1sssso. + -+sssssssssssssssss%2yyy%1ssss+- ':+ssssssssssssssssss+:' .-\+oossssoo+/-. diff --git a/config/ascii/void b/config/ascii/void index 114790d..782b1b2 100644 --- a/config/ascii/void +++ b/config/ascii/void @@ -1,19 +1,19 @@ #/2;36;72;15 - ${C2}:::::----::::: - ${C2}::----------------::= - ${C2}:-------------------:: -${C1} : ${C2}:::. ..:-------: -${C1} =*=. ${C2}.:-----: -${C1} =****- ${C2}:-----: -${C1}=*****- ${C2}:::::: :-----: -${C1}=****+ ${C2}:--------: :----: -${C1}+****- ${C2}.----------. :----: -${C1}+****- ${C2}.----------. :----- -${C1}=****+ ${C2}:--------: :----: -${C1}=*****- ${C2}:::::: :-----: -${C1} =*****- ${C2}:----: -${C1} =*****+: ${C2}:-: -${C1} =*******=:: ::=-. ${C2}. -${C1} ==*******************- -${C1} ===****************=- -${C1} ===+=****++=== \ No newline at end of file + %2:::::----::::: + %2::----------------::= + %2:-------------------:: +%1 : %2:::. ..:-------: +%1 =*=. %2.:-----: +%1 =****- %2:-----: +%1=*****- %2:::::: :-----: +%1=****+ %2:--------: :----: +%1+****- %2.----------. :----: +%1+****- %2.----------. :----- +%1=****+ %2:--------: :----: +%1=*****- %2:::::: :-----: +%1 =*****- %2:----: +%1 =*****+: %2:-: +%1 =*******=:: ::=-. %2. +%1 ==*******************- +%1 ===****************=- +%1 ===+=****++=== \ No newline at end of file diff --git a/config/config.yaml b/config/config.yaml index 03e705b..fbfe842 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,7 +1,16 @@ distro_ascii: auto -fetch_script: auto -show_fs_type: true -hidden_partitions: [] -# Hiding squashfs prevents snaps from showing up -hidden_filesystems: ["squashfs"] -hidden_gpus: [] +modules: + - name: distribution + - name: hostname + - name: kernel + - name: packages + - name: shell + - name: init + - name: motherboard + - name: cpu + - name: gpus + - name: memory + - name: partitions + - name: local_ip + - name: de_wm + - name: monitors diff --git a/config/fetch_script.sh b/config/fetch_script.sh deleted file mode 100644 index 6881ec3..0000000 --- a/config/fetch_script.sh +++ /dev/null @@ -1,46 +0,0 @@ -echo -e "${C3}Distribution: ${C4}${DISTRO_LONG_NAME} ($(uname -m))" -echo -e "${C3}Hostname: ${C4}$(cat /etc/hostname)" -echo -e "${C3}Kernel: ${C4}$(uname -s) $(uname -r)" -echo -e "${C3}Packages: ${C4}${PACKAGES}" -echo -e "${C3}Shell: ${C4}${USER_SHELL}" -echo -e "${C3}Init: ${C4}${INIT_SYSTEM}" -echo -e "${C3}Libc: ${C4}${LIBC}" -[ -n "$MOTHERBOARD" ] && echo -e "${C3}Motherboard: ${C4}${MOTHERBOARD}" -[ -n "$CPU_MODEL" ] && echo -e "${C3}CPU: ${C4}${CPU_MODEL} (${CPU_THREADS} threads)" -for i in $(seq "${CONNECTED_GPUS}"); do - gpu="GPU$i" - echo -e "${C3}GPU: ${C4}${!gpu}" - done -[ -n "$MEM_TOTAL" ] && [ -n "$MEM_USED" ] && echo -e "${C3}Memory: ${C4}${MEM_USED} MiB / ${MEM_TOTAL} MiB" -for i in $(seq "${MOUNTED_PARTITIONS}"); do - mountpoint="PARTITION${i}_MOUNTPOINT" - label="PARTITION${i}_LABEL" - type="PARTITION${i}_TYPE" - total="PARTITION${i}_TOTAL_SIZE" - used="PARTITION${i}_USED_SIZE" - if [ -z "${!type}" ]; then - if [ -z "${!label}" ]; then - echo -e "${C3}Partition ${!mountpoint}: ${C4}${!used}/${!total}" - else - echo -e "${C3}Partition ${!label}: ${C4}${!used}/${!total}" - fi - else - if [ -z "${!label}" ]; then - echo -e "${C3}Partition ${!mountpoint} (${!type}): ${C4}${!used}/${!total}" - else - echo -e "${C3}Partition ${!label} (${!type}): ${C4}${!used}/${!total}" - fi - fi -done -[ -n "$LOCAL_IPV4" ] && echo -e "${C3}Local IPv4 Address: ${C4}${LOCAL_IPV4}" -if [ -n "$DISPLAY_PROTOCOL" ]; then - echo -e "${C3}Display Protocol: ${C4}${DISPLAY_PROTOCOL}" - for i in $(seq "${CONNECTED_MONITORS}"); do - monitor="MONITOR$i" - echo -e "${C3}Screen $i: ${C4}${!monitor}" - done -fi -[ -n "$DE_WM" ] && echo -e "${C3}DE/WM: ${C4}${DE_WM}" - -# Exiting with error code 0 in case the condition above returns 1 -exit 0 \ No newline at end of file diff --git a/src/color.go b/src/color.go index a846141..62b0804 100644 --- a/src/color.go +++ b/src/color.go @@ -2,21 +2,22 @@ package main import ( "fmt" - "strconv" "strings" ) -func setupColorMap(asciiArt string) map[string]string { - colorMap := make(map[string]string) +func setupColorMap(asciiArtHeader string) map[int]string { + colorMap := make(map[int]string) + colorMap[0] = "\033[0m" - // Read colors from ascii art - if strings.HasPrefix(asciiArt, "#/") { - firstLine := strings.Split(asciiArt, "\n")[0] - ansiColors := strings.Split(strings.TrimPrefix(firstLine, "#/"), ";") + // Return if header is empty + if asciiArtHeader == "" { + return colorMap + } - for i, ansiColor := range ansiColors { - colorMap["C"+strconv.Itoa(i+1)] = fmt.Sprintf("\033[38;5;%sm", ansiColor) - } + // Read colors from ascii art header + ansiColors := strings.Split(strings.TrimPrefix(asciiArtHeader, "#/"), ";") + for i, ansiColor := range ansiColors { + colorMap[i+1] = fmt.Sprintf("\033[38;5;%sm", ansiColor) } return colorMap diff --git a/src/config.go b/src/config.go new file mode 100644 index 0000000..98069c2 --- /dev/null +++ b/src/config.go @@ -0,0 +1,46 @@ +package main + +import ( + "log" + "os" + "path" + + "gopkg.in/yaml.v3" +) + +type StormfetchConfig struct { + Ascii string `yaml:"distro_ascii"` + DistroName string `yaml:"distro_name"` + Modules []stormfetchModuleConfig `yaml:"modules"` + AnsiiColors []int `yaml:"ansii_colors"` + ForceConfigAnsii bool `yaml:"force_config_ansii"` +} + +var config = StormfetchConfig{ + Ascii: "auto", + Modules: make([]stormfetchModuleConfig, 0), +} + +func readConfig() { + // Get home directory + userConfigDir, _ := os.UserConfigDir() + + // Find valid config directory + if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/config.yaml")); err == nil { + configPath = path.Join(userConfigDir, "stormfetch/config.yaml") + } else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/config.yaml")); err == nil { + configPath = path.Join(systemConfigDir, "stormfetch/config.yaml") + } else { + log.Fatalf("Config file not found: %s", err.Error()) + } + + // Parse config + bytes, err := os.ReadFile(configPath) + if err != nil { + log.Fatal(err) + } + err = yaml.Unmarshal(bytes, &config) + if err != nil { + log.Fatal(err) + } +} diff --git a/src/hardware.go b/src/hardware.go index 767cb21..bd88ffe 100644 --- a/src/hardware.go +++ b/src/hardware.go @@ -1,15 +1,21 @@ package main import ( - "fmt" - "github.com/go-gl/glfw/v3.3/glfw" - "github.com/jackmordaunt/ghw" "os" "os/exec" "slices" "strings" + + "github.com/go-gl/glfw/v3.3/glfw" + "github.com/jackmordaunt/ghw" ) +type Monitor struct { + Width int + Height int + RefreshRate int +} + func GetCPUModel() string { cpu, err := ghw.CPU() if err != nil { @@ -29,7 +35,7 @@ func GetCPUThreads() int { return int(cpu.TotalThreads) } -func GetGPUModels() (ret []string) { +func GetGPUModels(hiddenGPUS []int) (ret []string) { cmd := exec.Command("sh", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:'") bytes, err := cmd.Output() if err != nil { @@ -37,7 +43,7 @@ func GetGPUModels() (ret []string) { } for i, gpu := range strings.Split(string(bytes), "\n") { - if slices.Contains(config.HiddenGPUS, i+1) { + if slices.Contains(hiddenGPUS, i+1) { continue } if gpu == "" { @@ -58,16 +64,22 @@ func GetMotherboardModel() string { return strings.TrimSpace(string(bytes)) } -func GetMonitorResolution() []string { - var monitors []string +func GetMonitors() []Monitor { + var monitors []Monitor if GetDisplayProtocol() != "" { err := glfw.Init() if err != nil { panic(err) } + for _, monitor := range glfw.GetMonitors() { mode := monitor.GetVideoMode() - monitors = append(monitors, fmt.Sprintf("%dx%d %dHz", mode.Width, mode.Height, mode.RefreshRate)) + + monitors = append(monitors, Monitor{ + Width: mode.Width, + Height: mode.Height, + RefreshRate: mode.RefreshRate, + }) } defer glfw.Terminate() } diff --git a/src/main.go b/src/main.go index 7fe7623..ff74592 100644 --- a/src/main.go +++ b/src/main.go @@ -3,258 +3,131 @@ package main import ( "flag" "fmt" - "log" - "os" - "os/exec" - "path" - "regexp" "strconv" "strings" "time" - - "gopkg.in/yaml.v3" ) +// Build-time variables var systemConfigDir = "/etc/" +// Flag variables +var ShowModuleTimeTaken = false + var configPath = "" -var fetchScriptPath = "" - -var TimeTaken = false - -var config = StormfetchConfig{ - Ascii: "auto", - FetchScript: "auto", - ShowFSType: false, - HiddenPartitions: make([]string, 0), - HiddenGPUS: make([]int, 0), -} - -type StormfetchConfig struct { - Ascii string `yaml:"distro_ascii"` - DistroName string `yaml:"distro_name"` - FetchScript string `yaml:"fetch_script"` - AnsiiColors []int `yaml:"ansii_colors"` - ForceConfigAnsii bool `yaml:"force_config_ansii"` - ShowFSType bool `yaml:"show_fs_type"` - HiddenPartitions []string `yaml:"hidden_partitions"` - HiddenFilesystems []string `yaml:"hidden_filesystems"` - HiddenGPUS []int `yaml:"hidden_gpus"` -} func main() { readConfig() - readFlags() - runStormfetch() + parseFlags() + initializeModuleMap() + run() } -func readConfig() { - // Get home directory - userConfigDir, _ := os.UserConfigDir() - // Find valid config directory - if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/config.yaml")); err == nil { - configPath = path.Join(userConfigDir, "stormfetch/config.yaml") - } else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/config.yaml")); err == nil { - configPath = path.Join(systemConfigDir, "stormfetch/config.yaml") - } else { - log.Fatalf("Config file not found: %s", err.Error()) - } - // Parse config - bytes, err := os.ReadFile(configPath) - if err != nil { - log.Fatal(err) - } - err = yaml.Unmarshal(bytes, &config) - if err != nil { - log.Fatal(err) - } - if config.FetchScript == "" { - log.Fatalf("Fetch script path is empty") - } else if config.FetchScript != "auto" { - stat, err := os.Stat(config.FetchScript) - if err != nil { - log.Fatalf("Fetch script file not found: %s", err.Error()) - } else if stat.IsDir() { - log.Fatalf("Fetch script path points to a directory") - } - } - if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/fetch_script.sh")); err == nil { - fetchScriptPath = path.Join(userConfigDir, "stormfetch/fetch_script.sh") - } else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/fetch_script.sh")); err == nil { - fetchScriptPath = path.Join(systemConfigDir, "stormfetch/fetch_script.sh") - } else { - log.Fatalf("Fetch script file not found: %s", err.Error()) - } -} - -func readFlags() { +func parseFlags() { flag.StringVar(&config.Ascii, "ascii", config.Ascii, "Set distro ascii") flag.StringVar(&config.DistroName, "distro-name", config.DistroName, "Set distro name") - flag.BoolVar(&TimeTaken, "time-taken", false, "Show time taken for fetched information") + flag.BoolVar(&ShowModuleTimeTaken, "time-taken", false, "Show time taken to execute each module") flag.Parse() } -func SetupFetchEnv(showTimeTaken bool) []string { - var env = make(map[string]string) - setVariable := func(key string, setter func() string) { - start := time.Now().UnixMilli() - env[key] = setter() - end := time.Now().UnixMilli() - if showTimeTaken { - fmt.Printf("Setting '%s' took %d milliseconds\n", key, end-start) - } - } - setVariable("PACKAGES", func() string { return GetInstalledPackages() }) - setVariable("DISTRO_LONG_NAME", func() string { return GetDistroInfo().LongName }) - setVariable("DISTRO_SHORT_NAME", func() string { return GetDistroInfo().ShortName }) - setVariable("CPU_MODEL", func() string { return GetCPUModel() }) - setVariable("MOTHERBOARD", func() string { return GetMotherboardModel() }) - setVariable("CPU_THREADS", func() string { return strconv.Itoa(GetCPUThreads()) }) - start := time.Now().UnixMilli() - memory := GetMemoryInfo() - end := time.Now().UnixMilli() - if showTimeTaken { - fmt.Printf("Setting '%s' took %d milliseconds\n", "MEM_*", end-start) - } - if memory != nil { - env["MEM_TOTAL"] = strconv.Itoa(memory.MemTotal) - env["MEM_USED"] = strconv.Itoa(memory.MemTotal - memory.MemAvailable) - env["MEM_FREE"] = strconv.Itoa(memory.MemAvailable) - } - start = time.Now().UnixMilli() - partitions := GetMountedPartitions(config.HiddenPartitions, config.HiddenFilesystems) - end = time.Now().UnixMilli() - if showTimeTaken { - fmt.Printf("Setting '%s' took %d milliseconds\n", "PARTITION_*", end-start) - } - if len(partitions) != 0 { - env["MOUNTED_PARTITIONS"] = strconv.Itoa(len(partitions)) - for i, part := range partitions { - env["PARTITION"+strconv.Itoa(i+1)+"_DEVICE"] = part.Device - env["PARTITION"+strconv.Itoa(i+1)+"_MOUNTPOINT"] = part.MountPoint - if part.Label != "" { - env["PARTITION"+strconv.Itoa(i+1)+"_LABEL"] = part.Label - } - if part.FileystemType != "" && config.ShowFSType { - env["PARTITION"+strconv.Itoa(i+1)+"_TYPE"] = part.FileystemType - } - env["PARTITION"+strconv.Itoa(i+1)+"_TOTAL_SIZE"] = FormatBytes(part.TotalSize) - env["PARTITION"+strconv.Itoa(i+1)+"_USED_SIZE"] = FormatBytes(part.UsedSize) - env["PARTITION"+strconv.Itoa(i+1)+"_FREE_SIZE"] = FormatBytes(part.FreeSize) - } - } - setVariable("DE_WM", func() string { return GetDEWM() }) - setVariable("USER_SHELL", func() string { return GetShell() }) - setVariable("DISPLAY_PROTOCOL", func() string { return GetDisplayProtocol() }) - setVariable("LIBC", func() string { return GetLibc() }) - setVariable("INIT_SYSTEM", func() string { return GetInitSystem() }) - setVariable("LOCAL_IPV4", func() string { return GetLocalIP() }) - start = time.Now().UnixMilli() - monitors := GetMonitorResolution() - end = time.Now().UnixMilli() - if showTimeTaken { - fmt.Printf("Setting '%s' took %d milliseconds\n", "MONITOR_*", end-start) - } - if len(monitors) != 0 { - env["CONNECTED_MONITORS"] = strconv.Itoa(len(monitors)) - for i, monitor := range monitors { - env["MONITOR"+strconv.Itoa(i+1)] = monitor - } - } - start = time.Now().UnixMilli() - gpus := GetGPUModels() - end = time.Now().UnixMilli() - if showTimeTaken { - fmt.Printf("Setting '%s' took %d milliseconds\n", "GPU_*", end-start) - } - if len(gpus) != 0 { - env["CONNECTED_GPUS"] = strconv.Itoa(len(gpus)) - for i, gpu := range gpus { - if gpu == "" { - continue - } - env["GPU"+strconv.Itoa(i+1)] = gpu - } - } - - var ret = make([]string, len(env)) - i := 0 - for key, value := range env { - ret[i] = fmt.Sprintf("%s=%s", key, value) - i++ - } - return ret -} - -func runStormfetch() { - // Fetch ascii art +func run() { + // Fetch ascii art and remove header asciiArt := GetDistroAsciiArt() - - // Setup color map - colorMap := setupColorMap(asciiArt) - if len(colorMap) > 0 { - asciiArt = os.Expand(asciiArt, func(s string) string { - return colorMap[s] - }) - + asciiArtHeader := "" + if strings.HasPrefix(asciiArt, "#/") { + asciiArtHeader = strings.SplitN(asciiArt, "\n", 2)[0] asciiArt = strings.SplitN(asciiArt, "\n", 2)[1] } + asciiArtNoColor := asciiArt - asciiSplit := strings.Split(asciiArt, "\n") - asciiNoColor := StripAnsii(asciiArt) - - // Execute fetch script - cmd := exec.Command("/bin/bash", fetchScriptPath) - cmd.Dir = path.Dir(fetchScriptPath) - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, SetupFetchEnv(TimeTaken)...) - cmd.Env = append(cmd.Env, "C0=\033[0m") + // Setup color map and replace colors in ascii art + colorMap := setupColorMap(asciiArtHeader) for key, value := range colorMap { - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value)) + asciiArt = strings.ReplaceAll(asciiArt, "%"+strconv.Itoa(key), value) + asciiArtNoColor = strings.ReplaceAll(asciiArtNoColor, "%"+strconv.Itoa(key), "") } - out, err := cmd.Output() - if err != nil { - log.Fatalf("Error: Could not run fetch script: %s", err) + + // Execute modules in order + modulesText := make([]string, 0) + for _, moduleConfig := range config.Modules { + module, ok := Modules[moduleConfig.Name] + if !ok { + continue + } + + // Set module config options + if moduleConfig.Format != "" { + module.Format = moduleConfig.Format + } + if moduleConfig.Data != nil { + module.Data = moduleConfig.Data + } + + // Execute module + start := time.Now().UnixMilli() + text := module.Execute(module) + end := time.Now().UnixMilli() + + // Show time taken + if ShowModuleTimeTaken { + fmt.Printf("Module '%s' took %d milliseconds\n", module.Name, end-start) + } + + // Replace colors in returned string + textNoColor := text + for key, value := range colorMap { + text = strings.ReplaceAll(text, "%"+strconv.Itoa(key), value) + textNoColor = strings.ReplaceAll(textNoColor, "%"+strconv.Itoa(key), value) + } + + // Continue if text length is 0 + if len(textNoColor) == 0 { + continue + } + + // Add text to slice + for _, line := range strings.Split(strings.TrimSpace(text), "\n") { + modulesText = append(modulesText, line) + } } - // Print Distro Information + + // Get longest line in ascii art maxWidth := 0 - for _, line := range strings.Split(asciiNoColor, "\n") { + for _, line := range strings.Split(asciiArtNoColor, "\n") { if len(line) > maxWidth { maxWidth = len(line) } } - final := "" - y := len(asciiSplit) - if len(asciiSplit) < len(strings.Split(string(out), "\n")) { - y = len(strings.Split(string(out), "\n")) + + // Split ascii art into each lien + asciiArtSplit := strings.Split(asciiArt, "\n") + asciiArtNoColorSplit := strings.Split(asciiArtNoColor, "\n") + + // Get amount of lines to print + lineCount := max(len(asciiArtSplit), len(modulesText)) + + // Combine ascii art and module text + final := strings.Builder{} + for i := range lineCount { + // Write ascii art + currentLineLength := 0 + if i < len(asciiArtSplit) { + final.WriteString(asciiArtSplit[i]) + currentLineLength += len(asciiArtNoColorSplit[i]) + } + + // Write blank space between ascii art and module text + for i := currentLineLength; i < maxWidth+3; i++ { + final.WriteString(" ") + } + + // Write module text + if i < len(modulesText) { + final.WriteString(modulesText[i]) + } + + final.WriteString("\n") } - for lineIndex := 0; lineIndex < y; lineIndex++ { - line := "" - for i := 0; i < maxWidth+5; i++ { - line = line + " " - } - lastAsciiColor := "" - if lineIndex < len(asciiSplit) { - line = asciiSplit[lineIndex] - lineVisibleLength := len(strings.Split(asciiNoColor, "\n")[lineIndex]) - if lineIndex != 0 { - r := regexp.MustCompile("\033[38;5;[0-9]+m") - matches := r.FindAllString(asciiSplit[lineIndex-1], -1) - if len(matches) != 0 { - lastAsciiColor = r.FindAllString(asciiSplit[lineIndex-1], -1)[len(matches)-1] - } - } - for i := lineVisibleLength; i < maxWidth+5; i++ { - line = line + " " - } - asciiSplit[lineIndex] = lastAsciiColor + line - } - str := string(out) - if lineIndex < len(strings.Split(str, "\n")) { - line = line + colorMap["C0"] + strings.Split(str, "\n")[lineIndex] - } - final += lastAsciiColor + line + "\n" - } - final = strings.TrimRight(final, "\n\t ") - fmt.Println(final + "\033[0m") + + fmt.Println(strings.TrimRight(final.String(), "\n") + "\033[0m") } diff --git a/src/modules.go b/src/modules.go new file mode 100644 index 0000000..6fc7adf --- /dev/null +++ b/src/modules.go @@ -0,0 +1,345 @@ +package main + +import ( + "os" + "reflect" + "strconv" + "strings" +) + +// Used to declare modules in config files +type stormfetchModuleConfig struct { + Name string `yaml:"name"` + Format string `yaml:"format"` + Data map[string]any `yaml:"data"` +} + +type StormfetchModule struct { + Execute func(StormfetchModule) string + stormfetchModuleConfig +} + +var Modules map[string]StormfetchModule = make(map[string]StormfetchModule) + +func (sm StormfetchModule) GetData(key string, defaultValue any) (any, bool) { + if sm.Data == nil { + return defaultValue, false + } + + data, ok := sm.Data[key] + if !ok { + return defaultValue, false + } + + if reflect.ValueOf(data).Kind() != reflect.ValueOf(defaultValue).Kind() { + return defaultValue, false + } + + return data, true +} + +func RegisterModule(module StormfetchModule) bool { + if _, ok := Modules[module.Name]; ok { + return false + } + + Modules[module.Name] = module + return true +} + +func initializeModuleMap() { + // Distribution Module + distributionModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "distribution", Format: "%3Distribution: %4$DISTRO_SHORT ($ARCH)"}, Execute: func(sm StormfetchModule) string { + distroInfo := GetDistroInfo() + return os.Expand(sm.Format, func(s string) string { + switch s { + case "DISTRO_ID": + return distroInfo.ID + case "DISTRO_SHORT": + return distroInfo.ShortName + case "DISTRO_LONG": + return distroInfo.LongName + case "ARCH": + return GetArch() + default: + return "" + } + }) + }} + RegisterModule(distributionModule) + + // Hostname module + hostnameModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "hostname", Format: "%3Hostname: %4$HOSTNAME"}, Execute: func(sm StormfetchModule) string { + hostname, _ := os.Hostname() + return os.Expand(sm.Format, func(s string) string { + switch s { + case "HOSTNAME": + return hostname + default: + return "" + } + }) + }} + RegisterModule(hostnameModule) + + // Kernel module + kernelModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "kernel", Format: "%3Kernel: %4$KERNEL_NAME $KERNEL_RELEASE"}, Execute: func(sm StormfetchModule) string { + kernelName, kernelRelease := GetKernel() + return os.Expand(sm.Format, func(s string) string { + switch s { + case "KERNEL_NAME": + return kernelName + case "KERNEL_RELEASE": + return kernelRelease + default: + return "" + } + }) + }} + RegisterModule(kernelModule) + + // Packages module + packagesModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "packages", Format: "%3Packages: %4$PACKAGES"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "PACKAGES": + return GetInstalledPackages() + default: + return "" + } + }) + }} + RegisterModule(packagesModule) + + // Shell module + shellModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "shell", Format: "%3Shell: %4$SHELL"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "SHELL": + return GetShell() + default: + return "" + } + }) + }} + RegisterModule(shellModule) + + // Init system module + initSystemModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "init_system", Format: "%3Init: %4$INIT"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "INIT": + return GetInitSystem() + default: + return "" + } + }) + }} + RegisterModule(initSystemModule) + + // Libc module + libcModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "libc", Format: "%3Libc: %4$LIBC"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "LIBC": + return GetLibc() + default: + return "" + } + }) + }} + RegisterModule(libcModule) + + // Motherboard module + MotherboardModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "motherboard", Format: "%3Motherboard: %4$MOTHERBOARD"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "MOTHERBOARD": + return GetMotherboardModel() + default: + return "" + } + }) + }} + RegisterModule(MotherboardModule) + + // Motherboard module + cpuModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "cpu", Format: "%3CPU: %4$CPU_MODEL ($CPU_THREADS threads)"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "CPU_MODEL": + return GetCPUModel() + case "CPU_THREADS": + return strconv.Itoa(GetCPUThreads()) + default: + return "" + } + }) + }} + RegisterModule(cpuModule) + + // GPUs module + gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_MODEL"}, Execute: func(sm StormfetchModule) string { + hiddenGPUsInterface, _ := sm.GetData("hidden_gpus", make([]any, 0)) + + // Convert interface slices to string slices + hiddenGPUs := make([]int, 0) + for _, value := range hiddenGPUsInterface.([]any) { + hiddenGPUs = append(hiddenGPUs, value.(int)) + } + + builder := strings.Builder{} + gpus := GetGPUModels(hiddenGPUs) + + for i, gpu := range gpus { + expanded := os.Expand(sm.Format, func(s string) string { + switch s { + case "GPU_NUM": + return strconv.Itoa(i + 1) + case "GPU_MODEL": + return gpu + default: + return "" + } + }) + + builder.WriteString(expanded + "\n") + } + + return builder.String() + }} + RegisterModule(gpusModule) + + // Memory module + memoryModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "memory", Format: "%3Memory: %4$MEM_USED MiB / $MEM_TOTAL MiB"}, Execute: func(sm StormfetchModule) string { + memoryInfo := GetMemoryInfo() + + return os.Expand(sm.Format, func(s string) string { + switch s { + case "MEM_TOTAL": + return strconv.Itoa(memoryInfo.MemTotal) + case "MEM_AVAILABLE": + return strconv.Itoa(memoryInfo.MemAvailable) + case "MEM_FREE": + return strconv.Itoa(memoryInfo.MemFree) + case "MEM_USED": + return strconv.Itoa(memoryInfo.MemTotal - memoryInfo.MemAvailable) + default: + return "" + } + }) + }} + RegisterModule(memoryModule) + + // Partitions module + partitionsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "partitions", Format: "%3Partition $PART_AUTONAME: %4$PART_USED / $PART_TOTAL"}, Execute: func(sm StormfetchModule) string { + hiddenPartitionsInterface, _ := sm.GetData("hidden_partitions", make([]any, 0)) + hiddenFilesystemsInterface, _ := sm.GetData("hidden_filesystems", make([]any, 0)) + + // Convert interface slices to string slices + hiddenPartitions := make([]string, 0) + for _, value := range hiddenPartitionsInterface.([]any) { + hiddenPartitions = append(hiddenPartitions, value.(string)) + } + hiddenFilesystems := make([]string, 0) + for _, value := range hiddenFilesystemsInterface.([]any) { + hiddenFilesystems = append(hiddenFilesystems, value.(string)) + } + + builder := strings.Builder{} + partitions := GetMountedPartitions(hiddenPartitions, hiddenFilesystems) + + for i, partition := range partitions { + partitionAutoname := partition.Label + if partitionAutoname == "" { + partitionAutoname = partition.MountPoint + } + + expanded := os.Expand(sm.Format, func(s string) string { + switch s { + case "PART_NUM": + return strconv.Itoa(i + 1) + case "PART_FS": + return partition.FileystemType + case "PART_DEVICE": + return partition.Device + case "PART_AUTONAME": + return partitionAutoname + case "PART_LABEL": + return partition.Label + case "PART_MOUNTPOINT": + return partition.MountPoint + case "PART_FREE": + return FormatBytes(partition.FreeSize) + case "PART_USED": + return FormatBytes(partition.UsedSize) + case "PART_TOTAL": + return FormatBytes(partition.TotalSize) + default: + return "" + } + }) + + builder.WriteString(expanded + "\n") + } + + return builder.String() + }} + RegisterModule(partitionsModule) + + // Local IP module + localIpModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "local_ip", Format: "%3Local IP: %4$LOCAL_IP"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "LOCAL_IP": + return GetLocalIP() + default: + return "" + } + }) + }} + RegisterModule(localIpModule) + + // DEWM module + dewmModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "de_wm", Format: "%3DE/WM: %4$DE_WM ($DISPLAY_PROTOCOL)"}, Execute: func(sm StormfetchModule) string { + return os.Expand(sm.Format, func(s string) string { + switch s { + case "DE_WM": + return GetDEWM() + case "DISPLAY_PROTOCOL": + return GetDisplayProtocol() + default: + return "" + } + }) + }} + RegisterModule(dewmModule) + + // Monitors module + monitorsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "monitors", Format: "%3Monitor: %4${MONITOR_WIDTH}x${MONITOR_HEIGHT} ${MONITOR_REFRESH_RATE}Hz"}, Execute: func(sm StormfetchModule) string { + builder := strings.Builder{} + monitors := GetMonitors() + + for i, monitor := range monitors { + expanded := os.Expand(sm.Format, func(s string) string { + switch s { + case "MONITOR_NUM": + return strconv.Itoa(i + 1) + case "MONITOR_WIDTH": + return strconv.Itoa(monitor.Width) + case "MONITOR_HEIGHT": + return strconv.Itoa(monitor.Height) + case "MONITOR_REFRESH_RATE": + return strconv.Itoa(monitor.RefreshRate) + default: + return "" + } + }) + + builder.WriteString(expanded + "\n") + } + + return builder.String() + }} + RegisterModule(monitorsModule) +} diff --git a/src/partitions.go b/src/partitions.go index 8a070c1..047c307 100644 --- a/src/partitions.go +++ b/src/partitions.go @@ -95,7 +95,7 @@ func GetMountedPartitions(hiddenPartitions, hiddenFilesystems []string) []partit // Set partition label if available if value, ok := labels[p.Device]; ok { - p.Label = value + p.Label = strings.ReplaceAll(value, "\\x20", " ") } // Get partition total, used and free space diff --git a/src/system.go b/src/system.go index 69629d5..8a427c0 100644 --- a/src/system.go +++ b/src/system.go @@ -5,6 +5,7 @@ import ( "os/exec" "path" "strings" + "syscall" "github.com/mitchellh/go-ps" ) @@ -99,9 +100,46 @@ func GetDistroAsciiArt() string { } } +func GetArch() string { + uname := syscall.Utsname{} + err := syscall.Uname(&uname) + if err != nil { + return "unknown" + } + + var byteString [65]byte + var indexLength int + for ; uname.Machine[indexLength] != 0; indexLength++ { + byteString[indexLength] = uint8(uname.Machine[indexLength]) + } + return string(byteString[:indexLength]) +} + +func GetKernel() (string, string) { + uname := syscall.Utsname{} + err := syscall.Uname(&uname) + if err != nil { + return "unknown", "unknown" + } + + var kernelNameByteString [65]byte + var kernelNameLength int + for ; uname.Sysname[kernelNameLength] != 0; kernelNameLength++ { + kernelNameByteString[kernelNameLength] = uint8(uname.Sysname[kernelNameLength]) + } + + var kernelReleaseByteString [65]byte + var kernelReleaseLength int + for ; uname.Release[kernelReleaseLength] != 0; kernelReleaseLength++ { + kernelReleaseByteString[kernelReleaseLength] = uint8(uname.Release[kernelReleaseLength]) + } + + return string(kernelNameByteString[:kernelNameLength]), string(kernelReleaseByteString[:kernelReleaseLength]) +} + func GetInitSystem() string { runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) + cmd := exec.Command("/bin/sh", "-c", command) workdir, err := os.Getwd() if err != nil { return "" diff --git a/src/user.go b/src/user.go index b46aa79..b1aab08 100644 --- a/src/user.go +++ b/src/user.go @@ -14,7 +14,7 @@ import ( func GetShell() string { runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) + cmd := exec.Command("/bin/sh", "-c", command) workdir, err := os.Getwd() if err != nil { return "" @@ -74,7 +74,7 @@ func GetDEWM() string { return slices.Contains(executables, process) } runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) + cmd := exec.Command("/bin/sh", "-c", command) workdir, err := os.Getwd() if err != nil { return ""