aboutsummaryrefslogblamecommitdiffstats
path: root/src/mds-kbdc/parse-error.c
blob: 1ad5ae12b4208e6ae1037f916f859cbef8080fb1 (plain) (tree)






























































































                                                                                                               








                                                           




























































                                                                              
/**
 * mds — A micro-display server
 * Copyright © 2014  Mattias Andrée (maandree@member.fsf.org)
 * 
 * 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 <http://www.gnu.org/licenses/>.
 */
#include "parse-error.h"

#include <stdlib.h>
#include <alloca.h>
#include <string.h>



/**
 * Print information about a parsing error
 * 
 * @param  this    The error structure
 * @param  output  The output file
 */
void mds_kbdc_parse_error_print(const mds_kbdc_parse_error_t* restrict this, FILE* restrict output)
{
  size_t i, n, start = 0, end = 0;
  ssize_t m;
  const char* restrict code = this->code;
  char* desc;
  char* dend = this->description + strlen(this->description);
  char* dstart;
  char* dptr;
  char* p;
  char* q;
  
  /* Count the number points in the description we should modify to format it. */
  for (p = this->description, n = 0;;)
    {
      if (q = strstr(p, "‘"), q == NULL)  q = dend;
      if (p = strstr(p, "’"), p == NULL)  p = dend;
      if (q < p)  p = q;
      if (*p++)   n++;
      else        break;
    }
  
  /* Allocate string for the formatted description. */
  n = 1 + strlen(this->description) + strlen("\033[xxm’") * n;
  dptr = desc = alloca(n * sizeof(char));
  
  /* Format description. */
  for (p = this->description;;)
    {
      dstart = p;
      if (q = strstr(p, "‘"), q == NULL)  q = dend;
      if (p = strstr(p, "’"), p == NULL)  p = dend;
      if (q < p)  p = q;
      if ((n = (size_t)(p - dstart)))
	memcpy(dptr, dstart, n), dptr += n;
      if (p == dend)
	break;
      if (strstr(p, "‘") == p)  sprintf(dptr, "\033[01m‘%zn", &m),  dptr += (size_t)m,  p += strlen("‘");
      else                      sprintf(dptr, "’\033[21m%zn", &m),  dptr += (size_t)m,  p += strlen("’");
    }
  *dptr = '\0';
  
  /* Convert bytes count to character count for the code position */
  for (i = 0, n = this->start; i < n; i++)
    start++;
  for (n = this->end; i < n; i++)
    if ((code[i] & 0xC0) != 0x80)
      end++;
  end += start;
  
  /* Print error information. */
  fprintf(output, "\033[01m%s\033[21m:", this->pathname); /* TODO should be relative to the current dir */
  if (this->error_is_in_file)
    fprintf(output, "%zu:%zu–%zu:", this->line + 1, start + 1, end + 1);
  switch (this->severity)
    {
    case MDS_KBDC_PARSE_ERROR_NOTE:            fprintf(output, " \033[01;36mnote:\033[00m ");            break;
    case MDS_KBDC_PARSE_ERROR_WARNING:         fprintf(output, " \033[01;35mwarning:\033[00m ");         break;
    case MDS_KBDC_PARSE_ERROR_ERROR:           fprintf(output, " \033[01;31merror:\033[00m ");           break;
    case MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR:  fprintf(output, " \033[01;31minternal error:\033[00m ");  break;
    default:
      abort();
      break;
    }
  if (this->error_is_in_file)
    {
      fprintf(output, "%s\n %s\n \033[01;32m", desc, code);
      i = 0;
      for (n = start; i < n; i++)  fputc(' ', output);
      for (n = end;   i < n; i++)  fputc('^', output);
    }
  else
    fprintf(output, "%s\n", desc);
  fprintf(output, "\033[00m\n");
}



/**
 * Release all resources allocated in a `mds_kbdc_parse_error_t*`
 * 
 * @param  this  The error structure
 */
void mds_kbdc_parse_error_destroy(mds_kbdc_parse_error_t* restrict this)
{
  if (this == NULL)
    return;
  free(this->pathname),    this->pathname    = NULL;
  free(this->code),        this->code        = NULL;
  free(this->description), this->description = NULL;
}


/**
 * Release all resources allocated in a `mds_kbdc_parse_error_t*`
 * and release the allocation of the structure itself
 * 
 * @param  this  The error structure
 */
void mds_kbdc_parse_error_free(mds_kbdc_parse_error_t* restrict this)
{
  mds_kbdc_parse_error_destroy(this);
  free(this);
}


/**
 * Release all resources allocated in a `NULL`-terminated group
 * of `mds_kbdc_parse_error_t*`:s
 * 
 * @param  this  The group of error structures
 */
void mds_kbdc_parse_error_destroy_all(mds_kbdc_parse_error_t** restrict these)
{
  mds_kbdc_parse_error_t* restrict that;
  if (these == NULL)
    return;
  while (that = *these, that != NULL)
    mds_kbdc_parse_error_free(that), *these++ = NULL;
}


/**
 * Release all resources allocated in a `NULL`-terminated group of
 * `mds_kbdc_parse_error_t*`:s and release the allocation of the group itself
 * 
 * @param  this  The group of error structures
 */
void mds_kbdc_parse_error_free_all(mds_kbdc_parse_error_t** restrict these)
{
  mds_kbdc_parse_error_destroy_all(these);
  free(these);
}