coan 4.2.4
io.c
Go to the documentation of this file.
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*/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines