/* * ex_source -- :source file * Execute ex commands from a file. * * PUBLIC: int ex_source __P((SCR *, EXCMD *)); */ int ex_source(SCR *sp, EXCMD *cmdp) { struct stat sb; int fd, len; char *bp; const char *name; size_t nlen; const CHAR_T *wp; CHAR_T *dp; size_t wlen; INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, name, nlen); if ((fd = open(name, O_RDONLY, 0)) < 0 || fstat(fd, &sb)) goto err; /* * XXX * I'd like to test to see if the file is too large to malloc. Since * we don't know what size or type off_t's or size_t's are, what the * largest unsigned integral type is, or what random insanity the local * C compiler will perpetrate, doing the comparison in a portable way * is flatly impossible. So, put an fairly unreasonable limit on it, * I don't want to be dropping core here. */ #define MEGABYTE 1048576 if (sb.st_size > MEGABYTE) { errno = ENOMEM; goto err; } MALLOC(sp, bp, char *, (size_t)sb.st_size + 1); if (bp == NULL) { (void)close(fd); return (1); } bp[sb.st_size] = '\0'; /* Read the file into memory. */ len = read(fd, bp, (int)sb.st_size); (void)close(fd); if (len == -1 || len != sb.st_size) { if (len != sb.st_size) errno = EIO; free(bp); err: msgq_str(sp, M_SYSERR, name, "%s"); return (1); } if (CHAR2INT(sp, bp, (size_t)sb.st_size + 1, wp, wlen)) msgq(sp, M_ERR, "323|Invalid input. Truncated."); dp = v_wstrdup(sp, wp, wlen - 1); free(bp); /* Put it on the ex queue. */ INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, name, nlen); return (ex_run_str(sp, name, dp, wlen - 1, 1, 1)); }
void transPrint(char *p, int j){ int i = j; if (*p != '\0' && *p > '0' && *p <= '9'){ char c = *p; fakePrint(CHAR2CHAR(c), i++); transPrint(p+1, i); } else if (*p == '\0'){ fakePrint('\0', i); printf("%s\n", gPrintBuffer); return; } else { return; } i = j; if (*(p+1) != '\0' && *(p+1) >= '0' && *(p+1) <= '9'){ char c = *p; char cn = *(p+1); int doubleC = CHAR2INT(c) * 10 + CHAR2INT(cn); if (doubleC <= 26){ fakePrint(INT2CHAR(doubleC), i++); transPrint(p+2, i); } } return; }
/* * ex_tag_next -- * Switch context to the next TAG. * * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *)); */ int ex_tag_next(SCR *sp, EXCMD *cmdp) { EX_PRIVATE *exp; TAG *tp; TAGQ *tqp; char *np; size_t nlen; exp = EXP(sp); if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) { tag_msg(sp, TAG_EMPTY, NULL); return (1); } if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) { msgq(sp, M_ERR, "282|Already at the last tag of this group"); return (1); } if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); tqp->current = tp; if (F_ISSET(tqp, TAG_CSCOPE)) (void)cscope_search(sp, tqp, tp); else (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); if (tqp->current->msg) { INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, np, nlen); msgq(sp, M_INFO, "%s", np); } return (0); }
/* * ex_tag_prev -- * Switch context to the next TAG. * * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *)); */ int ex_tag_prev(SCR *sp, EXCMD *cmdp) { EX_PRIVATE *exp; TAG *tp; TAGQ *tqp; const char *np; size_t nlen; exp = EXP(sp); if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) { tag_msg(sp, TAG_EMPTY, NULL); return (0); } if ((tp = tqp->current->q.cqe_prev) == (void *)&tqp->tagq) { msgq(sp, M_ERR, "255|Already at the first tag of this group"); return (1); } if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); tqp->current = tp; if (F_ISSET(tqp, TAG_CSCOPE)) (void)cscope_search(sp, tqp, tp); else (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); if (tqp->current->msg) { INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, np, nlen); msgq(sp, M_INFO, "%s", np); } return (0); }
/* * ex_edit -- :e[dit][!] [+cmd] [file] * :ex[!] [+cmd] [file] * :vi[sual][!] [+cmd] [file] * * Edit a file; if none specified, re-edit the current file. The third * form of the command can only be executed while in vi mode. See the * hack in ex.c:ex_cmd(). * * !!! * Historic vi didn't permit the '+' command form without specifying * a file name as well. This seems unreasonable, so we support it * regardless. * * PUBLIC: int ex_edit __P((SCR *, EXCMD *)); */ int ex_edit(SCR *sp, EXCMD *cmdp) { FREF *frp; int attach, setalt; const char *np; size_t nlen; switch (cmdp->argc) { case 0: /* * If the name has been changed, we edit that file, not the * original name. If the user was editing a temporary file * (or wasn't editing any file), create another one. The * reason for not reusing temporary files is that there is * special exit processing of them, and reuse is tricky. */ frp = sp->frp; if (sp->ep == NULL || F_ISSET(frp, FR_TMPFILE)) { if ((frp = file_add(sp, NULL)) == NULL) return (1); attach = 0; } else attach = 1; setalt = 0; break; case 1: INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, np, nlen); if ((frp = file_add(sp, np)) == NULL) return (1); attach = 0; setalt = 1; set_alt_name(sp, np); break; default: abort(); } if (F_ISSET(cmdp, E_NEWSCREEN) || cmdp->cmd == &cmds[C_VSPLIT]) return (ex_N_edit(sp, cmdp, frp, attach)); /* * Check for modifications. * * !!! * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit. */ if (file_m2(sp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); /* Switch files. */ if (file_init(sp, frp, NULL, (setalt ? FS_SETALT : 0) | (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0))) return (1); F_SET(sp, SC_FSWITCH); return (0); }
/* * vs_update -- * Update a command. * * PUBLIC: void vs_update __P((SCR *, const char *, const CHAR_T *)); */ void vs_update(SCR *sp, const char *m1, const CHAR_T *m2) { GS *gp; size_t len, mlen, oldx, oldy; const char *np; size_t nlen; gp = sp->gp; /* * This routine displays a message on the bottom line of the screen, * without updating any of the command structures that would keep it * there for any period of time, i.e. it is overwritten immediately. * * It's used by the ex read and ! commands when the user's command is * expanded, and by the ex substitution confirmation prompt. */ if (F_ISSET(sp, SC_SCR_EXWROTE)) { if (m2 != NULL) INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen); (void)ex_printf(sp, "%s%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np); (void)ex_fflush(sp); } /* * Save the cursor position, the substitute-with-confirmation code * will have already set it correctly. */ (void)gp->scr_cursor(sp, &oldy, &oldx); /* Clear the bottom line. */ (void)gp->scr_move(sp, LASTLINE(sp), 0); (void)gp->scr_clrtoeol(sp); /* * XXX * Don't let long file names screw up the screen. */ if (m1 != NULL) { mlen = len = strlen(m1); if (len > sp->cols - 2) mlen = len = sp->cols - 2; (void)gp->scr_addstr(sp, m1, mlen); } else len = 0; if (m2 != NULL) { mlen = STRLEN(m2); if (len + mlen > sp->cols - 2) mlen = (sp->cols - 2) - len; (void)gp->scr_waddstr(sp, m2, mlen); } (void)gp->scr_move(sp, oldy, oldx); (void)gp->scr_refresh(sp, 0); }
/* * ex_emsg -- * Display a few common ex and vi error messages. * * PUBLIC: void ex_wemsg __P((SCR *, const CHAR_T *, exm_t)); */ void ex_wemsg(SCR* sp, const CHAR_T *p, exm_t which) { const char *np; size_t nlen; if (p) INT2CHAR(sp, p, STRLEN(p), np, nlen); else np = NULL; ex_emsg(sp, np, which); }
/* * msgq_wstr -- * Display a message with an embedded string. * * PUBLIC: void msgq_wstr(SCR *, mtype_t, const CHAR_T *, const char *); */ void msgq_wstr(SCR *sp, mtype_t mtype, const CHAR_T *str, const char *fmt) { size_t nlen; CONST char *nstr; if (str == NULL) { msgq(sp, mtype, "%s", fmt); return; } INT2CHAR(sp, str, STRLEN(str) + 1, nstr, nlen); msgq_str(sp, mtype, nstr, fmt); }
/* * ex_file -- :f[ile] [name] * Change the file's name and display the status line. * * PUBLIC: int ex_file __P((SCR *, EXCMD *)); */ int ex_file(SCR *sp, EXCMD *cmdp) { char *p; FREF *frp; const char *np; size_t nlen; NEEDFILE(sp, cmdp); switch (cmdp->argc) { case 0: break; case 1: frp = sp->frp; /* Make sure can allocate enough space. */ INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, np, nlen); if ((p = v_strdup(sp, np, nlen - 1)) == NULL) return (1); /* If already have a file name, it becomes the alternate. */ if (!F_ISSET(frp, FR_TMPFILE)) set_alt_name(sp, frp->name); /* Free the previous name. */ free(frp->name); frp->name = p; /* * The file has a real name, it's no longer a temporary, * clear the temporary file flags. */ F_CLR(frp, FR_TMPEXIT | FR_TMPFILE); /* Have to force a write if the file exists, next time. */ F_SET(frp, FR_NAMECHANGE); /* Notify the screen. */ (void)sp->gp->scr_rename(sp, sp->frp->name, 1); break; default: abort(); } msgq_status(sp, sp->lno, MSTAT_SHOWLAST); return (0); }
/* * ex_prchars -- * Local routine to dump characters to the screen. */ static int ex_prchars(SCR *sp, const CHAR_T *p, size_t *colp, size_t len, u_int flags, int repeatc) { CHAR_T ch; const char *kp; size_t col, tlen, ts; if (O_ISSET(sp, O_LIST)) LF_SET(E_C_LIST); ts = O_VAL(sp, O_TABSTOP); for (col = *colp; len--;) if ((ch = *p++) == L('\t') && !LF_ISSET(E_C_LIST)) for (tlen = ts - col % ts; col < sp->cols && tlen--; ++col) { (void)ex_printf(sp, "%c", repeatc ? repeatc : ' '); if (INTERRUPTED(sp)) goto intr; } else { /* XXXX */ if (INTISWIDE(ch)) { CHAR_T str[2] = {0, 0}; str[0] = ch; INT2CHAR(sp, str, 2, kp, tlen); } else { kp = (char *)KEY_NAME(sp, ch); tlen = KEY_LEN(sp, ch); } if (!repeatc && col + tlen < sp->cols) { (void)ex_puts(sp, kp); col += tlen; } else for (; tlen--; ++kp, ++col) { if (col == sp->cols) { col = 0; (void)ex_puts(sp, "\n"); } (void)ex_printf(sp, "%c", repeatc ? repeatc : *kp); if (INTERRUPTED(sp)) goto intr; } } intr: *colp = col; return (0); }
/****************************************************************************** * * Creates 2D (stereo) source * ******************************************************************************/ EXPORT INT32 HWRAPI (Add2DSource) (sfx_data_t *sfx) { FSOUND_SAMPLE *fmsample = NULL; INT32 chan = -1; if (!sfx) return chan; fmsample = FSOUND_Sample_Load(FSOUND_FREE, INT2CHAR(sfx->data), FSOUND_DOOMLOAD, SFXLENGTH); if (fmsample) { if (!FSOUND_Sample_SetDefaults(fmsample, (INT32)((float)(*((UINT16 *)sfx->data+1)) * recalc_pitch(sfx->pitch)), sfx->volume == -1 ? 255 : sfx->volume, sfx->sep == NORMAL_SEP ? FSOUND_STEREOPAN : sfx->sep, sfx->priority)) DBG_Printf("FMOD(Add2DSource, FSOUND_Sample_SetDefaults, sfxid# %i): %s\n", sfx->id,FMOD_ErrorString(FSOUND_GetError())); if (!FSOUND_Sample_SetMode(fmsample,FSOUND_2D)) DBG_Printf("FMOD(Add2DSource, FSOUND_Sample_SetMode, sfxid# %i): %s\n", sfx->id,FMOD_ErrorString(FSOUND_GetError())); chan = FSOUND_PlaySoundEx(FSOUND_FREE,fmsample,NULL,true); if (chan == -1) { DBG_Printf("FMOD(Add2DSource, FSOUND_PlaySoundEx, sfxid# %i): %s\n", sfx->id,FMOD_ErrorString(FSOUND_GetError())); return chan; } #ifdef MORESTUFF else DBG_Printf("FMOD(Add2DSource, FSOUND_PlaySoundEx): sfxid# %i is playing on channel %i\n", sfx->id,chan); #endif } else { DBG_Printf("FMOD(Add2DSource,FSOUND_Sample_Load, sfxid# %i): %s\n", sfx->id,FMOD_ErrorString(FSOUND_GetError())); return chan; } if (FSOUND_GetCurrentSample(chan)) { if (!FSOUND_SetCurrentPosition(chan, 0)) DBG_Printf("FMOD(Add2DSource, FSOUND_SetCurrentPosition, channel %i, sfxid# %i): %s\n", chan,sfx->id,FMOD_ErrorString(FSOUND_GetError())); } return chan; }
/* * v_editopt -- * Set an option value. */ static int v_editopt(SCR *sp, VICMD *vp) { int rval; const char *np; size_t nlen; char *p2; INT2CHAR(sp, vp->ev.e_str2, STRLEN(vp->ev.e_str2)+1, np, nlen); p2 = strdup(np); rval = api_opts_set(sp, vp->ev.e_str1, p2, vp->ev.e_val1, vp->ev.e_val1); if (sp->gp->scr_reply != NULL) (void)sp->gp->scr_reply(sp, rval, NULL); free(p2); return (rval); }
/* * ex_cscope -- * Perform an ex cscope. * * PUBLIC: int ex_cscope __P((SCR *, EXCMD *)); */ int ex_cscope(SCR *sp, EXCMD *cmdp) { CC const *ccp; EX_PRIVATE *exp; int i; CHAR_T *cmd; CHAR_T *p; const char *np; size_t nlen; /* Initialize the default cscope directories. */ exp = EXP(sp); if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp)) return (1); F_SET(exp, EXP_CSCINIT); /* Skip leading whitespace. */ for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p) if (!ISBLANK((UCHAR_T)*p)) break; if (i == 0) goto usage; /* Skip the command to any arguments. */ for (cmd = p; i > 0; --i, ++p) if (ISBLANK((UCHAR_T)*p)) break; if (*p != '\0') { *p++ = '\0'; for (; *p && ISBLANK((UCHAR_T)*p); ++p); } INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); if ((ccp = lookup_ccmd(np)) == NULL) { usage: msgq(sp, M_ERR, "309|Use \"cscope help\" for help"); return (1); } /* Call the underlying function. */ return (ccp->function(sp, cmdp, p)); }
/* * sscr_setprompt -- * * Set the prompt to the last line we got from the shell. * */ static int sscr_setprompt(SCR *sp, CHAR_T *buf, size_t len) { SCRIPT *sc; const char *np; size_t nlen; sc = sp->script; if (sc->sh_prompt) free(sc->sh_prompt); MALLOC(sp, sc->sh_prompt, char *, len + 1); if (sc->sh_prompt == NULL) { sscr_end(sp); return (1); } INT2CHAR(sp, buf, len, np, nlen); memmove(sc->sh_prompt, np, nlen); sc->sh_prompt_len = len; sc->sh_prompt[len] = '\0'; return (0); }
/* * ex_read -- :read [file] * :read [!cmd] * Read from a file or utility. * * !!! * Historical vi wouldn't undo a filter read, for no apparent reason. * * PUBLIC: int ex_read __P((SCR *, EXCMD *)); */ int ex_read(SCR *sp, EXCMD *cmdp) { enum { R_ARG, R_EXPANDARG, R_FILTER } which; struct stat sb; CHAR_T *arg; char *name; size_t nlen; EX_PRIVATE *exp; FILE *fp; FREF *frp; GS *gp; MARK rm; db_recno_t nlines; size_t arglen; int argc, rval; char *p; char *np; gp = sp->gp; /* * 0 args: read the current pathname. * 1 args: check for "read !arg". */ switch (cmdp->argc) { case 0: which = R_ARG; break; case 1: arg = cmdp->argv[0]->bp; arglen = cmdp->argv[0]->len; if (*arg == '!') { ++arg; --arglen; which = R_FILTER; /* Secure means no shell access. */ if (O_ISSET(sp, O_SECURE)) { ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F); return (1); } } else which = R_EXPANDARG; break; default: abort(); /* NOTREACHED */ } /* Load a temporary file if no file being edited. */ if (sp->ep == NULL) { if ((frp = file_add(sp, NULL)) == NULL) return (1); if (file_init(sp, frp, NULL, 0)) return (1); } switch (which) { case R_FILTER: /* * File name and bang expand the user's argument. If * we don't get an additional argument, it's illegal. */ argc = cmdp->argc; if (argv_exp1(sp, cmdp, arg, arglen, 1)) return (1); if (argc == cmdp->argc) { ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } argc = cmdp->argc - 1; /* Set the last bang command. */ exp = EXP(sp); if (exp->lastbcomm != NULL) free(exp->lastbcomm); if ((exp->lastbcomm = v_wstrdup(sp, cmdp->argv[argc]->bp, cmdp->argv[argc]->len)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } /* * Vi redisplayed the user's argument if it changed, ex * always displayed a !, plus the user's argument if it * changed. */ if (F_ISSET(sp, SC_VI)) { if (F_ISSET(cmdp, E_MODIFY)) (void)vs_update(sp, "!", cmdp->argv[argc]->bp); } else { if (F_ISSET(cmdp, E_MODIFY)) (void)ex_printf(sp, "!%s\n", cmdp->argv[argc]->bp); else (void)ex_puts(sp, "!\n"); (void)ex_fflush(sp); } /* * Historically, filter reads as the first ex command didn't * wait for the user. If SC_SCR_EXWROTE not already set, set * the don't-wait flag. */ if (!F_ISSET(sp, SC_SCR_EXWROTE)) F_SET(sp, SC_EX_WAIT_NO); /* * Switch into ex canonical mode. The reason to restore the * original terminal modes for read filters is so that users * can do things like ":r! cat /dev/tty". * * !!! * We do not output an extra <newline>, so that we don't touch * the screen on a normal read. */ if (F_ISSET(sp, SC_VI)) { if (gp->scr_screen(sp, SC_EX)) { ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON_F); return (1); } /* * !!! * Historically, the read command doesn't switch to * the alternate X11 xterm screen, if doing a filter * read -- don't set SA_ALTERNATE. */ F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); } if (ex_filter(sp, cmdp, &cmdp->addr1, NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ)) return (1); /* The filter version of read set the autoprint flag. */ F_SET(cmdp, E_AUTOPRINT); /* * If in vi mode, move to the first nonblank. Might have * switched into ex mode, so saved the original SC_VI value. */ sp->lno = rm.lno; if (F_ISSET(sp, SC_VI)) { sp->cno = 0; (void)nonblank(sp, sp->lno, &sp->cno); } return (0); case R_ARG: name = sp->frp->name; break; case R_EXPANDARG: if (argv_exp2(sp, cmdp, arg, arglen)) return (1); /* * 0 args: impossible. * 1 args: impossible (I hope). * 2 args: read it. * >2 args: object, too many args. * * The 1 args case depends on the argv_sexp() function refusing * to return success without at least one non-blank character. */ switch (cmdp->argc) { case 0: case 1: abort(); /* NOTREACHED */ case 2: INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len + 1, name, nlen); /* * !!! * Historically, the read and write commands renamed * "unnamed" files, or, if the file had a name, set * the alternate file name. */ if (F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(sp->frp, FR_EXNAMED)) { if ((p = strdup(name)) != NULL) { free(sp->frp->name); sp->frp->name = p; } /* * The file has a real name, it's no longer a * temporary, clear the temporary file flags. */ F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); /* Notify the screen. */ (void)sp->gp->scr_rename(sp, sp->frp->name, 1); name = sp->frp->name; } else { set_alt_name(sp, name); name = sp->alt_name; } break; default: ex_wemsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT); return (1); } break; } /* * !!! * Historically, vi did not permit reads from non-regular files, nor * did it distinguish between "read !" and "read!", so there was no * way to "force" it. We permit reading from named pipes too, since * they didn't exist when the original implementation of vi was done * and they seem a reasonable addition. */ if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) { msgq_str(sp, M_SYSERR, name, "%s"); return (1); } if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) { (void)fclose(fp); msgq(sp, M_ERR, "145|Only regular files and named pipes may be read"); return (1); } /* Try and get a lock. */ if (file_lock(sp, NULL, NULL, fileno(fp), 0) == LOCK_UNAVAIL) msgq(sp, M_ERR, "146|%s: read lock was unavailable", name); rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0); /* * In vi, set the cursor to the first line read in, if anything read * in, otherwise, the address. (Historic vi set it to the line after * the address regardless, but since that line may not exist we don't * bother.) * * In ex, set the cursor to the last line read in, if anything read in, * otherwise, the address. */ if (F_ISSET(sp, SC_VI)) { sp->lno = cmdp->addr1.lno; if (nlines) ++sp->lno; } else sp->lno = cmdp->addr1.lno + nlines; return (rval); }
/* * ex_next -- :next [+cmd] [files] * Edit the next file, optionally setting the list of files. * * !!! * The :next command behaved differently from the :rewind command in * historic vi. See nvi/docs/autowrite for details, but the basic * idea was that it ignored the force flag if the autowrite flag was * set. This implementation handles them all identically. * * PUBLIC: int ex_next __P((SCR *, EXCMD *)); */ int ex_next(SCR *sp, EXCMD *cmdp) { ARGS **argv; FREF *frp; int noargs; char **ap; const CHAR_T *wp; size_t wlen; const char *np; size_t nlen; /* Check for file to move to. */ if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) { msgq(sp, M_ERR, "111|No more files to edit"); return (1); } if (F_ISSET(cmdp, E_NEWSCREEN)) { /* By default, edit the next file in the old argument list. */ if (cmdp->argc == 0) { CHAR2INT(sp, sp->cargv[1], strlen(sp->cargv[1]) + 1, wp, wlen); if (argv_exp0(sp, cmdp, wp, wlen - 1)) return (1); return (ex_edit(sp, cmdp)); } return (ex_N_next(sp, cmdp)); } /* Check modification. */ if (file_m1(sp, FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE)) return (1); /* Any arguments are a replacement file list. */ if (cmdp->argc) { /* Free the current list. */ if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) { for (ap = sp->argv; *ap != NULL; ++ap) free(*ap); free(sp->argv); } F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER); sp->cargv = NULL; /* Create a new list. */ CALLOC_RET(sp, sp->argv, char **, cmdp->argc + 1, sizeof(char *)); for (ap = sp->argv, argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv) { INT2CHAR(sp, argv[0]->bp, argv[0]->len, np, nlen); if ((*ap = v_strdup(sp, np, nlen)) == NULL) return (1); } *ap = NULL; /* Switch to the first file. */ sp->cargv = sp->argv; if ((frp = file_add(sp, *sp->cargv)) == NULL) return (1); noargs = 0; /* Display a file count with the welcome message. */ F_SET(sp, SC_STATUS_CNT); } else { if ((frp = file_add(sp, sp->cargv[1])) == NULL)
/* * cscope_add -- * The cscope add command. */ static int cscope_add(SCR *sp, EXCMD *cmdp, const CHAR_T *dname) { struct stat sb; EX_PRIVATE *exp; CSC *csc; size_t len; int cur_argc; const char *dbname; char path[MAXPATHLEN]; const char *np; char *npp; size_t nlen; exp = EXP(sp); /* * 0 additional args: usage. * 1 additional args: matched a file. * >1 additional args: object, too many args. */ cur_argc = cmdp->argc; if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) { return (1); } if (cmdp->argc == cur_argc) { (void)csc_help(sp, "add"); return (1); } if (cmdp->argc == cur_argc + 1) dname = cmdp->argv[cur_argc]->bp; else { INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen); ex_emsg(sp, np, EXM_FILECOUNT); return (1); } INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen); /* * The user can specify a specific file (so they can have multiple * Cscope databases in a single directory) or a directory. If the * file doesn't exist, we're done. If it's a directory, append the * standard database file name and try again. Store the directory * name regardless so that we can use it as a base for searches. */ if (stat(np, &sb)) { msgq(sp, M_SYSERR, "%s", np); return (1); } if (S_ISDIR(sb.st_mode)) { (void)snprintf(path, sizeof(path), "%s/%s", np, CSCOPE_DBFILE); if (stat(path, &sb)) { msgq(sp, M_SYSERR, "%s", path); return (1); } dbname = CSCOPE_DBFILE; } else if ((npp = strrchr(np, '/')) != NULL) { *npp = '\0'; dbname = npp + 1; } else { dbname = np; np = "."; } /* Allocate a cscope connection structure and initialize its fields. */ len = strlen(np); CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len); csc->dname = csc->buf; csc->dlen = len; memcpy(csc->dname, np, len); csc->mtime = sb.st_mtime; /* Get the search paths for the cscope. */ if (get_paths(sp, csc)) goto err; /* Start the cscope process. */ if (run_cscope(sp, csc, dbname)) goto err; /* * Add the cscope connection to the screen's list. From now on, * on error, we have to call terminate, which expects the csc to * be on the chain. */ LIST_INSERT_HEAD(&exp->cscq, csc, q); /* Read the initial prompt from the cscope to make sure it's okay. */ return read_prompt(sp, csc); err: free(csc); return (1); }
/* * ex_mkexrc -- :mkexrc[!] [file] * * Create (or overwrite) a .exrc file with the current info. * * PUBLIC: int ex_mkexrc __P((SCR *, EXCMD *)); */ int ex_mkexrc(SCR *sp, EXCMD *cmdp) { struct stat sb; FILE *fp; int fd, sverrno; char *fname; size_t flen; switch (cmdp->argc) { case 0: fname = _PATH_EXRC; break; case 1: INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, fname, flen); set_alt_name(sp, fname); break; default: abort(); } if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && !stat(fname, &sb)) { msgq_str(sp, M_ERR, fname, "137|%s exists, not written; use ! to override"); return (1); } /* Create with max permissions of rw-r--r--. */ if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { msgq_str(sp, M_SYSERR, fname, "%s"); return (1); } if ((fp = fdopen(fd, "w")) == NULL) { sverrno = errno; (void)close(fd); goto e2; } if (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV) || ferror(fp)) goto e1; if (seq_save(sp, fp, "map ", SEQ_COMMAND) || ferror(fp)) goto e1; if (seq_save(sp, fp, "map! ", SEQ_INPUT) || ferror(fp)) goto e1; if (opts_save(sp, fp) || ferror(fp)) goto e1; if (fclose(fp)) { sverrno = errno; goto e2; } msgq_str(sp, M_INFO, fname, "138|New exrc file: %s"); return (0); e1: sverrno = errno; (void)fclose(fp); e2: errno = sverrno; msgq_str(sp, M_SYSERR, fname, "%s"); return (1); }
/* * sscr_exec -- * Take a line and hand it off to the shell. * * PUBLIC: int sscr_exec __P((SCR *, db_recno_t)); */ int sscr_exec(SCR *sp, db_recno_t lno) { SCRIPT *sc; db_recno_t last_lno; size_t blen, len, last_len; int isempty, matchprompt, rval; ssize_t nw; char *bp = NULL; const char *p; const CHAR_T *ip; size_t ilen; sc = sp->script; /* If there's a prompt on the last line, append the command. */ if (db_last(sp, &last_lno)) return (1); if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen)) return (1); INT2CHAR(sp, ip, ilen, p, last_len); if (last_len == sc->sh_prompt_len && memcmp(p, sc->sh_prompt, last_len) == 0) { matchprompt = 1; GET_SPACE_RETC(sp, bp, blen, last_len + 128); memmove(bp, p, last_len); } else matchprompt = 0; /* Get something to execute. */ if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) { if (isempty) goto empty; goto err1; } /* Empty lines aren't interesting. */ if (ilen == 0) goto empty; INT2CHAR(sp, ip, ilen, p, len); /* Delete any prompt. */ if (len >= sc->sh_prompt_len && memcmp(p, sc->sh_prompt, sc->sh_prompt_len) == 0) { len -= sc->sh_prompt_len; if (len == 0) { empty: msgq(sp, M_BERR, "151|No command to execute"); goto err1; } p += sc->sh_prompt_len; } /* Push the line to the shell. */ if ((size_t)(nw = write(sc->sh_master, p, len)) != len) goto err2; rval = 0; if (write(sc->sh_master, "\n", 1) != 1) { err2: if (nw == 0) errno = EIO; msgq(sp, M_SYSERR, "shell"); goto err1; } if (matchprompt) { ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len); memmove(bp + last_len, p, len); CHAR2INT(sp, bp, last_len + len, ip, ilen); if (db_set(sp, last_lno, ip, ilen)) err1: rval = 1; } if (matchprompt) alloc_err: FREE_SPACE(sp, bp, blen); return (rval); }
/* * ex_filter -- * Run a range of lines through a filter utility and optionally * replace the original text with the stdout/stderr output of * the utility. * * PUBLIC: int ex_filter __P((SCR *, * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype)); */ int ex_filter(SCR *sp, EXCMD *cmdp, MARK *fm, MARK *tm, MARK *rp, CHAR_T *cmd, enum filtertype ftype) { FILE *ifp, *ofp; pid_t parent_writer_pid, utility_pid; db_recno_t nread; int input[2], output[2], rval; char *name; char *np; size_t nlen; rval = 0; /* Set return cursor position, which is never less than line 1. */ *rp = *fm; if (rp->lno == 0) rp->lno = 1; /* We're going to need a shell. */ if (opts_empty(sp, O_SHELL, 0)) return (1); /* * There are three different processes running through this code. * They are the utility, the parent-writer and the parent-reader. * The parent-writer is the process that writes from the file to * the utility, the parent reader is the process that reads from * the utility. * * Input and output are named from the utility's point of view. * The utility reads from input[0] and the parent(s) write to * input[1]. The parent(s) read from output[0] and the utility * writes to output[1]. * * !!! * Historically, in the FILTER_READ case, the utility reads from * the terminal (e.g. :r! cat works). Otherwise open up utility * input pipe. */ ofp = NULL; input[0] = input[1] = output[0] = output[1] = -1; if (ftype != FILTER_READ && pipe(input) < 0) { msgq(sp, M_SYSERR, "pipe"); goto err; } /* Open up utility output pipe. */ if (pipe(output) < 0) { msgq(sp, M_SYSERR, "pipe"); goto err; } if ((ofp = fdopen(output[0], "r")) == NULL) { msgq(sp, M_SYSERR, "fdopen"); goto err; } /* Fork off the utility process. */ switch (utility_pid = vfork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "vfork"); err: if (input[0] != -1) (void)close(input[0]); if (input[1] != -1) (void)close(input[1]); if (ofp != NULL) (void)fclose(ofp); else if (output[0] != -1) (void)close(output[0]); if (output[1] != -1) (void)close(output[1]); return (1); case 0: /* Utility. */ /* * Redirect stdin from the read end of the input pipe, and * redirect stdout/stderr to the write end of the output pipe. * * !!! * Historically, ex only directed stdout into the input pipe, * letting stderr come out on the terminal as usual. Vi did * not, directing both stdout and stderr into the input pipe. * We match that practice in both ex and vi for consistency. */ if (input[0] != -1) (void)dup2(input[0], STDIN_FILENO); (void)dup2(output[1], STDOUT_FILENO); (void)dup2(output[1], STDERR_FILENO); /* Close the utility's file descriptors. */ if (input[0] != -1) (void)close(input[0]); if (input[1] != -1) (void)close(input[1]); (void)close(output[0]); (void)close(output[1]); if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) name = O_STR(sp, O_SHELL); else ++name; INT2SYS(sp, cmd, STRLEN(cmd)+1, np, nlen); execl(O_STR(sp, O_SHELL), name, "-c", np, (char *)NULL); msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); _exit (127); /* NOTREACHED */ default: /* Parent-reader, parent-writer. */ /* Close the pipe ends neither parent will use. */ if (input[0] != -1) (void)close(input[0]); (void)close(output[1]); break; } /* * FILTER_RBANG, FILTER_READ: * * Reading is the simple case -- we don't need a parent writer, * so the parent reads the output from the read end of the output * pipe until it finishes, then waits for the child. Ex_readfp * appends to the MARK, and closes ofp. * * For FILTER_RBANG, there is nothing to write to the utility. * Make sure it doesn't wait forever by closing its standard * input. * * !!! * Set the return cursor to the last line read in for FILTER_READ. * Historically, this behaves differently from ":r file" command, * which leaves the cursor at the first line read in. Check to * make sure that it's not past EOF because we were reading into an * empty file. */ if (ftype == FILTER_RBANG || ftype == FILTER_READ) { if (ftype == FILTER_RBANG) (void)close(input[1]); if (ex_readfp(sp, "filter", ofp, fm, &nread, 1)) rval = 1; sp->rptlines[L_ADDED] += nread; if (ftype == FILTER_READ) if (fm->lno == 0) rp->lno = nread; else rp->lno += nread; goto uwait; } /* * FILTER_BANG, FILTER_WRITE * * Here we need both a reader and a writer. Temporary files are * expensive and we'd like to avoid disk I/O. Using pipes has the * obvious starvation conditions. It's done as follows: * * fork * child * write lines out * exit * parent * FILTER_BANG: * read lines into the file * delete old lines * FILTER_WRITE * read and display lines * wait for child * * XXX * We get away without locking the underlying database because we know * that none of the records that we're reading will be modified until * after we've read them. This depends on the fact that the current * B+tree implementation doesn't balance pages or similar things when * it inserts new records. When the DB code has locking, we should * treat vi as if it were multiple applications sharing a database, and * do the required locking. If necessary a work-around would be to do * explicit locking in the line.c:db_get() code, based on the flag set * here. */ F_SET(sp->ep, F_MULTILOCK); switch (parent_writer_pid = fork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "fork"); (void)close(input[1]); (void)close(output[0]); rval = 1; break; case 0: /* Parent-writer. */ /* * Write the selected lines to the write end of the input * pipe. This instance of ifp is closed by ex_writefp. */ (void)close(output[0]); if ((ifp = fdopen(input[1], "w")) == NULL) _exit (1); _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1)); /* NOTREACHED */ default: /* Parent-reader. */ (void)close(input[1]); if (ftype == FILTER_WRITE) { /* * Read the output from the read end of the output * pipe and display it. Filter_ldisplay closes ofp. */ if (filter_ldisplay(sp, ofp)) rval = 1; } else { /* * Read the output from the read end of the output * pipe. Ex_readfp appends to the MARK and closes * ofp. */ if (ex_readfp(sp, "filter", ofp, tm, &nread, 1)) rval = 1; sp->rptlines[L_ADDED] += nread; } /* Wait for the parent-writer. */ if (proc_wait(sp, (long)parent_writer_pid, "parent-writer", 0, 1)) rval = 1; /* Delete any lines written to the utility. */ if (rval == 0 && ftype == FILTER_BANG && (cut(sp, NULL, fm, tm, CUT_LINEMODE) || del(sp, fm, tm, 1))) { rval = 1; break; } /* * If the filter had no output, we may have just deleted * the cursor. Don't do any real error correction, we'll * try and recover later. */ if (rp->lno > 1 && !db_exist(sp, rp->lno)) --rp->lno; break; } F_CLR(sp->ep, F_MULTILOCK); /* * !!! * Ignore errors on vi file reads, to make reads prettier. It's * completely inconsistent, and historic practice. */ uwait: INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); return (proc_wait(sp, (long)utility_pid, np, ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval); }
/* * ex_usage -- :exusage [cmd] * Display ex usage strings. * * PUBLIC: int ex_usage __P((SCR *, EXCMD *)); */ int ex_usage(SCR *sp, EXCMD *cmdp) { ARGS *ap; EXCMDLIST const *cp; int newscreen; CHAR_T *p, nb[MAXCMDNAMELEN + 5]; const CHAR_T *name; switch (cmdp->argc) { case 1: ap = cmdp->argv[0]; if (ISUPPER((UCHAR_T)ap->bp[0])) { newscreen = 1; ap->bp[0] = TOLOWER((UCHAR_T)ap->bp[0]); } else newscreen = 0; for (cp = cmds; cp->name != NULL && memcmp(ap->bp, cp->name, ap->len); ++cp); if (cp->name == NULL || (newscreen && !F_ISSET(cp, E_NEWSCREEN))) { const char *nstr; size_t nlen; if (newscreen) ap->bp[0] = TOUPPER((UCHAR_T)ap->bp[0]); INT2CHAR(sp, ap->bp, ap->len + 1, nstr, nlen); (void)ex_printf(sp, "The %.*s command is unknown\n", (int)ap->len, nstr); } else { (void)ex_printf(sp, "Command: %s\n Usage: %s\n", cp->help, cp->usage); /* * !!! * The "visual" command has two modes, one from ex, * one from the vi colon line. Don't ask. */ if (cp != &cmds[C_VISUAL_EX] && cp != &cmds[C_VISUAL_VI]) break; if (cp == &cmds[C_VISUAL_EX]) cp = &cmds[C_VISUAL_VI]; else cp = &cmds[C_VISUAL_EX]; (void)ex_printf(sp, "Command: %s\n Usage: %s\n", cp->help, cp->usage); } break; case 0: for (cp = cmds; cp->name != NULL && !INTERRUPTED(sp); ++cp) { /* * The ^D command has an unprintable name. * * XXX * We display both capital and lower-case versions of * the appropriate commands -- no need to add in extra * room, they're all short names. */ if (cp == &cmds[C_SCROLL]) name = L("^D"); else if (F_ISSET(cp, E_NEWSCREEN)) { nb[0] = L('['); nb[1] = TOUPPER((UCHAR_T)cp->name[0]); nb[2] = cp->name[0]; nb[3] = L(']'); for (name = cp->name + 1, p = nb + 4; (*p++ = *name++) != '\0';); name = nb; } else name = cp->name; (void)ex_printf(sp, WVS": %s\n", MAXCMDNAMELEN, name, cp->help); } break; default: abort(); } return (0); }
/* * ex_bang -- :[line [,line]] ! command * * Pass the rest of the line after the ! character to the program named by * the O_SHELL option. * * Historical vi did NOT do shell expansion on the arguments before passing * them, only file name expansion. This means that the O_SHELL program got * "$t" as an argument if that is what the user entered. Also, there's a * special expansion done for the bang command. Any exclamation points in * the user's argument are replaced by the last, expanded ! command. * * There's some fairly amazing slop in this routine to make the different * ways of getting here display the right things. It took a long time to * get it right (wrong?), so be careful. * * PUBLIC: int ex_bang(SCR *, EXCMD *); */ int ex_bang(SCR *sp, EXCMD *cmdp) { enum filtertype ftype; ARGS *ap; EX_PRIVATE *exp; MARK rm; recno_t lno; int rval; const char *msg; char *np; size_t nlen; ap = cmdp->argv[0]; if (ap->len == 0) { ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } /* Set the "last bang command" remembered value. */ exp = EXP(sp); free(exp->lastbcomm); if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } /* * If the command was modified by the expansion, it was historically * redisplayed. */ if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) { /* * Display the command if modified. Historic ex/vi displayed * the command if it was modified due to file name and/or bang * expansion. If piping lines in vi, it would be immediately * overwritten by any error or line change reporting. */ if (F_ISSET(sp, SC_VI)) vs_update(sp, "!", ap->bp); else { (void)ex_printf(sp, "!"WS"\n", ap->bp); (void)ex_fflush(sp); } } /* * If no addresses were specified, run the command. If there's an * underlying file, it's been modified and autowrite is set, write * the file back. If the file has been modified, autowrite is not * set and the warn option is set, tell the user about the file. */ if (cmdp->addrcnt == 0) { msg = NULL; if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED)) if (O_ISSET(sp, O_AUTOWRITE)) { if (file_aw(sp, FS_ALL)) return (0); } else if (O_ISSET(sp, O_WARN) && !F_ISSET(sp, SC_EX_SILENT)) msg = msg_cat(sp, "303|File modified since last write.", NULL); /* If we're still in a vi screen, move out explicitly. */ INT2CHAR(sp, ap->bp, ap->len+1, np, nlen); (void)ex_exec_proc(sp, cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)); } /* * If addresses were specified, pipe lines from the file through the * command. * * Historically, vi lines were replaced by both the stdout and stderr * lines of the command, but ex lines by only the stdout lines. This * makes no sense to me, so nvi makes it consistent for both, and * matches vi's historic behavior. */ else { NEEDFILE(sp, cmdp); /* Autoprint is set historically, even if the command fails. */ F_SET(cmdp, E_AUTOPRINT); /* * !!! * Historical vi permitted "!!" in an empty file. When this * happens, we arrive here with two addresses of 1,1 and a * bad attitude. The simple solution is to turn it into a * FILTER_READ operation, with the exception that stdin isn't * opened for the utility, and the cursor position isn't the * same. The only historic glitch (I think) is that we don't * put an empty line into the default cut buffer, as historic * vi did. Imagine, if you can, my disappointment. */ ftype = FILTER_BANG; if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) { if (db_last(sp, &lno)) return (1); if (lno == 0) { cmdp->addr1.lno = cmdp->addr2.lno = 0; ftype = FILTER_RBANG; } } rval = ex_filter(sp, cmdp, &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype); /* * If in vi mode, move to the first nonblank. * * !!! * Historic vi wasn't consistent in this area -- if you used * a forward motion it moved to the first nonblank, but if you * did a backward motion it didn't. And, if you followed a * backward motion with a forward motion, it wouldn't move to * the nonblank for either. Going to the nonblank generally * seems more useful and consistent, so we do it. */ sp->lno = rm.lno; if (F_ISSET(sp, SC_VI)) { sp->cno = 0; (void)nonblank(sp, sp->lno, &sp->cno); } else sp->cno = rm.cno; } /* Ex terminates with a bang, even if the command fails. */ if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) (void)ex_puts(sp, "!\n"); /* * XXX * The ! commands never return an error, so that autoprint always * happens in the ex parser. */ return (0); }
/* * opts_set -- * Change the values of one or more options. * * PUBLIC: int opts_set __P((SCR *, ARGS *[], const char *)); */ int opts_set(SCR *sp, ARGS **argv, const char *usage) { enum optdisp disp; enum nresult nret; OPTLIST const *op; OPTION *spo; u_long isset, turnoff, value; int ch, equals, nf, nf2, offset, qmark, rval; CHAR_T *endp, *name, *p, *sep; char *p2, *t2; const char *np; size_t nlen; disp = NO_DISPLAY; for (rval = 0; argv[0]->len != 0; ++argv) { /* * The historic vi dumped the options for each occurrence of * "all" in the set list. Puhleeze. */ if (!STRCMP(argv[0]->bp, L("all"))) { disp = ALL_DISPLAY; continue; } /* Find equals sign or question mark. */ for (sep = NULL, equals = qmark = 0, p = name = argv[0]->bp; (ch = *p) != '\0'; ++p) if (ch == '=' || ch == '?') { if (p == name) { if (usage != NULL) msgq(sp, M_ERR, "032|Usage: %s", usage); return (1); } sep = p; if (ch == '=') equals = 1; else qmark = 1; break; } turnoff = 0; op = NULL; if (sep != NULL) *sep++ = '\0'; /* Search for the name, then name without any leading "no". */ if ((op = opts_search(name)) == NULL && name[0] == L('n') && name[1] == L('o')) { turnoff = 1; name += 2; op = opts_search(name); } if (op == NULL) { opts_nomatch(sp, name); rval = 1; continue; } /* Find current option values. */ offset = op - optlist; spo = sp->opts + offset; /* * !!! * Historically, the question mark could be a separate * argument. */ if (!equals && !qmark && argv[1]->len == 1 && argv[1]->bp[0] == '?') { ++argv; qmark = 1; } /* Set name, value. */ switch (op->type) { case OPT_0BOOL: case OPT_1BOOL: /* Some options may not be reset. */ if (F_ISSET(op, OPT_NOUNSET) && turnoff) { msgq_wstr(sp, M_ERR, name, "291|set: the %s option may not be turned off"); rval = 1; break; } /* Some options may not be set. */ if (F_ISSET(op, OPT_NOSET) && !turnoff) { msgq_wstr(sp, M_ERR, name, "313|set: the %s option may never be turned on"); rval = 1; break; } if (equals) { msgq_wstr(sp, M_ERR, name, "034|set: [no]%s option doesn't take a value"); rval = 1; break; } if (qmark) { if (!disp) disp = SELECT_DISPLAY; F_SET(spo, OPT_SELECTED); break; } /* * Do nothing if the value is unchanged, the underlying * functions can be expensive. */ isset = !turnoff; if (!F_ISSET(op, OPT_ALWAYS)) { if (isset) { if (O_ISSET(sp, offset)) break; } else if (!O_ISSET(sp, offset)) break; } /* Report to subsystems. */ if ((op->func != NULL && op->func(sp, spo, NULL, &isset)) || ex_optchange(sp, offset, NULL, &isset) || v_optchange(sp, offset, NULL, &isset) || sp->gp->scr_optchange(sp, offset, NULL, &isset)) { rval = 1; break; } /* Set the value. */ if (isset) O_SET(sp, offset); else O_CLR(sp, offset); break; case OPT_NUM: if (turnoff) { msgq_wstr(sp, M_ERR, name, "035|set: %s option isn't a boolean"); rval = 1; break; } if (qmark || !equals) { if (!disp) disp = SELECT_DISPLAY; F_SET(spo, OPT_SELECTED); break; } if (!ISDIGIT((UCHAR_T)sep[0])) goto badnum; if ((nret = nget_uslong(sp, &value, sep, &endp, 10)) != NUM_OK) { INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen); p2 = msg_print(sp, np, &nf); INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen); t2 = msg_print(sp, np, &nf2); switch (nret) { case NUM_ERR: msgq(sp, M_SYSERR, "036|set: %s option: %s", p2, t2); break; case NUM_OVER: msgq(sp, M_ERR, "037|set: %s option: %s: value overflow", p2, t2); break; case NUM_OK: case NUM_UNDER: abort(); } if (nf) FREE_SPACE(sp, p2, 0); if (nf2) FREE_SPACE(sp, t2, 0); rval = 1; break; } if (*endp && !ISBLANK(*endp)) { badnum: INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen); p2 = msg_print(sp, np, &nf); INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen); t2 = msg_print(sp, np, &nf2); msgq(sp, M_ERR, "038|set: %s option: %s is an illegal number", p2, t2); if (nf) FREE_SPACE(sp, p2, 0); if (nf2) FREE_SPACE(sp, t2, 0); rval = 1; break; } /* Some options may never be set to zero. */ if (F_ISSET(op, OPT_NOZERO) && value == 0) { msgq_wstr(sp, M_ERR, name, "314|set: the %s option may never be set to 0"); rval = 1; break; } /* * Do nothing if the value is unchanged, the underlying * functions can be expensive. */ if (!F_ISSET(op, OPT_ALWAYS) && O_VAL(sp, offset) == value) break; /* Report to subsystems. */ INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen); if ((op->func != NULL && op->func(sp, spo, np, &value)) || ex_optchange(sp, offset, np, &value) || v_optchange(sp, offset, np, &value) || sp->gp->scr_optchange(sp, offset, np, &value)) { rval = 1; break; } /* Set the value. */ if (o_set(sp, offset, 0, NULL, value)) rval = 1; break; case OPT_STR: if (turnoff) { msgq_wstr(sp, M_ERR, name, "039|set: %s option isn't a boolean"); rval = 1; break; } if (qmark || !equals) { if (!disp) disp = SELECT_DISPLAY; F_SET(spo, OPT_SELECTED); break; } /* Check for strings that must have even length */ if (F_ISSET(op, OPT_PAIRS) && STRLEN(sep) & 1) { msgq_wstr(sp, M_ERR, name, "047|set: the %s option must be in two character groups"); rval = 1; break; } /* * Do nothing if the value is unchanged, the underlying * functions can be expensive. */ INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen); if (!F_ISSET(op, OPT_ALWAYS) && O_STR(sp, offset) != NULL && !strcmp(O_STR(sp, offset), np)) break; /* Report to subsystems. */ if ((op->func != NULL && op->func(sp, spo, np, NULL)) || ex_optchange(sp, offset, np, NULL) || v_optchange(sp, offset, np, NULL) || sp->gp->scr_optchange(sp, offset, np, NULL)) { rval = 1; break; } /* Set the value. */ if (o_set(sp, offset, OS_STRDUP, np, 0)) rval = 1; break; default: abort(); } } if (disp != NO_DISPLAY) opts_dump(sp, disp); return (rval); }
EXPORT INT32 HWRAPI (Add3DSource) (source3D_data_t *src, sfx_data_t *sfx) { FSOUND_SAMPLE *fmsample = NULL; INT32 chan = -1; float pos[3]; float vel[3]; #ifdef MORESTUFF src->min_distance = MIN_DISTANCE; src->max_distance = MAX_DISTANCE; #endif pos[0] = src->pos.x; pos[1] = src->pos.z; pos[2] = src->pos.y; vel[0] = src->pos.momx; vel[1] = src->pos.momz; vel[2] = src->pos.momy; if (sfx) fmsample = FSOUND_Sample_Load(FSOUND_FREE, INT2CHAR(sfx->data), FSOUND_DOOMLOAD, SFXLENGTH); else fmsample = blankfmsample; if (fmsample) { if (sfx && !FSOUND_Sample_SetDefaults(fmsample, (INT32)((*((UINT16 *)sfx->data+1))*recalc_pitch(sfx->pitch)), (sfx->volume == -1 ? 255 : sfx->volume), (sfx->sep == NORMAL_SEP ? FSOUND_STEREOPAN : sfx->sep), (sfx->priority) ) ) DBG_Printf("FMOD(Add3DSource, FSOUND_Sample_SetDefaults, SFX's ID# %i): %s\n", sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); #if 0 if (!FSOUND_Sample_SetMinMaxDistance(fmsample, src->min_distance, src->max_distance)) DBG_Printf("FMOD(Add3DSource, FSOUND_Sample_SetMinMaxDistance, SFX's ID# %i): %s\n", sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); #endif chan = FSOUND_PlaySoundEx(FSOUND_FREE,fmsample,NULL,true); if (chan == -1) { DBG_Printf("FMOD(Add3DSource, FSOUND_PlaySoundEx, SFX's ID# %i): %s\n",sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); return chan; } else { if (!sfx) DBG_Printf("FMOD(Add3DSource, Main): Added blank-sound added to channel %i\n",chan); #ifdef MORESTUFF else DBG_Printf("FMOD(Add3DSource, Main): Added sfxid# %i added to channel %i\n",sfx->id,chan); #endif } } else { if (sfx) DBG_Printf("FMOD(Add3DSource, FSOUND_Sample_Load, sfxid# %i): %s\n",sfx->id,FMOD_ErrorString(FSOUND_GetError())); else DBG_Printf("FMOD(Add3DSource, FSOUND_Sample_Alloc): %s\n", FMOD_ErrorString(FSOUND_GetError())); return chan; } if (FSOUND_GetCurrentSample(chan)) { if (!FSOUND_SetCurrentPosition(chan, 0)) DBG_Printf("FMOD(Add3DSource, FSOUND_SetCurrentPosition, channel %i, sfxid# %i): %s\n", chan,sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); if (!FSOUND_3D_SetAttributes(chan,pos,vel)) DBG_Printf("FMOD(Add3DSource, FSOUND_3D_SetAttributes, channel %i, sfxid# %i): %s\n", chan,sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); if (!FSOUND_SetReserved(chan, (signed char)src->permanent)) DBG_Printf("FMOD(Add3DSource, FSOUND_SetReserved, channel %i, sfxid# %i): %s\n", chan,sfx?sfx->id:0,FMOD_ErrorString(FSOUND_GetError())); if (src->head_relative) reladd(chan); } return chan; }
/* * exwr -- * The guts of the ex write commands. */ static int exwr(SCR *sp, EXCMD *cmdp, enum which cmd) { MARK rm; int flags; char *name; CHAR_T *p = NULL; size_t nlen; char *n; int rc; EX_PRIVATE *exp; NEEDFILE(sp, cmdp); /* All write commands can have an associated '!'. */ LF_INIT(FS_POSSIBLE); if (FL_ISSET(cmdp->iflags, E_C_FORCE)) LF_SET(FS_FORCE); /* Skip any leading whitespace. */ if (cmdp->argc != 0) for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p); /* If "write !" it's a pipe to a utility. */ if (cmdp->argc != 0 && cmd == WRITE && *p == '!') { /* Secure means no shell access. */ if (O_ISSET(sp, O_SECURE)) { ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F); return (1); } /* Expand the argument. */ for (++p; *p && cmdskip(*p); ++p); if (*p == '\0') { ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } if (argv_exp1(sp, cmdp, p, STRLEN(p), 1)) return (1); /* Set the last bang command */ exp = EXP(sp); free(exp->lastbcomm); exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len); /* * Historically, vi waited after a write filter even if there * wasn't any output from the command. People complained when * nvi waited only if there was output, wanting the visual cue * that the program hadn't written anything. */ F_SET(sp, SC_EX_WAIT_YES); /* * !!! * Ignore the return cursor position, the cursor doesn't * move. */ if (ex_filter(sp, cmdp, &cmdp->addr1, &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE)) return (1); /* Ex terminates with a bang, even if the command fails. */ if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) (void)ex_puts(sp, "!\n"); return (0); } /* Set the FS_ALL flag if we're writing the entire file. */ if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1)) LF_SET(FS_ALL); /* If "write >>" it's an append to a file. */ if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') { LF_SET(FS_APPEND); /* Skip ">>" and whitespace. */ for (p += 2; *p && cmdskip(*p); ++p); } /* If no other arguments, just write the file back. */ if (cmdp->argc == 0 || *p == '\0') return (file_write(sp, &cmdp->addr1, &cmdp->addr2, NULL, flags)); /* Build an argv so we get an argument count and file expansion. */ if (argv_exp2(sp, cmdp, p, STRLEN(p))) return (1); /* * 0 args: impossible. * 1 args: impossible (I hope). * 2 args: read it. * >2 args: object, too many args. * * The 1 args case depends on the argv_sexp() function refusing * to return success without at least one non-blank character. */ switch (cmdp->argc) { case 0: case 1: abort(); /* NOTREACHED */ case 2: INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1, n, nlen); name = v_strdup(sp, n, nlen - 1); /* * !!! * Historically, the read and write commands renamed * "unnamed" files, or, if the file had a name, set * the alternate file name. */ if (F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(sp->frp, FR_EXNAMED)) { if ((n = v_strdup(sp, name, nlen - 1)) != NULL) { free(sp->frp->name); sp->frp->name = n; } /* * The file has a real name, it's no longer a * temporary, clear the temporary file flags. * * !!! * If we're writing the whole file, FR_NAMECHANGE * will be cleared by the write routine -- this is * historic practice. */ F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); /* Notify the screen. */ (void)sp->gp->scr_rename(sp, sp->frp->name, 1); } else set_alt_name(sp, name); break; default: INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen); ex_emsg(sp, n, EXM_FILECOUNT); return (1); } rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags); free(name); return rc; }