xref: /csrg-svn/contrib/ed/address.c (revision 58315)
1 /*-
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rodney Ruddock of the University of Guelph.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)address.c	5.3 (Berkeley) 02/28/93";
13 #endif /* not lint */
14 
15 #include <sys/types.h>
16 
17 #include <regex.h>
18 #include <setjmp.h>
19 #include <stdio.h>
20 #include <string.h>
21 
22 #ifdef DBI
23 #include <db.h>
24 #endif
25 
26 #include "ed.h"
27 #include "extern.h"
28 
29 /*
30  * Make sure that address one comes before address two in the buffer
31  */
32 
33 int
34 address_check(one, two)
35 	LINE *one, *two;
36 {
37 	LINE   *l_cl;
38 
39 	for (l_cl = one; l_cl != NULL; l_cl = l_cl->below)
40 		if (l_cl == two)
41 			return (0);
42 	return (-1);
43 }
44 
45 /*
46  * convert a number given by the user into variable
47  */
48 int
49 dig_num_conv(inputt, errnum)
50 	FILE *inputt;
51 	int *errnum;
52 {
53 	int l_line = 0;
54 
55 	l_line = ss - '0';
56 	while ((ss = getc(inputt)) != EOF) {
57 		if ((ss < '0') || (ss > '9'))
58 			break;
59 		l_line = (l_line * 10) + (ss - '0');
60 	}
61 	*errnum = 0;
62 	ungetc(ss, inputt);
63 	return (l_line);
64 }
65 
66 /*
67  * Convert a numeric address into a LINE address (more useful for ed)
68  */
69 LINE *
70 num_to_address(num, errnum)
71 	int num, *errnum;
72 {
73 	int l_line = 1;
74 	LINE *temp1;
75 
76 	for (temp1 = top; temp1->below != NULL; temp1 = temp1->below) {
77 		/* find the matching line number in the buffer */
78 		if (l_line >= num)
79 			break;
80 		l_line++;
81 	}
82 
83 	if (l_line < num) {
84 		/* the number was wacko */
85 		*errnum = -1;
86 		strcpy(help_msg, "bad line number");
87 		return (NULL);
88 	} else
89 		if (num == 0)	/* special case */
90 			return (NULL);
91 		else
92 			return (temp1);
93 }
94 
95 
96 /*
97  * Figure out what the addresses are spec'd by the user.  Note for backward
98  * compatability the most recent addresses in a chain are used by the commands
99  * (i.e. 3,5,17,22d deletes lines 17 to 22 inclusive. The two leading addresses
100  * 3 and 5 are dropped as cmd_loop rolls through here the extra times).  Hence,
101  * the code may look a little wierder than it should.  The variable l_order is
102  * used to control for legally constructed addresses as described in ed(1).  So,
103  * "$-21" and "/RE/++++" are legal but /RE/-$ is not.
104  */
105 LINE *
106 address_conv(tempp, inputt, errnum)
107 	LINE *tempp;
108 	FILE *inputt;
109 	int *errnum;
110 {
111 	LINE *l_dot;
112 	int l_last, l_cnt, l_num, l_order, l_pass_flag;
113 
114 	l_dot = NULL;
115 	l_order = 0;
116 	l_pass_flag = 0;
117 	address_flag = 0;
118 
119 	l_last = ss;
120 	if (tempp == NULL)
121 		l_dot = current;
122 	else
123 		l_dot = tempp;
124 
125 	while ((ss = getc(inputt)) != EOF) {
126 		switch (ss) {
127 		case '0': case '1': case '2': case '3': case '4':
128 		case '5': case '6': case '7': case '8': case '9':
129 			if (l_order == (l_order | 4)) {
130 				*errnum = -1;
131 				strcpy(help_msg, "malformed address");
132 				return (NULL);
133 			}
134 			l_order |= 1;
135 			l_num = dig_num_conv(inputt, errnum);
136 			/*
137 			 * " " (<space>), historically, gets counted as a "+"
138 			 * if it's between two 'addable' address forms. Goofy,
139 			 * but it makes it compatible for those strange old
140 			 * scripts (or users?)
141 			 */
142 			if ((l_last == '+') ||
143 			    ((l_last == ' ') && l_pass_flag)) {
144 				if (l_last == ' ')
145 					l_num++;
146 				for (l_cnt = 0; l_cnt < l_num - 1; l_cnt++) {
147 					if (l_dot == NULL) {
148 						*errnum = -1;
149 						return (NULL);
150 					}
151 					l_dot = l_dot->below;
152 				}
153 			} else
154 				if ((l_last == '-') || (l_last == '^')) {
155 					for (l_cnt = l_num - 1;
156 					    l_cnt > 0; l_cnt--) {
157 						if (l_dot == NULL) {
158 							*errnum = -1;
159 							return (NULL);
160 						}
161 						l_dot = l_dot->above;
162 					}
163 				} else
164 					l_dot = num_to_address(l_num, errnum);
165 			if (*errnum < 0)
166 				return (NULL);
167 			l_pass_flag = 1;
168 			break;
169 		case '\'':
170 		case '$':
171 		case '?':
172 		case '/':
173 		case '.':
174 			if (l_order != 0) {
175 				*errnum = -1;
176 				strcpy(help_msg, "malformed address");
177 				return (NULL);
178 			}
179 			l_order = 4;
180 			switch (ss) {
181 			case '\'':
182 				l_dot = get_mark(inputt, errnum);
183 				break;
184 			case '$':
185 				l_dot = bottom;	/* set to bottom */
186 				break;
187 			case '?':
188 				l_dot = search_r(inputt, errnum);
189 				break;
190 			case '/':
191 				l_dot = search(inputt, errnum);
192 				break;
193 			case '.':
194 				l_dot = current;
195 				break;
196 			}
197 			break;
198 		case '-':
199 		case '^':
200 		case '+':
201 			l_order = 2;
202 			if (ss == '+') {
203 				l_dot = l_dot->below;
204 				if (l_dot == NULL) {
205 					strcpy(help_msg, "at end of buffer");
206 					*errnum = -1;
207 					return (NULL);
208 				}
209 			} else {
210 				l_dot = l_dot->above;
211 				if (l_dot == NULL) {
212 					strcpy(help_msg, "at top of buffer");
213 					*errnum = -1;
214 					return (NULL);
215 				}
216 			}
217 			break;
218 		case ' ':
219 			break;	/* ignore here, but see comment above */
220 		default:
221 			ungetc(ss, inputt);
222 			return (l_dot);
223 			break;
224 		}
225 
226 		if (*errnum < 0)
227 			break;	/* from the while-loop */
228 		l_last = ss;
229 	}
230 
231 	if (ss == EOF)
232 		return (l_dot);
233 	*errnum = -1;
234 	return (NULL);
235 }
236