"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/read.c" (12 Oct 2016, 10510 Bytes) of package /linux/misc/tin-2.4.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "read.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.0_vs_2.4.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : read.c
    4  *  Author    : Jason Faultless <jason@altarstone.com>
    5  *  Created   : 1997-04-10
    6  *  Updated   : 2016-07-29
    7  *
    8  * Copyright (c) 1997-2017 Jason Faultless <jason@altarstone.com>
    9  * All rights reserved.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. The name of the author may not be used to endorse or promote
   20  *    products derived from this software without specific prior written
   21  *    permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
   24  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
   27  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   29  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   34  */
   35 
   36 
   37 #ifndef TIN_H
   38 #   include "tin.h"
   39 #endif /* !TIN_H */
   40 #ifndef TNNTP_H
   41 #   include "tnntp.h"
   42 #endif /* !TNNTP_H */
   43 
   44 
   45 /*
   46  * The initial and expansion sizes to use for allocating read data
   47  */
   48 #define INIT                    512
   49 #define RCHUNK                  256
   50 
   51 /*
   52  * Global error flag. Set if something abnormal happens during file I/O
   53  */
   54 int tin_errno;
   55 
   56 /* How many chars we read at last tin_read() */
   57 static int offset = 0;
   58 
   59 /*
   60  * local prototypes
   61  */
   62 static char *tin_read(char *buffer, size_t len, FILE *fp, t_bool header);
   63 #if defined(NNTP_ABLE) && defined(HAVE_SELECT)
   64     static t_bool wait_for_input(void);
   65 #endif /* NNTP_ABLE && HAVE_SELECT */
   66 
   67 
   68 #if defined(NNTP_ABLE) && defined(HAVE_SELECT)
   69 /*
   70  * Used by the I/O read routine to look for keyboard input
   71  * Returns TRUE if user aborted with 'q' or 'z' (lynx-style)
   72  *         FALSE otherwise
   73  * TODO: document 'z' (, and allow it's remapping?) and 'Q'
   74  *       add a !HAVE_SELECT code path
   75  */
   76 static t_bool
   77 wait_for_input(
   78     void)
   79 {
   80     int nfds, ch;
   81     fd_set readfds;
   82     struct timeval tv;
   83 
   84     /*
   85      * Main loop. Wait for input from keyboard or file or for a timeout.
   86      */
   87     forever {
   88         FD_ZERO(&readfds);
   89         FD_SET(STDIN_FILENO, &readfds);
   90 /*      FD_SET(fileno(NEED_REAL_NNTP_FD_HERE), &readfds); */
   91 
   92         tv.tv_sec = 0;      /* NNTP_READ_TIMEOUT; */
   93         tv.tv_usec = 0;
   94 
   95 /* DEBUG_IO((stderr, "waiting on %d and %d...", STDIN_FILENO, fileno(fd))); */
   96 #   ifdef HAVE_SELECT_INTP
   97         if ((nfds = select(STDIN_FILENO + 1, (int *) &readfds, NULL, NULL, &tv)) == -1) {
   98 #   else
   99         if ((nfds = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv)) == -1) {
  100 #   endif /* HAVE_SELECT_INTP */
  101 
  102             if (errno != EINTR) {
  103                 perror_message("select() failed");
  104                 free(tin_progname);
  105                 giveup();
  106             } else
  107                 return FALSE;
  108         }
  109 
  110         /* No input pending */
  111         if (nfds == 0)
  112             return FALSE;
  113 
  114         /*
  115          * Something is waiting. See what's cooking...
  116          */
  117         if (nfds > 0) {
  118             /*
  119              * User pressed something. If 'q'uit, then handle this. Process
  120              * user input 1st so they get chance to quit on busy (or stalled)
  121              * reads
  122              */
  123             if (FD_ISSET(STDIN_FILENO, &readfds)) {
  124                 if ((ch = ReadCh()) == EOF)
  125                     return FALSE;
  126 
  127                 /*
  128                  * check if keymap key was pressed. Unfortunately, keymap keys
  129                  * are encoded by a sequence of bytes starting with ESC, so we
  130                  * must first see if it was really an ESC or a keymap key before
  131                  * asking the user if he wants to abort.
  132                  */
  133                 if (ch == iKeyAbort) {
  134                     int keymap_ch = get_arrow_key(ch);
  135 
  136                     if (keymap_ch != KEYMAP_UNKNOWN)
  137                         ch = keymap_ch;
  138                 }
  139 
  140                 if (ch == iKeyQuit || ch == 'z' || ch == iKeyAbort) {
  141                     if (post_article_and_exit)
  142                         return FALSE;
  143                     if (prompt_yn(_(txt_read_abort), FALSE) == 1)
  144                         return TRUE;
  145                 }
  146 
  147                 if (ch == iKeyQuitTin) {
  148                     if (post_article_and_exit)
  149                         return FALSE;
  150                     if (prompt_yn(_(txt_read_exit), FALSE) == 1)
  151                         tin_done(EXIT_SUCCESS, NULL);
  152                 }
  153 
  154             }
  155 
  156 #   if 0
  157             /*
  158              * Our file has something for us to read
  159              */
  160             if (FD_ISSET(fileno(NEED_NNTP_FD_HERE), &readfds))
  161                 return TRUE;
  162 #   endif /* 0 */
  163         }
  164 
  165     }
  166 }
  167 #endif /* NNTP_ABLE && HAVE_SELECT */
  168 
  169 
  170 /*
  171  * Support routine to read a fixed size buffer. This does most of the
  172  * hard work for tin_fgets()
  173  */
  174 static t_bool partial_read;
  175 
  176 
  177 static char *
  178 tin_read(
  179     char *buffer,
  180     size_t len,
  181     FILE *fp,
  182     t_bool header)
  183 {
  184     char *ptr;
  185     int c;
  186     int i;
  187 #ifdef NNTP_ABLE
  188     t_bool check_dot_only_line;
  189 
  190     /*
  191      * We have to check '.' line when reading via NNTP and
  192      * reading first line.
  193      */
  194     check_dot_only_line = (header && fp == FAKE_NNTP_FP && partial_read == FALSE);
  195 #endif /* NNTP_ABLE */
  196 
  197     partial_read = FALSE;
  198 
  199 #ifdef NNTP_ABLE
  200 #   ifdef HAVE_SELECT
  201     if (wait_for_input()) {         /* Check if okay to read */
  202         info_message(_("Aborting read, please wait..."));
  203         drain_buffer(fp);
  204         clear_message();
  205         tin_errno = TIN_ABORT;
  206         /* fflush(stdin); */
  207         return NULL;
  208     }
  209 #   endif /* HAVE_SELECT */
  210 
  211     errno = 0;      /* To check errno after read, clear it here */
  212 
  213     /*
  214      * Initially try and fit into supplied buffer
  215      */
  216     if (fp == FAKE_NNTP_FP)
  217         ptr = get_server(buffer, len);
  218     else
  219         ptr = fgets(buffer, len, fp);
  220 #else
  221     errno = 0;      /* To check errno after read, clear it here */
  222 
  223     ptr = fgets(buffer, len, fp);
  224 #endif /* NNTP_ABLE */
  225 
  226 /* TODO: develop this next line? */
  227 #ifdef DEBUG
  228     if (errno && (debug & DEBUG_MISC))
  229         fprintf(stderr, "tin_read(%s)", strerror(errno));
  230 #endif /* DEBUG */
  231 
  232     if (ptr == 0)   /* End of data? */
  233         return NULL;
  234 
  235     /*
  236      * Was this only a partial read?
  237      * We strip trailing \r and \n here and here _only_
  238      * 'offset' is the # of chars which we read now
  239      */
  240     i = strlen(buffer);
  241     if (i >= 1 && buffer[i - 1] == '\n') {
  242 
  243         if (i >= 2 && buffer[i - 2] == '\r') {
  244             buffer[i - 2] = '\0';
  245             offset = i -= 2;
  246         } else {
  247             buffer[i - 1] = '\0';
  248             offset = --i;
  249         }
  250 
  251         /*
  252          * If we're looking for continuation headers, mark this as a partial
  253          * read and put back a newline. Unfolding (removing of this newline
  254          * and whitespace, if necessary) must be done at a higher level --
  255          * there are headers where whitespace is significant even in folded
  256          * lines.
  257          */
  258 #ifdef NNTP_ABLE
  259         if (check_dot_only_line && i == 1 && buffer[0] == '.') { /* EMPTY */
  260             /* Find a terminator, don't check next line. */
  261         } else
  262 #endif /* NNTP_ABLE */
  263         {
  264             if (header) {
  265                 if (!i) { /* EMPTY */
  266                     /* Find a header separator, don't check next line. */
  267                 } else {
  268                     if ((c = fgetc(get_nntp_fp(fp))) == ' ' || c == '\t') {
  269                         partial_read = TRUE;
  270                         /* This is safe because we removed at least one char above */
  271                         buffer[offset++] = '\n';
  272                         buffer[offset] = '\0';
  273                     }
  274 
  275                     /* Push back the 1st char of next line */
  276                     if (c != EOF)
  277                         ungetc(c, get_nntp_fp(fp));
  278                 }
  279             }
  280         }
  281     } else {
  282         partial_read = TRUE;
  283         offset = i;
  284     }
  285 
  286     return buffer;
  287 }
  288 
  289 
  290 /*
  291  * This is the main routine for reading news data from local spool or NNTP.
  292  * It can handle arbitrary length lines of data, failed connections and
  293  * user initiated aborts (where possible)
  294  *
  295  * We simply request data from an fd and data is read up to the next \n
  296  * Any trailing \r and \n will be stripped.
  297  * If fp is FAKE_NNTP_FP, then we are reading via a socket to an NNTP
  298  * server. The required post-processing of the data will be done such that
  299  * we look like a local read to the calling function.
  300  *
  301  * Header lines: If header is TRUE, then we assume we're reading a news
  302  * article header. In some cases, article headers are split over multiple
  303  * lines. The rule is that if the next line starts with \t or ' ', then it
  304  * will be included as part of the current line. Line breaks are NOT
  305  * stripped (but replaced by \n) in continuated lines except the trailing
  306  * one; unfolding MUST be done at a higher level because it may be
  307  * significant or not.
  308  *
  309  * Dynamic read code based on code by <emcmanus@gr.osf.org>
  310  *
  311  * Caveat: We try to keep the code path for a trivial read as short as
  312  * possible.
  313  */
  314 char *
  315 tin_fgets(
  316     FILE *fp,
  317     t_bool header)
  318 {
  319     static char *dynbuf = NULL;
  320     static int size = 0;
  321 
  322     int next;
  323 
  324     tin_errno = 0;                  /* Clear errors */
  325     partial_read = FALSE;
  326 
  327 #if 1
  328     if (fp == NULL) {
  329         FreeAndNull(dynbuf);
  330         return NULL;
  331     }
  332     /* Allocate initial buffer */
  333     if (dynbuf == NULL) {
  334         dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  335         size = INIT;
  336     }
  337     /* Otherwise reuse last buffer */
  338     /* TODO: Should we free too large buffer? */
  339 #else
  340     FreeIfNeeded(dynbuf);   /* Free any previous allocation */
  341     dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  342     size = INIT;
  343 #endif /* 1 */
  344 
  345     if (tin_read(dynbuf, size, fp, header) == NULL)
  346         return NULL;
  347 
  348     if (tin_errno != 0) {
  349         DEBUG_IO((stderr, _("Aborted read\n")));
  350         return NULL;
  351     }
  352 
  353     next = offset;
  354 
  355     while (partial_read) {
  356         if (next + RCHUNK > size)
  357             size = next + RCHUNK;
  358         dynbuf = my_realloc(dynbuf, size * sizeof(*dynbuf));
  359         (void) tin_read(dynbuf + next, size - next, fp, header); /* What if == NULL? */
  360         next += offset;
  361 
  362         if (tin_errno != 0)
  363             return NULL;
  364     }
  365 
  366     /*
  367      * Do processing of leading . for NNTP
  368      * This only occurs at the start of lines
  369      * At this point, dynbuf won't be NULL
  370      */
  371 #ifdef NNTP_ABLE
  372     if (fp == FAKE_NNTP_FP) {
  373         if (dynbuf[0] == '.') {         /* reduce leading .'s */
  374             if (dynbuf[1] == '\0') {
  375                 DEBUG_IO((stderr, "tin_fgets(NULL)\n"));
  376                 return NULL;
  377             }
  378             DEBUG_IO((stderr, "tin_fgets(%s)\n", dynbuf + 1));
  379             return (dynbuf + 1);
  380         }
  381     }
  382 #endif /* NNTP_ABLE */
  383 
  384 DEBUG_IO((stderr, "tin_fgets(%s)\n", (dynbuf) ? dynbuf : "NULL"));
  385 
  386     return dynbuf;
  387 }
  388 
  389 
  390 /*
  391  * We can't just stop reading a socket once we are through with it. This
  392  * drains out any pending data on the NNTP port
  393  */
  394 #ifdef NNTP_ABLE
  395 void
  396 drain_buffer(
  397     FILE *fp)
  398 {
  399     int i = 0;
  400 
  401     if (fp != FAKE_NNTP_FP)
  402         return;
  403 
  404 DEBUG_IO((stderr, _("Draining\n")));
  405     while (tin_fgets(fp, FALSE) != NULL) {
  406         if (++i % MODULO_COUNT_NUM == 0)
  407             spin_cursor();
  408     }
  409 }
  410 #endif /* NNTP_ABLE */
  411 
  412 /* end of read.c */