Advertisement
Guest User

text-ed.c

a guest
Apr 1st, 2019
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.93 KB | None | 0 0
  1.  
  2. /** text-ed.c - stupid little text editor, line command terminal interface
  3.               - enter ".l" to insert a literal newline
  4.               - enter ".." to insert a literal period (period is command escape character)
  5.               - enter ".?" for additional help
  6.               - cursor movements with ".[" and ".]" and ".p" and ".n"
  7.               - this is the poorest text editor you have ever seen! and awkward to use...
  8.  
  9.               - Public Domain, do what you want with it!
  10.               --> this program can be difficult to debug, beware edge cases, all manner of edge cases.
  11.                   every new feature improves usability.
  12.                   NOTE: not usable enough to be comfortable to use yet however.
  13.               --> you may want to have a mini scratch buffer and insert strings instead of single chars.
  14.                   that would introduce new possible edge cases but improve effeciency.
  15.               --> you may want to have simple string search
  16.  
  17.               initial release by Wayne Colvin, programmed aprox. Fri? Mar 29 2019  - Mon, Apr 1 2019.
  18.  **/
  19.  
  20. #include <stdio.h>  /* printf(), puts(), getc(), fgets(), EOF */
  21. #include <stdlib.h> /* exit() */
  22. #include <ctype.h>  /* isdigit(), isblank() */
  23.  
  24. #define BLOCK 1024
  25. #define BUFSZ (1024 * BLOCK)
  26.  
  27. #define SHOWLINES 21
  28. #define CMD '.'
  29.  
  30. unsigned char TEXT[BUFSZ];
  31. int pt  = 0;
  32. int len = 0;
  33. int column = 0;
  34.  
  35. /* sticky column is for moving up/down lines, managed in command interface */
  36. int col_sticky = -1;
  37.  
  38. char *filename = NULL;
  39. char name_buffer[BLOCK];
  40.  
  41. /*----- buffer management -----*/
  42.  
  43. void insert(int ch, int pos) {
  44.     int i;
  45.     i = BUFSZ-1;
  46.     while(i--) if(i <= pos) break; else TEXT[i] = TEXT[i-1];
  47.     TEXT[pos] = ch;
  48.     if(len < BUFSZ) len++;
  49.     if(pt < BUFSZ-1) pt++;
  50.     return;
  51. }
  52.  
  53. void delete(int pos) {
  54.     int i;
  55.     for(i = pos; i < BUFSZ-1; i++) TEXT[i] = TEXT[i+1];
  56.     if(pos >= len) return;
  57.     else len--;
  58.     return;
  59. }
  60.  
  61. /* this function uses an OUT parameter for secondary info */
  62. /* NULL accepted if info not needed                       */
  63.  
  64. int get_line_col(int pos, int *col) {
  65.     int i, c = '\n', line = 0;
  66.     int dummy[1];
  67.    
  68.     if(!col) col = dummy;
  69.     *col = 1;
  70.    
  71.     if(!len) { *col = 0; return 0; }
  72.    
  73.     for(i = 0; i <= pos; i++) {
  74.         *col = *col + 1;
  75.         if(c == '\n') { line++; *col = 1; }
  76.         c = TEXT[i];
  77.     }
  78.     return line;
  79. }
  80.  
  81. int get_startline(int pos) {
  82.     while(pos--) if(TEXT[pos] == '\n') return pos+1;
  83.     return 0;
  84. }
  85.  
  86. int get_endline(int pos) {
  87.     pos++;
  88.     while(pos < len) if(TEXT[pos] == '\n') return pos; else pos++;
  89.     return len;
  90. }
  91.  
  92. int get_upline(int pos) {
  93.     int i;
  94.     if(col_sticky < 0) get_line_col(pos, &col_sticky);
  95.     pos = get_startline(pos);
  96.     pos--;
  97.     if(pos < 0) pos = 0;
  98.     pos = get_startline(pos);
  99.     for(i = pos; i < pos+col_sticky; i++) { if(TEXT[i] == '\n') return i; }
  100.     return pos + col_sticky - 1;
  101. }
  102.  
  103. int get_downline(int pos) {
  104.     int i;
  105.     if(col_sticky < 0) get_line_col(pos, &col_sticky);
  106.    
  107.     /* we might already be at end of current line */
  108.     if(TEXT[pos] != '\n') pos = get_endline(pos);
  109.     pos++;
  110.    
  111.     if(pos + col_sticky >= len) return len;
  112.     for(i = pos; i < pos+col_sticky; i++) { if(TEXT[i] == '\n') return i; }
  113.     return pos + col_sticky - 1;
  114. }
  115.  
  116. /* this should be rewriten if text buffer used better data structures */
  117.  
  118. int seek_line(int n) {
  119.     int i, x = 0, c = '\n';
  120.     if(n < 1) return 0;
  121.     for(i = 0; i < len; i++) {
  122.         if(c == '\n') {
  123.             x++;
  124.             if(x == n) return i;
  125.         }
  126.         c = TEXT[i]; /* don't forget this part */
  127.     }
  128.     return len;
  129. }
  130.  
  131. /*----- text buffer file I/O -----*/
  132.  
  133. int load_file(char *fn) {
  134.     int c;
  135.     FILE *f = fopen(fn, "r");
  136.     if(!f) return -1;
  137.     c = fgetc(f);
  138.     len = 0; /* global variable */
  139.     while(c != EOF) {
  140.         TEXT[len++] = c;
  141.         if(len >= BUFSZ) { puts("file too big!"); fclose(f); exit(1); }
  142.         c = getc(f);
  143.     }
  144.     fclose(f);
  145.     column = 0; col_sticky = -1;
  146.     return 1;
  147. }
  148.  
  149. int write_file(char *fn) {
  150.     int i, sz = 0;;
  151.     FILE *f = fopen(fn, "w"); /* an empty filename doesn't create a temporary file, C doesn't work that way... */
  152.     if(!f) return -1;
  153.     for(i = 0; i < len; i++) { putc(TEXT[i], f); sz++; }
  154.     return sz;
  155. }
  156.  
  157. /*----- terminal/console interface -----*/
  158.  
  159. char *RED       = "\033[31m"; /* matching paren XXX */
  160. char *BG_RED    = "\033[41m"; /* cursor position (except EOF */
  161. char *YELLOW    = "\033[33m"; /* line numbers */
  162. char *BLUE      = "\033[34m"; /* control-chars and dummy line */
  163. char *BG_BLUE   = "\033[44m"; /* EOF cursor */
  164. char *CYAN      = "\033[36m"; /* alert / notice  */
  165. char *RESET  = "\033[0m";
  166. char *EMPTY  = "";
  167.  
  168.  
  169. /* the constant strings above aren't "compile-time constants", but toggle_ansi() still works */
  170. char *CURSOR="", *CURSOR_EOF="", *LINE="", *LINE_DUMMY="", *CTRL="", *ALERT="", *OFF="";
  171. int ANSI = 0;
  172.  
  173. void toggle_ansi() {
  174.     if(ANSI) { CURSOR=EMPTY; CURSOR_EOF=EMPTY; LINE=EMPTY; LINE_DUMMY=EMPTY; CTRL=EMPTY; ALERT=EMPTY; OFF=EMPTY; ANSI=0; }
  175.     else { CURSOR=BG_RED; CURSOR_EOF=BG_BLUE; LINE=YELLOW; LINE_DUMMY=BLUE; CTRL=BLUE; ALERT=CYAN; OFF=RESET; ANSI=1; }
  176.     return;
  177. }
  178.  
  179.  
  180. void print_char(c) {
  181.     char ctrl_char[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
  182.     if(c < sizeof(ctrl_char)-1) printf("^%c", ctrl_char[c]);
  183.     else printf("%c", c);
  184.     return;
  185. }
  186.  
  187. void print_text_view(int pos) {
  188.     int i, prev, c, num;
  189.     int line, total, before, after;
  190.  
  191.     line = get_line_col(pos, &column);
  192.     total = get_line_col(len-1, NULL); /* Note: line can exceed total when at EOF */
  193.    
  194.     before = line - (SHOWLINES / 2); if(before < 1) before = 1;
  195.     after  = before + SHOWLINES;     if(after > total) after = total;
  196.    
  197.     /* greater-than is possible if cursor is at EOF */
  198.     if(total <= after) { after = total; before = after - SHOWLINES; if(before < 1) before = 1; }
  199.    
  200.     c = '\n'; num = 0;
  201.     printf("%s", "\n");
  202.  
  203.     for(i = 0; i < len; i++) {
  204.         prev = c; c = TEXT[i];
  205.         if(prev == '\n') num++;
  206.  
  207.         if(num < before || after < num) continue;
  208.         if(prev == '\n') printf("%s%3d%s ", LINE, num, OFF);
  209.        
  210.         if(i != pos) { if(c < ' ') { printf("%s", CTRL); print_char(c); printf("%s", OFF); } else print_char(c); }
  211.         else { printf("%s", CURSOR); print_char(c); printf("%s", OFF); }
  212.        
  213.         if(c == '\n') printf("%s", "\n");
  214.     }
  215.  
  216.     if(!len || TEXT[len-1] == '\n') { printf("%s%c  %s ", LINE_DUMMY, '~', OFF); }
  217.     if(pos >= len) printf("%s$%s", CURSOR_EOF, OFF);
  218.     puts("");
  219.     printf("%schar %d/%d line %d/%d col=%d %s|", ALERT, pos, len, line, total, column, OFF);
  220.     printf(" %sfile='%s' %s|", ALERT, filename, OFF);
  221.     printf(" press '%s.?%s' for help.\n", ALERT, OFF);
  222.     return;
  223. }
  224.  
  225. int eat_line() {
  226.     int ch;
  227.     while(1) {
  228.         ch = getc(stdin);
  229.         if(ch == '\n' || ch == EOF) break;
  230.     }
  231.     return ch;
  232. }
  233.  
  234. /* C automatic stdout flushing requires the prompt to be written first */
  235. /* we usually use eat_line() before calling this to flush input line   */
  236. void pause() {
  237.     int ch;
  238.     puts(" [press ENTER to continue]");
  239.     ch = eat_line();
  240.     ungetc(ch, stdin);
  241.     return;
  242. }
  243.  
  244. void prompt_filename() {
  245.     int i;
  246.     eat_line();
  247.     printf("%snew filename please%s: ", ALERT, OFF);
  248.     /* name_buffer must be global */
  249.     fgets(name_buffer, sizeof(name_buffer)-1, stdin);
  250.     for(i = 0; i < BLOCK; i++) {
  251.         if(name_buffer[i] == '\n') name_buffer[i] = '\0';
  252.     }
  253.     return;
  254. }
  255.  
  256. /*----- command interface -----*/
  257.  
  258. int CMD_MODE;
  259. int X; /* repete multiplier */
  260.  
  261. void cmd(int *c) {
  262.     int f_size, tmp;
  263.    
  264.     /* most actions remove stickyness, restore for exceptions then */
  265.     tmp = col_sticky; col_sticky = -1;
  266.     if(X <= 0) X = 1;
  267.  
  268.     switch(*c) {
  269.         case 'q' : X = 1; col_sticky = tmp; putc('\n', stdout); exit(0); break;
  270.         case '/' : X = 1;
  271.                    col_sticky = tmp;
  272.                    prompt_filename(); /* fgets() consumes the newline, eat_line() not needed this time */
  273.                    filename=name_buffer;
  274.                    if(!filename || !filename[0]) printf("\nWARNING: %sfilename empty%s", ALERT, OFF);
  275.                    printf("%sfilename is now: '%s%s%s'%s", ALERT, OFF, filename, ALERT, OFF);
  276.                    pause();
  277.                    break;
  278.         case 'w' : X = 1;
  279.                    col_sticky = tmp;
  280.                    if(!filename || !filename[0]) {
  281.                        printf("%sset a filename with%s '.n' %sfirst!%s", ALERT, OFF, ALERT, OFF);
  282.                        eat_line();
  283.                        pause();
  284.                        break;
  285.                    }
  286.                    else {
  287.                        f_size = write_file(filename);
  288.                        if(f_size >= 0) printf("%swrote%s %d %sbytes%s", ALERT, OFF, f_size, ALERT, OFF);
  289.                        else printf("%ssomething went wrong!%s", ALERT, OFF);
  290.                        eat_line();
  291.                        pause();
  292.                    }
  293.                    break;
  294.         //case 'a' : toggle_ansi(); break;
  295.         case '.' : insert('.', pt); CMD_MODE = 0; break;
  296.         case 'l' : insert('\n', pt); break;
  297.         case 'x' : delete(pt); break;
  298.         case '[' : if(pt > 0) pt--; break;
  299.         case ']' : if(pt < len) pt++; break;
  300.         case '<' : pt = get_startline(pt); break;
  301.         case '>' : pt = get_endline(pt); break;
  302.         case 'p' : col_sticky = tmp;
  303.                    pt = get_upline(pt);
  304.                    break;
  305.         case 'n' : col_sticky = tmp;
  306.                    pt = get_downline(pt);
  307.                    break;
  308.         case 'g' : pt = seek_line(X); X = 0; break;
  309.        
  310.     /* this feature was hard to debug! writing pause() was best move */
  311.     /* better indent commands to see easier off edge of screen...    */
  312.        
  313.         default: X = 1;
  314.                  col_sticky = tmp;
  315.                  eat_line();
  316.                  printf("%sHELP**-----%s\n", ALERT, OFF);
  317.                  puts("");
  318.                  puts("  .q    quit");
  319.                  puts("  ./    assign a filename for output (about 1023 char limit, no whitespace, including path if any)");
  320.                  puts("  .w    output text buffer to active filename (assigned or same editor opened)");
  321.                  puts("  ..    insert a literal '.' and exit command mode (repete accepted)");
  322.                  puts("  .l    insert a literal newline (typed newlines are for line command input)");
  323.                  puts("  .x    delete character");
  324.                  puts("  .[    back cursor");
  325.                  puts("  .]    forward cursor");
  326.                  puts("  .<    goto start of line");
  327.                  puts("  .>    goto end of line");
  328.                  puts("  .p    up line   [previous]");
  329.                  puts("  .n    down line [next]");
  330.                  puts("  .g    goto line number (uses number prefix, might jump to beginning or end)");
  331.                  puts("");
  332.                  puts("most commands can have a positive number in front for repetition");
  333.                  puts("if the command character '.' wasn't entered then insert text literally (except . and newlines)");
  334.                  puts("");
  335.                  printf("%s-----**HELP%s", ALERT, OFF);
  336.                  pause();
  337.                  break;
  338.     };
  339.     return;
  340. }
  341.  
  342. /*----- MAIN -----*/
  343.  
  344. /* maybe we should accept options (besides a single filename) */
  345.  
  346. int main(int argc, char *argv[]) {
  347.     int f_size, c = '?';
  348.     FILE *f = NULL;
  349.  
  350.     len = 0; pt = 0; CMD_MODE = 0;
  351.    
  352.     if(argc > 1) {
  353.         filename = argv[1];
  354.         f_size = load_file(filename);
  355.         if(f_size < 0) { printf("couldn't open file '%s'\n", filename); exit(1); }
  356.     }
  357.  
  358.     toggle_ansi();
  359.     print_text_view(pt);
  360.     c = getc(stdin);
  361.  
  362.     while(c != EOF) {
  363.         if(c == '\n') { CMD_MODE = 0; print_text_view(pt); }
  364.         else if(c == CMD && !CMD_MODE) { CMD_MODE = 1; }
  365.         else if(CMD_MODE &&  isdigit(c)) { ungetc(c, stdin); scanf("%d", &X); }
  366.         else if(CMD_MODE && !isblank(c)) {
  367.             /* if X were less than 1 then cmd() will fix it */
  368.             do { cmd(&c); X--; } while(X > 0);
  369.         }
  370.         else insert(c, pt);
  371.        
  372.         c = getc(stdin);
  373.     }
  374.  
  375.     putc('\n', stdout);
  376.     return 0;
  377. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement