C代写:COMP1921 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 assignment 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 assignment 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:

prompt$ ./lite

or

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:

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.

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

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!

then an example of an edit session that involves the user

  • opening the file Mackellar.txt,
  • printing its contents by entering the command p,
  • moving to the 8th line in the file (this is the last line) by entering 8,
  • appending 4 lines (consisting of a blank line, the name of the poem, the author and her year of birth and death)
    • notice the text is terminated by a line containing the symbol ‘.’ only (just like UNIX’s ed)
    • the current line number after an insertion is the last line to be entered
  • entering an incorrect command v,
  • printing the contents again by entering p,
  • saving the text by entering s, and finally
  • quitting the editor by entering q

is shown below:

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!
? 8
--->   8: The wide brown land for me!
? a

"My Country"
Poem by Dorothea Mackellar
(1885--1968)
.
? v
Unknown command: ignoring
? p
Line   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
--->  12: (1885--1968)
? s
Saving file "Mackellar.txt"
? q
bye
prompt$

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.

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.

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:

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:

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.

Stage 1 – Reading Text

  1. Write a program that reads lines of text from stdin and creates a linked list in which each node contains a single line.
    • You will need a function that prints all the nodes in a linked list to check that the read was successful.
      You are allowed to copy chunks of code from the list.c library from lectures, if you find it helpful. If you use code like this, you must mark it with a comment to indicate its source.
  2. Change the program to read from a file, where the name of the file is given on the command line.

Stage 2 – Navigating

  1. Change the program to accept simple commands from the keyboard:
    • p to print the complete contents of the linked list (it calls the function you wrote earlier)
    • h to print the help menu
    • x to exit the program (you will need to write a function to free the linked-list nodes)
    • if the command is not one of the above, an appropriate message should be printed and the program should wait for the next command
  2. Now add the ‘move around’ commands to the program:
    • an integer to go to a particular node in the linked list and print the corresponding line
      • this raises the issue of the current line number, which is central to the workings of the editor
    • and to print the line before and after the current line
      • it should be possible to enter any number of + and - to step through the linked list
      • address the problem of what happens to the current line when you reach the start and end of the linked list

Stage 3 – Editing

  1. Now add the delete and insert/append operations:
    • do d first; remember to free the linked-list node when the line of text is deleted
    • be mindful of what happens to the current line when you have inserted/appended or deleted lines
  2. Add file I/O to the program, which involve the s, q and f commands:
    • the f command is tricky as it requires a filename argument
    • the s command will write the complete linked list to a file
    • the q command requires the program to know whether the lines of text have changed or not

Hints

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

1
#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.