Chapter 8: The Line Editor - A Final Example

Chapter 8: The Line Editor - A Final Example

Contents

8.1. Introduction

We finish the course with a final example which pulls together many of the concepts we have seen in recent chapters. The example is an implementation of a Line Editor which can be used to edit a specific file a line at a time. At first sight this might seem a challenging prospect. The chapter aims to show that, given suitable general purpose classes, the task is in fact relatively easy. This should serve as a demonstration of the power of Abstract Data Typing as an approach to programming and should highlight the issues of encapsulation and re-use of code.

However, in to order to build our Line editor we do need to briefly digress for a moment to consider a final new topic - the issue of command line arguments to C++ programs.

8.2. Command line arguments

You should be aware that most operating system commands may be given "command line arguments" when they are called. For example, the UNIX cat command may take the names of files to be displayed as its arguments:

Two common uses of command line arguments are to specify filenames which commands will then access or to give flags to commands in order to modify their behaviour in some way. For example, the ls command can be instructed to give extra information about files by using the -l flag:

On this course we have learned how to write, compile and then run our own C++ programs from within UNIX. Running a program simply involves entering the name of the file which contains its executable binary code. It would be very useful if our own programs could be given command line arguments whenever they were run in just the same way as normal UNIX commands and if these arguments could then be used inside the program.

This is possible in C++ by declaring special arguments for the function called main(). If you remember, main is the function where execution of a C++ program begins. It turns out that, like any other function, main can be declared with arguments. These will contain the particular command line arguments that were given to UNIX when the program was first invoked. If you want to use these arguments, you must declare main in the following way.

We are using two arguments to main, argc and argv (in fact, there can be a third which we don't need to consider here). Argc is an integer which will contain the number of command line arguments that were given to the program. You need to be a little careful here because the name of the program is ALWAYS the first argument and so argc is always >= 1. For example, if our executable is in the file a.out and the user enters the command a.out -q fred jim at the UNIX shell then argc would have the initial value 4 at the start of function main.

The second argument, argv, is a pointer to a pointer to a char.

This double pointer is no mistake. Remembering that a pointer can represent an array, you should think of argv as pointing to the beginning of an array, each of whose elements itself points at an array of characters. In other words we have an array of character strings!

This array will contain each of the arguments given to the program in the order the were given. The first string will always be the name of the program itself. Thus, for the above example, argv would point to the following.

Remembering that we can use the [] operator with pointers, we arrive at the following.

And so on.

Extending this idea, argv[3][1] is the second character in the third argument (i.e. the single letter 'i').

The following program uses argc and argv to implement the UNIX echo command in C++.

8.3. Designing the line editor

The goal of our line editor is to allow us to edit a UNIX file. However, unlike emacs which is a "screen editor", our line editor will only let us change specific lines at one time by explicitly giving their line numbers (e.g. delete line 6). This kind of limited editor can still be useful in some circumstances and UNIX contains a well known example called ed.

Our simple line editor will support the following:

  • edit a named file;
  • Insert new text after the specified line number;
  • Display all lines between two line numbers;
  • Delete all lines between two line numbers;
  • Copy lines between two line numbers to a different position.
  • Save the file at any time;
  • Quit the editor;

    Our solution will involve two steps. First, design an Editor Abstract Data Type which supports these functions in an abstract way and then implement this as a C++ class. Second, implement an interface built on top of this ADT. The advantage of separating the interface from the rest of the implementation is that we can easily alter the interface at a later time - perhaps even replacing it with a screen interface!

    The following is the C++ class definition for the Editor class.

    This class uses the new class List which implements a linked list of Strings. Although we haven't seen the code for this it is the same at the linked list of characters from chapter 7 except that the value stored in each list element is a String not a char.

    The constructor function takes the name of the file to be edited (a char*). Its job is to open the file and to read its contents into an edit buffer. Display shows the text between lines n and m. Remove deletes the text between lines n and m. Insert starts inserting new text after line n. It stops when a full stop is entered at the beginning of a line, on its own. Copy copies the lines between m and n and inserts them after line o. Save saves the current buffer back to the file. Quit quits the editor. If the changes have been made since the last save, it asks the user if they want to save to the file.

    8.4. Implementing the Line Editor

    The Editor uses three internal data members:

  • List buffer - a linked list of Strings containing the text being edited (each String represents a separate line).
  • char* filename - the name of the file being edited.
  • int changed - an indication of whether the buffer had been changed since the file was last saved (the value 1 indicates that it has and 0 that it has not).

    The overall approach towards implementation is the following.

  • 1. The constructor opens the filename given as its argument, reads it a line at a time and builds up the linked list of strings in buffer.
  • 2. Operations to edit the text alter the contents of buffer. This is easy given the available linked list methods.
  • 3. Saving a file writes the contents of the buffer back to the file a line at a time, overwriting the old contents.

    We now look at the code for each Editor method in turn.

    Notice how the constructor has to allocate new memory to make a permanent copy of the filename. In addition, it has to check that the file exists and is readable. Finally, it has to note that the file doesn't need saving.

    The destructor needs to free the memory that was allocated for the permanent copy of the filename.

    Display simply retrieves the specified range of elements from the buffer linked list. It displays all lines found and returns a status code indicating if the specified range was out of bounds.

    Insert reads new lines from the input and inserts them into the buffer. It also has to note that the buffer now differs from the file.

    Delete simply calls the deleteN function on the list.

    Copy is also straight forward.

    The save method indicates that the buffer is being written back to the file and then opens the file in write mode. Each line of buffer is then written to the file and the data member changed set to the value 0.

    Finally, quit only needs to check whether the file needs saving.

    8.5. The Line Editor Interface

    This section outlines one possible interface to the Editor class. The syntax of its commands is loosely based on the UNIX ed line editor.

    Assuming that the executable program is in the file edit, the user starts the editor by entering the UNIX command:

    They then see a $ prompt indicating that the editor is ready to receive commands. Each command is represented by a single letter followed by appropriate line numbers. A summary of the syntax is:

    The first job of the interface program is to process the command line arguments to get the filename to be edited. Notice that an error message is generated if no filename is supplied. Beyond this, the program enters a continuous while loop, processing commands through a switch statement until the user selects the quit option.


    Notes converted from troff to HTML by an Eric Foxley shell script, email errors to me!