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