"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.4/src/read.c" (20 Nov 2019, 10693 Bytes) of package /linux/misc/tin-2.4.4.tar.xz:


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.3_vs_2.4.4.

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