4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
37 #include <sys/types.h>
44 static const char *msgtab
[] =
46 "write or open on pipe failed", /* 0 */
47 "warning: expecting `w'", /* 1 */
48 "mark not lower case ascii", /* 2 */
49 "Cannot open input file", /* 3 */
50 "PWB spec problem", /* 4 */
51 "nothing to undo", /* 5 */
52 "restricted shell", /* 6 */
53 "cannot create output file", /* 7 */
54 "filesystem out of space!", /* 8 */
55 "cannot open file", /* 9 */
56 "cannot link", /* 10 */
57 "Range endpoint too large", /* 11 */
58 "unknown command", /* 12 */
59 "search string not found", /* 13 */
61 "line out of range", /* 15 */
62 "bad number", /* 16 */
64 "Illegal address count", /* 18 */
65 "incomplete global expression", /* 19 */
66 "illegal suffix", /* 20 */
67 "illegal or missing filename", /* 21 */
68 "no space after command", /* 22 */
69 "fork failed - try again", /* 23 */
70 "maximum of 64 characters in file names", /* 24 */
71 "`\\digit' out of range", /* 25 */
73 "line too long", /* 27 */
74 "illegal character in input file", /* 28 */
75 "write error", /* 29 */
76 "out of memory for append", /* 30 */
77 "temp file too big", /* 31 */
78 "I/O error on temp file", /* 32 */
79 "multiple globals not allowed", /* 33 */
80 "global too long", /* 34 */
82 "illegal or missing delimiter", /* 36 */
84 "replacement string too long", /* 38 */
85 "illegal move destination", /* 39 */
87 "no remembered search string", /* 41 */
88 "'\\( \\)' imbalance", /* 42 */
89 "Too many `\\(' s", /* 43 */
90 "more than 2 numbers given", /* 44 */
91 "'\\}' expected", /* 45 */
92 "first number exceeds second", /* 46 */
93 "incomplete substitute", /* 47 */
94 "newline unexpected", /* 48 */
95 "'[ ]' imbalance", /* 49 */
96 "regular expression overflow", /* 50 */
97 "regular expression error", /* 51 */
98 "command expected", /* 52 */
99 "a, i, or c not allowed in G", /* 53 */
100 "end of line expected", /* 54 */
101 "no remembered replacement string", /* 55 */
102 "no remembered command", /* 56 */
103 "illegal redirection", /* 57 */
104 "possible concurrent update", /* 58 */
106 "the x command has become X (upper case)", /* 60 */
107 "Warning: 'w' may destroy input file "
108 "(due to `illegal char' read earlier)",
110 "Caution: 'q' may lose data in buffer;"
111 " 'w' may destroy input file",
113 "Encryption of string failed", /* 63 */
114 "Encryption facility not available", /* 64 */
115 "Cannot encrypt temporary file", /* 65 */
116 "Enter key:", /* 66 */
117 "Illegal byte sequence", /* 67 */
118 "File does not exist", /* 68 */
119 "tempnam failed", /* 69 */
120 "Cannot open temporary file", /* 70 */
128 #include <sys/types.h>
129 #include <sys/stat.h>
130 #include <sys/statvfs.h>
136 #include <wchar.h> /* I18N */
137 #include <wctype.h> /* I18N */
138 #include <widec.h> /* I18N */
140 #define FTYPE(A) (A.st_mode)
141 #define FMODE(A) (A.st_mode)
142 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
143 #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK)
144 #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR)
145 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
146 #define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO)
147 #define ISREG(A) ((A.st_mode & S_IFMT) == S_IFREG)
149 #define PUTM() if (xcode >= 0) puts(gettext(msgtab[xcode]))
150 #define UNGETC(c) (peekc = c)
151 #define FNSIZE PATH_MAX
152 #define LBSIZE LINE_MAX
154 /* size of substitution replacement pattern buffer */
155 #define RHSIZE (LINE_MAX*2)
162 extern char *optarg
; /* Value of argument */
163 extern int optind
; /* Indicator of argument */
164 extern int __xpg4
; /* defined in xpg4.c; 0 if not xpg4-compiled program */
173 static struct Fspec fss
;
177 static char line
[70];
178 static char *linp
= line
;
182 static char savedfile
[FNSIZE
];
183 static char file
[FNSIZE
];
184 static char funny
[FNSIZE
];
185 static int funlink
= 0;
186 static char linebuf
[LBSIZE
];
187 static char *tstring
= linebuf
;
191 static char rhsbuf
[RHSIZE
];
196 typedef struct lin
*LINE
;
201 static LINE fendcore
;
204 static LINE savdol
, savdot
;
207 static char genbuf
[LBSIZE
];
209 static int numpass
; /* Number of passes thru dosub(). */
210 static int gsubf
; /* Occurrence value. LBSIZE-1=all. */
211 static int ocerr1
; /* Allows lines NOT changed by dosub() to NOT be put */
212 /* out. Retains last line changed as current line. */
213 static int ocerr2
; /* Flags if ANY line changed by substitute(). 0=nc. */
219 static void (*oldhup
)(), (*oldintr
)();
220 static void (*oldquit
)(), (*oldpipe
)();
221 static void quit(int);
222 static int vflag
= 1;
227 /* Flag for determining if file being read is encrypted */
229 static int xcode
= -1;
230 static char crbuf
[LBSIZE
];
234 static int tpermflag
;
237 static int tfile
= -1;
241 static char ibuff
[LBSIZE
];
242 static int iblock
= -1;
243 static char obuff
[LBSIZE
];
244 static int oblock
= -1;
247 static long savnames
[26], names
[26];
252 static int fflg
, shflg
;
253 static char prompt
[16] = "*";
262 static int flag28
= 0; /* Prevents write after a partial read */
263 static int save28
= 0; /* Flag whether buffer empty at start of read */
265 static char *name
= "SHELL";
266 static char *rshell
= "/usr/lib/rsh";
272 int _mbftowc(char *, wchar_t *, int (*)(), int *);
273 static int error(int code
);
274 static void tlist(struct Fspec
*);
275 static void tstd(struct Fspec
*);
276 static void gdelete(void);
277 static void delete(void);
278 static void exfile(void);
279 static void filename(int comm
);
280 static void newline(void);
281 static int gettty(void);
282 static void commands(void);
283 static void undo(void);
284 static void save(void);
285 static void strcopy(char *source
, char *dest
);
286 static int strequal(char **scan1
, char *str
);
287 static int stdtab(char *, char *);
288 static int lenchk(char *, struct Fspec
*);
289 static void clear(struct Fspec
*);
290 static int expnd(char *, char *, int *, struct Fspec
*);
291 static void tincr(int, struct Fspec
*);
292 static void targ(struct Fspec
*);
293 static int numb(void);
294 static int fspec(char *, struct Fspec
*, int);
295 static void red(char *);
296 static void newtime(void);
297 static void chktime(void);
298 static void getime(void);
299 static void mkfunny(void);
300 static int eopen(char *, int);
301 static void eclose(int f
);
302 static void globaln(int);
303 static char *getkey(const char *);
304 static int execute(int, LINE
);
305 static void error1(int);
306 static int getcopy(void);
307 static void move(int);
308 static void dosub(void);
309 static int getsub(void);
310 static int compsub(void);
311 static void substitute(int);
312 static void join(void);
313 static void global(int);
314 static void init(void);
315 static void rdelete(LINE
, LINE
);
316 static void append(int (*)(void), LINE
);
317 static int getfile(void);
318 static void putfile(void);
319 static void onpipe(int);
320 static void onhup(int);
321 static void onintr(int);
322 static void setdot(void);
323 static void setall(void);
324 static void setnoaddr(void);
325 static void nonzero(void);
326 static void setzeroasone(void);
327 static long putline(void);
328 static LINE
address(void);
329 static char *getaline(long);
330 static char *getblock(long, long);
331 static char *place(char *, char *, char *);
332 static void comple(wchar_t);
333 static void putchr(unsigned char);
334 static void putwchr(wchar_t);
335 static int getchr(void);
336 static void unixcom(void);
337 static void blkio(int, char *, ssize_t (*)());
338 static void reverse(LINE
, LINE
);
340 static wchar_t get_wchr(void);
342 static struct stat Fl
, Tf
;
344 static struct statvfs U
;
345 static int Short
= 0;
346 static mode_t oldmask
; /* No umask while writing */
348 static jmp_buf savej
;
351 int nulls
; /* Null count */
355 static int errcnt
= 0;
365 main(int argc
, char **argv
)
370 (void) setlocale(LC_ALL
, "");
371 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
372 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
374 (void) textdomain(TEXT_DOMAIN
);
376 oldquit
= signal(SIGQUIT
, SIG_IGN
);
377 oldhup
= signal(SIGHUP
, SIG_IGN
);
378 oldintr
= signal(SIGINT
, SIG_IGN
);
379 oldpipe
= signal(SIGPIPE
, onpipe
);
380 if (signal(SIGTERM
, SIG_IGN
) != SIG_IGN
)
381 signal(SIGTERM
, quit
);
384 while (--p1
>= *argv
)
388 /* if SHELL set in environment and is /usr/lib/rsh, set rflg */
389 if ((val
= getenv(name
)) != NULL
)
390 if (strcmp(val
, rshell
) == 0)
394 home
= getenv("HOME");
396 while ((c
= getopt(argc
, argv
, "sp:qxC")) != EOF
) {
404 strncpy(prompt
, optarg
, sizeof (prompt
)-1);
409 signal(SIGQUIT
, SIG_DFL
);
424 (void) fprintf(stderr
, gettext(
425 "Usage: ed [- | -s] [-p string] [-x] [-C] [file]\n"
426 " red [- | -s] [-p string] [-x] [-C] [file]\n"));
430 if (argv
[optind
] && strcmp(argv
[optind
], "-") == 0 &&
431 strcmp(argv
[optind
-1], "--") != 0) {
438 argc
= argc
- optind
;
439 argv
= &argv
[optind
];
445 if ((kflag
= run_setkey(&perm
[0], getkey(msgtab
[66])))
447 puts(gettext(msgtab
[64]));
457 if (strlen(p1
) >= (size_t)FNSIZE
) {
458 puts(gettext("file name too long"));
464 while (*p2
++ = *p1
++);
467 } else /* editing with no file so set savtime to 0 */
470 if ((tfname
= tempnam("", "ea")) == NULL
) {
471 puts(gettext(msgtab
[69]));
475 fendcore
= (LINE
)sbrk(0);
477 if (oldintr
!= SIG_IGN
)
478 signal(SIGINT
, onintr
);
479 if (oldhup
!= SIG_IGN
)
480 signal(SIGHUP
, onhup
);
502 if (shflg
&& globp
== 0)
503 write(1, gettext(prompt
), strlen(gettext(prompt
)));
506 if ((c
= getchr()) == ',') {
510 /* XPG4 - it was an error if the second address was */
511 /* input and the first address was ommitted */
512 /* Parse second address */
513 if ((a1
= address()) != 0) {
519 } else if (c
== ';') {
523 /* XPG4 - it was an error if the second address was */
524 /* input and the first address was ommitted */
525 /* Parse second address */
526 if ((a1
= address()) != 0) {
536 if ((a1
= address()) == 0) {
541 if ((c
= getchr()) == ';') {
554 if (!globflg
) save();
555 append(gettty
, addr2
);
563 append(gettty
, addr1
-1);
565 /* XPG4 - If no new lines are inserted, then the current */
566 /* line becomes the line after the lines deleted. */
568 if (((linebuf
[0] != '.') || (dot
== (addr1
-1))) &&
583 if (vflag
&& fchange
) {
596 if (!ncflg
) /* there is a filename */
634 if (!globflg
) save();
635 append(gettty
, addr2
-1);
648 if (!globflg
) save();
653 if ((c
= getchr()) < 'a' || c
> 'z')
658 names
[c
-'a'] = addr2
->cur
& ~01;
691 puts(getaline((a1
++)->cur
));
711 save28
= (dol
!= fendcore
);
712 if (crflag
== 2 || crflag
== -2)
713 crflag
= -1; /* restore crflag for next file */
715 if ((io
= eopen(file
, O_RDONLY
)) < 0) {
717 /* if first entering editor and file does not exist */
718 /* set saved access time to 0 */
722 if (c
== 'e' && vflag
== 0)
725 if (errno
== ENOENT
) {
731 /* get last mod time of file */
732 /* eflg - entered editor with ed or e */
743 if (!globflg
&& (c
== 'r')) save();
744 append(getfile
, addr2
);
753 if (!globflg
) save();
754 substitute(globp
!= 0);
787 /* on NULL-RE condition do not generate error */
789 if ((linebuf
[0] != '.') && (zero
!= dol
) &&
790 (addr1
<= zero
|| addr2
> dol
))
794 io
= eopen(file
, O_WRONLY
);
795 n
= 1; /* set n so newtime will not execute */
799 if (stat(file
, &Fl
) < 0) {
800 if ((io
= creat(file
, S_IRUSR
|S_IWUSR
|S_IRGRP
801 |S_IWGRP
|S_IROTH
|S_IWOTH
)) < 0)
811 * Must determine if file is
819 * Determine if there are enough free blocks on system
821 if (!Short
&& statvfs(file
, &U
) == 0 &&
822 U
.f_bfree
< ((Tf
.st_size
/U
.f_frsize
) + 100)) {
828 p1
= savedfile
; /* The current filename */
831 if (c
== 'w' && Fl
.st_nlink
== 1 && ISREG(lFl
)) {
832 if (close(open(file
, O_WRONLY
)) < 0)
838 * If funlink equals one it means that
839 * funny points to a valid file which must
840 * be unlinked when interrupted.
844 if ((io
= creat(funny
, FMODE(Fl
))) >= 0) {
845 chown(funny
, Fl
.st_uid
, Fl
.st_gid
);
846 chmod(funny
, FMODE(Fl
));
850 if (rename(funny
, file
))
853 /* if filenames are the same */
856 /* check if entire buffer was written */
858 fchange
= (((addr1
== zero
) || (addr1
== (zero
+ 1))) &&
859 (addr2
== dol
)) ? 0 : 1;
860 if (fchange
== 1 && m
!= 0) fchange
= fsave
;
864 n
= 1; /* set n so newtime will not execute */
866 (c
== 'w') ? O_WRONLY
|O_CREAT
|O_TRUNC
867 : O_WRONLY
|O_CREAT
|O_APPEND
, S_IRUSR
|S_IWUSR
868 |S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
)) < 0)
875 fchange
= (((addr1
== zero
) || (addr1
== (zero
+ 1))) &&
876 (addr2
== dol
)) ? 0 : 1;
877 /* Leave fchange alone if partial write was to another file */
878 if (fchange
== 1 && m
!= 0) fchange
= fsave
;
884 * C is same as X, but always assume input files are
896 (void) crypt_close(perm
);
898 if ((kflag
= run_setkey(&perm
[0], getkey(msgtab
[66])))
912 count
= (addr2
-zero
)&077777;
945 int n
, relerr
, retval
;
951 if ('0' <= c
&& c
<= '9') {
956 } while ((c
= getchr()) >= '0' && c
<= '9');
1018 if ((c
= getchr()) < 'a' || c
> 'z')
1020 for (a1
= zero
; a1
<= dol
; a1
++)
1021 if (names
[c
-'a'] == (a1
->cur
& ~01))
1031 /* on NULL-RE condition do not generate error */
1033 if ((linebuf
[0] != '.') && (a1
< zero
|| a1
> dol
))
1046 addr1
= addr2
= dot
;
1073 /* on NULL-RE condition do not generate error */
1075 if ((linebuf
[0] != '.') && (addr1
<= zero
|| addr2
> dol
))
1082 /* for the c and i commands 0 equal to 1 address */
1083 if (addr1
== zero
) {
1086 if (addr2
== zero
) {
1097 if ((c
= getchr()) == '\n')
1099 if (c
== 'p' || c
== 'l' || c
== 'n') {
1101 if (c
== 'l') listf
++;
1102 if (c
== 'n') listn
++;
1103 if ((c
= getchr()) == '\n')
1118 if (c
== '\n' || c
== EOF
) {
1120 if (*p1
== 0 && comm
!= 'f')
1122 /* ncflg set means do not get mod time of file */
1123 /* since no filename followed f */
1127 while (*p2
++ = *p1
++);
1133 while ((c
= getchr()) == ' ');
1135 ++Xqt
, c
= getchr();
1143 if (c
== EOF
|| (c
== ' ' && !Xqt
))
1145 } while ((c
= getchr()) != '\n');
1154 if (savedfile
[0] == 0 || comm
== 'e' || comm
== 'f') {
1157 while (*p1
++ = *p2
++);
1187 puts(gettext(" nulls replaced by '\\0'"));
1197 signal(SIGINT
, onintr
);
1201 if (funlink
) unlink(funny
); /* remove tmp file */
1202 /* if interrupted a read, only part of file may be in buffer */
1204 sprintf(tstring
, "\007read may be incomplete - beware!\007");
1205 puts(gettext(tstring
));
1214 signal(SIGINT
, SIG_IGN
);
1215 signal(SIGHUP
, SIG_IGN
);
1217 * if there are lines in file and file was not written
1218 * since last update, save in ed.hup, or $HOME/ed.hup
1220 if (dol
> zero
&& fchange
== 1) {
1223 io
= creat("ed.hup",
1224 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
1225 if (io
< 0 && home
) {
1228 fn
= (char *)calloc(strlen(home
) + 8, sizeof (char));
1231 strcat(fn
, "/ed.hup");
1232 io
= creat(fn
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
1250 if (code
== 28 && save28
== 0) {
1264 #ifdef NULLS /* Not really nulls, but close enough */
1265 /* This is a bug because of buffering */
1266 if (code
== 28) /* illegal char. */
1269 /* Cant open file or file does not exist */
1270 if ((code
== 3) || (code
== 68)) {
1284 lseek(0, (long)0, 2);
1290 while ((c
= getchr()) != '\n' && c
!= EOF
);
1299 return (0); /* Non-fatal error. */
1308 if (lastc
= peekc
) {
1313 if ((lastc
= (unsigned char)*globp
++) != 0)
1318 if (read(0, &c
, 1) <= 0)
1319 return (lastc
= EOF
);
1320 lastc
= (unsigned char)c
;
1333 while ((c
= getchr()) != '\n') {
1343 if (p
> &linebuf
[LBSIZE
-1])
1347 if (linebuf
[0] == '.' && linebuf
[1] == 0)
1351 * POSIX.2/XPG4 explicitly says no to this:
1353 * in Solaris backslash followed by special character "." is
1354 * special character "." itself; (so terminating input mode can be
1357 * however, POSIX2/XPG4 says, input mode is terminated by
1358 * entering line consisting of only 2 characters: ".\n"
1360 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1378 if ((ninbuf
= read(io
, genbuf
, LBSIZE
)-1) < 0)
1380 puts(gettext("'\\n' appended"));
1386 if (isencrypt(genbuf
, ninbuf
+ 1))
1393 if (run_crypt(count
, genbuf
, ninbuf
+1, perm
) == -1)
1396 if (lp
>= &linebuf
[LBSIZE
]) {
1400 if ((*lp
++ = c
= *fp
++) == 0) {
1411 } while (c
!= '\n');
1414 if (fss
.Ffill
&& fss
.Flim
&& lenchk(linebuf
, &fss
) < 0) {
1415 write(1, gettext("line too long: lno = "),
1416 strlen(gettext("line too long: lno = ")));
1418 count
= (++dot
-zero
)&077777;
1439 lp
= getaline(a1
++->cur
);
1440 if (fss
.Ffill
&& fss
.Flim
&& lenchk(linebuf
, &fss
) < 0) {
1441 write(1, gettext("line too long: lno = "),
1442 strlen(gettext("line too long: lno = ")));
1444 count
= (a1
-zero
-1)&077777;
1453 if (run_crypt(count
-n
, genbuf
, n
, perm
) == -1)
1455 if (write(io
, genbuf
, n
) != n
)
1460 if (dol
->cur
== 0L)break; /* Allow write of null file */
1462 if ((*fp
++ = *lp
++) == 0) {
1467 } while (a1
<= addr2
);
1470 if (run_crypt(count
-n
, genbuf
, n
, perm
) == -1)
1472 if (write(io
, genbuf
, n
) != n
)
1477 append(int (*f
)(void), LINE a
)
1484 while ((*f
)() == 0) {
1485 if (dol
>= endcore
) {
1486 if ((int)sbrk(512 * sizeof (struct lin
)) == -1) {
1498 (--a2
)->cur
= (--a1
)->cur
;
1509 static char savcmd
[LBSIZE
]; /* last command */
1510 char curcmd
[LBSIZE
]; /* current command */
1511 char *psavcmd
, *pcurcmd
, *psavedfile
;
1512 int endflg
= 1, shflg
= 0;
1520 /* read command til end */
1523 * a '!' found in beginning of command is replaced with the saved
1524 * command. a '%' found in command is replaced with the current
1534 while (*pcurcmd
++ = *psavcmd
++);
1539 UNGETC(c
); /* put c back */
1540 while (endflg
== 1) {
1541 while ((c
= get_wchr()) != '\n' && c
!= '%' && c
!= '\\') {
1542 if ((len
= wctomb(pcurcmd
, c
)) <= 0) {
1543 *pcurcmd
= (unsigned char)c
;
1550 if (savedfile
[0] == 0)
1553 psavedfile
= savedfile
;
1554 while (pcurcmd
< curcmd
+ LBSIZE
&&
1555 (*pcurcmd
++ = *psavedfile
++));
1559 } else if (c
== '\\') {
1563 if ((len
= wctomb(pcurcmd
, c
)) <= 0) {
1564 *pcurcmd
= (unsigned char)c
;
1570 /* end of command hit */
1577 strcpy(savcmd
, curcmd
);
1579 if ((pid
= fork()) == 0) {
1580 signal(SIGHUP
, oldhup
);
1581 signal(SIGQUIT
, oldquit
);
1583 execlp(_PATH_BSHELL
, "sh", "-c", curcmd
, (char *)0);
1586 savint
= signal(SIGINT
, SIG_IGN
);
1587 while ((rpid
= wait(&retcode
)) != pid
&& rpid
!= (pid_t
)-1);
1588 signal(SIGINT
, savint
);
1589 if (vflag
) puts("!");
1595 if (vflag
&& fchange
) {
1603 * For case where user reads in BOTH a good
1622 if (!globflg
) save();
1623 rdelete(addr1
, addr2
);
1627 rdelete(LINE ad1
, LINE ad2
)
1636 (a1
++)->cur
= (a2
++)->cur
;
1651 for (a1
= zero
+1; (a1
->cur
&01) == 0; a1
++)
1654 for (a2
= a1
+ 1; a2
<= a3
; ) {
1659 (a1
++)->cur
= (a2
++)->cur
;
1674 bp
= getblock(tl
, READ
);
1677 while (*lp
++ = *bp
++)
1679 bp
= getblock(tl
+= 0400, READ
);
1695 bp
= getblock(tl
, WRITE
);
1698 while (*bp
= *lp
++) {
1699 if (*bp
++ == '\n') {
1705 bp
= getblock(tl
+= 0400, WRITE
);
1710 tline
+= (((lp
-linebuf
)+03)>>1)&077776;
1715 getblock(long atl
, long iof
)
1722 off
= (atl
<<1)&0774;
1724 /* bno is limited to 16 bits */
1730 if (bno
== iblock
) {
1739 if (run_crypt(0L, ibuff
, 512, tperm
) == -1)
1741 blkio(iblock
, ibuff
, write
);
1745 blkio(bno
, ibuff
, read
);
1747 if (run_crypt(0L, ibuff
, 512, tperm
) == -1)
1758 if (run_crypt(0L, crbuf
, 512, tperm
) == -1)
1760 blkio(oblock
, crbuf
, write
);
1762 blkio(oblock
, obuff
, write
);
1769 blkio(int b
, char *buf
, ssize_t (*iofcn
)())
1771 lseek(tfile
, (long)b
<<9, 0);
1772 if ((*iofcn
)(tfile
, buf
, 512) != 512) {
1774 (void) error(32); /* Bypass this if writing null file */
1785 (void) close(tfile
);
1786 (void) unlink(tfname
);
1790 for (markp
= names
; markp
< &names
[26]; )
1800 if ((tfile
= open(tfname
, O_CREAT
|O_EXCL
|O_RDWR
,
1801 S_IRUSR
|S_IWUSR
)) < 0) {
1802 puts(gettext(msgtab
[70]));
1810 (void) crypt_close(tperm
);
1812 if (makekey(tperm
)) {
1814 puts(gettext(msgtab
[65]));
1817 brk((char *)fendcore
);
1818 dot
= zero
= dol
= savdot
= savdol
= fendcore
;
1819 flag28
= save28
= 0;
1820 endcore
= fendcore
- sizeof (struct lin
);
1828 char multic
[MB_LEN_MAX
];
1831 char globuf
[LBSIZE
];
1839 if ((n
= _mbftowc(multic
, &l
, getchr
, &peekc
)) <= 0)
1846 while ((c
= get_wchr()) != '\n') {
1850 /* '\\' has special meaning only if preceding a '\n' */
1856 if ((gp
+ (unsigned int)MB_CUR_MAX
) >= &globuf
[LBSIZE
-1])
1858 if ((len
= wctomb(gp
, c
)) <= 0) {
1859 *gp
= (unsigned char)c
;
1868 for (a1
= zero
; a1
<= dol
; a1
++) {
1870 if (a1
>= addr1
&& a1
<= addr2
&& execute(0, a1
) == k
)
1874 * Special case: g/.../d (avoid n^2 algorithm)
1876 if (globuf
[0] == 'd' && globuf
[1] == '\n' && globuf
[2] == '\0') {
1880 for (a1
= zero
; a1
<= dol
; a1
++) {
1902 for (a1
= addr1
; a1
<= addr2
; a1
++) {
1903 lp
= getaline(a1
->cur
);
1905 if (gp
++ > &genbuf
[LBSIZE
-1])
1910 while (*lp
++ = *gp
++);
1911 addr1
->cur
= putline();
1913 rdelete(addr1
+1, addr2
);
1918 substitute(int inglob
)
1923 int ingsav
; /* For saving arg. */
1928 for (a1
= addr1
; a1
<= addr2
; a1
++) {
1929 if (execute(0, a1
) == 0)
1937 if (execute(1, (LINE
)0) == 0)
1942 if (ocerr1
== 0)continue; /* Don't put out-not changed. */
1943 subnewa
= putline();
1946 for (markp
= names
; markp
< &names
[26]; markp
++)
1947 if (*markp
== a1
->cur
)
1957 return; /* Was in global-no error msg allowed. */
1959 (void) error(35); /* Not in global, but not found. */
1961 (void) error(35); /* RE found, but occurrence match failed. */
1970 char multic
[MB_LEN_MAX
];
1972 static char remem
[RHSIZE
];
1973 static int remflg
= -1;
1976 if ((n
= _mbftowc(multic
, &seof
, getchr
, &peekc
)) <= 0)
1978 if (seof
== '\n' || seof
== ' ')
1984 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
1988 if (p
>= &rhsbuf
[RHSIZE
])
1990 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
1992 } else if (cl
== '\n') {
1997 if (!(globp
&& globp
[0])) {
2002 } else if (cl
== seof
)
2004 if (p
+ n
> &rhsbuf
[RHSIZE
])
2006 (void) strncpy(p
, multic
, n
);
2010 if (rhsbuf
[0] == '%' && rhsbuf
[1] == 0)
2012 * If there isn't a remembered string, it is an error;
2013 * otherwise the right hand side is the previous right
2020 strcpy(rhsbuf
, remem
);
2022 strcpy(remem
, rhsbuf
);
2026 peekc
= getchr(); /* Gets char after third delimiter. */
2028 c
= LBSIZE
; peekc
= 0;
2030 if (peekc
>= '1' && peekc
<= '9') {
2032 peekc
= 0; /* Allows getchr() to get next char. */
2035 if (i
< '0' || i
> '9')
2039 (void) error(20); /* "Illegal suffix" */
2041 peekc
= i
; /* Effectively an unget. */
2047 * Returns occurrence value. 0 & 1 both do first occurrence
2048 * only: c = 0 if ordinary substitute; c = 1
2049 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2059 if ((p2
= linebp
) == 0)
2061 while (*p1
++ = *p2
++);
2072 if (gsubf
> 0 && gsubf
< LBSIZE
) {
2074 if (gsubf
!= numpass
)
2086 sp
= place(sp
, loc1
, loc2
);
2088 } else if (c
== '\\') {
2090 if (c
>= '1' && c
< nbra
+ '1') {
2091 sp
= place(sp
, braslist
[c
-'1'], braelist
[c
-'1']);
2096 if (sp
>= &genbuf
[LBSIZE
])
2100 loc2
= sp
- genbuf
+ linebuf
;
2101 while (*sp
++ = *lp
++)
2102 if (sp
>= &genbuf
[LBSIZE
])
2106 while (*lp
++ = *sp
++);
2110 place(char *sp
, char *l1
, char *l2
)
2115 if (sp
>= &genbuf
[LBSIZE
])
2122 comple(wchar_t seof
)
2128 char multic
[MB_LEN_MAX
];
2131 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2133 if (n
== 0 || c
== '\n') {
2139 if (c
== seof
&& !cclass
)
2141 if (cclass
&& c
== ']') {
2143 if (cp
> &genbuf
[LBSIZE
-1])
2148 if (c
== '[' && !cclass
) {
2150 if (cp
> &genbuf
[LBSIZE
-1])
2153 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2155 if (n
== 0 || c
== '\n')
2158 if (c
== '\\' && !cclass
) {
2159 if (cp
> &genbuf
[LBSIZE
-1])
2162 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2164 if (n
== 0 || c
== '\n')
2167 if (cp
+ n
> &genbuf
[LBSIZE
-1])
2169 (void) strncpy(cp
, multic
, n
);
2173 if (n
!= 0 && c
== '\n')
2175 if (n
== 0 || c
== '\n')
2179 * NULL RE: do not compile a null regular expression; but process
2180 * input with last regular expression encountered
2183 if (genbuf
[0] != '\0') {
2186 expbuf
= compile(genbuf
, (char *)0, (char *)0);
2199 if ((adt
= address()) == 0)
2202 if (!globflg
) save();
2205 append(getcopy
, ad1
++);
2209 for (ad1
= addr1
; ad1
<= ad2
; )
2210 (ad1
++)->cur
&= ~01;
2215 dot
= adt
+ (ad2
-ad1
);
2221 } else if (adt
>= ad2
) {
2232 reverse(LINE a1
, LINE a2
)
2251 (void) getaline((addr1
++)->cur
);
2257 * Handles error code returned from comple() routine: regular expression
2258 * compile and match routines
2270 execute(int gf
, LINE addr
)
2275 for (c
= 0; c
< nbra
; c
++) {
2284 p1
= getaline(addr
->cur
);
2287 return (step(p1
, expbuf
));
2296 r
= (int)(count
%10);
2305 puts(const char *sp
)
2310 if (fss
.Ffill
&& (listf
== 0)) {
2312 /* deliberate attempt to remove constness of sp because */
2313 /* it needs to be expanded */
2315 if ((i
= expnd((char *)sp
, funny
, &sz
, &fss
)) == -1) {
2316 write(1, funny
, fss
.Flim
& 0377);
2318 write(1, gettext("too long"),
2319 strlen(gettext("too long")));
2322 write(1, funny
, sz
);
2325 write(1, gettext("tab count\n"),
2326 strlen(gettext("tab count\n")));
2331 n
= mbtowc(&c
, sp
, MB_LEN_MAX
);
2336 putchr((unsigned char)*sp
++);
2342 putchr((unsigned char)*sp
++);
2347 putchr('$'); /* end of line is marked with a $ */
2350 /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2351 /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2352 /* statement in putchr() */
2354 putchr('$'); /* end of line is marked with a $ */
2366 char buf
[MB_LEN_MAX
], *p
;
2376 if ((len
= wctomb(p
, c
)) <= 0) {
2377 *p
= (unsigned char)c
;
2381 if (col
+ 4 >= 72) {
2386 (void) sprintf(lp
, "\\%03o",
2387 *(unsigned char *)p
++);
2392 if ((len
= wcwidth(c
)) <= 0)
2394 if (col
+ len
>= 72) {
2400 if ((len
= wctomb(lp
, c
)) <= 0) {
2401 *lp
= (unsigned char)c
;
2407 if ((len
= wctomb(lp
, c
)) <= 0) {
2408 *lp
= (unsigned char)c
;
2413 if (c
== '\n' || lp
>= &line
[64]) {
2416 write(1, line
, len
);
2424 putchr(unsigned char c
)
2430 if (listf
&& c
!= '\n') {
2468 /* if $ characters are within the line preceed with \ */
2480 (void) sprintf(lp
, "\\%03o", c
);
2488 * long lines are folded w/ pt of folding indicated by writing
2489 * backslash/newline character
2492 if (col
+ 1 >= 72) {
2499 if (c
== '\n' || lp
>= &line
[64]) {
2502 (void) write(1, line
, len
);
2510 getkey(const char *prompt
)
2515 static char key
[KSIZE
+1];
2519 sig
= signal(SIGINT
, SIG_IGN
);
2520 ioctl(0, TCGETA
, &b
);
2522 b
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
2523 ioctl(0, TCSETAW
, &b
);
2524 write(1, gettext(prompt
), strlen(gettext(prompt
)));
2526 while (((c
= getchr()) != EOF
) && (c
!= '\n')) {
2527 if (p
< &key
[KSIZE
])
2533 ioctl(0, TCSETAW
, &b
);
2534 signal(SIGINT
, sig
);
2548 char globuf
[LBSIZE
];
2549 char multic
[MB_LEN_MAX
];
2559 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
2565 for (a1
= zero
; a1
<= dol
; a1
++) {
2567 if (a1
>= addr1
&& a1
<= addr2
&& execute(0, a1
) == k
)
2573 * preserve the p, l, and n suffix commands of the G and V
2574 * commands during the interactive section and restore
2575 * on completion of the G and V command.
2583 for (a1
= zero
; a1
<= dol
; a1
++) {
2587 puts(getaline(a1
->cur
));
2588 if ((c
= get_wchr()) == EOF
)
2590 if (c
== 'a' || c
== 'i' || c
== 'c')
2598 if ((len
= wctomb(gp
, c
)) <= 0) {
2599 *gp
= (unsigned char)c
;
2603 while ((c
= get_wchr()) != '\n') {
2605 /* '\\' has special meaning only if preceding a '\n' */
2611 if ((gp
+ (unsigned int)MB_CUR_MAX
) >=
2615 if ((len
= wctomb(gp
, c
)) <= 0) {
2616 *gp
= (unsigned char)c
;
2624 } else if ((c
= get_wchr()) != '\n')
2644 eopen(char *string
, int rw
)
2646 #define w_or_r(a, b) (rw ? a : b)
2650 int chcount
; /* # of char read. */
2652 if (rflg
) { /* restricted shell */
2659 if ((io
= open(string
, rw
)) >= 0) {
2661 chcount
= read(io
, crbuf
, LBSIZE
);
2663 if (isencrypt(crbuf
, chcount
))
2669 if (run_crypt(0L, crbuf
, chcount
, perm
) == -1)
2671 if (fspec(crbuf
, &fss
, 0) < 0) {
2683 xerr
: (void) error(0);
2684 if ((i
= fork()) == 0) {
2685 signal(SIGHUP
, oldhup
);
2686 signal(SIGQUIT
, oldquit
);
2687 signal(SIGPIPE
, oldpipe
);
2688 signal(SIGINT
, (void (*)()) 0);
2689 close(w_or_r(pf
[1], pf
[0]));
2690 close(w_or_r(0, 1));
2691 dup(w_or_r(pf
[0], pf
[1]));
2692 close(w_or_r(pf
[0], pf
[1]));
2693 execlp(_PATH_BSHELL
, "sh", "-c", string
, (char *)0);
2698 close(w_or_r(pf
[0], pf
[1]));
2699 return (w_or_r(pf
[1], pf
[0]));
2708 Xqt
= 0, wait((int *)0);
2720 * Go to end of file name
2724 while (*--p
== '/') /* delete trailing slashes */
2727 * go back to beginning of file
2731 * Copy file name to funny setting p2 at
2735 if (*p
++ == '/') p2
= p1
;
2737 * Set p1 to point to basename of tfname.
2739 p1
= strrchr(tfname
, '/');
2740 if (strlen(tfname
) > (size_t)6)
2741 p1
= &tfname
[strlen(tfname
)-6];
2743 *p2
= '\007'; /* add unprintable char for funny a unique name */
2745 * Copy tfname to file.
2747 while (*++p2
= *p1
++);
2752 getime(void) /* get modified time of file and save */
2754 if (stat(file
, &Fl
) < 0)
2757 savtime
= Fl
.st_mtime
;
2762 chktime(void) /* check saved mod time against current mod time */
2764 if (savtime
!= 0 && Fl
.st_mtime
!= 0) {
2765 if (savtime
!= Fl
.st_mtime
)
2772 newtime(void) /* get new mod time and save */
2775 savtime
= Fl
.st_mtime
;
2780 red(char *op
) /* restricted - check for '/' in name */
2781 /* and delete trailing '/' */
2787 if (*p
++ == '/'&& rflg
) {
2791 /* delete trailing '/' */
2801 * Searches thru beginning of file looking for a string of the form
2804 * where "values" are
2807 * s<num> sets the Flim to <num>
2808 * t??? sets tab stop stuff
2815 fspec(char line
[], struct Fspec
*f
, int up
)
2823 havespec
= fsprtn
= 0;
2824 for (fsp
= line
; *fsp
&& *fsp
!= '\n'; fsp
+= len
) {
2825 if ((len
= mblen(fsp
, MB_CUR_MAX
)) <= 0)
2829 case '<': if (havespec
)
2831 if (*(fsp
+1) == ':') {
2834 if (!ioctl(1, TCGETA
, &arg
) &&
2835 ((arg
.c_oflag
&TAB3
) == TAB3
))
2843 case 's': if (havespec
&& (n
= numb()) >= 0)
2847 case 't': if (havespec
) targ(f
);
2852 case 'm': if (havespec
) n
= numb();
2856 case ':': if (!havespec
) continue;
2857 if (*(fsp
+1) != '>') fsprtn
= -1;
2860 default: if (!havespec
) continue;
2874 while (*++fsp
>= '0' && *fsp
<= '9')
2875 n
= 10*n
+ *fsp
-'0';
2882 targ(struct Fspec
*f
)
2885 if (*++fsp
== '-') {
2886 if (*(fsp
+ 1) >= '0' && *(fsp
+1) <= '9') tincr(numb(), f
);
2890 if (*fsp
>= '0' && *fsp
<= '9') {
2900 tincr(int n
, struct Fspec
*f
)
2905 for (i
= 0; i
< 20; i
++)
2906 f
->Ftabs
[i
] = l
+= n
;
2912 tstd(struct Fspec
*f
)
2917 if (*(fsp
+1) >= '0' && *(fsp
+1) <= '9') {
2920 } else std
[1] = '\0';
2921 fsprtn
= stdtab(std
, f
->Ftabs
);
2926 tlist(struct Fspec
*f
)
2934 if ((n
= numb()) <= last
|| i
>= 20) {
2938 f
->Ftabs
[i
++] = last
= n
;
2939 } while (*++fsp
== ',');
2947 expnd(char line
[], char buf
[], int *sz
, struct Fspec
*f
)
2957 while (*++l
&& *l
!= '\n' && b
< 511) {
2959 while (*t
&& b
>= *t
) t
++;
2960 if (*t
== 0) fsprtn
= -2;
2961 do buf
[b
-1] = ' '; while (++b
< *t
);
2962 } else buf
[b
++ - 1] = *l
;
2967 if (*l
!= '\0' && *l
!= '\n') {
2972 if (f
->Flim
&& (b
-1 > (int)f
->Flim
))
2979 clear(struct Fspec
*f
)
2981 f
->Ftabs
[0] = f
->Fdel
= f
->Fmov
= f
->Ffill
= 0;
2987 lenchk(char line
[], struct Fspec
*f
)
2996 while (*++l
&& *l
!= '\n' && b
< 511) {
2998 while (*t
&& b
>= *t
) t
++;
3003 if ((*l
!= '\0' && *l
!= '\n') || (f
->Flim
&& (b
-1 > (int)f
->Flim
)))
3011 * stdtabs: standard tabs table
3012 * format: option code letter(s), null, tabs, null
3015 static char stdtabs
[] = {
3016 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */
3017 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */
3018 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */
3019 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */
3020 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3022 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */
3023 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3025 's', 0, 1, 10, 55, 0, /* SNOBOL */
3026 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
3031 * stdtab: return tab list for any "canned" tab option.
3032 * entry: option points to null-terminated option string
3033 * tabvect points to vector to be filled in
3034 * exit: return (0) if legal, tabvect filled, ending with zero
3035 * return (-1) if unknown option
3040 stdtab(char option
[], char tabvect
[NTABS
])
3046 if (strequal(&scan
, option
)) {
3047 strcopy(scan
, tabvect
);
3050 while (*scan
++); /* skip over tab specs */
3053 /* later: look up code in /etc/something */
3054 return (tabvect
[0] ? 0 : -1);
3059 * strequal: checks strings for equality
3060 * entry: scan1 points to scan pointer, str points to string
3061 * exit: return (1) if equal, return (0) if not
3062 * *scan1 is advanced to next nonzero byte after null
3067 strequal(char **scan1
, char *str
)
3071 while ((c
= *scan
++) == *str
&& c
) str
++;
3073 if (c
== 0 && *str
== 0)
3082 /* strcopy: copy source to destination */
3086 strcopy(char *source
, char *dest
)
3088 while (*dest
++ = *source
++);
3092 /* This is called before a buffer modifying command so that the */
3093 /* current array of line ptrs is saved in sav and dot and dol are saved */
3103 for (j
= 0; j
<= 25; j
++)
3104 savnames
[j
] = names
[j
];
3106 for (i
= zero
+ 1; i
<= dol
; i
++)
3112 /* The undo command calls this to restore the previous ptr array sav */
3113 /* and swap with cur - dot and dol are swapped also. This allows user to */
3121 LINE i
, tmpdot
, tmpdol
;
3123 tmpdot
= dot
; dot
= savdot
; savdot
= tmpdot
;
3124 tmpdol
= dol
; dol
= savdol
; savdol
= tmpdol
;
3125 /* swap arrays using the greater of dol or savdol as upper limit */
3126 for (i
= zero
+ 1; i
<= ((dol
> savdol
) ? dol
: savdol
); i
++) {
3132 * If the current text lines are swapped with the
3133 * text lines in the save buffer, then swap the current
3134 * marks with those in the save area.
3137 for (j
= 0; j
<= 25; j
++) {
3139 names
[j
] = savnames
[j
];
3148 char multi
[MB_LEN_MAX
];
3150 if (_mbftowc(multi
, &wc
, getchr
, &peekc
) <= 0)