home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / octave-1.1.1p1-base.tgz / octave-1.1.1p1-base.tar / fsf / octave / src / utils.cc < prev    next >
C/C++ Source or Header  |  1995-02-14  |  15KB  |  842 lines

  1. // utils.cc                                              -*- C++ -*-
  2. /*
  3.  
  4. Copyright (C) 1992, 1993, 1994, 1995 John W. Eaton
  5.  
  6. This file is part of Octave.
  7.  
  8. Octave is free software; you can redistribute it and/or modify it
  9. under the terms of the GNU General Public License as published by the
  10. Free Software Foundation; either version 2, or (at your option) any
  11. later version.
  12.  
  13. Octave is distributed in the hope that it will be useful, but WITHOUT
  14. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16. for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with Octave; see the file COPYING.  If not, write to the Free
  20. Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. */
  23.  
  24. #ifdef HAVE_CONFIG_H
  25. #include "config.h"
  26. #endif
  27.  
  28. #include <sys/types.h>
  29. #ifdef HAVE_UNISTD_H
  30. #include <unistd.h>
  31. #endif
  32. #include <sys/param.h>
  33. #include <string.h>
  34. #include <limits.h>
  35. #include <iostream.h>
  36. #include <strstream.h>
  37. #include <fstream.h>
  38.  
  39. #include <Complex.h>
  40.  
  41. extern "C"
  42. {
  43. #include <setjmp.h>
  44.  
  45. #ifndef HAVE_STRNCASECMP
  46. extern int strncasecmp (const char*, const char*, size_t);
  47. #endif
  48.  
  49. #define boolean kpathsea_boolean
  50. #define false kpathsea_false
  51. #define true kpathsea_true
  52. #include <kpathsea/pathsearch.h>
  53.  
  54. #if defined (HAVE_TERMIOS_H)
  55. #include <termios.h>
  56. #elif defined (HAVE_TERMIO_H)
  57. #include <termio.h>
  58. #elif defined (HAVE_SGTTY_H)
  59. #include <sgtty.h>
  60. #else
  61. LOSE! LOSE!
  62. #endif
  63.  
  64. #include <readline/tilde.h>
  65. }
  66.  
  67. // This mess suggested by the autoconf manual.
  68. // unistd.h defines _POSIX_VERSION on POSIX.1 systems.
  69. #if defined (HAVE_DIRENT_H) || defined (_POSIX_VERSION)
  70. #include <dirent.h>
  71. #define NLENGTH(dirent) (strlen((dirent)->d_name))
  72. #else
  73. #define dirent direct
  74. #define NLENGTH(dirent) ((dirent)->d_namlen)
  75. #if defined (HAVE_SYS_NDIR_H)
  76. #include <sys/ndir.h>
  77. #endif
  78. #if defined (HAVE_SYS_DIR_H)
  79. #include <sys/dir.h>
  80. #endif
  81. #if defined (HAVE_NDIR_H)
  82. #include <ndir.h>
  83. #endif
  84. #endif
  85.  
  86. #include "SLStack.h"
  87.  
  88. #include "procstream.h"
  89. #include "user-prefs.h"
  90. #include "variables.h"
  91. #include "dirfns.h"
  92. #include "defun.h"
  93. #include "error.h"
  94. #include "help.h"
  95. #include "gripes.h"
  96. #include "pager.h"
  97. #include "utils.h"
  98. #include "input.h"
  99. #include "octave.h"
  100. #include "oct-obj.h"
  101. #include "mappers.h"
  102. #include "tree-const.h"
  103. #include "unwind-prot.h"
  104. #include "octave-hist.h"
  105.  
  106. // Top level context (?)
  107. extern jmp_buf toplevel;
  108.  
  109. // Save a string.
  110.  
  111. char *
  112. strsave (const char *s)
  113. {
  114.   if (! s)
  115.     return 0;
  116.  
  117.   int len = strlen (s);
  118.   char *tmp = new char [len+1];
  119.   tmp = strcpy (tmp, s);
  120.   return tmp;
  121. }
  122.  
  123. // Concatenate two strings.
  124.  
  125. char *
  126. strconcat (const char *s, const char *t)
  127. {
  128.   int len = strlen (s) + strlen (t);
  129.   char *tmp = new char [len+1];
  130.   strcpy (tmp, s);
  131.   return strcat (tmp, t);
  132. }
  133.  
  134. // Throw away input until a given character is read.
  135.  
  136. void
  137. discard_until (istream& stream, char character)
  138. {
  139.   int c;
  140.   for (;;)
  141.     {
  142.       stream >> c;
  143.       if (c == EOF || c == character)
  144.     break;
  145.     }
  146.   if (c != EOF)
  147.     stream.putback ((char) c);
  148.  
  149. #if 0
  150.  
  151. // XXX UNTESTED XXX
  152.  
  153. // Read input until a given character is read.  Returns characters
  154. // read in a new string that must be freed by the caller.
  155.  
  156. char *
  157. read_until (istream& stream, char character)
  158. {
  159.   int grow_size = 8;
  160.   int limit = grow_size;
  161.   char *buf = new char [limit];
  162.   char *bp = buf;
  163.  
  164.  get_more:
  165.   is.getline (bp, limit, character);
  166.  
  167.   if (is.gcount () == 0)
  168.     {
  169.       delete [] buf;
  170.       return 0;
  171.     }
  172.  
  173.   if (is.gcount () == limit && buf[limit-1] != '\0')
  174.     {
  175.       char *tmp = new char [limit + grow_size];
  176.       strcpy (tmp, buf);
  177.       delete [] buf;
  178.       buf = tmp;
  179.       bp = tmp + limit - 1;
  180.       limit += grow_size;
  181.       grow_size *= 2;
  182.       goto get_more;
  183.     }
  184.  
  185.   return buf;
  186. }
  187. #endif
  188.  
  189. // Get a temporary file name.
  190.  
  191. char *
  192. octave_tmp_file_name (void)
  193. {
  194.   static char *retval = 0;
  195.  
  196.   if (retval)
  197.     free (retval);
  198.  
  199.   retval = tempnam (0, "oct-");
  200.  
  201.   if (! retval)
  202.     error ("can't open temporary file!");
  203.  
  204.   return retval;
  205. }
  206.  
  207. DEFUN ("octave_tmp_file_name", Foctave_tmp_file_name,
  208.        Soctave_tmp_file_name, 0, 1,
  209.  "octave_tmp_file_name ()")
  210. {
  211.   tree_constant retval;
  212.  
  213.   if (args.length () == 0)
  214.     retval = octave_tmp_file_name ();
  215.   else
  216.     print_usage ("octave_tmp_file_name");
  217.  
  218.   return retval;
  219. }
  220.  
  221. char **
  222. pathstring_to_vector (char *pathstring)
  223. {
  224.   static char **path = 0;
  225.  
  226.   if (pathstring)
  227.     {
  228.       int nelem = 0;
  229.       char *tmp_path = strsave (pathstring);
  230.       if (*tmp_path != '\0')
  231.     {
  232.       nelem++;
  233.       char *ptr = tmp_path;
  234.       while (*ptr != '\0')
  235.         {
  236.           if (*ptr == SEPCHAR)
  237.         nelem++;
  238.           ptr++;
  239.         }
  240.     }
  241.  
  242.       char **foo = path;
  243.       while (foo && *foo)
  244.     delete [] *foo++;
  245.       delete [] path;
  246.  
  247.       path = new char * [nelem+1];
  248.       path[nelem] = 0;
  249.  
  250.       int i = 0;
  251.       char *ptr = tmp_path;
  252.       while (i < nelem)
  253.     {
  254.       char *end = strchr (ptr, SEPCHAR);
  255.       if (end)
  256.         *end = '\0';
  257.       char *result = tilde_expand (ptr);
  258.       path[i] = strsave (result);
  259.       free (result);
  260.       ptr = end + 1;
  261.       i++;
  262.     }
  263.  
  264.       delete [] tmp_path;
  265.     }
  266.  
  267.   return path;
  268. }
  269.  
  270. // Return to the main command loop in octave.cc.
  271.  
  272. void
  273. jump_to_top_level (void)
  274. {
  275.   run_all_unwind_protects ();
  276.  
  277.   longjmp (toplevel, 1);
  278. }
  279.  
  280. int
  281. almost_match (const char *std, const char *s, int min_match_len,
  282.           int case_sens)
  283. {
  284.   int stdlen = strlen (std);
  285.   int slen = strlen (s);
  286.  
  287.   return (slen <= stdlen
  288.       && slen >= min_match_len
  289.       && (case_sens
  290.           ? (strncmp (std, s, slen) == 0)
  291.           : (strncasecmp (std, s, slen) == 0)));
  292. }
  293.  
  294. // Ugh.
  295.  
  296. int
  297. keyword_almost_match (const char **std, int *min_len, const char *s,
  298.               int min_toks_to_match, int max_toks)
  299. {
  300.   int status = 0;
  301.   int tok_count = 0;
  302.   int toks_matched = 0;
  303.  
  304.   if (! s || *s == '\0' || max_toks < 1)
  305.     return status;
  306.  
  307.   char *kw = strsave (s);
  308.  
  309.   char *t = kw;
  310.   while (*t != '\0')
  311.     {
  312.       if (*t == '\t')
  313.     *t = ' ';
  314.       t++;
  315.     }
  316.  
  317.   char *beg = kw;
  318.   while (*beg == ' ')
  319.     beg++;
  320.  
  321.   if (*beg == '\0')
  322.     return status;
  323.  
  324.  
  325.   char **to_match = new char * [max_toks + 1];
  326.   const char **s1 = std;
  327.   char **s2 = to_match;
  328.  
  329.   if (! s1 || ! s2)
  330.     goto done;
  331.  
  332.   s2[tok_count] = beg;
  333.   char *end;
  334.   while ((end = strchr (beg, ' ')) != 0)
  335.     {
  336.       *end = '\0';
  337.       beg = end + 1;
  338.  
  339.       while (*beg == ' ')
  340.     beg++;
  341.  
  342.       if (*beg == '\0')
  343.     break;
  344.  
  345.       tok_count++;
  346.       if (tok_count >= max_toks)
  347.     goto done;
  348.  
  349.       s2[tok_count] = beg;
  350.     }
  351.   s2[tok_count+1] = 0;
  352.  
  353.   s2 = to_match;
  354.  
  355.   for (;;)
  356.     {
  357.       if (! almost_match (*s1, *s2, min_len[toks_matched], 0))
  358.     goto done;
  359.  
  360.       toks_matched++;
  361.  
  362.       s1++;
  363.       s2++;
  364.  
  365.       if (! *s2)
  366.     {
  367.       status = (toks_matched >= min_toks_to_match);
  368.       goto done;
  369.     }
  370.  
  371.       if (! *s1)
  372.     goto done;
  373.     }
  374.  
  375.  done:
  376.  
  377.   delete [] kw;
  378.   delete [] to_match;
  379.  
  380.   return status;
  381. }
  382.  
  383. char **
  384. get_fcn_file_names (int& num, const char *dir, int no_suffix)
  385. {
  386.   static int num_max = 256;
  387.   char **retval = new char * [num_max];
  388.   int i = 0;
  389.  
  390.   DIR *dirp = opendir (dir);
  391.   if (dirp)
  392.     {
  393.       struct dirent *entry;
  394.       while ((entry = readdir (dirp)) != 0)
  395.     {
  396.       int len = NLENGTH (entry);
  397. #ifdef WITH_DLD
  398.       if ((len > 2
  399.            && entry->d_name[len-2] == '.'
  400.            && entry->d_name[len-1] == 'm')
  401.           || (len > 4
  402.           && entry->d_name[len-4] == '.'
  403.           && entry->d_name[len-3] == 'o'
  404.           && entry->d_name[len-2] == 'c'
  405.           && entry->d_name[len-1] == 't'))
  406. #else
  407.       if (len > 2
  408.           && entry->d_name[len-2] == '.'
  409.           && entry->d_name[len-1] == 'm')
  410. #endif
  411.         {
  412.           retval[i] = strsave (entry->d_name);
  413.           if (no_suffix)
  414.         {
  415.           if (retval[i][len-1] == 'm')
  416.             retval[i][len-2] = '\0';
  417.           else
  418.             retval[i][len-4] = '\0';
  419.         }
  420.  
  421.           i++;
  422.  
  423.           if (i == num_max - 1)
  424.         {
  425. // Reallocate the array.  Only copy pointers, not the strings they
  426. // point to, then only delete the original array of pointers, and not
  427. // the strings they point to.
  428.  
  429.           num_max += 256;
  430.           char **tmp = new char * [num_max];
  431.           for (int j = 0; j < i; j++)
  432.             tmp[j] = retval[j];
  433.  
  434.           delete [] retval;
  435.  
  436.           retval = tmp;
  437.         }
  438.         }
  439.     }
  440.       closedir (dirp);
  441.     }
  442.  
  443.   retval[i] = 0;
  444.   num = i;
  445.  
  446.   return retval;
  447. }
  448.  
  449. char **
  450. get_fcn_file_names (int& num, int no_suffix)
  451. {
  452.   static int num_max = 1024;
  453.   char **retval = new char * [num_max];
  454.   int i = 0;
  455.  
  456.   char *path_elt = kpse_path_element (user_pref.loadpath);
  457.  
  458.   while (path_elt)
  459.     {
  460.       str_llist_type *elt_dirs = kpse_element_dirs (path_elt);
  461.  
  462.       str_llist_elt_type *dir;
  463.       for (dir = *elt_dirs; dir; dir = STR_LLIST_NEXT (*dir))
  464.     {
  465.       char *elt_dir = STR_LLIST (*dir);
  466.  
  467.       if (elt_dir)
  468.         {
  469.           int tmp_num;
  470.           char **names = get_fcn_file_names (tmp_num, elt_dir, no_suffix);
  471.  
  472.           if (i + tmp_num >= num_max - 1)
  473.         {
  474. // Reallocate the array.  Only copy pointers, not the strings they
  475. // point to, then only delete the original array of pointers, and not
  476. // the strings they point to.
  477.  
  478.           num_max += 1024;
  479.           char **tmp = new char * [num_max];
  480.           for (int j = 0; j < i; j++)
  481.             tmp[j] = retval[j];
  482.  
  483.           delete [] retval;
  484.  
  485.           retval = tmp;
  486.         }
  487.  
  488.           int k = 0;
  489.           while (k < tmp_num)
  490.         retval[i++] = names[k++];
  491.         }
  492.     }
  493.  
  494.       path_elt = kpse_path_element (0);
  495.     }
  496.  
  497.   retval[i] = 0;
  498.   num = i;
  499.  
  500.   return retval;
  501. }
  502.  
  503. // Convert X to the nearest integer value.  Should not pass NaN to
  504. // this function.
  505.  
  506. int
  507. NINT (double x)
  508. {
  509.   if (x > INT_MAX)
  510.     return INT_MAX;
  511.   else if (x < INT_MIN)
  512.     return INT_MIN;
  513.   else
  514.     return (x > 0) ? ((int) (x + 0.5)) : ((int) (x - 0.5));
  515. }
  516.  
  517. double
  518. D_NINT (double x)
  519. {
  520.   if (xisinf (x) || xisnan (x))
  521.     return x;
  522.   else
  523.     return floor (x + 0.5);
  524. }
  525.  
  526. // XXX FIXME XXX --  put these in some file, and make them extern.
  527.  
  528. static int
  529. all_strings (const Octave_object& args)
  530. {
  531.   int n = args.length ();
  532.   for (int i = 0; i < n; i++)
  533.     if (! args(i).is_string ())
  534.       return 0;
  535.   return 1;
  536. }
  537.  
  538. char **
  539. make_argv (const Octave_object& args, const char *fcn_name)
  540. {
  541.   char **argv = 0;
  542.   if (all_strings (args))
  543.     {
  544.       int n = args.length ();
  545.       argv = new char * [n + 1];
  546.       argv[0] = strsave (fcn_name);
  547.       for (int i = 0; i < n; i++)
  548.     argv[i+1] = strsave (args(i).string_value ());
  549.     }
  550.   else
  551.     error ("%s: expecting all arguments to be strings", fcn_name);
  552.  
  553.   return argv;
  554. }
  555.  
  556. // Return non-zero if either NR or NC is zero.  Return -1 if this
  557. // should be considered fatal; return 1 if this is ok.
  558.  
  559. int
  560. empty_arg (const char *name, int nr, int nc)
  561. {
  562.   int is_empty = 0;
  563.  
  564.   if (nr == 0 || nc == 0)
  565.     {
  566.       int flag = user_pref.propagate_empty_matrices;
  567.  
  568.       if (flag < 0)
  569.     {
  570.       gripe_empty_arg (name, 0);
  571.       is_empty = 1;
  572.     }
  573.       else if (flag == 0)
  574.     {
  575.       gripe_empty_arg (name, 1);
  576.       is_empty = -1;
  577.     }
  578.       else
  579.     is_empty = 1;
  580.     }
  581.  
  582.   return is_empty;
  583. }
  584.  
  585. // Format a list in neat columns.  Mostly stolen from GNU ls.  This
  586. // should maybe be in utils.cc.
  587.  
  588. ostrstream&
  589. list_in_columns (ostrstream& os, char **list)
  590. {
  591. // Compute the maximum name length.
  592.  
  593.   int max_name_length = 0;
  594.   int total_names = 0;
  595.   for (char **names = list; *names; names++)
  596.     {
  597.       total_names++;
  598.       int name_length = strlen (*names);
  599.       if (name_length > max_name_length)
  600.     max_name_length = name_length;
  601.     }
  602.  
  603. // Allow at least two spaces between names.
  604.  
  605.   max_name_length += 2;
  606.  
  607. // Calculate the maximum number of columns that will fit.
  608.  
  609.   int line_length = terminal_columns ();
  610.   int cols = line_length / max_name_length;
  611.   if (cols == 0)
  612.     cols = 1;
  613.  
  614. // Calculate the number of rows that will be in each column except
  615. // possibly  for a short column on the right.
  616.  
  617.   int rows = total_names / cols + (total_names % cols != 0);
  618.  
  619. // Recalculate columns based on rows.
  620.  
  621.   cols = total_names / rows + (total_names % rows != 0);
  622.  
  623.   names = list;
  624.   int count;
  625.   for (int row = 0; row < rows; row++)
  626.     {
  627.       count = row;
  628.       int pos = 0;
  629.  
  630. // Print the next row.
  631.  
  632.       while (1)
  633.     {
  634.       os << *(names + count);
  635.       int name_length = strlen (*(names + count));
  636.  
  637.       count += rows;
  638.       if (count >= total_names)
  639.         break;
  640.  
  641.       int spaces_to_pad = max_name_length - name_length;
  642.       for (int i = 0; i < spaces_to_pad; i++)
  643.         os << " ";
  644.       pos += max_name_length;
  645.     }
  646.       os << "\n";
  647.     }
  648.  
  649.   return os;
  650. }
  651.  
  652. // See if the given file is in the path.
  653.  
  654. char *
  655. search_path_for_file (const char *path, const char *name)
  656. {
  657.   char *retval = 0;
  658.  
  659.   char *tmp = kpse_path_search (path, name, kpathsea_true);
  660.  
  661.   if (tmp)
  662.     {
  663.       retval = make_absolute (tmp, the_current_working_directory);
  664.       free (tmp);
  665.     }
  666.  
  667.   return retval;
  668. }
  669.  
  670. DEFUN ("file_in_path", Ffile_in_path, Sfile_in_path, 3, 1,
  671.   "file_in_path (PATH, NAME)")
  672. {
  673.   Octave_object retval;
  674.  
  675.   DEFINE_ARGV("file_in_path");
  676.  
  677.   if (argc == 3)
  678.     {
  679.       char *fname = search_path_for_file (argv[1], argv[2]);
  680.  
  681.       if (fname)
  682.     retval = fname;
  683.       else
  684.     retval = Matrix ();
  685.     }
  686.   else
  687.     print_usage ("file_in_path");
  688.  
  689.   DELETE_ARGV;
  690.  
  691.   return retval;
  692. }
  693.  
  694.  
  695. char *
  696. file_in_path (const char *name, const char *suffix)
  697. {
  698.   char *retval = 0;
  699.  
  700.   char *nm = strsave (name);
  701.  
  702.   if (suffix)
  703.     {
  704.       char *tmp = nm;
  705.       nm = strconcat (tmp, suffix);
  706.       delete [] tmp;
  707.     }
  708.  
  709.   if (! the_current_working_directory)
  710.     get_working_directory ("file_in_path");
  711.  
  712.   retval = search_path_for_file (user_pref.loadpath, nm);
  713.  
  714.   delete [] nm;
  715.  
  716.   return retval;
  717. }
  718.  
  719. // See if there is an function file in the path.  If so, return the
  720. // full path to the file.
  721.  
  722. char *
  723. fcn_file_in_path (const char *name)
  724. {
  725.   if (name)
  726.     {
  727.       int len = strlen (name);
  728.  
  729.       if (name [len - 2] == '.' && name [len - 1] == 'm')
  730.     return file_in_path (name, "");
  731.       else
  732.     return file_in_path (name, ".m");
  733.     }
  734.   else
  735.     return 0;
  736. }
  737.  
  738. // See if there is an octave file in the path.  If so, return the
  739. // full path to the file.
  740.  
  741. char *
  742. oct_file_in_path (const char *name)
  743. {
  744.   if (name)
  745.     {
  746.       int len = strlen (name);
  747.  
  748.       if (name [len - 4] == '.' && name [len - 3] == 'o'
  749.       && name [len - 2] == 'c' && name [len - 1] == 't')
  750.     return file_in_path (name, "");
  751.       else
  752.     return file_in_path (name, ".oct");
  753.     }
  754.   else
  755.     return 0;
  756. }
  757.  
  758. char *
  759. undo_string_escape (char c)
  760. {
  761.   static char retval[2];
  762.   retval[1] = '\0';
  763.  
  764.   if (! c)
  765.     return 0;
  766.  
  767.   switch (c)
  768.     {
  769.     case '\a':
  770.       return "\\a";
  771.  
  772.     case '\b': // backspace
  773.       return "\\b";
  774.  
  775.     case '\f': // formfeed
  776.       return "\\f";
  777.  
  778.     case '\n': // newline
  779.       return "\\n";
  780.  
  781.     case '\r': // carriage return
  782.       return "\\r";
  783.  
  784.     case '\t': // horizontal tab
  785.       return "\\t";
  786.  
  787.     case '\v': // vertical tab
  788.       return "\\v";
  789.  
  790.     case '\\': // backslash
  791.       return "\\\\";
  792.  
  793.     case '"': // double quote
  794.       return "\\\"";
  795.  
  796.     default:
  797.       retval[0] = c;
  798.       return retval;
  799.     }
  800. }
  801.  
  802. char *
  803. undo_string_escapes (char *s)
  804. {
  805.   ostrstream buf;
  806.  
  807.   char *t;
  808.   while (t = undo_string_escape (*s++))
  809.     buf << t;
  810.   buf << ends;
  811.  
  812.   return buf.str ();
  813. }
  814.  
  815. DEFUN ("undo_string_escapes", Fundo_string_escapes,
  816.        Sundo_string_escapes, 1, 1,
  817.   "undo_string_escapes (STRING)")
  818. {
  819.   tree_constant retval;
  820.  
  821.   int nargin = args.length ();
  822.  
  823.   if (nargin == 1 && args(0).is_string ())
  824.     {
  825.       char *str = undo_string_escapes (args(0).string_value ());
  826.       retval = str;
  827.       delete [] str;
  828.     }
  829.   else
  830.     print_usage ("undo_string_escapes");
  831.  
  832.   return retval;
  833. }
  834.  
  835. /*
  836. ;;; Local Variables: ***
  837. ;;; mode: C++ ***
  838. ;;; page-delimiter: "^/\\*" ***
  839. ;;; End: ***
  840. */
  841.