Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gskpathparser.c

  Sprache: C
 

/*
 * Copyright © 2020 Benjamin Otte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */


#include "config.h"

#include "gskpathprivate.h"

#include "gskcurveprivate.h"
#include "gskpathbuilder.h"
#include "gskpathpoint.h"
#include "gskcontourprivate.h"

static void
skip_whitespace (const char **p)
{
  while (g_ascii_isspace (**p))
    (*p)++;
}

static void
skip_optional_comma (const char **p)
{
  skip_whitespace (p);
  if (**p == ',')
    (*p)++;
}

static gboolean
parse_number (const char **p,
              double      *c)
{
  char *e;
  *c = g_ascii_strtod (*p, &e);
  if (e == *p)
    return FALSE;
  *p = e;
  skip_optional_comma (p);
  return TRUE;
}

static gboolean
parse_coordinate (const char **p,
                  double      *c)
{
  return parse_number (p, c);
}

static gboolean
parse_coordinate_pair (const char **p,
                       double      *x,
                       double      *y)
{
  double xx, yy;
  const char *o = *p;

  if (!parse_coordinate (p, &xx))
    {
      *p = o;
      return FALSE;
    }
  if (!parse_coordinate (p, &yy))
    {
      *p = o;
      return FALSE;
    }

  *x = xx;
  *y = yy;

  return TRUE;
}

static gboolean
parse_nonnegative_number (const char **p,
                          double      *x)
{
  const char *o = *p;
  double n;

  if (!parse_number (p, &n))
    return FALSE;

  if (n < 0)
    {
      *p = o;
      return FALSE;
    }

  *x = n;

  return TRUE;
}

/* This fixes a flaw in our use of strchr() below:
 *
 * If p already points at the end of the string,
 * we misinterpret strchr ("xyz", *p) returning
 * non-NULL to mean that we can increment p.
 *
 * But strchr() will return a pointer to the
 * final NUL byte in this case, and we walk off
 * the end of the string. Oops
 */

static inline const char *
_strchr (const char *str,
         int         c)
{
  if (c == 0)
    return NULL;
  else
    return strchr (str, c);
}

static gboolean
parse_flag (const char **p,
            gboolean    *f)
{
  skip_whitespace (p);
  if (_strchr ("01", **p))
    {
      *f = **p == '1';
      (*p)++;
      skip_optional_comma (p);
      return TRUE;
    }

  return FALSE;
}

static gboolean
parse_command (const char **p,
               char        *cmd)
{
  const char *s;
  const char *allowed;

  if (*cmd == 'X')
    allowed = "mM";
  else
    allowed = "mMhHvVzZlLcCsStTqQaAoO";

  skip_whitespace (p);
  s = _strchr (allowed, **p);
  if (s)
    {
      *cmd = *s;
      (*p)++;
      return TRUE;
    }
  return FALSE;
}

static gboolean
parse_string (const char **p,
              const char  *s)

  int len = strlen (s);
  if (strncmp (*p, s, len) != 0)
    return FALSE;
  (*p) += len;
  return TRUE;
}

#define NEAR(x, y) (fabs ((x) - (y)) < 0.001)

static gboolean
is_rect (double x0, double y0,
         double x1, double y1,
         double x2, double y2,
         double x3, double y3)
{
  return NEAR (x0, x3) && NEAR (x1, x2) &&
         NEAR (y0, y1) && NEAR (y2, y3) &&
         x0 < x1 && y1 < y2;
}

static gboolean
is_line (double x0, double y0,
         double x1, double y1,
         double x2, double y2,
         double x3, double y3)
{
  if (NEAR (y0, y3))
    return x0 <= x1 && x1 <= x2 && x2 <= x3 &&
           NEAR (y0, y1) && NEAR (y0, y2) && NEAR (y0, y3);
  else
    return y0 <= y1 && y1 <= y2 && y2 <= y3 &&
           NEAR (x0, x1) && NEAR (x0, x2) && NEAR (x0, x3);
}

static gboolean
parse_rectangle (const char **p,
                 double      *x,
                 double      *y,
                 double      *w,
                 double      *h)
{
  const char *o = *p;
  double w2;

  if (parse_coordinate_pair (p, x, y) &&
      parse_string (p, "h") &&
      parse_coordinate (p, w) &&
      parse_string (p, "v") &&
      parse_coordinate (p, h) &&
      parse_string (p, "h") &&
      parse_coordinate (p, &w2) &&
      parse_string (p, "z") &&
      w2 == -*w && *w >= 0 && *h >= 0)
    {
      skip_whitespace (p);

      return TRUE;
    }

  *p = o;
  return FALSE;
}

static gboolean
parse_circle (const char **p,
              double      *cx,
              double      *cy,
              double      *r)
{
  const char *o = *p;
  double x0, y0, x1, y1, x2, y2, x3, y3;
  double x4, y4, x5, y5, x6, y6, x7, y7;
  double x8, y8, w0, w1, w2, w3;
  double rr;

  if (parse_coordinate_pair (p, &x0, &y0) &&
      parse_string (p, "o") &&
      parse_coordinate_pair (p, &x1, &y1) &&
      parse_coordinate_pair (p, &x2, &y2) &&
      parse_nonnegative_number (p, &w0) &&
      parse_string (p, "o") &&
      parse_coordinate_pair (p, &x3, &y3) &&
      parse_coordinate_pair (p, &x4, &y4) &&
      parse_nonnegative_number (p, &w1) &&
      parse_string (p, "o") &&
      parse_coordinate_pair (p, &x5, &y5) &&
      parse_coordinate_pair (p, &x6, &y6) &&
      parse_nonnegative_number (p, &w2) &&
      parse_string (p, "o") &&
      parse_coordinate_pair (p, &x7, &y7) &&
      parse_coordinate_pair (p, &x8, &y8) &&
      parse_nonnegative_number (p, &w3) &&
      parse_string (p, "z"))
    {
      rr = y1;

      if (x1 == 0   && y1 == rr  &&
          x2 == -rr && y2 == rr  &&
          x3 == -rr && y3 == 0   &&
          x4 == -rr && y4 == -rr &&
          x5 == 0   && y5 == -rr &&
          x6 == rr  && y6 == -rr &&
          x7 == rr  && y7 == 0   &&
          x8 == rr  && y8 == rr &&
          NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) &&
          NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2))
        {
          *cx = x0 - rr;
          *cy = y0;
          *r = rr;

          skip_whitespace (p);

          return TRUE;
        }
    }

  *p = o;
  return FALSE;
}

static gboolean
parse_rounded_rect (const char     **p,
                    GskRoundedRect  *rr)
{
  const char *o = *p;
  double x0, y0, x1, y1, x2, y2, x3, y3;
  double x4, y4, x5, y5, x6, y6, x7, y7;
  double x8, y8, x9, y9, x10, y10, x11, y11;
  double x12, y12, w0, w1, w2, w3;

  if (parse_coordinate_pair (p, &x0, &y0) &&
      parse_string (p, "L") &&
      parse_coordinate_pair (p, &x1, &y1) &&
      parse_string (p, "O") &&
      parse_coordinate_pair (p, &x2, &y2) &&
      parse_coordinate_pair (p, &x3, &y3) &&
      parse_nonnegative_number (p, &w0) &&
      parse_string (p, "L") &&
      parse_coordinate_pair (p, &x4, &y4) &&
      parse_string (p, "O") &&
      parse_coordinate_pair (p, &x5, &y5) &&
      parse_coordinate_pair (p, &x6, &y6) &&
      parse_nonnegative_number (p, &w1) &&
      parse_string (p, "L") &&
      parse_coordinate_pair (p, &x7, &y7) &&
      parse_string (p, "O") &&
      parse_coordinate_pair (p, &x8, &y8) &&
      parse_coordinate_pair (p, &x9, &y9) &&
      parse_nonnegative_number (p, &w2) &&
      parse_string (p, "L") &&
      parse_coordinate_pair (p, &x10, &y10) &&
      parse_string (p, "O") &&
      parse_coordinate_pair (p, &x11, &y11) &&
      parse_coordinate_pair (p, &x12, &y12) &&
      parse_nonnegative_number (p, &w3) &&
      parse_string (p, "Z"))
    {
      if (NEAR (x0, x12) && NEAR (y0, y12) &&
          is_rect (x11, y11, x2, y2, x5, y5, x8, y8) &&
          is_line (x11, y11, x0, y0, x1, y1, x2, y2) &&
          is_line (x2, y2, x3, y3, x4, y4, x5, y5) &&
          is_line (x8, y8, x7, y7, x6, y6, x5, y5) &&
          is_line (x11, y11, x10, y10, x9, y9, x8, y8) &&
          NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) &&
          NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2))
        {
          rr->bounds = GRAPHENE_RECT_INIT (x11, y11, x5 - x11, y5 - y11);
          rr->corner[GSK_CORNER_TOP_LEFT] = GRAPHENE_SIZE_INIT (x12 - x11, y10 - y11);
          rr->corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (x2 - x1, y3 - y2);
          rr->corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (x5 - x6, y5 - y4);
          rr->corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (x7 - x8, y8 - y9);

          skip_whitespace (p);

          return TRUE;
        }
    }

  *p = o;
  return FALSE;
}

#undef NEAR

static gboolean
add_op (GskPathOperation        op,
        const graphene_point_t *pts,
        size_t                  n_pts,
        float                   weight,
        gpointer                user_data)
{
  GskPathBuilder *builder = user_data;

  gsk_path_builder_add_op (builder, op, pts, n_pts, weight);
  return TRUE;
}

static gboolean
add_arc (float    rx,
         float    ry,
         float    rotation,
         gboolean large,
         gboolean sweep,
         float    x,
         float    y,
         gpointer user_data)
{
  GskPathBuilder *builder = user_data;

  gsk_path_builder_svg_arc_to (builder, rx, ry, rotation, large, sweep, x, y);
  return TRUE;
}

static gboolean
add_rect (const graphene_rect_t *rect,
          gpointer               user_data)
{
  GskPathBuilder *builder = user_data;

  gsk_path_builder_add_rect (builder, rect);
  return TRUE;
}

static gboolean
add_circle (const graphene_point_t *center,
            float                   radius,
            gpointer               user_data)
{
  GskPathBuilder *builder = user_data;

  gsk_path_builder_add_circle (builder, center, radius);
  return TRUE;
}

static gboolean
add_rounded_rect (const GskRoundedRect *rect,
                  gpointer              user_data)
{
  GskPathBuilder *builder = user_data;

  gsk_path_builder_add_rounded_rect (builder, rect);
  return TRUE;
}

/**
 * gsk_path_parse:
 * @string: a string
 *
 * Constructs a path from a serialized form.
 *
 * The string is expected to be in (a superset of)
 * [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
 * as e.g. produced by [method@Gsk.Path.to_string].
 *
 * A high-level summary of the syntax:
 *
 * - `M x y` Move to `(x, y)`
 * - `L x y` Add a line from the current point to `(x, y)`
 * - `Q x1 y1 x2 y2` Add a quadratic Bézier from the current point to `(x2, y2)`, with control point `(x1, y1)`
 * - `C x1 y1 x2 y2 x3 y3` Add a cubic Bézier from the current point to `(x3, y3)`, with control points `(x1, y1)` and `(x2, y2)`
 * - `Z` Close the contour by drawing a line back to the start point
 * - `H x` Add a horizontal line from the current point to the given x value
 * - `V y` Add a vertical line from the current point to the given y value
 * - `T x2 y2` Add a quadratic Bézier, using the reflection of the previous segments' control point as control point
 * - `S x2 y2 x3 y3` Add a cubic Bézier, using the reflection of the previous segments' second control point as first control point
 * - `A rx ry r l s x y` Add an elliptical arc from the current point to `(x, y)` with radii rx and ry. See the SVG documentation for how the other parameters influence the arc.
 * - `O x1 y1 x2 y2 w` Add a rational quadratic Bézier from the current point to `(x2, y2)` with control point `(x1, y1)` and weight `w`.
 *
 * All the commands have lowercase variants that interpret coordinates
 * relative to the current point.
 *
 * The `O` command is an extension that is not supported in SVG.
 *
 * Returns: (nullable): a new `GskPath`, or `NULL` if @string could not be parsed
 *
 * Since: 4.14
 */

GskPath *
gsk_path_parse (const char *string)
{
  GskPathParser parser = {
    add_op, add_arc, add_rect, add_circle, add_rounded_rect,
  };
  GskPathBuilder *builder;

  builder = gsk_path_builder_new ();

  if (!gsk_path_parse_full (string, &parser, builder))
    {
      gsk_path_builder_unref (builder);
      return NULL;
    }

  return gsk_path_builder_free_to_path (builder);
}

#define move_to(builder, x0, y0) \
  if (!parser->add_op (GSK_PATH_MOVE, (const graphene_point_t []) { { x0, y0 }, }, 11, builder)) \
    return FALSE;
#define close(builder) \
  if (!parser->add_op (GSK_PATH_CLOSE, \
                  (const graphene_point_t []) { \
                    { x, y }, \
                  }, \
                  01, builder)) \
    return FALSE;
#define line_to(builder, x1, y1) \
  if (!parser->add_op (GSK_PATH_LINE, \
                       (const graphene_point_t []) { \
                         { x, y }, \
                         { x1, y1 }, \
                       }, \
                       21, builder)) \
    return FALSE;
#define quad_to(builder, x1, y1, x2, y2) \
  if (!parser->add_op (GSK_PATH_QUAD, \
                  (const graphene_point_t []) { \
                    { x, y }, \
                    { x1, y1 }, \
                    { x2, y2 }, \
                  }, \
                  31, builder)) \
    return FALSE;
#define cubic_to(builder, x1, y1, x2, y2, x3, y3) \
  if (!parser->add_op (GSK_PATH_CUBIC, \
                  (const graphene_point_t []) { \
                    { x, y }, \
                    { x1, y1 }, \
                    { x2, y2 }, \
                    { x3, y3 }, \
                  }, \
                  41, builder)) \
    return FALSE;
#define conic_to(builder, x1, y1, x2, y2, w) \
  if (!parser->add_op (GSK_PATH_CONIC, \
                    (const graphene_point_t []) { \
                      { x, y }, \
                      { x1, y1 }, \
                      { x2, y2 }, \
                    }, \
                    3, w, builder)) \
    return FALSE;

gboolean
gsk_path_parse_full (const char    *string,
                     GskPathParser *parser,
                     gpointer       builder)
{
  double x, y; /* current point */
  double prev_x1, prev_y1;
  double path_x, path_y; /* start point of the current subpath */
  const char *p;
  char cmd;
  char prev_cmd;
  gboolean after_comma;
  gboolean repeat;

  cmd = 'X';
  path_x = path_y = 0;
  x = y = 0;
  prev_x1 = prev_y1 = 0;
  after_comma = FALSE;

  p = string;
  while (*p)
    {
      prev_cmd = g_ascii_toupper (cmd);
      repeat = !parse_command (&p, &cmd);

      if (after_comma && !repeat)
        return FALSE;

      switch (cmd)
        {
        case 'X':
          return FALSE;

        case 'Z':
        case 'z':
          if (repeat)
            return FALSE;
          else
            {
              close (builder);
              x = path_x;
              y = path_y;
            }
          break;

        case 'M':
        case 'm':
          {
            double x1, y1, w, h, r;
            GskRoundedRect rr;

            /* Look for special contours */
            if (parser->add_rect && parse_rectangle (&p, &x1, &y1, &w, &h))
              {
                if (cmd == 'm')
                  {
                    x1 += x;
                    y1 += y;
                  }
                if (!parser->add_rect (&GRAPHENE_RECT_INIT (x1, y1, w, h), builder))
                  return FALSE;

                path_x = x1;
                path_y = y1;

                cmd = 'z';
                x = x1;
                y = y1;
              }
            else if (parser->add_circle && parse_circle (&p, &x1, &y1, &r))
              {
                if (cmd == 'm')
                  {
                    x1 += x;
                    y1 += y;
                  }
                if (!parser->add_circle (&GRAPHENE_POINT_INIT (x1, y1), r, builder))
                  return FALSE;

                path_x = x1 + r;
                path_y = y1;

                cmd = 'z';
                x = x1 + r;
                y = y1;
              }
            else if (parser->add_rounded_rect && parse_rounded_rect (&p, &rr))
              {
                if (cmd == 'm')
                  {
                    rr.bounds.origin.x += x;
                    rr.bounds.origin.y += y;
                  }
                if (!parser->add_rounded_rect (&rr, builder))
                  return FALSE;

                path_x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width;
                path_y = rr.bounds.origin.y;

                cmd = 'Z';
                x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width;
                y = rr.bounds.origin.y;
              }
            else if (parse_coordinate_pair (&p, &x1, &y1))
              {
                if (cmd == 'm')
                  {
                    x1 += x;
                    y1 += y;
                  }

                if (repeat)
                  {
                    line_to (builder, x1, y1);
                  }
                else
                  {
                    move_to (builder, x1, y1);
                    path_x = x1;
                    path_y = y1;
                  }

                x = x1;
                y = y1;
              }
            else
              return FALSE;
          }
          break;

        case 'L':
        case 'l':
          {
            double x1, y1;

            if (parse_coordinate_pair (&p, &x1, &y1))
              {
                if (cmd == 'l')
                  {
                    x1 += x;
                    y1 += y;
                  }

                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                line_to (builder, x1, y1);
                x = x1;
                y = y1;
              }
            else
              return FALSE;
          }
          break;

        case 'H':
        case 'h':
          {
            double x1;

            if (parse_coordinate (&p, &x1))
              {
                if (cmd == 'h')
                  x1 += x;
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);

                    path_x = x;
                    path_y = y;
                  }
                line_to (builder, x1, y);
                x = x1;
              }
            else
              return FALSE;
          }
          break;

        case 'V':
        case 'v':
          {
            double y1;

            if (parse_coordinate (&p, &y1))
              {
                if (cmd == 'v')
                  y1 += y;
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);

                    path_x = x;
                    path_y = y;
                  }
                line_to (builder, x, y1);
                y = y1;
              }
            else
              return FALSE;
          }
          break;

        case 'C':
        case 'c':
          {
            double x0, y0, x1, y1, x2, y2;

            if (parse_coordinate_pair (&p, &x0, &y0) &&
                parse_coordinate_pair (&p, &x1, &y1) &&
                parse_coordinate_pair (&p, &x2, &y2))
              {
                if (cmd == 'c')
                  {
                    x0 += x;
                    y0 += y;
                    x1 += x;
                    y1 += y;
                    x2 += x;
                    y2 += y;
                  }
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                cubic_to (builder, x0, y0, x1, y1, x2, y2);
                prev_x1 = x1;
                prev_y1 = y1;
                x = x2;
                y = y2;
              }
            else
              return FALSE;
          }
          break;

        case 'S':
        case 's':
          {
            double x0, y0, x1, y1, x2, y2;

            if (parse_coordinate_pair (&p, &x1, &y1) &&
                parse_coordinate_pair (&p, &x2, &y2))
              {
                if (cmd == 's')
                  {
                    x1 += x;
                    y1 += y;
                    x2 += x;
                    y2 += y;
                  }
                if (prev_cmd == 'C' || prev_cmd == 'S')
                  {
                    x0 = 2 * x - prev_x1;
                    y0 = 2 * y - prev_y1;
                  }
                else
                  {
                    x0 = x;
                    y0 = y;
                  }
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                cubic_to (builder, x0, y0, x1, y1, x2, y2);

                prev_x1 = x1;
                prev_y1 = y1;
                x = x2;
                y = y2;
              }
            else
              return FALSE;
          }
          break;

        case 'Q':
        case 'q':
          {
            double x1, y1, x2, y2;

            if (parse_coordinate_pair (&p, &x1, &y1) &&
                parse_coordinate_pair (&p, &x2, &y2))
              {
                if (cmd == 'q')
                  {
                    x1 += x;
                    y1 += y;
                    x2 += x;
                    y2 += y;
                  }
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                quad_to (builder, x1, y1, x2, y2);
                prev_x1 = x1;
                prev_y1 = y1;
                x = x2;
                y = y2;
              }
            else
              return FALSE;
          }
          break;

        case 'T':
        case 't':
          {
            double x1, y1, x2, y2;

            if (parse_coordinate_pair (&p, &x2, &y2))
              {
                if (cmd == 't')
                  {
                    x2 += x;
                    y2 += y;
                  }
                if (prev_cmd == 'Q' || prev_cmd == 'T')
                  {
                    x1 = 2 * x - prev_x1;
                    y1 = 2 * y - prev_y1;
                  }
                else
                  {
                    x1 = x;
                    y1 = y;
                  }
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                quad_to (builder, x1, y1, x2, y2);
                prev_x1 = x1;
                prev_y1 = y1;
                x = x2;
                y = y2;
              }
            else
              return FALSE;
          }
          break;

        case 'O':
        case 'o':
          {
            double x1, y1, x2, y2, weight;

            if (parse_coordinate_pair (&p, &x1, &y1) &&
                parse_coordinate_pair (&p, &x2, &y2) &&
                parse_nonnegative_number (&p, &weight))
              {
                if (cmd == 'o')
                  {
                    x1 += x;
                    y1 += y;
                    x2 += x;
                    y2 += y;
                  }
                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                conic_to (builder, x1, y1, x2, y2, weight);
                x = x2;
                y = y2;
              }
            else
              return FALSE;
          }
          break;

        case 'A':
        case 'a':
          {
            double rx, ry;
            double x_axis_rotation;
            int large_arc, sweep;
            double x1, y1;

            if (parse_nonnegative_number (&p, &rx) &&
                parse_nonnegative_number (&p, &ry) &&
                parse_number (&p, &x_axis_rotation) &&
                parse_flag (&p, &large_arc) &&
                parse_flag (&p, &sweep) &&
                parse_coordinate_pair (&p, &x1, &y1))
              {
                if (cmd == 'a')
                  {
                    x1 += x;
                    y1 += y;
                  }

                if (prev_cmd == 'Z')
                  {
                    move_to (builder, x, y);
                    path_x = x;
                    path_y = y;
                  }
                if (!parser->add_arc (rx, ry, x_axis_rotation, large_arc, sweep, x1, y1, builder))
                  return FALSE;
                x = x1;
                y = y1;
              }
            else
              return FALSE;
          }
          break;

        default:
          return FALSE;
        }

      after_comma = (p > string) && p[-1] == ',';

      skip_whitespace (&p);
    }

  if (after_comma)
    return FALSE;

  return TRUE;
}

/* vim:set foldmethod=marker: */

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik