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