Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/io.e | 15 | 23 | 65.22% | 171 | 257 | 66.54% | 86 |
Routine | Executed | Lines | Unexecuted | |
writef() | 0 | 31 | 0.00% | 31 |
get_dstring() | 0 | 13 | 0.00% | 13 |
get_integer32() | 0 | 6 | 0.00% | 6 |
get_bytes() | 18 | 23 | 78.26% | 5 |
writefln() | 0 | 5 | 0.00% | 5 |
get_integer16() | 0 | 4 | 0.00% | 4 |
process_lines() | 23 | 27 | 85.19% | 4 |
put_integer16() | 0 | 4 | 0.00% | 4 |
put_integer32() | 0 | 4 | 0.00% | 4 |
byte_range() | 3 | 6 | 50.00% | 3 |
flush() | 0 | 3 | 0.00% | 3 |
read_lines() | 21 | 23 | 91.30% | 2 |
read_file() | 25 | 26 | 96.15% | 1 |
write_file() | 25 | 26 | 96.15% | 1 |
append_lines() | 9 | 9 | 100.00% | 0 |
file_number() | 2 | 2 | 100.00% | 0 |
file_position() | 2 | 2 | 100.00% | 0 |
lock_file() | 2 | 2 | 100.00% | 0 |
lock_type() | 2 | 2 | 100.00% | 0 |
seek() | 2 | 2 | 100.00% | 0 |
unlock_file() | 3 | 3 | 100.00% | 0 |
where() | 2 | 2 | 100.00% | 0 |
write_lines() | 12 | 12 | 100.00% | 0 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | -- | |
3 | --**** | |
4 | -- == I/O | |
5 | -- | |
6 | -- < | |
7 | namespace io | |
8 | ||
9 | include std/sort.e | |
10 | include std/wildcard.e | |
11 | include std/types.e | |
12 | include std/machine.e | |
13 | include std/text.e | |
14 | include std/sequence.e | |
15 | include std/error.e | |
16 | ||
17 | 101 | constant M_SEEK = 19, |
18 | 101 | M_WHERE = 20, |
19 | 101 | M_FLUSH = 60, |
20 | 101 | M_LOCK_FILE = 61, |
21 | 101 | M_UNLOCK_FILE = 62 |
22 | ||
23 | --**** | |
24 | -- === Constants | |
25 | ||
26 | --** | |
27 | -- Standard Input | |
28 | ||
29 | 101 | public constant STDIN = 0 |
30 | ||
31 | --** | |
32 | -- Standard Output | |
33 | ||
34 | 101 | public constant STDOUT = 1 |
35 | ||
36 | --** | |
37 | -- Standard Error | |
38 | ||
39 | 101 | public constant STDERR = 2 |
40 | ||
41 | --** | |
42 | -- Screen (Standard Out) | |
43 | ||
44 | 101 | public constant SCREEN = 1 |
45 | ||
46 | --** | |
47 | -- End of file | |
48 | ||
49 | 101 | public constant EOF = (-1) |
50 | ||
51 | --**** | |
52 | -- === Read/Write Routines | |
53 | ||
54 | --**** | |
55 | -- @[q_print|] | |
56 | -- Signature: | |
57 | -- | |
58 | -- | |
59 | -- Description: | |
60 | -- Shorthand way of saying: ##pretty_print(STDOUT, x, {})## ~-- i.e. printing the value of an | |
61 | -- expression to the standard output, with braces and indentation to show the structure. | |
62 | -- | |
63 | -- Example 1: | |
64 | -- | |
65 | -- ? {1, 2} + {3, 4} -- will display {4, 6} | |
66 | -- | |
67 | -- | |
68 | -- See Also: | |
69 | -- [[:print]] | |
70 | ||
71 | --**** | |
72 | -- Signature: | |
73 | -- | |
74 | -- | |
75 | -- Description: | |
76 | -- Writes out a **text** representation of an object to a file or device. | |
77 | -- If the object ##x## is a sequence, it uses braces **##{ , , , }##** to | |
78 | -- show the structure. | |
79 | -- | |
80 | -- Parameters: | |
81 | -- # ##fn## : an integer, the handle to a file or device to output to | |
82 | -- # ##x## : the object to print | |
83 | -- | |
84 | -- Errors: | |
85 | -- The target file or device must be open. | |
86 | -- | |
87 | -- Comments: | |
88 | -- This is not used to write to "binary" files as it only outputs text. | |
89 | -- | |
90 | -- Example 1: | |
91 | -- | |
92 | -- print(STDOUT, "ABC") -- output is: "{65,66,67}" | |
93 | -- puts(STDOUT, "ABC") -- output is: "ABC" | |
94 | -- print(STDOUT, 65) -- output is: "65" | |
95 | -- puts(STDOUT, 65) -- output is: "A" (ASCII-65 ==> 'A') | |
96 | -- puts(STDOUT, 65.1234) -- output is: "65.1234" | |
97 | -- puts(STDOUT, 65.1234) -- output is: "A" (Converts to integer first) | |
98 | -- | |
99 | -- | |
100 | -- Example 2: | |
101 | -- | |
102 | -- print(STDOUT, repeat({10,20}, 3)) -- output is: {{10,20},{10,20},{10,20}} | |
103 | -- | |
104 | -- | |
105 | -- See Also: | |
106 | -- [[:q_print|?]], [[:puts]] | |
107 | ||
108 | --**** | |
109 | -- Signature: | |
110 | -- | |
111 | -- | |
112 | -- Description: | |
113 | -- Print one or more values to a file or device, using a format string to embed them in and define how they should be represented. | |
114 | -- | |
115 | -- Parameters: | |
116 | -- # ##fn## : an integer, the handle to a file or device to output to | |
117 | -- # ##format## : a sequence, the text to print. This text may contain format specifiers. | |
118 | -- # ##values## : usually, a sequence of values. It should have as many elements as format specifiers in ##format##, as these values will be substituted to the specifiers. | |
119 | -- | |
120 | -- Errors: | |
121 | -- If there are less values to show than format specifiers, a run time error will occur. | |
122 | -- | |
123 | -- The target file or device must be open. | |
124 | -- | |
125 | -- Comments: | |
126 | -- A format specifier is a string of characters starting with a percent sign ( ~%~ ) and ending | |
127 | -- in a letter. Some extra information may come in the middle. | |
128 | -- | |
129 | -- ##format## will be scanned for format specifiers. Whenever one is found, the current value | |
130 | -- in ##values## will be turned into a string according to the format specifier. The resulting | |
131 | -- string will be plugged in the result, as if replacing the modifier with the printed value. | |
132 | -- Then moving on to next value and carrying the process on. | |
133 | -- | |
134 | -- This way, ##printf##() always takes | |
135 | -- exactly 3 arguments, no matter how many values are to be printed. Only the length of the last | |
136 | -- argument, containing the values to be printed, will vary. | |
137 | -- | |
138 | -- The basic format specifiers are... | |
139 | -- | |
140 | -- * ##%d## ~-- print an atom as a decimal integer | |
141 | -- * ##%x## ~-- print an atom as a hexadecimal integer. Negative numbers are printed | |
142 | -- in two's complement, so -1 will print as FFFFFFFF | |
143 | -- * ##%o## ~-- print an atom as an octal integer | |
144 | -- * ##%s## ~-- print a sequence as a string of characters, or print an atom as a single | |
145 | -- character | |
146 | -- * ##%e## ~-- print an atom as a floating-point number with exponential notation | |
147 | -- * ##%f## ~-- print an atom as a floating-point number with a decimal point but no exponent | |
148 | -- * ##%g## ~-- print an atom as a floating-point number using whichever format seems | |
149 | -- appropriate, given the magnitude of the number | |
150 | -- * ##~%~%## ~-- print the '%' character itself. This is not an actual format specifier. | |
151 | -- | |
152 | -- Field widths can be added to the basic formats, e.g. %5d, %8.2f, %10.4s. The number | |
153 | -- before the decimal point is the minimum field width to be used. The number after | |
154 | -- the decimal point is the precision to be used. | |
155 | -- | |
156 | -- If the field width is negative, e.g. %-5d then the value will be left-justified | |
157 | -- within the field. Normally it will be right-justified. If the field width | |
158 | -- starts with a leading 0, e.g. %08d then leading zeros will be supplied to fill up | |
159 | -- the field. If the field width starts with a '+' e.g. %+7d then a plus sign will | |
160 | -- be printed for positive values. | |
161 | -- | |
162 | -- Comments: | |
163 | -- Watch out for the following common mistake: | |
164 | -- | |
165 | -- | |
166 | -- name="John Smith" | |
167 | -- printf(STDOUT, "%s", name) -- error! | |
168 | -- | |
169 | -- | |
170 | -- This will print only the first character, J, of name, as each element of | |
171 | -- name is taken to be a separate value to be formatted. You must say this instead: | |
172 | -- | |
173 | -- | |
174 | -- name="John Smith" | |
175 | -- printf(STDOUT, "%s", {name}) -- correct | |
176 | -- | |
177 | -- | |
178 | -- Now, the third argument of ##printf##() is a one-element sequence containing the | |
179 | -- item to be formatted. | |
180 | -- | |
181 | -- If there is only one ##%## format specifier, and if the value it stands for is an atom, then ##values## may be simply that atom. | |
182 | -- | |
183 | -- Example 1: | |
184 | -- | |
185 | -- rate = 7.875 | |
186 | -- printf(my_file, "The interest rate is: %8.2f\n", rate) | |
187 | -- | |
188 | -- -- The interest rate is: 7.88 | |
189 | -- | |
190 | -- | |
191 | -- Example 2: | |
192 | -- | |
193 | -- name="John Smith" | |
194 | -- score=97 | |
195 | -- printf(STDOUT, "%15s, %5d\n", {name, score}) | |
196 | -- | |
197 | -- -- John Smith, 97 | |
198 | -- | |
199 | -- | |
200 | -- Example 3: | |
201 | -- | |
202 | -- printf(STDOUT, "%-10.4s $ %s", {"ABCDEFGHIJKLMNOP", "XXX"}) | |
203 | -- -- ABCD $ XXX | |
204 | -- | |
205 | -- | |
206 | -- Example 4: | |
207 | -- | |
208 | -- printf(STDOUT, "%d %e %f %g", 7.75) -- same value in different formats | |
209 | -- | |
210 | -- -- 7 7.750000e+000 7.750000 7.75 | |
211 | -- | |
212 | -- | |
213 | -- See Also: | |
214 | -- [[:sprintf]], [[:sprint]], [[:print]] | |
215 | ||
216 | --**** | |
217 | -- Signature: | |
218 | -- | |
219 | -- | |
220 | -- Description: | |
221 | -- Output, to a file or device, a single byte (atom) or sequence of bytes. The low order | |
222 | -- 8-bits of each value is actually sent out. If outputting to the screen you will see text | |
223 | -- characters displayed. | |
224 | -- | |
225 | -- Parameters: | |
226 | -- # ##fn## : an integer, the handle to an opened file or device | |
227 | -- # ##text## : an object, either a single character or a sequence of characters. | |
228 | -- | |
229 | -- Errors: | |
230 | -- The target file or device must be open. | |
231 | -- | |
232 | -- Comments: | |
233 | -- When you output a sequence of bytes it must not have any (sub)sequences within it. It | |
234 | -- must be a sequence of atoms only. (Typically a string of ASCII codes). | |
235 | -- | |
236 | -- Avoid outputting 0's to the screen or to standard output. Your output might get truncated. | |
237 | -- | |
238 | -- Remember that if the output file was opened in text mode, //Windows// will change ##\n## (10) | |
239 | -- to ##\r\n## (13 10). Open the file in binary mode if this is not what you want. | |
240 | -- | |
241 | -- Example 1: | |
242 | -- | |
243 | -- puts(SCREEN, "Enter your first name: ") | |
244 | -- | |
245 | -- | |
246 | -- Example 2: | |
247 | -- | |
248 | -- puts(output, 'A') -- the single byte 65 will be sent to output | |
249 | -- | |
250 | -- | |
251 | -- See Also: | |
252 | -- [[:print]] | |
253 | ||
254 | --**** | |
255 | -- Signature: | |
256 | -- | |
257 | -- | |
258 | -- Description: | |
259 | -- Get the next character (byte) from a file or device fn. | |
260 | -- | |
261 | -- Parameters: | |
262 | -- # ##fn## : an integer, the handle of the file or device to read from. | |
263 | -- | |
264 | -- Returns: | |
265 | -- An **integer**, the character read from the file, in the 0..255 range. If no character is left to read, [[:EOF]] is returned instead. | |
266 | -- | |
267 | -- Errors: | |
268 | -- The target file or device must be open. | |
269 | -- | |
270 | -- Comments: | |
271 | -- File input using ##getc##() is buffered, i.e. ##getc##() does not actually go out to the disk | |
272 | -- for each character. Instead, a large block of characters will be read in at one time | |
273 | -- and returned to you one by one from a memory buffer. | |
274 | -- | |
275 | -- When ##getc##() reads from the keyboard, it will not see any characters until the user | |
276 | -- presses Enter. Note that the user can type CTRL+Z, which the operating system treats | |
277 | -- as "end of file". [[:EOF]] will be returned. | |
278 | -- | |
279 | -- See Also: | |
280 | -- [[:gets]], [[:get_key]] | |
281 | ||
282 | --**** | |
283 | -- Signature: | |
284 | -- | |
285 | -- | |
286 | -- Description: | |
287 | -- Get the next sequence (one line, including '\n') of characters from a file or device. | |
288 | -- Parameters: | |
289 | -- # ##fn## : an integer, the handle of the file or device to read from. | |
290 | -- | |
291 | -- Returns: | |
292 | -- An **object**, either [[:EOF]] on end of file, or the next line of text from the file. | |
293 | -- | |
294 | -- Errors: | |
295 | -- The file or device must be open. | |
296 | -- | |
297 | -- Comments: | |
298 | -- The characters will have values from 0 to 255. | |
299 | -- | |
300 | -- If the line had an end of line marker, a ~'\n'~ terminates the line. The last line of a file needs not have an end of line marker. | |
301 | -- | |
302 | -- After reading a line of text from the keyboard, you should normally output a \n character, | |
303 | -- e.g. puts(1, '\n'), before printing something. Only on the last line of the screen does the | |
304 | -- operating system automatically scroll the screen and advance to the next line. | |
305 | -- | |
306 | -- When your program reads from the keyboard, the user can type control-Z, which the operating | |
307 | -- system treats as "end of file". [[:EOF]] will be returned. | |
308 | -- | |
309 | -- Example 1: | |
310 | -- | |
311 | -- sequence buffer | |
312 | -- object line | |
313 | -- integer fn | |
314 | -- | |
315 | -- -- read a text file into a sequence | |
316 | -- fn = open("my_file.txt", "r") | |
317 | -- if fn = -1 then | |
318 | -- puts(1, "Couldn't open my_file.txt\n") | |
319 | -- abort(1) | |
320 | -- end if | |
321 | -- | |
322 | -- buffer = {} | |
323 | -- while 1 do | |
324 | -- line = gets(fn) | |
325 | -- if atom(line) then | |
326 | -- exit -- EOF is returned at end of file | |
327 | -- end if | |
328 | -- buffer = append(buffer, line) | |
329 | -- end while | |
330 | -- | |
331 | -- | |
332 | -- Example 2: | |
333 | -- | |
334 | -- object line | |
335 | -- | |
336 | -- puts(1, "What is your name?\n") | |
337 | -- line = gets(0) -- read standard input (keyboard) | |
338 | -- line = line[1..$-1] -- get rid of \n character at end | |
339 | -- puts(1, '\n') -- necessary | |
340 | -- puts(1, line & " is a nice name.\n") | |
341 | -- | |
342 | -- | |
343 | -- See Also: | |
344 | -- [[:getc]], [[:read_lines]] | |
345 | ||
346 | --**** | |
347 | -- Signature: | |
348 | -- | |
349 | -- | |
350 | -- Description: | |
351 | -- Get the next keystroke without waiting for it or echoing it on the console. | |
352 | -- | |
353 | -- Parameters: | |
354 | -- # None. | |
355 | -- | |
356 | -- Returns: | |
357 | -- An **integer**, the code number for the key pressed. If there is no key | |
358 | -- press waiting, then this returns -1. | |
359 | -- | |
360 | -- See Also: | |
361 | -- [[:gets]], [[:getc]] | |
362 | ||
363 | 101 | constant CHUNK = 100 |
364 | ||
365 | --** | |
366 | -- Read the next bytes from a file. | |
367 | -- | |
368 | -- Parameters: | |
369 | -- # ##fn## : an integer, the handle to an open file to read from. | |
370 | -- # ##n## : a positive integer, the number of bytes to read. | |
371 | -- | |
372 | -- Returns: | |
373 | -- A **sequence**, of length at most ##n##, made of the bytes that could be read from the file. | |
374 | -- | |
375 | -- Comments: | |
376 | -- When ##n## > 0 and the function returns a sequence of length less than ##n## you know | |
377 | -- you've reached the end of file. Eventually, an | |
378 | -- empty sequence will be returned. | |
379 | -- | |
380 | -- This function is normally used with files opened in binary mode, "rb". | |
381 | -- This avoids the confusing situation in text mode where //Windows// will convert CR LF | |
382 | -- pairs to LF. | |
383 | -- | |
384 | -- Example 1: | |
385 | -- | |
386 | -- | |
387 | -- integer fn | |
388 | -- fn = open("temp", "rb") -- an existing file | |
389 | -- | |
390 | -- sequence whole_file | |
391 | -- whole_file = {} | |
392 | -- | |
393 | -- sequence chunk | |
394 | -- | |
395 | -- while 1 do | |
396 | -- chunk = get_bytes(fn, 100) -- read 100 bytes at a time | |
397 | -- whole_file &= chunk -- chunk might be empty, that's ok | |
398 | -- if length(chunk) < 100 then | |
399 | -- exit | |
400 | -- end if | |
401 | -- end while | |
402 | -- | |
403 | -- close(fn) | |
404 | -- ? length(whole_file) -- should match DIR size of "temp" | |
405 | -- | |
406 | -- | |
407 | -- See Also: | |
408 | -- [[:getc]], [[:gets]], [[:get_integer32]], [[:get_dstring]] | |
409 | ||
410 | 138 | |
411 | sequence s | |
412 | integer c, first, last | |
413 | ||
414 | 138 | if n = 0 then |
415 | 52 | return {} |
416 | end if | |
417 | ||
418 | 86 | c = getc(fn) |
419 | 86 | if c = EOF then |
420 | 0 | return {} |
421 | end if | |
422 | ||
423 | 86 | s = repeat(c, n) |
424 | ||
425 | 86 | last = 1 |
426 | 86 | while last < n do |
427 | -- for speed, read a chunk without checking for EOF | |
428 | 110 | first = last+1 |
429 | 110 | last = last+CHUNK |
430 | 110 | if last > n then |
431 | 86 | last = n |
432 | end if | |
433 | 110 | for i = first to last do |
434 | 5710 | s[i] = getc(fn) |
435 | 5710 | end for |
436 | -- check for EOF after each chunk | |
437 | 110 | if s[last] = EOF then |
438 | -- trim the EOF's and return | |
439 | 0 | while s[last] = EOF do |
440 | 0 | last -= 1 |
441 | 0 | end while |
442 | 0 | return s[1..last] |
443 | end if | |
444 | 110 | end while |
445 | 86 | return s |
446 | end function | |
447 | ||
448 | ||
449 | atom mem0, mem1, mem2, mem3 | |
450 | 101 | mem0 = allocate(4) |
451 | 101 | mem1 = mem0 + 1 |
452 | 101 | mem2 = mem0 + 2 |
453 | 101 | mem3 = mem0 + 3 |
454 | ||
455 | --** | |
456 | -- Read the next four bytes from a file and returns them as a single integer. | |
457 | -- | |
458 | -- Parameters: | |
459 | -- # ##fh## : an integer, the handle to an open file to read from. | |
460 | -- | |
461 | -- Returns: | |
462 | -- An **atom**, made of the bytes that could be read from the file. | |
463 | -- | |
464 | -- Comments: | |
465 | -- * This function is normally used with files opened in binary mode, "rb". | |
466 | -- * Assumes that there at least four bytes available to be read. | |
467 | -- | |
468 | -- Example 1: | |
469 | -- | |
470 | -- | |
471 | -- integer fn | |
472 | -- fn = open("temp", "rb") -- an existing file | |
473 | -- | |
474 | -- atom file_type_code | |
475 | -- file_type_code = get_integer32(fn) | |
476 | -- | |
477 | -- | |
478 | -- See Also: | |
479 | -- [[:getc]], [[:gets]], [[:get_bytes]], [[:get_dstring]] | |
480 | ||
481 | 0 | |
482 | -- read the 4 bytes as a single integer value at current position in file | |
483 | 0 | poke(mem0, getc(fh)) |
484 | 0 | poke(mem1, getc(fh)) |
485 | 0 | poke(mem2, getc(fh)) |
486 | 0 | poke(mem3, getc(fh)) |
487 | 0 | return peek4u(mem0) |
488 | end function | |
489 | ||
490 | --** | |
491 | -- Read the next two bytes from a file and returns them as a single integer. | |
492 | -- | |
493 | -- Parameters: | |
494 | -- # ##fh## : an integer, the handle to an open file to read from. | |
495 | -- | |
496 | -- Returns: | |
497 | -- An **atom**, made of the bytes that could be read from the file. | |
498 | -- | |
499 | -- Comments: | |
500 | -- * This function is normally used with files opened in binary mode, "rb". | |
501 | -- * Assumes that there at least two bytes available to be read. | |
502 | -- | |
503 | -- Example 1: | |
504 | -- | |
505 | -- | |
506 | -- integer fn | |
507 | -- fn = open("temp", "rb") -- an existing file | |
508 | -- | |
509 | -- atom file_type_code | |
510 | -- file_type_code = get_integer16(fn) | |
511 | -- | |
512 | -- | |
513 | -- See Also: | |
514 | -- [[:getc]], [[:gets]], [[:get_bytes]], [[:get_dstring]] | |
515 | ||
516 | 0 | |
517 | -- read the 4 bytes as a single integer value at current position in file | |
518 | 0 | poke(mem0, getc(fh)) |
519 | 0 | poke(mem1, getc(fh)) |
520 | 0 | return peek2u(mem0) |
521 | end function | |
522 | ||
523 | --** | |
524 | -- Write the supplied integer as four bytes to a file. | |
525 | -- | |
526 | -- Parameters: | |
527 | -- # ##fh## : an integer, the handle to an open file to write to. | |
528 | -- # ##val## : an integer | |
529 | -- | |
530 | -- Comments: | |
531 | -- * This function is normally used with files opened in binary mode, "wb". | |
532 | -- | |
533 | -- Example 1: | |
534 | -- | |
535 | -- | |
536 | -- integer fn | |
537 | -- fn = open("temp", "wb") | |
538 | -- | |
539 | -- put_integer32(fn, 1234) | |
540 | -- | |
541 | -- | |
542 | -- See Also: | |
543 | -- [[:getc]], [[:gets]], [[:get_bytes]], [[:get_dstring]] | |
544 | ||
545 | 0 | |
546 | 0 | poke4(mem0, val) |
547 | 0 | puts(fh, peek({mem0,4})) |
548 | 0 | end procedure |
549 | ||
550 | --** | |
551 | -- Write the supplied integer as two bytes to a file. | |
552 | -- | |
553 | -- Parameters: | |
554 | -- # ##fh## : an integer, the handle to an open file to write to. | |
555 | -- # ##val## : an integer | |
556 | -- | |
557 | -- Comments: | |
558 | -- * This function is normally used with files opened in binary mode, "wb". | |
559 | -- | |
560 | -- Example 1: | |
561 | -- | |
562 | -- | |
563 | -- integer fn | |
564 | -- fn = open("temp", "wb") | |
565 | -- | |
566 | -- put_integer16(fn, 1234) | |
567 | -- | |
568 | -- | |
569 | -- See Also: | |
570 | -- [[:getc]], [[:gets]], [[:get_bytes]], [[:get_dstring]] | |
571 | ||
572 | 0 | |
573 | 0 | poke2(mem0, val) |
574 | 0 | puts(fh, peek({mem0,2})) |
575 | 0 | end procedure |
576 | ||
577 | --** | |
578 | -- Read a delimited byte string from an opened file . | |
579 | -- | |
580 | -- Parameters: | |
581 | -- # ##fh## : an integer, the handle to an open file to read from. | |
582 | -- # ##delim## : an integer, the delimiter that marks the end of a byte string. | |
583 | -- If omitted, a zero is assumed. | |
584 | -- | |
585 | -- Returns: | |
586 | -- An **sequence**, made of the bytes that could be read from the file. | |
587 | -- | |
588 | -- Comments: | |
589 | -- * If the end-of-file is found before the delimiter, the delimiter is appended | |
590 | -- to the returned string. | |
591 | -- | |
592 | -- Example 1: | |
593 | -- | |
594 | -- | |
595 | -- integer fn | |
596 | -- fn = open("temp", "rb") -- an existing file | |
597 | -- | |
598 | -- sequence text | |
599 | -- text = get_dstring(fn) -- Get a zero-delimited string | |
600 | -- text = get_dstring(fn, '$') -- Get a '$'-delimited string | |
601 | -- | |
602 | -- | |
603 | -- See Also: | |
604 | -- [[:getc]], [[:gets]], [[:get_bytes]], [[:get_integer32]] | |
605 | ||
606 | 0 | |
607 | sequence s | |
608 | integer c | |
609 | integer i | |
610 | ||
611 | 0 | s = repeat(-1, 256) |
612 | 0 | i = 0 |
613 | 0 | while c != delim with entry do |
614 | 0 | i += 1 |
615 | 0 | if i > length(s) then |
616 | 0 | s &= repeat(-1, 256) |
617 | end if | |
618 | ||
619 | 0 | if c = -1 then |
620 | 0 | exit |
621 | end if | |
622 | 0 | s[i] = c |
623 | entry | |
624 | 0 | c = getc(fh) |
625 | 0 | end while |
626 | ||
627 | 0 | return s[1..i] |
628 | end function | |
629 | ||
630 | --**** | |
631 | -- === Low Level File/Device Handling | |
632 | ||
633 | -- | |
634 | -- Described under lock_file() | |
635 | -- | |
636 | ||
637 | 101 | public enum LOCK_SHARED, LOCK_EXCLUSIVE |
638 | ||
639 | --** | |
640 | -- File number type | |
641 | ||
642 | 23492 | |
643 | 23492 | return f >= 0 |
644 | end type | |
645 | ||
646 | --** | |
647 | -- File position type | |
648 | ||
649 | 21501 | |
650 | 21501 | return p >= -1 |
651 | end type | |
652 | ||
653 | ||
654 | --** | |
655 | -- Lock Type | |
656 | ||
657 | 5 | |
658 | 5 | return t = LOCK_SHARED or t = LOCK_EXCLUSIVE |
659 | end type | |
660 | ||
661 | --** | |
662 | -- Byte Range Type | |
663 | ||
664 | 4 | |
665 | 4 | if length(r) = 0 then |
666 | 4 | return 1 |
667 | 0 | elsif length(r) = 2 and r[1] <= r[2] then |
668 | 0 | return 1 |
669 | else | |
670 | 0 | return 0 |
671 | end if | |
672 | end type | |
673 | ||
674 | --**** | |
675 | -- Signature: | |
676 | -- | |
677 | -- | |
678 | -- Description: | |
679 | -- Open a file or device, to get the file number. | |
680 | -- | |
681 | -- Parameters: | |
682 | -- # ##path## : a string, the path to the file or device to open. | |
683 | -- # ##mode## : a string, the mode being used o open the file. | |
684 | -- # ##cleanup## : an integer, if 0, then the file must be manually closed by the | |
685 | --coder. If 1, then the file will be closed when either the file handle's references | |
686 | --goes to 0, or if called as a parameter to ##delete##(). | |
687 | -- | |
688 | -- Returns: | |
689 | -- A small **integer**, -1 on failure, else 0 or more. | |
690 | -- | |
691 | -- Errors: | |
692 | -- There is a limit on the number of files that can be simultaneously opened, currently 40. | |
693 | -- If this limit is reached, the next attempt to ##open##() a file will error out. | |
694 | -- | |
695 | -- The length of ##path## should not exceed 1,024 characters. | |
696 | -- | |
697 | -- Comments: | |
698 | -- Possible modes are: | |
699 | -- | |
700 | -- * ##"r"## ~-- open text file for reading | |
701 | -- * ##"rb"## ~-- open binary file for reading | |
702 | -- * ##"w"## ~-- create text file for writing | |
703 | -- * ##"wb"## ~-- create binary file for writing | |
704 | -- * ##"u"## ~-- open text file for update (reading and writing) | |
705 | -- * ##"ub"## ~-- open binary file for update | |
706 | -- * ##"a"## ~-- open text file for appending | |
707 | -- * ##"ab"## ~-- open binary file for appending | |
708 | -- | |
709 | -- Files opened for read or update must already exist. Files opened for write or append will | |
710 | -- be created if necessary. A file opened for write will be set to 0 bytes. Output to a | |
711 | -- file opened for append will start at the end of file. | |
712 | -- | |
713 | -- On //Windows//, output to text files will have carriage-return characters automatically | |
714 | -- added before linefeed characters. On input, these carriage-return characters are removed. | |
715 | -- A control-Z character (ASCII 26) will signal an immediate end of file. | |
716 | -- | |
717 | -- I/O to binary files is not modified in any way. Any byte values from 0 to 255 can be | |
718 | -- read or written. On //Unix//, all files are binary files, so "r" mode and "rb" | |
719 | -- mode are equivalent, as are "w" and "wb", "u" and "ub", and "a" and "ab". | |
720 | -- | |
721 | -- Some typical devices that you can open on Windows are: | |
722 | -- | |
723 | -- * ##"CON"## ~-- the console (screen) | |
724 | -- * ##"AUX"## ~-- the serial auxiliary port | |
725 | -- * ##"COM1"## ~-- serial port 1 | |
726 | -- * ##"COM2"## ~-- serial port 2 | |
727 | -- * ##"PRN"## ~-- the printer on the parallel port | |
728 | -- * ##"NUL"## ~-- a non-existent device that accepts and discards output | |
729 | -- | |
730 | -- Close a file or device when done with it, flushing out any still-buffered characters prior. | |
731 | -- | |
732 | -- //WIN32// and //Unix//: Long filenames are fully supported for reading and writing and | |
733 | -- creating. | |
734 | -- | |
735 | -- //WIN32//: Be careful not to use the special device names in a file name, even if you add an | |
736 | -- extension. e.g. ##CON.TXT##, ##CON.DAT##, ##CON.JPG## etc. all refer to the ##CON## device, | |
737 | -- **not a file**. | |
738 | -- | |
739 | -- Example 1: | |
740 | -- | |
741 | -- integer file_num, file_num95 | |
742 | -- sequence first_line | |
743 | -- constant ERROR = 2 | |
744 | -- | |
745 | -- file_num = open("my_file", "r") | |
746 | -- if file_num = -1 then | |
747 | -- puts(ERROR, "couldn't open my_file\n") | |
748 | -- else | |
749 | -- first_line = gets(file_num) | |
750 | -- end if | |
751 | -- | |
752 | -- file_num = open("PRN", "w") -- open printer for output | |
753 | -- | |
754 | -- -- on Windows 95: | |
755 | -- file_num95 = open("big_directory_name\\very_long_file_name.abcdefg", | |
756 | -- "r") | |
757 | -- if file_num95 != -1 then | |
758 | -- puts(STDOUT, "it worked!\n") | |
759 | -- end if | |
760 | -- | |
761 | ||
762 | --**** | |
763 | -- Signature: | |
764 | -- | |
765 | -- | |
766 | -- Description: | |
767 | -- Close a file or device and flush out any still-buffered characters. | |
768 | -- | |
769 | -- Parameters: | |
770 | -- # ##fn## : an integer, the handle to the file or device to query. | |
771 | -- | |
772 | -- Errors: | |
773 | -- The target file or device must be open. | |
774 | -- | |
775 | -- Comments: | |
776 | -- Any still-open files will be closed automatically when your program terminates. | |
777 | ||
778 | --** | |
779 | -- Seek (move) to any byte position in a file. | |
780 | -- | |
781 | -- Parameters: | |
782 | -- # ##fn## : an integer, the handle to the file or device to seek() | |
783 | -- # ##pos## : an atom, either an absolute 0-based position or -1 to seek to end of file. | |
784 | -- | |
785 | -- Returns: | |
786 | -- An **integer**, 0 on success, 1 on failure. | |
787 | -- | |
788 | -- Errors: | |
789 | -- The target file or device must be open. | |
790 | -- | |
791 | -- Comments: | |
792 | -- For each open file, there is a current byte position that is updated as a result of I/O | |
793 | -- operations on the file. The initial file position is 0 for files opened for read, write | |
794 | -- or update. The initial position is the end of file for files opened for append. | |
795 | -- It is possible to seek past the end of a file. If you seek past the end of the file, and | |
796 | -- write some data, undefined bytes will be inserted into the gap between the original end | |
797 | -- of file and your new data. | |
798 | -- | |
799 | -- After seeking and reading (writing) a series of bytes, you may need to call seek() | |
800 | -- explicitly before you switch to writing (reading) bytes, even though the file position | |
801 | -- should already be what you want. | |
802 | -- | |
803 | -- This function is normally used with files opened in binary mode. In text mode, Windows | |
804 | -- converts CR LF to LF on input, and LF to CR LF on output, which can cause great confusion | |
805 | -- when you are trying to count bytes because seek() counts the Windows end of line sequences | |
806 | -- as two bytes, even if the file has been opened in text mode. | |
807 | -- | |
808 | -- Example 1: | |
809 | -- | |
810 | -- include std/io.e | |
811 | -- | |
812 | -- integer fn | |
813 | -- fn = open("my.data", "rb") | |
814 | -- -- read and display first line of file 3 times: | |
815 | -- for i = 1 to 3 do | |
816 | -- puts(STDOUT, gets(fn)) | |
817 | -- if seek(fn, 0) then | |
818 | -- puts(STDOUT, "rewind failed!\n") | |
819 | -- end if | |
820 | -- end for | |
821 | -- | |
822 | -- | |
823 | -- See Also: | |
824 | -- [[:get_bytes]], [[:puts]], [[:where]] | |
825 | ||
826 | 21496 | |
827 | 21496 | return machine_func(M_SEEK, {fn, pos}) |
828 | end function | |
829 | ||
830 | --** | |
831 | -- Retrieves the current file position for an opened file or device. | |
832 | -- | |
833 | -- Parameters: | |
834 | -- # ##fn## : an integer, the handle to the file or device to query. | |
835 | -- | |
836 | -- | |
837 | -- Returns: | |
838 | -- An **atom**, the current byte position in the file. | |
839 | -- | |
840 | -- Errors: | |
841 | -- The target file or device must be open. | |
842 | -- | |
843 | -- | |
844 | -- Comments: | |
845 | -- The file position is is the place in the file where the next byte will be read from, or | |
846 | -- written to. It is updated by reads, writes and seeks on the file. This procedure always | |
847 | -- counts Windows end of line sequences (CR LF) as two bytes even when the file number has | |
848 | -- been opened in text mode. | |
849 | -- | |
850 | ||
851 | 1989 | |
852 | 1989 | return machine_func(M_WHERE, fn) |
853 | end function | |
854 | ||
855 | --** | |
856 | -- Force writing any buffered data to an open file or device. | |
857 | -- | |
858 | -- Parameters: | |
859 | -- # ##fn## : an integer, the handle to the file or device to close. | |
860 | -- | |
861 | -- Errors: | |
862 | -- The target file or device must be open. | |
863 | -- | |
864 | -- Comments: | |
865 | -- When you write data to a file, Euphoria normally stores the data | |
866 | -- in a memory buffer until a large enough chunk of data has accumulated. | |
867 | -- This large chunk can then be written to disk very efficiently. | |
868 | -- Sometimes you may want to force, or flush, all data out immediately, | |
869 | -- even if the memory buffer is not full. To do this you must call flush(fn), | |
870 | -- where fn is the file number of a file open for writing or appending. | |
871 | -- | |
872 | -- When a file is closed, (see close()), all buffered data is flushed out. | |
873 | -- When a program terminates, all open files are flushed and closed | |
874 | -- automatically. Use flush() when another process may need to | |
875 | -- see all of the data written so far, but you are not ready | |
876 | -- to close the file yet. flush() is also used in crash routines, where files may not be closed in the cleanest possible way. | |
877 | -- | |
878 | -- Example 1: | |
879 | -- | |
880 | -- f = open("file.log", "w") | |
881 | -- puts(f, "Record#1\n") | |
882 | -- puts(STDOUT, "Press Enter when ready\n") | |
883 | -- | |
884 | -- flush(f) -- This forces "Record #1" into "file.log" on disk. | |
885 | -- -- Without this, "file.log" will appear to have | |
886 | -- -- 0 characters when we stop for keyboard input. | |
887 | -- | |
888 | -- s = gets(0) -- wait for keyboard input | |
889 | -- | |
890 | -- | |
891 | -- See Also: | |
892 | -- [[:close]], [[:crash_routine]] | |
893 | ||
894 | 0 | |
895 | 0 | machine_proc(M_FLUSH, fn) |
896 | 0 | end procedure |
897 | ||
898 | --** | |
899 | -- When multiple processes can simultaneously access a | |
900 | -- file, some kind of locking mechanism may be needed to avoid mangling | |
901 | -- the contents of the file, or causing erroneous data to be read from the file. | |
902 | -- | |
903 | -- Parameters: | |
904 | -- # ##fn## : an integer, the handle to the file or device to (partially) lock. | |
905 | -- # ##t## : an integer which defines the kind of lock to apply. | |
906 | -- # ##r## : a sequence, defining a section of the file to be locked, or {} for the whole file (the default). | |
907 | -- | |
908 | -- Returns: | |
909 | -- An **integer**, 0 on failure, 1 on success. | |
910 | -- | |
911 | -- Errors: | |
912 | -- The target file or device must be open. | |
913 | -- | |
914 | -- Comments: | |
915 | -- ##lock_file##() attempts to place a lock on an open file, ##fn##, to stop | |
916 | -- other processes from using the file while your program is reading it | |
917 | -- or writing it. | |
918 | -- | |
919 | -- Under //Unix//, there are two types of locks that | |
920 | -- you can request using the ##t## parameter. (Under //WIN32// the | |
921 | -- parameter ##t## is ignored, but should be an integer.) | |
922 | -- Ask for a **shared** lock when you intend to read a file, and you want to | |
923 | -- temporarily block other processes from writing it. Ask for an | |
924 | -- **exclusive** lock when you intend to write to a file and you want to temporarily | |
925 | -- block other processes from reading or writing it. It's ok for many processes to | |
926 | -- simultaneously have shared locks on the same file, but only one process | |
927 | -- can have an exclusive lock, and that can happen only when no other | |
928 | -- process has any kind of lock on the file. io.e contains the following declarations: | |
929 | -- | |
930 | -- | |
931 | -- public enum | |
932 | -- LOCK_SHARED, | |
933 | -- LOCK_EXCLUSIVE | |
934 | -- | |
935 | -- | |
936 | -- On ///WIN32// you can lock a specified portion of a file using the ##r## parameter. | |
937 | -- ##r## is a sequence of the form: ##{first_byte, last_byte}##. It indicates the first byte and | |
938 | -- last byte in the file, that the lock applies to. Specify the empty sequence ##{}##, | |
939 | -- if you want to lock the whole file, or don't specify it at all, as this is the default. In the current release for //Unix//, locks | |
940 | -- always apply to the whole file, and you should use this default value. | |
941 | -- | |
942 | -- ##lock_file##() does not wait | |
943 | -- for other processes to relinquish their locks. You may have to call it repeatedly, | |
944 | -- before the lock request is granted. | |
945 | -- | |
946 | -- On //Unix//, these locks are called advisory locks, which means they aren't enforced | |
947 | -- by the operating system. It is up to the processes that use a particular file to cooperate | |
948 | -- with each other. A process can access a file without first obtaining a lock on it. On | |
949 | -- //WIN32// locks are enforced by the operating system. | |
950 | -- | |
951 | -- Example 1: | |
952 | -- | |
953 | -- include std/io.e | |
954 | -- integer v | |
955 | -- atom t | |
956 | -- v = open("visitor_log", "a") -- open for append | |
957 | -- t = time() | |
958 | -- while not lock_file(v, LOCK_EXCLUSIVE, {}) do | |
959 | -- if time() > t + 60 then | |
960 | -- puts(STDOUT, "One minute already ... I can't wait forever!\n") | |
961 | -- abort(1) | |
962 | -- end if | |
963 | -- sleep(5) -- let other processes run | |
964 | -- end while | |
965 | -- puts(v, "Yet another visitor\n") | |
966 | -- unlock_file(v, {}) | |
967 | -- close(v) | |
968 | -- | |
969 | -- | |
970 | -- See Also: | |
971 | -- [[:unlock_file]] | |
972 | ||
973 | 2 | |
974 | 2 | return machine_func(M_LOCK_FILE, {fn, t, r}) |
975 | end function | |
976 | ||
977 | --** | |
978 | -- Unlock (a portion of) an open file. | |
979 | -- | |
980 | -- Parameters: | |
981 | -- # ##fn## : an integer, the handle to the file or device to (partially) lock. | |
982 | -- # ##r## : a sequence, defining a section of the file to be locked, or {} for the whole file (the default). | |
983 | -- | |
984 | -- Errors: | |
985 | -- The target file or device must be open. | |
986 | -- | |
987 | -- Comments: | |
988 | -- You must have previously locked the | |
989 | -- file using ##lock_file##(). On //WIN32// you can unlock a range of bytes within a | |
990 | -- file by specifying the ##r## as {first_byte, last_byte}. The same range of bytes | |
991 | -- must have been locked by a previous call to [[:lock_file]](). On //Unix// you can | |
992 | -- currently only lock or unlock an entire file. ##r## should be {} when you | |
993 | -- want to unlock an entire file. On //Unix//, ##r## must always be {}, which is the default. | |
994 | -- | |
995 | -- You should unlock a file as soon as possible so other processes can use it. | |
996 | -- | |
997 | -- Any files that you have locked, will automatically be unlocked when your program | |
998 | -- terminates. | |
999 | -- | |
1000 | -- See Also: | |
1001 | -- [[:lock_file]] | |
1002 | ||
1003 | 2 | |
1004 | -- The byte range can be {} if you want to unlock the whole file. | |
1005 | 2 | machine_proc(M_UNLOCK_FILE, {fn, r}) |
1006 | 2 | end procedure |
1007 | ||
1008 | --**** | |
1009 | -- === File Reading/Writing | |
1010 | ||
1011 | --** | |
1012 | -- Read the contents of a file as a sequence of lines. | |
1013 | -- | |
1014 | -- Parameters: | |
1015 | -- ##file## : an object, either a file path or the handle to an open file. | |
1016 | -- If this is an empty string, STDIN (the console) is used. | |
1017 | -- | |
1018 | -- Returns: | |
1019 | -- A **sequence**, made of lines from the file, as [[:gets]] could read them. | |
1020 | -- | |
1021 | -- Comments: | |
1022 | -- If ##file## was a sequence, the file will be closed on completion. Otherwise, it will remain open, but at end of file. | |
1023 | -- | |
1024 | -- Example 1: | |
1025 | -- | |
1026 | -- data = read_lines("my_file.txt") | |
1027 | -- -- data contains the entire contents of ##my_file.txt##, 1 sequence per line: | |
1028 | -- -- {"Line 1", "Line 2", "Line 3"} | |
1029 | -- | |
1030 | -- | |
1031 | -- Example 2: | |
1032 | -- | |
1033 | -- fh = open("my_file.txt", "r") | |
1034 | -- data = read_lines(fh) | |
1035 | -- close(fh) | |
1036 | -- | |
1037 | -- -- data contains the entire contents of ##my_file.txt##, 1 sequence per line: | |
1038 | -- -- {"Line 1", "Line 2", "Line 3"} | |
1039 | -- | |
1040 | -- | |
1041 | -- See Also: | |
1042 | -- [[:gets]], [[:write_lines]], [[:read_file]] | |
1043 | ||
1044 | 11 | |
1045 | object fn, ret, y | |
1046 | 11 | if sequence(file) then |
1047 | 10 | if length(file) = 0 then |
1048 | 0 | fn = 0 |
1049 | else | |
1050 | 10 | fn = open(file, "r") |
1051 | end if | |
1052 | else | |
1053 | 1 | fn = file |
1054 | end if | |
1055 | 11 | if fn < 0 then return -1 end if |
1056 | ||
1057 | 9 | ret = {} |
1058 | 9 | while sequence(y) with entry do |
1059 | 61 | if y[$] = '\n' then |
1060 | 61 | y = y[1..$-1] |
1061 | 61 | ifdef UNIX then |
1062 | 61 | if length(y) then |
1063 | 54 | if y[$] = '\r' then |
1064 | 30 | y = y[1..$-1] |
1065 | end if | |
1066 | end if | |
1067 | end ifdef | |
1068 | end if | |
1069 | 61 | ret = append(ret, y) |
1070 | 61 | if fn = 0 then |
1071 | 0 | puts(2, '\n') |
1072 | end if | |
1073 | entry | |
1074 | 70 | y = gets(fn) |
1075 | 70 | end while |
1076 | ||
1077 | 9 | if sequence(file) and length(file) != 0 then |
1078 | 8 | close(fn) |
1079 | end if | |
1080 | ||
1081 | 9 | return ret |
1082 | end function | |
1083 | ||
1084 | --** | |
1085 | -- Process the contents of a file, one line at a time. | |
1086 | -- | |
1087 | -- Parameters: | |
1088 | -- # ##file## : an object. Either a file path or the handle to an open file. An | |
1089 | -- empty string signifies STDIN - the console keyboard. | |
1090 | -- # ##proc## : an integer. The routine_id of a function that will process the line. | |
1091 | -- # ##user_data## : on object. This is passed untouched to ##proc## for each line. | |
1092 | -- | |
1093 | -- Returns: | |
1094 | -- An object. If 0 then all the file was processed successfully. Anything else | |
1095 | -- means that something went wrong and this is whatever value was returned by ##proc##. | |
1096 | -- | |
1097 | -- Comments: | |
1098 | -- * The function ##proc## must accept three parameters ... | |
1099 | -- ** A sequence: The line to process. It will **not** contain an end-of-line character. | |
1100 | -- ** An integer: The line number. | |
1101 | -- ** An object : This is the ##user_data## that was passed to ##process_lines##. | |
1102 | -- * If ##file## was a sequence, the file will be closed on completion. | |
1103 | -- Otherwise, it will remain open, and be positioned where ever reading stopped. | |
1104 | -- | |
1105 | -- Example: | |
1106 | -- | |
1107 | -- -- Format each supplied line according to the format pattern supplied as well. | |
1108 | -- function show(sequence aLine, integer line_no, object data) | |
1109 | -- writefln( data[1], {line_no, aLine}) | |
1110 | -- if data[2] > 0 and line_no = data[2] then | |
1111 | -- return 1 | |
1112 | -- else | |
1113 | -- return 0 | |
1114 | -- end if | |
1115 | -- end function | |
1116 | -- -- Show the first 20 lines. | |
1117 | -- process_lines("sample.txt", routine_id("show"), {"[1z:4] : [2]", 20}) | |
1118 | -- | |
1119 | -- | |
1120 | -- See Also: | |
1121 | -- [[:gets]], [[:read_lines]], [[:read_file]] | |
1122 | ||
1123 | 1 | |
1124 | integer fh | |
1125 | object aLine | |
1126 | object res | |
1127 | 1 | integer line_no = 0 |
1128 | ||
1129 | 1 | res = 0 |
1130 | 1 | if sequence(file) then |
1131 | 1 | if length(file) = 0 then |
1132 | 0 | fh = 0 |
1133 | else | |
1134 | 1 | fh = open(file, "r") |
1135 | end if | |
1136 | else | |
1137 | 0 | fh = file |
1138 | end if | |
1139 | 1 | if fh < 0 then |
1140 | 0 | return -1 |
1141 | end if | |
1142 | ||
1143 | 1 | while sequence(aLine) with entry do |
1144 | 9 | line_no += 1 |
1145 | 9 | if length(aLine) then |
1146 | 9 | if aLine[$] = '\n' then |
1147 | 9 | aLine = aLine[1 .. $-1] |
1148 | 9 | ifdef UNIX then |
1149 | 9 | if length(aLine) then |
1150 | 9 | if aLine[$] = '\r' then |
1151 | 9 | aLine = aLine[1 .. $-1] |
1152 | end if | |
1153 | end if | |
1154 | end ifdef | |
1155 | end if | |
1156 | end if | |
1157 | 9 | res = call_func(proc, {aLine, line_no, user_data}) |
1158 | 9 | if not equal(res, 0) then |
1159 | 0 | exit |
1160 | end if | |
1161 | ||
1162 | entry | |
1163 | 10 | aLine = gets(fh) |
1164 | 10 | end while |
1165 | ||
1166 | 1 | if sequence(file) and length(file) != 0 then |
1167 | 1 | close(fh) |
1168 | end if | |
1169 | ||
1170 | 1 | return res |
1171 | end function | |
1172 | ||
1173 | --** | |
1174 | -- Write a sequence of lines to a file. | |
1175 | -- | |
1176 | -- Parameters: | |
1177 | -- # ##file## : an object, either a file path or the handle to an open file. | |
1178 | -- # ##lines## : the sequence of lines to write | |
1179 | -- | |
1180 | -- Returns: | |
1181 | -- An **integer**, 1 on success, -1 on failure. | |
1182 | -- | |
1183 | -- Errors: | |
1184 | -- If [[:puts]]() cannot write some line of text, a runtime error will occur. | |
1185 | -- | |
1186 | -- Comments: | |
1187 | -- If ##file## was a sequence, the file will be closed on completion. Otherwise, it will remain open, but at end of file. | |
1188 | -- | |
1189 | -- Whatever integer the lines in ##lines## holds will be truncated to its 8 lowest bits so as to fall in the 0.255 range. | |
1190 | -- | |
1191 | -- Example 1: | |
1192 | -- | |
1193 | -- if write_lines("data.txt", {"This is important data", "Goodbye"}) != -1 then | |
1194 | -- puts(STDERR, "Failed to write data\n") | |
1195 | -- end if | |
1196 | -- | |
1197 | -- | |
1198 | -- See Also: | |
1199 | -- [[:read_lines]], [[:write_file]], [[:puts]] | |
1200 | ||
1201 | 3 | |
1202 | object fn | |
1203 | ||
1204 | 3 | if sequence(file) then |
1205 | 2 | fn = open(file, "w") |
1206 | else | |
1207 | 1 | fn = file |
1208 | end if | |
1209 | 3 | if fn < 0 then return -1 end if |
1210 | ||
1211 | 3 | for i = 1 to length(lines) do |
1212 | 5 | puts(fn, lines[i]) |
1213 | 5 | puts(fn, '\n') |
1214 | 5 | end for |
1215 | ||
1216 | 3 | if sequence(file) then |
1217 | 2 | close(fn) |
1218 | end if | |
1219 | ||
1220 | 3 | return 1 |
1221 | end function | |
1222 | ||
1223 | --** | |
1224 | -- Append a sequence of lines to a file. | |
1225 | -- | |
1226 | -- Parameters: | |
1227 | -- # ##file## : an object, either a file path or the handle to an open file. | |
1228 | -- # ##lines## : the sequence of lines to write | |
1229 | -- | |
1230 | -- Returns: | |
1231 | -- An **integer**, 1 on success, -1 on failure. | |
1232 | -- | |
1233 | -- Errors: | |
1234 | -- If [[:puts]]() cannot write some line of text, a runtime error will occur. | |
1235 | -- | |
1236 | -- Comments: | |
1237 | -- ##file## is opened, written to and then closed. | |
1238 | -- | |
1239 | -- Example 1: | |
1240 | -- | |
1241 | -- if append_lines("data.txt", {"This is important data", "Goodbye"}) != -1 then | |
1242 | -- puts(STDERR, "Failed to append data\n") | |
1243 | -- end if | |
1244 | -- | |
1245 | -- | |
1246 | -- See Also: | |
1247 | -- [[:write_lines]], [[:puts]] | |
1248 | ||
1249 | 1 | |
1250 | object fn | |
1251 | ||
1252 | 1 | fn = open(file, "a") |
1253 | 1 | if fn < 0 then return -1 end if |
1254 | ||
1255 | 1 | for i = 1 to length(lines) do |
1256 | 1 | puts(fn, lines[i]) |
1257 | 1 | puts(fn, '\n') |
1258 | 1 | end for |
1259 | ||
1260 | 1 | close(fn) |
1261 | ||
1262 | 1 | return 1 |
1263 | end function | |
1264 | ||
1265 | public enum | |
1266 | 101 | BINARY_MODE, |
1267 | 101 | TEXT_MODE, |
1268 | 101 | UNIX_TEXT, |
1269 | 101 | DOS_TEXT |
1270 | ||
1271 | --** | |
1272 | -- Read the contents of a file as a single sequence of bytes. | |
1273 | -- | |
1274 | -- Parameters: | |
1275 | -- # ##file## : an object, either a file path or the handle to an open file. | |
1276 | -- # ##as_text## : integer, **BINARY_MODE** (the default) assumes //binary mode// that | |
1277 | -- causes every byte to be read in, | |
1278 | -- and **TEXT_MODE** assumes //text mode// that ensures that | |
1279 | -- lines end with just a Ctrl-J (NewLine) character, | |
1280 | -- and the first byte value of 26 (Ctrl-Z) is interpreted as End-Of-File. | |
1281 | -- | |
1282 | -- Returns: | |
1283 | -- A **sequence**, holding the entire file. | |
1284 | -- | |
1285 | -- Comments | |
1286 | -- * When using BINARY_MODE, each byte in the file is returned as an element in | |
1287 | -- the return sequence. | |
1288 | -- * When not using BINARY_MODE, the file will be interpreted as a text file. This | |
1289 | -- means that all line endings will be transformed to a single 0x0A character and | |
1290 | -- the first 0x1A character (Ctrl-Z) will indicate the end of file (all data after this | |
1291 | -- will not be returned to the caller.) | |
1292 | -- | |
1293 | -- Example 1: | |
1294 | -- | |
1295 | -- data = read_file("my_file.txt") | |
1296 | -- -- data contains the entire contents of ##my_file.txt## | |
1297 | -- | |
1298 | -- | |
1299 | -- Example 2: | |
1300 | -- | |
1301 | -- fh = open("my_file.txt", "r") | |
1302 | -- data = read_file(fh) | |
1303 | -- close(fh) | |
1304 | -- | |
1305 | -- -- data contains the entire contents of ##my_file.txt## | |
1306 | -- | |
1307 | -- | |
1308 | -- See Also: | |
1309 | -- [[:write_file]], [[:read_lines]] | |
1310 | ||
1311 | 21 | |
1312 | integer fn | |
1313 | integer len | |
1314 | sequence ret | |
1315 | object temp | |
1316 | atom adr | |
1317 | ||
1318 | 21 | if sequence(file) then |
1319 | 19 | fn = open(file, "rb") |
1320 | else | |
1321 | 2 | fn = file |
1322 | end if | |
1323 | 21 | if fn < 0 then return -1 end if |
1324 | ||
1325 | 21 | temp = seek(fn, -1) |
1326 | 21 | len = where(fn) |
1327 | 21 | temp = seek(fn, 0) |
1328 | ||
1329 | 21 | ret = repeat(0, len) |
1330 | 21 | for i = 1 to len do |
1331 | 1305 | ret[i] = getc(fn) |
1332 | 1305 | end for |
1333 | ||
1334 | 21 | if sequence(file) then |
1335 | 19 | close(fn) |
1336 | end if | |
1337 | ||
1338 | 21 | ifdef WINDOWS then |
1339 | -- Remove any extra -1 (EOF) characters in case file | |
1340 | -- had been opened in Windows 'text mode'. | |
1341 | for i = len to 1 by -1 do | |
1342 | if ret[i] != -1 then | |
1343 | if i != len then | |
1344 | ret = ret[1 .. i] | |
1345 | end if | |
1346 | exit | |
1347 | end if | |
1348 | end for | |
1349 | end ifdef | |
1350 | ||
1351 | 21 | if as_text = BINARY_MODE then |
1352 | 18 | return ret |
1353 | end if | |
1354 | ||
1355 | -- Treat as a text file. | |
1356 | 3 | fn = find(26, ret) -- Any Ctrl-Z found? |
1357 | 3 | if fn then |
1358 | -- Ok, so truncate the file data | |
1359 | 1 | ret = ret[1 .. fn - 1] |
1360 | end if | |
1361 | ||
1362 | -- Convert Windows endings | |
1363 | 3 | ret = replace_all(ret, {13,10}, {10}) |
1364 | 3 | if length(ret) > 0 then |
1365 | 3 | if ret[$] != 10 then |
1366 | 2 | ret &= 10 |
1367 | end if | |
1368 | else | |
1369 | 0 | ret = {10} |
1370 | end if | |
1371 | ||
1372 | 3 | return ret |
1373 | end function | |
1374 | ||
1375 | --** | |
1376 | -- Write a sequence of bytes to a file. | |
1377 | -- | |
1378 | -- Parameters: | |
1379 | -- # ##file## : an object, either a file path or the handle to an open file. | |
1380 | -- # ##data## : the sequence of bytes to write | |
1381 | -- # ##as_text## : integer | |
1382 | -- ** **BINARY_MODE** (the default) assumes //binary mode// that | |
1383 | -- causes every byte to be written out as is, | |
1384 | -- ** **TEXT_MODE** assumes //text mode// that causes a NewLine | |
1385 | -- to be written out according to the operating system's | |
1386 | -- end of line convention. In Unix this is Ctrl-J and in | |
1387 | -- Windows this is the pair {Ctrl-L, Ctrl-J}. | |
1388 | -- ** **UNIX_TEXT** ensures that lines are written out with unix style | |
1389 | -- line endings (Ctrl-J). | |
1390 | -- ** **DOS_TEXT** ensures that lines are written out with Windows style | |
1391 | -- line endings {Ctrl-L, Ctrl-J}. | |
1392 | -- Returns: | |
1393 | -- An **integer**, 1 on success, -1 on failure. | |
1394 | -- | |
1395 | -- Errors: | |
1396 | -- If [[:puts]] cannot write ##data##, a runtime error will occur. | |
1397 | -- | |
1398 | -- Comments: | |
1399 | -- * When ##file## is a file handle, the file is not closed after writing is finished. When ##file## is a | |
1400 | -- file name, it is opened, written to and then closed. | |
1401 | -- * Note that when writing the file in ony of the text modes, the file is truncated | |
1402 | -- at the first Ctrl-Z character in the input data. | |
1403 | -- | |
1404 | -- Example 1: | |
1405 | -- | |
1406 | -- if write_file("data.txt", "This is important data\nGoodbye") = -1 then | |
1407 | -- puts(STDERR, "Failed to write data\n") | |
1408 | -- end if | |
1409 | -- | |
1410 | -- | |
1411 | -- See Also: | |
1412 | -- [[:read_file]], [[:write_lines]] | |
1413 | ||
1414 | 24 | |
1415 | integer fn | |
1416 | atom adr | |
1417 | ||
1418 | 24 | if as_text != BINARY_MODE then |
1419 | -- Truncate at first Ctrl-Z | |
1420 | 17 | fn = find(26, data) |
1421 | 17 | if fn then |
1422 | 3 | data = data[1 .. fn-1] |
1423 | end if | |
1424 | -- Ensure last line has a line-end marker. | |
1425 | 17 | if length(data) > 0 then |
1426 | 17 | if data[$] != 10 then |
1427 | 10 | data &= 10 |
1428 | end if | |
1429 | else | |
1430 | 0 | data = {10} |
1431 | end if | |
1432 | ||
1433 | 17 | if as_text = TEXT_MODE then |
1434 | -- Standardize all line endings | |
1435 | 8 | data = replace_all(data, {13,10}, {10}) |
1436 | ||
1437 | 9 | elsif as_text = UNIX_TEXT then |
1438 | 3 | data = replace_all(data, {13,10}, {10}) |
1439 | ||
1440 | 6 | elsif as_text = DOS_TEXT then |
1441 | 6 | data = replace_all(data, {13,10}, {10}) |
1442 | 6 | data = replace_all(data, {10}, {13,10}) |
1443 | ||
1444 | end if | |
1445 | end if | |
1446 | ||
1447 | ||
1448 | 24 | if sequence(file) then |
1449 | 23 | if as_text = TEXT_MODE then |
1450 | 8 | fn = open(file, "w") |
1451 | else | |
1452 | 15 | fn = open(file, "wb") |
1453 | end if | |
1454 | else | |
1455 | 1 | fn = file |
1456 | end if | |
1457 | 24 | if fn < 0 then return -1 end if |
1458 | ||
1459 | 24 | puts(fn, data) |
1460 | ||
1461 | 24 | if sequence(file) then |
1462 | 23 | close(fn) |
1463 | end if | |
1464 | ||
1465 | 24 | return 1 |
1466 | end function | |
1467 | ||
1468 | ||
1469 | --** | |
1470 | -- Write formatted text to a file.. | |
1471 | -- | |
1472 | -- Parameters: | |
1473 | -- There are two ways to pass arguments to this function, | |
1474 | -- # Traditional way with first arg being a file handle. | |
1475 | -- ## : integer, The file handle. | |
1476 | -- ## : sequence, The format pattern. | |
1477 | -- ## : object, The data that will be formatted. | |
1478 | -- ## ##data_not_string##: object, If not 0 then the ##data## is not a string. | |
1479 | -- By default this is 0 meaning that ##data## could be a single string. | |
1480 | -- # Alternative way with first argument being the format pattern. | |
1481 | -- ## : sequence, Format pattern. | |
1482 | -- ## : sequence, The data that will be formatted, | |
1483 | -- ## : object, The file to receive the formatted output. Default is | |
1484 | -- to the STDOUT device (console). | |
1485 | -- ## ##data_not_string##: object, If not 0 then the ##data## is not a string. | |
1486 | -- By default this is 0 meaning that ##data## could be a single string. | |
1487 | -- | |
1488 | -- Comments: | |
1489 | -- * With the traditional arguments, the first argument must be an integer file handle. | |
1490 | -- * With the alternative arguments, the thrid argument can be a file name string, | |
1491 | -- in which case it is opened for output, written to and then closed. | |
1492 | -- * With the alternative arguments, the third argument can be a two-element sequence | |
1493 | -- containing a file name string and an output type ("a" for append, "w" for write), | |
1494 | -- in which case it is opened accordingly, written to and then closed. | |
1495 | -- * With the alternative arguments, the third argument can a file handle, | |
1496 | -- in which case it is written to only | |
1497 | -- * The format pattern uses the formatting codes defined in [[:text:format]]. | |
1498 | -- * When the data to be formatted is a single text string, it does not have to | |
1499 | -- be enclosed in braces, | |
1500 | -- | |
1501 | -- Example 1: | |
1502 | -- | |
1503 | -- -- To console | |
1504 | -- writef("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}) | |
1505 | -- -- To "sample.txt" | |
1506 | -- writef("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}, "sample.txt") | |
1507 | -- -- To "sample.dat" | |
1508 | -- integer dat = open("sample.dat", "w") | |
1509 | -- writef("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}, dat) | |
1510 | -- -- Appended to "sample.log" | |
1511 | -- writef("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}, {"sample.log", "a"}) | |
1512 | -- -- Simple message to console | |
1513 | -- writef("A message") | |
1514 | -- -- Another console message | |
1515 | -- writef(STDERR, "This is a []", "message") | |
1516 | -- -- Outputs two numbers | |
1517 | -- writef(STDERR, "First [], second []", {65, 100},, 1) -- Note that {65, 100} is also "Ad" | |
1518 | -- | |
1519 | -- | |
1520 | -- See Also: | |
1521 | -- [[:text:format]], [[:writefln]], [[:write_lines]] | |
1522 | ||
1523 | 0 | |
1524 | 0 | integer real_fn = 0 |
1525 | 0 | integer close_fn = 0 |
1526 | 0 | sequence out_style = "w" |
1527 | ||
1528 | 0 | if integer(fm) then |
1529 | 0 | object ts |
1530 | -- File Handle in first arguement so rotate the arguments. | |
1531 | 0 | ts = fm |
1532 | 0 | fm = data |
1533 | 0 | data = fn |
1534 | 0 | fn = ts |
1535 | end if | |
1536 | ||
1537 | 0 | if sequence(fn) then |
1538 | 0 | if length(fn) = 2 then |
1539 | 0 | if sequence(fn[1]) then |
1540 | 0 | if equal(fn[2], 'a') then |
1541 | 0 | out_style = "a" |
1542 | 0 | elsif not equal(fn[2], "a") then |
1543 | 0 | out_style = "w" |
1544 | else | |
1545 | 0 | out_style = "a" |
1546 | end if | |
1547 | 0 | fn = fn[1] |
1548 | end if | |
1549 | end if | |
1550 | 0 | real_fn = open(fn, out_style) |
1551 | ||
1552 | 0 | if real_fn = -1 then |
1553 | 0 | crash("Unable to write to '%s'", {fn}) |
1554 | end if | |
1555 | 0 | close_fn = 1 |
1556 | else | |
1557 | 0 | real_fn = fn |
1558 | end if | |
1559 | ||
1560 | 0 | if equal(data_not_string, 0) then |
1561 | 0 | if t_display(data) then |
1562 | 0 | data = {data} |
1563 | end if | |
1564 | end if | |
1565 | 0 | puts(real_fn, format(fm, data)) |
1566 | 0 | if close_fn then |
1567 | 0 | close(real_fn) |
1568 | end if | |
1569 | 0 | end procedure |
1570 | ||
1571 | --** | |
1572 | -- Write formatted text to a file, ensuring that a new line is also output. | |
1573 | -- | |
1574 | -- Parameters: | |
1575 | -- # ##fm## : sequence, Format pattern. | |
1576 | -- # ##data## : sequence, The data that will be formatted, | |
1577 | -- # ##fn## : object, The file to receive the formatted output. Default is | |
1578 | -- to the STDOUT device (console). | |
1579 | -- # ##data_not_string##: object, If not 0 then the ##data## is not a string. | |
1580 | -- By default this is 0 meaning that ##data## could be a single string. | |
1581 | -- | |
1582 | -- Comments: | |
1583 | -- * This is the same as [[:writef]], except that it always adds a New Line to | |
1584 | -- the output. | |
1585 | -- * When ##fn## is a file name string, it is opened for output, | |
1586 | -- written to and then closed. | |
1587 | -- * When ##fn## is a two-element sequence containing a file name string and | |
1588 | -- an output type ("a" for append, "w" for write), it is opened accordingly, | |
1589 | -- written to and then closed. | |
1590 | -- * When ##fn## is a file handle, it is written to only | |
1591 | -- * The ##fm## uses the formatting codes defined in [[:text:format]]. | |
1592 | -- | |
1593 | -- Example 1: | |
1594 | -- | |
1595 | -- -- To console | |
1596 | -- writefln("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}) | |
1597 | -- -- To "sample.txt" | |
1598 | -- writefln("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}, "sample.txt") | |
1599 | -- -- Appended to "sample.log" | |
1600 | -- writefln("Today is [4], [u2:3] [3:02], [1:4].", {Year, MonthName, Day, DayName}, {"sample.log", "a"}) | |
1601 | -- | |
1602 | -- | |
1603 | -- See Also: | |
1604 | -- [[:text:format]], [[:writef]], [[:write_lines]] | |
1605 | 0 | |
1606 | 0 | if integer(fm) then |
1607 | 0 | writef(data & '\n', fn, fm, data_not_string) |
1608 | else | |
1609 | 0 | writef(fm & '\n', data, fn, data_not_string) |
1610 | end if | |
1611 | 0 | end procedure |