1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 #ifndef DEVICESTREAM_HPP
40 #define DEVICESTREAM_HPP
41 
42 #include <string>
43 #include <termios.h> // POSIX terminal control definitions
44 
45 #include "FDStreamBuff.hpp"
46 #include "TCPStreamBuff.hpp"
47 
48 namespace gpstk
49 {
50       // A class to hid the details of setting up an iostream to take data from
51       // write data to one of several sources:
52       //   standard input/output
53       //   a file
54       //   a tcp socket
55       //   a serial port
56       // See the open() function for how to specify these targets
57       // Note that the base type needs to be a decendant of an fstream for this class to work.
58    template<class T>
59    class DeviceStream : public T
60    {
61    public:
62       DeviceStream(void);
63 
64       DeviceStream(const std::string& target,
65                    std::ios::openmode mode = std::ios::in);
66 
67       void open(const std::string& target,
68                 std::ios::openmode mode = std::ios::in);
69 
70       void open(const char* p,
71                 std::ios::openmode mode = std::ios::in);
72 
73       virtual ~DeviceStream();
74 
75       bool is_open() const;
76 
77       enum DeviceType {dtStdio, dtFile, dtTCP, dtSerial};
78 
getDeviceType()79       DeviceType getDeviceType() {return deviceType;}
80 
getTarget() const81       std::string getTarget() const {return target;}
82 
83    private:
84       mutable FDStreamBuff *fdbuff; // mutable so rdbuf() can be const
85       std::string target;
86       DeviceType deviceType;
87    };
88 
89 
90    template<class T>
DeviceStream(void)91    DeviceStream<T>::DeviceStream(void)
92          : fdbuff(NULL)
93    {
94       open("");
95    }
96 
97 
98    template<class T>
DeviceStream(const std::string & target,std::ios::openmode mode)99    DeviceStream<T>::DeviceStream(const std::string& target, std::ios::openmode mode)
100          : fdbuff(NULL),
101            target(target)
102    {
103       open(target, mode);
104    };
105 
106 
107    template<class T>
~DeviceStream()108    DeviceStream<T>::~DeviceStream()
109    {
110       if (fdbuff)
111          delete fdbuff;
112    }
113 
114 
115    template<class T>
open(const char * p,std::ios::openmode mode)116    void DeviceStream<T>::open(const char* p, std::ios::openmode mode)
117    {
118       open(std::string(p), mode);
119    }
120 
121    template<class T>
is_open() const122    bool DeviceStream<T>::is_open() const
123    {
124       if (deviceType == dtStdio)
125          return true;
126       if (fdbuff != NULL)
127          return fdbuff->is_open();
128    }
129 
130    template<class T>
open(const std::string & target,std::ios::openmode mode)131    void DeviceStream<T>::open(const std::string& target, std::ios::openmode mode)
132    {
133       using namespace std;
134       this->target = target;
135       if (target != "")
136       {
137          if (target.substr(0, 4) == "tcp:")
138          {
139             string ifn=target;
140             int port = 25;
141             ifn.erase(0,4);
142             string::size_type i = ifn.find(":");
143             if (i<ifn.size())
144             {
145                port = StringUtils::asInt(ifn.substr(i+1));
146                ifn.erase(i);
147             }
148 
149             TCPStreamBuff *tcpbuff = new TCPStreamBuff();
150 
151             SocketAddr client(ifn, port);
152             if (tcpbuff->connect(client))
153             {
154                cerr << "Could not connect to " << ifn << endl;
155                exit(-1);
156             }
157 
158             deviceType = dtTCP;
159             fdbuff = tcpbuff;
160             basic_ios<char>::rdbuf(fdbuff);
161          }
162          else if (target.substr(0, 4) == "ser:") // A serial port
163          {
164             string ifn=target;
165             ifn.erase(0,4);
166 
167             int fd = ::open(ifn.c_str(), O_RDWR | O_NOCTTY);
168             if (fd<0)
169             {
170                cout << "Error opening " << ifn.c_str() << endl;
171                return;
172             }
173 
174                // Not sure why this was being done it really should have been
175                // This is just another way to force blocking I/O
176             int rc;
177             rc = fcntl(fd, F_SETFL, 0);
178             if (rc < 0)
179             {
180                cout << "Error in fcntl, rc=" << rc << endl;
181                return;
182             }
183 
184             struct termios options;
185             rc=tcgetattr(fd, &options);
186 
187             options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
188                                  | IGNCR | ICRNL | IXON);
189             options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG | IEXTEN);
190             options.c_oflag &= ~OPOST;
191             options.c_cflag &= ~(CSIZE | PARENB);
192             options.c_cflag |= CS8 | CREAD | HUPCL | CLOCAL;
193 
194             options.c_cc[VTIME] = 0; // Wait forever
195             options.c_cc[VMIN] = 16; // And always get at least 16 characters
196             if (rc==cfsetospeed(&options, B115200))
197                cout << "Error in cfsetospeed(), rc=" << rc << endl;
198             if (rc==cfsetispeed(&options, B115200))
199                cout << "Error in cfsetispeed(), rc=" << rc << endl;
200 
201                // Final step... apply them
202             if (rc==tcsetattr(fd, TCSANOW, &options))
203                cout << "Error in tcsetattr(), rc=" << rc << endl;
204 
205             deviceType = dtSerial;
206             fdbuff = new FDStreamBuff(fd);
207             basic_ios<char>::rdbuf(fdbuff);
208          }
209          else // a regular file
210          {
211             int flags=O_RDONLY;
212             if (mode & ios::out)
213                flags=O_WRONLY|O_CREAT;
214             if (mode & ios::app) flags |= O_APPEND;
215             if (mode & ios::trunc) flags |= O_TRUNC;
216 
217             int fd = ::open(target.c_str(), flags, 0666);
218             if (fd<0)
219             {
220                cerr << "Could not open: " << target.c_str() << endl;
221                return;
222             }
223             fdbuff = new FDStreamBuff(fd);
224             basic_ios<char>::rdbuf(fdbuff);
225 
226             deviceType = dtFile;
227          }
228       }
229       else  // Use standard input/output
230       {
231          if (mode & ios::out)
232          {
233             T::copyfmt(cout);
234             T::clear(cin.rdstate());
235             basic_ios<char>::rdbuf(cout.rdbuf());
236             this->target = "<stdout>";
237          }
238          else
239          {
240             T::copyfmt(cin);
241             T::clear(cin.rdstate());
242             basic_ios<char>::rdbuf(cin.rdbuf());
243             this->target = "<stdin>";
244          }
245          deviceType = dtStdio;
246       }
247    }
248 
249 } // end of namespace
250 #endif
251