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 */
|
---|