coan 4.2.4
|
00001 /*************************************************************************** 00002 * Copyright (C) 2004, 2006 Symbian Software Ltd. * 00003 * All rights reserved. * 00004 * * 00005 * Contributed originally by Mike Kinghan, imk@strudl.org * 00006 * * 00007 * Redistribution and use in source and binary forms, with or without * 00008 * modification, are permitted provided that the following conditions * 00009 * are met: * 00010 * * 00011 * Redistributions of source code must retain the above copyright * 00012 * notice, this list of conditions and the following disclaimer. * 00013 * * 00014 * Redistributions in binary form must reproduce the above copyright * 00015 * notice, this list of conditions and the following disclaimer in the * 00016 * documentation and/or other materials provided with the distribution. * 00017 * * 00018 * Neither the name of Symbian Software Ltd. nor the names of its * 00019 * contributors may be used to endorse or promote products derived from * 00020 * this software without specific prior written permission. * 00021 * * 00022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * 00023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * 00024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * 00025 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * 00026 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * 00027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * 00028 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * 00029 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * 00030 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* 00031 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * 00032 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * 00033 * DAMAGE. * 00034 * * 00035 **************************************************************************/ 00036 00037 #include "report.h" 00038 #include "filesys.h" 00039 #include "io.h" 00040 #include "args.h" 00041 #include "platform.h" 00042 #include "dataset.h" 00043 #include "configured_symbols.h" 00044 00052 00056 static bool 00057 readon(void); 00058 00066 static void 00067 make_tempfile(void); 00068 00076 static void 00077 replace_infile(void); 00078 00086 static void 00087 make_backup_name(const char *filename); 00088 00093 static void 00094 backup_infile(void); 00095 00099 static void 00100 delete_infile(void) 00101 { 00102 if (remove(GET_PUBLIC(io,filename))) { 00103 bail(GRIPE_CANT_DELETE_FILE, 00104 "Cannot remove file \"%s\"",GET_PUBLIC(io,filename)); 00105 } 00106 } 00107 00108 00114 static void 00115 open_output(void); 00116 00121 00123 STATE_DEF(io) 00124 { 00125 INCLUDE_PUBLIC(io); 00126 FILE * input; 00127 size_t bufsz; 00128 size_t infile; 00129 char * in_name_buf; 00131 char * out_name_buf; 00133 char * bak_name_buf; 00134 size_t saved_read_pos; 00135 } 00136 STATE_T(io); 00141 IMPLEMENT(io,ZERO_INITABLE); 00149 char * read_filename(void) 00150 { 00151 int ch; 00152 size_t pos = 0; 00153 bool quoted; 00154 char * in_name_buf = GET_STATE(io,in_name_buf); 00155 if (in_name_buf == NULL) { 00156 in_name_buf = SET_STATE(io,in_name_buf) = zallocate(PATH_MAX + 1); 00157 } 00158 ch = getchar(); 00159 in_name_buf[0] = '\0'; 00160 /* Skip whitespace on stdin*/ 00161 for ( ; ch != EOF && isspace(ch); ch = getchar()) {}; 00162 if (ch == EOF) { 00163 return NULL; 00164 } 00165 quoted = ch == '\"'; 00166 if (quoted) { 00167 for (ch = getchar() ; ch != EOF && ch != '\"'; ch = getchar()) { 00168 if (isspace(ch) && ch != ' ') { 00169 in_name_buf[pos] = '\0'; 00170 bail(GRIPE_ILLEGAL_FILENAME, 00171 "Illegal whitespace in input filename: \"%s...", 00172 in_name_buf); 00173 } 00174 in_name_buf[pos++] = ch; 00175 if (pos == PATH_MAX) { 00176 in_name_buf[pos] = '\0'; 00177 bail(GRIPE_FILENAME_TOO_LONG, 00178 "A filename exceeds max %d bytes: \"%s...", 00179 PATH_MAX,in_name_buf); 00180 } 00181 } 00182 in_name_buf[pos] = '\0'; 00183 if (ch == EOF) { 00184 bail(GRIPE_EOF_IN_FILENAME, 00185 "A quoted input filename is unterminated: \"%s...", 00186 in_name_buf); 00187 } 00188 } else { 00189 for ( ; ch != EOF && !isspace(ch); ch = getchar()) { 00190 in_name_buf[pos++] = ch; 00191 if (pos == PATH_MAX) { 00192 in_name_buf[pos] = '\0'; 00193 bail(GRIPE_FILENAME_TOO_LONG, 00194 "An input filename exceeds max %d bytes: \"%s...", 00195 PATH_MAX,in_name_buf); 00196 } 00197 } 00198 in_name_buf[pos] = '\0'; 00199 } 00200 return in_name_buf; 00201 } 00202 00203 static bool 00204 readon(void) 00205 { 00206 size_t read = 0; 00207 for (;;) { 00208 char *bufp; 00209 if (GET_PUBLIC(io,line_len) + 1 >= GET_STATE(io,bufsz)) { 00210 /* Need more buffer*/ 00211 SET_PUBLIC(io,line_start) 00212 = reallocate(GET_PUBLIC(io,line_start), 00213 SET_STATE(io,bufsz) += BUFSIZ); 00214 } 00215 /* Position to end of current line*/ 00216 bufp = LINE_END; 00217 /* Read some more at that position*/ 00218 if (NULL == fgets(bufp,(int)(GET_STATE(io,bufsz) - 00219 GET_PUBLIC(io,line_len)),GET_STATE(io,input))) { 00220 /* EOF (or possibly read error).*/ 00221 if (ferror(GET_STATE(io,input))) { 00222 bail(GRIPE_CANT_READ_INPUT,"Read error on file %s", 00223 GET_PUBLIC(io,filename)); 00224 } 00225 break; 00226 } 00227 /* Update length of line*/ 00228 SET_PUBLIC(io,line_len) += read = strlen(bufp); 00229 bufp += read; 00230 if (bufp[-1] == '\n') { 00231 /* End of line. That's all*/ 00232 break; 00233 } 00234 } 00235 return read != 0; 00236 } 00237 00241 static bool 00242 extend_line(void) 00243 { 00244 bool eof = !readon(); 00245 if (!eof) { 00246 ++SET_PUBLIC(io,line_num); 00247 return true; 00248 } 00249 return false; 00250 } 00251 00252 static void 00253 make_tempfile(void) 00254 { 00255 char *out_name_buf = GET_STATE(io,out_name_buf); 00256 const char *infile = GET_PUBLIC(io,filename); 00257 const char *delim = strrchr(infile,PATH_DELIM); 00258 char const *tempname = NULL; 00259 size_t dirlen = delim ? delim - infile : 0; 00260 if (out_name_buf == NULL) { 00261 out_name_buf = SET_STATE(io,out_name_buf) = 00262 zallocate(PATH_MAX); 00263 } 00264 if (dirlen) { 00265 strncpy(out_name_buf,infile,dirlen); 00266 out_name_buf[dirlen++] = PATH_DELIM; 00267 } 00268 strcpy(out_name_buf + dirlen,"coan_out_XXXXXX"); 00269 tempname = fs_tempname(out_name_buf); 00270 if (!tempname) { 00271 bail(GRIPE_NO_TEMPFILE, 00272 "Cannot create temporary file"); 00273 } 00274 } 00275 00276 static void 00277 replace_infile(void) 00278 { 00279 if (rename(GET_STATE(io,out_name_buf), 00280 GET_PUBLIC(io,filename))) { 00281 bail(GRIPE_CANT_RENAME_FILE, 00282 "Cannot rename file \"%s\" as \"%s\"", 00283 GET_STATE(io,out_name_buf), 00284 GET_PUBLIC(io,filename)); 00285 } 00286 } 00287 00288 static void 00289 make_backup_name(const char *filename) 00290 { 00291 FILE *exists = NULL; 00292 size_t suffix_len = strlen(GET_PUBLIC(args,backup_suffix)); 00293 size_t namelen = strlen(filename) + suffix_len; 00294 if (GET_STATE(io,bak_name_buf) == NULL) { 00295 SET_STATE(io,bak_name_buf) = zallocate(PATH_MAX + 1); 00296 } 00297 strncpy(GET_STATE(io,bak_name_buf), 00298 GET_PUBLIC(io,filename),PATH_MAX)[PATH_MAX] = 0; 00299 do { 00300 if (namelen > PATH_MAX) { 00301 bail(GRIPE_FILENAME_TOO_LONG, 00302 "A filename exceeds max %d bytes: \"%s...", 00303 PATH_MAX,GET_STATE(io,bak_name_buf)); 00304 } 00305 strcat(GET_STATE(io,bak_name_buf), 00306 GET_PUBLIC(args,backup_suffix)); 00307 exists = fopen(GET_STATE(io,bak_name_buf),"r"); 00308 if (!exists) { 00309 break; 00310 } 00311 fclose(exists); 00312 namelen += suffix_len; 00313 } while(true); 00314 } 00315 00316 static void 00317 backup_infile(void) 00318 { 00319 make_backup_name(GET_PUBLIC(io,filename)); 00320 if (rename(GET_PUBLIC(io,filename), 00321 GET_STATE(io,bak_name_buf))) { 00322 bail(GRIPE_CANT_RENAME_FILE, 00323 "Cannot rename file \"%s\" as \"%s\"", 00324 GET_PUBLIC(io,filename), 00325 GET_STATE(io,bak_name_buf)); 00326 } 00327 } 00328 00329 00330 static void 00331 open_output(void) 00332 { 00333 if (!GET_PUBLIC(args,replace)) { 00334 SET_PUBLIC(io,output) = stdout; 00335 } else { 00336 make_tempfile(); 00337 SET_PUBLIC(io,output) = 00338 open_file(GET_STATE(io,out_name_buf),"w"); 00339 } 00340 } 00341 00342 /* API **************************************************************/ 00343 00344 FILE * open_file(const char *file, const char *mode) 00345 { 00346 FILE * stream = fopen(file, mode); 00347 if (stream == NULL) { 00348 bail(GRIPE_CANT_OPEN_INPUT,"Can't open %s for %s", 00349 file, (mode[0] == 'r' ? "reading": "writing")); 00350 } 00351 return stream; 00352 } 00353 00354 void 00355 close_io(int error) 00356 { 00357 ++SET_PUBLIC(dataset,donefiles); 00358 if (GET_STATE(io,input) != stdin && 00359 GET_STATE(io,input) != NULL) { 00360 00361 fclose(GET_STATE(io,input)); 00362 SET_STATE(io,input) = NULL; 00363 00364 if (GET_PUBLIC(io,output) != stdout && 00365 GET_PUBLIC(io,output) != NULL) { 00366 fclose(GET_PUBLIC(io,output)); 00367 SET_PUBLIC(io,output) = NULL; 00368 } 00369 if (!error && GET_PUBLIC(args,replace)) { 00370 if (GET_PUBLIC(args,backup_suffix) != NULL) { 00371 backup_infile(); 00372 } else { 00373 delete_infile(); 00374 } 00375 replace_infile(); 00376 } 00377 00378 } 00379 if (error) { 00380 ++SET_PUBLIC(dataset,errorfiles); 00381 if (!GET_PUBLIC(args,keepgoing)) { 00382 exit(exitcode()); 00383 } 00384 } 00385 io_toplevel(); 00386 } 00387 00388 00389 void 00390 open_io(char const *filename) 00391 { 00392 SET_PUBLIC(io,filename) = filename; 00393 if (!strcmp(GET_PUBLIC(io,filename),STDIN_NAME)) { 00394 SET_STATE(io,input) = stdin; 00395 } else { 00396 SET_STATE(io,input) = 00397 open_file(GET_PUBLIC(io,filename),"r"); 00398 SET_PUBLIC(io,line_num) = 0; 00399 } 00400 open_output(); 00401 configured_symbols_rewind(); 00402 } 00403 00404 bool get_line(void) 00405 { 00406 bool got_line; 00407 char *pristine_line; 00408 char * last_line_end = LINE_END - 1; 00409 if (last_line_end < LINE_START) { 00410 last_line_end = "\n"; 00411 } 00412 SET_PUBLIC(io,line_len) = 0; 00413 SET_PUBLIC(io,extension_lines) = 0; 00414 pristine_line = GET_PUBLIC(io,pristine_line); 00415 if (pristine_line) { 00416 free(pristine_line); 00417 SET_PUBLIC(io,pristine_line) = NULL; 00418 } 00419 got_line = extend_line(); 00420 if (!got_line) { 00421 if (INSOURCE && *last_line_end != '\n') { 00422 report(GRIPE_MISSING_EOF_NEWLINE,NULL,"Missing newline at EOF"); 00423 } 00424 } 00425 else { /* If we've read a line then save a pristine copy */ 00426 SET_PUBLIC(io,pristine_line) = clone(GET_PUBLIC(io,line_start),0); 00427 } 00428 return got_line; 00429 } 00430 00431 char * 00432 read_more(char const *readpos) 00433 { 00434 save_read_pos(readpos); 00435 if (extend_line()) { 00436 return saved_read_pos(); 00437 } 00438 early_eof(); 00439 return NULL; 00440 } 00441 00442 size_t 00443 read_offset(char const *readpos) 00444 { 00445 char *linestart = LINE_START; 00446 return readpos - linestart; 00447 } 00448 00449 char * 00450 read_pos(size_t readoff) 00451 { 00452 return LINE_START + readoff; 00453 } 00454 00455 00456 void 00457 ensure_buf(size_t extra) 00458 { 00459 size_t spare = GET_STATE(io,bufsz) - GET_PUBLIC(io,line_len); 00460 int i; 00461 for ( i = 0; spare <= extra; ++i) { 00462 SET_STATE(io,bufsz) += BUFSIZ; 00463 spare = GET_STATE(io,bufsz) - GET_PUBLIC(io,line_len); 00464 } 00465 if (i) { 00466 SET_PUBLIC(io,line_start) 00467 = reallocate(GET_PUBLIC(io,line_start), 00468 GET_STATE(io,bufsz)); 00469 } 00470 } 00471 00472 bool 00473 input_opened(void) 00474 { 00475 return !!GET_STATE(io,input); 00476 } 00477 00478 void 00479 save_read_pos(char const *cp) 00480 { 00481 SET_STATE(io,saved_read_pos) = cp - GET_PUBLIC(io,line_start); 00482 } 00483 00484 char * 00485 saved_read_pos(void) 00486 { 00487 return GET_PUBLIC(io,line_start) + GET_STATE(io,saved_read_pos); 00488 } 00489 00490 void 00491 io_toplevel(void) 00492 { 00493 SET_PUBLIC(io,line_num) = 0; 00494 SET_PUBLIC(io,filename) = NULL; 00495 } 00496 00497 /* EOF*/