summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--TODO6
-rw-r--r--arg.h37
-rw-r--r--config.h14
-rw-r--r--draw.c324
-rw-r--r--files.c73
-rw-r--r--gramscii.c1057
-rw-r--r--gramscii.h138
-rw-r--r--main.c175
-rw-r--r--screen.c419
10 files changed, 1183 insertions, 1066 deletions
diff --git a/Makefile b/Makefile
index 55959ce..b88dbf5 100644
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,8 @@
include config.mk
-SRC = gramscii.c
-INC = config.h
+SRC = main.c draw.c screen.c files.c
+INC = config.h gramscii.h
all: options gramscii
@@ -22,7 +22,7 @@ gramscii: ${SRC} ${INC}
clean:
@echo cleaning
- @rm -f $(SRC:.c=)
+ @rm -f $(SRC:.c=) gramscii
install: all
@echo installing executable to ${DESTDIR}${BINDIR}
diff --git a/TODO b/TODO
index 222bad1..d521256 100644
--- a/TODO
+++ b/TODO
@@ -5,10 +5,13 @@
- use [ENTER] to exit from text insert
- maybe move "text" mode to "t"
- implement ellipse
+- filled box (B)
+- manage fill character (as for other styles)
+- implement comment (#: ignore until the end of the line)
+ parse control characters
+ parse arrows (text-mode will allow movements as well)
- (?) implement CTRL+G as abort (aside ESC)
-- add crop command (c)
+- add crop command (C)
- (?) remove extra blanks until EOL when saving to file
+ visual selection
- crop-to
@@ -21,6 +24,7 @@
- allow scrolling (both vertical and horizontal)
- catch SIGWINCH and react appropriately (after scrolling is
enabled)
+* reorganise code
* change screen management (i.e., dynamic array of lines)
* add action multiplier (e.g., "7h" moves left by 7 cols)
* add scripting mode option ("-s"?)
diff --git a/arg.h b/arg.h
new file mode 100644
index 0000000..1b1fd63
--- /dev/null
+++ b/arg.h
@@ -0,0 +1,37 @@
+#ifndef ARG_H
+#define ARG_H
+
+#define USED(x) ((void)(x))
+
+extern char *argv0;
+
+#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char _argc;\
+ char **_argv;\
+ if(argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ int i_;\
+ for(i_ = 1, _argv = argv; argv[0][i_];\
+ i_++) {\
+ if(_argv != argv)\
+ break;\
+ _argc = argv[0][i_];\
+ switch(_argc)
+
+#define ARGEND }\
+ USED(_argc);\
+ }\
+ USED(argv);\
+ USED(argc);
+
+#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\
+ (argc--, argv++, argv[0]))
+
+#endif
+
diff --git a/config.h b/config.h
index a9e5ddc..fa2eb7a 100644
--- a/config.h
+++ b/config.h
@@ -1,18 +1,22 @@
/* This is part of `gramscii`` -- see COPYING for details */
+#ifndef __LOCAL_CONFIG_H__
+#define __LOCAL_CONFIG_H__
/* Config options */
/** MARKERS -- the first character is the default one **/
/* markers for horizontal lines */
-char hlines[] = {"-~=#*@._ "};
+static char hlines[] = {"-~=#*@._ "};
/* markers for vertical lines */
-char vlines[] = {"|H#*@:;i "};
+static char vlines[] = {"|H#*@:;i "};
/* markers for corners */
-char corners[] = {"+'H#*@.\"`"};
+static char corners[] = {"+'H#*@.\"`"};
/* markers for arrow start points */
-char st_marks[] = {"+o-|<>^v*"};
+static char st_marks[] = {"+o-|<>^v*"};
/* markers for arrow endpoints */
-char end_marks[] = {">+o-|<^v*"};
+static char end_marks[] = {">+o-|<^v*"};
/** LONG_STEP (movements through uppercase HJKL) **/
#define LONG_STEP 5
+
+#endif
diff --git a/draw.c b/draw.c
new file mode 100644
index 0000000..9e20601
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,324 @@
+#include <stdlib.h>
+
+#include "gramscii.h"
+#include "config.h"
+
+/*** drawing-related functions ***/
+
+/*** Lines and markers ***/
+
+void toggle_hline(){
+
+ cur_hl = (cur_hl + 1) % hlines_sz;
+ line_h = hlines[cur_hl];
+
+}
+
+void toggle_corner(){
+
+ cur_corn = (cur_corn + 1 ) % corners_sz;
+ corner = corners[cur_corn];
+
+}
+
+void toggle_vline(){
+
+ cur_vl = (cur_vl + 1) % vlines_sz;
+ line_v = vlines[cur_vl];
+
+}
+
+void toggle_st_mark(){
+
+ cur_start = (cur_start + 1 ) % stmarks_sz;
+ mark_st = st_marks[cur_start];
+}
+
+void toggle_end_mark(){
+
+ cur_end = (cur_end+ 1 ) % endmarks_sz;
+ mark_end = end_marks[cur_end];
+}
+
+int change_style(char c){
+ switch(c){
+ case '-':
+ toggle_hline();
+ break;
+ case '|':
+ toggle_vline();
+ break;
+ case '+':
+ toggle_corner();
+ break;
+ case '<':
+ toggle_st_mark();
+ break;
+ case '>':
+ toggle_end_mark();
+ break;
+ case '.':
+ reset_styles();
+ break;
+ default:
+ return 0;
+ }
+ return c;
+}
+
+
+
+
+/***** text, box, arrows *****/
+
+void get_text(FILE *fc){
+ char c;
+ int orig_x = x;
+
+ redraw();
+ while((c=fgetc(fc))!=EOF && c != 27){
+ if(c=='\n'){
+ set_cur(BG);
+ y += 1;
+ x = orig_x;
+ }
+ else {
+ set_cur(c);
+ update_current();
+ modified = 1;
+ x += 1;
+ if (x >= WIDTH)
+ x = orig_x;
+ }
+ check_bound();
+ status_bar();
+ show_cursor();
+ }
+ mode=MOVE;
+}
+
+void draw_box(int x1, int y1, int fix){
+
+ int xmin, ymin, xmax, ymax;
+ int i;
+ void (*f)(int, int, char);
+
+ if (fix == FIX)
+ f = set_xy;
+ else
+ f = draw_xy;
+
+ xmin = MIN(x, x1);
+ xmax = MAX(x, x1);
+ ymin = MIN(y, y1);
+ ymax = MAX(y, y1);
+
+ for(i=xmin+1; i<=xmax; i++){
+ f(i, ymin, line_h);
+ f(i, ymax, line_h);
+ }
+ for(i=ymin+1; i<=ymax; i++){
+ f(xmin, i, line_v);
+ f(xmax, i, line_v);
+ }
+ f(xmin, ymin, corner);
+ f(xmin, ymax, corner);
+ f(xmax, ymin, corner);
+ f(xmax, ymax, corner);
+ show_cursor();
+}
+
+void get_box(FILE *fc){
+ char c;
+ int orig_x=x, orig_y=y;
+ redraw();
+ step = 1;
+ draw_box(x,y,NOFIX);
+ while((c=fgetc(fc))!=EOF && c != 27 && c!= 'b' && c != '\n'){
+ if (change_style(c))
+ goto update_box;
+ if (!move_around(c, fc))
+ continue;
+ check_bound();
+ redraw();
+ step = 1;
+update_box:
+ draw_box(orig_x, orig_y, NOFIX);
+ status_bar();
+ show_cursor();
+ }
+ if (c == 'b' || c == '\n'){
+ draw_box(orig_x, orig_y, FIX);
+ modified = 1;
+ }
+ redraw();
+ mode = MOVE;
+}
+
+void draw_arrow(int x, int y, char *a, int a_len, int fix){
+
+ int i, j, cur_dir;
+ char line;
+ void (*f)(int, int, char);
+
+
+ if (fix == FIX)
+ f = set_xy;
+ else
+ f = draw_xy;
+
+ f(x,y,mark_st);
+ if (!a_len){
+ show_cursor();
+ return;
+ }
+ cur_dir=DIR_N;
+ for (i=0; i<a_len; i+=2){
+ if (i>0) {
+ /* If we are switching between horizontal and vertical, put a "corner" */
+ if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) ||
+ ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){
+ f(x,y,corner);
+ show_cursor();
+ }
+ }
+ for(j=0; j<a[i+1]; j++){
+ line = (a[i] & DIR_L) || (a[i] & DIR_R) ? line_h : line_v;
+ x += progr_x(a[i]);
+ y += progr_y(a[i]);
+ f(x, y, line);
+ }
+ /* f(x,y,mark_end);*/
+ cur_dir = a[i];
+ }
+ if (autoend){
+ if (cur_dir != DIR_N)
+ f(x,y, get_mark(cur_dir));
+ }
+ else
+ f(x,y,mark_end);
+ show_cursor();
+}
+
+void get_arrow(FILE *fc){
+
+ char c;
+ int orig_x=x, orig_y=y, arrow_len;
+ static char *arrow = NULL;
+ static int arrow_sz;
+
+ if (!arrow){
+ arrow_sz = 100;
+ arrow = malloc(arrow_sz * sizeof(char));
+ }
+ arrow_len = 0;
+ dir = DIR_N;
+
+ redraw();
+ step = 1;
+ draw_arrow(x,y, arrow, 0, NOFIX);
+ while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){
+ if (change_style(c))
+ goto update_arrow;
+ if (!move_around(c, fc))
+ continue;
+ check_bound();
+ /* FIXME: if we are out of bound, do nothing? */
+ if (arrow_len == arrow_sz){
+ arrow_sz *=2;
+ arrow = realloc(arrow, arrow_sz * sizeof(char));
+ }
+ arrow[arrow_len++] = dir;
+ arrow[arrow_len++] = step;
+ redraw();
+ step = 1;
+update_arrow:
+ draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX);
+ status_bar();
+ show_cursor();
+ }
+ if (c == 'a' || c == '\n'){
+ draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX);
+ modified = 1;
+ }
+ redraw();
+ mode = MOVE;
+}
+
+
+void do_erase(int x1, int y1){
+ int i;
+ switch(dir){
+ case DIR_R:
+ for(i=x1; i<=x; i++) set_xy(i,y,BG);
+ break;
+ case DIR_L:
+ for(i=x1; i>=x; i--) set_xy(i,y,BG);
+ break;
+ case DIR_U:
+ for(i=y1; i>=y; i--) set_xy(x,i,BG);
+ break;
+ case DIR_D:
+ for(i=y1; i<=y; i++) set_xy(x,i,BG);
+ break;
+ }
+}
+
+
+void erase(FILE *fc){
+ char c;
+ int orig_x = x, orig_y = y;
+ status_bar();
+ show_cursor();
+ while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){
+ if (!move_around(c, fc)) continue;
+ check_bound();
+ do_erase(orig_x, orig_y);
+ step = 1;
+ modified = 1;
+ orig_x = x;
+ orig_y = y;
+ redraw();
+ status_bar();
+ show_cursor();
+ }
+ mode = MOVE;
+}
+
+
+/*** Visual ***/
+
+
+void visual_box(FILE *fc){
+ int orig_x =x, orig_y = y;
+ char c, f = BG;
+
+ redraw();
+ step = 1;
+ set_video(VIDEO_REV);
+ draw_box(x,y,NOFIX);
+ while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){
+ if (!move_around(c, fc)) switch(c){
+ case 'f':/* fill */
+ f = get_key(fc, "fill char: "); /** FALLTHROUGH **/
+ case 'x':/* erase */
+ erase_box(orig_x, orig_y, f);
+ modified = 1;
+ goto vis_exit;
+ break;
+ }
+ check_bound();
+ set_video(VIDEO_NRM);
+ redraw();
+ step = 1;
+ f = BG;
+ set_video(VIDEO_REV);
+ draw_box(orig_x, orig_y, NOFIX);
+ status_bar();
+ show_cursor();
+ }
+vis_exit:
+ set_video(VIDEO_NRM);
+ redraw();
+ mode = MOVE;
+}
diff --git a/files.c b/files.c
new file mode 100644
index 0000000..811a9bc
--- /dev/null
+++ b/files.c
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <string.h>
+#include "gramscii.h"
+
+
+/*** File management ***/
+
+void write_file(FILE *fc){
+ FILE *fout;
+ int i;
+
+ if (!fname[0] || force_new){
+ get_string(fc, "Write to: ", fname, 255);
+ if ((fout=fopen(fname, "r"))!=NULL){
+ if (!is_yes(get_key(fc,"File exists. Overwrite [y/n]?")) ){
+ fclose(fout);
+ return;
+ }
+ fclose(fout);
+ }
+ }
+ if((fout=fopen(fname, "w"))==NULL){
+ get_key(fc, "Error opening file.");
+ return;
+ }
+ for (i=0; i<HEIGHT; i++){
+ fprintf(fout, "%s\n", screen[i].s);
+ }
+ fclose(fout);
+ modified = 0;
+ get_key(fc, "File saved.");
+}
+
+void check_modified(FILE *fc){
+
+ if (modified){
+ if (!is_yes(get_key(fc, "Unsaved changes. Write to file [y/n]?")) ){
+ return;
+ }
+ write_file(fc);
+ }
+}
+
+void load_file(FILE *fc){
+
+ char newfname[256];
+ FILE *fin;
+ int i;
+
+ get_string(fc, "Load file: ", newfname, 255);
+ if ((fin=fopen(newfname, "r")) != NULL){
+ i = 0;
+ while((fgets(screen[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT)
+ screen[i++].s[WIDTH-1]='\0';
+ for(;i<HEIGHT; i++){
+ erase_line(screen[i].s);
+ }
+ fclose(fin);
+ }
+ strcpy(fname, newfname);
+ modified=0;
+ redraw();
+}
+
+void new_file(FILE *fc){
+ check_modified(fc);
+ erase_screen();
+ go_to(HOME);
+ redraw();
+ fname[0] = '\0';
+ modified=0;
+}
+
diff --git a/gramscii.c b/gramscii.c
deleted file mode 100644
index 4342da9..0000000
--- a/gramscii.c
+++ /dev/null
@@ -1,1057 +0,0 @@
-/*
-*
-* gramscii: a simple editor for ASCII box-and-arrow charts
-*
-* Copyright (c) 2019 Vincenzo "KatolaZ" Nicosia <katolaz@freaknet.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. Please see the attached file COPYING.
-* Otherwise, please visit <https://www.gnu.org/licenses/>.
-*
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <termios.h>
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <ctype.h>
-
-#include "config.h"
-
-#include "arg.h"
-
-typedef struct{
- int sz;
- int lst;
- char *s;
-} line_t;
-
-
-#define MOVE 0x00
-#define BOX 0x01
-#define ARROW 0x02
-#define TEXT 0x04
-#define DEL 0x08
-#define VIS 0x10
-
-#define DIR_N 0x00
-#define DIR_R 0x01
-#define DIR_U 0x02
-#define DIR_D 0x04
-#define DIR_L 0x08
-
-#define DIR_HOR (DIR_R | DIR_L)
-#define DIR_VER (DIR_D | DIR_U)
-
-
-#define NOFIX 0x0
-#define FIX 0x1
-
-#define BG ' '
-#define PTR '+'
-#define UND '_'
-#define ARR_L '<'
-#define ARR_R '>'
-#define ARR_U '^'
-#define ARR_D 'v'
-
-#define HOME 0x01
-#define END 0x02
-#define MIDDLE 0x04
-
-#define VIDEO_NRM 0
-#define VIDEO_REV 7
-
-#define MIN(x,y) (x) < (y) ? (x) : (y)
-#define MAX(x,y) (x) > (y) ? (x) : (y)
-
-#define DEBUG 1
-
-line_t *screen;
-int num_lines;
-int WIDTH, HEIGHT;
-
-int state;
-int dir;
-int x;
-int y;
-int step;
-int mult;
-int force_new;
-char cursor;
-char corner;
-
-int hlines_sz= sizeof(hlines) -1;
-int vlines_sz= sizeof(vlines) -1;
-int corners_sz = sizeof(corners) -1;
-int stmarks_sz = sizeof(st_marks) - 1;
-int endmarks_sz = sizeof(st_marks) - 1;
-
-int cur_hl, cur_vl, cur_corn, cur_start, cur_end;
-char line_h;
-char line_v;
-char mark_st;
-char mark_end;
-
-char modified;
-char fname[256];
-
-char visual;
-char silent;
-char autoend;
-
-char *argv0;
-
-struct termios t1, t2, t3;
-
-
-void dump_lines(){
- int i;
- for (i=0; i<HEIGHT; i++){
- printf("%s\n", screen[i].s);
- }
-}
-
-void cleanup(int s){
-
- if (!silent)
- printf("\033[;H\033[2J");
- if (silent)
- dump_lines();
- tcsetattr(0, TCSANOW, &t1);
- fflush(stdout);
- exit(0);
-}
-
-void exit_cleanup(void){
- cleanup(0);
-}
-
-
-/*** Status bar ***/
-
-char* state_str(){
- switch(state){
- case MOVE:
- return "mov";
- case TEXT:
- return "txt";
- case BOX:
- return "box";
- case ARROW:
- return "arr";
- case DEL:
- return "del";
- case VIS:
- return "vis";
- default:
- return "ERR";
- }
- return "ERR";
-}
-
-char get_mark(char dir){
- switch(dir){
- case DIR_U:
- return '^';
- case DIR_D:
- return 'v';
- case DIR_L:
- return '<';
- case DIR_R:
- return '>';
- }
- return '>';
-}
-
-
-void status_bar(){
-
- if (silent)
- return;
- printf("\033[%d;1f\033[7m", HEIGHT+1);
- printf("%*s", WIDTH-1, "");
- printf("\033[%d;1f\033[7m", HEIGHT+1);
- printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s",
- x, y, state_str(), line_h, line_v, corner, mark_st, mark_end, "");
- if (!modified)
- printf(" [%s]", fname );
- else
- printf(" *%s*", fname );
-#ifdef DEBUG
- printf(" '%d' ", screen[y].s[x]);
-#endif
- printf("\033[0m");
- fflush(stdout);
-}
-
-char get_key(FILE *fc, char *msg){
-
- if (silent)
- return 0;
- printf("\033[%d;1f\033[7m", HEIGHT+1);
- printf("%*s", WIDTH, "");
- printf("\033[%d;1f\033[7m", HEIGHT+1);
- printf("%s", msg);
- fflush(stdout);
- printf("\033[0m");
- fflush(stdout);
- return fgetc(fc);
-}
-
-void get_string(FILE *fc, char *msg, char *s, int sz){
-
- if (!silent){
- printf("\033[%d;1f\033[7m", HEIGHT+1);
- printf("%*s", WIDTH, "");
- printf("\033[%d;1f\033[7m", HEIGHT+1);
-
- /* We must activate echo now */
- t3 = t2;
- t3.c_lflag |= (ECHO | ICANON);
- tcsetattr(0, TCSANOW, &t3);
- printf("%s", msg);
- printf("\033[0m");
- }
- fgets(s, sz, fc);
- s[strlen(s)-1] = '\0';
- tcsetattr(0, TCSANOW, &t2);
- if (!silent)
- fflush(stdout);
-}
-
-int is_yes(char c){
- return c=='y' ? 1 : c == 'Y'? 1 : 0;
-}
-
-/*** Screen management ***/
-
-void show_cursor(){
- if (silent)
- return;
- printf("\033[%d;%df", y+1, x+1);
- fflush(stdout);
-}
-
-
-void set_xy(int _x, int _y, char c){
- line_t *tmp;
- if (_y >= num_lines){
- tmp = realloc(screen, (_y + LONG_STEP)* sizeof(line_t));
- if (tmp == NULL){
- fprintf(stderr, "Unable to allocate memory for more lines");
- exit(1);
- }
- else while ( num_lines < _y + LONG_STEP){
- screen[num_lines].sz = WIDTH+1;
- screen[num_lines].s = malloc((screen[num_lines].sz) * sizeof(char));
- if (screen[num_lines].s == NULL){
- perror("allocating screen[num_lines].s");
- exit(1);
- }
- memset(screen[num_lines].s, BG, screen[num_lines].sz);
- screen[num_lines].lst = 0;
- screen[num_lines].s[screen[num_lines].lst+1]='\0';
- num_lines ++;
- }
- }
- if (screen[_y].sz < _x + 2){
- screen[_y].sz = (_x +2) * 2;
- screen[_y].s = realloc(screen[_y].s, screen[_y].sz * sizeof(char));
- }
- while (screen[_y].lst<_x){
- screen[_y].lst ++;
- screen[_y].s[screen[_y].lst] = BG;
- }
- screen[_y].s[_x] = c;
- if (_x == screen[_y].lst)
- screen[_y].s[_x+1] = '\0';
-}
-
-void set_cur(char c){
- set_xy(x, y, c);
-}
-
-void draw_xy(int x, int y, char c){
- /* FIXME: check if x and y are valid!!!! */
- if (silent)
- return;
- printf("\033[%d;%df",y+1,x+1);
- putchar(c);
- fflush(stdout);
-}
-
-void update_current(){
- if (silent)
- return;
- printf("\033[%d'%df",y+1,x+1);
- putchar(screen[y].s[x]);
- fflush(stdout);
-}
-
-void erase_line(char *s){
- while(*s){
- *s = BG;
- s++;
- }
-}
-
-void erase_box(int x1, int y1, char c){
- int x_incr, y_incr, i;
-
- x_incr = x1 < x? +1: -1;
- y_incr = y1 < y? +1: -1;
- do{
- i = y1;
- do{
- set_xy(x1, i, c);
- } while(i != y && (1 | (i += y_incr)));
- } while(x1 != x && (1 | (x1 += x_incr)));
-
-}
-
-void erase_screen(){
- int i;
- for(i=0;i<HEIGHT; i++)
- erase_line(screen[i].s);
-}
-
-void check_bound(){
- if (x<0) x=0;
- else if (x>=WIDTH) x = WIDTH-1;
- if (y<0) y=0;
- else if (y>=HEIGHT) y = HEIGHT -1;
-}
-
-void reset_styles(){
-
- cur_corn = 0;
- corner = corners[0];
- cur_hl = cur_vl = 0;
- cur_start = cur_end = 0;
- line_h = hlines[cur_hl];
- line_v = vlines[cur_vl];
- mark_st = st_marks[cur_start];
- mark_end = end_marks[cur_end];
-}
-
-void redraw(){
- int i;
-
- if (silent)
- return;
- printf("\033[2J\033[1;1H");
- for (i=0;i<HEIGHT;i++){
- fprintf(stdout,"%s\n",screen[i].s);
- }
- status_bar();
- show_cursor();
-}
-
-void go_to(int where){
- switch(where){
- case HOME:
- x = y = 0;
- break;
- case END:
- x = WIDTH-1;
- y = HEIGHT-1;
- break;
- case MIDDLE:
- x = WIDTH/2;
- y = HEIGHT/2;
- break;
- }
- check_bound();
- show_cursor();
-}
-
-void handle_goto(){
- char c;
- c=getchar();
- switch(c){
- case 'h':
- dir = DIR_L;
- x = 0;
- break;
- case 'l':
- dir = DIR_R;
- x = WIDTH - 1;
- break;
- case 'j':
- dir = DIR_D;
- y = HEIGHT - 1;
- break;
- case 'k':
- dir = DIR_U;
- y = 0;
- break;
- case 'g':
- dir = DIR_N;
- go_to(HOME);
- break;
- case 'G':
- dir = DIR_N;
- go_to(END);
- break;
- case 'm':
- dir = DIR_N;
- go_to(MIDDLE);
- break;
- }
- check_bound();
- show_cursor();
-}
-
-
-int get_escape(FILE *fc){
- char c[4];
-
- c[0] = fgetc(fc);
- if (c[0] == '['){
- c[1] = fgetc(fc);
- switch(c[1]){
- case 'D':
- dir = DIR_L;
- x -= step;
- break;
- case 'B':
- dir = DIR_D;
- y += step;
- break;
- case 'A':
- dir = DIR_U;
- y -= step;
- break;
- case 'C':
- dir = DIR_R;
- x += step;
- break;
- }
- return 1;
- }
- else{
- ungetc(c[0], fc);
- return 0;
- }
-
-}
-
-
-int move_around(char c, FILE *fc){
-
- if (isdigit(c)){
- if (mult)
- mult *=10;
- mult += c - '0';
- return 0;
- }
- switch(c){
- case 27: /* control sequence? */
- c = get_escape(fc);
- break;
- case 'H': step = LONG_STEP;/** FALLTHROUGH **/
- case 'h':
- dir = DIR_L;
- if (mult)
- step *= mult;
- x -= step;
- break;
- case 'J': step = LONG_STEP;/** FALLTHROUGH **/
- case 'j':
- if (mult)
- step *= mult;
- dir = DIR_D;
- y += step;
- break;
- case 'K': step = LONG_STEP;/** FALLTHROUGH **/
- case 'k':
- if (mult)
- step *= mult;
- dir = DIR_U;
- y -= step;
- break;
- case 'L': step = LONG_STEP;/** FALLTHROUGH **/
- case 'l':
- if (mult)
- step *= mult;
- dir = DIR_R;
- x += step;
- break;
- case 'g':
- handle_goto();
- break;
- default:
- return 0;
- }
- mult = 0;
- return c;
-}
-
-int progr_x(int dir){
- return dir == DIR_L ? -1 : dir == DIR_R ? 1: 0;
-}
-
-
-int progr_y(int dir){
- return dir == DIR_U ? -1 : dir == DIR_D ? 1: 0;
-}
-
-void set_video(int v){
- if (silent)
- return;
- printf("\033[%dm", v);
- fflush(stdout);
-}
-
-/*** Lines and markers ***/
-
-void toggle_hline(){
-
- cur_hl = (cur_hl + 1) % hlines_sz;
- line_h = hlines[cur_hl];
-
-}
-
-void toggle_corner(){
-
- cur_corn = (cur_corn + 1 ) % corners_sz;
- corner = corners[cur_corn];
-
-}
-
-void toggle_vline(){
-
- cur_vl = (cur_vl + 1) % vlines_sz;
- line_v = vlines[cur_vl];
-
-}
-
-void toggle_st_mark(){
-
- cur_start = (cur_start + 1 ) % stmarks_sz;
- mark_st = st_marks[cur_start];
-}
-
-void toggle_end_mark(){
-
- cur_end = (cur_end+ 1 ) % endmarks_sz;
- mark_end = end_marks[cur_end];
-}
-
-int change_style(char c){
- switch(c){
- case '-':
- toggle_hline();
- break;
- case '|':
- toggle_vline();
- break;
- case '+':
- toggle_corner();
- break;
- case '<':
- toggle_st_mark();
- break;
- case '>':
- toggle_end_mark();
- break;
- case '.':
- reset_styles();
- break;
- default:
- return 0;
- }
- return c;
-}
-
-
-
-
-/***** text, box, arrows *****/
-
-void get_text(FILE *fc){
- char c;
- int orig_x = x;
-
- redraw();
- while((c=fgetc(fc))!=EOF && c != 27){
- if(c=='\n'){
- set_cur(BG);
- y += 1;
- x = orig_x;
- }
- else {
- set_cur(c);
- update_current();
- modified = 1;
- x += 1;
- if (x >= WIDTH)
- x = orig_x;
- }
- check_bound();
- status_bar();
- show_cursor();
- }
- state=MOVE;
-}
-
-void draw_box(int x1, int y1, int fix){
-
- int xmin, ymin, xmax, ymax;
- int i;
- void (*f)(int, int, char);
-
- if (fix == FIX)
- f = set_xy;
- else
- f = draw_xy;
-
- xmin = MIN(x, x1);
- xmax = MAX(x, x1);
- ymin = MIN(y, y1);
- ymax = MAX(y, y1);
-
- for(i=xmin+1; i<=xmax; i++){
- f(i, ymin, line_h);
- f(i, ymax, line_h);
- }
- for(i=ymin+1; i<=ymax; i++){
- f(xmin, i, line_v);
- f(xmax, i, line_v);
- }
- f(xmin, ymin, corner);
- f(xmin, ymax, corner);
- f(xmax, ymin, corner);
- f(xmax, ymax, corner);
- show_cursor();
-}
-
-void get_box(FILE *fc){
- char c;
- int orig_x=x, orig_y=y;
- redraw();
- step = 1;
- draw_box(x,y,NOFIX);
- while((c=fgetc(fc))!=EOF && c != 27 && c!= 'b' && c != '\n'){
- if (change_style(c))
- goto update_box;
- if (!move_around(c, fc))
- continue;
- check_bound();
- redraw();
- step = 1;
-update_box:
- draw_box(orig_x, orig_y, NOFIX);
- status_bar();
- show_cursor();
- }
- if (c == 'b' || c == '\n'){
- draw_box(orig_x, orig_y, FIX);
- modified = 1;
- }
- redraw();
- state = MOVE;
-}
-
-void draw_arrow(int x, int y, char *a, int a_len, int fix){
-
- int i, j, cur_dir;
- char line;
- void (*f)(int, int, char);
-
-
- if (fix == FIX)
- f = set_xy;
- else
- f = draw_xy;
-
- f(x,y,mark_st);
- if (!a_len){
- show_cursor();
- return;
- }
- cur_dir=DIR_N;
- for (i=0; i<a_len; i+=2){
- if (i>0) {
- /* If we are switching between horizontal and vertical, put a "corner" */
- if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) ||
- ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){
- f(x,y,corner);
- show_cursor();
- }
- }
- for(j=0; j<a[i+1]; j++){
- line = (a[i] & DIR_L) || (a[i] & DIR_R) ? line_h : line_v;
- x += progr_x(a[i]);
- y += progr_y(a[i]);
- f(x, y, line);
- }
- /* f(x,y,mark_end);*/
- cur_dir = a[i];
- }
- if (autoend){
- if (cur_dir != DIR_N)
- f(x,y, get_mark(cur_dir));
- }
- else
- f(x,y,mark_end);
- show_cursor();
-}
-
-void get_arrow(FILE *fc){
-
- char c;
- int orig_x=x, orig_y=y, arrow_len;
- static char *arrow = NULL;
- static int arrow_sz;
-
- if (!arrow){
- arrow_sz = 100;
- arrow = malloc(arrow_sz * sizeof(char));
- }
- arrow_len = 0;
- dir = DIR_N;
-
- redraw();
- step = 1;
- draw_arrow(x,y, arrow, 0, NOFIX);
- while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){
- if (change_style(c))
- goto update_arrow;
- if (!move_around(c, fc))
- continue;
- check_bound();
- /* FIXME: if we are out of bound, do nothing? */
- if (arrow_len == arrow_sz){
- arrow_sz *=2;
- arrow = realloc(arrow, arrow_sz * sizeof(char));
- }
- arrow[arrow_len++] = dir;
- arrow[arrow_len++] = step;
- redraw();
- step = 1;
-update_arrow:
- draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX);
- status_bar();
- show_cursor();
- }
- if (c == 'a' || c == '\n'){
- draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX);
- modified = 1;
- }
- redraw();
- state = MOVE;
-}
-
-
-void do_delete(int x1, int y1){
- int i;
- switch(dir){
- case DIR_R:
- for(i=x1; i<=x; i++) set_xy(i,y,BG);
- break;
- case DIR_L:
- for(i=x1; i>=x; i--) set_xy(i,y,BG);
- break;
- case DIR_U:
- for(i=y1; i>=y; i--) set_xy(x,i,BG);
- break;
- case DIR_D:
- for(i=y1; i<=y; i++) set_xy(x,i,BG);
- break;
- }
-}
-
-
-void delete(FILE *fc){
- char c;
- int orig_x = x, orig_y = y;
- status_bar();
- show_cursor();
- while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){
- if (!move_around(c, fc)) continue;
- check_bound();
- do_delete(orig_x, orig_y);
- step = 1;
- modified = 1;
- orig_x = x;
- orig_y = y;
- redraw();
- status_bar();
- show_cursor();
- }
- state = MOVE;
-}
-
-/*** File management ***/
-
-void write_file(FILE *fc){
- FILE *fout;
- int i;
-
- if (!fname[0] || force_new){
- get_string(fc, "Write to: ", fname, 255);
- if ((fout=fopen(fname, "r"))!=NULL){
- if (!is_yes(get_key(fc,"File exists. Overwrite [y/n]?")) ){
- fclose(fout);
- return;
- }
- fclose(fout);
- }
- }
- if((fout=fopen(fname, "w"))==NULL){
- get_key(fc, "Error opening file.");
- return;
- }
- for (i=0; i<HEIGHT; i++){
- fprintf(fout, "%s\n", screen[i].s);
- }
- fclose(fout);
- modified = 0;
- get_key(fc, "File saved.");
-}
-
-void check_modified(FILE *fc){
-
- if (modified){
- if (!is_yes(get_key(fc, "Unsaved changes. Write to file [y/n]?")) ){
- return;
- }
- write_file(fc);
- }
-}
-
-void load_file(FILE *fc){
-
- char newfname[256];
- FILE *fin;
- int i;
-
- get_string(fc, "Load file: ", newfname, 255);
- if ((fin=fopen(newfname, "r")) != NULL){
- i = 0;
- while((fgets(screen[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT)
- screen[i++].s[WIDTH-1]='\0';
- for(;i<HEIGHT; i++){
- erase_line(screen[i].s);
- }
- fclose(fin);
- }
- strcpy(fname, newfname);
- modified=0;
- redraw();
-}
-
-void new_file(FILE *fc){
- check_modified(fc);
- erase_screen();
- go_to(HOME);
- redraw();
- fname[0] = '\0';
- modified=0;
-}
-
-/*** Visual ***/
-
-
-void visual_box(FILE *fc){
- int orig_x =x, orig_y = y;
- char c, f = BG;
-
- redraw();
- step = 1;
- set_video(VIDEO_REV);
- draw_box(x,y,NOFIX);
- while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){
- if (!move_around(c, fc)) switch(c){
- case 'f':/* fill */
- f = get_key(fc, "fill char: "); /** FALLTHROUGH **/
- case 'x':/* erase */
- erase_box(orig_x, orig_y, f);
- modified = 1;
- goto vis_exit;
- break;
- }
- check_bound();
- set_video(VIDEO_NRM);
- redraw();
- step = 1;
- f = BG;
- set_video(VIDEO_REV);
- draw_box(orig_x, orig_y, NOFIX);
- status_bar();
- show_cursor();
- }
-vis_exit:
- set_video(VIDEO_NRM);
- redraw();
- state = MOVE;
-}
-
-/*** Initialisation ***/
-
-void init_screen(){
- int i;
- struct winsize wsz;
-
- if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){
- WIDTH=wsz.ws_col - 2;
- HEIGHT=wsz.ws_row - 1;
- }
- else {
- WIDTH=80;
- HEIGHT=24;
- }
- screen = malloc(HEIGHT * sizeof(line_t));
- num_lines = HEIGHT;
- if (screen == NULL){
- perror("allocating screen");
- exit(1);
- }
- for (i=0; i<HEIGHT; i++){
- screen[i].sz = WIDTH+1;
- screen[i].s = malloc((screen[i].sz) * sizeof(char));
- if (screen[i].s == NULL){
- perror("allocating screen[i].s");
- exit(1);
- }
- memset(screen[i].s, BG, screen[i].sz);
- screen[i].lst = 0;
- screen[i].s[screen[i].lst+1]='\0';
- }
- reset_styles();
-}
-
-void init(){
-
- signal(SIGHUP, cleanup);
- signal(SIGINT, cleanup);
- signal(SIGTERM, cleanup);
- signal(SIGQUIT, cleanup);
- atexit(exit_cleanup);
-
- tcgetattr(0, &t1);
- t2 = t1;
- t2.c_lflag &= ~(ICANON | ECHO);
- tcsetattr(0, TCSANOW, &t2);
-
- init_screen();
- x = 0;
- y = 0;
- modified = 0;
- fname[0] = '\0';
- redraw();
-}
-
-
-/*** Commands ***/
-
-void commands(FILE *fc){
-
- char c;
- while((c=fgetc(fc))!=EOF){
- if (!change_style(c) && !move_around(c, fc)){
- switch(c){
- case 'i':
- state = TEXT;
- get_text(fc);
- break;
- case 'R':
- redraw();
- break;
- case 'b':
- state = BOX;
- get_box(fc);
- break;
- case 'A': autoend=1;
- case 'a':
- state = ARROW;
- get_arrow(fc);
- autoend = 0;
- break;
- case 'W':
- force_new = 1;/** FALLTHROUGH **/
- case 'w':
- write_file(fc);
- break;
- case 'e':
- check_modified(fc);/** FALLTHROUGH **/
- case 'E':
- load_file(fc);
- break;
- case 'N':
- new_file(fc);
- break;
- case 'x':
- state = DEL;
- delete(fc);
- break;
- case 'v':
- state = VIS;
- visual_box(fc);
- break;
- case 'q':
- check_modified(fc);/** FALLTHROUGH **/
- case 'Q':
- exit(0);
- break;
- }
- }
- check_bound();
- status_bar();
- show_cursor();
- step = 1;
- force_new = 0;
- }
-
-}
-
-void usage(){
- fprintf(stderr, "Usage: %s [-s] [-h] [file ...]\n", argv0);
- exit(1);
-}
-
-
-int main(int argc, char *argv[]){
- FILE *fc;
-
- ARGBEGIN {
- case 's':
- silent = 1;
- break;
- case 'h': /* FALLTHROUGH */
- default:
- usage();
- } ARGEND;
-
- init();
- while (argc){
- fc = fopen(argv[0], "r");
- if (fc == NULL){
- fprintf(stderr, "Error opening file %s\n", argv[0]);
- }
- else {
- commands(fc);
- fclose(fc);
- redraw();
- }
- argv++;
- argc--;
- }
- commands(stdin);
- return 0;
-}
diff --git a/gramscii.h b/gramscii.h
new file mode 100644
index 0000000..0b2c81f
--- /dev/null
+++ b/gramscii.h
@@ -0,0 +1,138 @@
+#ifndef __GRAMSCII_H__
+#define __GRAMSCII_H__
+
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+
+/** types **/
+
+typedef struct{
+ int sz;
+ int lst;
+ char *s;
+} line_t;
+
+/** constants **/
+
+#define MOVE 0x00
+#define BOX 0x01
+#define ARROW 0x02
+#define TEXT 0x04
+#define DEL 0x08
+#define VIS 0x10
+
+#define DIR_N 0x00
+#define DIR_R 0x01
+#define DIR_U 0x02
+#define DIR_D 0x04
+#define DIR_L 0x08
+
+#define DIR_HOR (DIR_R | DIR_L)
+#define DIR_VER (DIR_D | DIR_U)
+
+
+#define NOFIX 0x0
+#define FIX 0x1
+
+#define BG ' '
+#define PTR '+'
+#define UND '_'
+#define ARR_L '<'
+#define ARR_R '>'
+#define ARR_U '^'
+#define ARR_D 'v'
+
+#define HOME 0x01
+#define END 0x02
+#define MIDDLE 0x04
+
+#define VIDEO_NRM 0
+#define VIDEO_REV 7
+
+
+/** MACROS **/
+
+#define MIN(x,y) (x) < (y) ? (x) : (y)
+#define MAX(x,y) (x) > (y) ? (x) : (y)
+
+#define progr_x(d) ((d) == DIR_L ? -1 : (d) == DIR_R ? 1 : 0)
+#define progr_y(d) ((d) == DIR_U ? -1 : (d) == DIR_D ? 1 : 0)
+
+/* #define DEBUG 1 */
+
+/** global variables **/
+
+line_t *screen;
+int num_lines;
+int WIDTH, HEIGHT;
+
+int mode;
+int dir;
+int x;
+int y;
+int step;
+int mult;
+int force_new;
+char cursor;
+char corner;
+
+int hlines_sz;
+int vlines_sz;
+int corners_sz;
+int stmarks_sz;
+int endmarks_sz;
+
+int cur_hl, cur_vl, cur_corn, cur_start, cur_end;
+char line_h;
+char line_v;
+char mark_st;
+char mark_end;
+
+char modified;
+char fname[256];
+
+char visual;
+char silent;
+char autoend;
+
+struct termios t1, t2, t3;
+
+/** screen-related functions **/
+void reset_styles();
+void redraw();
+int move_around(char c, FILE *fc);
+void check_bound();
+void status_bar();
+void show_cursor();
+void set_cur(char c);
+void update_current();
+void set_xy(int _x, int _y, char c);
+void draw_xy(int x, int y, char c);
+char get_mark(char dir);
+void set_video(int v);
+char get_key(FILE *fc, char *msg);
+void get_string(FILE *fc, char *msg, char *s, int sz);
+void erase_box(int x1, int y1, char c);
+int is_yes(char c);
+void init_screen();
+void erase_line(char *s);
+void erase_screen();
+void go_to(int where);
+
+/** drawing-related functions **/
+int change_style(char c);
+void get_text(FILE *fc);
+void get_box(FILE *fc);
+void get_arrow(FILE *fc);
+void erase(FILE *fc);
+void visual_box(FILE *fc);
+
+/** file-related functions **/
+void write_file(FILE *fc);
+void check_modified(FILE *fc);
+void load_file(FILE *fc);
+void new_file(FILE *fc);
+
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..c0f4b6b
--- /dev/null
+++ b/main.c
@@ -0,0 +1,175 @@
+/*
+*
+* gramscii: a simple editor for ASCII box-and-arrow charts
+*
+* Copyright (c) 2019 Vincenzo "KatolaZ" Nicosia <katolaz@freaknet.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. Please see the attached file COPYING.
+* Otherwise, please visit <https://www.gnu.org/licenses/>.
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "arg.h"
+#include "gramscii.h"
+
+
+char *argv0;
+
+void dump_lines(){
+ int i;
+ for (i=0; i<HEIGHT; i++){
+ printf("%s\n", screen[i].s);
+ }
+}
+
+void cleanup(int s){
+
+ if (!silent)
+ printf("\033[;H\033[2J");
+ else
+ dump_lines();
+ tcsetattr(0, TCSANOW, &t1);
+ fflush(stdout);
+ exit(0);
+}
+
+void exit_cleanup(void){
+ cleanup(0);
+}
+
+/*** Initialisation ***/
+
+void init(){
+
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGQUIT, cleanup);
+ atexit(exit_cleanup);
+
+ tcgetattr(0, &t1);
+ t2 = t1;
+ t2.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(0, TCSANOW, &t2);
+
+ init_screen();
+ x = 0;
+ y = 0;
+ modified = 0;
+ fname[0] = '\0';
+ redraw();
+}
+
+
+/*** Commands ***/
+
+void commands(FILE *fc){
+
+ char c;
+ while((c=fgetc(fc))!=EOF){
+ if (!change_style(c) && !move_around(c, fc)){
+ switch(c){
+ case 'i':
+ mode = TEXT;
+ get_text(fc);
+ break;
+ case 'R':
+ redraw();
+ break;
+ case 'b':
+ mode = BOX;
+ get_box(fc);
+ break;
+ case 'A': autoend=1;
+ case 'a':
+ mode = ARROW;
+ get_arrow(fc);
+ autoend = 0;
+ break;
+ case 'W':
+ force_new = 1;/** FALLTHROUGH **/
+ case 'w':
+ write_file(fc);
+ break;
+ case 'e':
+ check_modified(fc);/** FALLTHROUGH **/
+ case 'E':
+ load_file(fc);
+ break;
+ case 'N':
+ new_file(fc);
+ break;
+ case 'x':
+ mode = DEL;
+ erase(fc);
+ break;
+ case 'v':
+ mode = VIS;
+ visual_box(fc);
+ break;
+ case 'q':
+ check_modified(fc);/** FALLTHROUGH **/
+ case 'Q':
+ exit(0);
+ break;
+ }
+ }
+ check_bound();
+ status_bar();
+ show_cursor();
+ step = 1;
+ force_new = 0;
+ }
+
+}
+
+void usage(){
+ fprintf(stderr, "Usage: %s [-s] [-h] [file ...]\n", argv0);
+ exit(1);
+}
+
+
+int main(int argc, char *argv[]){
+ FILE *fc;
+
+ ARGBEGIN {
+ case 's':
+ silent = 1;
+ break;
+ case 'h': /* FALLTHROUGH */
+ default:
+ usage();
+ } ARGEND;
+
+ init();
+ while (argc){
+ fc = fopen(argv[0], "r");
+ if (fc == NULL){
+ fprintf(stderr, "Error opening file %s\n", argv[0]);
+ }
+ else {
+ commands(fc);
+ fclose(fc);
+ redraw();
+ }
+ argv++;
+ argc--;
+ }
+ commands(stdin);
+ return 0;
+}
diff --git a/screen.c b/screen.c
new file mode 100644
index 0000000..2e61acf
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,419 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "gramscii.h"
+#include "config.h"
+
+/*** screen management functions ***/
+
+
+/*** Status bar ***/
+
+char* mode_str(){
+ switch(mode){
+ case MOVE:
+ return "mov";
+ case TEXT:
+ return "txt";
+ case BOX:
+ return "box";
+ case ARROW:
+ return "arr";
+ case DEL:
+ return "del";
+ case VIS:
+ return "vis";
+ default:
+ return "ERR";
+ }
+ return "ERR";
+}
+
+char get_mark(char dir){
+ switch(dir){
+ case DIR_U:
+ return '^';
+ case DIR_D:
+ return 'v';
+ case DIR_L:
+ return '<';
+ case DIR_R:
+ return '>';
+ }
+ return '>';
+}
+
+
+void status_bar(){
+
+ if (silent)
+ return;
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+ printf("%*s", WIDTH-1, "");
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+ printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s",
+ x, y, mode_str(), line_h, line_v, corner, mark_st, mark_end, "");
+ if (!modified)
+ printf(" [%s]", fname );
+ else
+ printf(" *%s*", fname );
+#ifdef DEBUG
+ printf(" '%d' ", screen[y].s[x]);
+#endif
+ printf("\033[0m");
+ fflush(stdout);
+}
+
+char get_key(FILE *fc, char *msg){
+
+ if (silent)
+ return 0;
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+ printf("%*s", WIDTH, "");
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+ printf("%s", msg);
+ fflush(stdout);
+ printf("\033[0m");
+ fflush(stdout);
+ return fgetc(fc);
+}
+
+void get_string(FILE *fc, char *msg, char *s, int sz){
+
+ if (!silent){
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+ printf("%*s", WIDTH, "");
+ printf("\033[%d;1f\033[7m", HEIGHT+1);
+
+ /* We must activate echo now */
+ t3 = t2;
+ t3.c_lflag |= (ECHO | ICANON);
+ tcsetattr(0, TCSANOW, &t3);
+ printf("%s", msg);
+ printf("\033[0m");
+ }
+ fgets(s, sz, fc);
+ s[strlen(s)-1] = '\0';
+ tcsetattr(0, TCSANOW, &t2);
+ if (!silent)
+ fflush(stdout);
+}
+
+int is_yes(char c){
+ return c=='y' ? 1 : c == 'Y'? 1 : 0;
+}
+
+/*** Screen management ***/
+
+void show_cursor(){
+ if (silent)
+ return;
+ printf("\033[%d;%df", y+1, x+1);
+ fflush(stdout);
+}
+
+
+void set_xy(int _x, int _y, char c){
+ line_t *tmp;
+ if (_y >= num_lines){
+ tmp = realloc(screen, (_y + LONG_STEP)* sizeof(line_t));
+ if (tmp == NULL){
+ fprintf(stderr, "Unable to allocate memory for more lines");
+ exit(1);
+ }
+ else while ( num_lines < _y + LONG_STEP){
+ screen[num_lines].sz = WIDTH+1;
+ screen[num_lines].s = malloc((screen[num_lines].sz) * sizeof(char));
+ if (screen[num_lines].s == NULL){
+ perror("allocating screen[num_lines].s");
+ exit(1);
+ }
+ memset(screen[num_lines].s, BG, screen[num_lines].sz);
+ screen[num_lines].lst = 0;
+ screen[num_lines].s[screen[num_lines].lst+1]='\0';
+ num_lines ++;
+ }
+ }
+ if (screen[_y].sz < _x + 2){
+ screen[_y].sz = (_x +2) * 2;
+ screen[_y].s = realloc(screen[_y].s, screen[_y].sz * sizeof(char));
+ }
+ while (screen[_y].lst<_x){
+ screen[_y].lst ++;
+ screen[_y].s[screen[_y].lst] = BG;
+ }
+ screen[_y].s[_x] = c;
+ if (_x == screen[_y].lst)
+ screen[_y].s[_x+1] = '\0';
+}
+
+void set_cur(char c){
+ set_xy(x, y, c);
+}
+
+void draw_xy(int x, int y, char c){
+ /* FIXME: check if x and y are valid!!!! */
+ if (silent)
+ return;
+ printf("\033[%d;%df",y+1,x+1);
+ putchar(c);
+ fflush(stdout);
+}
+
+void update_current(){
+ if (silent)
+ return;
+ printf("\033[%d'%df",y+1,x+1);
+ putchar(screen[y].s[x]);
+ fflush(stdout);
+}
+
+void erase_line(char *s){
+ while(*s){
+ *s = BG;
+ s++;
+ }
+}
+
+void erase_box(int x1, int y1, char c){
+ int x_incr, y_incr, i;
+
+ x_incr = x1 < x? +1: -1;
+ y_incr = y1 < y? +1: -1;
+ do{
+ i = y1;
+ do{
+ set_xy(x1, i, c);
+ } while(i != y && (1 | (i += y_incr)));
+ } while(x1 != x && (1 | (x1 += x_incr)));
+
+}
+
+void erase_screen(){
+ int i;
+ for(i=0;i<HEIGHT; i++)
+ erase_line(screen[i].s);
+}
+
+void check_bound(){
+ if (x<0) x=0;
+ else if (x>=WIDTH) x = WIDTH-1;
+ if (y<0) y=0;
+ else if (y>=HEIGHT) y = HEIGHT -1;
+}
+
+void reset_styles(){
+
+ cur_corn = 0;
+ corner = corners[0];
+ cur_hl = cur_vl = 0;
+ cur_start = cur_end = 0;
+ line_h = hlines[cur_hl];
+ line_v = vlines[cur_vl];
+ mark_st = st_marks[cur_start];
+ mark_end = end_marks[cur_end];
+}
+
+void redraw(){
+ int i;
+
+ if (silent)
+ return;
+ printf("\033[2J\033[1;1H");
+ for (i=0;i<HEIGHT;i++){
+ fprintf(stdout,"%s\n",screen[i].s);
+ }
+ status_bar();
+ show_cursor();
+}
+
+void go_to(int where){
+ switch(where){
+ case HOME:
+ x = y = 0;
+ break;
+ case END:
+ x = WIDTH-1;
+ y = HEIGHT-1;
+ break;
+ case MIDDLE:
+ x = WIDTH/2;
+ y = HEIGHT/2;
+ break;
+ }
+ check_bound();
+ show_cursor();
+}
+
+void handle_goto(){
+ char c;
+ c=getchar();
+ switch(c){
+ case 'h':
+ dir = DIR_L;
+ x = 0;
+ break;
+ case 'l':
+ dir = DIR_R;
+ x = WIDTH - 1;
+ break;
+ case 'j':
+ dir = DIR_D;
+ y = HEIGHT - 1;
+ break;
+ case 'k':
+ dir = DIR_U;
+ y = 0;
+ break;
+ case 'g':
+ dir = DIR_N;
+ go_to(HOME);
+ break;
+ case 'G':
+ dir = DIR_N;
+ go_to(END);
+ break;
+ case 'm':
+ dir = DIR_N;
+ go_to(MIDDLE);
+ break;
+ }
+ check_bound();
+ show_cursor();
+}
+
+
+int get_escape(FILE *fc){
+ char c[4];
+
+ c[0] = fgetc(fc);
+ if (c[0] == '['){
+ c[1] = fgetc(fc);
+ switch(c[1]){
+ case 'D':
+ dir = DIR_L;
+ x -= step;
+ break;
+ case 'B':
+ dir = DIR_D;
+ y += step;
+ break;
+ case 'A':
+ dir = DIR_U;
+ y -= step;
+ break;
+ case 'C':
+ dir = DIR_R;
+ x += step;
+ break;
+ }
+ return 1;
+ }
+ else{
+ ungetc(c[0], fc);
+ return 0;
+ }
+
+}
+
+
+int move_around(char c, FILE *fc){
+
+ if (isdigit(c)){
+ if (mult)
+ mult *=10;
+ mult += c - '0';
+ return 0;
+ }
+ switch(c){
+ case 27: /* control sequence? */
+ c = get_escape(fc);
+ break;
+ case 'H': step = LONG_STEP;/** FALLTHROUGH **/
+ case 'h':
+ dir = DIR_L;
+ if (mult)
+ step *= mult;
+ x -= step;
+ break;
+ case 'J': step = LONG_STEP;/** FALLTHROUGH **/
+ case 'j':
+ if (mult)
+ step *= mult;
+ dir = DIR_D;
+ y += step;
+ break;
+ case 'K': step = LONG_STEP;/** FALLTHROUGH **/
+ case 'k':
+ if (mult)
+ step *= mult;
+ dir = DIR_U;
+ y -= step;
+ break;
+ case 'L': step = LONG_STEP;/** FALLTHROUGH **/
+ case 'l':
+ if (mult)
+ step *= mult;
+ dir = DIR_R;
+ x += step;
+ break;
+ case 'g':
+ handle_goto();
+ break;
+ default:
+ return 0;
+ }
+ mult = 0;
+ return c;
+}
+
+
+void set_video(int v){
+ if (silent)
+ return;
+ printf("\033[%dm", v);
+ fflush(stdout);
+}
+
+
+void init_screen(){
+ int i;
+ struct winsize wsz;
+
+ if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){
+ WIDTH=wsz.ws_col - 2;
+ HEIGHT=wsz.ws_row - 1;
+ }
+ else {
+ WIDTH=80;
+ HEIGHT=24;
+ }
+ screen = malloc(HEIGHT * sizeof(line_t));
+ num_lines = HEIGHT;
+ if (screen == NULL){
+ perror("allocating screen");
+ exit(1);
+ }
+ for (i=0; i<HEIGHT; i++){
+ screen[i].sz = WIDTH+1;
+ screen[i].s = malloc((screen[i].sz) * sizeof(char));
+ if (screen[i].s == NULL){
+ perror("allocating screen[i].s");
+ exit(1);
+ }
+ memset(screen[i].s, BG, screen[i].sz);
+ screen[i].lst = 0;
+ screen[i].s[screen[i].lst+1]='\0';
+ }
+ hlines_sz= sizeof(hlines) -1;
+ vlines_sz= sizeof(vlines) -1;
+ corners_sz = sizeof(corners) -1;
+ stmarks_sz = sizeof(st_marks) - 1;
+ endmarks_sz = sizeof(st_marks) - 1;
+ reset_styles();
+}
+