E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gbatchfile.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gbatchfile.cpp
19 //
20
21 #include "gdef.h"
22 #include "gbatchfile.h"
23 #include "garg.h"
24 #include "gstr.h"
25 #include "gdebug.h"
26 #include <stdexcept>
27 #include <fstream>
28
29 G::BatchFile::BatchFile( const G::Path & path )
30 {
31 std::ifstream stream( path.str().c_str() ) ;
32 if( !stream.good() )
33 throw Error( "cannot open batch file" , path.str() ) ;
34 m_line = readFrom( stream , path.str() , true ) ;
35 m_args = split( m_line ) ;
36 }
37
38 G::BatchFile::BatchFile( const G::Path & path , NoThrow )
39 {
40 std::ifstream stream( path.str().c_str() ) ;
41 if( stream.good() )
42 {
43 m_line = readFrom( stream , path.str() , false ) ;
44 m_args = split( m_line ) ;
45 }
46 }
47
48 G::BatchFile::BatchFile( std::istream & stream , const std::string & name )
49 {
50 m_line = readFrom( stream , name , true ) ;
51 m_args = split( m_line ) ;
52 }
53
54 bool G::BatchFile::ignorable( const std::string & trimmed_line )
55 {
56 return
57 trimmed_line.empty() ||
58 Str::lower(trimmed_line+" ").find("@echo ") == 0U ||
59 Str::lower(trimmed_line+" ").find("rem ") == 0U ;
60 }
61
62 bool G::BatchFile::relevant( const std::string & trimmed_line )
63 {
64 return ! ignorable( trimmed_line ) ;
65 }
66
67 std::string G::BatchFile::join( const std::string & file_name , unsigned int line_number )
68 {
69 std::ostringstream ss ;
70 ss << file_name << "(" << line_number << ")" ;
71 return ss.str() ;
72 }
73
74 std::string G::BatchFile::readFrom( std::istream & stream , const std::string & stream_name , bool strict )
75 {
76 std::string line ;
77 unsigned int line_number = 1U ;
78 for( ; stream.good() ; line_number++ )
79 {
80 std::string s = Str::readLineFrom( stream ) ;
81 if( !stream ) break ;
82 Str::trim( s , Str::ws() ) ;
83 Str::replaceAll( s , "\t" , " " ) ;
84 s = Str::unique( s ) ;
85 if( relevant(s) )
86 {
87 if( line.empty() )
88 line = s ;
89 else
90 throw Error( "too many lines in batch file" , join(stream_name,line_number) ) ;
91 }
92 }
93
94 if( strict && line.empty() )
95 throw Error( "batch file is empty" , join(stream_name,line_number) ) ;
96
97 // strip off any "start" prefix -- allow the "start" to have a quoted window
98 // title but dont expect any "start" options such as "/min"
99 if( !line.empty() )
100 {
101 typedef std::string::size_type size_type ;
102 size_type const npos = std::string::npos ;
103
104 std::string start = "start " ;
105 size_type start_pos = Str::lower(line).find(start) ;
106 size_type command_pos = start_pos == npos ? 0U : line.find_first_not_of(" ",start_pos+start.size()) ;
107
108 bool named = start_pos != npos && line.at(command_pos) == '"' ;
109 if( named )
110 {
111 size_t name_start_pos = command_pos ;
112 size_t name_end_pos = line.find( "\"" , name_start_pos+1U ) ;
113 if( name_end_pos == npos )
114 throw Error( "mismatched quotes in batch file" , stream_name ) ;
115 if( (name_end_pos+2U) >= line.size() || line.at(name_end_pos+1U) != ' ' )
116 throw Error( "invalid window name in batch file" , stream_name ) ;
117
118 m_name = line.substr( name_start_pos+1U , name_end_pos-(name_start_pos+1U) ) ;
119 dequote( m_name ) ;
120 Str::trim( m_name , Str::ws() ) ;
121
122 command_pos = line.find_first_not_of(" ",name_end_pos+2U) ;
123 }
124
125 if( command_pos != npos )
126 {
127 line.erase( 0U , command_pos ) ;
128 Str::trimLeft( line , Str::ws() ) ;
129 }
130 }
131
132 // percent characters are doubled up in batch files so un-double them here
133 G::Str::replaceAll( line , "%%" , "%" ) ;
134
135 return line ;
136 }
137
138 std::string G::BatchFile::line() const
139 {
140 return m_line ;
141 }
142
143 std::string G::BatchFile::name() const
144 {
145 return m_name ;
146 }
147
148 const G::StringArray & G::BatchFile::args() const
149 {
150 return m_args ;
151 }
152
153 void G::BatchFile::dequote( std::string & s )
154 {
155 if( s.size() >= 2U && s.find("\"") == 0U && (s.rfind("\"")+1U) == s.size() )
156 s = s.substr( 1U , s.size()-2U ) ;
157 }
158
159 void G::BatchFile::write( const G::Path & path , const StringArray & args , const std::string & name_in )
160 {
161 G_ASSERT( args.size() > 0U ) ;
162 if( args.size() == 0U )
163 throw Error( "invalid contents for startup batch file" ) ;
164
165 std::string name = name_in ;
166 if( name.empty() )
167 {
168 name = args.at(0U) ;
169 dequote( name ) ;
170 name = Path(name).withoutExtension().basename() ;
171 }
172
173 std::ofstream stream( path.str().c_str() , std::ios::binary | std::ios::out | std::ios::trunc ) ;
174 if( !stream.good() )
175 throw Error( "cannot create batch file" , path.str() ) ;
176
177 stream << "start \"" << name << "\"" ;
178 for( StringArray::const_iterator p = args.begin() ; p != args.end() ; ++p )
179 {
180 stream << " " << percents(quote(*p)) ;
181 }
182 stream << "\r\n" ;
183
184 stream.close() ;
185 if( stream.fail() )
186 throw Error( "cannot write batch file" , path.str() ) ;
187 }
188
189 G::StringArray G::BatchFile::split( const std::string & line )
190 {
191 // get G::Arg to deal with the quotes
192 Arg args ;
193 if( !line.empty() )
194 args.parse( line ) ;
195 return args.array() ;
196 }
197
198 std::string G::BatchFile::percents( const std::string & s )
199 {
200 std::string result( s ) ;
201 G::Str::replaceAll( result , "%" , "%%" ) ;
202 return result ;
203 }
204
205 std::string G::BatchFile::quote( const std::string & s )
206 {
207 return
208 s.find("\"") == std::string::npos && s.find_first_of(" \t") != std::string::npos ?
209 "\"" + s + "\"" : s ;
210 }
211
212 /// \file gbatchfile.cpp
G::BatchFile::line
std::string line() const
Returns the main command-line from within the batchfile, with normalised spaces, without any "start" ...
Definition: gbatchfile.cpp:138
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
G::BatchFile::write
static void write(const G::Path &, const StringArray &args, const std::string &start_window_name=std::string())
Writes a startup batch file, including a "start" prefix.
Definition: gbatchfile.cpp:159
G::BatchFile::BatchFile
BatchFile(const G::Path &)
Constructor that reads from a file.
Definition: gbatchfile.cpp:29
G::Path::withoutExtension
Path withoutExtension() const
Returns a path without the basename extension, if any.
Definition: gpath.cpp:328
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::BatchFile::name
std::string name() const
Returns the "start" window name, if any.
Definition: gbatchfile.cpp:143
G::Str::trim
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:246
garg.h
G::Str::trimLeft
static void trimLeft(std::string &s, const std::string &ws, size_type limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:224
G::BatchFile::args
const G::StringArray & args() const
Returns the startup command-line broken up into de-quoted pieces.
Definition: gbatchfile.cpp:148
G::Str::lower
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 uppercase characters have been replaced by lowercase chara...
Definition: gstr.cpp:599
G::Arg::array
StringArray array(unsigned int shift=0U) const
Returns the arguments a string array, including the program name in the first position.
Definition: garg.cpp:100
gstr.h
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
Definition: gstr.cpp:195
G::Str::unique
static std::string unique(std::string s, char c= ' ', char r= ' ')
Returns a string with repeated 'c' characters replaced by one 'r' character.
Definition: gstr.cpp:1268
G::Arg::parse
void parse(HINSTANCE hinstance, const std::string &command_line_tail)
Parses the given command-line tail, splitting it up into an array of tokens.
Definition: garg.cpp:68
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:733
G::Arg
A class which holds a represention of the argc/argv command line array, and supports simple command-l...
Definition: garg.h:43
G::BatchFile::NoThrow
Overload discriminator for G::BatchFile.
Definition: gbatchfile.h:45
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Str::ws
static std::string ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1078
gdebug.h
gbatchfile.h