www.pudn.com > vim53src.zip > if_perl.xs


/* vi:set ts=8 sts=4 sw=4: 
 * 
 * VIM - Vi IMproved	by Bram Moolenaar 
 * 
 * Do ":help uganda"  in Vim to read copying and usage conditions. 
 * Do ":help credits" in Vim to see a list of people who contributed. 
 */ 
/* 
 * if_perl.xs: Main code for Perl interface support. 
 *		Mostly written by Sven Verdoolaege. 
 */ 
 
#define _memory_h	/* avoid memset redeclaration */ 
#define IN_PERL_FILE	/* don't include if_perl.pro from proto.h */ 
 
#include "vim.h" 
 
/* 
 * Avoid clashes between Perl and Vim namespace. 
 */ 
#undef MAGIC 
#undef NORMAL 
#undef STRLEN 
#undef FF 
#undef OP_DELETE 
#ifdef __BORLANDC__ 
#define NOPROTO 1 
#endif 
#include  
#include  
#include  
 
/* 
 * Work around clashes between Perl and Vim namespace.	proto.h doesn't 
 * include if_perl.pro and perlsfio.pro when IN_PERL_FILE is defined, because 
 * we need the CV typedef.  proto.h can't be moved to after including 
 * if_perl.h, because we get all sorts of name clashes then. 
 */ 
#ifndef PROTO 
# include "proto/if_perl.pro" 
# include "proto/if_perlsfio.pro" 
#endif 
 
static void *perl_interp = NULL; 
static void xs_init __ARGS((void)); 
static void VIM_init __ARGS((void)); 
 
/* 
 * perl_init(): initialize perl interpreter 
 * We have to call perl_parse to initialize some structures, 
 * there's nothing to actually parse. 
 */ 
    static void 
perl_init() 
{ 
    char	*bootargs[] = { "VI", NULL }; 
    static char *args[] = { "", "-e", "" }; 
 
    perl_interp = perl_alloc(); 
    perl_construct(perl_interp); 
    perl_parse(perl_interp, xs_init, 3, args, 0); 
    perl_call_argv("VIM::bootstrap", (long)G_DISCARD, bootargs); 
    VIM_init(); 
#ifdef USE_SFIO 
    sfdisc(PerlIO_stdout(), sfdcnewvim()); 
    sfdisc(PerlIO_stderr(), sfdcnewvim()); 
    sfsetbuf(PerlIO_stdout(), NULL, 0); 
    sfsetbuf(PerlIO_stderr(), NULL, 0); 
#endif 
} 
 
/* 
 * perl_end(): clean up after ourselves 
 */ 
    void 
perl_end() 
{ 
    if (perl_interp) 
    { 
	perl_run(perl_interp); 
	perl_destruct(perl_interp); 
	perl_free(perl_interp); 
    } 
} 
 
/* 
 * msg_split(): send a message to the message handling routines 
 * split at '\n' first though. 
 */ 
    void 
msg_split(s, attr) 
    char_u	*s; 
    int		attr;	/* highlighting attributes */ 
{ 
    char *next; 
    char *token = (char *)s; 
 
    while ((next = strchr(token, '\n'))) 
    { 
	*next++ = '\0';			/* replace \n with \0 */ 
	msg_attr((char_u *)token, attr); 
	token = next; 
    } 
    if (*token) 
	msg_attr((char_u *)token, attr); 
} 
 
#ifndef WANT_EVAL 
/* 
 * This stub is needed because an "#ifdef WANT_EVAL" around Eval() doesn't work properly. 
 */ 
    char_u * 
eval_to_string(arg, nextcmd) 
    char_u	*arg; 
    char_u	**nextcmd; 
{ 
    return NULL; 
} 
#endif 
 
/* 
 * Create a new reference to an SV pointing to the SCR structure 
 * The perl_private part of the SCR structure points to the SV, 
 * so there can only be one such SV for a particular SCR structure. 
 * When the last reference has gone (DESTROY is called), 
 * perl_private is reset; When the screen goes away before 
 * all references are gone, the value of the SV is reset; 
 * any subsequent use of any of those reference will produce 
 * a warning. (see typemap) 
 */ 
#define newANYrv(TYPE)						\ 
static SV *							\ 
new ## TYPE ## rv(rv, ptr)					\ 
    SV *rv;							\ 
    TYPE *ptr;							\ 
{								\ 
    sv_upgrade(rv, SVt_RV);					\ 
    if (!ptr->perl_private)					\ 
    {								\ 
	ptr->perl_private = newSV(0);				\ 
	sv_setiv(ptr->perl_private, (IV)ptr);			\ 
    }								\ 
    else							\ 
	SvREFCNT_inc(ptr->perl_private);			\ 
    SvRV(rv) = ptr->perl_private;				\ 
    SvROK_on(rv);						\ 
    return sv_bless(rv, gv_stashpv("VI" #TYPE, TRUE));		\ 
} 
 
newANYrv(WIN) 
newANYrv(BUF) 
 
/* 
 * perl_win_free 
 *	Remove all refences to the window to be destroyed 
 */ 
    void 
perl_win_free(wp) 
    WIN *wp; 
{ 
    if (wp->perl_private) 
	sv_setiv((SV *)wp->perl_private, 0); 
    return; 
} 
 
    void 
perl_buf_free(bp) 
    BUF *bp; 
{ 
    if (bp->perl_private) 
	sv_setiv((SV *)bp->perl_private, 0); 
    return; 
} 
 
#ifndef PROTO 
I32 cur_val(IV iv, SV *sv); 
 
/* 
 * Handler for the magic variables $main::curwin and $main::curbuf. 
 * The handler is put into the magic vtbl for these variables. 
 * (This is effectively a C-level equivalent of a tied variable). 
 * There is no "set" function as the variables are read-only. 
 */ 
I32 cur_val(IV iv, SV *sv) 
{ 
    SV *rv; 
    if (iv == 0) 
	rv = newWINrv(newSV(0), curwin); 
    else 
	rv = newBUFrv(newSV(0), curbuf); 
    sv_setsv(sv, rv); 
    return 0; 
} 
#endif /* !PROTO */ 
 
struct ufuncs cw_funcs = { cur_val, 0, 0 }; 
struct ufuncs cb_funcs = { cur_val, 0, 1 }; 
 
/* 
 * VIM_init(): Vim-specific initialisation. 
 * Make the magical main::curwin and main::curbuf variables 
 */ 
    static void 
VIM_init() 
{ 
    static char cw[] = "main::curwin"; 
    static char cb[] = "main::curbuf"; 
    MAGIC *m; 
    SV *sv; 
 
    sv = perl_get_sv(cw, TRUE); 
    sv_magic(sv, NULL, 'U', cw, strlen(cw)); 
    m = mg_find(sv, 'U'); 
    m->mg_ptr = (char *)&cw_funcs; 
    SvREADONLY_on(sv); 
 
    sv = perl_get_sv(cb, TRUE); 
    sv_magic(sv, NULL, 'U', cb, strlen(cb)); 
    m = mg_find(sv, 'U'); 
    m->mg_ptr = (char *)&cb_funcs; 
    SvREADONLY_on(sv); 
} 
 
    int 
do_perl(eap) 
    EXARG	*eap; 
{ 
    char	*err; 
    STRLEN	length; 
    SV		*sv; 
    dSP; 
 
    if (!perl_interp) 
    { 
	perl_init(); 
	SPAGAIN; 
    } 
 
    ENTER; 
    SAVETMPS; 
 
    sv = newSVpv((char *)eap->arg, 0); 
    perl_eval_sv(sv, G_DISCARD | G_NOARGS); 
    SvREFCNT_dec(sv); 
 
    err = SvPV(GvSV(errgv), length); 
 
    FREETMPS; 
    LEAVE; 
 
    if (!length) 
	return OK; 
 
    msg_split((char_u *)err, highlight_attr[HLF_E]); 
    return FAIL; 
     
} 
 
    static int 
replace_line(line, end) 
    linenr_t	*line, *end; 
{ 
    char *str; 
 
    if (SvOK(GvSV(defgv))) 
    { 
	str = SvPV(GvSV(defgv), na); 
	ml_replace(*line, (char_u *)str, 1); 
#ifdef SYNTAX_HL 
	syn_changed(*line); /* recompute syntax hl. for this line */ 
#endif 
    } 
    else 
    { 
	mark_adjust(*line, *line, MAXLNUM, -1); 
	ml_delete((*line)--, FALSE); 
	(*end)--; 
    } 
    changed(); 
    return OK; 
} 
 
    int 
do_perldo(eap) 
    EXARG	*eap; 
{ 
    STRLEN	length; 
    SV		*sv; 
    char	*str; 
    linenr_t	i; 
    dSP; 
 
    if (bufempty()) 
	return FAIL; 
 
    if (!perl_interp) 
    { 
	perl_init(); 
	SPAGAIN; 
    } 
    length = strlen((char *)eap->arg); 
    sv = newSV(length + sizeof("sub VIM::perldo {")-1 + 1); 
    sv_setpvn(sv, "sub VIM::perldo {", sizeof("sub VIM::perldo {")-1); 
    sv_catpvn(sv, (char *)eap->arg, length); 
    sv_catpvn(sv, "}", 1); 
    perl_eval_sv(sv, G_DISCARD | G_NOARGS); 
    SvREFCNT_dec(sv); 
    str = SvPV(GvSV(errgv), length); 
    if (length) 
	goto err; 
 
    if (u_save(eap->line1 - 1, eap->line2 + 1) != OK) 
	return FAIL; 
 
    ENTER; 
    SAVETMPS; 
    for (i = eap->line1; i <= eap->line2; i++) 
    { 
	sv_setpv(GvSV(defgv),(char *)ml_get(i)); 
	PUSHMARK(sp); 
	perl_call_pv("VIM::perldo", G_SCALAR | G_EVAL); 
	str = SvPV(GvSV(errgv), length); 
	if (length) 
	    break; 
	SPAGAIN; 
	if (SvTRUEx(POPs)) 
	{ 
	    if (replace_line(&i, &eap->line2) != OK) 
	    { 
		PUTBACK; 
		break; 
	    } 
	} 
	PUTBACK; 
    } 
    FREETMPS; 
    LEAVE; 
    adjust_cursor(); 
    update_screen(NOT_VALID); 
    if (!length) 
	return OK; 
 
err: 
    msg_split((char_u *)str, highlight_attr[HLF_E]); 
    return FAIL; 
} 
 
/* Register any extra external extensions */ 
extern void  
#ifdef __BORLANDC__ 
__import 
#endif 
boot_DynaLoader _((CV* cv)); 
extern void boot_VIM _((CV* cv)); 
 
    static void 
xs_init() 
{ 
#if 0 
    dXSUB_SYS;	    /* causes an error with Perl 5.003_97 */ 
#endif 
    char *file = __FILE__; 
 
    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); 
    newXS("VIM::bootstrap", boot_VIM, file); 
} 
 
typedef WIN *	VIWIN; 
typedef BUF *	VIBUF; 
 
MODULE = VIM	    PACKAGE = VIM 
 
void 
Msg(text, hl=NULL) 
    char	*text; 
    char	*hl; 
 
    PREINIT: 
    int		attr; 
    int		id; 
 
    PPCODE: 
    if (text != NULL) 
    { 
	attr = 0; 
	if (hl != NULL) 
	{ 
	    id = syn_name2id((char_u *)hl); 
	    if (id != 0) 
		attr = syn_id2attr(id); 
	} 
	msg_split((char_u *)text, attr); 
    } 
 
void 
SetOption(line) 
    char *line; 
 
    PPCODE: 
    if (line != NULL) 
	do_set((char_u *)line); 
    update_screen(NOT_VALID); 
 
void 
DoCommand(line) 
    char *line; 
 
    PPCODE: 
    if (line != NULL) 
	do_cmdline((char_u *)line, NULL, NULL, DOCMD_VERBOSE + DOCMD_NOWAIT); 
 
void 
Eval(str) 
    char *str; 
 
    PREINIT: 
	char_u *value; 
    PPCODE: 
	value = eval_to_string((char_u *)str, (char_u**)0); 
	if (value == NULL) 
	{ 
	    XPUSHs(sv_2mortal(newSViv(0))); 
	    XPUSHs(sv_2mortal(newSVpv("", 0))); 
	} 
	else 
	{ 
	    XPUSHs(sv_2mortal(newSViv(1))); 
	    XPUSHs(sv_2mortal(newSVpv((char *)value, 0))); 
	    vim_free(value); 
	} 
 
void 
Buffers(...) 
 
    PREINIT: 
    BUF *vimbuf; 
    int i, b; 
 
    PPCODE: 
    if (items == 0) 
    { 
	if (GIMME == G_SCALAR) 
	{ 
	    i = 0; 
	    for (vimbuf = firstbuf; vimbuf; vimbuf = vimbuf->b_next) 
		++i; 
 
	    XPUSHs(sv_2mortal(newSViv(i))); 
	} 
	else 
	{ 
	    for (vimbuf = firstbuf; vimbuf; vimbuf = vimbuf->b_next) 
		XPUSHs(newBUFrv(newSV(0), vimbuf)); 
	} 
    } 
    else 
    { 
	for (i = 0; i < items; i++) 
	{ 
	    SV *sv = ST(i); 
	    if (SvIOK(sv)) 
		b = SvIV(ST(i)); 
	    else 
	    { 
		char_u *pat; 
		int len; 
		pat = (char_u *)SvPV(sv, len); 
		++emsg_off; 
		b = buflist_findpat(pat, pat+len); 
		--emsg_off; 
	    } 
 
	    if (b >= 0) 
	    { 
		vimbuf = buflist_findnr(b); 
		if (vimbuf) 
		    XPUSHs(newBUFrv(newSV(0), vimbuf)); 
	    } 
	} 
    } 
 
void 
Windows(...) 
 
    PREINIT: 
    WIN *vimwin; 
    int i, w; 
 
    PPCODE: 
    if (items == 0) 
    { 
	if (GIMME == G_SCALAR) 
	    XPUSHs(sv_2mortal(newSViv(win_count()))); 
	else 
	{ 
	    for (vimwin = firstwin; vimwin != NULL; vimwin = vimwin->w_next) 
		XPUSHs(newWINrv(newSV(0), vimwin)); 
	} 
    } 
    else 
    { 
	for (i = 0; i < items; i++) 
	{ 
	    w = SvIV(ST(i)); 
	    vimwin = win_goto_nr(w); 
	    if (vimwin) 
		XPUSHs(newWINrv(newSV(0), vimwin)); 
	} 
    } 
 
MODULE = VIM	    PACKAGE = VIWIN 
 
void 
DESTROY(win) 
    VIWIN win 
 
    CODE: 
    if (win_valid(win)) 
	win->perl_private = 0; 
 
SV * 
Buffer(win) 
    VIWIN win 
 
    CODE: 
    if (!win_valid(win)) 
	win = curwin; 
    RETVAL = newBUFrv(newSV(0), win->w_buffer); 
    OUTPUT: 
    RETVAL 
 
void 
SetHeight(win, height) 
    VIWIN win 
    int height; 
 
    PREINIT: 
    WIN *savewin; 
 
    PPCODE: 
    if (!win_valid(win)) 
	win = curwin; 
    savewin = curwin; 
    curwin = win; 
    win_setheight(height); 
    curwin = savewin; 
 
void 
Cursor(win, ...) 
    VIWIN win 
 
    PPCODE: 
    if(items == 1) 
    { 
      EXTEND(sp, 2); 
      if (!win_valid(win)) 
	  win = curwin; 
      PUSHs(sv_2mortal(newSViv(win->w_cursor.lnum))); 
      PUSHs(sv_2mortal(newSViv(win->w_cursor.col))); 
    } 
    else if(items == 3) 
    { 
      int lnum, col; 
 
      if (!win_valid(win)) 
	  win = curwin; 
      lnum = SvIV(ST(1)); 
      col = SvIV(ST(2)); 
      win->w_cursor.lnum = lnum; 
      win->w_cursor.col = col; 
      adjust_cursor();		    /* put cursor on an existing line */ 
      update_screen(NOT_VALID); 
    } 
 
MODULE = VIM	    PACKAGE = VIBUF 
 
void 
DESTROY(vimbuf) 
    VIBUF vimbuf; 
 
    CODE: 
    if (buf_valid(vimbuf)) 
	vimbuf->perl_private = 0; 
 
void 
Name(vimbuf) 
    VIBUF vimbuf; 
 
    PPCODE: 
    if (!buf_valid(vimbuf)) 
	vimbuf = curbuf; 
    /* No file name returns an empty string */ 
    if (vimbuf->b_fname == NULL) 
	XPUSHs(sv_2mortal(newSVpv("", 0))); 
    else 
	XPUSHs(sv_2mortal(newSVpv((char *)vimbuf->b_fname, 0))); 
 
void 
Number(vimbuf) 
    VIBUF vimbuf; 
 
    PPCODE: 
    if (!buf_valid(vimbuf)) 
	vimbuf = curbuf; 
    XPUSHs(sv_2mortal(newSViv(vimbuf->b_fnum))); 
 
void 
Count(vimbuf) 
    VIBUF vimbuf; 
 
    PPCODE: 
    if (!buf_valid(vimbuf)) 
	vimbuf = curbuf; 
    XPUSHs(sv_2mortal(newSViv(vimbuf->b_ml.ml_line_count))); 
 
void 
Get(vimbuf, ...) 
    VIBUF vimbuf; 
 
    PREINIT: 
    char_u *line; 
    int i; 
    long lnum; 
    PPCODE: 
    if (buf_valid(vimbuf)) 
    { 
	for (i = 1; i < items; i++) 
	{ 
	    lnum = SvIV(ST(i)); 
	    if (lnum > 0 && lnum <= vimbuf->b_ml.ml_line_count) 
	    { 
		line = ml_get_buf(vimbuf, lnum, FALSE); 
		XPUSHs(sv_2mortal(newSVpv((char *)line, 0))); 
	    } 
	} 
    } 
 
void 
Set(vimbuf, ...) 
    VIBUF vimbuf; 
 
    PREINIT: 
    int i; 
    long lnum; 
    char *line; 
    BUF *savebuf; 
    PPCODE: 
    if (buf_valid(vimbuf)) 
    { 
	if (items < 3) 
	    croak("Usage: VIBUF::Set(vimbuf, lnum, @lines)"); 
 
	lnum = SvIV(ST(1)); 
	for(i=2; i 0 && lnum <= vimbuf->b_ml.ml_line_count && line != NULL) 
	    { 
		savebuf = curbuf; 
		curbuf = vimbuf; 
		if (u_savesub(lnum) == OK) 
		{ 
		    ml_replace(lnum, (char_u *)line, TRUE); 
		    changed(); 
#ifdef SYNTAX_HL 
		    syn_changed(lnum); /* recompute syntax hl. for this line */ 
#endif 
		} 
		curbuf = savebuf; 
		update_curbuf(NOT_VALID); 
	    } 
	} 
    } 
 
void 
Delete(vimbuf, ...) 
    VIBUF vimbuf; 
 
    PREINIT: 
    long i, lnum = 0, count = 0; 
    BUF *savebuf; 
    PPCODE: 
    if (buf_valid(vimbuf)) 
    { 
	if (items == 2) 
	{ 
	    lnum = SvIV(ST(1)); 
	    count = 1; 
	} 
	else if (items == 3) 
	{ 
	    lnum = SvIV(ST(1)); 
	    count = 1 + SvIV(ST(2)) - lnum; 
	    if(count == 0) 
		count = 1; 
	    if(count < 0) 
	    { 
		lnum -= count; 
		count = -count; 
	    } 
	} 
	if (items >= 2) 
	{ 
	    for (i=0; i 0 && lnum <= vimbuf->b_ml.ml_line_count) 
		{ 
		    savebuf = curbuf; 
		    curbuf = vimbuf; 
		    if (u_savedel(lnum, 1) == OK) 
		    { 
			mark_adjust(lnum, lnum, MAXLNUM, -1); 
			ml_delete(lnum, 0); 
			changed(); 
		    } 
		    curbuf = savebuf; 
		    update_curbuf(NOT_VALID); 
		} 
	    } 
	} 
    } 
 
void 
Append(vimbuf, ...) 
    VIBUF vimbuf; 
 
    PREINIT: 
    int i; 
    long lnum; 
    char *line; 
    BUF *savebuf; 
    PPCODE: 
    if (buf_valid(vimbuf)) 
    { 
	if (items < 3) 
	    croak("Usage: VIBUF::Append(vimbuf, lnum, @lines)"); 
 
	lnum = SvIV(ST(1)); 
	for(i=2; i= 0 && lnum <= vimbuf->b_ml.ml_line_count && line != NULL) 
	    { 
		savebuf = curbuf; 
		curbuf = vimbuf; 
		if (u_inssub(lnum + 1) == OK) 
		{ 
		    mark_adjust(lnum + 1, MAXLNUM, 1L, 0L); 
		    ml_append(lnum, (char_u *)line, (colnr_t)0, FALSE); 
		    changed(); 
		} 
		curbuf = savebuf; 
		update_curbuf(NOT_VALID); 
	    } 
	} 
    }