(linenum→info "unix/slp.c:2238")

glibc/2.7/argp/argp-fmtstream.c

    1: /* Word-wrapping and line-truncating streams
    2:    Copyright (C) 1997-1999,2001,2002,2003,2005 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4:    Written by Miles Bader <miles@gnu.ai.mit.edu>.
    5: 
    6:    The GNU C Library is free software; you can redistribute it and/or
    7:    modify it under the terms of the GNU Lesser General Public
    8:    License as published by the Free Software Foundation; either
    9:    version 2.1 of the License, or (at your option) any later version.
   10: 
   11:    The GNU C Library is distributed in the hope that it will be useful,
   12:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14:    Lesser General Public License for more details.
   15: 
   16:    You should have received a copy of the GNU Lesser General Public
   17:    License along with the GNU C Library; if not, write to the Free
   18:    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   19:    02111-1307 USA.  */
   20: 
   21: /* This package emulates glibc `line_wrap_stream' semantics for systems that
   22:    don't have that.  */
   23: 
   24: #ifdef HAVE_CONFIG_H
   25: # include <config.h>
   26: #endif
   27: 
   28: #include <stdlib.h>
   29: #include <string.h>
   30: #include <errno.h>
   31: #include <stdarg.h>
   32: #include <ctype.h>
   33: 
   34: #include "argp-fmtstream.h"
   35: #include "argp-namefrob.h"
   36: 
   37: #ifndef ARGP_FMTSTREAM_USE_LINEWRAP
   38: 
   39: #ifndef isblank
   40: #define isblank(ch) ((ch)==' ' || (ch)=='\t')
   41: #endif
   42: 
   43: #if defined _LIBC && defined USE_IN_LIBIO
   44: # include <wchar.h>
   45: # include <libio/libioP.h>
   46: # define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
   47: #endif
   48: 
   49: #define INIT_BUF_SIZE 200
   50: #define PRINTF_SIZE_GUESS 150
   51: ^L
   52: /* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines
   53:    written on it with LMARGIN spaces and limits them to RMARGIN columns
   54:    total.  If WMARGIN >= 0, words that extend past RMARGIN are wrapped by
   55:    replacing the whitespace before them with a newline and WMARGIN spaces.
   56:    Otherwise, chars beyond RMARGIN are simply dropped until a newline.
   57:    Returns NULL if there was an error.  */
   58: argp_fmtstream_t
   59: __argp_make_fmtstream (FILE *stream,
   60:                        size_t lmargin, size_t rmargin, ssize_t wmargin)
   61: {
   62:   argp_fmtstream_t fs;
   63: 
   64:   fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
   65:   if (fs != NULL)
   66:     {
   67:       fs->stream = stream;
   68: 
   69:       fs->lmargin = lmargin;
   70:       fs->rmargin = rmargin;
   71:       fs->wmargin = wmargin;
   72:       fs->point_col = 0;
   73:       fs->point_offs = 0;
   74: 
   75:       fs->buf = (char *) malloc (INIT_BUF_SIZE);
   76:       if (! fs->buf)
   77:         {
   78:           free (fs);
   79:           fs = 0;
   80:         }
   81:       else
   82:         {
   83:           fs->p = fs->buf;
   84:           fs->end = fs->buf + INIT_BUF_SIZE;
   85:         }
   86:     }
   87: 
   88:   return fs;
   89: }
   90: #if 0
   91: /* Not exported.  */
   92: #ifdef weak_alias
   93: weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
   94: #endif
   95: #endif
   96: 
   97: /* Flush FS to its stream, and free it (but don't close the stream).  */
   98: void
   99: __argp_fmtstream_free (argp_fmtstream_t fs)
  100: {
  101:   __argp_fmtstream_update (fs);
  102:   if (fs->p > fs->buf)
  103:     {
  104: #ifdef USE_IN_LIBIO
  105:       __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
  106: #else
  107:       fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
  108: #endif
  109:     }
  110:   free (fs->buf);
  111:   free (fs);
  112: }
  113: #if 0
  114: /* Not exported.  */
  115: #ifdef weak_alias
  116: weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
  117: #endif
  118: #endif
  119: ^L
  120: /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
  121:    end of its buffer.  This code is mostly from glibc stdio/linewrap.c.  */
  122: void
  123: __argp_fmtstream_update (argp_fmtstream_t fs)
  124: {
  125:   char *buf, *nl;
  126:   size_t len;
  127: 
  128:   /* Scan the buffer for newlines.  */
  129:   buf = fs->buf + fs->point_offs;
  130:   while (buf < fs->p)
  131:     {
  132:       size_t r;
  133: 
  134:       if (fs->point_col == 0 && fs->lmargin != 0)
  135:         {
  136:           /* We are starting a new line.  Print spaces to the left margin.  */
  137:           const size_t pad = fs->lmargin;
  138:           if (fs->p + pad < fs->end)
  139:             {
  140:               /* We can fit in them in the buffer by moving the
  141:                  buffer text up and filling in the beginning.  */
  142:               memmove (buf + pad, buf, fs->p - buf);
  143:               fs->p += pad; /* Compensate for bigger buffer. */
  144:               memset (buf, ' ', pad); /* Fill in the spaces.  */
  145:               buf += pad; /* Don't bother searching them.  */
  146:             }
  147:           else
  148:             {
  149:               /* No buffer space for spaces.  Must flush.  */
  150:               size_t i;
  151:               for (i = 0; i < pad; i++)
  152:                 {
  153: #ifdef USE_IN_LIBIO
  154:                   if (_IO_fwide (fs->stream, 0) > 0)
  155:                     putwc_unlocked (L' ', fs->stream);
  156:                   else
  157: #endif
  158:                     putc_unlocked (' ', fs->stream);
  159:                 }
  160:             }
  161:           fs->point_col = pad;
  162:         }
  163: 
  164:       len = fs->p - buf;
  165:       nl = memchr (buf, '\n', len);
  166: 
  167:       if (fs->point_col < 0)
  168:         fs->point_col = 0;
  169: 
  170:       if (!nl)
  171:         {
  172:           /* The buffer ends in a partial line.  */
  173: 
  174:           if (fs->point_col + len < fs->rmargin)
  175:             {
  176:               /* The remaining buffer text is a partial line and fits
  177:                  within the maximum line width.  Advance point for the
  178:                  characters to be written and stop scanning.  */
  179:               fs->point_col += len;
  180:               break;
  181:             }
  182:           else
  183:             /* Set the end-of-line pointer for the code below to
  184:                the end of the buffer.  */
  185:             nl = fs->p;
  186:         }
  187:       else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
  188:         {
  189:           /* The buffer contains a full line that fits within the maximum
  190:              line width.  Reset point and scan the next line.  */
  191:           fs->point_col = 0;
  192:           buf = nl + 1;
  193:           continue;
  194:         }
  195: 
  196:       /* This line is too long.  */
  197:       r = fs->rmargin - 1;
  198: 
  199:       if (fs->wmargin < 0)
  200:         {
  201:           /* Truncate the line by overwriting the excess with the
  202:              newline and anything after it in the buffer.  */
  203:           if (nl < fs->p)
  204:             {
  205:               memmove (buf + (r - fs->point_col), nl, fs->p - nl);
  206:               fs->p -= buf + (r - fs->point_col) - nl;
  207:               /* Reset point for the next line and start scanning it.  */
  208:               fs->point_col = 0;
  209:               buf += r + 1; /* Skip full line plus \n. */
  210:             }
  211:           else
  212:             {
  213:               /* The buffer ends with a partial line that is beyond the
  214:                  maximum line width.  Advance point for the characters
  215:                  written, and discard those past the max from the buffer.  */
  216:               fs->point_col += len;
  217:               fs->p -= fs->point_col - r;
  218:               break;
  219:             }
  220:         }
  221:       else
  222:         {
  223:           /* Do word wrap.  Go to the column just past the maximum line
  224:              width and scan back for the beginning of the word there.
  225:              Then insert a line break.  */
  226: 
  227:           char *p, *nextline;
  228:           int i;
  229: 
  230:           p = buf + (r + 1 - fs->point_col);
  231:           while (p >= buf && !isblank (*p))
  232:             --p;
  233:           nextline = p + 1;    /* This will begin the next line.  */
  234: 
  235:           if (nextline > buf)
  236:             {
  237:               /* Swallow separating blanks.  */
  238:               if (p >= buf)
  239:                 do
  240:                   --p;
  241:                 while (p >= buf && isblank (*p));
  242:               nl = p + 1;      /* The newline will replace the first blank. */
  243:             }
  244:           else
  245:             {
  246:               /* A single word that is greater than the maximum line width.
  247:                  Oh well.  Put it on an overlong line by itself.  */
  248:               p = buf + (r + 1 - fs->point_col);
  249:               /* Find the end of the long word.  */
  250:               do
  251:                 ++p;
  252:               while (p < nl && !isblank (*p));
  253:               if (p == nl)
  254:                 {
  255:                   /* It already ends a line.  No fussing required.  */
  256:                   fs->point_col = 0;
  257:                   buf = nl + 1;
  258:                   continue;
  259:                 }
  260:               /* We will move the newline to replace the first blank.  */
  261:               nl = p;
  262:               /* Swallow separating blanks.  */
  263:               do
  264:                 ++p;
  265:               while (isblank (*p));
  266:               /* The next line will start here.  */
  267:               nextline = p;
  268:             }
  269: 
  270:           /* Note: There are a bunch of tests below for
  271:              NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall
  272:              at the end of the buffer, and NEXTLINE is in fact empty (and so
  273:              we need not be careful to maintain its contents).  */
  274: 
  275:           if ((nextline == buf + len + 1
  276:                ? fs->end - nl < fs->wmargin + 1
  277:                : nextline - (nl + 1) < fs->wmargin)
  278:               && fs->p > nextline)
  279:             {
  280:               /* The margin needs more blanks than we removed.  */
  281:               if (fs->end - fs->p > fs->wmargin + 1)
  282:                 /* Make some space for them.  */
  283:                 {
  284:                   size_t mv = fs->p - nextline;
  285:                   memmove (nl + 1 + fs->wmargin, nextline, mv);
  286:                   nextline = nl + 1 + fs->wmargin;
  287:                   len = nextline + mv - buf;
  288:                   *nl++ = '\n';
  289:                 }
  290:               else
  291:                 /* Output the first line so we can use the space.  */
  292:                 {
  293: #ifdef _LIBC
  294:                   __fxprintf (fs->stream, "%.*s\n",
  295:                               (int) (nl - fs->buf), fs->buf);
  296: #else
  297:                   if (nl > fs->buf)
  298:                     fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
  299:                   putc_unlocked ('\n', fs->stream);
  300: #endif
  301: 
  302:                   len += buf - fs->buf;
  303:                   nl = buf = fs->buf;
  304:                 }
  305:             }
  306:           else
  307:             /* We can fit the newline and blanks in before
  308:                the next word.  */
  309:             *nl++ = '\n';
  310: 
  311:           if (nextline - nl >= fs->wmargin
  312:               || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin))
  313:             /* Add blanks up to the wrap margin column.  */
  314:             for (i = 0; i < fs->wmargin; ++i)
  315:               *nl++ = ' ';
  316:           else
  317:             for (i = 0; i < fs->wmargin; ++i)
  318: #ifdef USE_IN_LIBIO
  319:               if (_IO_fwide (fs->stream, 0) > 0)
  320:                 putwc_unlocked (L' ', fs->stream);
  321:               else
  322: #endif
  323:                 putc_unlocked (' ', fs->stream);
  324: 
  325:           /* Copy the tail of the original buffer into the current buffer
  326:              position.  */
  327:           if (nl < nextline)
  328:             memmove (nl, nextline, buf + len - nextline);
  329:           len -= nextline - buf;
  330: 
  331:           /* Continue the scan on the remaining lines in the buffer.  */
  332:           buf = nl;
  333: 
  334:           /* Restore bufp to include all the remaining text.  */
  335:           fs->p = nl + len;
  336: 
  337:           /* Reset the counter of what has been output this line.  If wmargin
  338:              is 0, we want to avoid the lmargin getting added, so we set
  339:              point_col to a magic value of -1 in that case.  */
  340:           fs->point_col = fs->wmargin ? fs->wmargin : -1;
  341:         }
  342:     }
  343: 
  344:   /* Remember that we've scanned as far as the end of the buffer.  */
  345:   fs->point_offs = fs->p - fs->buf;
  346: }
  347: ^L
  348: /* Ensure that FS has space for AMOUNT more bytes in its buffer, either by
  349:    growing the buffer, or by flushing it.  True is returned iff we succeed. */
  350: int
  351: __argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
  352: {
  353:   if ((size_t) (fs->end - fs->p) < amount)
  354:     {
  355:       ssize_t wrote;
  356: 
  357:       /* Flush FS's buffer.  */
  358:       __argp_fmtstream_update (fs);
  359: 
  360: #ifdef _LIBC
  361:       __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
  362:       wrote = fs->p - fs->buf;
  363: #else
  364:       wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
  365: #endif
  366:       if (wrote == fs->p - fs->buf)
  367:         {
  368:           fs->p = fs->buf;
  369:           fs->point_offs = 0;
  370:         }
  371:       else
  372:         {
  373:           fs->p -= wrote;
  374:           fs->point_offs -= wrote;
  375:           memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
  376:           return 0;
  377:         }
  378: 
  379:       if ((size_t) (fs->end - fs->buf) < amount)
  380:         /* Gotta grow the buffer.  */
  381:         {
  382:           size_t old_size = fs->end - fs->buf;
  383:           size_t new_size = old_size + amount;
  384:           char *new_buf;
  385: 
  386:           if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
  387:             {
  388:               __set_errno (ENOMEM);
  389:               return 0;
  390:             }
  391: 
  392:           fs->buf = new_buf;
  393:           fs->end = new_buf + new_size;
  394:           fs->p = fs->buf;
  395:         }
  396:     }
  397: 
  398:   return 1;
  399: }
  400: ^L
  401: ssize_t
  402: __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
  403: {
  404:   int out;
  405:   size_t avail;
  406:   size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
  407: 
  408:   do
  409:     {
  410:       va_list args;
  411: 
  412:       if (! __argp_fmtstream_ensure (fs, size_guess))
  413:         return -1;
  414: 
  415:       va_start (args, fmt);
  416:       avail = fs->end - fs->p;
  417:       out = __vsnprintf (fs->p, avail, fmt, args);
  418:       va_end (args);
  419:       if ((size_t) out >= avail)
  420:         size_guess = out + 1;
  421:     }
  422:   while ((size_t) out >= avail);
  423: 
  424:   fs->p += out;
  425: 
  426:   return out;
  427: }
  428: #if 0
  429: /* Not exported.  */
  430: #ifdef weak_alias
  431: weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
  432: #endif
  433: #endif
  434: 
  435: #endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */
Syntax (Markdown)