/* Convert ASCII quotations to Unicode quotations.
   Copyright (C) 2014-2026 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* Written by Daiki Ueno.  */

#include <config.h>

/* Specification.  */
#include "filters.h"

#include "quote.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "xalloc.h"

#define BOLD_START "\x1b[1m"
#define BOLD_END "\x1b[0m"

struct result
{
  char *output;
  char *offset;
  bool bold;
};

static void
convert_quote_callback (char quote, const char *quoted, size_t quoted_length,
                        void *data)
{
  struct result *result = data;

  switch (quote)
    {
    case '\0':
      memcpy (result->offset, quoted, quoted_length);
      result->offset += quoted_length;
      break;

    case '"':
      /* U+201C: LEFT DOUBLE QUOTATION MARK */
      memcpy (result->offset, "\xe2\x80\x9c", 3);
      result->offset += 3;
      if (result->bold)
        {
          memcpy (result->offset, BOLD_START, 4);
          result->offset += 4;
        }
      memcpy (result->offset, quoted, quoted_length);
      result->offset += quoted_length;
      if (result->bold)
        {
          memcpy (result->offset, BOLD_END, 4);
          result->offset += 4;
        }
      /* U+201D: RIGHT DOUBLE QUOTATION MARK */
      memcpy (result->offset, "\xe2\x80\x9d", 3);
      result->offset += 3;
      break;

    case '\'':
      /* U+2018: LEFT SINGLE QUOTATION MARK */
      memcpy (result->offset, "\xe2\x80\x98", 3);
      result->offset += 3;
      if (result->bold)
        {
          memcpy (result->offset, BOLD_START, 4);
          result->offset += 4;
        }
      memcpy (result->offset, quoted, quoted_length);
      result->offset += quoted_length;
      if (result->bold)
        {
          memcpy (result->offset, BOLD_END, 4);
          result->offset += 4;
        }
      /* U+2019: RIGHT SINGLE QUOTATION MARK */
      memcpy (result->offset, "\xe2\x80\x99", 3);
      result->offset += 3;
      break;
    }
}

/* This is a direct translation of po/quot.sed and po/boldquot.sed.  */
static void
convert_ascii_quote_to_unicode (const char *input, size_t input_len,
                                char **output_p, size_t *output_len_p,
                                bool bold)
{
  /* Count the number of quotation characters.  */
  size_t quote_count = 0;
  for (const char *p = input; p < input + input_len; p++)
    {
      size_t len;

      p = strpbrk (p, "`'\"");
      if (!p)
        break;

      len = strspn (p, "`'\"");
      quote_count += len;
      p += len;
    }

  /* Large enough.  */
  struct result result;
  result.output = XNMALLOC (input_len - quote_count
                            + (bold ? 7 : 3) * quote_count + 1,
                            char);
  result.offset = result.output;
  result.bold = bold;

  scan_quoted (input, input_len, convert_quote_callback, &result);

  *output_p = result.output;
  *output_len_p = result.offset - result.output;
}

void
ascii_quote_to_unicode (const char *input, size_t input_len,
                        char **output_p, size_t *output_len_p)
{
  convert_ascii_quote_to_unicode (input, input_len,
                                  output_p, output_len_p,
                                  false);
}

void
ascii_quote_to_unicode_bold (const char *input, size_t input_len,
                             char **output_p, size_t *output_len_p)
{
  convert_ascii_quote_to_unicode (input, input_len,
                                  output_p, output_len_p,
                                  true);
}
