]> git.decadent.org.uk Git - dak.git/blob - tools/dsync-0.0/test/pathtest.cc
Readme foo
[dak.git] / tools / dsync-0.0 / test / pathtest.cc
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <sys/stat.h>
4 #include <dsync/error.h>
5 #include <iostream>
6
7 // SimplifyPath - Short function to remove relative path components     /*{{{*/
8 // ---------------------------------------------------------------------
9 /* This short function removes relative path components such as ./ and ../
10    from the path and removes double // as well. It works by seperating
11    the path into a list of components and then removing any un-needed
12    compoments */
13 bool SimplifyPath(char *Buffer)
14 {
15    // Create a list of path compoments
16    char *Pos[100];
17    unsigned CurPos = 0;
18    Pos[CurPos] = Buffer;
19    CurPos++;   
20    for (char *I = Buffer; *I != 0;)
21    {
22       if (*I == '/')
23       {
24          *I = 0;
25          I++;
26          Pos[CurPos] = I;
27          CurPos++;
28       }
29       else
30          I++;
31    }
32    
33    // Strip //, ./ and ../
34    for (unsigned I = 0; I != CurPos; I++)
35    {
36       if (Pos[I] == 0)
37          continue;
38       
39       // Double slash
40       if (Pos[I][0] == 0)
41       {
42          if (I != 0)
43             Pos[I] = 0;
44          continue;
45       }
46       
47       // Dot slash
48       if (Pos[I][0] == '.' && Pos[I][1] == 0)
49       {
50          Pos[I] = 0;
51          continue;
52       }
53       
54       // Dot dot slash
55       if (Pos[I][0] == '.' && Pos[I][1] == '.' && Pos[I][2] == 0)
56       {
57          Pos[I] = 0;
58          unsigned J = I;
59          for (; Pos[J] == 0 && J != 0; J--);
60          if (Pos[J] == 0)
61             return _error->Error("Invalid path, too many ../s");
62          Pos[J] = 0;     
63          continue;
64       }
65    }  
66
67    // Recombine the path into full path
68    for (unsigned I = 0; I != CurPos; I++)
69    {
70       if (Pos[I] == 0)
71          continue;
72       memmove(Buffer,Pos[I],strlen(Pos[I]));
73       Buffer += strlen(Pos[I]);
74       
75       if (I + 1 != CurPos)
76          *Buffer++ = '/';
77    }            
78    *Buffer = 0;
79    
80    return true;
81 }
82                                                                         /*}}}*/
83 // ResolveLink - Resolve a file into an unsymlinked path                /*{{{*/
84 // ---------------------------------------------------------------------
85 /* The returned path is a path that accesses the same file without 
86    traversing a symlink, the memory buffer used should be twice as large
87    as the largest path. It uses an LRU cache of past lookups to speed things
88    up, just don't change directores :> */
89 struct Cache
90 {
91    string Dir;
92    string Trans;
93    unsigned long Age;
94 };
95 static Cache DirCache[400];
96 static unsigned long CacheAge = 0;
97 bool ResolveLink(char *Buffer,unsigned long Max)
98 {
99    if (Buffer[0] == 0 || (Buffer[0] == '/' && Buffer[1] == 0))
100       return true;
101  
102    // Lookup in the cache
103    Cache *Entry = 0;
104    for (int I = 0; I != 400; I++)
105    {
106       // Store an empty entry
107       if (DirCache[I].Dir.empty() == true)
108       {
109          Entry = &DirCache[I];
110          Entry->Age = 0;
111          continue;
112       }
113       
114       // Store the LRU entry
115       if (Entry != 0 && Entry->Age > DirCache[I].Age)
116          Entry = &DirCache[I];
117       
118       if (DirCache[I].Dir != Buffer || DirCache[I].Trans.empty() == true)
119          continue;
120       strcpy(Buffer,DirCache[I].Trans.c_str());
121       DirCache[I].Age = CacheAge++;
122       return true;
123    }
124    
125    // Prepare the cache for our new entry
126    if (Entry != 0 && Buffer[strlen(Buffer) - 1] == '/')
127    {
128       Entry->Age = CacheAge++;
129       Entry->Dir = Buffer;
130    }   
131    else
132       Entry = 0;
133
134    // Resolve any symlinks
135    unsigned Counter = 0;
136    while (1)
137    {
138       Counter++;
139       if (Counter > 50)
140          return _error->Error("Exceeded allowed symlink depth");
141       
142       // Strip off the final component name
143       char *I = Buffer + strlen(Buffer);
144       for (; I != Buffer && (*I == '/' || *I == 0); I--);
145       for (; I != Buffer && *I != '/'; I--);
146       if (I != Buffer)
147          I++;
148
149       // If it is a link then read the link dest over the final component
150       int Res = readlink(Buffer,I,Max - (I - Buffer));
151       if (Res > 0)
152       {
153          I[Res] = 0;
154          
155          // Absolute path..
156          if (*I == '/')
157             memmove(Buffer,I,strlen(I)+1);
158
159          if (SimplifyPath(Buffer) == false)
160             return false;
161       }
162       else
163          break;
164    }
165    
166    /* Here we are abusive and move the current path component to the end 
167       of the buffer to advoid allocating space */
168    char *I = Buffer + strlen(Buffer);
169    for (; I != Buffer && (*I == '/' || *I == 0); I--);
170    for (; I != Buffer && *I != '/'; I--);
171    if (I != Buffer)
172       I++;
173    unsigned Len = strlen(I) + 1;
174    char *End = Buffer + Max - Len;
175    memmove(End,I,Len);
176    *I = 0;
177
178    // Recurse to deal with any links in the files path
179    if (ResolveLink(Buffer,Max - Len) == false)
180       return false;
181    I = Buffer + strlen(Buffer);
182    memmove(I,End,Len);
183
184    // Store in the cache
185    if (Entry != 0)
186       Entry->Trans = Buffer;
187    
188    return true;
189 }
190                                                                         /*}}}*/
191
192 int main(int argc,char *argv[])
193 {
194    char Buf[1024*4];
195 //   strcpy(Buf,argv[1]);
196    while (!cin == false)
197    {
198       char Buf2[200];
199       cin.getline(Buf2,sizeof(Buf2));
200       strcpy(Buf,Buf2);
201       
202       if (ResolveLink(Buf,sizeof(Buf)) == false)
203          _error->DumpErrors();
204       else
205       {
206 /*       struct stat StA;
207          struct stat StB;
208          if (stat(Buf,&StA) != 0 || stat(Buf2,&StB) != 0)
209          {
210             cerr << Buf << ',' << Buf2 << endl;
211             cerr << "Stat failure" << endl;
212          }
213          
214          if (StA.st_ino != StB.st_ino)
215             cerr << "Inode mismatch" << endl;*/
216          
217          cout << Buf << endl;
218       }      
219    }
220    return 0;
221 }