cut.c 13 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
325
    int i;

    wrap_reset();
326
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
327
328
329
    if (cutbuffer == NULL || fileptr == NULL)
	return 0;		/* AIEEEEEEEEEEEE */

Chris Allegretta's avatar
Chris Allegretta committed
330
331
332
333
334
335
336
337
338
339
340
    /* 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
341
342
343
344
345
    /* If we're going to uncut on the magicline, always make a new
       magicline in advance. */
    if (current->next == NULL)
	new_magicline();

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

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

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

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
364
	    current->data = charealloc(current->data, cur_len + buf_len + 1);
Chris Allegretta's avatar
Chris Allegretta committed
365
366
367
	    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);
368
		/* Use strncpy() to not copy the terminal '\0'. */
Chris Allegretta's avatar
Chris Allegretta committed
369
370
371

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

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

	    /* New end */
384
	    tmpstr2 = charalloc(strlen(newend->data) +
Chris Allegretta's avatar
Chris Allegretta committed
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
			      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
403
404
	       prev pointer.  If it IS null, we're at the end; update
	       the filebot pointer */
Chris Allegretta's avatar
Chris Allegretta committed
405
406
407

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

	    /* 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
421
422
	    if (editbot->lineno < newend->lineno)
		recenter_me = 1;
Chris Allegretta's avatar
Chris Allegretta committed
423
424
	}

425
426
	/* 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
427
428
429
	   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. */
430

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

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

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

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

    /* 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
475
    /* Recalculate size *sigh* */
Chris Allegretta's avatar
Chris Allegretta committed
476
477
478
479
480
    for (tmp = newbuf; tmp != fileptr; tmp = tmp->next)
	totsize += strlen(tmp->data) + 1;

    i = editbot->lineno;
    renumber(newbuf);
481
482
483
    /* 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)
484
	edit_update(fileptr, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
485
    else
486
	edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
487

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

    set_modified();
    return 1;
}