Exemple #1
0
/* Search forwards or backwards for anything matching the regexp in the text
   delimited by BINDING. The search is forwards if BINDING->start is greater
   than BINDING->end.

   If PRET is specified, it receives a copy of BINDING at the end of a
   succeded search.  Its START and END fields contain bounds of the found
   string instance. 
*/
long
regexp_search (char *regexp, SEARCH_BINDING *binding, long length,
	       SEARCH_BINDING *pret)
{
  static char *previous_regexp = NULL;
  static char *previous_content = NULL;
  static int was_insensitive = 0;
  static regex_t preg;
  static regmatch_t *matches;
  static int match_alloc = 0;
  static int match_count = 0;
  regoff_t pos;

  if (previous_regexp == NULL
      || ((binding->flags & S_FoldCase) != was_insensitive)
      || (strcmp (previous_regexp, regexp) != 0))
    {
      /* need to compile a new regexp */
      int result;
      char *unescaped_regexp;
      char *p, *q;

      previous_content = NULL;

      if (previous_regexp != NULL)
        {
          free (previous_regexp);
          previous_regexp = NULL;
          regfree (&preg);
        }

      was_insensitive = binding->flags & S_FoldCase;

      /* expand the \n and \t in regexp */
      unescaped_regexp = xmalloc (1 + strlen (regexp));
      for (p = regexp, q = unescaped_regexp; *p != '\0'; p++, q++)
        {
          if (*p == '\\')
            switch(*++p)
              {
              case 'n':
                *q = '\n';
                break;
              case 't':
                *q = '\t';
                break;
              case '\0':
                *q = '\\';
                p--;
                break;
              default:
                *q++ = '\\';
                *q = *p;
                break;
              }
          else
            *q = *p;
        }
      *q = '\0';

      result = regcomp (&preg, unescaped_regexp,
                       REG_EXTENDED|
                       REG_NEWLINE|
                       (was_insensitive ? REG_ICASE : 0));
      free (unescaped_regexp);

      if (result != 0)
        {
          int size = regerror (result, &preg, NULL, 0);
          char *buf = xmalloc (size);
          regerror (result, &preg, buf, size);
          info_error (_("regexp error: %s"), buf, NULL);
          return -1;
        }

      previous_regexp = xstrdup(regexp);
    }

  if (previous_content != binding->buffer)
    {
      /* new buffer to search in, let's scan it */
      regoff_t start = 0;
      char saved_char;

      previous_content = binding->buffer;
      saved_char = previous_content[length-1];
      previous_content[length-1] = '\0';

      for (match_count = 0; start < length; )
        {
          int result = 0;
          if (match_count >= match_alloc)
            {
              /* match list full. Initially allocate 256 entries, then double
                 every time we fill it */
              match_alloc = (match_alloc > 0 ? match_alloc * 2 : 256);
              matches = xrealloc (matches,
				  match_alloc * sizeof(regmatch_t));
            }

          result = regexec (&preg, &previous_content[start],
                            1, &matches[match_count], 0);
          if (result == 0)
            {
              if (matches[match_count].rm_eo == 0)
                {
                  /* ignore empty matches */
                  start++;
                }
              else
                {
                  matches[match_count].rm_so += start;
                  matches[match_count].rm_eo += start;
                  start = matches[match_count++].rm_eo;
                }
            }
          else
            {
              break;
            }
        }
      previous_content[length-1] = saved_char;
    }

  pos = binding->start;
  if (pos > binding->end)
    {
      /* searching backward */
      int i;
      for (i = match_count - 1; i >= 0; i--)
        {
          if (matches[i].rm_so <= pos)
	    {
	      if (pret)
		{
		  pret->buffer = binding->buffer;
		  pret->flags = binding->flags;
		  pret->start = matches[i].rm_so;
		  pret->end = matches[i].rm_eo;
		}
	      return matches[i].rm_so;
	    }
        }
    }
  else
    {
      /* searching forward */
      int i;
      for (i = 0; i < match_count; i++)
        {
          if (matches[i].rm_so >= pos)
            {
	      if (pret)
		{
		  pret->buffer = binding->buffer;
		  pret->flags = binding->flags;
		  pret->start = matches[i].rm_so;
		  pret->end = matches[i].rm_eo;
		}
              if (binding->flags & S_SkipDest)
                return matches[i].rm_eo;
              else
                return matches[i].rm_so;
            }
        }
    }

  /* not found */
  return -1;
}
Exemple #2
0
/* Read the init file.  Return true if no error was encountered.  Set
   SUPPRESS_INFO or SUPPRESS_EA to true if the init file specified to ignore
   default key bindings. */
int
compile (FILE *fp, const char *filename, int *suppress_info, int *suppress_ea)
{
  int error = 0; /* Set if there was a fatal error in reading init file. */
  char rescan = 0; /* Whether to reuse the same character when moving onto the
                      next state. */
  unsigned int lnum = 0;
  int c = 0;

  /* This parser is a true state machine, with no sneaky fetching
     of input characters inside the main loop.  In other words, all
     state is fully represented by the following variables:
   */
  enum
    {
      start_of_line,
      start_of_comment,
      in_line_comment,
      in_trailing_comment,
      get_keyseq,
      got_keyseq,
      get_action,
      got_action,
      get_varname,
      got_varname,
      get_equals,
      got_equals,
      get_value
    }
  state = start_of_line;
  enum sect_e section = info;
  enum
    {
      normal,
      slosh,
      control,
      octal,
      special_key
    }
  seqstate = normal;	/* used if state == get_keyseq */
  char meta = 0;
  char ocnt = 0;	/* used if state == get_keyseq && seqstate == octal */

  /* Data is accumulated in the following variables.  The code
     avoids overflowing these strings, and throws an error
     where appropriate if a string limit is exceeded.  These string
     lengths are arbitrary (and should be large enough) and their
     lengths are not hard-coded anywhere else, so increasing them
     here will not break anything.  */
  char oval = 0;
  char comment[10];
  unsigned int clen = 0;
  int seq[20];
  unsigned int slen = 0;
  char act[80];
  unsigned int alen = 0;
  char varn[80];
  unsigned int varlen = 0;
  char val[80];
  unsigned int vallen = 0;

#define	To_seq(c) \
		  do { \
		    if (slen < sizeof seq/sizeof(int)) \
		      seq[slen++] = meta ? KEYMAP_META(c) : (c); \
		    else \
		      { \
			syntax_error(filename, lnum, \
				     _("key sequence too long")); \
			error = 1; \
		      } \
		    meta = 0; \
		  } while (0)

  while (!error && (rescan || (c = fgetc (fp)) != EOF))
    {
      rescan = 0;
      switch (state)
	{
	case start_of_line:
	  lnum++;
	  if (c == '#')
	    state = start_of_comment;
	  else if (c != '\n')
	    {
	      switch (section)
		{
		case info:
		case ea:
		  state = get_keyseq;
		  seqstate = normal;
		  slen = 0;
		  break;
		case var:
		  state = get_varname;
		  varlen = 0;
		  break;
		}
	      rescan = 1;
	    }
	  break;

	case start_of_comment:
	  clen = 0;
	  state = in_line_comment;
	  /* fall through */
	case in_line_comment:
	  if (c == '\n')
	    {
	      state = start_of_line;
	      comment[clen] = '\0';
	      if (strcmp (comment, "info") == 0)
		section = info;
	      else if (strcmp (comment, "echo-area") == 0)
		section = ea;
	      else if (strcmp (comment, "var") == 0)
		section = var;
	      else if (strcmp (comment, "stop") == 0
		       && (section == info || section == ea))
                {
                  if (section == info)
                    *suppress_info = 1;
                  else
                    *suppress_ea = 1;
                }
	    }
	  else if (clen < sizeof comment - 1)
	    comment[clen++] = c;
	  break;

	case in_trailing_comment:
	  if (c == '\n')
	    state = start_of_line;
	  break;

	case get_keyseq:
	  switch (seqstate)
	    {
	    case normal:
	      if (c == '\n' || isspace (c))
		{
		  state = got_keyseq;
		  rescan = 1;
		  if (slen == 0)
		    {
		      syntax_error (filename, lnum, _("missing key sequence"));
		      error = 1;
		    }
		}
	      else if (c == '\\')
		seqstate = slosh;
	      else if (c == '^')
		seqstate = control;
	      else
		To_seq (c);
	      break;

	    case slosh:
	      switch (c)
		{
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		  seqstate = octal;
		  oval = c - '0';
		  ocnt = 1;
		  break;
		case 'b':
		  To_seq ('\b');
		  seqstate = normal;
		  break;
		case 'e':
		  To_seq ('\033');
		  seqstate = normal;
		  break;
		case 'n':
		  To_seq ('\n');
		  seqstate = normal;
		  break;
		case 'r':
		  To_seq ('\r');
		  seqstate = normal;
		  break;
		case 't':
		  To_seq ('\t');
		  seqstate = normal;
		  break;
		case 'm':
		  meta = 1;
		  seqstate = normal;
		  break;
		case 'k':
		  seqstate = special_key;
		  break;
		default:
		  /* Backslash followed by any other char
		     just means that char.  */
		  To_seq (c);
		  seqstate = normal;
		  break;
		}
	      break;

	    case octal:
	      switch (c)
		{
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		  if (++ocnt <= 3)
		    oval = oval * 8 + c - '0';
		  if (ocnt == 3)
		    seqstate = normal;
		  break;
		default:
		  ocnt = 4;
		  seqstate = normal;
		  rescan = 1;
		  break;
		}
	      if (seqstate != octal)
		{
		  if (oval)
		    To_seq (oval);
		  else
		    {
		      syntax_error (filename, lnum,
				    _("NUL character (\\000) not permitted"));
		      error = 1;
		    }
		}
	      break;

	    case special_key:
	      switch (c)
		{
		case 'u': To_seq (KEY_UP_ARROW); break;
		case 'd': To_seq (KEY_DOWN_ARROW); break;
		case 'r': To_seq (KEY_RIGHT_ARROW); break;
		case 'l': To_seq (KEY_LEFT_ARROW); break;
		case 'U': To_seq (KEY_PAGE_UP); break;
		case 'D': To_seq (KEY_PAGE_DOWN); break;
		case 'h': To_seq (KEY_HOME); break;
		case 'e': To_seq (KEY_END); break;
		case 'x': To_seq (KEY_DELETE); break;
		default:  To_seq (c); rescan = 1; break;
		}
	      seqstate = normal;
	      break;

	    case control:
	      if (CONTROL (c))
		To_seq (CONTROL (c));
	      else
		{
		  syntax_error (filename, lnum,
				_("NUL character (^%c) not permitted"), c);
		  error = 1;
		}
	      seqstate = normal;
	      break;
	    }
	  break;

	case got_keyseq:
	  if (isspace (c) && c != '\n')
	    break;
	  state = get_action;
	  alen = 0;
	  /* fall through */
	case get_action:
	  if (c == '\n' || isspace (c))
	    {
	      int a;

	      state = got_action;
	      rescan = 1;
	      if (alen == 0)
		{
		  syntax_error (filename, lnum, _("missing action name"));
		  error = 1;
		}
	      else
		{
		  act[alen] = '\0';
		  a = lookup_action (act);
                  if (a == A_info_menu_digit)
		    {
                      /* It does not make sense for menu-digit to be anything
                         other than '0' .. '9'. */
		      syntax_error (filename, lnum,
                        _("cannot bind key sequence to menu-digit"));
		    }
		  else if (a == -1)
		    {
                      /* Print an error message, but keep going (don't set
                         error = 1) for compatibility with infokey files aimed
                         at future versions which may have different
                         actions. */
		      syntax_error (filename, lnum, _("unknown action `%s'"),
				    act);
		    }
                  else
		    {
                      int keymap_bind_keyseq (Keymap, int *, KEYMAP_ENTRY *);

                      KEYMAP_ENTRY ke;
                      
                      ke.type = ISFUNC;
                      ke.value.function = &function_doc_array[a];
                      To_seq (0);

                      if (section == info)
                        keymap_bind_keyseq (info_keymap, seq, &ke);
                      else /* section == ea */
                        keymap_bind_keyseq (echo_area_keymap, seq, &ke);
		    }
		}
	    }
	  else if (alen < sizeof act - 1)
	    act[alen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("action name too long"));
	      error = 1;
	    }
	  break;

	case got_action:
	  if (c == '#')
	    state = in_trailing_comment;
	  else if (c == '\n')
	    state = start_of_line;
	  else if (!isspace (c))
	    {
	      syntax_error (filename, lnum,
			    _("extra characters following action `%s'"),
			    act);
	      error = 1;
	    }
	  break;

	case get_varname:
	  if (c == '=')
	    {
	      if (varlen == 0)
		{
		  syntax_error (filename, lnum, _("missing variable name"));
		  error = 1;
		}
	      state = get_value;
	      vallen = 0;
	    }
	  else if (c == '\n' || isspace (c))
	    {
	      syntax_error (filename, lnum,
			    _("missing `=' immediately after variable name"));
	      error = 1;
	    }
	  else if (varlen < sizeof varn - 1)
	    varn[varlen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("variable name too long"));
	      error = 1;
	    }
	  break;

	case get_value:
	  if (c == '\n')
	    {
              VARIABLE_ALIST *v;

              state = start_of_line;
              varn[varlen] = '\0';
              val[vallen] = '\0';
              v = variable_by_name (varn);
              if (!v)
                info_error (_("%s: no such variable"), varn);
              else if (!set_variable_to_value (v, val, SET_IN_CONFIG_FILE))
                info_error (_("value %s is not valid for variable %s"),
                              val, varn);
	    }
	  else if (vallen < sizeof val - 1)
	    val[vallen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("value too long"));
	      error = 1;
	    }
	  break;

        case get_equals:
        case got_equals:
        case got_varname:
          break;
	}
    }

#undef To_seq

  return !error;
}