1/*
2 * Copyright 2012 Aleksas Pantechovskis, <alexp.frl@gmail.com>
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include <exception>
7#include <fstream>
8#include <iostream>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string>
12#include <string.h>
13
14#include <ByteOrder.h>
15#include <FindDirectory.h>
16#include <Path.h>
17#include <String.h>
18#include <TypeConstants.h>
19
20
21using namespace std;
22
23
24const char* kUsageMessage = \
25	"proj2make usage:\n"
26	"# proj2make <projPath> [makePath]\n"
27	"# if makePath parameter doesn't specified makefile will be created in\n"
28	"#	the same directory as .proj file\n"
29	"# example: proj2make /boot/home/myprog/myprog.proj\n";
30
31fstream gProjFile;
32uint32 gProjLength;
33uint8* gProjData;
34
35fstream gMakeFile;
36
37fstream gTemplateFile;
38
39string gSPthString;
40string gPPthString;
41string gFil1String;
42string gLinkString;
43string gPLnkString;
44
45const char* gAppTypes[] = {
46	"APP",
47	"SHARED",
48	"STAITC",
49	"DRIVER"
50};
51
52uint8 gAppType;
53string gAppName;
54
55struct hdr
56{
57			uint32	Id() { return
58						static_cast<uint32>(B_BENDIAN_TO_HOST_INT32(fId)); }
59			uint32	Size() { return
60						static_cast<uint32>(B_BENDIAN_TO_HOST_INT32(fSize)); }
61			const char* Data() { return (char*)(this + 1); }
62private:
63			uint32 fId;
64			uint32 fSize;
65};
66
67
68class Error : public std::exception
69{
70			BString		fWhat;
71public:
72						Error(const char* what, ...);
73	virtual				~Error() throw() {}
74	virtual const char*	what() const throw() { return fWhat.String(); }
75};
76
77
78Error::Error(const char* what, ...)
79{
80	const int size = 1024;
81	va_list args;
82	va_start(args, what);
83	vsnprintf(fWhat.LockBuffer(size), size, what, args);
84	fWhat.UnlockBuffer();
85	va_end(args);
86}
87
88
89void
90CheckFiles(const char* projPath, const char* makePath)
91{
92	gProjFile.open(projPath, fstream::in | fstream::binary);
93	if (!gProjFile.is_open())
94		throw Error("%s not found", projPath);
95
96	gProjFile.seekg(0, ios::end);
97	uint32 projFileLength = gProjFile.tellg();
98	gProjFile.seekg(0, ios::beg);
99
100	char* name = new char[5];
101	gProjFile.read(name, 4);
102
103	uint32 length;
104	gProjFile.read((char*)&length, 4);
105	name[4] = '\0';
106	length = static_cast<uint32>(B_BENDIAN_TO_HOST_INT32(length));
107	gProjLength = length + 8;
108
109	if (strcmp(name, "MIDE") != 0 || gProjLength > projFileLength)
110		throw Error("File corrupted or it is not BeIDE *.proj file");
111
112	gMakeFile.open(makePath, fstream::in);
113	if (gMakeFile.is_open())
114		throw Error("%s already exists", makePath);
115
116	gMakeFile.open(makePath, fstream::out);
117	if (!gMakeFile.is_open())
118		throw Error("Can not create makefile");
119
120	BPath templateFileName;
121	// not supporter yet in my haiku rev
122	// find_directory(B_SYSTEM_DEVELOP_DIR, &templateFileName);
123	// templateFileName.Append("etc/makefile");
124	templateFileName.SetTo("/boot/develop/etc/makefile");
125
126	gTemplateFile.open(templateFileName.Path(), fstream::in);
127	if (!gTemplateFile.is_open())
128		throw Error("Can not open template %s", templateFileName.Path());
129}
130
131
132void
133ParseGenB(hdr* data)
134{
135	hdr* child = (hdr*)data->Data();
136	char* name = (char*)(child + 1);
137	int len = strlen(name) + 1;
138
139	uint32 u = child->Id();
140	char* c = (char*)&u;
141	printf("\t%c%c%c%c:%d:%s\n", c[3], c[2], c[1], c[0], child->Size(), name);
142
143	if (strncmp(name, "ProjectPrefsx86", len - 1) == 0) {
144		const char* type = child->Data() + len + 8;
145		if (*type <= 3)
146			gAppType = *type;
147		type++;
148		type += 64; // skip the mime type name
149		gAppName = type;
150	}
151}
152
153
154class _l {
155	static string _s;
156public:
157	_l() { _s += " " ; }
158	~_l() { _s.resize(_s.size() - 1); }
159
160	char* str() { _s.c_str(); }
161};
162
163string _l::_s;
164
165void
166Parse(hdr* current, hdr* parent)
167{
168	_l l;
169
170	uint32 u = current->Id();
171	char* c = (char*)&u;
172	printf("%#06x:%s%c%c%c%c:%d\n",
173		(uint8*)current - gProjData, l.str(),
174		c[3], c[2], c[1], c[0], current->Size());
175
176	bool useGrandParent = false;
177	size_t off = 0;
178	switch(current->Id()) {
179		case 'Fil1':
180		case 'Link':
181		case 'PLnk':
182			off = 24;
183			break;
184		case 'MIDE':
185		case 'DPrf':
186		case 'GPrf':
187			break;
188		case 'MSFl':
189			off = 8;
190			useGrandParent = true;
191			break;
192		case 'SPth':
193			gSPthString += " \\\n\t";
194			gSPthString += &current->Data()[5];
195			return;
196		case 'PPth':
197			gPPthString += " \\\n\t";
198			gPPthString += &current->Data()[5];
199			return;
200		case 'Name':
201			if (parent->Id() == 'Fil1') {
202				gFil1String += " \\\n\t";
203				gFil1String += &current->Data()[4];
204			} else if (parent->Id() == 'Link') {
205				gLinkString += " \\\n\t";
206				gLinkString += &current->Data()[4];
207			} else if (parent->Id() == 'PLnk') {
208				gPLnkString += " \\\n\t";
209				gPLnkString += &current->Data()[4];
210			}
211			return;
212		case 'GenB':
213			ParseGenB(current);
214			return;
215		default:
216			return;
217	}
218
219	hdr* child = (hdr*)(current->Data() + off);
220	while (off < current->Size()) {
221		Parse(child, useGrandParent ? parent : current);
222		off += child->Size() + sizeof(hdr);
223		child = (hdr*)(child->Data() + child->Size());
224	}
225}
226
227
228void
229ReadProj()
230{
231	gProjFile.seekg(0, ios::beg);
232	gProjData = new uint8[gProjLength];
233	gProjFile.read((char*)gProjData, gProjLength);
234	gProjFile.close();
235
236	Parse((hdr*)gProjData, NULL);
237}
238
239
240void
241Proj2Make()
242{
243	gFil1String = " ";
244	gLinkString = " ";
245	gPLnkString = " ";
246	gSPthString = " ";
247	gPPthString = " ";
248
249	ReadProj();
250	string str;
251	while (gTemplateFile.good()) {
252		getline(gTemplateFile, str);
253
254		if (str.find("SRCS") == 0)
255			str = str + gFil1String;
256		else if (str.find("LIBS") == 0)
257			str = str + gLinkString;
258		else if (str.find("SYSTEM_INCLUDE_PATHS") == 0)
259			str = str + gSPthString;
260		else if (str.find("LOCAL_INCLUDE_PATHS") == 0)
261			str = str + gPPthString;
262		else if (str.find("TYPE") == 0)
263			str = str + gAppTypes[gAppType];
264		else if (str.find("NAME") == 0)
265			str = str + gAppName;
266		else if (str.find("RSRCS") == 0)
267			str = str + gPLnkString;
268
269		gMakeFile << str << endl;
270	}
271
272	gMakeFile.close();
273	gTemplateFile.close();
274}
275
276
277int
278main(int argc, char** argv)
279{
280	try {
281		if (argc <= 1 || (argc > 1 && strcmp(argv[1], "--help") == 0))
282			throw Error("");
283
284		BString projPath = argv[1];
285
286		BString makePath;
287		// if makefile path specified
288		if (argc > 2)
289			makePath = argv[2];
290		// default makefile path
291		else {
292			BPath path(argv[1]);
293			path.GetParent(&path);
294			path.Append("makefile");
295			makePath = path.Path();
296		}
297
298		CheckFiles(projPath.String(), makePath.String());
299
300		Proj2Make();
301
302	} catch(exception& exc) {
303		cerr << argv[0] << " : " << exc.what() << endl;
304		cerr << kUsageMessage;
305		return B_ERROR;
306	}
307
308	return B_OK;
309}
310
311