comparison libinterp/interpfcn/file-io.cc @ 16590:2d968b7830d6

handle A, R, and W fopen modes correctly (bug #38851) * file-io.cc (normalize_fopen_mode): New function. Handle 'A'. Also handle 'b' and 't' suffixes here. Use Octave:fopen-mode warning id. (fopen_mode_to_ios_mode): Only convert from mode string to ios mode. (do_stream_open): Call normalize_fopen_mode before calling fopen_mode_to_ios_mode. Don't process mode string directly. * io.tst: Update test for fopen modes.
author John W. Eaton <jwe@octave.org>
date Mon, 29 Apr 2013 13:46:55 -0400
parents 122c1a7a3004
children 7268845c0a1e
comparison
equal deleted inserted replaced
16589:fe6beca15813 16590:2d968b7830d6
128 tmp_files.pop (); 128 tmp_files.pop ();
129 gnulib::unlink (filename.c_str ()); 129 gnulib::unlink (filename.c_str ());
130 } 130 }
131 } 131 }
132 132
133 static std::ios::openmode 133 static void
134 fopen_mode_to_ios_mode (const std::string& mode_arg) 134 normalize_fopen_mode (std::string& mode, bool& use_zlib)
135 { 135 {
136 std::ios::openmode retval = std::ios::in; 136 use_zlib = false;
137 137
138 if (! mode_arg.empty ()) 138 if (! mode.empty ())
139 { 139 {
140 // Could probably be faster, but does it really matter? 140 // Could probably be faster, but does it really matter?
141 141
142 std::string mode = mode_arg; 142 // Accept 'W', 'R', and 'A' as 'w', 'r', and 'a' but we warn about
143 143 // them because Matlab says they don't perform "automatic
144 // 'W' and 'R' are accepted as 'w' and 'r', but we warn about 144 // flushing" but we don't know precisely what action that implies.
145 // them because Matlab says they perform "automatic flushing"
146 // but we don't know precisely what action that implies.
147 145
148 size_t pos = mode.find ('W'); 146 size_t pos = mode.find ('W');
149 147
150 if (pos != std::string::npos) 148 if (pos != std::string::npos)
151 { 149 {
152 warning ("fopen: treating mode \"W\" as equivalent to \"w\""); 150 warning_with_id ("Octave:fopen-mode",
151 "fopen: treating mode \"W\" as equivalent to \"w\"");
153 mode[pos] = 'w'; 152 mode[pos] = 'w';
154 } 153 }
155 154
156 pos = mode.find ('R'); 155 pos = mode.find ('R');
157 156
158 if (pos != std::string::npos) 157 if (pos != std::string::npos)
159 { 158 {
160 warning ("fopen: treating mode \"R\" as equivalent to \"r\""); 159 warning_with_id ("Octave:fopen-mode",
160 "fopen: treating mode \"R\" as equivalent to \"r\"");
161 mode[pos] = 'r'; 161 mode[pos] = 'r';
162 } 162 }
163 163
164 pos = mode.find ('A');
165
166 if (pos != std::string::npos)
167 {
168 warning_with_id ("Octave:fopen-mode",
169 "fopen: treating mode \"A\" as equivalent to \"a\"");
170 mode[pos] = 'a';
171 }
172
164 pos = mode.find ('z'); 173 pos = mode.find ('z');
165 174
166 if (pos != std::string::npos) 175 if (pos != std::string::npos)
167 { 176 {
168 #if defined (HAVE_ZLIB) 177 #if defined (HAVE_ZLIB)
178 use_zlib = true;
169 mode.erase (pos, 1); 179 mode.erase (pos, 1);
170 #else 180 #else
171 error ("this version of Octave does not support gzipped files"); 181 error ("this version of Octave does not support gzipped files");
172 #endif 182 #endif
173 } 183 }
174 184
175 if (! error_state) 185 if (! error_state)
176 { 186 {
177 if (mode == "rt") 187 // Use binary mode if 't' is not specified, but don't add
178 retval = std::ios::in; 188 // 'b' if it is already present.
179 else if (mode == "wt") 189
180 retval = std::ios::out | std::ios::trunc; 190 size_t bpos = mode.find ('b');
181 else if (mode == "at") 191 size_t tpos = mode.find ('t');
182 retval = std::ios::out | std::ios::app; 192
183 else if (mode == "r+t" || mode == "rt+") 193 if (bpos == std::string::npos && tpos == std::string::npos)
184 retval = std::ios::in | std::ios::out; 194 mode += 'b';
185 else if (mode == "w+t" || mode == "wt+") 195 }
186 retval = std::ios::in | std::ios::out | std::ios::trunc; 196 }
187 else if (mode == "a+t" || mode == "at+") 197 }
188 retval = std::ios::in | std::ios::out | std::ios::app; 198
189 else if (mode == "rb" || mode == "r") 199 static std::ios::openmode
190 retval = std::ios::in | std::ios::binary; 200 fopen_mode_to_ios_mode (const std::string& mode)
191 else if (mode == "wb" || mode == "w") 201 {
192 retval = std::ios::out | std::ios::trunc | std::ios::binary; 202 std::ios::openmode retval = std::ios::in;
193 else if (mode == "ab" || mode == "a") 203
194 retval = std::ios::out | std::ios::app | std::ios::binary; 204 if (! error_state)
195 else if (mode == "r+b" || mode == "rb+" || mode == "r+") 205 {
196 retval = std::ios::in | std::ios::out | std::ios::binary; 206 if (mode == "rt")
197 else if (mode == "w+b" || mode == "wb+" || mode == "w+") 207 retval = std::ios::in;
198 retval = (std::ios::in | std::ios::out | std::ios::trunc 208 else if (mode == "wt")
199 | std::ios::binary); 209 retval = std::ios::out | std::ios::trunc;
200 else if (mode == "a+b" || mode == "ab+" || mode == "a+") 210 else if (mode == "at")
201 retval = (std::ios::in | std::ios::out | std::ios::app 211 retval = std::ios::out | std::ios::app;
202 | std::ios::binary); 212 else if (mode == "r+t" || mode == "rt+")
203 else 213 retval = std::ios::in | std::ios::out;
204 ::error ("invalid mode specified"); 214 else if (mode == "w+t" || mode == "wt+")
205 } 215 retval = std::ios::in | std::ios::out | std::ios::trunc;
216 else if (mode == "a+t" || mode == "at+")
217 retval = std::ios::in | std::ios::out | std::ios::app;
218 else if (mode == "rb" || mode == "r")
219 retval = std::ios::in | std::ios::binary;
220 else if (mode == "wb" || mode == "w")
221 retval = std::ios::out | std::ios::trunc | std::ios::binary;
222 else if (mode == "ab" || mode == "a")
223 retval = std::ios::out | std::ios::app | std::ios::binary;
224 else if (mode == "r+b" || mode == "rb+" || mode == "r+")
225 retval = std::ios::in | std::ios::out | std::ios::binary;
226 else if (mode == "w+b" || mode == "wb+" || mode == "w+")
227 retval = (std::ios::in | std::ios::out | std::ios::trunc
228 | std::ios::binary);
229 else if (mode == "a+b" || mode == "ab+" || mode == "a+")
230 retval = (std::ios::in | std::ios::out | std::ios::app
231 | std::ios::binary);
232 else
233 ::error ("invalid mode specified");
206 } 234 }
207 235
208 return retval; 236 return retval;
209 } 237 }
210 238
446 return retval; 474 return retval;
447 } 475 }
448 476
449 477
450 static octave_stream 478 static octave_stream
451 do_stream_open (const std::string& name, const std::string& mode, 479 do_stream_open (const std::string& name, const std::string& mode_arg,
452 const std::string& arch, int& fid) 480 const std::string& arch, int& fid)
453 { 481 {
454 octave_stream retval; 482 octave_stream retval;
455 483
456 fid = -1; 484 fid = -1;
485
486 std::string mode = mode_arg;
487 bool use_zlib = false;
488 normalize_fopen_mode (mode, use_zlib);
457 489
458 std::ios::openmode md = fopen_mode_to_ios_mode (mode); 490 std::ios::openmode md = fopen_mode_to_ios_mode (mode);
459 491
460 if (! error_state) 492 if (! error_state)
461 { 493 {
486 } 518 }
487 } 519 }
488 520
489 if (! fs.is_dir ()) 521 if (! fs.is_dir ())
490 { 522 {
491 std::string tmode = mode;
492
493 // Use binary mode if 't' is not specified, but don't add
494 // 'b' if it is already present.
495
496 size_t bpos = tmode.find ('b');
497 size_t tpos = tmode.find ('t');
498
499 if (bpos == std::string::npos && tpos == std::string::npos)
500 tmode += 'b';
501
502 #if defined (HAVE_ZLIB) 523 #if defined (HAVE_ZLIB)
503 size_t pos = tmode.find ('z'); 524 if (use_zlib)
504
505 if (pos != std::string::npos)
506 { 525 {
507 tmode.erase (pos, 1); 526 FILE *fptr = gnulib::fopen (fname.c_str (), mode.c_str ());
508
509 FILE *fptr = gnulib::fopen (fname.c_str (), tmode.c_str ());
510 527
511 int fd = fileno (fptr); 528 int fd = fileno (fptr);
512 529
513 gzFile gzf = ::gzdopen (fd, tmode.c_str ()); 530 gzFile gzf = ::gzdopen (fd, mode.c_str ());
514 531
515 if (fptr) 532 if (fptr)
516 retval = octave_zstdiostream::create (fname, gzf, fd, 533 retval = octave_zstdiostream::create (fname, gzf, fd,
517 md, flt_fmt); 534 md, flt_fmt);
518 else 535 else
519 retval.error (gnulib::strerror (errno)); 536 retval.error (gnulib::strerror (errno));
520 } 537 }
521 else 538 else
522 #endif 539 #endif
523 { 540 {
524 FILE *fptr = gnulib::fopen (fname.c_str (), tmode.c_str ()); 541 FILE *fptr = gnulib::fopen (fname.c_str (), mode.c_str ());
525 542
526 retval = octave_stdiostream::create (fname, fptr, md, flt_fmt); 543 retval = octave_stdiostream::create (fname, fptr, md, flt_fmt);
527 544
528 if (! fptr) 545 if (! fptr)
529 retval.error (gnulib::strerror (errno)); 546 retval.error (gnulib::strerror (errno));