This source file includes following definitions.
- skip_space
- skip_token
- skip_string
- next_token
- parse_line
- next_line
- print_args
- set_op_name
- set_op_comment
- new_op
- chk_args
- parse_FILE
- parse_ENDFILE
- parse_COPY
- parse_FUNC
- parse_ENDFUNC
- parse_ASM
- parse_ENDASM
- parse_FW
- parse_PATCHSUB
- parse_PATCHVAL
- parse_REM
- parse_SKIP
- parse_CONTFW
- parse_LI
- op_prelabel
- op_COPY
- op_FUNC
- op_FW
- op_PATCHSUB
- op_PATCHVAL
- op_REM
- do_ops
- run_op
- parse_ops
- usage
- error
- main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 #include <stdio.h>
304 #include <stdlib.h>
305 #include <stdint.h>
306 #include <string.h>
307 #include <ctype.h>
308 #include <sys/stat.h>
309
310 #include "stubs_load.h"
311 #include "firmware_load.h"
312 #include "chdk_dasm.h"
313
314
315
316 firmware *fw;
317
318
319
320 int lineno;
321 int largc;
322 char largs[20][256];
323 char *last_line;
324 char token[1024];
325
326 char* skip_space(char* c)
327 {
328 while (*c && ((*c == ' ') || (*c == '\t') || (*c == '='))) c++;
329 return c;
330 }
331
332 char* skip_token(char* c)
333 {
334 while (*c && (*c != ' ') && (*c != '\t') && (*c != '=')) c++;
335 return c;
336 }
337
338 char* skip_string(char* c)
339 {
340 while (*c && (*c != '"')) c++;
341 return c;
342 }
343
344 char* next_token(char *line)
345 {
346 token[0] = 0;
347 char *c = skip_space(line);
348 char *n;
349 if (*c)
350 {
351 if (*c == '"')
352 {
353 c++;
354 n = skip_string(c);
355 strncpy(token, c, n-c);
356 token[n-c] = 0;
357 return skip_space((*n)?n+1:n);
358 }
359 else
360 {
361 n = skip_token(c);
362 strncpy(token, c, n-c);
363 token[n-c] = 0;
364 return skip_space(n);
365 }
366 }
367 return c;
368 }
369
370 void parse_line(char *line)
371 {
372 last_line = line;
373 largc = 0;
374 largs[largc][0] = 0;
375 while (line && *line)
376 {
377 line = next_token(line);
378 strcpy(largs[largc++], token);
379 }
380 }
381
382 char* next_line(char *line)
383 {
384 char *nxt = strchr(line,'\r');
385 if (nxt != 0)
386 {
387 *nxt++ = 0;
388 if (*nxt == '\n')
389 nxt++;
390 }
391 else
392 {
393 nxt = strchr(line,'\n');
394 if (nxt != 0)
395 *nxt++ = 0;
396 }
397 lineno++;
398 return nxt;
399 }
400
401 void print_args()
402 {
403 int n;
404 for (n=0; n<largc; n++) fprintf(stderr,"\t%d %s\n",n,largs[n]);
405 }
406
407
408
409
410 FILE *outfile;
411 int direct_copy;
412 int in_func;
413
414
415
416 #define NULL_OP 0
417 #define FILE_OP 1
418 #define ENDFILE_OP 2
419 #define COPY_OP 3
420 #define COPY_LINE 4
421 #define FUNC_OP 5
422 #define ENDFUNC_OP 6
423 #define ASM_OP 7
424 #define ENDASM_OP 8
425 #define FW_OP 9
426 #define PATCHSUB_OP 10
427 #define PATCHVAL_OP 11
428 #define REM_OP 12
429 #define SKIP_OP 13
430 #define CONTFW_OP 14
431 #define LI_OP 15
432
433 typedef struct _op
434 {
435 struct _op *next;
436
437 int operation;
438 char *source;
439 int lineno;
440
441 char *name;
442 char *comment;
443 int prelabel;
444 t_address func_start;
445 t_address func_end;
446 int func_len;
447 int patch_ref;
448 t_address fw_start;
449 t_address fw_end;
450 int fw_func_end_offset;
451 int fw_is_func_end_offset;
452 int fw_func_start_offset;
453 int fw_is_func_start_offset;
454 int fw_len;
455 t_address patch_new_val;
456 int skip_len;
457 int li_state;
458 } op;
459
460 op *op_head, *op_tail, *cur_func;
461
462 void set_op_name(op *p, char *nm)
463 {
464 if (nm)
465 {
466 p->name = malloc(strlen(nm)+1);
467 strcpy(p->name,nm);
468 }
469 else
470 {
471 p->name = 0;
472 }
473 }
474
475 void set_op_comment(op *p, char *s)
476 {
477 if (s)
478 {
479 p->comment = malloc(strlen(s)+1);
480 strcpy(p->comment,s);
481 }
482 else
483 {
484 p->comment = 0;
485 }
486 }
487
488 op *new_op(int type)
489 {
490 op *p = malloc(sizeof(op));
491
492 p->operation = type;
493
494 p->source = malloc(strlen(last_line)+1);
495 strcpy(p->source,last_line);
496 p->lineno = lineno;
497
498 p->name = 0;
499 p->comment = 0;
500 p->prelabel = 0;
501 p->func_start = 0;
502 p->func_end = 0;
503 p->func_len = 0;
504 p->patch_ref = -1;
505 p->fw_start = 0;
506 p->fw_end = 0;
507 p->fw_func_end_offset = 0;
508 p->fw_is_func_end_offset = 0;
509 p->fw_func_start_offset = 0;
510 p->fw_is_func_start_offset = 0;
511 p->fw_len = -1;
512 p->patch_new_val = 0;
513 p->skip_len = 0;
514 p->li_state = 0;
515
516 if (op_tail)
517 {
518 op_tail->next = p;
519 }
520 else
521 {
522 op_head = p;
523 }
524
525 op_tail = p;
526 p->next = 0;
527
528 return p;
529 }
530
531 void chk_args(int count, char *msg, op *p)
532 {
533 if (largc != count+1)
534 {
535 fprintf(stderr,"ERROR - %s\n",msg);
536 fprintf(stderr,"LINE - %d, SOURCE - %s\n",p->lineno,p->source);
537 exit(1);
538 }
539 }
540
541
542
543
544 void parse_FILE()
545 {
546 op *p = new_op(FILE_OP);
547 chk_args(1,"Missing FILE name",p);
548 set_op_name(p,largs[1]);
549 }
550
551
552 void parse_ENDFILE()
553 {
554 new_op(ENDFILE_OP);
555 }
556
557
558 void parse_COPY()
559 {
560 op *p = new_op(COPY_OP);
561 int n;
562
563
564 for (n=1; n<largc; n++)
565 {
566 if (strcmp(largs[n],"prelabel") == 0)
567 p->prelabel = 1;
568 }
569
570 direct_copy = 1;
571
572
573 for (n=1; n<largc; n++)
574 {
575 if (strcmp(largs[n],"file") == 0)
576 {
577 set_op_name(p,largs[++n]);
578 direct_copy = 0;
579 }
580 }
581 }
582
583
584
585
586
587 void parse_FUNC()
588 {
589 op *p = new_op(FUNC_OP);
590 int n;
591
592 for (n=1; n<largc; n++)
593 {
594 if (strcmp(largs[n], "name") == 0)
595 {
596 set_op_name(p,largs[++n]);
597 }
598 else if (strcmp(largs[n], "start") == 0)
599 {
600 p->func_start = strtoul(largs[++n],0,0);
601 }
602 else if (strcmp(largs[n], "end") == 0)
603 {
604 p->func_end = strtoul(largs[++n],0,0);
605 }
606 else if (strcmp(largs[n], "length") == 0)
607 {
608 p->func_len = strtoul(largs[++n],0,0);
609 }
610 else if (strcmp(largs[n], "ref") == 0)
611 {
612 p->patch_ref = strtol(largs[++n],0,0);
613 }
614 else if (strcmp(largs[n], "sig") == 0)
615 {
616 osig *sig = find_sig(fw->sv->stubs, largs[++n]);
617 if (sig == 0) chk_args(-1,"Can't find firmware function for 'sig='",p);
618 p->func_start = sig->val;
619 p->name = sig->nm;
620 }
621 else
622 {
623 chk_args(-1,"Invalid FUNC argument",p);
624 }
625 }
626 }
627
628
629 void parse_ENDFUNC()
630 {
631 new_op(ENDFUNC_OP);
632 }
633
634
635 void parse_ASM()
636 {
637 new_op(ASM_OP);
638 }
639
640
641 void parse_ENDASM()
642 {
643 op *p = new_op(ENDASM_OP);
644 int n;
645
646
647 for (n=1; n<largc; n++)
648 {
649 if (strcmp(largs[n],"prelabel") == 0)
650 p->prelabel = 1;
651 }
652 }
653
654
655 void parse_FW()
656 {
657 op *p = new_op(FW_OP);
658 int n;
659
660 for (n=1; n<largc; n++)
661 {
662 switch (largs[n][0])
663 {
664 case '^':
665 p->fw_start = strtoul(largs[n]+1,0,0);
666 break;
667 case '-':
668 p->fw_end = strtoul(largs[n]+1,0,0);
669 break;
670 case '$':
671 p->fw_func_end_offset = strtol(largs[n]+1,0,0) * 4;
672 p->fw_is_func_end_offset = 1;
673 break;
674 case 'L':
675 p->fw_func_start_offset = strtol(largs[n]+1,0,0) * 4 - 4;
676 p->fw_is_func_start_offset = 1;
677 break;
678 default:
679 if (strcmp(largs[n], "comment") == 0)
680 {
681 set_op_comment(p,largs[++n]);
682 }
683 else
684 {
685 p->fw_len = strtoul(largs[n],0,0);
686 if (p->fw_len == 0) p->fw_len++;
687 }
688 break;
689 }
690 }
691
692 if ((p->fw_start != 0) && (p->fw_end != 0))
693 p->fw_len = (p->fw_end - p->fw_start) / 4 + 1;
694 }
695
696
697
698 void parse_PATCHSUB()
699 {
700 op *p = new_op(PATCHSUB_OP);
701 int n;
702
703 for (n=1; n<largc; n++)
704 {
705 if (strcmp(largs[n], "name") == 0)
706 {
707 set_op_name(p,largs[++n]);
708 }
709 else if (strcmp(largs[n], "ref") == 0)
710 {
711 p->patch_ref = strtoul(largs[++n],0,0);
712 }
713 else if (strcmp(largs[n], "comment") == 0)
714 {
715 set_op_comment(p,largs[++n]);
716 }
717 else
718 {
719 chk_args(-1,"Invalid PATCHSUB argument",p);
720 }
721 }
722 }
723
724
725 void parse_PATCHVAL()
726 {
727 op *p = new_op(PATCHVAL_OP);
728 int n;
729
730 for (n=1; n<largc; n++)
731 {
732 if (strcmp(largs[n], "value") == 0)
733 {
734 p->patch_new_val = strtoul(largs[++n],0,0);
735 }
736 else if (strcmp(largs[n], "comment") == 0)
737 {
738 set_op_comment(p,largs[++n]);
739 }
740 else
741 {
742 chk_args(-1,"Invalid PATCHVAL argument",p);
743 }
744 }
745 }
746
747
748
749 void parse_REM()
750 {
751 op *p = new_op(REM_OP);
752
753 if (largc > 1)
754 {
755 set_op_comment(p,largs[1]);
756 }
757 }
758
759
760 void parse_SKIP()
761 {
762 op *p = new_op(SKIP_OP);
763
764 if (largc > 1)
765 p->skip_len = strtol(largs[1],0,0);
766 else
767 p->skip_len = 1;
768 }
769
770
771 void parse_CONTFW()
772 {
773 new_op(CONTFW_OP);
774 }
775
776
777 void parse_LI()
778 {
779 op *p = new_op(LI_OP);
780 if (largc > 1)
781 p->li_state = strtol(largs[1],0,0);
782 }
783
784
785
786 void op_prelabel(op *p)
787 {
788
789 if (in_func && (p->prelabel == 0) && (addr <= options.end_address))
790 {
791 if (l_search(branch_list, addr))
792 {
793 fprintf(outfile,"\n\"loc_%08X:\\n\"\n", addr) ;
794 l_remove(branch_list, addr);
795 }
796 }
797 }
798
799
800 void op_COPY(op *p)
801 {
802 int x;
803
804 op_prelabel(p);
805
806
807 if (p->name)
808 {
809 FILE *fp = fopen(p->name,"r");
810 if (!fp)
811 {
812 fprintf(stderr,"Can't open file '%s'\n",p->name);
813 exit(1);
814 }
815
816 char buf[1024];
817 do
818 {
819 x = fread(buf,1,1024,fp);
820 if (x > 0) fwrite(buf,1,x,outfile);
821 } while (x > 0);
822
823 fclose(fp);
824 direct_copy = 0;
825 }
826 }
827
828
829
830
831
832 void op_FUNC(op *p)
833 {
834 char func_name[256];
835
836 *func_name = 0;
837
838 cur_func = p;
839
840 if (p->name)
841 strcpy(func_name, p->name);
842
843 if (p->patch_ref >= 0)
844 {
845 p->func_start = patch_ref_address[p->patch_ref];
846 strcpy(func_name, patch_ref_name[p->patch_ref]);
847 }
848
849 in_func = 1;
850
851 if (p->func_start == 0) chk_args(-1,"Missing FUNC start address",p);
852
853 if ((p->func_end == 0) && (p->func_len == 0))
854 {
855 op *n = p->next;
856 int cont = 1;
857 while (n && cont)
858 {
859 switch (n->operation)
860 {
861 case FW_OP:
862 if (n->fw_is_func_end_offset)
863 {
864 cont = 0;
865 p->func_len = 0;
866 }
867 else
868 {
869 p->func_len += n->fw_len;
870 }
871 break;
872 case PATCHSUB_OP:
873 case PATCHVAL_OP:
874 case REM_OP:
875 p->func_len++;
876 break;
877 case SKIP_OP:
878 p->func_len += n->skip_len;
879 break;
880 case ENDFUNC_OP:
881 cont = 0;
882 break;
883 }
884 n = n->next;
885 }
886
887 if (p->func_len == 0)
888 {
889 p->func_end = find_end(fw, p->func_start);
890 if (p->func_end == 0)
891 chk_args(-1,"Missing FUNC end address or length",p);
892 }
893 }
894
895 if ((p->func_end == 0) && (p->func_len > 0))
896 p->func_end = p->func_start + p->func_len * 4 - 4;
897 if ((p->func_len == 0) && (p->func_end > 0))
898 p->func_len = (p->func_end - p->func_start) / 4 + 1;
899 if (p->func_end < p->func_start) chk_args(-1,"FUNC start > end",p);
900 if (p->func_len != (int)((p->func_end - p->func_start) / 4 + 1)) chk_args(-1,"FUNC start/end/length mismatch",p);
901
902 if (*func_name == 0)
903 sprintf(func_name, "sub_%08X_my", p->func_start);
904
905 options.start_address = p->func_start;
906 options.end_address = p->func_end;
907
908 set_ignore_errors(1);
909 options.flags |= disopt_remember_branches;
910 disassemble1(fw, p->func_start, p->func_len);
911 options.flags &= ~disopt_remember_branches;
912 set_ignore_errors(0);
913
914 fprintf(outfile,"\n/*************************************************************/");
915 fprintf(outfile,"\n//** %s @ 0x%08X - 0x%08X, length=%d\n", func_name, p->func_start, last_used_addr, (last_used_addr - p->func_start) / 4 + 1 ) ;
916 fprintf(outfile,"void __attribute__((naked,noinline)) %s() {\n", func_name);
917
918 addr = p->func_start;
919 }
920
921
922 void op_FW(op *p)
923 {
924 t_address start_address = addr;
925 t_address end_address = addr;
926
927 patch_comment = p->comment;
928 if (patch_comment)
929 {
930 options.flags |= disopt_patch_comment;
931 }
932
933 if (p->fw_start > 0)
934 start_address = p->fw_start;
935 if (p->fw_end > 0)
936 end_address = p->fw_end;
937 if (p->fw_is_func_end_offset)
938 end_address = cur_func->func_end + p->fw_func_end_offset;
939 if (p->fw_is_func_start_offset)
940 end_address = cur_func->func_start + p->fw_func_start_offset;
941 if (p->fw_len > 0)
942 end_address = start_address + p->fw_len * 4 - 4;
943
944 disassemble(fw, outfile, start_address, (end_address + 4 - start_address) / 4);
945 }
946
947
948
949 void op_PATCHSUB(op *p)
950 {
951 patch_func_name = p->name;
952 save_patch_ref = p->patch_ref;
953 patch_comment = p->comment;
954 if (patch_comment)
955 {
956 options.flags |= disopt_patch_comment;
957 }
958
959 options.flags |= disopt_patch_branch;
960 disassemble(fw, outfile, addr, 1);
961 options.flags &= ~(disopt_patch_branch|disopt_patch_comment);
962 }
963
964
965 void op_PATCHVAL(op *p)
966 {
967 patch_new_val = p->patch_new_val;
968 patch_comment = p->comment;
969 if (patch_comment)
970 {
971 options.flags |= disopt_patch_comment;
972 }
973
974 options.flags |= disopt_patch_value;
975 disassemble(fw, outfile, addr, 1);
976 options.flags &= ~(disopt_patch_value|disopt_patch_comment);
977 }
978
979
980
981 void op_REM(op *p)
982 {
983 patch_comment = p->comment;
984 if (patch_comment)
985 {
986 options.flags |= disopt_patch_comment;
987 }
988
989 options.flags |= disopt_comment_lines;
990 disassemble(fw, outfile, addr, 1);
991 options.flags &= ~(disopt_comment_lines|disopt_patch_comment);
992 }
993
994
995
996
997 void do_ops(op *p)
998 {
999 switch (p->operation)
1000 {
1001 case FILE_OP:
1002
1003 outfile = fopen(p->name, "w");
1004 fprintf(outfile, "/*\n * %s - auto-generated by CHDK code_gen.\n */\n", p->name);
1005 break;
1006 case ENDFILE_OP:
1007
1008 if (outfile != stdout)
1009 {
1010 fclose(outfile);
1011 outfile = stdout;
1012 }
1013 break;
1014 case COPY_OP:
1015 op_COPY(p);
1016 break;
1017 case COPY_LINE:
1018 fprintf(outfile, "%s\n", p->comment);
1019 break;
1020 case FUNC_OP:
1021 op_FUNC(p);
1022 break;
1023 case ENDFUNC_OP:
1024
1025 fprintf(outfile,"}\n");
1026 in_func = 0;
1027 break;
1028 case ASM_OP:
1029
1030 fprintf(outfile,"asm volatile (\n");
1031 break;
1032 case ENDASM_OP:
1033
1034 op_prelabel(p);
1035 fprintf(outfile,");\n");
1036 break;
1037 case FW_OP:
1038 op_FW(p);
1039 break;
1040 case PATCHSUB_OP:
1041 op_PATCHSUB(p);
1042 break;
1043 case PATCHVAL_OP:
1044 op_PATCHVAL(p);
1045 break;
1046 case REM_OP:
1047 op_REM(p);
1048 break;
1049 case SKIP_OP:
1050
1051 addr += (p->skip_len * 4);
1052 break;
1053 case CONTFW_OP:
1054
1055 if (options.flags & disopt_line_numbers) fprintf(outfile," ");
1056 fprintf(outfile,"\" LDR PC, =0x%08X \\n\" // Continue in firmware\n",addr);
1057 break;
1058 case LI_OP:
1059
1060 if (p->li_state != 0)
1061 options.flags |= disopt_line_numbers;
1062 else
1063 options.flags &= ~disopt_line_numbers;
1064 break;
1065 }
1066 }
1067
1068
1069 int run_op(char *name, void (*func)())
1070 {
1071 if (strcmp(largs[0], name) == 0)
1072 {
1073 func();
1074 return 1;
1075 }
1076 return 0;
1077 }
1078
1079
1080 void parse_ops()
1081 {
1082 if (largc > 0)
1083 {
1084 if (run_op("FILE", parse_FILE)) return;
1085 if (run_op("ENDFILE", parse_ENDFILE)) return;
1086 if (run_op(">>>", parse_COPY)) return;
1087 if (run_op("FUNC", parse_FUNC)) return;
1088 if (run_op("ENDFUNC", parse_ENDFUNC)) return;
1089 if (run_op("ASM", parse_ASM)) return;
1090 if (run_op("ENDASM", parse_ENDASM)) return;
1091 if (run_op("FW", parse_FW)) return;
1092 if (run_op("PATCHSUB", parse_PATCHSUB)) return;
1093 if (run_op("PATCHVAL", parse_PATCHVAL)) return;
1094 if (run_op("REM", parse_REM)) return;
1095 if (run_op("SKIP", parse_SKIP)) return;
1096 if (run_op("->FW", parse_CONTFW)) return;
1097 if (run_op("LI", parse_LI)) return;
1098 }
1099 }
1100
1101
1102
1103 void usage(char *err)
1104 {
1105 fprintf(stderr,"code_gen <base> <primary> [alt base] - Error = %s\n",err);
1106 exit(1);
1107 }
1108
1109 op* last_op = 0;
1110
1111 void error(__attribute__ ((unused))char *fmt, __attribute__ ((unused))int n)
1112 {
1113 if (last_op)
1114 fprintf(stderr,"Line - %d, Source --> %s\n",last_op->lineno,last_op->source);
1115 exit(1);
1116 }
1117
1118
1119
1120 int main(int ac, const char * av[])
1121 {
1122
1123 if (ac < 4)
1124 {
1125 fprintf(stderr,"Usage: code_gen ROMBASE code_gen.txt PRIMARY.BIN [ALT_ROMBASE]\n");
1126 exit(1);
1127 }
1128
1129
1130 options.ROM_start = strtoul(av[1],0,0);
1131 if (options.ROM_start == 0)
1132 {
1133 fprintf(stderr,"Invalid ROMBASE.\n");
1134 exit(1);
1135 }
1136
1137
1138 struct stat st;
1139 if (stat(av[2],&st) != 0)
1140 {
1141 fprintf(stderr, "Failed to stat \"%s\".\n", av[2]);
1142 exit(1);
1143 }
1144 size_t len = st.st_size;
1145
1146
1147 FILE *fp = fopen(av[2], "rb");
1148 if (!fp)
1149 {
1150 fprintf(stderr, "Failed to open \"%s\".\n", av[2]);
1151 exit(1);
1152 }
1153
1154
1155 char *def = malloc(len+1);
1156 if (def == 0)
1157 {
1158 fprintf(stderr, "Not enough memory.\n");
1159 exit(1);
1160 }
1161
1162
1163 size_t n = fread(def, 1, len, fp);
1164 def[n] = 0;
1165 fclose(fp);
1166
1167
1168 fw = malloc(sizeof(firmware));
1169 load_firmware(fw, av[3], av[1], (ac==5)?av[4]:0, OS_DRYOS);
1170
1171
1172 fw->sv = new_stub_values();
1173 load_funcs(fw->sv, "funcs_by_name.csv");
1174 load_stubs(fw->sv, "stubs_entry.S", 0);
1175 load_stubs(fw->sv, "stubs_entry_2.S", 0);
1176
1177 outfile = stdout;
1178 direct_copy = 0;
1179 in_func = 0;
1180
1181 op_head = op_tail = 0;
1182
1183
1184 lineno = 0;
1185 char *line = def;
1186 while ((line != 0) && (*line != 0))
1187 {
1188 char *nxt = next_line(line);
1189
1190
1191 if (direct_copy)
1192 {
1193
1194 if (strncmp(line,"<<<",3) == 0)
1195 {
1196 direct_copy = 0;
1197 }
1198 else
1199 {
1200 op *p = new_op(COPY_LINE);
1201 set_op_comment(p,line);
1202 }
1203 }
1204 else
1205 {
1206 parse_line(line);
1207 parse_ops();
1208 }
1209
1210 line = nxt;
1211 }
1212
1213
1214 op *p = op_head;
1215 while (p)
1216 {
1217 last_op = p;
1218 do_ops(p);
1219 p = p->next;
1220 }
1221
1222 if (outfile != stdout)
1223 fclose(outfile);
1224
1225 return 0;
1226 }