├── .editorconfig ├── ISSUE_TEMPLATE.md ├── LICENSE ├── Makefile ├── README.md ├── completions ├── gti.bash └── gti.zsh ├── gti.6 └── gti.c /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 4 8 | charset = "utf-8" 9 | trim_trailing_whitespace = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hey, I just wanted to say everything works fantastically. 2 | 3 | Not sure why I even opened this issue. 4 | 5 | 6 | I'm using `gti` on 7 | * [ ] HP-UX running on a 68k 8 | * [ ] NetBSD, with output piped to a slice of toast 9 | * [ ] an unreleased beta version of Windows NT 4.0 with a cmd.exe replacement from ReactOS 10 | 11 | 12 | and it 13 | * [ ] set fire to my line printer 14 | * [ ] hard-linked `/bin/emacs` to`/bin/vi` 15 | * [ ] crashes when run backwards in my home-grown CPU emulator 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Richard Wossal and contributors. 2 | 3 | Permission to use, copy, modify, distribute, and sell this software 4 | and its documentation for any purpose is hereby granted without fee, 5 | provided that the above copyright notice appear in all copies and 6 | that both that copyright notice and this permission notice appear in 7 | supporting documentation. No representations are made about the 8 | suitability of this software for any purpose. It is provided "as 9 | is" without express or implied warranty. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/sh 2 | 3 | # DESTDIR is used in a non-standard way for compatibility 4 | prefix=$(DESTDIR)/usr 5 | 6 | CC=cc 7 | CFLAGS+=-O2 -std=c89 -Wpedantic -Wall -Wextra -Wunused -Wshadow -Wdouble-promotion -Wstrict-overflow=5 8 | 9 | INSTALL=install 10 | INSTALL_DATA=$(INSTALL) -m 644 11 | 12 | bindir=$(prefix)/bin 13 | datarootdir=$(prefix)/share 14 | mandir=$(datarootdir)/man 15 | man6dir=$(mandir)/man6 16 | BASHCOMPLDIR=$(datarootdir)/bash-completion/completions 17 | ZSHCOMPLDIR=$(datarootdir)/zsh/site-functions 18 | DIRS=$(prefix) $(bindir) $(datarootdir) $(mandir) $(man6dir) $(BASHCOMPLDIR) $(ZSHCOMPLDIR) 19 | 20 | STRIP=strip 21 | ifeq ($(OS),Windows_NT) 22 | X = .exe 23 | else 24 | ifeq ($(shell uname), SunOS) 25 | STRIP=gstrip 26 | else ifeq ($(shell uname), Darwin) 27 | STRIP=echo 28 | endif 29 | endif 30 | 31 | PROG=gti$X 32 | MANPAGE=gti.6.gz 33 | 34 | $(PROG): *.c 35 | $(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ 36 | -$(STRIP) -s $@ 37 | 38 | $(MANPAGE): gti.6 39 | gzip -9 -n -c gti.6 > gti.6.gz 40 | 41 | $(DIRS): 42 | mkdir -p "$@" 43 | 44 | install: $(PROG) $(MANPAGE) $(DIRS) 45 | $(INSTALL) $(PROG) $(bindir)/$(PROG) 46 | $(INSTALL_DATA) $(MANPAGE) $(man6dir)/$(MANPAGE) 47 | $(INSTALL_DATA) completions/gti.bash $(BASHCOMPLDIR)/$(PROG) 48 | $(INSTALL_DATA) completions/gti.zsh $(ZSHCOMPLDIR)/_$(PROG) 49 | 50 | uninstall: 51 | rm -f $(bindir)/$(PROG) 52 | rm -f $(man6dir)/$(MANPAGE) 53 | 54 | fmt: *.c 55 | VERSION_CONTROL=never indent -kr -i4 -ppi4 -nut -l100 -cp0 -ncs -sob \ 56 | -T HANDLE \ 57 | $^ 58 | 59 | .PHONY: clean install uninstall 60 | clean: 61 | rm -f $(PROG) 62 | rm -f $(MANPAGE) 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gti 2 | === 3 | 4 | Just a silly `git` launcher, basically. Inspired by `sl`. 5 | 6 | Build and Install 7 | ----------------- 8 | 9 | From a package system: 10 | * [Fedora](https://src.fedoraproject.org/rpms/gti) 11 | * it's in ArchLinux' AUR as [gti-git](https://aur.archlinux.org/packages/gti-git/) and as [gti](https://aur.archlinux.org/packages/gti/) 12 | * [Gentoo](https://packages.gentoo.org/packages/dev-vcs/gti) 13 | * [Nix/NixOS](https://search.nixos.org/packages?show=gti&query=gti) 14 | * [FreeBSD ports](http://svnweb.freebsd.org/ports/head/games/gti/) 15 | * [OpenBSD ports](http://openports.se/games/gti) 16 | * [Homebrew/MacOS X](https://formulae.brew.sh/formula/gti#default) 17 | * [Cygwin](https://cygwin.com/packages/summary/gti.html) 18 | 19 | From source: 20 | 21 | $ make 22 | $ make install # as root, probably 23 | 24 | or 25 | 26 | $ make prefix=~/.local install # as a user 27 | 28 | The default install *prefix* is `/usr/bin`. 29 | 30 | You can change the speed of the car at runtime via `GTI_SPEED`. 31 | For example: 32 | 33 | $ GTI_SPEED=2000 gti push # default is 1000 34 | 35 | Usage 36 | ----- 37 | 38 | Try typing `git` really fast, on an unfamiliar keyboard. 39 | 40 | Author and License 41 | ------------------ 42 | 43 | Copyright 2012 Richard Wossal and contributors. 44 | 45 | Permission to use, copy, modify, distribute, and sell this software 46 | and its documentation for any purpose is hereby granted without fee, 47 | provided that the above copyright notice appear in all copies and 48 | that both that copyright notice and this permission notice appear in 49 | supporting documentation. No representations are made about the 50 | suitability of this software for any purpose. It is provided "as 51 | is" without express or implied warranty. 52 | 53 | -------------------------------------------------------------------------------- /completions/gti.bash: -------------------------------------------------------------------------------- 1 | # bash completion for gti -*- shell-script -*- 2 | 3 | # We assume that git is installed and its autocompletion script is nearby 4 | ___gti_complete() { 5 | local PWD 6 | PWD=$(dirname "${BASH_SOURCE[0]}") 7 | 8 | if [ -f "${PWD}/git" ]; then 9 | source "${PWD}/git" 10 | if [ "$(type -t __git_complete)" ]; then 11 | # gti is just a proxy, so it supports all things from original git 12 | __git_complete gti __git_main 13 | fi 14 | fi 15 | } 16 | 17 | ___gti_complete 18 | -------------------------------------------------------------------------------- /completions/gti.zsh: -------------------------------------------------------------------------------- 1 | #compdef gti=git 2 | # pretty simple one, it's just reuse the git one 3 | 4 | type -w _git | grep -q function && \ 5 | _git 6 | -------------------------------------------------------------------------------- /gti.6: -------------------------------------------------------------------------------- 1 | .TH GTI 6 2013-04-16 2 | 3 | .SH NAME 4 | \fBgti\fP \- Humorous typo-based git runner; drives a car over the terminal. 5 | 6 | .SH DESCRIPTION 7 | \fBgti\fP is intended to catch accidental typos of 'gti' instead of 'git'. It 8 | displays an animation of a car driving by, and then launches git. Any 9 | parameters or arguments given to \fBgti\fP will be passed through to git. 10 | 11 | The car image is derived from the look of an old VW Golf GTI. 12 | 13 | .SH ENVIRONMENT 14 | .TP 15 | .B GIT 16 | \fBgti\fP respects the 17 | .RB $ GIT 18 | environment variable. If GIT is set, its value will 19 | be used to launch git after the animation finishes, instead of searching for 20 | git in your PATH. 21 | .TP 22 | .B GTI_SPEED 23 | If 24 | .RB $ GTI_SPEED 25 | is set to a parsable integer, its value is used to control the speed of the 26 | animation. 27 | 28 | 29 | .SH SEE ALSO 30 | \fBgit\fP(1), \fBsl\fP(6) 31 | 32 | .SH AUTHOR 33 | \fBgti\fP was written by Richard Wossal . This manual was 34 | written by Felix Crux for the Debian project, but may 35 | be used by others under the same terms as \fBgti\fP itself. 36 | -------------------------------------------------------------------------------- /gti.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gti - a git launcher 3 | * 4 | * Copyright 2012 Richard Wossal and contributors. 5 | * 6 | * Permission to use, copy, modify, distribute, and sell this software 7 | * and its documentation for any purpose is hereby granted without fee, 8 | * provided that the above copyright notice appear in all copies and 9 | * that both that copyright notice and this permission notice appear in 10 | * supporting documentation. No representations are made about the 11 | * suitability of this software for any purpose. It is provided "as 12 | * is" without express or implied warranty. 13 | */ 14 | 15 | #if defined (_WIN32) && !defined(WIN32) 16 | # define WIN32 17 | #else 18 | /* fileno() */ 19 | #if defined(__MVS__) 20 | /* _POSIX_C_SOURCE has to be 1, 2 or 200112L on z/OS */ 21 | # define _POSIX_C_SOURCE 200112L 22 | #else 23 | # define _POSIX_C_SOURCE 199506L 24 | #endif 25 | /* usleep() */ 26 | # define _DEFAULT_SOURCE 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(WIN32) && !defined(__CYGWIN__) 34 | # include 35 | # include 36 | 37 | /* usleep() doesn't exist on MSVC, instead use Sleep() from Win32 API */ 38 | # define usleep(a) Sleep((a) / 1000) 39 | 40 | /* 41 | * exec*() on MSVC makes the parent process exit; that means that gti.exe will finish as git is starting, 42 | * which causes cmd.exe to print its prompt over git's output (because it sees that the child process has 43 | * finished). The solution is to use synchronous spawn*(): it will make gti.exe to wait until git finishes. 44 | */ 45 | # define execv(a, b) do { i = _spawnv(_P_WAIT, (a), (b)); if (i != -1) return i; } while(0) 46 | # define execvp(a, b) do { i = _spawnvp(_P_WAIT, (a), (b)); if (i != -1) return i; } while(0) 47 | 48 | #else 49 | # include 50 | #endif 51 | 52 | #ifndef WIN32 53 | # include 54 | #else 55 | # include 56 | HANDLE WIN_CONSOLE; 57 | #endif 58 | 59 | /* SunOS defines winsize in termios.h */ 60 | #if defined(__sun) && defined(__SVR4) 61 | # include 62 | #endif 63 | 64 | #define GIT_NAME "git" 65 | 66 | #ifndef GTI_SPEED 67 | # define GTI_SPEED 1000 68 | #endif 69 | 70 | int term_width(void); 71 | void init_space(void); 72 | void open_term(); 73 | void move_to_top(void); 74 | void line_at(int start_x, const char *s); 75 | void clear_car(int x); 76 | 77 | typedef void (*draw_fn_t)(int x); 78 | void draw_std(int x); 79 | void draw_push(int x); 80 | void draw_pull(int x); 81 | void draw_tag(int x); 82 | void draw_commit(int x); 83 | draw_fn_t select_command(int argc, char **argv); 84 | 85 | FILE *TERM_FH; 86 | int TERM_WIDTH; 87 | unsigned int FRAME_TIME; 88 | 89 | int main(int argc, char **argv) 90 | { 91 | int i; 92 | char *git_path; 93 | char *tmp; 94 | unsigned int gti_speed; 95 | draw_fn_t draw_fn; 96 | 97 | tmp = getenv("GTI_SPEED"); 98 | if (!tmp || sscanf(tmp, "%u", >i_speed) != 1) { 99 | gti_speed = GTI_SPEED; 100 | } 101 | open_term(); 102 | TERM_WIDTH = term_width(); 103 | FRAME_TIME = 1000 * 1000 * 10 / (gti_speed + TERM_WIDTH + 1); 104 | 105 | draw_fn = select_command(argc, argv); 106 | init_space(); 107 | for (i = -20; i < TERM_WIDTH; i++) { 108 | draw_fn(i); 109 | } 110 | move_to_top(); 111 | fflush(TERM_FH); 112 | 113 | git_path = getenv("GIT"); 114 | if (git_path) { 115 | execv(git_path, argv); 116 | } else { 117 | execvp(GIT_NAME, argv); 118 | } 119 | /* error in exec if we land here */ 120 | perror(GIT_NAME); 121 | return 1; 122 | } 123 | 124 | draw_fn_t select_command(int argc, char **argv) 125 | { 126 | int i; 127 | 128 | for (i = 1; i < argc; i++) { 129 | if (argv[i][0] == '-') 130 | continue; 131 | if (!strcmp(argv[i], "push")) 132 | return draw_push; 133 | if (!strcmp(argv[i], "pull")) 134 | return draw_pull; 135 | if (!strcmp(argv[i], "tag")) 136 | return draw_tag; 137 | if (!strcmp(argv[i], "commit")) 138 | return draw_commit; 139 | break; 140 | } 141 | return draw_std; 142 | } 143 | 144 | void init_space(void) 145 | { 146 | fputs("\n\n\n\n\n\n\n\n", TERM_FH); /* 9 lines, to not remove the PS1 line */ 147 | fflush(TERM_FH); 148 | } 149 | 150 | void open_term() 151 | { 152 | #ifndef WIN32 153 | TERM_FH = fopen("/dev/tty", "w"); 154 | if (!TERM_FH) 155 | TERM_FH = stdout; 156 | #else 157 | TERM_FH = fopen("CONOUT$", "w+"); 158 | WIN_CONSOLE = (HANDLE)_get_osfhandle(fileno(TERM_FH)); 159 | #endif 160 | } 161 | 162 | int term_width(void) 163 | { 164 | #ifndef WIN32 165 | struct winsize w; 166 | ioctl(fileno(TERM_FH), TIOCGWINSZ, &w); 167 | return w.ws_col; 168 | #else 169 | CONSOLE_SCREEN_BUFFER_INFO ci; 170 | GetConsoleScreenBufferInfo(WIN_CONSOLE, &ci); 171 | return ci.dwSize.X; 172 | #endif 173 | } 174 | 175 | void move_to_top(void) 176 | { 177 | #ifndef WIN32 178 | # ifndef __MVS__ 179 | fprintf(TERM_FH, "\033[8A"); 180 | # else 181 | fprintf(TERM_FH, "\047[8A"); 182 | # endif 183 | #else 184 | CONSOLE_SCREEN_BUFFER_INFO ci; 185 | GetConsoleScreenBufferInfo(WIN_CONSOLE, &ci); 186 | ci.dwCursorPosition.X = 0; 187 | ci.dwCursorPosition.Y -= 8; 188 | SetConsoleCursorPosition(WIN_CONSOLE, ci.dwCursorPosition); 189 | #endif 190 | } 191 | 192 | void move_to_x(int x) 193 | { 194 | #ifndef WIN32 195 | # ifndef __MVS__ 196 | fprintf(TERM_FH, "\033[%dC", x); 197 | # else 198 | fprintf(TERM_FH, "\047[%dC", x); 199 | # endif 200 | #else 201 | CONSOLE_SCREEN_BUFFER_INFO ci; 202 | GetConsoleScreenBufferInfo(WIN_CONSOLE, &ci); 203 | ci.dwCursorPosition.X = x; 204 | SetConsoleCursorPosition(WIN_CONSOLE, ci.dwCursorPosition); 205 | #endif 206 | } 207 | 208 | void line_at(int start_x, const char *s) 209 | { 210 | int x; 211 | size_t i; 212 | if (start_x > 1) 213 | move_to_x(start_x); 214 | for (x = start_x, i = 0; i < strlen(s); x++, i++) { 215 | if (x > 0 && x < TERM_WIDTH) 216 | fputc(s[i], TERM_FH); 217 | } 218 | #ifdef WIN32 219 | /* 220 | * It seems Windows wraps on whe cursor when it's about to overflow, 221 | * rather than after it has overflown (unless the overflowing character 222 | * is a newline), as other systems seems to do. 223 | */ 224 | if (x < TERM_WIDTH) 225 | #endif 226 | fputc('\n', TERM_FH); 227 | 228 | fflush(TERM_FH); 229 | } 230 | 231 | void draw_std(int x) 232 | { 233 | /* *INDENT-OFF* */ 234 | move_to_top(); 235 | line_at(x, " "); 236 | line_at(x, " ,---------------."); 237 | line_at(x, " / /``````|``````\\\\"); 238 | line_at(x, " / /_______|_______\\\\________"); 239 | line_at(x, "|] GTI |' | |]"); 240 | if (x % 2) { 241 | line_at(x, "= .-:-. |________| .-:-. ="); 242 | line_at(x, " ` -+- -------------- -+- '"); 243 | line_at(x, " '-:-' '-:-' "); 244 | } else { 245 | line_at(x, "= .:-:. |________| .:-:. ="); 246 | line_at(x, " ` X -------------- X '"); 247 | line_at(x, " ':-:' ':-:' "); 248 | } 249 | /* *INDENT-ON* */ 250 | usleep(FRAME_TIME); 251 | 252 | clear_car(x); 253 | } 254 | 255 | void draw_push(int x) 256 | { 257 | /* *INDENT-OFF* */ 258 | move_to_top(); 259 | line_at(x, " "); 260 | line_at(x, " __ ,---------------."); 261 | line_at(x, " /--\\ / /``````|``````\\\\"); 262 | line_at(x, " \\__/ / /_______|_______\\\\________"); 263 | line_at(x, " ||-< |] GTI |' | |]"); 264 | if (x % 2) { 265 | line_at(x, " ||-< = .-:-. |________| .-:-. ="); 266 | line_at(x, " || ` -+- -------------- -+- '"); 267 | line_at(x, " || '-:-' '-:-' "); 268 | } else { 269 | line_at(x, " ||-< = .:-:. |________| .:-:. ="); 270 | line_at(x, " /\\ ` X -------------- X '"); 271 | line_at(x, " / \\ ':-:' ':-:' "); 272 | } 273 | /* *INDENT-ON* */ 274 | usleep(FRAME_TIME * 10); 275 | 276 | clear_car(x); 277 | } 278 | 279 | void draw_pull(int x) 280 | { 281 | /* *INDENT-OFF* */ 282 | move_to_top(); 283 | line_at(x, " ------."); 284 | line_at(x, " ,---------------. | | ,"); 285 | line_at(x, " / /``````|``````\\\\ | | ||"); 286 | line_at(x, " / /_______|_______\\\\________ ,-------.--+-------++--,"); 287 | if (x % 2) { 288 | line_at(x, "|] GTI |' | |] / .:-:. | |"); 289 | line_at(x, "= .-:-. |________| .-:-. = ----------- . `------- .-:-."); 290 | line_at(x, " ` -+- -------------- -+- ' ' `----' +"); 291 | line_at(x, " '-:-' '-:-' '-:-' '-:-'"); 292 | } else { 293 | line_at(x, "|] GTI |' | |] / .-:-. | |"); 294 | line_at(x, "= .:-:. |________| .:-:. = ----------- , `------- .:-:."); 295 | line_at(x, " ` X -------------- X ' ' `----' X"); 296 | line_at(x, " ':-:' ':-:' ':-:' ':-:'"); 297 | } 298 | /* *INDENT-ON* */ 299 | usleep(FRAME_TIME * 8); 300 | 301 | clear_car(x); 302 | } 303 | 304 | void draw_tag(int iteration) 305 | { 306 | const int car_x = 4; 307 | int car_y = 0; 308 | int keyframe = ((iteration + 20) / 4) % 3; 309 | 310 | /* *INDENT-OFF* */ 311 | move_to_top(); 312 | line_at(car_x, " ,-------------, . "); 313 | line_at(car_x, " / [_o_] \\| "); 314 | line_at(car_x, " []/_________________|[] "); 315 | line_at(car_x, " /__/_____________\\__\\ "); 316 | line_at(car_x, "d|/``\\=(_)=====(_)=/``\\|b"); 317 | line_at(car_x, " |\\__/=============\\__/| "); 318 | line_at(car_x, " \\-----|__G_T_I__|-----/ "); 319 | if (keyframe == 1) 320 | line_at(car_x, " !\\/! !\\/!"); 321 | else if (keyframe == 2) 322 | line_at(car_x, " ;/\\; ;/\\;"); 323 | else 324 | line_at(car_x, " |||| ||||"); 325 | /* *INDENT-ON* */ 326 | usleep(FRAME_TIME * 2); 327 | 328 | /* clear it */ 329 | move_to_top(); 330 | for (car_y = 0; car_y < 8; car_y++) { 331 | line_at(car_x, " "); 332 | } 333 | } 334 | 335 | void draw_commit(int iteration) 336 | { 337 | const int car_x = 4; 338 | int car_y = 0; 339 | int keyframe = ((iteration + 20) / 4) % 3; 340 | 341 | /* *INDENT-OFF* */ 342 | move_to_top(); 343 | line_at(car_x, " ,-------------, . "); 344 | line_at(car_x, " / [_o_] \\| "); 345 | line_at(car_x, " []/_________________|[] "); 346 | line_at(car_x, " /__/_____________\\__\\ "); 347 | if (keyframe == 1) 348 | line_at(car_x, "d|/$$\\=(_)=====(_)=/$$\\|b"); 349 | else if (keyframe == 2) 350 | line_at(car_x, "d|/##\\=(_)=====(_)=/##\\|b"); 351 | else 352 | line_at(car_x, "d|/``\\=(_)=====(_)=/``\\|b"); 353 | if (keyframe == 1) 354 | line_at(car_x, " |\\$$/=============\\$$/| "); 355 | else if (keyframe == 2) 356 | line_at(car_x, " |\\##/=============\\##/| "); 357 | else 358 | line_at(car_x, " |\\__/=============\\__/| "); 359 | line_at(car_x, " \\-----|__G_T_I__|-----/ "); 360 | line_at(car_x, " |||| ||||"); 361 | /* *INDENT-ON* */ 362 | usleep(FRAME_TIME * 2); 363 | 364 | /* clear it */ 365 | move_to_top(); 366 | for (car_y = 0; car_y < 8; car_y++) { 367 | line_at(car_x, " "); 368 | } 369 | } 370 | 371 | void clear_car(int x) 372 | { 373 | move_to_top(); 374 | line_at(x, " "); 375 | line_at(x, " "); 376 | line_at(x, " "); 377 | line_at(x, " "); 378 | line_at(x, " "); 379 | line_at(x, " "); 380 | line_at(x, " "); 381 | line_at(x, " "); 382 | } 383 | --------------------------------------------------------------------------------