184 lines
4.9 KiB
Python
184 lines
4.9 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
"""
|
||
|
tools/mkfsdata.py
|
||
|
|
||
|
Copyright (c) 2024 Alexey Matveev
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||
|
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||
|
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
POSSIBILITY OF SUCH DAMAGE.
|
||
|
"""
|
||
|
|
||
|
import argparse
|
||
|
import logging
|
||
|
from pathlib import Path
|
||
|
from typing import Iterator, List, Optional, TextIO, Tuple
|
||
|
|
||
|
|
||
|
def get_files(dirname: Path) -> Iterator[Tuple[Path, str]]:
|
||
|
"""
|
||
|
Generator return files in the directory dirname
|
||
|
"""
|
||
|
for p in dirname.glob("**/*"):
|
||
|
if p.is_file():
|
||
|
relative_full_path = "/" + str(p.relative_to(dirname))
|
||
|
yield p, relative_full_path
|
||
|
|
||
|
|
||
|
def conver_c_name(prefix: str, r_full_path: str) -> str:
|
||
|
"""
|
||
|
Convert file name to C variable name
|
||
|
"""
|
||
|
return "{}{}".format(prefix, r_full_path.replace(".", "_").replace("/", "_"))
|
||
|
|
||
|
|
||
|
def write_f_data(
|
||
|
fd: TextIO,
|
||
|
file: Path,
|
||
|
r_full_path: str,
|
||
|
is_add_progmem: bool = False,
|
||
|
):
|
||
|
"""
|
||
|
Write file as hex dump
|
||
|
|
||
|
Args:
|
||
|
file - source file
|
||
|
is_add_progmem - add PROGMEM, for AVR
|
||
|
"""
|
||
|
rs_name = conver_c_name("data", r_full_path)
|
||
|
st_progmem = "PROGMEM " if is_add_progmem else ""
|
||
|
fd.write(
|
||
|
f"static const unsigned char {rs_name}[] {st_progmem}=\n"
|
||
|
"{\n"
|
||
|
f" /* {r_full_path} */\n\n"
|
||
|
)
|
||
|
|
||
|
# write file name
|
||
|
result = " "
|
||
|
for ch in r_full_path:
|
||
|
result += "0x{0:02x}, ".format(ord(ch))
|
||
|
result += "0x00,"
|
||
|
fd.write(result)
|
||
|
|
||
|
# write file content
|
||
|
with open(file, "rb") as f_data:
|
||
|
for j, ch in enumerate(iter(lambda: f_data.read(1), b"")):
|
||
|
if j % 10 == 0:
|
||
|
fd.write("\n")
|
||
|
fd.write(" ")
|
||
|
fd.write("0x{0:02x}, ".format(ch[0]))
|
||
|
fd.write("\n")
|
||
|
|
||
|
# End
|
||
|
fd.write("};\n")
|
||
|
|
||
|
|
||
|
def write_s_data(
|
||
|
fd: TextIO,
|
||
|
r_full_path: str,
|
||
|
prev_file: Optional[str],
|
||
|
is_replace_to_root: bool = False,
|
||
|
):
|
||
|
"""
|
||
|
Write struct httpd_fsdata_file
|
||
|
"""
|
||
|
if is_replace_to_root:
|
||
|
st_name = "g_httpdfs_root"
|
||
|
else:
|
||
|
st_name = conver_c_name("file", r_full_path)
|
||
|
|
||
|
if prev_file is None:
|
||
|
st_prev_name = "NULL"
|
||
|
else:
|
||
|
st_prev_name = conver_c_name("file", prev_file)
|
||
|
rs_name = conver_c_name("data", r_full_path)
|
||
|
n_len = len(r_full_path) + 1
|
||
|
fd.write(
|
||
|
f"const struct httpd_fsdata_file {st_name}[] =\n"
|
||
|
" {{"
|
||
|
f"{st_prev_name}, {rs_name}, {rs_name} + {n_len}, "
|
||
|
f"sizeof({rs_name}) - {n_len}"
|
||
|
"}};\n\n"
|
||
|
)
|
||
|
|
||
|
|
||
|
def main():
|
||
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||
|
logger = logging.getLogger()
|
||
|
|
||
|
args = parser().parse_args()
|
||
|
|
||
|
# Get files
|
||
|
files: List[Tuple[Path, str]] = []
|
||
|
for file, r_full_path in get_files(args.dirname):
|
||
|
logger.info(f"Adding file {r_full_path}")
|
||
|
files.append((file, r_full_path))
|
||
|
|
||
|
with args.out.open("w") as fd:
|
||
|
# write header
|
||
|
fd.write(
|
||
|
'#include "netutils/httpd.h"\n'
|
||
|
"\n"
|
||
|
"#ifndef NULL\n"
|
||
|
" #define NULL 0\n"
|
||
|
"#endif\n\n"
|
||
|
)
|
||
|
|
||
|
# write content
|
||
|
for file, r_full_path in files:
|
||
|
write_f_data(fd, file, r_full_path, args.add_progmem)
|
||
|
fd.write("\n") # Add empty line beetween files
|
||
|
|
||
|
# write list of files
|
||
|
prev_file = None
|
||
|
for j, (file, r_full_path) in enumerate(files):
|
||
|
write_s_data(
|
||
|
fd=fd,
|
||
|
r_full_path=r_full_path,
|
||
|
prev_file=prev_file,
|
||
|
is_replace_to_root=j == len(files) - 1,
|
||
|
)
|
||
|
prev_file = r_full_path
|
||
|
|
||
|
# end
|
||
|
fd.write(f"const int g_httpd_numfiles = {len(files)};\n")
|
||
|
|
||
|
|
||
|
def parser() -> argparse.ArgumentParser:
|
||
|
"""Create parser"""
|
||
|
parser = argparse.ArgumentParser(description="Create C file with data for httpd")
|
||
|
parser.add_argument(
|
||
|
"-d",
|
||
|
"--dirname",
|
||
|
default="httpd-fs",
|
||
|
type=Path,
|
||
|
help="Directory with files to add",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-o",
|
||
|
"--out",
|
||
|
default="httpd_fsdata.c",
|
||
|
type=Path,
|
||
|
help="Output C file",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"--add-progmem",
|
||
|
action="store_true",
|
||
|
help="Add PROGMEM to data",
|
||
|
)
|
||
|
return parser
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|