-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathbare.asm
More file actions
19776 lines (18488 loc) · 478 KB
/
Copy pathbare.asm
File metadata and controls
19776 lines (18488 loc) · 478 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; bare - interactive shell in x86_64 Linux assembly
; No libc. Pure syscalls. Bare metal.
;
; Build: nasm -f elf64 bare.asm -o bare.o && ld bare.o -o bare
BITS 64
DEFAULT REL
; ── Syscall numbers ──────────────────────────────────────────────────
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_CLOSE 3
%define SYS_STAT 4
%define SYS_FSTAT 5
%define SYS_ACCESS 21
%define X_OK 1
%define SYS_LSEEK 8
%define SYS_MMAP 9
%define SYS_MPROTECT 10
%define SYS_MUNMAP 11
%define SYS_BRK 12
%define SYS_IOCTL 16
%define SYS_FCNTL 72
%define SYS_POLL 7
%define POLLIN 1
%define SYS_PIPE 22
%define SYS_DUP2 33
%define SYS_RENAME 82
%define SYS_LINK 86
%define SYS_UNLINK 87
%define SYS_FORK 57
%define SYS_EXECVE 59
%define SYS_EXIT 60
%define SYS_WAIT4 61
%define SYS_KILL 62
%define SYS_GETCWD 79
%define SYS_CHDIR 80
%define SYS_GETDENTS64 217
%define SYS_GETPID 39
%define SYS_GETUID 102
%define SYS_GETGID 104
%define SYS_SETPGID 109
%define SYS_GETPGID 121
%define SYS_GETPPID 110
%define SYS_TCSETPGRP 0 ; via ioctl
%define SYS_RT_SIGACTION 13
%define SYS_RT_SIGPROCMASK 14
; ioctl constants
%define TCGETS 0x5401
%define TCSETS 0x5402
%define TCSETSW 0x5403
%define TIOCSPGRP 0x5410
%define TIOCGPGRP 0x5411
; termios flags
%define ICANON 0x2
%define ECHO 0x8
%define ISIG 0x1
%define ECHOCTL 0x200
%define VMIN 6
%define VTIME 5
; open flags
%define O_RDONLY 0
%define O_WRONLY 1
%define O_RDWR 2
%define O_CREAT 0x40
%define O_TRUNC 0x200
%define O_APPEND 0x400
%define O_DIRECTORY 0x10000
; signal constants
%define SIGINT 2
%define SIGQUIT 3
%define SIGWINCH 28
%define SA_RESTORER 0x04000000
%define SYS_RT_SIGRETURN 15
%define SIGCONT 18
%define SIGTSTP 20
%define SIGTTIN 21
%define SIGTTOU 22
%define SIGCHLD 17
%define SIGHUP 1
%define SIGTERM 15
%define SIG_IGN 1
%define SIG_DFL 0
; wait flags
%define WUNTRACED 2
%define WNOHANG 1
; dirent64 structure offsets
%define DIRENT64_D_INO 0
%define DIRENT64_D_OFF 8
%define DIRENT64_D_RECLEN 16
%define DIRENT64_D_TYPE 18
%define DIRENT64_D_NAME 19
; Max constants
%define MAX_ENV_ENTRIES 256
%define MAX_ENV_STORAGE 16384
%define MAX_GLOB_RESULTS 256
%define MAX_GLOB_BUF 16384
%define MAX_TAB_RESULTS 128
%define MAX_NICKS 64
%define MAX_NICK_STORAGE 8192
%define MAX_GNICKS 64
%define MAX_GNICK_STORAGE 4096
%define MAX_ABBREVS 64
%define MAX_ABBREV_STORAGE 4096
%define MAX_BOOKMARKS 64
%define MAX_BM_STORAGE 8192
%define MAX_DIR_HISTORY 64
; Hard cap on persistent shell history entries. With dedup on (smart or
; full mode), 1024 distinct commands is more than any normal user
; accumulates in months. Older entries roll off when the cap is hit so
; suggestion / Ctrl-R lookups stay O(N) at a small N.
%define MAX_HIST 1024
%define MAX_PIPE_SEGMENTS 16
%define MAX_JOBS 32
; Color setting indices
%define C_USER 0
%define C_HOST 1
%define C_CWD 2
%define C_PROMPT 3
%define C_CMD 4
%define C_NICK 5
%define C_GNICK 6
%define C_PATH 7
%define C_SWITCH 8
%define C_BOOKMARK 9
%define C_COLON 10
%define C_GIT 11
%define C_STAMP 12
%define C_TABSEL 13
%define C_TABOPT 14
%define C_SUGGEST 15
%define C_USER_ROOT 16
%define C_HOST_ROOT 17
%define NUM_COLORS 18
; Config flag bits
%define CFG_AUTO_CORRECT 0
%define CFG_COMPLETION_FUZZY 1
%define CFG_RPROMPT 2
%define CFG_AUTO_PAIR 3
%define CFG_SHOW_TIPS 4
%define CFG_SHOW_CMD 5
%define CFG_HIST_DEDUP_FULL 6
%define CFG_HIST_DEDUP_SMART 7
%define CFG_SHOW_GIT_BRANCH 8
%define CFG_GIT_STATUS_FORK 9
; Syscalls for timing and terminal size
%define SYS_CLOCK_GETTIME 228
%define CLOCK_MONOTONIC 1
; TIOCGWINSZ ioctl
%define TIOCGWINSZ 0x5413
; ── Data section ─────────────────────────────────────────────────────
section .data
prompt_str: db 27, '[1m', 27, '[38;5;39m' ; bold + blue
prompt_user: db 'bare', 27, '[0m' ; reset
prompt_sep: db 27, '[38;5;245m', '> ', 27, '[0m' ; gray >
prompt_len equ $ - prompt_str
newline: db 10
space_char: db ' '
err_fork: db "bare: fork failed", 10
err_fork_len equ $ - err_fork
err_exec: db "bare: command not found: "
err_exec_len equ $ - err_exec
err_usage_bare:
db "Usage: bare [-l|--login] [-c command] [--bench] [--help]", 10
db "Interactive shell in x86_64 Linux assembly. No libc, pure syscalls.", 10
err_usage_bare_len equ $ - err_usage_bare
err_cd: db "bare: cd: no such directory", 10
err_cd_len equ $ - err_cd
err_pipe: db "bare: pipe failed", 10
err_pipe_len equ $ - err_pipe
err_export: db "bare: export: invalid format", 10
err_export_len equ $ - err_export
; Builtin command strings
str_cd: db "cd", 0
str_exit: db "exit", 0
str_pwd: db "pwd", 0
str_export: db "export", 0
str_unset: db "unset", 0
str_history: db "history", 0
; Colon command strings
str_nick: db ":nick", 0
str_gnick: db ":gnick", 0
str_abbrev: db ":abbrev", 0
str_bm: db ":bm", 0
str_dirs: db ":dirs", 0
str_rmhistory: db ":rmhistory", 0
str_rehash: db ":rehash", 0
str_reload: db ":reload", 0
str_theme: db ":theme", 0
str_calc: db ":calc", 0
str_stats: db ":stats", 0
str_jobs: db ":jobs", 0
str_fg: db ":fg", 0
str_bg: db ":bg", 0
str_env: db ":env", 0
str_config: db ":config", 0
str_validate: db ":validate", 0
str_save_sess: db ":save_session", 0
str_load_sess: db ":load_session", 0
str_list_sess: db ":list_sessions", 0
str_del_sess: db ":delete_session", 0
str_record: db ":record", 0
str_replay: db ":replay", 0
str_save: db ":save", 0
str_backup: db ":backup", 0
str_restore: db ":restore", 0
str_version: db ":version", 0
str_info: db ":info", 0
str_help: db ":help", 0
str_time: db "time", 0
str_pushd: db "pushd", 0
str_popd: db "popd", 0
str_exec: db "exec", 0
script_err_msg: db "bare: cannot open script", 10
script_err_msg_len equ $ - script_err_msg
exec_usage_msg: db "exec: usage: exec COMMAND [ARG...]", 10
exec_usage_len equ $ - exec_usage_msg
exec_notfound_pre: db "exec: "
exec_notfound_pre_len equ $ - exec_notfound_pre
exec_notfound_suf: db ": not found", 10
exec_notfound_suf_len equ $ - exec_notfound_suf
; Colon command dispatch table: pairs of (string_ptr, handler_ptr), sentinel (0,0)
colon_dispatch_table:
dq str_nick, handle_nick
dq str_gnick, handle_gnick
dq str_abbrev, handle_abbrev
dq str_bm, handle_bm
dq str_dirs, handle_dirs
dq str_rmhistory, handle_rmhistory
dq str_rehash, handle_rehash
dq str_reload, handle_reload
dq str_version, handle_version
dq str_help, handle_help
dq str_jobs, handle_jobs
dq str_fg, handle_fg
dq str_bg, handle_bg
dq str_theme, handle_theme
dq str_env, handle_env
dq str_config, handle_config
dq str_calc, handle_calc
dq str_stats, handle_stats
dq str_validate, handle_validate
dq str_info, handle_info
dq str_save, handle_save
dq str_backup, handle_backup
dq str_restore, handle_restore
dq str_save_sess, handle_save_session
dq str_load_sess, handle_load_session
dq str_list_sess, handle_list_sessions
dq 0, 0
; Version string
version_str: db "bare 0.2.42", 10, 0
version_str_len equ $ - version_str - 1
; Config file suffix
config_suffix: db "/.barerc", 0
; State file suffix
state_suffix: db "/.barestate", 0
; PATH executable cache file (persists exe_cache between sessions so we
; can skip the ~600ms PATH directory scan on every shell startup).
exec_cache_suffix: db "/.bare_exe_cache", 0
; Error messages for new features
err_nick: db "bare: nick: usage: :nick name = value", 10
err_nick_len equ $ - err_nick
err_bm: db "bare: bm: usage: :bm [name] [path] [#tags]", 10
err_bm_len equ $ - err_bm
; Prompt components
prompt_at: db "@", 0
prompt_colon: db ": ", 0
prompt_arrow: db "> ", 0
prompt_git_open: db " (", 0
prompt_git_close: db ")", 0
prompt_tilde: db "~", 0
git_head_prefix: db "ref: refs/heads/", 0
git_head_file: db "/.git/HEAD", 0
dot_git_dir: db ".git", 0
etc_hostname: db "/etc/hostname", 0
; Nick display
nick_arrow: db " = ", 0
; Clear screen sequence
clear_screen_seq: db 27, "[2J", 27, "[H"
clear_screen_len equ $ - clear_screen_seq
; Clear to end of line (used for suggestion cleanup)
clr_eol_global: db 27, "[K"
clr_eol_len equ $ - clr_eol_global
; Color themes: each is NUM_COLORS bytes in order of C_* constants
; Indices: user, host, cwd, prompt, cmd, nick, gnick, path, switch, bookmark, colon, git, stamp, tabsel, tabopt, suggest
theme_names:
dq .tn_default, .tn_solarized, .tn_dracula, .tn_gruvbox, .tn_nord, .tn_monokai
dq 0
.tn_default: db "default", 0
.tn_solarized: db "solarized", 0
.tn_dracula: db "dracula", 0
.tn_gruvbox: db "gruvbox", 0
.tn_nord: db "nord", 0
.tn_monokai: db "monokai", 0
theme_data:
; default: user host cwd prompt cmd nick gnick path switch bm colon git stamp tabsel tabopt suggest
; Indices: user host cwd prompt cmd nick gnick path switch bm colon git stamp tabsel tabopt suggest user_root host_root
.td_default: db 2, 2, 81, 208, 48, 6, 33, 3, 6, 5, 4, 208, 245, 7, 245, 240, 196, 196
.td_solarized: db 64, 64, 37, 136, 33, 37, 33, 136, 37, 125,33, 166, 245, 7, 245, 240, 196, 196
.td_dracula: db 84, 84, 141,212, 84, 117,189, 228, 117, 212,189, 215, 245, 7, 245, 240, 196, 196
.td_gruvbox: db 142,142,214,208, 142, 108,109, 223, 108, 175,109, 208, 245, 7, 245, 240, 196, 196
.td_nord: db 110,110,111,173, 110, 110,111, 222, 110, 139,111, 173, 245, 7, 245, 240, 196, 196
.td_monokai: db 148,148,81, 208, 148, 81, 141, 228, 81, 197,141, 208, 245, 7, 245, 240, 196, 196
; :info text
info_text:
db 27, "[38;5;48m"
db " _ ", 10
db " | |__ __ _ _ __ ___ ", 10
db " | '_ \ / _` | '__/ _ \ ", 10
db " | |_) | (_| | | | __/ ", 10
db " |_.__/ \__,_|_| \___| ", 10
db 27, "[0m", 10
db " Interactive shell in x86_64 Linux assembly", 10
db " No libc, no runtime, pure syscalls. Part of CHasm.", 10, 10
db " Features: dynamic prompt with git dirty indicator, multi-pipe, command substitution,", 10
db " brace/history/glob expansion (including **), nick/gnick/abbrev aliases, bookmarks,", 10
db " inline NAME? help (type s? / show? / gs? to see what a name resolves to),", 10
db " interactive tab cycling with LS_COLORS, switch completion from --help, Ctrl-R search,", 10
db " inline suggestions, job control, 6 color themes, syntax highlighting, here-strings,", 10
db " auto-pair brackets, multi-line editing, calculator, backslash-space file escaping,", 10
db " SIGWINCH resize handling, UTF-8 cursor movement, config persistence, and more.", 10, 10
db " Config: ~/.barerc History: ~/.bare_history Plugins: ~/.bare/plugins/", 10
db " Companion: bareconf (TUI configurator) Website: https://isene.org", 10, 10
info_text_len equ $ - info_text
; Startup tips
tip_count equ 9
tip_table:
dq .tip0, .tip1, .tip2, .tip3, .tip4, .tip5, .tip6, .tip7, .tip8
.tip0: db "Tip: Use :nick to create command aliases", 10, 0
.tip1: db "Tip: Ctrl-R searches history interactively", 10, 0
.tip2: db "Tip: Type a directory name to auto-cd into it", 10, 0
.tip3: db "Tip: :bm name saves a bookmark, type its name to jump", 10, 0
.tip4: db "Tip: :theme dracula/gruvbox/nord/solarized/monokai", 10, 0
.tip5: db "Tip: $(cmd) for command substitution, {a,b,c} for braces", 10, 0
.tip6: db "Tip: :abbrev gs = git status (expands on space)", 10, 0
.tip7: db "Tip: Ctrl-Z suspends, :jobs lists, :fg resumes", 10, 0
.tip8: db "Tip: Type NAME? (e.g. s? show?) to see what a name resolves to", 10, 0
; Plugin path suffix
plugin_suffix: db "/.bare/plugins/", 0
; Default PATH for searching executables
default_path: db "/usr/local/bin:/usr/bin:/bin", 0
; Shell name for display
shell_name: db "bare", 0
; History file path suffix
hist_suffix: db "/.bare_history", 0
; Background job message
bg_open: db "[", 0
bg_close: db "]", 10, 0
bg_jobsep: db "] ", 0
; Dot and dotdot for filtering directory entries
dot_name: db ".", 0
dotdot_name: db "..", 0
; Default LS_COLORS used when the env var is unset (e.g. bare spawned
; by a window manager outside any login-shell context). Matches the
; spirit of GNU dircolors -b output trimmed to the most-used keys.
default_ls_colors:
db "di=38;5;111;1:ln=38;5;248;1:ex=38;5;46:bd=38;5;196;4:cd=38;5;198;4:"
db "pi=38;5;124;4:so=38;5;196;4:do=38;5;197;4:fi=0:no=0:or=38;5;212;3:"
db "ow=38;5;197;4;100:tw=38;5;255;3;100:st=38;5;255;3:sg=38;5;255;4;100:"
db "su=38;5;255;4;100;1:mh=38;5;248;3:ca=38;5;197;4:"
db "*.tar=38;5;130:*.tgz=38;5;130:*.gz=38;5;130:*.bz2=38;5;130:"
db "*.xz=38;5;130:*.zip=38;5;130:*.7z=38;5;130:*.deb=38;5;130:"
db "*.rpm=38;5;130:*.jar=38;5;130:"
db "*.jpg=38;5;141:*.jpeg=38;5;141:*.png=38;5;141:*.gif=38;5;141:"
db "*.bmp=38;5;141:*.svg=38;5;141:*.webp=38;5;141:*.ico=38;5;141:"
db "*.mp3=38;5;81:*.flac=38;5;81:*.wav=38;5;81:*.ogg=38;5;81:"
db "*.mp4=38;5;81:*.mkv=38;5;81:*.webm=38;5;81:*.mov=38;5;81:"
db "*.pdf=38;5;160:*.epub=38;5;160:*.mobi=38;5;160:"
db "*.md=38;5;229:*.txt=38;5;229:*.org=38;5;229:"
db "*.c=38;5;148:*.cpp=38;5;148:*.h=38;5;148:*.hpp=38;5;148:"
db "*.rs=38;5;148:*.go=38;5;148:*.py=38;5;148:*.rb=38;5;148:"
db "*.js=38;5;148:*.ts=38;5;148:*.sh=38;5;148:*.asm=38;5;148:"
db 0
default_ls_colors_end:
section .bss
; TTY flag (1 if stdin is a terminal, 0 if pipe)
is_tty: resq 1
; Input buffer
input_buf: resb 4096
input_len: resq 1
; Line editing buffer (16KB to handle large exports like LS_COLORS)
line_buf: resb 16384
line_len: resq 1
cursor_pos: resq 1
; Partial-redraw state. prev_line_buf is a snapshot of line_buf taken
; after each successful full or partial render. On the next keystroke,
; full_redraw compares line_buf against the snapshot to find the first
; differing byte and skips re-outputting the unchanged prefix (incl.
; the prompt) — fixes the per-keystroke flicker noticed on low-
; bandwidth connections. Issue isene/bare#5.
;
; needs_full_redraw forces the slow path on the next render. Set by:
; - read_line entry (terminal state unknown)
; - history navigation Up/Down (whole buffer changes)
; - Ctrl-L screen clear
; - tab completion (after drawing candidates)
; - SIGWINCH (terminal resized — wrap positions may have shifted)
prev_line_buf: resb 16384
prev_line_len: resq 1
needs_full_redraw: resq 1
pr_first_diff: resq 1
pr_first_diff_col: resq 1
pr_byte_off: resq 1
pr_output_len: resq 1
; Argument parsing
argv_ptrs: resq 128 ; max 128 args
; Leading-env prefix support: `VAR=val [VAR2=val2 ...] cmd args`.
; Detected after parse_argv; applied in the child between fork and
; execve so the parent's env stays clean (matches bash semantics).
env_prefix_ptrs: resq 16
env_prefix_count: resq 1
argc: resq 1
; Working directory
cwd_buf: resb 4096
; Path search buffer
path_buf: resb 4096
exec_path: resb 4096
; Environment pointer (saved from stack at entry)
envp: resq 1
; Original termios (for raw mode toggle)
orig_termios: resb 60
raw_termios: resb 60
; History
hist_buf: resb 524288 ; 512KB history buffer
hist_lines: resq 8192 ; pointers to history lines
hist_count: resq 1
hist_dirty: resq 1 ; 1 if an entry rolled off via cap rotation
; this session — next save needs to be a
; rewrite, not an append, to compact the
; on-disk file.
hist_persisted: resq 1 ; entries already written to disk; save
; appends only newer ones so concurrent
; bare instances don't overwrite each
; other's history
hist_pos: resq 1 ; current position when browsing
hist_path: resb 256 ; full path to history file
; Pipe file descriptors
pipe_fds: resd 2
; Temp buffers
tmp_buf: resb 4096
num_buf: resb 32
; Redirect filenames
redir_out: resq 1 ; pointer to output redirect filename
redir_in: resq 1 ; pointer to input redirect filename
redir_herestring: resq 1 ; pointer to here-string content (<<<)
redir_append: resq 1 ; 1 if >>, 0 if >
; Signal handling
child_pid: resq 1
; ── New BSS for added features ───────────────────────────────────────
; Last exit status for $? and && / ||
last_status: resq 1
; Expand buffer (tilde + env var expansion)
expand_buf: resb 4096
; LS_COLORS cache. Cached once at startup (find in envp, copy raw value
; here, null-terminate). Lookups are linear scans over this buffer
; — LS_COLORS typically has 500–700 entries (~10 KB) which is fine
; for cold-tab-completion frequency.
lscolors_buf: resb 16384
lscolors_len: resq 1 ; bytes copied (0 = unset)
lscolors_inited: resq 1
; dir_color path-pattern table. Loaded from ~/.barerc lines like
; dir_color = MakeItSimple 172
; The syntax highlighter substring-matches each path-shaped token
; against these patterns; matches paint with `\033[38;5;Nm`.
%define DIR_COLOR_MAX 64
%define DIR_COLOR_POOL_SZ 4096
dir_color_count: resq 1
dir_color_pat_ptrs: resq DIR_COLOR_MAX ; pointers into dir_color_pool
dir_color_pat_lens: resq DIR_COLOR_MAX ; cached strlen for each pattern
dir_color_codes: resb DIR_COLOR_MAX ; 256-color code (1..255)
dir_color_pool: resb DIR_COLOR_POOL_SZ
dir_color_pool_pos: resq 1
; Restore-sequence stash for emit_path_token. Set by caller, read after
; each match. Lets the prompt's CWD render keep its outer color across
; pattern matches without emit_path_token having to know about prompt
; state.
emit_path_restore_ptr: resq 1
emit_path_restore_len: resq 1
; Pre-built "\033[38;5;<c_cwd>m" used by the prompt CWD render as the
; restore sequence handed to emit_path_token.
cwd_restore_buf: resb 16
cwd_restore_len: resq 1
; Custom environment array and storage (for export/unset)
env_array: resq MAX_ENV_ENTRIES ; pointers to "VAR=VALUE" strings
env_count: resq 1 ; number of entries
env_storage: resb MAX_ENV_STORAGE ; storage for new entries
env_storage_pos: resq 1 ; next free byte in env_storage
env_inited: resq 1 ; 1 if env_array has been initialized
; Glob expansion
glob_results: resq MAX_GLOB_RESULTS ; pointers to matched filenames
glob_count: resq 1
glob_buf: resb MAX_GLOB_BUF ; storage for matched filenames
glob_buf_pos: resq 1
glob_dir_buf: resb 4096 ; buffer for getdents64
glob_path_buf: resb 4096 ; temp for building paths
glob_queue: resb 32768 ; BFS queue for ** glob (null-separated dir paths)
glob_queue_wpos: resq 1 ; write position in queue
glob_queue_rpos: resq 1 ; read position in queue
; Expanded argv (after glob expansion)
expanded_argv: resq 512 ; expanded argv array
expanded_argc: resq 1
; Tab completion
tab_results: resq MAX_TAB_RESULTS ; matching completions
tab_types: resb MAX_TAB_RESULTS ; file type for each match (d_type)
tab_count: resq 1
tab_buf: resb 8192 ; storage for tab matches
tab_buf_pos: resq 1
tab_word_buf: resb 256 ; current word being completed
csi_params: resb 32 ; collected CSI parameter bytes
csi_param_len: resq 1 ; live count (rcx is clobbered by syscall)
tab_saved_dtype: resb 1 ; d_type from last file match
tab_dir_buf: resb 4096 ; directory listing buffer
; Chain parsing
chain_cmds: resq 64 ; pointers to individual commands
chain_ops: resb 64 ; operator: 0=none, 1=;, 2=&&, 3=||
chain_count: resq 1
; PID cache
my_pid: resq 1
; ── Config system BSS ───────────────────────────────────────────────
; Config file buffer
config_buf: resb 16384
; Nick aliases (command aliases)
nick_names: resq MAX_NICKS ; pointers to name strings
nick_values: resq MAX_NICKS ; pointers to expansion strings
nick_count: resq 1
nick_storage: resb MAX_NICK_STORAGE
nick_storage_pos: resq 1 ; next free byte in storage
; Global nick aliases
gnick_names: resq MAX_GNICKS
gnick_values: resq MAX_GNICKS
gnick_count: resq 1
gnick_storage: resb MAX_GNICK_STORAGE
gnick_storage_pos: resq 1
; Abbreviations
abbrev_names: resq MAX_ABBREVS
abbrev_values: resq MAX_ABBREVS
abbrev_count: resq 1
abbrev_storage: resb MAX_ABBREV_STORAGE
abbrev_storage_pos: resq 1
; Bookmarks
bm_names: resq MAX_BOOKMARKS ; name strings
bm_paths: resq MAX_BOOKMARKS ; path strings
bm_tags: resq MAX_BOOKMARKS ; tag strings (space-separated)
bm_count: resq 1
bm_storage: resb MAX_BM_STORAGE
; Color settings (256-color codes, one byte each)
color_settings: resb NUM_COLORS
; Config flags (bitfield)
config_flags: resq 1
; Completion limit
completion_limit: resq 1
; Slow command threshold (seconds, 0 = disabled)
slow_cmd_threshold: resq 1
; Directory history
dir_history: resq MAX_DIR_HISTORY ; pointers to path strings
dir_hist_count: resq 1
dir_hist_storage: resb 8192
dir_hist_pos: resq 1 ; next free byte in storage
; Directory stack (pushd/popd)
dir_stack: resq 32
dir_stack_count: resq 1
dir_stack_storage: resb 4096
; Multi-pipe support
pipe_segments: resq MAX_PIPE_SEGMENTS ; pointers to pipe segments
pipe_seg_count: resq 1
pipe_fds_array: resd 32 ; 16 pipes x 2 fds
pipe_child_pids: resq MAX_PIPE_SEGMENTS
; Job control
job_pids: resq MAX_JOBS
job_pgids: resq MAX_JOBS
job_status: resq MAX_JOBS ; 0=running, 1=stopped, 2=done
job_cmds: resq MAX_JOBS ; pointers to command strings
job_count: resq 1
job_cmd_storage: resb 4096
; Prompt building
hostname_buf: resb 256
username_buf: resb 64
prompt_build_buf: resb 1024
git_branch_buf: resb 128
git_head_buf: resb 256
term_width: resq 1
rprompt_buf: resb 256
; Command timing
cmd_start_time: resq 2 ; tv_sec, tv_nsec
cmd_end_time: resq 2
; Command frequency tracking
cmd_freq_names: resq 128
cmd_freq_counts: resq 128
cmd_freq_count: resq 1
cmd_freq_storage: resb 8192
; Config file path
config_path: resb 256
config_path_tmp: resb 256 ; <config_path>.tmp — atomic-write target
config_path_bak: resb 256 ; <config_path>.bak — previous good copy
; PATH exe cache file path
exec_cache_path: resb 256
; Command-line flags
login_flag: resq 1 ; 1 if -l/--login
cmd_flag: resq 1 ; pointer to -c command string
script_mode: resq 1 ; 1 if invoked as `bare scriptfile` (#11)
time_flag: resq 1 ; 1 if "time" prefix was used
git_status_cached: resb 1 ; cached git dirty result (0=clean, 1=dirty)
git_status_cache_time: resq 1 ; monotonic time of last fork check
git_root_buf: resb 4096 ; path to git repo root (where .git/ is)
git_root_prev: resb 4096 ; previous git root (to detect repo change)
; Previous directory for cd -
prev_dir: resb 4096
; Prompt visible width (characters, excluding ANSI escapes)
prompt_visible_width: resq 1
; Nick expansion buffer
nick_expand_buf: resb 4096
; Command substitution
subst_buf: resb 8192
subst_tmp: resb 4096
; Brace expansion
brace_buf: resb 4096
; History search
search_buf: resb 256
search_len: resq 1
rs_skip_count: resq 1 ; how many matches to skip (Ctrl-R again)
; Prefix history search (Up/Down with typed prefix)
hist_prefix_buf: resb 256 ; saved prefix for Up/Down search
hist_prefix_len: resq 1 ; length of prefix (0 = no prefix search)
; Inline suggestion
suggestion_buf: resb 4096
suggestion_ptr: resq 1 ; pointer to suggestion remainder
suggestion_len: resq 1 ; length of suggestion
; Undo stack (4 snapshots of line_buf)
undo_stack: resb 16384
undo_lens: resq 4
undo_positions: resq 4
undo_count: resq 1
; Validation rules
valid_patterns: resq 32
valid_actions: resb 32 ; 0=warn, 1=confirm, 2=block
valid_count: resq 1
valid_storage: resb 4096
; Config save timestamp (to prevent overwriting newer config from another terminal)
config_save_time: resq 1
; Config dirty flag: set ONLY by commands that mutate config state
; (:nick/:gnick/:abbrev/:bm/:theme/:config/color-set/:save/:restore).
; save_config is a no-op when clear, so the many bares that exit
; without changing anything (shell exits, shutdown SIGTERM stampede)
; never touch ~/.barerc. A never-loaded bare (started while the file
; was missing or mid-rename) can therefore never publish its compiled
; defaults over a real config -- the repeat ".barerc nuked" incident.
config_dirty: resq 1
; Switch completion buffers
switch_cmd_buf: resb 256 ; command name extracted from line
switch_help_buf: resb 16384 ; captured --help output
switch_tmp_buf: resb 64 ; temp buffer for switch dedup
; Executable cache for syntax highlighting
exe_cache: resb 65536 ; cached executable names (null-separated)
exe_cache_pos: resq 1 ; current write position
exe_cache_count: resq 1 ; number of cached names
; Render buffer for batched screen output (single write per redraw)
render_buf: resb 16384
render_pos: resq 1
render_to_buf: resq 1 ; flag: 1 = write prompt to render_buf
prev_cursor_row: resq 1 ; visual row offset of the cursor at the
; end of the previous full_redraw — i.e.
; where the terminal cursor actually
; sits when this redraw begins. Using
; the CURRENT r12 to compute the up-
; movement was wrong: r12 has already
; been mutated by the keystroke that
; triggered this redraw, but the
; terminal cursor still reflects the
; pre-keystroke position. Reset to 0
; on each new read_line iteration and
; SIGWINCH (cursor is at the prompt's
; first row after both events).
sigwinch_flag: resq 1 ; set by SIGWINCH handler
tz_offset: resq 1 ; timezone offset in seconds from UTC
shl_output_len: resq 1 ; syntax_highlight_line output length
; Logging fd. 0 = unavailable (kernel never returns 0 from open()
; while stdin is still attached). log_write_buf no-ops in that case.
log_fd_bare: resq 1
; Session buffer
session_buf: resb 16384
section .text
global _start
; ══════════════════════════════════════════════════════════════════════
; Entry point
; ══════════════════════════════════════════════════════════════════════
_start:
; Record startup time for --bench
sub rsp, 16
mov rax, SYS_CLOCK_GETTIME
mov rdi, CLOCK_MONOTONIC
mov rsi, rsp
syscall
mov rax, [rsp]
mov [cmd_start_time], rax ; reuse cmd_start_time for startup
; /tmp/bare.log is only useful when bare is running under a
; terminal whose stderr might be captured/scrolled away. For
; `echo cmd | bare` (non-interactive), stderr goes straight to the
; caller's terminal anyway, so the extra open is pure cost.
; Defer: log_open_bare moved into the interactive-init block below.
mov rax, [rsp + 8]
mov [cmd_start_time + 8], rax
add rsp, 16
; Save environment pointer from stack
; Stack layout: [argc] [argv...] [NULL] [envp...] [NULL]
mov rdi, [rsp] ; argc
lea rsi, [rsp + 8] ; argv
; Skip past argv to envp
lea rax, [rdi + 1]
lea rcx, [rsi + rax*8] ; envp
mov [envp], rcx
; Parse command-line flags (-l/--login, -c "cmd")
mov qword [login_flag], 0
mov qword [cmd_flag], 0
; Login shell convention: login(1)/getty/sshd exec the shell with
; argv[0] starting with '-'. Detect that so a raw TTY login sources
; ~/.bare_profile (without it PATH (~/bin) and LS_COLORS stay unset).
mov rax, [rsi] ; argv[0]
test rax, rax
jz .argv0_login_done
cmp byte [rax], '-'
jne .argv0_login_done
mov qword [login_flag], 1
.argv0_login_done:
cmp rdi, 1
jle .no_args
; Check argv[1]
mov rax, [rsi + 8] ; argv[1]
test rax, rax
jz .no_args
cmp word [rax], '-l'
jne .check_login_long
cmp byte [rax + 2], 0
je .set_login
.check_login_long:
cmp dword [rax], '--lo'
jne .check_help
mov qword [login_flag], 1
jmp .no_args
.check_help:
cmp dword [rax], '--he'
jne .check_bench
; Print help text and exit (no config save)
mov rax, SYS_WRITE
mov rdi, 1
lea rsi, [err_usage_bare]
mov rdx, err_usage_bare_len
syscall
xor edi, edi
mov rax, SYS_EXIT
syscall
.check_bench:
cmp dword [rax], '--be'
jne .check_c_flag
cmp word [rax+4], 'nc'
jne .check_c_flag
cmp byte [rax+6], 'h'
jne .check_c_flag
; Benchmark mode: measure and print startup time
sub rsp, 16
mov rax, SYS_CLOCK_GETTIME
mov rdi, CLOCK_MONOTONIC
mov rsi, rsp
syscall
; Calculate elapsed: (end_sec - start_sec) * 1000000 + (end_nsec - start_nsec) / 1000
mov rax, [rsp]
sub rax, [cmd_start_time]
imul rax, 1000000 ; seconds to microseconds
mov rcx, [rsp + 8]
sub rcx, [cmd_start_time + 8]
push rax
mov rax, rcx
xor edx, edx
mov rcx, 1000
cqo
idiv rcx ; nanoseconds to microseconds
pop rcx
add rax, rcx ; total microseconds
add rsp, 16
; Print result
push rax
mov rax, SYS_WRITE
mov rdi, 1
lea rsi, [.bench_pre]
mov rdx, .bench_pre_len
syscall
pop rax
lea rdi, [num_buf]
call itoa
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, 1
lea rsi, [num_buf]
syscall
mov rax, SYS_WRITE
mov rdi, 1
lea rsi, [.bench_post]
mov rdx, .bench_post_len
syscall
xor edi, edi
mov rax, SYS_EXIT
syscall
.bench_pre: db "bare startup: "
.bench_pre_len equ $ - .bench_pre
.bench_post: db " microseconds", 10
.bench_post_len equ $ - .bench_post
.check_c_flag:
cmp word [rax], '-c'
jne .check_script
cmp byte [rax + 2], 0
jne .check_script
; -c mode: argv[2] is the command
mov rax, [rsi + 16]
mov [cmd_flag], rax
jmp .no_args
.check_script:
; argv[1] is something other than a recognised flag. If it doesn't
; start with '-', treat it as a script path: open the file, dup2
; it onto stdin, and let the existing non-interactive read loop
; consume the lines. The shebang line (#!) is skipped by the
; comment-skip in .main_loop. Issue isene/bare#11.
cmp byte [rax], '-'
je .no_args ; unknown flag — ignore (current behaviour)
cmp byte [rax], 0
je .no_args ; empty argv[1]
; Open the script file (O_RDONLY = 0).
mov rdi, rax
mov rax, SYS_OPEN
xor esi, esi
xor edx, edx
syscall
test rax, rax
js .script_open_fail
; Move fd onto stdin (fd 0). dup2 closes fd 0 first.
mov rdi, rax ; old fd
push rdi
xor esi, esi ; new fd = 0
mov rax, SYS_DUP2
syscall
pop rdi
; Close the original (now we're reading via fd 0).
mov rax, SYS_CLOSE
syscall
mov qword [script_mode], 1
jmp .no_args
.script_open_fail:
; Couldn't open the file. Print a fixed-text error and exit 127.
; (We don't decode -errno; "cannot open script" is enough for the
; shebang use case where the kernel already verified the path.)
mov rax, SYS_WRITE
mov edi, 2
lea rsi, [script_err_msg]
mov rdx, script_err_msg_len
syscall
mov rax, SYS_EXIT
mov edi, 127
syscall
.set_login:
mov qword [login_flag], 1
.no_args:
; Get and cache PID
mov rax, SYS_GETPID
syscall
mov [my_pid], rax
; Initialize custom environment
call init_env_array
; Cache LS_COLORS for tab-completion file coloring. Empty if unset
; — looker is a no-op then, falls through to default unstyled
; output (no extra branches in the hot tab path).
call lscolors_init
; Initialize default colors
call init_default_colors
; Build config file path and load config (always — nicks/abbrevs/
; bookmarks affect command resolution even in non-interactive mode).
call build_config_path
call load_config
; Detect stdin tty FIRST so the rest of the init can branch on it.
; Without this gating, `echo cmd | bare` paid for history loading,
; PATH cache build, and per-prompt state every invocation — putting
; bare ~2 orders of magnitude behind bash on the