xref: /original-bsd/contrib/ed/address.c (revision 0999a820)
1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  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	8.1 (Berkeley) 05/31/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 	if (top) {
77 		for (temp1 = top; temp1->below != NULL; temp1 = temp1->below) {
78 			/* find the matching line number in the buffer */
79 			if (l_line >= num)
80 				break;
81 			l_line++;
82 		}
83 	}
84 
85 	if ((l_line < num) || (!top)) {
86 		/* the number was wacko */
87 		*errnum = -1;
88 		strcpy(help_msg, "bad line number");
89 		return (NULL);
90 	} else
91 		if (num == 0)	/* special case */
92 			return (NULL);
93 		else
94 			return (temp1);
95 }
96 
97 
98 /*
99  * Figure out what the addresses are spec'd by the user.  Note for backward
100  * compatability the most recent addresses in a chain are used by the commands
101  * (i.e. 3,5,17,22d deletes lines 17 to 22 inclusive. The two leading addresses
102  * 3 and 5 are dropped as cmd_loop rolls through here the extra times).  Hence,
103  * the code may look a little wierder than it should.  The variable l_order is
104  * used to control for legally constructed addresses as described in ed(1).  So,
105  * "$-21" and "/RE/++++" are legal but /RE/-$ is not.
106  */
107 LINE *
108 address_conv(tempp, inputt, errnum)
109 	LINE *tempp;
110 	FILE *inputt;
111 	int *errnum;
112 {
113 	LINE *l_dot;
114 	int l_last, l_cnt, l_num, l_order, l_pass_flag;
115 
116 	l_dot = NULL;
117 	l_order = 0;
118 	l_pass_flag = 0;
119 	address_flag = 0;
120 
121 	l_last = ss;
122 	if (tempp == NULL)
123 		l_dot = current;
124 	else
125 		l_dot = tempp;
126 
127 	while ((ss = getc(inputt)) != EOF) {
128 		switch (ss) {
129 		case '0': case '1': case '2': case '3': case '4':
130 		case '5': case '6': case '7': case '8': case '9':
131 			if (l_order == (l_order | 4)) {
132 				*errnum = -1;
133 				strcpy(help_msg, "malformed address");
134 				return (NULL);
135 			}
136 			l_order |= 1;
137 			l_num = dig_num_conv(inputt, errnum);
138 			/*
139 			 * " " (<space>), historically, gets counted as a "+"
140 			 * if it's between two 'addable' address forms. Goofy,
141 			 * but it makes it compatible for those strange old
142 			 * scripts (or users?)
143 			 */
144 			if ((l_last == '+') ||
145 			    ((l_last == ' ') && l_pass_flag)) {
146 				if (l_last == ' ')
147 					l_num++;
148 				for (l_cnt = 0; l_cnt < l_num - 1; l_cnt++) {
149 					if (l_dot == NULL) {
150 						*errnum = -1;
151 						return (NULL);
152 					}
153 					l_dot = l_dot->below;
154 				}
155 			} else
156 				if ((l_last == '-') || (l_last == '^')) {
157 					for (l_cnt = l_num - 1;
158 					    l_cnt > 0; l_cnt--) {
159 						if (l_dot == NULL) {
160 							*errnum = -1;
161 							return (NULL);
162 						}
163 						l_dot = l_dot->above;
164 					}
165 				} else
166 					l_dot = num_to_address(l_num, errnum);
167 			if (*errnum < 0)
168 				return (NULL);
169 			l_pass_flag = 1;
170 			break;
171 		case '\'':
172 		case '$':
173 		case '?':
174 		case '/':
175 		case '.':
176 			if (l_order != 0) {
177 				*errnum = -1;
178 				strcpy(help_msg, "malformed address");
179 				return (NULL);
180 			}
181 			l_order = 4;
182 			switch (ss) {
183 			case '\'':
184 				l_dot = get_mark(inputt, errnum);
185 				break;
186 			case '$':
187 				l_dot = bottom;	/* set to bottom */
188 				break;
189 			case '?':
190 				l_dot = search_r(inputt, errnum);
191 				break;
192 			case '/':
193 				l_dot = search(inputt, errnum);
194 				break;
195 			case '.':
196 				l_dot = current;
197 				break;
198 			}
199 			break;
200 		case '-':
201 		case '^':
202 		case '+':
203 			l_order = 2;
204 			if (ss == '+') {
205 				l_dot = l_dot->below;
206 				if (l_dot == NULL) {
207 					strcpy(help_msg, "at end of buffer");
208 					*errnum = -1;
209 					return (NULL);
210 				}
211 			} else {
212 				l_dot = l_dot->above;
213 				if (l_dot == NULL) {
214 					strcpy(help_msg, "at top of buffer");
215 					*errnum = -1;
216 					return (NULL);
217 				}
218 			}
219 			break;
220 		case ' ':
221 			break;	/* ignore here, but see comment above */
222 		default:
223 			ungetc(ss, inputt);
224 			return (l_dot);
225 			break;
226 		}
227 
228 		if (*errnum < 0)
229 			break;	/* from the while-loop */
230 		l_last = ss;
231 	}
232 
233 	if (ss == EOF)
234 		return (l_dot);
235 	*errnum = -1;
236 	return (NULL);
237 }
238