* Path for renames during restore and renames during share (thanks to Bryan Aldrich...
[vss2svn.git] / contrib / filterorphan / filterorphan.cpp
blob4e4905379ae0c78a2d7132a91029ea249a301158
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <fcntl.h>
5 #include <io.h>
7 struct ReplaceNode
9 inline operator const char *(void) const { return Text; }
10 const char *Text;
11 bool CreatedDir;
12 bool CreatedParent;
13 bool Used;
16 enum TriggerType
18 Type_MoveOrphan,
19 Type_SuppressCreate,
20 Type_OneShotReplace,
21 Type_NoMove,
22 Type_OneShotDupe,
23 Type_MultiReplace,
24 Type_MultiDupe,
27 struct TriggerNode
29 const char *TriggerText;
30 const char *CopyText;
31 union
33 ReplaceNode *Node;
34 int SkipCount;
36 TriggerType Type;
39 // increase max number of triggers as needed,
40 // but NB current implementation cannot cope with DIFFERENT text starting with same letter.
41 // (same text with different Node is ok though)
42 TriggerNode Triggers[200];
43 int TriggerCount=0;
45 int RevSubst[100000];
46 int MaxRevSeen=0;
48 char DupeBuffer[1000000];
49 int DupeIndex=0;
50 bool DoneDupePrint;
52 ReplaceNode ReplaceIndex[26][26][26][26];
54 struct
56 const char *ReplaceText;
57 const char *NextText;
58 const char *NextReplace; // of just the NextText for a non-dupe, or of entire path for dupe.
59 bool Dupe;
60 char SkipCount;
61 } SpecialReplace[] =
63 { "Plugins","/Gsc200s/Gsc200s.rc2","/Gsc200s/res/Gsc200s.rc2",false,0 },
64 // snip other SpecialReplace entries.
67 void Replace(const char *FileID,const char *ReplaceText,bool DirAlreadyExists=true,const char *ReplaceText2=NULL,const char *ReplaceTrigger=NULL,const char *ParentCreate=NULL)
69 ReplaceNode *Node=&ReplaceIndex[FileID[0]-'A'][FileID[1]-'A'][FileID[2]-'A'][FileID[3]-'A'];
70 Node->Text=ReplaceText;
71 Node->CreatedDir=DirAlreadyExists;
72 Node->CreatedParent=ParentCreate==NULL;
73 if(ReplaceText2)
75 Triggers[TriggerCount].Node=Node;
76 Triggers[TriggerCount].CopyText=ReplaceText2;
77 Triggers[TriggerCount].TriggerText=ReplaceTrigger?ReplaceTrigger:ReplaceText2;
78 Triggers[TriggerCount].Type=Type_MoveOrphan;
79 TriggerCount++;
81 if(!DirAlreadyExists && strncmp(ReplaceText,"orphaned",8)!=0)
83 if(ParentCreate)
85 if(strncmp(ParentCreate,ReplaceText,strlen(ParentCreate))!=0)
86 fprintf(stderr,"!!!!!! BAD PARAMETER !!!!!!\n");
87 // suppress parent when it gets created
88 Triggers[TriggerCount].Node=NULL;
89 Triggers[TriggerCount].CopyText=NULL;
90 Triggers[TriggerCount].TriggerText=ParentCreate;
91 Triggers[TriggerCount].Type=Type_SuppressCreate;
92 TriggerCount++;
94 // suppress when it does get created.
95 Triggers[TriggerCount].Node=NULL;
96 Triggers[TriggerCount].CopyText=NULL;
97 Triggers[TriggerCount].TriggerText=ReplaceText;
98 Triggers[TriggerCount].Type=Type_SuppressCreate;
99 TriggerCount++;
103 void OneShotReplace(const char *Trigger,const char *ReplaceText,int SkipCount=0)
105 Triggers[TriggerCount].SkipCount=SkipCount;
106 Triggers[TriggerCount].CopyText=ReplaceText;
107 Triggers[TriggerCount].TriggerText=Trigger;
108 Triggers[TriggerCount].Type=Type_OneShotReplace;
109 TriggerCount++;
111 void OneShotDupe(const char *Trigger,const char *ReplaceText,int SkipCount=0)
113 Triggers[TriggerCount].SkipCount=SkipCount;
114 Triggers[TriggerCount].CopyText=ReplaceText;
115 Triggers[TriggerCount].TriggerText=Trigger;
116 Triggers[TriggerCount].Type=Type_OneShotDupe;
117 TriggerCount++;
119 void MultiReplace(const char *Trigger,const char *ReplaceText,int SkipCount=0)
121 Triggers[TriggerCount].SkipCount=SkipCount;
122 Triggers[TriggerCount].CopyText=ReplaceText;
123 Triggers[TriggerCount].TriggerText=Trigger;
124 Triggers[TriggerCount].Type=Type_MultiReplace;
125 TriggerCount++;
127 void MultiDupe(const char *Trigger,const char *ReplaceText,int SkipCount=0)
129 Triggers[TriggerCount].SkipCount=SkipCount;
130 Triggers[TriggerCount].CopyText=ReplaceText;
131 Triggers[TriggerCount].TriggerText=Trigger;
132 Triggers[TriggerCount].Type=Type_MultiDupe;
133 TriggerCount++;
135 void NoMove(const char *Source,const char *Dest)
137 Triggers[TriggerCount].Node=NULL;
138 Triggers[TriggerCount].CopyText=Source;
139 Triggers[TriggerCount].TriggerText=Dest;
140 Triggers[TriggerCount].Type=Type_NoMove;
141 TriggerCount++;
144 void SetupReplacements(void)
146 // first parameter to Replace MUST begin with 4 capital letters.
147 Replace("AAAAAAAA","MyProject"); //
148 Replace("CAAAAAAA","orphaned/DongleCheck",false); // /DongleCheck.cpp
149 Replace("DAAAAAAA","orphaned/DongleCheck"); // /DongleCheck.dsp
150 Replace("EAAAAAAA","orphaned/DongleCheck"); // /DongleCheck.h
151 Replace("FAAAAAAA","orphaned/DongleCheck"); // /DongleCheck.rc
152 Replace("GAAAAAAA","orphaned/DongleCheck"); // /DongleCheckDlg.cpp
153 Replace("HAAAAAAA","orphaned/DongleCheck"); // /DongleCheckDlg.h
154 Replace("IAAAAAAA","orphaned/DongleCheck"); // /ReadMe.txt
155 Replace("JAAAAAAA","orphaned/DongleCheck"); // /resource.h
156 Replace("KAAAAAAA","orphaned/DongleCheck"); // /StdAfx.cpp
157 Replace("LAAAAAAA","orphaned/DongleCheck"); // /StdAfx.h
158 Replace("EKAAAAAA","orphaned/DongleCheck/res",false); // /cross.bmp
159 Replace("FKAAAAAA","orphaned/DongleCheck/res"); // /DongleCheck.ico
160 Replace("GKAAAAAA","orphaned/DongleCheck/res"); // /DongleCheck.rc2
161 Replace("HKAAAAAA","orphaned/DongleCheck/res"); // /tick.bmp
162 // snip various other Replace(...) lines...
163 Replace("CJBAAAAA","Plugin/Gsc200s",true,"Plugins/Gsc200s","Plugins"); // /Gsc200sPlugin.cpp
164 // snip many more Replace(...) lines...
166 // special triggers
167 NoMove("Plugins/Flyte","Flyte");
168 RevSubst[302]=370;
169 OneShotReplace("InstallImage/Install/v2.26.ipr","InstallImage/Install/V2.26.ipr");
170 OneShotReplace("InstallImage/Install/v2.26.ipr","InstallImage/Install/V2.26.ipr");
172 // snip various fixups of stuff that failed to get put in the Plugins/Flyte project
173 // mainly OneShotReplace and OneShotDupe lines, and some Multi... lines.
177 int main(void)
179 _setmode( _fileno( stdin ), _O_BINARY );
180 _setmode( _fileno( stdout ), _O_BINARY );
182 memset(ReplaceIndex,0,sizeof(ReplaceIndex));
183 memset(RevSubst,0,sizeof(RevSubst));
185 SetupReplacements();
187 const char *DirCreateText="\n"
188 "Node-kind: dir\n"
189 "Node-action: add\n"
190 "Prop-content-length: 10\n"
191 "Content-length: 10\n"
192 "\n"
193 "PROPS-END\n"
194 "\n"
195 "\n"
196 "Node-path: ";
197 int DirCreateTextLen=strlen(DirCreateText);
198 const char NodePathTrigger[]="Node-path:";
199 const char NodeRevTrigger[]="Node-copyfrom-rev: ";
200 const char NextRevTrigger[]="Revision-number:";
201 bool NodePathFlag;
202 bool LastSpace=false;
203 const char Prefix[]="orphaned/_";
204 const char *TriggerStart=NULL;
205 const char *TriggerPtr=NULL;
206 const char *Marker="!!!!!!!this should not appear in the output file!!!!!!!";
207 char c;
208 // int dbgcount=50;
209 while(fread(&c,1,1,stdin)) // horribly slow but simple 1 char at a time...
211 // if(dbgcount-->0)
212 // fprintf(stderr,"Got char: %d ('%c')\n",c,c);
213 if(TriggerPtr && c==*TriggerPtr)
215 TriggerPtr++;
216 if(!*TriggerPtr)
218 if(DupeIndex && TriggerStart!=NodePathTrigger && TriggerStart!=NextRevTrigger && TriggerStart!=NodeRevTrigger)
220 fprintf(stderr,"Matched trigger in Dupe mode: %s\n",TriggerStart);
221 return 1;
223 // complete trigger matched.
224 if(TriggerStart==Prefix)
226 // matched prefix. next 8 chars are the file ID, followed by the original filename.
227 char FileID[9];
228 fread(&FileID,1,8,stdin);
229 FileID[8]='\0';
230 int ValidChars;
231 for(ValidChars=0;ValidChars<8;ValidChars++)
233 // fprintf(stderr,"FileID[%d]: %d ('%c')\n",ValidChars,FileID[ValidChars],FileID[ValidChars]);
234 if(FileID[ValidChars]<'A' || FileID[ValidChars]>'Z')
235 break;
236 /* {
237 FileID[8]='\0';
238 fprintf(stderr,"Sorry I can't cope with %s%s\n",Prefix,FileID);
239 return 1;
240 // TODO replace the above with code to ignore the false trigger if necessary.
243 ReplaceNode *Node=ValidChars<8?NULL:&ReplaceIndex[FileID[0]-'A'][FileID[1]-'A'][FileID[2]-'A'][FileID[3]-'A'];
244 const char *ReplaceText=Node?Node->Text:NULL;
245 if(ReplaceText)
247 if(ReplaceText==Marker)
249 fwrite(&Prefix,1,TriggerPtr-Prefix,stdout);
250 fwrite(FileID,1,8,stdout);
252 else
254 if(!Node->CreatedParent)
256 // need to create the parent of ReplaceText first.
257 int LastSep=0;
258 for(int i=0;ReplaceText[i];i++)
259 if(ReplaceText[i]=='/')
260 LastSep=i;
261 fwrite(ReplaceText,1,LastSep,stdout);
262 fwrite(DirCreateText,1,DirCreateTextLen,stdout);
263 Node->CreatedParent=true; // avoid re-creating when it is updated, copied, etc.
265 fwrite(ReplaceText,1,strlen(ReplaceText),stdout);
267 Node->Used=true;
269 // now check if this was a dir creation, and if so if we already created the same
270 // replacement dir already. If so skip ahead as next section will always be the
271 // node creation within the dir.
273 fread(&c,1,1,stdin);
274 // fprintf(stderr,"Next char: %d ('%c')\n",c,c);
275 if(c=='\n')
277 // this is referring just to the orphaned dir itself. next characters should match
278 // the following text:
279 int DirCreateMatchLen=1;
281 while(DirCreateMatchLen<DirCreateTextLen)
283 fread(&c,1,1,stdin);
284 if(c!=DirCreateText[DirCreateMatchLen++])
286 fprintf(stderr,"DirCreateText Mismatch at character %d (got '%c' instead of '%c'):%s\n",
287 DirCreateMatchLen,c,DirCreateText[DirCreateMatchLen-1],DirCreateText);
288 // don't modify output, so write the stuff we read.
289 fwrite(DirCreateText,1,DirCreateMatchLen-1,stdout);
290 fwrite(&c,1,1,stdout);
291 break;
294 if(!(DirCreateMatchLen<DirCreateTextLen))
296 char LabelPathText[256];
297 char LabelPathTextLen=0;
298 char PathTriggerText[64]="orphaned/_";
299 char PathTriggerTextLen=strlen(strncat(PathTriggerText,FileID,8));
300 char PathTriggerMatchLen=0;
301 while(PathTriggerMatchLen<PathTriggerTextLen)
303 fread(&c,1,1,stdin);
304 if(c==PathTriggerText[PathTriggerMatchLen])
305 PathTriggerMatchLen++;
306 else if(c=='\n' || LabelPathTextLen+PathTriggerTextLen>250)
308 LabelPathText[LabelPathTextLen]='\0';
309 fprintf(stderr,"PathTriggerText not found after %c chars. Looking for %s in:\n%s",
310 LabelPathTextLen+PathTriggerTextLen,PathTriggerText,LabelPathText);
311 fwrite(DirCreateText,1,DirCreateTextLen,stdout);
312 fwrite(LabelPathText,1,LabelPathTextLen,stdout);
313 fwrite(PathTriggerText,1,PathTriggerMatchLen,stdout);
314 fwrite(&c,1,1,stdout);
315 break;
317 else
319 if(PathTriggerMatchLen)
321 memcpy(LabelPathText+LabelPathTextLen,PathTriggerText,PathTriggerMatchLen);
322 LabelPathTextLen+=PathTriggerMatchLen;
323 PathTriggerMatchLen=0;
325 LabelPathText[LabelPathTextLen++]=c;
329 if(!(DirCreateMatchLen<DirCreateTextLen))
331 // full text matched... so do we output or not?
332 LabelPathText[LabelPathTextLen]='\0';
333 if(!Node->CreatedDir)
335 // write out DirCreateText, but replacing the last section with ReplaceText...
336 fwrite(DirCreateText,1,DirCreateTextLen,stdout);
337 fwrite(LabelPathText,1,LabelPathTextLen,stdout);
338 fwrite(ReplaceText,1,strlen(ReplaceText),stdout);
339 // Node->CreatedDir=true;
340 // fprintf(stderr,"DirCreateText Matched and output! Label:'%s'\n",LabelPathText);
342 // else
343 // fprintf(stderr,"DirCreateText Matched and suppressed label:'%s'\n",LabelPathText);
347 else
349 int Index;
350 char Buffer[100];
351 int Count=0;
352 Buffer[Count++]=c;
353 for(Index=0;Index<sizeof(SpecialReplace)/sizeof(*SpecialReplace) && Count;Index++)
355 if(strcmp(SpecialReplace[Index].ReplaceText,ReplaceText)==0)
357 if(strncmp(SpecialReplace[Index].NextText,Buffer,Count)==0)
359 for(;;)
361 if(SpecialReplace[Index].NextText[Count]=='\0')
363 if(SpecialReplace[Index].SkipCount)
364 SpecialReplace[Index].SkipCount--;
365 else
367 // fprintf(stderr,"%s -> %s ",SpecialReplace[Index].NextText,SpecialReplace[Index].NextReplace);
368 if(SpecialReplace[Index].Dupe)
370 if(NodePathFlag)
372 DupeBuffer[0]=' ';
373 DupeIndex=1+strlen(strcpy(&DupeBuffer[1],SpecialReplace[Index].NextReplace));
374 // fprintf(stderr,"[DupeStart]\n");
376 // else
377 // fprintf(stderr,"[Ignored]\n");
379 else
381 fwrite(SpecialReplace[Index].NextReplace,1,strlen(SpecialReplace[Index].NextReplace),stdout);
382 Count=0;
384 break;
387 fread(&Buffer[Count++],1,1,stdin);
388 if(Buffer[Count-1]!=SpecialReplace[Index].NextText[Count-1])
389 break;
395 if(Count)
396 fwrite(Buffer,1,Count,stdout);
397 // else
398 // fprintf(stderr,"...Done SpecialReplace!\n");
402 else
404 fwrite(&Prefix,1,TriggerPtr-Prefix,stdout);
405 fwrite(FileID,1,8,stdout);
406 if(ValidChars==8)
408 char LeafName[256];
409 int LeafLen=0;
410 while(LeafLen<sizeof(LeafName)-1)
412 fread(&LeafName[LeafLen++],1,1,stdin);
413 if(LeafName[LeafLen-1]=='\n')
414 break;
416 LeafName[LeafLen]='\0';
417 fwrite(LeafName,1,LeafLen,stdout);
418 if(LeafLen>1)
420 fprintf(stderr," Replace(\"%s\",\"????\"); // %s",FileID,LeafName);
421 Replace(FileID,Marker);
426 else if(TriggerStart==NodePathTrigger || TriggerStart==NextRevTrigger)
428 if(DupeIndex)
430 fwrite(NodePathTrigger,1,strlen(NodePathTrigger),stdout);
431 fwrite(DupeBuffer,1,DupeIndex,stdout);
432 // fprintf(stderr,"Done Dupe!\n");
433 DupeIndex=0;
435 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
436 NodePathFlag=(TriggerStart==NodePathTrigger);
438 else if(TriggerStart==NodeRevTrigger)
440 if(DupeIndex)
442 memcpy(&DupeBuffer[DupeIndex],TriggerStart,TriggerPtr-TriggerStart);
443 DupeIndex+=TriggerPtr-TriggerStart;
445 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
446 char Buffer[8];
447 int i;
448 for(i=0;i<7;i++)
450 fread(&Buffer[i],1,1,stdin);
451 if(Buffer[i]=='\n')
453 i++;
454 break;
456 else if(Buffer[i]<'0' || Buffer[i]>'9')
458 Buffer[i+1]='\0';
459 fprintf(stderr,"Non-numeric revision! (starting \"%s\")\n",Buffer);
460 return 1;
463 int Rev;
464 if(sscanf(Buffer,"%d\n",&Rev))
466 if(Rev>MaxRevSeen)
467 MaxRevSeen=Rev;
468 else if(RevSubst[Rev] && RevSubst[Rev]<MaxRevSeen)
470 i=sprintf(Buffer,"%d\n",RevSubst[Rev]);
471 fprintf(stderr,"RevSubst OK: %d -> %s",Rev,Buffer);
474 else
475 fprintf(stderr,"sscanf failed!!!\n");
476 fwrite(Buffer,1,i,stdout);
477 if(DupeIndex)
479 memcpy(&DupeBuffer[DupeIndex],Buffer,i);
480 DupeIndex+=i;
483 else
485 int i=0;
486 bool ActionTaken=false;
487 while(i<TriggerCount)
489 // NB strcmp rather than pointer comparison, as there may be multiple matches.
490 if(strcmp(TriggerStart,Triggers[i].TriggerText)==0)
492 bool RemoveTrigger=true;
493 switch(Triggers[i].Type)
495 case Type_MoveOrphan:
496 if(!ActionTaken)
498 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
499 ActionTaken=true;
501 Triggers[i].Node->Text=Triggers[i].CopyText;
502 break;
503 case Type_SuppressCreate:
504 if(!ActionTaken)
506 if(Triggers[i].SkipCount)
508 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
509 Triggers[i].SkipCount--;
510 RemoveTrigger=false;
512 else
514 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
515 fwrite("-suppressed",1,11,stdout);
518 else
520 fprintf(stderr,"ActionTaken before a SuppressCreate\nTriggered on '%s', type %d\n",Triggers[i].TriggerText,Triggers[i].Type);
521 return 1;
523 break;
524 case Type_MultiReplace:
525 RemoveTrigger=false;
526 // follow through
527 case Type_OneShotReplace:
528 if(!ActionTaken)
530 if(Triggers[i].SkipCount)
532 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
533 Triggers[i].SkipCount--;
534 RemoveTrigger=false;
536 else
537 fwrite(Triggers[i].CopyText,1,strlen(Triggers[i].CopyText),stdout);
538 ActionTaken=true;
540 else
541 RemoveTrigger=false; // could be a second "OneShotReplace" stacked up to make a TwoShotReplace, etc...
542 break;
543 case Type_MultiDupe:
544 RemoveTrigger=false;
545 // follow through
546 case Type_OneShotDupe:
547 if(!ActionTaken)
549 if(Triggers[i].SkipCount)
551 Triggers[i].SkipCount--;
552 RemoveTrigger=false;
554 else
556 fprintf(stderr,"OneShotDupe: %s -> %s\n",Triggers[i].TriggerText,Triggers[i].CopyText);
557 DupeBuffer[0]=' ';
558 DupeIndex=1+strlen(strcpy(&DupeBuffer[1],Triggers[i].CopyText));
560 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
561 ActionTaken=true;
563 else
564 RemoveTrigger=false; // could be a second "OneShotDupe" stacked up to make a TwoShotDupe, etc...
565 break;
566 case Type_NoMove:
567 // prevent a 'move' from occuring... do this by creating a OneShotReplace so that the
568 // delete action applies to the intended target dir rather than the source dir.
569 // this means that the copy still occurs, as does the delete, but delete has a different target.
570 if(!ActionTaken)
572 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
573 ActionTaken=true;
575 Triggers[i].Node=NULL;
577 register const char *Temp=Triggers[i].CopyText;
578 Triggers[i].CopyText=Triggers[i].TriggerText;
579 Triggers[i].TriggerText=Temp;
581 Triggers[i].Type=Type_OneShotReplace;
582 // TriggerCount++;
583 RemoveTrigger=false;
584 break;
585 default:
586 fprintf(stderr,"SOMEONE FORGOT TO WRITE THIS BIT!!!!\nTriggered on '%s', type %d\n",Triggers[i].TriggerText,Triggers[i].Type);
587 return 1;
589 if(!RemoveTrigger)
590 i++;
591 else if(i<--TriggerCount)
592 memmove(&Triggers[i],&Triggers[i+1],sizeof(*Triggers)*(TriggerCount-i));
594 else
595 i++;
598 TriggerPtr=TriggerStart=NULL;
601 else
603 if(TriggerPtr!=TriggerStart)
605 int CurrentOffset=TriggerPtr-TriggerStart;
606 // look for an alternative trigger starting with the same letter(s)
607 if(strncmp(TriggerStart,NodeRevTrigger,CurrentOffset)==0 &&
608 c==NodeRevTrigger[CurrentOffset])
610 TriggerStart=NodeRevTrigger;
611 TriggerPtr=TriggerStart+CurrentOffset;
613 else if(NodePathFlag)
615 for(int i=0;i<TriggerCount;i++)
617 if(strncmp(TriggerStart,Triggers[i].TriggerText,CurrentOffset)==0 &&
618 c==Triggers[i].TriggerText[CurrentOffset] && (Triggers[i].Type!=Type_MoveOrphan || Triggers[i].Node->Used))
620 TriggerStart=Triggers[i].TriggerText;
621 TriggerPtr=TriggerStart+CurrentOffset;
622 break;
626 if(c!=*TriggerPtr)
628 fwrite(TriggerStart,1,TriggerPtr-TriggerStart,stdout);
629 if(DupeIndex)
631 memcpy(&DupeBuffer[DupeIndex],TriggerStart,TriggerPtr-TriggerStart);
632 DupeIndex+=TriggerPtr-TriggerStart;
634 TriggerPtr=TriggerStart=NULL;
635 NodePathFlag=false;
638 if(!TriggerStart)
640 if(LastSpace && c==Prefix[0])
641 TriggerPtr=TriggerStart=Prefix;
642 else if(c==NodePathTrigger[0])
643 TriggerPtr=TriggerStart=NodePathTrigger;
644 else if(c==NextRevTrigger[0])
645 TriggerPtr=TriggerStart=NextRevTrigger;
646 else if(NodePathFlag)
648 for(int i=0;i<TriggerCount;i++)
649 if(c==Triggers[i].TriggerText[0] && (Triggers[i].Type!=Type_MoveOrphan || Triggers[i].Node->Used))
651 TriggerPtr=TriggerStart=Triggers[i].TriggerText;
652 break;
656 if(TriggerPtr && c==*TriggerPtr)
657 TriggerPtr++;
658 else
660 fwrite(&c,1,1,stdout);
661 if(DupeIndex)
662 DupeBuffer[DupeIndex++]=c;
663 LastSpace=c==' ';
664 if(!LastSpace)
665 NodePathFlag=false;
669 while(TriggerCount-->0)
671 fprintf(stderr,"Unused Trigger: \"%s\" (Type %d, Copy '%s' to %p)\n",
672 Triggers[TriggerCount].TriggerText,Triggers[TriggerCount].Type,Triggers[TriggerCount].CopyText,Triggers[TriggerCount].Node);
674 return 0;