Browse Source

Merge branch 'mc-4.6'

Patrick Winnertz 16 years ago
parent
commit
7abcb66aba
5 changed files with 248 additions and 58 deletions
  1. 5 0
      ChangeLog
  2. 6 1
      mhl/string.h
  3. 177 57
      vfs/fish.c
  4. 58 0
      vfs/utilvfs.c
  5. 2 0
      vfs/utilvfs.h

+ 5 - 0
ChangeLog

@@ -4,6 +4,11 @@
 	* src/main.c, src/panel.h, src/screen.c, src/setup.c:
 	  - Add support for showing executables at first in the panel view
 
+2009-01-25 Patrick Winnertz <winnie@debian.org> & Sergei Trofimovich  <slyfox@inbox.ru>
+
+	* mhl/string.h, vfs/fish.c, vfs/utilvfs.c, vfs/utilvfs.h: Reworked fish code
+	  so that symlinks and files which special characters works now
+
 2009-01-25 Enrico Weigelt <weigelt@metux.de>
 
 	* edit/editcmd.c, src/cmd.c, src/ext.c, src/history.h:

+ 6 - 1
mhl/string.h

@@ -9,6 +9,11 @@
 #define mhl_str_ndup(str,len)	((str ? strndup(str,len) : strdup("")))
 #define mhl_str_len(str)	((str ? strlen(str) : 0))
 
+static inline char * mhl_str_dup_range(const char * s_start, const char * s_bound)
+{
+    return mhl_str_ndup(s_start, s_bound - s_start);
+}
+
 static inline char* mhl_str_trim(char* str)
 {
     if (!str) return NULL;	// NULL string ?! bail out.
@@ -121,4 +126,4 @@ static inline char* mhl_str_reverse(char* ptr)
     return ptr;
 }
 
-#endif
+#endif /* __MHL_STRING_H */

+ 177 - 57
vfs/fish.c

@@ -50,6 +50,9 @@
 #include "tcputil.h"
 #include "../src/unixcompat.h"
 #include "fish.h"
+#include "../mhl/memory.h"
+#include "../mhl/string.h"
+#include "../mhl/escape.h"
 
 int fish_directory_timeout = 900;
 
@@ -358,33 +361,100 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
     char *quoted_path;
     int reply_code;
 
+#if 0
+    /*
+     * Simple FISH debug interface :]
+     */
+    if (!(MEDATA->logfile))
+    {
+        MEDATA->logfile = fopen("/tmp/mc-FISH.sh", "w");
+    }
+#endif // 0
+
     logfile = MEDATA->logfile;
 
     print_vfs_message(_("fish: Reading directory %s..."), remote_path);
 
     gettimeofday(&dir->timestamp, NULL);
     dir->timestamp.tv_sec += fish_directory_timeout;
-    quoted_path = name_quote (remote_path, 0);
+    quoted_path = mhl_shell_escape_dup (remote_path);
     fish_command (me, super, NONE,
-	    "#LIST /%s\n"
-	    "if ls -1 /%s >/dev/null 2>&1 ;\n"
-	    "then\n"
-	    "ls -lLan /%s 2>/dev/null | grep '^[^cbt]' | (\n"
-	      "while read p l u g s m d y n; do\n"
-	        "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
-	      "done\n"
-	    ")\n"
-	    "ls -lan /%s 2>/dev/null | grep '^[cb]' | (\n"
-	      "while read p l u g a i m d y n; do\n"
-	        "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
-	      "done\n"
-	    ")\n"
-	    "echo '### 200'\n"
-	    "else\n"
-	    "echo '### 500'\n"
-	    "fi\n",
-	    remote_path, quoted_path, quoted_path, quoted_path);
-    g_free (quoted_path);
+		"#LIST /%s\n"
+		"if `perl -v > /dev/null 2>&1` ; then\n"
+		  "perl -e '\n"
+		   "use strict;\n"
+		   "use POSIX;\n"
+		   "use Fcntl;\n"
+		   "use POSIX \":fcntl_h\"; #S_ISLNK was here until 5.6\n"
+		   "import Fcntl \":mode\" unless defined &S_ISLNK; #and is now here\n"
+		   "my $dirname = $ARGV[0];\n"
+		   "if (opendir ( DIR, $dirname )) {\n"
+		   "while( (my $filename = readdir(DIR))){\n"
+		   "my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat(\"$dirname/$filename\");\n"
+		   "my $mloctime= scalar localtime $mtime;\n"
+		   "my $shell_escape_regex= s/([;<>\\*\\|`&\\$!#\\(\\)\\[\\]\\{\\}:'\\''\"\\ \\\\])/\\\\$1/g;\n"
+		   "my $e_filename = $filename;\n"
+		   "$e_filename =~ $shell_escape_regex;\n"
+		   "if (S_ISLNK($mode) ) {\n"
+		   "my $linkname = readlink (\"$dirname/$filename\");\n"
+		   "$linkname =~ $shell_escape_regex;\n"
+		   "\n"
+			  "printf(\"R%%o %%o $uid.$gid\\n"
+				    "S$size\\n"
+				    "d$mloctime\\n"
+				    ":\\\"$e_filename\\\" -> \\\"$linkname\\\"\\n"
+				    "\\n\", S_IMODE($mode), S_IFMT($mode));\n"
+		   "} else {\n"
+			  "printf(\"R%%o %%o $uid.$gid\\n"
+				    "S$size\\n"
+				    "d$mloctime\\n"
+				    ":\\\"$e_filename\\\"\\n"
+				    "\\n\", S_IMODE($mode), S_IFMT($mode));\n"
+		   "}}\n"
+		   "printf(\"### 200\\n\");\n"
+		   "closedir(DIR);\n"
+		   "} else {\n"
+		    "printf(\"### 500\\n\");\n"
+		   "}\n"
+		   "exit 0\n"
+		   "' /%s ||\n" /* ARGV[0] - path to browse */
+		   "    echo '### 500'\n" /* do not hang if perl is to eval it */
+		"elif `ls -1 /%s >/dev/null 2>&1` ; then\n"
+		  "if `ls -Q /%s >/dev/null 2>&1`; then\n"
+			  "LSOPT=\"-Qlan\";\n"
+			  "ADD=0;\n"
+		  "else\n"
+			  "LSOPT=\"-lan\";\n"
+			  "ADD=1;\n"
+		  "fi\n"
+		  "ls $LSOPT \"/%s\" 2>/dev/null | grep '^[^cbt]' | (\n"
+			  "while read p l u g s m d y n; do\n"
+				  "if [ $ADD  = 0 ]; then\n"
+					  "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
+				  "elif `sed --version >/dev/null 2>&1` ; then\n"
+					  "file=`echo $n | sed -e 's#^\\(.*\\) -> \\(.*\\)#\\1\" -> \"\\2#'`\n"
+					  "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$file\"\n\"\n"
+				  "else\n"
+					  "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$n\"\n\"\n"
+				  "fi\n"
+			  "done )\n"
+		  "ls $LSOPT /%s 2>/dev/null | grep '^[cb]' | (\n"
+			  "while read p l u g a i m d y n; do\n"
+				  "if [ $ADD = 0 ]; then\n"
+					  "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
+				  "elif `sed --version >/dev/null 2>&1` ; then\n"
+					  "file=`echo $n | sed -e 's#^\\(.*\\) -> \\(.*\\)#\\1\" -> \"\\2#'`\n"
+					  "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$file\"\n\"\n"
+				  "else\n"
+					  "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$n\"\n\"\n"
+				  "fi\n"
+			  "done)\n"
+		  "echo '### 200'\n"
+	"else\n"
+		  "echo '### 500'\n"
+	"fi\n",
+	    quoted_path, quoted_path, quoted_path, quoted_path, quoted_path, quoted_path);
+    mhl_mem_free (quoted_path);
     ent = vfs_s_generate_entry(me, NULL, dir, 0);
     while (1) {
 	int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr); 
@@ -412,11 +482,61 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
 
 	switch(buffer[0]) {
 	case ':': {
-		      if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, ".."))
-			  break;  /* We'll do . and .. ourself */
-		      ent->name = g_strdup(buffer+1); 
-		      break;
-	          }
+	    char *data_start = buffer+1;
+	    char *filename = data_start;
+	    char *linkname = data_start;
+	    char *filename_bound = filename + strlen(filename);
+	    char *linkname_bound = filename_bound;
+	    if (!strcmp(data_start, "\".\"") || !strcmp(data_start, "\"..\""))
+			break;  /* We'll do "." and ".." ourselves */
+
+		if (S_ISLNK(ST.st_mode)) {
+			// we expect: "escaped-name" -> "escaped-name"
+			//     -> cannot occur in filenames,
+			//     because it will be escaped to -\>
+
+			if (*filename == '"')
+				++filename;
+
+			linkname = strstr(filename, "\" -> \"");
+			if (!linkname)
+			{
+				/* broken client, or smth goes wrong */
+				linkname = filename_bound;
+				if (filename_bound > filename
+				    && *(filename_bound - 1) == '"')
+					--filename_bound; // skip trailing "
+			}
+			else
+			{
+				filename_bound = linkname;
+				linkname += 6; // strlen ("\" -> \"")
+				if (*(linkname_bound - 1) == '"')
+					--linkname_bound; // skip trailing "
+			}
+
+			ent->name = mhl_str_dup_range(filename, filename_bound);
+			mhl_shell_unescape_buf(ent->name);
+
+			ent->ino->linkname = mhl_str_dup_range(linkname, linkname_bound);
+			mhl_shell_unescape_buf(ent->ino->linkname);
+		} else {
+			// we expect: "escaped-name"
+			if (filename_bound - filename > 2)
+			{
+				// there is at least 2 "
+				// and we skip them
+				if (*filename == '"')
+					++filename;
+				if (*(filename_bound - 1) == '"')
+					--filename_bound;
+			}
+
+			ent->name = mhl_str_dup_range(filename, filename_bound);
+			mhl_shell_unescape_buf(ent->name);
+		}
+		break;
+	}
 	case 'S':
 #ifdef HAVE_ATOLL
 	    ST.st_size = (off_t) atoll (buffer+1);
@@ -426,11 +546,14 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
 	    break;
 	case 'P': {
 	    size_t skipped;
-
-	    if (vfs_parse_filemode (buffer + 1, &skipped, &ST.st_mode)) {
-		if (S_ISLNK(ST.st_mode))
-		    ST.st_mode = 0;
-	    }
+	    vfs_parse_filemode (buffer + 1, &skipped, &ST.st_mode);
+	    break;
+	}
+	case 'R': {
+	    // raw filemode:
+	    // we expect: Roctal-filemode octal-filetype uid.gid
+	    size_t skipped;
+	    vfs_parse_raw_filemode (buffer + 1, &skipped, &ST.st_mode);
 	    break;
 	}
 	case 'd': {
@@ -456,8 +579,6 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
 		      ST.st_rdev = makedev (maj, min);
 #endif
 	          }
-	case 'L': ent->ino->linkname = g_strdup(buffer+1);
-	          break;
 	}
     }
     
@@ -528,8 +649,8 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
      *	algorithm for file appending case, therefore just "dd" is used for it.
      */
 
-    print_vfs_message(_("fish: store %s: sending command..."), name );
-    quoted_name = name_quote (name, 0);
+    quoted_name = mhl_shell_escape_dup(name);
+    print_vfs_message(_("fish: store %s: sending command..."), quoted_name );
 
     /* FIXME: File size is limited to ULONG_MAX */
     if (!fh->u.fish.append)
@@ -541,7 +662,7 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
 		 "(\n"
 		   "head -c %lu -q - || echo DD >&3\n"
 		 ") 2>/dev/null | (\n"
-		   "cat > \"$file\"\n"
+		   "cat > $file\n"
 		   "cat > /dev/null\n"
 		 ")`; [ \"$res\" = DD ] && {\n"
 			"> \"$file\"\n"
@@ -553,7 +674,7 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
 			"    rest=`expr $rest - $n`\n"
 			"done\n"
 		 "}; echo '### 200'\n",
-		 (unsigned long) s.st_size, name,
+		 (unsigned long) s.st_size, quoted_name,
 		 quoted_name, (unsigned long) s.st_size,
 		 (unsigned long) s.st_size);
     else
@@ -566,14 +687,13 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
 			"while [ $rest -gt 0 ]\n"
 			"do\n"
 			"    cnt=`expr \\( $rest + 255 \\) / 256`\n"
-			"    n=`dd bs=256 count=$cnt | tee -a \"$file\" | wc -c`\n"
+			"    n=`dd bs=256 count=$cnt | tee -a $file | wc -c`\n"
 			"    rest=`expr $rest - $n`\n"
 			"done\n"
 		 "}; echo '### 200'\n",
-		 (unsigned long) s.st_size, name,
+		 (unsigned long) s.st_size, quoted_name,
 		 quoted_name, (unsigned long) s.st_size);
 
-    g_free (quoted_name);
     if (n != PRELIM) {
 	close (h);
         ERRNOR(E_REMOTE, -1);
@@ -606,12 +726,14 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
 			  (unsigned long) s.st_size);
     }
     close(h);
+    mhl_mem_free(quoted_name);
     if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
         ERRNOR (E_REMOTE, -1);
     return 0;
 error_return:
     close(h);
     fish_get_reply(me, SUP.sockr, NULL, 0);
+    mhl_mem_free(quoted_name);
     return -1;
 }
 
@@ -625,9 +747,7 @@ fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
     name = vfs_s_fullpath (me, fh->ino);
     if (!name)
 	return 0;
-    quoted_name = name_quote (name, 0);
-    g_free (name);
-    name = quoted_name;
+    quoted_name = mhl_shell_escape_dup(name);
     fh->u.fish.append = 0;
 
     /*
@@ -642,16 +762,16 @@ fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
 		"then\n"
 		"ls -ln /%s 2>/dev/null | (\n"
 		  "read p l u g s r\n"
-		  "echo \"$s\"\n"
+		  "echo $s\n"
 		")\n"
 		"echo '### 100'\n"
 		"cat /%s\n"
 		"echo '### 200'\n"
 		"else\n"
-		"echo '### 500'\n" 
+		"echo '### 500'\n"
 		"fi\n",
-		name, name, name, name );
-    g_free (name);
+		quoted_name, quoted_name, quoted_name, quoted_name );
+    g_free (quoted_name);
     if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
     fh->linear = LS_LINEAR_OPEN;
     fh->u.fish.got = 0;
@@ -766,7 +886,7 @@ fish_send_command(struct vfs_class *me, struct vfs_s_super *super, const char *c
 	g_free (mpath); \
 	return -1; \
     } \
-    rpath = name_quote (crpath, 0); \
+    rpath = mhl_shell_escape_dup(crpath); \
     g_free (mpath);
 
 #define POSTFIX(flags) \
@@ -778,7 +898,7 @@ fish_chmod (struct vfs_class *me, const char *path, int mode)
 {
     PREFIX
     g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
-				 "chmod %4.4o \"/%s\" 2>/dev/null\n"
+				 "chmod %4.4o /%s 2>/dev/null\n"
 				 "echo '### 000'\n", 
 	    mode & 07777, rpath,
 	    mode & 07777, rpath);
@@ -801,13 +921,13 @@ static int fish_##name (struct vfs_class *me, const char *path1, const char *pat
 	g_free (mpath2); \
 	return -1; \
     } \
-    rpath1 = name_quote (crpath1, 0); \
+    rpath1 = mhl_shell_escape_dup (crpath1); \
     g_free (mpath1); \
-    rpath2 = name_quote (crpath2, 0); \
+    rpath2 = mhl_shell_escape_dup (crpath2); \
     g_free (mpath2); \
     g_snprintf(buf, sizeof(buf), string "\n", rpath1, rpath2, rpath1, rpath2); \
-    g_free (rpath1); \
-    g_free (rpath2); \
+    mhl_mem_free (rpath1); \
+    mhl_mem_free (rpath2); \
     return fish_send_command(me, super2, buf, OPT_FLUSH); \
 }
 
@@ -822,13 +942,13 @@ static int fish_symlink (struct vfs_class *me, const char *setto, const char *pa
 {
     char *qsetto;
     PREFIX
-    qsetto = name_quote (setto, 0);
+    qsetto = mhl_shell_escape_dup (setto);
     g_snprintf(buf, sizeof(buf),
             "#SYMLINK %s /%s\n"
 	    "ln -s %s /%s 2>/dev/null\n"
 	    "echo '### 000'\n",
 	    qsetto, rpath, qsetto, rpath);
-    g_free (qsetto);
+    mhl_mem_free (qsetto);
     POSTFIX(OPT_FLUSH);
 }
 
@@ -850,7 +970,7 @@ fish_chown (struct vfs_class *me, const char *path, int owner, int group)
     {
 	PREFIX
 	g_snprintf (buf, sizeof(buf),
-    	    "#CHOWN /%s /%s\n"
+    	    "#CHOWN %s /%s\n"
 	    "chown %s /%s 2>/dev/null\n"
 	    "echo '### 000'\n", 
 	    sowner, rpath,
@@ -858,8 +978,8 @@ fish_chown (struct vfs_class *me, const char *path, int owner, int group)
 	fish_send_command (me, super, buf, OPT_FLUSH); 
 	/* FIXME: what should we report if chgrp succeeds but chown fails? */
 	g_snprintf (buf, sizeof(buf),
-            "#CHGRP /%s /%s\n"
-	    "chgrp %s /%s 2>/dev/null\n"
+            "#CHGRP /%s \"/%s\"\n"
+	    "chgrp %s \"/%s\" 2>/dev/null\n"
 	    "echo '### 000'\n", 
 	    sgroup, rpath,
 	    sgroup, rpath);

+ 58 - 0
vfs/utilvfs.c

@@ -539,6 +539,64 @@ vfs_parse_filemode (const char *s, size_t *ret_skipped,
     return TRUE;
 }
 
+gboolean
+vfs_parse_raw_filemode (const char *s, size_t *ret_skipped,
+		    mode_t *ret_mode)
+{
+    const char *p;
+    mode_t remote_type = 0, local_type, perms = 0;
+
+    p = s;
+
+    // isoctal
+    while(*p >= '0' && *p <= '7')
+    {
+	perms *= 010;
+	perms += (*p - '0');
+	++p;
+    }
+
+    if (*p++ != ' ')
+	return FALSE;
+
+    while(*p >= '0' && *p <= '7')
+    {
+	remote_type *= 010;
+	remote_type += (*p - '0');
+	++p;
+    }
+
+    if (*p++ != ' ')
+	return FALSE;
+
+    /* generated with:
+        $ perl -e 'use Fcntl ":mode";
+                   my @modes = (S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFREG);
+                   foreach $t (@modes) { printf ("%o\n", $t); };'
+        TODO: S_IFDOOR, S_IFIFO, S_IFSOCK (if supported by os)
+              (see vfs_parse_filetype)
+     */
+
+    switch (remote_type)
+    {
+	case 020000:
+	    local_type = S_IFCHR; break;
+	case 040000:
+	    local_type = S_IFDIR; break;
+	case 060000:
+	    local_type = S_IFBLK; break;
+	case 0120000:
+	    local_type = S_IFLNK; break;
+	case 0100000:
+	default:// don't know what is it
+	    local_type = S_IFREG; break;
+    }
+
+    *ret_skipped = p - s;
+    *ret_mode = local_type | perms;
+    return TRUE;
+}
+
 /* This function parses from idx in the columns[] array */
 int
 vfs_parse_filedate (int idx, time_t *t)

+ 2 - 0
vfs/utilvfs.h

@@ -24,6 +24,8 @@ gboolean vfs_parse_fileperms (const char *s, size_t *ret_skipped,
 			      mode_t *ret_perms);
 gboolean vfs_parse_filemode (const char *s, size_t *ret_skipped,
 			     mode_t *ret_mode);
+gboolean vfs_parse_raw_filemode (const char *s, size_t *ret_skipped,
+			     mode_t *ret_mode);
 
 int vfs_parse_ls_lga (const char *p, struct stat *s, char **filename,
 		      char **linkname);