"src/cut.c" did not exist on "0f4a7dc53ab5cab9f6c75083f719b1d794ab428a"
cut.c 13.1 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   cut.c                                                                *
 *                                                                        *
5
 *   Copyright (C) 1999-2003 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
26
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Chris Allegretta's avatar
Chris Allegretta committed
27
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
28
29
30
#include "proto.h"
#include "nano.h"

Chris Allegretta's avatar
Chris Allegretta committed
31
static int marked_cut;		/* Is the cutbuffer from a mark? */
Chris Allegretta's avatar
Chris Allegretta committed
32
33
34
35
36
37

#ifndef NANO_SMALL
static int concatenate_cut;	/* Should we add this cut string to the
				   end of the last one? */
#endif

38
39
static filestruct *cutbottom = NULL;
				/* Pointer to end of cutbuffer */
Chris Allegretta's avatar
Chris Allegretta committed
40

Chris Allegretta's avatar
Chris Allegretta committed
41
42
43
44
45
46
filestruct *get_cutbottom(void)
{
    return cutbottom;
}

void add_to_cutbuffer(filestruct *inptr)
Chris Allegretta's avatar
Chris Allegretta committed
47
48
{
#ifdef DEBUG
49
    fprintf(stderr, "add_to_cutbuffer() called with inptr->data = %s\n",
Chris Allegretta's avatar
Chris Allegretta committed
50
51
52
53
54
55
	    inptr->data);
#endif

    if (cutbuffer == NULL) {
	cutbuffer = inptr;
	inptr->prev = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
56
#ifndef NANO_SMALL
57
    } else if (concatenate_cut && !ISSET(JUSTIFY_MODE)) {
Chris Allegretta's avatar
Chris Allegretta committed
58
59
60
61
62
63
64
	/* Just tack the text in inptr onto the text in cutbottom,
	   unless we're backing up lines while justifying text. */
	cutbottom->data = charealloc(cutbottom->data,
		strlen(cutbottom->data) + strlen(inptr->data) + 1);
	strcat(cutbottom->data, inptr->data);
	return;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
65
66
67
68
69
70
71
72
73
74
    } else {
	cutbottom->next = inptr;
	inptr->prev = cutbottom;
    }

    inptr->next = NULL;
    cutbottom = inptr;
}

#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
75
/* Cut a marked segment instead of a whole line.
76
 *
Chris Allegretta's avatar
Chris Allegretta committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 * The first cut character is top->data[top_x].  Unless top == bot, the
 * last cut line has length bot_x.  That is, if bot_x > 0 then we cut to
 * bot->data[bot_x - 1].
 *
 * destructive is whether to actually modify the file structure, if not
 * then just copy the buffer into cutbuffer and don't pull it from the
 * file.
 *
 * If destructive, then we maintain totsize, totlines, filebot, the
 * magic line, and line numbers.  Also, we set current and current_x so
 * the cursor will be on the first character after what was cut.  We do
 * not do any screen updates. */
void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
			size_t bot_x, int destructive)
Chris Allegretta's avatar
Chris Allegretta committed
91
{
Chris Allegretta's avatar
Chris Allegretta committed
92
93
    filestruct *tmp, *next;
    size_t newsize;
94

Chris Allegretta's avatar
Chris Allegretta committed
95
96
97
    if (top == bot && top_x == bot_x)
	return;
    assert(top != NULL && bot != NULL);
98

Chris Allegretta's avatar
Chris Allegretta committed
99
100
101
102
    /* Make top be no later than bot. */
    if (top->lineno > bot->lineno) {
	filestruct *swap = top;
	int swap2 = top_x;
103

Chris Allegretta's avatar
Chris Allegretta committed
104
105
	top = bot;
	bot = swap;
106

Chris Allegretta's avatar
Chris Allegretta committed
107
108
109
110
111
	top_x = bot_x;
	bot_x = swap2;
    } else if (top == bot && top_x > bot_x) {
	/* And bot_x can't be an earlier character than top_x. */
	int swap = top_x;
112

Chris Allegretta's avatar
Chris Allegretta committed
113
114
	top_x = bot_x;
	bot_x = swap;
115
    }
Chris Allegretta's avatar
Chris Allegretta committed
116

Chris Allegretta's avatar
Chris Allegretta committed
117
    /* Make the first cut line manually. */
Chris Allegretta's avatar
Chris Allegretta committed
118
    tmp = copy_node(top);
Chris Allegretta's avatar
Chris Allegretta committed
119
120
121
122
    newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x));
    memmove(tmp->data, top->data + top_x, newsize);
    null_at(&tmp->data, newsize);
    add_to_cutbuffer(tmp);
123

Chris Allegretta's avatar
Chris Allegretta committed
124
    /* And make the remainder line manually too. */
125
    if (destructive) {
Chris Allegretta's avatar
Chris Allegretta committed
126
127
128
129
130
131
132
133
134
135
	current_x = top_x;
	totsize -= newsize;
	totlines -= bot->lineno - top->lineno;

	newsize = top_x + strlen(bot->data + bot_x) + 1;
	if (top == bot) {
	    /* In this case, the remainder line is shorter, so we must
	       move text from the end forward first. */
	    memmove(top->data + top_x, bot->data + bot_x,
			newsize - top_x);
Chris Allegretta's avatar
Chris Allegretta committed
136
	    top->data = charealloc(top->data, newsize);
Chris Allegretta's avatar
Chris Allegretta committed
137
	} else {
Chris Allegretta's avatar
Chris Allegretta committed
138
	    totsize -= bot_x + 1;
Chris Allegretta's avatar
Chris Allegretta committed
139

140
141
	    /* Here, the remainder line might get longer, so we
	       realloc() it first. */
Chris Allegretta's avatar
Chris Allegretta committed
142
	    top->data = charealloc(top->data, newsize);
Chris Allegretta's avatar
Chris Allegretta committed
143
144
	    memmove(top->data + top_x, bot->data + bot_x,
			newsize - top_x);
145
	}
Chris Allegretta's avatar
Chris Allegretta committed
146
147
    }

Chris Allegretta's avatar
Chris Allegretta committed
148
149
150
151
    if (top == bot) {
#ifdef DEBUG
	dump_buffer(cutbuffer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
152
	return;
153
    }
154

Chris Allegretta's avatar
Chris Allegretta committed
155
156
157
158
159
160
    tmp = top->next;
    while (tmp != bot) {
	next = tmp->next;
	if (!destructive)
	    tmp = copy_node(tmp);
	else
Chris Allegretta's avatar
Chris Allegretta committed
161
	    totsize -= strlen(tmp->data) + 1;
Chris Allegretta's avatar
Chris Allegretta committed
162
163
164
	add_to_cutbuffer(tmp);
	tmp = next;
    }
165

Chris Allegretta's avatar
Chris Allegretta committed
166
167
168
169
170
171
172
    /* Make the last cut line manually. */
    tmp = copy_node(bot);
    null_at(&tmp->data, bot_x);
    add_to_cutbuffer(tmp);
#ifdef DEBUG
    dump_buffer(cutbuffer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
173

174
    if (destructive) {
Chris Allegretta's avatar
Chris Allegretta committed
175
176
177
178
	top->next = bot->next;
	if (top->next != NULL)
	    top->next->prev = top;
	delete_node(bot);
179
180
181
	renumber(top);
	current = top;
	if (bot == filebot) {
Chris Allegretta's avatar
Chris Allegretta committed
182
183
184
185
	    filebot = top;
	    assert(bot_x == 0);
	    if (top_x > 0)
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
186
187
	}
    }
Chris Allegretta's avatar
Chris Allegretta committed
188
189
190
#ifdef DEBUG
    dump_buffer(cutbuffer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
191
192
193
194
195
}
#endif

int do_cut_text(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
196
    filestruct *fileptr;
Chris Allegretta's avatar
Chris Allegretta committed
197
#ifndef NANO_SMALL
198
    int dontupdate = 0;
Chris Allegretta's avatar
Chris Allegretta committed
199
200
#endif

Chris Allegretta's avatar
Chris Allegretta committed
201
    assert(current != NULL && current->data != NULL);
202

203
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
204

205
    if (!ISSET(KEEP_CUTBUFFER)) {
Chris Allegretta's avatar
Chris Allegretta committed
206
207
	free_filestruct(cutbuffer);
	cutbuffer = NULL;
208
	marked_cut = 0;
Chris Allegretta's avatar
Chris Allegretta committed
209
210
211
#ifndef NANO_SMALL
	concatenate_cut = 0;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
212
#ifdef DEBUG
213
	fprintf(stderr, "Blew away cutbuffer =)\n");
Chris Allegretta's avatar
Chris Allegretta committed
214
215
#endif
    }
216

Chris Allegretta's avatar
Chris Allegretta committed
217
218
219
220
221
222
223
    /* You can't cut the magic line except with the mark.  But
       trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */
    if (current == filebot
#ifndef NANO_SMALL
			&& !ISSET(MARK_ISSET)
#endif
						)
224
225
	return 0;

Chris Allegretta's avatar
Chris Allegretta committed
226
227
    SET(KEEP_CUTBUFFER);

Chris Allegretta's avatar
Chris Allegretta committed
228
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
229
    if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) {
Chris Allegretta's avatar
Chris Allegretta committed
230
	assert(current_x >= 0 && current_x <= strlen(current->data));
231

Chris Allegretta's avatar
Chris Allegretta committed
232
233
234
	if (current->data[current_x] == '\0') {
	    /* If the line is empty and we didn't just cut a non-blank
	       line, create a dummy line and add it to the cutbuffer */
235
	    if (marked_cut != 1 && current->next != filebot) {
Chris Allegretta's avatar
Chris Allegretta committed
236
		filestruct *junk = make_new_node(current);
237

238
	        junk->data = charalloc(1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
239
		junk->data[0] = '\0';
240
		add_to_cutbuffer(junk);
Chris Allegretta's avatar
Chris Allegretta committed
241
#ifdef DEBUG
242
		dump_buffer(cutbuffer);
Chris Allegretta's avatar
Chris Allegretta committed
243
#endif
244
	    }
245

246
	    do_delete();
Chris Allegretta's avatar
Chris Allegretta committed
247
	    marked_cut = 2;
248
	    return 1;
249
	} else {
250
251
252
253
	    SET(MARK_ISSET);

	    mark_beginx = strlen(current->data);
	    mark_beginbuf = current;
Chris Allegretta's avatar
Chris Allegretta committed
254
	    dontupdate = 1;
255
	}
256
    }
257

Chris Allegretta's avatar
Chris Allegretta committed
258
    if (ISSET(MARK_ISSET)) {
Chris Allegretta's avatar
Chris Allegretta committed
259
260
261
262
263
264
265
266
	/* Don't do_update() and move the screen position if the marked
	   area lies entirely within the screen buffer */
	dontupdate |= current->lineno >= edittop->lineno &&
			current->lineno <= editbot->lineno &&
			mark_beginbuf->lineno >= edittop->lineno &&
			mark_beginbuf->lineno <= editbot->lineno;
	cut_marked_segment(current, current_x, mark_beginbuf,
				mark_beginx, 1);
Chris Allegretta's avatar
Chris Allegretta committed
267
268
269

	placewewant = xplustabs();
	UNSET(MARK_ISSET);
270

Chris Allegretta's avatar
Chris Allegretta committed
271
272
273
274
275
	/* If we just did a marked cut of part of a line, we should add
	   the first line of any cut done immediately afterward to the
	   end of this cut, as Pico does. */
	if (current == mark_beginbuf && current_x < strlen(current->data))
	    concatenate_cut = 1;
Chris Allegretta's avatar
Chris Allegretta committed
276
	marked_cut = 1;
277
	if (dontupdate)
278
	    edit_refresh();
279
	else
280
	    edit_update(current, CENTER);
281
	set_modified();
Chris Allegretta's avatar
Chris Allegretta committed
282

Chris Allegretta's avatar
Chris Allegretta committed
283
284
	return 1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
285
286
287
288
289
290
291
292
293
294
295
#endif /* !NANO_SMALL */

    totlines--;
    totsize -= strlen(current->data) + 1;
    fileptr = current;
    current = current->next;
    current->prev = fileptr->prev;
    add_to_cutbuffer(fileptr);
#ifdef DEBUG
    dump_buffer(cutbuffer);
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
296

Chris Allegretta's avatar
Chris Allegretta committed
297
298
299
300
    if (fileptr == fileage)
	fileage = current;
    else
	current->prev->next = current;
Chris Allegretta's avatar
Chris Allegretta committed
301
302
303
304

    if (fileptr == edittop)
	edittop = current;

Chris Allegretta's avatar
Chris Allegretta committed
305
    renumber(current);
306
    current_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
307
308
309
    edit_refresh();
    set_modified();
    marked_cut = 0;
Chris Allegretta's avatar
Chris Allegretta committed
310
311
312
#ifndef NANO_SMALL
    concatenate_cut = 0;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
313
314
315
316
317
318
    placewewant = 0;
    return 1;
}

int do_uncut_text(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
319
    filestruct *tmp = current, *fileptr = current;
320
    filestruct *newbuf = NULL, *newend = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
321
    char *tmpstr, *tmpstr2;
322
    filestruct *hold = current;
Chris Allegretta's avatar
Chris Allegretta committed
323
324
    int i;

325
#ifndef DISABLE_WRAPPING
Chris Allegretta's avatar
Chris Allegretta committed
326
    wrap_reset();
327
#endif
328
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
329
330
331
    if (cutbuffer == NULL || fileptr == NULL)
	return 0;		/* AIEEEEEEEEEEEE */

Chris Allegretta's avatar
Chris Allegretta committed
332
333
334
335
336
337
338
339
340
341
342
    /* If we're uncutting a previously non-marked block, uncut to end if
       we're not at the beginning of the line.  If we are at the
       beginning of the line, set placewewant to 0.  Pico does both of
       these. */
    if (marked_cut == 0) {
	if (current_x != 0)
	    marked_cut = 2;
	else
	    placewewant = 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
343
344
345
346
347
    /* If we're going to uncut on the magicline, always make a new
       magicline in advance. */
    if (current->next == NULL)
	new_magicline();

348
    if (marked_cut == 0 || cutbuffer->next != NULL)
Chris Allegretta's avatar
Chris Allegretta committed
349
350
351
352
353
    {
	newbuf = copy_filestruct(cutbuffer);
	for (newend = newbuf; newend->next != NULL && newend != NULL;
		newend = newend->next)
	    totlines++;
Chris Allegretta's avatar
Chris Allegretta committed
354
355
356
    }

    /* Hook newbuf into fileptr */
357
    if (marked_cut != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
358
359
360
	int recenter_me = 0;
	    /* Should we eventually use edit_update(CENTER)? */

Chris Allegretta's avatar
Chris Allegretta committed
361
362
	/* If there's only one line in the cutbuffer */
	if (cutbuffer->next == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
363
364
365
	    size_t buf_len = strlen(cutbuffer->data);
	    size_t cur_len = strlen(current->data);

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
366
	    current->data = charealloc(current->data, cur_len + buf_len + 1);
Chris Allegretta's avatar
Chris Allegretta committed
367
368
369
	    memmove(current->data + current_x + buf_len,
			current->data + current_x, cur_len - current_x + 1);
	    strncpy(current->data + current_x, cutbuffer->data, buf_len);
370
		/* Use strncpy() to not copy the terminal '\0'. */
Chris Allegretta's avatar
Chris Allegretta committed
371
372
373

	    current_x += buf_len;
	    totsize += buf_len;
Chris Allegretta's avatar
Chris Allegretta committed
374
375
376
377
378
379

	    placewewant = xplustabs();
	    update_cursor();
	} else {		/* yuck -- no kidding! */
	    tmp = current->next;
	    /* New beginning */
380
	    tmpstr = charalloc(current_x + strlen(newbuf->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
381
382
383
384
385
	    strncpy(tmpstr, current->data, current_x);
	    strcpy(&tmpstr[current_x], newbuf->data);
	    totsize += strlen(newbuf->data) + strlen(newend->data) + 1;

	    /* New end */
386
	    tmpstr2 = charalloc(strlen(newend->data) +
Chris Allegretta's avatar
Chris Allegretta committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
			      strlen(&current->data[current_x]) + 1);
	    strcpy(tmpstr2, newend->data);
	    strcat(tmpstr2, &current->data[current_x]);

	    free(current->data);
	    current->data = tmpstr;
	    current->next = newbuf->next;
	    newbuf->next->prev = current;
	    delete_node(newbuf);

	    current_x = strlen(newend->data);
	    placewewant = xplustabs();
	    free(newend->data);
	    newend->data = tmpstr2;

	    newend->next = tmp;

	    /* If tmp isn't null, we're in the middle: update the
Chris Allegretta's avatar
Chris Allegretta committed
405
406
	       prev pointer.  If it IS null, we're at the end; update
	       the filebot pointer */
Chris Allegretta's avatar
Chris Allegretta committed
407
408
409

	    if (tmp != NULL)
		tmp->prev = newend;
Chris Allegretta's avatar
Chris Allegretta committed
410
411
412
413
	    else {
		/* Fix the editbot pointer too */
		if (editbot == filebot)
		    editbot = newend;
Chris Allegretta's avatar
Chris Allegretta committed
414
		filebot = newend;
415
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
416
	    }
Chris Allegretta's avatar
Chris Allegretta committed
417
418
419
420
421
422

	    /* Now why don't we update the totsize also */
	    for (tmp = current->next; tmp != newend; tmp = tmp->next)
		totsize += strlen(tmp->data) + 1;

	    current = newend;
Chris Allegretta's avatar
Chris Allegretta committed
423
424
	    if (editbot->lineno < newend->lineno)
		recenter_me = 1;
Chris Allegretta's avatar
Chris Allegretta committed
425
426
	}

427
428
	/* If marked cut == 2, that means that we're doing a cut to end
	   and we don't want anything else on the line, so we have to
Chris Allegretta's avatar
Chris Allegretta committed
429
430
431
	   screw up all the work we just did and separate the line.
	   There must be a better way to do this, but not at 1AM on a
	   work night. */
432

433
	if (marked_cut == 2) {
434
	    tmp = make_new_node(current);
435
	    tmp->data = mallocstrcpy(NULL, current->data + current_x);
436
	    splice_node(current, tmp, current->next);
437
	    null_at(&current->data, current_x);
438
439
440
	    current = current->next;
	    current_x = 0;
	    placewewant = 0;
441
442
443
444

	    /* Extra line added, update stuff */
	    totlines++;
	    totsize++;
445
	}
Chris Allegretta's avatar
Chris Allegretta committed
446
447
448
	/* Renumber from BEFORE where we pasted ;) */
	renumber(hold);

Chris Allegretta's avatar
Chris Allegretta committed
449
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
450
451
	dump_buffer(fileage);
	dump_buffer(cutbuffer);
Chris Allegretta's avatar
Chris Allegretta committed
452
#endif
Chris Allegretta's avatar
Chris Allegretta committed
453
	set_modified();
Chris Allegretta's avatar
Chris Allegretta committed
454
455
456
457
	if (recenter_me)
	    edit_update(current, CENTER);
	else
	    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
458
	return 0;
Chris Allegretta's avatar
Chris Allegretta committed
459
460
461
    }

    if (fileptr != fileage) {
Chris Allegretta's avatar
Chris Allegretta committed
462
463
464
	tmp = fileptr->prev;
	tmp->next = newbuf;
	newbuf->prev = tmp;
Chris Allegretta's avatar
Chris Allegretta committed
465
    } else
Chris Allegretta's avatar
Chris Allegretta committed
466
	fileage = newbuf;
Chris Allegretta's avatar
Chris Allegretta committed
467
    totlines++;		/* Unmarked uncuts don't split lines */
Chris Allegretta's avatar
Chris Allegretta committed
468
469
470
471
472
473
474
475
476

    /* This is so uncutting at the top of the buffer will work => */
    if (current_y == 0)
	edittop = newbuf;

    /* Connect the end of the buffer to the filestruct */
    newend->next = fileptr;
    fileptr->prev = newend;

Chris Allegretta's avatar
Chris Allegretta committed
477
    /* Recalculate size *sigh* */
Chris Allegretta's avatar
Chris Allegretta committed
478
479
480
481
482
    for (tmp = newbuf; tmp != fileptr; tmp = tmp->next)
	totsize += strlen(tmp->data) + 1;

    i = editbot->lineno;
    renumber(newbuf);
483
484
485
    /* Center the screen if we've moved beyond the line numbers of both
       the old and new editbots */
    if (i < newend->lineno && editbot->lineno < newend->lineno)
486
	edit_update(fileptr, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
487
    else
488
	edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
489

Chris Allegretta's avatar
Chris Allegretta committed
490
491
492
#ifdef DEBUG
    dump_buffer_reverse();
#endif
Chris Allegretta's avatar
Chris Allegretta committed
493
494
495
496

    set_modified();
    return 1;
}