]> git.tdb.fi Git - libs/datafile.git/blob - tool/packer.cpp
Respect the TMPDIR environment variable when creating data packs
[libs/datafile.git] / tool / packer.cpp
1 #include <msp/datafile/parser.h>
2 #include <msp/datafile/statement.h>
3 #include <msp/datafile/writer.h>
4 #include <msp/fs/utils.h>
5 #include <msp/io/memory.h>
6 #include <msp/strings/format.h>
7 #include "packer.h"
8 #include "tool.h"
9
10 using namespace std;
11 using namespace Msp;
12
13 Packer::Packer(DataTool &t):
14         tool(t),
15         tmp_file(tempfile()),
16         tmp_buf(new IO::Buffered(*tmp_file)),
17         dir_alloc(0)
18 { }
19
20 IO::File *Packer::tempfile()
21 {
22         FS::Path tmpdir;
23         const char *tmp_env = getenv("TMPDIR");
24         if(tmp_env)
25                 tmpdir = tmp_env;
26         else
27                 tmpdir = "/tmp";
28
29         for(unsigned i=0;; ++i)
30         {
31                 try
32                 {
33                         FS::Path filename = tmpdir/format("mspdatatool.%d", i);
34                         IO::File *file = new IO::File(filename.str(), IO::M_RDWR, IO::File::C_NEW);
35                         FS::unlink(filename.str());
36                         return file;
37                 }
38                 catch(const IO::file_already_exists &)
39                 {
40                         continue;
41                 }
42         }
43 }
44
45 Packer::~Packer()
46 {
47         delete tmp_buf;
48         delete tmp_file;
49 }
50
51 void Packer::pack_file(const string &fn)
52 {
53         if(!tmp_file)
54                 throw logic_error("Packer::pack_file");
55
56         IO::SeekOffset offset = tmp_file->tell();
57
58         IO::BufferedFile in(fn);
59         ObjectList objects;
60         bool raw = detect_raw(in);
61         if(raw)
62                 transfer_raw(in, *tmp_file);
63         else
64         {
65                 transfer_datafile(in, fn, *tmp_buf, objects);
66                 tmp_buf->flush();
67         }
68
69         unsigned length = tmp_file->tell()-offset;
70
71         DataFile::Statement st("file");
72         st.append(FS::basename(fn));
73         st.sub.push_back((DataFile::Statement("slice"), offset, length));
74         for(list<Object>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
75                 st.sub.push_back((DataFile::Statement("object"), i->name, i->keyword));
76         directory.push_back(st);
77         dir_alloc += 100+objects.size()*100;
78 }
79
80 bool Packer::detect_raw(IO::Seekable &in)
81 {
82         IO::SeekOffset offset = in.tell();
83         bool raw = false;
84         DataFile::Parser parser(in, "-");
85         while(!raw && parser && in.tell()<10240)
86         {
87                 try
88                 {
89                         parser.parse();
90                 }
91                 catch(...)
92                 {
93                         raw = true;
94                 }
95         }
96         in.seek(offset, IO::S_BEG);
97         return raw;
98 }
99
100 void Packer::transfer_datafile(IO::Base &in, const string &fn, IO::Base &out, ObjectList &objects)
101 {
102         DataFile::Parser parser(in, fn);
103
104         DataFile::Writer *writer = tool.create_writer(out);
105
106         bool collection = FS::extpart(fn)==".mdc";
107         while(parser)
108         {
109                 DataFile::Statement st = parser.parse(true);
110                 if(st.valid)
111                 {
112                         if(collection && !st.control)
113                         {
114                                 if(st.get_signature()=="s")
115                                 {
116                                         Object obj;
117                                         obj.name = st.args[0].get<string>();
118                                         obj.keyword = st.keyword;
119                                         objects.push_back(obj);
120                                 }
121                                 else
122                                         collection = false;
123                         }
124                         if(!st.control || st.keyword=="__src")
125                                 writer->write(st);
126                 }
127         }
128         writer->write(DataFile::Statement("__end"));
129         delete writer;
130 }
131
132 void Packer::transfer_raw(IO::Base &in, IO::Base &out)
133 {
134         while(!in.eof())
135         {
136                 char buf[4096];
137                 unsigned len = in.read(buf, sizeof(buf));
138                 if(len==0)
139                         break;
140                 out.write(buf, len);
141         }
142 }
143
144 void Packer::create_pack(const string &fn)
145 {
146         vector<char> dir_buffer(dir_alloc);
147         IO::Memory mem(&dir_buffer[0], dir_buffer.size(), IO::M_WRITE);
148
149         unsigned base_offset = 0;
150         while(1)
151         {
152                 mem.seek(0, IO::S_BEG);
153
154                 DataFile::Writer *writer = tool.create_writer(mem);
155
156                 for(list<DataFile::Statement>::const_iterator i=directory.begin(); i!=directory.end(); ++i)
157                         writer->write(*i);
158                 if(base_offset==0)
159                         base_offset = mem.tell();
160                 writer->write((DataFile::Statement("base_offset"), base_offset));
161                 writer->write(DataFile::Statement("__end"));
162                 delete writer;
163
164                 unsigned dir_size = mem.tell();
165                 if(dir_size<=base_offset)
166                         break;
167                 base_offset = dir_size;
168         }
169
170         IO::File out(fn, IO::M_WRITE);
171         out.write(&dir_buffer[0], base_offset);
172         tmp_file->seek(0, IO::S_BEG);
173         while(!tmp_file->eof())
174         {
175                 char buf[16384];
176                 unsigned len = tmp_file->read(buf, sizeof(buf));
177                 if(!len)
178                         break;
179                 out.write(buf, len);
180         }
181 }