cut.c 12.3 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? */
32
33
static filestruct *cutbottom = NULL;
				/* Pointer to end of cutbuffer */
Chris Allegretta's avatar
Chris Allegretta committed
34

Chris Allegretta's avatar
Chris Allegretta committed
35
36
37
38
39
40
filestruct *get_cutbottom(void)
{
    return cutbottom;
}

void add_to_cutbuffer(filestruct *inptr)
Chris Allegretta's avatar
Chris Allegretta committed
41
42
{
#ifdef DEBUG
43
    fprintf(stderr, _("add_to_cutbuffer() called with inptr->data = %s\n"),
Chris Allegretta's avatar
Chris Allegretta committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	    inptr->data);
#endif

    if (cutbuffer == NULL) {
	cutbuffer = inptr;
	inptr->prev = NULL;
    } else {
	cutbottom->next = inptr;
	inptr->prev = cutbottom;
    }

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

#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
60
/* Cut a marked segment instead of a whole line.
61
 *
Chris Allegretta's avatar
Chris Allegretta committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 * 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
76
{
Chris Allegretta's avatar
Chris Allegretta committed
77
78
    filestruct *tmp, *next;
    size_t newsize;
79

Chris Allegretta's avatar
Chris Allegretta committed
80
81
82
    if (top == bot && top_x == bot_x)
	return;
    assert(top != NULL && bot != NULL);
83

Chris Allegretta's avatar
Chris Allegretta committed
84
85
86
87
    /* Make top be no later than bot. */
    if (top->lineno > bot->lineno) {
	filestruct *swap = top;
	int swap2 = top_x;
88

Chris Allegretta's avatar
Chris Allegretta committed
89
90
	top = bot;
	bot = swap;
91

Chris Allegretta's avatar
Chris Allegretta committed
92
93
94
95
96
	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;
97

Chris Allegretta's avatar
Chris Allegretta committed
98
99
	top_x = bot_x;
	bot_x = swap;
100
    }
Chris Allegretta's avatar
Chris Allegretta committed
101

Chris Allegretta's avatar
Chris Allegretta committed
102
    /* Make the first cut line manually. */
Chris Allegretta's avatar
Chris Allegretta committed
103
    tmp = copy_node(top);
Chris Allegretta's avatar
Chris Allegretta committed
104
105
106
107
    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);
108

Chris Allegretta's avatar
Chris Allegretta committed
109
    /* And make the remainder line manually too. */
110
    if (destructive) {
Chris Allegretta's avatar
Chris Allegretta committed
111
112
113
114
115
116
117
118
119
120
121
122
123
	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);
	    top->data = (char *)nrealloc(top->data,
					sizeof(char) * newsize);
	} else {
Chris Allegretta's avatar
Chris Allegretta committed
124
	    totsize -= bot_x + 1;
Chris Allegretta's avatar
Chris Allegretta committed
125

126
127
	    /* Here, the remainder line might get longer, so we
	       realloc() it first. */
Chris Allegretta's avatar
Chris Allegretta committed
128
129
130
131
	    top->data = (char *)nrealloc(top->data,
					sizeof(char) * newsize);
	    memmove(top->data + top_x, bot->data + bot_x,
			newsize - top_x);
132
	}
Chris Allegretta's avatar
Chris Allegretta committed
133
134
    }

Chris Allegretta's avatar
Chris Allegretta committed
135
136
137
138
    if (top == bot) {
#ifdef DEBUG
	dump_buffer(cutbuffer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
139
	return;
140
    }
141

Chris Allegretta's avatar
Chris Allegretta committed
142
143
144
145
146
147
    tmp = top->next;
    while (tmp != bot) {
	next = tmp->next;
	if (!destructive)
	    tmp = copy_node(tmp);
	else
Chris Allegretta's avatar
Chris Allegretta committed
148
	    totsize -= strlen(tmp->data) + 1;
Chris Allegretta's avatar
Chris Allegretta committed
149
150
151
	add_to_cutbuffer(tmp);
	tmp = next;
    }
152

Chris Allegretta's avatar
Chris Allegretta committed
153
154
155
156
157
158
159
    /* 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
160

161
    if (destructive) {
Chris Allegretta's avatar
Chris Allegretta committed
162
163
164
165
	top->next = bot->next;
	if (top->next != NULL)
	    top->next->prev = top;
	delete_node(bot);
166
167
168
	renumber(top);
	current = top;
	if (bot == filebot) {
Chris Allegretta's avatar
Chris Allegretta committed
169
170
171
172
	    filebot = top;
	    assert(bot_x == 0);
	    if (top_x > 0)
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
173
174
175
176
177
178
179
	}
    }
}
#endif

int do_cut_text(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
180
    filestruct *fileptr;
Chris Allegretta's avatar
Chris Allegretta committed
181
#ifndef NANO_SMALL
182
    int dontupdate = 0;
Chris Allegretta's avatar
Chris Allegretta committed
183
184
#endif

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

187
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
188

189
    if (!ISSET(KEEP_CUTBUFFER)) {
Chris Allegretta's avatar
Chris Allegretta committed
190
191
	free_filestruct(cutbuffer);
	cutbuffer = NULL;
192
	marked_cut = 0;
Chris Allegretta's avatar
Chris Allegretta committed
193
194
195
196
#ifdef DEBUG
	fprintf(stderr, _("Blew away cutbuffer =)\n"));
#endif
    }
197

Chris Allegretta's avatar
Chris Allegretta committed
198
199
200
201
202
203
204
    /* 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
						)
205
206
	return 0;

Chris Allegretta's avatar
Chris Allegretta committed
207
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
208
    if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) {
Chris Allegretta's avatar
Chris Allegretta committed
209
	assert(current_x >= 0 && current_x <= strlen(current->data));
210

Chris Allegretta's avatar
Chris Allegretta committed
211
212
213
	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 */
214
	    if (marked_cut != 1 && current->next != filebot) {
Chris Allegretta's avatar
Chris Allegretta committed
215
		filestruct *junk = make_new_node(current);
216

217
	        junk->data = charalloc(1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
218
		junk->data[0] = '\0';
219
		add_to_cutbuffer(junk);
Chris Allegretta's avatar
Chris Allegretta committed
220
#ifdef DEBUG
221
		dump_buffer(cutbuffer);
Chris Allegretta's avatar
Chris Allegretta committed
222
#endif
223
	    }
224

225
	    do_delete();
226
	    SET(KEEP_CUTBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
227
	    marked_cut = 2;
228
	    return 1;
229
	} else {
230
231
232
233
234
	    SET(MARK_ISSET);
	    SET(KEEP_CUTBUFFER);

	    mark_beginx = strlen(current->data);
	    mark_beginbuf = current;
Chris Allegretta's avatar
Chris Allegretta committed
235
	    dontupdate = 1;
236
	}
237
    }
238

Chris Allegretta's avatar
Chris Allegretta committed
239
    if (ISSET(MARK_ISSET)) {
Chris Allegretta's avatar
Chris Allegretta committed
240
241
242
243
244
245
246
247
	/* 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
248
249
250

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

Chris Allegretta's avatar
Chris Allegretta committed
252
	marked_cut = 1;
253
	if (dontupdate)
254
	    edit_refresh();
255
	else
256
	    edit_update(current, CENTER);
257
	set_modified();
Chris Allegretta's avatar
Chris Allegretta committed
258

Chris Allegretta's avatar
Chris Allegretta committed
259
260
	return 1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
261
262
263
264
265
266
267
268
269
270
271
#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
272

Chris Allegretta's avatar
Chris Allegretta committed
273
274
275
276
    if (fileptr == fileage)
	fileage = current;
    else
	current->prev->next = current;
Chris Allegretta's avatar
Chris Allegretta committed
277
278
279
280

    if (fileptr == edittop)
	edittop = current;

Chris Allegretta's avatar
Chris Allegretta committed
281
    renumber(current);
282
    current_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
283
284
285
286
287
288
289
290
291
292
    edit_refresh();
    set_modified();
    marked_cut = 0;
    placewewant = 0;
    SET(KEEP_CUTBUFFER);
    return 1;
}

int do_uncut_text(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
293
    filestruct *tmp = current, *fileptr = current;
294
    filestruct *newbuf = NULL, *newend = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
295
    char *tmpstr, *tmpstr2;
296
    filestruct *hold = current;
Chris Allegretta's avatar
Chris Allegretta committed
297
298
299
    int i;

    wrap_reset();
300
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
301
302
303
    if (cutbuffer == NULL || fileptr == NULL)
	return 0;		/* AIEEEEEEEEEEEE */

Chris Allegretta's avatar
Chris Allegretta committed
304
305
306
307
308
309
310
311
312
313
314
    /* 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;
    }

315
    if (marked_cut == 0 || cutbuffer->next != NULL)
Chris Allegretta's avatar
Chris Allegretta committed
316
317
318
319
320
    {
	newbuf = copy_filestruct(cutbuffer);
	for (newend = newbuf; newend->next != NULL && newend != NULL;
		newend = newend->next)
	    totlines++;
Chris Allegretta's avatar
Chris Allegretta committed
321
322
323
    }

    /* Hook newbuf into fileptr */
324
    if (marked_cut != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
325
326
327
	int recenter_me = 0;
	    /* Should we eventually use edit_update(CENTER)? */

Chris Allegretta's avatar
Chris Allegretta committed
328
329
	/* If there's only one line in the cutbuffer */
	if (cutbuffer->next == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
330
331
332
333
334
335
336
	    size_t buf_len = strlen(cutbuffer->data);
	    size_t cur_len = strlen(current->data);

	    current->data = nrealloc(current->data, cur_len + buf_len + 1);
	    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);
337
		/* Use strncpy() to not copy the terminal '\0'. */
Chris Allegretta's avatar
Chris Allegretta committed
338
339
340

	    current_x += buf_len;
	    totsize += buf_len;
Chris Allegretta's avatar
Chris Allegretta committed
341
342
343
344
	    /* If we've uncut a line, make sure there's a magicline after
	       it */
	    if (current->next == NULL)
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
345
346
347
348
349
350

	    placewewant = xplustabs();
	    update_cursor();
	} else {		/* yuck -- no kidding! */
	    tmp = current->next;
	    /* New beginning */
351
	    tmpstr = charalloc(current_x + strlen(newbuf->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
352
353
354
355
356
	    strncpy(tmpstr, current->data, current_x);
	    strcpy(&tmpstr[current_x], newbuf->data);
	    totsize += strlen(newbuf->data) + strlen(newend->data) + 1;

	    /* New end */
357
	    tmpstr2 = charalloc(strlen(newend->data) +
Chris Allegretta's avatar
Chris Allegretta committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
			      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
376
377
	       prev pointer.  If it IS null, we're at the end; update
	       the filebot pointer */
Chris Allegretta's avatar
Chris Allegretta committed
378
379
380

	    if (tmp != NULL)
		tmp->prev = newend;
Chris Allegretta's avatar
Chris Allegretta committed
381
382
383
384
	    else {
		/* Fix the editbot pointer too */
		if (editbot == filebot)
		    editbot = newend;
Chris Allegretta's avatar
Chris Allegretta committed
385
		filebot = newend;
386
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
387
	    }
Chris Allegretta's avatar
Chris Allegretta committed
388
389
390
391
392
393

	    /* 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
394
395
	    if (editbot->lineno < newend->lineno)
		recenter_me = 1;
Chris Allegretta's avatar
Chris Allegretta committed
396
397
	}

398
399
	/* 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
400
401
402
	   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. */
403

404
	if (marked_cut == 2) {
405
	    tmp = make_new_node(current);
406
	    tmp->data = mallocstrcpy(NULL, current->data + current_x);
407
	    splice_node(current, tmp, current->next);
408
	    null_at(&current->data, current_x);
409
410
411
	    current = current->next;
	    current_x = 0;
	    placewewant = 0;
412
413
414
415

	    /* Extra line added, update stuff */
	    totlines++;
	    totsize++;
416
	}
Chris Allegretta's avatar
Chris Allegretta committed
417
418
419
	/* Renumber from BEFORE where we pasted ;) */
	renumber(hold);

Chris Allegretta's avatar
Chris Allegretta committed
420
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
421
422
	dump_buffer(fileage);
	dump_buffer(cutbuffer);
Chris Allegretta's avatar
Chris Allegretta committed
423
#endif
Chris Allegretta's avatar
Chris Allegretta committed
424
	set_modified();
Chris Allegretta's avatar
Chris Allegretta committed
425
426
427
428
	if (recenter_me)
	    edit_update(current, CENTER);
	else
	    edit_refresh();
429
	UNSET(KEEP_CUTBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
430
	return 0;
Chris Allegretta's avatar
Chris Allegretta committed
431
432
433
    }

    if (fileptr != fileage) {
Chris Allegretta's avatar
Chris Allegretta committed
434
435
436
	tmp = fileptr->prev;
	tmp->next = newbuf;
	newbuf->prev = tmp;
Chris Allegretta's avatar
Chris Allegretta committed
437
    } else
Chris Allegretta's avatar
Chris Allegretta committed
438
	fileage = newbuf;
Chris Allegretta's avatar
Chris Allegretta committed
439
    totlines++;		/* Unmarked uncuts don't split lines */
Chris Allegretta's avatar
Chris Allegretta committed
440
441
442
443
444
445
446
447
448

    /* 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
449
    /* Recalculate size *sigh* */
Chris Allegretta's avatar
Chris Allegretta committed
450
451
452
453
454
    for (tmp = newbuf; tmp != fileptr; tmp = tmp->next)
	totsize += strlen(tmp->data) + 1;

    i = editbot->lineno;
    renumber(newbuf);
455
456
457
    /* 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)
458
	edit_update(fileptr, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
459
    else
460
	edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
461

Chris Allegretta's avatar
Chris Allegretta committed
462
463
464
#ifdef DEBUG
    dump_buffer_reverse();
#endif
Chris Allegretta's avatar
Chris Allegretta committed
465
466
467
468
469

    set_modified();
    UNSET(KEEP_CUTBUFFER);
    return 1;
}