lynx-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

lynx-dev Dired for DOS (PATCH)


From: Doug Kaufman
Subject: lynx-dev Dired for DOS (PATCH)
Date: Mon, 31 May 1999 12:24:23 -0700 (PDT)

Here is a version of the patch for dired support in the DOS port
that I have been working on. It still has some problems that I hope
can be fixed by someone who knows C. This is modified from the patch
submitted by Igor Poretsky on 21 April 1999. I don't know that it is
yet ready to be integrated with the main distribution (after release,
of course), but I don't think that I can fix the problems.

Notes:

1. Since this is DJGPP based, it may be incompatible with a called
program that uses a WATCOM DOS extender. I think I had this problem
with a precompiled binary for zip. The current zip binary doesn't have
this problem.

2. The key to making this work with DJGPP is the __sysflags = 0x501d
in LYMain.c, since this lets the system() call handle multiple
commands on one line and allows the cd command to work across disks.

3. I think I was conservative in redoing the FMT_? definitions in
LYLocal.c by ifdef'ing for DJGPP. Someone more familiar with other
systems might look to see if the rewritten definitions would also work
with unix.

4. The line adding the call to wwwName in LYLocal.c should have no
effect in unix, but will in VMS. Does dired work in VMS, and if so
does this change break it? If it does, this needs to be ifdef'd to
DJGPP.

5. I didn't look at Igor's changes to LYMainLoop.c as regards to the
HTUncache calls. He said he wasn't sure about this.

6. Remember that I am not a C programmer. It would be good for someone
to check the patch for potential memory handling problems, since I
don't know much about this.

7. This patch was made against a fresh pre.5.

8. This was tested with Info-Zip's zip and unzip, GNU tar, gzip,
comp430d, PKZIP, and PKUNZIP. This was only tested when built with
slang. I don't see where PDCurses is likely to cause a problem, however.

Problems:

1. To make this work, I made lynx_edit_mode = TRUE in LYMainLoop.c.
This does not seem to be the correct solution. It seems that this
should be handled by the code in HTAccess.c, but I don't understand
how this works. If lynx_edit_mode isn't true, the dired menu doesn't
appear with the "f" invocation of LYK_DIRED_MENU.

Bugs:

1. I'll try to explain this briefly, but this bug very reproducible.
If a program is invoked from the dired menu which needs to interact
with the user, the system sometimes freezes and has to be rebooted.
For example, a call to unzip a zip archive where the files already
exist has zip ask if the file should be overwritten. If done initially
after lynx is started, no keystrokes are accepted and the system
always hangs. If, however, any file has been moved or had its name
changed via the dired menu, then the above problem doesn't occur. I
have tried to see what changes after the name change that fixes this
problem, but I am stumped. Gzip and unzip seem to have this problem,
but PKUNZIP doesn't.

                                  Doug

                                  
--- lynx2-8-2/src/LYLocal.c     Wed May 12 19:06:06 1999
+++ lynx2-8-2/src/LYLocal.c.new Mon May 31 11:20:34 1999
@@ -105,7 +105,9 @@
 #define DE_TAG     1
 #define DE_DIR     2
 #define DE_FILE    3
+#ifdef S_IFLNK
 #define DE_SYMLINK 4
+#endif /* S_IFLNK */
     char *sfx;
     char *link;
     char *rest;
@@ -133,8 +135,10 @@
 "(of current selection)", "LYNXDIRED://MODIFY_NAME%p",         NULL },
 { DE_DIR,            "", "Modify Directory Name",
 "(of current selection)", "LYNXDIRED://MODIFY_NAME%p",         NULL },
+#ifdef S_IFLNK
 { DE_SYMLINK,        "", "Modify Name",
 "(of selected symbolic link)", "LYNXDIRED://MODIFY_NAME%p",    NULL },
+#endif  /* S_IFLNK */
 
 #ifdef OK_PERMIT
 { DE_FILE,           "", "Modify File Permissions",
@@ -147,15 +151,19 @@
 "(of selected file)"   , "LYNXDIRED://MODIFY_LOCATION%p",      NULL },
 { DE_DIR,            "", "Change Location",
 "(of selected directory)", "LYNXDIRED://MODIFY_LOCATION%p",    NULL },
+#ifdef S_IFLNK
 { DE_SYMLINK,        "", "Change Location",
 "(of selected symbolic link)", "LYNXDIRED://MODIFY_LOCATION%p", NULL },
+#endif /* S_IFLNK */
 
 { DE_FILE,           "", "Remove File",
    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",       NULL },
 { DE_DIR,            "", "Remove Directory",
    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",       NULL },
+#ifdef S_IFLNK
 { DE_SYMLINK,        "", "Remove Symbolic Link",
    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",       NULL },
+#endif /* S_IFLNK */
 
 #if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
 { DE_FILE,           "", "UUDecode",
@@ -163,6 +171,10 @@
 #endif /* OK_UUDECODE && !ARCHIVE_ONLY */
 
 #if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
+#ifdef __DJGPP__
+{ DE_FILE,     ".taz", "Expand",
+   "(current selection)", "LYNXDIRED://UNTAR_Z%p",             NULL },
+#endif /* __DJGPP__ */
 { DE_FILE,     ".tar.Z", "Expand",
    "(current selection)", "LYNXDIRED://UNTAR_Z%p",             NULL },
 #endif /* OK_TAR && !ARCHIVE_ONLY */
@@ -176,6 +188,10 @@
 #endif /* OK_TAR && OK_GZIP && !ARCHIVE_ONLY */
 
 #ifndef ARCHIVE_ONLY
+#ifdef __DJGPP__
+{ DE_FILE,         ".z", "Uncompress",
+   "(current selection)", "LYNXDIRED://DECOMPRESS%p",          NULL },
+#endif /* __DJGPP__ */
 { DE_FILE,         ".Z", "Uncompress",
    "(current selection)", "LYNXDIRED://DECOMPRESS%p",          NULL },
 #endif /* ARCHIVE_ONLY */
@@ -588,10 +604,19 @@
        /*
         *  Do not allow the user to also change the location at this time.
         */
+#ifdef __DJGPP__
+       if ((strchr(tmpbuf, '/') != NULL) || (strchr(tmpbuf, '\\') != NULL)){
+           HTAlert(gettext("Illegal character \"/\" or \"\\\" found! Request 
ignored."));
+#else
        if (strchr(tmpbuf, '/') != NULL) {
            HTAlert(gettext("Illegal character \"/\" found! Request ignored."));
+#endif /* __DJGPP__ */
        } else if (strlen(tmpbuf) &&
+#ifdef __DJGPP__
+                  (cp = strrchr(testpath, '\\')) != NULL) {
+#else
                   (cp = strrchr(testpath, '/')) != NULL) {
+#endif /* __DJGPP__ */
            strcpy(savepath,testpath);
            *(++cp) = '\0';
            strcpy(newpath,testpath);
@@ -657,17 +682,25 @@
            strcat(newpath, (tmpbuf + 1));
            strcpy(tmpbuf, newpath);
        }
-       if (!LYIsPathSep(tmpbuf[0])) {
+#ifdef __DJGPP__
+       if (LYIsPathSep(tmpbuf[0]) || (isalpha(tmpbuf[0]) && (tmpbuf[1] == ':') 
&& LYIsPathSep(tmpbuf[2]))) {
+#else
+       if (LYIsPathSep(tmpbuf[0])) {
+#endif /* __DJGPP__ */
+           strcpy(newpath,tmpbuf);
+       } else {
+#ifdef __DJGPP__
+           if ((cp = strrchr(newpath,'\\')) != NULL) {
+#else
            if ((cp = strrchr(newpath,'/')) != NULL) {
+#endif /* __DJGPP__ */
                *++cp = '\0';
                strcat(newpath,tmpbuf);
            } else {
                HTAlert(gettext("Unexpected failure - unable to find trailing 
\"/\""));
                return 0;
            }
-       } else {
-           strcpy(newpath,tmpbuf);
-       }
+    }
 
        /*
         *  Make sure the source and target have the same owner (uid).
@@ -1319,17 +1352,55 @@
        char *,         pathname)
 {
     char *result = 0;
+    char *result1 = 0;
     char *leaf;
 
     StrAllocCopy(result, pathname);
     leaf = LYPathLeaf(result);
     if (leaf != result) {
        *leaf = '\0';
+#ifdef __DJGPP__
+       if (LYIsPathSep(result[0]) || (strlen(result) == 3 && 
isalpha(result[0]) && (result[1] == ':') && LYIsPathSep(result[2]))) {}
+       else
+#endif /* __DJGPP__ */
        LYTrimPathSep(result);
+       result1=wwwName(result);
+       StrAllocCopy (result, result1);
     }
     return result;
 }
 
+/*  Convert filenames to acceptable 8+3 names when necessary
+*/
+PRIVATE char * LYonedot ARGS1(
+               char *,  line)
+#ifdef __DJGPP__
+{
+       char *dot;
+       char line1[512] = "\0";
+       char line2[512] = "\0";
+       char *line3;
+       strcpy (line1, line);
+   if (pathconf (line1, _PC_NAME_MAX) <= 12) {
+   dot=strrchr(line1, '.');
+   if ((dot != NULL) && (strlen(dot) >0) && (strchr(dot, '/') == NULL) && 
(strchr(dot, '\\') == NULL)) {
+   strncpy(line2, line, (strlen(line) - strlen(dot)));
+   if  (strlen(dot) > 1) {
+   strcat (line2, "_");
+   strcat (line2, dot+1); }
+   strcpy (line3, line2);
+   return(line3);
+          }
+   else { return(line); }
+          }
+   else { return(line); }
+   }
+#else
+{return(line);}
+#endif /*  __DJGPP__ */
+
+
+
 /*
  *  Perform file management operations for LYNXDIRED URL's.
  *  Attempt to be consistent.  These are (pseudo) URLs - i.e., they should
@@ -1360,6 +1431,16 @@
     HTUnEscape(line);  /* _file_ (not URL) syntax, for those functions
                           that need it.  Don't forget to FREE it. */
 
+#ifdef __DJGPP__
+    if (!strncmp(line, "LYNXDIRED://", 12)) {
+        char *s;
+        int i;
+        s = strchr(&line[12], '/');
+        if(s!=NULL)
+        for(i=0; s[i]!=0; i++)
+        s[i] = s[i+1];
+    }
+#endif /* __DJGPP__ */
     if (!strncmp(line, "LYNXDIRED://NEW_FILE", 20)) {
        if (create_file(&line[20]) > 0)
            LYforce_no_cache = TRUE;
@@ -1417,7 +1498,11 @@
            LYforce_no_cache = TRUE;
     } else {
        LYTrimPathSep(line);
+#ifdef __DJGPP__
+       if ((strrchr(line, '/') == NULL) && (strrchr(line, '\\') == NULL)) {
+#else
        if (strrchr(line, '/') == NULL) {
+#endif /* __DJGPP__ */
            FREE(line);
            return 0;
        }
@@ -1445,6 +1530,15 @@
 # ifndef ARCHIVE_ONLY
 #  ifdef OK_GZIP
        } else if (!strncmp(line, "LYNXDIRED://UNTAR_GZ", 20)) {
+#ifdef __DJGPP__
+#define FMT_UNTAR_GZ "cd %s; %s -qdc %s |  %s -xf -"
+           dirname = DirectoryOf(line+20);
+           HTAddParam(&buffer, FMT_UNTAR_GZ, 1, dirname);
+           HTAddParam(&buffer, FMT_UNTAR_GZ, 2, GZIP_PATH);
+           HTAddParam(&buffer, FMT_UNTAR_GZ, 3, line+20);
+           HTAddParam(&buffer, FMT_UNTAR_GZ, 4, TAR_PATH);
+           HTEndParam(&buffer, FMT_UNTAR_GZ, 4);
+#else
 #define FMT_UNTAR_GZ "%s -qdc %s | (cd %s; %s -xf -)"
            dirname = DirectoryOf(line+20);
            HTAddParam(&buffer, FMT_UNTAR_GZ, 1, GZIP_PATH);
@@ -1452,9 +1546,19 @@
            HTAddParam(&buffer, FMT_UNTAR_GZ, 3, dirname);
            HTAddParam(&buffer, FMT_UNTAR_GZ, 4, TAR_PATH);
            HTEndParam(&buffer, FMT_UNTAR_GZ, 4);
+#endif /* __DJGPP__ */
 #  endif /* OK_GZIP */
 
        } else if (!strncmp(line, "LYNXDIRED://UNTAR_Z", 19)) {
+#ifdef __DJGPP__
+#define FMT_UNTAR_Z "cd %s; %s %s |  %s -xf -"
+           dirname = DirectoryOf(line+19);
+           HTAddParam(&buffer, FMT_UNTAR_Z, 1, dirname);
+           HTAddParam(&buffer, FMT_UNTAR_Z, 2, ZCAT_PATH);
+           HTAddParam(&buffer, FMT_UNTAR_Z, 3, line+19);
+           HTAddParam(&buffer, FMT_UNTAR_Z, 4, TAR_PATH);
+           HTEndParam(&buffer, FMT_UNTAR_Z, 4);
+#else
 #define FMT_UNTAR_Z "%s %s | (cd %s; %s -xf -)"
            dirname = DirectoryOf(line+19);
            HTAddParam(&buffer, FMT_UNTAR_Z, 1, ZCAT_PATH);
@@ -1462,6 +1566,7 @@
            HTAddParam(&buffer, FMT_UNTAR_Z, 3, dirname);
            HTAddParam(&buffer, FMT_UNTAR_Z, 4, TAR_PATH);
            HTEndParam(&buffer, FMT_UNTAR_Z, 4);
+#endif /* __DJGPP__ */
 
        } else if (!strncmp(line, "LYNXDIRED://UNTAR", 17)) {
 #define FMT_UNTAR "cd %s; %s -xf %s"
@@ -1474,6 +1579,16 @@
 
 # ifdef OK_GZIP
        } else if (!strncmp(line, "LYNXDIRED://TAR_GZ", 18)) {
+#ifdef __DJGPP__
+#define FMT_TAR_GZ "cd %s; %s -cf - %s | %s -qc >%s.tgz"
+           dirname = DirectoryOf(line+18);
+           HTAddParam(&buffer, FMT_TAR_GZ, 1, dirname);
+           HTAddParam(&buffer, FMT_TAR_GZ, 2, TAR_PATH);
+           HTAddParam(&buffer, FMT_TAR_GZ, 3, LYPathLeaf(line+18));
+           HTAddParam(&buffer, FMT_TAR_GZ, 4, GZIP_PATH);
+           HTAddParam(&buffer, FMT_TAR_GZ, 5, LYonedot(LYPathLeaf(line+18)));
+           HTEndParam(&buffer, FMT_TAR_GZ, 5);
+#else
 #define FMT_TAR_GZ "(cd %s; %s -cf - %s) | %s -qc >%s/%s.tar.gz"
            dirname = DirectoryOf(line+18);
            HTAddParam(&buffer, FMT_TAR_GZ, 1, dirname);
@@ -1483,9 +1598,20 @@
            HTAddParam(&buffer, FMT_TAR_GZ, 5, dirname);
            HTAddParam(&buffer, FMT_TAR_GZ, 6, LYPathLeaf(line+18));
            HTEndParam(&buffer, FMT_TAR_GZ, 6);
+#endif /* __DJGPP__ */
 # endif /* OK_GZIP */
 
        } else if (!strncmp(line, "LYNXDIRED://TAR_Z", 17)) {
+#ifdef __DJGPP__
+#define FMT_TAR_Z "cd %s; %s -cf - %s | %s >%s.taz"
+           dirname = DirectoryOf(line+17);
+           HTAddParam(&buffer, FMT_TAR_Z, 1, dirname);
+           HTAddParam(&buffer, FMT_TAR_Z, 2, TAR_PATH);
+           HTAddParam(&buffer, FMT_TAR_Z, 3, LYPathLeaf(line+17));
+           HTAddParam(&buffer, FMT_TAR_Z, 4, COMPRESS_PATH);
+           HTAddParam(&buffer, FMT_TAR_Z, 6, LYonedot(LYPathLeaf(line+17)));
+           HTEndParam(&buffer, FMT_TAR_Z, 5);
+#else
 #define FMT_TAR_Z "(cd %s; %s -cf - %s) | %s >%s/%s.tar.Z"
            dirname = DirectoryOf(line+17);
            HTAddParam(&buffer, FMT_TAR_Z, 1, dirname);
@@ -1495,13 +1621,18 @@
            HTAddParam(&buffer, FMT_TAR_Z, 5, dirname);
            HTAddParam(&buffer, FMT_TAR_Z, 6, LYPathLeaf(line+17));
            HTEndParam(&buffer, FMT_TAR_Z, 6);
+#endif /* __DJGPP__ */
 
        } else if (!strncmp(line, "LYNXDIRED://TAR", 15)) {
+#ifdef __DJGPP__
+#define FMT_TAR "cd %s; %s -cf %s.tar %s"
+#else
 #define FMT_TAR "(cd %s; %s -cf %s.tar %s)"
+#endif /* __DJGPP__ */
            dirname = DirectoryOf(line+15);
            HTAddParam(&buffer, FMT_TAR, 1, dirname);
            HTAddParam(&buffer, FMT_TAR, 2, TAR_PATH);
-           HTAddParam(&buffer, FMT_TAR, 3, LYPathLeaf(line+15));
+           HTAddParam(&buffer, FMT_TAR, 3, LYonedot(LYPathLeaf(line+15)));
            HTAddParam(&buffer, FMT_TAR, 4, LYPathLeaf(line+15));
            HTEndParam(&buffer, FMT_TAR, 4);
 #endif /* OK_TAR */
@@ -1524,12 +1655,21 @@
 #ifdef OK_ZIP
        } else if (!strncmp(line, "LYNXDIRED://ZIP", 15)) {
 #define FMT_ZIP "cd %s; %s -rq %s.zip %s"
+#ifdef __DJGPP__
+           dirname = DirectoryOf(line+15);
+           HTAddParam(&buffer, FMT_ZIP, 1, dirname);
+           HTAddParam(&buffer, FMT_ZIP, 2, ZIP_PATH);
+           HTAddParam(&buffer, FMT_ZIP, 3, LYonedot(LYPathLeaf(line+15)));
+           HTAddParam(&buffer, FMT_ZIP, 4, LYPathLeaf(line+15));
+           HTEndParam(&buffer, FMT_ZIP, 4);
+#else
            dirname = DirectoryOf(line+15);
            HTAddParam(&buffer, FMT_ZIP, 1, dirname);
            HTAddParam(&buffer, FMT_ZIP, 2, ZIP_PATH);
            HTAddParam(&buffer, FMT_ZIP, 3, line+15);
            HTAddParam(&buffer, FMT_ZIP, 4, LYPathLeaf(line+15));
            HTEndParam(&buffer, FMT_ZIP, 4);
+#endif /* __DJGPP__ */
 #ifndef ARCHIVE_ONLY
        } else if (!strncmp(line, "LYNXDIRED://UNZIP", 17)) {
 #define FMT_UNZIP "cd %s; %s -q %s"
@@ -1557,7 +1697,7 @@
            }
            _statusline(tmpbuf);
            stop_curses();
-           printf("%s\n", tmpbuf);
+           printf("%s\r\n", tmpbuf);
            LYSystem(buffer);
 #ifdef VMS
            HadVMSInterrupt = FALSE;
@@ -1889,8 +2029,10 @@
        new->cond = DE_DIR;
     } else if (strcasecomp(str, "file") == 0) {
        new->cond = DE_FILE;
+#ifdef S_IFLNK
     } else if (strcasecomp(str, "link") == 0) {
        new->cond = DE_SYMLINK;
+#endif /* S_IFLNK */
     }
 
     /*
@@ -2034,6 +2176,20 @@
     int rc;
     char *tmpbuf = 0;
     pid_t pid;
+#ifdef __DJGPP__
+    {
+        int n;
+        char *the_command = 0;
+
+       stop_curses();
+        HTSprintf(&the_command, "%s", path);
+        for (n = 1; argv[n] != 0; n++)
+        HTSprintf(&the_command, " %s", argv[n]);
+        HTSprintf(&the_command, "\n");
+        rc = LYSystem(the_command) ? 0 : 1;
+        FREE(the_command);
+    }
+#else
 #ifdef HAVE_TYPE_UNIONWAIT
     union wait wstatus;
 #else
@@ -2086,6 +2242,7 @@
                rc = 0;
            }
     }
+#endif /* __DJGPP__ */
 
     if (rc == 0) {
        /*
--- lynx2-8-2/src/LYMain.c      Sun May 16 20:11:26 1999
+++ lynx2-8-2/src/LYMain.c.new  Sun May 23 21:19:18 1999
@@ -693,6 +693,7 @@
     atexit(reset_break);
     dbug_init();
     sock_init();
+    __system_flags = 0x501D;
 #endif
 
     /*
--- lynx2-8-2/src/LYMainLoop.c  Thu May 20 05:48:10 1999
+++ lynx2-8-2/src/LYMainLoop.c.new      Sun May 23 21:19:24 1999
@@ -3264,7 +3264,7 @@
                    psrc_view = FALSE;  /* we get here if link is not internal 
*/
 #endif
 
-#ifdef DIRED_SUPPORT
+#if defined(DIRED_SUPPORT) && !defined(__DJGPP__)
                    if (lynx_edit_mode) {
                          HTuncache_current_document();
                          /*
@@ -3275,7 +3275,7 @@
                          HTUnEscapeSome(newdoc.address,"/");
                          strip_trailing_slash(newdoc.address);
                    }
-#endif /* DIRED_SUPPORT */
+#endif /* DIRED_SUPPORT  && !__DJGPP__ */
                    if (!strncmp(curdoc.address, "LYNXCOOKIE:", 11)) {
                        HTuncache_current_document();
                    }
@@ -4875,6 +4875,9 @@
            /*
             *  Don't do if not allowed or already viewing the menu.
             */
+#ifdef __DJGPP__
+           lynx_edit_mode=TRUE;
+#endif /* __DJGPP__ */
            if (lynx_edit_mode && !no_dired_support &&
                strcmp(curdoc.address, LYDiredFileURL) &&
                strcmp((curdoc.title ? curdoc.title : ""),
--- lynx2-8-2/src/LYShowInfo.c  Fri Apr 23 07:56:36 1999
+++ lynx2-8-2/src/LYShowInfo.c.new      Sun May 23 21:19:26 1999
@@ -18,6 +18,9 @@
 #include <HTAAProt.h>
 #include <time.h>
 #include <LYLocal.h>
+#ifndef HAVE_LSTAT
+#define lstat stat
+#endif /* HAVE_LSTAT */
 #endif /* DIRED_SUPPORT */
 
 #define ADVANCED_INFO 1                /* to get more info in advanced mode */
--- lynx2-8-2/src/makefile.dsl  Fri Apr 23 07:56:36 1999
+++ lynx2-8-2/src/makefile.dsl.new      Sun May 23 21:19:26 1999
@@ -13,6 +13,7 @@
 
 CC = gcc
 MCFLAGS = -O2 -DDISP_PARTIAL -DUSE_ZLIB -DUSE_EXTERNALS \
+-DDIRED_SUPPORT -DOK_UUDECODE -DOK_TAR -DOK_GZIP -DOK_ZIP \
 -DSOURCE_CACHE -DUSE_PSRC \
 -DUSE_SLANG -DDJGPP_KEYHANDLER -DACCESS_AUTH -DNO_CUSERID \
 -DNOUSERS -DDOSPATH -DNO_TTYTYPE -DNO_UTMP -I../WWW/Library/Implementation \
--- lynx2-8-2/WWW/Library/djgpp/makefile.sla    Fri Apr 23 07:56:36 1999
+++ lynx2-8-2/WWW/Library/djgpp/makefile.sla.new        Sun May 23 21:15:48 1999
@@ -8,7 +8,7 @@
 #ASIS_MACH = hardware/os
 
 CFLAGS = -O3 -DUSE_SLANG -DUSE_ZLIB -DDOSPATH -DNOUSERS -DDISP_PARTIAL \
--DSOURCE_CACHE -DUSE_PSRC \
+-DDIRED_SUPPORT -DSOURCE_CACHE -DUSE_PSRC \
 -I../Implementation \
 -I../../../djgpp/tcplib/include \
 -I../../../djgpp/tcplib/include/tcp \

__
Doug Kaufman
Internet: address@hidden (preferred)
          address@hidden


reply via email to

[Prev in Thread] Current Thread [Next in Thread]