]>
Softwares of Agnibho - statin.git/blob - statin/statin.py
2 Copyright (c) 2018 Agnibho Mondal
5 This file is part of Statin.
7 Statin is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 Statin is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Statin. If not, see <http://www.gnu.org/licenses/>.
22 from os
import path
, popen
, unlink
, makedirs
23 from shutil
import copyfile
, rmtree
, copytree
, ignore_patterns
24 from datetime
import datetime
29 from statin
.conf
import *
32 conflist
= {"timefmt": TIMEFMT
, "sizefmt": SIZEFMT
, "errmsg": ERRMSG
}
34 varlist
["DATE_LOCAL"] = datetime
.now().strftime(conflist
["timefmt"])
35 varlist
["DATE_GMT"] = datetime
.utcnow().strftime(conflist
["timefmt"])
43 global OUTPUT_DIR
, PROCESS_PATT
, MAX_RECURSION
44 PROCESS_PATT
= set(PROCESS_PATT
)
47 parser
= argparse
.ArgumentParser(description
="Generate static html files")
48 verbo
= parser
.add_mutually_exclusive_group()
49 verbo
.add_argument("-q", "--quiet", help="Suppress text output to console", action
="store_true")
50 verbo
.add_argument("-v", "--verbose", help="Verbose text output to console", action
="store_true")
51 parser
.add_argument("-s", "--safe", help="Disable python eval of strings", action
="store_true")
52 parser
.add_argument("-r", "--recursive", help="Process files recursively", action
="store_true")
53 parser
.add_argument("-l", "--level", help="Maximum recursion level", type=int)
54 parser
.add_argument("-p", "--pattern", help="Filename patterns to be processed", action
="append")
55 parser
.add_argument("-o", "--output", help="Specify the output directory")
56 parser
.add_argument("files", help="List of files to be processed", nargs
="*")
57 args
= parser
.parse_args()
59 # Reassign variables from option
60 if(args
.level
!= None):
61 MAX_RECURSION
= args
.level
62 if(args
.pattern
!= None):
63 PROCESS_PATT
= PROCESS_PATT
.union(args
.pattern
)
64 if(args
.output
!= None):
65 if(args
.output
[-1:] != "/"):
66 args
.output
= args
.output
+ "/"
67 if(args
.output
[:2] != "./" and args
.output
[:1] != "/"):
68 args
.output
= "./" + args
.output
69 OUTPUT_DIR
= args
.output
71 # List all files to be processed
76 for patt
in PROCESS_PATT
:
77 filelist
.extend(glob(patt
))
79 # Purge output directory and rebuild if specific filenames not supplied
81 rmtree(OUTPUT_DIR
, True)
82 copytree(".", OUTPUT_DIR
, ignore
=ignore_patterns(*PROCESS_PATT
))
84 print("Contents copied to " + OUTPUT_DIR
+ "\n")
86 makedirs(OUTPUT_DIR
, exist_ok
=True)
88 # Send each file for processing
89 for filename
in filelist
:
91 print("Processing '" + filename
+ "'")
95 print("Creating temporary files")
97 fdir
= path
.dirname(path
.realpath(filename
))
98 temp
.append(tempfile
.NamedTemporaryFile(dir=fdir
, prefix
=".", delete
=False))
99 temp
.append(tempfile
.NamedTemporaryFile(dir=fdir
, prefix
=".", delete
=False))
100 copyfile(filename
, temp
[0].name
)
102 print("'" + filename
+ "' copied to '" + temp
[0].name
+ "'")
104 print("Processing '" + temp
[0].name
+ "' to '" + temp
[1].name
+ "'")
105 while(rlvl
< MAX_RECURSION
and not process_file(temp
[0].name
, temp
[1].name
, filename
)):
106 temp
.append(tempfile
.NamedTemporaryFile(dir=fdir
, prefix
=".", delete
=False))
107 unlink(temp
.pop(0).name
)
109 print("Processing '" + temp
[0].name
+ "' to '" + temp
[1].name
+ "'")
111 if(not args
.quiet
and rlvl
>= MAX_RECURSION
):
112 print("Maximum recursion level reached")
113 outfile
= OUTPUT_DIR
+ path
.splitext(path
.basename(filename
))[0] + ".html"
114 copyfile(temp
[0].name
, outfile
)
116 print("Output saved to '" + outfile
+ "'")
118 print("Cleaning up temporary files")
122 outfile
= OUTPUT_DIR
+ path
.splitext(path
.basename(filename
))[0] + ".html"
123 process_file(filename
, outfile
)
125 print("Output saved to '" + outfile
+ "'")
128 def process_file(filename
, outfile
, original
= None):
130 global openif
, ifstatus
, ifskip
132 # Assign variable values
134 if(original
== None):
138 varlist
["DOCUMENT_URI"] = original
139 varlist
["DOCUMENT_NAME"] = path
.basename(original
)
140 varlist
["LAST_MODIFIED"] = datetime
.fromtimestamp(path
.getmtime(original
)).strftime(conflist
["timefmt"])
141 with
open(filename
) as src
, open(outfile
, "w") as out
:
143 line
= re
.split("(<!--#.+-->)", line
)
146 if(item
.strip()[:5] == "<!--#"):
148 item
= process_directive(item
.strip()[5:][:-3].strip(), filename
)
149 if(not ifskip
and (not openif
or ifstatus
)):
151 if(openif
and not args
.quiet
):
152 print("Error: Unexpected end of file reached. Expecting 'endif'.")
153 except FileNotFoundError
as e
:
155 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
156 except IsADirectoryError
as e
:
158 print("Error: can't process directory '" + e
.filename
+ "'. Please provide file names only.")
162 # Process the directives
163 def process_directive(line
, filename
):
165 global varlist
, conflist
166 global openif
, ifstatus
, ifskip
169 print(" Processing directive : "+line
)
171 # Tokenize directives
172 line
= re
.split('''\s(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', line
)
173 directive
= line
.pop(0);
176 pair
= re
.split('''=(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', pair
)
177 params
[pair
[0]] = pair
[1][1:-1]
180 if(directive
== "if"):
183 ifstatus
= (evaluate_expression(params
["expr"]) == True)
186 print(" Error: no expression to process")
187 return(conflist
["errmsg"])
189 elif(directive
== "elif"):
196 ifstatus
= (evaluate_expression(params
["expr"]) == True)
199 print(" Error: no expression to process")
200 return(conflist
["errmsg"])
202 elif(directive
== "else"):
206 ifstatus
= not ifstatus
208 elif(directive
== "endif"):
213 # Skip if conditional false
214 if(ifskip
or (openif
and not ifstatus
)):
218 if(directive
== "include"):
220 with
open(params
["virtual"]) as f
:
224 except FileNotFoundError
as e
:
226 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
228 with
open(path
.dirname(path
.realpath(filename
)) + "/" + params
["file"]) as f
:
232 except FileNotFoundError
as e
:
234 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
236 print(" Error: no file to include")
237 return(conflist
["errmsg"])
238 elif(directive
== "exec"):
240 return(popen(params
["cmd"]).read())
244 return(popen(params
["cgi"]).read())
248 print(" Error: no command to execute")
249 return(conflist
["errmsg"])
250 elif(directive
== "echo"):
252 return(varlist
[params
["var"]])
255 print(" Error: no variable to display")
256 return(conflist
["errmsg"])
257 elif(directive
== "config"):
258 conflist
.update(params
)
259 varlist
["DATE_LOCAL"] = datetime
.now().strftime(conflist
["timefmt"])
260 varlist
["DATE_GMT"] = datetime
.utcnow().strftime(conflist
["timefmt"])
261 varlist
["LAST_MODIFIED"] = datetime
.fromtimestamp(path
.getmtime(filename
)).strftime(conflist
["timefmt"])
262 elif(directive
== "flastmod"):
264 return(datetime
.fromtimestamp(path
.getmtime(params
["virtual"])).strftime(conflist
["timefmt"]))
267 except FileNotFoundError
as e
:
269 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
270 return(conflist
["errmsg"])
272 return(datetime
.fromtimestamp(path
.getmtime(path
.dirname(path
.realpath(filename
)) + "/" + params
["file"])).strftime(conflist
["timefmt"]))
275 except FileNotFoundError
as e
:
277 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
278 return(conflist
["errmsg"])
280 print(" Error: missing filename")
281 return(conflist
["errmsg"])
282 elif(directive
== "fsize"):
283 idx
= { "B":1, "KB":1024, "MB":1048576, "GB":1073741824, "TB":1099511627776, "b":1, "kb":1024, "mb":1048576, "gb":1073741824, "tb":1099511627776, "bytes":1, "kilobytes":1024, "megabytes":1048576, "gigabytes":1073741824, "terabytes":1099511627776 }
284 if(conflist
["sizefmt"] == "abbrev"):
285 conflist
["sizefmt"] = "kb"
286 if(not conflist
["sizefmt"] in idx
):
288 print(" Error: invalid size format")
289 return(conflist
["errmsg"])
291 return("{0:.2f}".format(path
.getsize(params
["virtual"]) / idx
[conflist
["sizefmt"]]) + " " + conflist
["sizefmt"])
294 except FileNotFoundError
as e
:
296 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
297 return(conflist
["errmsg"])
299 return("{0:.2f}".format(path
.getsize(path
.dirname(path
.realpath(filename
)) + "/" + params
["file"]) / idx
[conflist
["sizefmt"]]) + " " + conflist
["sizefmt"])
302 except FileNotFoundError
as e
:
304 print("Error: file '" + e
.filename
+ "' could not be found. Please check if the file exists.")
305 return(conflist
["errmsg"])
307 print(" Error: missing filename")
308 return(conflist
["errmsg"])
309 elif(directive
== "printenv"):
311 elif(directive
== "set"):
313 varlist
[params
["var"]] = evaluate_expression(params
["value"])
316 print(" Error: missing variable or value")
317 return(conflist
["errmsg"])
320 print(" Error: unrecognized directive")
321 return(conflist
["errmsg"])
324 # Expression evaluation
325 def evaluate_expression(expr
):
330 print(" Can't evaluate expression in safe mode")
331 return(conflist
["errmsg"])
333 m
=re
.findall("\$\{*[^\}\s=><!+\-*/^%]+\}*", expr
)
335 expr
= expr
.replace(v
, str(varlist
[v
.replace("$", "").replace("{", "").replace("}", "")]))
338 expr
= re
.sub("([\w\s]+)", r
"'\1'", expr
)
339 expr
= re
.sub("'([\d]+)'", r
"\1", expr
)
343 return(re
.sub("'([\w\s]+)'", r
"\1", expr
))
345 if(__name__
== "__main__"):