Example #1
0
void load_menu (void)
{
    char    menu_filename[1024], *language;
    
    // find out what language user wants
    if (options.english_menu || cmdline.english)
    {
        language = "english";
    }
    else
    {
        language = cfg_get_string (CONFIG_NFTP, fl_opt.platform_nick, "language");
        str_strip (language, " ");
    }

    // Try $user_libpath directory
    snprintf1 (menu_filename, sizeof (menu_filename),
               "%s/nftp.mnu", paths.user_libpath);
    if (access (menu_filename, R_OK) == 0) goto Found;

    // Try $system_libpath directory
    snprintf1 (menu_filename, sizeof (menu_filename),
               "%s/nftp.mnu", paths.system_libpath);
    if (access (menu_filename, R_OK) == 0) goto Found;

    // Try current directory
    strcpy (menu_filename, "nftp.mnu");
    if (access (menu_filename, R_OK) == 0) goto Found;

    /*
    if (strcmp (language, "english") != 0)
    {
        if (fl_opt.has_console && !fl_opt.initialized)
            fly_ask_ok (0, "Failed to load \"%s.mnu\", trying English.\n", language);
        language = "english";
        goto Rescan;
    }
    */

    fly_error ("Failed to load \"nftp.mnu\".");

Found:

    if (main_menu != NULL)
    {
        menu_unload (main_menu);
    }
    
    if (fl_opt.has_console && !fl_opt.initialized)
        fly_ask_ok (0, "loading %s......\n", menu_filename);
    main_menu = menu_load (menu_filename, options.keytable,
                           sizeof(options.keytable)/sizeof(options.keytable[0]));
}
Example #2
0
char *fly_get_font (void)
{
    char buffer[64];
    
    snprintf1 (buffer, sizeof(buffer), "%s %dx%d", "System VIO", cxChar, cyChar);
    return strdup (buffer);
}
Example #3
0
char *
mangle_char_for_debug_log(char c, int quote_me)
{
  char *special = NULL;
  char scrap[10], code, *format;
  char *remainder = "\\]^_"; 
  
  switch (c) {
  case 0: special = "<NUL>"; break;
  case 8: special  = "<BS>";  break;
  case 9: special  = "<TAB>"; break;
  case 10: special = "<NL>";  break;
  case 13: special = "<CR>";  break;
  case 27: special = "<ESC>"; break;
  case 127: special = "<DEL>"; break;
  }
  if (!special) {
    if (c > 0 && c < 27  ) {
      format = "<CTRL-%c>"; code =  c + 64;
    } else if (c > 27 && c < 32) {
      format = "<CTRL-%c>"; code =  remainder[c-28];
    } else {
      format = (quote_me ? "\"%c\"" : "%c"); code = c;
    }   
    snprintf1(scrap, sizeof(scrap), format, code);
  }
  return mysavestring (special ? special : scrap);
}       
Example #4
0
/* allocate string representation of an integer on the heap */
char *
as_string(int i)
{
#define MAXDIGITS 10 /* let's pray no-one edits multi-line input more than 10000000000 lines long :-) */
  char *newstring = mymalloc(MAXDIGITS+1); 

  snprintf1(newstring, MAXDIGITS, "%d", i);
  return (newstring);
}
Example #5
0
int editor_save_file (char *filename)
{
    int            i, l, l1;
    char           *p, *p1, *backup = NULL;
    unsigned long  rc;
    
    // count memory needed
    l = 0;
    for (i=0; i<nl; i++)
        l += strlen(lines[i]) + 1;

    // allocate buffer
    p = malloc (l+1);
    if (p == NULL) goto failure;

    // copy everything into the buffer
    p1 = p;
    for (i=0; i<nl; i++)
    {
        l1 = strlen (lines[i]);
        memcpy (p1, lines[i], l1);
        p1 += l1;
        *p1 = '\n';
        p1++;
    }
    *p1 = '\0';

    // rename original file
    backup = malloc (strlen (filename)+6);
    snprintf1 (backup, strlen (filename)+6, "%s.bak", filename);
    rc = rename (filename, backup);
    if (rc < 0) goto failure;

    // dump buffer into the file and free buffer
    rc = dump_file (filename, p, l);

    // check for save error
    if (rc != l)
    {
        remove (filename);
        rename (backup, filename);
        goto failure;
    }

    remove (backup);
    free (backup);
    free (p);
    return 0;

failure:
    if (backup != NULL) free (backup);
    if (p != NULL) free (p);
    fly_ask_ok (ASK_WARN, "      Save failed!      ");
    return -1;
}
Example #6
0
int fly_get_fontlist (flyfont **F, int restrict_by_language)
{
    ULONG             remfonts, nf, nff;
    FONTMETRICS       *fm;
    int               rc, i;
    char              buffer[64];

    // find how many fonts are available
    nf = 0;
    VioQueryFonts (&remfonts, NULL, sizeof(FONTMETRICS), &nf, NULL, VQF_PUBLIC, hvps);
    nf = remfonts;
    // retrieve information about them
    fm = malloc (sizeof (FONTMETRICS) * nf);
    *F = malloc (sizeof (flyfont) * nf);
    memset (fm, 0, sizeof (fm));
    rc = VioQueryFonts (&remfonts, fm, sizeof(FONTMETRICS), &nf, NULL, VQF_PUBLIC, hvps);
    // filter out what we need
    nff = 0;
    for (i=0; i<nf; i++)
    {
        if (strcmp (fm[i].szFamilyname, "System VIO")) continue;
        //if (!(fm[i].fsType & FM_TYPE_FIXED)) continue;
        
        debug_tools ("Font # %d\nfamily: %s, face: %s, width = %d, height = %d\n",
                     i, fm[i].szFamilyname, fm[i].szFacename, fm[i].lAveCharWidth, fm[i].lMaxBaselineExt);
        
        (*F)[nff].cx = fm[i].lAveCharWidth;
        (*F)[nff].cy = fm[i].lMaxBaselineExt;
        snprintf1 (buffer, sizeof(buffer), "%s %ldx%ld", fm[i].szFacename, fm[i].lAveCharWidth, fm[i].lMaxBaselineExt);
        (*F)[nff].name = strdup (buffer);
        (*F)[nff].signature = (void *)fm[i].lMatch;
        nff++;
    }

    free (fm);
    return nff;
}
Example #7
0
/* generates ElGamal key pair. returns 0 when generation went ok, and
 -1 if error occured. 'bits' is the number of bits in p; it should not
 be too low (at least 512 is recommended, 1024 is more realistic number.
 you can use precomputed p,g pairs; set bits to the ordinal of the
 precomputed combination (see table above). generator is either 2 or 5.
 public_key and secret_key will be malloc()ed and contain keys */
int eg_keypair (int bits, int generator, char **public_key, char **secret_key)
{
    BIGNUM       *p, *g, *t1, *t2, *key, *pbk;
    BN_CTX       *ctx2;
    BN_MONT_CTX  *mont;
    char         *buf1, *buf2, *buf3, *buf4, buf[8];
    int          rc;

    // create things needed for work
    ctx2 = BN_CTX_new ();         if (ctx2 == NULL) return -1;
    t1   = BN_new ();             if (t1 == NULL)   return -1;
    t2   = BN_new ();             if (t2 == NULL)   return -1;
    g    = BN_new ();             if (g == NULL)    return -1;
    key  = BN_new ();             if (key == NULL)  return -1;
    pbk  = BN_new ();             if (pbk == NULL)  return -1;
    mont = BN_MONT_CTX_new ();    if (mont == NULL) return -1;

    if (bits < 32)
    {
        if (bits > sizeof(precomp)/sizeof(precomp[0])-1) return -1;
        p = NULL;
        rc = BN_hex2bn (&p, precomp[bits].prime);
        if (rc == 0) return -1;
        // put generator into bignum
        BN_set_word (g, precomp[bits].generator);
    }
    else
    {
        // set values which will be used for checking when generating proper prime
        if (generator == 2)
        {
            BN_set_word (t1,24);
            BN_set_word (t2,11);
        }
        else if (generator == 5)
        {
            BN_set_word (t1,10);
            BN_set_word (t2,3);
            /* BN_set_word(t3,7); just have to miss
             * out on these ones :-( */
        }
        else
            goto err;
    
        // generate proper prime
        p = BN_generate_prime (NULL, bits, 1, t1, t2, NULL, NULL);
        if (p == NULL) goto err;

        // put generator into bignum
        BN_set_word (g, generator);
    }

    // create random private key
    if (!BN_rand (key, BN_num_bits (p)-1, 0, 0)) goto err;

    // create public part of the key
    BN_MONT_CTX_set (mont, p, ctx2);
    if (!BN_mod_exp_mont (pbk, g, key, p, ctx2, mont)) goto err;

    // p, g, key, pbk are ready. secret key: p,g:key, public key: p,g:pbk
    if (bits < 32)
    {
        snprintf1 (buf, sizeof(buf), "%d", bits);
        buf1 = strdup (buf);
    }
    else
    {
        buf1 = BN_bn2hex (p);
    }
    buf2 = BN_bn2hex (key);
    buf3 = BN_bn2hex (pbk);
    buf4 = BN_bn2hex (g);

    *secret_key = malloc (strlen(buf1) + strlen(buf2) + strlen(buf4) + 4);
    *public_key = malloc (strlen(buf1) + strlen(buf3) + strlen(buf4) + 4);

    strcpy (*secret_key, buf1);
    if (bits >= 32)
    {
        strcat (*secret_key, ",");
        strcat (*secret_key, buf4);
    }
    strcat (*secret_key, ":");
    strcat (*secret_key, buf2);
    
    strcpy (*public_key, buf1);
    if (bits >= 32)
    {
        strcat (*public_key, ",");
        strcat (*public_key, buf4);
    }
    strcat (*public_key, ":");
    strcat (*public_key, buf3);
    memset (buf2, 0, strlen (buf2));
    free (buf1); free (buf2); free (buf3);

    // cleanup
    BN_free (p);            BN_free (g);
    BN_clear_free (key);    BN_free (pbk);
    BN_CTX_free (ctx2);
    return 0;
    
err:
    return -1;
}
Example #8
0
void fly_launch (char *command, int wait, int pause)
{
    char    failbuf[512], *cmd, *p, que_name[64], *w;
    int     rc;
    PID     pid;
    ULONG   sess_id, datalen;
    void    *data;
    
    STARTDATA    sdata;
    HQUEUE       hq;
    REQUESTDATA  rd;
    BYTE         prty;

    if (wait)
    {
        debug_tools ("entered fly_launch(%s)\n", command);
        w = get_window_name ();
        set_window_name (command);
        snprintf1 (que_name, sizeof(que_name), "\\QUEUES\\FLY\\%u\\LAUNCH", getpid ());
        rc = DosCreateQueue (&hq, QUE_FIFO, que_name);
        debug_tools ("rc = %d after DosCreateQueue\n", rc);
        if (rc != 0)
        {
            set_window_name (w);
            return;
        }
        
        cmd = strdup (command);
        p = strchr (cmd, ' ');
        if (p != NULL) *p = '\0';

        sdata.Length = sizeof (STARTDATA);
        sdata.Related = SSF_RELATED_CHILD;
        sdata.FgBg = SSF_FGBG_FORE;
        sdata.TraceOpt = SSF_TRACEOPT_NONE;
        sdata.PgmTitle = NULL;
        sdata.PgmName = cmd;
        sdata.PgmInputs = (p == NULL) ? NULL : p+1;
        sdata.TermQ = que_name;
        sdata.Environment = NULL;
        sdata.InheritOpt = SSF_INHERTOPT_SHELL;
        sdata.SessionType = SSF_TYPE_DEFAULT;
        sdata.IconFile = NULL;
        sdata.PgmHandle = 0;
        sdata.PgmControl = 0;
        sdata.InitXPos = 0;
        sdata.InitYPos = 0;
        sdata.InitXSize = 100;
        sdata.InitYSize = 100;
        sdata.Reserved = 0;
        sdata.ObjectBuffer = failbuf;
        sdata.ObjectBuffLen = sizeof (failbuf);

        debug_tools ("going for DosStartSession()\n");
        rc = DosStartSession (&sdata, &sess_id, &pid);
        
        debug_tools ("rc = %d, failbuf: [%s]\n", rc, failbuf);

        if (rc == 0)
        {
            datalen = sizeof (rd);
            prty = 0;
            DosReadQueue (hq, &rd, &datalen, &data, 0, DCWW_WAIT, &prty, 0);
        }
        DosCloseQueue (hq);
        
        free (cmd);
        set_window_name (w);
    }
    else
    {
        cmd = malloc (strlen (command)+32);
        snprintf1 (cmd, strlen (command)+32, "detach %s >nul 2>&1", command);
        str_translate (cmd, '/', '\\');
        debug_tools ("detaching [%s]\n", cmd);
        system (cmd);
        free (cmd);
    }
}
Example #9
0
void initialize (int *argc, char **argv[], char ***envp)
{
    char    buf[512];
    char    inifile[MAX_PATH], ifile[MAX_PATH];
#ifdef __MINGW32__    
    WSADATA wsa;
#endif

    //putenv ("TZ=GMT0");
    //tzset ();
    
    // first we need to initialize FLY variables
    fly_initialize ();
    fl_opt.appname = "nftp";
    
    if (fl_opt.has_console)
        fprintf (stderr, "\nNFTP - Version%sof %s, %s"
                 "\nCopyright (C) Sergey Ayukov 1994-2003.\n\n",
                 NFTP_VERSION, __DATE__, __TIME__);

    fly_process_args (argc, argv, envp);
    
    // checking command-line arguments
    check_args (*argc, *argv);
    
    // find where our config files are
    determine_paths ((*argv)[0]);

    // delete old temporary files
    clear_tmp ();
    
    // loading configuration files
    init_config ();

    // initilialize debug subsystem if specified
    if (options.debug)
    {
        snprintf1 (buf, sizeof(buf), "%s/debug", paths.user_libpath);
#ifdef __WIN32__        
        mkdir (buf);
#else
        mkdir (buf, 0700);
#endif
        snprintf1 (buf, sizeof(buf), "%s/debug/nftp.dbg.%u", paths.user_libpath, (int)getpid ());
        dbfile = fopen (buf, "w");
        tools_debug = dbfile;
    }

    // load language-specific things
    /*nls_init (paths.system_libpath, paths.user_libpath);*/
    load_menu ();

    // check for correct NFTP.INI version
    strcpy (buf, NFTP_VERSION);
    str_strip2 (buf, " ");
    //if (str_numchars (buf, '.') > 1)  *(strrchr (buf, '.')) = '\0';
    if (strcmp (options.version, buf) != 0)
    {
        // looking for nftp.i file in user dir, then in system dir
        strcpy (ifile, paths.user_libpath);
        str_cats (ifile, "nftp.i");
        if (access (ifile, R_OK) != 0)
        {
            strcpy (ifile, paths.system_libpath);
            str_cats (ifile, "nftp.i");
            if (access (ifile, R_OK) != 0)
                fly_error (M("Cannot find nftp.i to update nftp.ini\n"
                             "neither in '%s' nor in '%s'!"),
                           paths.user_libpath, paths.system_libpath);
        }
        strcpy (inifile, paths.user_libpath);
        str_cats (inifile, "nftp.ini");
        // ask user and update ini file
        //if (fl_opt.has_console) // otherwise it's too annoying
        //{
        //    fprintf (stderr, M("Your current config file, %s, has version %s.\n"
        //         "NFTP executable version number is %s."),
        //         inifile, options.version, buf);
        //    fprintf (stderr, M(
        //            "Going to update   '%s'\n"
        //            "from              '%s'.\n"
        //            "Proceed? [Ynq]", inifile, ifile);
        //    fgets (buf, sizeof(buf), stdin);
        //    switch (buf[0])
        //    {
        //    case 'Q': case 'q': exit (0);
        //    case 'N': case 'n': break;
        //    case 'Y': case 'y': default:
        //        update_inifile (inifile, ifile);
        //    }
        //}
        //else
        //{
            update_inifile (inifile, ifile);
        //}
        GetProfileOptions (inifile);
    }
    
    config_fly ();

#ifdef __MINGW32__    
    WSAStartup (MAKEWORD(1,1), &wsa);
#endif
}
Example #10
0
void update_inifile (char *inifile, char *ifile)
{
    char        *sections[MAX_SECTIONS];
    char        buffer[1024], backupname[1024], *varname, *varvalue;
    ini_entry   *items;
    int         i, current_section, n_sect = 0, n_items = 0;
    FILE        *fp, *fp1, *fp2;

    //if (fl_opt.has_console)
    //    fly_ask_ok (0, M("Updating %s with %s..."), inifile, ifile);
    items = (ini_entry *) malloc (sizeof (ini_entry)*MAX_ENTRIES);

    strcpy (buffer, "\n");
    str_strip2 (buffer, " \n\r");

    // open files checking for errors
    fp = fopen (inifile, "r");
    if (fp == NULL) fly_error (M("Cannot open %s for reading"), inifile);
    fp1 = fopen (ifile, "r");
    if (fp1 == NULL) fly_error (M("Cannot open %s for reading"), ifile);
    
    // gather updates
    while (fgets (buffer, sizeof (buffer), fp) != NULL)
    {
        str_strip2 (buffer, " \n\r");
        if (strlen(buffer) == 0 || buffer[0] == ';') continue;
        if (buffer[0] == '[')
        {
            sections[n_sect] = strdup (buffer);
            n_sect++;
        }
        else
        {
            if (n_sect == 0)
            {
                fly_error (M("Error -- entry outside of section:\n"
                             "%s"), buffer);
            }
            items[n_items].section = n_sect-1;
            if (str_break_ini_line (buffer, &(items[n_items].variable),
                                    &(items[n_items].value)) == 0)
                n_items++;
        }
    }
    fclose (fp);

    // printing for visual check
    //if (fl_opt.has_console)
    //    for (i=0; i<n_items; i++)
    //    {
    //        fprintf (stderr, "%s : %s = %s\n", sections[items[i].section],
    //                 items[i].variable, items[i].value);
    //    }

    // rename nftp.ini into nftp.ini.001, nftp.ini.002 etc.
    for (i=1; ; i++)
    {
        snprintf1 (backupname, sizeof(backupname), "%s.%03u", inifile, i);
        if (access (backupname, F_OK) != 0) break;
    }
    if (rename (inifile, backupname) < 0)
    {
        if (fl_opt.has_console)
            fprintf (stderr, M("Cannot rename '%s' to '%s'"), inifile, backupname);
        // assume we're on FAT...
        for (i=1; ; i++)
        {
            snprintf1 (backupname, sizeof(backupname), "nftp.%03u", i);
            if (access (backupname, F_OK) != 0) break;
        }
        if (rename (inifile, backupname) < 0)
            fly_error (M("Cannot rename '%s' to '%s'"), inifile, backupname);
    }
    if (fl_opt.has_console)
        fprintf (stderr, M("Backup version of your %s\n"
                           "was saved as           %s\n"), inifile, backupname);

    // process .i file and create new ini file
    fp2 = fopen (inifile, "w");
    if (fp2 == NULL) fly_error (M("Cannot open %s for writing"), inifile);

    current_section = -1;
    while (fgets (buffer, sizeof (buffer), fp1) != NULL)
    {
        str_strip2 (buffer, " \n\r");
        switch (buffer[0])
        {
        case '[':
            for (i=0; i<n_sect; i++)
                if (strcmp (sections[i], buffer) == 0) break;
            if (i == n_sect) current_section = -1;
            else             current_section = i;
            break;
        case ';':
            if (str_break_ini_line (buffer+1, &varname, &varvalue) == 0)
            {
                for (i=0; i<n_items; i++)
                    if (items[i].section == current_section &&
                        strcmp (items[i].variable, varname) == 0)
                    {
                        snprintf1 (buffer, sizeof(buffer), "%s = %s", varname, items[i].value);
                        //if (fl_opt.has_console)
                        //    fprintf (stderr, M("updating: %s"), buffer);
                    }
                free (varname);
                free (varvalue);
            }
            break;
        default:
            break;
        }
        fputs (buffer, fp2); fputs ("\n", fp2);
    }
    fclose (fp1);
    fclose (fp2);

    // free arrays
    for (i=0; i<n_sect; i++)
        free (sections[i]);
    for (i=0; i<n_items; i++)
    {
        free (items[i].variable);
        free (items[i].value);
    }
    free (items);

    //if (fl_opt.has_console)
    //{
    //    fprintf (stderr, M("Finished updating; press ENTER to continue..."));
    //    fgets (buffer, sizeof(buffer), stdin);
    //}
}
Example #11
0
void editor (char *filename, int startpos)
{
    int stop    = FALSE; // exit main loop?
    int fline   = 0;     // no. of first displayed line
    int cline   = 0;     // no. of line with cursor
    int shift   = 0;     // shift to the right
    int ccol    = 0;     // column of the cursor position in the file window

    int        k, i, ndisp, rc, reply;
    char       *p, buf[1024];

    if (editor_open_file (filename) < 0) return;
    cline = min1 (startpos, nl-1);
    fline = max1 (0, cline - video_vsize()/2);

    // enter the loop
    while (1)
    {
        if (stop)
        {
            rc = 0;
            if (changed)
            {
                rc = -1;
                reply = fly_ask (0, "   Save file `%s'?   ", " Yes \n No \n Cancel ", filename);
                if (reply == 1) rc = editor_save_file (filename);
                if (reply == 2) rc = 0;
                if (reply == 3) stop = FALSE;
            }
            if (rc == 0) break;
        }
        
        ndisp = video_vsize()-1;
        // draw the screen
        for (i=0; i<ndisp; i++)
        {
            video_put_n_cell (' ', _BackWhite+_Black, video_hsize(), i, 0);
            if (i+fline < nl)
                editor_display_line (i, fline+i, shift);
        }
        video_put_n_cell (' ', _BackBlue+_White, video_hsize(), video_vsize()-1, 0);
        snprintf1 (buf, sizeof(buf), "L%d:C%d:S%d %c %s%s", cline, ccol, shift, fl_sym.v,
                 changed ? "*" : "", filename);
        video_put (buf, video_vsize()-1, 0);
        video_set_cursor (cline-fline, ccol-shift);
        video_update (0);

        // get a keyboard/mouse event and process it
        k = getmessage (-1);
        if (IS_KEY(k))
        {
            switch (k)
            {
                // Navigation keys
                
            case _Up:
            case _Down:
            case _PgUp:
            case _PgDn:
                fly_scroll_it (k, &fline, &cline, nl, video_vsize()-1);
                break;

            case _Right:
                ccol++;
                if (ccol-shift > video_hsize()-1) shift = ccol-video_hsize()+1;
                break;

            case _Left:
                ccol = max1 (ccol-1, 0);
                if (ccol < shift) shift = ccol;
                break;

            case _Home:
                ccol = 0; shift = 0;
                break;

            case _End:
                ccol = strlen(lines[cline]);
                if (ccol-shift > video_hsize()-1) shift = ccol-video_hsize()+1;
                break;

            case _CtrlHome:
                fline = 0; cline = 0; ccol = 0; shift = 0; break;

            case _CtrlEnd:
                fline = max1 (0, nl-video_vsize()+1);
                cline = min1 (fline+video_vsize()-1, nl-1);
                shift = 0;
                ccol = 0;
                break;

                // Action keys

            case _CtrlY:
                put_clipboard (lines[cline]);
                free (lines[cline]);
                for (i=cline; i<nl-1; i++)
                    lines[i] = lines[i+1];
                nl--;
                changed = TRUE;
                break;

            case _ShiftInsert:
            case _CtrlV:
                p = get_clipboard ();
                if (p == NULL || *p == '\0') break;
                if (nl == na)
                {
                    na *= 2;
                    lines = realloc (lines, sizeof(char *) * na);
                }
                for (i=nl-1; i>cline; i--)
                    lines[i+1] = lines[i];
                lines[cline+1] = p;
                ccol = 0;
                shift = 0;
                cline++;
                if (cline-fline == video_vsize()-1) fline++;
                nl++;
                changed = TRUE;
                break;

            case _BackSpace:
                if (ccol == 0)
                {
                    // ccol == 0: glue this line to the previous
                    if (cline == 0) break;
                    p = malloc (strlen (lines[cline])+strlen(lines[cline-1])+1);
                    strcpy (p, lines[cline-1]);
                    strcat (p, lines[cline]);
                    ccol = strlen (lines[cline-1]);
                    if (ccol-shift > video_hsize()-1) shift = ccol-video_hsize()+1;
                    free (lines[cline-1]);
                    free (lines[cline]);
                    lines[cline-1] = p;
                    for (i=cline; i<nl-1; i++)
                        lines[i] = lines[i+1];
                    cline--;
                    nl--;
                }
                else
                {
                    // ccol != 0: delete char at ccol-1, move cursor left
                    str_delete (lines[cline], lines[cline]+ccol-1);
                    ccol--;
                    if (ccol < shift) shift = ccol;
                }
                changed = TRUE;
                break;

            case _Enter:
                if (nl == na)
                {
                    na *= 2;
                    lines = realloc (lines, sizeof(char *) * na);
                }
                for (i=nl-1; i>cline; i--)
                    lines[i+1] = lines[i];
                if (ccol < strlen (lines[cline]))
                {
                    lines[cline+1] = strdup (lines[cline]+ccol);
                    lines[cline][ccol] = '\0';
                }
                else
                {
                    lines[cline+1] = strdup ("");
                }
                ccol = 0;
                shift = 0;
                cline++;
                if (cline-fline == video_vsize()-1) fline++;
                nl++;
                changed = TRUE;
                break;

            case _Delete:
                if (ccol >= strlen (lines[cline]))
                {
                    // glue previous line to this one
                    if (cline == nl-1) break;
                    p = malloc (ccol+strlen(lines[cline+1])+1);
                    strcpy (p, lines[cline]);
                    memset (p+strlen(lines[cline]), ' ', ccol-strlen(lines[cline]));
                    strcpy (p+ccol, lines[cline+1]);
                    free (lines[cline]);
                    free (lines[cline+1]);
                    lines[cline] = p;
                    for (i=cline+1; i<nl-1; i++)
                        lines[i] = lines[i+1];
                    nl--;
                }
                else
                {
                    // ccol != 0: delete char at ccol-1, move cursor left
                    str_delete (lines[cline], lines[cline]+ccol);
                }
                changed = TRUE;
                break;

            case _F2:
                rc = editor_save_file (filename);
                if (rc == 0) changed = FALSE;
                break;
                
            case _Esc:
            case _F10:
                stop = TRUE; break;

                // character keys
                
            default:
                if (k >= ' ' && k <= 255)
                {
                    str_insert_at (cline, k, ccol);
                    ccol++;
                    changed = TRUE;
                }
            }
        }
        else if (IS_MOUSE(k))
        {
        }
        else if (IS_SYSTEM(k))
        {
            switch (SYS_TYPE(k))
            {
            case SYSTEM_QUIT:
                stop = TRUE; break;
            }
        }
    }

    if (nl != 0 && lines != NULL)
        for (i=0; i<nl; i++)
            free (lines[i]);
    if (na != 0 && lines != NULL) free (lines);
    na = 0;
    lines = NULL;
}