/*- * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 1992, 1993, 1994, 1995, 1996 * Keith Bostic. All rights reserved. * Copyright (c) 1995 * George V. Neville-Neil. All rights reserved. * Copyright (c) 1996-2001 * Sven Verdoolaege. All rights reserved. * * See the LICENSE file for redistribution information. */ #undef VI #ifndef lint static const char sccsid[] = "Id: perl.xs,v 8.46 2001/08/28 11:33:42 skimo Exp (Berkeley) Date: 2001/08/28 11:33:42 "; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* perl redefines them * avoid warnings */ #undef USE_DYNAMIC_LOADING #undef DEBUG #undef PACKAGE #undef ARGS #define ARGS ARGS #include "config.h" #include "../common/common.h" #include "perl_api_extern.h" #ifndef DEFSV #define DEFSV GvSV(defgv) #endif #ifndef ERRSV #define ERRSV GvSV(errgv) #endif #ifndef dTHX #define dTHXs #else #define dTHXs dTHX; #endif static void msghandler __P((SCR *, mtype_t, char *, size_t)); typedef struct _perl_data { PerlInterpreter* interp; SV *svcurscr, *svstart, *svstop, *svid; CONVWIN cw; char *errmsg; } perl_data_t; #define PERLP(sp) ((perl_data_t *)sp->wp->perl_private) #define CHAR2INTP(sp,n,nlen,w,wlen) \ CHAR2INT5(sp,((perl_data_t *)sp->wp->perl_private)->cw,n,nlen,w,wlen) /* * INITMESSAGE -- * Macros to point messages at the Perl message handler. */ #define INITMESSAGE(sp) \ scr_msg = sp->wp->scr_msg; \ sp->wp->scr_msg = msghandler; #define ENDMESSAGE(sp) \ sp->wp->scr_msg = scr_msg; \ if (rval) croak(PERLP(sp)->errmsg); void xs_init __P((pTHXo)); /* * perl_end -- * Clean up perl interpreter * * PUBLIC: int perl_end __P((GS *)); */ int perl_end(gp) GS *gp; { /* * Call perl_run and perl_destuct to call END blocks and DESTROY * methods. */ if (gp->perl_interp) { perl_run(gp->perl_interp); perl_destruct(gp->perl_interp); #if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY) perl_free(gp->perl_interp); #endif /* XXX rather make sure only one thread calls perl_end */ gp->perl_interp = 0; } } /* * perl_eval * Evaluate a string * We don't use mortal SVs because no one will clean up after us */ static void perl_eval(string) char *string; { dTHXs SV* sv = newSVpv(string, 0); /* G_KEEPERR to catch syntax error; better way ? */ sv_setpv(ERRSV,""); perl_eval_sv(sv, G_DISCARD | G_NOARGS | G_KEEPERR); SvREFCNT_dec(sv); } /* * perl_init -- * Create the perl commands used by nvi. * * PUBLIC: int perl_init __P((SCR *)); */ int perl_init(scrp) SCR *scrp; { AV * av; GS *gp; WIN *wp; char *bootargs[] = { "VI", NULL }; #ifndef USE_SFIO SV *svcurscr; #endif perl_data_t *pp; static char *args[] = { "", "-e", "" }; size_t length; char *file = __FILE__; gp = scrp->gp; wp = scrp->wp; if (gp->perl_interp == NULL) { gp->perl_interp = perl_alloc(); perl_construct(gp->perl_interp); if (perl_parse(gp->perl_interp, xs_init, 3, args, 0)) { perl_destruct(gp->perl_interp); perl_free(gp->perl_interp); gp->perl_interp = NULL; return 1; } { dTHXs perl_call_argv("VI::bootstrap", G_DISCARD, bootargs); perl_eval("$SIG{__WARN__}='VI::Warn'"); av_unshift(av = GvAVn(PL_incgv), 1); av_store(av, 0, newSVpv(_PATH_PERLSCRIPTS, sizeof(_PATH_PERLSCRIPTS)-1)); #ifdef USE_SFIO sfdisc(PerlIO_stdout(), sfdcnewnvi(scrp)); sfdisc(PerlIO_stderr(), sfdcnewnvi(scrp)); #else svcurscr = perl_get_sv("curscr", TRUE); sv_magic((SV *)gv_fetchpv("STDOUT",TRUE, SVt_PVIO), svcurscr, 'q', Nullch, 0); sv_magic((SV *)gv_fetchpv("STDERR",TRUE, SVt_PVIO), svcurscr, 'q', Nullch, 0); #endif /* USE_SFIO */ } } MALLOC(scrp, pp, perl_data_t *, sizeof(perl_data_t)); wp->perl_private = pp; memset(&pp->cw, 0, sizeof(pp->cw)); #ifdef USE_ITHREADS pp->interp = perl_clone(gp->perl_interp, 0); if (1) { /* hack for bug fixed in perl-current (5.6.1) */ dTHXa(pp->interp); if (PL_scopestack_ix == 0) { ENTER; } } #else pp->interp = gp->perl_interp; #endif pp->errmsg = 0; { dTHXs SvREADONLY_on(pp->svcurscr = perl_get_sv("curscr", TRUE)); SvREADONLY_on(pp->svstart = perl_get_sv("VI::StartLine", TRUE)); SvREADONLY_on(pp->svstop = perl_get_sv("VI::StopLine", TRUE)); SvREADONLY_on(pp->svid = perl_get_sv("VI::ScreenId", TRUE)); } return (0); } /* * perl_screen_end * Remove all refences to the screen to be destroyed * * PUBLIC: int perl_screen_end __P((SCR*)); */ int perl_screen_end(scrp) SCR *scrp; { dTHXs if (scrp->perl_private) { sv_setiv((SV*) scrp->perl_private, 0); } return 0; } static void my_sighandler(i) int i; { croak("Perl command interrupted by SIGINT"); } /* 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) */ static SV * newVIrv(rv, screen) SV *rv; SCR *screen; { dTHXs if (!screen) return sv_setsv(rv, &PL_sv_undef), rv; sv_upgrade(rv, SVt_RV); if (!screen->perl_private) { screen->perl_private = newSV(0); sv_setiv(screen->perl_private, (IV) screen); } else SvREFCNT_inc(screen->perl_private); SvRV(rv) = screen->perl_private; SvROK_on(rv); return sv_bless(rv, gv_stashpv("VI", TRUE)); } /* * perl_setenv * Use perl's setenv if perl interpreter has been started. * Perl uses its own setenv and gets confused if we change * the environment after it has started. * * PUBLIC: int perl_setenv __P((SCR* sp, const char *name, const char *value)); */ int perl_setenv(SCR* scrp, const char *name, const char *value) { if (scrp->wp->perl_private == NULL) { if (value == NULL) unsetenv(name); else setenv(name, value, 1); } else my_setenv(name, value); } /* * perl_ex_perl -- :[line [,line]] perl [command] * Run a command through the perl interpreter. * * PUBLIC: int perl_ex_perl __P((SCR*, CHAR_T *, size_t, db_recno_t, db_recno_t)); */ int perl_ex_perl(scrp, cmdp, cmdlen, f_lno, t_lno) SCR *scrp; CHAR_T *cmdp; size_t cmdlen; db_recno_t f_lno, t_lno; { WIN *wp; size_t length; size_t len; char *err; char *np; size_t nlen; Signal_t (*istat)(); perl_data_t *pp; /* Initialize the interpreter. */ if (scrp->wp->perl_private == NULL && perl_init(scrp)) return (1); pp = scrp->wp->perl_private; { dTHXs dSP; sv_setiv(pp->svstart, f_lno); sv_setiv(pp->svstop, t_lno); newVIrv(pp->svcurscr, scrp); /* Backwards compatibility. */ newVIrv(pp->svid, scrp); istat = signal(SIGINT, my_sighandler); INT2CHAR(scrp, cmdp, STRLEN(cmdp)+1, np, nlen); perl_eval(np); signal(SIGINT, istat); SvREFCNT_dec(SvRV(pp->svcurscr)); SvROK_off(pp->svcurscr); SvREFCNT_dec(SvRV(pp->svid)); SvROK_off(pp->svid); err = SvPV(ERRSV, length); if (!length) return (0); err[length - 1] = '\0'; msgq(scrp, M_ERR, "perl: %s", err); return (1); } } /* * replace_line * replace a line with the contents of the perl variable $_ * lines are split at '\n's * if $_ is undef, the line is deleted * returns possibly adjusted linenumber */ static int replace_line(scrp, line, t_lno, defsv) SCR *scrp; db_recno_t line, *t_lno; SV *defsv; { char *str, *next; CHAR_T *wp; size_t len, wlen; dTHXs if (SvOK(defsv)) { str = SvPV(defsv,len); next = memchr(str, '\n', len); CHAR2INTP(scrp, str, next ? (next - str) : len, wp, wlen); api_sline(scrp, line, wp, wlen); while (next++) { len -= next - str; next = memchr(str = next, '\n', len); CHAR2INTP(scrp, str, next ? (next - str) : len, wp, wlen); api_iline(scrp, ++line, wp, wlen); (*t_lno)++; } } else { api_dline(scrp, line--); (*t_lno)--; } return line; } /* * perl_ex_perldo -- :[line [,line]] perl [command] * Run a set of lines through the perl interpreter. * * PUBLIC: int perl_ex_perldo __P((SCR*, CHAR_T *, size_t, db_recno_t, db_recno_t)); */ int perl_ex_perldo(scrp, cmdp, cmdlen, f_lno, t_lno) SCR *scrp; CHAR_T *cmdp; size_t cmdlen; db_recno_t f_lno, t_lno; { CHAR_T *p; WIN *wp; size_t length; size_t len; db_recno_t i; CHAR_T *str; char *estr; SV* cv; char *command; perl_data_t *pp; char *np; size_t nlen; /* Initialize the interpreter. */ if (scrp->wp->perl_private == NULL && perl_init(scrp)) return (1); pp = scrp->wp->perl_private; { dTHXs dSP; newVIrv(pp->svcurscr, scrp); /* Backwards compatibility. */ newVIrv(pp->svid, scrp); INT2CHAR(scrp, cmdp, STRLEN(cmdp)+1, np, nlen); if (!(command = malloc(length = nlen - 1 + sizeof("sub {}")))) return 1; snprintf(command, length, "sub {%s}", np); ENTER; SAVETMPS; cv = perl_eval_pv(command, FALSE); free (command); estr = SvPV(ERRSV,length); if (length) goto err; for (i = f_lno; i <= t_lno && !api_gline(scrp, i, &str, &len); i++) { INT2CHAR(scrp, str, len, np, nlen); sv_setpvn(DEFSV,np,nlen); sv_setiv(pp->svstart, i); sv_setiv(pp->svstop, i); PUSHMARK(sp); perl_call_sv(cv, G_SCALAR | G_EVAL); estr = SvPV(ERRSV, length); if (length) break; SPAGAIN; if(SvTRUEx(POPs)) i = replace_line(scrp, i, &t_lno, DEFSV); PUTBACK; } FREETMPS; LEAVE; SvREFCNT_dec(SvRV(pp->svcurscr)); SvROK_off(pp->svcurscr); SvREFCNT_dec(SvRV(pp->svid)); SvROK_off(pp->svid); if (!length) return (0); err: estr[length - 1] = '\0'; msgq(scrp, M_ERR, "perl: %s", estr); return (1); } } /* * msghandler -- * Perl message routine so that error messages are processed in * Perl, not in nvi. */ static void msghandler(sp, mtype, msg, len) SCR *sp; mtype_t mtype; char *msg; size_t len; { char *errmsg; errmsg = PERLP(sp)->errmsg; /* Replace the trailing with an EOS. */ /* Let's do that later instead */ if (errmsg) free (errmsg); errmsg = malloc(len + 1); memcpy(errmsg, msg, len); errmsg[len] = '\0'; PERLP(sp)->errmsg = errmsg; } typedef SCR * VI; typedef SCR * VI__OPT; typedef SCR * VI__MAP; typedef SCR * VI__MARK; typedef SCR * VI__LINE; typedef AV * AVREF; typedef struct { SV *sprv; TAGQ *tqp; } perl_tagq; typedef perl_tagq * VI__TAGQ; typedef perl_tagq * VI__TAGQ2; MODULE = VI PACKAGE = VI # msg -- # Set the message line to text. # # Perl Command: VI::Msg # Usage: VI::Msg screenId text void Msg(screen, text) VI screen char * text ALIAS: PRINT = 1 CODE: api_imessage(screen, text); # XS_VI_escreen -- # End a screen. # # Perl Command: VI::EndScreen # Usage: VI::EndScreen screenId void EndScreen(screen) VI screen PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_escreen(screen); ENDMESSAGE(screen); # XS_VI_iscreen -- # Create a new screen. If a filename is specified then the screen # is opened with that file. # # Perl Command: VI::NewScreen # Usage: VI::NewScreen screenId [file] VI Edit(screen, ...) VI screen ALIAS: NewScreen = 1 PROTOTYPE: $;$ PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; char *file; SCR *nsp; CODE: file = (items == 1) ? NULL : (char *)SvPV(ST(1),PL_na); INITMESSAGE(screen); rval = api_edit(screen, file, &nsp, ix); ENDMESSAGE(screen); RETVAL = ix ? nsp : screen; OUTPUT: RETVAL # XS_VI_fscreen -- # Return the screen id associated with file name. # # Perl Command: VI::FindScreen # Usage: VI::FindScreen file VI FindScreen(file) char *file PREINIT: SCR *fsp; CODE: RETVAL = api_fscreen(0, file); OUTPUT: RETVAL # XS_VI_GetFileName -- # Return the file name of the screen # # Perl Command: VI::GetFileName # Usage: VI::GetFileName screenId char * GetFileName(screen) VI screen; PPCODE: EXTEND(sp,1); PUSHs(sv_2mortal(newSVpv(screen->frp->name, 0))); # XS_VI_aline -- # -- Append the string text after the line in lineNumber. # # Perl Command: VI::AppendLine # Usage: VI::AppendLine screenId lineNumber text void AppendLine(screen, linenumber, text) VI screen int linenumber char *text PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; size_t length; CODE: SvPV(ST(2), length); INITMESSAGE(screen); rval = api_aline(screen, linenumber, text, length); ENDMESSAGE(screen); # XS_VI_dline -- # Delete lineNum. # # Perl Command: VI::DelLine # Usage: VI::DelLine screenId lineNum void DelLine(screen, linenumber) VI screen int linenumber PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_dline(screen, (db_recno_t)linenumber); ENDMESSAGE(screen); # XS_VI_gline -- # Return lineNumber. # # Perl Command: VI::GetLine # Usage: VI::GetLine screenId lineNumber char * GetLine(screen, linenumber) VI screen int linenumber PREINIT: size_t len; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; char *line; CHAR_T *p; PPCODE: INITMESSAGE(screen); rval = api_gline(screen, (db_recno_t)linenumber, &p, &len); ENDMESSAGE(screen); EXTEND(sp,1); PUSHs(sv_2mortal(newSVpv(len ? (char *)p : "", len))); # XS_VI_sline -- # Set lineNumber to the text supplied. # # Perl Command: VI::SetLine # Usage: VI::SetLine screenId lineNumber text void SetLine(screen, linenumber, text) VI screen int linenumber char *text PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; size_t length; size_t len; CHAR_T *line; CODE: SvPV(ST(2), length); INITMESSAGE(screen); CHAR2INTP(screen, text, length, line, len); rval = api_sline(screen, linenumber, line, len); ENDMESSAGE(screen); # XS_VI_iline -- # Insert the string text before the line in lineNumber. # # Perl Command: VI::InsertLine # Usage: VI::InsertLine screenId lineNumber text void InsertLine(screen, linenumber, text) VI screen int linenumber char *text PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; size_t length; size_t len; CHAR_T *line; CODE: SvPV(ST(2), length); INITMESSAGE(screen); CHAR2INTP(screen, text, length, line, len); rval = api_iline(screen, linenumber, line, len); ENDMESSAGE(screen); # XS_VI_lline -- # Return the last line in the screen. # # Perl Command: VI::LastLine # Usage: VI::LastLine screenId int LastLine(screen) VI screen PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_lline(screen, &last); ENDMESSAGE(screen); RETVAL=last; OUTPUT: RETVAL # XS_VI_getmark -- # Return the mark's cursor position as a list with two elements. # {line, column}. # # Perl Command: VI::GetMark # Usage: VI::GetMark screenId mark void GetMark(screen, mark) VI screen char mark PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; PPCODE: INITMESSAGE(screen); rval = api_getmark(screen, (int)mark, &cursor); ENDMESSAGE(screen); EXTEND(sp,2); PUSHs(sv_2mortal(newSViv(cursor.lno))); PUSHs(sv_2mortal(newSViv(cursor.cno))); # XS_VI_setmark -- # Set the mark to the line and column numbers supplied. # # Perl Command: VI::SetMark # Usage: VI::SetMark screenId mark line column void SetMark(screen, mark, line, column) VI screen char mark int line int column PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); cursor.lno = line; cursor.cno = column; rval = api_setmark(screen, (int)mark, &cursor); ENDMESSAGE(screen); # XS_VI_getcursor -- # Return the current cursor position as a list with two elements. # {line, column}. # # Perl Command: VI::GetCursor # Usage: VI::GetCursor screenId void GetCursor(screen) VI screen PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; PPCODE: INITMESSAGE(screen); rval = api_getcursor(screen, &cursor); ENDMESSAGE(screen); EXTEND(sp,2); PUSHs(sv_2mortal(newSViv(cursor.lno))); PUSHs(sv_2mortal(newSViv(cursor.cno))); # XS_VI_setcursor -- # Set the cursor to the line and column numbers supplied. # # Perl Command: VI::SetCursor # Usage: VI::SetCursor screenId line column void SetCursor(screen, line, column) VI screen int line int column PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); cursor.lno = line; cursor.cno = column; rval = api_setcursor(screen, &cursor); ENDMESSAGE(screen); # XS_VI_swscreen -- # Change the current focus to screen. # # Perl Command: VI::SwitchScreen # Usage: VI::SwitchScreen screenId screenId void SwitchScreen(screenFrom, screenTo) VI screenFrom VI screenTo PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screenFrom); rval = api_swscreen(screenFrom, screenTo); ENDMESSAGE(screenFrom); # XS_VI_map -- # Associate a key with a perl procedure. # # Perl Command: VI::MapKey # Usage: VI::MapKey screenId key perlproc void MapKey(screen, key, commandsv) VI screen char *key SV *commandsv PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; int length; char *command; CODE: INITMESSAGE(screen); command = SvPV(commandsv, length); rval = api_map(screen, key, command, length); ENDMESSAGE(screen); # XS_VI_unmap -- # Unmap a key. # # Perl Command: VI::UnmapKey # Usage: VI::UnmmapKey screenId key void UnmapKey(screen, key) VI screen char *key PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_unmap(screen, key); ENDMESSAGE(screen); # XS_VI_opts_set -- # Set an option. # # Perl Command: VI::SetOpt # Usage: VI::SetOpt screenId setting void SetOpt(screen, setting) VI screen char *setting PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; SV *svc; CODE: INITMESSAGE(screen); svc = sv_2mortal(newSVpv(":set ", 5)); sv_catpv(svc, setting); rval = api_run_str(screen, SvPV(svc, PL_na)); ENDMESSAGE(screen); # XS_VI_opts_get -- # Return the value of an option. # # Perl Command: VI::GetOpt # Usage: VI::GetOpt screenId option void GetOpt(screen, option) VI screen char *option PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; char *value; CHAR_T *wp; size_t wlen; PPCODE: INITMESSAGE(screen); CHAR2INTP(screen, option, strlen(option)+1, wp, wlen); rval = api_opts_get(screen, wp, &value, NULL); ENDMESSAGE(screen); EXTEND(SP,1); PUSHs(sv_2mortal(newSVpv(value, 0))); free(value); # XS_VI_run -- # Run the ex command cmd. # # Perl Command: VI::Run # Usage: VI::Run screenId cmd void Run(screen, command) VI screen char *command; PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_run_str(screen, command); ENDMESSAGE(screen); void DESTROY(screensv) SV* screensv PREINIT: VI screen; CODE: if (sv_isa(screensv, "VI")) { IV tmp = SvIV((SV*)SvRV(screensv)); screen = (SCR *) tmp; } else croak("screen is not of type VI"); if (screen) screen->perl_private = 0; void Warn(warning) char *warning; CODE: sv_catpv(ERRSV,warning); #define TIED(kind,package) \ sv_magic((SV *) (var = \ (kind##V *)sv_2mortal((SV *)new##kind##V())), \ sv_setref_pv(sv_newmortal(), package, \ newVIrv(newSV(0), screen)),\ 'P', Nullch, 0);\ RETVAL = newRV((SV *)var) SV * Opt(screen) VI screen; PREINIT: HV *var; CODE: TIED(H,"VI::OPT"); OUTPUT: RETVAL SV * Map(screen) VI screen; PREINIT: HV *var; CODE: TIED(H,"VI::MAP"); OUTPUT: RETVAL SV * Mark(screen) VI screen PREINIT: HV *var; CODE: TIED(H,"VI::MARK"); OUTPUT: RETVAL SV * Line(screen) VI screen PREINIT: AV *var; CODE: TIED(A,"VI::LINE"); OUTPUT: RETVAL SV * TagQ(screen, tag) VI screen char *tag; PREINIT: perl_tagq *ptag; PPCODE: if ((ptag = malloc(sizeof(perl_tagq))) == NULL) goto err; ptag->sprv = newVIrv(newSV(0), screen); ptag->tqp = api_tagq_new(screen, tag); if (ptag->tqp != NULL) { EXTEND(SP,1); PUSHs(sv_2mortal(sv_setref_pv(newSV(0), "VI::TAGQ", ptag))); } else { err: ST(0) = &PL_sv_undef; return; } MODULE = VI PACKAGE = VI::OPT void DESTROY(screen) VI::OPT screen CODE: # typemap did all the checking SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0)))); void FETCH(screen, key) VI::OPT screen char *key PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; char *value; int boolvalue; CHAR_T *wp; size_t wlen; PPCODE: INITMESSAGE(screen); CHAR2INTP(screen, key, strlen(key)+1, wp, wlen); rval = api_opts_get(screen, wp, &value, &boolvalue); if (!rval) { EXTEND(SP,1); PUSHs(sv_2mortal((boolvalue == -1) ? newSVpv(value, 0) : newSViv(boolvalue))); free(value); } else ST(0) = &PL_sv_undef; rval = 0; ENDMESSAGE(screen); void STORE(screen, key, value) VI::OPT screen char *key SV *value PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CHAR_T *wp; size_t wlen; CODE: INITMESSAGE(screen); CHAR2INTP(screen, key, strlen(key)+1, wp, wlen); rval = api_opts_set(screen, wp, SvPV(value, PL_na), SvIV(value), SvTRUEx(value)); ENDMESSAGE(screen); MODULE = VI PACKAGE = VI::MAP void DESTROY(screen) VI::MAP screen CODE: # typemap did all the checking SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0)))); void STORE(screen, key, commandsv) VI::MAP screen char *key SV *commandsv PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; int length; char *command; CODE: INITMESSAGE(screen); command = SvPV(commandsv, length); rval = api_map(screen, key, command, length); ENDMESSAGE(screen); void DELETE(screen, key) VI::MAP screen char *key PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_unmap(screen, key); ENDMESSAGE(screen); MODULE = VI PACKAGE = VI::MARK void DESTROY(screen) VI::MARK screen CODE: # typemap did all the checking SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0)))); int EXISTS(screen, mark) VI::MARK screen char mark PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval = 0; /* never croak */ int missing; CODE: INITMESSAGE(screen); missing = api_getmark(screen, (int)mark, &cursor); ENDMESSAGE(screen); RETVAL = !missing; OUTPUT: RETVAL AV * FETCH(screen, mark) VI::MARK screen char mark PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_getmark(screen, (int)mark, &cursor); ENDMESSAGE(screen); RETVAL = newAV(); av_push(RETVAL, newSViv(cursor.lno)); av_push(RETVAL, newSViv(cursor.cno)); OUTPUT: RETVAL void STORE(screen, mark, pos) VI::MARK screen char mark AVREF pos PREINIT: struct _mark cursor; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: if (av_len(pos) < 1) croak("cursor position needs 2 elements"); INITMESSAGE(screen); cursor.lno = SvIV(*av_fetch(pos, 0, 0)); cursor.cno = SvIV(*av_fetch(pos, 1, 0)); rval = api_setmark(screen, (int)mark, &cursor); ENDMESSAGE(screen); void FIRSTKEY(screen, ...) VI::MARK screen ALIAS: NEXTKEY = 1 PROTOTYPE: $;$ PREINIT: int next; char key[] = {0, 0}; PPCODE: if (items == 2) { next = 1; *key = *(char *)SvPV(ST(1),PL_na); } else next = 0; if (api_nextmark(screen, next, key) != 1) { EXTEND(sp, 1); PUSHs(sv_2mortal(newSVpv(key, 1))); } else ST(0) = &PL_sv_undef; MODULE = VI PACKAGE = VI::LINE void DESTROY(screen) VI::LINE screen CODE: # typemap did all the checking SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0)))); # similar to SetLine void STORE(screen, linenumber, text) VI::LINE screen int linenumber char *text PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; size_t length; db_recno_t last; size_t len; CHAR_T *line; CODE: ++linenumber; /* vi 1 based ; perl 0 based */ SvPV(ST(2), length); INITMESSAGE(screen); rval = api_lline(screen, &last); if (!rval) { if (linenumber > last) rval = api_extend(screen, linenumber); if (!rval) CHAR2INTP(screen, text, length, line, len); rval = api_sline(screen, linenumber, line, len); } ENDMESSAGE(screen); # similar to GetLine char * FETCH(screen, linenumber) VI::LINE screen int linenumber PREINIT: size_t len; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; char *line; CHAR_T *p; PPCODE: ++linenumber; /* vi 1 based ; perl 0 based */ INITMESSAGE(screen); rval = api_gline(screen, (db_recno_t)linenumber, &p, &len); ENDMESSAGE(screen); EXTEND(sp,1); PUSHs(sv_2mortal(newSVpv(len ? (char*)p : "", len))); # similar to LastLine int FETCHSIZE(screen) VI::LINE screen PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_lline(screen, &last); ENDMESSAGE(screen); RETVAL=last; OUTPUT: RETVAL void STORESIZE(screen, count) VI::LINE screen int count PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_lline(screen, &last); if (!rval) { if (count > last) rval = api_extend(screen, count); else while(last && last > count) { rval = api_dline(screen, last--); if (rval) break; } } ENDMESSAGE(screen); void EXTEND(screen, count) VI::LINE screen int count CODE: void CLEAR(screen) VI::LINE screen PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval; CODE: INITMESSAGE(screen); rval = api_lline(screen, &last); if (!rval) { while(last) { rval = api_dline(screen, last--); if (rval) break; } } ENDMESSAGE(screen); void PUSH(screen, ...) VI::LINE screen; PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval, i, len; char *line; CODE: INITMESSAGE(screen); rval = api_lline(screen, &last); if (!rval) for (i = 1; i < items; ++i) { line = SvPV(ST(i), len); if ((rval = api_aline(screen, last++, line, len))) break; } ENDMESSAGE(screen); SV * POP(screen) VI::LINE screen; PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval, i, len; CHAR_T *line; PPCODE: INITMESSAGE(screen); rval = api_lline(screen, &last); if (rval || last < 1) ST(0) = &PL_sv_undef; else { rval = api_gline(screen, last, &line, &len) || api_dline(screen, last); EXTEND(sp,1); PUSHs(sv_2mortal(newSVpv(len ? (char *)line : "", len))); } ENDMESSAGE(screen); SV * SHIFT(screen) VI::LINE screen; PREINIT: db_recno_t last; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval, i, len; CHAR_T *line; PPCODE: INITMESSAGE(screen); rval = api_lline(screen, &last); if (rval || last < 1) ST(0) = &PL_sv_undef; else { rval = api_gline(screen, (db_recno_t)1, &line, &len) || api_dline(screen, (db_recno_t)1); EXTEND(sp,1); PUSHs(sv_2mortal(newSVpv(len ? (char *)line : "", len))); } ENDMESSAGE(screen); void UNSHIFT(screen, ...) VI::LINE screen; PREINIT: void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval, i, len; char *np; size_t nlen; CHAR_T *line; CODE: INITMESSAGE(screen); while (--items != 0) { np = SvPV(ST(items), nlen); CHAR2INTP(screen, np, nlen, line, len); if ((rval = api_iline(screen, (db_recno_t)1, line, len))) break; } ENDMESSAGE(screen); void SPLICE(screen, ...) VI::LINE screen; PREINIT: db_recno_t last, db_offset; void (*scr_msg) __P((SCR *, mtype_t, char *, size_t)); int rval, length, common, len, i, offset; CHAR_T *line; char *np; size_t nlen; PPCODE: INITMESSAGE(screen); rval = api_lline(screen, &last); offset = items > 1 ? (int)SvIV(ST(1)) : 0; if (offset < 0) offset += last; if (offset < 0) { ENDMESSAGE(screen); croak("Invalid offset"); } length = items > 2 ? (int)SvIV(ST(2)) : last - offset; if (length > last - offset) length = last - offset; db_offset = offset + 1; /* 1 based */ EXTEND(sp,length); for (common = MIN(length, items - 3), i = 3; common > 0; --common, ++db_offset, --length, ++i) { rval |= api_gline(screen, db_offset, &line, &len); INT2CHAR(screen, line, len, np, nlen); PUSHs(sv_2mortal(newSVpv(nlen ? np : "", nlen))); np = SvPV(ST(i), nlen); CHAR2INTP(screen, np, nlen, line, len); rval |= api_sline(screen, db_offset, line, len); } for (; length; --length) { rval |= api_gline(screen, db_offset, &line, &len); INT2CHAR(screen, line, len, np, nlen); PUSHs(sv_2mortal(newSVpv(len ? np : "", nlen))); rval |= api_dline(screen, db_offset); } for (; i < items; ++i) { np = SvPV(ST(i), len); CHAR2INTP(screen, np, len, line, nlen); rval |= api_iline(screen, db_offset, line, nlen); } ENDMESSAGE(screen); MODULE = VI PACKAGE = VI::TAGQ void Add(tagq, filename, search, msg) VI::TAGQ tagq; char *filename; char *search; char *msg; PREINIT: SCR *sp; CODE: sp = (SCR *)SvIV((SV*)SvRV(tagq->sprv)); if (!sp) croak("screen no longer exists"); api_tagq_add(sp, tagq->tqp, filename, search, msg); void Push(tagq) VI::TAGQ tagq; PREINIT: SCR *sp; CODE: sp = (SCR *)SvIV((SV*)SvRV(tagq->sprv)); if (!sp) croak("screen no longer exists"); api_tagq_push(sp, &tagq->tqp); void DESTROY(tagq) # Can already be invalidated by push VI::TAGQ2 tagq; PREINIT: SCR *sp; CODE: sp = (SCR *)SvIV((SV*)SvRV(tagq->sprv)); if (sp) api_tagq_free(sp, tagq->tqp); SvREFCNT_dec(tagq->sprv); free(tagq);