Fossil SCM

Fix a bug in error recovery logic in the 3-way merge. Added new comments to the 3-way merge code to hopefully make it easier to understand.

drh 2009-03-22 12:25 trunk
Commit 3e89b0c52648512790ae4c02101e2a25c520a502
1 file changed +51 -9
+51 -9
--- src/merge3.c
+++ src/merge3.c
@@ -37,13 +37,14 @@
3737
/* The minimum of two integers */
3838
#define min(A,B) (A<B?A:B)
3939
4040
/*
4141
** Compare N lines of text from pV1 and pV2. If the lines
42
-** are the same, return true. Return false if they are different.
42
+** are the same, return true. Return false if one or more of the N
43
+** lines are different.
4344
**
44
-** The cursor on both pV1 and pV2 is unchanged.
45
+** The cursors on both pV1 and pV2 is unchanged by this comparison.
4546
*/
4647
static int sameLines(Blob *pV1, Blob *pV2, int N){
4748
char *z1, *z2;
4849
int i;
4950
@@ -58,15 +59,22 @@
5859
}
5960
return 0;
6061
}
6162
6263
/*
63
-** Look at the next edit triple in both aC1 and aC2. If they describe
64
-** an identical edit, then return 1. If the edits are different,
65
-** return 0.
64
+** Look at the next edit triple in both aC1 and aC2. (An "edit triple" is
65
+** three integers describing the number of copies, deletes, and inserts in
66
+** moving from the original to the edited copy of the file.) If the three
67
+** integers of the edit triples describe an identical edit, then return 1.
68
+** If the edits are different, return 0.
6669
*/
67
-static int sameEdit(int *aC1, int *aC2, Blob *pV1, Blob *pV2){
70
+static int sameEdit(
71
+ int *aC1, /* Array of edit integers for file 1 */
72
+ int *aC2, /* Array of edit integers for file 2 */
73
+ Blob *pV1, /* Text of file 1 */
74
+ Blob *pV2 /* Text of file 2 */
75
+){
6876
if( aC1[0]!=aC2[0] ) return 0;
6977
if( aC1[1]!=aC2[1] ) return 0;
7078
if( aC1[2]!=aC2[2] ) return 0;
7179
if( sameLines(pV1, pV2, aC1[2]) ) return 1;
7280
return 0;
@@ -78,11 +86,11 @@
7886
**
7987
** (0) The number of lines to copy
8088
** (1) The number of lines to delete
8189
** (2) The number of liens to insert
8290
**
83
-** Suppose we want to advance over sz lines of the pivot. This routine
91
+** Suppose we want to advance over sz lines of the originl file. This routine
8492
** returns true if that advance would land us on a copy operation. It
8593
** returns false if the advance would end on a delete.
8694
*/
8795
static int ends_at_CPY(int *aC, int sz){
8896
while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
@@ -98,10 +106,15 @@
98106
/*
99107
** pSrc contains an edited file where aC[] describes the edit. Part of
100108
** pSrc has already been output. This routine outputs additional lines
101109
** of pSrc - lines that correspond to the next sz lines of the original
102110
** unedited file.
111
+**
112
+** Note that sz counts the number of lines of text in the original file.
113
+** But text is output from the edited file. So the number of lines transfer
114
+** to pOut might be different from sz. Fewer lines appear in pOut if there
115
+** are deletes. More lines appear if there are inserts.
103116
**
104117
** The aC[] array is updated and the new index into aC[] is returned.
105118
*/
106119
static int output_one_side(
107120
Blob *pOut, /* Write to this blob */
@@ -148,14 +161,21 @@
148161
int nConflict = 0; /* Number of merge conflicts seen so far */
149162
static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n";
150163
static const char zMid[] = "============================\n";
151164
static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n";
152165
153
- /* Compute the edits that occur from pPivot => pV1 and pPivot => pV2 */
166
+ /* Compute the edits that occur from pPivot => pV1 (into aC1)
167
+ ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
168
+ ** an array of integer triples. Within each triple, the first integer
169
+ ** is the number of lines of text to copy directly from the pivot,
170
+ ** the second integer is the number of lines of text to omit from the
171
+ ** pivot, and the third integer is the number of lines of text that are
172
+ ** inserted. The edit array ends with a triple of 0,0,0.
173
+ */
154174
aC1 = text_diff(pPivot, pV1, 0, 0);
155175
aC2 = text_diff(pPivot, pV2, 0, 0);
156
- if( aC2==0 || aC2==0 ){
176
+ if( aC1==0 || aC2==0 ){
157177
free(aC1);
158178
free(aC2);
159179
return -1;
160180
}
161181
@@ -177,26 +197,33 @@
177197
for(i2=0; i2<limit2; i2+=3){
178198
printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
179199
}
180200
)
181201
202
+ /* Loop over the two edit vectors and use them to compute merged text
203
+ ** which is written into pOut. i1 and i2 are multiples of 3 which are
204
+ ** indices into aC1[] and aC2[] to the edit triple currently being
205
+ ** processed
206
+ */
182207
i1 = i2 = 0;
183208
while( i1<limit1 && i2<limit2 ){
184209
DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
185210
i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
186211
i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
187212
188213
if( aC1[i1]>0 && aC2[i2]>0 ){
214
+ /* Output text that is unchanged in both V1 and V2 */
189215
nCpy = min(aC1[i1], aC2[i2]);
190216
DEBUG( printf("COPY %d\n", nCpy); )
191217
blob_copy_lines(pOut, pPivot, nCpy);
192218
blob_copy_lines(0, pV1, nCpy);
193219
blob_copy_lines(0, pV2, nCpy);
194220
aC1[i1] -= nCpy;
195221
aC2[i2] -= nCpy;
196222
}else
197223
if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
224
+ /* Output edits to V2 that occurs within unchanged regions of V1 */
198225
nDel = aC2[i2+1];
199226
nIns = aC2[i2+2];
200227
DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); )
201228
blob_copy_lines(0, pPivot, nDel);
202229
blob_copy_lines(0, pV1, nDel);
@@ -203,10 +230,11 @@
203230
blob_copy_lines(pOut, pV2, nIns);
204231
aC1[i1] -= nDel;
205232
i2 += 3;
206233
}else
207234
if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
235
+ /* Output edits to V1 that occur within unchanged regions of V2 */
208236
nDel = aC1[i1+1];
209237
nIns = aC1[i1+2];
210238
DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); )
211239
blob_copy_lines(0, pPivot, nDel);
212240
blob_copy_lines(0, pV2, nDel);
@@ -213,10 +241,11 @@
213241
blob_copy_lines(pOut, pV1, nIns);
214242
aC2[i2] -= nDel;
215243
i1 += 3;
216244
}else
217245
if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){
246
+ /* Output edits that are identical in both V1 and V2. */
218247
assert( aC1[i1]==0 );
219248
nDel = aC1[i1+1];
220249
nIns = aC1[i1+2];
221250
DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); )
222251
blob_copy_lines(0, pPivot, nDel);
@@ -224,10 +253,14 @@
224253
blob_copy_lines(0, pV2, nIns);
225254
i1 += 3;
226255
i2 += 3;
227256
}else
228257
{
258
+ /* We have found a region where different edits to V1 and V2 overlap.
259
+ ** This is a merge conflict. Find the size of the conflict, then
260
+ ** output both possible edits separate by distinctive marks.
261
+ */
229262
int sz = 1; /* Size of the conflict in lines */
230263
nConflict++;
231264
while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
232265
sz++;
233266
}
@@ -237,13 +270,22 @@
237270
blob_appendf(pOut, zMid);
238271
i2 = output_one_side(pOut, pV2, aC2, i2, sz);
239272
blob_appendf(pOut, zEnd);
240273
blob_copy_lines(0, pPivot, sz);
241274
}
275
+
276
+ /* If we are finished with an edit triple, advance to the next
277
+ ** triple.
278
+ */
242279
if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
243280
if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
244281
}
282
+
283
+ /* When one of the two edit vectors reaches its end, there might still
284
+ ** be an insert in the other edit vector. Output this remaining
285
+ ** insert.
286
+ */
245287
DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
246288
i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
247289
i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
248290
if( i1<limit1 && aC1[i1+2]>0 ){
249291
DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); )
250292
--- src/merge3.c
+++ src/merge3.c
@@ -37,13 +37,14 @@
37 /* The minimum of two integers */
38 #define min(A,B) (A<B?A:B)
39
40 /*
41 ** Compare N lines of text from pV1 and pV2. If the lines
42 ** are the same, return true. Return false if they are different.
 
43 **
44 ** The cursor on both pV1 and pV2 is unchanged.
45 */
46 static int sameLines(Blob *pV1, Blob *pV2, int N){
47 char *z1, *z2;
48 int i;
49
@@ -58,15 +59,22 @@
58 }
59 return 0;
60 }
61
62 /*
63 ** Look at the next edit triple in both aC1 and aC2. If they describe
64 ** an identical edit, then return 1. If the edits are different,
65 ** return 0.
 
 
66 */
67 static int sameEdit(int *aC1, int *aC2, Blob *pV1, Blob *pV2){
 
 
 
 
 
68 if( aC1[0]!=aC2[0] ) return 0;
69 if( aC1[1]!=aC2[1] ) return 0;
70 if( aC1[2]!=aC2[2] ) return 0;
71 if( sameLines(pV1, pV2, aC1[2]) ) return 1;
72 return 0;
@@ -78,11 +86,11 @@
78 **
79 ** (0) The number of lines to copy
80 ** (1) The number of lines to delete
81 ** (2) The number of liens to insert
82 **
83 ** Suppose we want to advance over sz lines of the pivot. This routine
84 ** returns true if that advance would land us on a copy operation. It
85 ** returns false if the advance would end on a delete.
86 */
87 static int ends_at_CPY(int *aC, int sz){
88 while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
@@ -98,10 +106,15 @@
98 /*
99 ** pSrc contains an edited file where aC[] describes the edit. Part of
100 ** pSrc has already been output. This routine outputs additional lines
101 ** of pSrc - lines that correspond to the next sz lines of the original
102 ** unedited file.
 
 
 
 
 
103 **
104 ** The aC[] array is updated and the new index into aC[] is returned.
105 */
106 static int output_one_side(
107 Blob *pOut, /* Write to this blob */
@@ -148,14 +161,21 @@
148 int nConflict = 0; /* Number of merge conflicts seen so far */
149 static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n";
150 static const char zMid[] = "============================\n";
151 static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n";
152
153 /* Compute the edits that occur from pPivot => pV1 and pPivot => pV2 */
 
 
 
 
 
 
 
154 aC1 = text_diff(pPivot, pV1, 0, 0);
155 aC2 = text_diff(pPivot, pV2, 0, 0);
156 if( aC2==0 || aC2==0 ){
157 free(aC1);
158 free(aC2);
159 return -1;
160 }
161
@@ -177,26 +197,33 @@
177 for(i2=0; i2<limit2; i2+=3){
178 printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
179 }
180 )
181
 
 
 
 
 
182 i1 = i2 = 0;
183 while( i1<limit1 && i2<limit2 ){
184 DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
185 i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
186 i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
187
188 if( aC1[i1]>0 && aC2[i2]>0 ){
 
189 nCpy = min(aC1[i1], aC2[i2]);
190 DEBUG( printf("COPY %d\n", nCpy); )
191 blob_copy_lines(pOut, pPivot, nCpy);
192 blob_copy_lines(0, pV1, nCpy);
193 blob_copy_lines(0, pV2, nCpy);
194 aC1[i1] -= nCpy;
195 aC2[i2] -= nCpy;
196 }else
197 if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
 
198 nDel = aC2[i2+1];
199 nIns = aC2[i2+2];
200 DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); )
201 blob_copy_lines(0, pPivot, nDel);
202 blob_copy_lines(0, pV1, nDel);
@@ -203,10 +230,11 @@
203 blob_copy_lines(pOut, pV2, nIns);
204 aC1[i1] -= nDel;
205 i2 += 3;
206 }else
207 if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
 
208 nDel = aC1[i1+1];
209 nIns = aC1[i1+2];
210 DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); )
211 blob_copy_lines(0, pPivot, nDel);
212 blob_copy_lines(0, pV2, nDel);
@@ -213,10 +241,11 @@
213 blob_copy_lines(pOut, pV1, nIns);
214 aC2[i2] -= nDel;
215 i1 += 3;
216 }else
217 if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){
 
218 assert( aC1[i1]==0 );
219 nDel = aC1[i1+1];
220 nIns = aC1[i1+2];
221 DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); )
222 blob_copy_lines(0, pPivot, nDel);
@@ -224,10 +253,14 @@
224 blob_copy_lines(0, pV2, nIns);
225 i1 += 3;
226 i2 += 3;
227 }else
228 {
 
 
 
 
229 int sz = 1; /* Size of the conflict in lines */
230 nConflict++;
231 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
232 sz++;
233 }
@@ -237,13 +270,22 @@
237 blob_appendf(pOut, zMid);
238 i2 = output_one_side(pOut, pV2, aC2, i2, sz);
239 blob_appendf(pOut, zEnd);
240 blob_copy_lines(0, pPivot, sz);
241 }
 
 
 
 
242 if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
243 if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
244 }
 
 
 
 
 
245 DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
246 i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
247 i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
248 if( i1<limit1 && aC1[i1+2]>0 ){
249 DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); )
250
--- src/merge3.c
+++ src/merge3.c
@@ -37,13 +37,14 @@
37 /* The minimum of two integers */
38 #define min(A,B) (A<B?A:B)
39
40 /*
41 ** Compare N lines of text from pV1 and pV2. If the lines
42 ** are the same, return true. Return false if one or more of the N
43 ** lines are different.
44 **
45 ** The cursors on both pV1 and pV2 is unchanged by this comparison.
46 */
47 static int sameLines(Blob *pV1, Blob *pV2, int N){
48 char *z1, *z2;
49 int i;
50
@@ -58,15 +59,22 @@
59 }
60 return 0;
61 }
62
63 /*
64 ** Look at the next edit triple in both aC1 and aC2. (An "edit triple" is
65 ** three integers describing the number of copies, deletes, and inserts in
66 ** moving from the original to the edited copy of the file.) If the three
67 ** integers of the edit triples describe an identical edit, then return 1.
68 ** If the edits are different, return 0.
69 */
70 static int sameEdit(
71 int *aC1, /* Array of edit integers for file 1 */
72 int *aC2, /* Array of edit integers for file 2 */
73 Blob *pV1, /* Text of file 1 */
74 Blob *pV2 /* Text of file 2 */
75 ){
76 if( aC1[0]!=aC2[0] ) return 0;
77 if( aC1[1]!=aC2[1] ) return 0;
78 if( aC1[2]!=aC2[2] ) return 0;
79 if( sameLines(pV1, pV2, aC1[2]) ) return 1;
80 return 0;
@@ -78,11 +86,11 @@
86 **
87 ** (0) The number of lines to copy
88 ** (1) The number of lines to delete
89 ** (2) The number of liens to insert
90 **
91 ** Suppose we want to advance over sz lines of the originl file. This routine
92 ** returns true if that advance would land us on a copy operation. It
93 ** returns false if the advance would end on a delete.
94 */
95 static int ends_at_CPY(int *aC, int sz){
96 while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
@@ -98,10 +106,15 @@
106 /*
107 ** pSrc contains an edited file where aC[] describes the edit. Part of
108 ** pSrc has already been output. This routine outputs additional lines
109 ** of pSrc - lines that correspond to the next sz lines of the original
110 ** unedited file.
111 **
112 ** Note that sz counts the number of lines of text in the original file.
113 ** But text is output from the edited file. So the number of lines transfer
114 ** to pOut might be different from sz. Fewer lines appear in pOut if there
115 ** are deletes. More lines appear if there are inserts.
116 **
117 ** The aC[] array is updated and the new index into aC[] is returned.
118 */
119 static int output_one_side(
120 Blob *pOut, /* Write to this blob */
@@ -148,14 +161,21 @@
161 int nConflict = 0; /* Number of merge conflicts seen so far */
162 static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n";
163 static const char zMid[] = "============================\n";
164 static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n";
165
166 /* Compute the edits that occur from pPivot => pV1 (into aC1)
167 ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
168 ** an array of integer triples. Within each triple, the first integer
169 ** is the number of lines of text to copy directly from the pivot,
170 ** the second integer is the number of lines of text to omit from the
171 ** pivot, and the third integer is the number of lines of text that are
172 ** inserted. The edit array ends with a triple of 0,0,0.
173 */
174 aC1 = text_diff(pPivot, pV1, 0, 0);
175 aC2 = text_diff(pPivot, pV2, 0, 0);
176 if( aC1==0 || aC2==0 ){
177 free(aC1);
178 free(aC2);
179 return -1;
180 }
181
@@ -177,26 +197,33 @@
197 for(i2=0; i2<limit2; i2+=3){
198 printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
199 }
200 )
201
202 /* Loop over the two edit vectors and use them to compute merged text
203 ** which is written into pOut. i1 and i2 are multiples of 3 which are
204 ** indices into aC1[] and aC2[] to the edit triple currently being
205 ** processed
206 */
207 i1 = i2 = 0;
208 while( i1<limit1 && i2<limit2 ){
209 DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
210 i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
211 i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
212
213 if( aC1[i1]>0 && aC2[i2]>0 ){
214 /* Output text that is unchanged in both V1 and V2 */
215 nCpy = min(aC1[i1], aC2[i2]);
216 DEBUG( printf("COPY %d\n", nCpy); )
217 blob_copy_lines(pOut, pPivot, nCpy);
218 blob_copy_lines(0, pV1, nCpy);
219 blob_copy_lines(0, pV2, nCpy);
220 aC1[i1] -= nCpy;
221 aC2[i2] -= nCpy;
222 }else
223 if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
224 /* Output edits to V2 that occurs within unchanged regions of V1 */
225 nDel = aC2[i2+1];
226 nIns = aC2[i2+2];
227 DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); )
228 blob_copy_lines(0, pPivot, nDel);
229 blob_copy_lines(0, pV1, nDel);
@@ -203,10 +230,11 @@
230 blob_copy_lines(pOut, pV2, nIns);
231 aC1[i1] -= nDel;
232 i2 += 3;
233 }else
234 if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
235 /* Output edits to V1 that occur within unchanged regions of V2 */
236 nDel = aC1[i1+1];
237 nIns = aC1[i1+2];
238 DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); )
239 blob_copy_lines(0, pPivot, nDel);
240 blob_copy_lines(0, pV2, nDel);
@@ -213,10 +241,11 @@
241 blob_copy_lines(pOut, pV1, nIns);
242 aC2[i2] -= nDel;
243 i1 += 3;
244 }else
245 if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){
246 /* Output edits that are identical in both V1 and V2. */
247 assert( aC1[i1]==0 );
248 nDel = aC1[i1+1];
249 nIns = aC1[i1+2];
250 DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); )
251 blob_copy_lines(0, pPivot, nDel);
@@ -224,10 +253,14 @@
253 blob_copy_lines(0, pV2, nIns);
254 i1 += 3;
255 i2 += 3;
256 }else
257 {
258 /* We have found a region where different edits to V1 and V2 overlap.
259 ** This is a merge conflict. Find the size of the conflict, then
260 ** output both possible edits separate by distinctive marks.
261 */
262 int sz = 1; /* Size of the conflict in lines */
263 nConflict++;
264 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
265 sz++;
266 }
@@ -237,13 +270,22 @@
270 blob_appendf(pOut, zMid);
271 i2 = output_one_side(pOut, pV2, aC2, i2, sz);
272 blob_appendf(pOut, zEnd);
273 blob_copy_lines(0, pPivot, sz);
274 }
275
276 /* If we are finished with an edit triple, advance to the next
277 ** triple.
278 */
279 if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
280 if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
281 }
282
283 /* When one of the two edit vectors reaches its end, there might still
284 ** be an insert in the other edit vector. Output this remaining
285 ** insert.
286 */
287 DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n",
288 i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
289 i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
290 if( i1<limit1 && aC1[i1+2]>0 ){
291 DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); )
292

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button