getc: make sure EOF is "sticky"
[profile/ivi/syslinux.git] / getc.inc
1 ;; -----------------------------------------------------------------------
2 ;;
3 ;;   Copyright 1994-2007 H. Peter Anvin - All Rights Reserved
4 ;;
5 ;;   This program is free software; you can redistribute it and/or modify
6 ;;   it under the terms of the GNU General Public License as published by
7 ;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 ;;   Boston MA 02111-1307, USA; either version 2 of the License, or
9 ;;   (at your option) any later version; incorporated herein by reference.
10 ;;
11 ;; -----------------------------------------------------------------------
12
13 ;;
14 ;; getc.inc
15 ;;
16 ;; Simple file handling library (open, getc, ungetc)
17 ;;
18 ;; WARNING: This interface uses the real_mode_seg/comboot_seg.
19 ;;
20
21 MAX_GETC_LG2    equ 4                   ; Max number of file nesting
22 MAX_GETC        equ (1 << MAX_GETC_LG2)
23 bytes_per_getc_lg2      equ 16-MAX_GETC_LG2
24 bytes_per_getc          equ (1 << bytes_per_getc_lg2)
25 secs_per_getc   equ bytes_per_getc/SECTOR_SIZE
26 MAX_UNGET       equ 5                   ; Max bytes that can be pushed back
27
28                 struc getc_file
29 gc_file         resw 1                  ; File pointer
30 gc_bufbytes     resw 1                  ; Bytes left in buffer
31 gc_bytes        resd 1                  ; Bytes left in file
32 gc_bufdata      resw 1                  ; Pointer to data in buffer
33 gc_unget_cnt    resb 1                  ; Character pushed back count
34 gc_unget_buf    resb MAX_UNGET          ; Character pushed back buffer
35                 endstruc
36 getc_file_lg2   equ 4                   ; Size of getc_file as a power of 2
37
38 %ifndef DEPEND
39 %if (getc_file_size != (1 << getc_file_lg2))
40 %error "getc_file_size != (1 << getc_file_lg2)"
41 %endif
42 %endif
43
44 ;
45 ; open,getc:    Load a file a character at a time for parsing in a manner
46 ;               similar to the C library getc routine.
47 ;               Up to MAX_GETC files can be open at the same time,
48 ;               they are accessed in a stack-like fashion.
49 ;
50 ;               All routines assume CS == DS.
51 ;
52 ;               open:   Input:  mangled filename in DS:DI
53 ;                       Output: ZF set on file not found or zero length
54 ;
55 ;               openfd: Input:  file handle in SI
56 ;                       Output: ZF set on getc stack overflow
57 ;
58 ;               getc:   Output: CF set on end of file
59 ;                               Character loaded in AL
60 ;
61 ;               close:  Output: CF set if nothing open
62 ;
63 open:
64                 call searchdir
65                 jz openfd.ret
66 openfd:
67                 push bx
68
69                 mov bx,[CurrentGetC]
70                 sub bx,getc_file_size
71                 cmp bx,GetCStack
72                 jb .stack_full          ; Excessive nesting
73                 mov [CurrentGetC],bx
74
75                 mov [bx+gc_file],si     ; File pointer
76                 mov [bx+gc_bytes],ax    ; Bytes available
77                 mov [bx+gc_bytes+2],dx
78                 xor ax,ax
79                 mov [bx+gc_bufbytes],ax         ; Buffer empty
80                 mov [bx+gc_unget_cnt],al        ; ungetc buffer empty
81                 
82                 inc ax                  ; ZF <- 0
83                 pop bx
84 .ret:           ret
85
86 .stack_full:
87                 call close_file
88                 xor ax,ax               ; ZF <- 1
89                 pop bx
90                 ret
91
92 getc:
93                 push bx
94                 push si
95                 push di
96                 push es
97
98                 mov di,[CurrentGetC]
99                 movzx bx,byte [di+gc_unget_cnt]
100                 and bx,bx
101                 jnz .have_unget
102
103                 mov ax,real_mode_seg    ; Borrow the real_mode_seg
104                 mov es,ax
105 .got_data:
106                 sub word [di+gc_bufbytes],1
107                 jc .get_data            ; Was it zero already?
108                 mov si,[di+gc_bufdata]
109                 mov al,[es:si]
110                 inc si
111                 mov [di+gc_bufdata],si
112 .done:
113                 clc
114 .ret:
115                 pop es
116                 pop di
117                 pop si
118                 pop bx
119                 ret
120 .have_unget:
121                 dec bx
122                 mov al,[di+bx+gc_unget_buf]
123                 mov [di+gc_unget_cnt],bl
124                 jmp .done
125
126 .get_data:
127                 pushad
128                 ; Compute start of buffer
129                 mov bx,di
130                 sub bx,GetCStack
131                 shl bx,bytes_per_getc_lg2-getc_file_lg2
132
133                 mov [di+gc_bufdata],bx
134                 mov si,[di+gc_file]
135                 mov ecx,[di+gc_bytes]
136                 jecxz .empty
137                 cmp ecx,bytes_per_getc
138                 jna .sizeok
139                 mov ecx,bytes_per_getc
140 .sizeok:
141                 mov [di+gc_bufbytes],cx
142                 sub [di+gc_bytes],ecx
143                 add cx,SECTOR_SIZE-1
144                 shr cx,SECTOR_SHIFT
145                 call getfssec
146                 mov [di+gc_file],si
147                 popad
148                 jmp .got_data
149
150 .empty:
151                 ; CX == 0 at this point; gc_bufbytes was clobbered
152                 ; by the subtract; we need to restore it to zero so
153                 ; we will continue to get EOF on any further attempts
154                 ; to read the file.
155                 mov [di+gc_bufbytes],cx
156                 popad
157                 stc
158                 jmp .ret
159
160 close:
161                 push bx
162                 push si
163                 mov bx,[CurrentGetC]
164                 mov si,[bx+gc_file]
165                 and si,si
166                 jz .closed
167 .closed:
168                 call close_file
169                 add bx,getc_file_size
170                 mov [CurrentGetC],bx
171                 pop si
172                 pop bx
173                 ret
174
175 ;
176 ; ungetc:       Push a character (in AL) back into the getc buffer
177 ;               Note: if more than MAX_UNGET bytes are pushed back, all
178 ;               hell will break loose.
179 ;
180 ungetc:
181                 push di
182                 push bx
183                 mov di,[CurrentGetC]
184                 movzx bx,[di+gc_unget_cnt]
185                 mov [bx+di+gc_unget_buf],al
186                 inc bx
187                 mov [di+gc_unget_cnt],bl
188                 pop bx
189                 pop di
190                 ret
191
192 ;
193 ; skipspace:    Skip leading whitespace using "getc".  If we hit end-of-line
194 ;               or end-of-file, return with carry set; ZF = true of EOF
195 ;               ZF = false for EOLN; otherwise CF = ZF = 0.
196 ;
197 ;               Otherwise AL = first character after whitespace
198 ;
199 skipspace:
200 .loop:          call getc
201                 jc .eof
202                 cmp al,1Ah                      ; DOS EOF
203                 je .eof
204                 cmp al,0Ah
205                 je .eoln
206                 cmp al,' '
207                 jbe .loop
208                 ret                             ; CF = ZF = 0
209 .eof:           cmp al,al                       ; Set ZF
210                 stc                             ; Set CF
211                 ret
212 .eoln:          add al,0FFh                     ; Set CF, clear ZF
213                 ret
214
215 ;
216 ; getint:       Load an integer from the getc file.
217 ;               Return CF if error; otherwise return integer in EBX
218 ;
219 getint:
220                 mov di,NumBuf
221 .getnum:        cmp di,NumBufEnd        ; Last byte in NumBuf
222                 jae .loaded
223                 push di
224                 call getc
225                 pop di
226                 jc .loaded
227                 stosb
228                 cmp al,'-'
229                 jnb .getnum
230                 call ungetc             ; Unget non-numeric
231 .loaded:        mov byte [di],0
232                 mov si,NumBuf
233                 ; Fall through to parseint
234
235 ;
236 ; parseint:     Convert an integer to a number in EBX
237 ;               Get characters from string in DS:SI
238 ;               Return CF on error
239 ;               DS:SI points to first character after number
240 ;
241 ;               Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+[KMG]
242 ;
243 parseint:
244                 push eax
245                 push ecx
246                 push bp
247                 xor eax,eax             ; Current digit (keep eax == al)
248                 mov ebx,eax             ; Accumulator
249                 mov ecx,ebx             ; Base
250                 xor bp,bp               ; Used for negative flag
251 .begin:         lodsb
252                 cmp al,'-'
253                 jne .not_minus
254                 xor bp,1                ; Set unary minus flag
255                 jmp short .begin
256 .not_minus:
257                 cmp al,'0'
258                 jb .err
259                 je .octhex
260                 cmp al,'9'
261                 ja .err
262                 mov cl,10               ; Base = decimal
263                 jmp short .foundbase
264 .octhex:
265                 lodsb
266                 cmp al,'0'
267                 jb .km          ; Value is zero
268                 or al,20h               ; Downcase
269                 cmp al,'x'
270                 je .ishex
271                 cmp al,'7'
272                 ja .err
273                 mov cl,8                ; Base = octal
274                 jmp short .foundbase
275 .ishex:
276                 mov al,'0'              ; No numeric value accrued yet
277                 mov cl,16               ; Base = hex
278 .foundbase:
279                 call unhexchar
280                 jc .km                ; Not a (hex) digit
281                 cmp al,cl
282                 jae .km                 ; Invalid for base
283                 imul ebx,ecx            ; Multiply accumulated by base
284                 add ebx,eax             ; Add current digit
285                 lodsb
286                 jmp short .foundbase
287 .km:
288                 dec si                  ; Back up to last non-numeric
289                 lodsb
290                 or al,20h
291                 cmp al,'k'
292                 je .isk
293                 cmp al,'m'
294                 je .ism
295                 cmp al,'g'
296                 je .isg
297                 dec si                  ; Back up
298 .fini:          and bp,bp
299                 jz .ret         ; CF=0!
300                 neg ebx                 ; Value was negative
301 .done:          clc
302 .ret:           pop bp
303                 pop ecx
304                 pop eax
305                 ret
306 .err:           stc
307                 jmp short .ret
308 .isg:           shl ebx,10              ; * 2^30
309 .ism:           shl ebx,10              ; * 2^20
310 .isk:           shl ebx,10              ; * 2^10
311                 jmp .fini
312
313                 section .bss
314                 alignb 4
315 NumBuf          resb 15                 ; Buffer to load number
316 NumBufEnd       resb 1                  ; Last byte in NumBuf
317
318 GetCStack       resb getc_file_size*MAX_GETC
319 .end            equ $
320
321                 section .data
322 CurrentGetC     dw GetCStack.end        ; GetCStack empty
323
324 ;
325 ; unhexchar:    Convert a hexadecimal digit in AL to the equivalent number;
326 ;               return CF=1 if not a hex digit
327 ;
328                 section .text
329 unhexchar:
330                 cmp al,'0'
331                 jb .ret                 ; If failure, CF == 1 already
332                 cmp al,'9'
333                 ja .notdigit
334                 sub al,'0'              ; CF <- 0
335                 ret
336 .notdigit:      or al,20h               ; upper case -> lower case
337                 cmp al,'a'
338                 jb .ret                 ; If failure, CF == 1 already
339                 cmp al,'f'
340                 ja .err
341                 sub al,'a'-10           ; CF <- 0
342                 ret
343 .err:           stc
344 .ret:           ret
345
346 ;
347 ;
348 ; getline:      Get a command line, converting control characters to spaces
349 ;               and collapsing streches to one; a space is appended to the
350 ;               end of the string, unless the line is empty.
351 ;               The line is terminated by ^J, ^Z or EOF and is written
352 ;               to ES:DI.  On return, DI points to first char after string.
353 ;               CF is set if we hit EOF.
354 ;
355 getline:
356                 call skipspace
357                 mov dl,1                ; Empty line -> empty string.
358                 jz .eof               ; eof
359                 jc .eoln              ; eoln
360                 call ungetc
361 .fillloop:      push dx
362                 push di
363                 call getc
364                 pop di
365                 pop dx
366                 jc .ret         ; CF set!
367                 cmp al,' '
368                 jna .ctrl
369                 xor dx,dx
370 .store:         stosb
371                 jmp short .fillloop
372 .ctrl:          cmp al,10
373                 je .ret         ; CF clear!
374                 cmp al,26
375                 je .eof
376                 and dl,dl
377                 jnz .fillloop           ; Ignore multiple spaces
378                 mov al,' '              ; Ctrl -> space
379                 inc dx
380                 jmp short .store
381 .eoln:          clc                     ; End of line is not end of file
382                 jmp short .ret
383 .eof:           stc
384 .ret:           pushf                   ; We want the last char to be space!
385                 and dl,dl
386                 jnz .xret
387                 mov al,' '
388                 stosb
389 .xret:          popf
390                 ret