1 // -*- C++ -*-
2 
3 /*
4  * OpenBabel server
5  * babel-socket.cc
6  *
7  * Copyright (C) 2011 Jean Bréfort <jean.brefort@normalesup.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24 
25 #include "config.h"
26 #include "socket.h"
27 #include <unistd.h>
28 #include <cstring>
29 #include <cstdlib>
30 
31 #define bufsize 128 // should be large enough
32 
33 enum {
34 	STEP_INIT,
35 	STEP_INIT_OPTION,
36 	STEP_OPTION_IN,
37 	STEP_INPUT, // file name
38 	STEP_OPTION_OUT,
39 	STEP_OUTPUT, // file name
40 	STEP_SIZE,
41 	STEP_DATA,
42 	STEP_FORMAT_IN_OPTIONS,
43 	STEP_FORMAT_OUT_OPTIONS,
44 	STEP_GEN_OPTIONS,
45 	STEP_LONG_OPTION
46 };
47 
BabelSocket(int socket)48 BabelSocket::BabelSocket (int socket):
49 m_Socket (socket),
50 m_Index (0),
51 m_Cur (0),
52 m_Start (0),
53 m_Size (bufsize),
54 m_WaitSpace (false),
55 m_Step (STEP_INIT)
56 {
57 	m_InBuf = new char[m_Size + 1];
58 }
59 
~BabelSocket()60 BabelSocket::~BabelSocket ()
61 {
62 	if (m_Socket)
63 		close (m_Socket);
64 	delete m_InBuf;
65 }
66 
Read()67 size_t BabelSocket::Read ()
68 {
69 	size_t res = read (m_Socket, m_InBuf + m_Index, m_Size - m_Index);
70 	if (res != -1) {
71 		m_Index += res;
72 		m_InBuf[m_Index] = 0;
73 	}
74 	while (m_Cur < m_Index) {
75 		if (!m_WaitSpace && m_Step != STEP_DATA) {
76 			while (m_InBuf[m_Cur] == ' ')
77 				m_Cur++;
78 			m_Start = m_Cur;
79 			m_WaitSpace = true;
80 		}
81 		switch (m_Step) {
82 		case STEP_INIT:
83 			if (m_InBuf[m_Cur] != '-') // error
84 				return -1;
85 			m_Step = STEP_INIT_OPTION;
86 			m_Cur++;
87 			break;
88 		case STEP_INIT_OPTION: {
89 			char option = m_InBuf[m_Cur];
90 			if (!option)
91 				break;
92 			switch (option) {
93 			case 'i':
94 				m_Step = STEP_OPTION_IN;
95 				break;
96 			case 'o':
97 				m_Step = STEP_OPTION_OUT;
98 				break;
99 			case 'l':
100 				m_Step = STEP_SIZE;
101 				break;
102 			case 'D':
103 				m_Step = STEP_DATA;
104 				if (m_Input.length () == 0) {
105 					memmove (m_InBuf, m_InBuf + 2, m_Index + 1);
106 					m_Index -= 2;
107 				} else
108 					m_Cur = 0; // avoids exiting the loop
109 				break;
110 			case 'a': // output format options
111 				m_Step = STEP_FORMAT_IN_OPTIONS;
112 				break;
113 			case 'x': // output format options
114 				m_Step = STEP_FORMAT_OUT_OPTIONS;
115 				break;
116 			case '-': // option entered with --
117 				m_Step = STEP_LONG_OPTION;
118 				break;
119 			default:
120 				// generic options
121 				m_Step = STEP_GEN_OPTIONS;
122 				m_Cur--;
123 				break;
124 			}
125 			m_WaitSpace = false;
126 			m_Cur++;
127 			m_Start = m_Cur;
128 			break;
129 		}
130 		case STEP_OPTION_IN:
131 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
132 				m_Cur++;
133 			if (m_InBuf[m_Cur] == ' ') {
134 				m_InBuf[m_Cur] = 0;
135 				OpenBabel::OBFormat *format = m_Conv.FindFormat (m_InBuf + m_Start);
136 				if (!format)
137 					format = m_Conv.FormatFromMIME (m_InBuf + m_Start);
138 				if (format)
139 					m_Conv.SetInFormat (format);
140 				else
141 					return -1;
142 				FinishOption (STEP_INPUT);
143 			}
144 			break;
145 		case STEP_INPUT:
146 			if (!m_Cur && m_InBuf[0] == '-') {
147 				m_Step = STEP_INIT;
148 				break;
149 			}
150 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
151 				m_Cur++;
152 			if (m_InBuf[m_Cur] == ' ') {
153 				m_InBuf[m_Cur] = 0;
154 				m_Input = m_InBuf;
155 				FinishOption (STEP_INIT);
156 			}
157 			break;
158 		case STEP_OPTION_OUT:
159 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
160 				m_Cur++;
161 			if (m_InBuf[m_Cur] == ' ') {
162 				m_InBuf[m_Cur] = 0;
163 				OpenBabel::OBFormat *format = m_Conv.FindFormat (m_InBuf + m_Start);
164 				if (!format)
165 					format = m_Conv.FormatFromMIME (m_InBuf + m_Start);
166 				if (format)
167 					m_Conv.SetOutFormat (format);
168 				else
169 					return -1;
170 				FinishOption (STEP_OUTPUT);
171 			}
172 			break;
173 		case STEP_OUTPUT:
174 			if (!m_Cur && m_InBuf[0] == '-') {
175 				m_Step = STEP_INIT;
176 				break;
177 			}
178 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
179 				m_Cur++;
180 			if (m_InBuf[m_Cur] == ' ') {
181 				m_InBuf[m_Cur] = 0;
182 				m_Output = m_InBuf;
183 				FinishOption (STEP_INIT);
184 			}
185 			break;
186 		case STEP_SIZE:
187 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
188 				m_Cur++;
189 			if (m_InBuf[m_Cur] == ' ') {
190 				char *end;
191 				m_InBuf[m_Cur] = 0;
192 				m_Size = strtoul (m_InBuf + m_Start, &end, 10);
193 				if (end && *end)
194 					return -1; // invalid size
195 				FinishOption (STEP_INIT);
196 				if (m_Size > bufsize) {
197 					char *new_buf = new char[m_Size + 3]; // we need a bit more, at least because of the "-D"
198 					memcpy (new_buf, m_InBuf, m_Index);
199 					delete [] m_InBuf;
200 					m_InBuf = new_buf;
201 					m_InBuf[m_Index] = 0;
202 				}
203 				return res; // force more read
204 			}
205 			break;
206 		case STEP_DATA:
207 			if (m_Input.length () > 0 || m_Index == m_Size) {
208 				std::istream *is;
209 				if (m_Input.length ())
210 					is = new std::ifstream (m_Input.c_str ());
211 				else
212 					is = new std::istringstream (m_InBuf);
213 				std::ostream *os;
214 				if (m_Output.length ())
215 					os = new std::ofstream (m_Output.c_str ());
216 				else
217 					os = new std::ostringstream ();
218 				m_Conv.Convert (is, os);
219 				if (m_Output.length () == 0) {
220 					std::ostringstream *oss = static_cast <std::ostringstream *> (os), l;
221 					l << oss->str ().length () << " ";
222 					write (m_Socket, l.str ().c_str (), l.str ().length ());
223 					write (m_Socket, oss->str ().c_str (), oss->str ().length ());
224 				} else
225 					static_cast <std::ofstream *> (os)->close ();
226 				delete is;
227 				delete os;
228 				return -1;
229 			} else
230 				return res;
231 		case STEP_FORMAT_IN_OPTIONS:
232 			if (!m_Cur && m_InBuf[0] == '-') {
233 				m_Step = STEP_INIT;
234 				break;
235 			}
236 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
237 				m_Cur++;
238 			if (m_InBuf[m_Cur] == ' ') {
239 				m_InBuf[m_Cur] = 0;
240 				m_Conv.SetOptions (m_InBuf + m_Start, OpenBabel::OBConversion::INOPTIONS);
241 				FinishOption (STEP_INIT);
242 			}
243 			break;
244 		case STEP_FORMAT_OUT_OPTIONS:
245 			if (!m_Cur && m_InBuf[0] == '-') {
246 				m_Step = STEP_INIT;
247 				break;
248 			}
249 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
250 				m_Cur++;
251 			if (m_InBuf[m_Cur] == ' ') {
252 				m_InBuf[m_Cur] = 0;
253 				m_Conv.SetOptions (m_InBuf + m_Start, OpenBabel::OBConversion::OUTOPTIONS);
254 				FinishOption (STEP_INIT);
255 			}
256 			break;
257 		case STEP_GEN_OPTIONS:
258 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
259 				m_Cur++;
260 			if (m_InBuf[m_Cur] == ' ') {
261 				m_InBuf[m_Cur] = 0;
262 				m_Conv.SetOptions (m_InBuf + m_Start, OpenBabel::OBConversion::GENOPTIONS);
263 				FinishOption (STEP_INIT);
264 			}
265 			break;
266 		case STEP_LONG_OPTION:
267 			while (m_InBuf[m_Cur] != ' ' && m_Cur < m_Index)
268 				m_Cur++;
269 			if (m_InBuf[m_Cur] == ' ') {
270 				m_InBuf[m_Cur] = 0;
271 				m_Conv.AddOption (m_InBuf + m_Start, OpenBabel::OBConversion::GENOPTIONS);
272 				FinishOption (STEP_INIT);
273 			}
274 		default:
275 			break;
276 		}
277 	}
278 	return res;
279 }
280 
FinishOption(unsigned step)281 void BabelSocket::FinishOption (unsigned step)
282 {
283 	m_Cur++;
284 	if (m_Cur < m_Index)
285 			memmove (m_InBuf, m_InBuf + m_Cur, m_Index - m_Cur + 1);
286 	m_WaitSpace = false;
287 	m_Index -= m_Cur;
288 	m_Cur = m_Start = 0;
289 	m_Step = step;
290 }
291