From 65e7c864245871ba3d64a82b22a8102a91a64e3c Mon Sep 17 00:00:00 2001 From: Schmidt Peter <117939077+jokerz5575@users.noreply.github.com> Date: Thu, 21 May 2026 14:27:40 +0200 Subject: [PATCH] Project is published. --- Makefile | 173 +++++++++ README.md | 153 ++++++++ bin/gbuild | Bin 0 -> 41592 bytes bin/gconfig | Bin 0 -> 17224 bytes dist/gbuild-1.0.0-1.x86_64.rpm | Bin 0 -> 23100 bytes dist/gbuild-1.0.0-x86_64-bin.tar.gz | Bin 0 -> 17209 bytes dist/gbuild-1.0.0.tar.gz | Bin 0 -> 14553 bytes dist/gbuild-debuginfo-1.0.0-1.x86_64.rpm | Bin 0 -> 11771 bytes dist/gbuild_1.0.0_amd64.deb | Bin 0 -> 16020 bytes include/config.h | 28 ++ include/git_ops.h | 18 + include/logger.h | 22 ++ include/make_ops.h | 17 + include/tui.h | 16 + pkg/deb/DEBIAN/control.in | 10 + pkg/deb/DEBIAN/postinst.in | 2 + pkg/rpm/gbuild.spec.in | 34 ++ src/config.c | 195 +++++++++++ src/gbuild.c | 194 ++++++++++ src/gconfig.c | 91 +++++ src/git_ops.c | 125 +++++++ src/logger.c | 158 +++++++++ src/make_ops.c | 128 +++++++ src/tui.c | 429 +++++++++++++++++++++++ 24 files changed, 1793 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 bin/gbuild create mode 100644 bin/gconfig create mode 100644 dist/gbuild-1.0.0-1.x86_64.rpm create mode 100644 dist/gbuild-1.0.0-x86_64-bin.tar.gz create mode 100644 dist/gbuild-1.0.0.tar.gz create mode 100644 dist/gbuild-debuginfo-1.0.0-1.x86_64.rpm create mode 100644 dist/gbuild_1.0.0_amd64.deb create mode 100644 include/config.h create mode 100644 include/git_ops.h create mode 100644 include/logger.h create mode 100644 include/make_ops.h create mode 100644 include/tui.h create mode 100644 pkg/deb/DEBIAN/control.in create mode 100644 pkg/deb/DEBIAN/postinst.in create mode 100644 pkg/rpm/gbuild.spec.in create mode 100644 src/config.c create mode 100644 src/gbuild.c create mode 100644 src/gconfig.c create mode 100644 src/git_ops.c create mode 100644 src/logger.c create mode 100644 src/make_ops.c create mode 100644 src/tui.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2c21f21 --- /dev/null +++ b/Makefile @@ -0,0 +1,173 @@ +# ============================================================ +# gbuild — C rewrite build system +# ============================================================ + +CC = gcc +CFLAGS = -Wall -Wextra -O2 -Iinclude +LDLIBS_GBUILD = -lncurses +LDLIBS_GCONFIG = + +# ---- package metadata -------------------------------------- + +NAME = gbuild +VERSION = 1.0.0 +ARCH := $(shell uname -m) +DEB_ARCH := $(shell dpkg --print-architecture 2>/dev/null || \ + echo $(ARCH) | sed 's/x86_64/amd64/;s/aarch64/arm64/') +RPM_DATE := $(shell date "+%a %b %d %Y") +PREFIX = /usr/local + +# ---- release output dirs ----------------------------------- + +DIST = dist +DEB_ROOT = $(DIST)/deb +RPM_ROOT = $(DIST)/rpmbuild + +# ---- sources ----------------------------------------------- + +COMMON_SRCS = src/config.c + +GBUILD_SRCS = \ + src/gbuild.c \ + src/git_ops.c \ + src/logger.c \ + src/make_ops.c \ + src/tui.c \ + $(COMMON_SRCS) + +GCONFIG_SRCS = \ + src/gconfig.c \ + $(COMMON_SRCS) + +HEADERS = $(wildcard include/*.h) + +# ============================================================ +# Standard targets +# ============================================================ + +.PHONY: all clean install uninstall \ + release release-src release-bin release-deb release-rpm + +all: bin/gbuild bin/gconfig + +bin/gbuild: $(GBUILD_SRCS) $(HEADERS) | bin + $(CC) $(CFLAGS) -o $@ $(GBUILD_SRCS) $(LDLIBS_GBUILD) + +bin/gconfig: $(GCONFIG_SRCS) $(HEADERS) | bin + $(CC) $(CFLAGS) -o $@ $(GCONFIG_SRCS) $(LDLIBS_GCONFIG) + +bin: + mkdir -p bin + +clean: + rm -rf bin/ dist/ + +install: all + install -Dm755 bin/gbuild $(DESTDIR)$(PREFIX)/bin/gbuild + install -Dm755 bin/gconfig $(DESTDIR)$(PREFIX)/bin/gconfig + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/gbuild + rm -f $(DESTDIR)$(PREFIX)/bin/gconfig + +# ============================================================ +# release — build all four artefacts into dist/ +# ============================================================ +# +# dist/ +# ├── gbuild-1.0.0.tar.gz ← source tarball +# ├── gbuild-1.0.0-x86_64-bin.tar.gz ← stripped binaries +# ├── gbuild_1.0.0_amd64.deb ← Debian package +# └── gbuild-1.0.0-1.x86_64.rpm ← RPM package +# +release: release-src release-bin release-deb release-rpm + @echo "" + @echo "Release artefacts:" + @ls -1sh $(DIST)/*.tar.gz $(DIST)/*.deb $(DIST)/*.rpm 2>/dev/null \ + | awk '{print " " $$0}' + +# ============================================================ +# release-src — source tarball +# ============================================================ + +release-src: + @echo ">>> [1/4] Source tarball ..." + mkdir -p $(DIST) + mkdir -p $(DIST)/$(NAME)-$(VERSION) + cp -r src include pkg Makefile README.md $(DIST)/$(NAME)-$(VERSION)/ + tar -C $(DIST) -czf $(DIST)/$(NAME)-$(VERSION).tar.gz \ + $(NAME)-$(VERSION) + rm -rf $(DIST)/$(NAME)-$(VERSION) + @echo " OK $(DIST)/$(NAME)-$(VERSION).tar.gz" + +# ============================================================ +# release-bin — stripped binary tarball +# ============================================================ + +release-bin: all + @echo ">>> [2/4] Binary tarball ..." + mkdir -p $(DIST) + mkdir -p $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin/usr/local/bin + cp bin/gbuild $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin/usr/local/bin/ + cp bin/gconfig $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin/usr/local/bin/ + strip $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin/usr/local/bin/gbuild + strip $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin/usr/local/bin/gconfig + tar -C $(DIST) -czf \ + $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin.tar.gz \ + $(NAME)-$(VERSION)-$(ARCH)-bin + rm -rf $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin + @echo " OK $(DIST)/$(NAME)-$(VERSION)-$(ARCH)-bin.tar.gz" + +# ============================================================ +# release-deb — Debian .deb package +# ============================================================ + +release-deb: all + @echo ">>> [3/4] .deb package ..." + mkdir -p $(DIST) + rm -rf $(DEB_ROOT) + + install -Dm755 bin/gbuild $(DEB_ROOT)/usr/local/bin/gbuild + install -Dm755 bin/gconfig $(DEB_ROOT)/usr/local/bin/gconfig + strip $(DEB_ROOT)/usr/local/bin/gbuild + strip $(DEB_ROOT)/usr/local/bin/gconfig + + install -dm755 $(DEB_ROOT)/usr/share/doc/$(NAME) + cp README.md $(DEB_ROOT)/usr/share/doc/$(NAME)/ + + install -dm755 $(DEB_ROOT)/DEBIAN + sed -e 's/@VERSION@/$(VERSION)/g' \ + -e 's/@DEB_ARCH@/$(DEB_ARCH)/g' \ + pkg/deb/DEBIAN/control.in > $(DEB_ROOT)/DEBIAN/control + sed -e 's/@VERSION@/$(VERSION)/g' \ + pkg/deb/DEBIAN/postinst.in > $(DEB_ROOT)/DEBIAN/postinst + chmod 755 $(DEB_ROOT)/DEBIAN/postinst + + dpkg-deb --build --root-owner-group $(DEB_ROOT) \ + $(DIST)/$(NAME)_$(VERSION)_$(DEB_ARCH).deb + rm -rf $(DEB_ROOT) + @echo " OK $(DIST)/$(NAME)_$(VERSION)_$(DEB_ARCH).deb" + +# ============================================================ +# release-rpm — RPM package +# ============================================================ + +release-rpm: release-src + @echo ">>> [4/4] .rpm package ..." + rm -rf $(RPM_ROOT) + mkdir -p $(RPM_ROOT)/BUILD $(RPM_ROOT)/BUILDROOT $(RPM_ROOT)/RPMS $(RPM_ROOT)/SOURCES $(RPM_ROOT)/SPECS $(RPM_ROOT)/SRPMS + + cp $(DIST)/$(NAME)-$(VERSION).tar.gz \ + $(RPM_ROOT)/SOURCES/ + + sed -e 's/@VERSION@/$(VERSION)/g' \ + -e 's/@RPM_DATE@/$(RPM_DATE)/g' \ + pkg/rpm/gbuild.spec.in > $(RPM_ROOT)/SPECS/$(NAME).spec + + rpmbuild -bb --nodeps \ + --define "_topdir $(CURDIR)/$(RPM_ROOT)" \ + $(RPM_ROOT)/SPECS/$(NAME).spec + + find $(RPM_ROOT)/RPMS -name "*.rpm" -exec cp {} $(DIST)/ \; + rm -rf $(RPM_ROOT) + @echo " OK $(DIST)/$(NAME)-$(VERSION)-1.$(ARCH).rpm" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a0cde6 --- /dev/null +++ b/README.md @@ -0,0 +1,153 @@ +# gbuild — C rewrite + +A C rewrite of the original [gbuild](https://spdlab.hu/gbuild) bash tool. +Clone, pull, pick a target, build — all from one command. + +--- + +## Features + +- **Clone or pull** — automatically clones a fresh repo on first run; pulls on subsequent runs +- **Interactive target picker** — reads your `Makefile` and presents an ncurses TUI to select the build target; pass `--target` to skip it +- **Timestamped log files** — every run writes a log to `~/.local/log/gbuild/` named `_.log` +- **Log browser** — built-in two-pane TUI: file list on the left, live preview on the right; open in `less`, delete, or quit +- **Configurable** — reads `~/.gconfig`; override with `--url` and `--user` at runtime +- **`gconfig` companion** — manages `~/.gconfig` with `init`, `show`, and `help` commands + +--- + +## Dependencies + +| Library | Purpose | Install (Debian/Ubuntu) | +|----------|------------------------------|------------------------------| +| ncurses | TUI for picker & log browser | `sudo apt install libncurses-dev` | +| git | Clone / pull | `sudo apt install git` | +| make | Build projects | `sudo apt install make` | +| less | Open logs from browser | `sudo apt install less` | + +--- + +## Build + +```sh +# Install ncurses dev headers if needed +sudo apt install libncurses-dev + +# Build both binaries into bin/ +make + +# Optionally install system-wide +sudo make install +``` + +--- + +## Quick setup + +```sh +# 1. Initialise ~/.gconfig with defaults +gconfig init + +# 2. Fill in your values +$EDITOR ~/.gconfig + +# 3. Verify +gconfig show + +# 4. Build a project +gbuild myproject +``` + +### `~/.gconfig` format + +```ini +# ~/.gconfig — managed by gconfig + +[git] +GIT_URL = http://localhost:3000 +GIT_USER = jokerz + +[auth] +# Use token-based OR password-based auth; leave the other blank +GIT_TOKEN = +GIT_PASSWORD = + +[build] +DEFAULT_TARGET = +CLONE_DIR = ~/projects + +[log] +LOG_ENABLED = true +LOG_DIR = ~/.local/log/gbuild +``` + +--- + +## Usage + +``` +gbuild [options] +gbuild --logs + +Options: + --url Override Git base URL (default: ~/.gconfig) + --user Override Git username (default: ~/.gconfig) + --target Run target directly, skip interactive picker + --logs Open the interactive log browser + -h, --help Print this help and exit +``` + +### Examples + +```sh +# Build with defaults +gbuild myproject + +# Skip the target picker +gbuild --target clean myproject + +# Different server and user for this run +gbuild --url http://10.0.0.5:3000 --user alice myproject + +# Browse past build logs +gbuild --logs +``` + +--- + +## `gconfig` commands + +| Command | Description | +|---------------------|-------------------------------------------------------------| +| `gconfig init` | Create `~/.gconfig` with defaults (skips if already exists) | +| `gconfig init --force` | Overwrite; backs up old file as `~/.gconfig.bak.` | +| `gconfig show` | Print current config (auth fields masked) | +| `gconfig help` | Print usage | + +--- + +## Project layout + +``` +gbuild/ +├── Makefile +├── README.md +├── include/ +│ ├── config.h — GConfig struct, INI load/save/init/show +│ ├── git_ops.h — clone / pull +│ ├── logger.h — timestamped coloured logging +│ ├── make_ops.h — Makefile target parser + build runner +│ └── tui.h — ncurses target picker + log browser +└── src/ + ├── config.c + ├── gbuild.c — gbuild main() + ├── gconfig.c — gconfig main() + ├── git_ops.c + ├── logger.c + ├── make_ops.c + └── tui.c +``` + +--- + +MIT License — maintained by jokerz / spdlab.hu diff --git a/bin/gbuild b/bin/gbuild new file mode 100644 index 0000000000000000000000000000000000000000..6c3664f9f5284b0f0a1f4c17c7611fee646d809e GIT binary patch literal 41592 zcmeIb4R}=5wKsky2@oP=qC$&;dc;A4LP$W6AgLLcffJpOGz0~~PKIPcB1tCB2LWp} znuIzx3{Y#Wx7W5@+qbnVCDAnd` zPJHUxS=wmeS%PMzms$X+)?;K2h1rr$GV|Rd(}%+;l21XUAtCdndrBu+DGF+N7S4R5 z_^R^*k1jq_@S2M)TtRz2QC^MY9|n6)wde}k^U*ey-?7pw6GP!kvJ$0ZEV{~<0hIar zWxjryPr<{i2p$EMJt>a=ZkF~O4qeQIM?v!PA>_ZB2(jZ~si3mWt!H?7J7m7$P?!7) zs`8fqJzDqwrMya+uXDX6csTq*mZzX9uUh8o4_gew;TD-sK~=wRA|Kn~fA^7Yrd7%E z(zW|eHZUFqWpj!)m;gGPZ|SlX+P}TG?b_vAKQ5SX=7ffKCI)}%{pf==Uq6f zscvRdV@qt~%#HKs&75~(K_pZ#TjVQ4Udj`CJ3zWIZz^e*;UAy;Gqy}wRq^5(FI})@ zO7Vx?s%u$^!#<}&&?&=cM8Cqnk?1vP=yOotk@)k{$j?bbU!8`2QX2WsrQ!c}8vd8l z=)Wk9oVU}^LDqEq8~$7i!bo=bLmD}^rr|G1!yijiucy<<`7n)~|B;6Oa2mQhO}YLw z^nXoL?xktyUr$57B@KOInsQgBq5nCJoR8AbV`=DrNMj!(O}Rf#!{3xf{-wQ+`_k}Vkfz*Z8adxdBj@2X{AZ`(-!Ekl?;-*kbusX1|DX97V>zYF?{zxxhhPfoL@B z57pOeZM99oKv-)F2kXPZNP`xO1R=dX5NnG1YeP+;a71eh)Ya8C0Ej#-%o2`1)EsJ`rxM4K%HjQOz5SxHHS6?wU$t@wjqRU zZ89GViPVO*mS7t*Mxu4Z>Y+hIYmMO}5)HT1wr+y4Agy-2zqVn$zdq2|q`?3|mJlUN zU945BZ3;z#bx;?0xV8aO>YHM)QFE|alwBWc4WfiiXoqISYm> zz&!PB;l?Pf5w2}+)vk$z!ci0txh)}9ED&uBwP?`0g>4^2$XCJBd#a>0`8qC4Qes&(iuN9y`5pENAi00_VeN_!gN){Gtu#LEdQXbxF%C=LkMZ z`&i<}zs^K@hL$H4RO?0+kNO$N@vM~VLPc+v^qOIG-&%{mVHmw#(p!ho|3%U_4x@ix z(%Xm8e=O-Y52N2L>D!0VcS-u~!|1(|zGE2uc}c%(82vR#-#Lu_4@rMu82vLz?-@pS z)>`(_hm8{o@7ZRhqfy`$gU{nOdhQ@u>$lNo+UPIY=$F{&`)%|kHo6+OxM#sNoO;x_ zMf#7_P$4hixxZ1j^95wOxmA7`Vlw9&`g=ruOF(?)Nw z(erHdRvZ0f8-1gVZlCY8+vsu&N#wfOM(2KjdbZo>6BQBgb{qX`Hu?@5{p&XRT{gPh zDif(YZS>P@{14dZr`zZ~Hu_{6{c#)JzQNFMqjT+`o|kO&GZhhVzm0yDjc(fLx{ZF= zMnBs|KWd{-wb8ZjSqOdY92-5`Mt9lhc{chq8-0?Eey)wK+vw-n==nB!zKvdJqffWd z=iBJ#+vue>`UN(+VWZ0-L8MmN=mj?Zl{We;8@8@<#<-*2P4ZFJK{Uu2^nw$T^c=tpgIkBzR$@s9rQwb8R} z^rbd>o{etU=qFxJSm1;OPFUcC1x{Gtga!WJv%p8rGmjY^?`Ip`qxOU}&FJchW(@Qj z9s9C-MP&x&{t4uPX;0$Qc~&Xjh&M1Txqkr9w7tZ+giQ7e{6XSeIwpGr{vX7-WK8Z9 z_^*j`shHd$@H>ffiJ06j@E;N9(lFUB@LPy;nv!f4_`eY6QZQK~@SBKpX`ZYU__f5j zButhHJWQNR!DOMpuO`kVU{V+OTH;*#CG!M+C2=n4lbXP*h&zcN{tAR?mlDq-zF**r zh@VWnU*HRfpF+Gx;IoNyiI?0d@bigtX_wp~@TtVPq)ToW`02#CluNb?{1oC`!X;Y; zoN5swGPW{vmNL(UOG%e~&nqW=UP(ZxQE`ESV?pSBP^d zmed6P9C0qel83)!{r^N+&mFySzgT%SiO7;l+KZtXQmE0-tUlZrjD!D`8 zcM|84D!EL$MEeuxk|?=f;ERYC5$_lH0^+lY z_XvD8aV~w5I|Y6|aV~k1I|M$JIF~xf?E*iYIF~rdc7dNloJ*TztH5)Jb4in|5qJi1 zE@hIH0zcLaoJ*KwslY!Z&ZSGTP~h(o=aMC<3;ZqOT&g7V1pW$fE>V)2z@H<|Evw|= z&qezaFD1TT;CqR4iIVIW_=Ci`G)eXVcVFSY+`YnGy<)j>V!&e_m)JCL$G`9v9dz$zSTR6kcNZ`0cX-JXOY;L~*Kc=s^f|o6 zA3L}9BERMeY}m#Z zA29DkYj^cHJKwa(U%`YVcMEd#hv58&ByF@fyXJ#4RodGq-{|&OK8qNC(>xFEg`&H-iCo@wFq-$^0BY`we_$Z-d|1)wzR+cK z9f_VoQH2Pe4-TMmrjtpa>_m8c&?*T+w<6^QI5<;&hrl?PvK=W8A*JIwnAzENBXz@j zHjMEwgcconFi)s^ol7^*LF2bWG$X6CD_>YQ>dG$aF%n3IHEvo&6+bk8ovUfPd4Hv- zr#yaG)D>$*Be6wjVk9PJ!ocRwp{g%_L?lzeC{(7kc|QcgRzR2#T-0N(f;O&a}eZhQ>tG^qx?x-v8KXoV!E#rqix*H~QcK!?{Ja_|$Aepa! z28+etlerWpr1&vXQ=qiaEVcsq!8MtC6UNa%df;-+2h% z@m^|pKvc&v(1Q?->Rc)`q)AJWH~ACV^DI{5S15)aHXWwv>WNKDxl)UAB_r-}O@ACJ@;Cf?;ppQFFK9B?d-gjnXJa_TF=&sqhXVW(2CBpJXX(QZ%V!niqXA- zf+n(_iIS7i`Dw6c%{O74qF0UXPSG*zj0>_vPjzcxqYe@758*U)9*)h6o{m z=O4Hc8I$Xgkpqy*_!6R#c@8r2P*+Q?K@{?#snI;JDm8zF-o|c$N%!`E_T{K@ z@-oaIMh=0`V+derA}-P&&bPYwuc;^LqGfEM&CF0aU{x8?toUY{=RsEY_}?`S^PAh@ z*yaJ$P5S#ZjIxYhC1)X!)87xFcFA8_jT&L@vu%xfC&ozgURtIL9HORC2uPlcCKlLK zVws}BOXecCFMbTx_&&11q;;Pnqt7z>q*P)b#(g9%W#TL(KG==|Wh7yfn=>&n?CRM# zX)qd1{1);c0=ee1W>nui4WCk+Q){PlX0|xF(78-UF8~UdP`xTR#>I(aG)C zWew!SCiHo-2E?MsRz9tqnA^q35kG{c-oh4&zaj=m%9w)IK_Ri#4f!$U?@x9Jn z`#O#dI6Zrf?ps~d4FPz92*9z6jBZPR1YuUE*Zlayfr0M%W;u0E%&md`D9?!RMUkt< zYT5&^z`%iBM%)r^KKhXy2QUk}OH}K#viM=46-;7O2)$8^h&EI{MI>^GStO z+5ycCuxdF6`>PNj-Rr7>+&BQSI|*Mc;3~&UF@Q@GK;?UyR9DDP;OE z))jK0UKW43EN;51(KZLL>M#;MSD|P}OeN6x3!%u$D}890?A^GwKxNsU6Fb3M9{-y! z{+W3iSj61&kog8rxp)yZb61zelV$OjmP6Iis#-}G zl4UN%%|3Epyep`@Fh$-q> zj{GZV%Tn{$Vbr2ypPsxQxwl*S;z`&J*`VD8Dw9>>ljN4(k?cYa^GZ+#>kC)@OnCD| zLuG_Z=ZOwt>v!z#oXNavewza0Z}7F0bEV^A!C15m^RMnT4;``>CE5qVCt>y?77*y! z7mD~{#P4%ub50XScz)M>=>wQ$m&fIR`*NY7b@oJ;F;rNy|1~bhfeFfMox2u0jE-Ih zSk75ED|WaqQ`>!z?h${NWbRUW8%egDFvnTdCqZy?r9`g>Qb3)1DQxnQqHtIk% zdsV5X3sqq#$2u|oIXgXkcSx|F4?-NV8r?uRws{LAcYK-Q?EDNh==d_z+1Vpr9L~;X z@M0upzF$Bv_n-hJ=U|al)Z@e4uTacr#*YWVd|&8SGS`pyJet1i)#z%Y<6ucZp&?!o{D(~P8}UrW4_99YJ;MRtN3flrib1hc&;<=E z`1D$X*&C)tG2LUBFCVp-B$phInfF68Yu5c3gh8o}eUo}q^Nb<$1Gv^p zD27A^+)mei(2e+q1`I;yDZ<*9gACsDuz!(5IRo z=6&W)mc{wl4>4eSyMIA;_l;i+Acu2X2WZY+okC!u^3(QXjt#vF&xt;=n_I`6;_iJ{ z(%$AN`-?o`8%$*pFjOK=S zFe~s($;0@{x?x_GXLJ{$v8Ol>oLRr!rZ4Beocwkiz_@)0N3~sfM?9B1^w{5x#F{C2 z&Rr)P@%|%OmuJWF5>7}%SQ&B55;yj^c!zt9uk@56#)&;Bt!2R4cjtOtF78Ip?BBay3M^^L1P9 zNDl{!IhK?-%$>}gC>K*{IR%D)FSC2)hfGb(b+~9)YdvT#gAvN&k8%V)Bc0gf3XEoA z$WB(UJyA(pd!s+)Ln-tpvHrxoM=Z@rLLpif@fV=VGt-9kpKP$8|E#gJT7S-92J zoAC47gzI9-D@y1+Ft2_)=erNQEz+Z>TI=?4a@~&BVQE~izW}!pD<$(a#!=gPy@};v zt$ROx;28>nwJCr2dL5*}HnG<0)B@{ufL_z{Ut+!fW2%~%yOZjx_4=={>uO)GgWsHp zlC1fB_YZ|py0;R*J&K=o-a$VoepW0sofUg~?fnV%HBl{wB*ynL&iLULqNY(Sp$*%j z<1SEP(l}HT=DQXlfidTCk#S$qLCggi?j8Wn7_=YI{Ig|DXXhipZv2v`4$jVb$l1Q} z)B|(|kxtKSlyJ+(p$NGv`1Kx@6aXf$YY1Bcl@5J>`u9TnqU! zG3mw%%bf8RJ?g0@RjbUlkA4%{T#_aPcE8G|xK2M_#u(Au&z zv|T?Wq^$qt-!N1SY448rHyT|7&dw#HG_8A^){gzdC!C%0fpL1AZ^YNQ3?u#*U%bw> z5(@z%-sGzE;iJZfUHb-}6118ZK{0dfetdi>s$8<9TJFA^J`k`J_&XExP}D7T zBlOu|GN*us_lbCS#~(GRpV1w4<-=d03Z~V1Vp9e*+WK$E6n_fAbzE8eO*h&T zDFoM#+z;oYl7{0G_SZMfYzXP-$#NI(^EiF`MX)VC5c|6?{y8Q%UHO>C zpzETRnBmjxSXvx1*Fd)USA5WL{89<`aJ)rNh)vk5p+(UVuwAWPt(o7X?XdA+-X+;< zz>XU0=q6iNv0v4qg!0;VJZL$WC;m~{p0~&R5p<11SGnU0pW`S;uSo|k8yvqp5Riw> z3lS`MWHtW$v*}OR6>`TvF#kyP=8N)TQLN|LQ##&n_Y~v!&AIhW$aWY1lr8Dp`W%qr zHLh{dI&+N35d9f#h)&6R{`?KN^ey$HAz$~{sGFCTW;eo z)fo|3t9QXrV@yqiRA;>12-F9hXpVl8W1%& z2F&~ev>?A>bhAGq`$`;Mqf|$I)w? zUFQIyw?oiCG9Mp<{)VO**Y!lgJ>Xon$6eeP-^(8Fb}oCd>j{su;=`ngbt22{IC{0S z>s27`?(fpSm=E*7;>Vp^9|aXNJ6J-GKaL18&YhSrZ~9?Op^E?HaV|TY{JD_PiK?L= ze*0CJb2C10dOaG1M^H!X$D;8cqNa3vj2@81~*%25q20d$ZFLaqVgU!s8;ttS`$HjC8 zbj~Ss;h!R^KZjh@@;wgJ&=MY>mst7v1N2vHidXc)E~OYLyOQusmLo!?umU%f#&4Po zRmP)Kp~F7$f=P>c>?LRulfp~>sW$oML1yC^&yb``d@5j!UgHtmj{us%IIhhTO zw@IQmWI$v}tPZhd=o(y^U55nDP3Ge|0b}AMU!u-c<3V35Jm8b_r^Bczn8o><-#UNu z4?lnN;A{kK^{=0PUY=O^Vp;bk=CA*1ZEBtZMatr1u=gt$>F=7`U<3$^-N~lHh{7Wl zls(_9M@gRI1KzlafYa;TwhT4%au{BK(19W#-W`9o=n33EN@R@%R@R;MB5L^*Tw#yH zUHteB?|N8g$Z&4;vrSO7LT~&jPyA6-#W_2c@fIWYfi-n zZg|-j|CE{)ny(?j{4T1*@oy6S>2!#PZqYStMo(G%ptvRBPBb_eXUn_mUO!si^>(yK zIB8jb{*RogGDu1 z#H86=z*gxlIrUNqD6rng@clXDV3hA1i!9Eq=fJDwpmY?k&TTh=EC$+#fQn&-_s4{D z|1~B2DOQ*Ljq`1W*wlR6*|ig%DEb(u+aj=HjxGW#a1mHB9T$NWb9M$+&gDfAx6UJz z+h4$ylC$9B?nlnVXY#9KECHjn6JaEf-qv6ZyU$ z>7qtSWOQ!hSAaW?);T+mLawmHfS^ovcD)0NxJaD ztaIs($KBJP60>GB)CUJH!u%NCfg?aKW}ZSlsJa?m&=davi=jiP5NB{V?xTc@(Tkj& zccTu)P3+SDjyEH*hBNrZ$d0#PJ&o#Dz=#- zpn;fAjBSo*qWKv!bwYQ`VY21q z#@t4cZ1utUi|}cc{>)#He($jKg)Wu6lgS#Ad2Z8PaH&4ixjayG>E&*9@Vyg}WyOVnJVfz2~uI>t!i^NWyd9s{3XN%kS#sz~QW@GEP2+;u6+O8z^ieu$6R=4u!OhPcp+@+z$Ne7=8# zlGs>X1+)|v0TaY9h&;I9HF4ln_}~#l*2XoiN_Ivga}CDJ_{$xK9HzmR=`Q)32V%Z} z7`(s0_Z<|u|ILo0tDIf!R3PyXhf;GsH0HWOr$#Hy({L=u5j(!8s|SY86Eg^B#U7gU zY1np#)7MwL$7%x3pJycJK$uz8kB_VHF=V35_I?nRw@;KOrD%f_<*XD9(OP>1HPXz( z(AUZfEwx854r%6Vpe5Trfx=?MD>_(oP?$Y=Dmnw3X)@L5!w0;q^M8mQ0yDFVK&H?y zxeZkFXV2i{H~1K$?srl}j%JZCy=FG1XoKpmNYMt>b*E^9>dpi$IpIs%7lWbMLgl7Q z<+7nh@>?*?abr&myV*mA60yhHjtcxQ=k^OB{QC2StHs=w+n~wx<7Q8oIwhx|cZmS- zF8kiL-+fFpC;9}|p3!~DJApd4-GNW(!x6AMw{??XE&jHP{MR@;e<|``Bl2TF-YDo; z;=1vYyb|QW_$SVWI^M^=`MHgU`&<3}9chD&QV=fQLQ;1XA5#D$APC=tU~BT~?Vn+TlV$IB3iFton? zG%7MED|O$D#rhJXo;VqoSKAREh5ZsG)0rD)w9NA9lgONy=p<2D2W2_E{Gv@>QO{~# z0ZL5#3OAFI-$p_9`ojHh#Dy-|F4<-`(#7o#Ouk=623mIvYunKd_jibkO^K3UA;o!e z$9{AW04`PmcrgP&w?Lr_lXniW+=FvQNjsOUxDFmHj#hQMK8KA(^N%8o&Rr-L;cuE-*@|Mhfv_MB9%P@*2P}*4<5GM+ zUw1DptM;#`@`*)xDJhQ#%5rbj(D}PB{+7JxfcrYE5v8v~fZ@TzZ`mVvWw{XQZpGo6 z^VYrDe~CS{aJ92@6gYO5AbOE)CGCc3*YEpifd;toyJi$wInQnaOkLpDU}X|N1<_Z0 zq08C%Iz;%27vfFacYA@qUdI7$SKmVEuOK>h}qeVt?*Wh*_JN zCgywGSAPH`bwLt0e6R{&b8<7DZOI*bw%~C$shnNkWPK3H={`I?Dv;u#8NR9@Gr-S=W?0P(eaIl=(F16i zD=MlyFtNuq%Gp&TGeU=<8K*L1LX_lTv-+#uRZG3qBHtKi*Fu>O9ZnudN)6w89Qk-A zC+sEkt8#XoZLza|4l%L$5ZTX7>4!?QV-Dr{A$c;$gO<&LO@hv@uR~z+OneOQR;o{N zdyDO07Na5$aLOUJfQR>Qaj$0$M-c3^eTpFWb3{M$1vW9e78|AgA-WKH0+uxh*g?^` zAH|jB6?{v4Uqlj|t~tGXs*FT88_frRvVDmTaf%dwrP4@rh~Si%TZY(86u-ixRU*{? z3i=^1>FjA&p{87TBS>2-Lkt1(KGGp!Pkybb65IZzokxZQAOkc}g?A93V`XL?gyKvJ z4yWPhG$GGZ&*Tghm;Ds}743%`f6P62nz#-=$uckyZZ{_jL9vH1VK1bjIKS0VK}62A z*#0Yv_fVLQSJY${wTGt?9#dqy`lSSMo771=_>>Dc**)ha0ydN1clNzQoLv z0o>KT#I$U@fId?LR0C7{BebmMb9zm@!q!l!B#E{IWxx4{K5^rdcLbbX-0dxS2`MPM z86~!hGcd6Ko-Kv6r;uhiJ)a=G9OaDS+CY`_S8LZRugBwAaRyVyss|vP;B8|~d zc#|G$tqVkhxbnBB3_a+}ea6?kuo=)l)JGk(P=DsGzoB)-0_d8f^uf7FjSYQmWJ+ zq*~RnRb}e5$W$F4NSLb6{>HgQ+O){9%p?qoB4(2^b1`#IwTvtBujbEk>Q`x#SI?Q( ztgWuNOxG=JcJt)bMW9^au3BbM<^c0nRaI08?jkFF^6J87p~SR^J}r`?O*0luD_<~e zIcUHw(yD3A)9U=wjA`Y94t@O7A_Z^>txYSaG%A*@0{5))!1`c4nw#&<>YXpT(i-xF z&IQX?ELl>v(z{$MkZTf%sq!u^t5^Y2*|KVHRk_zw=C1Z?1=k+n8&s**FS1j|3Dk0m_WffJ(;H$X8U*%n1;agE%hSch^a!M)ld6!jd1>TjF z6;;)K$nan8t}1gc@=*yaftDLypHI`L%{)J%Yr241eoIKF-|O|ESWDe>4T%NkM-*9) zNROMYH3cIP(Jqh)Z(v7Q%gQVF7vps<617LMZ+<+Xu;aR`hsaqb=vuOXo4u}ADyn{==y4OhO1yR zsf4bV=yW7Wz~WgF$Qp=68x*h558gV2U#V)#Bl;vW4b?uCEmaH3Qqou$48&}2N(Y3KIs4qpNT#H|0*0B?z z>ncny(667Rykxp10Imc9{0U(pVTb^RZ>B(WdI?sf)E7qAMJb^w)*@phsuzZ{HEo)q zN7gsC>iA7){6KVVv~fdFZ*8ny9}Ei(MBfm~TOSqp{bD`Z5FEm9b9`oMn1Q-;yixfo z`HR!Y)fmwQInYw4bF|3G@oo$-3`b}$*^8PtNy`qB)EK4nx3pXI5jkLFT}J96408By zuIN~723iq6>0BEeIVqvDO0NWbiXNj#^F-4qg+3xg&Z&-w z#6{huJ_xI_8TAPCYOHU>Bp?U%mSdH^I26P1OcnT(*o?tC2NsxAL>ARg#SfBW#An}> z0>v0D<``&HHqs{e1&Xf|)!>wbGlC{46^ZJ#4S|+*Fg#});#n4w9YBVB7N|xwAs`%U z5fhd3IINwgw>3r^pr*`Jpr!Js#s-lqfFE3sqFh;uiZctW_(Fx)8mjM!cS|K}PNR!4 zR5rE#*5>cMwfXycOJGCeI@(s>d%v!G*+FHY7UdefHqcTVY!da-kIyBu2>YUzVg%Fm zIy9{qz;yi@9c~#_&6^eq`RCX@hoQ4M(1N&Fr?1_l%kGeqBZi5bp@W1PudKM7V+{{E z=jbaC{-UAv!IqhfgmrpFmEIbNMB4Df^^(GY z(2*r)wKc5dj5Mf>G@|5);Uwp{!Q{A+1QuC@pSFkAu~tU12%BEF#&D)rm^K6=mj|A-GPDE0G|Y$fU~(N=D@&(fHRT< z18V@s9fW>>4+ENj{9fod?3ak!Vzj}vRhnaCo@4U3G1=QuKhkjxOe=k4VBlOp%`*n4 zC)VGux)8A8_ox^2&~o#Zqhq`*G>o;@TPs_}4xf*O9R*cSm+cX-;mQn{^@&{c$^Z9wrYgn00MOM#hrfb)wQn2Y;|b z+Yg=@``}A}l(QMOo79ncY3}~Kj1O{hCxHZco|EPyc^v9q34iX#J8h6)du_>FlB;jd zLbWyWk+Y5$o@yC;=)JJPDrmqtosKRY@?S|Q}{JnRniy-BJ1daU{$c4RsnsS3NX zQ!O3?pX{f%qkp;qspDx?9b=Yn&8$sieP{FNEu*$&WFEvXX*_8>vq`A8GRLBNPs*s0 zZ+%ZbgRHc7CHpY?@&zm(J~nCf=8TM`aK;*&Ga`wd={oT4K!11PolhzLF(y0=+J9O3 z4?7GfvU&97TSl$y7_%)S<7h7xX+o3b9(Ht$p>}I~pM0i{uZgT}88wjcJY+QBz_$kR zK=b6bI|3bJj4h+C*gQIswKWsfxdzVbxHNaWqb&D!$D-UFfb9-cve(<^ebW1s_i67l zMsD^}5Ie@OhBDiFM{nPgPd)w2%@E6zojSy#cg6u>UotwzWYpQ1cR6w&7<*}M&)6lo zkB?oH+dp<0J6y{M#LQ30S`Jn}pqp&J%b~rWGM+Y`sbCMybd44zuN4i@Fj$O;QTw3F zMTy9Ks{`mY&4EC6UWc5^|@& zemjxRo07XMSFg*>TMXZ3n=M58exy56zGl$ZSom|fld2gRv#Y^Z2foMg)rDu#F0fTU z_NuckgSp(S$7NeHtI#BOIr=VrlJ#xL%{IWoB+*v09eqze{Y;I*y~H4fzRtK`iTHmv z-qFsww^=Iin9oaN&US; z>U-h6IOM=X-{-iia31^N4xRSAIF*v)4RCa2Jek>9c%v#>W*+_s34XWWWOkp-?2|ws zSNb$w+P7?lYKy@$fwYll`<%?tTA=lCd`f8&l1mrLT&Eim2!THR{v1*mvdL~aAJ#x^Kd@rNq)61=K7ouzRob@ zk!+f|%_7TcX@(TceIq_@^Ob;q;n4c-}&^*_8P9kJl&!!k??W}>m}SE;bsYMlkgW3?vk)i z!WSicN5Uf#jy_ux&(|b8SHgJ`E|KtZ3F{@?AmL^SZix=qm zSbuoqo`I;AH$w`L;Cx9h9Y!yabQPafF8rhe^qH*Pe5*yF-hBM{=Rg!v@lFu&u^InZ zu8Q}HPIEc1BdOx2qW=yrBkBJT=#;Zx>M8b+LF-Gy|BU4C7aN_}g8|OxMf{_j?Xv}n zAJeelTL2@Kdk}Po_BBoYz0g*RUE{B`jKmLSA%6!Ah)20euB7)%y4W{FI`s8vY4cBiCzA8u~W{eVlf9rVy`*vnjj=K+l7n^M~2F zLGrgQwD`q2170I(1-IGSne#zfD z&*B&7TzGvy4Zj2ZZX`WjY3Lq7AE#AvV8!FLfL5J`zdj9p6X^8A;r)HHl&?#{;v5KN z@UL}`r01^`|CN>^;%ou0-$~Q%e*nL3u@3p%mqz~cpr4#^dTPF;GX0Ho4U2&qivqV4 z&*3z32F8r6zcUT}oHX<$Y3Qp!&%-=tyYwHaj`l6VkNLw&iz3c+@OpI`IX4LXF>krR z;y+gk>XveL&b0{2uYQn5&V8U$pVC{T010}eoE_4C#2FXT`jz|(Ek-#F(q5PJ8ka>E zXa0D7OVanxvFQC4&_0m#LK)AMo}UZ4WU_(0G=73_hmGiO^U~1yOV14Kq#*;c&M$+m+v=}g`30Df^qGJO{YdoLY3N=-$G9Wg zQJl}CkZ-2puTMkoOhdmD^r=c|8~lj#kPPfP=r$r=|CmNje;T?8I^((e`|2=@^(?Oq zM~ezVn%^H-+vtx5*2ybs1+^Md)&_AmemRosf?>Qya97J8ZT5?cX%Xb93;EYIh1Ld| z{B^h&AMpob8#Q$Wy$*NsN96F=Gv8k)$mkl{-wT(MQ)$JV#$)_UVQVFx0U@`OR%l2 zv88m`;uTfPz00qd=bwuO>ulVfF1)C4K1vIOqyE}ZQz)E{1vf$c@~Z+CViP1L7ti0<~zvpB2ZAX8f)W_5^RlSMMi&H0Do3e z)M~hy*9SMX2I^Gy5zA`}2hr0bX=@=p3Yz#^1JMSp9&bZk7qI}ndPqGnGQcmaP{3F$ z!mj$73EQN(bqK#KB+?LSg9of@4l(#MjQANL2W>cK4wCC4AwOziV*kG&l+s?@1~6TwwqO^jr%0eE?u zKrfprv|g~aL52TP% zK%RA~{3%AM=zLZV54K~3o+`gOmoz}9$|}D)UsdotSs$>7hjq0-l|XoQsq(AyN(Fx; znU&oXpMqB*ooAs6$JV;_NVr3y)So;`e@WJw@Iu)tzdDaqP@TuB@>PCS{|z$#A}LUv z?<%OygPH#{{2QLX9W>em+ZfiP4~6R7TT)ZaqwsIz#hzc+trP`ImEtl(-Y~csxIMqx zw@@%k?n}sQnyy|X+yZFNug>ok96yYIm^#`mGQYCFRwFY=coUt|_9*))68(eatNiL* zMZqr=qlClDziU|jb~*$e1uIhxW6_6~{~sXZhs#p_*B`d{6jbL~7Im=w75*Sz?D3-} zVx=fpsu(34Uj9>{+w-e)Oa=8}`G?p4RiyKZfa<2|ys+{+R>tAwDO|yK!N#tp@~iX7 zmH(UhKSEx%pUSV!0c&Lb;r7$PJLExTHj&D&&fEHB{=*cGhy9$$;#`4&ihs{9J_+|iz2-3Kvc{zB!LQi95-;Ma%cSNqG8Zn82cJ(U6~pTf^Te$rI^ z)&6C^%wIXId|hRfaH=f7a2Q?XA0B^LS3Y()tO${A$nvvg0N{7Z_)qDt;wQGRQo4(b j@}p9=!{=nMJ*r$qIvFpj?utfu*}qztIu}xD!}I@77;8Y5 literal 0 HcmV?d00001 diff --git a/bin/gconfig b/bin/gconfig new file mode 100644 index 0000000000000000000000000000000000000000..2b1ee61791965b2729a817569f8edbcaa1b069f6 GIT binary patch literal 17224 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 Clcrz* literal 0 HcmV?d00001 diff --git a/dist/gbuild-1.0.0-1.x86_64.rpm b/dist/gbuild-1.0.0-1.x86_64.rpm new file mode 100644 index 0000000000000000000000000000000000000000..68a8eeb313cacbcc006ccbe0f16d1e69906208f6 GIT binary patch literal 23100 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 E1pbx>`~Uy| literal 0 HcmV?d00001 diff --git a/dist/gbuild-1.0.0-x86_64-bin.tar.gz b/dist/gbuild-1.0.0-x86_64-bin.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..53d9c4feb92b4b6bd11c45b0ae3871419e235547 GIT binary patch literal 17209 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 B;6DHW literal 0 HcmV?d00001 diff --git a/dist/gbuild_1.0.0_amd64.deb b/dist/gbuild_1.0.0_amd64.deb new file mode 100644 index 0000000000000000000000000000000000000000..bb426e2434b85169411c6923faba2deeb2df95c9 GIT binary patch literal 16020 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≠2&V@10ne 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 literal 0 HcmV?d00001 diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..c3a911d --- /dev/null +++ b/include/config.h @@ -0,0 +1,28 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include + +#define CFG_MAX 512 + +typedef struct { + char git_url[CFG_MAX]; + char git_user[256]; + char git_token[256]; + char git_password[256]; + char default_target[128]; + char clone_dir[CFG_MAX]; + bool log_enabled; + char log_dir[CFG_MAX]; +} GConfig; + +void config_defaults(GConfig *cfg); +int config_load(GConfig *cfg, const char *path); +int config_save(const GConfig *cfg, const char *path); +int config_init(const char *path, bool force); +void config_show(const GConfig *cfg); +void config_get_path(char *out, size_t n); +char *expand_tilde(const char *in, char *out, size_t n); + +#endif /* CONFIG_H */ diff --git a/include/git_ops.h b/include/git_ops.h new file mode 100644 index 0000000..bf22f63 --- /dev/null +++ b/include/git_ops.h @@ -0,0 +1,18 @@ +#ifndef GIT_OPS_H +#define GIT_OPS_H + +#include "logger.h" + +/* Returns 1 if clone_dir/project/.git exists, 0 otherwise */ +int git_repo_exists(const char *clone_dir, const char *project); + +/* Clone repo under clone_dir/project using token or password auth */ +int git_clone(const char *base_url, const char *user, + const char *token, const char *password, + const char *project, const char *clone_dir, + Logger *log); + +/* Pull latest in repo_path */ +int git_pull(const char *repo_path, Logger *log); + +#endif /* GIT_OPS_H */ diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 0000000..f5c37e4 --- /dev/null +++ b/include/logger.h @@ -0,0 +1,22 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include + +typedef struct { + FILE *fp; + char path[512]; + bool enabled; +} Logger; + +int logger_open (Logger *log, const char *log_dir, const char *project); +void logger_close(Logger *log); + +void logger_info (Logger *log, const char *fmt, ...); +void logger_ok (Logger *log, const char *fmt, ...); +void logger_warn (Logger *log, const char *fmt, ...); +void logger_error(Logger *log, const char *fmt, ...); +void logger_raw (Logger *log, const char *fmt, ...); + +#endif /* LOGGER_H */ diff --git a/include/make_ops.h b/include/make_ops.h new file mode 100644 index 0000000..9c4f598 --- /dev/null +++ b/include/make_ops.h @@ -0,0 +1,17 @@ +#ifndef MAKE_OPS_H +#define MAKE_OPS_H + +#include "logger.h" + +#define MAX_TARGETS 256 +#define TARGET_NAME 128 + +typedef struct { + char names[MAX_TARGETS][TARGET_NAME]; + int count; +} MakeTargets; + +int make_parse_targets(const char *repo_path, MakeTargets *out); +int make_build(const char *repo_path, const char *target, Logger *log); + +#endif /* MAKE_OPS_H */ diff --git a/include/tui.h b/include/tui.h new file mode 100644 index 0000000..a2c035f --- /dev/null +++ b/include/tui.h @@ -0,0 +1,16 @@ +#ifndef TUI_H +#define TUI_H + +#include "make_ops.h" +#include + +/* Arrow-key driven target picker. + * Fills `selected` with the chosen target name. + * Returns 0 on selection, -1 if the user cancelled (q / ESC). */ +int tui_pick_target(const MakeTargets *targets, char *selected, size_t sel_size); + +/* Two-pane log browser: list on left, preview on right. + * Keys: ↑↓ navigate Enter open in less d delete q quit */ +void tui_log_browser(const char *log_dir); + +#endif /* TUI_H */ diff --git a/pkg/deb/DEBIAN/control.in b/pkg/deb/DEBIAN/control.in new file mode 100644 index 0000000..8f03668 --- /dev/null +++ b/pkg/deb/DEBIAN/control.in @@ -0,0 +1,10 @@ +Package: gbuild +Version: @VERSION@ +Architecture: @DEB_ARCH@ +Maintainer: jokerz +Depends: git, make, less, libncurses6 +Section: devel +Priority: optional +Description: Build tool for your Linux distro + Clone, pull, pick a target, build — all from one command. + Includes the gconfig companion for managing ~/.gconfig. diff --git a/pkg/deb/DEBIAN/postinst.in b/pkg/deb/DEBIAN/postinst.in new file mode 100644 index 0000000..3178fec --- /dev/null +++ b/pkg/deb/DEBIAN/postinst.in @@ -0,0 +1,2 @@ +#!/bin/sh +echo "gbuild @VERSION@ installed. Run: gconfig init" diff --git a/pkg/rpm/gbuild.spec.in b/pkg/rpm/gbuild.spec.in new file mode 100644 index 0000000..403fd5e --- /dev/null +++ b/pkg/rpm/gbuild.spec.in @@ -0,0 +1,34 @@ +Name: gbuild +Version: @VERSION@ +Release: 1%{?dist} +Summary: Build tool for your Linux distro +License: MIT +URL: https://spdlab.hu/gbuild +Source0: gbuild-@VERSION@.tar.gz + +BuildRequires: gcc make ncurses-devel +Requires: git make less ncurses-libs + +%description +Clone, pull, pick a target, build — all from one command. +Includes the gconfig companion for managing ~/.gconfig. + +%prep +%autosetup + +%build +make all + +%install +make install DESTDIR=%{buildroot} PREFIX=/usr/local + +%files +/usr/local/bin/gbuild +/usr/local/bin/gconfig + +%post +echo "gbuild @VERSION@ installed. Run: gconfig init" + +%changelog +* @RPM_DATE@ jokerz - @VERSION@-1 +- Initial C rewrite release diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..46b8b5f --- /dev/null +++ b/src/config.c @@ -0,0 +1,195 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------ helpers */ + +char *expand_tilde(const char *in, char *out, size_t n) +{ + if (!in || in[0] != '~') { + strncpy(out, in ? in : "", n - 1); + out[n - 1] = '\0'; + return out; + } + const char *home = getenv("HOME"); + if (!home) home = "/tmp"; + snprintf(out, n, "%s%s", home, in + 1); + return out; +} + +void config_get_path(char *out, size_t n) +{ + const char *home = getenv("HOME"); + if (!home) home = "/tmp"; + snprintf(out, n, "%s/.gconfig", home); +} + +/* ---------------------------------------------------------------- defaults */ + +void config_defaults(GConfig *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + strncpy(cfg->git_url, "http://localhost:3000", CFG_MAX - 1); + strncpy(cfg->git_user, "", sizeof(cfg->git_user) - 1); + cfg->log_enabled = true; + + const char *home = getenv("HOME"); + if (!home) home = "/tmp"; + snprintf(cfg->clone_dir, CFG_MAX, "%s/projects", home); + snprintf(cfg->log_dir, CFG_MAX, "%s/.local/log/gbuild", home); +} + +/* ------------------------------------------------------------------ loader */ + +static void strip(char *s) +{ + /* strip leading whitespace */ + char *p = s; + while (*p == ' ' || *p == '\t') p++; + if (p != s) memmove(s, p, strlen(p) + 1); + /* strip trailing whitespace / newline */ + size_t l = strlen(s); + while (l > 0 && (s[l-1] == '\n' || s[l-1] == '\r' || + s[l-1] == ' ' || s[l-1] == '\t')) + s[--l] = '\0'; +} + +int config_load(GConfig *cfg, const char *path) +{ + config_defaults(cfg); + + FILE *f = fopen(path, "r"); + if (!f) return -1; + + char line[512]; + char section[64] = ""; + + while (fgets(line, sizeof(line), f)) { + strip(line); + if (line[0] == '\0' || line[0] == '#') continue; + + /* section header */ + if (line[0] == '[') { + char *end = strchr(line, ']'); + if (end) { + *end = '\0'; + strncpy(section, line + 1, sizeof(section) - 1); + } + continue; + } + + /* key = value */ + char *eq = strchr(line, '='); + if (!eq) continue; + *eq = '\0'; + char *key = line; + char *val = eq + 1; + strip(key); + strip(val); + + char expanded[CFG_MAX]; + expand_tilde(val, expanded, sizeof(expanded)); + + if (!strcmp(key, "GIT_URL")) strncpy(cfg->git_url, expanded, CFG_MAX - 1); + else if (!strcmp(key, "GIT_USER")) strncpy(cfg->git_user, expanded, sizeof(cfg->git_user) - 1); + else if (!strcmp(key, "GIT_TOKEN")) strncpy(cfg->git_token, expanded, sizeof(cfg->git_token) - 1); + else if (!strcmp(key, "GIT_PASSWORD")) strncpy(cfg->git_password, expanded, sizeof(cfg->git_password) - 1); + else if (!strcmp(key, "DEFAULT_TARGET")) strncpy(cfg->default_target, expanded, sizeof(cfg->default_target) - 1); + else if (!strcmp(key, "CLONE_DIR")) strncpy(cfg->clone_dir, expanded, CFG_MAX - 1); + else if (!strcmp(key, "LOG_DIR")) strncpy(cfg->log_dir, expanded, CFG_MAX - 1); + else if (!strcmp(key, "LOG_ENABLED")) cfg->log_enabled = (strcmp(expanded, "false") != 0 && + strcmp(expanded, "0") != 0); + (void)section; + } + + fclose(f); + return 0; +} + +/* ------------------------------------------------------------------ saver */ + +int config_save(const GConfig *cfg, const char *path) +{ + FILE *f = fopen(path, "w"); + if (!f) return -1; + + fprintf(f, + "# ~/.gconfig — managed by gconfig\n\n" + "[git]\n" + "GIT_URL = %s\n" + "GIT_USER = %s\n\n" + "[auth]\n" + "# Use token-based OR password-based auth; leave the other blank\n" + "GIT_TOKEN = %s\n" + "GIT_PASSWORD = %s\n\n" + "[build]\n" + "DEFAULT_TARGET = %s\n" + "CLONE_DIR = %s\n\n" + "[log]\n" + "LOG_ENABLED = %s\n" + "LOG_DIR = %s\n", + cfg->git_url, + cfg->git_user, + cfg->git_token, + cfg->git_password, + cfg->default_target, + cfg->clone_dir, + cfg->log_enabled ? "true" : "false", + cfg->log_dir); + + fclose(f); + return 0; +} + +/* ------------------------------------------------------------------ init */ + +int config_init(const char *path, bool force) +{ + struct stat st; + if (!force && stat(path, &st) == 0) { + /* file exists and we're not forcing */ + return 1; /* already exists */ + } + + if (force && stat(path, &st) == 0) { + /* back up existing file */ + time_t now = time(NULL); + char backup[CFG_MAX + 32]; + snprintf(backup, sizeof(backup), "%s.bak.%ld", path, (long)now); + rename(path, backup); + printf("Backed up existing config to: %s\n", backup); + } + + GConfig cfg; + config_defaults(&cfg); + return config_save(&cfg, path); +} + +/* ------------------------------------------------------------------ show */ + +void config_show(const GConfig *cfg) +{ + const char *tok = cfg->git_token[0] ? "****" : "(not set)"; + const char *pass = cfg->git_password[0] ? "****" : "(not set)"; + + printf("\n [git]\n"); + printf(" GIT_URL = %s\n", cfg->git_url[0] ? cfg->git_url : "(not set)"); + printf(" GIT_USER = %s\n", cfg->git_user[0] ? cfg->git_user : "(not set)"); + + printf("\n [auth]\n"); + printf(" GIT_TOKEN = %s\n", tok); + printf(" GIT_PASSWORD = %s\n", pass); + + printf("\n [build]\n"); + printf(" DEFAULT_TARGET = %s\n", cfg->default_target[0] ? cfg->default_target : "(not set)"); + printf(" CLONE_DIR = %s\n", cfg->clone_dir); + + printf("\n [log]\n"); + printf(" LOG_ENABLED = %s\n", cfg->log_enabled ? "true" : "false"); + printf(" LOG_DIR = %s\n\n", cfg->log_dir); +} diff --git a/src/gbuild.c b/src/gbuild.c new file mode 100644 index 0000000..9bc8b8a --- /dev/null +++ b/src/gbuild.c @@ -0,0 +1,194 @@ +#include "config.h" +#include "git_ops.h" +#include "logger.h" +#include "make_ops.h" +#include "tui.h" + +#include +#include +#include +#include +#include + +#define VERSION "1.0.0" + +/* ----------------------------------------------------------------- usage */ + +static void print_usage(const char *prog) +{ + printf( + "gbuild %s — A build tool for your Linux distro\n\n" + "Usage:\n" + " %s [options] \n" + " %s --logs\n\n" + "Options:\n" + " --url Override Git base URL (default: ~/.gconfig)\n" + " --user Override Git username (default: ~/.gconfig)\n" + " --target Run target directly, skip interactive picker\n" + " --logs Open the interactive log browser\n" + " -h, --help Print this help and exit\n\n" + "Examples:\n" + " gbuild myproject\n" + " gbuild --target clean myproject\n" + " gbuild --url http://10.0.0.5:3000 --user alice myproject\n" + " gbuild --logs\n\n" + "Configuration is read from ~/.gconfig. Run 'gconfig init' to set it up.\n", + VERSION, prog, prog); +} + +/* ----------------------------------------------------------------- main */ + +int main(int argc, char *argv[]) +{ + /* ---- parse arguments ---- */ + const char *arg_url = NULL; + const char *arg_user = NULL; + const char *arg_target = NULL; + const char *arg_project = NULL; + int flag_logs = 0; + + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { + print_usage(argv[0]); + return 0; + } else if (!strcmp(argv[i], "--url")) { + if (++i >= argc) { fprintf(stderr, "error: --url requires a value\n"); return 1; } + arg_url = argv[i]; + } else if (!strcmp(argv[i], "--user")) { + if (++i >= argc) { fprintf(stderr, "error: --user requires a value\n"); return 1; } + arg_user = argv[i]; + } else if (!strcmp(argv[i], "--target")) { + if (++i >= argc) { fprintf(stderr, "error: --target requires a value\n"); return 1; } + arg_target = argv[i]; + } else if (!strcmp(argv[i], "--logs")) { + flag_logs = 1; + } else if (argv[i][0] == '-') { + fprintf(stderr, "error: unknown flag '%s'\n", argv[i]); + return 1; + } else { + if (arg_project) { + fprintf(stderr, "error: unexpected argument '%s'\n", argv[i]); + return 1; + } + arg_project = argv[i]; + } + } + + /* ---- load config ---- */ + GConfig cfg; + char cfg_path[512]; + config_get_path(cfg_path, sizeof(cfg_path)); + + if (config_load(&cfg, cfg_path) != 0) { + fprintf(stderr, + "[WARN ] ~/.gconfig not found — using built-in defaults.\n" + " Run 'gconfig init' to create one.\n"); + config_defaults(&cfg); + } + + /* apply overrides */ + if (arg_url) strncpy(cfg.git_url, arg_url, CFG_MAX - 1); + if (arg_user) strncpy(cfg.git_user, arg_user, sizeof(cfg.git_user) - 1); + + /* expand paths that might contain ~ */ + char clone_dir[CFG_MAX], log_dir[CFG_MAX]; + expand_tilde(cfg.clone_dir, clone_dir, sizeof(clone_dir)); + expand_tilde(cfg.log_dir, log_dir, sizeof(log_dir)); + + /* ---- log browser mode ---- */ + if (flag_logs) { + tui_log_browser(log_dir); + return 0; + } + + /* ---- need a project name ---- */ + if (!arg_project) { + fprintf(stderr, "error: no project specified.\n\n"); + print_usage(argv[0]); + return 1; + } + + /* ---- open logger ---- */ + Logger log; + memset(&log, 0, sizeof(log)); + if (cfg.log_enabled) { + if (logger_open(&log, log_dir, arg_project) == 0) + logger_info(&log, "Logging to: %s", log.path); + else + fprintf(stderr, "[WARN ] Could not open log file in %s\n", log_dir); + } + + logger_info(&log, "gbuild started for project: %s", arg_project); + + /* ---- clone or pull ---- */ + char repo_path[CFG_MAX * 2]; + snprintf(repo_path, sizeof(repo_path), "%s/%s", clone_dir, arg_project); + + if (git_repo_exists(clone_dir, arg_project)) { + logger_info(&log, "Repo already cloned — pulling latest changes ..."); + int rc = git_pull(repo_path, &log); + if (rc != 0) { + logger_error(&log, "git pull failed (exit %d).", rc); + logger_close(&log); + return 1; + } + logger_ok(&log, "Repository updated."); + } else { + logger_info(&log, "Cloning %s/%s/%s ...", + cfg.git_url, cfg.git_user, arg_project); + int rc = git_clone(cfg.git_url, cfg.git_user, + cfg.git_token, cfg.git_password, + arg_project, clone_dir, &log); + if (rc != 0) { + logger_error(&log, "git clone failed (exit %d).", rc); + logger_close(&log); + return 1; + } + logger_ok(&log, "Repository cloned."); + } + + /* ---- resolve make target ---- */ + char target[TARGET_NAME]; + memset(target, 0, sizeof(target)); + + if (arg_target) { + /* explicit --target flag */ + strncpy(target, arg_target, sizeof(target) - 1); + logger_info(&log, "Selected target: %s", target); + } else if (cfg.default_target[0]) { + /* default from config */ + strncpy(target, cfg.default_target, sizeof(target) - 1); + logger_info(&log, "Using default target: %s", target); + } else { + /* interactive TUI picker */ + MakeTargets targets; + if (make_parse_targets(repo_path, &targets) != 0) { + logger_warn(&log, + "No targets found in Makefile — running 'make' with no target."); + target[0] = '\0'; + } else { + if (tui_pick_target(&targets, target, sizeof(target)) < 0) { + logger_warn(&log, "No target selected — aborting."); + logger_close(&log); + return 0; + } + } + logger_info(&log, "Selected target: %s", + target[0] ? target : "(default)"); + } + + /* ---- build ---- */ + logger_info(&log, "gbuild: building '%s' target '%s' ...", + arg_project, target[0] ? target : "(default)"); + + int rc = make_build(repo_path, target, &log); + if (rc != 0) { + logger_error(&log, "Build failed (exit %d).", rc); + logger_close(&log); + return 1; + } + + logger_ok(&log, "gbuild complete."); + logger_close(&log); + return 0; +} diff --git a/src/gconfig.c b/src/gconfig.c new file mode 100644 index 0000000..eb548ce --- /dev/null +++ b/src/gconfig.c @@ -0,0 +1,91 @@ +#include "config.h" + +#include +#include +#include +#include + +#define VERSION "1.0.0" + +/* ----------------------------------------------------------------- usage */ + +static void print_usage(void) +{ + printf( + "gconfig %s — Manage ~/.gconfig for gbuild\n\n" + "Usage:\n" + " gconfig [flags]\n\n" + "Commands:\n" + " init Create ~/.gconfig with defaults (skips if already exists)\n" + " init --force Overwrite existing config; backs up the old file first\n" + " show Print current config values (auth fields masked)\n" + " help Print this help and exit\n\n" + "Quick setup:\n" + " gconfig init\n" + " $EDITOR ~/.gconfig\n" + " gconfig show\n", + VERSION); +} + +/* ----------------------------------------------------------------- main */ + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + print_usage(); + return 1; + } + + char cfg_path[512]; + config_get_path(cfg_path, sizeof(cfg_path)); + + const char *cmd = argv[1]; + + /* ---- help ---- */ + if (!strcmp(cmd, "help") || !strcmp(cmd, "-h") || !strcmp(cmd, "--help")) { + print_usage(); + return 0; + } + + /* ---- init ---- */ + if (!strcmp(cmd, "init")) { + int force = 0; + for (int i = 2; i < argc; i++) { + if (!strcmp(argv[i], "--force") || !strcmp(argv[i], "-f")) + force = 1; + } + + int rc = config_init(cfg_path, force); + if (rc < 0) { + fprintf(stderr, "error: could not write %s: ", cfg_path); + perror(NULL); + return 1; + } + if (rc == 1) { + printf("~/.gconfig already exists. Use 'gconfig init --force' to overwrite.\n"); + return 0; + } + printf("Created %s with default values.\n", cfg_path); + printf("Edit it with: $EDITOR %s\n", cfg_path); + printf("Verify with: gconfig show\n"); + return 0; + } + + /* ---- show ---- */ + if (!strcmp(cmd, "show")) { + GConfig cfg; + if (config_load(&cfg, cfg_path) != 0) { + fprintf(stderr, + "error: could not read %s\n" + "Run 'gconfig init' to create it.\n", cfg_path); + return 1; + } + printf("\n%s\n", cfg_path); + config_show(&cfg); + return 0; + } + + fprintf(stderr, "error: unknown command '%s'\n\n", cmd); + print_usage(); + return 1; +} diff --git a/src/git_ops.c b/src/git_ops.c new file mode 100644 index 0000000..4923c3d --- /dev/null +++ b/src/git_ops.c @@ -0,0 +1,125 @@ +#include "git_ops.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------- helpers */ + +/* + * Run `cmd`, streaming output to stdout and log simultaneously. + * Returns the exit status of the child process. + */ +static int run_stream(const char *cmd, Logger *log) +{ + FILE *pipe = popen(cmd, "r"); + if (!pipe) { + logger_error(log, "popen failed: %s", strerror(errno)); + return -1; + } + + char line[1024]; + while (fgets(line, sizeof(line), pipe)) { + /* strip trailing newline for logger_raw so it adds its own */ + size_t l = strlen(line); + if (l > 0 && line[l-1] == '\n') line[l-1] = '\0'; + logger_raw(log, "%s\n", line); + } + + int status = pclose(pipe); + return (status == -1) ? -1 : WEXITSTATUS(status); +} + +/* + * Inject credentials into a URL of the form scheme://host/path + * producing scheme://user:auth@host/path. + * + * If both token and password are empty the URL is left unmodified + * (useful for public repos). + */ +static void build_auth_url(char *out, size_t n, + const char *base_url, const char *user, + const char *token, const char *password, + const char *project) +{ + const char *auth = (token && token[0]) ? token : + (password && password[0]) ? password : NULL; + + /* find end of scheme:// */ + const char *sep = strstr(base_url, "://"); + if (!sep) { + /* fallback: treat whole thing as host */ + if (auth) + snprintf(out, n, "http://%s:%s@%s/%s/%s", + user, auth, base_url, user, project); + else + snprintf(out, n, "http://%s/%s/%s", base_url, user, project); + return; + } + + /* scheme = base_url[0 .. sep+3) */ + char scheme[16] = {0}; + size_t slen = (size_t)(sep + 3 - base_url); + if (slen >= sizeof(scheme)) slen = sizeof(scheme) - 1; + strncpy(scheme, base_url, slen); + + const char *host = base_url + slen; + + /* strip trailing slash from host fragment */ + char hostbuf[512]; + strncpy(hostbuf, host, sizeof(hostbuf) - 1); + hostbuf[sizeof(hostbuf) - 1] = '\0'; + size_t hl = strlen(hostbuf); + while (hl > 0 && hostbuf[hl-1] == '/') hostbuf[--hl] = '\0'; + + if (auth && user && user[0]) + snprintf(out, n, "%s%s:%s@%s/%s/%s", + scheme, user, auth, hostbuf, user, project); + else if (user && user[0]) + snprintf(out, n, "%s%s@%s/%s/%s", + scheme, user, hostbuf, user, project); + else + snprintf(out, n, "%s%s/%s/%s", scheme, hostbuf, user, project); +} + +/* --------------------------------------------------------- public API */ + +int git_repo_exists(const char *clone_dir, const char *project) +{ + char git_path[CFG_MAX * 2]; + snprintf(git_path, sizeof(git_path), "%s/%s/.git", clone_dir, project); + struct stat st; + return (stat(git_path, &st) == 0 && S_ISDIR(st.st_mode)) ? 1 : 0; +} + +int git_clone(const char *base_url, const char *user, + const char *token, const char *password, + const char *project, const char *clone_dir, + Logger *log) +{ + char url[CFG_MAX * 2]; + build_auth_url(url, sizeof(url), + base_url, user, token, password, project); + + /* NOTE: credentials appear in the URL which git masks in its own output, + * but they may briefly be visible in /proc//cmdline. + * For a higher-security setup, consider GIT_ASKPASS instead. */ + char cmd[CFG_MAX * 4]; + snprintf(cmd, sizeof(cmd), + "git clone \"%s\" \"%s/%s\" 2>&1", + url, clone_dir, project); + + return run_stream(cmd, log); +} + +int git_pull(const char *repo_path, Logger *log) +{ + char cmd[CFG_MAX * 2]; + snprintf(cmd, sizeof(cmd), + "git -C \"%s\" pull 2>&1", repo_path); + return run_stream(cmd, log); +} diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..de2eead --- /dev/null +++ b/src/logger.c @@ -0,0 +1,158 @@ +#include "logger.h" + +#include +#include +#include +#include +#include +#include +#include + +/* ANSI colours */ +#define COL_RESET "\033[0m" +#define COL_CYAN "\033[36m" +#define COL_GREEN "\033[32m" +#define COL_YELLOW "\033[33m" +#define COL_RED "\033[31m" +#define COL_BOLD "\033[1m" + +/* ---------------------------------------------------------------- helpers */ + +static void mkdirs(const char *path) +{ + char tmp[512]; + strncpy(tmp, path, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + for (char *p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + mkdir(tmp, 0755); + *p = '/'; + } + } + mkdir(tmp, 0755); +} + +/* ------------------------------------------------------------------ open */ + +int logger_open(Logger *log, const char *log_dir, const char *project) +{ + memset(log, 0, sizeof(*log)); + + mkdirs(log_dir); + + time_t now = time(NULL); + struct tm *t = localtime(&now); + char ts[32]; + strftime(ts, sizeof(ts), "%Y%m%d_%H%M%S", t); + + snprintf(log->path, sizeof(log->path), + "%s/%s_%s.log", log_dir, ts, project); + + log->fp = fopen(log->path, "w"); + if (!log->fp) return -1; + + log->enabled = true; + return 0; +} + +void logger_close(Logger *log) +{ + if (log->fp) { + fclose(log->fp); + log->fp = NULL; + } +} + +/* ----------------------------------------------------------------- write */ + +typedef enum { LVL_INFO, LVL_OK, LVL_WARN, LVL_ERROR } LogLevel; + +static const char *level_tag(LogLevel lv) +{ + switch (lv) { + case LVL_INFO: return "[INFO ]"; + case LVL_OK: return "[OK ]"; + case LVL_WARN: return "[WARN ]"; + case LVL_ERROR: return "[ERROR]"; + } + return "[ ]"; +} + +static const char *level_color(LogLevel lv) +{ + switch (lv) { + case LVL_INFO: return COL_CYAN; + case LVL_OK: return COL_GREEN COL_BOLD; + case LVL_WARN: return COL_YELLOW; + case LVL_ERROR: return COL_RED COL_BOLD; + } + return ""; +} + +static void write_level(Logger *log, LogLevel lv, const char *fmt, va_list ap) +{ + char msg[2048]; + vsnprintf(msg, sizeof(msg), fmt, ap); + + /* stdout — coloured */ + printf("%s%s%s %s\n", level_color(lv), level_tag(lv), COL_RESET, msg); + fflush(stdout); + + /* log file — plain */ + if (log && log->fp) { + /* prepend a timestamp in the file */ + time_t now = time(NULL); + struct tm *t = localtime(&now); + char ts[32]; + strftime(ts, sizeof(ts), "%H:%M:%S", t); + fprintf(log->fp, "%s %s %s\n", ts, level_tag(lv), msg); + fflush(log->fp); + } +} + +void logger_info(Logger *log, const char *fmt, ...) +{ + va_list ap; va_start(ap, fmt); + write_level(log, LVL_INFO, fmt, ap); + va_end(ap); +} + +void logger_ok(Logger *log, const char *fmt, ...) +{ + va_list ap; va_start(ap, fmt); + write_level(log, LVL_OK, fmt, ap); + va_end(ap); +} + +void logger_warn(Logger *log, const char *fmt, ...) +{ + va_list ap; va_start(ap, fmt); + write_level(log, LVL_WARN, fmt, ap); + va_end(ap); +} + +void logger_error(Logger *log, const char *fmt, ...) +{ + va_list ap; va_start(ap, fmt); + write_level(log, LVL_ERROR, fmt, ap); + va_end(ap); +} + +void logger_raw(Logger *log, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + fflush(stdout); + + if (log && log->fp) { + va_start(ap, fmt); + vfprintf(log->fp, fmt, ap); + va_end(ap); + fflush(log->fp); + } +} diff --git a/src/make_ops.c b/src/make_ops.c new file mode 100644 index 0000000..156228e --- /dev/null +++ b/src/make_ops.c @@ -0,0 +1,128 @@ +#include "make_ops.h" + +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------ helpers */ + +/* Special make targets we want to skip in the picker */ +static const char *SKIP_TARGETS[] = { + ".PHONY", ".SUFFIXES", ".DEFAULT", ".PRECIOUS", + ".INTERMEDIATE", ".SECONDARY", ".NOTPARALLEL", + ".DELETE_ON_ERROR", ".IGNORE", ".LOW_RESOLUTION_TIME", + ".SILENT", ".EXPORT_ALL_VARIABLES", ".ONESHELL", + NULL +}; + +static int is_special(const char *name) +{ + for (int i = 0; SKIP_TARGETS[i]; i++) + if (!strcmp(name, SKIP_TARGETS[i])) return 1; + return 0; +} + +static int is_valid_target_char(char c) +{ + return isalnum((unsigned char)c) || c == '_' || c == '-' || c == '.'; +} + +/* -------------------------------------------------------- parser */ + +int make_parse_targets(const char *repo_path, MakeTargets *out) +{ + /* Try Makefile then makefile */ + char mf_path[1024]; + FILE *f = NULL; + + snprintf(mf_path, sizeof(mf_path), "%s/Makefile", repo_path); + f = fopen(mf_path, "r"); + if (!f) { + snprintf(mf_path, sizeof(mf_path), "%s/makefile", repo_path); + f = fopen(mf_path, "r"); + } + if (!f) return -1; + + out->count = 0; + + char line[2048]; + while (fgets(line, sizeof(line), f) && out->count < MAX_TARGETS) { + /* skip comment lines and recipe lines (start with tab) */ + if (line[0] == '#' || line[0] == '\t' || line[0] == '\n') continue; + + /* find first ':' */ + char *colon = strchr(line, ':'); + if (!colon) continue; + + /* must not be := += ?= (variable assignment) */ + if (colon > line && (*(colon-1) == ':' || + *(colon-1) == '+' || + *(colon-1) == '?')) continue; + + /* extract the name part before ':' */ + size_t name_len = (size_t)(colon - line); + if (name_len == 0 || name_len >= TARGET_NAME) continue; + + char name[TARGET_NAME]; + strncpy(name, line, name_len); + name[name_len] = '\0'; + + /* strip surrounding whitespace */ + /* (shouldn't have leading whitespace since we skipped tabs, + * but strip trailing) */ + while (name_len > 0 && isspace((unsigned char)name[name_len - 1])) + name[--name_len] = '\0'; + + if (name_len == 0) continue; + + /* skip if name contains non-target chars (%, =, space) */ + int valid = 1; + for (size_t i = 0; i < name_len; i++) { + if (!is_valid_target_char(name[i])) { valid = 0; break; } + } + if (!valid) continue; + + /* skip special targets */ + if (is_special(name)) continue; + + /* skip duplicates */ + int dup = 0; + for (int i = 0; i < out->count; i++) + if (!strcmp(out->names[i], name)) { dup = 1; break; } + if (dup) continue; + + strncpy(out->names[out->count], name, TARGET_NAME - 1); + out->names[out->count][TARGET_NAME - 1] = '\0'; + out->count++; + } + + fclose(f); + return (out->count > 0) ? 0 : -1; +} + +/* -------------------------------------------------------- build */ + +int make_build(const char *repo_path, const char *target, Logger *log) +{ + char cmd[2048]; + snprintf(cmd, sizeof(cmd), + "make -C \"%s\" %s 2>&1", repo_path, target); + + FILE *pipe = popen(cmd, "r"); + if (!pipe) { + logger_error(log, "popen failed: %s", strerror(errno)); + return -1; + } + + char line[1024]; + while (fgets(line, sizeof(line), pipe)) { + size_t l = strlen(line); + if (l > 0 && line[l-1] == '\n') line[l-1] = '\0'; + logger_raw(log, "%s\n", line); + } + + int status = pclose(pipe); + return (status == -1) ? -1 : WEXITSTATUS(status); +} diff --git a/src/tui.c b/src/tui.c new file mode 100644 index 0000000..2498c6f --- /dev/null +++ b/src/tui.c @@ -0,0 +1,429 @@ +#include "tui.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* ================================================================ + * SECTION 1 — TARGET PICKER + * ================================================================ */ + +#define PICKER_MIN_W 40 +#define PICKER_MIN_H 8 + +int tui_pick_target(const MakeTargets *targets, char *selected, size_t sel_size) +{ + if (!targets || targets->count == 0) return -1; + + initscr(); + noecho(); + cbreak(); + keypad(stdscr, TRUE); + curs_set(0); + + if (has_colors()) { + start_color(); + use_default_colors(); + init_pair(1, COLOR_BLACK, COLOR_CYAN); /* selected row */ + init_pair(2, COLOR_CYAN, -1); /* border colour */ + init_pair(3, COLOR_WHITE, -1); /* normal row */ + } + + int rows, cols; + getmaxyx(stdscr, rows, cols); + + int list_h = targets->count + 2; /* +2 for border */ + int list_w = PICKER_MIN_W; + + /* find longest target name and widen accordingly */ + for (int i = 0; i < targets->count; i++) { + int l = (int)strlen(targets->names[i]) + 4; + if (l > list_w) list_w = l; + } + if (list_w > cols - 4) list_w = cols - 4; + if (list_h > rows - 4) list_h = rows - 4; + + int win_y = (rows - list_h) / 2; + int win_x = (cols - list_w) / 2; + + /* instruction line above the box */ + int cur = 0; + int scroll_offset = 0; + int visible = list_h - 2; /* rows inside border */ + + WINDOW *win = newwin(list_h, list_w, win_y, win_x); + + /* header hint */ + attron(A_DIM); + mvprintw(win_y - 2, win_x, + "Select make target K - UP J - DOWN to navigate Enter - select \u00b7 q - cancel"); + attroff(A_DIM); + refresh(); + + int done = 0; + int result = -1; + + while (!done) { + werase(win); + if (has_colors()) wattron(win, COLOR_PAIR(2)); + box(win, 0, 0); + if (has_colors()) wattroff(win, COLOR_PAIR(2)); + + for (int i = 0; i < visible && (i + scroll_offset) < targets->count; i++) { + int idx = i + scroll_offset; + if (idx == cur) { + if (has_colors()) wattron(win, COLOR_PAIR(1) | A_BOLD); + else wattron(win, A_REVERSE); + mvwprintw(win, i + 1, 1, " %-*s ", + list_w - 4, targets->names[idx]); + if (has_colors()) wattroff(win, COLOR_PAIR(1) | A_BOLD); + else wattroff(win, A_REVERSE); + } else { + if (has_colors()) wattron(win, COLOR_PAIR(3)); + mvwprintw(win, i + 1, 1, " %-*s", + list_w - 4, targets->names[idx]); + if (has_colors()) wattroff(win, COLOR_PAIR(3)); + } + } + + wrefresh(win); + + int ch = wgetch(win); + switch (ch) { + case KEY_UP: + case 'k': + if (cur > 0) { + cur--; + if (cur < scroll_offset) scroll_offset = cur; + } + break; + case KEY_DOWN: + case 'j': + if (cur < targets->count - 1) { + cur++; + if (cur >= scroll_offset + visible) + scroll_offset = cur - visible + 1; + } + break; + case '\n': + case '\r': + case KEY_ENTER: + strncpy(selected, targets->names[cur], sel_size - 1); + selected[sel_size - 1] = '\0'; + result = cur; + done = 1; + break; + case 'q': + case 27: /* ESC */ + done = 1; + break; + } + } + + delwin(win); + endwin(); + return result; +} + +/* ================================================================ + * SECTION 2 — LOG BROWSER + * ================================================================ */ + +#define MAX_LOGS 512 + +typedef struct { + char name[256]; /* filename only */ + char path[768]; /* full path */ + time_t mtime; +} LogEntry; + +static int log_entry_cmp(const void *a, const void *b) +{ + /* newest first */ + const LogEntry *la = (const LogEntry *)a; + const LogEntry *lb = (const LogEntry *)b; + if (lb->mtime > la->mtime) return 1; + if (lb->mtime < la->mtime) return -1; + return 0; +} + +static int load_log_entries(const char *log_dir, + LogEntry *entries, int max) +{ + DIR *d = opendir(log_dir); + if (!d) return 0; + + int count = 0; + struct dirent *de; + while ((de = readdir(d)) && count < max) { + if (de->d_name[0] == '.') continue; + /* only .log files */ + const char *dot = strrchr(de->d_name, '.'); + if (!dot || strcmp(dot, ".log")) continue; + + strncpy(entries[count].name, de->d_name, + sizeof(entries[count].name) - 1); + snprintf(entries[count].path, sizeof(entries[count].path), + "%s/%s", log_dir, de->d_name); + + struct stat st; + if (stat(entries[count].path, &st) == 0) + entries[count].mtime = st.st_mtime; + else + entries[count].mtime = 0; + count++; + } + closedir(d); + + qsort(entries, count, sizeof(LogEntry), log_entry_cmp); + return count; +} + +/* + * Read up to `max_lines` trailing lines from `path` into `lines`. + * Returns the number of lines read. + * Caller owns the memory and should free each pointer. + */ +#define PREVIEW_MAX 128 + +static int read_tail(const char *path, char **lines, int max_lines) +{ + FILE *f = fopen(path, "r"); + if (!f) return 0; + + /* Circular buffer approach */ + char **buf = calloc(max_lines, sizeof(char *)); + if (!buf) { fclose(f); return 0; } + + char tmp[1024]; + int idx = 0, total = 0; + while (fgets(tmp, sizeof(tmp), f)) { + /* strip newline */ + size_t l = strlen(tmp); + if (l > 0 && tmp[l-1] == '\n') tmp[l-1] = '\0'; + + free(buf[idx % max_lines]); + buf[idx % max_lines] = strdup(tmp); + idx++; + total++; + } + fclose(f); + + int have = (total < max_lines) ? total : max_lines; + int start = (total >= max_lines) ? (idx % max_lines) : 0; + + for (int i = 0; i < have; i++) + lines[i] = buf[(start + i) % max_lines]; + + /* free slots that weren't returned */ + for (int i = have; i < max_lines; i++) { + free(buf[(start + i) % max_lines]); + buf[(start + i) % max_lines] = NULL; + } + free(buf); + return have; +} + +/* ---------------------------------------------------------------- browser */ + +void tui_log_browser(const char *log_dir) +{ + LogEntry *entries = calloc(MAX_LOGS, sizeof(LogEntry)); + if (!entries) return; + + int count = load_log_entries(log_dir, entries, MAX_LOGS); + + initscr(); + noecho(); + cbreak(); + keypad(stdscr, TRUE); + curs_set(0); + + if (has_colors()) { + start_color(); + use_default_colors(); + init_pair(1, COLOR_BLACK, COLOR_CYAN); /* selected row */ + init_pair(2, COLOR_CYAN, -1); /* border */ + init_pair(3, COLOR_WHITE, -1); /* normal row */ + init_pair(4, COLOR_YELLOW,-1); /* preview text */ + init_pair(5, COLOR_RED, -1); /* status/warn */ + } + + int rows, cols; + getmaxyx(stdscr, rows, cols); + + /* layout: left pane = 40% width, right pane = rest */ + int left_w = cols * 40 / 100; + if (left_w < 30) left_w = 30; + int right_w = cols - left_w - 1; /* -1 for divider */ + int pane_h = rows - 3; /* -3 for title + hint bar */ + + WINDOW *wleft = newwin(pane_h, left_w, 1, 0); + WINDOW *wright = newwin(pane_h, right_w, 1, left_w + 1); + + int cur = 0, scroll = 0; + int done = 0; + + while (!done) { + getmaxyx(stdscr, rows, cols); + pane_h = rows - 3; + + /* ---- title bar ---- */ + attron(A_BOLD); + mvprintw(0, 2, "gbuild log browser"); + attroff(A_BOLD); + clrtoeol(); + + /* ---- hint bar ---- */ + if (has_colors()) attron(COLOR_PAIR(2)); + mvprintw(rows - 2, 0, + " \u2191\u2193 navigate Enter open in less d delete q quit " + " "); + if (has_colors()) attroff(COLOR_PAIR(2)); + + /* ---- vertical divider ---- */ + for (int y = 1; y < rows - 2; y++) + mvaddch(y, left_w, ACS_VLINE); + + /* ---- left pane: file list ---- */ + werase(wleft); + if (has_colors()) wattron(wleft, COLOR_PAIR(2)); + box(wleft, 0, 0); + if (has_colors()) wattroff(wleft, COLOR_PAIR(2)); + + int visible = pane_h - 2; + if (visible < 1) visible = 1; + + if (count == 0) { + mvwprintw(wleft, 2, 2, "(no logs found)"); + } else { + for (int i = 0; i < visible && (i + scroll) < count; i++) { + int idx = i + scroll; + /* trim the .log extension for display */ + char disp[256]; + strncpy(disp, entries[idx].name, sizeof(disp) - 1); + char *dot = strrchr(disp, '.'); + if (dot) *dot = '\0'; + + if (idx == cur) { + if (has_colors()) wattron(wleft, COLOR_PAIR(1) | A_BOLD); + else wattron(wleft, A_REVERSE); + mvwprintw(wleft, i + 1, 1, " %-*s ", + left_w - 4, disp); + if (has_colors()) wattroff(wleft, COLOR_PAIR(1) | A_BOLD); + else wattroff(wleft, A_REVERSE); + } else { + mvwprintw(wleft, i + 1, 2, "%-*s", + left_w - 4, disp); + } + } + } + wrefresh(wleft); + + /* ---- right pane: preview ---- */ + werase(wright); + if (has_colors()) wattron(wright, COLOR_PAIR(2)); + box(wright, 0, 0); + if (has_colors()) wattroff(wright, COLOR_PAIR(2)); + + if (count > 0 && cur < count) { + int preview_lines = pane_h - 2; + char **lines = calloc(preview_lines, sizeof(char *)); + if (lines) { + int n = read_tail(entries[cur].path, lines, preview_lines); + if (has_colors()) wattron(wright, COLOR_PAIR(4)); + for (int i = 0; i < n; i++) { + if (lines[i]) { + mvwprintw(wright, i + 1, 1, "%-*.*s", + right_w - 2, right_w - 2, lines[i]); + free(lines[i]); + } + } + if (has_colors()) wattroff(wright, COLOR_PAIR(4)); + free(lines); + } + } else { + mvwprintw(wright, 2, 2, "(no log selected)"); + } + wrefresh(wright); + refresh(); + + /* ---- input ---- */ + int ch = getch(); + switch (ch) { + case KEY_UP: + case 'k': + if (cur > 0) { + cur--; + if (cur < scroll) scroll = cur; + } + break; + case KEY_DOWN: + case 'j': + if (cur < count - 1) { + cur++; + if (cur >= scroll + visible) + scroll = cur - visible + 1; + } + break; + case '\n': + case '\r': + case KEY_ENTER: + if (count > 0 && cur < count) { + endwin(); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), + "less \"%s\"", entries[cur].path); + system(cmd); + /* re-init after less exits */ + initscr(); + noecho(); + cbreak(); + keypad(stdscr, TRUE); + curs_set(0); + if (has_colors()) { + start_color(); + use_default_colors(); + init_pair(1, COLOR_BLACK, COLOR_CYAN); + init_pair(2, COLOR_CYAN, -1); + init_pair(3, COLOR_WHITE, -1); + init_pair(4, COLOR_YELLOW,-1); + init_pair(5, COLOR_RED, -1); + } + clear(); + } + break; + case 'd': + if (count > 0 && cur < count) { + /* confirm */ + mvprintw(rows - 1, 0, + "Delete %s? [y/N] ", entries[cur].name); + refresh(); + int confirm = getch(); + if (confirm == 'y' || confirm == 'Y') { + remove(entries[cur].path); + /* reload list */ + count = load_log_entries(log_dir, entries, MAX_LOGS); + if (cur >= count) cur = count > 0 ? count - 1 : 0; + if (scroll > cur) scroll = cur; + } + clear(); + } + break; + case 'q': + case 27: /* ESC */ + done = 1; + break; + } + } + + delwin(wleft); + delwin(wright); + endwin(); + free(entries); +}