/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved
*
*/
/*
* Author:
* K.Tsuchida (ken) kenichi-tsu@aix.or.jp/gdh01571@niftyserve.or.jp
*/
/*
* Author: (base code: winnt.c jelvis version)
* T.Watanabe (Tom_W) tom_w@st.rim.or.jp/GCD02235@niftyserve.or.jp
*/
/* Author: (base code: msdos.c)
* Code Contributions By: Bram Moolenaar mool@oce.nl
* Tim Thompson twitch!tjt
* Tony Andrews onecom!wldrdg!tony
* G. R. (Fred) Walter watmath!watcgl!grwalter
*/
/*
* winnt.c
*
* WINDOWS-NT system-dependent routines.
*/
#include <windows.h>
#include <io.h>
#include "vim.h"
#include "globals.h"
#include "param.h"
#include "proto.h"
#include <fcntl.h>
#include <process.h>
#ifdef FEPCTRL
#include "fepctrl.h"
#endif
static HANDLE h_stdin, h_stdout;
static int sy_fg_color;
static int sy_bg_color;
#define NM_COLOR (sy_fg_color|(sy_bg_color<<4))
#define SO_COLOR (sy_bg_color|(sy_fg_color<<4))
#define gotoxy(x, y) {COORD csr; csr.X = (x-1); csr.Y = (y-1); (void) SetConsoleCursorPosition(h_stdout, csr);}
typedef struct filelist
{
char **file;
int nfiles;
int maxfiles;
} FileList;
static void addfile __ARGS((FileList *, char *, int));
static int pstrcmp(); /* __ARGS((char **, char **)); BCC does not like this */
static void strlowcpy __ARGS((char *, char *));
static int expandpath __ARGS((FileList *, char *, int, int, int));
static int ctrlc_pressed = FALSE; /* set when ctrl-C or ctrl-break detected */
static int delayed_redraw = FALSE; /* set when ctrl-C detected */
#ifdef DEBUG
/*
* Put two characters in the video buffer without calling BIOS or DOS.
*/
blink(n)
int n;
{
/* do nothing */
}
#endif
void
sleep(time)
unsigned time;
{
INPUT_RECORD ir;
DWORD cnt;
time *= 1000; /* s -> ms */
while (time > 0) {
Sleep(200);
if (WaitForSingleObject(h_stdin, 0) == WAIT_OBJECT_0) {
while (PeekConsoleInput(h_stdin, &ir, 1, &cnt) && cnt) {
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
return;
}
ReadConsoleInput(h_stdin, &ir, 1, &cnt);
}
}
time -= 200;
}
}
void
vim_delay()
{
sleep(1);
}
/*
* this version of remove is not scared by a readonly (backup) file
*/
int
vim_remove(name)
char *name;
{
setperm(name, 0); /* default permissions */
return unlink(name);
}
static void
clrscr()
{
DWORD cnt, dummy;
COORD csr;
csr.X = csr.Y = 0;
cnt = Rows * Columns;
(void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy);
(void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy);
}
static void
clreol()
{
CONSOLE_SCREEN_BUFFER_INFO pcinf;
COORD csr;
DWORD cnt, dummy;
(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
csr.X = pcinf.dwCursorPosition.X;
csr.Y = pcinf.dwCursorPosition.Y;
cnt = Columns - pcinf.dwCursorPosition.X;
(void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy);
(void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy);
}
static void
insline()
{
CONSOLE_SCREEN_BUFFER_INFO pcinf;
SMALL_RECT area, clip;
COORD dest;
CHAR_INFO fill;
(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
area.Left = 0;
area.Right = Columns - 1;
area.Top = pcinf.dwCursorPosition.Y;
area.Bottom = Rows - 2;
clip.Left = 0;
clip.Right = Columns - 1;
clip.Top = pcinf.dwCursorPosition.Y;
clip.Bottom = Rows - 1;
dest.X = 0;
dest.Y = pcinf.dwCursorPosition.Y + 1;
fill.Char.AsciiChar = ' ';
fill.Attributes = NM_COLOR;
(void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill);
}
static void
delline()
{
CONSOLE_SCREEN_BUFFER_INFO pcinf;
SMALL_RECT area, clip;
COORD dest;
CHAR_INFO fill;
(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
area.Left = 0;
area.Right = Columns - 1;
area.Top = pcinf.dwCursorPosition.Y + 1;
area.Bottom = Rows - 1;
clip.Left = 0;
clip.Right = Columns - 1;
clip.Top = pcinf.dwCursorPosition.Y;
clip.Bottom = Rows - 1;
dest.X = 0;
dest.Y = pcinf.dwCursorPosition.Y;
fill.Char.AsciiChar = ' ';
fill.Attributes = NM_COLOR;
(void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill);
}
/*
* mch_write(): write the output buffer to the screen
*/
void
mch_write(s, len)
char *s;
int len;
{
char *p;
int row, col;
LPDWORD cnt;
static char buffer[IOSIZE];
int pos = 0;
#define SETBUF(_c) {if (pos >= sizeof(buffer)) {(void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;} buffer[pos++] = (_c);}
#define PUTBUF() {if (pos != 0) (void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;}
if (term_console) { /* translate ESC | sequences into bios calls */
while (len--)
{
if (s[0] == '\n')
{
PUTBUF();
(void) WriteConsole(h_stdout, "\r", (unsigned)1, &cnt, NULL);
}
else if ((s[0] == '\007') && p_vb)
{
HWND console;
console = GetForegroundWindow();
if (IsWindow(console)) {
FlashWindow(console, TRUE);
Sleep(80);
FlashWindow(console, TRUE);
Sleep(80);
FlashWindow(console, TRUE);
Sleep(80);
FlashWindow(console, TRUE);
}
s++;
continue;
}
else if (s[0] == ESC && len > 1 && s[1] == '|')
{
switch (s[2])
{
case 'J': PUTBUF();
clrscr();
goto got3;
case 'K': PUTBUF();
clreol();
goto got3;
case 'L': PUTBUF();
insline();
goto got3;
case 'M': PUTBUF();
delline();
got3: s += 3;
len -= 2;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': p = s + 2;
row = getdigits(&p); /* no check for length! */
if (p > s + len)
break;
if (*p == ';')
{
++p;
col = getdigits(&p);/* no check for length! */
if (p > s + len)
break;
if (*p == 'H')
{
PUTBUF();
gotoxy(col, row);
len -= p - s;
s = p + 1;
continue;
}
}
else if (*p == 'm')
{
PUTBUF();
if (row == 0)
SetConsoleTextAttribute(h_stdout, NM_COLOR);
else
SetConsoleTextAttribute(h_stdout, SO_COLOR);
len -= p - s;
s = p + 1;
continue;
}
}
}
SETBUF(*s++);
}
PUTBUF();
} else
write(1, s, (unsigned)len);
}
/*
* GetChars(): low level input funcion.
* Get a characters from the keyboard.
* If time == 0 do not wait for characters.
* If time == n wait a short time for characters.
* If time == -1 wait forever for characters.
*/
int
GetChars(buf, maxlen, time)
char *buf;
int maxlen;
int time;
{
CONSOLE_SCREEN_BUFFER_INFO pcinf;
INPUT_RECORD ir;
DWORD cnt;
int len = 0;
int update = FALSE;
if (delayed_redraw)
{
delayed_redraw = FALSE;
updateScreen(CLEAR);
setcursor();
}
flushbuf();
#ifdef FEPCTRL
if (p_fc && p_fs)
fep_disp_sync(h_stdout);
#endif
if (time >= 0)
{
if (time == 0) /* don't know if time == 0 is allowed */
time = 1;
}
else /* time == -1 */
{
/*
* If there is no character available within 2 seconds (default)
* write the autoscript file to disk
*/
time = p_ut;
update = TRUE;
}
if (WaitForSingleObject(h_stdin, time) != WAIT_OBJECT_0) {
if (update)
updatescript(0);
return 0; /* no KEY data */
}
while (len < maxlen-1) {
if ((PeekConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) {
return len;
}
if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) {
return len;
}
if (ir.EventType == KEY_EVENT /* KEY HIT ? */
&& ir.Event.KeyEvent.bKeyDown) {
if (ir.Event.KeyEvent.uChar.AsciiChar) {
buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar;
#ifdef JP
#define IsSJISkanji(c) ((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xfc)
if (IsSJISkanji((u_char)(buf[len-1]))) {
/* get 2nd kanji code */
for (;;) {
if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE)
|| (cnt == 0)) {
continue;
}
if (ir.EventType == KEY_EVENT
&& ir.Event.KeyEvent.bKeyDown
&& ir.Event.KeyEvent.uChar.AsciiChar) {
break; /* for loop */
}
}
buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar;
}
#endif
continue; /* while loop */
}
switch (ir.Event.KeyEvent.wVirtualKeyCode) {
case VK_UP:
buf[len++] = 0xff; buf[len++] = 'H';
return len;
case VK_DOWN:
buf[len++] = 0xff; buf[len++] = 'P';
return len;
case VK_LEFT:
if (ir.Event.KeyEvent.dwControlKeyState
& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
buf[len++] = 0xff; buf[len++] = 's';
} else {
buf[len++] = 0xff; buf[len++] = 'K';
}
return len;
case VK_RIGHT:
if (ir.Event.KeyEvent.dwControlKeyState
& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
buf[len++] = 0xff; buf[len++] = 't';
} else {
buf[len++] = 0xff; buf[len++] = 'M';
}
return len;
case VK_PRIOR:
buf[len++] = Ctrl('B');
return len;
case VK_NEXT:
buf[len++] = Ctrl('F');
return len;
#ifdef NT106KEY
case 0xf3: case 0xf4: /* ZENKAKU / HANKAKU KEY */
buf[len++] = '[' & 0x1f; /* ESC key !! */
return len;
#endif
case '6': /* jkeyb.sys */ /* ken add */
case 0xde: /* ^ key */
if (ir.Event.KeyEvent.dwControlKeyState
& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
buf[len++] = '^' & 0x1f;
/* CTRL+^=switch file */
return len;
}
break;
}
}
}
return(len);
}
/*
* We have no job control, fake it by starting a new shell.
*/
void
mch_suspend()
{
outstr("new shell started\n");
call_shell(NULL, 0, TRUE);
}
extern int _fmode;
/*
* we do not use windows, there is not much to do here
*/
void
mch_windinit()
{
CONSOLE_SCREEN_BUFFER_INFO pcinf;
_fmode = O_BINARY; /* we do our own CR-LF translation */
if ((h_stdin = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
FatalAppExit(0, "stdin handle get error\n");
exit(99);
}
if ((h_stdout = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
FatalAppExit(0, "stdout handle get error\n");
exit(99);
}
term_console = 1;
flushbuf();
mch_get_winsize();
#ifdef FEPCTRL
if (p_fc)
fep_init();
#endif
}
void
check_win(argc, argv)
int argc;
char **argv;
{
if (!isatty(0) || !isatty(1))
{
fprintf(stderr, "VIM: no controlling terminal\n");
exit(2);
}
}
/*
* fname_case(): Set the case of the filename, if it already exists.
* msdos filesystem is far to primitive for that. do nothing.
*/
void
fname_case(name)
char *name;
{
}
/*
* settitle(): set titlebar of our window.
* Dos console has no title.
*/
void
settitle(str)
char *str;
{
if (*str)
SetConsoleTitle(str);
else
SetConsoleTitle("-JVim-");
}
void
resettitle()
{
SetConsoleTitle("");
}
/*
* Get name of current directory into buffer 'buf' of length 'len' bytes.
* Return non-zero for success.
*/
int
dirname(buf, len)
char *buf;
int len;
{
return (getcwd(buf, len) != NULL);
}
/*
* get absolute filename into buffer 'buf' of length 'len' bytes
*/
int
FullName(fname, buf, len)
char *fname, *buf;
int len;
{
if (fname == NULL) /* always fail */
return 0;
#ifdef __BORLANDC__ /* the old Turbo C does not have this */
if (_fullpath(buf, fname, len) == NULL)
{
strncpy(buf, fname, len); /* failed, use the relative path name */
return 0;
}
return 1;
#else /* almost the same as FullName in unix.c */
{
int l;
char olddir[MAXPATHL];
char *p, *q;
int c;
int retval = 1;
*buf = 0;
/*
* change to the directory for a moment,
* and then do the getwd() (and get back to where we were).
* This will get the correct path name with "../" things.
*/
p = strrchr(fname, '/');
q = strrchr(fname, '\\');
if (q && (p == NULL || q > p))
p = q;
q = strrchr(fname, ':');
if (q && (p == NULL || q > p))
p = q;
if (p != NULL)
{
if (getcwd(olddir, MAXPATHL) == NULL)
{
p = NULL; /* can't get current dir: don't chdir */
retval = 0;
}
else
{
if (*p == ':' || (p > fname && p[-1] == ':'))
q = p + 1;
else
q = p;
c = *q;
*q = NUL;
if (chdir(fname))
retval = 0;
else
fname = p + 1;
*q = c;
}
}
if (getcwd(buf, len) == NULL)
{
retval = 0;
*buf = NUL;
}
l = strlen(buf);
if (l && buf[l - 1] != '/' && buf[l - 1] != '\\')
strcat(buf, "\\");
if (p)
chdir(olddir);
strcat(buf, fname);
return retval;
}
#endif
}
/*
* get file permissions for 'name'
* -1 : error
* else FA_attributes defined in dos.h
*/
long
getperm(name)
char *name;
{
return(_chmod(name, 0, 0)); /* get file mode */
}
/*
* set file permission for 'name' to 'perm'
*/
int
setperm(name, perm)
char *name;
long perm;
{
perm &= ~FA_ARCH;
return _chmod(name, 1, (int)perm);
}
/*
* check if "name" is a directory
*/
int
isdir(name)
char *name;
{
int f;
f = _chmod(name, 0, 0);
if (f == -1)
return -1; /* file does not exist at all */
if ((f & FA_DIREC) == 0)
return 0; /* not a directory */
return 1;
}
/*
* Careful: mch_windexit() may be called before mch_windinit()!
*/
void
mch_windexit(r)
int r;
{
#ifdef FEPCTRL
if (p_fc)
fep_term();
#endif
settmode(0);
stoptermcap();
flushbuf();
stopscript(); /* remove autoscript file */
exit(r);
}
/*
*
*/
BOOL
cbrk_handler(type)
DWORD type;
{
switch (type) {
case CTRL_BREAK_EVENT:
delayed_redraw = TRUE;
return(TRUE);
case CTRL_C_EVENT:
/*
FlushConsoleInputBuffer(h_stdin);
*/
ctrlc_pressed = TRUE;
return(TRUE);
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
if (p_wa && !p_ro) {
extern int global_busy;
global_busy = 1;
p_bk = TRUE;
if (Filename == NULL)
setfname("-JVim-", NULL);
docmdline(":x!");
} else {
docmdline(":q!");
}
getout(1);
return(TRUE);
}
return(FALSE);
}
void
mch_settmode(raw)
int raw;
{
DWORD bits;
GetConsoleMode(h_stdin, &bits);
if (raw)
{
SetConsoleCtrlHandler((PHANDLER_ROUTINE)cbrk_handler, TRUE);
bits &= ~(ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT);
}
else
{
SetConsoleTextAttribute(h_stdout, NM_COLOR);
bits |= (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT);
}
SetConsoleMode(h_stdin, bits);
}
/*
* Structure used by Turbo-C/Borland-C to store video parameters.
*/
int
mch_get_winsize()
{
int i;
CONSOLE_SCREEN_BUFFER_INFO pcinf;
SMALL_RECT rect;
if (GetConsoleScreenBufferInfo(h_stdout, &pcinf)) {
Columns = pcinf.dwSize.X;
Rows = pcinf.dwSize.Y;
sy_fg_color = pcinf.wAttributes & 0x0f;
sy_bg_color = (pcinf.wAttributes & 0xf0) >> 4;
rect.Left = rect.Top = 0;
rect.Right = Columns - 1;
rect.Bottom = Rows - 1;
SetConsoleWindowInfo(h_stdout, TRUE, &rect);
/*
COORD coord;
SetConsoleScreenBufferSize(h_stdout, coord);
*/
} else {
Columns = 25;
Rows = 80;
sy_fg_color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
sy_bg_color = 0;
}
set_window();
if (Columns < 5 || Columns > MAX_COLUMNS ||
Rows < 2 || Rows > MAX_COLUMNS)
{
/* these values are overwritten by termcap size or default */
Columns = 80;
Rows = 25;
return 1;
}
Rows_max = Rows; /* remember physical max height */
check_winsize();
script_winsize();
return 0;
}
/*
* Set the active window for delline/insline.
*/
void
set_window()
{
/* nothing */
}
void
mch_set_winsize()
{
/* should try to set the window size to Rows and Columns */
/* may involve switching display mode.... */
}
int
call_shell(cmd, filter, cooked)
char *cmd;
int filter; /* if != 0: called by dofilter() */
int cooked;
{
int x;
char newcmd[200];
flushbuf();
#ifdef FEPCTRL
if (p_fc)
fep_term();
#endif
if (cooked)
settmode(0); /* set to cooked mode */
if (cmd == NULL)
x = spawnlp(P_WAIT, p_sh, p_sh, NULL);
else
{ /* we use "command" to start the shell, slow but easy */
sprintf(newcmd, "%cc", get_switchar());
x = spawnlp(P_WAIT, p_sh, p_sh, newcmd, cmd, NULL);
}
outchar('\n');
if (cooked)
settmode(1); /* set to raw mode */
if (x)
{
smsg("%d returned", x);
outchar('\n');
}
#ifdef FEPCTRL
if (p_fc)
fep_init();
#endif
return x;
}
/*
* check for an "interrupt signal": CTRL-break or CTRL-C
*/
void
breakcheck()
{
if (ctrlc_pressed)
{
ctrlc_pressed = FALSE;
got_int = TRUE;
}
}
#define FL_CHUNK 512
static void
addfile(fl, f, isdir)
FileList *fl;
char *f;
int isdir;
{
char *p;
if (!fl->file)
{
fl->file = (char **)alloc(sizeof(char *) * FL_CHUNK);
if (!fl->file)
return;
fl->nfiles = 0;
fl->maxfiles = FL_CHUNK;
}
if (fl->nfiles >= fl->maxfiles)
{
char **t;
int i;
t = (char **)lalloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK), TRUE);
if (!t)
return;
for (i = fl->nfiles - 1; i >= 0; i--)
t[i] = fl->file[i];
free(fl->file);
fl->file = t;
fl->maxfiles += FL_CHUNK;
}
p = alloc((unsigned)(strlen(f) + 1 + isdir));
if (p)
{
strcpy(p, f);
if (isdir)
strcat(p, "\\");
}
fl->file[fl->nfiles++] = p;
}
static int
pstrcmp(a, b)
char **a, **b;
{
return (strcmp(*a, *b));
}
int
has_wildcard(s)
char *s;
{
if (s)
for ( ; *s; ++s)
if (*s == '?' || *s == '*')
return 1;
return 0;
}
static void
strlowcpy(d, s)
char *d, *s;
{
while (*s)
*d++ = tolower(*s++);
*d = '\0';
}
static int
expandpath(fl, path, fonly, donly, notf)
FileList *fl;
char *path;
int fonly, donly, notf;
{
char buf[MAXPATH];
char *p, *s, *e;
int lastn, c, r;
struct ffblk fb;
lastn = fl->nfiles;
/*
* Find the first part in the path name that contains a wildcard.
* Copy it into buf, including the preceding characters.
*/
p = buf;
s = NULL;
e = NULL;
while (*path)
{
if (*path == '\\' || *path == ':' || *path == '/')
{
if (e)
break;
else
s = p;
}
if (*path == '*' || *path == '?')
e = p;
*p++ = *path++;
}
e = p;
if (s)
s++;
else
s = buf;
/* now we have one wildcard component between s and e */
*e = '\0';
r = 0;
/* If we are expanding wildcards we try both files and directories */
if ((c = findfirst(buf, &fb, (*path || !notf) ? FA_DIREC : 0)) != 0)
{
/* not found */
strcpy(e, path);
if (notf)
addfile(fl, buf, FALSE);
return 1; /* unexpanded or empty */
}
while (!c)
{
strlowcpy(s, fb.ff_name);
if (*s != '.' || (s[1] != '\0' && (s[1] != '.' || s[2] != '\0')))
{
strcat(buf, path);
if (!has_wildcard(path))
addfile(fl, buf, (isdir(buf) > 0));
else
r |= expandpath(fl, buf, fonly, donly, notf);
}
c = findnext(&fb);
}
qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp);
return r;
}
/*
* MSDOS rebuilt of Scott Ballantynes ExpandWildCard for amiga/arp.
* jw
*/
int
ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
int num_pat;
char **pat;
int *num_file;
char ***file;
int files_only, list_notfound;
{
int i, r = 0;
FileList f;
f.file = NULL;
f.nfiles = 0;
for (i = 0; i < num_pat; i++)
{
if (!has_wildcard(pat[i]))
addfile(&f, pat[i], files_only ? FALSE : (isdir(pat[i]) > 0));
else
r |= expandpath(&f, pat[i], files_only, 0, list_notfound);
}
if (r == 0)
{
*num_file = f.nfiles;
*file = f.file;
}
else
{
*num_file = 0;
*file = NULL;
}
return r;
}
void
FreeWild(num, file)
int num;
char **file;
{
if (file == NULL || num <= 0)
return;
while (num--)
free(file[num]);
free(file);
}
/*
* The normal chdir() does not change the default drive.
* This one does.
*/
#undef chdir
int
vim_chdir(path)
char *path;
{
if (path[0] == NUL) /* just checking... */
return 0;
if (path[1] == ':') /* has a drive name */
{
if (_chdrive(toupper(path[0]) - 'A' + 1))
return -1; /* invalid drive name */
path += 2;
}
if (*path == NUL) /* drive name only */
return 0;
return chdir(path); /* let the normal chdir() do the rest */
}
int
get_switchar()
{
return '/';
}
syntax highlighted by Code2HTML, v. 0.9.1