"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/read.c" (9 Dec 2022, 10778 Bytes) of package /linux/misc/tin-2.6.2.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.6.1_vs_2.6.2.

    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   : 2022-09-19
    7  *
    8  * Copyright (c) 1997-2023 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, (int) len);
  220     else
  221         ptr = fgets(buffer, (int) 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 = (int) 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 #ifdef NNTP_ABLE
  271                     if (fp == FAKE_NNTP_FP)
  272                         c = fgetc_server(fp);
  273                     else
  274 #endif /* NNTP_ABLE */
  275                         c = fgetc(fp);
  276                     if (c == ' ' || c == '\t') {
  277                         partial_read = TRUE;
  278                         /* This is safe because we removed at least one char above */
  279                         buffer[offset++] = '\n';
  280                         buffer[offset] = '\0';
  281                     }
  282 
  283                     /* Push back the 1st char of next line */
  284                     if (c != EOF) {
  285 #ifdef NNTP_ABLE
  286                         if (fp == FAKE_NNTP_FP)
  287                             ungetc_server(c, fp);
  288                         else
  289 #endif /* NNTP_ABLE */
  290                             ungetc(c, fp);
  291                     }
  292                 }
  293             }
  294         }
  295     } else {
  296         partial_read = TRUE;
  297         offset = i;
  298     }
  299 
  300     return buffer;
  301 }
  302 
  303 
  304 /*
  305  * This is the main routine for reading news data from local spool or NNTP.
  306  * It can handle arbitrary length lines of data, failed connections and
  307  * user initiated aborts (where possible)
  308  *
  309  * We simply request data from an fd and data is read up to the next \n
  310  * Any trailing \r and \n will be stripped.
  311  * If fp is FAKE_NNTP_FP, then we are reading via a socket to an NNTP
  312  * server. The required post-processing of the data will be done such that
  313  * we look like a local read to the calling function.
  314  *
  315  * Header lines: If header is TRUE, then we assume we're reading a news
  316  * article header. In some cases, article headers are split over multiple
  317  * lines. The rule is that if the next line starts with \t or ' ', then it
  318  * will be included as part of the current line. Line breaks are NOT
  319  * stripped (but replaced by \n) in continuated lines except the trailing
  320  * one; unfolding MUST be done at a higher level because it may be
  321  * significant or not.
  322  *
  323  * Dynamic read code based on code by <emcmanus@gr.osf.org>
  324  *
  325  * Caveat: We try to keep the code path for a trivial read as short as
  326  * possible.
  327  */
  328 char *
  329 tin_fgets(
  330     FILE *fp,
  331     t_bool header)
  332 {
  333     static char *dynbuf = NULL;
  334     static int size = 0;
  335 
  336     int next;
  337 
  338     tin_errno = 0;                  /* Clear errors */
  339     partial_read = FALSE;
  340 
  341 #if 1
  342     if (fp == NULL) {
  343         FreeAndNull(dynbuf);
  344         return NULL;
  345     }
  346     /* Allocate initial buffer */
  347     if (dynbuf == NULL) {
  348         dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  349         size = INIT;
  350     }
  351     /* Otherwise reuse last buffer */
  352     /* TODO: Should we free too large buffer? */
  353 #else
  354     FreeIfNeeded(dynbuf);   /* Free any previous allocation */
  355     dynbuf = my_malloc(INIT * sizeof(*dynbuf));
  356     size = INIT;
  357 #endif /* 1 */
  358 
  359     if (tin_read(dynbuf, (size_t) size, fp, header) == NULL)
  360         return NULL;
  361 
  362     if (tin_errno != 0) {
  363         DEBUG_IO((stderr, _("Aborted read\n")));
  364         return NULL;
  365     }
  366 
  367     next = offset;
  368 
  369     while (partial_read) {
  370         if (next + RCHUNK > size)
  371             size = next + RCHUNK;
  372         dynbuf = my_realloc(dynbuf, (size_t) size * sizeof(*dynbuf));
  373         (void) tin_read(dynbuf + next, (size_t) (size - next), fp, header); /* What if == NULL? */
  374         next += offset;
  375 
  376         if (tin_errno != 0)
  377             return NULL;
  378     }
  379 
  380     /*
  381      * Do processing of leading . for NNTP
  382      * This only occurs at the start of lines
  383      * At this point, dynbuf won't be NULL
  384      */
  385 #ifdef NNTP_ABLE
  386     if (fp == FAKE_NNTP_FP) {
  387         if (dynbuf[0] == '.') {         /* reduce leading .'s */
  388             if (dynbuf[1] == '\0') {
  389                 DEBUG_IO((stderr, "tin_fgets(NULL)\n"));
  390                 return NULL;
  391             }
  392             DEBUG_IO((stderr, "tin_fgets(%s)\n", dynbuf + 1));
  393             return (dynbuf + 1);
  394         }
  395     }
  396 #endif /* NNTP_ABLE */
  397 
  398 DEBUG_IO((stderr, "tin_fgets(%s)\n", (dynbuf) ? dynbuf : "NULL"));
  399 
  400     return dynbuf;
  401 }
  402 
  403 
  404 /*
  405  * We can't just stop reading a socket once we are through with it. This
  406  * drains out any pending data on the NNTP port
  407  */
  408 #ifdef NNTP_ABLE
  409 void
  410 drain_buffer(
  411     FILE *fp)
  412 {
  413     int i = 0;
  414 
  415     if (fp != FAKE_NNTP_FP)
  416         return;
  417 
  418 DEBUG_IO((stderr, _("Draining\n")));
  419     while (tin_fgets(fp, FALSE) != NULL) {
  420         if (++i % MODULO_COUNT_NUM == 0)
  421             spin_cursor();
  422     }
  423 }
  424 #endif /* NNTP_ABLE */
  425 
  426 /* end of read.c */