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
);
385 while (--p1
>= *argv
)
389 /* if SHELL set in environment and is /usr/lib/rsh, set rflg */
390 if ((val
= getenv(name
)) != NULL
)
391 if (strcmp(val
, rshell
) == 0)
395 home
= getenv("HOME");
397 while ((c
= getopt(argc
, argv
, "sp:qxC")) != EOF
) {
405 strncpy(prompt
, optarg
, sizeof (prompt
)-1);
410 signal(SIGQUIT
, SIG_DFL
);
425 (void) fprintf(stderr
, gettext(
426 "Usage: ed [- | -s] [-p string] [-x] [-C] [file]\n"
427 " red [- | -s] [-p string] [-x] [-C] [file]\n"));
431 if (argv
[optind
] && strcmp(argv
[optind
], "-") == 0 &&
432 strcmp(argv
[optind
-1], "--") != 0) {
439 argc
= argc
- optind
;
440 argv
= &argv
[optind
];
446 kflag
= run_setkey(&perm
[0], getkey(msgtab
[66]));
448 puts(gettext(msgtab
[64]));
458 if (strlen(p1
) >= (size_t)FNSIZE
) {
459 puts(gettext("file name too long"));
465 while (*p2
++ = *p1
++)
469 } else /* editing with no file so set savtime to 0 */
472 if ((tfname
= tempnam("", "ea")) == NULL
) {
473 puts(gettext(msgtab
[69]));
477 fendcore
= (LINE
)sbrk(0);
479 if (oldintr
!= SIG_IGN
)
480 signal(SIGINT
, onintr
);
481 if (oldhup
!= SIG_IGN
)
482 signal(SIGHUP
, onhup
);
504 if (shflg
&& globp
== 0)
505 write(1, gettext(prompt
), strlen(gettext(prompt
)));
508 if ((c
= getchr()) == ',') {
512 /* XPG4 - it was an error if the second address was */
513 /* input and the first address was ommitted */
514 /* Parse second address */
515 if ((a1
= address()) != 0) {
521 } else if (c
== ';') {
525 /* XPG4 - it was an error if the second address was */
526 /* input and the first address was ommitted */
527 /* Parse second address */
528 if ((a1
= address()) != 0) {
538 if ((a1
= address()) == 0) {
543 if ((c
= getchr()) == ';') {
556 if (!globflg
) save();
557 append(gettty
, addr2
);
565 append(gettty
, addr1
-1);
567 /* XPG4 - If no new lines are inserted, then the current */
568 /* line becomes the line after the lines deleted. */
570 if (((linebuf
[0] != '.') || (dot
== (addr1
-1))) &&
585 if (vflag
&& fchange
) {
598 if (!ncflg
) /* there is a filename */
636 if (!globflg
) save();
637 append(gettty
, addr2
-1);
650 if (!globflg
) save();
655 if ((c
= getchr()) < 'a' || c
> 'z')
660 names
[c
-'a'] = addr2
->cur
& ~01;
693 puts(getaline((a1
++)->cur
));
694 } while (a1
<= addr2
);
712 save28
= (dol
!= fendcore
);
713 if (crflag
== 2 || crflag
== -2)
714 crflag
= -1; /* restore crflag for next file */
716 if ((io
= eopen(file
, O_RDONLY
)) < 0) {
718 /* if first entering editor and file does not exist */
719 /* set saved access time to 0 */
723 if (c
== 'e' && vflag
== 0)
726 if (errno
== ENOENT
) {
732 /* get last mod time of file */
733 /* eflg - entered editor with ed or e */
744 if (!globflg
&& (c
== 'r')) save();
745 append(getfile
, addr2
);
754 if (!globflg
) save();
755 substitute(globp
!= 0);
788 /* on NULL-RE condition do not generate error */
790 if ((linebuf
[0] != '.') && (zero
!= dol
) &&
791 (addr1
<= zero
|| addr2
> dol
))
795 io
= eopen(file
, O_WRONLY
);
796 n
= 1; /* set n so newtime will not execute */
800 if (stat(file
, &Fl
) < 0) {
801 if ((io
= creat(file
, S_IRUSR
|S_IWUSR
|S_IRGRP
802 |S_IWGRP
|S_IROTH
|S_IWOTH
)) < 0)
812 * Must determine if file is
820 * Determine if there are enough free blocks on system
822 if (!Short
&& statvfs(file
, &U
) == 0 &&
823 U
.f_bfree
< ((Tf
.st_size
/U
.f_frsize
) + 100)) {
829 p1
= savedfile
; /* The current filename */
832 if (c
== 'w' && Fl
.st_nlink
== 1 && ISREG(lFl
)) {
833 if (close(open(file
, O_WRONLY
)) < 0)
839 * If funlink equals one it means that
840 * funny points to a valid file which must
841 * be unlinked when interrupted.
845 if ((io
= creat(funny
, FMODE(Fl
))) >= 0) {
846 chown(funny
, Fl
.st_uid
, Fl
.st_gid
);
847 chmod(funny
, FMODE(Fl
));
851 if (rename(funny
, file
))
854 /* if filenames are the same */
857 /* check if entire buffer was written */
859 if (((addr1
== zero
) ||
860 (addr1
== (zero
+ 1))) &&
865 if (fchange
== 1 && m
!= 0)
870 n
= 1; /* set n so newtime will not execute */
873 (c
== 'w') ? O_WRONLY
|O_CREAT
|O_TRUNC
874 : O_WRONLY
|O_CREAT
|O_APPEND
, S_IRUSR
|S_IWUSR
875 |S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
)) < 0)
883 fchange
= (((addr1
== zero
) || (addr1
== (zero
+ 1))) &&
884 (addr2
== dol
)) ? 0 : 1;
885 /* Leave fchange alone if partial write was to another file */
886 if (fchange
== 1 && m
!= 0) fchange
= fsave
;
892 * C is same as X, but always assume input files are
904 (void) crypt_close(perm
);
906 if ((kflag
= run_setkey(&perm
[0], getkey(msgtab
[66]))) == -1) {
919 count
= (addr2
-zero
)&077777;
952 int n
, relerr
, retval
;
958 if ('0' <= c
&& c
<= '9') {
963 } while ((c
= getchr()) >= '0' && c
<= '9');
1025 if ((c
= getchr()) < 'a' || c
> 'z')
1027 for (a1
= zero
; a1
<= dol
; a1
++)
1028 if (names
[c
-'a'] == (a1
->cur
& ~01))
1038 /* on NULL-RE condition do not generate error */
1040 if ((linebuf
[0] != '.') && (a1
< zero
|| a1
> dol
))
1053 addr1
= addr2
= dot
;
1080 /* on NULL-RE condition do not generate error */
1082 if ((linebuf
[0] != '.') && (addr1
<= zero
|| addr2
> dol
))
1089 /* for the c and i commands 0 equal to 1 address */
1090 if (addr1
== zero
) {
1093 if (addr2
== zero
) {
1104 if ((c
= getchr()) == '\n')
1106 if (c
== 'p' || c
== 'l' || c
== 'n') {
1108 if (c
== 'l') listf
++;
1109 if (c
== 'n') listn
++;
1110 if ((c
= getchr()) == '\n')
1125 if (c
== '\n' || c
== EOF
) {
1127 if (*p1
== 0 && comm
!= 'f')
1129 /* ncflg set means do not get mod time of file */
1130 /* since no filename followed f */
1134 while (*p2
++ = *p1
++)
1141 while ((c
= getchr()) == ' ')
1144 ++Xqt
, c
= getchr();
1152 if (c
== EOF
|| (c
== ' ' && !Xqt
))
1154 } while ((c
= getchr()) != '\n');
1163 if (savedfile
[0] == 0 || comm
== 'e' || comm
== 'f') {
1166 while (*p1
++ = *p2
++)
1197 puts(gettext(" nulls replaced by '\\0'"));
1207 signal(SIGINT
, onintr
);
1211 if (funlink
) unlink(funny
); /* remove tmp file */
1212 /* if interrupted a read, only part of file may be in buffer */
1214 sprintf(tstring
, "\007read may be incomplete - beware!\007");
1215 puts(gettext(tstring
));
1224 signal(SIGINT
, SIG_IGN
);
1225 signal(SIGHUP
, SIG_IGN
);
1227 * if there are lines in file and file was not written
1228 * since last update, save in ed.hup, or $HOME/ed.hup
1230 if (dol
> zero
&& fchange
== 1) {
1233 io
= creat("ed.hup",
1234 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
1235 if (io
< 0 && home
) {
1238 fn
= (char *)calloc(strlen(home
) + 8, sizeof (char));
1241 strcat(fn
, "/ed.hup");
1242 io
= creat(fn
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
1260 if (code
== 28 && save28
== 0) {
1274 #ifdef NULLS /* Not really nulls, but close enough */
1275 /* This is a bug because of buffering */
1276 if (code
== 28) /* illegal char. */
1279 /* Cant open file or file does not exist */
1280 if ((code
== 3) || (code
== 68)) {
1294 lseek(0, (long)0, 2);
1300 while ((c
= getchr()) != '\n' && c
!= EOF
)
1310 return (0); /* Non-fatal error. */
1319 if (lastc
= peekc
) {
1324 if ((lastc
= (unsigned char)*globp
++) != 0)
1329 if (read(0, &c
, 1) <= 0)
1330 return (lastc
= EOF
);
1331 lastc
= (unsigned char)c
;
1344 while ((c
= getchr()) != '\n') {
1354 if (p
> &linebuf
[LBSIZE
-1])
1358 if (linebuf
[0] == '.' && linebuf
[1] == 0)
1362 * POSIX.2/XPG4 explicitly says no to this:
1364 * in Solaris backslash followed by special character "." is
1365 * special character "." itself; (so terminating input mode can be
1368 * however, POSIX2/XPG4 says, input mode is terminated by
1369 * entering line consisting of only 2 characters: ".\n"
1371 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1389 if ((ninbuf
= read(io
, genbuf
, LBSIZE
)-1) < 0)
1391 puts(gettext("'\\n' appended"));
1397 if (isencrypt(genbuf
, ninbuf
+ 1))
1404 if (run_crypt(count
, genbuf
, ninbuf
+1, perm
) == -1)
1407 if (lp
>= &linebuf
[LBSIZE
]) {
1411 if ((*lp
++ = c
= *fp
++) == 0) {
1422 } while (c
!= '\n');
1425 if (fss
.Ffill
&& fss
.Flim
&& lenchk(linebuf
, &fss
) < 0) {
1426 write(1, gettext("line too long: lno = "),
1427 strlen(gettext("line too long: lno = ")));
1429 count
= (++dot
-zero
)&077777;
1450 lp
= getaline(a1
++->cur
);
1451 if (fss
.Ffill
&& fss
.Flim
&& lenchk(linebuf
, &fss
) < 0) {
1452 write(1, gettext("line too long: lno = "),
1453 strlen(gettext("line too long: lno = ")));
1455 count
= (a1
-zero
-1)&077777;
1464 if (run_crypt(count
-n
, genbuf
, n
, perm
) == -1)
1466 if (write(io
, genbuf
, n
) != n
)
1471 if (dol
->cur
== 0L)break; /* Allow write of null file */
1473 if ((*fp
++ = *lp
++) == 0) {
1478 } while (a1
<= addr2
);
1481 if (run_crypt(count
-n
, genbuf
, n
, perm
) == -1)
1483 if (write(io
, genbuf
, n
) != n
)
1488 append(int (*f
)(void), LINE a
)
1495 while ((*f
)() == 0) {
1496 if (dol
>= endcore
) {
1497 if ((int)sbrk(512 * sizeof (struct lin
)) == -1) {
1509 (--a2
)->cur
= (--a1
)->cur
;
1520 static char savcmd
[LBSIZE
]; /* last command */
1521 char curcmd
[LBSIZE
]; /* current command */
1522 char *psavcmd
, *pcurcmd
, *psavedfile
;
1523 int endflg
= 1, shflg
= 0;
1531 /* read command til end */
1534 * a '!' found in beginning of command is replaced with the saved
1535 * command. a '%' found in command is replaced with the current
1545 while (*pcurcmd
++ = *psavcmd
++)
1551 UNGETC(c
); /* put c back */
1552 while (endflg
== 1) {
1553 while ((c
= get_wchr()) != '\n' && c
!= '%' && c
!= '\\') {
1554 if ((len
= wctomb(pcurcmd
, c
)) <= 0) {
1555 *pcurcmd
= (unsigned char)c
;
1562 if (savedfile
[0] == 0)
1565 psavedfile
= savedfile
;
1566 while (pcurcmd
< curcmd
+ LBSIZE
&&
1567 (*pcurcmd
++ = *psavedfile
++))
1572 } else if (c
== '\\') {
1576 if ((len
= wctomb(pcurcmd
, c
)) <= 0) {
1577 *pcurcmd
= (unsigned char)c
;
1583 /* end of command hit */
1590 strcpy(savcmd
, curcmd
);
1592 if ((pid
= fork()) == 0) {
1593 signal(SIGHUP
, oldhup
);
1594 signal(SIGQUIT
, oldquit
);
1596 execlp(_PATH_BSHELL
, "sh", "-c", curcmd
, NULL
);
1599 savint
= signal(SIGINT
, SIG_IGN
);
1600 while ((rpid
= wait(&retcode
)) != pid
&& rpid
!= (pid_t
)-1)
1602 signal(SIGINT
, savint
);
1603 if (vflag
) puts("!");
1609 if (vflag
&& fchange
) {
1617 * For case where user reads in BOTH a good
1638 rdelete(addr1
, addr2
);
1642 rdelete(LINE ad1
, LINE ad2
)
1651 (a1
++)->cur
= (a2
++)->cur
;
1666 for (a1
= zero
+1; (a1
->cur
&01) == 0; a1
++)
1669 for (a2
= a1
+ 1; a2
<= a3
; ) {
1674 (a1
++)->cur
= (a2
++)->cur
;
1689 bp
= getblock(tl
, READ
);
1692 while (*lp
++ = *bp
++)
1694 bp
= getblock(tl
+= 0400, READ
);
1710 bp
= getblock(tl
, WRITE
);
1713 while (*bp
= *lp
++) {
1714 if (*bp
++ == '\n') {
1720 bp
= getblock(tl
+= 0400, WRITE
);
1725 tline
+= (((lp
-linebuf
)+03)>>1)&077776;
1730 getblock(long atl
, long iof
)
1737 off
= (atl
<<1)&0774;
1739 /* bno is limited to 16 bits */
1745 if (bno
== iblock
) {
1754 if (run_crypt(0L, ibuff
, 512, tperm
) == -1)
1756 blkio(iblock
, ibuff
, write
);
1760 blkio(bno
, ibuff
, read
);
1762 if (run_crypt(0L, ibuff
, 512, tperm
) == -1)
1773 if (run_crypt(0L, crbuf
, 512, tperm
) == -1)
1775 blkio(oblock
, crbuf
, write
);
1777 blkio(oblock
, obuff
, write
);
1784 blkio(int b
, char *buf
, ssize_t (*iofcn
)())
1786 lseek(tfile
, (long)b
<<9, 0);
1787 if ((*iofcn
)(tfile
, buf
, 512) != 512) {
1789 (void) error(32); /* Bypass this if writing null file */
1800 (void) close(tfile
);
1801 (void) unlink(tfname
);
1805 for (markp
= names
; markp
< &names
[26]; )
1815 if ((tfile
= open(tfname
, O_CREAT
|O_EXCL
|O_RDWR
,
1816 S_IRUSR
|S_IWUSR
)) < 0) {
1817 puts(gettext(msgtab
[70]));
1825 (void) crypt_close(tperm
);
1827 if (makekey(tperm
)) {
1829 puts(gettext(msgtab
[65]));
1832 brk((char *)fendcore
);
1833 dot
= zero
= dol
= savdot
= savdol
= fendcore
;
1834 flag28
= save28
= 0;
1835 endcore
= fendcore
- sizeof (struct lin
);
1843 char multic
[MB_LEN_MAX
];
1846 char globuf
[LBSIZE
];
1854 if ((n
= _mbftowc(multic
, &l
, getchr
, &peekc
)) <= 0)
1861 while ((c
= get_wchr()) != '\n') {
1865 /* '\\' has special meaning only if preceding a '\n' */
1871 if ((gp
+ (unsigned int)MB_CUR_MAX
) >= &globuf
[LBSIZE
-1])
1873 if ((len
= wctomb(gp
, c
)) <= 0) {
1874 *gp
= (unsigned char)c
;
1883 for (a1
= zero
; a1
<= dol
; a1
++) {
1885 if (a1
>= addr1
&& a1
<= addr2
&& execute(0, a1
) == k
)
1889 * Special case: g/.../d (avoid n^2 algorithm)
1891 if (globuf
[0] == 'd' && globuf
[1] == '\n' && globuf
[2] == '\0') {
1895 for (a1
= zero
; a1
<= dol
; a1
++) {
1917 for (a1
= addr1
; a1
<= addr2
; a1
++) {
1918 lp
= getaline(a1
->cur
);
1920 if (gp
++ > &genbuf
[LBSIZE
-1])
1925 while (*lp
++ = *gp
++)
1927 addr1
->cur
= putline();
1929 rdelete(addr1
+1, addr2
);
1934 substitute(int inglob
)
1939 int ingsav
; /* For saving arg. */
1944 for (a1
= addr1
; a1
<= addr2
; a1
++) {
1945 if (execute(0, a1
) == 0)
1953 if (execute(1, (LINE
)0) == 0)
1958 if (ocerr1
== 0)continue; /* Don't put out-not changed. */
1959 subnewa
= putline();
1962 for (markp
= names
; markp
< &names
[26]; markp
++)
1963 if (*markp
== a1
->cur
)
1973 return; /* Was in global-no error msg allowed. */
1975 (void) error(35); /* Not in global, but not found. */
1977 (void) error(35); /* RE found, but occurrence match failed. */
1986 char multic
[MB_LEN_MAX
];
1988 static char remem
[RHSIZE
];
1989 static int remflg
= -1;
1992 if ((n
= _mbftowc(multic
, &seof
, getchr
, &peekc
)) <= 0)
1994 if (seof
== '\n' || seof
== ' ')
2000 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
2004 if (p
>= &rhsbuf
[RHSIZE
])
2006 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
2008 } else if (cl
== '\n') {
2013 if (!(globp
&& globp
[0])) {
2018 } else if (cl
== seof
)
2020 if (p
+ n
> &rhsbuf
[RHSIZE
])
2022 (void) strncpy(p
, multic
, n
);
2026 if (rhsbuf
[0] == '%' && rhsbuf
[1] == 0)
2028 * If there isn't a remembered string, it is an error;
2029 * otherwise the right hand side is the previous right
2036 strcpy(rhsbuf
, remem
);
2038 strcpy(remem
, rhsbuf
);
2042 peekc
= getchr(); /* Gets char after third delimiter. */
2044 c
= LBSIZE
; peekc
= 0;
2046 if (peekc
>= '1' && peekc
<= '9') {
2048 peekc
= 0; /* Allows getchr() to get next char. */
2051 if (i
< '0' || i
> '9')
2055 (void) error(20); /* "Illegal suffix" */
2057 peekc
= i
; /* Effectively an unget. */
2063 * Returns occurrence value. 0 & 1 both do first occurrence
2064 * only: c = 0 if ordinary substitute; c = 1
2065 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2075 if ((p2
= linebp
) == 0)
2077 while (*p1
++ = *p2
++)
2089 if (gsubf
> 0 && gsubf
< LBSIZE
) {
2091 if (gsubf
!= numpass
)
2103 sp
= place(sp
, loc1
, loc2
);
2105 } else if (c
== '\\') {
2107 if (c
>= '1' && c
< nbra
+ '1') {
2108 sp
= place(sp
, braslist
[c
-'1'],
2114 if (sp
>= &genbuf
[LBSIZE
])
2118 loc2
= sp
- genbuf
+ linebuf
;
2119 while (*sp
++ = *lp
++)
2120 if (sp
>= &genbuf
[LBSIZE
])
2124 while (*lp
++ = *sp
++)
2129 place(char *sp
, char *l1
, char *l2
)
2134 if (sp
>= &genbuf
[LBSIZE
])
2141 comple(wchar_t seof
)
2147 char multic
[MB_LEN_MAX
];
2150 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2152 if (n
== 0 || c
== '\n') {
2158 if (c
== seof
&& !cclass
)
2160 if (cclass
&& c
== ']') {
2162 if (cp
> &genbuf
[LBSIZE
-1])
2167 if (c
== '[' && !cclass
) {
2169 if (cp
> &genbuf
[LBSIZE
-1])
2172 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2174 if (n
== 0 || c
== '\n')
2177 if (c
== '\\' && !cclass
) {
2178 if (cp
> &genbuf
[LBSIZE
-1])
2181 if ((n
= _mbftowc(multic
, &c
, getchr
, &peekc
)) < 0)
2183 if (n
== 0 || c
== '\n')
2186 if (cp
+ n
> &genbuf
[LBSIZE
-1])
2188 (void) strncpy(cp
, multic
, n
);
2192 if (n
!= 0 && c
== '\n')
2194 if (n
== 0 || c
== '\n')
2198 * NULL RE: do not compile a null regular expression; but process
2199 * input with last regular expression encountered
2202 if (genbuf
[0] != '\0') {
2204 expbuf
= compile(genbuf
, (char *)0, (char *)0);
2217 if ((adt
= address()) == 0)
2220 if (!globflg
) save();
2223 append(getcopy
, ad1
++);
2227 for (ad1
= addr1
; ad1
<= ad2
; )
2228 (ad1
++)->cur
&= ~01;
2233 dot
= adt
+ (ad2
-ad1
);
2239 } else if (adt
>= ad2
) {
2250 reverse(LINE a1
, LINE a2
)
2269 (void) getaline((addr1
++)->cur
);
2275 * Handles error code returned from comple() routine: regular expression
2276 * compile and match routines
2288 execute(int gf
, LINE addr
)
2293 for (c
= 0; c
< nbra
; c
++) {
2302 p1
= getaline(addr
->cur
);
2305 return (step(p1
, expbuf
));
2314 r
= (int)(count
%10);
2323 puts(const char *sp
)
2328 if (fss
.Ffill
&& (listf
== 0)) {
2330 /* deliberate attempt to remove constness of sp because */
2331 /* it needs to be expanded */
2333 if ((i
= expnd((char *)sp
, funny
, &sz
, &fss
)) == -1) {
2334 write(1, funny
, fss
.Flim
& 0377);
2336 write(1, gettext("too long"),
2337 strlen(gettext("too long")));
2340 write(1, funny
, sz
);
2343 write(1, gettext("tab count\n"),
2344 strlen(gettext("tab count\n")));
2349 n
= mbtowc(&c
, sp
, MB_LEN_MAX
);
2354 putchr((unsigned char)*sp
++);
2360 putchr((unsigned char)*sp
++);
2365 putchr('$'); /* end of line is marked with a $ */
2368 /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2369 /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2370 /* statement in putchr() */
2372 putchr('$'); /* end of line is marked with a $ */
2384 char buf
[MB_LEN_MAX
], *p
;
2394 if ((len
= wctomb(p
, c
)) <= 0) {
2395 *p
= (unsigned char)c
;
2399 if (col
+ 4 >= 72) {
2404 (void) sprintf(lp
, "\\%03o",
2405 *(unsigned char *)p
++);
2410 if ((len
= wcwidth(c
)) <= 0)
2412 if (col
+ len
>= 72) {
2418 if ((len
= wctomb(lp
, c
)) <= 0) {
2419 *lp
= (unsigned char)c
;
2425 if ((len
= wctomb(lp
, c
)) <= 0) {
2426 *lp
= (unsigned char)c
;
2431 if (c
== '\n' || lp
>= &line
[64]) {
2434 write(1, line
, len
);
2442 putchr(unsigned char c
)
2448 if (listf
&& c
!= '\n') {
2486 /* if $ characters are within the line preceed with \ */
2498 (void) sprintf(lp
, "\\%03o", c
);
2506 * long lines are folded w/ pt of folding indicated by writing
2507 * backslash/newline character
2510 if (col
+ 1 >= 72) {
2517 if (c
== '\n' || lp
>= &line
[64]) {
2520 (void) write(1, line
, len
);
2528 getkey(const char *prompt
)
2533 static char key
[KSIZE
+1];
2537 sig
= signal(SIGINT
, SIG_IGN
);
2538 ioctl(0, TCGETA
, &b
);
2540 b
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
2541 ioctl(0, TCSETAW
, &b
);
2542 write(1, gettext(prompt
), strlen(gettext(prompt
)));
2544 while (((c
= getchr()) != EOF
) && (c
!= '\n')) {
2545 if (p
< &key
[KSIZE
])
2551 ioctl(0, TCSETAW
, &b
);
2552 signal(SIGINT
, sig
);
2566 char globuf
[LBSIZE
];
2567 char multic
[MB_LEN_MAX
];
2577 if ((n
= _mbftowc(multic
, &cl
, getchr
, &peekc
)) <= 0)
2583 for (a1
= zero
; a1
<= dol
; a1
++) {
2585 if (a1
>= addr1
&& a1
<= addr2
&& execute(0, a1
) == k
)
2591 * preserve the p, l, and n suffix commands of the G and V
2592 * commands during the interactive section and restore
2593 * on completion of the G and V command.
2601 for (a1
= zero
; a1
<= dol
; a1
++) {
2605 puts(getaline(a1
->cur
));
2606 if ((c
= get_wchr()) == EOF
)
2608 if (c
== 'a' || c
== 'i' || c
== 'c')
2616 if ((len
= wctomb(gp
, c
)) <= 0) {
2617 *gp
= (unsigned char)c
;
2621 while ((c
= get_wchr()) != '\n') {
2623 /* '\\' has special meaning only if preceding a '\n' */
2629 if ((gp
+ (unsigned int)MB_CUR_MAX
) >=
2633 if ((len
= wctomb(gp
, c
)) <= 0) {
2634 *gp
= (unsigned char)c
;
2642 } else if ((c
= get_wchr()) != '\n')
2662 eopen(char *string
, int rw
)
2664 #define w_or_r(a, b) (rw ? a : b)
2668 int chcount
; /* # of char read. */
2670 if (rflg
) { /* restricted shell */
2677 if ((io
= open(string
, rw
)) >= 0) {
2679 chcount
= read(io
, crbuf
, LBSIZE
);
2681 if (isencrypt(crbuf
, chcount
))
2687 if (run_crypt(0L, crbuf
, chcount
, perm
) == -1)
2689 if (fspec(crbuf
, &fss
, 0) < 0) {
2701 xerr
: (void) error(0);
2702 if ((i
= fork()) == 0) {
2703 signal(SIGHUP
, oldhup
);
2704 signal(SIGQUIT
, oldquit
);
2705 signal(SIGPIPE
, oldpipe
);
2706 signal(SIGINT
, (void (*)()) 0);
2707 close(w_or_r(pf
[1], pf
[0]));
2708 close(w_or_r(0, 1));
2709 dup(w_or_r(pf
[0], pf
[1]));
2710 close(w_or_r(pf
[0], pf
[1]));
2711 execlp(_PATH_BSHELL
, "sh", "-c", string
, (char *)0);
2716 close(w_or_r(pf
[0], pf
[1]));
2717 return (w_or_r(pf
[1], pf
[0]));
2726 Xqt
= 0, wait((int *)0);
2738 * Go to end of file name
2742 while (*--p
== '/') /* delete trailing slashes */
2745 * go back to beginning of file
2749 * Copy file name to funny setting p2 at
2756 * Set p1 to point to basename of tfname.
2758 p1
= strrchr(tfname
, '/');
2759 if (strlen(tfname
) > (size_t)6)
2760 p1
= &tfname
[strlen(tfname
)-6];
2762 *p2
= '\007'; /* add unprintable char for funny a unique name */
2764 * Copy tfname to file.
2766 while (*++p2
= *p1
++)
2772 getime(void) /* get modified time of file and save */
2774 if (stat(file
, &Fl
) < 0)
2777 savtime
= Fl
.st_mtime
;
2782 chktime(void) /* check saved mod time against current mod time */
2784 if (savtime
!= 0 && Fl
.st_mtime
!= 0) {
2785 if (savtime
!= Fl
.st_mtime
)
2792 newtime(void) /* get new mod time and save */
2795 savtime
= Fl
.st_mtime
;
2800 * restricted - check for '/' in name and delete trailing '/'
2809 if (*p
++ == '/'&& rflg
) {
2813 /* delete trailing '/' */
2823 * Searches thru beginning of file looking for a string of the form
2826 * where "values" are
2829 * s<num> sets the Flim to <num>
2830 * t??? sets tab stop stuff
2837 fspec(char line
[], struct Fspec
*f
, int up
)
2845 havespec
= fsprtn
= 0;
2846 for (fsp
= line
; *fsp
&& *fsp
!= '\n'; fsp
+= len
) {
2847 if ((len
= mblen(fsp
, MB_CUR_MAX
)) <= 0)
2851 case '<': if (havespec
)
2853 if (*(fsp
+1) == ':') {
2856 if (!ioctl(1, TCGETA
, &arg
) &&
2857 ((arg
.c_oflag
& TAB3
) ==
2866 case 's': if (havespec
&& (n
= numb()) >= 0)
2870 case 't': if (havespec
) targ(f
);
2875 case 'm': if (havespec
) n
= numb();
2879 case ':': if (!havespec
) continue;
2880 if (*(fsp
+1) != '>') fsprtn
= -1;
2883 default: if (!havespec
) continue;
2897 while (*++fsp
>= '0' && *fsp
<= '9')
2898 n
= 10*n
+ *fsp
-'0';
2905 targ(struct Fspec
*f
)
2908 if (*++fsp
== '-') {
2909 if (*(fsp
+ 1) >= '0' && *(fsp
+1) <= '9') tincr(numb(), f
);
2913 if (*fsp
>= '0' && *fsp
<= '9') {
2923 tincr(int n
, struct Fspec
*f
)
2928 for (i
= 0; i
< 20; i
++)
2929 f
->Ftabs
[i
] = l
+= n
;
2935 tstd(struct Fspec
*f
)
2940 if (*(fsp
+1) >= '0' && *(fsp
+1) <= '9') {
2946 fsprtn
= stdtab(std
, f
->Ftabs
);
2951 tlist(struct Fspec
*f
)
2959 if ((n
= numb()) <= last
|| i
>= 20) {
2963 f
->Ftabs
[i
++] = last
= n
;
2964 } while (*++fsp
== ',');
2972 expnd(char line
[], char buf
[], int *sz
, struct Fspec
*f
)
2982 while (*++l
&& *l
!= '\n' && b
< 511) {
2984 while (*t
&& b
>= *t
)
2998 if (*l
!= '\0' && *l
!= '\n') {
3003 if (f
->Flim
&& (b
-1 > (int)f
->Flim
))
3010 clear(struct Fspec
*f
)
3012 f
->Ftabs
[0] = f
->Fdel
= f
->Fmov
= f
->Ffill
= 0;
3018 lenchk(char line
[], struct Fspec
*f
)
3027 while (*++l
&& *l
!= '\n' && b
< 511) {
3029 while (*t
&& b
>= *t
)
3038 if ((*l
!= '\0' && *l
!= '\n') || (f
->Flim
&& (b
-1 > (int)f
->Flim
)))
3046 * stdtabs: standard tabs table
3047 * format: option code letter(s), null, tabs, null
3050 static char stdtabs
[] = {
3051 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */
3052 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */
3053 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */
3054 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */
3055 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3057 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */
3058 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3060 's', 0, 1, 10, 55, 0, /* SNOBOL */
3061 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
3066 * stdtab: return tab list for any "canned" tab option.
3067 * entry: option points to null-terminated option string
3068 * tabvect points to vector to be filled in
3069 * exit: return (0) if legal, tabvect filled, ending with zero
3070 * return (-1) if unknown option
3075 stdtab(char option
[], char tabvect
[NTABS
])
3081 if (strequal(&scan
, option
)) {
3082 strcopy(scan
, tabvect
);
3085 while (*scan
++) /* skip over tab specs */
3089 /* later: look up code in /etc/something */
3090 return (tabvect
[0] ? 0 : -1);
3095 * strequal: checks strings for equality
3096 * entry: scan1 points to scan pointer, str points to string
3097 * exit: return (1) if equal, return (0) if not
3098 * *scan1 is advanced to next nonzero byte after null
3103 strequal(char **scan1
, char *str
)
3107 while ((c
= *scan
++) == *str
&& c
)
3110 if (c
== 0 && *str
== 0)
3120 /* strcopy: copy source to destination */
3124 strcopy(char *source
, char *dest
)
3126 while (*dest
++ = *source
++)
3131 /* This is called before a buffer modifying command so that the */
3132 /* current array of line ptrs is saved in sav and dot and dol are saved */
3143 for (j
= 0; j
<= 25; j
++)
3144 savnames
[j
] = names
[j
];
3146 for (i
= zero
+ 1; i
<= dol
; i
++)
3152 /* The undo command calls this to restore the previous ptr array sav */
3153 /* and swap with cur - dot and dol are swapped also. This allows user to */
3162 LINE i
, tmpdot
, tmpdol
;
3164 tmpdot
= dot
; dot
= savdot
; savdot
= tmpdot
;
3165 tmpdol
= dol
; dol
= savdol
; savdol
= tmpdol
;
3166 /* swap arrays using the greater of dol or savdol as upper limit */
3167 for (i
= zero
+ 1; i
<= ((dol
> savdol
) ? dol
: savdol
); i
++) {
3173 * If the current text lines are swapped with the
3174 * text lines in the save buffer, then swap the current
3175 * marks with those in the save area.
3178 for (j
= 0; j
<= 25; j
++) {
3180 names
[j
] = savnames
[j
];
3189 char multi
[MB_LEN_MAX
];
3191 if (_mbftowc(multi
, &wc
, getchr
, &peekc
) <= 0)