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