cut.c 12 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-2002 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
32
static int marked_cut;		/* Is the cutbuffer from a mark? */
static filestruct *cutbottom = NULL; /* Pointer to end of cutbuffer */
Chris Allegretta's avatar
Chris Allegretta committed
33

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

void add_to_cutbuffer(filestruct *inptr)
Chris Allegretta's avatar
Chris Allegretta committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
#ifdef DEBUG
    fprintf(stderr, _("add_to_cutbuffer called with inptr->data = %s\n"),
	    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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Cut a marked segment instead of a whole line.
 * 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
74
{
Chris Allegretta's avatar
Chris Allegretta committed
75
76
    filestruct *tmp, *next;
    size_t newsize;
77

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

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

Chris Allegretta's avatar
Chris Allegretta committed
87
88
	top = bot;
	bot = swap;
89

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

Chris Allegretta's avatar
Chris Allegretta committed
96
97
	top_x = bot_x;
	bot_x = swap;
98
    }
Chris Allegretta's avatar
Chris Allegretta committed
99

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

Chris Allegretta's avatar
Chris Allegretta committed
107
    /* And make the remainder line manually too. */
108
    if (destructive) {
Chris Allegretta's avatar
Chris Allegretta committed
109
110
111
112
113
114
115
116
117
118
119
120
121
	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
122
	    totsize -= bot_x + 1;
Chris Allegretta's avatar
Chris Allegretta committed
123
124
125
126
127
128
129

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

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

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

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

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

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

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

185
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
186

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

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

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

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

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

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
258
259
	return 1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#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
	
    if (fileptr == fileage)
	fileage = current;
    else
	current->prev->next = current;
Chris Allegretta's avatar
Chris Allegretta committed
276
277
278
279

    if (fileptr == edittop)
	edittop = current;

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

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

    wrap_reset();
303
    check_statblank();
Chris Allegretta's avatar
Chris Allegretta committed
304
305
306
    if (cutbuffer == NULL || fileptr == NULL)
	return 0;		/* AIEEEEEEEEEEEE */

Chris Allegretta's avatar
Chris Allegretta committed
307
308
309
310
311
312
313
314
#ifndef NANO_SMALL
    if (!marked_cut || cutbuffer->next != NULL)
#endif
    {
	newbuf = copy_filestruct(cutbuffer);
	for (newend = newbuf; newend->next != NULL && newend != NULL;
		newend = newend->next)
	    totlines++;
Chris Allegretta's avatar
Chris Allegretta committed
315
316
317
318
    }

    /* Hook newbuf into fileptr */
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
319
    if (marked_cut) {
Chris Allegretta's avatar
Chris Allegretta committed
320
321
322
	int recenter_me = 0;
	    /* Should we eventually use edit_update(CENTER)? */

Chris Allegretta's avatar
Chris Allegretta committed
323
324
	/* If there's only one line in the cutbuffer */
	if (cutbuffer->next == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
325
326
327
328
329
330
331
332
333
334
335
	    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);
		/* Use strncpy to not copy the terminal '\0'. */

	    current_x += buf_len;
	    totsize += buf_len;
Chris Allegretta's avatar
Chris Allegretta committed
336
337
338
339
	    /* 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
340
341
342
343
344
345

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

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

	    if (tmp != NULL)
		tmp->prev = newend;
Chris Allegretta's avatar
Chris Allegretta committed
376
377
378
379
	    else {
		/* Fix the editbot pointer too */
		if (editbot == filebot)
		    editbot = newend;
Chris Allegretta's avatar
Chris Allegretta committed
380
		filebot = newend;
381
		new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
382
	    }
Chris Allegretta's avatar
Chris Allegretta committed
383
384
385
386
387
388

	    /* 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
389
390
	    if (editbot->lineno < newend->lineno)
		recenter_me = 1;
Chris Allegretta's avatar
Chris Allegretta committed
391
392
	}

393
394
	/* 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
395
396
397
	   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. */
398

399
	if (marked_cut == 2) {
400
	    tmp = make_new_node(current);
401
	    tmp->data = charalloc(strlen(&current->data[current_x]) + 1);
402
	    strcpy(tmp->data, &current->data[current_x]);
403
	    splice_node(current, tmp, current->next);
404
	    null_at(&current->data, current_x);
405
406
407
	    current = current->next;
	    current_x = 0;
	    placewewant = 0;
408
409
410
411

	    /* Extra line added, update stuff */
	    totlines++;
	    totsize++;
412
	}
Chris Allegretta's avatar
Chris Allegretta committed
413
414
415
	/* Renumber from BEFORE where we pasted ;) */
	renumber(hold);

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

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

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

    i = editbot->lineno;
    renumber(newbuf);
Chris Allegretta's avatar
Chris Allegretta committed
452
    if (i < newend->lineno)
453
	edit_update(fileptr, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
454
    else
455
	edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
456

Chris Allegretta's avatar
Chris Allegretta committed
457
458
459
#ifdef DEBUG
    dump_buffer_reverse();
#endif
Chris Allegretta's avatar
Chris Allegretta committed
460
461
462
463
464

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