ex_cmd.c revision 259065
11558Srgrimes/*-
293492Sphk * Copyright (c) 1992, 1993, 1994
393492Sphk *	The Regents of the University of California.  All rights reserved.
493492Sphk * Copyright (c) 1992, 1993, 1994, 1995, 1996
51558Srgrimes *	Keith Bostic.  All rights reserved.
693492Sphk *
793492Sphk * See the LICENSE file for redistribution information.
893492Sphk */
993492Sphk
1093492Sphk#include "config.h"
111558Srgrimes
121558Srgrimes#ifndef lint
131558Srgrimesstatic const char sccsid[] = "$Id: ex_cmd.c,v 10.26 2011/07/14 15:11:16 zy Exp $";
141558Srgrimes#endif /* not lint */
151558Srgrimes
161558Srgrimes#include <sys/types.h>
171558Srgrimes#include <sys/queue.h>
181558Srgrimes#include <sys/time.h>
1993492Sphk
2093492Sphk#include <bitstring.h>
2193492Sphk#include <limits.h>
221558Srgrimes#include <stdio.h>
2393492Sphk
241558Srgrimes#include "../common/common.h"
251558Srgrimes
2693492Sphk/*
271558Srgrimes * This array maps ex command names to command functions.
281558Srgrimes *
291558Srgrimes * The order in which command names are listed below is important --
301558Srgrimes * ambiguous abbreviations are resolved to be the first possible match,
311558Srgrimes * e.g. "r" means "read", not "rewind", because "read" is listed before
321558Srgrimes * "rewind".
331558Srgrimes *
3496049Sfenner * The syntax of the ex commands is unbelievably irregular, and a special
3596049Sfenner * case from beginning to end.  Each command has an associated "syntax
3696049Sfenner * script" which describes the "arguments" that are possible.  The script
3796049Sfenner * syntax is as follows:
3896049Sfenner *
3996049Sfenner *	!		-- ! flag
4096049Sfenner *	1		-- flags: [+-]*[pl#][+-]*
4196049Sfenner *	2		-- flags: [-.+^]
4296049Sfenner *	3		-- flags: [-.+^=]
4396049Sfenner *	b		-- buffer
4496049Sfenner *	c[01+a]		-- count (0-N, 1-N, signed 1-N, address offset)
4596049Sfenner *	f[N#][or]	-- file (a number or N, optional or required)
4696049Sfenner *	l		-- line
4796049Sfenner *	S		-- string with file name expansion
4896049Sfenner *	s		-- string
4996049Sfenner *	W		-- word string
5096049Sfenner *	w[N#][or]	-- word (a number or N, optional or required)
5196049Sfenner */
5296049SfennerEXCMDLIST const cmds[] = {
5396049Sfenner/* C_SCROLL */
5496049Sfenner	{L("\004"),	ex_pr,		E_ADDR2,
5596049Sfenner	    "",
5696049Sfenner	    "^D",
5796049Sfenner	    "scroll lines"},
5896049Sfenner/* C_BANG */
5996049Sfenner	{L("!"),		ex_bang,	E_ADDR2_NONE|E_SECURE,
6096049Sfenner	    "S",
611558Srgrimes	    "[line [,line]] ! command",
621558Srgrimes	    "filter lines through commands or run commands"},
6395183Scharnier/* C_HASH */
6495183Scharnier	{L("#"),		ex_number,	E_ADDR2|E_CLRFLAG,
6595183Scharnier	    "ca1",
6696049Sfenner	    "[line [,line]] # [count] [l]",
6794580Smarcel	    "display numbered lines"},
6894580Smarcel/* C_SUBAGAIN */
6996025Smux	{L("&"),		ex_subagain,	E_ADDR2|E_ADDR_ZERO,
7094580Smarcel	    "s",
7194580Smarcel	    "[line [,line]] & [cgr] [count] [#lp]",
7293492Sphk	    "repeat the last subsitution"},
7393492Sphk/* C_STAR */
7496025Smux	{L("*"),		ex_at,		0,
75234069Srmh	    "b",
7696049Sfenner	    "* [buffer]",
7794580Smarcel	    "execute a buffer"},
7894580Smarcel/* C_SHIFTL */
7994580Smarcel	{L("<"),		ex_shiftl,	E_ADDR2|E_AUTOPRINT,
8096049Sfenner	    "ca1",
8193492Sphk	    "[line [,line]] <[<...] [count] [flags]",
8293492Sphk	    "shift lines left"},
831558Srgrimes/* C_EQUAL */
8497746Smarcel	{L("="),		ex_equal,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
8597746Smarcel	    "1",
8697746Smarcel	    "[line] = [flags]",
87142533Sobrien	    "display line number"},
88142533Sobrien/* C_SHIFTR */
89142533Sobrien	{L(">"),		ex_shiftr,	E_ADDR2|E_AUTOPRINT,
90142359Sobrien	    "ca1",
91133814Sru	    "[line [,line]] >[>...] [count] [flags]",
92133814Sru	    "shift lines right"},
93244320Spjd/* C_AT */
9494580Smarcel	{L("@"),		ex_at,		E_ADDR2,
9596049Sfenner	    "b",
9696049Sfenner	    "@ [buffer]",
97196528Slulf	    "execute a buffer"},
98196528Slulf/* C_APPEND */
99196528Slulf	{L("append"),	ex_append,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
10093492Sphk	    "!",
10194580Smarcel	    "[line] a[ppend][!]",
102142359Sobrien	    "append input to a line"},
1031558Srgrimes/* C_ABBR */
10493717Smarcel	{L("abbreviate"), 	ex_abbr,	0,
10593492Sphk	    "W",
106142359Sobrien	    "ab[brev] [word replace]",
1071558Srgrimes	    "specify an input abbreviation"},
108142359Sobrien/* C_ARGS */
10993492Sphk	{L("args"),	ex_args,	0,
110150818Smaxim	    "",
111150818Smaxim	    "ar[gs]",
11293717Smarcel	    "display file argument list"},
113142359Sobrien/* C_BG */
11493717Smarcel	{L("bg"),		ex_bg,		E_VIONLY,
11593717Smarcel	    "",
11693717Smarcel	    "bg",
11793492Sphk	    "put a foreground screen into the background"},
11893492Sphk/* C_CHANGE */
119142359Sobrien	{L("change"),	ex_change,	E_ADDR2|E_ADDR_ZERODEF,
120142359Sobrien	    "!ca",
121142359Sobrien	    "[line [,line]] c[hange][!] [count]",
122142359Sobrien	    "change lines to input"},
12396049Sfenner/* C_CD */
124142359Sobrien	{L("cd"),		ex_cd,		0,
125142359Sobrien	    "!f1o",
126142359Sobrien	    "cd[!] [directory]",
127142533Sobrien	    "change the current directory"},
128142533Sobrien/* C_CHDIR */
129142359Sobrien	{L("chdir"),	ex_cd,		0,
130142533Sobrien	    "!f1o",
131142533Sobrien	    "chd[ir][!] [directory]",
132142359Sobrien	    "change the current directory"},
133142533Sobrien/* C_COPY */
134142359Sobrien	{L("copy"),	ex_copy,	E_ADDR2|E_AUTOPRINT,
135142359Sobrien	    "l1",
13695039Sphk	    "[line [,line]] co[py] line [flags]",
1371558Srgrimes	    "copy lines elsewhere in the file"},
1381558Srgrimes/* C_CSCOPE */
13996049Sfenner	{L("cscope"),      ex_cscope,      0,
14096049Sfenner	    "!s",
14196049Sfenner	    "cs[cope] command [args]",
14296049Sfenner	    "create a set of tags using a cscope command"},
14396049Sfenner/*
14496049Sfenner * !!!
14596049Sfenner * Adding new commands starting with 'd' may break the delete command code
14696049Sfenner * in ex_cmd() (the ex parser).  Read through the comments there, first.
14796049Sfenner */
148150105Srwatson/* C_DELETE */
149150105Srwatson	{L("delete"),	ex_delete,	E_ADDR2|E_AUTOPRINT,
150147506Sdwhite	    "bca1",
15196049Sfenner	    "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
15296049Sfenner	    "delete lines from the file"},
15396049Sfenner/* C_DISPLAY */
15496049Sfenner	{L("display"),	ex_display,	0,
15596049Sfenner	    "w1r",
156147506Sdwhite	    "display b[uffers] | c[onnections] | s[creens] | t[ags]",
15796049Sfenner	    "display buffers, connections, screens or tags"},
15896049Sfenner/* C_EDIT */
15996049Sfenner	{L("edit"),	ex_edit,	E_NEWSCREEN,
16096049Sfenner	    "f1o",
16196049Sfenner	    "[Ee][dit][!] [+cmd] [file]",
16296049Sfenner	    "begin editing another file"},
163147506Sdwhite/* C_EX */
164147506Sdwhite	{L("ex"),		ex_edit,	E_NEWSCREEN,
16596049Sfenner	    "f1o",
166147506Sdwhite	    "[Ee]x[!] [+cmd] [file]",
167147506Sdwhite	    "begin editing another file"},
168147506Sdwhite/* C_EXUSAGE */
16996049Sfenner	{L("exusage"),	ex_usage,	0,
17096049Sfenner	    "w1o",
17196049Sfenner	    "[exu]sage [command]",
172147506Sdwhite	    "display ex command usage statement"},
17396049Sfenner/* C_FILE */
17496049Sfenner	{L("file"),	ex_file,	0,
17596049Sfenner	    "f1o",
176147506Sdwhite	    "f[ile] [name]",
17796049Sfenner	    "display (and optionally set) file name"},
178147506Sdwhite/* C_FG */
17996049Sfenner	{L("fg"),		ex_fg,		E_NEWSCREEN|E_VIONLY,
18096049Sfenner	    "f1o",
18196049Sfenner	    "[Ff]g [file]",
182244320Spjd	    "bring a backgrounded screen into the foreground"},
183244320Spjd/* C_GLOBAL */
184244320Spjd	{L("global"),	ex_global,	E_ADDR2_ALL,
185244320Spjd	    "!s",
186244320Spjd	    "[line [,line]] g[lobal][!] [;/]RE[;/] [commands]",
187244320Spjd	    "execute a global command on lines matching an RE"},
188244320Spjd/* C_HELP */
189244320Spjd	{L("help"),	ex_help,	0,
190244320Spjd	    "",
191244320Spjd	    "he[lp]",
192244320Spjd	    "display help statement"},
193244320Spjd/* C_INSERT */
194244320Spjd	{L("insert"),	ex_insert,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
195244320Spjd	    "!",
196244320Spjd	    "[line] i[nsert][!]",
197244320Spjd	    "insert input before a line"},
198244320Spjd/* C_JOIN */
199244320Spjd	{L("join"),	ex_join,	E_ADDR2|E_AUTOPRINT,
200244320Spjd	    "!ca1",
201244320Spjd	    "[line [,line]] j[oin][!] [count] [flags]",
202244320Spjd	    "join lines into a single line"},
203244320Spjd/* C_K */
204244320Spjd	{L("k"),		ex_mark,	E_ADDR1,
205244320Spjd	    "w1r",
206244320Spjd	    "[line] k key",
207244320Spjd	    "mark a line position"},
208244320Spjd/* C_LIST */
209244320Spjd	{L("list"),	ex_list,	E_ADDR2|E_CLRFLAG,
210244320Spjd	    "ca1",
211244320Spjd	    "[line [,line]] l[ist] [count] [#]",
212244320Spjd	    "display lines in an unambiguous form"},
213244320Spjd/* C_MOVE */
214244320Spjd	{L("move"),	ex_move,	E_ADDR2|E_AUTOPRINT,
215244320Spjd	    "l",
216244320Spjd	    "[line [,line]] m[ove] line",
217244320Spjd	    "move lines elsewhere in the file"},
218244320Spjd/* C_MARK */
219244320Spjd	{L("mark"),	ex_mark,	E_ADDR1,
220244320Spjd	    "w1r",
221244320Spjd	    "[line] ma[rk] key",
222244320Spjd	    "mark a line position"},
223244320Spjd/* C_MAP */
224244320Spjd	{L("map"),		ex_map,		0,
225244320Spjd	    "!W",
226244320Spjd	    "map[!] [keys replace]",
227244320Spjd	    "map input or commands to one or more keys"},
228244320Spjd/* C_MKEXRC */
229244320Spjd	{L("mkexrc"),	ex_mkexrc,	0,
230244320Spjd	    "!f1r",
231244320Spjd	    "mkexrc[!] file",
232244321Spjd	    "write a .exrc file"},
233244321Spjd/* C_NEXT */
234244321Spjd	{L("next"),	ex_next,	E_NEWSCREEN,
235244321Spjd	    "!fN",
236244321Spjd	    "[Nn][ext][!] [+cmd] [file ...]",
237244321Spjd	    "edit (and optionally specify) the next file"},
238244321Spjd/* C_NUMBER */
239244321Spjd	{L("number"),	ex_number,	E_ADDR2|E_CLRFLAG,
240244321Spjd	    "ca1",
241244321Spjd	    "[line [,line]] nu[mber] [count] [l]",
242244321Spjd	    "change display to number lines"},
24396025Smux/* C_OPEN */
24496025Smux	{L("open"),	ex_open,	E_ADDR1,
24596025Smux	    "s",
24696025Smux	    "[line] o[pen] [/RE/] [flags]",
24796025Smux	    "enter \"open\" mode (not implemented)"},
248244320Spjd/* C_PRINT */
24996025Smux	{L("print"),	ex_pr,		E_ADDR2|E_CLRFLAG,
25096025Smux	    "ca1",
25196049Sfenner	    "[line [,line]] p[rint] [count] [#l]",
25296025Smux	    "display lines"},
253244319Spjd/* C_PRESERVE */
25493717Smarcel	{L("preserve"),	ex_preserve,	0,
255244319Spjd	    "",
25696049Sfenner	    "pre[serve]",
25796049Sfenner	    "preserve an edit session for recovery"},
25896049Sfenner/* C_PREVIOUS */
259244215Spjd	{L("previous"),	ex_prev,	E_NEWSCREEN,
26096025Smux	    "!",
26196025Smux	    "[Pp]rev[ious][!]",
262244319Spjd	    "edit the previous file in the file argument list"},
26396025Smux/* C_PUT */
26496025Smux	{L("put"),		ex_put,
26596025Smux	    E_ADDR1|E_AUTOPRINT|E_ADDR_ZERO|E_ADDR_ZERODEF,
26696025Smux	    "b",
26796025Smux	    "[line] pu[t] [buffer]",
26896025Smux	    "append a cut buffer to the line"},
26996025Smux/* C_QUIT */
27096025Smux	{L("quit"),	ex_quit,	0,
27196025Smux	    "!",
27296049Sfenner	    "q[uit][!]",
273244320Spjd	    "exit ex/vi"},
274244320Spjd/* C_READ */
27596049Sfenner	{L("read"),	ex_read,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
27696049Sfenner	    "s",
27796025Smux	    "[line] r[ead] [!cmd | [file]]",
27896025Smux	    "append input from a command or file to the line"},
27996025Smux/* C_RECOVER */
28096025Smux	{L("recover"),	ex_recover,	0,
28196025Smux	    "!f1r",
28296049Sfenner	    "recover[!] file",
28396049Sfenner	    "recover a saved file"},
28496025Smux/* C_RESIZE */
28596025Smux	{L("resize"),	ex_resize,	E_VIONLY,
28696025Smux	    "c+",
28796049Sfenner	    "resize [+-]rows",
28896049Sfenner	    "grow or shrink the current screen"},
28996025Smux/* C_REWIND */
290174923Srwatson	{L("rewind"),	ex_rew,		0,
291174923Srwatson	    "!",
292174923Srwatson	    "rew[ind][!]",
293174923Srwatson	    "re-edit all the files in the file argument list"},
294174923Srwatson/*
295196528Slulf * !!!
296174923Srwatson * Adding new commands starting with 's' may break the substitute command code
297174923Srwatson * in ex_cmd() (the ex parser).  Read through the comments there, first.
298196528Slulf */
299174923Srwatson/* C_SUBSTITUTE */
300174923Srwatson	{L("s"),		ex_s,		E_ADDR2|E_ADDR_ZERO,
301174923Srwatson	    "s",
302174923Srwatson	    "[line [,line]] s [[/;]RE[/;]repl[/;] [cgr] [count] [#lp]]",
303174923Srwatson	    "substitute on lines matching an RE"},
304174923Srwatson/* C_SCRIPT */
305174923Srwatson	{L("script"),	ex_script,	E_SECURE,
306174923Srwatson	    "!f1o",
307174923Srwatson	    "sc[ript][!] [file]",
308174923Srwatson	    "run a shell in a screen"},
309174923Srwatson/* C_SET */
310174923Srwatson	{L("set"),		ex_set,		0,
311174923Srwatson	    "wN",
312174923Srwatson	    "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
313174923Srwatson	    "set options (use \":set all\" to see all options)"},
314174923Srwatson/* C_SHELL */
315174923Srwatson	{L("shell"),	ex_shell,	E_SECURE,
316174923Srwatson	    "",
317174923Srwatson	    "sh[ell]",
318174923Srwatson	    "suspend editing and run a shell"},
319174923Srwatson/* C_SOURCE */
320174923Srwatson	{L("source"),	ex_source,	0,
321174923Srwatson	    "f1r",
322174923Srwatson	    "so[urce] file",
323174923Srwatson	    "read a file of ex commands"},
324174923Srwatson/* C_STOP */
325174923Srwatson	{L("stop"),	ex_stop,	E_SECURE,
326174923Srwatson	    "!",
327244215Spjd	    "st[op][!]",
328174923Srwatson	    "suspend the edit session"},
329174923Srwatson/* C_SUSPEND */
330174923Srwatson	{L("suspend"),	ex_stop,	E_SECURE,
331174923Srwatson	    "!",
332174923Srwatson	    "su[spend][!]",
333174923Srwatson	    "suspend the edit session"},
334174923Srwatson/* C_T */
335174923Srwatson	{L("t"),		ex_copy,	E_ADDR2|E_AUTOPRINT,
336174923Srwatson	    "l1",
337174923Srwatson	    "[line [,line]] t line [flags]",
338174923Srwatson	    "copy lines elsewhere in the file"},
339174923Srwatson/* C_TAG */
340174923Srwatson	{L("tag"),		ex_tag_push,	E_NEWSCREEN,
341174923Srwatson	    "!w1o",
342174923Srwatson	    "[Tt]a[g][!] [string]",
343174923Srwatson	    "edit the file containing the tag"},
344174923Srwatson/* C_TAGNEXT */
345174923Srwatson	{L("tagnext"),	ex_tag_next,	0,
346174923Srwatson	    "!",
347174923Srwatson	    "tagn[ext][!]",
348174923Srwatson	    "move to the next tag"},
349174923Srwatson/* C_TAGPOP */
350174923Srwatson	{L("tagpop"),	ex_tag_pop,	0,
351174923Srwatson	    "!w1o",
352174923Srwatson	    "tagp[op][!] [number | file]",
353174923Srwatson	    "return to the previous group of tags"},
354174923Srwatson/* C_TAGPREV */
355174923Srwatson	{L("tagprev"),	ex_tag_prev,	0,
356174923Srwatson	    "!",
357174923Srwatson	    "tagpr[ev][!]",
358174923Srwatson	    "move to the previous tag"},
359174923Srwatson/* C_TAGTOP */
360174923Srwatson	{L("tagtop"),	ex_tag_top,	0,
361174923Srwatson	    "!",
362174923Srwatson	    "tagt[op][!]",
363174923Srwatson	    "discard all tags"},
364174923Srwatson/* C_UNDO */
365174923Srwatson	{L("undo"),	ex_undo,	E_AUTOPRINT,
366174923Srwatson	    "",
367174923Srwatson	    "u[ndo]",
368174923Srwatson	    "undo the most recent change"},
369196528Slulf/* C_UNABBREVIATE */
370196528Slulf	{L("unabbreviate"),ex_unabbr,	0,
371196528Slulf	    "w1r",
372196528Slulf	    "una[bbrev] word",
373196528Slulf	    "delete an abbreviation"},
374174923Srwatson/* C_UNMAP */
375174923Srwatson	{L("unmap"),	ex_unmap,	0,
376174923Srwatson	    "!w1r",
377174923Srwatson	    "unm[ap][!] word",
378174923Srwatson	    "delete an input or command map"},
379174923Srwatson/* C_V */
380174923Srwatson	{L("v"),		ex_v,		E_ADDR2_ALL,
381174923Srwatson	    "s",
382174923Srwatson	    "[line [,line]] v [;/]RE[;/] [commands]",
383174923Srwatson	    "execute a global command on lines NOT matching an RE"},
384174923Srwatson/* C_VERSION */
385174923Srwatson	{L("version"),	ex_version,	0,
386174923Srwatson	    "",
387174923Srwatson	    "version",
388174923Srwatson	    "display the program version information"},
389174923Srwatson/* C_VISUAL_EX */
390174923Srwatson	{L("visual"),	ex_visual,	E_ADDR1|E_ADDR_ZERODEF,
391174923Srwatson	    "2c11",
392174923Srwatson	    "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
393174923Srwatson	    "enter visual (vi) mode from ex mode"},
394174923Srwatson/* C_VISUAL_VI */
395174923Srwatson	{L("visual"),	ex_edit,	E_NEWSCREEN,
396174923Srwatson	    "f1o",
397174923Srwatson	    "[Vv]i[sual][!] [+cmd] [file]",
398174923Srwatson	    "edit another file (from vi mode only)"},
399174923Srwatson/* C_VIUSAGE */
400174923Srwatson	{L("viusage"),	ex_viusage,	0,
401174923Srwatson	    "w1o",
402174923Srwatson	    "[viu]sage [key]",
403174923Srwatson	    "display vi key usage statement"},
404174923Srwatson/* C_VSPLIT */
405174923Srwatson	{L("vsplit"),	ex_edit,	E_VIONLY,
406174923Srwatson	    "f1o",
407174923Srwatson	    "vs[plit] [+cmd] [file]",
408174923Srwatson	    "split the current screen vertically"},
409174923Srwatson/* C_WRITE */
410174923Srwatson	{L("write"),	ex_write,	E_ADDR2_ALL|E_ADDR_ZERODEF,
411174923Srwatson	    "!s",
412174923Srwatson	    "[line [,line]] w[rite][!] [ !cmd | [>>] [file]]",
413174923Srwatson	    "write the file"},
414174923Srwatson/* C_WN */
415174923Srwatson	{L("wn"),		ex_wn,		E_ADDR2_ALL|E_ADDR_ZERODEF,
416174923Srwatson	    "!s",
417174923Srwatson	    "[line [,line]] wn[!] [>>] [file]",
418174923Srwatson	    "write the file and switch to the next file"},
419174923Srwatson/* C_WQ */
420174923Srwatson	{L("wq"),		ex_wq,		E_ADDR2_ALL|E_ADDR_ZERODEF,
421174923Srwatson	    "!s",
422174923Srwatson	    "[line [,line]] wq[!] [>>] [file]",
423174923Srwatson	    "write the file and exit"},
424174923Srwatson/* C_XIT */
425174923Srwatson	{L("xit"),		ex_xit,		E_ADDR2_ALL|E_ADDR_ZERODEF,
426174923Srwatson	    "!f1o",
427174923Srwatson	    "[line [,line]] x[it][!] [file]",
428174923Srwatson	    "exit"},
429174923Srwatson/* C_YANK */
43093492Sphk	{L("yank"),	ex_yank,	E_ADDR2,
431146763Sdelphij	    "bca",
4321558Srgrimes	    "[line [,line]] ya[nk] [buffer] [count]",
433244321Spjd	    "copy lines to a cut buffer"},
43497340Smarcel/* C_Z */
43594580Smarcel	{L("z"),		ex_z,		E_ADDR1,
436174923Srwatson	    "3c01",
43796049Sfenner	    "[line] z [-|.|+|^|=] [count] [flags]",
438142533Sobrien	    "display different screens of the file"},
439174923Srwatson/* C_SUBTILDE */
440142359Sobrien	{L("~"),		ex_subtilde,	E_ADDR2|E_ADDR_ZERO,
44194580Smarcel	    "s",
442174923Srwatson	    "[line [,line]] ~ [cgr] [count] [#lp]",
4438871Srgrimes	    "replace previous RE with previous replacement string,"},
444142359Sobrien	{NULL},
44596049Sfenner};
446142359Sobrien