[9] | 1 | /*
|
---|
| 2 | * Part one of the mined editor.
|
---|
| 3 | */
|
---|
| 4 |
|
---|
| 5 | /*
|
---|
| 6 | * Author: Michiel Huisjes.
|
---|
| 7 | *
|
---|
| 8 | * 1. General remarks.
|
---|
| 9 | *
|
---|
| 10 | * Mined is a screen editor designed for the MINIX operating system.
|
---|
| 11 | * It is meant to be used on files not larger than 50K and to be fast.
|
---|
| 12 | * When mined starts up, it reads the file into its memory to minimize
|
---|
| 13 | * disk access. The only time that disk access is needed is when certain
|
---|
| 14 | * save, write or copy commands are given.
|
---|
| 15 | *
|
---|
| 16 | * Mined has the style of Emacs or Jove, that means that there are no modes.
|
---|
| 17 | * Each character has its own entry in an 256 pointer to function array,
|
---|
| 18 | * which is called when that character is typed. Only ASCII characters are
|
---|
| 19 | * connected with a function that inserts that character at the current
|
---|
| 20 | * location in the file. Two execptions are <linefeed> and <tab> which are
|
---|
| 21 | * inserted as well. Note that the mapping between commands and functions
|
---|
| 22 | * called is implicit in the table. Changing the mapping just implies
|
---|
| 23 | * changing the pointers in this table.
|
---|
| 24 | *
|
---|
| 25 | * The display consists of SCREENMAX + 1 lines and XMAX + 1 characters. When
|
---|
| 26 | * a line is larger (or gets larger during editing) than XBREAK characters,
|
---|
| 27 | * the line is either shifted SHIFT_SIZE characters to the left (which means
|
---|
| 28 | * that the first SHIFT_SIZE characters are not printed) or the end of the
|
---|
| 29 | * line is marked with the SHIFT_MARK character and the rest of the line is
|
---|
| 30 | * not printed. A line can never exceed MAX_CHARS characters. Mined will
|
---|
| 31 | * always try to keep the cursor on the same line and same (relative)
|
---|
| 32 | * x-coordinate if nothing changed. So if you scroll one line up, the cursor
|
---|
| 33 | * stays on the same line, or when you move one line down, the cursor will
|
---|
| 34 | * move to the same place on the line as it was on the previous.
|
---|
| 35 | * Every character on the line is available for editing including the
|
---|
| 36 | * linefeed at the the of the line. When the linefeed is deleted, the current
|
---|
| 37 | * line and the next line are joined. The last character of the file (which
|
---|
| 38 | * is always a linefeed) can never be deleted.
|
---|
| 39 | * The bottomline (as indicated by YMAX + 1) is used as a status line during
|
---|
| 40 | * editing. This line is usually blank or contains information mined needs
|
---|
| 41 | * during editing. This information (or rather questions) is displayed in
|
---|
| 42 | * reverse video.
|
---|
| 43 | *
|
---|
| 44 | * The terminal modes are changed completely. All signals like start/stop,
|
---|
| 45 | * interrupt etc. are unset. The only signal that remains is the quit signal.
|
---|
| 46 | * The quit signal (^\) is the general abort signal for mined. Typing a ^\
|
---|
| 47 | * during searching or when mined is asking for filenames, etc. will abort
|
---|
| 48 | * the function and mined will return to the main loop. Sending a quit
|
---|
| 49 | * signal during the main loop will abort the session (after confirmation)
|
---|
| 50 | * and the file is not (!) saved.
|
---|
| 51 | * The session will also be aborted when an unrecoverable error occurs. E.g
|
---|
| 52 | * when there is no more memory available. If the file has been modified,
|
---|
| 53 | * mined will ask if the file has to be saved or not.
|
---|
| 54 | * If there is no more space left on the disk, mined will just give an error
|
---|
| 55 | * message and continue.
|
---|
| 56 | *
|
---|
| 57 | * The number of system calls are minized. This is done to keep the editor
|
---|
| 58 | * as fast as possible. I/O is done in SCREEN_SIZE reads/writes. Accumulated
|
---|
| 59 | * output is also flushed at the end of each character typed.
|
---|
| 60 | *
|
---|
| 61 | * 2. Regular expressions
|
---|
| 62 | *
|
---|
| 63 | * Mined has a build in regular expression matcher, which is used for
|
---|
| 64 | * searching and replace routines. A regular expression consists of a
|
---|
| 65 | * sequence of:
|
---|
| 66 | *
|
---|
| 67 | * 1. A normal character matching that character.
|
---|
| 68 | * 2. A . matching any character.
|
---|
| 69 | * 3. A ^ matching the begin of a line.
|
---|
| 70 | * 4. A $ (as last character of the pattern) mathing the end of a line.
|
---|
| 71 | * 5. A \<character> matching <character>.
|
---|
| 72 | * 6. A number of characters enclosed in [] pairs matching any of these
|
---|
| 73 | * characters. A list of characters can be indicated by a '-'. So
|
---|
| 74 | * [a-z] matches any letter of the alphabet. If the first character
|
---|
| 75 | * after the '[' is a '^' then the set is negated (matching none of
|
---|
| 76 | * the characters).
|
---|
| 77 | * A ']', '^' or '-' can be escaped by putting a '\' in front of it.
|
---|
| 78 | * Of course this means that a \ must be represented by \\.
|
---|
| 79 | * 7. If one of the expressions as described in 1-6 is followed by a
|
---|
| 80 | * '*' than that expressions matches a sequence of 0 or more of
|
---|
| 81 | * that expression.
|
---|
| 82 | *
|
---|
| 83 | * Parsing of regular expression is done in two phases. In the first phase
|
---|
| 84 | * the expression is compiled into a more comprehensible form. In the second
|
---|
| 85 | * phase the actual matching is done. For more details see 3.6.
|
---|
| 86 | *
|
---|
| 87 | *
|
---|
| 88 | * 3. Implementation of mined.
|
---|
| 89 | *
|
---|
| 90 | * 3.1 Data structures.
|
---|
| 91 | *
|
---|
| 92 | * The main data structures are as follows. The whole file is kept in a
|
---|
| 93 | * double linked list of lines. The LINE structure looks like this:
|
---|
| 94 | *
|
---|
| 95 | * typedef struct Line {
|
---|
| 96 | * struct Line *next;
|
---|
| 97 | * struct Line *prev;
|
---|
| 98 | * char *text;
|
---|
| 99 | * unsigned char shift_count;
|
---|
| 100 | * } LINE;
|
---|
| 101 | *
|
---|
| 102 | * Each line entry contains a pointer to the next line, a pointer to the
|
---|
| 103 | * previous line and a pointer to the text of that line. A special field
|
---|
| 104 | * shift_count contains the number of shifts (in units of SHIFT_SIZE)
|
---|
| 105 | * that is performed on that line. The total size of the structure is 7
|
---|
| 106 | * bytes so a file consisting of 1000 empty lines will waste a lot of
|
---|
| 107 | * memory. A LINE structure is allocated for each line in the file. After
|
---|
| 108 | * that the number of characters of the line is counted and sufficient
|
---|
| 109 | * space is allocated to store them (including a linefeed and a '\0').
|
---|
| 110 | * The resulting address is assigned to the text field in the structure.
|
---|
| 111 | *
|
---|
| 112 | * A special structure is allocated and its address is assigned to the
|
---|
| 113 | * variable header as well as the variable tail. The text field of this
|
---|
| 114 | * structure is set to NIL_PTR. The tail->prev of this structure points
|
---|
| 115 | * to the last LINE of the file and the header->next to the first LINE.
|
---|
| 116 | * Other LINE *variables are top_line and bot_line which point to the
|
---|
| 117 | * first line resp. the last line on the screen.
|
---|
| 118 | * Two other variables are important as well. First the LINE *cur_line,
|
---|
| 119 | * which points to the LINE currently in use and the char *cur_text,
|
---|
| 120 | * which points to the character at which the cursor stands.
|
---|
| 121 | * Whenever an ASCII character is typed, a new line is build with this
|
---|
| 122 | * character inserted. Then the old data space (pointed to by
|
---|
| 123 | * cur_line->text) is freed, data space for the new line is allocated and
|
---|
| 124 | * assigned to cur_line->text.
|
---|
| 125 | *
|
---|
| 126 | * Two global variables called x and y represent the x and y coordinates
|
---|
| 127 | * from the cursor. The global variable nlines contains the number of
|
---|
| 128 | * lines in the file. Last_y indicates the maximum y coordinate of the
|
---|
| 129 | * screen (which is usually SCREENMAX).
|
---|
| 130 | *
|
---|
| 131 | * A few strings must be initialized by hand before compiling mined.
|
---|
| 132 | * These string are enter_string, which is printed upon entering mined,
|
---|
| 133 | * rev_video (turn on reverse video), normal_video, rev_scroll (perform a
|
---|
| 134 | * reverse scroll) and pos_string. The last string should hold the
|
---|
| 135 | * absolute position string to be printed for cursor motion. The #define
|
---|
| 136 | * X_PLUS and Y_PLUS should contain the characters to be added to the
|
---|
| 137 | * coordinates x and y (both starting at 0) to finish cursor positioning.
|
---|
| 138 | *
|
---|
| 139 | * 3.2 Starting up.
|
---|
| 140 | *
|
---|
| 141 | * Mined can be called with or without argument and the function
|
---|
| 142 | * load_file () is called with these arguments. load_file () checks
|
---|
| 143 | * if the file exists if it can be read and if it is writable and
|
---|
| 144 | * sets the writable flag accordingly. If the file can be read,
|
---|
| 145 | * load_file () reads a line from the file and stores this line into
|
---|
| 146 | * a structure by calling install_line () and line_insert () which
|
---|
| 147 | * installs the line into the double linked list, until the end of the
|
---|
| 148 | * file is reached.
|
---|
| 149 | * Lines are read by the function get_line (), which buffers the
|
---|
| 150 | * reading in blocks of SCREEN_SIZE. Load_file () also initializes the
|
---|
| 151 | * LINE *variables described above.
|
---|
| 152 | *
|
---|
| 153 | * 3.3 Moving around.
|
---|
| 154 | *
|
---|
| 155 | * Several commands are implemented for moving through the file.
|
---|
| 156 | * Moving up (UP), down (DN) left (LF) and right (RT) are done by the
|
---|
| 157 | * arrow keys. Moving one line below the screen scrolls the screen one
|
---|
| 158 | * line up. Moving one line above the screen scrolls the screen one line
|
---|
| 159 | * down. The functions forward_scroll () and reverse_scroll () take care
|
---|
| 160 | * of that.
|
---|
| 161 | * Several other move functions exist: move to begin of line (BL), end of
|
---|
| 162 | * line (EL) top of screen (HIGH), bottom of screen (LOW), top of file
|
---|
| 163 | * (HO), end of file (EF), scroll one page down (PD), scroll one page up
|
---|
| 164 | * (PU), scroll one line down (SD), scroll one line up (SU) and move to a
|
---|
| 165 | * certain line number (GOTO).
|
---|
| 166 | * Two functions called MN () and MP () each move one word further or
|
---|
| 167 | * backwards. A word is a number of non-blanks seperated by a space, a
|
---|
| 168 | * tab or a linefeed.
|
---|
| 169 | *
|
---|
| 170 | * 3.4 Modifying text.
|
---|
| 171 | *
|
---|
| 172 | * The modifying commands can be separated into two modes. The first
|
---|
| 173 | * being inserting text, and the other deleting text. Two functions are
|
---|
| 174 | * created for these purposes: insert () and delete (). Both are capable
|
---|
| 175 | * of deleting or inserting large amounts of text as well as one
|
---|
| 176 | * character. Insert () must be given the line and location at which
|
---|
| 177 | * the text must be inserted. Is doesn't make any difference whether this
|
---|
| 178 | * text contains linefeeds or not. Delete () must be given a pointer to
|
---|
| 179 | * the start line, a pointer from where deleting should start on that
|
---|
| 180 | * line and the same information about the end position. The last
|
---|
| 181 | * character of the file will never be deleted. Delete () will make the
|
---|
| 182 | * necessary changes to the screen after deleting, but insert () won't.
|
---|
| 183 | * The functions for modifying text are: insert one char (S), insert a
|
---|
| 184 | * file (file_insert (fd)), insert a linefeed and put cursor back to
|
---|
| 185 | * end of line (LIB), delete character under the cursor (DCC), delete
|
---|
| 186 | * before cursor (even linefeed) (DPC), delete next word (DNW), delete
|
---|
| 187 | * previous word (DPC) and delete to end of line (if the cursor is at
|
---|
| 188 | * a linefeed delete line) (DLN).
|
---|
| 189 | *
|
---|
| 190 | * 3.5 Yanking.
|
---|
| 191 | *
|
---|
| 192 | * A few utilities are provided for yanking pieces of text. The function
|
---|
| 193 | * MA () marks the current position in the file. This is done by setting
|
---|
| 194 | * LINE *mark_line and char *mark_text to the current position. Yanking
|
---|
| 195 | * of text can be done in two modes. The first mode just copies the text
|
---|
| 196 | * from the mark to the current position (or visa versa) into a buffer
|
---|
| 197 | * (YA) and the second also deletes the text (DT). Both functions call
|
---|
| 198 | * the function set_up () with the delete flag on or off. Set_up ()
|
---|
| 199 | * checks if the marked position is still a valid one (by using
|
---|
| 200 | * check_mark () and legal ()), and then calls the function yank () with
|
---|
| 201 | * a start and end position in the file. This function copies the text
|
---|
| 202 | * into a scratch_file as indicated by the variable yank_file. This
|
---|
| 203 | * scratch_file is made uniq by the function scratch_file (). At the end
|
---|
| 204 | * of copying yank will (if necessary) delete the text. A global flag
|
---|
| 205 | * called yank_status keeps track of the buffer (or file) status. It is
|
---|
| 206 | * initialized on NOT_VALID and set to EMPTY (by set_up ()) or VALID (by
|
---|
| 207 | * yank ()). Several things can be done with the buffer. It can be
|
---|
| 208 | * inserted somewhere else in the file (PT) or it can be copied into
|
---|
| 209 | * another file (WB), which will be prompted for.
|
---|
| 210 | *
|
---|
| 211 | * 3.6 Search and replace routines.
|
---|
| 212 | *
|
---|
| 213 | * Searching for strings and replacing strings are done by regular
|
---|
| 214 | * expressions. For any expression the function compile () is called
|
---|
| 215 | * with as argument the expression to compile. Compile () returns a
|
---|
| 216 | * pointer to a structure which looks like this:
|
---|
| 217 | *
|
---|
| 218 | * typedef struct regex {
|
---|
| 219 | * union {
|
---|
| 220 | * char *err_mess;
|
---|
| 221 | * int *expression;
|
---|
| 222 | * } result;
|
---|
| 223 | * char status;
|
---|
| 224 | * char *start_ptr;
|
---|
| 225 | * char *end_ptr;
|
---|
| 226 | * } REGEX;
|
---|
| 227 | *
|
---|
| 228 | * If something went wrong during compiling (e.g. an illegal expression
|
---|
| 229 | * was given), the function reg_error () is called, which sets the status
|
---|
| 230 | * field to REG_ERROR and the err_mess field to the error message. If the
|
---|
| 231 | * match must be anchored at the beginning of the line (end of line), the
|
---|
| 232 | * status field is set to BEGIN_LINE (END_LINE). If none of these special
|
---|
| 233 | * cases are true, the field is set to 0 and the function finished () is
|
---|
| 234 | * called. Finished () allocates space to hold the compiled expression
|
---|
| 235 | * and copies this expression into the expression field of the union
|
---|
| 236 | * (bcopy ()). Matching is done by the routines match() and line_check().
|
---|
| 237 | * Match () takes as argument the REGEX *program, a pointer to the
|
---|
| 238 | * startposition on the current line, and a flag indicating FORWARD or
|
---|
| 239 | * REVERSE search. Match () checks out the whole file until a match is
|
---|
| 240 | * found. If match is found it returns a pointer to the line in which the
|
---|
| 241 | * match was found else it returns a NIL_LINE. Line_check () takes the
|
---|
| 242 | * same arguments, but return either MATCH or NO_MATCH.
|
---|
| 243 | * During checking, the start_ptr and end_ptr fields of the REGEX
|
---|
| 244 | * structure are assigned to the start and end of the match.
|
---|
| 245 | * Both functions try to find a match by walking through the line
|
---|
| 246 | * character by character. For each possibility, the function
|
---|
| 247 | * check_string () is called with as arguments the REGEX *program and the
|
---|
| 248 | * string to search in. It starts walking through the expression until
|
---|
| 249 | * the end of the expression or the end of the string is reached.
|
---|
| 250 | * Whenever a * is encountered, this position of the string is marked,
|
---|
| 251 | * the maximum number of matches are performed and the function star ()
|
---|
| 252 | * is called in order to try to find the longest match possible. Star ()
|
---|
| 253 | * takes as arguments the REGEX program, the current position of the
|
---|
| 254 | * string, the marked position and the current position of the expression
|
---|
| 255 | * Star () walks from the current position of the string back to the
|
---|
| 256 | * marked position, and calls string_check () in order to find a match.
|
---|
| 257 | * It returns MATCH or NO_MATCH, just as string_check () does.
|
---|
| 258 | * Searching is now easy. Both search routines (forward (SF) and
|
---|
| 259 | * backwards search (SR)) call search () with an apropiate message and a
|
---|
| 260 | * flag indicating FORWARD or REVERSE search. Search () will get an
|
---|
| 261 | * expression from the user by calling get_expression(). Get_expression()
|
---|
| 262 | * returns a pointer to a REGEX structure or NIL_REG upon errors and
|
---|
| 263 | * prompts for the expression. If no expression if given, the previous is
|
---|
| 264 | * used instead. After that search will call match (), and if a match is
|
---|
| 265 | * found, we can move to that place in the file by the functions find_x()
|
---|
| 266 | * and find_y () which will find display the match on the screen.
|
---|
| 267 | * Replacing can be done in two ways. A global replace (GR) or a line
|
---|
| 268 | * replace (LR). Both functions call change () with a message an a flag
|
---|
| 269 | * indicating global or line replacement. Change () will prompt for the
|
---|
| 270 | * expression and for the replacement. Every & in the replacement pattern
|
---|
| 271 | * means substitute the match instead. An & can be escaped by a \. When
|
---|
| 272 | * a match is found, the function substitute () will perform the
|
---|
| 273 | * substitution.
|
---|
| 274 | *
|
---|
| 275 | * 3.6 Miscellaneous commands.
|
---|
| 276 | *
|
---|
| 277 | * A few commands haven't be discussed yet. These are redraw the screen
|
---|
| 278 | * (RD) fork a shell (SH), print file status (FS), write file to disc
|
---|
| 279 | * (WT), insert a file at current position (IF), leave editor (XT) and
|
---|
| 280 | * visit another file (VI). The last two functions will check if the file
|
---|
| 281 | * has been modified. If it has, they will ask if you want to save the
|
---|
| 282 | * file by calling ask_save ().
|
---|
| 283 | * The function ESC () will repeat a command n times. It will prompt for
|
---|
| 284 | * the number. Aborting the loop can be done by sending the ^\ signal.
|
---|
| 285 | *
|
---|
| 286 | * 3.7 Utility functions.
|
---|
| 287 | *
|
---|
| 288 | * Several functions exists for internal use. First allocation routines:
|
---|
| 289 | * alloc (bytes) and newline () will return a pointer to free data space
|
---|
| 290 | * if the given size. If there is no more memory available, the function
|
---|
| 291 | * panic () is called.
|
---|
| 292 | * Signal handling: The only signal that can be send to mined is the
|
---|
| 293 | * SIGQUIT signal. This signal, functions as a general abort command.
|
---|
| 294 | * Mined will abort if the signal is given during the main loop. The
|
---|
| 295 | * function abort_mined () takes care of that.
|
---|
| 296 | * Panic () is a function with as argument a error message. It will print
|
---|
| 297 | * the message and the error number set by the kernel (errno) and will
|
---|
| 298 | * ask if the file must be saved or not. It resets the terminal
|
---|
| 299 | * (raw_mode ()) and exits.
|
---|
| 300 | * String handling routines like copy_string(to, from), length_of(string)
|
---|
| 301 | * and build_string (buffer, format, arg1, arg2, ...). The latter takes
|
---|
| 302 | * a description of the string out out the format field and puts the
|
---|
| 303 | * result in the buffer. (It works like printf (3), but then into a
|
---|
| 304 | * string). The functions status_line (string1, string2), error (string1,
|
---|
| 305 | * string2), clear_status () and bottom_line () all print information on
|
---|
| 306 | * the status line.
|
---|
| 307 | * Get_string (message, buffer) reads a string and getchar () reads one
|
---|
| 308 | * character from the terminal.
|
---|
| 309 | * Num_out ((long) number) prints the number into a 11 digit field
|
---|
| 310 | * without leading zero's. It returns a pointer to the resulting string.
|
---|
| 311 | * File_status () prints all file information on the status line.
|
---|
| 312 | * Set_cursor (x, y) prints the string to put the cursor at coordinates
|
---|
| 313 | * x and y.
|
---|
| 314 | * Output is done by four functions: writeline(fd,string), clear_buffer()
|
---|
| 315 | * write_char (fd, c) and flush_buffer (fd). Three defines are provided
|
---|
| 316 | * to write on filedescriptor STD_OUT (terminal) which is used normally:
|
---|
| 317 | * string_print (string), putchar (c) and flush (). All these functions
|
---|
| 318 | * use the global I/O buffer screen and the global index for this array
|
---|
| 319 | * called out_count. In this way I/O can be buffered, so that reads or
|
---|
| 320 | * writes can be done in blocks of SCREEN_SIZE size.
|
---|
| 321 | * The following functions all handle internal line maintenance. The
|
---|
| 322 | * function proceed (start_line, count) returns the count'th line after
|
---|
| 323 | * start_line. If count is negative, the count'th line before the
|
---|
| 324 | * start_line is returned. If header or tail is encountered then that
|
---|
| 325 | * will be returned. Display (x, y, start_line, count) displays count
|
---|
| 326 | * lines starting at coordinates [x, y] and beginning at start_line. If
|
---|
| 327 | * the header or tail is encountered, empty lines are displayed instead.
|
---|
| 328 | * The function reset (head_line, ny) reset top_line, last_y, bot_line,
|
---|
| 329 | * cur_line and y-coordinate. This is not a neat way to do the
|
---|
| 330 | * maintenance, but it sure saves a lot of code. It is usually used in
|
---|
| 331 | * combination with display ().
|
---|
| 332 | * Put_line(line, offset, clear_line), prints a line (skipping characters
|
---|
| 333 | * according to the line->shift_size field) until XBREAK - offset
|
---|
| 334 | * characters are printed or a '\n' is encountered. If clear_line is
|
---|
| 335 | * TRUE, spaces are printed until XBREAK - offset characters.
|
---|
| 336 | * Line_print (line) is a #define from put_line (line, 0, TRUE).
|
---|
| 337 | * Moving is done by the functions move_to (x, y), move_addres (address)
|
---|
| 338 | * and move (x, adress, y). This function is the most important one in
|
---|
| 339 | * mined. New_y must be between 0 and last_y, new_x can be about
|
---|
| 340 | * anything, address must be a pointer to an character on the current
|
---|
| 341 | * line (or y). Move_to () first adjust the y coordinate together with
|
---|
| 342 | * cur_line. If an address is given, it finds the corresponding
|
---|
| 343 | * x-coordinate. If an new x-coordinate was given, it will try to locate
|
---|
| 344 | * the corresponding character. After that it sets the shift_count field
|
---|
| 345 | * of cur_line to an apropiate number according to new_x. The only thing
|
---|
| 346 | * left to do now is to assign the new values to cur_line, cur_text, x
|
---|
| 347 | * and y.
|
---|
| 348 | *
|
---|
| 349 | * 4. Summary of commands.
|
---|
| 350 | *
|
---|
| 351 | * CURSOR MOTION
|
---|
| 352 | * up-arrow Move cursor 1 line up. At top of screen, reverse scroll
|
---|
| 353 | * down-arrow Move cursor 1 line down. At bottom, scroll forward.
|
---|
| 354 | * left-arrow Move cursor 1 character left or to end of previous line
|
---|
| 355 | * right-arrow Move cursor 1 character right or to start of next line
|
---|
| 356 | * CTRL-A Move cursor to start of current line
|
---|
| 357 | * CTRL-Z Move cursor to end of current line
|
---|
| 358 | * CTRL-^ Move cursor to top of screen
|
---|
| 359 | * CTRL-_ Move cursor to bottom of screen
|
---|
| 360 | * CTRL-F Forward to start of next word (even to next line)
|
---|
| 361 | * CTRL-B Backward to first character of previous word
|
---|
| 362 | *
|
---|
| 363 | * SCREEN MOTION
|
---|
| 364 | * Home key Move cursor to first character of file
|
---|
| 365 | * End key Move cursor to last character of file
|
---|
| 366 | * PgUp Scroll backward 1 page. Bottom line becomes top line
|
---|
| 367 | * PgD Scroll backward 1 page. Top line becomes bottom line
|
---|
| 368 | * CTRL-D Scroll screen down one line (reverse scroll)
|
---|
| 369 | * CTRL-U Scroll screen up one line (forward scroll)
|
---|
| 370 | *
|
---|
| 371 | * MODIFYING TEXT
|
---|
| 372 | * ASCII char Self insert character at cursor
|
---|
| 373 | * tab Insert tab at cursor
|
---|
| 374 | * backspace Delete the previous char (left of cursor), even line feed
|
---|
| 375 | * Del Delete the character under the cursor
|
---|
| 376 | * CTRL-N Delete next word
|
---|
| 377 | * CTRL-P Delete previous word
|
---|
| 378 | * CTRL-O Insert line feed at cursor and back up 1 character
|
---|
| 379 | * CTRL-T Delete tail of line (cursor to end); if empty, delete line
|
---|
| 380 | * CTRL-@ Set the mark (remember the current location)
|
---|
| 381 | * CTRL-K Delete text from the mark to current position save on file
|
---|
| 382 | * CTRL-C Save the text from the mark to the current position
|
---|
| 383 | * CTRL-Y Insert the contents of the save file at current position
|
---|
| 384 | * CTRL-Q Insert the contents of the save file into a new file
|
---|
| 385 | * CTRL-G Insert a file at the current position
|
---|
| 386 | *
|
---|
| 387 | * MISCELLANEOUS
|
---|
| 388 | * CTRL-E Erase and redraw the screen
|
---|
| 389 | * CTRL-V Visit file (read a new file); complain if old one changed
|
---|
| 390 | * CTRL-W Write the current file back to the disk
|
---|
| 391 | * numeric + Search forward (prompt for regular expression)
|
---|
| 392 | * numeric - Search backward (prompt for regular expression)
|
---|
| 393 | * numeric 5 Print the current status of the file
|
---|
| 394 | * CTRL-R (Global) Replace str1 by str2 (prompts for each string)
|
---|
| 395 | * CTRL-L (Line) Replace string1 by string2
|
---|
| 396 | * CTRL-S Fork off a shell and wait for it to finish
|
---|
| 397 | * CTRL-X EXIT (prompt if file modified)
|
---|
| 398 | * CTRL-] Go to a line. Prompts for linenumber
|
---|
| 399 | * CTRL-\ Abort whatever editor was doing and start again
|
---|
| 400 | * escape key Repeat a command count times; (prompts for count)
|
---|
| 401 | */
|
---|
| 402 |
|
---|
| 403 | /* ======================================================================== *
|
---|
| 404 | * Utilities *
|
---|
| 405 | * ======================================================================== */
|
---|
| 406 |
|
---|
| 407 | #include "mined.h"
|
---|
| 408 | #include <signal.h>
|
---|
| 409 | #include <termios.h>
|
---|
| 410 | #include <limits.h>
|
---|
| 411 | #include <errno.h>
|
---|
| 412 | #include <sys/wait.h>
|
---|
| 413 | #include <sys/ioctl.h>
|
---|
| 414 | #if __STDC__
|
---|
| 415 | #include <stdarg.h>
|
---|
| 416 | #else
|
---|
| 417 | #include <varargs.h>
|
---|
| 418 | #endif
|
---|
| 419 |
|
---|
| 420 | extern int errno;
|
---|
| 421 | int ymax = YMAX;
|
---|
| 422 | int screenmax = SCREENMAX;
|
---|
| 423 |
|
---|
| 424 |
|
---|
| 425 | /*
|
---|
| 426 | * Print file status.
|
---|
| 427 | */
|
---|
| 428 | void FS()
|
---|
| 429 | {
|
---|
| 430 | fstatus(file_name[0] ? "" : "[buffer]", -1L);
|
---|
| 431 | }
|
---|
| 432 |
|
---|
| 433 | /*
|
---|
| 434 | * Visit (edit) another file. If the file has been modified, ask the user if
|
---|
| 435 | * he wants to save it.
|
---|
| 436 | */
|
---|
| 437 | void VI()
|
---|
| 438 | {
|
---|
| 439 | char new_file[LINE_LEN]; /* Buffer to hold new file name */
|
---|
| 440 |
|
---|
| 441 | if (modified == TRUE && ask_save() == ERRORS)
|
---|
| 442 | return;
|
---|
| 443 |
|
---|
| 444 | /* Get new file name */
|
---|
| 445 | if (get_file("Visit file:", new_file) == ERRORS)
|
---|
| 446 | return;
|
---|
| 447 |
|
---|
| 448 | /* Free old linked list, initialize global variables and load new file */
|
---|
| 449 | initialize();
|
---|
| 450 | #ifdef UNIX
|
---|
| 451 | tputs(CL, 0, _putchar);
|
---|
| 452 | #else
|
---|
| 453 | string_print (enter_string);
|
---|
| 454 | #endif /* UNIX */
|
---|
| 455 | load_file(new_file[0] == '\0' ? NIL_PTR : new_file);
|
---|
| 456 | }
|
---|
| 457 |
|
---|
| 458 | /*
|
---|
| 459 | * Write file in core to disc.
|
---|
| 460 | */
|
---|
| 461 | int WT()
|
---|
| 462 | {
|
---|
| 463 | register LINE *line;
|
---|
| 464 | register long count = 0L; /* Nr of chars written */
|
---|
| 465 | char file[LINE_LEN]; /* Buffer for new file name */
|
---|
| 466 | int fd; /* Filedescriptor of file */
|
---|
| 467 |
|
---|
| 468 | if (modified == FALSE) {
|
---|
| 469 | error ("Write not necessary.", NIL_PTR);
|
---|
| 470 | return FINE;
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | /* Check if file_name is valid and if file can be written */
|
---|
| 474 | if (file_name[0] == '\0' || writable == FALSE) {
|
---|
| 475 | if (get_file("Enter file name:", file) != FINE)
|
---|
| 476 | return ERRORS;
|
---|
| 477 | copy_string(file_name, file); /* Save file name */
|
---|
| 478 | }
|
---|
| 479 | if ((fd = creat(file_name, 0644)) < 0) { /* Empty file */
|
---|
| 480 | error("Cannot create ", file_name);
|
---|
| 481 | writable = FALSE;
|
---|
| 482 | return ERRORS;
|
---|
| 483 | }
|
---|
| 484 | else
|
---|
| 485 | writable = TRUE;
|
---|
| 486 |
|
---|
| 487 | clear_buffer();
|
---|
| 488 |
|
---|
| 489 | status_line("Writing ", file_name);
|
---|
| 490 | for (line = header->next; line != tail; line = line->next) {
|
---|
| 491 | if (line->shift_count & DUMMY) {
|
---|
| 492 | if (line->next == tail && line->text[0] == '\n')
|
---|
| 493 | continue;
|
---|
| 494 | }
|
---|
| 495 | if (writeline(fd, line->text) == ERRORS) {
|
---|
| 496 | count = -1L;
|
---|
| 497 | break;
|
---|
| 498 | }
|
---|
| 499 | count += (long) length_of(line->text);
|
---|
| 500 | }
|
---|
| 501 |
|
---|
| 502 | if (count > 0L && flush_buffer(fd) == ERRORS)
|
---|
| 503 | count = -1L;
|
---|
| 504 |
|
---|
| 505 | (void) close(fd);
|
---|
| 506 |
|
---|
| 507 | if (count == -1L)
|
---|
| 508 | return ERRORS;
|
---|
| 509 |
|
---|
| 510 | modified = FALSE;
|
---|
| 511 | rpipe = FALSE; /* File name is now assigned */
|
---|
| 512 |
|
---|
| 513 | /* Display how many chars (and lines) were written */
|
---|
| 514 | fstatus("Wrote", count);
|
---|
| 515 | return FINE;
|
---|
| 516 | }
|
---|
| 517 |
|
---|
| 518 | /* Call WT and discard value returned. */
|
---|
| 519 | void XWT()
|
---|
| 520 | {
|
---|
| 521 | (void) WT();
|
---|
| 522 | }
|
---|
| 523 |
|
---|
| 524 |
|
---|
| 525 |
|
---|
| 526 | /*
|
---|
| 527 | * Call an interactive shell.
|
---|
| 528 | */
|
---|
| 529 | void SH()
|
---|
| 530 | {
|
---|
| 531 | register int w;
|
---|
| 532 | int pid, status;
|
---|
| 533 | char *shell;
|
---|
| 534 |
|
---|
| 535 | if ((shell = getenv("SHELL")) == NIL_PTR) shell = "/bin/sh";
|
---|
| 536 |
|
---|
| 537 | switch (pid = fork()) {
|
---|
| 538 | case -1: /* Error */
|
---|
| 539 | error("Cannot fork.", NIL_PTR);
|
---|
| 540 | return;
|
---|
| 541 | case 0: /* This is the child */
|
---|
| 542 | set_cursor(0, ymax);
|
---|
| 543 | putchar('\n');
|
---|
| 544 | flush();
|
---|
| 545 | raw_mode(OFF);
|
---|
| 546 | if (rpipe) { /* Fix stdin */
|
---|
| 547 | close (0);
|
---|
| 548 | if (open("/dev/tty", 0) < 0)
|
---|
| 549 | exit (126);
|
---|
| 550 | }
|
---|
| 551 | execl(shell, shell, (char *) 0);
|
---|
| 552 | exit(127); /* Exit with 127 */
|
---|
| 553 | default : /* This is the parent */
|
---|
| 554 | signal(SIGINT, SIG_IGN);
|
---|
| 555 | signal(SIGQUIT, SIG_IGN);
|
---|
| 556 | do {
|
---|
| 557 | w = wait(&status);
|
---|
| 558 | } while (w != -1 && w != pid);
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | raw_mode(ON);
|
---|
| 562 | RD();
|
---|
| 563 |
|
---|
| 564 | if ((status >> 8) == 127) /* Child died with 127 */
|
---|
| 565 | error("Cannot exec ", shell);
|
---|
| 566 | else if ((status >> 8) == 126)
|
---|
| 567 | error("Cannot open /dev/tty as fd #0", NIL_PTR);
|
---|
| 568 | }
|
---|
| 569 |
|
---|
| 570 | /*
|
---|
| 571 | * Proceed returns the count'th line after `line'. When count is negative
|
---|
| 572 | * it returns the count'th line before `line'. When the next (previous)
|
---|
| 573 | * line is the tail (header) indicating EOF (tof) it stops.
|
---|
| 574 | */
|
---|
| 575 | LINE *proceed(line, count)
|
---|
| 576 | register LINE *line;
|
---|
| 577 | register int count;
|
---|
| 578 | {
|
---|
| 579 | if (count < 0)
|
---|
| 580 | while (count++ < 0 && line != header)
|
---|
| 581 | line = line->prev;
|
---|
| 582 | else
|
---|
| 583 | while (count-- > 0 && line != tail)
|
---|
| 584 | line = line->next;
|
---|
| 585 | return line;
|
---|
| 586 | }
|
---|
| 587 |
|
---|
| 588 | /*
|
---|
| 589 | * Show concatenation of s1 and s2 on the status line (bottom of screen)
|
---|
| 590 | * If revfl is TRUE, turn on reverse video on both strings. Set stat_visible
|
---|
| 591 | * only if bottom_line is visible.
|
---|
| 592 | */
|
---|
| 593 | int bottom_line(revfl, s1, s2, inbuf, statfl)
|
---|
| 594 | FLAG revfl;
|
---|
| 595 | char *s1, *s2;
|
---|
| 596 | char *inbuf;
|
---|
| 597 | FLAG statfl;
|
---|
| 598 | {
|
---|
| 599 | int ret = FINE;
|
---|
| 600 | char buf[LINE_LEN];
|
---|
| 601 | register char *p = buf;
|
---|
| 602 |
|
---|
| 603 | *p++ = ' ';
|
---|
| 604 | if (s1 != NIL_PTR)
|
---|
| 605 | while (*p = *s1++)
|
---|
| 606 | p++;
|
---|
| 607 | if (s2 != NIL_PTR)
|
---|
| 608 | while (*p = *s2++)
|
---|
| 609 | p++;
|
---|
| 610 | *p++ = ' ';
|
---|
| 611 | *p++ = 0;
|
---|
| 612 |
|
---|
| 613 | if (revfl == ON && stat_visible == TRUE)
|
---|
| 614 | clear_status ();
|
---|
| 615 | set_cursor(0, ymax);
|
---|
| 616 | if (revfl == ON) { /* Print rev. start sequence */
|
---|
| 617 | #ifdef UNIX
|
---|
| 618 | tputs(SO, 0, _putchar);
|
---|
| 619 | #else
|
---|
| 620 | string_print(rev_video);
|
---|
| 621 | #endif /* UNIX */
|
---|
| 622 | stat_visible = TRUE;
|
---|
| 623 | }
|
---|
| 624 | else /* Used as clear_status() */
|
---|
| 625 | stat_visible = FALSE;
|
---|
| 626 |
|
---|
| 627 | string_print(buf);
|
---|
| 628 |
|
---|
| 629 | if (inbuf != NIL_PTR)
|
---|
| 630 | ret = input(inbuf, statfl);
|
---|
| 631 |
|
---|
| 632 | /* Print normal video */
|
---|
| 633 | #ifdef UNIX
|
---|
| 634 | tputs(SE, 0, _putchar);
|
---|
| 635 | tputs(CE, 0, _putchar);
|
---|
| 636 | #else
|
---|
| 637 | string_print(normal_video);
|
---|
| 638 | string_print(blank_line); /* Clear the rest of the line */
|
---|
| 639 | #endif /* UNIX */
|
---|
| 640 | if (inbuf != NIL_PTR)
|
---|
| 641 | set_cursor(0, ymax);
|
---|
| 642 | else
|
---|
| 643 | set_cursor(x, y); /* Set cursor back to old position */
|
---|
| 644 | flush(); /* Perform the actual write */
|
---|
| 645 | if (ret != FINE)
|
---|
| 646 | clear_status();
|
---|
| 647 | return ret;
|
---|
| 648 | }
|
---|
| 649 |
|
---|
| 650 | /*
|
---|
| 651 | * Count_chars() count the number of chars that the line would occupy on the
|
---|
| 652 | * screen. Counting starts at the real x-coordinate of the line.
|
---|
| 653 | */
|
---|
| 654 | int count_chars(line)
|
---|
| 655 | LINE *line;
|
---|
| 656 | {
|
---|
| 657 | register int cnt = get_shift(line->shift_count) * -SHIFT_SIZE;
|
---|
| 658 | register char *textp = line->text;
|
---|
| 659 |
|
---|
| 660 | /* Find begin of line on screen */
|
---|
| 661 | while (cnt < 0) {
|
---|
| 662 | if (is_tab(*textp++))
|
---|
| 663 | cnt = tab(cnt);
|
---|
| 664 | else
|
---|
| 665 | cnt++;
|
---|
| 666 | }
|
---|
| 667 |
|
---|
| 668 | /* Count number of chars left */
|
---|
| 669 | cnt = 0;
|
---|
| 670 | while (*textp != '\n') {
|
---|
| 671 | if (is_tab(*textp++))
|
---|
| 672 | cnt = tab(cnt);
|
---|
| 673 | else
|
---|
| 674 | cnt++;
|
---|
| 675 | }
|
---|
| 676 | return cnt;
|
---|
| 677 | }
|
---|
| 678 |
|
---|
| 679 | /*
|
---|
| 680 | * Move to coordinates nx, ny at screen. The caller must check that scrolling
|
---|
| 681 | * is not needed.
|
---|
| 682 | * If new_x is lower than 0 or higher than XBREAK, move_to() will check if
|
---|
| 683 | * the line can be shifted. If it can it sets(or resets) the shift_count field
|
---|
| 684 | * of the current line accordingly.
|
---|
| 685 | * Move also sets cur_text to the right char.
|
---|
| 686 | * If we're moving to the same x coordinate, try to move the the x-coordinate
|
---|
| 687 | * used on the other previous call.
|
---|
| 688 | */
|
---|
| 689 | void move(new_x, new_address, new_y)
|
---|
| 690 | register int new_x;
|
---|
| 691 | int new_y;
|
---|
| 692 | char *new_address;
|
---|
| 693 | {
|
---|
| 694 | register LINE *line = cur_line; /* For building new cur_line */
|
---|
| 695 | int shift = 0; /* How many shifts to make */
|
---|
| 696 | static int rel_x = 0; /* Remember relative x position */
|
---|
| 697 | int tx = x;
|
---|
| 698 |
|
---|
| 699 | /* Check for illegal values */
|
---|
| 700 | if (new_y < 0 || new_y > last_y)
|
---|
| 701 | return;
|
---|
| 702 |
|
---|
| 703 | /* Adjust y-coordinate and cur_line */
|
---|
| 704 | if (new_y < y)
|
---|
| 705 | while (y != new_y) {
|
---|
| 706 | y--;
|
---|
| 707 | line = line->prev;
|
---|
| 708 | }
|
---|
| 709 | else
|
---|
| 710 | while (y != new_y) {
|
---|
| 711 | y++;
|
---|
| 712 | line = line->next;
|
---|
| 713 | }
|
---|
| 714 |
|
---|
| 715 | /* Set or unset relative x-coordinate */
|
---|
| 716 | if (new_address == NIL_PTR) {
|
---|
| 717 | new_address = find_address(line, (new_x == x) ? rel_x : new_x , &tx);
|
---|
| 718 | if (new_x != x)
|
---|
| 719 | rel_x = tx;
|
---|
| 720 | new_x = tx;
|
---|
| 721 | }
|
---|
| 722 | else
|
---|
| 723 | rel_x = new_x = find_x(line, new_address);
|
---|
| 724 |
|
---|
| 725 | /* Adjust shift_count if new_x lower than 0 or higher than XBREAK */
|
---|
| 726 | if (new_x < 0 || new_x >= XBREAK) {
|
---|
| 727 | if (new_x > XBREAK || (new_x == XBREAK && *new_address != '\n'))
|
---|
| 728 | shift = (new_x - XBREAK) / SHIFT_SIZE + 1;
|
---|
| 729 | else {
|
---|
| 730 | shift = new_x / SHIFT_SIZE;
|
---|
| 731 | if (new_x % SHIFT_SIZE)
|
---|
| 732 | shift--;
|
---|
| 733 | }
|
---|
| 734 |
|
---|
| 735 | if (shift != 0) {
|
---|
| 736 | line->shift_count += shift;
|
---|
| 737 | new_x = find_x(line, new_address);
|
---|
| 738 | set_cursor(0, y);
|
---|
| 739 | line_print(line);
|
---|
| 740 | rel_x = new_x;
|
---|
| 741 | }
|
---|
| 742 | }
|
---|
| 743 |
|
---|
| 744 | /* Assign and position cursor */
|
---|
| 745 | x = new_x;
|
---|
| 746 | cur_text = new_address;
|
---|
| 747 | cur_line = line;
|
---|
| 748 | set_cursor(x, y);
|
---|
| 749 | }
|
---|
| 750 |
|
---|
| 751 | /*
|
---|
| 752 | * Find_x() returns the x coordinate belonging to address.
|
---|
| 753 | * (Tabs are expanded).
|
---|
| 754 | */
|
---|
| 755 | int find_x(line, address)
|
---|
| 756 | LINE *line;
|
---|
| 757 | char *address;
|
---|
| 758 | {
|
---|
| 759 | register char *textp = line->text;
|
---|
| 760 | register int nx = get_shift(line->shift_count) * -SHIFT_SIZE;
|
---|
| 761 |
|
---|
| 762 | while (textp != address && *textp != '\0') {
|
---|
| 763 | if (is_tab(*textp++)) /* Expand tabs */
|
---|
| 764 | nx = tab(nx);
|
---|
| 765 | else
|
---|
| 766 | nx++;
|
---|
| 767 | }
|
---|
| 768 | return nx;
|
---|
| 769 | }
|
---|
| 770 |
|
---|
| 771 | /*
|
---|
| 772 | * Find_address() returns the pointer in the line with offset x_coord.
|
---|
| 773 | * (Tabs are expanded).
|
---|
| 774 | */
|
---|
| 775 | char *find_address(line, x_coord, old_x)
|
---|
| 776 | LINE *line;
|
---|
| 777 | int x_coord;
|
---|
| 778 | int *old_x;
|
---|
| 779 | {
|
---|
| 780 | register char *textp = line->text;
|
---|
| 781 | register int tx = get_shift(line->shift_count) * -SHIFT_SIZE;
|
---|
| 782 |
|
---|
| 783 | while (tx < x_coord && *textp != '\n') {
|
---|
| 784 | if (is_tab(*textp)) {
|
---|
| 785 | if (*old_x - x_coord == 1 && tab(tx) > x_coord)
|
---|
| 786 | break; /* Moving left over tab */
|
---|
| 787 | else
|
---|
| 788 | tx = tab(tx);
|
---|
| 789 | }
|
---|
| 790 | else
|
---|
| 791 | tx++;
|
---|
| 792 | textp++;
|
---|
| 793 | }
|
---|
| 794 |
|
---|
| 795 | *old_x = tx;
|
---|
| 796 | return textp;
|
---|
| 797 | }
|
---|
| 798 |
|
---|
| 799 | /*
|
---|
| 800 | * Length_of() returns the number of characters int the string `string'
|
---|
| 801 | * excluding the '\0'.
|
---|
| 802 | */
|
---|
| 803 | int length_of(string)
|
---|
| 804 | register char *string;
|
---|
| 805 | {
|
---|
| 806 | register int count = 0;
|
---|
| 807 |
|
---|
| 808 | if (string != NIL_PTR) {
|
---|
| 809 | while (*string++ != '\0')
|
---|
| 810 | count++;
|
---|
| 811 | }
|
---|
| 812 | return count;
|
---|
| 813 | }
|
---|
| 814 |
|
---|
| 815 | /*
|
---|
| 816 | * Copy_string() copies the string `from' into the string `to'. `To' must be
|
---|
| 817 | * long enough to hold `from'.
|
---|
| 818 | */
|
---|
| 819 | void copy_string(to, from)
|
---|
| 820 | register char *to;
|
---|
| 821 | register char *from;
|
---|
| 822 | {
|
---|
| 823 | while (*to++ = *from++)
|
---|
| 824 | ;
|
---|
| 825 | }
|
---|
| 826 |
|
---|
| 827 | /*
|
---|
| 828 | * Reset assigns bot_line, top_line and cur_line according to `head_line'
|
---|
| 829 | * which must be the first line of the screen, and an y-coordinate,
|
---|
| 830 | * which will be the current y-coordinate (if it isn't larger than last_y)
|
---|
| 831 | */
|
---|
| 832 | void reset(head_line, screen_y)
|
---|
| 833 | LINE *head_line;
|
---|
| 834 | int screen_y;
|
---|
| 835 | {
|
---|
| 836 | register LINE *line;
|
---|
| 837 |
|
---|
| 838 | top_line = line = head_line;
|
---|
| 839 |
|
---|
| 840 | /* Search for bot_line (might be last line in file) */
|
---|
| 841 | for (last_y = 0; last_y < nlines - 1 && last_y < screenmax
|
---|
| 842 | && line->next != tail; last_y++)
|
---|
| 843 | line = line->next;
|
---|
| 844 |
|
---|
| 845 | bot_line = line;
|
---|
| 846 | y = (screen_y > last_y) ? last_y : screen_y;
|
---|
| 847 |
|
---|
| 848 | /* Set cur_line according to the new y value */
|
---|
| 849 | cur_line = proceed(top_line, y);
|
---|
| 850 | }
|
---|
| 851 |
|
---|
| 852 | /*
|
---|
| 853 | * Set cursor at coordinates x, y.
|
---|
| 854 | */
|
---|
| 855 | void set_cursor(nx, ny)
|
---|
| 856 | int nx, ny;
|
---|
| 857 | {
|
---|
| 858 | #ifdef UNIX
|
---|
| 859 | extern char *tgoto();
|
---|
| 860 |
|
---|
| 861 | tputs(tgoto(CM, nx, ny), 0, _putchar);
|
---|
| 862 | #else
|
---|
| 863 | char text_buffer[10];
|
---|
| 864 |
|
---|
| 865 | build_string(text_buffer, pos_string, ny+1, nx+1);
|
---|
| 866 | string_print(text_buffer);
|
---|
| 867 | #endif /* UNIX */
|
---|
| 868 | }
|
---|
| 869 |
|
---|
| 870 | /*
|
---|
| 871 | * Routine to open terminal when mined is used in a pipeline.
|
---|
| 872 | */
|
---|
| 873 | void open_device()
|
---|
| 874 | {
|
---|
| 875 | if ((input_fd = open("/dev/tty", 0)) < 0)
|
---|
| 876 | panic("Cannot open /dev/tty for read");
|
---|
| 877 | }
|
---|
| 878 |
|
---|
| 879 | /*
|
---|
| 880 | * Getchar() reads one character from the terminal. The character must be
|
---|
| 881 | * masked with 0377 to avoid sign extension.
|
---|
| 882 | */
|
---|
| 883 | int getchar()
|
---|
| 884 | {
|
---|
| 885 | #ifdef UNIX
|
---|
| 886 | return (_getchar() & 0377);
|
---|
| 887 | #else
|
---|
| 888 | char c;
|
---|
| 889 |
|
---|
| 890 | if (read(input_fd, &c, 1) != 1 && quit == FALSE)
|
---|
| 891 | panic("Can't read one char from fd #0");
|
---|
| 892 |
|
---|
| 893 | return c & 0377;
|
---|
| 894 | #endif /* UNIX */
|
---|
| 895 | }
|
---|
| 896 |
|
---|
| 897 | /*
|
---|
| 898 | * Display() shows count lines on the terminal starting at the given
|
---|
| 899 | * coordinates. When the tail of the list is encountered it will fill the
|
---|
| 900 | * rest of the screen with blank_line's.
|
---|
| 901 | * When count is negative, a backwards print from `line' will be done.
|
---|
| 902 | */
|
---|
| 903 | void display(x_coord, y_coord, line, count)
|
---|
| 904 | int x_coord, y_coord;
|
---|
| 905 | register LINE *line;
|
---|
| 906 | register int count;
|
---|
| 907 | {
|
---|
| 908 | set_cursor(x_coord, y_coord);
|
---|
| 909 |
|
---|
| 910 | /* Find new startline if count is negative */
|
---|
| 911 | if (count < 0) {
|
---|
| 912 | line = proceed(line, count);
|
---|
| 913 | count = -count;
|
---|
| 914 | }
|
---|
| 915 |
|
---|
| 916 | /* Print the lines */
|
---|
| 917 | while (line != tail && count-- >= 0) {
|
---|
| 918 | line_print(line);
|
---|
| 919 | line = line->next;
|
---|
| 920 | }
|
---|
| 921 |
|
---|
| 922 | /* Print the blank lines (if any) */
|
---|
| 923 | if (loading == FALSE) {
|
---|
| 924 | while (count-- >= 0) {
|
---|
| 925 | #ifdef UNIX
|
---|
| 926 | tputs(CE, 0, _putchar);
|
---|
| 927 | #else
|
---|
| 928 | string_print(blank_line);
|
---|
| 929 | #endif /* UNIX */
|
---|
| 930 | putchar('\n');
|
---|
| 931 | }
|
---|
| 932 | }
|
---|
| 933 | }
|
---|
| 934 |
|
---|
| 935 | /*
|
---|
| 936 | * Write_char does a buffered output.
|
---|
| 937 | */
|
---|
| 938 | int write_char(fd, c)
|
---|
| 939 | int fd;
|
---|
| 940 | char c;
|
---|
| 941 | {
|
---|
| 942 | screen [out_count++] = c;
|
---|
| 943 | if (out_count == SCREEN_SIZE) /* Flush on SCREEN_SIZE chars */
|
---|
| 944 | return flush_buffer(fd);
|
---|
| 945 | return FINE;
|
---|
| 946 | }
|
---|
| 947 |
|
---|
| 948 | /*
|
---|
| 949 | * Writeline writes the given string on the given filedescriptor.
|
---|
| 950 | */
|
---|
| 951 | int writeline(fd, text)
|
---|
| 952 | register int fd;
|
---|
| 953 | register char *text;
|
---|
| 954 | {
|
---|
| 955 | while(*text)
|
---|
| 956 | if (write_char(fd, *text++) == ERRORS)
|
---|
| 957 | return ERRORS;
|
---|
| 958 | return FINE;
|
---|
| 959 | }
|
---|
| 960 |
|
---|
| 961 | /*
|
---|
| 962 | * Put_line print the given line on the standard output. If offset is not zero
|
---|
| 963 | * printing will start at that x-coordinate. If the FLAG clear_line is TRUE,
|
---|
| 964 | * then (screen) line will be cleared when the end of the line has been
|
---|
| 965 | * reached.
|
---|
| 966 | */
|
---|
| 967 | void put_line(line, offset, clear_line)
|
---|
| 968 | LINE *line; /* Line to print */
|
---|
| 969 | int offset; /* Offset to start */
|
---|
| 970 | FLAG clear_line; /* Clear to eoln if TRUE */
|
---|
| 971 | {
|
---|
| 972 | register char *textp = line->text;
|
---|
| 973 | register int count = get_shift(line->shift_count) * -SHIFT_SIZE;
|
---|
| 974 | int tab_count; /* Used in tab expansion */
|
---|
| 975 |
|
---|
| 976 | /* Skip all chars as indicated by the offset and the shift_count field */
|
---|
| 977 | while (count < offset) {
|
---|
| 978 | if (is_tab(*textp++))
|
---|
| 979 | count = tab(count);
|
---|
| 980 | else
|
---|
| 981 | count++;
|
---|
| 982 | }
|
---|
| 983 |
|
---|
| 984 | while (*textp != '\n' && count < XBREAK) {
|
---|
| 985 | if (is_tab(*textp)) { /* Expand tabs to spaces */
|
---|
| 986 | tab_count = tab(count);
|
---|
| 987 | while (count < XBREAK && count < tab_count) {
|
---|
| 988 | count++;
|
---|
| 989 | putchar(' ');
|
---|
| 990 | }
|
---|
| 991 | textp++;
|
---|
| 992 | }
|
---|
| 993 | else {
|
---|
| 994 | if (*textp >= '\01' && *textp <= '\037') {
|
---|
| 995 | #ifdef UNIX
|
---|
| 996 | tputs(SO, 0, _putchar);
|
---|
| 997 | #else
|
---|
| 998 | string_print (rev_video);
|
---|
| 999 | #endif /* UNIX */
|
---|
| 1000 | putchar(*textp++ + '\100');
|
---|
| 1001 | #ifdef UNIX
|
---|
| 1002 | tputs(SE, 0, _putchar);
|
---|
| 1003 | #else
|
---|
| 1004 | string_print (normal_video);
|
---|
| 1005 | #endif /* UNIX */
|
---|
| 1006 | }
|
---|
| 1007 | else
|
---|
| 1008 | putchar(*textp++);
|
---|
| 1009 | count++;
|
---|
| 1010 | }
|
---|
| 1011 | }
|
---|
| 1012 |
|
---|
| 1013 | /* If line is longer than XBREAK chars, print the shift_mark */
|
---|
| 1014 | if (count == XBREAK && *textp != '\n')
|
---|
| 1015 | putchar(textp[1]=='\n' ? *textp : SHIFT_MARK);
|
---|
| 1016 |
|
---|
| 1017 | /* Clear the rest of the line is clear_line is TRUE */
|
---|
| 1018 | if (clear_line == TRUE) {
|
---|
| 1019 | #ifdef UNIX
|
---|
| 1020 | tputs(CE, 0, _putchar);
|
---|
| 1021 | #else
|
---|
| 1022 | string_print(blank_line);
|
---|
| 1023 | #endif /* UNIX */
|
---|
| 1024 | putchar('\n');
|
---|
| 1025 | }
|
---|
| 1026 | }
|
---|
| 1027 |
|
---|
| 1028 | /*
|
---|
| 1029 | * Flush the I/O buffer on filedescriptor fd.
|
---|
| 1030 | */
|
---|
| 1031 | int flush_buffer(fd)
|
---|
| 1032 | int fd;
|
---|
| 1033 | {
|
---|
| 1034 | if (out_count <= 0) /* There is nothing to flush */
|
---|
| 1035 | return FINE;
|
---|
| 1036 | #ifdef UNIX
|
---|
| 1037 | if (fd == STD_OUT) {
|
---|
| 1038 | printf("%.*s", out_count, screen);
|
---|
| 1039 | _flush();
|
---|
| 1040 | }
|
---|
| 1041 | else
|
---|
| 1042 | #endif /* UNIX */
|
---|
| 1043 | if (write(fd, screen, out_count) != out_count) {
|
---|
| 1044 | bad_write(fd);
|
---|
| 1045 | return ERRORS;
|
---|
| 1046 | }
|
---|
| 1047 | clear_buffer(); /* Empty buffer */
|
---|
| 1048 | return FINE;
|
---|
| 1049 | }
|
---|
| 1050 |
|
---|
| 1051 | /*
|
---|
| 1052 | * Bad_write() is called when a write failed. Notify the user.
|
---|
| 1053 | */
|
---|
| 1054 | void bad_write(fd)
|
---|
| 1055 | int fd;
|
---|
| 1056 | {
|
---|
| 1057 | if (fd == STD_OUT) /* Cannot write to terminal? */
|
---|
| 1058 | exit(1);
|
---|
| 1059 |
|
---|
| 1060 | clear_buffer();
|
---|
| 1061 | build_string(text_buffer, "Command aborted: %s (File incomplete)",
|
---|
| 1062 | (errno == ENOSPC || errno == -ENOSPC) ?
|
---|
| 1063 | "No space on device" : "Write error");
|
---|
| 1064 | error(text_buffer, NIL_PTR);
|
---|
| 1065 | }
|
---|
| 1066 |
|
---|
| 1067 | /*
|
---|
| 1068 | * Catch the SIGQUIT signal (^\) send to mined. It turns on the quitflag.
|
---|
| 1069 | */
|
---|
| 1070 | void catch(sig)
|
---|
| 1071 | int sig;
|
---|
| 1072 | {
|
---|
| 1073 | /* Reset the signal */
|
---|
| 1074 | signal(SIGQUIT, catch);
|
---|
| 1075 | quit = TRUE;
|
---|
| 1076 | }
|
---|
| 1077 |
|
---|
| 1078 | /*
|
---|
| 1079 | * Abort_mined() will leave mined. Confirmation is asked first.
|
---|
| 1080 | */
|
---|
| 1081 | void abort_mined()
|
---|
| 1082 | {
|
---|
| 1083 | quit = FALSE;
|
---|
| 1084 |
|
---|
| 1085 | /* Ask for confirmation */
|
---|
| 1086 | status_line("Really abort? ", NIL_PTR);
|
---|
| 1087 | if (getchar() != 'y') {
|
---|
| 1088 | clear_status();
|
---|
| 1089 | return;
|
---|
| 1090 | }
|
---|
| 1091 |
|
---|
| 1092 | /* Reset terminal */
|
---|
| 1093 | raw_mode(OFF);
|
---|
| 1094 | set_cursor(0, ymax);
|
---|
| 1095 | putchar('\n');
|
---|
| 1096 | flush();
|
---|
| 1097 | #ifdef UNIX
|
---|
| 1098 | abort();
|
---|
| 1099 | #else
|
---|
| 1100 | exit(1);
|
---|
| 1101 | #endif /* UNIX */
|
---|
| 1102 | }
|
---|
| 1103 |
|
---|
| 1104 | #define UNDEF _POSIX_VDISABLE
|
---|
| 1105 |
|
---|
| 1106 | /*
|
---|
| 1107 | * Set and reset tty into CBREAK or old mode according to argument `state'. It
|
---|
| 1108 | * also sets all signal characters (except for ^\) to UNDEF. ^\ is caught.
|
---|
| 1109 | */
|
---|
| 1110 | void raw_mode(state)
|
---|
| 1111 | FLAG state;
|
---|
| 1112 | {
|
---|
| 1113 | static struct termios old_tty;
|
---|
| 1114 | static struct termios new_tty;
|
---|
| 1115 |
|
---|
| 1116 | if (state == OFF) {
|
---|
| 1117 | tcsetattr(input_fd, TCSANOW, &old_tty);
|
---|
| 1118 | return;
|
---|
| 1119 | }
|
---|
| 1120 |
|
---|
| 1121 | /* Save old tty settings */
|
---|
| 1122 | tcgetattr(input_fd, &old_tty);
|
---|
| 1123 |
|
---|
| 1124 | /* Set tty to CBREAK mode */
|
---|
| 1125 | tcgetattr(input_fd, &new_tty);
|
---|
| 1126 | new_tty.c_lflag &= ~(ICANON|ECHO|ECHONL);
|
---|
| 1127 | new_tty.c_iflag &= ~(IXON|IXOFF);
|
---|
| 1128 |
|
---|
| 1129 | /* Unset signal chars, leave only SIGQUIT set to ^\ */
|
---|
| 1130 | new_tty.c_cc[VINTR] = new_tty.c_cc[VSUSP] = UNDEF;
|
---|
| 1131 | new_tty.c_cc[VQUIT] = '\\' & 037;
|
---|
| 1132 | signal(SIGQUIT, catch); /* Which is caught */
|
---|
| 1133 |
|
---|
| 1134 | tcsetattr(input_fd, TCSANOW, &new_tty);
|
---|
| 1135 | }
|
---|
| 1136 |
|
---|
| 1137 | /*
|
---|
| 1138 | * Panic() is called with an error number and a message. It is called when
|
---|
| 1139 | * something unrecoverable has happened.
|
---|
| 1140 | * It writes the message to the terminal, resets the tty and exits.
|
---|
| 1141 | * Ask the user if he wants to save his file.
|
---|
| 1142 | */
|
---|
| 1143 | void panic(message)
|
---|
| 1144 | register char *message;
|
---|
| 1145 | {
|
---|
| 1146 | extern char yank_file[];
|
---|
| 1147 |
|
---|
| 1148 | #ifdef UNIX
|
---|
| 1149 | tputs(CL, 0, _putchar);
|
---|
| 1150 | build_string(text_buffer, "%s\nError code %d\n", message, errno);
|
---|
| 1151 | #else
|
---|
| 1152 | build_string(text_buffer, "%s%s\nError code %d\n", enter_string, message, errno);
|
---|
| 1153 | #endif /* UNIX */
|
---|
| 1154 | (void) write(STD_OUT, text_buffer, length_of(text_buffer));
|
---|
| 1155 |
|
---|
| 1156 | if (loading == FALSE)
|
---|
| 1157 | XT(); /* Check if file can be saved */
|
---|
| 1158 | else
|
---|
| 1159 | (void) unlink(yank_file);
|
---|
| 1160 | raw_mode(OFF);
|
---|
| 1161 |
|
---|
| 1162 | #ifdef UNIX
|
---|
| 1163 | abort();
|
---|
| 1164 | #else
|
---|
| 1165 | exit(1);
|
---|
| 1166 | #endif /* UNIX */
|
---|
| 1167 | }
|
---|
| 1168 |
|
---|
| 1169 | char *alloc(bytes)
|
---|
| 1170 | int bytes;
|
---|
| 1171 | {
|
---|
| 1172 | char *p;
|
---|
| 1173 |
|
---|
| 1174 | p = malloc((unsigned) bytes);
|
---|
| 1175 | if (p == NIL_PTR) {
|
---|
| 1176 | if (loading == TRUE)
|
---|
| 1177 | panic("File too big.");
|
---|
| 1178 | panic("Out of memory.");
|
---|
| 1179 | }
|
---|
| 1180 | return(p);
|
---|
| 1181 | }
|
---|
| 1182 |
|
---|
| 1183 | void free_space(p)
|
---|
| 1184 | char *p;
|
---|
| 1185 | {
|
---|
| 1186 | free(p);
|
---|
| 1187 | }
|
---|
| 1188 |
|
---|
| 1189 | /* ======================================================================== *
|
---|
| 1190 | * Main loops *
|
---|
| 1191 | * ======================================================================== */
|
---|
| 1192 |
|
---|
| 1193 | /* The mapping between input codes and functions. */
|
---|
| 1194 |
|
---|
| 1195 | void (*key_map[256])() = { /* map ASCII characters to functions */
|
---|
| 1196 | /* 000-017 */ MA, BL, MP, YA, SD, RD, MN, IF, DPC, S, S, DT, LR, S, DNW,LIB,
|
---|
| 1197 | /* 020-037 */ DPW, WB, GR, SH, DLN, SU, VI, XWT, XT, PT, EL, ESC, I, GOTO,
|
---|
| 1198 | HIGH, LOW,
|
---|
| 1199 | /* 040-057 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1200 | /* 060-077 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1201 | /* 100-117 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1202 | /* 120-137 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1203 | /* 140-157 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1204 | /* 160-177 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, DCC,
|
---|
| 1205 | /* 200-217 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1206 | /* 220-237 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1207 | /* 240-257 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1208 | /* 260-277 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1209 | /* 300-317 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1210 | /* 320-337 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1211 | /* 340-357 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1212 | /* 360-377 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
|
---|
| 1213 | };
|
---|
| 1214 |
|
---|
| 1215 | int nlines; /* Number of lines in file */
|
---|
| 1216 | LINE *header; /* Head of line list */
|
---|
| 1217 | LINE *tail; /* Last line in line list */
|
---|
| 1218 | LINE *cur_line; /* Current line in use */
|
---|
| 1219 | LINE *top_line; /* First line of screen */
|
---|
| 1220 | LINE *bot_line; /* Last line of screen */
|
---|
| 1221 | char *cur_text; /* Current char on current line in use */
|
---|
| 1222 | int last_y; /* Last y of screen. Usually SCREENMAX */
|
---|
| 1223 | char screen[SCREEN_SIZE]; /* Output buffer for "writes" and "reads" */
|
---|
| 1224 |
|
---|
| 1225 | int x, y; /* x, y coordinates on screen */
|
---|
| 1226 | FLAG modified = FALSE; /* Set when file is modified */
|
---|
| 1227 | FLAG stat_visible; /* Set if status_line is visible */
|
---|
| 1228 | FLAG writable; /* Set if file cannot be written */
|
---|
| 1229 | FLAG loading; /* Set if we are loading a file. */
|
---|
| 1230 | FLAG quit = FALSE; /* Set when quit character is typed */
|
---|
| 1231 | FLAG rpipe = FALSE; /* Set if file should be read from stdin */
|
---|
| 1232 | int input_fd = 0; /* Fd for command input */
|
---|
| 1233 | int out_count; /* Index in output buffer */
|
---|
| 1234 | char file_name[LINE_LEN]; /* Name of file in use */
|
---|
| 1235 | char text_buffer[MAX_CHARS]; /* Buffer for modifying text */
|
---|
| 1236 |
|
---|
| 1237 | /* Escape sequences. */
|
---|
| 1238 | #ifdef UNIX
|
---|
| 1239 | char *CE, *VS, *SO, *SE, *CL, *AL, *CM;
|
---|
| 1240 | #else
|
---|
| 1241 | char *enter_string = "\033[H\033[J"; /* String printed on entering mined */
|
---|
| 1242 | char *pos_string = "\033[%d;%dH"; /* Absolute cursor position */
|
---|
| 1243 | char *rev_scroll = "\033M"; /* String for reverse scrolling */
|
---|
| 1244 | char *rev_video = "\033[7m"; /* String for starting reverse video */
|
---|
| 1245 | char *normal_video = "\033[m"; /* String for leaving reverse video */
|
---|
| 1246 | char *blank_line = "\033[K"; /* Clear line to end */
|
---|
| 1247 | #endif /* UNIX */
|
---|
| 1248 |
|
---|
| 1249 | /*
|
---|
| 1250 | * Yank variables.
|
---|
| 1251 | */
|
---|
| 1252 | FLAG yank_status = NOT_VALID; /* Status of yank_file */
|
---|
| 1253 | char yank_file[] = "/tmp/mined.XXXXXX";
|
---|
| 1254 | long chars_saved; /* Nr of chars in buffer */
|
---|
| 1255 |
|
---|
| 1256 | /*
|
---|
| 1257 | * Initialize is called when a another file is edited. It free's the allocated
|
---|
| 1258 | * space and sets modified back to FALSE and fixes the header/tail pointer.
|
---|
| 1259 | */
|
---|
| 1260 | void initialize()
|
---|
| 1261 | {
|
---|
| 1262 | register LINE *line, *next_line;
|
---|
| 1263 |
|
---|
| 1264 | /* Delete the whole list */
|
---|
| 1265 | for (line = header->next; line != tail; line = next_line) {
|
---|
| 1266 | next_line = line->next;
|
---|
| 1267 | free_space(line->text);
|
---|
| 1268 | free_space((char*)line);
|
---|
| 1269 | }
|
---|
| 1270 |
|
---|
| 1271 | /* header and tail should point to itself */
|
---|
| 1272 | line->next = line->prev = line;
|
---|
| 1273 | x = y = 0;
|
---|
| 1274 | rpipe = modified = FALSE;
|
---|
| 1275 | }
|
---|
| 1276 |
|
---|
| 1277 | /*
|
---|
| 1278 | * Basename() finds the absolute name of the file out of a given path_name.
|
---|
| 1279 | */
|
---|
| 1280 | char *basename(path)
|
---|
| 1281 | char *path;
|
---|
| 1282 | {
|
---|
| 1283 | register char *ptr = path;
|
---|
| 1284 | register char *last = NIL_PTR;
|
---|
| 1285 |
|
---|
| 1286 | while (*ptr != '\0') {
|
---|
| 1287 | if (*ptr == '/')
|
---|
| 1288 | last = ptr;
|
---|
| 1289 | ptr++;
|
---|
| 1290 | }
|
---|
| 1291 | if (last == NIL_PTR)
|
---|
| 1292 | return path;
|
---|
| 1293 | if (*(last + 1) == '\0') { /* E.g. /usr/tmp/pipo/ */
|
---|
| 1294 | *last = '\0';
|
---|
| 1295 | return basename(path);/* Try again */
|
---|
| 1296 | }
|
---|
| 1297 | return last + 1;
|
---|
| 1298 | }
|
---|
| 1299 |
|
---|
| 1300 | /*
|
---|
| 1301 | * Load_file loads the file `file' into core. If file is a NIL_PTR or the file
|
---|
| 1302 | * couldn't be opened, just some initializations are done, and a line consisting
|
---|
| 1303 | * of a `\n' is installed.
|
---|
| 1304 | */
|
---|
| 1305 | void load_file(file)
|
---|
| 1306 | char *file;
|
---|
| 1307 | {
|
---|
| 1308 | register LINE *line = header;
|
---|
| 1309 | register int len;
|
---|
| 1310 | long nr_of_chars = 0L;
|
---|
| 1311 | int fd = -1; /* Filedescriptor for file */
|
---|
| 1312 |
|
---|
| 1313 | nlines = 0; /* Zero lines to start with */
|
---|
| 1314 |
|
---|
| 1315 | /* Open file */
|
---|
| 1316 | writable = TRUE; /* Benefit of the doubt */
|
---|
| 1317 | if (file == NIL_PTR) {
|
---|
| 1318 | if (rpipe == FALSE)
|
---|
| 1319 | status_line("No file.", NIL_PTR);
|
---|
| 1320 | else {
|
---|
| 1321 | fd = 0;
|
---|
| 1322 | file = "standard input";
|
---|
| 1323 | }
|
---|
| 1324 | file_name[0] = '\0';
|
---|
| 1325 | }
|
---|
| 1326 | else {
|
---|
| 1327 | copy_string(file_name, file); /* Save file name */
|
---|
| 1328 | if (access(file, 0) < 0) /* Cannot access file. */
|
---|
| 1329 | status_line("New file ", file);
|
---|
| 1330 | else if ((fd = open(file, 0)) < 0)
|
---|
| 1331 | status_line("Cannot open ", file);
|
---|
| 1332 | else if (access(file, 2) != 0) /* Set write flag */
|
---|
| 1333 | writable = FALSE;
|
---|
| 1334 | }
|
---|
| 1335 |
|
---|
| 1336 | /* Read file */
|
---|
| 1337 | loading = TRUE; /* Loading file, so set flag */
|
---|
| 1338 |
|
---|
| 1339 | if (fd >= 0) {
|
---|
| 1340 | status_line("Reading ", file);
|
---|
| 1341 | while ((len = get_line(fd, text_buffer)) != ERRORS) {
|
---|
| 1342 | line = line_insert(line, text_buffer, len);
|
---|
| 1343 | nr_of_chars += (long) len;
|
---|
| 1344 | }
|
---|
| 1345 | if (nlines == 0) /* The file was empty! */
|
---|
| 1346 | line = line_insert(line, "\n", 1);
|
---|
| 1347 | clear_buffer(); /* Clear output buffer */
|
---|
| 1348 | cur_line = header->next;
|
---|
| 1349 | fstatus("Read", nr_of_chars);
|
---|
| 1350 | (void) close(fd); /* Close file */
|
---|
| 1351 | }
|
---|
| 1352 | else /* Just install a "\n" */
|
---|
| 1353 | (void) line_insert(line, "\n", 1);
|
---|
| 1354 |
|
---|
| 1355 | reset(header->next, 0); /* Initialize pointers */
|
---|
| 1356 |
|
---|
| 1357 | /* Print screen */
|
---|
| 1358 | display (0, 0, header->next, last_y);
|
---|
| 1359 | move_to (0, 0);
|
---|
| 1360 | flush(); /* Flush buffer */
|
---|
| 1361 | loading = FALSE; /* Stop loading, reset flag */
|
---|
| 1362 | }
|
---|
| 1363 |
|
---|
| 1364 |
|
---|
| 1365 | /*
|
---|
| 1366 | * Get_line reads one line from filedescriptor fd. If EOF is reached on fd,
|
---|
| 1367 | * get_line() returns ERRORS, else it returns the length of the string.
|
---|
| 1368 | */
|
---|
| 1369 | int get_line(fd, buffer)
|
---|
| 1370 | int fd;
|
---|
| 1371 | register char *buffer;
|
---|
| 1372 | {
|
---|
| 1373 | static char *last = NIL_PTR;
|
---|
| 1374 | static char *current = NIL_PTR;
|
---|
| 1375 | static int read_chars;
|
---|
| 1376 | register char *cur_pos = current;
|
---|
| 1377 | char *begin = buffer;
|
---|
| 1378 |
|
---|
| 1379 | do {
|
---|
| 1380 | if (cur_pos == last) {
|
---|
| 1381 | if ((read_chars = read(fd, screen, SCREEN_SIZE)) <= 0)
|
---|
| 1382 | break;
|
---|
| 1383 | last = &screen[read_chars];
|
---|
| 1384 | cur_pos = screen;
|
---|
| 1385 | }
|
---|
| 1386 | if (*cur_pos == '\0')
|
---|
| 1387 | *cur_pos = ' ';
|
---|
| 1388 | } while ((*buffer++ = *cur_pos++) != '\n');
|
---|
| 1389 |
|
---|
| 1390 | current = cur_pos;
|
---|
| 1391 | if (read_chars <= 0) {
|
---|
| 1392 | if (buffer == begin)
|
---|
| 1393 | return ERRORS;
|
---|
| 1394 | if (*(buffer - 1) != '\n')
|
---|
| 1395 | if (loading == TRUE) /* Add '\n' to last line of file */
|
---|
| 1396 | *buffer++ = '\n';
|
---|
| 1397 | else {
|
---|
| 1398 | *buffer = '\0';
|
---|
| 1399 | return NO_LINE;
|
---|
| 1400 | }
|
---|
| 1401 | }
|
---|
| 1402 |
|
---|
| 1403 | *buffer = '\0';
|
---|
| 1404 | return buffer - begin;
|
---|
| 1405 | }
|
---|
| 1406 |
|
---|
| 1407 | /*
|
---|
| 1408 | * Install_line installs the buffer into a LINE structure It returns a pointer
|
---|
| 1409 | * to the allocated structure.
|
---|
| 1410 | */
|
---|
| 1411 | LINE *install_line(buffer, length)
|
---|
| 1412 | char *buffer;
|
---|
| 1413 | int length;
|
---|
| 1414 | {
|
---|
| 1415 | register LINE *new_line = (LINE *) alloc(sizeof(LINE));
|
---|
| 1416 |
|
---|
| 1417 | new_line->text = alloc(length + 1);
|
---|
| 1418 | new_line->shift_count = 0;
|
---|
| 1419 | copy_string(new_line->text, buffer);
|
---|
| 1420 |
|
---|
| 1421 | return new_line;
|
---|
| 1422 | }
|
---|
| 1423 |
|
---|
| 1424 | void main(argc, argv)
|
---|
| 1425 | int argc;
|
---|
| 1426 | char *argv[];
|
---|
| 1427 | {
|
---|
| 1428 | /* mined is the Minix editor. */
|
---|
| 1429 |
|
---|
| 1430 | register int index; /* Index in key table */
|
---|
| 1431 | struct winsize winsize;
|
---|
| 1432 |
|
---|
| 1433 | #ifdef UNIX
|
---|
| 1434 | get_term();
|
---|
| 1435 | tputs(VS, 0, _putchar);
|
---|
| 1436 | tputs(CL, 0, _putchar);
|
---|
| 1437 | #else
|
---|
| 1438 | string_print(enter_string); /* Hello world */
|
---|
| 1439 | #endif /* UNIX */
|
---|
| 1440 | if (ioctl(STD_OUT, TIOCGWINSZ, &winsize) == 0 && winsize.ws_row != 0) {
|
---|
| 1441 | ymax = winsize.ws_row - 1;
|
---|
| 1442 | screenmax = ymax - 1;
|
---|
| 1443 | }
|
---|
| 1444 |
|
---|
| 1445 | if (!isatty(0)) { /* Reading from pipe */
|
---|
| 1446 | if (argc != 1) {
|
---|
| 1447 | write(2, "Cannot find terminal.\n", 22);
|
---|
| 1448 | exit (1);
|
---|
| 1449 | }
|
---|
| 1450 | rpipe = TRUE;
|
---|
| 1451 | modified = TRUE; /* Set modified so he can write */
|
---|
| 1452 | open_device();
|
---|
| 1453 | }
|
---|
| 1454 |
|
---|
| 1455 | raw_mode(ON); /* Set tty to appropriate mode */
|
---|
| 1456 |
|
---|
| 1457 | header = tail = (LINE *) alloc(sizeof(LINE)); /* Make header of list*/
|
---|
| 1458 | header->text = NIL_PTR;
|
---|
| 1459 | header->next = tail->prev = header;
|
---|
| 1460 |
|
---|
| 1461 | /* Load the file (if any) */
|
---|
| 1462 | if (argc < 2)
|
---|
| 1463 | load_file(NIL_PTR);
|
---|
| 1464 | else {
|
---|
| 1465 | (void) get_file(NIL_PTR, argv[1]); /* Truncate filename */
|
---|
| 1466 | load_file(argv[1]);
|
---|
| 1467 | }
|
---|
| 1468 |
|
---|
| 1469 | /* Main loop of the editor. */
|
---|
| 1470 | for (;;) {
|
---|
| 1471 | index = getchar();
|
---|
| 1472 | if (stat_visible == TRUE)
|
---|
| 1473 | clear_status();
|
---|
| 1474 | if (quit == TRUE)
|
---|
| 1475 | abort_mined();
|
---|
| 1476 | else { /* Call the function for this key */
|
---|
| 1477 | (*key_map[index])(index);
|
---|
| 1478 | flush(); /* Flush output (if any) */
|
---|
| 1479 | if (quit == TRUE)
|
---|
| 1480 | quit = FALSE;
|
---|
| 1481 | }
|
---|
| 1482 | }
|
---|
| 1483 | /* NOTREACHED */
|
---|
| 1484 | }
|
---|
| 1485 |
|
---|
| 1486 | /* ======================================================================== *
|
---|
| 1487 | * Miscellaneous *
|
---|
| 1488 | * ======================================================================== */
|
---|
| 1489 |
|
---|
| 1490 | /*
|
---|
| 1491 | * Redraw the screen
|
---|
| 1492 | */
|
---|
| 1493 | void RD()
|
---|
| 1494 | {
|
---|
| 1495 | /* Clear screen */
|
---|
| 1496 | #ifdef UNIX
|
---|
| 1497 | tputs(VS, 0, _putchar);
|
---|
| 1498 | tputs(CL, 0, _putchar);
|
---|
| 1499 | #else
|
---|
| 1500 | string_print(enter_string);
|
---|
| 1501 | #endif /* UNIX */
|
---|
| 1502 |
|
---|
| 1503 | /* Print first page */
|
---|
| 1504 | display(0, 0, top_line, last_y);
|
---|
| 1505 |
|
---|
| 1506 | /* Clear last line */
|
---|
| 1507 | set_cursor(0, ymax);
|
---|
| 1508 | #ifdef UNIX
|
---|
| 1509 | tputs(CE, 0, _putchar);
|
---|
| 1510 | #else
|
---|
| 1511 | string_print(blank_line);
|
---|
| 1512 | #endif /* UNIX */
|
---|
| 1513 | move_to(x, y);
|
---|
| 1514 | }
|
---|
| 1515 |
|
---|
| 1516 | /*
|
---|
| 1517 | * Ignore this keystroke.
|
---|
| 1518 | */
|
---|
| 1519 | void I()
|
---|
| 1520 | {
|
---|
| 1521 | }
|
---|
| 1522 |
|
---|
| 1523 | /*
|
---|
| 1524 | * Leave editor. If the file has changed, ask if the user wants to save it.
|
---|
| 1525 | */
|
---|
| 1526 | void XT()
|
---|
| 1527 | {
|
---|
| 1528 | if (modified == TRUE && ask_save() == ERRORS)
|
---|
| 1529 | return;
|
---|
| 1530 |
|
---|
| 1531 | raw_mode(OFF);
|
---|
| 1532 | set_cursor(0, ymax);
|
---|
| 1533 | putchar('\n');
|
---|
| 1534 | flush();
|
---|
| 1535 | (void) unlink(yank_file); /* Might not be necessary */
|
---|
| 1536 | exit(0);
|
---|
| 1537 | }
|
---|
| 1538 |
|
---|
| 1539 | void (*escfunc(c))()
|
---|
| 1540 | int c;
|
---|
| 1541 | {
|
---|
| 1542 | #if (CHIP == M68000)
|
---|
| 1543 | #ifndef COMPAT
|
---|
| 1544 | int ch;
|
---|
| 1545 | #endif
|
---|
| 1546 | #endif
|
---|
| 1547 | if (c == '[') {
|
---|
| 1548 | /* Start of ASCII escape sequence. */
|
---|
| 1549 | c = getchar();
|
---|
| 1550 | #if (CHIP == M68000)
|
---|
| 1551 | #ifndef COMPAT
|
---|
| 1552 | if ((c >= '0') && (c <= '9')) ch = getchar();
|
---|
| 1553 | /* ch is either a tilde or a second digit */
|
---|
| 1554 | #endif
|
---|
| 1555 | #endif
|
---|
| 1556 | switch (c) {
|
---|
| 1557 | case 'H': return(HO);
|
---|
| 1558 | case 'A': return(UP);
|
---|
| 1559 | case 'B': return(DN);
|
---|
| 1560 | case 'C': return(RT);
|
---|
| 1561 | case 'D': return(LF);
|
---|
| 1562 | #if (CHIP == M68000)
|
---|
| 1563 | #ifndef COMPAT
|
---|
| 1564 | /* F1 = ESC [ 1 ~ */
|
---|
| 1565 | /* F2 = ESC [ 2 ~ */
|
---|
| 1566 | /* F3 = ESC [ 3 ~ */
|
---|
| 1567 | /* F4 = ESC [ 4 ~ */
|
---|
| 1568 | /* F5 = ESC [ 5 ~ */
|
---|
| 1569 | /* F6 = ESC [ 6 ~ */
|
---|
| 1570 | /* F7 = ESC [ 17 ~ */
|
---|
| 1571 | /* F8 = ESC [ 18 ~ */
|
---|
| 1572 | case '1':
|
---|
| 1573 | switch (ch) {
|
---|
| 1574 | case '~': return(SF);
|
---|
| 1575 | case '7': (void) getchar(); return(MA);
|
---|
| 1576 | case '8': (void) getchar(); return(CTL);
|
---|
| 1577 | }
|
---|
| 1578 | case '2': return(SR);
|
---|
| 1579 | case '3': return(PD);
|
---|
| 1580 | case '4': return(PU);
|
---|
| 1581 | case '5': return(FS);
|
---|
| 1582 | case '6': return(EF);
|
---|
| 1583 | #endif
|
---|
| 1584 | #endif
|
---|
| 1585 | #if (CHIP == INTEL)
|
---|
| 1586 | case 'G': return(FS);
|
---|
| 1587 | case 'S': return(SR);
|
---|
| 1588 | case 'T': return(SF);
|
---|
| 1589 | case 'U': return(PD);
|
---|
| 1590 | case 'V': return(PU);
|
---|
| 1591 | case 'Y': return(EF);
|
---|
| 1592 | #endif
|
---|
| 1593 | }
|
---|
| 1594 | return(I);
|
---|
| 1595 | }
|
---|
| 1596 | #if (CHIP == M68000)
|
---|
| 1597 | #ifdef COMPAT
|
---|
| 1598 | if (c == 'O') {
|
---|
| 1599 | /* Start of ASCII function key escape sequence. */
|
---|
| 1600 | switch (getchar()) {
|
---|
| 1601 | case 'P': return(SF);
|
---|
| 1602 | case 'Q': return(SR);
|
---|
| 1603 | case 'R': return(PD);
|
---|
| 1604 | case 'S': return(PU);
|
---|
| 1605 | case 'T': return(FS);
|
---|
| 1606 | case 'U': return(EF);
|
---|
| 1607 | case 'V': return(MA);
|
---|
| 1608 | case 'W': return(CTL);
|
---|
| 1609 | }
|
---|
| 1610 | }
|
---|
| 1611 | #endif
|
---|
| 1612 | #endif
|
---|
| 1613 | return(I);
|
---|
| 1614 | }
|
---|
| 1615 |
|
---|
| 1616 | /*
|
---|
| 1617 | * ESC() wants a count and a command after that. It repeats the
|
---|
| 1618 | * command count times. If a ^\ is given during repeating, stop looping and
|
---|
| 1619 | * return to main loop.
|
---|
| 1620 | */
|
---|
| 1621 | void ESC()
|
---|
| 1622 | {
|
---|
| 1623 | register int count = 0;
|
---|
| 1624 | register void (*func)();
|
---|
| 1625 | int index;
|
---|
| 1626 |
|
---|
| 1627 | index = getchar();
|
---|
| 1628 | while (index >= '0' && index <= '9' && quit == FALSE) {
|
---|
| 1629 | count *= 10;
|
---|
| 1630 | count += index - '0';
|
---|
| 1631 | index = getchar();
|
---|
| 1632 | }
|
---|
| 1633 | if (count == 0) {
|
---|
| 1634 | count = 1;
|
---|
| 1635 | func = escfunc(index);
|
---|
| 1636 | } else {
|
---|
| 1637 | func = key_map[index];
|
---|
| 1638 | if (func == ESC)
|
---|
| 1639 | func = escfunc(getchar());
|
---|
| 1640 | }
|
---|
| 1641 |
|
---|
| 1642 | if (func == I) { /* Function assigned? */
|
---|
| 1643 | clear_status();
|
---|
| 1644 | return;
|
---|
| 1645 | }
|
---|
| 1646 |
|
---|
| 1647 | while (count-- > 0 && quit == FALSE) {
|
---|
| 1648 | if (stat_visible == TRUE)
|
---|
| 1649 | clear_status();
|
---|
| 1650 | (*func)(index);
|
---|
| 1651 | flush();
|
---|
| 1652 | }
|
---|
| 1653 |
|
---|
| 1654 | if (quit == TRUE) /* Abort has been given */
|
---|
| 1655 | error("Aborted", NIL_PTR);
|
---|
| 1656 | }
|
---|
| 1657 |
|
---|
| 1658 | /*
|
---|
| 1659 | * Ask the user if he wants to save his file or not.
|
---|
| 1660 | */
|
---|
| 1661 | int ask_save()
|
---|
| 1662 | {
|
---|
| 1663 | register int c;
|
---|
| 1664 |
|
---|
| 1665 | status_line(file_name[0] ? basename(file_name) : "[buffer]" ,
|
---|
| 1666 | " has been modified. Save? (y/n)");
|
---|
| 1667 |
|
---|
| 1668 | while((c = getchar()) != 'y' && c != 'n' && quit == FALSE) {
|
---|
| 1669 | ring_bell();
|
---|
| 1670 | flush();
|
---|
| 1671 | }
|
---|
| 1672 |
|
---|
| 1673 | clear_status();
|
---|
| 1674 |
|
---|
| 1675 | if (c == 'y')
|
---|
| 1676 | return WT();
|
---|
| 1677 |
|
---|
| 1678 | if (c == 'n')
|
---|
| 1679 | return FINE;
|
---|
| 1680 |
|
---|
| 1681 | quit = FALSE; /* Abort character has been given */
|
---|
| 1682 | return ERRORS;
|
---|
| 1683 | }
|
---|
| 1684 |
|
---|
| 1685 | /*
|
---|
| 1686 | * Line_number() finds the line number we're on.
|
---|
| 1687 | */
|
---|
| 1688 | int line_number()
|
---|
| 1689 | {
|
---|
| 1690 | register LINE *line = header->next;
|
---|
| 1691 | register int count = 1;
|
---|
| 1692 |
|
---|
| 1693 | while (line != cur_line) {
|
---|
| 1694 | count++;
|
---|
| 1695 | line = line->next;
|
---|
| 1696 | }
|
---|
| 1697 |
|
---|
| 1698 | return count;
|
---|
| 1699 | }
|
---|
| 1700 |
|
---|
| 1701 | /*
|
---|
| 1702 | * Display a line telling how many chars and lines the file contains. Also tell
|
---|
| 1703 | * whether the file is readonly and/or modified.
|
---|
| 1704 | */
|
---|
| 1705 | void file_status(message, count, file, lines, writefl, changed)
|
---|
| 1706 | char *message;
|
---|
| 1707 | register long count; /* Contains number of characters in file */
|
---|
| 1708 | char *file;
|
---|
| 1709 | int lines;
|
---|
| 1710 | FLAG writefl, changed;
|
---|
| 1711 | {
|
---|
| 1712 | register LINE *line;
|
---|
| 1713 | char msg[LINE_LEN + 40];/* Buffer to hold line */
|
---|
| 1714 | char yank_msg[LINE_LEN];/* Buffer for msg of yank_file */
|
---|
| 1715 |
|
---|
| 1716 | if (count < 0) /* Not valid. Count chars in file */
|
---|
| 1717 | for (line = header->next; line != tail; line = line->next)
|
---|
| 1718 | count += length_of(line->text);
|
---|
| 1719 |
|
---|
| 1720 | if (yank_status != NOT_VALID) /* Append buffer info */
|
---|
| 1721 | build_string(yank_msg, " Buffer: %D char%s.", chars_saved,
|
---|
| 1722 | (chars_saved == 1L) ? "" : "s");
|
---|
| 1723 | else
|
---|
| 1724 | yank_msg[0] = '\0';
|
---|
| 1725 |
|
---|
| 1726 | build_string(msg, "%s %s%s%s %d line%s %D char%s.%s Line %d", message,
|
---|
| 1727 | (rpipe == TRUE && *message != '[') ? "standard input" : basename(file),
|
---|
| 1728 | (changed == TRUE) ? "*" : "",
|
---|
| 1729 | (writefl == FALSE) ? " (Readonly)" : "",
|
---|
| 1730 | lines, (lines == 1) ? "" : "s",
|
---|
| 1731 | count, (count == 1L) ? "" : "s",
|
---|
| 1732 | yank_msg, line_number());
|
---|
| 1733 |
|
---|
| 1734 | if (length_of(msg) + 1 > LINE_LEN - 4) {
|
---|
| 1735 | msg[LINE_LEN - 4] = SHIFT_MARK; /* Overflow on status line */
|
---|
| 1736 | msg[LINE_LEN - 3] = '\0';
|
---|
| 1737 | }
|
---|
| 1738 | status_line(msg, NIL_PTR); /* Print the information */
|
---|
| 1739 | }
|
---|
| 1740 |
|
---|
| 1741 | /*
|
---|
| 1742 | * Build_string() prints the arguments as described in fmt, into the buffer.
|
---|
| 1743 | * %s indicates an argument string, %d indicated an argument number.
|
---|
| 1744 | */
|
---|
| 1745 | #if __STDC__
|
---|
| 1746 | void build_string(char *buf, char *fmt, ...)
|
---|
| 1747 | {
|
---|
| 1748 | #else
|
---|
| 1749 | void build_string(buf, fmt, va_alist)
|
---|
| 1750 | char *buf, *fmt;
|
---|
| 1751 | va_dcl
|
---|
| 1752 | {
|
---|
| 1753 | #endif
|
---|
| 1754 | va_list argptr;
|
---|
| 1755 | char *scanp;
|
---|
| 1756 |
|
---|
| 1757 | #if __STDC__
|
---|
| 1758 | va_start(argptr, fmt);
|
---|
| 1759 | #else
|
---|
| 1760 | va_start(argptr);
|
---|
| 1761 | #endif
|
---|
| 1762 |
|
---|
| 1763 | while (*fmt) {
|
---|
| 1764 | if (*fmt == '%') {
|
---|
| 1765 | fmt++;
|
---|
| 1766 | switch (*fmt++) {
|
---|
| 1767 | case 's' :
|
---|
| 1768 | scanp = va_arg(argptr, char *);
|
---|
| 1769 | break;
|
---|
| 1770 | case 'd' :
|
---|
| 1771 | scanp = num_out((long) va_arg(argptr, int));
|
---|
| 1772 | break;
|
---|
| 1773 | case 'D' :
|
---|
| 1774 | scanp = num_out((long) va_arg(argptr, long));
|
---|
| 1775 | break;
|
---|
| 1776 | default :
|
---|
| 1777 | scanp = "";
|
---|
| 1778 | }
|
---|
| 1779 | while (*buf++ = *scanp++)
|
---|
| 1780 | ;
|
---|
| 1781 | buf--;
|
---|
| 1782 | }
|
---|
| 1783 | else
|
---|
| 1784 | *buf++ = *fmt++;
|
---|
| 1785 | }
|
---|
| 1786 | va_end(argptr);
|
---|
| 1787 | *buf = '\0';
|
---|
| 1788 | }
|
---|
| 1789 |
|
---|
| 1790 | /*
|
---|
| 1791 | * Output an (unsigned) long in a 10 digit field without leading zeros.
|
---|
| 1792 | * It returns a pointer to the first digit in the buffer.
|
---|
| 1793 | */
|
---|
| 1794 | char *num_out(number)
|
---|
| 1795 | long number;
|
---|
| 1796 | {
|
---|
| 1797 | static char num_buf[11]; /* Buffer to build number */
|
---|
| 1798 | register long digit; /* Next digit of number */
|
---|
| 1799 | register long pow = 1000000000L; /* Highest ten power of long */
|
---|
| 1800 | FLAG digit_seen = FALSE;
|
---|
| 1801 | int i;
|
---|
| 1802 |
|
---|
| 1803 | for (i = 0; i < 10; i++) {
|
---|
| 1804 | digit = number / pow; /* Get next digit */
|
---|
| 1805 | if (digit == 0L && digit_seen == FALSE && i != 9)
|
---|
| 1806 | num_buf[i] = ' ';
|
---|
| 1807 | else {
|
---|
| 1808 | num_buf[i] = '0' + (char) digit;
|
---|
| 1809 | number -= digit * pow; /* Erase digit */
|
---|
| 1810 | digit_seen = TRUE;
|
---|
| 1811 | }
|
---|
| 1812 | pow /= 10L; /* Get next digit */
|
---|
| 1813 | }
|
---|
| 1814 | for (i = 0; num_buf[i] == ' '; i++) /* Skip leading spaces */
|
---|
| 1815 | ;
|
---|
| 1816 | return (&num_buf[i]);
|
---|
| 1817 | }
|
---|
| 1818 |
|
---|
| 1819 | /*
|
---|
| 1820 | * Get_number() read a number from the terminal. The last character typed in is
|
---|
| 1821 | * returned. ERRORS is returned on a bad number. The resulting number is put
|
---|
| 1822 | * into the integer the arguments points to.
|
---|
| 1823 | */
|
---|
| 1824 | int get_number(message, result)
|
---|
| 1825 | char *message;
|
---|
| 1826 | int *result;
|
---|
| 1827 | {
|
---|
| 1828 | register int index;
|
---|
| 1829 | register int count = 0;
|
---|
| 1830 |
|
---|
| 1831 | status_line(message, NIL_PTR);
|
---|
| 1832 |
|
---|
| 1833 | index = getchar();
|
---|
| 1834 | if (quit == FALSE && (index < '0' || index > '9')) {
|
---|
| 1835 | error("Bad count", NIL_PTR);
|
---|
| 1836 | return ERRORS;
|
---|
| 1837 | }
|
---|
| 1838 |
|
---|
| 1839 | /* Convert input to a decimal number */
|
---|
| 1840 | while (index >= '0' && index <= '9' && quit == FALSE) {
|
---|
| 1841 | count *= 10;
|
---|
| 1842 | count += index - '0';
|
---|
| 1843 | index = getchar();
|
---|
| 1844 | }
|
---|
| 1845 |
|
---|
| 1846 | if (quit == TRUE) {
|
---|
| 1847 | clear_status();
|
---|
| 1848 | return ERRORS;
|
---|
| 1849 | }
|
---|
| 1850 |
|
---|
| 1851 | *result = count;
|
---|
| 1852 | return index;
|
---|
| 1853 | }
|
---|
| 1854 |
|
---|
| 1855 | /*
|
---|
| 1856 | * Input() reads a string from the terminal. When the KILL character is typed,
|
---|
| 1857 | * it returns ERRORS.
|
---|
| 1858 | */
|
---|
| 1859 | int input(inbuf, clearfl)
|
---|
| 1860 | char *inbuf;
|
---|
| 1861 | FLAG clearfl;
|
---|
| 1862 | {
|
---|
| 1863 | register char *ptr;
|
---|
| 1864 | register char c; /* Character read */
|
---|
| 1865 |
|
---|
| 1866 | ptr = inbuf;
|
---|
| 1867 |
|
---|
| 1868 | *ptr = '\0';
|
---|
| 1869 | while (quit == FALSE) {
|
---|
| 1870 | flush();
|
---|
| 1871 | switch (c = getchar()) {
|
---|
| 1872 | case '\b' : /* Erase previous char */
|
---|
| 1873 | if (ptr > inbuf) {
|
---|
| 1874 | ptr--;
|
---|
| 1875 | #ifdef UNIX
|
---|
| 1876 | tputs(SE, 0, _putchar);
|
---|
| 1877 | #else
|
---|
| 1878 | string_print(normal_video);
|
---|
| 1879 | #endif /* UNIX */
|
---|
| 1880 | if (is_tab(*ptr))
|
---|
| 1881 | string_print(" \b\b\b \b\b");
|
---|
| 1882 | else
|
---|
| 1883 | string_print(" \b\b \b");
|
---|
| 1884 | #ifdef UNIX
|
---|
| 1885 | tputs(SO, 0, _putchar);
|
---|
| 1886 | #else
|
---|
| 1887 | string_print(rev_video);
|
---|
| 1888 | #endif /* UNIX */
|
---|
| 1889 | string_print(" \b");
|
---|
| 1890 | *ptr = '\0';
|
---|
| 1891 | }
|
---|
| 1892 | else
|
---|
| 1893 | ring_bell();
|
---|
| 1894 | break;
|
---|
| 1895 | case '\n' : /* End of input */
|
---|
| 1896 | /* If inbuf is empty clear status_line */
|
---|
| 1897 | return (ptr == inbuf && clearfl == TRUE) ? NO_INPUT :FINE;
|
---|
| 1898 | default : /* Only read ASCII chars */
|
---|
| 1899 | if ((c >= ' ' && c <= '~') || c == '\t') {
|
---|
| 1900 | *ptr++ = c;
|
---|
| 1901 | *ptr = '\0';
|
---|
| 1902 | if (c == '\t')
|
---|
| 1903 | string_print("^I");
|
---|
| 1904 | else
|
---|
| 1905 | putchar(c);
|
---|
| 1906 | string_print(" \b");
|
---|
| 1907 | }
|
---|
| 1908 | else
|
---|
| 1909 | ring_bell();
|
---|
| 1910 | }
|
---|
| 1911 | }
|
---|
| 1912 | quit = FALSE;
|
---|
| 1913 | return ERRORS;
|
---|
| 1914 | }
|
---|
| 1915 |
|
---|
| 1916 | /*
|
---|
| 1917 | * Get_file() reads a filename from the terminal. Filenames longer than
|
---|
| 1918 | * FILE_LENGHT chars are truncated.
|
---|
| 1919 | */
|
---|
| 1920 | int get_file(message, file)
|
---|
| 1921 | char *message, *file;
|
---|
| 1922 | {
|
---|
| 1923 | char *ptr;
|
---|
| 1924 | int ret;
|
---|
| 1925 |
|
---|
| 1926 | if (message == NIL_PTR || (ret = get_string(message, file, TRUE)) == FINE) {
|
---|
| 1927 | if (length_of((ptr = basename(file))) > NAME_MAX)
|
---|
| 1928 | ptr[NAME_MAX] = '\0';
|
---|
| 1929 | }
|
---|
| 1930 | return ret;
|
---|
| 1931 | }
|
---|
| 1932 |
|
---|
| 1933 | /* ======================================================================== *
|
---|
| 1934 | * UNIX I/O Routines *
|
---|
| 1935 | * ======================================================================== */
|
---|
| 1936 |
|
---|
| 1937 | #ifdef UNIX
|
---|
| 1938 | #undef putchar
|
---|
| 1939 |
|
---|
| 1940 | int _getchar()
|
---|
| 1941 | {
|
---|
| 1942 | char c;
|
---|
| 1943 |
|
---|
| 1944 | if (read(input_fd, &c, 1) != 1 && quit == FALSE)
|
---|
| 1945 | panic ("Cannot read 1 byte from input");
|
---|
| 1946 | return c & 0377;
|
---|
| 1947 | }
|
---|
| 1948 |
|
---|
| 1949 | void _flush()
|
---|
| 1950 | {
|
---|
| 1951 | (void) fflush(stdout);
|
---|
| 1952 | }
|
---|
| 1953 |
|
---|
| 1954 | void _putchar(c)
|
---|
| 1955 | char c;
|
---|
| 1956 | {
|
---|
| 1957 | (void) write_char(STD_OUT, c);
|
---|
| 1958 | }
|
---|
| 1959 |
|
---|
| 1960 | void get_term()
|
---|
| 1961 | {
|
---|
| 1962 | static char termbuf[50];
|
---|
| 1963 | extern char *tgetstr(), *getenv();
|
---|
| 1964 | char *loc = termbuf;
|
---|
| 1965 | char entry[1024];
|
---|
| 1966 |
|
---|
| 1967 | if (tgetent(entry, getenv("TERM")) <= 0) {
|
---|
| 1968 | printf("Unknown terminal.\n");
|
---|
| 1969 | exit(1);
|
---|
| 1970 | }
|
---|
| 1971 |
|
---|
| 1972 | AL = tgetstr("al", &loc);
|
---|
| 1973 | CE = tgetstr("ce", &loc);
|
---|
| 1974 | VS = tgetstr("vs", &loc);
|
---|
| 1975 | CL = tgetstr("cl", &loc);
|
---|
| 1976 | SO = tgetstr("so", &loc);
|
---|
| 1977 | SE = tgetstr("se", &loc);
|
---|
| 1978 | CM = tgetstr("cm", &loc);
|
---|
| 1979 | ymax = tgetnum("li") - 1;
|
---|
| 1980 | screenmax = ymax - 1;
|
---|
| 1981 |
|
---|
| 1982 | if (!CE || !SO || !SE || !CL || !AL || !CM) {
|
---|
| 1983 | printf("Sorry, no mined on this type of terminal\n");
|
---|
| 1984 | exit(1);
|
---|
| 1985 | }
|
---|
| 1986 | }
|
---|
| 1987 | #endif /* UNIX */
|
---|