blob: 9606cfdde7e8542ebb3aad5c105ef7e8542a6d95 [file] [log] [blame]
Bernhard Reutner-Fischerd9ed35c2006-05-19 12:38:30 +00001/* vi: set sw=4 ts=4: */
Rob Landley3b890392006-05-04 20:56:43 +00002/*
3 * Copyright (c) 2002 by David I. Bell
4 * Permission is granted to use, distribute, or modify this source,
5 * provided that this copyright notice remains intact.
6 *
7 * The "ed" built-in command (much simplified)
8 */
9
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000010#include "libbb.h"
Rob Landley3b890392006-05-04 20:56:43 +000011
Denis Vlasenkod9391b12007-09-25 11:55:57 +000012typedef struct LINE {
13 struct LINE *next;
14 struct LINE *prev;
15 int len;
16 char data[1];
17} LINE;
18
19
Denis Vlasenko74324c82007-06-04 10:16:52 +000020#define searchString bb_common_bufsiz1
21
Denis Vlasenko55f30b02007-03-24 22:42:29 +000022enum {
Denis Vlasenko74324c82007-06-04 10:16:52 +000023 USERSIZE = sizeof(searchString) > 1024 ? 1024
24 : sizeof(searchString) - 1, /* max line length typed in by user */
Denis Vlasenko55f30b02007-03-24 22:42:29 +000025 INITBUF_SIZE = 1024, /* initial buffer size */
26};
27
Denis Vlasenkod9391b12007-09-25 11:55:57 +000028struct globals {
29 int curNum;
30 int lastNum;
31 int bufUsed;
32 int bufSize;
33 LINE *curLine;
34 char *bufBase;
35 char *bufPtr;
36 char *fileName;
37 LINE lines;
38 smallint dirty;
39 int marks[26];
40};
41#define G (*ptr_to_globals)
42#define curLine (G.curLine )
43#define bufBase (G.bufBase )
44#define bufPtr (G.bufPtr )
45#define fileName (G.fileName )
46#define curNum (G.curNum )
47#define lastNum (G.lastNum )
48#define bufUsed (G.bufUsed )
49#define bufSize (G.bufSize )
50#define dirty (G.dirty )
51#define lines (G.lines )
52#define marks (G.marks )
53#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +000054 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenkod9391b12007-09-25 11:55:57 +000055} while (0)
Rob Landley3b890392006-05-04 20:56:43 +000056
Rob Landley3b890392006-05-04 20:56:43 +000057
58static void doCommands(void);
59static void subCommand(const char *cmd, int num1, int num2);
Denis Vlasenkod9391b12007-09-25 11:55:57 +000060static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
Rob Landley3b890392006-05-04 20:56:43 +000061static int setCurNum(int num);
Rob Landley3b890392006-05-04 20:56:43 +000062static void addLines(int num);
63static int insertLine(int num, const char *data, int len);
Denis Vlasenkod9391b12007-09-25 11:55:57 +000064static void deleteLines(int num1, int num2);
Rob Landley3b890392006-05-04 20:56:43 +000065static int printLines(int num1, int num2, int expandFlag);
66static int writeLines(const char *file, int num1, int num2);
67static int readLines(const char *file, int num);
68static int searchLines(const char *str, int num1, int num2);
69static LINE *findLine(int num);
Rob Landley3b890392006-05-04 20:56:43 +000070static int findString(const LINE *lp, const char * str, int len, int offset);
71
Denis Vlasenkod9391b12007-09-25 11:55:57 +000072
73static int bad_nums(int num1, int num2, const char *for_what)
74{
75 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
76 bb_error_msg("bad line range for %s", for_what);
77 return 1;
78 }
79 return 0;
80}
81
82
83static char *skip_blank(const char *cp)
84{
Denis Vlasenkod9391b12007-09-25 11:55:57 +000085 while (isblank(*cp))
86 cp++;
87 return (char *)cp;
88}
89
90
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000091int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko1d426652008-03-17 09:09:09 +000092int ed_main(int argc ATTRIBUTE_UNUSED, char **argv)
Rob Landley3b890392006-05-04 20:56:43 +000093{
Denis Vlasenkod9391b12007-09-25 11:55:57 +000094 INIT_G();
95
96 bufSize = INITBUF_SIZE;
97 bufBase = xmalloc(bufSize);
98 bufPtr = bufBase;
99 lines.next = &lines;
100 lines.prev = &lines;
Rob Landley3b890392006-05-04 20:56:43 +0000101
Denis Vlasenko1d426652008-03-17 09:09:09 +0000102 if (argv[1]) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000103 fileName = xstrdup(argv[1]);
Rob Landley3b890392006-05-04 20:56:43 +0000104 if (!readLines(fileName, 1)) {
Rob Landley3b890392006-05-04 20:56:43 +0000105 return EXIT_SUCCESS;
106 }
Rob Landley3b890392006-05-04 20:56:43 +0000107 if (lastNum)
108 setCurNum(1);
Rob Landley3b890392006-05-04 20:56:43 +0000109 dirty = FALSE;
110 }
111
112 doCommands();
Rob Landley3b890392006-05-04 20:56:43 +0000113 return EXIT_SUCCESS;
114}
115
116/*
117 * Read commands until we are told to stop.
118 */
119static void doCommands(void)
120{
121 const char *cp;
122 char *endbuf, *newname, buf[USERSIZE];
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000123 int len, num1, num2;
124 smallint have1, have2;
Rob Landley3b890392006-05-04 20:56:43 +0000125
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000126 while (TRUE) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000127 /* Returns:
128 * -1 on read errors or EOF, or on bare Ctrl-D.
129 * 0 on ctrl-C,
130 * >0 length of input string, including terminating '\n'
131 */
132 len = read_line_input(": ", buf, sizeof(buf), NULL);
133 if (len <= 0)
Rob Landley3b890392006-05-04 20:56:43 +0000134 return;
Rob Landley3b890392006-05-04 20:56:43 +0000135 endbuf = &buf[len - 1];
Rob Landley3b890392006-05-04 20:56:43 +0000136 while ((endbuf > buf) && isblank(endbuf[-1]))
137 endbuf--;
Rob Landley3b890392006-05-04 20:56:43 +0000138 *endbuf = '\0';
139
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000140 cp = skip_blank(buf);
Rob Landley3b890392006-05-04 20:56:43 +0000141 have1 = FALSE;
142 have2 = FALSE;
143
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000144 if ((curNum == 0) && (lastNum > 0)) {
Rob Landley3b890392006-05-04 20:56:43 +0000145 curNum = 1;
146 curLine = lines.next;
147 }
148
149 if (!getNum(&cp, &have1, &num1))
150 continue;
151
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000152 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000153
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000154 if (*cp == ',') {
Rob Landley3b890392006-05-04 20:56:43 +0000155 cp++;
Rob Landley3b890392006-05-04 20:56:43 +0000156 if (!getNum(&cp, &have2, &num2))
157 continue;
Rob Landley3b890392006-05-04 20:56:43 +0000158 if (!have1)
159 num1 = 1;
Rob Landley3b890392006-05-04 20:56:43 +0000160 if (!have2)
161 num2 = lastNum;
Rob Landley3b890392006-05-04 20:56:43 +0000162 have1 = TRUE;
163 have2 = TRUE;
164 }
Rob Landley3b890392006-05-04 20:56:43 +0000165 if (!have1)
166 num1 = curNum;
Rob Landley3b890392006-05-04 20:56:43 +0000167 if (!have2)
168 num2 = num1;
169
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000170 switch (*cp++) {
Rob Landley3b890392006-05-04 20:56:43 +0000171 case 'a':
172 addLines(num1 + 1);
173 break;
174
175 case 'c':
176 deleteLines(num1, num2);
177 addLines(num1);
178 break;
179
180 case 'd':
181 deleteLines(num1, num2);
182 break;
183
184 case 'f':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000185 if (*cp && !isblank(*cp)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000186 bb_error_msg("bad file command");
Rob Landley3b890392006-05-04 20:56:43 +0000187 break;
188 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000189 cp = skip_blank(cp);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000190 if (*cp == '\0') {
Rob Landley3b890392006-05-04 20:56:43 +0000191 if (fileName)
192 printf("\"%s\"\n", fileName);
193 else
194 printf("No file name\n");
Rob Landley3b890392006-05-04 20:56:43 +0000195 break;
196 }
Rob Landley3b890392006-05-04 20:56:43 +0000197 newname = strdup(cp);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000198 if (newname == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000199 bb_error_msg("no memory for file name");
Rob Landley3b890392006-05-04 20:56:43 +0000200 break;
201 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000202 free(fileName);
Rob Landley3b890392006-05-04 20:56:43 +0000203 fileName = newname;
204 break;
205
206 case 'i':
207 addLines(num1);
208 break;
209
210 case 'k':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000211 cp = skip_blank(cp);
212 if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000213 bb_error_msg("bad mark name");
Rob Landley3b890392006-05-04 20:56:43 +0000214 break;
215 }
Rob Landley3b890392006-05-04 20:56:43 +0000216 marks[*cp - 'a'] = num2;
217 break;
218
219 case 'l':
220 printLines(num1, num2, TRUE);
221 break;
222
223 case 'p':
224 printLines(num1, num2, FALSE);
225 break;
226
227 case 'q':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000228 cp = skip_blank(cp);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000229 if (have1 || *cp) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000230 bb_error_msg("bad quit command");
Rob Landley3b890392006-05-04 20:56:43 +0000231 break;
232 }
Rob Landley3b890392006-05-04 20:56:43 +0000233 if (!dirty)
234 return;
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000235 len = read_line_input("Really quit? ", buf, 16, NULL);
236 /* read error/EOF - no way to continue */
237 if (len < 0)
Rob Landley3b890392006-05-04 20:56:43 +0000238 return;
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000239 cp = skip_blank(buf);
240 if ((*cp | 0x20) == 'y') /* Y or y */
241 return;
Rob Landley3b890392006-05-04 20:56:43 +0000242 break;
243
244 case 'r':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000245 if (*cp && !isblank(*cp)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000246 bb_error_msg("bad read command");
Rob Landley3b890392006-05-04 20:56:43 +0000247 break;
248 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000249 cp = skip_blank(cp);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000250 if (*cp == '\0') {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000251 bb_error_msg("no file name");
Rob Landley3b890392006-05-04 20:56:43 +0000252 break;
253 }
Rob Landley3b890392006-05-04 20:56:43 +0000254 if (!have1)
255 num1 = lastNum;
Rob Landley3b890392006-05-04 20:56:43 +0000256 if (readLines(cp, num1 + 1))
257 break;
Rob Landley3b890392006-05-04 20:56:43 +0000258 if (fileName == NULL)
259 fileName = strdup(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000260 break;
261
262 case 's':
263 subCommand(cp, num1, num2);
264 break;
265
266 case 'w':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000267 if (*cp && !isblank(*cp)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000268 bb_error_msg("bad write command");
Rob Landley3b890392006-05-04 20:56:43 +0000269 break;
270 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000271 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000272 if (!have1) {
273 num1 = 1;
274 num2 = lastNum;
275 }
Rob Landley3b890392006-05-04 20:56:43 +0000276 if (*cp == '\0')
277 cp = fileName;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000278 if (cp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000279 bb_error_msg("no file name specified");
Rob Landley3b890392006-05-04 20:56:43 +0000280 break;
281 }
Rob Landley3b890392006-05-04 20:56:43 +0000282 writeLines(cp, num1, num2);
283 break;
284
285 case 'z':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000286 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000287 case '-':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000288 printLines(curNum - 21, curNum, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000289 break;
290 case '.':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000291 printLines(curNum - 11, curNum + 10, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000292 break;
293 default:
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000294 printLines(curNum, curNum + 21, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000295 break;
296 }
297 break;
298
299 case '.':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000300 if (have1) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000301 bb_error_msg("no arguments allowed");
Rob Landley3b890392006-05-04 20:56:43 +0000302 break;
303 }
Rob Landley3b890392006-05-04 20:56:43 +0000304 printLines(curNum, curNum, FALSE);
305 break;
Bernhard Reutner-Fischerd9ed35c2006-05-19 12:38:30 +0000306
Rob Landley3b890392006-05-04 20:56:43 +0000307 case '-':
308 if (setCurNum(curNum - 1))
309 printLines(curNum, curNum, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000310 break;
311
312 case '=':
313 printf("%d\n", num1);
314 break;
Rob Landley3b890392006-05-04 20:56:43 +0000315 case '\0':
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000316 if (have1) {
Rob Landley3b890392006-05-04 20:56:43 +0000317 printLines(num2, num2, FALSE);
318 break;
319 }
Rob Landley3b890392006-05-04 20:56:43 +0000320 if (setCurNum(curNum + 1))
321 printLines(curNum, curNum, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000322 break;
323
324 default:
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000325 bb_error_msg("unimplemented command");
Rob Landley3b890392006-05-04 20:56:43 +0000326 break;
327 }
328 }
329}
330
331
332/*
333 * Do the substitute command.
334 * The current line is set to the last substitution done.
335 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000336static void subCommand(const char *cmd, int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000337{
338 char *cp, *oldStr, *newStr, buf[USERSIZE];
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000339 int delim, oldLen, newLen, deltaLen, offset;
Rob Landley3b890392006-05-04 20:56:43 +0000340 LINE *lp, *nlp;
341 int globalFlag, printFlag, didSub, needPrint;
342
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000343 if (bad_nums(num1, num2, "substitute"))
Rob Landley3b890392006-05-04 20:56:43 +0000344 return;
Rob Landley3b890392006-05-04 20:56:43 +0000345
346 globalFlag = FALSE;
347 printFlag = FALSE;
348 didSub = FALSE;
349 needPrint = FALSE;
350
351 /*
352 * Copy the command so we can modify it.
353 */
354 strcpy(buf, cmd);
355 cp = buf;
356
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000357 if (isblank(*cp) || (*cp == '\0')) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000358 bb_error_msg("bad delimiter for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000359 return;
360 }
361
362 delim = *cp++;
363 oldStr = cp;
364
365 cp = strchr(cp, delim);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000366 if (cp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000367 bb_error_msg("missing 2nd delimiter for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000368 return;
369 }
370
371 *cp++ = '\0';
372
373 newStr = cp;
374 cp = strchr(cp, delim);
375
376 if (cp)
377 *cp++ = '\0';
378 else
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000379 cp = (char*)"";
Rob Landley3b890392006-05-04 20:56:43 +0000380
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000381 while (*cp) switch (*cp++) {
Rob Landley3b890392006-05-04 20:56:43 +0000382 case 'g':
383 globalFlag = TRUE;
384 break;
Rob Landley3b890392006-05-04 20:56:43 +0000385 case 'p':
386 printFlag = TRUE;
387 break;
Rob Landley3b890392006-05-04 20:56:43 +0000388 default:
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000389 bb_error_msg("unknown option for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000390 return;
391 }
392
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000393 if (*oldStr == '\0') {
394 if (searchString[0] == '\0') {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000395 bb_error_msg("no previous search string");
Rob Landley3b890392006-05-04 20:56:43 +0000396 return;
397 }
Rob Landley3b890392006-05-04 20:56:43 +0000398 oldStr = searchString;
399 }
400
401 if (oldStr != searchString)
402 strcpy(searchString, oldStr);
403
404 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000405 if (lp == NULL)
406 return;
407
408 oldLen = strlen(oldStr);
409 newLen = strlen(newStr);
410 deltaLen = newLen - oldLen;
411 offset = 0;
412 nlp = NULL;
413
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000414 while (num1 <= num2) {
Rob Landley3b890392006-05-04 20:56:43 +0000415 offset = findString(lp, oldStr, oldLen, offset);
416
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000417 if (offset < 0) {
418 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000419 printLines(num1, num1, FALSE);
420 needPrint = FALSE;
421 }
Rob Landley3b890392006-05-04 20:56:43 +0000422 offset = 0;
423 lp = lp->next;
424 num1++;
Rob Landley3b890392006-05-04 20:56:43 +0000425 continue;
426 }
427
428 needPrint = printFlag;
429 didSub = TRUE;
430 dirty = TRUE;
431
432 /*
433 * If the replacement string is the same size or shorter
434 * than the old string, then the substitution is easy.
435 */
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000436 if (deltaLen <= 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000437 memcpy(&lp->data[offset], newStr, newLen);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000438 if (deltaLen) {
Rob Landley3b890392006-05-04 20:56:43 +0000439 memcpy(&lp->data[offset + newLen],
440 &lp->data[offset + oldLen],
441 lp->len - offset - oldLen);
442
443 lp->len += deltaLen;
444 }
Rob Landley3b890392006-05-04 20:56:43 +0000445 offset += newLen;
Rob Landley3b890392006-05-04 20:56:43 +0000446 if (globalFlag)
447 continue;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000448 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000449 printLines(num1, num1, FALSE);
450 needPrint = FALSE;
451 }
Rob Landley3b890392006-05-04 20:56:43 +0000452 lp = lp->next;
453 num1++;
Rob Landley3b890392006-05-04 20:56:43 +0000454 continue;
455 }
456
457 /*
458 * The new string is larger, so allocate a new line
459 * structure and use that. Link it in in place of
460 * the old line structure.
461 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000462 nlp = malloc(sizeof(LINE) + lp->len + deltaLen);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000463 if (nlp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000464 bb_error_msg("cannot get memory for line");
Rob Landley3b890392006-05-04 20:56:43 +0000465 return;
466 }
467
468 nlp->len = lp->len + deltaLen;
469
470 memcpy(nlp->data, lp->data, offset);
Rob Landley3b890392006-05-04 20:56:43 +0000471 memcpy(&nlp->data[offset], newStr, newLen);
Rob Landley3b890392006-05-04 20:56:43 +0000472 memcpy(&nlp->data[offset + newLen],
473 &lp->data[offset + oldLen],
474 lp->len - offset - oldLen);
475
476 nlp->next = lp->next;
477 nlp->prev = lp->prev;
478 nlp->prev->next = nlp;
479 nlp->next->prev = nlp;
480
481 if (curLine == lp)
482 curLine = nlp;
483
484 free(lp);
485 lp = nlp;
486
487 offset += newLen;
488
489 if (globalFlag)
490 continue;
491
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000492 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000493 printLines(num1, num1, FALSE);
494 needPrint = FALSE;
495 }
496
497 lp = lp->next;
498 num1++;
499 }
500
501 if (!didSub)
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000502 bb_error_msg("no substitutions found for \"%s\"", oldStr);
Rob Landley3b890392006-05-04 20:56:43 +0000503}
504
505
506/*
507 * Search a line for the specified string starting at the specified
508 * offset in the line. Returns the offset of the found string, or -1.
509 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000510static int findString(const LINE *lp, const char *str, int len, int offset)
Rob Landley3b890392006-05-04 20:56:43 +0000511{
512 int left;
513 const char *cp, *ncp;
514
515 cp = &lp->data[offset];
516 left = lp->len - offset;
517
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000518 while (left >= len) {
Rob Landley3b890392006-05-04 20:56:43 +0000519 ncp = memchr(cp, *str, left);
Rob Landley3b890392006-05-04 20:56:43 +0000520 if (ncp == NULL)
521 return -1;
Rob Landley3b890392006-05-04 20:56:43 +0000522 left -= (ncp - cp);
Rob Landley3b890392006-05-04 20:56:43 +0000523 if (left < len)
524 return -1;
Rob Landley3b890392006-05-04 20:56:43 +0000525 cp = ncp;
Rob Landley3b890392006-05-04 20:56:43 +0000526 if (memcmp(cp, str, len) == 0)
527 return (cp - lp->data);
Rob Landley3b890392006-05-04 20:56:43 +0000528 cp++;
529 left--;
530 }
531
532 return -1;
533}
534
535
536/*
537 * Add lines which are typed in by the user.
538 * The lines are inserted just before the specified line number.
539 * The lines are terminated by a line containing a single dot (ugly!),
540 * or by an end of file.
541 */
542static void addLines(int num)
543{
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000544 int len;
545 char buf[USERSIZE + 1];
Rob Landley3b890392006-05-04 20:56:43 +0000546
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000547 while (1) {
548 /* Returns:
549 * -1 on read errors or EOF, or on bare Ctrl-D.
550 * 0 on ctrl-C,
551 * >0 length of input string, including terminating '\n'
552 */
553 len = read_line_input("", buf, sizeof(buf), NULL);
554 if (len <= 0) {
555 /* Previously, ctrl-C was exiting to shell.
556 * Now we exit to ed prompt. Is in important? */
Rob Landley3b890392006-05-04 20:56:43 +0000557 return;
558 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000559 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
560 return;
Rob Landley3b890392006-05-04 20:56:43 +0000561 if (!insertLine(num++, buf, len))
562 return;
563 }
564}
565
566
567/*
568 * Parse a line number argument if it is present. This is a sum
569 * or difference of numbers, '.', '$', 'x, or a search string.
Bernhard Reutner-Fischerd9ed35c2006-05-19 12:38:30 +0000570 * Returns TRUE if successful (whether or not there was a number).
Rob Landley3b890392006-05-04 20:56:43 +0000571 * Returns FALSE if there was a parsing error, with a message output.
572 * Whether there was a number is returned indirectly, as is the number.
573 * The character pointer which stopped the scan is also returned.
574 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000575static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
Rob Landley3b890392006-05-04 20:56:43 +0000576{
577 const char *cp;
578 char *endStr, str[USERSIZE];
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000579 int value, num;
580 smallint haveNum, minus;
Rob Landley3b890392006-05-04 20:56:43 +0000581
582 cp = *retcp;
Rob Landley3b890392006-05-04 20:56:43 +0000583 value = 0;
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000584 haveNum = FALSE;
585 minus = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000586
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000587 while (TRUE) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000588 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000589
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000590 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000591 case '.':
592 haveNum = TRUE;
593 num = curNum;
594 cp++;
595 break;
596
597 case '$':
598 haveNum = TRUE;
599 num = lastNum;
600 cp++;
601 break;
602
603 case '\'':
604 cp++;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000605 if ((*cp < 'a') || (*cp > 'z')) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000606 bb_error_msg("bad mark name");
Rob Landley3b890392006-05-04 20:56:43 +0000607 return FALSE;
608 }
Rob Landley3b890392006-05-04 20:56:43 +0000609 haveNum = TRUE;
610 num = marks[*cp++ - 'a'];
611 break;
612
613 case '/':
614 strcpy(str, ++cp);
615 endStr = strchr(str, '/');
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000616 if (endStr) {
Rob Landley3b890392006-05-04 20:56:43 +0000617 *endStr++ = '\0';
618 cp += (endStr - str);
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000619 } else
Rob Landley3b890392006-05-04 20:56:43 +0000620 cp = "";
Rob Landley3b890392006-05-04 20:56:43 +0000621 num = searchLines(str, curNum, lastNum);
Rob Landley3b890392006-05-04 20:56:43 +0000622 if (num == 0)
623 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000624 haveNum = TRUE;
625 break;
626
627 default:
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000628 if (!isdigit(*cp)) {
Rob Landley3b890392006-05-04 20:56:43 +0000629 *retcp = cp;
630 *retHaveNum = haveNum;
631 *retNum = value;
Rob Landley3b890392006-05-04 20:56:43 +0000632 return TRUE;
633 }
Rob Landley3b890392006-05-04 20:56:43 +0000634 num = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000635 while (isdigit(*cp))
636 num = num * 10 + *cp++ - '0';
Rob Landley3b890392006-05-04 20:56:43 +0000637 haveNum = TRUE;
638 break;
639 }
640
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000641 value += (minus ? -num : num);
Rob Landley3b890392006-05-04 20:56:43 +0000642
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000643 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000644
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000645 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000646 case '-':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000647 minus = 1;
Rob Landley3b890392006-05-04 20:56:43 +0000648 cp++;
649 break;
650
651 case '+':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000652 minus = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000653 cp++;
654 break;
655
656 default:
657 *retcp = cp;
658 *retHaveNum = haveNum;
659 *retNum = value;
Rob Landley3b890392006-05-04 20:56:43 +0000660 return TRUE;
661 }
662 }
663}
664
665
666/*
Rob Landley3b890392006-05-04 20:56:43 +0000667 * Read lines from a file at the specified line number.
668 * Returns TRUE if the file was successfully read.
669 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000670static int readLines(const char *file, int num)
Rob Landley3b890392006-05-04 20:56:43 +0000671{
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000672 int fd, cc;
Rob Landley3b890392006-05-04 20:56:43 +0000673 int len, lineCount, charCount;
674 char *cp;
675
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000676 if ((num < 1) || (num > lastNum + 1)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000677 bb_error_msg("bad line for read");
Rob Landley3b890392006-05-04 20:56:43 +0000678 return FALSE;
679 }
680
681 fd = open(file, 0);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000682 if (fd < 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000683 perror(file);
Rob Landley3b890392006-05-04 20:56:43 +0000684 return FALSE;
685 }
686
687 bufPtr = bufBase;
688 bufUsed = 0;
689 lineCount = 0;
690 charCount = 0;
691 cc = 0;
692
693 printf("\"%s\", ", file);
694 fflush(stdout);
695
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000696 do {
Rob Landley3b890392006-05-04 20:56:43 +0000697 cp = memchr(bufPtr, '\n', bufUsed);
698
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000699 if (cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000700 len = (cp - bufPtr) + 1;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000701 if (!insertLine(num, bufPtr, len)) {
Rob Landley3b890392006-05-04 20:56:43 +0000702 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000703 return FALSE;
704 }
Rob Landley3b890392006-05-04 20:56:43 +0000705 bufPtr += len;
706 bufUsed -= len;
707 charCount += len;
708 lineCount++;
709 num++;
Rob Landley3b890392006-05-04 20:56:43 +0000710 continue;
711 }
712
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000713 if (bufPtr != bufBase) {
Rob Landley3b890392006-05-04 20:56:43 +0000714 memcpy(bufBase, bufPtr, bufUsed);
715 bufPtr = bufBase + bufUsed;
716 }
717
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000718 if (bufUsed >= bufSize) {
Rob Landley3b890392006-05-04 20:56:43 +0000719 len = (bufSize * 3) / 2;
720 cp = realloc(bufBase, len);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000721 if (cp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000722 bb_error_msg("no memory for buffer");
Rob Landley3b890392006-05-04 20:56:43 +0000723 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000724 return FALSE;
725 }
Rob Landley3b890392006-05-04 20:56:43 +0000726 bufBase = cp;
727 bufPtr = bufBase + bufUsed;
728 bufSize = len;
729 }
730
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000731 cc = safe_read(fd, bufPtr, bufSize - bufUsed);
Rob Landley3b890392006-05-04 20:56:43 +0000732 bufUsed += cc;
733 bufPtr = bufBase;
734
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000735 } while (cc > 0);
Rob Landley3b890392006-05-04 20:56:43 +0000736
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000737 if (cc < 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000738 perror(file);
739 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000740 return FALSE;
741 }
742
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000743 if (bufUsed) {
744 if (!insertLine(num, bufPtr, bufUsed)) {
Rob Landley3b890392006-05-04 20:56:43 +0000745 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000746 return -1;
747 }
Rob Landley3b890392006-05-04 20:56:43 +0000748 lineCount++;
749 charCount += bufUsed;
750 }
751
752 close(fd);
753
754 printf("%d lines%s, %d chars\n", lineCount,
755 (bufUsed ? " (incomplete)" : ""), charCount);
756
757 return TRUE;
758}
759
760
761/*
762 * Write the specified lines out to the specified file.
763 * Returns TRUE if successful, or FALSE on an error with a message output.
764 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000765static int writeLines(const char *file, int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000766{
767 LINE *lp;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000768 int fd, lineCount, charCount;
Rob Landley3b890392006-05-04 20:56:43 +0000769
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000770 if (bad_nums(num1, num2, "write"))
Rob Landley3b890392006-05-04 20:56:43 +0000771 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000772
773 lineCount = 0;
774 charCount = 0;
775
776 fd = creat(file, 0666);
Rob Landley3b890392006-05-04 20:56:43 +0000777 if (fd < 0) {
778 perror(file);
Rob Landley3b890392006-05-04 20:56:43 +0000779 return FALSE;
780 }
781
782 printf("\"%s\", ", file);
783 fflush(stdout);
784
785 lp = findLine(num1);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000786 if (lp == NULL) {
Rob Landley3b890392006-05-04 20:56:43 +0000787 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000788 return FALSE;
789 }
790
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000791 while (num1++ <= num2) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000792 if (full_write(fd, lp->data, lp->len) != lp->len) {
Rob Landley3b890392006-05-04 20:56:43 +0000793 perror(file);
794 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000795 return FALSE;
796 }
Rob Landley3b890392006-05-04 20:56:43 +0000797 charCount += lp->len;
798 lineCount++;
799 lp = lp->next;
800 }
801
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000802 if (close(fd) < 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000803 perror(file);
Rob Landley3b890392006-05-04 20:56:43 +0000804 return FALSE;
805 }
806
807 printf("%d lines, %d chars\n", lineCount, charCount);
Rob Landley3b890392006-05-04 20:56:43 +0000808 return TRUE;
809}
810
811
812/*
813 * Print lines in a specified range.
814 * The last line printed becomes the current line.
815 * If expandFlag is TRUE, then the line is printed specially to
816 * show magic characters.
817 */
818static int printLines(int num1, int num2, int expandFlag)
819{
820 const LINE *lp;
821 const char *cp;
822 int ch, count;
823
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000824 if (bad_nums(num1, num2, "print"))
Rob Landley3b890392006-05-04 20:56:43 +0000825 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000826
827 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000828 if (lp == NULL)
829 return FALSE;
830
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000831 while (num1 <= num2) {
832 if (!expandFlag) {
Rob Landley3b890392006-05-04 20:56:43 +0000833 write(1, lp->data, lp->len);
834 setCurNum(num1++);
835 lp = lp->next;
Rob Landley3b890392006-05-04 20:56:43 +0000836 continue;
837 }
838
839 /*
840 * Show control characters and characters with the
841 * high bit set specially.
842 */
843 cp = lp->data;
844 count = lp->len;
845
846 if ((count > 0) && (cp[count - 1] == '\n'))
847 count--;
848
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000849 while (count-- > 0) {
Denis Vlasenkod3c042f2007-12-30 01:59:53 +0000850 ch = (unsigned char) *cp++;
851 fputc_printable(ch | PRINTABLE_META, stdout);
Rob Landley3b890392006-05-04 20:56:43 +0000852 }
853
854 fputs("$\n", stdout);
855
856 setCurNum(num1++);
857 lp = lp->next;
858 }
859
860 return TRUE;
861}
862
863
864/*
865 * Insert a new line with the specified text.
866 * The line is inserted so as to become the specified line,
867 * thus pushing any existing and further lines down one.
868 * The inserted line is also set to become the current line.
869 * Returns TRUE if successful.
870 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000871static int insertLine(int num, const char *data, int len)
Rob Landley3b890392006-05-04 20:56:43 +0000872{
873 LINE *newLp, *lp;
874
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000875 if ((num < 1) || (num > lastNum + 1)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000876 bb_error_msg("inserting at bad line number");
Rob Landley3b890392006-05-04 20:56:43 +0000877 return FALSE;
878 }
879
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000880 newLp = malloc(sizeof(LINE) + len - 1);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000881 if (newLp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000882 bb_error_msg("failed to allocate memory for line");
Rob Landley3b890392006-05-04 20:56:43 +0000883 return FALSE;
884 }
885
886 memcpy(newLp->data, data, len);
887 newLp->len = len;
888
889 if (num > lastNum)
890 lp = &lines;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000891 else {
Rob Landley3b890392006-05-04 20:56:43 +0000892 lp = findLine(num);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000893 if (lp == NULL) {
Rob Landley3b890392006-05-04 20:56:43 +0000894 free((char *) newLp);
Rob Landley3b890392006-05-04 20:56:43 +0000895 return FALSE;
896 }
897 }
898
899 newLp->next = lp;
900 newLp->prev = lp->prev;
901 lp->prev->next = newLp;
902 lp->prev = newLp;
903
904 lastNum++;
905 dirty = TRUE;
Rob Landley3b890392006-05-04 20:56:43 +0000906 return setCurNum(num);
907}
908
909
910/*
911 * Delete lines from the given range.
912 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000913static void deleteLines(int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000914{
915 LINE *lp, *nlp, *plp;
916 int count;
917
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000918 if (bad_nums(num1, num2, "delete"))
919 return;
Rob Landley3b890392006-05-04 20:56:43 +0000920
921 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000922 if (lp == NULL)
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000923 return;
Rob Landley3b890392006-05-04 20:56:43 +0000924
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000925 if ((curNum >= num1) && (curNum <= num2)) {
Rob Landley3b890392006-05-04 20:56:43 +0000926 if (num2 < lastNum)
927 setCurNum(num2 + 1);
928 else if (num1 > 1)
929 setCurNum(num1 - 1);
930 else
931 curNum = 0;
932 }
933
934 count = num2 - num1 + 1;
Rob Landley3b890392006-05-04 20:56:43 +0000935 if (curNum > num2)
936 curNum -= count;
Rob Landley3b890392006-05-04 20:56:43 +0000937 lastNum -= count;
938
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000939 while (count-- > 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000940 nlp = lp->next;
941 plp = lp->prev;
942 plp->next = nlp;
943 nlp->prev = plp;
Rob Landley3b890392006-05-04 20:56:43 +0000944 free(lp);
945 lp = nlp;
946 }
947
948 dirty = TRUE;
Rob Landley3b890392006-05-04 20:56:43 +0000949}
950
951
952/*
953 * Search for a line which contains the specified string.
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000954 * If the string is "", then the previously searched for string
Rob Landley3b890392006-05-04 20:56:43 +0000955 * is used. The currently searched for string is saved for future use.
956 * Returns the line number which matches, or 0 if there was no match
957 * with an error printed.
958 */
959static int searchLines(const char *str, int num1, int num2)
960{
961 const LINE *lp;
962 int len;
963
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000964 if (bad_nums(num1, num2, "search"))
Rob Landley3b890392006-05-04 20:56:43 +0000965 return 0;
Rob Landley3b890392006-05-04 20:56:43 +0000966
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000967 if (*str == '\0') {
968 if (searchString[0] == '\0') {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000969 bb_error_msg("no previous search string");
Rob Landley3b890392006-05-04 20:56:43 +0000970 return 0;
971 }
Rob Landley3b890392006-05-04 20:56:43 +0000972 str = searchString;
973 }
974
975 if (str != searchString)
976 strcpy(searchString, str);
977
978 len = strlen(str);
979
980 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000981 if (lp == NULL)
982 return 0;
983
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000984 while (num1 <= num2) {
Rob Landley3b890392006-05-04 20:56:43 +0000985 if (findString(lp, str, len, 0) >= 0)
986 return num1;
Rob Landley3b890392006-05-04 20:56:43 +0000987 num1++;
988 lp = lp->next;
989 }
990
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000991 bb_error_msg("cannot find string \"%s\"", str);
Rob Landley3b890392006-05-04 20:56:43 +0000992 return 0;
993}
994
995
996/*
997 * Return a pointer to the specified line number.
998 */
999static LINE *findLine(int num)
1000{
1001 LINE *lp;
1002 int lnum;
1003
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001004 if ((num < 1) || (num > lastNum)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +00001005 bb_error_msg("line number %d does not exist", num);
Rob Landley3b890392006-05-04 20:56:43 +00001006 return NULL;
1007 }
1008
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001009 if (curNum <= 0) {
Rob Landley3b890392006-05-04 20:56:43 +00001010 curNum = 1;
1011 curLine = lines.next;
1012 }
1013
1014 if (num == curNum)
1015 return curLine;
1016
1017 lp = curLine;
1018 lnum = curNum;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001019 if (num < (curNum / 2)) {
Rob Landley3b890392006-05-04 20:56:43 +00001020 lp = lines.next;
1021 lnum = 1;
Denis Vlasenkod9391b12007-09-25 11:55:57 +00001022 } else if (num > ((curNum + lastNum) / 2)) {
Rob Landley3b890392006-05-04 20:56:43 +00001023 lp = lines.prev;
1024 lnum = lastNum;
1025 }
1026
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001027 while (lnum < num) {
Rob Landley3b890392006-05-04 20:56:43 +00001028 lp = lp->next;
1029 lnum++;
1030 }
1031
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001032 while (lnum > num) {
Rob Landley3b890392006-05-04 20:56:43 +00001033 lp = lp->prev;
1034 lnum--;
1035 }
Rob Landley3b890392006-05-04 20:56:43 +00001036 return lp;
1037}
1038
1039
1040/*
1041 * Set the current line number.
1042 * Returns TRUE if successful.
1043 */
1044static int setCurNum(int num)
1045{
1046 LINE *lp;
1047
1048 lp = findLine(num);
Rob Landley3b890392006-05-04 20:56:43 +00001049 if (lp == NULL)
1050 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +00001051 curNum = num;
1052 curLine = lp;
Rob Landley3b890392006-05-04 20:56:43 +00001053 return TRUE;
1054}