"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.5/src/read.c" (1 Dec 2020, 10523 Bytes) of package /linux/misc/tin-2.4.5.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.4_vs_2.4.5.

    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   : 2020-05-27
    7  *
    8  * Copyright (c) 1997-2021 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 
   69 
   70 #ifdef HAVE_SELECT
   71 /*
   72  * Used by the I/O read routine to look for keyboard input
   73  * Returns TRUE if user aborted with 'q' or 'z' (lynx-style)
   74  *         FALSE otherwise
   75  * TODO: document 'z' (, and allow it's remapping?) and 'Q'
   76  *       add a !HAVE_SELECT code path
   77  */
   78 t_bool
   79 wait_for_input(
   80     void)
   81 {
   82     int nfds, ch;
   83     fd_set readfds;
   84     struct timeval tv;
   85 
   86     /*
   87      * Main loop. Wait for input from keyboard or file or for a timeout.
   88      */
   89     forever {
   90         FD_ZERO(&readfds);
   91         FD_SET(STDIN_FILENO, &readfds);
   92 /*      FD_SET(fileno(NEED_REAL_NNTP_FD_HERE), &readfds); */
   93 
   94         tv.tv_sec = 0;      /* NNTP_READ_TIMEOUT; */
   95         tv.tv_usec = 0;
   96 
   97 /* DEBUG_IO((stderr, "waiting on %d and %d...", STDIN_FILENO, fileno(fd))); */
   98 #   ifdef HAVE_SELECT_INTP
   99         if ((nfds = select(STDIN_FILENO + 1, (int *) &readfds, NULL, NULL, &tv)) == -1)
  100 #   else
  101         if ((nfds = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv)) == -1)
  102 #   endif /* HAVE_SELECT_INTP */
  103         {
  104             if (errno != EINTR) {
  105                 perror_message("select() failed");
  106                 free(tin_progname);
  107                 giveup();
  108             } else
  109                 return FALSE;
  110         }
  111 
  112         /* No input pending */
  113         if (nfds == 0)
  114             return FALSE;
  115 
  116         /*
  117          * Something is waiting. See what's cooking...
  118          */
  119         if (nfds > 0) {
  120             /*
  121              * User pressed something. If 'q'uit, then handle this. Process
  122              * user input 1st so they get chance to quit on busy (or stalled)
  123              * reads
  124              */
  125             if (FD_ISSET(STDIN_FILENO, &readfds)) {
  126                 if ((ch = ReadCh()) == EOF)
  127                     return FALSE;
  128 
  129                 /*
  130                  * check if keymap key was pressed. Unfortunately, keymap keys
  131                  * are encoded by a sequence of bytes starting with ESC, so we
  132                  * must first see if it was really an ESC or a keymap key before
  133                  * asking the user if he wants to abort.
  134                  */
  135                 if (ch == iKeyAbort) {
  136                     int keymap_ch = get_arrow_key(ch);
  137 
  138                     if (keymap_ch != KEYMAP_UNKNOWN)
  139                         ch = keymap_ch;
  140                 }
  141 
  142                 if (ch == iKeyQuit || ch == 'z' || ch == iKeyAbort) {
  143                     if (post_article_and_exit)
  144                         return FALSE;
  145                     if (prompt_yn(_(txt_read_abort), FALSE) == 1)
  146                         return TRUE;
  147                 }
  148 
  149                 if (ch == iKeyQuitTin) {
  150                     if (post_article_and_exit)
  151                         return FALSE;
  152                     if (prompt_yn(_(txt_read_exit), FALSE) == 1)
  153                         tin_done(EXIT_SUCCESS, NULL);
  154                 }
  155 
  156             }
  157 
  158 #   if 0
  159             /*
  160              * Our file has something for us to read
  161              */
  162             if (FD_ISSET(fileno(NEED_NNTP_FD_HERE), &readfds))
  163                 return TRUE;
  164 #   endif /* 0 */
  165         }
  166 
  167     }
  168 }
  169 #endif /* HAVE_SELECT */
  170 
  171 
  172 /*
  173  * Support routine to read a fixed size buffer. This does most of the
  174  * hard work for tin_fgets()
  175  */
  176 static t_bool partial_read;
  177 
  178 
  179 static char *
  180 tin_read(
  181     char *buffer,
  182     size_t len,
  183     FILE *fp,
  184     t_bool header)
  185 {
  186     char *ptr;
  187     int c;
  188     int i;
  189 #ifdef NNTP_ABLE
  190     t_bool check_dot_only_line;
  191 
  192     /*
  193      * We have to check '.' line when reading via NNTP and
  194      * reading first line.
  195      */
  196     check_dot_only_line = (header && fp == FAKE_NNTP_FP && partial_read == FALSE);
  197 #endif /* NNTP_ABLE */
  198 
  199     partial_read = FALSE;
  200 
  201 #ifdef NNTP_ABLE
  202 #   ifdef HAVE_SELECT
  203     if (wait_for_input()) {         /* Check if okay to read */
  204         info_message(_("Aborting read, please wait..."));
  205         drain_buffer(fp);
  206         clear_message();
  207         tin_errno = TIN_ABORT;
  208         /* fflush(stdin); */
  209         return NULL;
  210     }
  211 #   endif /* HAVE_SELECT */
  212 
  213     errno = 0;      /* To check errno after read, clear it here */
  214 
  215     /*
  216      * Initially try and fit into supplied buffer
  217      */
  218     if (fp == FAKE_NNTP_FP)
  219         ptr = get_server(buffer, len);
  220     else
  221         ptr = fgets(buffer, len, fp);
  222 #else
  223     errno = 0;      /* To check errno after read, clear it here */
  224 
  225     ptr = fgets(buffer, len, fp);
  226 #endif /* NNTP_ABLE */
  227 
  228 /* TODO: develop this next line? */
  229 #ifdef DEBUG
  230     if (errno && (debug & DEBUG_MISC))
  231         fprintf(stderr, "tin_read(%s)", strerror(errno));
  232 #endif /* DEBUG */
  233 
  234     if (ptr == NULL)    /* End of data? */
  235         return NULL;
  236 
  237     /*
  238      * Was this only a partial read?
  239      * We strip trailing \r and \n here and here _only_
  240      * 'offset' is the # of chars which we read now
  241      */
  242     i = strlen(buffer);
  243     if (i >= 1 && buffer[i - 1] == '\n') {
  244 
  245         if (i >= 2 && buffer[i - 2] == '\r') {
  246             buffer[i - 2] = '\0';
  247             offset = i -= 2;
  248         } else {
  249             buffer[i - 1] = '\0';
  250             offset = --i;
  251         }
  252 
  253         /*
  254          * If we're looking for continuation headers, mark this as a partial
  255          * read and put back a newline. Unfolding (removing of this newline
  256          * and whitespace, if necessary) must be done at a higher level --
  257          * there are headers where whitespace is significant even in folded
  258          * lines.
  259          */
  260 #ifdef NNTP_ABLE
  261         if (check_dot_only_line && i == 1 && buffer[0] == '.') { /* EMPTY */
  262             /* Find a terminator, don't check next line. */
  263         } else
  264 #endif /* NNTP_ABLE */
  265         {
  266             if (header) {
  267                 if (!i) { /* EMPTY */
  268                     /* Find a header separator, don't check next line. */
  269                 } else {
  270                     if ((c = fgetc(get_nntp_fp(fp))) == ' ' || c == '\t') {
  271                         partial_read = TRUE;
  272                         /* This is safe because we removed at least one char above */
  273                         buffer[offset++] = '\n';
  274                         buffer[offset] = '\0';
  275                     }
  276 
  277                     /* Push back the 1st char of next line */
  278                     if (c != EOF)
  279                         ungetc(c, get_nntp_fp(fp));
  280                 }
  281             }
  282         }
  283     } else {
  284         partial_read = TRUE;
  285         offset = i;
  286     }
  287 
  288     return buffer;
  289 }
  290 
  291 
  292 /*
  293  * This is the main routine for reading news data from local spool or NNTP.
  294  * It can handle arbitrary length lines of data, failed connections and
  295  * user initiated aborts (where possible)
  296  *
  297  * We simply request data from an fd and data is read up to the next \n
  298  * Any trailing \r and \n will be stripped.
  299  * If fp is FAKE_NNTP_FP, then we are reading via a socket to an NNTP
  300  * server. The required post-processing of the data will be done such that
  301  * we look like a local read to the calling function.
  302  *
  303  * Header lines: If header is TRUE, then we assume we're reading a news
  304  * article header. In some cases, article headers are split over multiple
  305  * lines. The rule is that if the next line starts with \t or ' ', then it
  306  * will be included as part of the current line. Line breaks are NOT
  307  * stripped (but replaced by \n) in continuated lines except the trailing
  308  * one; unfolding MUST be done at a higher level because it may be
  309  * significant or not.
  310  *
  311  * Dynamic read code based on code by <emcmanus@gr.osf.org>
  312  *
  313  * Caveat: We try to keep the code path for a trivial read as short as
  314  * possible.
  315  */
  316 char *
  317 tin_fgets(
  318     FILE *fp,
  319     t_bool header)
  320 {
  321     static char *dynbuf = NULL;
  322     static int size = 0;
  323 
  324     int next;
  325 
  326     tin_errno = 0;                  /* Clear errors */
  327     partial_read = FALSE;
  328 
  329 #if 1
  330     if (fp == NULL) {
  331         FreeAndNull(dynbuf);
  332         return NULL;
  333     }
  334     /* Allocate initial buffer */
  335     if (dynbuf == NULL) {
  336         dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  337         size = INIT;
  338     }
  339     /* Otherwise reuse last buffer */
  340     /* TODO: Should we free too large buffer? */
  341 #else
  342     FreeIfNeeded(dynbuf);   /* Free any previous allocation */
  343     dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  344     size = INIT;
  345 #endif /* 1 */
  346 
  347     if (tin_read(dynbuf, size, fp, header) == NULL)
  348         return NULL;
  349 
  350     if (tin_errno != 0) {
  351         DEBUG_IO((stderr, _("Aborted read\n")));
  352         return NULL;
  353     }
  354 
  355     next = offset;
  356 
  357     while (partial_read) {
  358         if (next + RCHUNK > size)
  359             size = next + RCHUNK;
  360         dynbuf = my_realloc(dynbuf, size * sizeof(*dynbuf));
  361         (void) tin_read(dynbuf + next, size - next, fp, header); /* What if == NULL? */
  362         next += offset;
  363 
  364         if (tin_errno != 0)
  365             return NULL;
  366     }
  367 
  368     /*
  369      * Do processing of leading . for NNTP
  370      * This only occurs at the start of lines
  371      * At this point, dynbuf won't be NULL
  372      */
  373 #ifdef NNTP_ABLE
  374     if (fp == FAKE_NNTP_FP) {
  375         if (dynbuf[0] == '.') {         /* reduce leading .'s */
  376             if (dynbuf[1] == '\0') {
  377                 DEBUG_IO((stderr, "tin_fgets(NULL)\n"));
  378                 return NULL;
  379             }
  380             DEBUG_IO((stderr, "tin_fgets(%s)\n", dynbuf + 1));
  381             return (dynbuf + 1);
  382         }
  383     }
  384 #endif /* NNTP_ABLE */
  385 
  386 DEBUG_IO((stderr, "tin_fgets(%s)\n", (dynbuf) ? dynbuf : "NULL"));
  387 
  388     return dynbuf;
  389 }
  390 
  391 
  392 /*
  393  * We can't just stop reading a socket once we are through with it. This
  394  * drains out any pending data on the NNTP port
  395  */
  396 #ifdef NNTP_ABLE
  397 void
  398 drain_buffer(
  399     FILE *fp)
  400 {
  401     int i = 0;
  402 
  403     if (fp != FAKE_NNTP_FP)
  404         return;
  405 
  406 DEBUG_IO((stderr, _("Draining\n")));
  407     while (tin_fgets(fp, FALSE) != NULL) {
  408         if (++i % MODULO_COUNT_NUM == 0)
  409             spin_cursor();
  410     }
  411 }
  412 #endif /* NNTP_ABLE */
  413 
  414 /* end of read.c */