detached d6faee0

Rewrite router logic

commit
d6faee08867b9b95b1d771bc07881170013e210b
author
Brice · · 2023-09-16 06:02 UTC
parent
c686884
signature
unsigned
7 files · +2614 -17

this diff holds invisible characters (direction controls, zero-width characters). They are shown as <U+XXXX> markers, never applied.

M internal/httpserve/handler/oauth.go +1 -0
@@ -12,6 +12,7 @@ import (
1212 "github.com/markbates/goth/gothic"
1313 "github.com/markbates/goth/providers/github"
1414 "net/http"
15+ "os"
1516 )
1617
1718 var providerNames []string
@@ -12,6 +12,7 @@ import (
12 "github.com/markbates/goth/gothic"12 "github.com/markbates/goth/gothic"
13 "github.com/markbates/goth/providers/github"13 "github.com/markbates/goth/providers/github"
14 "net/http"14 "net/http"
15+ "os"
15 )16 )
16 17
17 var providerNames []string18 var providerNames []string
M internal/httpserve/router.go +17 -17
@@ -19,34 +19,19 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
1919 // Use session middleware
2020 // Add session middleware
2121 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))
22- // e.Use(middleware.SecureRoutes())
2322 e.Use(middleware.ColorSchemeDetection)
2423 e.Use(middleware.LanguageDetection)
2524
2625 // Register routes
27-
28- // Serve admin routes
2926 bindAdminRoute(e, a, AdminPath)
30- // Serve static files
3127 bindStaticRoute(e, a, "/*")
28+ bindLoginRoute(e, a, AdminPath)
29+
3230 // Protect the root path with a 403
3331 e.GET("/", func(c echo.Context) error {
3432 return c.String(403, "403 Forbidden")
3533 })
3634
37- e.GET(a.AdminPath+"/login", func(c echo.Context) error {
38- return handler.RenderLoginPage(c, a)
39- })
40- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {
41- return handler.StartOAuth(c, a)
42- })
43- e.GET(a.OauthCallbackURL, func(c echo.Context) error {
44- return handler.OAuthCallback(c, a)
45- })
46- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {
47- return handler.Logout(c, a)
48- })
49-
5035 return e
5136 }
5237
@@ -61,3 +46,18 @@ func bindAdminRoute(e *echo.Echo, a *app.App, path string) {
6146 return handler.AdminRoute(c, a)
6247 })
6348 }
49+
50+func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {
51+ e.GET(adminPath+"/login", func(c echo.Context) error {
52+ return handler.RenderLoginPage(c, a)
53+ })
54+ e.GET(adminPath+"/login/:provider", func(c echo.Context) error {
55+ return handler.StartOAuth(c, a)
56+ })
57+ e.GET(a.OauthCallbackURL, func(c echo.Context) error {
58+ return handler.OAuthCallback(c, a)
59+ })
60+ e.GET(adminPath+"/logout", func(c echo.Context) error {
61+ return handler.Logout(c, a)
62+ })
63+}
@@ -19,34 +19,19 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
19 // Use session middleware19 // Use session middleware
20 // Add session middleware20 // Add session middleware
21 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))21 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))
22- // e.Use(middleware.SecureRoutes())
23 e.Use(middleware.ColorSchemeDetection)22 e.Use(middleware.ColorSchemeDetection)
24 e.Use(middleware.LanguageDetection)23 e.Use(middleware.LanguageDetection)
25 24
26 // Register routes25 // Register routes
27-
28- // Serve admin routes
29 bindAdminRoute(e, a, AdminPath)26 bindAdminRoute(e, a, AdminPath)
30- // Serve static files
31 bindStaticRoute(e, a, "/*")27 bindStaticRoute(e, a, "/*")
28+ bindLoginRoute(e, a, AdminPath)
29+
32 // Protect the root path with a 40330 // Protect the root path with a 403
33 e.GET("/", func(c echo.Context) error {31 e.GET("/", func(c echo.Context) error {
34 return c.String(403, "403 Forbidden")32 return c.String(403, "403 Forbidden")
35 })33 })
36 34
37- e.GET(a.AdminPath+"/login", func(c echo.Context) error {
38- return handler.RenderLoginPage(c, a)
39- })
40- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {
41- return handler.StartOAuth(c, a)
42- })
43- e.GET(a.OauthCallbackURL, func(c echo.Context) error {
44- return handler.OAuthCallback(c, a)
45- })
46- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {
47- return handler.Logout(c, a)
48- })
49-
50 return e35 return e
51 }36 }
52 37
@@ -61,3 +46,18 @@ func bindAdminRoute(e *echo.Echo, a *app.App, path string) {
61 return handler.AdminRoute(c, a)46 return handler.AdminRoute(c, a)
62 })47 })
63 }48 }
49+
50+func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {
51+ e.GET(adminPath+"/login", func(c echo.Context) error {
52+ return handler.RenderLoginPage(c, a)
53+ })
54+ e.GET(adminPath+"/login/:provider", func(c echo.Context) error {
55+ return handler.StartOAuth(c, a)
56+ })
57+ e.GET(a.OauthCallbackURL, func(c echo.Context) error {
58+ return handler.OAuthCallback(c, a)
59+ })
60+ e.GET(adminPath+"/logout", func(c echo.Context) error {
61+ return handler.Logout(c, a)
62+ })
63+}
A s="flex flex-col md:flex-row justify-items-center w-full"> +404 -0
@@ -0,0 +1,404 @@
1+<U+001B>[1mdiff --git a/changes.txt b/changes.txt<U+001B>[m
2+<U+001B>[1mdeleted file mode 100644<U+001B>[m
3+<U+001B>[1mindex ab78b90..0000000<U+001B>[m
4+<U+001B>[1m--- a/changes.txt<U+001B>[m
5+<U+001B>[1m+++ /dev/null<U+001B>[m
6+<U+001B>[36m@@ -1,326 +0,0 @@<U+001B>[m
7+<U+001B>[31m-diff --git a/:memory b/:memory<U+001B>[m
8+<U+001B>[31m-new file mode 100644<U+001B>[m
9+<U+001B>[31m-index 0000000..9760229<U+001B>[m
10+<U+001B>[31m-Binary files /dev/null and b/:memory differ<U+001B>[m
11+<U+001B>[31m-diff --git a/cmd/main.go b/cmd/main.go<U+001B>[m
12+<U+001B>[31m-index 6a95d32..b8e2cd4 100644<U+001B>[m
13+<U+001B>[31m---- a/cmd/main.go<U+001B>[m
14+<U+001B>[31m-+++ b/cmd/main.go<U+001B>[m
15+<U+001B>[31m-@@ -4,17 +4,30 @@ import (<U+001B>[m
16+<U+001B>[31m- "fmt"<U+001B>[m
17+<U+001B>[31m- "log/slog"<U+001B>[m
18+<U+001B>[31m- <U+001B>[m
19+<U+001B>[31m-+ "github.com/labstack/echo/v4"<U+001B>[m
20+<U+001B>[31m-+<U+001B>[m
21+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
22+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve"<U+001B>[m
23+<U+001B>[31m-- "github.com/labstack/echo/v4"<U+001B>[m
24+<U+001B>[31m- )<U+001B>[m
25+<U+001B>[31m- <U+001B>[m
26+<U+001B>[31m- func main() {<U+001B>[m
27+<U+001B>[31m- a := app.NewApp()<U+001B>[m
28+<U+001B>[31m-+<U+001B>[m
29+<U+001B>[31m-+ // Initialize database<U+001B>[m
30+<U+001B>[31m-+ db, err := app.InitializeDB(a)<U+001B>[m
31+<U+001B>[31m-+ if err != nil {<U+001B>[m
32+<U+001B>[31m-+ slog.Error("Failed to load database", err)<U+001B>[m
33+<U+001B>[31m-+ } else {<U+001B>[m
34+<U+001B>[31m-+ slog.Info("Database loaded")<U+001B>[m
35+<U+001B>[31m-+ }<U+001B>[m
36+<U+001B>[31m-+<U+001B>[m
37+<U+001B>[31m-+ fmt.Println(db)<U+001B>[m
38+<U+001B>[31m-+<U+001B>[m
39+<U+001B>[31m- e := echo.New()<U+001B>[m
40+<U+001B>[31m- e.HideBanner = true<U+001B>[m
41+<U+001B>[31m- e.HidePort = true<U+001B>[m
42+<U+001B>[31m- e = httpserve.RegisterRoutes(e, a)<U+001B>[m
43+<U+001B>[31m- slog.Info("Starting server", "port", a.HttpPort)<U+001B>[m
44+<U+001B>[31m- slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort)))<U+001B>[m
45+<U+001B>[31m-+<U+001B>[m
46+<U+001B>[31m- }<U+001B>[m
47+<U+001B>[31m-diff --git a/go.mod b/go.mod<U+001B>[m
48+<U+001B>[31m-index 35e2d3c..8a3ac59 100644<U+001B>[m
49+<U+001B>[31m---- a/go.mod<U+001B>[m
50+<U+001B>[31m-+++ b/go.mod<U+001B>[m
51+<U+001B>[31m-@@ -24,6 +24,7 @@ require (<U+001B>[m
52+<U+001B>[31m- github.com/gorilla/securecookie v1.1.1 // indirect<U+001B>[m
53+<U+001B>[31m- github.com/gorilla/sessions v1.2.1 // indirect<U+001B>[m
54+<U+001B>[31m- github.com/klauspost/compress v1.16.7 // indirect<U+001B>[m
55+<U+001B>[31m-+ github.com/kr/text v0.1.0 // indirect<U+001B>[m
56+<U+001B>[31m- github.com/labstack/gommon v0.4.0 // indirect<U+001B>[m
57+<U+001B>[31m- github.com/mattn/go-colorable v0.1.13 // indirect<U+001B>[m
58+<U+001B>[31m- github.com/mattn/go-isatty v0.0.19 // indirect<U+001B>[m
59+<U+001B>[31m-@@ -35,6 +36,7 @@ require (<U+001B>[m
60+<U+001B>[31m- github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect<U+001B>[m
61+<U+001B>[31m- github.com/opencontainers/runc v1.1.9 // indirect<U+001B>[m
62+<U+001B>[31m- github.com/pkg/errors v0.9.1 // indirect<U+001B>[m
63+<U+001B>[31m-+ github.com/rogpeppe/go-internal v1.11.0 // indirect<U+001B>[m
64+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 // indirect<U+001B>[m
65+<U+001B>[31m- github.com/valyala/bytebufferpool v1.0.0 // indirect<U+001B>[m
66+<U+001B>[31m- github.com/valyala/fasttemplate v1.2.2 // indirect<U+001B>[m
67+<U+001B>[31m-@@ -45,5 +47,6 @@ require (<U+001B>[m
68+<U+001B>[31m- golang.org/x/text v0.11.0 // indirect<U+001B>[m
69+<U+001B>[31m- golang.org/x/time v0.3.0 // indirect<U+001B>[m
70+<U+001B>[31m- golang.org/x/tools v0.7.0 // indirect<U+001B>[m
71+<U+001B>[31m-+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect<U+001B>[m
72+<U+001B>[31m- gotest.tools/v3 v3.5.0 // indirect<U+001B>[m
73+<U+001B>[31m- )<U+001B>[m
74+<U+001B>[31m-diff --git a/go.sum b/go.sum<U+001B>[m
75+<U+001B>[31m-index 7b9b857..17b0bcd 100644<U+001B>[m
76+<U+001B>[31m---- a/go.sum<U+001B>[m
77+<U+001B>[31m-+++ b/go.sum<U+001B>[m
78+<U+001B>[31m-@@ -39,6 +39,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI<U+001B>[m
79+<U+001B>[31m- github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=<U+001B>[m
80+<U+001B>[31m- github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=<U+001B>[m
81+<U+001B>[31m- github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=<U+001B>[m
82+<U+001B>[31m-+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=<U+001B>[m
83+<U+001B>[31m-+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=<U+001B>[m
84+<U+001B>[31m-+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=<U+001B>[m
85+<U+001B>[31m-+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=<U+001B>[m
86+<U+001B>[31m-+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=<U+001B>[m
87+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU=<U+001B>[m
88+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4=<U+001B>[m
89+<U+001B>[31m- github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=<U+001B>[m
90+<U+001B>[31m-@@ -72,6 +77,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=<U+001B>[m
91+<U+001B>[31m- github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=<U+001B>[m
92+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=<U+001B>[m
93+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=<U+001B>[m
94+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=<U+001B>[m
95+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=<U+001B>[m
96+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=<U+001B>[m
97+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=<U+001B>[m
98+<U+001B>[31m- github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=<U+001B>[m
99+<U+001B>[31m-@@ -153,8 +160,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T<U+001B>[m
100+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
101+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
102+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
103+<U+001B>[31m--gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=<U+001B>[m
104+<U+001B>[31m- gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
105+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=<U+001B>[m
106+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
107+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
108+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
109+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=<U+001B>[m
110+<U+001B>[31m-diff --git a/internal/app/app.go b/internal/app/app.go<U+001B>[m
111+<U+001B>[31m-index c6f49ac..1dd44b8 100644<U+001B>[m
112+<U+001B>[31m---- a/internal/app/app.go<U+001B>[m
113+<U+001B>[31m-+++ b/internal/app/app.go<U+001B>[m
114+<U+001B>[31m-@@ -27,6 +27,7 @@ const (<U+001B>[m
115+<U+001B>[31m- type App struct {<U+001B>[m
116+<U+001B>[31m- TemplateFS fs.FS<U+001B>[m
117+<U+001B>[31m- PublicFS fs.FS<U+001B>[m
118+<U+001B>[31m-+ DB interface{}<U+001B>[m
119+<U+001B>[31m- BuildVersion string<U+001B>[m
120+<U+001B>[31m- BuildDir string<U+001B>[m
121+<U+001B>[31m- DBDir string<U+001B>[m
122+<U+001B>[31m-diff --git a/internal/app/sqlite.go b/internal/app/sqlite.go<U+001B>[m
123+<U+001B>[31m-index 9f1b1ab..ddc6a1e 100644<U+001B>[m
124+<U+001B>[31m---- a/internal/app/sqlite.go<U+001B>[m
125+<U+001B>[31m-+++ b/internal/app/sqlite.go<U+001B>[m
126+<U+001B>[31m-@@ -5,58 +5,78 @@ import (<U+001B>[m
127+<U+001B>[31m- "database/sql"<U+001B>[m
128+<U+001B>[31m- "fmt"<U+001B>[m
129+<U+001B>[31m- "os"<U+001B>[m
130+<U+001B>[31m-+ "path/filepath"<U+001B>[m
131+<U+001B>[31m- <U+001B>[m
132+<U+001B>[31m-- sqlite3 "github.com/mattn/go-sqlite3"<U+001B>[m
133+<U+001B>[31m-+ "github.com/bnema/gordon/internal/database/migrate"<U+001B>[m
134+<U+001B>[31m-+ "github.com/mattn/go-sqlite3"<U+001B>[m
135+<U+001B>[31m- )<U+001B>[m
136+<U+001B>[31m- <U+001B>[m
137+<U+001B>[31m--// Initialize in-memory SQLite database and returns its connection<U+001B>[m
138+<U+001B>[31m--func InitDB() (db *sql.DB, err error) {<U+001B>[m
139+<U+001B>[31m-- db, err = sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
140+<U+001B>[31m-- if err != nil {<U+001B>[m
141+<U+001B>[31m-- return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
142+<U+001B>[31m-- }<U+001B>[m
143+<U+001B>[31m-- return<U+001B>[m
144+<U+001B>[31m--}<U+001B>[m
145+<U+001B>[31m--<U+001B>[m
146+<U+001B>[31m--// CloseDB closes the database connection and backs up the in-memory database to disk<U+001B>[m
147+<U+001B>[31m--func CloseDB(db *sql.DB, a *App) error {<U+001B>[m
148+<U+001B>[31m-- if err := BackupDB(db, a); err != nil {<U+001B>[m
149+<U+001B>[31m-- return err<U+001B>[m
150+<U+001B>[31m-- }<U+001B>[m
151+<U+001B>[31m-+// InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory.<U+001B>[m
152+<U+001B>[31m-+// Otherwise, it creates a new in-memory database and bootstraps it.<U+001B>[m
153+<U+001B>[31m-+func InitializeDB(a *App) (*sql.DB, error) {<U+001B>[m
154+<U+001B>[31m-+ dbfp := a.GetDBFilePath()<U+001B>[m
155+<U+001B>[31m- <U+001B>[m
156+<U+001B>[31m-- if err := db.Close(); err != nil {<U+001B>[m
157+<U+001B>[31m-- return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
158+<U+001B>[31m-+ // Check if the directory exists, if not create it<U+001B>[m
159+<U+001B>[31m-+ dir := filepath.Dir(a.DBDir)<U+001B>[m
160+<U+001B>[31m-+ fmt.Println("dir", dir)<U+001B>[m
161+<U+001B>[31m-+ if _, err := os.Stat(dir); os.IsNotExist(err) {<U+001B>[m
162+<U+001B>[31m-+ err = os.MkdirAll(dir, 0755)<U+001B>[m
163+<U+001B>[31m-+ if err != nil {<U+001B>[m
164+<U+001B>[31m-+ return nil, fmt.Errorf("failed to create directory: %v", err)<U+001B>[m
165+<U+001B>[31m-+ }<U+001B>[m
166+<U+001B>[31m- }<U+001B>[m
167+<U+001B>[31m- <U+001B>[m
168+<U+001B>[31m-- return nil<U+001B>[m
169+<U+001B>[31m--}<U+001B>[m
170+<U+001B>[31m-+ // If the database file doesn't exist on disk<U+001B>[m
171+<U+001B>[31m-+ if _, err := os.Stat(dbfp); os.IsNotExist(err) {<U+001B>[m
172+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
173+<U+001B>[31m-+ if err != nil {<U+001B>[m
174+<U+001B>[31m-+ return nil, err<U+001B>[m
175+<U+001B>[31m-+ }<U+001B>[m
176+<U+001B>[31m- <U+001B>[m
177+<U+001B>[31m--// LoadDBIntoMemory loads the database from disk into memory<U+001B>[m
178+<U+001B>[31m--func LoadDBIntoMemory(a *App) (*sql.DB, error) {<U+001B>[m
179+<U+001B>[31m-- dbfp := a.GetDBFilePath()<U+001B>[m
180+<U+001B>[31m-+ // Run the bootstrap<U+001B>[m
181+<U+001B>[31m-+ RunBootstrap(memDB)<U+001B>[m
182+<U+001B>[31m- <U+001B>[m
183+<U+001B>[31m-- if _, err := os.Stat(dbfp); err != nil {<U+001B>[m
184+<U+001B>[31m-- return nil, nil<U+001B>[m
185+<U+001B>[31m-+ // Explicitly backup the in-memory database to disk<U+001B>[m
186+<U+001B>[31m-+ err = BackupDB(memDB, a)<U+001B>[m
187+<U+001B>[31m-+ if err != nil {<U+001B>[m
188+<U+001B>[31m-+ return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err)<U+001B>[m
189+<U+001B>[31m-+ }<U+001B>[m
190+<U+001B>[31m- }<U+001B>[m
191+<U+001B>[31m- <U+001B>[m
192+<U+001B>[31m-- memDB, err := InitDB()<U+001B>[m
193+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
194+<U+001B>[31m- if err != nil {<U+001B>[m
195+<U+001B>[31m- return nil, err<U+001B>[m
196+<U+001B>[31m- }<U+001B>[m
197+<U+001B>[31m--<U+001B>[m
198+<U+001B>[31m- diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
199+<U+001B>[31m- if err != nil {<U+001B>[m
200+<U+001B>[31m- return nil, fmt.Errorf("failed to open database file: %v", err)<U+001B>[m
201+<U+001B>[31m- }<U+001B>[m
202+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
203+<U+001B>[31m- <U+001B>[m
204+<U+001B>[31m- if err := transferData(diskDB, memDB); err != nil {<U+001B>[m
205+<U+001B>[31m- return nil, err<U+001B>[m
206+<U+001B>[31m- }<U+001B>[m
207+<U+001B>[31m--<U+001B>[m
208+<U+001B>[31m- return memDB, nil<U+001B>[m
209+<U+001B>[31m- }<U+001B>[m
210+<U+001B>[31m- <U+001B>[m
211+<U+001B>[31m--// transferData transfers data from srcDB to dstDB<U+001B>[m
212+<U+001B>[31m-+func initInMemoryDB() (*sql.DB, error) {<U+001B>[m
213+<U+001B>[31m-+ db, err := sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
214+<U+001B>[31m-+ if err != nil {<U+001B>[m
215+<U+001B>[31m-+ return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
216+<U+001B>[31m-+ }<U+001B>[m
217+<U+001B>[31m-+ return db, nil<U+001B>[m
218+<U+001B>[31m-+}<U+001B>[m
219+<U+001B>[31m-+<U+001B>[m
220+<U+001B>[31m-+func CloseAndBackupDB(db *sql.DB, a *App) error {<U+001B>[m
221+<U+001B>[31m-+ if err := BackupDB(db, a); err != nil {<U+001B>[m
222+<U+001B>[31m-+ return err<U+001B>[m
223+<U+001B>[31m-+ }<U+001B>[m
224+<U+001B>[31m-+ if err := db.Close(); err != nil {<U+001B>[m
225+<U+001B>[31m-+ return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
226+<U+001B>[31m-+ }<U+001B>[m
227+<U+001B>[31m-+ return nil<U+001B>[m
228+<U+001B>[31m-+}<U+001B>[m
229+<U+001B>[31m-+<U+001B>[m
230+<U+001B>[31m- func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
231+<U+001B>[31m- srcConn, err := srcDB.Conn(context.Background())<U+001B>[m
232+<U+001B>[31m- if err != nil {<U+001B>[m
233+<U+001B>[31m-@@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
234+<U+001B>[31m- return dstConn.Raw(func(dstRawConn interface{}) error {<U+001B>[m
235+<U+001B>[31m- srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
236+<U+001B>[31m- dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
237+<U+001B>[31m--<U+001B>[m
238+<U+001B>[31m- _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main")<U+001B>[m
239+<U+001B>[31m- return err<U+001B>[m
240+<U+001B>[31m- })<U+001B>[m
241+<U+001B>[31m- })<U+001B>[m
242+<U+001B>[31m- }<U+001B>[m
243+<U+001B>[31m- <U+001B>[m
244+<U+001B>[31m--// BackupDB backs up the in-memory database to disk<U+001B>[m
245+<U+001B>[31m- func BackupDB(db *sql.DB, a *App) error {<U+001B>[m
246+<U+001B>[31m- dbfp := a.GetDBFilePath()<U+001B>[m
247+<U+001B>[31m--<U+001B>[m
248+<U+001B>[31m- conn, err := db.Conn(context.Background())<U+001B>[m
249+<U+001B>[31m- if err != nil {<U+001B>[m
250+<U+001B>[31m- return fmt.Errorf("failed to get underlying database connection: %v", err)<U+001B>[m
251+<U+001B>[31m- }<U+001B>[m
252+<U+001B>[31m-+ defer conn.Close()<U+001B>[m
253+<U+001B>[31m- <U+001B>[m
254+<U+001B>[31m- return conn.Raw(func(sqliteConn interface{}) error {<U+001B>[m
255+<U+001B>[31m- sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn)<U+001B>[m
256+<U+001B>[31m-- backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main")<U+001B>[m
257+<U+001B>[31m-+ diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
258+<U+001B>[31m-+ fmt.Println("diskDB", diskDB)<U+001B>[m
259+<U+001B>[31m-+ if err != nil {<U+001B>[m
260+<U+001B>[31m-+ return fmt.Errorf("failed to open destination database file: %v", err)<U+001B>[m
261+<U+001B>[31m-+ }<U+001B>[m
262+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
263+<U+001B>[31m-+<U+001B>[m
264+<U+001B>[31m-+ diskConn, err := diskDB.Conn(context.Background())<U+001B>[m
265+<U+001B>[31m- if err != nil {<U+001B>[m
266+<U+001B>[31m-- return fmt.Errorf("failed to create backup: %v", err)<U+001B>[m
267+<U+001B>[31m-+ return fmt.Errorf("failed to get connection from diskDB: %v", err)<U+001B>[m
268+<U+001B>[31m- }<U+001B>[m
269+<U+001B>[31m-+ defer diskConn.Close()<U+001B>[m
270+<U+001B>[31m-+<U+001B>[m
271+<U+001B>[31m-+ var backupDB *sqlite3.SQLiteBackup<U+001B>[m
272+<U+001B>[31m-+ err = diskConn.Raw(func(diskRawConn interface{}) error {<U+001B>[m
273+<U+001B>[31m-+ backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main")<U+001B>[m
274+<U+001B>[31m-+ return err<U+001B>[m
275+<U+001B>[31m-+ })<U+001B>[m
276+<U+001B>[31m- defer backupDB.Close()<U+001B>[m
277+<U+001B>[31m- <U+001B>[m
278+<U+001B>[31m- _, err = backupDB.Step(-1)<U+001B>[m
279+<U+001B>[31m- if err != nil {<U+001B>[m
280+<U+001B>[31m- return fmt.Errorf("failed to backup in-memory database: %v", err)<U+001B>[m
281+<U+001B>[31m- }<U+001B>[m
282+<U+001B>[31m--<U+001B>[m
283+<U+001B>[31m- return nil<U+001B>[m
284+<U+001B>[31m- })<U+001B>[m
285+<U+001B>[31m- }<U+001B>[m
286+<U+001B>[31m-+<U+001B>[m
287+<U+001B>[31m-+func RunBootstrap(db *sql.DB) error {<U+001B>[m
288+<U+001B>[31m-+ return migrate.CreateUserTable(db)<U+001B>[m
289+<U+001B>[31m-+}<U+001B>[m
290+<U+001B>[31m-diff --git a/internal/database/migrate/bootstrapdb.go b/internal/database/migrate/bootstrapdb.go<U+001B>[m
291+<U+001B>[31m-new file mode 100644<U+001B>[m
292+<U+001B>[31m-index 0000000..1f53246<U+001B>[m
293+<U+001B>[31m---- /dev/null<U+001B>[m
294+<U+001B>[31m-+++ b/internal/database/migrate/bootstrapdb.go<U+001B>[m
295+<U+001B>[31m-@@ -0,0 +1,24 @@<U+001B>[m
296+<U+001B>[31m-+package migrate<U+001B>[m
297+<U+001B>[31m-+<U+001B>[m
298+<U+001B>[31m-+import (<U+001B>[m
299+<U+001B>[31m-+ "database/sql"<U+001B>[m
300+<U+001B>[31m-+ "fmt"<U+001B>[m
301+<U+001B>[31m-+)<U+001B>[m
302+<U+001B>[31m-+<U+001B>[m
303+<U+001B>[31m-+// createUserTable creates the 'user' table in the provided SQLite database.<U+001B>[m
304+<U+001B>[31m-+func CreateUserTable(db *sql.DB) error {<U+001B>[m
305+<U+001B>[31m-+ createTableSQL := `<U+001B>[m
306+<U+001B>[31m-+ CREATE TABLE user (<U+001B>[m
307+<U+001B>[31m-+ id INTEGER PRIMARY KEY AUTOINCREMENT,<U+001B>[m
308+<U+001B>[31m-+ username TEXT NOT NULL,<U+001B>[m
309+<U+001B>[31m-+ password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario.<U+001B>[m
310+<U+001B>[31m-+ oauth_token TEXT<U+001B>[m
311+<U+001B>[31m-+ );`<U+001B>[m
312+<U+001B>[31m-+<U+001B>[m
313+<U+001B>[31m-+ _, err := db.Exec(createTableSQL)<U+001B>[m
314+<U+001B>[31m-+ if err != nil {<U+001B>[m
315+<U+001B>[31m-+ return fmt.Errorf("failed to create user table: %v", err)<U+001B>[m
316+<U+001B>[31m-+ }<U+001B>[m
317+<U+001B>[31m-+<U+001B>[m
318+<U+001B>[31m-+ return nil<U+001B>[m
319+<U+001B>[31m-+}<U+001B>[m
320+<U+001B>[31m-diff --git a/internal/httpserve/handler/adminroutes.go b/internal/httpserve/handler/adminroutes.go<U+001B>[m
321+<U+001B>[31m-index 8701ae3..f4acd64 100644<U+001B>[m
322+<U+001B>[31m---- a/internal/httpserve/handler/adminroutes.go<U+001B>[m
323+<U+001B>[31m-+++ b/internal/httpserve/handler/adminroutes.go<U+001B>[m
324+<U+001B>[31m-@@ -1,8 +1,6 @@<U+001B>[m
325+<U+001B>[31m- package handler<U+001B>[m
326+<U+001B>[31m- <U+001B>[m
327+<U+001B>[31m- import (<U+001B>[m
328+<U+001B>[31m-- "fmt"<U+001B>[m
329+<U+001B>[31m--<U+001B>[m
330+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
331+<U+001B>[31m- "github.com/bnema/gordon/internal/gotemplate/render"<U+001B>[m
332+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve/middleware"<U+001B>[m
333+<U+001B>[1mdiff --git a/internal/httpserve/handler/oauth.go b/internal/httpserve/handler/oauth.go<U+001B>[m
334+<U+001B>[1mindex bd40379..b876c75 100644<U+001B>[m
335+<U+001B>[1m--- a/internal/httpserve/handler/oauth.go<U+001B>[m
336+<U+001B>[1m+++ b/internal/httpserve/handler/oauth.go<U+001B>[m
337+<U+001B>[36m@@ -12,6 +12,7 @@<U+001B>[m <U+001B>[mimport (<U+001B>[m
338+ "github.com/markbates/goth/gothic"<U+001B>[m
339+ "github.com/markbates/goth/providers/github"<U+001B>[m
340+ "net/http"<U+001B>[m
341+<U+001B>[32m+<U+001B>[m <U+001B>[32m"os"<U+001B>[m
342+ )<U+001B>[m
343+ <U+001B>[m
344+ var providerNames []string<U+001B>[m
345+<U+001B>[1mdiff --git a/internal/httpserve/router.go b/internal/httpserve/router.go<U+001B>[m
346+<U+001B>[1mindex bc1368c..edd2748 100644<U+001B>[m
347+<U+001B>[1m--- a/internal/httpserve/router.go<U+001B>[m
348+<U+001B>[1m+++ b/internal/httpserve/router.go<U+001B>[m
349+<U+001B>[36m@@ -19,34 +19,19 @@<U+001B>[m <U+001B>[mfunc RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {<U+001B>[m
350+ // Use session middleware<U+001B>[m
351+ // Add session middleware<U+001B>[m
352+ e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))<U+001B>[m
353+<U+001B>[31m- // e.Use(middleware.SecureRoutes())<U+001B>[m
354+ e.Use(middleware.ColorSchemeDetection)<U+001B>[m
355+ e.Use(middleware.LanguageDetection)<U+001B>[m
356+ <U+001B>[m
357+ // Register routes<U+001B>[m
358+<U+001B>[31m-<U+001B>[m
359+<U+001B>[31m- // Serve admin routes<U+001B>[m
360+ bindAdminRoute(e, a, AdminPath)<U+001B>[m
361+<U+001B>[31m- // Serve static files<U+001B>[m
362+ bindStaticRoute(e, a, "/*")<U+001B>[m
363+<U+001B>[32m+<U+001B>[m <U+001B>[32mbindLoginRoute(e, a, AdminPath)<U+001B>[m
364+<U+001B>[32m+<U+001B>[m
365+ // Protect the root path with a 403<U+001B>[m
366+ e.GET("/", func(c echo.Context) error {<U+001B>[m
367+ return c.String(403, "403 Forbidden")<U+001B>[m
368+ })<U+001B>[m
369+ <U+001B>[m
370+<U+001B>[31m- e.GET(a.AdminPath+"/login", func(c echo.Context) error {<U+001B>[m
371+<U+001B>[31m- return handler.RenderLoginPage(c, a)<U+001B>[m
372+<U+001B>[31m- })<U+001B>[m
373+<U+001B>[31m- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
374+<U+001B>[31m- return handler.StartOAuth(c, a)<U+001B>[m
375+<U+001B>[31m- })<U+001B>[m
376+<U+001B>[31m- e.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
377+<U+001B>[31m- return handler.OAuthCallback(c, a)<U+001B>[m
378+<U+001B>[31m- })<U+001B>[m
379+<U+001B>[31m- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {<U+001B>[m
380+<U+001B>[31m- return handler.Logout(c, a)<U+001B>[m
381+<U+001B>[31m- })<U+001B>[m
382+<U+001B>[31m-<U+001B>[m
383+ return e<U+001B>[m
384+ }<U+001B>[m
385+ <U+001B>[m
386+<U+001B>[36m@@ -61,3 +46,18 @@<U+001B>[m <U+001B>[mfunc bindAdminRoute(e *echo.Echo, a *app.App, path string) {<U+001B>[m
387+ return handler.AdminRoute(c, a)<U+001B>[m
388+ })<U+001B>[m
389+ }<U+001B>[m
390+<U+001B>[32m+<U+001B>[m
391+<U+001B>[32m+<U+001B>[m<U+001B>[32mfunc bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {<U+001B>[m
392+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login", func(c echo.Context) error {<U+001B>[m
393+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.RenderLoginPage(c, a)<U+001B>[m
394+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
395+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
396+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.StartOAuth(c, a)<U+001B>[m
397+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
398+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
399+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.OAuthCallback(c, a)<U+001B>[m
400+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
401+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/logout", func(c echo.Context) error {<U+001B>[m
402+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.Logout(c, a)<U+001B>[m
403+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
404+<U+001B>[32m+<U+001B>[m<U+001B>[32m}<U+001B>[m
@@ -0,0 +1,404 @@
1+<U+001B>[1mdiff --git a/changes.txt b/changes.txt<U+001B>[m
2+<U+001B>[1mdeleted file mode 100644<U+001B>[m
3+<U+001B>[1mindex ab78b90..0000000<U+001B>[m
4+<U+001B>[1m--- a/changes.txt<U+001B>[m
5+<U+001B>[1m+++ /dev/null<U+001B>[m
6+<U+001B>[36m@@ -1,326 +0,0 @@<U+001B>[m
7+<U+001B>[31m-diff --git a/:memory b/:memory<U+001B>[m
8+<U+001B>[31m-new file mode 100644<U+001B>[m
9+<U+001B>[31m-index 0000000..9760229<U+001B>[m
10+<U+001B>[31m-Binary files /dev/null and b/:memory differ<U+001B>[m
11+<U+001B>[31m-diff --git a/cmd/main.go b/cmd/main.go<U+001B>[m
12+<U+001B>[31m-index 6a95d32..b8e2cd4 100644<U+001B>[m
13+<U+001B>[31m---- a/cmd/main.go<U+001B>[m
14+<U+001B>[31m-+++ b/cmd/main.go<U+001B>[m
15+<U+001B>[31m-@@ -4,17 +4,30 @@ import (<U+001B>[m
16+<U+001B>[31m- "fmt"<U+001B>[m
17+<U+001B>[31m- "log/slog"<U+001B>[m
18+<U+001B>[31m- <U+001B>[m
19+<U+001B>[31m-+ "github.com/labstack/echo/v4"<U+001B>[m
20+<U+001B>[31m-+<U+001B>[m
21+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
22+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve"<U+001B>[m
23+<U+001B>[31m-- "github.com/labstack/echo/v4"<U+001B>[m
24+<U+001B>[31m- )<U+001B>[m
25+<U+001B>[31m- <U+001B>[m
26+<U+001B>[31m- func main() {<U+001B>[m
27+<U+001B>[31m- a := app.NewApp()<U+001B>[m
28+<U+001B>[31m-+<U+001B>[m
29+<U+001B>[31m-+ // Initialize database<U+001B>[m
30+<U+001B>[31m-+ db, err := app.InitializeDB(a)<U+001B>[m
31+<U+001B>[31m-+ if err != nil {<U+001B>[m
32+<U+001B>[31m-+ slog.Error("Failed to load database", err)<U+001B>[m
33+<U+001B>[31m-+ } else {<U+001B>[m
34+<U+001B>[31m-+ slog.Info("Database loaded")<U+001B>[m
35+<U+001B>[31m-+ }<U+001B>[m
36+<U+001B>[31m-+<U+001B>[m
37+<U+001B>[31m-+ fmt.Println(db)<U+001B>[m
38+<U+001B>[31m-+<U+001B>[m
39+<U+001B>[31m- e := echo.New()<U+001B>[m
40+<U+001B>[31m- e.HideBanner = true<U+001B>[m
41+<U+001B>[31m- e.HidePort = true<U+001B>[m
42+<U+001B>[31m- e = httpserve.RegisterRoutes(e, a)<U+001B>[m
43+<U+001B>[31m- slog.Info("Starting server", "port", a.HttpPort)<U+001B>[m
44+<U+001B>[31m- slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort)))<U+001B>[m
45+<U+001B>[31m-+<U+001B>[m
46+<U+001B>[31m- }<U+001B>[m
47+<U+001B>[31m-diff --git a/go.mod b/go.mod<U+001B>[m
48+<U+001B>[31m-index 35e2d3c..8a3ac59 100644<U+001B>[m
49+<U+001B>[31m---- a/go.mod<U+001B>[m
50+<U+001B>[31m-+++ b/go.mod<U+001B>[m
51+<U+001B>[31m-@@ -24,6 +24,7 @@ require (<U+001B>[m
52+<U+001B>[31m- github.com/gorilla/securecookie v1.1.1 // indirect<U+001B>[m
53+<U+001B>[31m- github.com/gorilla/sessions v1.2.1 // indirect<U+001B>[m
54+<U+001B>[31m- github.com/klauspost/compress v1.16.7 // indirect<U+001B>[m
55+<U+001B>[31m-+ github.com/kr/text v0.1.0 // indirect<U+001B>[m
56+<U+001B>[31m- github.com/labstack/gommon v0.4.0 // indirect<U+001B>[m
57+<U+001B>[31m- github.com/mattn/go-colorable v0.1.13 // indirect<U+001B>[m
58+<U+001B>[31m- github.com/mattn/go-isatty v0.0.19 // indirect<U+001B>[m
59+<U+001B>[31m-@@ -35,6 +36,7 @@ require (<U+001B>[m
60+<U+001B>[31m- github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect<U+001B>[m
61+<U+001B>[31m- github.com/opencontainers/runc v1.1.9 // indirect<U+001B>[m
62+<U+001B>[31m- github.com/pkg/errors v0.9.1 // indirect<U+001B>[m
63+<U+001B>[31m-+ github.com/rogpeppe/go-internal v1.11.0 // indirect<U+001B>[m
64+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 // indirect<U+001B>[m
65+<U+001B>[31m- github.com/valyala/bytebufferpool v1.0.0 // indirect<U+001B>[m
66+<U+001B>[31m- github.com/valyala/fasttemplate v1.2.2 // indirect<U+001B>[m
67+<U+001B>[31m-@@ -45,5 +47,6 @@ require (<U+001B>[m
68+<U+001B>[31m- golang.org/x/text v0.11.0 // indirect<U+001B>[m
69+<U+001B>[31m- golang.org/x/time v0.3.0 // indirect<U+001B>[m
70+<U+001B>[31m- golang.org/x/tools v0.7.0 // indirect<U+001B>[m
71+<U+001B>[31m-+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect<U+001B>[m
72+<U+001B>[31m- gotest.tools/v3 v3.5.0 // indirect<U+001B>[m
73+<U+001B>[31m- )<U+001B>[m
74+<U+001B>[31m-diff --git a/go.sum b/go.sum<U+001B>[m
75+<U+001B>[31m-index 7b9b857..17b0bcd 100644<U+001B>[m
76+<U+001B>[31m---- a/go.sum<U+001B>[m
77+<U+001B>[31m-+++ b/go.sum<U+001B>[m
78+<U+001B>[31m-@@ -39,6 +39,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI<U+001B>[m
79+<U+001B>[31m- github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=<U+001B>[m
80+<U+001B>[31m- github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=<U+001B>[m
81+<U+001B>[31m- github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=<U+001B>[m
82+<U+001B>[31m-+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=<U+001B>[m
83+<U+001B>[31m-+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=<U+001B>[m
84+<U+001B>[31m-+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=<U+001B>[m
85+<U+001B>[31m-+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=<U+001B>[m
86+<U+001B>[31m-+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=<U+001B>[m
87+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU=<U+001B>[m
88+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4=<U+001B>[m
89+<U+001B>[31m- github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=<U+001B>[m
90+<U+001B>[31m-@@ -72,6 +77,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=<U+001B>[m
91+<U+001B>[31m- github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=<U+001B>[m
92+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=<U+001B>[m
93+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=<U+001B>[m
94+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=<U+001B>[m
95+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=<U+001B>[m
96+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=<U+001B>[m
97+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=<U+001B>[m
98+<U+001B>[31m- github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=<U+001B>[m
99+<U+001B>[31m-@@ -153,8 +160,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T<U+001B>[m
100+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
101+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
102+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
103+<U+001B>[31m--gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=<U+001B>[m
104+<U+001B>[31m- gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
105+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=<U+001B>[m
106+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
107+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
108+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
109+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=<U+001B>[m
110+<U+001B>[31m-diff --git a/internal/app/app.go b/internal/app/app.go<U+001B>[m
111+<U+001B>[31m-index c6f49ac..1dd44b8 100644<U+001B>[m
112+<U+001B>[31m---- a/internal/app/app.go<U+001B>[m
113+<U+001B>[31m-+++ b/internal/app/app.go<U+001B>[m
114+<U+001B>[31m-@@ -27,6 +27,7 @@ const (<U+001B>[m
115+<U+001B>[31m- type App struct {<U+001B>[m
116+<U+001B>[31m- TemplateFS fs.FS<U+001B>[m
117+<U+001B>[31m- PublicFS fs.FS<U+001B>[m
118+<U+001B>[31m-+ DB interface{}<U+001B>[m
119+<U+001B>[31m- BuildVersion string<U+001B>[m
120+<U+001B>[31m- BuildDir string<U+001B>[m
121+<U+001B>[31m- DBDir string<U+001B>[m
122+<U+001B>[31m-diff --git a/internal/app/sqlite.go b/internal/app/sqlite.go<U+001B>[m
123+<U+001B>[31m-index 9f1b1ab..ddc6a1e 100644<U+001B>[m
124+<U+001B>[31m---- a/internal/app/sqlite.go<U+001B>[m
125+<U+001B>[31m-+++ b/internal/app/sqlite.go<U+001B>[m
126+<U+001B>[31m-@@ -5,58 +5,78 @@ import (<U+001B>[m
127+<U+001B>[31m- "database/sql"<U+001B>[m
128+<U+001B>[31m- "fmt"<U+001B>[m
129+<U+001B>[31m- "os"<U+001B>[m
130+<U+001B>[31m-+ "path/filepath"<U+001B>[m
131+<U+001B>[31m- <U+001B>[m
132+<U+001B>[31m-- sqlite3 "github.com/mattn/go-sqlite3"<U+001B>[m
133+<U+001B>[31m-+ "github.com/bnema/gordon/internal/database/migrate"<U+001B>[m
134+<U+001B>[31m-+ "github.com/mattn/go-sqlite3"<U+001B>[m
135+<U+001B>[31m- )<U+001B>[m
136+<U+001B>[31m- <U+001B>[m
137+<U+001B>[31m--// Initialize in-memory SQLite database and returns its connection<U+001B>[m
138+<U+001B>[31m--func InitDB() (db *sql.DB, err error) {<U+001B>[m
139+<U+001B>[31m-- db, err = sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
140+<U+001B>[31m-- if err != nil {<U+001B>[m
141+<U+001B>[31m-- return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
142+<U+001B>[31m-- }<U+001B>[m
143+<U+001B>[31m-- return<U+001B>[m
144+<U+001B>[31m--}<U+001B>[m
145+<U+001B>[31m--<U+001B>[m
146+<U+001B>[31m--// CloseDB closes the database connection and backs up the in-memory database to disk<U+001B>[m
147+<U+001B>[31m--func CloseDB(db *sql.DB, a *App) error {<U+001B>[m
148+<U+001B>[31m-- if err := BackupDB(db, a); err != nil {<U+001B>[m
149+<U+001B>[31m-- return err<U+001B>[m
150+<U+001B>[31m-- }<U+001B>[m
151+<U+001B>[31m-+// InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory.<U+001B>[m
152+<U+001B>[31m-+// Otherwise, it creates a new in-memory database and bootstraps it.<U+001B>[m
153+<U+001B>[31m-+func InitializeDB(a *App) (*sql.DB, error) {<U+001B>[m
154+<U+001B>[31m-+ dbfp := a.GetDBFilePath()<U+001B>[m
155+<U+001B>[31m- <U+001B>[m
156+<U+001B>[31m-- if err := db.Close(); err != nil {<U+001B>[m
157+<U+001B>[31m-- return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
158+<U+001B>[31m-+ // Check if the directory exists, if not create it<U+001B>[m
159+<U+001B>[31m-+ dir := filepath.Dir(a.DBDir)<U+001B>[m
160+<U+001B>[31m-+ fmt.Println("dir", dir)<U+001B>[m
161+<U+001B>[31m-+ if _, err := os.Stat(dir); os.IsNotExist(err) {<U+001B>[m
162+<U+001B>[31m-+ err = os.MkdirAll(dir, 0755)<U+001B>[m
163+<U+001B>[31m-+ if err != nil {<U+001B>[m
164+<U+001B>[31m-+ return nil, fmt.Errorf("failed to create directory: %v", err)<U+001B>[m
165+<U+001B>[31m-+ }<U+001B>[m
166+<U+001B>[31m- }<U+001B>[m
167+<U+001B>[31m- <U+001B>[m
168+<U+001B>[31m-- return nil<U+001B>[m
169+<U+001B>[31m--}<U+001B>[m
170+<U+001B>[31m-+ // If the database file doesn't exist on disk<U+001B>[m
171+<U+001B>[31m-+ if _, err := os.Stat(dbfp); os.IsNotExist(err) {<U+001B>[m
172+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
173+<U+001B>[31m-+ if err != nil {<U+001B>[m
174+<U+001B>[31m-+ return nil, err<U+001B>[m
175+<U+001B>[31m-+ }<U+001B>[m
176+<U+001B>[31m- <U+001B>[m
177+<U+001B>[31m--// LoadDBIntoMemory loads the database from disk into memory<U+001B>[m
178+<U+001B>[31m--func LoadDBIntoMemory(a *App) (*sql.DB, error) {<U+001B>[m
179+<U+001B>[31m-- dbfp := a.GetDBFilePath()<U+001B>[m
180+<U+001B>[31m-+ // Run the bootstrap<U+001B>[m
181+<U+001B>[31m-+ RunBootstrap(memDB)<U+001B>[m
182+<U+001B>[31m- <U+001B>[m
183+<U+001B>[31m-- if _, err := os.Stat(dbfp); err != nil {<U+001B>[m
184+<U+001B>[31m-- return nil, nil<U+001B>[m
185+<U+001B>[31m-+ // Explicitly backup the in-memory database to disk<U+001B>[m
186+<U+001B>[31m-+ err = BackupDB(memDB, a)<U+001B>[m
187+<U+001B>[31m-+ if err != nil {<U+001B>[m
188+<U+001B>[31m-+ return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err)<U+001B>[m
189+<U+001B>[31m-+ }<U+001B>[m
190+<U+001B>[31m- }<U+001B>[m
191+<U+001B>[31m- <U+001B>[m
192+<U+001B>[31m-- memDB, err := InitDB()<U+001B>[m
193+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
194+<U+001B>[31m- if err != nil {<U+001B>[m
195+<U+001B>[31m- return nil, err<U+001B>[m
196+<U+001B>[31m- }<U+001B>[m
197+<U+001B>[31m--<U+001B>[m
198+<U+001B>[31m- diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
199+<U+001B>[31m- if err != nil {<U+001B>[m
200+<U+001B>[31m- return nil, fmt.Errorf("failed to open database file: %v", err)<U+001B>[m
201+<U+001B>[31m- }<U+001B>[m
202+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
203+<U+001B>[31m- <U+001B>[m
204+<U+001B>[31m- if err := transferData(diskDB, memDB); err != nil {<U+001B>[m
205+<U+001B>[31m- return nil, err<U+001B>[m
206+<U+001B>[31m- }<U+001B>[m
207+<U+001B>[31m--<U+001B>[m
208+<U+001B>[31m- return memDB, nil<U+001B>[m
209+<U+001B>[31m- }<U+001B>[m
210+<U+001B>[31m- <U+001B>[m
211+<U+001B>[31m--// transferData transfers data from srcDB to dstDB<U+001B>[m
212+<U+001B>[31m-+func initInMemoryDB() (*sql.DB, error) {<U+001B>[m
213+<U+001B>[31m-+ db, err := sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
214+<U+001B>[31m-+ if err != nil {<U+001B>[m
215+<U+001B>[31m-+ return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
216+<U+001B>[31m-+ }<U+001B>[m
217+<U+001B>[31m-+ return db, nil<U+001B>[m
218+<U+001B>[31m-+}<U+001B>[m
219+<U+001B>[31m-+<U+001B>[m
220+<U+001B>[31m-+func CloseAndBackupDB(db *sql.DB, a *App) error {<U+001B>[m
221+<U+001B>[31m-+ if err := BackupDB(db, a); err != nil {<U+001B>[m
222+<U+001B>[31m-+ return err<U+001B>[m
223+<U+001B>[31m-+ }<U+001B>[m
224+<U+001B>[31m-+ if err := db.Close(); err != nil {<U+001B>[m
225+<U+001B>[31m-+ return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
226+<U+001B>[31m-+ }<U+001B>[m
227+<U+001B>[31m-+ return nil<U+001B>[m
228+<U+001B>[31m-+}<U+001B>[m
229+<U+001B>[31m-+<U+001B>[m
230+<U+001B>[31m- func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
231+<U+001B>[31m- srcConn, err := srcDB.Conn(context.Background())<U+001B>[m
232+<U+001B>[31m- if err != nil {<U+001B>[m
233+<U+001B>[31m-@@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
234+<U+001B>[31m- return dstConn.Raw(func(dstRawConn interface{}) error {<U+001B>[m
235+<U+001B>[31m- srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
236+<U+001B>[31m- dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
237+<U+001B>[31m--<U+001B>[m
238+<U+001B>[31m- _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main")<U+001B>[m
239+<U+001B>[31m- return err<U+001B>[m
240+<U+001B>[31m- })<U+001B>[m
241+<U+001B>[31m- })<U+001B>[m
242+<U+001B>[31m- }<U+001B>[m
243+<U+001B>[31m- <U+001B>[m
244+<U+001B>[31m--// BackupDB backs up the in-memory database to disk<U+001B>[m
245+<U+001B>[31m- func BackupDB(db *sql.DB, a *App) error {<U+001B>[m
246+<U+001B>[31m- dbfp := a.GetDBFilePath()<U+001B>[m
247+<U+001B>[31m--<U+001B>[m
248+<U+001B>[31m- conn, err := db.Conn(context.Background())<U+001B>[m
249+<U+001B>[31m- if err != nil {<U+001B>[m
250+<U+001B>[31m- return fmt.Errorf("failed to get underlying database connection: %v", err)<U+001B>[m
251+<U+001B>[31m- }<U+001B>[m
252+<U+001B>[31m-+ defer conn.Close()<U+001B>[m
253+<U+001B>[31m- <U+001B>[m
254+<U+001B>[31m- return conn.Raw(func(sqliteConn interface{}) error {<U+001B>[m
255+<U+001B>[31m- sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn)<U+001B>[m
256+<U+001B>[31m-- backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main")<U+001B>[m
257+<U+001B>[31m-+ diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
258+<U+001B>[31m-+ fmt.Println("diskDB", diskDB)<U+001B>[m
259+<U+001B>[31m-+ if err != nil {<U+001B>[m
260+<U+001B>[31m-+ return fmt.Errorf("failed to open destination database file: %v", err)<U+001B>[m
261+<U+001B>[31m-+ }<U+001B>[m
262+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
263+<U+001B>[31m-+<U+001B>[m
264+<U+001B>[31m-+ diskConn, err := diskDB.Conn(context.Background())<U+001B>[m
265+<U+001B>[31m- if err != nil {<U+001B>[m
266+<U+001B>[31m-- return fmt.Errorf("failed to create backup: %v", err)<U+001B>[m
267+<U+001B>[31m-+ return fmt.Errorf("failed to get connection from diskDB: %v", err)<U+001B>[m
268+<U+001B>[31m- }<U+001B>[m
269+<U+001B>[31m-+ defer diskConn.Close()<U+001B>[m
270+<U+001B>[31m-+<U+001B>[m
271+<U+001B>[31m-+ var backupDB *sqlite3.SQLiteBackup<U+001B>[m
272+<U+001B>[31m-+ err = diskConn.Raw(func(diskRawConn interface{}) error {<U+001B>[m
273+<U+001B>[31m-+ backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main")<U+001B>[m
274+<U+001B>[31m-+ return err<U+001B>[m
275+<U+001B>[31m-+ })<U+001B>[m
276+<U+001B>[31m- defer backupDB.Close()<U+001B>[m
277+<U+001B>[31m- <U+001B>[m
278+<U+001B>[31m- _, err = backupDB.Step(-1)<U+001B>[m
279+<U+001B>[31m- if err != nil {<U+001B>[m
280+<U+001B>[31m- return fmt.Errorf("failed to backup in-memory database: %v", err)<U+001B>[m
281+<U+001B>[31m- }<U+001B>[m
282+<U+001B>[31m--<U+001B>[m
283+<U+001B>[31m- return nil<U+001B>[m
284+<U+001B>[31m- })<U+001B>[m
285+<U+001B>[31m- }<U+001B>[m
286+<U+001B>[31m-+<U+001B>[m
287+<U+001B>[31m-+func RunBootstrap(db *sql.DB) error {<U+001B>[m
288+<U+001B>[31m-+ return migrate.CreateUserTable(db)<U+001B>[m
289+<U+001B>[31m-+}<U+001B>[m
290+<U+001B>[31m-diff --git a/internal/database/migrate/bootstrapdb.go b/internal/database/migrate/bootstrapdb.go<U+001B>[m
291+<U+001B>[31m-new file mode 100644<U+001B>[m
292+<U+001B>[31m-index 0000000..1f53246<U+001B>[m
293+<U+001B>[31m---- /dev/null<U+001B>[m
294+<U+001B>[31m-+++ b/internal/database/migrate/bootstrapdb.go<U+001B>[m
295+<U+001B>[31m-@@ -0,0 +1,24 @@<U+001B>[m
296+<U+001B>[31m-+package migrate<U+001B>[m
297+<U+001B>[31m-+<U+001B>[m
298+<U+001B>[31m-+import (<U+001B>[m
299+<U+001B>[31m-+ "database/sql"<U+001B>[m
300+<U+001B>[31m-+ "fmt"<U+001B>[m
301+<U+001B>[31m-+)<U+001B>[m
302+<U+001B>[31m-+<U+001B>[m
303+<U+001B>[31m-+// createUserTable creates the 'user' table in the provided SQLite database.<U+001B>[m
304+<U+001B>[31m-+func CreateUserTable(db *sql.DB) error {<U+001B>[m
305+<U+001B>[31m-+ createTableSQL := `<U+001B>[m
306+<U+001B>[31m-+ CREATE TABLE user (<U+001B>[m
307+<U+001B>[31m-+ id INTEGER PRIMARY KEY AUTOINCREMENT,<U+001B>[m
308+<U+001B>[31m-+ username TEXT NOT NULL,<U+001B>[m
309+<U+001B>[31m-+ password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario.<U+001B>[m
310+<U+001B>[31m-+ oauth_token TEXT<U+001B>[m
311+<U+001B>[31m-+ );`<U+001B>[m
312+<U+001B>[31m-+<U+001B>[m
313+<U+001B>[31m-+ _, err := db.Exec(createTableSQL)<U+001B>[m
314+<U+001B>[31m-+ if err != nil {<U+001B>[m
315+<U+001B>[31m-+ return fmt.Errorf("failed to create user table: %v", err)<U+001B>[m
316+<U+001B>[31m-+ }<U+001B>[m
317+<U+001B>[31m-+<U+001B>[m
318+<U+001B>[31m-+ return nil<U+001B>[m
319+<U+001B>[31m-+}<U+001B>[m
320+<U+001B>[31m-diff --git a/internal/httpserve/handler/adminroutes.go b/internal/httpserve/handler/adminroutes.go<U+001B>[m
321+<U+001B>[31m-index 8701ae3..f4acd64 100644<U+001B>[m
322+<U+001B>[31m---- a/internal/httpserve/handler/adminroutes.go<U+001B>[m
323+<U+001B>[31m-+++ b/internal/httpserve/handler/adminroutes.go<U+001B>[m
324+<U+001B>[31m-@@ -1,8 +1,6 @@<U+001B>[m
325+<U+001B>[31m- package handler<U+001B>[m
326+<U+001B>[31m- <U+001B>[m
327+<U+001B>[31m- import (<U+001B>[m
328+<U+001B>[31m-- "fmt"<U+001B>[m
329+<U+001B>[31m--<U+001B>[m
330+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
331+<U+001B>[31m- "github.com/bnema/gordon/internal/gotemplate/render"<U+001B>[m
332+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve/middleware"<U+001B>[m
333+<U+001B>[1mdiff --git a/internal/httpserve/handler/oauth.go b/internal/httpserve/handler/oauth.go<U+001B>[m
334+<U+001B>[1mindex bd40379..b876c75 100644<U+001B>[m
335+<U+001B>[1m--- a/internal/httpserve/handler/oauth.go<U+001B>[m
336+<U+001B>[1m+++ b/internal/httpserve/handler/oauth.go<U+001B>[m
337+<U+001B>[36m@@ -12,6 +12,7 @@<U+001B>[m <U+001B>[mimport (<U+001B>[m
338+ "github.com/markbates/goth/gothic"<U+001B>[m
339+ "github.com/markbates/goth/providers/github"<U+001B>[m
340+ "net/http"<U+001B>[m
341+<U+001B>[32m+<U+001B>[m <U+001B>[32m"os"<U+001B>[m
342+ )<U+001B>[m
343+ <U+001B>[m
344+ var providerNames []string<U+001B>[m
345+<U+001B>[1mdiff --git a/internal/httpserve/router.go b/internal/httpserve/router.go<U+001B>[m
346+<U+001B>[1mindex bc1368c..edd2748 100644<U+001B>[m
347+<U+001B>[1m--- a/internal/httpserve/router.go<U+001B>[m
348+<U+001B>[1m+++ b/internal/httpserve/router.go<U+001B>[m
349+<U+001B>[36m@@ -19,34 +19,19 @@<U+001B>[m <U+001B>[mfunc RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {<U+001B>[m
350+ // Use session middleware<U+001B>[m
351+ // Add session middleware<U+001B>[m
352+ e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))<U+001B>[m
353+<U+001B>[31m- // e.Use(middleware.SecureRoutes())<U+001B>[m
354+ e.Use(middleware.ColorSchemeDetection)<U+001B>[m
355+ e.Use(middleware.LanguageDetection)<U+001B>[m
356+ <U+001B>[m
357+ // Register routes<U+001B>[m
358+<U+001B>[31m-<U+001B>[m
359+<U+001B>[31m- // Serve admin routes<U+001B>[m
360+ bindAdminRoute(e, a, AdminPath)<U+001B>[m
361+<U+001B>[31m- // Serve static files<U+001B>[m
362+ bindStaticRoute(e, a, "/*")<U+001B>[m
363+<U+001B>[32m+<U+001B>[m <U+001B>[32mbindLoginRoute(e, a, AdminPath)<U+001B>[m
364+<U+001B>[32m+<U+001B>[m
365+ // Protect the root path with a 403<U+001B>[m
366+ e.GET("/", func(c echo.Context) error {<U+001B>[m
367+ return c.String(403, "403 Forbidden")<U+001B>[m
368+ })<U+001B>[m
369+ <U+001B>[m
370+<U+001B>[31m- e.GET(a.AdminPath+"/login", func(c echo.Context) error {<U+001B>[m
371+<U+001B>[31m- return handler.RenderLoginPage(c, a)<U+001B>[m
372+<U+001B>[31m- })<U+001B>[m
373+<U+001B>[31m- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
374+<U+001B>[31m- return handler.StartOAuth(c, a)<U+001B>[m
375+<U+001B>[31m- })<U+001B>[m
376+<U+001B>[31m- e.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
377+<U+001B>[31m- return handler.OAuthCallback(c, a)<U+001B>[m
378+<U+001B>[31m- })<U+001B>[m
379+<U+001B>[31m- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {<U+001B>[m
380+<U+001B>[31m- return handler.Logout(c, a)<U+001B>[m
381+<U+001B>[31m- })<U+001B>[m
382+<U+001B>[31m-<U+001B>[m
383+ return e<U+001B>[m
384+ }<U+001B>[m
385+ <U+001B>[m
386+<U+001B>[36m@@ -61,3 +46,18 @@<U+001B>[m <U+001B>[mfunc bindAdminRoute(e *echo.Echo, a *app.App, path string) {<U+001B>[m
387+ return handler.AdminRoute(c, a)<U+001B>[m
388+ })<U+001B>[m
389+ }<U+001B>[m
390+<U+001B>[32m+<U+001B>[m
391+<U+001B>[32m+<U+001B>[m<U+001B>[32mfunc bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {<U+001B>[m
392+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login", func(c echo.Context) error {<U+001B>[m
393+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.RenderLoginPage(c, a)<U+001B>[m
394+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
395+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
396+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.StartOAuth(c, a)<U+001B>[m
397+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
398+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
399+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.OAuthCallback(c, a)<U+001B>[m
400+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
401+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/logout", func(c echo.Context) error {<U+001B>[m
402+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.Logout(c, a)<U+001B>[m
403+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
404+<U+001B>[32m+<U+001B>[m<U+001B>[32m}<U+001B>[m
A s="flex flex-row items-center"> +1192 -0
@@ -0,0 +1,1192 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
299+
300+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
301+
302+ Commands marked with * may be preceded by a number, _<U+0008>N.
303+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
304+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
305+
306+ h H Display this help.
307+ q :q Q :Q ZZ Exit.
308+ ---------------------------------------------------------------------------
309+
310+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
311+
312+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
313+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
314+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
315+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
316+ z * Forward one window (and set window to _<U+0008>N).
317+ w * Backward one window (and set window to _<U+0008>N).
318+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
319+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
320+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
321+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
322+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
323+ ESC-} ^RightArrow Right to last column displayed.
324+ ESC-{ ^LeftArrow Left to first column.
325+ F Forward forever; like "tail -f".
326+ ESC-F Like F but stop when search pattern is found.
327+ r ^R ^L Repaint screen.
328+ R Repaint screen, discarding buffered input.
329+ ---------------------------------------------------
330+ Default "window" is the screen height.
331+ Default "half-window" is half of the screen height.
332+ ---------------------------------------------------------------------------
333+
334+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
335+
336+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
337+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
338+ n * Repeat previous search (for _<U+0008>N-th occurrence).
339+ N * Repeat previous search in reverse direction.
340+ ESC-n * Repeat previous search, spanning files.
341+ ESC-N * Repeat previous search, reverse dir. & spanning files.
342+ ESC-u Undo (toggle) search highlighting.
343+ ESC-U Clear search highlighting.
344+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
345+ ---------------------------------------------------
346+ A search pattern may begin with one or more of:
347+ ^N or ! Search for NON-matching lines.
348+ ^E or * Search multiple files (pass thru END OF FILE).
349+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
350+ ^K Highlight matches, but don't move (KEEP position).
351+ ^R Don't use REGULAR EXPRESSIONS.
352+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
353+ ^W WRAP search if no match found.
354+ ---------------------------------------------------------------------------
355+
356+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
357+
358+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
359+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
360+ p % * Go to beginning of file (or _<U+0008>N percent into file).
361+ t * Go to the (_<U+0008>N-th) next tag.
362+ T * Go to the (_<U+0008>N-th) previous tag.
363+ { ( [ * Find close bracket } ) ].
364+ } ) ] * Find open bracket { ( [.
365+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
366+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
367+ ---------------------------------------------------
368+ Each "find close bracket" command goes forward to the close bracket
369+ matching the (_<U+0008>N-th) open bracket in the top line.
370+ Each "find open bracket" command goes backward to the open bracket
371+ matching the (_<U+0008>N-th) close bracket in the bottom line.
372+
373+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
374+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
375+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
376+ '' Go to the previous position.
377+ ^X^X Same as '.
378+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
379+ ---------------------------------------------------
380+ A mark is any upper-case or lower-case letter.
381+ Certain marks are predefined:
382+ ^ means beginning of the file
383+ $ means end of the file
384+ ---------------------------------------------------------------------------
385+
386+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
387+
388+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
389+ ^X^V Same as :e.
390+ :n * Examine the (_<U+0008>N-th) next file from the command line.
391+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
392+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
393+ :d Delete the current file from the command line list.
394+ = ^G :f Print current file name.
395+ ---------------------------------------------------------------------------
396+
397+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
398+
399+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
400+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
401+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
402+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
403+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
404+
405+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
406+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
407+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
408+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
409+ v Edit the current file with $VISUAL or $EDITOR.
410+ V Print version number of "less".
411+ ---------------------------------------------------------------------------
412+
413+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
414+
415+ Most options may be changed either on the command line,
416+ or from within less by using the - or -- command.
417+ Options may be given in one of two forms: either a single
418+ character preceded by a -, or a name preceded by --.
419+
420+ -? ........ --help
421+ Display help (from command line).
422+ -a ........ --search-skip-screen
423+ Search skips current screen.
424+ -A ........ --SEARCH-SKIP-SCREEN
425+ Search starts just after target line.
426+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
427+ Number of buffers.
428+ -B ........ --auto-buffers
429+ Don't automatically allocate buffers for pipes.
430+ -c ........ --clear-screen
431+ Repaint by clearing rather than scrolling.
432+ -d ........ --dumb
433+ Dumb terminal.
434+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
435+ Set screen colors.
436+ -e -E .... --quit-at-eof --QUIT-AT-EOF
437+ Quit at end of file.
438+ -f ........ --force
439+ Force open non-regular files.
440+ -F ........ --quit-if-one-screen
441+ Quit if entire file fits on first screen.
442+ -g ........ --hilite-search
443+ Highlight only last match for searches.
444+ -G ........ --HILITE-SEARCH
445+ Don't highlight any matches for searches.
446+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
447+ Backward scroll limit.
448+ -i ........ --ignore-case
449+ Ignore case in searches that do not contain uppercase.
450+ -I ........ --IGNORE-CASE
451+ Ignore case in all searches.
452+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
453+ Screen position of target lines.
454+ -J ........ --status-column
455+ Display a status column at left edge of screen.
456+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
457+ Use a lesskey file.
458+ -K ........ --quit-on-intr
459+ Exit less in response to ctrl-C.
460+ -L ........ --no-lessopen
461+ Ignore the LESSOPEN environment variable.
462+ -m -M .... --long-prompt --LONG-PROMPT
463+ Set prompt style.
464+ -n ......... --line-numbers
465+ Suppress line numbers in prompts and messages.
466+ -N ......... --LINE-NUMBERS
467+ Display line number at start of each line.
468+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
469+ Copy to log file (standard input only).
470+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
471+ Copy to log file (unconditionally overwrite).
472+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
473+ Start at pattern (from command line).
474+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
475+ Define new prompt.
476+ -q -Q .... --quiet --QUIET --silent --SILENT
477+ Quiet the terminal bell.
478+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
479+ Output "raw" control characters.
480+ -s ........ --squeeze-blank-lines
481+ Squeeze multiple blank lines.
482+ -S ........ --chop-long-lines
483+ Chop (truncate) long lines rather than wrapping.
484+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
485+ Find a tag.
486+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
487+ Use an alternate tags file.
488+ -u -U .... --underline-special --UNDERLINE-SPECIAL
489+ Change handling of backspaces, tabs and carriage returns.
490+ -V ........ --version
491+ Display the version number of "less".
492+ -w ........ --hilite-unread
493+ Highlight first new line after forward-screen.
494+ -W ........ --HILITE-UNREAD
495+ Highlight first new line after any forward movement.
496+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
497+ Set tab stops.
498+ -X ........ --no-init
499+ Don't use termcap init/deinit strings.
500+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
501+ Forward scroll limit.
502+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
503+ Set size of window.
504+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
505+ Set shell quote characters.
506+ -~ ........ --tilde
507+ Don't display tildes after end of file.
508+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
509+ Set horizontal scroll amount (0 = one half screen width).
510+ --exit-follow-on-close
511+ Exit F command on a pipe when writer closes pipe.
512+ --file-size
513+ Automatically determine the size of the input file.
514+ --follow-name
515+ The F command changes files if the input file is renamed.
516+ --header=[_<U+0008>N[,_<U+0008>M]]
517+ Use N lines and M columns to display file headers.
518+ --incsearch
519+ Search file as each pattern character is typed in.
520+ --intr=_<U+0008>C
521+ Use _<U+0008>C instead of ^X to interrupt a read.
522+ --line-num-width=_<U+0008>N
523+ Set the width of the -N line number field to _<U+0008>N characters.
524+ --modelines=_<U+0008>N
525+ Read _<U+0008>N lines from the input file and look for vim modelines.
526+ --mouse
527+ Enable mouse input.
528+ --no-keypad
529+ Don't send termcap keypad init/deinit strings.
530+ --no-histdups
531+ Remove duplicates from command history.
532+ --no-number-headers
533+ Don't give line numbers to header lines.
534+ --no-search-headers
535+ Don't search in header lines or columns.
536+ --no-vbell
537+ Disable the terminal's visual bell.
538+ --redraw-on-quit
539+ Redraw final screen when quitting.
540+ --rscroll=_<U+0008>C
541+ Set the character used to mark truncated lines.
542+ --save-marks
543+ Retain marks across invocations of less.
544+ --search-options=[EFKNRW-]
545+ Set default options for every search.
546+ --show-preproc-errors
547+ Display a message if preprocessor exits with an error status.
548+ --proc-backspace
549+ Process backspaces for bold/underline.
550+ --SPECIAL-BACKSPACE
551+ Treat backspaces as control characters.
552+ --proc-return
553+ Delete carriage returns before newline.
554+ --SPECIAL-RETURN
555+ Treat carriage returns as control characters.
556+ --proc-tab
557+ Expand tabs to spaces.
558+ --SPECIAL-TAB
559+ Treat tabs as control characters.
560+ --status-col-width=_<U+0008>N
561+ Set the width of the -J status column to _<U+0008>N characters.
562+ --status-line
563+ Highlight or color the entire line containing a mark.
564+ --use-backslash
565+ Subsequent options use backslash as escape char.
566+ --use-color
567+ Enables colored text.
568+ --wheel-lines=_<U+0008>N
569+ Each click of the mouse wheel moves _<U+0008>N lines.
570+ --wordwrap
571+ Wrap lines at spaces.
572+
573+
574+ ---------------------------------------------------------------------------
575+
576+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
577+
578+ These keys can be used to edit text being entered
579+ on the "command line" at the bottom of the screen.
580+
581+ RightArrow ..................... ESC-l ... Move cursor right one character.
582+ LeftArrow ...................... ESC-h ... Move cursor left one character.
583+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
584+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
585+ HOME ........................... ESC-0 ... Move cursor to start of line.
586+ END ............................ ESC-$ ... Move cursor to end of line.
587+ BACKSPACE ................................ Delete char to left of cursor.
588+ DELETE ......................... ESC-x ... Delete char under cursor.
589+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
590+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
591+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
592+ UpArrow ........................ ESC-k ... Retrieve previous command line.
593+ DownArrow ...................... ESC-j ... Retrieve next command line.
594+ TAB ...................................... Complete filename & cycle.
595+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
596+ ctrl-L ................................... Complete filename, list all.
597+
598+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
599+
600+ Commands marked with * may be preceded by a number, _<U+0008>N.
601+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
602+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
603+
604+ h H Display this help.
605+ q :q Q :Q ZZ Exit.
606+ ---------------------------------------------------------------------------
607+
608+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
609+
610+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
611+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
612+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
613+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
614+ z * Forward one window (and set window to _<U+0008>N).
615+ w * Backward one window (and set window to _<U+0008>N).
616+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
617+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
618+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
619+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
620+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
621+ ESC-} ^RightArrow Right to last column displayed.
622+ ESC-{ ^LeftArrow Left to first column.
623+ F Forward forever; like "tail -f".
624+ ESC-F Like F but stop when search pattern is found.
625+ r ^R ^L Repaint screen.
626+ R Repaint screen, discarding buffered input.
627+ ---------------------------------------------------
628+ Default "window" is the screen height.
629+ Default "half-window" is half of the screen height.
630+ ---------------------------------------------------------------------------
631+
632+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
633+
634+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
635+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
636+ n * Repeat previous search (for _<U+0008>N-th occurrence).
637+ N * Repeat previous search in reverse direction.
638+ ESC-n * Repeat previous search, spanning files.
639+ ESC-N * Repeat previous search, reverse dir. & spanning files.
640+ ESC-u Undo (toggle) search highlighting.
641+ ESC-U Clear search highlighting.
642+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
643+ ---------------------------------------------------
644+ A search pattern may begin with one or more of:
645+ ^N or ! Search for NON-matching lines.
646+ ^E or * Search multiple files (pass thru END OF FILE).
647+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
648+ ^K Highlight matches, but don't move (KEEP position).
649+ ^R Don't use REGULAR EXPRESSIONS.
650+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
651+ ^W WRAP search if no match found.
652+ ---------------------------------------------------------------------------
653+
654+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
655+
656+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
657+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
658+ p % * Go to beginning of file (or _<U+0008>N percent into file).
659+ t * Go to the (_<U+0008>N-th) next tag.
660+ T * Go to the (_<U+0008>N-th) previous tag.
661+ { ( [ * Find close bracket } ) ].
662+ } ) ] * Find open bracket { ( [.
663+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
664+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
665+ ---------------------------------------------------
666+ Each "find close bracket" command goes forward to the close bracket
667+ matching the (_<U+0008>N-th) open bracket in the top line.
668+ Each "find open bracket" command goes backward to the open bracket
669+ matching the (_<U+0008>N-th) close bracket in the bottom line.
670+
671+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
672+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
673+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
674+ '' Go to the previous position.
675+ ^X^X Same as '.
676+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
677+ ---------------------------------------------------
678+ A mark is any upper-case or lower-case letter.
679+ Certain marks are predefined:
680+ ^ means beginning of the file
681+ $ means end of the file
682+ ---------------------------------------------------------------------------
683+
684+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
685+
686+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
687+ ^X^V Same as :e.
688+ :n * Examine the (_<U+0008>N-th) next file from the command line.
689+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
690+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
691+ :d Delete the current file from the command line list.
692+ = ^G :f Print current file name.
693+ ---------------------------------------------------------------------------
694+
695+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
696+
697+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
698+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
699+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
700+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
701+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
702+
703+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
704+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
705+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
706+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
707+ v Edit the current file with $VISUAL or $EDITOR.
708+ V Print version number of "less".
709+ ---------------------------------------------------------------------------
710+
711+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
712+
713+ Most options may be changed either on the command line,
714+ or from within less by using the - or -- command.
715+ Options may be given in one of two forms: either a single
716+ character preceded by a -, or a name preceded by --.
717+
718+ -? ........ --help
719+ Display help (from command line).
720+ -a ........ --search-skip-screen
721+ Search skips current screen.
722+ -A ........ --SEARCH-SKIP-SCREEN
723+ Search starts just after target line.
724+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
725+ Number of buffers.
726+ -B ........ --auto-buffers
727+ Don't automatically allocate buffers for pipes.
728+ -c ........ --clear-screen
729+ Repaint by clearing rather than scrolling.
730+ -d ........ --dumb
731+ Dumb terminal.
732+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
733+ Set screen colors.
734+ -e -E .... --quit-at-eof --QUIT-AT-EOF
735+ Quit at end of file.
736+ -f ........ --force
737+ Force open non-regular files.
738+ -F ........ --quit-if-one-screen
739+ Quit if entire file fits on first screen.
740+ -g ........ --hilite-search
741+ Highlight only last match for searches.
742+ -G ........ --HILITE-SEARCH
743+ Don't highlight any matches for searches.
744+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
745+ Backward scroll limit.
746+ -i ........ --ignore-case
747+ Ignore case in searches that do not contain uppercase.
748+ -I ........ --IGNORE-CASE
749+ Ignore case in all searches.
750+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
751+ Screen position of target lines.
752+ -J ........ --status-column
753+ Display a status column at left edge of screen.
754+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
755+ Use a lesskey file.
756+ -K ........ --quit-on-intr
757+ Exit less in response to ctrl-C.
758+ -L ........ --no-lessopen
759+ Ignore the LESSOPEN environment variable.
760+ -m -M .... --long-prompt --LONG-PROMPT
761+ Set prompt style.
762+ -n ......... --line-numbers
763+ Suppress line numbers in prompts and messages.
764+ -N ......... --LINE-NUMBERS
765+ Display line number at start of each line.
766+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
767+ Copy to log file (standard input only).
768+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
769+ Copy to log file (unconditionally overwrite).
770+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
771+ Start at pattern (from command line).
772+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
773+ Define new prompt.
774+ -q -Q .... --quiet --QUIET --silent --SILENT
775+ Quiet the terminal bell.
776+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
777+ Output "raw" control characters.
778+ -s ........ --squeeze-blank-lines
779+ Squeeze multiple blank lines.
780+ -S ........ --chop-long-lines
781+ Chop (truncate) long lines rather than wrapping.
782+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
783+ Find a tag.
784+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
785+ Use an alternate tags file.
786+ -u -U .... --underline-special --UNDERLINE-SPECIAL
787+ Change handling of backspaces, tabs and carriage returns.
788+ -V ........ --version
789+ Display the version number of "less".
790+ -w ........ --hilite-unread
791+ Highlight first new line after forward-screen.
792+ -W ........ --HILITE-UNREAD
793+ Highlight first new line after any forward movement.
794+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
795+ Set tab stops.
796+ -X ........ --no-init
797+ Don't use termcap init/deinit strings.
798+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
799+ Forward scroll limit.
800+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
801+ Set size of window.
802+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
803+ Set shell quote characters.
804+ -~ ........ --tilde
805+ Don't display tildes after end of file.
806+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
807+ Set horizontal scroll amount (0 = one half screen width).
808+ --exit-follow-on-close
809+ Exit F command on a pipe when writer closes pipe.
810+ --file-size
811+ Automatically determine the size of the input file.
812+ --follow-name
813+ The F command changes files if the input file is renamed.
814+ --header=[_<U+0008>N[,_<U+0008>M]]
815+ Use N lines and M columns to display file headers.
816+ --incsearch
817+ Search file as each pattern character is typed in.
818+ --intr=_<U+0008>C
819+ Use _<U+0008>C instead of ^X to interrupt a read.
820+ --line-num-width=_<U+0008>N
821+ Set the width of the -N line number field to _<U+0008>N characters.
822+ --modelines=_<U+0008>N
823+ Read _<U+0008>N lines from the input file and look for vim modelines.
824+ --mouse
825+ Enable mouse input.
826+ --no-keypad
827+ Don't send termcap keypad init/deinit strings.
828+ --no-histdups
829+ Remove duplicates from command history.
830+ --no-number-headers
831+ Don't give line numbers to header lines.
832+ --no-search-headers
833+ Don't search in header lines or columns.
834+ --no-vbell
835+ Disable the terminal's visual bell.
836+ --redraw-on-quit
837+ Redraw final screen when quitting.
838+ --rscroll=_<U+0008>C
839+ Set the character used to mark truncated lines.
840+ --save-marks
841+ Retain marks across invocations of less.
842+ --search-options=[EFKNRW-]
843+ Set default options for every search.
844+ --show-preproc-errors
845+ Display a message if preprocessor exits with an error status.
846+ --proc-backspace
847+ Process backspaces for bold/underline.
848+ --SPECIAL-BACKSPACE
849+ Treat backspaces as control characters.
850+ --proc-return
851+ Delete carriage returns before newline.
852+ --SPECIAL-RETURN
853+ Treat carriage returns as control characters.
854+ --proc-tab
855+ Expand tabs to spaces.
856+ --SPECIAL-TAB
857+ Treat tabs as control characters.
858+ --status-col-width=_<U+0008>N
859+ Set the width of the -J status column to _<U+0008>N characters.
860+ --status-line
861+ Highlight or color the entire line containing a mark.
862+ --use-backslash
863+ Subsequent options use backslash as escape char.
864+ --use-color
865+ Enables colored text.
866+ --wheel-lines=_<U+0008>N
867+ Each click of the mouse wheel moves _<U+0008>N lines.
868+ --wordwrap
869+ Wrap lines at spaces.
870+
871+
872+ ---------------------------------------------------------------------------
873+
874+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
875+
876+ These keys can be used to edit text being entered
877+ on the "command line" at the bottom of the screen.
878+
879+ RightArrow ..................... ESC-l ... Move cursor right one character.
880+ LeftArrow ...................... ESC-h ... Move cursor left one character.
881+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
882+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
883+ HOME ........................... ESC-0 ... Move cursor to start of line.
884+ END ............................ ESC-$ ... Move cursor to end of line.
885+ BACKSPACE ................................ Delete char to left of cursor.
886+ DELETE ......................... ESC-x ... Delete char under cursor.
887+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
888+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
889+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
890+ UpArrow ........................ ESC-k ... Retrieve previous command line.
891+ DownArrow ...................... ESC-j ... Retrieve next command line.
892+ TAB ...................................... Complete filename & cycle.
893+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
894+ ctrl-L ................................... Complete filename, list all.
895+
896+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
897+
898+ Commands marked with * may be preceded by a number, _<U+0008>N.
899+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
900+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
901+
902+ h H Display this help.
903+ q :q Q :Q ZZ Exit.
904+ ---------------------------------------------------------------------------
905+
906+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
907+
908+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
909+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
910+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
911+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
912+ z * Forward one window (and set window to _<U+0008>N).
913+ w * Backward one window (and set window to _<U+0008>N).
914+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
915+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
916+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
917+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
918+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
919+ ESC-} ^RightArrow Right to last column displayed.
920+ ESC-{ ^LeftArrow Left to first column.
921+ F Forward forever; like "tail -f".
922+ ESC-F Like F but stop when search pattern is found.
923+ r ^R ^L Repaint screen.
924+ R Repaint screen, discarding buffered input.
925+ ---------------------------------------------------
926+ Default "window" is the screen height.
927+ Default "half-window" is half of the screen height.
928+ ---------------------------------------------------------------------------
929+
930+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
931+
932+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
933+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
934+ n * Repeat previous search (for _<U+0008>N-th occurrence).
935+ N * Repeat previous search in reverse direction.
936+ ESC-n * Repeat previous search, spanning files.
937+ ESC-N * Repeat previous search, reverse dir. & spanning files.
938+ ESC-u Undo (toggle) search highlighting.
939+ ESC-U Clear search highlighting.
940+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
941+ ---------------------------------------------------
942+ A search pattern may begin with one or more of:
943+ ^N or ! Search for NON-matching lines.
944+ ^E or * Search multiple files (pass thru END OF FILE).
945+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
946+ ^K Highlight matches, but don't move (KEEP position).
947+ ^R Don't use REGULAR EXPRESSIONS.
948+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
949+ ^W WRAP search if no match found.
950+ ---------------------------------------------------------------------------
951+
952+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
953+
954+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
955+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
956+ p % * Go to beginning of file (or _<U+0008>N percent into file).
957+ t * Go to the (_<U+0008>N-th) next tag.
958+ T * Go to the (_<U+0008>N-th) previous tag.
959+ { ( [ * Find close bracket } ) ].
960+ } ) ] * Find open bracket { ( [.
961+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
962+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
963+ ---------------------------------------------------
964+ Each "find close bracket" command goes forward to the close bracket
965+ matching the (_<U+0008>N-th) open bracket in the top line.
966+ Each "find open bracket" command goes backward to the open bracket
967+ matching the (_<U+0008>N-th) close bracket in the bottom line.
968+
969+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
970+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
971+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
972+ '' Go to the previous position.
973+ ^X^X Same as '.
974+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
975+ ---------------------------------------------------
976+ A mark is any upper-case or lower-case letter.
977+ Certain marks are predefined:
978+ ^ means beginning of the file
979+ $ means end of the file
980+ ---------------------------------------------------------------------------
981+
982+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
983+
984+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
985+ ^X^V Same as :e.
986+ :n * Examine the (_<U+0008>N-th) next file from the command line.
987+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
988+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
989+ :d Delete the current file from the command line list.
990+ = ^G :f Print current file name.
991+ ---------------------------------------------------------------------------
992+
993+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
994+
995+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
996+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
997+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
998+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
999+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
1000+
1001+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
1002+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
1003+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
1004+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
1005+ v Edit the current file with $VISUAL or $EDITOR.
1006+ V Print version number of "less".
1007+ ---------------------------------------------------------------------------
1008+
1009+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
1010+
1011+ Most options may be changed either on the command line,
1012+ or from within less by using the - or -- command.
1013+ Options may be given in one of two forms: either a single
1014+ character preceded by a -, or a name preceded by --.
1015+
1016+ -? ........ --help
1017+ Display help (from command line).
1018+ -a ........ --search-skip-screen
1019+ Search skips current screen.
1020+ -A ........ --SEARCH-SKIP-SCREEN
1021+ Search starts just after target line.
1022+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
1023+ Number of buffers.
1024+ -B ........ --auto-buffers
1025+ Don't automatically allocate buffers for pipes.
1026+ -c ........ --clear-screen
1027+ Repaint by clearing rather than scrolling.
1028+ -d ........ --dumb
1029+ Dumb terminal.
1030+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
1031+ Set screen colors.
1032+ -e -E .... --quit-at-eof --QUIT-AT-EOF
1033+ Quit at end of file.
1034+ -f ........ --force
1035+ Force open non-regular files.
1036+ -F ........ --quit-if-one-screen
1037+ Quit if entire file fits on first screen.
1038+ -g ........ --hilite-search
1039+ Highlight only last match for searches.
1040+ -G ........ --HILITE-SEARCH
1041+ Don't highlight any matches for searches.
1042+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
1043+ Backward scroll limit.
1044+ -i ........ --ignore-case
1045+ Ignore case in searches that do not contain uppercase.
1046+ -I ........ --IGNORE-CASE
1047+ Ignore case in all searches.
1048+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
1049+ Screen position of target lines.
1050+ -J ........ --status-column
1051+ Display a status column at left edge of screen.
1052+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1053+ Use a lesskey file.
1054+ -K ........ --quit-on-intr
1055+ Exit less in response to ctrl-C.
1056+ -L ........ --no-lessopen
1057+ Ignore the LESSOPEN environment variable.
1058+ -m -M .... --long-prompt --LONG-PROMPT
1059+ Set prompt style.
1060+ -n ......... --line-numbers
1061+ Suppress line numbers in prompts and messages.
1062+ -N ......... --LINE-NUMBERS
1063+ Display line number at start of each line.
1064+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1065+ Copy to log file (standard input only).
1066+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1067+ Copy to log file (unconditionally overwrite).
1068+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
1069+ Start at pattern (from command line).
1070+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
1071+ Define new prompt.
1072+ -q -Q .... --quiet --QUIET --silent --SILENT
1073+ Quiet the terminal bell.
1074+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
1075+ Output "raw" control characters.
1076+ -s ........ --squeeze-blank-lines
1077+ Squeeze multiple blank lines.
1078+ -S ........ --chop-long-lines
1079+ Chop (truncate) long lines rather than wrapping.
1080+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
1081+ Find a tag.
1082+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1083+ Use an alternate tags file.
1084+ -u -U .... --underline-special --UNDERLINE-SPECIAL
1085+ Change handling of backspaces, tabs and carriage returns.
1086+ -V ........ --version
1087+ Display the version number of "less".
1088+ -w ........ --hilite-unread
1089+ Highlight first new line after forward-screen.
1090+ -W ........ --HILITE-UNREAD
1091+ Highlight first new line after any forward movement.
1092+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
1093+ Set tab stops.
1094+ -X ........ --no-init
1095+ Don't use termcap init/deinit strings.
1096+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
1097+ Forward scroll limit.
1098+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
1099+ Set size of window.
1100+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
1101+ Set shell quote characters.
1102+ -~ ........ --tilde
1103+ Don't display tildes after end of file.
1104+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
1105+ Set horizontal scroll amount (0 = one half screen width).
1106+ --exit-follow-on-close
1107+ Exit F command on a pipe when writer closes pipe.
1108+ --file-size
1109+ Automatically determine the size of the input file.
1110+ --follow-name
1111+ The F command changes files if the input file is renamed.
1112+ --header=[_<U+0008>N[,_<U+0008>M]]
1113+ Use N lines and M columns to display file headers.
1114+ --incsearch
1115+ Search file as each pattern character is typed in.
1116+ --intr=_<U+0008>C
1117+ Use _<U+0008>C instead of ^X to interrupt a read.
1118+ --line-num-width=_<U+0008>N
1119+ Set the width of the -N line number field to _<U+0008>N characters.
1120+ --modelines=_<U+0008>N
1121+ Read _<U+0008>N lines from the input file and look for vim modelines.
1122+ --mouse
1123+ Enable mouse input.
1124+ --no-keypad
1125+ Don't send termcap keypad init/deinit strings.
1126+ --no-histdups
1127+ Remove duplicates from command history.
1128+ --no-number-headers
1129+ Don't give line numbers to header lines.
1130+ --no-search-headers
1131+ Don't search in header lines or columns.
1132+ --no-vbell
1133+ Disable the terminal's visual bell.
1134+ --redraw-on-quit
1135+ Redraw final screen when quitting.
1136+ --rscroll=_<U+0008>C
1137+ Set the character used to mark truncated lines.
1138+ --save-marks
1139+ Retain marks across invocations of less.
1140+ --search-options=[EFKNRW-]
1141+ Set default options for every search.
1142+ --show-preproc-errors
1143+ Display a message if preprocessor exits with an error status.
1144+ --proc-backspace
1145+ Process backspaces for bold/underline.
1146+ --SPECIAL-BACKSPACE
1147+ Treat backspaces as control characters.
1148+ --proc-return
1149+ Delete carriage returns before newline.
1150+ --SPECIAL-RETURN
1151+ Treat carriage returns as control characters.
1152+ --proc-tab
1153+ Expand tabs to spaces.
1154+ --SPECIAL-TAB
1155+ Treat tabs as control characters.
1156+ --status-col-width=_<U+0008>N
1157+ Set the width of the -J status column to _<U+0008>N characters.
1158+ --status-line
1159+ Highlight or color the entire line containing a mark.
1160+ --use-backslash
1161+ Subsequent options use backslash as escape char.
1162+ --use-color
1163+ Enables colored text.
1164+ --wheel-lines=_<U+0008>N
1165+ Each click of the mouse wheel moves _<U+0008>N lines.
1166+ --wordwrap
1167+ Wrap lines at spaces.
1168+
1169+
1170+ ---------------------------------------------------------------------------
1171+
1172+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
1173+
1174+ These keys can be used to edit text being entered
1175+ on the "command line" at the bottom of the screen.
1176+
1177+ RightArrow ..................... ESC-l ... Move cursor right one character.
1178+ LeftArrow ...................... ESC-h ... Move cursor left one character.
1179+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
1180+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
1181+ HOME ........................... ESC-0 ... Move cursor to start of line.
1182+ END ............................ ESC-$ ... Move cursor to end of line.
1183+ BACKSPACE ................................ Delete char to left of cursor.
1184+ DELETE ......................... ESC-x ... Delete char under cursor.
1185+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
1186+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
1187+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
1188+ UpArrow ........................ ESC-k ... Retrieve previous command line.
1189+ DownArrow ...................... ESC-j ... Retrieve next command line.
1190+ TAB ...................................... Complete filename & cycle.
1191+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
1192+ ctrl-L ................................... Complete filename, list all.
@@ -0,0 +1,1192 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
299+
300+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
301+
302+ Commands marked with * may be preceded by a number, _<U+0008>N.
303+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
304+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
305+
306+ h H Display this help.
307+ q :q Q :Q ZZ Exit.
308+ ---------------------------------------------------------------------------
309+
310+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
311+
312+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
313+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
314+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
315+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
316+ z * Forward one window (and set window to _<U+0008>N).
317+ w * Backward one window (and set window to _<U+0008>N).
318+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
319+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
320+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
321+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
322+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
323+ ESC-} ^RightArrow Right to last column displayed.
324+ ESC-{ ^LeftArrow Left to first column.
325+ F Forward forever; like "tail -f".
326+ ESC-F Like F but stop when search pattern is found.
327+ r ^R ^L Repaint screen.
328+ R Repaint screen, discarding buffered input.
329+ ---------------------------------------------------
330+ Default "window" is the screen height.
331+ Default "half-window" is half of the screen height.
332+ ---------------------------------------------------------------------------
333+
334+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
335+
336+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
337+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
338+ n * Repeat previous search (for _<U+0008>N-th occurrence).
339+ N * Repeat previous search in reverse direction.
340+ ESC-n * Repeat previous search, spanning files.
341+ ESC-N * Repeat previous search, reverse dir. & spanning files.
342+ ESC-u Undo (toggle) search highlighting.
343+ ESC-U Clear search highlighting.
344+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
345+ ---------------------------------------------------
346+ A search pattern may begin with one or more of:
347+ ^N or ! Search for NON-matching lines.
348+ ^E or * Search multiple files (pass thru END OF FILE).
349+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
350+ ^K Highlight matches, but don't move (KEEP position).
351+ ^R Don't use REGULAR EXPRESSIONS.
352+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
353+ ^W WRAP search if no match found.
354+ ---------------------------------------------------------------------------
355+
356+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
357+
358+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
359+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
360+ p % * Go to beginning of file (or _<U+0008>N percent into file).
361+ t * Go to the (_<U+0008>N-th) next tag.
362+ T * Go to the (_<U+0008>N-th) previous tag.
363+ { ( [ * Find close bracket } ) ].
364+ } ) ] * Find open bracket { ( [.
365+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
366+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
367+ ---------------------------------------------------
368+ Each "find close bracket" command goes forward to the close bracket
369+ matching the (_<U+0008>N-th) open bracket in the top line.
370+ Each "find open bracket" command goes backward to the open bracket
371+ matching the (_<U+0008>N-th) close bracket in the bottom line.
372+
373+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
374+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
375+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
376+ '' Go to the previous position.
377+ ^X^X Same as '.
378+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
379+ ---------------------------------------------------
380+ A mark is any upper-case or lower-case letter.
381+ Certain marks are predefined:
382+ ^ means beginning of the file
383+ $ means end of the file
384+ ---------------------------------------------------------------------------
385+
386+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
387+
388+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
389+ ^X^V Same as :e.
390+ :n * Examine the (_<U+0008>N-th) next file from the command line.
391+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
392+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
393+ :d Delete the current file from the command line list.
394+ = ^G :f Print current file name.
395+ ---------------------------------------------------------------------------
396+
397+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
398+
399+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
400+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
401+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
402+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
403+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
404+
405+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
406+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
407+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
408+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
409+ v Edit the current file with $VISUAL or $EDITOR.
410+ V Print version number of "less".
411+ ---------------------------------------------------------------------------
412+
413+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
414+
415+ Most options may be changed either on the command line,
416+ or from within less by using the - or -- command.
417+ Options may be given in one of two forms: either a single
418+ character preceded by a -, or a name preceded by --.
419+
420+ -? ........ --help
421+ Display help (from command line).
422+ -a ........ --search-skip-screen
423+ Search skips current screen.
424+ -A ........ --SEARCH-SKIP-SCREEN
425+ Search starts just after target line.
426+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
427+ Number of buffers.
428+ -B ........ --auto-buffers
429+ Don't automatically allocate buffers for pipes.
430+ -c ........ --clear-screen
431+ Repaint by clearing rather than scrolling.
432+ -d ........ --dumb
433+ Dumb terminal.
434+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
435+ Set screen colors.
436+ -e -E .... --quit-at-eof --QUIT-AT-EOF
437+ Quit at end of file.
438+ -f ........ --force
439+ Force open non-regular files.
440+ -F ........ --quit-if-one-screen
441+ Quit if entire file fits on first screen.
442+ -g ........ --hilite-search
443+ Highlight only last match for searches.
444+ -G ........ --HILITE-SEARCH
445+ Don't highlight any matches for searches.
446+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
447+ Backward scroll limit.
448+ -i ........ --ignore-case
449+ Ignore case in searches that do not contain uppercase.
450+ -I ........ --IGNORE-CASE
451+ Ignore case in all searches.
452+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
453+ Screen position of target lines.
454+ -J ........ --status-column
455+ Display a status column at left edge of screen.
456+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
457+ Use a lesskey file.
458+ -K ........ --quit-on-intr
459+ Exit less in response to ctrl-C.
460+ -L ........ --no-lessopen
461+ Ignore the LESSOPEN environment variable.
462+ -m -M .... --long-prompt --LONG-PROMPT
463+ Set prompt style.
464+ -n ......... --line-numbers
465+ Suppress line numbers in prompts and messages.
466+ -N ......... --LINE-NUMBERS
467+ Display line number at start of each line.
468+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
469+ Copy to log file (standard input only).
470+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
471+ Copy to log file (unconditionally overwrite).
472+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
473+ Start at pattern (from command line).
474+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
475+ Define new prompt.
476+ -q -Q .... --quiet --QUIET --silent --SILENT
477+ Quiet the terminal bell.
478+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
479+ Output "raw" control characters.
480+ -s ........ --squeeze-blank-lines
481+ Squeeze multiple blank lines.
482+ -S ........ --chop-long-lines
483+ Chop (truncate) long lines rather than wrapping.
484+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
485+ Find a tag.
486+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
487+ Use an alternate tags file.
488+ -u -U .... --underline-special --UNDERLINE-SPECIAL
489+ Change handling of backspaces, tabs and carriage returns.
490+ -V ........ --version
491+ Display the version number of "less".
492+ -w ........ --hilite-unread
493+ Highlight first new line after forward-screen.
494+ -W ........ --HILITE-UNREAD
495+ Highlight first new line after any forward movement.
496+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
497+ Set tab stops.
498+ -X ........ --no-init
499+ Don't use termcap init/deinit strings.
500+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
501+ Forward scroll limit.
502+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
503+ Set size of window.
504+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
505+ Set shell quote characters.
506+ -~ ........ --tilde
507+ Don't display tildes after end of file.
508+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
509+ Set horizontal scroll amount (0 = one half screen width).
510+ --exit-follow-on-close
511+ Exit F command on a pipe when writer closes pipe.
512+ --file-size
513+ Automatically determine the size of the input file.
514+ --follow-name
515+ The F command changes files if the input file is renamed.
516+ --header=[_<U+0008>N[,_<U+0008>M]]
517+ Use N lines and M columns to display file headers.
518+ --incsearch
519+ Search file as each pattern character is typed in.
520+ --intr=_<U+0008>C
521+ Use _<U+0008>C instead of ^X to interrupt a read.
522+ --line-num-width=_<U+0008>N
523+ Set the width of the -N line number field to _<U+0008>N characters.
524+ --modelines=_<U+0008>N
525+ Read _<U+0008>N lines from the input file and look for vim modelines.
526+ --mouse
527+ Enable mouse input.
528+ --no-keypad
529+ Don't send termcap keypad init/deinit strings.
530+ --no-histdups
531+ Remove duplicates from command history.
532+ --no-number-headers
533+ Don't give line numbers to header lines.
534+ --no-search-headers
535+ Don't search in header lines or columns.
536+ --no-vbell
537+ Disable the terminal's visual bell.
538+ --redraw-on-quit
539+ Redraw final screen when quitting.
540+ --rscroll=_<U+0008>C
541+ Set the character used to mark truncated lines.
542+ --save-marks
543+ Retain marks across invocations of less.
544+ --search-options=[EFKNRW-]
545+ Set default options for every search.
546+ --show-preproc-errors
547+ Display a message if preprocessor exits with an error status.
548+ --proc-backspace
549+ Process backspaces for bold/underline.
550+ --SPECIAL-BACKSPACE
551+ Treat backspaces as control characters.
552+ --proc-return
553+ Delete carriage returns before newline.
554+ --SPECIAL-RETURN
555+ Treat carriage returns as control characters.
556+ --proc-tab
557+ Expand tabs to spaces.
558+ --SPECIAL-TAB
559+ Treat tabs as control characters.
560+ --status-col-width=_<U+0008>N
561+ Set the width of the -J status column to _<U+0008>N characters.
562+ --status-line
563+ Highlight or color the entire line containing a mark.
564+ --use-backslash
565+ Subsequent options use backslash as escape char.
566+ --use-color
567+ Enables colored text.
568+ --wheel-lines=_<U+0008>N
569+ Each click of the mouse wheel moves _<U+0008>N lines.
570+ --wordwrap
571+ Wrap lines at spaces.
572+
573+
574+ ---------------------------------------------------------------------------
575+
576+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
577+
578+ These keys can be used to edit text being entered
579+ on the "command line" at the bottom of the screen.
580+
581+ RightArrow ..................... ESC-l ... Move cursor right one character.
582+ LeftArrow ...................... ESC-h ... Move cursor left one character.
583+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
584+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
585+ HOME ........................... ESC-0 ... Move cursor to start of line.
586+ END ............................ ESC-$ ... Move cursor to end of line.
587+ BACKSPACE ................................ Delete char to left of cursor.
588+ DELETE ......................... ESC-x ... Delete char under cursor.
589+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
590+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
591+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
592+ UpArrow ........................ ESC-k ... Retrieve previous command line.
593+ DownArrow ...................... ESC-j ... Retrieve next command line.
594+ TAB ...................................... Complete filename & cycle.
595+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
596+ ctrl-L ................................... Complete filename, list all.
597+
598+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
599+
600+ Commands marked with * may be preceded by a number, _<U+0008>N.
601+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
602+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
603+
604+ h H Display this help.
605+ q :q Q :Q ZZ Exit.
606+ ---------------------------------------------------------------------------
607+
608+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
609+
610+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
611+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
612+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
613+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
614+ z * Forward one window (and set window to _<U+0008>N).
615+ w * Backward one window (and set window to _<U+0008>N).
616+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
617+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
618+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
619+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
620+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
621+ ESC-} ^RightArrow Right to last column displayed.
622+ ESC-{ ^LeftArrow Left to first column.
623+ F Forward forever; like "tail -f".
624+ ESC-F Like F but stop when search pattern is found.
625+ r ^R ^L Repaint screen.
626+ R Repaint screen, discarding buffered input.
627+ ---------------------------------------------------
628+ Default "window" is the screen height.
629+ Default "half-window" is half of the screen height.
630+ ---------------------------------------------------------------------------
631+
632+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
633+
634+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
635+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
636+ n * Repeat previous search (for _<U+0008>N-th occurrence).
637+ N * Repeat previous search in reverse direction.
638+ ESC-n * Repeat previous search, spanning files.
639+ ESC-N * Repeat previous search, reverse dir. & spanning files.
640+ ESC-u Undo (toggle) search highlighting.
641+ ESC-U Clear search highlighting.
642+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
643+ ---------------------------------------------------
644+ A search pattern may begin with one or more of:
645+ ^N or ! Search for NON-matching lines.
646+ ^E or * Search multiple files (pass thru END OF FILE).
647+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
648+ ^K Highlight matches, but don't move (KEEP position).
649+ ^R Don't use REGULAR EXPRESSIONS.
650+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
651+ ^W WRAP search if no match found.
652+ ---------------------------------------------------------------------------
653+
654+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
655+
656+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
657+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
658+ p % * Go to beginning of file (or _<U+0008>N percent into file).
659+ t * Go to the (_<U+0008>N-th) next tag.
660+ T * Go to the (_<U+0008>N-th) previous tag.
661+ { ( [ * Find close bracket } ) ].
662+ } ) ] * Find open bracket { ( [.
663+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
664+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
665+ ---------------------------------------------------
666+ Each "find close bracket" command goes forward to the close bracket
667+ matching the (_<U+0008>N-th) open bracket in the top line.
668+ Each "find open bracket" command goes backward to the open bracket
669+ matching the (_<U+0008>N-th) close bracket in the bottom line.
670+
671+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
672+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
673+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
674+ '' Go to the previous position.
675+ ^X^X Same as '.
676+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
677+ ---------------------------------------------------
678+ A mark is any upper-case or lower-case letter.
679+ Certain marks are predefined:
680+ ^ means beginning of the file
681+ $ means end of the file
682+ ---------------------------------------------------------------------------
683+
684+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
685+
686+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
687+ ^X^V Same as :e.
688+ :n * Examine the (_<U+0008>N-th) next file from the command line.
689+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
690+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
691+ :d Delete the current file from the command line list.
692+ = ^G :f Print current file name.
693+ ---------------------------------------------------------------------------
694+
695+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
696+
697+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
698+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
699+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
700+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
701+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
702+
703+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
704+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
705+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
706+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
707+ v Edit the current file with $VISUAL or $EDITOR.
708+ V Print version number of "less".
709+ ---------------------------------------------------------------------------
710+
711+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
712+
713+ Most options may be changed either on the command line,
714+ or from within less by using the - or -- command.
715+ Options may be given in one of two forms: either a single
716+ character preceded by a -, or a name preceded by --.
717+
718+ -? ........ --help
719+ Display help (from command line).
720+ -a ........ --search-skip-screen
721+ Search skips current screen.
722+ -A ........ --SEARCH-SKIP-SCREEN
723+ Search starts just after target line.
724+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
725+ Number of buffers.
726+ -B ........ --auto-buffers
727+ Don't automatically allocate buffers for pipes.
728+ -c ........ --clear-screen
729+ Repaint by clearing rather than scrolling.
730+ -d ........ --dumb
731+ Dumb terminal.
732+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
733+ Set screen colors.
734+ -e -E .... --quit-at-eof --QUIT-AT-EOF
735+ Quit at end of file.
736+ -f ........ --force
737+ Force open non-regular files.
738+ -F ........ --quit-if-one-screen
739+ Quit if entire file fits on first screen.
740+ -g ........ --hilite-search
741+ Highlight only last match for searches.
742+ -G ........ --HILITE-SEARCH
743+ Don't highlight any matches for searches.
744+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
745+ Backward scroll limit.
746+ -i ........ --ignore-case
747+ Ignore case in searches that do not contain uppercase.
748+ -I ........ --IGNORE-CASE
749+ Ignore case in all searches.
750+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
751+ Screen position of target lines.
752+ -J ........ --status-column
753+ Display a status column at left edge of screen.
754+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
755+ Use a lesskey file.
756+ -K ........ --quit-on-intr
757+ Exit less in response to ctrl-C.
758+ -L ........ --no-lessopen
759+ Ignore the LESSOPEN environment variable.
760+ -m -M .... --long-prompt --LONG-PROMPT
761+ Set prompt style.
762+ -n ......... --line-numbers
763+ Suppress line numbers in prompts and messages.
764+ -N ......... --LINE-NUMBERS
765+ Display line number at start of each line.
766+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
767+ Copy to log file (standard input only).
768+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
769+ Copy to log file (unconditionally overwrite).
770+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
771+ Start at pattern (from command line).
772+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
773+ Define new prompt.
774+ -q -Q .... --quiet --QUIET --silent --SILENT
775+ Quiet the terminal bell.
776+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
777+ Output "raw" control characters.
778+ -s ........ --squeeze-blank-lines
779+ Squeeze multiple blank lines.
780+ -S ........ --chop-long-lines
781+ Chop (truncate) long lines rather than wrapping.
782+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
783+ Find a tag.
784+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
785+ Use an alternate tags file.
786+ -u -U .... --underline-special --UNDERLINE-SPECIAL
787+ Change handling of backspaces, tabs and carriage returns.
788+ -V ........ --version
789+ Display the version number of "less".
790+ -w ........ --hilite-unread
791+ Highlight first new line after forward-screen.
792+ -W ........ --HILITE-UNREAD
793+ Highlight first new line after any forward movement.
794+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
795+ Set tab stops.
796+ -X ........ --no-init
797+ Don't use termcap init/deinit strings.
798+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
799+ Forward scroll limit.
800+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
801+ Set size of window.
802+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
803+ Set shell quote characters.
804+ -~ ........ --tilde
805+ Don't display tildes after end of file.
806+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
807+ Set horizontal scroll amount (0 = one half screen width).
808+ --exit-follow-on-close
809+ Exit F command on a pipe when writer closes pipe.
810+ --file-size
811+ Automatically determine the size of the input file.
812+ --follow-name
813+ The F command changes files if the input file is renamed.
814+ --header=[_<U+0008>N[,_<U+0008>M]]
815+ Use N lines and M columns to display file headers.
816+ --incsearch
817+ Search file as each pattern character is typed in.
818+ --intr=_<U+0008>C
819+ Use _<U+0008>C instead of ^X to interrupt a read.
820+ --line-num-width=_<U+0008>N
821+ Set the width of the -N line number field to _<U+0008>N characters.
822+ --modelines=_<U+0008>N
823+ Read _<U+0008>N lines from the input file and look for vim modelines.
824+ --mouse
825+ Enable mouse input.
826+ --no-keypad
827+ Don't send termcap keypad init/deinit strings.
828+ --no-histdups
829+ Remove duplicates from command history.
830+ --no-number-headers
831+ Don't give line numbers to header lines.
832+ --no-search-headers
833+ Don't search in header lines or columns.
834+ --no-vbell
835+ Disable the terminal's visual bell.
836+ --redraw-on-quit
837+ Redraw final screen when quitting.
838+ --rscroll=_<U+0008>C
839+ Set the character used to mark truncated lines.
840+ --save-marks
841+ Retain marks across invocations of less.
842+ --search-options=[EFKNRW-]
843+ Set default options for every search.
844+ --show-preproc-errors
845+ Display a message if preprocessor exits with an error status.
846+ --proc-backspace
847+ Process backspaces for bold/underline.
848+ --SPECIAL-BACKSPACE
849+ Treat backspaces as control characters.
850+ --proc-return
851+ Delete carriage returns before newline.
852+ --SPECIAL-RETURN
853+ Treat carriage returns as control characters.
854+ --proc-tab
855+ Expand tabs to spaces.
856+ --SPECIAL-TAB
857+ Treat tabs as control characters.
858+ --status-col-width=_<U+0008>N
859+ Set the width of the -J status column to _<U+0008>N characters.
860+ --status-line
861+ Highlight or color the entire line containing a mark.
862+ --use-backslash
863+ Subsequent options use backslash as escape char.
864+ --use-color
865+ Enables colored text.
866+ --wheel-lines=_<U+0008>N
867+ Each click of the mouse wheel moves _<U+0008>N lines.
868+ --wordwrap
869+ Wrap lines at spaces.
870+
871+
872+ ---------------------------------------------------------------------------
873+
874+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
875+
876+ These keys can be used to edit text being entered
877+ on the "command line" at the bottom of the screen.
878+
879+ RightArrow ..................... ESC-l ... Move cursor right one character.
880+ LeftArrow ...................... ESC-h ... Move cursor left one character.
881+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
882+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
883+ HOME ........................... ESC-0 ... Move cursor to start of line.
884+ END ............................ ESC-$ ... Move cursor to end of line.
885+ BACKSPACE ................................ Delete char to left of cursor.
886+ DELETE ......................... ESC-x ... Delete char under cursor.
887+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
888+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
889+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
890+ UpArrow ........................ ESC-k ... Retrieve previous command line.
891+ DownArrow ...................... ESC-j ... Retrieve next command line.
892+ TAB ...................................... Complete filename & cycle.
893+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
894+ ctrl-L ................................... Complete filename, list all.
895+
896+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
897+
898+ Commands marked with * may be preceded by a number, _<U+0008>N.
899+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
900+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
901+
902+ h H Display this help.
903+ q :q Q :Q ZZ Exit.
904+ ---------------------------------------------------------------------------
905+
906+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
907+
908+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
909+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
910+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
911+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
912+ z * Forward one window (and set window to _<U+0008>N).
913+ w * Backward one window (and set window to _<U+0008>N).
914+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
915+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
916+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
917+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
918+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
919+ ESC-} ^RightArrow Right to last column displayed.
920+ ESC-{ ^LeftArrow Left to first column.
921+ F Forward forever; like "tail -f".
922+ ESC-F Like F but stop when search pattern is found.
923+ r ^R ^L Repaint screen.
924+ R Repaint screen, discarding buffered input.
925+ ---------------------------------------------------
926+ Default "window" is the screen height.
927+ Default "half-window" is half of the screen height.
928+ ---------------------------------------------------------------------------
929+
930+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
931+
932+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
933+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
934+ n * Repeat previous search (for _<U+0008>N-th occurrence).
935+ N * Repeat previous search in reverse direction.
936+ ESC-n * Repeat previous search, spanning files.
937+ ESC-N * Repeat previous search, reverse dir. & spanning files.
938+ ESC-u Undo (toggle) search highlighting.
939+ ESC-U Clear search highlighting.
940+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
941+ ---------------------------------------------------
942+ A search pattern may begin with one or more of:
943+ ^N or ! Search for NON-matching lines.
944+ ^E or * Search multiple files (pass thru END OF FILE).
945+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
946+ ^K Highlight matches, but don't move (KEEP position).
947+ ^R Don't use REGULAR EXPRESSIONS.
948+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
949+ ^W WRAP search if no match found.
950+ ---------------------------------------------------------------------------
951+
952+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
953+
954+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
955+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
956+ p % * Go to beginning of file (or _<U+0008>N percent into file).
957+ t * Go to the (_<U+0008>N-th) next tag.
958+ T * Go to the (_<U+0008>N-th) previous tag.
959+ { ( [ * Find close bracket } ) ].
960+ } ) ] * Find open bracket { ( [.
961+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
962+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
963+ ---------------------------------------------------
964+ Each "find close bracket" command goes forward to the close bracket
965+ matching the (_<U+0008>N-th) open bracket in the top line.
966+ Each "find open bracket" command goes backward to the open bracket
967+ matching the (_<U+0008>N-th) close bracket in the bottom line.
968+
969+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
970+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
971+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
972+ '' Go to the previous position.
973+ ^X^X Same as '.
974+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
975+ ---------------------------------------------------
976+ A mark is any upper-case or lower-case letter.
977+ Certain marks are predefined:
978+ ^ means beginning of the file
979+ $ means end of the file
980+ ---------------------------------------------------------------------------
981+
982+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
983+
984+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
985+ ^X^V Same as :e.
986+ :n * Examine the (_<U+0008>N-th) next file from the command line.
987+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
988+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
989+ :d Delete the current file from the command line list.
990+ = ^G :f Print current file name.
991+ ---------------------------------------------------------------------------
992+
993+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
994+
995+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
996+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
997+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
998+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
999+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
1000+
1001+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
1002+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
1003+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
1004+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
1005+ v Edit the current file with $VISUAL or $EDITOR.
1006+ V Print version number of "less".
1007+ ---------------------------------------------------------------------------
1008+
1009+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
1010+
1011+ Most options may be changed either on the command line,
1012+ or from within less by using the - or -- command.
1013+ Options may be given in one of two forms: either a single
1014+ character preceded by a -, or a name preceded by --.
1015+
1016+ -? ........ --help
1017+ Display help (from command line).
1018+ -a ........ --search-skip-screen
1019+ Search skips current screen.
1020+ -A ........ --SEARCH-SKIP-SCREEN
1021+ Search starts just after target line.
1022+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
1023+ Number of buffers.
1024+ -B ........ --auto-buffers
1025+ Don't automatically allocate buffers for pipes.
1026+ -c ........ --clear-screen
1027+ Repaint by clearing rather than scrolling.
1028+ -d ........ --dumb
1029+ Dumb terminal.
1030+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
1031+ Set screen colors.
1032+ -e -E .... --quit-at-eof --QUIT-AT-EOF
1033+ Quit at end of file.
1034+ -f ........ --force
1035+ Force open non-regular files.
1036+ -F ........ --quit-if-one-screen
1037+ Quit if entire file fits on first screen.
1038+ -g ........ --hilite-search
1039+ Highlight only last match for searches.
1040+ -G ........ --HILITE-SEARCH
1041+ Don't highlight any matches for searches.
1042+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
1043+ Backward scroll limit.
1044+ -i ........ --ignore-case
1045+ Ignore case in searches that do not contain uppercase.
1046+ -I ........ --IGNORE-CASE
1047+ Ignore case in all searches.
1048+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
1049+ Screen position of target lines.
1050+ -J ........ --status-column
1051+ Display a status column at left edge of screen.
1052+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1053+ Use a lesskey file.
1054+ -K ........ --quit-on-intr
1055+ Exit less in response to ctrl-C.
1056+ -L ........ --no-lessopen
1057+ Ignore the LESSOPEN environment variable.
1058+ -m -M .... --long-prompt --LONG-PROMPT
1059+ Set prompt style.
1060+ -n ......... --line-numbers
1061+ Suppress line numbers in prompts and messages.
1062+ -N ......... --LINE-NUMBERS
1063+ Display line number at start of each line.
1064+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1065+ Copy to log file (standard input only).
1066+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1067+ Copy to log file (unconditionally overwrite).
1068+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
1069+ Start at pattern (from command line).
1070+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
1071+ Define new prompt.
1072+ -q -Q .... --quiet --QUIET --silent --SILENT
1073+ Quiet the terminal bell.
1074+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
1075+ Output "raw" control characters.
1076+ -s ........ --squeeze-blank-lines
1077+ Squeeze multiple blank lines.
1078+ -S ........ --chop-long-lines
1079+ Chop (truncate) long lines rather than wrapping.
1080+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
1081+ Find a tag.
1082+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
1083+ Use an alternate tags file.
1084+ -u -U .... --underline-special --UNDERLINE-SPECIAL
1085+ Change handling of backspaces, tabs and carriage returns.
1086+ -V ........ --version
1087+ Display the version number of "less".
1088+ -w ........ --hilite-unread
1089+ Highlight first new line after forward-screen.
1090+ -W ........ --HILITE-UNREAD
1091+ Highlight first new line after any forward movement.
1092+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
1093+ Set tab stops.
1094+ -X ........ --no-init
1095+ Don't use termcap init/deinit strings.
1096+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
1097+ Forward scroll limit.
1098+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
1099+ Set size of window.
1100+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
1101+ Set shell quote characters.
1102+ -~ ........ --tilde
1103+ Don't display tildes after end of file.
1104+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
1105+ Set horizontal scroll amount (0 = one half screen width).
1106+ --exit-follow-on-close
1107+ Exit F command on a pipe when writer closes pipe.
1108+ --file-size
1109+ Automatically determine the size of the input file.
1110+ --follow-name
1111+ The F command changes files if the input file is renamed.
1112+ --header=[_<U+0008>N[,_<U+0008>M]]
1113+ Use N lines and M columns to display file headers.
1114+ --incsearch
1115+ Search file as each pattern character is typed in.
1116+ --intr=_<U+0008>C
1117+ Use _<U+0008>C instead of ^X to interrupt a read.
1118+ --line-num-width=_<U+0008>N
1119+ Set the width of the -N line number field to _<U+0008>N characters.
1120+ --modelines=_<U+0008>N
1121+ Read _<U+0008>N lines from the input file and look for vim modelines.
1122+ --mouse
1123+ Enable mouse input.
1124+ --no-keypad
1125+ Don't send termcap keypad init/deinit strings.
1126+ --no-histdups
1127+ Remove duplicates from command history.
1128+ --no-number-headers
1129+ Don't give line numbers to header lines.
1130+ --no-search-headers
1131+ Don't search in header lines or columns.
1132+ --no-vbell
1133+ Disable the terminal's visual bell.
1134+ --redraw-on-quit
1135+ Redraw final screen when quitting.
1136+ --rscroll=_<U+0008>C
1137+ Set the character used to mark truncated lines.
1138+ --save-marks
1139+ Retain marks across invocations of less.
1140+ --search-options=[EFKNRW-]
1141+ Set default options for every search.
1142+ --show-preproc-errors
1143+ Display a message if preprocessor exits with an error status.
1144+ --proc-backspace
1145+ Process backspaces for bold/underline.
1146+ --SPECIAL-BACKSPACE
1147+ Treat backspaces as control characters.
1148+ --proc-return
1149+ Delete carriage returns before newline.
1150+ --SPECIAL-RETURN
1151+ Treat carriage returns as control characters.
1152+ --proc-tab
1153+ Expand tabs to spaces.
1154+ --SPECIAL-TAB
1155+ Treat tabs as control characters.
1156+ --status-col-width=_<U+0008>N
1157+ Set the width of the -J status column to _<U+0008>N characters.
1158+ --status-line
1159+ Highlight or color the entire line containing a mark.
1160+ --use-backslash
1161+ Subsequent options use backslash as escape char.
1162+ --use-color
1163+ Enables colored text.
1164+ --wheel-lines=_<U+0008>N
1165+ Each click of the mouse wheel moves _<U+0008>N lines.
1166+ --wordwrap
1167+ Wrap lines at spaces.
1168+
1169+
1170+ ---------------------------------------------------------------------------
1171+
1172+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
1173+
1174+ These keys can be used to edit text being entered
1175+ on the "command line" at the bottom of the screen.
1176+
1177+ RightArrow ..................... ESC-l ... Move cursor right one character.
1178+ LeftArrow ...................... ESC-h ... Move cursor left one character.
1179+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
1180+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
1181+ HOME ........................... ESC-0 ... Move cursor to start of line.
1182+ END ............................ ESC-$ ... Move cursor to end of line.
1183+ BACKSPACE ................................ Delete char to left of cursor.
1184+ DELETE ......................... ESC-x ... Delete char under cursor.
1185+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
1186+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
1187+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
1188+ UpArrow ........................ ESC-k ... Retrieve previous command line.
1189+ DownArrow ...................... ESC-j ... Retrieve next command line.
1190+ TAB ...................................... Complete filename & cycle.
1191+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
1192+ ctrl-L ................................... Complete filename, list all.
A s=flex flex-col items-center bg-gordonmascot-hblue rounded-md +298 -0
@@ -0,0 +1,298 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
@@ -0,0 +1,298 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
A s=w-full py-2 px-4 my-2 border border-gray-300 rounded-md +298 -0
@@ -0,0 +1,298 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
@@ -0,0 +1,298 @@
1+
2+ S<U+0008>SU<U+0008>UM<U+0008>MM<U+0008>MA<U+0008>AR<U+0008>RY<U+0008>Y O<U+0008>OF<U+0008>F L<U+0008>LE<U+0008>ES<U+0008>SS<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
3+
4+ Commands marked with * may be preceded by a number, _<U+0008>N.
5+ Notes in parentheses indicate the behavior if _<U+0008>N is given.
6+ A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
7+
8+ h H Display this help.
9+ q :q Q :Q ZZ Exit.
10+ ---------------------------------------------------------------------------
11+
12+ M<U+0008>MO<U+0008>OV<U+0008>VI<U+0008>IN<U+0008>NG<U+0008>G
13+
14+ e ^E j ^N CR * Forward one line (or _<U+0008>N lines).
15+ y ^Y k ^K ^P * Backward one line (or _<U+0008>N lines).
16+ f ^F ^V SPACE * Forward one window (or _<U+0008>N lines).
17+ b ^B ESC-v * Backward one window (or _<U+0008>N lines).
18+ z * Forward one window (and set window to _<U+0008>N).
19+ w * Backward one window (and set window to _<U+0008>N).
20+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
21+ d ^D * Forward one half-window (and set half-window to _<U+0008>N).
22+ u ^U * Backward one half-window (and set half-window to _<U+0008>N).
23+ ESC-) RightArrow * Right one half screen width (or _<U+0008>N positions).
24+ ESC-( LeftArrow * Left one half screen width (or _<U+0008>N positions).
25+ ESC-} ^RightArrow Right to last column displayed.
26+ ESC-{ ^LeftArrow Left to first column.
27+ F Forward forever; like "tail -f".
28+ ESC-F Like F but stop when search pattern is found.
29+ r ^R ^L Repaint screen.
30+ R Repaint screen, discarding buffered input.
31+ ---------------------------------------------------
32+ Default "window" is the screen height.
33+ Default "half-window" is half of the screen height.
34+ ---------------------------------------------------------------------------
35+
36+ S<U+0008>SE<U+0008>EA<U+0008>AR<U+0008>RC<U+0008>CH<U+0008>HI<U+0008>IN<U+0008>NG<U+0008>G
37+
38+ /_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search forward for (_<U+0008>N-th) matching line.
39+ ?_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Search backward for (_<U+0008>N-th) matching line.
40+ n * Repeat previous search (for _<U+0008>N-th occurrence).
41+ N * Repeat previous search in reverse direction.
42+ ESC-n * Repeat previous search, spanning files.
43+ ESC-N * Repeat previous search, reverse dir. & spanning files.
44+ ESC-u Undo (toggle) search highlighting.
45+ ESC-U Clear search highlighting.
46+ &_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n * Display only matching lines.
47+ ---------------------------------------------------
48+ A search pattern may begin with one or more of:
49+ ^N or ! Search for NON-matching lines.
50+ ^E or * Search multiple files (pass thru END OF FILE).
51+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
52+ ^K Highlight matches, but don't move (KEEP position).
53+ ^R Don't use REGULAR EXPRESSIONS.
54+ ^S _<U+0008>n Search for match in _<U+0008>n-th parenthesized subpattern.
55+ ^W WRAP search if no match found.
56+ ---------------------------------------------------------------------------
57+
58+ J<U+0008>JU<U+0008>UM<U+0008>MP<U+0008>PI<U+0008>IN<U+0008>NG<U+0008>G
59+
60+ g < ESC-< * Go to first line in file (or line _<U+0008>N).
61+ G > ESC-> * Go to last line in file (or line _<U+0008>N).
62+ p % * Go to beginning of file (or _<U+0008>N percent into file).
63+ t * Go to the (_<U+0008>N-th) next tag.
64+ T * Go to the (_<U+0008>N-th) previous tag.
65+ { ( [ * Find close bracket } ) ].
66+ } ) ] * Find open bracket { ( [.
67+ ESC-^F _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find close bracket _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>>.
68+ ESC-^B _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>> _<U+0008><_<U+0008>c_<U+0008>2_<U+0008>> * Find open bracket _<U+0008><_<U+0008>c_<U+0008>1_<U+0008>>.
69+ ---------------------------------------------------
70+ Each "find close bracket" command goes forward to the close bracket
71+ matching the (_<U+0008>N-th) open bracket in the top line.
72+ Each "find open bracket" command goes backward to the open bracket
73+ matching the (_<U+0008>N-th) close bracket in the bottom line.
74+
75+ m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current top line with <letter>.
76+ M_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Mark the current bottom line with <letter>.
77+ '_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Go to a previously marked position.
78+ '' Go to the previous position.
79+ ^X^X Same as '.
80+ ESC-m_<U+0008><_<U+0008>l_<U+0008>e_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>> Clear a mark.
81+ ---------------------------------------------------
82+ A mark is any upper-case or lower-case letter.
83+ Certain marks are predefined:
84+ ^ means beginning of the file
85+ $ means end of the file
86+ ---------------------------------------------------------------------------
87+
88+ C<U+0008>CH<U+0008>HA<U+0008>AN<U+0008>NG<U+0008>GI<U+0008>IN<U+0008>NG<U+0008>G F<U+0008>FI<U+0008>IL<U+0008>LE<U+0008>ES<U+0008>S
89+
90+ :e [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] Examine a new file.
91+ ^X^V Same as :e.
92+ :n * Examine the (_<U+0008>N-th) next file from the command line.
93+ :p * Examine the (_<U+0008>N-th) previous file from the command line.
94+ :x * Examine the first (or _<U+0008>N-th) file from the command line.
95+ :d Delete the current file from the command line list.
96+ = ^G :f Print current file name.
97+ ---------------------------------------------------------------------------
98+
99+ M<U+0008>MI<U+0008>IS<U+0008>SC<U+0008>CE<U+0008>EL<U+0008>LL<U+0008>LA<U+0008>AN<U+0008>NE<U+0008>EO<U+0008>OU<U+0008>US<U+0008>S C<U+0008>CO<U+0008>OM<U+0008>MM<U+0008>MA<U+0008>AN<U+0008>ND<U+0008>DS<U+0008>S
100+
101+ -_<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Toggle a command line option [see OPTIONS below].
102+ --_<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Toggle a command line option, by name.
103+ __<U+0008><_<U+0008>f_<U+0008>l_<U+0008>a_<U+0008>g_<U+0008>> Display the setting of a command line option.
104+ ___<U+0008><_<U+0008>n_<U+0008>a_<U+0008>m_<U+0008>e_<U+0008>> Display the setting of an option, by name.
105+ +_<U+0008>c_<U+0008>m_<U+0008>d Execute the less cmd each time a new file is examined.
106+
107+ !_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command with $SHELL.
108+ #_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Execute the shell command, expanded like a prompt.
109+ |X<U+0008>X_<U+0008>c_<U+0008>o_<U+0008>m_<U+0008>m_<U+0008>a_<U+0008>n_<U+0008>d Pipe file between current pos & mark X<U+0008>X to shell command.
110+ s _<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e Save input to a file.
111+ v Edit the current file with $VISUAL or $EDITOR.
112+ V Print version number of "less".
113+ ---------------------------------------------------------------------------
114+
115+ O<U+0008>OP<U+0008>PT<U+0008>TI<U+0008>IO<U+0008>ON<U+0008>NS<U+0008>S
116+
117+ Most options may be changed either on the command line,
118+ or from within less by using the - or -- command.
119+ Options may be given in one of two forms: either a single
120+ character preceded by a -, or a name preceded by --.
121+
122+ -? ........ --help
123+ Display help (from command line).
124+ -a ........ --search-skip-screen
125+ Search skips current screen.
126+ -A ........ --SEARCH-SKIP-SCREEN
127+ Search starts just after target line.
128+ -b [_<U+0008>N] .... --buffers=[_<U+0008>N]
129+ Number of buffers.
130+ -B ........ --auto-buffers
131+ Don't automatically allocate buffers for pipes.
132+ -c ........ --clear-screen
133+ Repaint by clearing rather than scrolling.
134+ -d ........ --dumb
135+ Dumb terminal.
136+ -D x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r . --color=x<U+0008>x_<U+0008>c_<U+0008>o_<U+0008>l_<U+0008>o_<U+0008>r
137+ Set screen colors.
138+ -e -E .... --quit-at-eof --QUIT-AT-EOF
139+ Quit at end of file.
140+ -f ........ --force
141+ Force open non-regular files.
142+ -F ........ --quit-if-one-screen
143+ Quit if entire file fits on first screen.
144+ -g ........ --hilite-search
145+ Highlight only last match for searches.
146+ -G ........ --HILITE-SEARCH
147+ Don't highlight any matches for searches.
148+ -h [_<U+0008>N] .... --max-back-scroll=[_<U+0008>N]
149+ Backward scroll limit.
150+ -i ........ --ignore-case
151+ Ignore case in searches that do not contain uppercase.
152+ -I ........ --IGNORE-CASE
153+ Ignore case in all searches.
154+ -j [_<U+0008>N] .... --jump-target=[_<U+0008>N]
155+ Screen position of target lines.
156+ -J ........ --status-column
157+ Display a status column at left edge of screen.
158+ -k [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --lesskey-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
159+ Use a lesskey file.
160+ -K ........ --quit-on-intr
161+ Exit less in response to ctrl-C.
162+ -L ........ --no-lessopen
163+ Ignore the LESSOPEN environment variable.
164+ -m -M .... --long-prompt --LONG-PROMPT
165+ Set prompt style.
166+ -n ......... --line-numbers
167+ Suppress line numbers in prompts and messages.
168+ -N ......... --LINE-NUMBERS
169+ Display line number at start of each line.
170+ -o [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --log-file=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
171+ Copy to log file (standard input only).
172+ -O [_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] . --LOG-FILE=[_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
173+ Copy to log file (unconditionally overwrite).
174+ -p [_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n] --pattern=[_<U+0008>p_<U+0008>a_<U+0008>t_<U+0008>t_<U+0008>e_<U+0008>r_<U+0008>n]
175+ Start at pattern (from command line).
176+ -P [_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t] --prompt=[_<U+0008>p_<U+0008>r_<U+0008>o_<U+0008>m_<U+0008>p_<U+0008>t]
177+ Define new prompt.
178+ -q -Q .... --quiet --QUIET --silent --SILENT
179+ Quiet the terminal bell.
180+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
181+ Output "raw" control characters.
182+ -s ........ --squeeze-blank-lines
183+ Squeeze multiple blank lines.
184+ -S ........ --chop-long-lines
185+ Chop (truncate) long lines rather than wrapping.
186+ -t [_<U+0008>t_<U+0008>a_<U+0008>g] .. --tag=[_<U+0008>t_<U+0008>a_<U+0008>g]
187+ Find a tag.
188+ -T [_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e] --tag-file=[_<U+0008>t_<U+0008>a_<U+0008>g_<U+0008>s_<U+0008>f_<U+0008>i_<U+0008>l_<U+0008>e]
189+ Use an alternate tags file.
190+ -u -U .... --underline-special --UNDERLINE-SPECIAL
191+ Change handling of backspaces, tabs and carriage returns.
192+ -V ........ --version
193+ Display the version number of "less".
194+ -w ........ --hilite-unread
195+ Highlight first new line after forward-screen.
196+ -W ........ --HILITE-UNREAD
197+ Highlight first new line after any forward movement.
198+ -x [_<U+0008>N[,...]] --tabs=[_<U+0008>N[,...]]
199+ Set tab stops.
200+ -X ........ --no-init
201+ Don't use termcap init/deinit strings.
202+ -y [_<U+0008>N] .... --max-forw-scroll=[_<U+0008>N]
203+ Forward scroll limit.
204+ -z [_<U+0008>N] .... --window=[_<U+0008>N]
205+ Set size of window.
206+ -" [_<U+0008>c[_<U+0008>c]] . --quotes=[_<U+0008>c[_<U+0008>c]]
207+ Set shell quote characters.
208+ -~ ........ --tilde
209+ Don't display tildes after end of file.
210+ -# [_<U+0008>N] .... --shift=[_<U+0008>N]
211+ Set horizontal scroll amount (0 = one half screen width).
212+ --exit-follow-on-close
213+ Exit F command on a pipe when writer closes pipe.
214+ --file-size
215+ Automatically determine the size of the input file.
216+ --follow-name
217+ The F command changes files if the input file is renamed.
218+ --header=[_<U+0008>N[,_<U+0008>M]]
219+ Use N lines and M columns to display file headers.
220+ --incsearch
221+ Search file as each pattern character is typed in.
222+ --intr=_<U+0008>C
223+ Use _<U+0008>C instead of ^X to interrupt a read.
224+ --line-num-width=_<U+0008>N
225+ Set the width of the -N line number field to _<U+0008>N characters.
226+ --modelines=_<U+0008>N
227+ Read _<U+0008>N lines from the input file and look for vim modelines.
228+ --mouse
229+ Enable mouse input.
230+ --no-keypad
231+ Don't send termcap keypad init/deinit strings.
232+ --no-histdups
233+ Remove duplicates from command history.
234+ --no-number-headers
235+ Don't give line numbers to header lines.
236+ --no-search-headers
237+ Don't search in header lines or columns.
238+ --no-vbell
239+ Disable the terminal's visual bell.
240+ --redraw-on-quit
241+ Redraw final screen when quitting.
242+ --rscroll=_<U+0008>C
243+ Set the character used to mark truncated lines.
244+ --save-marks
245+ Retain marks across invocations of less.
246+ --search-options=[EFKNRW-]
247+ Set default options for every search.
248+ --show-preproc-errors
249+ Display a message if preprocessor exits with an error status.
250+ --proc-backspace
251+ Process backspaces for bold/underline.
252+ --SPECIAL-BACKSPACE
253+ Treat backspaces as control characters.
254+ --proc-return
255+ Delete carriage returns before newline.
256+ --SPECIAL-RETURN
257+ Treat carriage returns as control characters.
258+ --proc-tab
259+ Expand tabs to spaces.
260+ --SPECIAL-TAB
261+ Treat tabs as control characters.
262+ --status-col-width=_<U+0008>N
263+ Set the width of the -J status column to _<U+0008>N characters.
264+ --status-line
265+ Highlight or color the entire line containing a mark.
266+ --use-backslash
267+ Subsequent options use backslash as escape char.
268+ --use-color
269+ Enables colored text.
270+ --wheel-lines=_<U+0008>N
271+ Each click of the mouse wheel moves _<U+0008>N lines.
272+ --wordwrap
273+ Wrap lines at spaces.
274+
275+
276+ ---------------------------------------------------------------------------
277+
278+ L<U+0008>LI<U+0008>IN<U+0008>NE<U+0008>E E<U+0008>ED<U+0008>DI<U+0008>IT<U+0008>TI<U+0008>IN<U+0008>NG<U+0008>G
279+
280+ These keys can be used to edit text being entered
281+ on the "command line" at the bottom of the screen.
282+
283+ RightArrow ..................... ESC-l ... Move cursor right one character.
284+ LeftArrow ...................... ESC-h ... Move cursor left one character.
285+ ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
286+ ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
287+ HOME ........................... ESC-0 ... Move cursor to start of line.
288+ END ............................ ESC-$ ... Move cursor to end of line.
289+ BACKSPACE ................................ Delete char to left of cursor.
290+ DELETE ......................... ESC-x ... Delete char under cursor.
291+ ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
292+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
293+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
294+ UpArrow ........................ ESC-k ... Retrieve previous command line.
295+ DownArrow ...................... ESC-j ... Retrieve next command line.
296+ TAB ...................................... Complete filename & cycle.
297+ SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
298+ ctrl-L ................................... Complete filename, list all.
A tall-traefik-form" +404 -0
@@ -0,0 +1,404 @@
1+<U+001B>[1mdiff --git a/changes.txt b/changes.txt<U+001B>[m
2+<U+001B>[1mdeleted file mode 100644<U+001B>[m
3+<U+001B>[1mindex ab78b90..0000000<U+001B>[m
4+<U+001B>[1m--- a/changes.txt<U+001B>[m
5+<U+001B>[1m+++ /dev/null<U+001B>[m
6+<U+001B>[36m@@ -1,326 +0,0 @@<U+001B>[m
7+<U+001B>[31m-diff --git a/:memory b/:memory<U+001B>[m
8+<U+001B>[31m-new file mode 100644<U+001B>[m
9+<U+001B>[31m-index 0000000..9760229<U+001B>[m
10+<U+001B>[31m-Binary files /dev/null and b/:memory differ<U+001B>[m
11+<U+001B>[31m-diff --git a/cmd/main.go b/cmd/main.go<U+001B>[m
12+<U+001B>[31m-index 6a95d32..b8e2cd4 100644<U+001B>[m
13+<U+001B>[31m---- a/cmd/main.go<U+001B>[m
14+<U+001B>[31m-+++ b/cmd/main.go<U+001B>[m
15+<U+001B>[31m-@@ -4,17 +4,30 @@ import (<U+001B>[m
16+<U+001B>[31m- "fmt"<U+001B>[m
17+<U+001B>[31m- "log/slog"<U+001B>[m
18+<U+001B>[31m- <U+001B>[m
19+<U+001B>[31m-+ "github.com/labstack/echo/v4"<U+001B>[m
20+<U+001B>[31m-+<U+001B>[m
21+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
22+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve"<U+001B>[m
23+<U+001B>[31m-- "github.com/labstack/echo/v4"<U+001B>[m
24+<U+001B>[31m- )<U+001B>[m
25+<U+001B>[31m- <U+001B>[m
26+<U+001B>[31m- func main() {<U+001B>[m
27+<U+001B>[31m- a := app.NewApp()<U+001B>[m
28+<U+001B>[31m-+<U+001B>[m
29+<U+001B>[31m-+ // Initialize database<U+001B>[m
30+<U+001B>[31m-+ db, err := app.InitializeDB(a)<U+001B>[m
31+<U+001B>[31m-+ if err != nil {<U+001B>[m
32+<U+001B>[31m-+ slog.Error("Failed to load database", err)<U+001B>[m
33+<U+001B>[31m-+ } else {<U+001B>[m
34+<U+001B>[31m-+ slog.Info("Database loaded")<U+001B>[m
35+<U+001B>[31m-+ }<U+001B>[m
36+<U+001B>[31m-+<U+001B>[m
37+<U+001B>[31m-+ fmt.Println(db)<U+001B>[m
38+<U+001B>[31m-+<U+001B>[m
39+<U+001B>[31m- e := echo.New()<U+001B>[m
40+<U+001B>[31m- e.HideBanner = true<U+001B>[m
41+<U+001B>[31m- e.HidePort = true<U+001B>[m
42+<U+001B>[31m- e = httpserve.RegisterRoutes(e, a)<U+001B>[m
43+<U+001B>[31m- slog.Info("Starting server", "port", a.HttpPort)<U+001B>[m
44+<U+001B>[31m- slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort)))<U+001B>[m
45+<U+001B>[31m-+<U+001B>[m
46+<U+001B>[31m- }<U+001B>[m
47+<U+001B>[31m-diff --git a/go.mod b/go.mod<U+001B>[m
48+<U+001B>[31m-index 35e2d3c..8a3ac59 100644<U+001B>[m
49+<U+001B>[31m---- a/go.mod<U+001B>[m
50+<U+001B>[31m-+++ b/go.mod<U+001B>[m
51+<U+001B>[31m-@@ -24,6 +24,7 @@ require (<U+001B>[m
52+<U+001B>[31m- github.com/gorilla/securecookie v1.1.1 // indirect<U+001B>[m
53+<U+001B>[31m- github.com/gorilla/sessions v1.2.1 // indirect<U+001B>[m
54+<U+001B>[31m- github.com/klauspost/compress v1.16.7 // indirect<U+001B>[m
55+<U+001B>[31m-+ github.com/kr/text v0.1.0 // indirect<U+001B>[m
56+<U+001B>[31m- github.com/labstack/gommon v0.4.0 // indirect<U+001B>[m
57+<U+001B>[31m- github.com/mattn/go-colorable v0.1.13 // indirect<U+001B>[m
58+<U+001B>[31m- github.com/mattn/go-isatty v0.0.19 // indirect<U+001B>[m
59+<U+001B>[31m-@@ -35,6 +36,7 @@ require (<U+001B>[m
60+<U+001B>[31m- github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect<U+001B>[m
61+<U+001B>[31m- github.com/opencontainers/runc v1.1.9 // indirect<U+001B>[m
62+<U+001B>[31m- github.com/pkg/errors v0.9.1 // indirect<U+001B>[m
63+<U+001B>[31m-+ github.com/rogpeppe/go-internal v1.11.0 // indirect<U+001B>[m
64+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 // indirect<U+001B>[m
65+<U+001B>[31m- github.com/valyala/bytebufferpool v1.0.0 // indirect<U+001B>[m
66+<U+001B>[31m- github.com/valyala/fasttemplate v1.2.2 // indirect<U+001B>[m
67+<U+001B>[31m-@@ -45,5 +47,6 @@ require (<U+001B>[m
68+<U+001B>[31m- golang.org/x/text v0.11.0 // indirect<U+001B>[m
69+<U+001B>[31m- golang.org/x/time v0.3.0 // indirect<U+001B>[m
70+<U+001B>[31m- golang.org/x/tools v0.7.0 // indirect<U+001B>[m
71+<U+001B>[31m-+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect<U+001B>[m
72+<U+001B>[31m- gotest.tools/v3 v3.5.0 // indirect<U+001B>[m
73+<U+001B>[31m- )<U+001B>[m
74+<U+001B>[31m-diff --git a/go.sum b/go.sum<U+001B>[m
75+<U+001B>[31m-index 7b9b857..17b0bcd 100644<U+001B>[m
76+<U+001B>[31m---- a/go.sum<U+001B>[m
77+<U+001B>[31m-+++ b/go.sum<U+001B>[m
78+<U+001B>[31m-@@ -39,6 +39,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI<U+001B>[m
79+<U+001B>[31m- github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=<U+001B>[m
80+<U+001B>[31m- github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=<U+001B>[m
81+<U+001B>[31m- github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=<U+001B>[m
82+<U+001B>[31m-+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=<U+001B>[m
83+<U+001B>[31m-+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=<U+001B>[m
84+<U+001B>[31m-+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=<U+001B>[m
85+<U+001B>[31m-+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=<U+001B>[m
86+<U+001B>[31m-+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=<U+001B>[m
87+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU=<U+001B>[m
88+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4=<U+001B>[m
89+<U+001B>[31m- github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=<U+001B>[m
90+<U+001B>[31m-@@ -72,6 +77,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=<U+001B>[m
91+<U+001B>[31m- github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=<U+001B>[m
92+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=<U+001B>[m
93+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=<U+001B>[m
94+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=<U+001B>[m
95+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=<U+001B>[m
96+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=<U+001B>[m
97+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=<U+001B>[m
98+<U+001B>[31m- github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=<U+001B>[m
99+<U+001B>[31m-@@ -153,8 +160,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T<U+001B>[m
100+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
101+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
102+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
103+<U+001B>[31m--gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=<U+001B>[m
104+<U+001B>[31m- gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
105+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=<U+001B>[m
106+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
107+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
108+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
109+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=<U+001B>[m
110+<U+001B>[31m-diff --git a/internal/app/app.go b/internal/app/app.go<U+001B>[m
111+<U+001B>[31m-index c6f49ac..1dd44b8 100644<U+001B>[m
112+<U+001B>[31m---- a/internal/app/app.go<U+001B>[m
113+<U+001B>[31m-+++ b/internal/app/app.go<U+001B>[m
114+<U+001B>[31m-@@ -27,6 +27,7 @@ const (<U+001B>[m
115+<U+001B>[31m- type App struct {<U+001B>[m
116+<U+001B>[31m- TemplateFS fs.FS<U+001B>[m
117+<U+001B>[31m- PublicFS fs.FS<U+001B>[m
118+<U+001B>[31m-+ DB interface{}<U+001B>[m
119+<U+001B>[31m- BuildVersion string<U+001B>[m
120+<U+001B>[31m- BuildDir string<U+001B>[m
121+<U+001B>[31m- DBDir string<U+001B>[m
122+<U+001B>[31m-diff --git a/internal/app/sqlite.go b/internal/app/sqlite.go<U+001B>[m
123+<U+001B>[31m-index 9f1b1ab..ddc6a1e 100644<U+001B>[m
124+<U+001B>[31m---- a/internal/app/sqlite.go<U+001B>[m
125+<U+001B>[31m-+++ b/internal/app/sqlite.go<U+001B>[m
126+<U+001B>[31m-@@ -5,58 +5,78 @@ import (<U+001B>[m
127+<U+001B>[31m- "database/sql"<U+001B>[m
128+<U+001B>[31m- "fmt"<U+001B>[m
129+<U+001B>[31m- "os"<U+001B>[m
130+<U+001B>[31m-+ "path/filepath"<U+001B>[m
131+<U+001B>[31m- <U+001B>[m
132+<U+001B>[31m-- sqlite3 "github.com/mattn/go-sqlite3"<U+001B>[m
133+<U+001B>[31m-+ "github.com/bnema/gordon/internal/database/migrate"<U+001B>[m
134+<U+001B>[31m-+ "github.com/mattn/go-sqlite3"<U+001B>[m
135+<U+001B>[31m- )<U+001B>[m
136+<U+001B>[31m- <U+001B>[m
137+<U+001B>[31m--// Initialize in-memory SQLite database and returns its connection<U+001B>[m
138+<U+001B>[31m--func InitDB() (db *sql.DB, err error) {<U+001B>[m
139+<U+001B>[31m-- db, err = sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
140+<U+001B>[31m-- if err != nil {<U+001B>[m
141+<U+001B>[31m-- return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
142+<U+001B>[31m-- }<U+001B>[m
143+<U+001B>[31m-- return<U+001B>[m
144+<U+001B>[31m--}<U+001B>[m
145+<U+001B>[31m--<U+001B>[m
146+<U+001B>[31m--// CloseDB closes the database connection and backs up the in-memory database to disk<U+001B>[m
147+<U+001B>[31m--func CloseDB(db *sql.DB, a *App) error {<U+001B>[m
148+<U+001B>[31m-- if err := BackupDB(db, a); err != nil {<U+001B>[m
149+<U+001B>[31m-- return err<U+001B>[m
150+<U+001B>[31m-- }<U+001B>[m
151+<U+001B>[31m-+// InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory.<U+001B>[m
152+<U+001B>[31m-+// Otherwise, it creates a new in-memory database and bootstraps it.<U+001B>[m
153+<U+001B>[31m-+func InitializeDB(a *App) (*sql.DB, error) {<U+001B>[m
154+<U+001B>[31m-+ dbfp := a.GetDBFilePath()<U+001B>[m
155+<U+001B>[31m- <U+001B>[m
156+<U+001B>[31m-- if err := db.Close(); err != nil {<U+001B>[m
157+<U+001B>[31m-- return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
158+<U+001B>[31m-+ // Check if the directory exists, if not create it<U+001B>[m
159+<U+001B>[31m-+ dir := filepath.Dir(a.DBDir)<U+001B>[m
160+<U+001B>[31m-+ fmt.Println("dir", dir)<U+001B>[m
161+<U+001B>[31m-+ if _, err := os.Stat(dir); os.IsNotExist(err) {<U+001B>[m
162+<U+001B>[31m-+ err = os.MkdirAll(dir, 0755)<U+001B>[m
163+<U+001B>[31m-+ if err != nil {<U+001B>[m
164+<U+001B>[31m-+ return nil, fmt.Errorf("failed to create directory: %v", err)<U+001B>[m
165+<U+001B>[31m-+ }<U+001B>[m
166+<U+001B>[31m- }<U+001B>[m
167+<U+001B>[31m- <U+001B>[m
168+<U+001B>[31m-- return nil<U+001B>[m
169+<U+001B>[31m--}<U+001B>[m
170+<U+001B>[31m-+ // If the database file doesn't exist on disk<U+001B>[m
171+<U+001B>[31m-+ if _, err := os.Stat(dbfp); os.IsNotExist(err) {<U+001B>[m
172+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
173+<U+001B>[31m-+ if err != nil {<U+001B>[m
174+<U+001B>[31m-+ return nil, err<U+001B>[m
175+<U+001B>[31m-+ }<U+001B>[m
176+<U+001B>[31m- <U+001B>[m
177+<U+001B>[31m--// LoadDBIntoMemory loads the database from disk into memory<U+001B>[m
178+<U+001B>[31m--func LoadDBIntoMemory(a *App) (*sql.DB, error) {<U+001B>[m
179+<U+001B>[31m-- dbfp := a.GetDBFilePath()<U+001B>[m
180+<U+001B>[31m-+ // Run the bootstrap<U+001B>[m
181+<U+001B>[31m-+ RunBootstrap(memDB)<U+001B>[m
182+<U+001B>[31m- <U+001B>[m
183+<U+001B>[31m-- if _, err := os.Stat(dbfp); err != nil {<U+001B>[m
184+<U+001B>[31m-- return nil, nil<U+001B>[m
185+<U+001B>[31m-+ // Explicitly backup the in-memory database to disk<U+001B>[m
186+<U+001B>[31m-+ err = BackupDB(memDB, a)<U+001B>[m
187+<U+001B>[31m-+ if err != nil {<U+001B>[m
188+<U+001B>[31m-+ return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err)<U+001B>[m
189+<U+001B>[31m-+ }<U+001B>[m
190+<U+001B>[31m- }<U+001B>[m
191+<U+001B>[31m- <U+001B>[m
192+<U+001B>[31m-- memDB, err := InitDB()<U+001B>[m
193+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
194+<U+001B>[31m- if err != nil {<U+001B>[m
195+<U+001B>[31m- return nil, err<U+001B>[m
196+<U+001B>[31m- }<U+001B>[m
197+<U+001B>[31m--<U+001B>[m
198+<U+001B>[31m- diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
199+<U+001B>[31m- if err != nil {<U+001B>[m
200+<U+001B>[31m- return nil, fmt.Errorf("failed to open database file: %v", err)<U+001B>[m
201+<U+001B>[31m- }<U+001B>[m
202+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
203+<U+001B>[31m- <U+001B>[m
204+<U+001B>[31m- if err := transferData(diskDB, memDB); err != nil {<U+001B>[m
205+<U+001B>[31m- return nil, err<U+001B>[m
206+<U+001B>[31m- }<U+001B>[m
207+<U+001B>[31m--<U+001B>[m
208+<U+001B>[31m- return memDB, nil<U+001B>[m
209+<U+001B>[31m- }<U+001B>[m
210+<U+001B>[31m- <U+001B>[m
211+<U+001B>[31m--// transferData transfers data from srcDB to dstDB<U+001B>[m
212+<U+001B>[31m-+func initInMemoryDB() (*sql.DB, error) {<U+001B>[m
213+<U+001B>[31m-+ db, err := sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
214+<U+001B>[31m-+ if err != nil {<U+001B>[m
215+<U+001B>[31m-+ return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
216+<U+001B>[31m-+ }<U+001B>[m
217+<U+001B>[31m-+ return db, nil<U+001B>[m
218+<U+001B>[31m-+}<U+001B>[m
219+<U+001B>[31m-+<U+001B>[m
220+<U+001B>[31m-+func CloseAndBackupDB(db *sql.DB, a *App) error {<U+001B>[m
221+<U+001B>[31m-+ if err := BackupDB(db, a); err != nil {<U+001B>[m
222+<U+001B>[31m-+ return err<U+001B>[m
223+<U+001B>[31m-+ }<U+001B>[m
224+<U+001B>[31m-+ if err := db.Close(); err != nil {<U+001B>[m
225+<U+001B>[31m-+ return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
226+<U+001B>[31m-+ }<U+001B>[m
227+<U+001B>[31m-+ return nil<U+001B>[m
228+<U+001B>[31m-+}<U+001B>[m
229+<U+001B>[31m-+<U+001B>[m
230+<U+001B>[31m- func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
231+<U+001B>[31m- srcConn, err := srcDB.Conn(context.Background())<U+001B>[m
232+<U+001B>[31m- if err != nil {<U+001B>[m
233+<U+001B>[31m-@@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
234+<U+001B>[31m- return dstConn.Raw(func(dstRawConn interface{}) error {<U+001B>[m
235+<U+001B>[31m- srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
236+<U+001B>[31m- dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
237+<U+001B>[31m--<U+001B>[m
238+<U+001B>[31m- _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main")<U+001B>[m
239+<U+001B>[31m- return err<U+001B>[m
240+<U+001B>[31m- })<U+001B>[m
241+<U+001B>[31m- })<U+001B>[m
242+<U+001B>[31m- }<U+001B>[m
243+<U+001B>[31m- <U+001B>[m
244+<U+001B>[31m--// BackupDB backs up the in-memory database to disk<U+001B>[m
245+<U+001B>[31m- func BackupDB(db *sql.DB, a *App) error {<U+001B>[m
246+<U+001B>[31m- dbfp := a.GetDBFilePath()<U+001B>[m
247+<U+001B>[31m--<U+001B>[m
248+<U+001B>[31m- conn, err := db.Conn(context.Background())<U+001B>[m
249+<U+001B>[31m- if err != nil {<U+001B>[m
250+<U+001B>[31m- return fmt.Errorf("failed to get underlying database connection: %v", err)<U+001B>[m
251+<U+001B>[31m- }<U+001B>[m
252+<U+001B>[31m-+ defer conn.Close()<U+001B>[m
253+<U+001B>[31m- <U+001B>[m
254+<U+001B>[31m- return conn.Raw(func(sqliteConn interface{}) error {<U+001B>[m
255+<U+001B>[31m- sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn)<U+001B>[m
256+<U+001B>[31m-- backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main")<U+001B>[m
257+<U+001B>[31m-+ diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
258+<U+001B>[31m-+ fmt.Println("diskDB", diskDB)<U+001B>[m
259+<U+001B>[31m-+ if err != nil {<U+001B>[m
260+<U+001B>[31m-+ return fmt.Errorf("failed to open destination database file: %v", err)<U+001B>[m
261+<U+001B>[31m-+ }<U+001B>[m
262+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
263+<U+001B>[31m-+<U+001B>[m
264+<U+001B>[31m-+ diskConn, err := diskDB.Conn(context.Background())<U+001B>[m
265+<U+001B>[31m- if err != nil {<U+001B>[m
266+<U+001B>[31m-- return fmt.Errorf("failed to create backup: %v", err)<U+001B>[m
267+<U+001B>[31m-+ return fmt.Errorf("failed to get connection from diskDB: %v", err)<U+001B>[m
268+<U+001B>[31m- }<U+001B>[m
269+<U+001B>[31m-+ defer diskConn.Close()<U+001B>[m
270+<U+001B>[31m-+<U+001B>[m
271+<U+001B>[31m-+ var backupDB *sqlite3.SQLiteBackup<U+001B>[m
272+<U+001B>[31m-+ err = diskConn.Raw(func(diskRawConn interface{}) error {<U+001B>[m
273+<U+001B>[31m-+ backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main")<U+001B>[m
274+<U+001B>[31m-+ return err<U+001B>[m
275+<U+001B>[31m-+ })<U+001B>[m
276+<U+001B>[31m- defer backupDB.Close()<U+001B>[m
277+<U+001B>[31m- <U+001B>[m
278+<U+001B>[31m- _, err = backupDB.Step(-1)<U+001B>[m
279+<U+001B>[31m- if err != nil {<U+001B>[m
280+<U+001B>[31m- return fmt.Errorf("failed to backup in-memory database: %v", err)<U+001B>[m
281+<U+001B>[31m- }<U+001B>[m
282+<U+001B>[31m--<U+001B>[m
283+<U+001B>[31m- return nil<U+001B>[m
284+<U+001B>[31m- })<U+001B>[m
285+<U+001B>[31m- }<U+001B>[m
286+<U+001B>[31m-+<U+001B>[m
287+<U+001B>[31m-+func RunBootstrap(db *sql.DB) error {<U+001B>[m
288+<U+001B>[31m-+ return migrate.CreateUserTable(db)<U+001B>[m
289+<U+001B>[31m-+}<U+001B>[m
290+<U+001B>[31m-diff --git a/internal/database/migrate/bootstrapdb.go b/internal/database/migrate/bootstrapdb.go<U+001B>[m
291+<U+001B>[31m-new file mode 100644<U+001B>[m
292+<U+001B>[31m-index 0000000..1f53246<U+001B>[m
293+<U+001B>[31m---- /dev/null<U+001B>[m
294+<U+001B>[31m-+++ b/internal/database/migrate/bootstrapdb.go<U+001B>[m
295+<U+001B>[31m-@@ -0,0 +1,24 @@<U+001B>[m
296+<U+001B>[31m-+package migrate<U+001B>[m
297+<U+001B>[31m-+<U+001B>[m
298+<U+001B>[31m-+import (<U+001B>[m
299+<U+001B>[31m-+ "database/sql"<U+001B>[m
300+<U+001B>[31m-+ "fmt"<U+001B>[m
301+<U+001B>[31m-+)<U+001B>[m
302+<U+001B>[31m-+<U+001B>[m
303+<U+001B>[31m-+// createUserTable creates the 'user' table in the provided SQLite database.<U+001B>[m
304+<U+001B>[31m-+func CreateUserTable(db *sql.DB) error {<U+001B>[m
305+<U+001B>[31m-+ createTableSQL := `<U+001B>[m
306+<U+001B>[31m-+ CREATE TABLE user (<U+001B>[m
307+<U+001B>[31m-+ id INTEGER PRIMARY KEY AUTOINCREMENT,<U+001B>[m
308+<U+001B>[31m-+ username TEXT NOT NULL,<U+001B>[m
309+<U+001B>[31m-+ password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario.<U+001B>[m
310+<U+001B>[31m-+ oauth_token TEXT<U+001B>[m
311+<U+001B>[31m-+ );`<U+001B>[m
312+<U+001B>[31m-+<U+001B>[m
313+<U+001B>[31m-+ _, err := db.Exec(createTableSQL)<U+001B>[m
314+<U+001B>[31m-+ if err != nil {<U+001B>[m
315+<U+001B>[31m-+ return fmt.Errorf("failed to create user table: %v", err)<U+001B>[m
316+<U+001B>[31m-+ }<U+001B>[m
317+<U+001B>[31m-+<U+001B>[m
318+<U+001B>[31m-+ return nil<U+001B>[m
319+<U+001B>[31m-+}<U+001B>[m
320+<U+001B>[31m-diff --git a/internal/httpserve/handler/adminroutes.go b/internal/httpserve/handler/adminroutes.go<U+001B>[m
321+<U+001B>[31m-index 8701ae3..f4acd64 100644<U+001B>[m
322+<U+001B>[31m---- a/internal/httpserve/handler/adminroutes.go<U+001B>[m
323+<U+001B>[31m-+++ b/internal/httpserve/handler/adminroutes.go<U+001B>[m
324+<U+001B>[31m-@@ -1,8 +1,6 @@<U+001B>[m
325+<U+001B>[31m- package handler<U+001B>[m
326+<U+001B>[31m- <U+001B>[m
327+<U+001B>[31m- import (<U+001B>[m
328+<U+001B>[31m-- "fmt"<U+001B>[m
329+<U+001B>[31m--<U+001B>[m
330+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
331+<U+001B>[31m- "github.com/bnema/gordon/internal/gotemplate/render"<U+001B>[m
332+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve/middleware"<U+001B>[m
333+<U+001B>[1mdiff --git a/internal/httpserve/handler/oauth.go b/internal/httpserve/handler/oauth.go<U+001B>[m
334+<U+001B>[1mindex bd40379..b876c75 100644<U+001B>[m
335+<U+001B>[1m--- a/internal/httpserve/handler/oauth.go<U+001B>[m
336+<U+001B>[1m+++ b/internal/httpserve/handler/oauth.go<U+001B>[m
337+<U+001B>[36m@@ -12,6 +12,7 @@<U+001B>[m <U+001B>[mimport (<U+001B>[m
338+ "github.com/markbates/goth/gothic"<U+001B>[m
339+ "github.com/markbates/goth/providers/github"<U+001B>[m
340+ "net/http"<U+001B>[m
341+<U+001B>[32m+<U+001B>[m <U+001B>[32m"os"<U+001B>[m
342+ )<U+001B>[m
343+ <U+001B>[m
344+ var providerNames []string<U+001B>[m
345+<U+001B>[1mdiff --git a/internal/httpserve/router.go b/internal/httpserve/router.go<U+001B>[m
346+<U+001B>[1mindex bc1368c..edd2748 100644<U+001B>[m
347+<U+001B>[1m--- a/internal/httpserve/router.go<U+001B>[m
348+<U+001B>[1m+++ b/internal/httpserve/router.go<U+001B>[m
349+<U+001B>[36m@@ -19,34 +19,19 @@<U+001B>[m <U+001B>[mfunc RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {<U+001B>[m
350+ // Use session middleware<U+001B>[m
351+ // Add session middleware<U+001B>[m
352+ e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))<U+001B>[m
353+<U+001B>[31m- // e.Use(middleware.SecureRoutes())<U+001B>[m
354+ e.Use(middleware.ColorSchemeDetection)<U+001B>[m
355+ e.Use(middleware.LanguageDetection)<U+001B>[m
356+ <U+001B>[m
357+ // Register routes<U+001B>[m
358+<U+001B>[31m-<U+001B>[m
359+<U+001B>[31m- // Serve admin routes<U+001B>[m
360+ bindAdminRoute(e, a, AdminPath)<U+001B>[m
361+<U+001B>[31m- // Serve static files<U+001B>[m
362+ bindStaticRoute(e, a, "/*")<U+001B>[m
363+<U+001B>[32m+<U+001B>[m <U+001B>[32mbindLoginRoute(e, a, AdminPath)<U+001B>[m
364+<U+001B>[32m+<U+001B>[m
365+ // Protect the root path with a 403<U+001B>[m
366+ e.GET("/", func(c echo.Context) error {<U+001B>[m
367+ return c.String(403, "403 Forbidden")<U+001B>[m
368+ })<U+001B>[m
369+ <U+001B>[m
370+<U+001B>[31m- e.GET(a.AdminPath+"/login", func(c echo.Context) error {<U+001B>[m
371+<U+001B>[31m- return handler.RenderLoginPage(c, a)<U+001B>[m
372+<U+001B>[31m- })<U+001B>[m
373+<U+001B>[31m- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
374+<U+001B>[31m- return handler.StartOAuth(c, a)<U+001B>[m
375+<U+001B>[31m- })<U+001B>[m
376+<U+001B>[31m- e.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
377+<U+001B>[31m- return handler.OAuthCallback(c, a)<U+001B>[m
378+<U+001B>[31m- })<U+001B>[m
379+<U+001B>[31m- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {<U+001B>[m
380+<U+001B>[31m- return handler.Logout(c, a)<U+001B>[m
381+<U+001B>[31m- })<U+001B>[m
382+<U+001B>[31m-<U+001B>[m
383+ return e<U+001B>[m
384+ }<U+001B>[m
385+ <U+001B>[m
386+<U+001B>[36m@@ -61,3 +46,18 @@<U+001B>[m <U+001B>[mfunc bindAdminRoute(e *echo.Echo, a *app.App, path string) {<U+001B>[m
387+ return handler.AdminRoute(c, a)<U+001B>[m
388+ })<U+001B>[m
389+ }<U+001B>[m
390+<U+001B>[32m+<U+001B>[m
391+<U+001B>[32m+<U+001B>[m<U+001B>[32mfunc bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {<U+001B>[m
392+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login", func(c echo.Context) error {<U+001B>[m
393+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.RenderLoginPage(c, a)<U+001B>[m
394+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
395+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
396+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.StartOAuth(c, a)<U+001B>[m
397+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
398+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
399+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.OAuthCallback(c, a)<U+001B>[m
400+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
401+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/logout", func(c echo.Context) error {<U+001B>[m
402+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.Logout(c, a)<U+001B>[m
403+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
404+<U+001B>[32m+<U+001B>[m<U+001B>[32m}<U+001B>[m
@@ -0,0 +1,404 @@
1+<U+001B>[1mdiff --git a/changes.txt b/changes.txt<U+001B>[m
2+<U+001B>[1mdeleted file mode 100644<U+001B>[m
3+<U+001B>[1mindex ab78b90..0000000<U+001B>[m
4+<U+001B>[1m--- a/changes.txt<U+001B>[m
5+<U+001B>[1m+++ /dev/null<U+001B>[m
6+<U+001B>[36m@@ -1,326 +0,0 @@<U+001B>[m
7+<U+001B>[31m-diff --git a/:memory b/:memory<U+001B>[m
8+<U+001B>[31m-new file mode 100644<U+001B>[m
9+<U+001B>[31m-index 0000000..9760229<U+001B>[m
10+<U+001B>[31m-Binary files /dev/null and b/:memory differ<U+001B>[m
11+<U+001B>[31m-diff --git a/cmd/main.go b/cmd/main.go<U+001B>[m
12+<U+001B>[31m-index 6a95d32..b8e2cd4 100644<U+001B>[m
13+<U+001B>[31m---- a/cmd/main.go<U+001B>[m
14+<U+001B>[31m-+++ b/cmd/main.go<U+001B>[m
15+<U+001B>[31m-@@ -4,17 +4,30 @@ import (<U+001B>[m
16+<U+001B>[31m- "fmt"<U+001B>[m
17+<U+001B>[31m- "log/slog"<U+001B>[m
18+<U+001B>[31m- <U+001B>[m
19+<U+001B>[31m-+ "github.com/labstack/echo/v4"<U+001B>[m
20+<U+001B>[31m-+<U+001B>[m
21+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
22+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve"<U+001B>[m
23+<U+001B>[31m-- "github.com/labstack/echo/v4"<U+001B>[m
24+<U+001B>[31m- )<U+001B>[m
25+<U+001B>[31m- <U+001B>[m
26+<U+001B>[31m- func main() {<U+001B>[m
27+<U+001B>[31m- a := app.NewApp()<U+001B>[m
28+<U+001B>[31m-+<U+001B>[m
29+<U+001B>[31m-+ // Initialize database<U+001B>[m
30+<U+001B>[31m-+ db, err := app.InitializeDB(a)<U+001B>[m
31+<U+001B>[31m-+ if err != nil {<U+001B>[m
32+<U+001B>[31m-+ slog.Error("Failed to load database", err)<U+001B>[m
33+<U+001B>[31m-+ } else {<U+001B>[m
34+<U+001B>[31m-+ slog.Info("Database loaded")<U+001B>[m
35+<U+001B>[31m-+ }<U+001B>[m
36+<U+001B>[31m-+<U+001B>[m
37+<U+001B>[31m-+ fmt.Println(db)<U+001B>[m
38+<U+001B>[31m-+<U+001B>[m
39+<U+001B>[31m- e := echo.New()<U+001B>[m
40+<U+001B>[31m- e.HideBanner = true<U+001B>[m
41+<U+001B>[31m- e.HidePort = true<U+001B>[m
42+<U+001B>[31m- e = httpserve.RegisterRoutes(e, a)<U+001B>[m
43+<U+001B>[31m- slog.Info("Starting server", "port", a.HttpPort)<U+001B>[m
44+<U+001B>[31m- slog.Error("Server error", e.Start(fmt.Sprintf(":%d", a.HttpPort)))<U+001B>[m
45+<U+001B>[31m-+<U+001B>[m
46+<U+001B>[31m- }<U+001B>[m
47+<U+001B>[31m-diff --git a/go.mod b/go.mod<U+001B>[m
48+<U+001B>[31m-index 35e2d3c..8a3ac59 100644<U+001B>[m
49+<U+001B>[31m---- a/go.mod<U+001B>[m
50+<U+001B>[31m-+++ b/go.mod<U+001B>[m
51+<U+001B>[31m-@@ -24,6 +24,7 @@ require (<U+001B>[m
52+<U+001B>[31m- github.com/gorilla/securecookie v1.1.1 // indirect<U+001B>[m
53+<U+001B>[31m- github.com/gorilla/sessions v1.2.1 // indirect<U+001B>[m
54+<U+001B>[31m- github.com/klauspost/compress v1.16.7 // indirect<U+001B>[m
55+<U+001B>[31m-+ github.com/kr/text v0.1.0 // indirect<U+001B>[m
56+<U+001B>[31m- github.com/labstack/gommon v0.4.0 // indirect<U+001B>[m
57+<U+001B>[31m- github.com/mattn/go-colorable v0.1.13 // indirect<U+001B>[m
58+<U+001B>[31m- github.com/mattn/go-isatty v0.0.19 // indirect<U+001B>[m
59+<U+001B>[31m-@@ -35,6 +36,7 @@ require (<U+001B>[m
60+<U+001B>[31m- github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect<U+001B>[m
61+<U+001B>[31m- github.com/opencontainers/runc v1.1.9 // indirect<U+001B>[m
62+<U+001B>[31m- github.com/pkg/errors v0.9.1 // indirect<U+001B>[m
63+<U+001B>[31m-+ github.com/rogpeppe/go-internal v1.11.0 // indirect<U+001B>[m
64+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 // indirect<U+001B>[m
65+<U+001B>[31m- github.com/valyala/bytebufferpool v1.0.0 // indirect<U+001B>[m
66+<U+001B>[31m- github.com/valyala/fasttemplate v1.2.2 // indirect<U+001B>[m
67+<U+001B>[31m-@@ -45,5 +47,6 @@ require (<U+001B>[m
68+<U+001B>[31m- golang.org/x/text v0.11.0 // indirect<U+001B>[m
69+<U+001B>[31m- golang.org/x/time v0.3.0 // indirect<U+001B>[m
70+<U+001B>[31m- golang.org/x/tools v0.7.0 // indirect<U+001B>[m
71+<U+001B>[31m-+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect<U+001B>[m
72+<U+001B>[31m- gotest.tools/v3 v3.5.0 // indirect<U+001B>[m
73+<U+001B>[31m- )<U+001B>[m
74+<U+001B>[31m-diff --git a/go.sum b/go.sum<U+001B>[m
75+<U+001B>[31m-index 7b9b857..17b0bcd 100644<U+001B>[m
76+<U+001B>[31m---- a/go.sum<U+001B>[m
77+<U+001B>[31m-+++ b/go.sum<U+001B>[m
78+<U+001B>[31m-@@ -39,6 +39,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI<U+001B>[m
79+<U+001B>[31m- github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=<U+001B>[m
80+<U+001B>[31m- github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=<U+001B>[m
81+<U+001B>[31m- github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=<U+001B>[m
82+<U+001B>[31m-+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=<U+001B>[m
83+<U+001B>[31m-+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=<U+001B>[m
84+<U+001B>[31m-+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=<U+001B>[m
85+<U+001B>[31m-+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=<U+001B>[m
86+<U+001B>[31m-+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=<U+001B>[m
87+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU=<U+001B>[m
88+<U+001B>[31m- github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4=<U+001B>[m
89+<U+001B>[31m- github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=<U+001B>[m
90+<U+001B>[31m-@@ -72,6 +77,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=<U+001B>[m
91+<U+001B>[31m- github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=<U+001B>[m
92+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=<U+001B>[m
93+<U+001B>[31m- github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=<U+001B>[m
94+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=<U+001B>[m
95+<U+001B>[31m-+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=<U+001B>[m
96+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=<U+001B>[m
97+<U+001B>[31m- github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=<U+001B>[m
98+<U+001B>[31m- github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=<U+001B>[m
99+<U+001B>[31m-@@ -153,8 +160,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T<U+001B>[m
100+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
101+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
102+<U+001B>[31m- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=<U+001B>[m
103+<U+001B>[31m--gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=<U+001B>[m
104+<U+001B>[31m- gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
105+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=<U+001B>[m
106+<U+001B>[31m-+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=<U+001B>[m
107+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
108+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=<U+001B>[m
109+<U+001B>[31m- gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=<U+001B>[m
110+<U+001B>[31m-diff --git a/internal/app/app.go b/internal/app/app.go<U+001B>[m
111+<U+001B>[31m-index c6f49ac..1dd44b8 100644<U+001B>[m
112+<U+001B>[31m---- a/internal/app/app.go<U+001B>[m
113+<U+001B>[31m-+++ b/internal/app/app.go<U+001B>[m
114+<U+001B>[31m-@@ -27,6 +27,7 @@ const (<U+001B>[m
115+<U+001B>[31m- type App struct {<U+001B>[m
116+<U+001B>[31m- TemplateFS fs.FS<U+001B>[m
117+<U+001B>[31m- PublicFS fs.FS<U+001B>[m
118+<U+001B>[31m-+ DB interface{}<U+001B>[m
119+<U+001B>[31m- BuildVersion string<U+001B>[m
120+<U+001B>[31m- BuildDir string<U+001B>[m
121+<U+001B>[31m- DBDir string<U+001B>[m
122+<U+001B>[31m-diff --git a/internal/app/sqlite.go b/internal/app/sqlite.go<U+001B>[m
123+<U+001B>[31m-index 9f1b1ab..ddc6a1e 100644<U+001B>[m
124+<U+001B>[31m---- a/internal/app/sqlite.go<U+001B>[m
125+<U+001B>[31m-+++ b/internal/app/sqlite.go<U+001B>[m
126+<U+001B>[31m-@@ -5,58 +5,78 @@ import (<U+001B>[m
127+<U+001B>[31m- "database/sql"<U+001B>[m
128+<U+001B>[31m- "fmt"<U+001B>[m
129+<U+001B>[31m- "os"<U+001B>[m
130+<U+001B>[31m-+ "path/filepath"<U+001B>[m
131+<U+001B>[31m- <U+001B>[m
132+<U+001B>[31m-- sqlite3 "github.com/mattn/go-sqlite3"<U+001B>[m
133+<U+001B>[31m-+ "github.com/bnema/gordon/internal/database/migrate"<U+001B>[m
134+<U+001B>[31m-+ "github.com/mattn/go-sqlite3"<U+001B>[m
135+<U+001B>[31m- )<U+001B>[m
136+<U+001B>[31m- <U+001B>[m
137+<U+001B>[31m--// Initialize in-memory SQLite database and returns its connection<U+001B>[m
138+<U+001B>[31m--func InitDB() (db *sql.DB, err error) {<U+001B>[m
139+<U+001B>[31m-- db, err = sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
140+<U+001B>[31m-- if err != nil {<U+001B>[m
141+<U+001B>[31m-- return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
142+<U+001B>[31m-- }<U+001B>[m
143+<U+001B>[31m-- return<U+001B>[m
144+<U+001B>[31m--}<U+001B>[m
145+<U+001B>[31m--<U+001B>[m
146+<U+001B>[31m--// CloseDB closes the database connection and backs up the in-memory database to disk<U+001B>[m
147+<U+001B>[31m--func CloseDB(db *sql.DB, a *App) error {<U+001B>[m
148+<U+001B>[31m-- if err := BackupDB(db, a); err != nil {<U+001B>[m
149+<U+001B>[31m-- return err<U+001B>[m
150+<U+001B>[31m-- }<U+001B>[m
151+<U+001B>[31m-+// InitializeDB initializes the SQLite database. If a database file exists on disk, it loads it into memory.<U+001B>[m
152+<U+001B>[31m-+// Otherwise, it creates a new in-memory database and bootstraps it.<U+001B>[m
153+<U+001B>[31m-+func InitializeDB(a *App) (*sql.DB, error) {<U+001B>[m
154+<U+001B>[31m-+ dbfp := a.GetDBFilePath()<U+001B>[m
155+<U+001B>[31m- <U+001B>[m
156+<U+001B>[31m-- if err := db.Close(); err != nil {<U+001B>[m
157+<U+001B>[31m-- return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
158+<U+001B>[31m-+ // Check if the directory exists, if not create it<U+001B>[m
159+<U+001B>[31m-+ dir := filepath.Dir(a.DBDir)<U+001B>[m
160+<U+001B>[31m-+ fmt.Println("dir", dir)<U+001B>[m
161+<U+001B>[31m-+ if _, err := os.Stat(dir); os.IsNotExist(err) {<U+001B>[m
162+<U+001B>[31m-+ err = os.MkdirAll(dir, 0755)<U+001B>[m
163+<U+001B>[31m-+ if err != nil {<U+001B>[m
164+<U+001B>[31m-+ return nil, fmt.Errorf("failed to create directory: %v", err)<U+001B>[m
165+<U+001B>[31m-+ }<U+001B>[m
166+<U+001B>[31m- }<U+001B>[m
167+<U+001B>[31m- <U+001B>[m
168+<U+001B>[31m-- return nil<U+001B>[m
169+<U+001B>[31m--}<U+001B>[m
170+<U+001B>[31m-+ // If the database file doesn't exist on disk<U+001B>[m
171+<U+001B>[31m-+ if _, err := os.Stat(dbfp); os.IsNotExist(err) {<U+001B>[m
172+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
173+<U+001B>[31m-+ if err != nil {<U+001B>[m
174+<U+001B>[31m-+ return nil, err<U+001B>[m
175+<U+001B>[31m-+ }<U+001B>[m
176+<U+001B>[31m- <U+001B>[m
177+<U+001B>[31m--// LoadDBIntoMemory loads the database from disk into memory<U+001B>[m
178+<U+001B>[31m--func LoadDBIntoMemory(a *App) (*sql.DB, error) {<U+001B>[m
179+<U+001B>[31m-- dbfp := a.GetDBFilePath()<U+001B>[m
180+<U+001B>[31m-+ // Run the bootstrap<U+001B>[m
181+<U+001B>[31m-+ RunBootstrap(memDB)<U+001B>[m
182+<U+001B>[31m- <U+001B>[m
183+<U+001B>[31m-- if _, err := os.Stat(dbfp); err != nil {<U+001B>[m
184+<U+001B>[31m-- return nil, nil<U+001B>[m
185+<U+001B>[31m-+ // Explicitly backup the in-memory database to disk<U+001B>[m
186+<U+001B>[31m-+ err = BackupDB(memDB, a)<U+001B>[m
187+<U+001B>[31m-+ if err != nil {<U+001B>[m
188+<U+001B>[31m-+ return nil, fmt.Errorf("failed to backup in-memory database to disk: %v", err)<U+001B>[m
189+<U+001B>[31m-+ }<U+001B>[m
190+<U+001B>[31m- }<U+001B>[m
191+<U+001B>[31m- <U+001B>[m
192+<U+001B>[31m-- memDB, err := InitDB()<U+001B>[m
193+<U+001B>[31m-+ memDB, err := initInMemoryDB()<U+001B>[m
194+<U+001B>[31m- if err != nil {<U+001B>[m
195+<U+001B>[31m- return nil, err<U+001B>[m
196+<U+001B>[31m- }<U+001B>[m
197+<U+001B>[31m--<U+001B>[m
198+<U+001B>[31m- diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
199+<U+001B>[31m- if err != nil {<U+001B>[m
200+<U+001B>[31m- return nil, fmt.Errorf("failed to open database file: %v", err)<U+001B>[m
201+<U+001B>[31m- }<U+001B>[m
202+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
203+<U+001B>[31m- <U+001B>[m
204+<U+001B>[31m- if err := transferData(diskDB, memDB); err != nil {<U+001B>[m
205+<U+001B>[31m- return nil, err<U+001B>[m
206+<U+001B>[31m- }<U+001B>[m
207+<U+001B>[31m--<U+001B>[m
208+<U+001B>[31m- return memDB, nil<U+001B>[m
209+<U+001B>[31m- }<U+001B>[m
210+<U+001B>[31m- <U+001B>[m
211+<U+001B>[31m--// transferData transfers data from srcDB to dstDB<U+001B>[m
212+<U+001B>[31m-+func initInMemoryDB() (*sql.DB, error) {<U+001B>[m
213+<U+001B>[31m-+ db, err := sql.Open("sqlite3", "file::memory?cache=shared")<U+001B>[m
214+<U+001B>[31m-+ if err != nil {<U+001B>[m
215+<U+001B>[31m-+ return nil, fmt.Errorf("failed to open in-memory database: %v", err)<U+001B>[m
216+<U+001B>[31m-+ }<U+001B>[m
217+<U+001B>[31m-+ return db, nil<U+001B>[m
218+<U+001B>[31m-+}<U+001B>[m
219+<U+001B>[31m-+<U+001B>[m
220+<U+001B>[31m-+func CloseAndBackupDB(db *sql.DB, a *App) error {<U+001B>[m
221+<U+001B>[31m-+ if err := BackupDB(db, a); err != nil {<U+001B>[m
222+<U+001B>[31m-+ return err<U+001B>[m
223+<U+001B>[31m-+ }<U+001B>[m
224+<U+001B>[31m-+ if err := db.Close(); err != nil {<U+001B>[m
225+<U+001B>[31m-+ return fmt.Errorf("failed to close database: %v", err)<U+001B>[m
226+<U+001B>[31m-+ }<U+001B>[m
227+<U+001B>[31m-+ return nil<U+001B>[m
228+<U+001B>[31m-+}<U+001B>[m
229+<U+001B>[31m-+<U+001B>[m
230+<U+001B>[31m- func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
231+<U+001B>[31m- srcConn, err := srcDB.Conn(context.Background())<U+001B>[m
232+<U+001B>[31m- if err != nil {<U+001B>[m
233+<U+001B>[31m-@@ -74,35 +94,50 @@ func transferData(srcDB, dstDB *sql.DB) error {<U+001B>[m
234+<U+001B>[31m- return dstConn.Raw(func(dstRawConn interface{}) error {<U+001B>[m
235+<U+001B>[31m- srcSqlite3Conn := srcRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
236+<U+001B>[31m- dstSqlite3Conn := dstRawConn.(*sqlite3.SQLiteConn)<U+001B>[m
237+<U+001B>[31m--<U+001B>[m
238+<U+001B>[31m- _, err := srcSqlite3Conn.Backup("main", dstSqlite3Conn, "main")<U+001B>[m
239+<U+001B>[31m- return err<U+001B>[m
240+<U+001B>[31m- })<U+001B>[m
241+<U+001B>[31m- })<U+001B>[m
242+<U+001B>[31m- }<U+001B>[m
243+<U+001B>[31m- <U+001B>[m
244+<U+001B>[31m--// BackupDB backs up the in-memory database to disk<U+001B>[m
245+<U+001B>[31m- func BackupDB(db *sql.DB, a *App) error {<U+001B>[m
246+<U+001B>[31m- dbfp := a.GetDBFilePath()<U+001B>[m
247+<U+001B>[31m--<U+001B>[m
248+<U+001B>[31m- conn, err := db.Conn(context.Background())<U+001B>[m
249+<U+001B>[31m- if err != nil {<U+001B>[m
250+<U+001B>[31m- return fmt.Errorf("failed to get underlying database connection: %v", err)<U+001B>[m
251+<U+001B>[31m- }<U+001B>[m
252+<U+001B>[31m-+ defer conn.Close()<U+001B>[m
253+<U+001B>[31m- <U+001B>[m
254+<U+001B>[31m- return conn.Raw(func(sqliteConn interface{}) error {<U+001B>[m
255+<U+001B>[31m- sqlite3Conn := sqliteConn.(*sqlite3.SQLiteConn)<U+001B>[m
256+<U+001B>[31m-- backupDB, err := sqlite3Conn.Backup(dbfp, sqlite3Conn, "main")<U+001B>[m
257+<U+001B>[31m-+ diskDB, err := sql.Open("sqlite3", dbfp)<U+001B>[m
258+<U+001B>[31m-+ fmt.Println("diskDB", diskDB)<U+001B>[m
259+<U+001B>[31m-+ if err != nil {<U+001B>[m
260+<U+001B>[31m-+ return fmt.Errorf("failed to open destination database file: %v", err)<U+001B>[m
261+<U+001B>[31m-+ }<U+001B>[m
262+<U+001B>[31m-+ defer diskDB.Close()<U+001B>[m
263+<U+001B>[31m-+<U+001B>[m
264+<U+001B>[31m-+ diskConn, err := diskDB.Conn(context.Background())<U+001B>[m
265+<U+001B>[31m- if err != nil {<U+001B>[m
266+<U+001B>[31m-- return fmt.Errorf("failed to create backup: %v", err)<U+001B>[m
267+<U+001B>[31m-+ return fmt.Errorf("failed to get connection from diskDB: %v", err)<U+001B>[m
268+<U+001B>[31m- }<U+001B>[m
269+<U+001B>[31m-+ defer diskConn.Close()<U+001B>[m
270+<U+001B>[31m-+<U+001B>[m
271+<U+001B>[31m-+ var backupDB *sqlite3.SQLiteBackup<U+001B>[m
272+<U+001B>[31m-+ err = diskConn.Raw(func(diskRawConn interface{}) error {<U+001B>[m
273+<U+001B>[31m-+ backupDB, err = sqlite3Conn.Backup("main", diskRawConn.(*sqlite3.SQLiteConn), "main")<U+001B>[m
274+<U+001B>[31m-+ return err<U+001B>[m
275+<U+001B>[31m-+ })<U+001B>[m
276+<U+001B>[31m- defer backupDB.Close()<U+001B>[m
277+<U+001B>[31m- <U+001B>[m
278+<U+001B>[31m- _, err = backupDB.Step(-1)<U+001B>[m
279+<U+001B>[31m- if err != nil {<U+001B>[m
280+<U+001B>[31m- return fmt.Errorf("failed to backup in-memory database: %v", err)<U+001B>[m
281+<U+001B>[31m- }<U+001B>[m
282+<U+001B>[31m--<U+001B>[m
283+<U+001B>[31m- return nil<U+001B>[m
284+<U+001B>[31m- })<U+001B>[m
285+<U+001B>[31m- }<U+001B>[m
286+<U+001B>[31m-+<U+001B>[m
287+<U+001B>[31m-+func RunBootstrap(db *sql.DB) error {<U+001B>[m
288+<U+001B>[31m-+ return migrate.CreateUserTable(db)<U+001B>[m
289+<U+001B>[31m-+}<U+001B>[m
290+<U+001B>[31m-diff --git a/internal/database/migrate/bootstrapdb.go b/internal/database/migrate/bootstrapdb.go<U+001B>[m
291+<U+001B>[31m-new file mode 100644<U+001B>[m
292+<U+001B>[31m-index 0000000..1f53246<U+001B>[m
293+<U+001B>[31m---- /dev/null<U+001B>[m
294+<U+001B>[31m-+++ b/internal/database/migrate/bootstrapdb.go<U+001B>[m
295+<U+001B>[31m-@@ -0,0 +1,24 @@<U+001B>[m
296+<U+001B>[31m-+package migrate<U+001B>[m
297+<U+001B>[31m-+<U+001B>[m
298+<U+001B>[31m-+import (<U+001B>[m
299+<U+001B>[31m-+ "database/sql"<U+001B>[m
300+<U+001B>[31m-+ "fmt"<U+001B>[m
301+<U+001B>[31m-+)<U+001B>[m
302+<U+001B>[31m-+<U+001B>[m
303+<U+001B>[31m-+// createUserTable creates the 'user' table in the provided SQLite database.<U+001B>[m
304+<U+001B>[31m-+func CreateUserTable(db *sql.DB) error {<U+001B>[m
305+<U+001B>[31m-+ createTableSQL := `<U+001B>[m
306+<U+001B>[31m-+ CREATE TABLE user (<U+001B>[m
307+<U+001B>[31m-+ id INTEGER PRIMARY KEY AUTOINCREMENT,<U+001B>[m
308+<U+001B>[31m-+ username TEXT NOT NULL,<U+001B>[m
309+<U+001B>[31m-+ password TEXT NOT NULL, -- This should be hashed and salted in a real-world scenario.<U+001B>[m
310+<U+001B>[31m-+ oauth_token TEXT<U+001B>[m
311+<U+001B>[31m-+ );`<U+001B>[m
312+<U+001B>[31m-+<U+001B>[m
313+<U+001B>[31m-+ _, err := db.Exec(createTableSQL)<U+001B>[m
314+<U+001B>[31m-+ if err != nil {<U+001B>[m
315+<U+001B>[31m-+ return fmt.Errorf("failed to create user table: %v", err)<U+001B>[m
316+<U+001B>[31m-+ }<U+001B>[m
317+<U+001B>[31m-+<U+001B>[m
318+<U+001B>[31m-+ return nil<U+001B>[m
319+<U+001B>[31m-+}<U+001B>[m
320+<U+001B>[31m-diff --git a/internal/httpserve/handler/adminroutes.go b/internal/httpserve/handler/adminroutes.go<U+001B>[m
321+<U+001B>[31m-index 8701ae3..f4acd64 100644<U+001B>[m
322+<U+001B>[31m---- a/internal/httpserve/handler/adminroutes.go<U+001B>[m
323+<U+001B>[31m-+++ b/internal/httpserve/handler/adminroutes.go<U+001B>[m
324+<U+001B>[31m-@@ -1,8 +1,6 @@<U+001B>[m
325+<U+001B>[31m- package handler<U+001B>[m
326+<U+001B>[31m- <U+001B>[m
327+<U+001B>[31m- import (<U+001B>[m
328+<U+001B>[31m-- "fmt"<U+001B>[m
329+<U+001B>[31m--<U+001B>[m
330+<U+001B>[31m- "github.com/bnema/gordon/internal/app"<U+001B>[m
331+<U+001B>[31m- "github.com/bnema/gordon/internal/gotemplate/render"<U+001B>[m
332+<U+001B>[31m- "github.com/bnema/gordon/internal/httpserve/middleware"<U+001B>[m
333+<U+001B>[1mdiff --git a/internal/httpserve/handler/oauth.go b/internal/httpserve/handler/oauth.go<U+001B>[m
334+<U+001B>[1mindex bd40379..b876c75 100644<U+001B>[m
335+<U+001B>[1m--- a/internal/httpserve/handler/oauth.go<U+001B>[m
336+<U+001B>[1m+++ b/internal/httpserve/handler/oauth.go<U+001B>[m
337+<U+001B>[36m@@ -12,6 +12,7 @@<U+001B>[m <U+001B>[mimport (<U+001B>[m
338+ "github.com/markbates/goth/gothic"<U+001B>[m
339+ "github.com/markbates/goth/providers/github"<U+001B>[m
340+ "net/http"<U+001B>[m
341+<U+001B>[32m+<U+001B>[m <U+001B>[32m"os"<U+001B>[m
342+ )<U+001B>[m
343+ <U+001B>[m
344+ var providerNames []string<U+001B>[m
345+<U+001B>[1mdiff --git a/internal/httpserve/router.go b/internal/httpserve/router.go<U+001B>[m
346+<U+001B>[1mindex bc1368c..edd2748 100644<U+001B>[m
347+<U+001B>[1m--- a/internal/httpserve/router.go<U+001B>[m
348+<U+001B>[1m+++ b/internal/httpserve/router.go<U+001B>[m
349+<U+001B>[36m@@ -19,34 +19,19 @@<U+001B>[m <U+001B>[mfunc RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {<U+001B>[m
350+ // Use session middleware<U+001B>[m
351+ // Add session middleware<U+001B>[m
352+ e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))<U+001B>[m
353+<U+001B>[31m- // e.Use(middleware.SecureRoutes())<U+001B>[m
354+ e.Use(middleware.ColorSchemeDetection)<U+001B>[m
355+ e.Use(middleware.LanguageDetection)<U+001B>[m
356+ <U+001B>[m
357+ // Register routes<U+001B>[m
358+<U+001B>[31m-<U+001B>[m
359+<U+001B>[31m- // Serve admin routes<U+001B>[m
360+ bindAdminRoute(e, a, AdminPath)<U+001B>[m
361+<U+001B>[31m- // Serve static files<U+001B>[m
362+ bindStaticRoute(e, a, "/*")<U+001B>[m
363+<U+001B>[32m+<U+001B>[m <U+001B>[32mbindLoginRoute(e, a, AdminPath)<U+001B>[m
364+<U+001B>[32m+<U+001B>[m
365+ // Protect the root path with a 403<U+001B>[m
366+ e.GET("/", func(c echo.Context) error {<U+001B>[m
367+ return c.String(403, "403 Forbidden")<U+001B>[m
368+ })<U+001B>[m
369+ <U+001B>[m
370+<U+001B>[31m- e.GET(a.AdminPath+"/login", func(c echo.Context) error {<U+001B>[m
371+<U+001B>[31m- return handler.RenderLoginPage(c, a)<U+001B>[m
372+<U+001B>[31m- })<U+001B>[m
373+<U+001B>[31m- e.GET(a.AdminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
374+<U+001B>[31m- return handler.StartOAuth(c, a)<U+001B>[m
375+<U+001B>[31m- })<U+001B>[m
376+<U+001B>[31m- e.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
377+<U+001B>[31m- return handler.OAuthCallback(c, a)<U+001B>[m
378+<U+001B>[31m- })<U+001B>[m
379+<U+001B>[31m- e.GET(a.AdminPath+"/logout", func(c echo.Context) error {<U+001B>[m
380+<U+001B>[31m- return handler.Logout(c, a)<U+001B>[m
381+<U+001B>[31m- })<U+001B>[m
382+<U+001B>[31m-<U+001B>[m
383+ return e<U+001B>[m
384+ }<U+001B>[m
385+ <U+001B>[m
386+<U+001B>[36m@@ -61,3 +46,18 @@<U+001B>[m <U+001B>[mfunc bindAdminRoute(e *echo.Echo, a *app.App, path string) {<U+001B>[m
387+ return handler.AdminRoute(c, a)<U+001B>[m
388+ })<U+001B>[m
389+ }<U+001B>[m
390+<U+001B>[32m+<U+001B>[m
391+<U+001B>[32m+<U+001B>[m<U+001B>[32mfunc bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) {<U+001B>[m
392+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login", func(c echo.Context) error {<U+001B>[m
393+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.RenderLoginPage(c, a)<U+001B>[m
394+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
395+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/login/:provider", func(c echo.Context) error {<U+001B>[m
396+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.StartOAuth(c, a)<U+001B>[m
397+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
398+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(a.OauthCallbackURL, func(c echo.Context) error {<U+001B>[m
399+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.OAuthCallback(c, a)<U+001B>[m
400+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
401+<U+001B>[32m+<U+001B>[m <U+001B>[32me.GET(adminPath+"/logout", func(c echo.Context) error {<U+001B>[m
402+<U+001B>[32m+<U+001B>[m <U+001B>[32mreturn handler.Logout(c, a)<U+001B>[m
403+<U+001B>[32m+<U+001B>[m <U+001B>[32m})<U+001B>[m
404+<U+001B>[32m+<U+001B>[m<U+001B>[32m}<U+001B>[m

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close