1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Diagnostics ;
4+ using System . IO ;
5+ using System . Runtime . InteropServices ;
6+
7+
8+ namespace Save_In_Companion
9+ {
10+ /// <summary>
11+ /// Class containing methods to retrieve specific file system paths.
12+ /// </summary>
13+ public class MenuEntry
14+ {
15+ public string FolderPath { get ; set ; }
16+
17+ public string Name { get ; set ; }
18+
19+ public string Comment { get ; set ; }
20+
21+ public List < MenuEntry > SubEntries { get ; set ; }
22+
23+ public int Deepness { get ; set ; }
24+
25+ public bool IsSeperator { get ; set ; }
26+
27+ public bool IsCategory { get ; set ; }
28+
29+ public MenuEntry ( )
30+ {
31+ SubEntries = new List < MenuEntry > ( ) ;
32+ }
33+
34+ string GetRootFolder ( string path )
35+ {
36+ string rootFolder ;
37+
38+ //HACK: I don't know of a good way of handling the root of a linux volume.
39+ if ( string . IsNullOrEmpty ( path ) )
40+ {
41+ rootFolder = "SaveInRoot" ;
42+ }
43+ else
44+ {
45+ rootFolder = path ;
46+
47+ while ( true )
48+ {
49+ string temp = Path . GetDirectoryName ( path ) ;
50+ if ( String . IsNullOrEmpty ( temp ) )
51+ break ;
52+ path = temp ;
53+ }
54+ }
55+ return rootFolder ;
56+ }
57+
58+ public void BuildOutput ( StringWriter stream , Settings settings )
59+ {
60+ if ( IsSeperator )
61+ {
62+ stream . WriteLine ( "---" ) ;
63+ }
64+ else if ( IsCategory )
65+ {
66+ if ( SubEntries . Count > 0 )
67+ {
68+ stream . WriteLine ( string . Format ( ". // (alias:{0})" , Name ) ) ;
69+ SubEntries . ForEach ( x => x . BuildOutput ( stream , settings ) ) ;
70+ }
71+ }
72+ else
73+ {
74+ string finalPath ;
75+
76+ if ( FolderPath . StartsWith ( settings . DownloadsFolderPath ) )
77+ {
78+ if ( FolderPath == settings . DownloadsFolderPath )
79+ {
80+ finalPath = string . Format ( ". //{0} (alias: {1})" , Comment , Name ) ;
81+ }
82+ else
83+ {
84+ finalPath = FolderPath . Remove ( 0 , settings . DownloadsFolderPath . Length + 1 ) ;
85+ }
86+ }
87+ else
88+ {
89+ // Get the drive prefix.
90+ string drivePrefix = Path . GetPathRoot ( FolderPath ) ;
91+
92+ // Get the folder path with the drive prefix removed
93+ string relativePath = FolderPath . Remove ( 0 , drivePrefix . Length ) ;
94+
95+ // Get the safe identifier of the drive
96+ char [ ] arr = drivePrefix . ToCharArray ( ) ;
97+ arr = Array . FindAll < char > ( arr , ( c => ( char . IsLetterOrDigit ( c )
98+ || char . IsWhiteSpace ( c )
99+ || c == '-' ) ) ) ;
100+ string driveLetter = new string ( arr ) ;
101+
102+ // Get the root folder of the relative path.
103+ string rootFolder = GetRootFolder ( relativePath ) ;
104+
105+ // create final written path
106+ finalPath = string . Format ( "{0}{1} //{2} (alias:{3})" , new string ( '>' , Deepness ) ,
107+ Path . Combine ( settings . SaveInLinksFolderName , driveLetter + relativePath ) , Comment , Name ) ;
108+
109+ if ( ! settings . SkipLinkCreation )
110+ {
111+ // Create a link path.
112+ string link = Path . Combine ( settings . SaveInLinksFolderPath , driveLetter + rootFolder ) ;
113+
114+ if ( ! Directory . Exists ( link ) )
115+ {
116+ string realPath ;
117+
118+ // Get real path folder
119+ //HACK:
120+ if ( rootFolder == "SaveInRoot" )
121+ {
122+ realPath = "/" ;
123+ }
124+ else
125+ {
126+ realPath = Path . Combine ( drivePrefix , rootFolder ) ;
127+
128+ }
129+ CreateSymbolicLink ( link , realPath ) ;
130+ }
131+ }
132+ }
133+
134+ // Write the final path
135+ stream . WriteLine ( finalPath ) ;
136+
137+ // Do all of this for each sub entry and so on.
138+ SubEntries . ForEach ( x => x . BuildOutput ( stream , settings ) ) ;
139+ }
140+ }
141+
142+ private void CreateSymbolicLink ( string linkPath , string realPath )
143+ {
144+ Process process = new Process ( ) ;
145+ ProcessStartInfo startInfo = new ProcessStartInfo ( ) ;
146+ startInfo . WindowStyle = ProcessWindowStyle . Hidden ;
147+
148+ if ( Environment . OSVersion . Platform == PlatformID . Unix || Environment . OSVersion . Platform == PlatformID . MacOSX )
149+ {
150+ startInfo . FileName = "ln" ;
151+
152+ startInfo . Arguments = string . Format ( "-s {0} {1}" , realPath , linkPath ) ;
153+ }
154+ else
155+ {
156+ startInfo . FileName = "cmd.exe" ;
157+
158+ string linktype ;
159+ if ( IsUserAnAdmin ( ) ) linktype = "D" ;
160+ else linktype = "J" ;
161+
162+ startInfo . Arguments = string . Format ( "/C mklink /{0} {1} {2}" , linktype , linkPath , realPath ) ;
163+ }
164+
165+ process . StartInfo = startInfo ;
166+
167+ process . Start ( ) ;
168+
169+ // Wait for finish, else this will get called several times in a tight loop
170+ process . WaitForExit ( ) ;
171+ }
172+
173+
174+ [ DllImport ( "shell32.dll" , SetLastError = true ) ]
175+ [ return : MarshalAs ( UnmanagedType . Bool ) ]
176+ static extern bool IsUserAnAdmin ( ) ;
177+
178+ public void LoadSubdirectories ( Settings settings )
179+ {
180+ if ( FolderPath == settings . SaveInLinksFolderPath || FolderPath == null || FolderPath == string . Empty )
181+ {
182+ return ;
183+ }
184+
185+ try
186+ {
187+ string [ ] subdirectoryEntries = Directory . GetDirectories ( FolderPath ) ;
188+
189+ if ( subdirectoryEntries . Length > 0 )
190+ {
191+ for ( int i = 0 ; i < subdirectoryEntries . Length ; i ++ )
192+ {
193+ SubEntries . Add ( new MenuEntry ( ) { FolderPath = subdirectoryEntries [ i ] , Name = Path . GetFileName ( subdirectoryEntries [ i ] ) , Deepness = this . Deepness + 1 } ) ;
194+ SubEntries [ i ] . LoadSubdirectories ( settings ) ;
195+ }
196+
197+ if ( ! settings . DisableLinkBack )
198+ {
199+ string insertName = settings . BackLinkStartText + this . Name + settings . BackLinkEndText ;
200+
201+
202+ if ( settings . IncludeNameInBackLink )
203+ insertName = settings . BackLinkStartText + this . Name + settings . BackLinkEndText ;
204+ else
205+ insertName = settings . BackLinkStartText + settings . BackLinkEndText ;
206+
207+ MenuEntry entry = new MenuEntry ( ) { FolderPath = this . FolderPath , Name = insertName , Deepness = this . Deepness + 1 } ;
208+
209+ if ( settings . LinkBackOnBottom )
210+ SubEntries . Add ( entry ) ;
211+ else
212+ SubEntries . Insert ( 0 , entry ) ;
213+ }
214+ }
215+ else if ( ! settings . DisableLinkBack && settings . ForceLinkBack && Deepness > 0 )
216+ {
217+ string insertName = settings . BackLinkStartText + this . Name + settings . BackLinkEndText ;
218+
219+
220+ if ( settings . IncludeNameInBackLink )
221+ insertName = settings . BackLinkStartText + this . Name + settings . BackLinkEndText ;
222+ else
223+ insertName = settings . BackLinkStartText + settings . BackLinkEndText ;
224+
225+ MenuEntry entry = new MenuEntry ( ) { FolderPath = this . FolderPath , Name = insertName , Deepness = this . Deepness + 1 } ;
226+
227+
228+ SubEntries . Add ( entry ) ;
229+ }
230+ }
231+ catch ( UnauthorizedAccessException )
232+ {
233+ return ;
234+ }
235+ catch ( DirectoryNotFoundException ) // Probably a broken link within the path, in this context..
236+ {
237+ return ;
238+ }
239+ //TODO: Should probably give some feedback.
240+ catch ( PathTooLongException )
241+ {
242+ return ;
243+ }
244+
245+ }
246+
247+ public override string ToString ( )
248+ {
249+ if ( IsSeperator )
250+ {
251+ return "###Seperator###" ;
252+ }
253+ else if ( IsCategory )
254+ {
255+ return string . Format ( "Category: {0}" , Name ) ;
256+ }
257+ else
258+ {
259+ if ( Comment != string . Empty && Comment != null )
260+ {
261+ return string . Format ( "{0} (Name: {1}) (Comment: {2})" , FolderPath , Name , Comment ) ;
262+ }
263+ else
264+ {
265+ return string . Format ( "{0} (Name: {1})" , FolderPath , Name ) ;
266+ }
267+ }
268+ }
269+ }
270+ }
0 commit comments