Subversion Repositories Kolibri OS

Rev

Rev 2816 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2816 clevermous 1
; This program parses .fas file generated by fasm
2
; and prints the list of dependencies for make.
3
; Usage: fasmdep [-e] [ []].
4
; If input file is not given, the program reads from stdin.
5
; If output file is not given, the program writes to stdout.
6
; If the option -e is given, the program also creates an empty
7
; goal for every dependency, this prevents make errors when
8
; files are renamed.
9
 
10
; This definition controls the choice of platform-specific code.
11
;define OS WINDOWS
12
define OS LINUX
13
 
14
; Program header.
15
match =WINDOWS,OS { include 'windows_header.inc' }
16
match =LINUX,OS { include 'linux_header.inc' }
17
 
18
include 'fas.inc'
19
 
20
; Main code
21
start:
22
; 1. Setup stack frame, zero-initialize all local variables.
23
virtual at ebp - .localsize
24
.begin:
25
.flags          dd      ?       ; 1 if '-e' is given
26
.in             dd      ?       ; handle of input file
27
.out            dd      ?       ; handle of output file
28
.buf            dd      ?       ; pointer to data of .fas file
29
.allocated      dd      ?       ; number of bytes allocated for .buf
30
.free           dd      ?       ; number of bytes free in .buf
31
.outstart       dd      ?       ; offset in .buf for start of data to output
32
.names          dd      ?       ; offset in .buf for start of output names
33
.include        dd      ?       ; offset in .buf to value of %INCLUDE%
34
.testname       dd      ?       ; offset in .buf for start of current file name
35
.prevfile       dd      ?       ; offset in .buf for previous included file name
36
.prevfilefrom   dd      ?       ; offset in .buf for .asm/.inc for .prevfile
37
.localsize = $ - .begin
38
match =LINUX,OS {
39
.argc           dd      ?
40
.argv:
41
}
42
end virtual
43
 
44
        mov     ebp, esp
45
        xor     eax, eax
46
repeat .localsize / 4
47
        push    eax
48
end repeat
49
; 2. Call the parser of the command line.
50
        call    get_params
51
; 3. Load the input file.
52
; Note that stdin can be a pipe,
53
; useful in bash-construction "fasm input.asm -s >(fasmdep > input.Po)",
54
; so its size is not always known in the beginning.
55
; So, we must read input file in portions, reallocating memory if needed.
56
.readloop:
57
; 3a. If the size is less than 32768 bytes, reallocate buffer.
58
; Otherwise, goto 3c.
59
        cmp     [.free], 32768
60
        jae     .norealloc
61
; 3b. Reallocate buffer.
62
; Start with 65536 bytes and then double the size every time.
63
        mov     eax, 65536
64
        mov     ecx, [.allocated]
65
        add     ecx, ecx
66
        jz      @f
67
        mov     eax, ecx
68
@@:
69
        call    realloc
70
.norealloc:
71
; 3c. Read the next portion.
72
        call    read
73
        sub     [.free], eax
74
        test    eax, eax
75
        jnz     .readloop
76
; 4. Sanity checks.
77
; We don't use .section_* and .symref_*, so allow them to be absent.
78
        mov     edi, [.allocated]
79
        sub     edi, [.free]
80
; Note that edi = number of bytes which were read
81
; and simultaneously pointer to free space in the buffer relative to [.buf].
82
        cmp     edi, fas_header.section_offs
83
        jb      badfile
84
        mov     ebx, [.buf]
85
        cmp     [ebx+fas_header.signature], FAS_SIGNATURE
86
        jnz     badfile
87
        cmp     [ebx+fas_header.headersize], fas_header.section_offs
88
        jb      badfile
89
; 5. Get %INCLUDE% environment variable, it will be useful.
90
        mov     [.include], edi
91
        mov     esi, include_variable
92
        sub     esi, ebx
93
        call    get_environment_variable
94
; 6. Start writing dependencies: copy output and input files.
95
        mov     [.outstart], edi
96
; 6a. Copy output name.
97
        mov     esi, [ebx+fas_header.output]
98
        call    copy_asciiz_escaped
99
; 6b. Write ": ".
100
        stdcall alloc_in_buf, 2
101
        mov     word [edi+ebx], ': '
102
        inc     edi
103
        inc     edi
104
; 6c. Copy input name.
105
        mov     [.names], edi
106
        mov     esi, [ebx+fas_header.input]
107
        call    copy_asciiz_escaped
108
; 7. Scan for 'include' dependencies.
109
; 7a. Get range for scanning.
110
        mov     edx, [ebx+fas_header.preproc_size]
111
        mov     esi, [ebx+fas_header.preproc_offs]
112
        add     edx, esi
113
.include_loop:
114
; 7b. Loop over preprocessed lines in the range.
115
        cmp     esi, edx
116
        jae     .include_done
117
; 7c. For every line, ignore header and do internal loop over tokens.
118
        add     esi, preproc_line_header.contents
119
.include_loop_int:
120
; There are five types of tokens:
121
; 1) "start preprocessor data" with code ';'  
122
; 2) "quoted string" with code '"'  
123
; 3) "word" with code 1Ah  
124
; 4) one-byte tokens like "+", "(" and so on
125
; 5) "end-of-line" token with code 0.
126
        mov     al, [esi+ebx]
127
        inc     esi
128
; 7d. First, check token type parsing the first byte.
129
; For preprocessor tokens, continue to 7e.
130
; For quoted strings, go to 7g.
131
; For words, go to 7h.
132
; For end-of-line, exit the internal loop, continue the external loop.
133
; Otherwise, just continue the internal loop.
134
        cmp     al, ';'
135
        jnz     .notprep
136
; 7e. For "include" tokens length=7, token is "include", the next token is
137
; quoted string.
138
; These tokens are handled in 7f, other preprocessor tokens in 7g.
139
        cmp     byte [esi+ebx], 7
140
        jnz     .notinclude
141
        cmp     dword [esi+ebx+1], 'incl'
142
        jnz     .notinclude
143
        cmp     dword [esi+ebx+5], 'ude"'
144
        jnz     .notinclude
145
; 7f. Skip over end of this token and the type byte of the next token;
146
; this gives the pointer to length-prefixed string, which should be added
147
; to dependencies. Note that doing that skips that string,
148
; so after copying just continue the internal loop.
149
        add     esi, 9
150
        call    add_separator
151
        call    copy_name_escaped
152
        jmp     .include_loop_int
153
.quoted:
154
; 7g. To skip a word, load the dword length and add it to pointer.
155
        add     esi, [esi+ebx]
156
        add     esi, 4
157
        jmp     .include_loop_int
158
.notinclude:
159
; 7h. To skip this token, load the byte length and add it to pointer.
160
; Note that word tokens and preprocessor tokens have a similar structure,
161
; so both are handled here.
162
        movzx   eax, byte [esi+ebx]
163
        lea     esi, [esi+eax+1]
164
        jmp     .include_loop_int
165
.notprep:
166
        cmp     al, 1Ah
167
        jz      .notinclude
168
        cmp     al, '"'
169
        jz      .quoted
170
        test    al, al
171
        jnz     .include_loop_int
172
        jmp     .include_loop
173
.include_done:
174
; 8. Scan for 'file' dependencies.
175
; 8a. Get range for scanning.
176
; Note that fas_header.asm_size can be slighly greater than the
177
; real size, so we break from the loop when there is no space left
178
; for the entire asm_row structure, not when .asm_size is exactly reached.
179
        mov     esi, [ebx+fas_header.asm_offs]
180
        mov     edx, [ebx+fas_header.asm_size]
181
        add     edx, esi
182
        sub     edx, asm_row.sizeof
183
        push    edx
184
        jc      .file_done
185
.file_loop:
186
; 8b. Loop over assembled lines in the range.
187
        cmp     esi, [esp]
188
        ja      .file_done
5062 clevermous 189
; 8c. For every assembled line, skip "word" and one-byte tokens
190
; scanning for three predefined word tokens.
191
; Note that lines like "a:b:c:d file 'something'" are possible and valid.
2816 clevermous 192
; go to 8d for token 'file',
193
; go to 8e for token 'format',
194
; go to 8f for token 'section',
195
; continue the loop otherwise.
196
        mov     eax, [esi+ebx+asm_row.preproc_offs]
197
        add     eax, [ebx+fas_header.preproc_offs]
5062 clevermous 198
; We save/restore esi since it is used for another thing in the internal loop;
199
; we push eax, which currently contains ebx-relative pointer to
200
; preproc_line_header, to be able to access it from resolve_name.
201
        push    esi eax
202
.file_tokens_loop:
203
        mov     cl, byte [eax+ebx+preproc_line_header.contents]
204
        cmp     cl, 1Ah
205
        jnz     .file_noword_token
2816 clevermous 206
        movzx   ecx, byte [eax+ebx+preproc_line_header.contents+1]
207
        cmp     cl, 4
208
        jnz     .file_no_file
209
        cmp     dword [eax+ebx+preproc_line_header.contents+2], 'file'
210
        jnz     .file_no_file4
211
; 8d. For lines starting with 'file' token, loop over tokens and get names.
212
; Note that there can be several names in one line.
213
; Parsing of tokens is similar to step 5 with the difference that
214
; preprocessor token stops processing: 'file' directives are processed
215
; in assembler stage.
216
        lea     esi, [eax+preproc_line_header.contents+6]
217
.file_loop_int:
218
        mov     al, [esi+ebx]
219
        inc     esi
220
        test    al, al
5062 clevermous 221
        jz      .file_next
2816 clevermous 222
        cmp     al, ';'
5062 clevermous 223
        jz      .file_next
2816 clevermous 224
        cmp     al, 1Ah
225
        jz      .fileword
226
        cmp     al, '"'
227
        jnz     .file_loop_int
228
        call    resolve_name
229
        add     esi, [esi+ebx-4]
230
        jmp     .file_loop_int
231
.fileword:
232
        movzx   eax, byte [esi+ebx]
233
        lea     esi, [esi+eax+1]
234
        jmp     .file_loop_int
235
.file_no_file4:
236
        cmp     dword [eax+ebx+preproc_line_header.contents+2], 'data'
5062 clevermous 237
        jnz     .file_word_token
2816 clevermous 238
        jmp     .file_scan_from
239
.file_no_file:
240
        cmp     cl, 6
241
        jnz     .file_no_format
242
        cmp     dword [eax+ebx+preproc_line_header.contents+2], 'form'
243
        jnz     .file_no_format
244
        cmp     word [eax+ebx+preproc_line_header.contents+6], 'at'
245
        jnz     .file_no_format
246
; 8e. For lines starting with 'format' token, loop over tokens and look for stub name.
247
; Note that there can be another quoted string, namely file extension;
248
; stub name always follows the token 'on'.
249
        mov     edx, TokenOn
250
        call    scan_after_word
251
        jmp     .file_next
252
.file_no_format:
253
        cmp     cl, 7
254
        jnz     .file_no_section
255
        cmp     dword [eax+ebx+preproc_line_header.contents+2], 'sect'
256
        jnz     .file_no_section
257
        mov     edx, dword [eax+ebx+preproc_line_header.contents+6]
258
        and     edx, 0FFFFFFh
259
        cmp     edx, 'ion'
260
        jnz     .file_no_section
261
.file_scan_from:
262
        mov     edx, TokenFrom
263
        call    scan_after_word
5062 clevermous 264
        jmp     .file_next
2816 clevermous 265
.file_no_section:
5062 clevermous 266
.file_word_token:
267
        lea     eax, [eax+ecx+2]
268
        jmp     .file_tokens_loop
269
.file_noword_token:
270
        inc     eax
271
        test    cl, cl
272
        jz      .file_next
273
        cmp     cl, 3Bh
274
        jz      .file_next
275
        cmp     cl, 22h
276
        jnz     .file_tokens_loop
2816 clevermous 277
.file_next:
5062 clevermous 278
        pop     eax esi
2816 clevermous 279
        add     esi, asm_row.sizeof
280
        jmp     .file_loop
281
.file_done:
282
        pop     edx
283
; 9. Write result.
284
; 9a. Append two newlines to the end of buffer.
285
        stdcall alloc_in_buf, 2
286
        mov     word [edi+ebx], 10 * 101h
287
        inc     edi
288
        inc     edi
289
; 9b. If '-e' option was given, duplicate dependencies list as fake goals
290
; = copy all of them and append ":\n\n".
291
        cmp     [.flags], 0
292
        jz      .nodup
293
        lea     ecx, [edi+1]
294
        mov     esi, [.names]
295
        sub     ecx, esi
296
        stdcall alloc_in_buf, ecx
297
        add     esi, ebx
298
        add     edi, ebx
299
        rep     movsb
300
        mov     byte [edi-3], ':'
301
        mov     word [edi-2], 10 * 101h
302
        sub     edi, ebx
303
.nodup:
304
; 9c. Write to output file.
305
        mov     eax, [.outstart]
306
        sub     edi, eax
307
        add     eax, ebx
308
        call    write
309
; 10. Exit.
310
        xor     eax, eax
311
        call    exit
312
 
313
; Helper procedure for steps 8e and 8f of main algorithm.
314
; Looks for quoted strings after given word in edx.
315
scan_after_word:
316
        push    esi eax
317
        lea     esi, [eax+preproc_line_header.contents+2+ecx]
318
.loop:
319
        xor     ecx, ecx
320
.loop_afterword:
321
        mov     al, [esi+ebx]
322
        inc     esi
323
        test    al, al
324
        jz      .loop_done
325
        cmp     al, ';'
326
        jz      .loop_done
327
        cmp     al, 1Ah
328
        jz      .word
329
        cmp     al, '"'
330
        jnz     .loop
331
        test    ecx, ecx
332
        jz      .skip_quoted
333
        call    resolve_name
334
.loop_done:
335
        pop     eax esi
336
        ret
337
.skip_quoted:
338
        add     esi, [esi+ebx]
339
        add     esi, 4
340
        jmp     .loop
341
.word:
342
        movzx   ecx, byte [esi+ebx]
343
        lea     esi, [esi+ecx+1]
344
        cmp     cl, byte [edx]
345
        jnz     .loop
346
        push    esi edi
347
        add     esi, ebx
348
        sub     esi, ecx
349
        lea     edi, [edx+1]
350
        repz    cmpsb
351
        pop     edi esi
352
        jnz     .loop
353
        dec     ecx
354
        jmp     .loop_afterword
355
 
356
; Helper procedure for step 6 of the main procedure.
357
; Copies the ASCIIZ name from strings section to the buffer.
358
copy_asciiz_escaped:
359
        add     esi, [ebx+fas_header.strings_offs]
360
.loop:
361
        mov     al, [esi+ebx]
362
        test    al, al
363
        jz      .nothing
364
        call    copy_char_escaped
365
        jmp     .loop
366
.nothing:
367
        ret
368
 
369
; Helper procedure for step 7 of the main procedure.
370
; Copies the name with known length to the buffer.
371
copy_name_escaped:
372
        mov     ecx, [esi+ebx]
373
        add     esi, 4
374
        test    ecx, ecx
375
        jz      .nothing
376
        push    ecx
377
.loop:
378
        mov     al, [esi+ebx]
379
        call    copy_char_escaped
380
        dec     dword [esp]
381
        jnz     .loop
382
        pop     ecx
383
.nothing:
384
        ret
385
 
386
; Helper procedure for steps 7 and 8 of the main procedure.
387
; Writes separator of file names in output = " \\\n".
388
add_separator:
389
        stdcall alloc_in_buf, 3
390
        mov     word [edi+ebx], ' \'
391
        mov     byte [edi+ebx+2], 10
392
        add     edi, 3
393
        ret
394
 
395
; Helper procedure for step 7 of the main procedure.
396
; Resolves the path to 'file' dependency and copies
397
; the full name to the buffer.
398
resolve_name:
399
; FASM uses the following order to search for referenced files:
400
; * path of currently assembling file, which may be .asm or .inc
401
; * paths from %INCLUDE% for versions >= 1.70
402
; * current directory = file name is taken as is, without prepending dir name
403
; We mirror this behaviour, trying to find an existing file somewhere.
404
; There can be following reasons for the file can not be found anywhere:
405
; * it has been deleted between compilation and our actions
406
; * it didn't exist at all, compilation has failed
407
; * we are running in environment different from fasm environment.
408
; Assume that the last reason is most probable and that invalid dependency
409
; is better than absent dependency (it is easier to fix an explicit error
410
; than a silent one) and output file name without prepending dir name,
411
; as in the last case. Actually, we even don't need to test existence
412
; of the file in the current directory.
413
        add     esi, 4  ; skip string length
414
; 1. Get ebx-relative pointer to preproc_line_header, see the comment in start.7d
415
        mov     eax, [esp+4]
416
; 2. Get the path to currently processing file.
417
        push    esi
418
.getpath:
419
        test    byte [eax+ebx+preproc_line_header.line_number+3], 80h
420
        jz      @f
421
        mov     eax, [eax+ebx+preproc_line_header.line_offset]
422
        add     eax, [ebx+fas_header.preproc_offs]
423
        jmp     .getpath
424
@@:
425
        mov     edx, [eax+ebx+preproc_line_header.source_name]
426
        test    edx, edx
427
        jz      .frommain
428
        add     edx, [ebx+fas_header.preproc_offs]
429
        jmp     @f
430
.frommain:
431
        mov     edx, [ebx+fas_header.input]
432
        add     edx, [ebx+fas_header.strings_offs]
433
@@:
434
; 3. Check that it is not a duplicate of the previous dependency.
435
; 3a. Compare preprocessor units.
436
        cmp     edx, [start.prevfilefrom]
437
        jnz     .nodup
438
; 3b. Compare string lengths.
439
        mov     eax, [start.prevfile]
440
        mov     ecx, [eax+ebx-4]
441
        cmp     ecx, [esi+ebx-4]
442
        jnz     .nodup
443
; 3c. Compare string contents.
444
        push    esi edi
445
        lea     edi, [eax+ebx]
446
        add     esi, ebx
447
        rep     cmpsb
448
        pop     edi esi
449
        jnz     .nodup
450
; 3d. It is duplicate, just return.
451
        pop     esi
452
        ret
453
.nodup:
454
; 3e. It is not duplicate. Output separator.
455
        mov     [start.prevfilefrom], edx
456
        mov     [start.prevfile], esi
457
        call    add_separator
458
; 4. Cut the last component of the path found in step 2.
459
        mov     ecx, edx
460
        mov     esi, edx
461
.scanpath:
462
        mov     al, [edx+ebx]
463
        test    al, al
464
        jz      .scandone
465
        cmp     al, '/'
466
        jz      .slash
467
        cmp     al, '\'
468
        jnz     .scannext
469
.slash:
470
        lea     ecx, [edx+1]
471
.scannext:
472
        inc     edx
473
        jmp     .scanpath
474
.scandone:
475
        sub     ecx, esi
476
; 5. Try path found in step 4. If found, go to step 8.
477
        mov     [start.testname], edi
478
        stdcall copy_string, esi, ecx
479
        pop     esi
480
        call    expand_environment
481
        call    test_file_exists
482
        test    eax, eax
483
        jns     .found
484
        call    revert_testname
485
; 6. Try each of include paths. For every path, if file is found, go to step 8.
486
; Otherwise, continue loop over include path.
487
; Skip this step before 1.70.
488
        cmp     [ebx+fas_header.major], 1
489
        ja      .checkenv
490
        jb      .nocheckenv
491
        cmp     [ebx+fas_header.minor], 70
492
        jb      .nocheckenv
493
.checkenv:
494
        mov     ecx, [start.include]
495
.includeloop_ext:
496
        mov     eax, ecx
497
.includeloop_int:
498
        cmp     byte [eax+ebx], 0
499
        jz      @f
500
        cmp     byte [eax+ebx], ';'
501
        jz      @f
502
        inc     eax
503
        jmp     .includeloop_int
504
@@:
505
        push    eax
506
        sub     eax, ecx
507
        jz      @f
508
        stdcall copy_string, ecx, eax
509
        cmp     byte [edi+ebx-1], '/'
510
        jz      .hasslash
511
@@:
512
        stdcall alloc_in_buf, 1
513
        mov     byte [edi+ebx], '/'
514
        inc     edi
515
.hasslash:
516
        call    expand_environment
517
        call    test_file_exists
518
        pop     ecx
519
        test    eax, eax
520
        jns     .found
521
        call    revert_testname
522
        cmp     byte [ecx+ebx], 0
523
        jz      .notfound
524
        inc     ecx
525
        cmp     byte [ecx+ebx], 0
526
        jnz     .includeloop_ext
527
.nocheckenv:
528
.notfound:
529
; 7. File not found neither near the current preprocessor unit nor in %INCLUDE%.
530
; Assume that it is in the current directory.
531
        call    expand_environment
532
.found:
533
; 8. Currently we have file name from [start.testname] to edi;
534
; it is zero-terminated and not space-escaped. Fix both issues.
535
        dec     edi
536
        inc     [start.free]
537
        push    esi
538
        mov     edx, [start.testname]
539
.escapeloop:
540
        cmp     edx, edi
541
        jae     .escapedone
542
        cmp     byte [edx+ebx], ' '
543
        jnz     .noescape
544
        stdcall alloc_in_buf, 1
545
        mov     ecx, edi
546
        sub     ecx, edx
547
        push    edi
548
        add     edi, ebx
549
        lea     esi, [edi-1]
550
        std
551
        rep     movsb
552
        cld
553
        pop     edi
554
        inc     edi
555
        mov     byte [edx+ebx], '\'
556
        inc     edx
557
.noescape:
558
        inc     edx
559
        jmp     .escapeloop
560
.escapedone:
561
        pop     esi
562
        ret
563
 
564
; Helper procedure for resolve_name.
565
; Allocates space in the buffer and appends the given string to the buffer.
566
copy_string:
567
        mov     eax, [esp+8]
568
        test    eax, eax
569
        jz      .nothing
570
        stdcall alloc_in_buf, eax
571
        mov     ecx, [esp+4]
572
.copy:
573
        mov     al, [ecx+ebx]
574
        inc     ecx
575
        cmp     al, '\'
576
        jnz     @f
577
        mov     al, '/'
578
@@:
579
        mov     [edi+ebx], al
580
        inc     edi
581
        dec     dword [esp+8]
582
        jnz     .copy
583
.nothing:
584
        ret     8
585
 
586
; Helper procedure for resolve_name. Undoes appending of last file name.
587
revert_testname:
588
        add     [start.free], edi
589
        mov     edi, [start.testname]
590
        sub     [start.free], edi
591
        ret
592
 
593
; Helper procedure for resolve_name. Copies string from esi to edi,
594
; expanding environment variables.
595
expand_environment:
596
; 1. Save esi to restore it in the end of function.
597
        push    esi
598
; 2. Push string length to the stack to be used as a variable.
599
        pushd   [esi+ebx-4]
600
; 3. Scan loop.
601
.scan:
602
; 3a. Scan for '%' sign.
603
        call    find_percent
604
.justcopy:
605
; 3b. Copy the part from the beginning of current portion to '%' sign,
606
; advance pointer to '%' sign, or end-of-string if no '%' found.
607
        push    eax
608
        sub     eax, esi
609
        stdcall copy_string, esi, eax
610
        pop     esi
611
; 3c. If string has ended, break from the loop.
612
        cmp     dword [esp], 0
613
        jz      .scandone
614
; 3d. Advance over '%' sign.
615
        inc     esi
616
        dec     dword [esp]
617
; 3e. Find paired '%'.
618
        call    find_percent
619
; 3f. If there is no paired '%', just return to 3b and copy remaining data,
620
; including skipped '%'; after that, 3c would break from the loop.
621
        dec     esi
622
        cmp     dword [esp], 0
623
        jz      .justcopy
624
; 3g. Otherwise, get the value of environment variable.
625
; Since get_environment_variable requires zero-terminated string
626
; and returns zero-terminated string, temporarily overwrite trailing '%'
627
; and ignore last byte in returned string.
628
; Also convert any backslashes to forward slashes.
629
        inc     esi
630
        mov     byte [eax+ebx], 0
631
        push    eax
632
        push    edi
633
        call    get_environment_variable
634
        dec     edi
635
        inc     [start.free]
636
        pop     eax
637
.replaceslash:
638
        cmp     eax, edi
639
        jz      .replaceslash_done
640
        cmp     byte [eax+ebx], '\'
641
        jnz     @f
642
        mov     byte [eax+ebx], '/'
643
@@:
644
        inc     eax
645
        jmp     .replaceslash
646
.replaceslash_done:
647
        pop     esi
648
        mov     byte [esi+ebx], '%'
649
; 3h. Advance over trailing '%'.
650
        inc     esi
651
        dec     dword [esp]
652
; 3i. Continue the loop.
653
        jmp     .scan
654
.scandone:
655
; 4. Zero-terminate resulting string.
656
        stdcall alloc_in_buf, 1
657
        mov     byte [edi+ebx], 0
658
        inc     edi
659
; 5. Pop stack variable initialized in step 2.
660
        pop     eax
661
; 6. Restore esi saved in step 1 and return.
662
        pop     esi
663
        ret
664
 
665
; Helper procedure for expand_environment.
666
; Scans the string in esi with length [esp+4]
667
; until '%' is found or line ended.
668
find_percent:
669
        mov     eax, esi
670
        cmp     dword [esp+4], 0
671
        jz      .nothing
672
.scan:
673
        cmp     byte [eax+ebx], '%'
674
        jz      .nothing
675
        inc     eax
676
        dec     dword [esp+4]
677
        jnz     .scan
678
.nothing:
679
        ret
680
 
681
; Helper procedure for copy_{name,asciiz}_escaped.
682
; Allocates space and writes one character, possibly escaped.
683
copy_char_escaped:
684
        cmp     al, ' '
685
        jnz     .noescape
686
        stdcall alloc_in_buf, 1
687
        mov     byte [edi+ebx], '\'
688
        inc     edi
689
.noescape:
690
        stdcall alloc_in_buf, 1
691
        mov     al, [esi+ebx]
692
        inc     esi
693
        cmp     al, '\'
694
        jnz     @f
695
        mov     al, '/'
696
@@:
697
        mov     [edi+ebx], al
698
        inc     edi
699
        ret
700
 
701
; Helper procedure for ensuring that there is at least [esp+4]
702
; free bytes in the buffer.
703
alloc_in_buf:
704
        mov     eax, [esp+4]
705
        sub     [start.free], eax
706
        jb      .need_realloc
707
        ret     4
708
.need_realloc:
709
        mov     eax, [start.allocated]
710
        add     eax, eax
711
        push    ecx edx
712
        call    realloc
713
        pop     edx ecx
714
        cmp     [start.free], 0
715
        jl      .need_realloc
716
        mov     ebx, [start.buf]
717
        ret     4
718
 
719
badfile:
720
        mov     esi, badfile_string
721
        call    sayerr
722
        mov     al, 1
723
        call    exit
724
 
725
information:
726
        mov     esi, information_string
727
        call    sayerr
728
        mov     al, 2
729
        call    exit
730
 
731
nomemory:
732
        mov     esi, nomemory_string
733
        call    sayerr
734
        mov     al, 3
735
        call    exit
736
 
737
in_openerr:
738
        mov     esi, in_openerr_string
739
        jmp     in_err
740
readerr:
741
        mov     esi, readerr_string
742
in_err:
743
        call    sayerr
744
        mov     al, 4
745
        call    exit
746
 
747
out_openerr:
748
        mov     esi, out_openerr_string
749
        jmp     out_err
750
writeerr:
751
        mov     esi, writeerr_string
752
out_err:
753
        call    sayerr
754
        mov     al, 5
755
        call    exit
756
 
757
; Platform-specific procedures.
758
match =WINDOWS,OS { include 'windows_sys.inc' }
759
match =LINUX,OS { include 'linux_sys.inc' }
760
 
761
; Data
762
macro string a, [b] {
763
common
764
        db      a ## _end - a
765
a       db      b
766
a ## _end:
767
}
768
 
769
string information_string, 'Usage: fasmdep [-e] [ []]',10
770
string badfile_string, 'Not .fas file',10
771
string nomemory_string, 'No memory',10
772
string in_openerr_string, 'Cannot open input file',10
773
string readerr_string, 'Read error',10
774
string out_openerr_string, 'Cannot create output file',10
775
string writeerr_string, 'Write error',10
776
 
777
include_variable        db      'INCLUDE',0
778
TokenOn         db      2,'on'
779
TokenFrom       db      4,'from'