Exemplo n.º 1
0
static void
do_scrolling (struct frame *frame, struct glyph_matrix *current_matrix,
              struct matrix_elt *matrix, int window_size,
              int unchanged_at_top)
{
  struct matrix_elt *p;
  int i, j, k;
  USE_SAFE_ALLOCA;

  /* True if we have set a terminal window with set_terminal_window.  */
  bool terminal_window_p = 0;

  /* A queue for line insertions to be done.  */
  struct queue { int count, pos; };
  struct queue *queue_start;
  SAFE_NALLOCA (queue_start, 1, current_matrix->nrows);
  struct queue *queue = queue_start;

  char *retained_p = SAFE_ALLOCA (window_size);
  int *copy_from;
  SAFE_NALLOCA (copy_from, 1, window_size);

  /* Zero means line is empty.  */
  memset (retained_p, 0, window_size * sizeof (char));
  for (k = 0; k < window_size; ++k)
    copy_from[k] = -1;

#ifdef GLYPH_DEBUG
# define CHECK_BOUNDS							\
  do									\
    {									\
      int ck;								\
      for (ck = 0; ck < window_size; ++ck)				\
	eassert (copy_from[ck] == -1					\
		 || (copy_from[ck] >= 0 && copy_from[ck] < window_size)); \
    }									\
  while (0);
#endif

  /* When j is advanced, this corresponds to deleted lines.
     When i is advanced, this corresponds to inserted lines.  */
  i = j = window_size;
  while (i > 0 || j > 0)
    {
      p = matrix + i * (window_size + 1) + j;

      if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
	{
	  /* Insert should be done at vpos i-1, plus maybe some before.
	     Queue the screen operation to be performed.  */
	  queue->count = p->insertcount;
	  queue->pos = i + unchanged_at_top - p->insertcount;
	  ++queue;

	  /* By incrementing I, we leave room in the result rows
	     for the empty rows opened up.  */
	  i -= p->insertcount;
	}
      else if (p->deletecost < p->writecost)
	{
	  /* Old line at vpos j-1, and maybe some before it, should be
	     deleted.  By decrementing J, we skip some lines in the
	     temp_rows which is equivalent to omitting these lines in
	     the result rows, thus deleting them.  */
	  j -= p->deletecount;

	  /* Set the terminal window, if not done already.  */
 	  if (! terminal_window_p)
	    {
	      set_terminal_window (frame, window_size + unchanged_at_top);
	      terminal_window_p = 1;
	    }

	  /* Delete lines on the terminal.  */
	  ins_del_lines (frame, j + unchanged_at_top, - p->deletecount);
	}
      else
	{
	  /* Best thing done here is no insert or delete, i.e. a write.  */
	  --i, --j;
	  eassert (i >= 0 && i < window_size);
	  eassert (j >= 0 && j < window_size);
	  copy_from[i] = j;
	  retained_p[j] = 1;

#ifdef GLYPH_DEBUG
	  CHECK_BOUNDS;
#endif
	}
    }

  /* Now do all insertions queued above.  */
  if (queue > queue_start)
    {
      int next = -1;

      /* Set the terminal window if not yet done.  */
      if (!terminal_window_p)
	{
	  set_terminal_window (frame, window_size + unchanged_at_top);
	  terminal_window_p = 1;
	}

      do
	{
	  --queue;

	  /* Do the deletion on the terminal.  */
	  ins_del_lines (frame, queue->pos, queue->count);

	  /* All lines in the range deleted become empty in the glyph
	     matrix.  Assign to them glyph rows that are not retained.
	     K is the starting position of the deleted range relative
	     to the window we are working in.  */
	  k = queue->pos - unchanged_at_top;
	  for (j = 0; j < queue->count; ++j)
	    {
	      /* Find the next row not retained.  */
	      while (retained_p[++next])
		;

	      /* Record that this row is to be used for the empty
		 glyph row j.  */
	      copy_from[k + j] = next;
	    }
	}
      while (queue > queue_start);

    }

  for (k = 0; k < window_size; ++k)
    eassert (copy_from[k] >= 0 && copy_from[k] < window_size);

  /* Perform the row swizzling.  */
  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
		       copy_from, retained_p);

  /* Some sanity checks if GLYPH_DEBUG is defined.  */
  CHECK_MATRIX (current_matrix);

  if (terminal_window_p)
    set_terminal_window (frame, 0);
  SAFE_FREE ();
}
Exemplo n.º 2
0
static void
do_direct_scrolling (struct frame *frame, struct glyph_matrix *current_matrix,
		     struct matrix_elt *cost_matrix, int window_size,
		     int unchanged_at_top)
{
  struct matrix_elt *p;
  int i, j;
  USE_SAFE_ALLOCA;

  /* A queue of deletions and insertions to be performed.  */
  struct alt_queue { int count, pos, window; };
  struct alt_queue *queue_start;
  SAFE_NALLOCA (queue_start, 1, window_size);
  struct alt_queue *queue = queue_start;

  /* True if a terminal window has been set with set_terminal_window.  */
  bool terminal_window_p = 0;

  /* If true, a write has been selected, allowing either an insert or a
     delete to be selected next.  If false, a delete cannot be selected
     unless j < i, and an insert cannot be selected unless i < j.
     This corresponds to a similar restriction (with the ordering
     reversed) in calculate_direct_scrolling, which is intended to
     ensure that lines marked as inserted will be blank. */
  bool write_follows_p = 1;

  /* For each row in the new matrix what row of the old matrix it is.  */
  int *copy_from;
  SAFE_NALLOCA (copy_from, 1, window_size);

  /* Non-zero for each row in the new matrix that is retained from the
     old matrix.  Lines not retained are empty.  */
  char *retained_p = SAFE_ALLOCA (window_size);

  memset (retained_p, 0, window_size * sizeof (char));

  /* Perform some sanity checks when GLYPH_DEBUG is on.  */
  CHECK_MATRIX (current_matrix);

  /* We are working on the line range UNCHANGED_AT_TOP ...
     UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX.
     We step through lines in this range from the end to the start.  I
     is an index into new lines, j an index into old lines.  The cost
     matrix determines what to do for ranges of indices.

     If i is decremented without also decrementing j, this corresponds
     to inserting empty lines in the result.  If j is decremented
     without also decrementing i, this corresponds to omitting these
     lines in the new rows, i.e. rows are deleted.  */
  i = j = window_size;

  while (i > 0 || j > 0)
    {
      p = cost_matrix + i * (window_size + 1) + j;

      if (p->insertcost < p->writecost
	  && p->insertcost < p->deletecost
	  && (write_follows_p || i < j))
	{
	  /* Insert is cheaper than deleting or writing lines.  Leave
	     a hole in the result display that will be filled with
	     empty lines when the queue is emptied.  */
	  queue->count = 0;
	  queue->window = i;
	  queue->pos = i - p->insertcount;
	  ++queue;

	  i -= p->insertcount;
	  write_follows_p = 0;
	}
      else if (p->deletecost < p->writecost
	       && (write_follows_p || i > j))
	{
	  /* Deleting lines is cheaper.  By decrementing J, omit
	     deletecount lines from the original.  */
	  write_follows_p = 0;
	  j -= p->deletecount;
	}
      else
	{
	  /* One or more lines should be written.  In the direct
	     scrolling method we do this by scrolling the lines to the
	     place they belong.  */
	  int n_to_write = p->writecount;
	  write_follows_p = 1;
	  eassert (n_to_write > 0);

	  if (i > j)
	    {
	      /* Immediately insert lines */
	      set_terminal_window (frame, i + unchanged_at_top);
	      terminal_window_p = 1;
	      ins_del_lines (frame, j - n_to_write + unchanged_at_top, i - j);
	    }
	  else if (i < j)
	    {
	      /* Queue the deletion of a group of lines */
	      queue->pos = i - n_to_write + unchanged_at_top;
	      queue->window = j + unchanged_at_top;
	      queue->count = i - j;
	      ++queue;
	    }

	  while (n_to_write > 0)
	    {
	      --i, --j, --n_to_write;
	      copy_from[i] = j;
	      retained_p[j] = 1;
	    }
	}
    }

  /* Do queued operations.  */
  if (queue > queue_start)
    {
      int next = -1;

      do
	{
	  --queue;
	  if (queue->count)
	    {
	      set_terminal_window (frame, queue->window);
	      terminal_window_p = 1;
	      ins_del_lines (frame, queue->pos, queue->count);
	    }
	  else
	    {
	      for (j = queue->window - 1; j >= queue->pos; --j)
		{
		  while (retained_p[++next])
		    ;
		  copy_from[j] = next;
		}
	    }
	}
      while (queue > queue_start);
    }

  /* Now, for each row I in the range of rows we are working on,
     copy_from[i] gives the original line to copy to I, and
     retained_p[copy_from[i]] is zero if line I in the new display is
     empty.  */
  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
		       copy_from, retained_p);

  if (terminal_window_p)
    set_terminal_window (frame, 0);
  SAFE_FREE ();
}
Exemplo n.º 3
0
Arquivo: ne.c Projeto: vigna/ne
int main(int argc, char **argv) {

	char *locale = setlocale(LC_ALL, "");
	for(int i = 0; i < 256; i++) localised_up_case[i] = toupper(i);

	if (locale) {
		struct re_pattern_buffer re_pb;
		struct re_registers re_reg;
		memset(&re_pb, 0, sizeof re_pb);
		memset(&re_reg, 0, sizeof re_reg);

		re_pb.translate = localised_up_case;
		re_compile_pattern(LOCALE_REGEX, strlen(LOCALE_REGEX), &re_pb);
		if (re_search(&re_pb, locale, strlen(locale), 0, strlen(locale), &re_reg) >= 0) {
			if (re_reg.start[1] >= 0) io_utf8 = true;
		}
		free(re_reg.start);
		free(re_reg.end);
	}

	bool no_config = false;
	char *macro_name = NULL, *key_bindings_name = NULL, *menu_conf_name = NULL, *startup_prefs_name = DEF_PREFS_NAME;

	char * const skiplist = calloc(argc, 1);
	if (!skiplist) exit(1);  /* We need this many flags. */

	for(int i = 1; i < argc; i++) {

		if (argv[i][0] == '-' && (!strcmp(&argv[i][1], "h") || !strcmp(&argv[i][1], "-help" "\0" VERSION_STRING))) {
			puts(ARG_HELP);
			exit(0);
		}

		/* Special arguments start with two dashes. If we find one, we
		   cancel its entry in argv[], so that it will be skipped when opening
		   the specified files. The only exception is +N for skipping to the
		   N-th line. */

		if (argv[i][0] == '-' && argv[i][1] == '-') {
			if (!argv[i][2]) i++; /* You can use "--" to force the next token to be a filename */
			else if (!strcmp(&argv[i][2], "noconfig") || !strcmp(&argv[i][2], "no-config")) {
				no_config = true;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "noansi") || !strcmp(&argv[i][2], "no-ansi")) {
				ansi = false;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "no-syntax")) {
				do_syntax = false;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "prefs")) {
				if (i < argc-1) {
					startup_prefs_name = argv[i+1];
					skiplist[i] = skiplist[i+1] = 1; /* argv[i] = argv[i+1] = NULL; */
				}
			}
			else if (!strcmp(&argv[i][2], "ansi")) {
				ansi = true;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "utf8")) {
				io_utf8 = true;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "no-utf8")) {
				io_utf8 = false;
				skiplist[i] = 1; /* argv[i] = NULL; */
			}
			else if (!strcmp(&argv[i][2], "macro")) {
				if (i < argc-1) {
					macro_name = argv[i+1];
					skiplist[i] = skiplist[i+1] = 1; /* argv[i] = argv[i+1] = NULL; */
				}
			}
			else if (!strcmp(&argv[i][2], "keys")) {
				if (i < argc-1) {
					key_bindings_name = argv[i+1];
					skiplist[i] = skiplist[i+1] = 1; /* argv[i] = argv[i+1] = NULL; */
				}
			}
			else if (!strcmp(&argv[i][2], "menus")) {
				if (i < argc-1) {
					menu_conf_name = argv[i+1];
					skiplist[i] = skiplist[i+1] = 1; /* argv[i] = argv[i+1] = NULL; */
				}
			}
		}
	}

#ifdef NE_TEST
	/* Dump the builtin menu and key bindings to compare to
	   doc/default.menus and doc/default.keys. */
	int dump_config(void);
	dump_config();
#endif

	/* Unless --noconfig was specified, we try to configure the
	   menus and the keyboard. Note that these functions can exit() on error. */

	if (!no_config) {
		get_menu_configuration(menu_conf_name);
		get_key_bindings(key_bindings_name);
	}

	/* If we cannot even create a buffer, better go... */

	if (!new_buffer()) exit(1);

	/* Now that key_bindings are loaded, try to fix up the message for NOT_FOUND. */
	{
		char *repeat_last_keystroke, *new_not_found;
		if ((repeat_last_keystroke = find_key_strokes(REPEATLAST_A, 1))) {
			if ((new_not_found = malloc(39+strlen(repeat_last_keystroke)))) {
				strcat(strcat(strcpy(new_not_found, "Not Found. (RepeatLast with "), repeat_last_keystroke), " to wrap.)");
				error_msg[NOT_FOUND] = new_not_found;
			}
			free(repeat_last_keystroke);
		}
	}

	clear_buffer(cur_buffer);

	/* The INT_MAX clip always exists, and it is used by the Through command. */

	clip_desc * const cd = alloc_clip_desc(INT_MAX, 0);
	if (!cd) exit(1);

	add_head(&clips, &cd->cd_node);

	/* General terminfo and cursor motion initalization. From here onwards,
	   we cannot exit() lightly. */

	term_init();

	/* We will be always using the last line for the status bar. */

	set_terminal_window(ne_lines-1);

	/* We read in all the key capabilities. */

	read_key_capabilities();

	/* Some initializations of other modules... */

	re_set_syntax(
		RE_CONTEXT_INDEP_ANCHORS |
		RE_CONTEXT_INDEP_OPS     | RE_HAT_LISTS_NOT_NEWLINE |
		RE_NEWLINE_ALT           | RE_NO_BK_PARENS          |
		RE_NO_BK_VBAR            | RE_NO_EMPTY_RANGES
	);

	bool first_file = true;

	load_virtual_extensions();
	load_auto_prefs(cur_buffer, startup_prefs_name);

	buffer *stdin_buffer = NULL;
	if (!isatty(fileno(stdin))) {
		first_file = false;
		const int error = load_fd_in_buffer(cur_buffer, fileno(stdin));
		print_error(error);
		stdin_buffer = cur_buffer;

		if (!(freopen("/dev/tty", "r", stdin))) {
			fprintf(stderr, "Cannot reopen input tty\n");
			abort();
		}
	}

	/* The terminal is prepared for interactive I/O. */

	set_interactive_mode();

	clear_entire_screen();

	/* This function sets fatal_code() as signal interrupt handler
	   for all the dangerous signals (SIGILL, SIGSEGV etc.). */

	set_fatal_code();

	if (argc > 1) {

		/* The first file opened does not need a NEWDOC_A action. Note that
		   file loading can be interrupted (wildcarding can sometimes produce
		   unwanted results). */

		uint64_t first_line = 0, first_col = 0;
		bool binary = false, skip_plus = false, read_only = false;
		stop = false;

		for(int i = 1; i < argc && !stop; i++) {
			if (argv[i] && !skiplist[i]) {
				if (argv[i][0] == '+' && !skip_plus) {       /* looking for "+", or "+N" or "+N,M"  */
					uint64_t tmp_l = INT64_MAX, tmp_c = 0;
					char *d;
					errno = 0;
					if (argv[i][1]) {
						if (isdigit((unsigned char)argv[i][1])) {
							tmp_l = strtoll(argv[i]+1, &d, 10);
							if (!errno) {
								if (*d) {  /* separator between N and M */
									if (isdigit((unsigned char)d[1])) {
										tmp_c = strtoll(d+1, &d, 10);
										if (*d) errno = ERANGE;
									}
									else errno = ERANGE;
								}
							}
						}
						else errno = ERANGE;
					}
					if (!errno) {
						first_line = tmp_l;
						first_col  = tmp_c;
					}
					else {
						skip_plus = true;
						i--;
					}
				}
				else if (!strcmp(argv[i], "--binary")) {
					binary = true;
				}
				else if (!strcmp(argv[i], "--read-only") || !strcmp(argv[i], "--readonly") || !strcmp(argv[i], "--ro")) {
					read_only = true;
				}
				else {
					if (!strcmp(argv[i], "-") && stdin_buffer) {
						stdin_buffer->opt.binary = binary;
						if (read_only) stdin_buffer->opt.read_only = read_only;
						if (first_line) do_action(stdin_buffer, GOTOLINE_A, first_line, NULL);
						if (first_col)  do_action(stdin_buffer, GOTOCOLUMN_A, first_col, NULL);
						stdin_buffer = NULL;
					}
					else {
						if (!strcmp(argv[i], "--")) i++;
						if (!first_file) do_action(cur_buffer, NEWDOC_A, -1, NULL);
						else first_file = false;
						cur_buffer->opt.binary = binary;
						if (i < argc) do_action(cur_buffer, OPEN_A, 0, str_dup(argv[i]));
						if (first_line) do_action(cur_buffer, GOTOLINE_A, first_line, NULL);
						if (first_col)  do_action(cur_buffer, GOTOCOLUMN_A, first_col, NULL);
						if (read_only) cur_buffer->opt.read_only = read_only;
					}
					first_line =
					first_col  = 0;
					skip_plus  =
					binary    =
					read_only  = false;
				}
			}
		}

		free(skiplist);

		/* This call makes current the first specified file. It is called
		   only if more than one buffer exist. */

		if (get_nth_buffer(1)) do_action(cur_buffer, NEXTDOC_A, -1, NULL);

	}

	/* We delay updates. In this way the macro activity does not cause display activity. */

	reset_window();
	delay_update();

	if (macro_name) do_action(cur_buffer, MACRO_A, -1, str_dup(macro_name));
	else if (first_file) {
		/* If there is no file to load, and no macro to execute, we display
		   the "NO WARRANTY" message. */
		about();
	}

	while(true) {
		/* If we are displaying the "NO WARRANTY" info, we should not refresh the
		   window now */
		if (!displaying_info) {
			refresh_window(cur_buffer);
			if (cur_buffer->opt.automatch) automatch_bracket(cur_buffer, true);
		}

		draw_status_bar();
		move_cursor(cur_buffer->cur_y, cur_buffer->cur_x);

		int c = get_key_code();

		if (window_changed_size) {
			print_error(do_action(cur_buffer, REFRESH_A, 0, NULL));
			window_changed_size = displaying_info = false;
			cur_buffer->automatch.shown = 0;
		}

		if (c == INVALID_CHAR) continue; /* Window resizing. */
		const input_class ic = CHAR_CLASS(c);

		if (displaying_info) {
			refresh_window(cur_buffer);
			displaying_info = false;
		}

		if (cur_buffer->automatch.shown) automatch_bracket(cur_buffer, false);

		switch(ic) {
		case INVALID:
			print_error(INVALID_CHARACTER);
			break;

		case ALPHA:
			print_error(do_action(cur_buffer, INSERTCHAR_A, c, NULL));
			break;

		case TAB:
			print_error(do_action(cur_buffer, INSERTTAB_A, 1, NULL));
			break;

		case RETURN:
			print_error(do_action(cur_buffer, INSERTLINE_A, -1, NULL));
			break;

		case COMMAND:
			if (c < 0) c = -c - 1;
			if (key_binding[c]) print_error(execute_command_line(cur_buffer, key_binding[c]));
			break;

		default:
			break;
		}
	}
}