-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile2c.py
executable file
·140 lines (120 loc) · 4.28 KB
/
file2c.py
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
#!/usr/bin/env python
"""
Generate C source with global variables embedding binary/text file contents.
"""
import itertools
import os
import sys
from textwrap import dedent
def file2c(
source_file: str,
output: str | None = None,
symbol: str | None = None,
header=False,
text=False,
):
"""
Generate C source with global variables embedding binary/text file contents
:param source_file: Source file path.
:param output_directory: Output directory where source files are generated.
:param symbol: Global variable symbol name. Defaults to source file name.
:param header: Generate header content instead of implementation.
:param text: If true, source file is read as text instead of binary
and the generated variable contents are null-terminated.
"""
if not symbol:
symbol = os.path.splitext(os.path.basename(source_file))[0]
if output and (dirname := os.path.dirname(output)):
os.makedirs(dirname, exist_ok=True)
# Write header file
with open(output, "w") if output else sys.stdout as output_file:
if header:
output_file.write(dedent("""
#pragma once
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {{
#endif
extern const {char_type} *{name};
extern const size_t {name}_size;
#ifdef __cplusplus
}}
#endif
""").format(
char_type="char" if text else "unsigned char",
name=symbol,
).lstrip())
# Write implementation file
else:
with (
open(source_file, "r" if text else "rb")
if source_file != "-"
else sys.stdin
) as f:
contents = f.read()
# Read file contents and format them in a way C will understand
if text:
c_contents = '\n"' + (
contents
.replace("\\", "\\\\")
.replace('"', '\\"')
.replace("\n", '\\n"\n"')
) + '"'
else:
c_contents = "{\n"
for batch in itertools.batched(contents, n=16):
c_contents += " "
c_contents += ", ".join(hex(b) for b in batch)
c_contents += ",\n"
c_contents += "}"
output_file.write(dedent("""
#include <stdlib.h>
static const {char_type} _data[] = {c_contents};
const {char_type} *{name} = _data;
const size_t {name}_size = sizeof(_data){minus_one_on_text};
""").format(
char_type="char" if text else "unsigned char",
name=symbol,
c_contents=c_contents,
minus_one_on_text=" - 1" if text else "",
).lstrip())
def bin2c(source_file, output_directory=None, symbol=None, header=False):
"""
Alias for file2c that always embed file as binary.
"""
return file2c(source_file, output_directory, symbol, header, text=False)
def text2c(source_file, output_directory=None, symbol=None, header=False):
"""
Alias for file2c that always embed file as text.
"""
return file2c(source_file, output_directory, symbol, header, text=True)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"input",
help="Input file. Pass \"-\" to use data from stdin.",
)
parser.add_argument(
"-o",
"--output",
help="Output file. Default to stdout.",
)
parser.add_argument(
"-s",
"--symbol",
help="Global symbol used for the embedded file contents",
)
parser.add_argument(
"--header",
help="Generate a header with the definitions instead of the implementation file",
action="store_true",
)
parser.add_argument(
"-t",
"--text",
help="Embed file as text, making sure variable is null-terminated",
action="store_true",
)
args = parser.parse_args()
file2c(args.input, args.output, args.symbol, args.header, args.text)