@@ -6,6 +6,7 @@ package cmd
66import (
77 "context"
88 "fmt"
9+ "os"
910 "path/filepath"
1011 "strings"
1112 "time"
@@ -63,6 +64,16 @@ Examples:
6364 return err
6465 }
6566
67+ // Get recursive and includeHidden flags
68+ recursive , _ := cmd .Flags ().GetBool ("recursive" )
69+ includeHidden , _ := cmd .Flags ().GetBool ("include-hidden" )
70+
71+ // Expand directories if needed
72+ filePairs , err = expandDirectories (filePairs , recursive , includeHidden )
73+ if err != nil {
74+ return err
75+ }
76+
6677 // Get tags from flags
6778 tagsFlag , err := cmd .Flags ().GetString ("tags" )
6879 if err != nil {
@@ -138,15 +149,61 @@ Examples:
138149 fmt .Printf ("Processing: %s\n " , pair .Source )
139150 }
140151
141- // Check if file exists and that it is not a directory or symlink
142- fileInfo , err := fs .VerifyFileAndReturnFileInfo (pair .Source )
152+ // Determine path type and handle accordingly
153+ fileInfo , pathType , err := fs .GetPathInfo (pair .Source )
143154 if err != nil {
144155 errorMsg := fmt .Sprintf ("✗ %s: %v" , filepath .Base (pair .Source ), err )
145156 fmt .Println (errorMsg )
146157 failedFiles = append (failedFiles , errorMsg )
147158 continue
148159 }
149160
161+ // Handle different path types
162+ var actualSourcePath string
163+ switch pathType {
164+ case fs .PathTypeFile :
165+ // Regular file - use as is
166+ actualSourcePath = pair .Source
167+
168+ case fs .PathTypeSymlink :
169+ // Resolve symlink and verify target is a regular file
170+ targetPath , targetInfo , targetType , err := fs .ResolveSymlink (pair .Source )
171+ if err != nil {
172+ errorMsg := fmt .Sprintf ("✗ %s: %v" , filepath .Base (pair .Source ), err )
173+ fmt .Println (errorMsg )
174+ failedFiles = append (failedFiles , errorMsg )
175+ continue
176+ }
177+
178+ if targetType != fs .PathTypeFile {
179+ errorMsg := fmt .Sprintf ("✗ %s: symlink target is not a regular file" , filepath .Base (pair .Source ))
180+ fmt .Println (errorMsg )
181+ failedFiles = append (failedFiles , errorMsg )
182+ continue
183+ }
184+
185+ // Use the resolved target path for processing
186+ actualSourcePath = targetPath
187+ fileInfo = targetInfo
188+
189+ if verbose {
190+ fmt .Printf (" Resolved symlink: %s → %s\n " , pair .Source , targetPath )
191+ }
192+
193+ case fs .PathTypeDir :
194+ // Directories should have been expanded already
195+ errorMsg := fmt .Sprintf ("✗ %s: unexpected directory in processing loop" , filepath .Base (pair .Source ))
196+ fmt .Println (errorMsg )
197+ failedFiles = append (failedFiles , errorMsg )
198+ continue
199+
200+ default :
201+ errorMsg := fmt .Sprintf ("✗ %s: unsupported file type" , filepath .Base (pair .Source ))
202+ fmt .Println (errorMsg )
203+ failedFiles = append (failedFiles , errorMsg )
204+ continue
205+ }
206+
150207 // Get file size in human-readable format
151208 sizeInBytes := fileInfo .Size ()
152209 sizeReadable := util .HumanReadableSize (sizeInBytes )
@@ -163,7 +220,7 @@ Examples:
163220
164221 // Process the file and store chunks - using the appropriate chunking function
165222 var chunkRefs []config.ChunkRef
166- chunkRefs , err = chunk .ChunkFile (ctx , pair . Source , chunkSize , vaultRoot , passphrase , progressMgr )
223+ chunkRefs , err = chunk .ChunkFile (ctx , actualSourcePath , chunkSize , vaultRoot , passphrase , progressMgr )
167224
168225 if err != nil {
169226 errorMsg := fmt .Sprintf ("✗ %s: chunking failed - %v" , filepath .Base (pair .Source ), err )
@@ -357,15 +414,90 @@ func parseFileArguments(args []string) ([]FilePair, error) {
357414 return pairs , nil
358415}
359416
417+ // expandDirectories expands directories into file pairs if recursive flag is set
418+ func expandDirectories (pairs []FilePair , recursive bool , includeHidden bool ) ([]FilePair , error ) {
419+ var expandedPairs []FilePair
420+
421+ for _ , pair := range pairs {
422+ // Get path info to determine type
423+ fileInfo , pathType , err := fs .GetPathInfo (pair .Source )
424+ if err != nil {
425+ return nil , err
426+ }
427+
428+ switch pathType {
429+ case fs .PathTypeFile :
430+ // Regular file - add as is
431+ expandedPairs = append (expandedPairs , pair )
432+
433+ case fs .PathTypeSymlink :
434+ // Symlink - will be handled in processing loop, add as is
435+ expandedPairs = append (expandedPairs , pair )
436+
437+ case fs .PathTypeDir :
438+ // Directory - expand if recursive, otherwise error
439+ if ! recursive {
440+ return nil , fmt .Errorf ("'%s' is a directory. Use --recursive flag to add directories" , pair .Source )
441+ }
442+
443+ // Walk the directory tree
444+ err := filepath .WalkDir (pair .Source , func (path string , d os.DirEntry , err error ) error {
445+ if err != nil {
446+ return err
447+ }
448+
449+ // Skip hidden files/directories if includeHidden is false
450+ if fs .ShouldSkipHidden (d .Name (), includeHidden ) {
451+ if d .IsDir () {
452+ return filepath .SkipDir
453+ }
454+ return nil
455+ }
456+
457+ // Only add regular files and symlinks
458+ if ! d .IsDir () {
459+ // Compute relative path from source directory
460+ relPath , err := filepath .Rel (pair .Source , path )
461+ if err != nil {
462+ return fmt .Errorf ("failed to compute relative path: %v" , err )
463+ }
464+
465+ // Preserve directory structure in destination
466+ destPath := filepath .Join (pair .Destination , relPath )
467+
468+ expandedPairs = append (expandedPairs , FilePair {
469+ Source : path ,
470+ Destination : destPath ,
471+ })
472+ }
473+
474+ return nil
475+ })
476+
477+ if err != nil {
478+ return nil , fmt .Errorf ("error walking directory '%s': %v" , pair .Source , err )
479+ }
480+
481+ default :
482+ return nil , fmt .Errorf ("'%s' is not a regular file, directory, or symlink" , pair .Source )
483+ }
484+
485+ _ = fileInfo // fileInfo might be used for verbose output later
486+ }
487+
488+ return expandedPairs , nil
489+ }
490+
360491func init () {
361492 rootCmd .AddCommand (addCmd )
362493
363494 // Optional flags for the add command
364495 addCmd .Flags ().BoolP ("force" , "f" , false , "Force add without confirmation" )
365496 addCmd .Flags ().StringP ("tags" , "t" , "" , "Comma-separated tags to associate with the file" )
366497 addCmd .Flags ().StringP ("passphrase-value" , "p" , "" , "Passphrase for encrypted vault (if required)" )
498+ addCmd .Flags ().BoolP ("recursive" , "r" , false , "Recursively add directories" )
499+ addCmd .Flags ().BoolP ("include-hidden" , "H" , false , "Include hidden files and directories" )
367500}
368501
369- //TODO: Add support for directories and symlinks
370502//TODO: Need to check how symlinks will be handled
371503//TODO: Interactive mode with real time progress indicators
0 commit comments