CS1921-Text-Editor

Aims

This exercise aims to give you practice in dealing with dynamic data structures, specifically linked lists. The goal is to complete the implementation of a medium-sized program that can do simple text editing.

Background

The request is to write a simple line-based text editor called lite (which stands for line text editor). The editor lite stores lines of text in a linked list: each line of text is stored in a node of the linked list. If there are n lines of text, they are numbered from 1 to n for the user. At every point in time the editor has a current line number (which is an index between 1 and n). When lite is started up, the current line number is 1. The line-based commands allow the user to print lines, to delete the current line, to insert and append lines before or after the current line and to go-to a particular line. There are also file-oriented commands that enable the user to save changes and quit the editor, to force an exit or to change the name of the file. Note that lite does not contain a search command or contain any way of changing text apart from deleting ‘old’ lines and appending and inserting new lines.

The main objectives of this request are:

  • use and manipulate dynamic data structures (like linked lists) to solve a complex problem
  • learn how to implement a linked list data structure and functions for maintaining the data structure
  • gain experience with implementing a more comprehensive functionality for editing text files
  • write a properly documented C program that adheres to the course Style Guide

NOTE: You should think carefully about the appropriate data structures and algorithms to use in your program. Before starting to write any code it is important that you fully understand the problem and determine the data structures that you will require and the algorithms to use. It is highly recommended that you start coding only after you have spent some time on these considerations. In particular, you must not make any assumptions about the number of lines in a text file; this means that you must use dynamically-allocated linked-list data structure to manage a text file.

What the editor should do

A file name is the only optional argument to the lite command. So you execute lite either by:

1
prompt$ ./lite

or

1
prompt$ ./lite filename

where filename is a file name that may or may not exist. If there are more arguments, then the usage error message from lite is:

1
Usage: ./lite [filename]

The square brackets indicate that filename is optional.

Examples of Use

When lite is waiting for user input it uses the prompt ‘?’. When text is printed, each line is preceded by its line number. The current line number is indicated by an arrow.

Example 1

The user can start up the editor without a file name, print the help command and then quit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
prompt$ ./lite
? h
Commands are (in upper or lower case):
q: quit
s: save
x: force exit
f <filename>: the file is called <filename>
h: print this help message
d: delete current line
a: append after current line, terminated by '.'
i: insert before current line, terminated by '.'
p: print all lines
.: print current line
+: increment line and print
<return>: same as '+'
-: decrement line and print
number: make 'number' the current line
? q
bye
prompt$

Example 2

In the next example, if the file Mackellar.txt contains the text

1
2
3
4
5
6
7
8
I love a sunburnt country,
A land of sweeping plains,
Of ragged mountain ranges,
Of droughts and flooding rains.
I love her far horizons,
I love her jewel-sea,
Her beauty and her terror -
The wide brown land for me!

Example 3

In the next example, the user steps down an existing C program to a particular line, deletes the line and inserts a new line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
prompt$ ./lite hw.c
Existing file "hw.c"
? p
---> 1: #include <stdio.h>
Line 2: #define NUMBER 10
Line 3: int main(void) {
Line 4: int i;
Line 5: for (i=0; i<NUMBER; i++) {
Line 6: printf("hello, world!\n");
Line 7: }
Line 8: return 0;
Line 9: }
? +
---> 2: #define NUMBER 10
? +
---> 3: int main(void) {
? +
---> 4: int i;
? +
---> 5: for (i=0; i<NUMBER; i++) {
? +
---> 6: printf("hello, world!\n");
? d
---> 6: }
? i
printf("goodbye, world!\n");
.
? p
Line 1: #include <stdio.h>
Line 2: #define NUMBER 10
Line 3: int main(void) {
Line 4: int i;
Line 5: for (i=0; i<NUMBER; i++) {
---> 6: printf("goodbye, world!\n");
Line 7: }
Line 8: return 0;
Line 9: }
? s
Saving file "hw.c"
? q
bye
prompt$

Note that it would have been faster, of course, to have typed in the line number, 6, rather than step down line by line.

Example 4

In the final example, the user starts the editor up, does a print to check that there is no text, inserts a poem from Banjo Paterson, does another print, sets the file name to Banjo.txt, saves the text and quits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
prompt$ ./lite
? p
Empty file
? i
A land of sombre, silent hills, where mountain cattle go
By twisted tracks, on sidelings steep, where giant gumtrees grow
And the wind replies, in the river oaks, to the song of the stream below.
.
? p
Line 1: A land of sombre, silent hills, where mountain cattle go
Line 2: By twisted tracks, on sidelings steep, where giant gumtrees grow
---> 3: And the wind replies, in the river oaks, to the song of the stream below.
? f Banjo.txt
Creating file "Banjo.txt"
? s
Saving file "Banjo.txt"
? q
bye
prompt$

Example 5

Note that a quit is not possible if the user has changed the file in any way. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
prompt$ ./lite
? i
1
2
3
.
? s
No filename. Use '-f <name>' command
? q
Cannot quit as file has changed. Use 'x' to force exit
? f FRED.txt
Creating file "FRED.txt"
? s
Saving file "FRED.txt"
? q
bye
prompt$

In this example, the user could have instead used an ‘x’ command to leave the editor without saving.

Example 6

You cannot step beyond the last line or the first line in the buffer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
prompt$ ./lite Mackellar.txt
Existing file "Mackellar.txt"
? p
---> 1: I love a sunburnt country,
Line 2: A land of sweeping plains,
Line 3: Of ragged mountain ranges,
Line 4: Of droughts and flooding rains.
Line 5: I love her far horizons,
Line 6: I love her jewel-sea,
Line 7: Her beauty and her terror -
Line 8: The wide brown land for me!
Line 9:
Line 10: "My Country"
Line 11: Poem by Dorothea Mackellar
Line 12: (1885--1968)
? 123
Line number does not exist: command ignored
? -
---> 1:I love a sunburnt country,
? -
---> 1:I love a sunburnt country,
? -
---> 1:I love a sunburnt country,
? 12
---> 12:(1885--1968)
?
---> 12:(1885--1968)
? +
---> 12:(1885--1968)
? +
---> 12:(1885--1968)
? +
---> 12:(1885--1968)
? q
bye
prompt$

Approach

Do not try to write the whole program at once. Break the development into pieces of functionality and implement the easiest first.

Hints

You may use fixed-length arrays to read a line of text from stdin:

1
2
#define LINELENGTH  1000  // max length of a line
and to read a filename or an editor command:
1
2
#define FNAMELENGTH 100   // max length of a file name
#define CMDLENGTH 102 // max length of a command (e.g.'f' + ' ' + filename)

However, you should use the heap to store the lines in the linked list. The nodes in the linked list, each storing one line of text, may be quite simple:

1
2
3
4
typedef struct _node {
char *data; // a line of text
struct _node *next;
} Line;

but you may place more data in this data structure if you wish.