home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / script.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  12.6 KB  |  605 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * script.c: functions for handling script files
  13.  */
  14.  
  15. #include "vim.h"
  16. #include "globals.h"
  17. #include "proto.h"
  18. #include "param.h"
  19.  
  20. static char *scriptname;            /* name of the script in use */
  21. static FILE *autoscriptfd = NULL;
  22. static char *makescriptname __ARGS((void));
  23. static void Supdatescript __ARGS((char *));
  24.  
  25. extern int global_busy;            /* this is in csearch.c */
  26.  
  27. /*
  28.  * for Amiga Dos 2.0x we use Open/Close/Flush instead of fopen/fclose
  29.  */
  30. #ifdef AMIGA
  31. # ifndef NO_ARP
  32. extern int dos2;                    /* this is in amiga.c */
  33. # endif
  34. # ifdef SASC
  35. #  include <proto/dos.h>
  36. # endif
  37. #endif
  38.  
  39. /*
  40.  * We use this flag to avoid writing :win to commands to the script file
  41.  * during startup.
  42.  */
  43. static int script_started = FALSE;
  44.  
  45. /*
  46.  * startscript(): open automatic script file
  47.  */
  48.     void
  49. startscript()
  50. {
  51.     int        n;
  52.     char    buf[25];
  53. #ifdef AMIGA
  54.     int        r;
  55.     FILE    *dummyfd = NULL;
  56. #endif
  57. #ifdef UNIX
  58. # ifdef SCO
  59.     mode_t    oldmask;
  60. # else
  61.     int        oldmask;
  62. # endif
  63. #endif
  64.  
  65.     script_started = TRUE;
  66.  
  67. #ifdef AMIGA
  68. /*
  69.  * With Amiga DOS 2.0 the system may lockup with the sequence: write to .vim
  70.  * file, close it, delete it, create a new .vim file and write to it.
  71.  * This is a problem in the filesystem hash chains (solved in version 39.xx).
  72.  * The Delay seems to solve this problem, maybe because DOS gets a chance to
  73.  * finish closing and deleting the old .vim file. Also do this for DOS 1.3,
  74.  * just in case.
  75.  */
  76.     if (stopscript())
  77.         Delay(10L);        /* This should fix the lockup bug */
  78. #else
  79.     stopscript();        /* stop any old script */
  80. #endif
  81.  
  82.     if (p_uc == 0 || exiting)    /* no auto script wanted/needed */
  83.         return;
  84.     if (Changed)
  85.         emsg("Warning: buffer already changed, auto script file will be incomplete");
  86.  
  87. #ifdef AMIGA
  88. /*
  89.  * If we start editing a new file, e.g. "test.doc", which resides on an MSDOS
  90.  * compatible filesystem, it is possible that the file "test.doc.vim" which we
  91.  * create will be exactly the same file. To avoid this problem we temporarily
  92.  * create "test.doc".
  93.  */
  94.     if (!(p_sn || thisfile_sn) && xFilename && getperm(xFilename) < 0)
  95.         dummyfd = fopen(xFilename, "w");
  96. #endif
  97.  
  98. /*
  99.  * we try different names until we find one that does not exist yet
  100.  */
  101.     scriptname = makescriptname();
  102.     for (;;)
  103.     {
  104.         if (scriptname == NULL)        /* must be out of memory */
  105.             break;
  106.         if ((n = strlen(scriptname)) == 0)    /* safety check */
  107.         {
  108.             free(scriptname);
  109.             break;
  110.         }
  111.         
  112.         /*
  113.          * check if the scriptfile already exists
  114.          */
  115.         if (getperm(scriptname) < 0)        /* it does not exist */
  116.         {
  117.                 /*
  118.                  * Create the autoscript file.
  119.                  */
  120. #ifdef UNIX
  121.             /*
  122.              * Disallow others to read our .vim file. This is useful if the
  123.              * .vim file is put in some public place like /tmp.
  124.              */
  125. # ifdef SCO
  126.             oldmask = umask((mode_t)066);    /* rw------- */
  127. # else
  128.             oldmask = umask(066);            /* rw------- */
  129. # endif
  130. #endif
  131. #ifdef AMIGA
  132. # ifndef NO_ARP
  133.             if (dos2)
  134. # endif
  135.                 autoscriptfd = (FILE *)Open((UBYTE *)scriptname, (long)MODE_NEWFILE);
  136. # ifndef NO_ARP
  137.             else
  138.                 autoscriptfd = fopen(scriptname, "w");
  139. # endif
  140. #else    /* !AMIGA */
  141.             autoscriptfd = fopen(scriptname, WRITEBIN);
  142. #endif    /* AMIGA */
  143. #ifdef UNIX
  144.             umask(oldmask);                /* back to default umask */
  145. #endif
  146.  
  147. #ifdef AMIGA
  148.             /*
  149.              * on the Amiga getperm() will return -1 when the file exists but
  150.              * is being used by another program. This happens if you edit
  151.              * a file twice.
  152.              */
  153.             if (autoscriptfd != NULL || (IoErr() != ERROR_OBJECT_IN_USE && IoErr() != ERROR_OBJECT_EXISTS))
  154. #endif
  155.                 break;
  156.         }
  157.     /*
  158.      * get here when file already exists
  159.      */
  160.         if (scriptname[n - 1] == 'm')        /* first try */
  161.         {
  162. #ifdef AMIGA
  163.         /*
  164.          * on MS-DOS compatible filesystems (e.g. messydos) file.doc.vim
  165.          * and file.doc are the same file. To guess if this problem is
  166.          * present try if file.doc.vix exists. If it does, we set thisfile_sn
  167.          * and try file_doc.vim (dots replaced by underscores for this file),
  168.          * and try again. If it doesn't we assume that "file.doc.vim" already
  169.          * exists.
  170.          */
  171.             if (!(p_sn || thisfile_sn))        /* not tried yet */
  172.             {
  173.                 scriptname[n - 1] = 'x';
  174.                 r = getperm(scriptname);    /* try "file.vix" */
  175.                 scriptname[n - 1] = 'm';
  176.                 if (r >= 0)                    /* it seems to exist */
  177.                 {
  178.                     thisfile_sn = TRUE;
  179.                     free(scriptname);
  180.                     scriptname = makescriptname();    /* '.' replaced by '_' */
  181.                     continue;                        /* try again */
  182.                 }
  183.             }
  184. #endif
  185.             /* if we get here ".vim" file really exists */
  186.             if (!recoverymode)
  187.                 emsg(".vim file exists: an edit of this file has not been finished");
  188.         }
  189.  
  190.         if (scriptname[n - 1] == 'a')    /* tried enough names, give up */
  191.         {
  192.             free(scriptname);
  193.             break;
  194.         }
  195.         --scriptname[n - 1];                /* change last char of the name */
  196.     }
  197.     if (autoscriptfd != NULL)        /* ".vim" file has been created */
  198.     {
  199.         script_winsize();            /* always start with a :win command */
  200.                                     /* output cursor position if neccessary */
  201.         if (Curpos.lnum > 1 || Curpos.col > 0)
  202.         {
  203.             sprintf(buf, "%ldG0%dl", (long)Curpos.lnum, (int)Curpos.col);
  204.             Supdatescript(buf);
  205.         }
  206.     }
  207.  
  208. #ifdef AMIGA
  209.     if (dummyfd)        /* file has been created temporarily */
  210.     {
  211.         fclose(dummyfd);
  212.         remove(xFilename);
  213.     }
  214. #endif
  215. }
  216.  
  217.     int
  218. stopscript()
  219. {
  220.     if (!autoscriptfd)
  221.         return FALSE;        /* nothing to stop */
  222.  
  223. #ifdef AMIGA
  224. # ifndef NO_ARP
  225.     if (dos2)
  226. # endif
  227.         Close((BPTR)autoscriptfd);
  228. # ifndef NO_ARP
  229.     else
  230.         fclose(autoscriptfd);
  231. # endif
  232. #else
  233.     fclose(autoscriptfd);
  234. #endif
  235.     remove(scriptname);        /* delete the file */
  236.     autoscriptfd = NULL;
  237.     free(scriptname);
  238.     return TRUE;
  239. }
  240.  
  241. /*
  242.  * open new script file
  243.  * return 0 on success, 1 on error
  244.  */
  245.     int
  246. openscript(name)
  247.     char *name;
  248. {
  249.     int oldcurscript;
  250.  
  251.     if (curscript + 1 == NSCRIPT)
  252.     {
  253.         emsg(e_nesting);
  254.         return 1;
  255.     }
  256.     else
  257.     {
  258.         if (scriptin[curscript] != NULL)    /* already reading script */
  259.             ++curscript;
  260.         if ((scriptin[curscript] = fopen((char *)name, READBIN)) == NULL)
  261.         {
  262.             emsg(e_notopen);
  263.             if (curscript)
  264.                 --curscript;
  265.             return 1;
  266.         }
  267.         /*
  268.          * With command ":g/pat/so! file" we have to execute the
  269.          * commands from the file now.
  270.          */
  271.         if (global_busy)
  272.         {
  273.             State = NORMAL;
  274.             oldcurscript = curscript;
  275.             do
  276.             {
  277.                 normal();
  278.                 vpeekc();            /* check for end of file */
  279.             }
  280.             while (scriptin[oldcurscript]);
  281.             State = CMDLINE;
  282.         }
  283.     }
  284.     return 0;
  285. }
  286.  
  287. /*
  288.  * updatescipt() is called when a character has to be written into the script file
  289.  * or when we have waited some time for a character (c == 0)
  290.  */
  291.     void
  292. updatescript(c)
  293.     int c;
  294. {
  295.     static int count = 0;
  296.  
  297.     if (c && scriptout)
  298.         putc(c, scriptout);
  299.     if (autoscriptfd == NULL || (c == 0 && count == 0))        /* nothing to do */
  300.         return;
  301.     if (c)
  302.     {
  303. #ifdef AMIGA
  304. # ifndef NO_ARP
  305.         if (dos2)
  306. # endif
  307.             FPutC((BPTR)autoscriptfd, (unsigned long)c);
  308. # ifndef NO_ARP
  309.         else
  310.             putc(c, autoscriptfd);
  311. # endif
  312. #else
  313.         putc(c, autoscriptfd);
  314. #endif
  315.         ++count;
  316.     }
  317.     if ((c == 0 || count >= p_uc) && Updated)
  318.     {
  319.         /*
  320.          * Before DOS 2.0x we have to close and open the file in order to really
  321.          * get the characters in the file to disk!
  322.          * With DOS 2.0x Flush() can be used for that
  323.          */
  324. #ifdef AMIGA
  325. # ifndef NO_ARP
  326.         if (dos2)
  327. # endif
  328.             Flush((BPTR)autoscriptfd);
  329. # ifndef NO_ARP
  330.         else
  331.         {
  332.             fclose(autoscriptfd);
  333.             autoscriptfd = fopen(scriptname, "a");
  334.         }
  335. # endif
  336. #else     /* !AMIGA */
  337.         fclose(autoscriptfd);
  338. # ifdef MSDOS
  339.         autoscriptfd = fopen(scriptname, "ab");
  340. # else
  341.         autoscriptfd = fopen(scriptname, "a");
  342. # endif
  343. #endif
  344.         count = 0;
  345.         Updated = 0;
  346.     }
  347. }
  348.  
  349.     static void
  350. Supdatescript(str)
  351.     char *str;
  352. {
  353.     while (*str)
  354.         updatescript(*str++);
  355. }
  356.  
  357. /*
  358.  * try to open the ".vim" file for recovery
  359.  * if recoverymode is 1: start recovery, set recoverymode to 2
  360.  * if recoverymode is 2: stop recovery mode
  361.  */
  362.     void
  363. openrecover()
  364. {
  365.     char *fname;
  366.     struct stat efile, rfile;
  367.  
  368.     if (recoverymode == 2)        /* end of recovery */
  369.     {
  370.         recoverymode = 0;
  371.         if (got_int)
  372.             emsg("Recovery Interrupted");
  373.         else
  374.             msg("Recovery completed; If everything is OK: Save this file and delete the .vim file");
  375.             /* The cursor will be in the wrong place after the msg() */
  376.             /* We need to fix it here because we are called from inchar() */
  377.         setcursor();
  378.         flushbuf();
  379.     }
  380.     else
  381.     {
  382.         fname = makescriptname();
  383.         if (fname)
  384.         {
  385.             recoverymode = 2;
  386.             if (xFilename != NULL &&
  387.                     stat(xFilename, &efile) != -1 &&
  388.                     stat(fname, &rfile) != -1 &&
  389.                     efile.st_mtime > rfile.st_mtime)
  390.                 emsg(".vim file is older; file not recovered");
  391.             else
  392.             {
  393.                 if (openscript(fname))
  394.                     emsg("Cannot open .vim file; file not recovered");
  395.             }
  396.             free(fname);
  397.         }
  398.     }
  399. }
  400.  
  401. /*
  402.  * make script name out of the filename
  403.  */
  404.     static char *
  405. makescriptname()
  406. {
  407.     char    *r, *s, *fname;
  408.  
  409.     r = modname(xFilename, ".vim");
  410.     if (*p_dir == 0 || r == NULL)
  411.         return r;
  412.  
  413.     fname = gettail(r);
  414.     s = alloc((unsigned)(strlen(p_dir) + strlen(fname) + 1));
  415.     if (s != NULL)
  416.     {
  417.         strcpy(s, p_dir);
  418.         if (*s && !ispathsep(*(s + strlen(s) - 1)))    /* don't add '/' after ':' */
  419.             strcat(s, PATHSEPSTR);
  420.         strcat(s, fname);
  421.     }
  422.     free(r);
  423.     return s;
  424. }
  425.  
  426. /*
  427.  * add full path to auto script name, used before first :cd command.
  428.  */
  429.     void
  430. scriptfullpath()
  431. {
  432.     char *s;
  433.  
  434.     if (!autoscriptfd)
  435.         return;        /* nothing to do */
  436.     /*
  437.      * on the Amiga we cannot get the full path name while the file is open
  438.      * so we close it for a moment
  439.      */
  440. #ifdef AMIGA
  441. # ifndef NO_ARP
  442.     if (dos2)
  443. # endif
  444.         Close((BPTR)autoscriptfd);
  445. # ifndef NO_ARP
  446.     else
  447.         fclose(autoscriptfd);
  448. # endif
  449. #endif
  450.  
  451.     if (FullName(scriptname, IObuff, IOSIZE))
  452.     {
  453.         s = strsave(IObuff);
  454.         if (s)
  455.         {
  456.             free(scriptname);
  457.             scriptname = s;
  458.         }
  459.     }
  460.  
  461. #ifdef AMIGA
  462. # ifndef NO_ARP
  463.     if (dos2)
  464. # endif
  465.     {
  466.         autoscriptfd = (FILE *)Open((UBYTE *)scriptname, (long)MODE_OLDFILE);
  467.         if (autoscriptfd)
  468.             Seek((BPTR)autoscriptfd, 0L, (long)OFFSET_END);
  469.     }
  470. # ifndef NO_ARP
  471.     else
  472.         autoscriptfd = fopen(scriptname, "a");
  473. # endif
  474. #endif
  475. }
  476.  
  477. /*
  478.  * add extention to filename - change path/fo.o.h to path/fo.o.h.ext or
  479.  * fo_o_h.ext for MSDOS or when dotfname option reset.
  480.  *
  481.  * Assumed that fname is a valid name found in the filesystem we assure that
  482.  * the return value is a different name and ends in ".ext".
  483.  * "ext" MUST start with a "." and MUST be at most 4 characters long.
  484.  * Space for the returned name is allocated, must be freed later.
  485.  */
  486.  
  487.     char *
  488. modname(fname, ext)
  489.     char *fname, *ext;
  490. {
  491.     char            *retval;
  492.     register char   *s;
  493.     register char   *ptr;
  494.     register int    fnamelen, extlen;
  495.     char            currentdir[512];
  496.  
  497.     extlen = strlen(ext);
  498.  
  499.     /*
  500.      * if there is no filename we must get the name of the current directory
  501.      * (we need the full path in case :cd is used)
  502.      */
  503.     if (fname == NULL || *fname == NUL)
  504.     {
  505.         (void)dirname(currentdir, 511);
  506.         strcat(currentdir, PATHSEPSTR);
  507.         fnamelen = strlen(currentdir);
  508.     }
  509.     else
  510.         fnamelen = strlen(fname);
  511.     retval = alloc((unsigned) (fnamelen + extlen + 1));
  512.     if (retval != NULL)
  513.     {
  514.         if (fname == NULL || *fname == NUL)
  515.             strcpy(retval, currentdir);
  516.         else
  517.             strcpy(retval, fname);
  518.         /*
  519.          * search backwards until we hit a '/', '\' or ':' replacing all '.' by '_'
  520.          * for MSDOS or when dotfname option reset.
  521.          * Then truncate what is after the '/', '\' or ':' to 8 characters for MSDOS
  522.          * and 26 characters for AMIGA and UNIX.
  523.          */
  524.         for (ptr = retval + fnamelen; ptr >= retval; ptr--)
  525.         {
  526. #ifndef MSDOS
  527.             if (p_sn || thisfile_sn)
  528. #endif
  529.                 if (*ptr == '.')    /* replace '.' by '_' */
  530.                     *ptr = '_';
  531.             if (ispathsep(*ptr))
  532.                 break;
  533.         }
  534.         ptr++;
  535.  
  536.         /* the filename has at most BASENAMELEN characters. */
  537.         if (strlen(ptr) > (unsigned)BASENAMELEN)
  538.             ptr[BASENAMELEN] = '\0';
  539. #ifndef MSDOS
  540.         if ((p_sn || thisfile_sn) && strlen(ptr) > (unsigned)8)
  541.             ptr[8] = '\0';
  542. #endif
  543.         s = ptr + strlen(ptr);
  544.  
  545.         /*
  546.          * Append the extention.
  547.          * ext must start with '.' and cannot exceed 3 more characters.
  548.          */
  549.         strcpy(s, ext);
  550.         if (fname != NULL && strcmp(fname, retval) == 0)
  551.         {
  552.             /* after modification still the same name? */
  553.             /* we search for a character that can be replaced by '_' */
  554.             while (--s >= ptr)
  555.             {
  556.                 if (*s != '_')
  557.                 {
  558.                     *s = '_';
  559.                     break;
  560.                 }
  561.             }
  562.             if (s < ptr)
  563.             {
  564.                 /* fname was "________.<ext>" how tricky! */
  565.                 *ptr = 'v';
  566.             }
  567.         }
  568.     }
  569.     return retval;
  570. }
  571.  
  572. /*
  573.  * the new window size must be used in scripts;
  574.  * write a ":winsize width height" command to the (auto)script
  575.  * Postpone this action if not in NORMAL State, otherwise we may insert the
  576.  * command halfway another command.
  577.  */
  578. int script_winsize_postponed = FALSE;
  579.  
  580.     void
  581. script_winsize()
  582. {
  583.     char            buf[25];
  584.  
  585.     if (!script_started || State != NORMAL)        /* postpone action */
  586.     {
  587.         script_winsize_postponed = TRUE;
  588.         return;
  589.     }
  590.  
  591.     sprintf(buf, ":win %d %d\r", (int)Columns, (int)Rows);
  592.     Supdatescript(buf);
  593.     script_winsize_postponed = FALSE;
  594. }
  595.  
  596. /*
  597.  * This function is called after each "State = NORMAL"
  598.  */
  599.     void
  600. script_winsize_pp()
  601. {
  602.     if (script_winsize_postponed)
  603.         script_winsize();
  604. }
  605.