-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExecutable.hs
207 lines (155 loc) · 6.73 KB
/
Executable.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
{-# LANGUAGE OverloadedStrings, NamedFieldPuns,
ScopedTypeVariables, CPP, RecordWildCards #-}
{-# OPTIONS_GHC -fwarn-unused-imports #-}
-- This module implements the commandline driver for the paragrep program.
{-
[2011.02.21]
Observing Mac/Linux differences. First with respect to the
matching of the "empty_line" pattern against lines containing only
spaces. Second, with the failure (or lack of) on broken symlinks.
The latter may be because of a problem with System.Directory.Tree.
[2011.03.08]
Switching from System.Directory.Tree to System.FilePath.Find
[2011.04.06]
Removing the WholeFile partition method from the default hierarchy.
-}
module Paragrep.Executable (paragrepMain) where
import Control.Monad
import Data.Maybe
import Data.Char
import Data.IORef
import Numeric (readDec)
import Prelude as P
import System.Environment
import System.Console.GetOpt
import System.Exit
import Paragrep.Globals
import Paragrep.Lib
import Paragrep.Server
-- Considering possible pager type functionality for navigating results....
-- import UI.HSCurses.Curses
-- import UI.HSCurses.Widgets
--------------------------------------------------------------------------------
-- Command line options
--------------------------------------------------------------------------------
-- | Recognized flags, for documentation see `options` below.
data CmdFlag =
NoColor
| Help
| HierarchyList String
| CaseInsensitive
| FollowIncludes
| Root String
| Verbose (Maybe Int)
| Version
| PrependDatePart
| WholeFile
| RunServer Int
deriving (Show,Eq)
-- <boilerplate> Pure boilerplate, would be nice to scrap it:
getRoot (Root x) = Just x
getRoot _ = Nothing
getHierarchy (HierarchyList str) = Just str
getHierarchy _ = Nothing
getVerbose (Verbose x) = Just x
getVerbose _ = Nothing
getRunServer (RunServer x) = Just x
getRunServer _ = Nothing
-- </boilerplate>
-- | Command line options and their meaning.
options :: [OptDescr CmdFlag]
options =
[
Option ['h'] ["help"] (NoArg Help) "show this help information"
, Option ['r'] ["root"] (ReqArg Root "PATH") "set the root file or directory to search"
, Option ['i'] ["ignore-case"] (NoArg CaseInsensitive) "treat file contents and search terms as completely lower-case"
, Option ['v'] ["verbose"] (OptArg (Verbose . fmap safeRead) "LVL")
"set or increment verbosity level 0-4, default 1"
, Option ['V'] ["version"] (NoArg Version) "Show version number."
, Option [] [] (NoArg undefined) ""
, Option ['d'] ["date"] (NoArg PrependDatePart) "prepend a splitter on date-tags '[2011.02.21]' to the hierarchy list"
, Option ['w'] ["wholefile"] (NoArg WholeFile) "append the WholeFile granularity to the list of splitters"
-- TODO / FIXME -- these still need to be implemented:
-- , Option [] ["custom"] (ReqArg HierarchyList "LIST") "use a custom hierarchy of partition methods"
-- , Option ['f'] ["follow"] (NoArg FollowIncludes) "follow \\include{...} expressions like the original 1988 'help'"
-- , Option ['n'] ["nocolor"] (NoArg NoColor) "disable ANSI color output"
, Option ['s'] ["server"] (ReqArg (RunServer . read) "PORT") "run as HTTP server listening on PORT"
]
usage = "\nVersion "++version++"\n"++
"Usage: "++progName++" [OPTION...] searchterm1 [searchterm2 ...]\n\n"++
"The "++progName++" program provides flexible search at paragraph\n"++
"granularity, or other custom granularities. A list of partitioning\n"++
"methods splits files into progressively smaller pieces. The program\n"++
"prints matching spans of text as output, printing the smallest delimited\n" ++
"span that matches.\n"++
" \n"++
"\nOptions include:\n"
defaultErr errs = error $ "ERROR!\n" ++ (P.concat errs ++ usageInfo usage options)
safeRead :: String -> Int
safeRead s =
case readDec s of
[(n,"")] -> n
_ -> error$ "Could not read '"++ s ++"' as an integer."
--------------------------------------------------------------------------------
-- The imperative main function.
--------------------------------------------------------------------------------
paragrepMain =
do
args <- getArgs
(opts,terms) <-
case getOpt Permute options args of
(o,rest,[]) -> return (o,rest)
(_,_,errs) -> defaultErr errs
------------------------------------------------------------
-- First handle execution modes that do something completely
-- different and exit through a separate control path:
when (Version `elem` opts)$ do
putStrLn$ "\nVersion: "++version
exitSuccess
when (Help `elem` opts)$ do
putStrLn$ usageInfo usage options
exitSuccess
------------------------------------------------------------
let caseinsensitive = CaseInsensitive `elem` opts
case mapMaybe getVerbose opts of
[] -> return ()
[Nothing] -> modifyIORef verbosityRef (+1)
[Just n] -> writeIORef verbosityRef n
_ -> error "More than one -verbose flag not currently allowed."
let
root = case mapMaybe getRoot opts of
[] -> "."
[r] -> r
_ -> error$ progName++": More than one --root option not currently supported"
hier1 = [partition_paragraphs]
hier2 = if PrependDatePart `elem` opts
then partition_dateTags : hier1
else hier1
default_hierarchy = hier2
initmethod =
if WholeFile `elem` opts
then "WholeFile"
else ""
hierarchy = case mapMaybe getHierarchy opts of
[] -> default_hierarchy
[str] -> error "Custom hierarchy descriptions not implemented yet."
----------------------------------------------------------------------
-- Choose either batch or server execution mode:
case (mapMaybe getRunServer opts) of
[port] -> do chatter 1$ "Running server on port " ++ show port
runServer port root
chatter 1$ "Server exited."
exitSuccess
a:b:t -> error$ "Cannot run server on multiple ports: "++ show (a:b:t)
[] -> do
----------------------------------------------------------------------
when (terms == [])$ defaultErr [" NO SEARCH TERMS"]
chatter 2$ "Searching for terms: " ++ show terms
txtfiles <- listAllFiles root
allhelp <- findHelpFiles caseinsensitive initmethod terms hierarchy txtfiles
-- Print out the structure of the match tree:
-- putStrLn$ render (pPrint allhelp)
printMatchTree allhelp
----------------------------------------------------------------------
-- BL.-putStrLn "Done."
----------------------------------------------------------------------