home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / user.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  15.5 KB  |  669 lines

  1. /* User Menu implementation
  2.    Copyright (C) 1994 Miguel de Icaza, Janne Kukonlehto
  3.    
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2 of the License, or
  7.    (at your option) any later version.
  8.    
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. #include <config.h>
  19. #include <stdio.h>
  20. #include "tty.h"
  21. #include <stdlib.h>    /* For free() */
  22. #include "fs.h"
  23. #include <string.h>
  24. #include <ctype.h>
  25. #include <errno.h>
  26.  
  27. #include "mad.h"
  28. #include "util.h"
  29. #include "global.h"
  30. #include "dialog.h"
  31. #include "color.h"
  32. #include "dir.h"
  33. #include "panel.h"
  34. #include "main.h"
  35. #include "user.h"
  36. #include "layout.h"
  37. #include "../vfs/vfs.h"
  38.  
  39. /* For the simple listbox manager */
  40. #include "dlg.h"
  41. #include "widget.h"
  42. #include "wtools.h"
  43.  
  44. #include "view.h" /* for default_* externs */
  45.  
  46. /* "$Id: user.c,v 1.16 1995/02/21 19:07:28 miguel Exp $" */
  47.  
  48. #define MAX_ENTRIES 40
  49. #define MAX_ENTRY_LEN 60
  50.  
  51. static char *data;
  52. static char *entries [MAX_ENTRIES];
  53. static int max_cols;
  54. static int menu_lines;
  55. static int debug_flag = 0;
  56. static int debug_error = 0;
  57. extern char *search_string (char *, char *);
  58.  
  59. /* Formats defined:
  60.    %%  The % character
  61.    %f  The current file (if non-local vfs, file will be copied locally and
  62.        %f will be full path to it).
  63.    %p  The current file
  64.    %d  The current working directory
  65.    %s  "Selected files"; the tagged files if any, otherwise the current file
  66.    %t  Tagged files
  67.    %u  Tagged files (and they are untagged on return from expand_format)
  68.    %view Runs the commands and pipes standard output to the view command
  69.        if %view is directly followed by '{', a list of keywords
  70.        ascii, hex, nroff, unformated and
  71.  
  72.    If the format letter is in uppercase, it refers to the other panel.
  73.  
  74.    expand_format returns a memory block that must be free()d.
  75. */
  76.  
  77. /* Returns how many characters we should advance if %view was found */
  78. int check_format_view (char *p)
  79. {
  80.     char *q = p;
  81.     if (!strncmp (p, "view", 4)){
  82.         q += 4;
  83.         if (*q == '{'){
  84.             for (q++;*q && *q != '}';q++){
  85.                 if (!strncmp (q, "ascii", 5)){
  86.                     default_hex_mode = 0;
  87.                     q += 4;
  88.                 } else if (!strncmp (q, "hex", 3)){
  89.                     default_hex_mode = 1;
  90.                     q += 2;
  91.                 } else if (!strncmp (q, "nroff", 5)){
  92.                     default_nroff_flag = 1;
  93.                     q += 4;
  94.                 } else if (!strncmp (q, "unformated", 10)){
  95.                     default_nroff_flag = 0;
  96.                     q += 9;
  97.                 } 
  98.             }
  99.             if (*q == '}')
  100.                 q++;
  101.         }
  102.         return q - p;
  103.     } else
  104.         return 0;
  105. }
  106.  
  107. int check_format_cd (char *p)
  108. {
  109.     if (!strncmp (p, "cd", 2))
  110.         return 3;
  111.     else
  112.         return 0;
  113. }
  114.  
  115. /* Check if p has a "^var\{var-name\}" */
  116. /* Returns the number of skipped characters (zero on not found) */
  117. /* V will be set to the expanded variable name */
  118. int check_format_var (char *p, char **v)
  119. {
  120.     char *q = p;
  121.     char *var_name;
  122.     char *value;
  123.     char *dots;
  124.     
  125.     *v = 0;
  126.     dots = 0;
  127.     if (!strncmp (p, "var{", 4)){
  128.     for (q += 4; *q && *q != '}'; q++){
  129.         if (*q == ':')
  130.         dots = q+1;
  131.         ;
  132.     }
  133.     if (!*q)
  134.         return 0;
  135.  
  136.     if (!dots || dots == q+5){
  137.         message (1,
  138.              " Format error on file Extensions File ",
  139.              !dots ? " The %%var macro does not have a default "
  140.              :       " The %%var macros does not have a variable " );
  141.         return 0;
  142.     }
  143.     
  144.     /* Copy the variable name */
  145.     var_name = xmalloc (dots - p, "check_format_var");
  146.     strncpy (var_name, p+4, dots-2 - (p+3));
  147.     var_name [dots-2 - (p+3)] = 0;
  148.  
  149.     value = getenv (var_name);
  150.     if (value){
  151.         *v = strdup (value);
  152.         return q-p;
  153.     }
  154.     free (var_name);
  155.     var_name = xmalloc (q - dots + 1, "check_format_var_2");
  156.     strncpy (var_name, dots, q - dots + 1);
  157.     var_name [q-dots] = 0;
  158.     *v = var_name;
  159.     return q-p;
  160.     }
  161.     return 0;
  162. }
  163.  
  164. /* strip file's extension */
  165. char *strip_ext(char *ss)
  166. {
  167.     register char *s = ss;
  168.     char *e = NULL;
  169.     while(*s) {
  170.         if(*s == '.') e = s;
  171.         if(*s == PATH_SEP && e) e=NULL; /* '.' in *directory* name */
  172.         s++;
  173.     }
  174.     if(e) *e = 0;
  175.     return ss;
  176. }
  177.  
  178. char *expand_format (char c)
  179. {
  180.     WPanel *panel;
  181.  
  182.     if (c == '%')
  183.     return strdup ("%");
  184.     
  185.     if (c >= 'a')
  186.     panel = current_panel;
  187.     else {
  188.     if (get_other_type () == view_listing){
  189.         panel = other_panel;
  190.     } else
  191.         return strdup ("");
  192.     }
  193.     if (!panel)
  194.     panel = cpanel;
  195.  
  196.     c |= 0x20;
  197.     
  198.     switch (c){
  199.     case 'f': 
  200.     case 'p': return strdup (panel->dir.list [panel->selected].fname);
  201.     case 'b':
  202.     return strip_ext(strdup (panel->dir.list [panel->selected].fname));
  203.     case 'd': return strdup (panel->cwd);
  204.     case 's':
  205.     if (!panel->marked)
  206.         return strdup (panel->dir.list [panel->selected].fname);
  207.  
  208.     /* Fall through */
  209.  
  210.     case 't':
  211.     case 'u':
  212.     {
  213.     int length = 2, i;
  214.     char *block;
  215.     
  216.     for (i = 0; i < panel->count; i++)
  217.         if (panel->dir.list [i].f.marked)
  218.         length += strlen (panel->dir.list [i].fname) + 1;
  219.  
  220.     block = xmalloc (length, "expand_format");
  221.     *block = 0;
  222.     for (i = 0; i < panel->count; i++)
  223.         if (panel->dir.list [i].f.marked){
  224.         strcat (block, panel->dir.list [i].fname);
  225.         strcat (block, " ");
  226.         if (c == 'u')
  227.             file_mark (panel, i, 0);
  228.         }
  229.     if (c == 'u'){
  230.         panel->marked = 0;
  231.         panel->total = 0;
  232.         panel->dirs_marked = 0;
  233.     }
  234.     return block;
  235.     } /* sub case block */
  236.     } /* switch */
  237.     return strdup ("");
  238. }
  239.  
  240. /* Checks for shell patterns defination */
  241. char *check_patterns (char *p)
  242. {
  243.     const char *def_name = "shell_patterns=";
  244.     int value;
  245.  
  246.     if (strncmp (p, def_name, sizeof (def_name)) == 0){
  247.     p += strlen (def_name);
  248.     value = *p++ - '0';
  249.     if (value == 0 || value == 1)
  250.         easy_patterns = value;
  251.     else
  252.         message (1, " Error ", " Invalid shell pattern defination \"%c\". ", value + '0');
  253.     }
  254.     while (*p == '\n' || *p == '\t' || *p == ' ') p++;
  255.     return p;
  256. }
  257.  
  258. /* Copies a whitespace separated argument from p to arg. Returns the
  259.    point after argument. */
  260. static char *extract_arg (char *p, char *arg)
  261. {
  262.     while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
  263.     p++;
  264.     while (*p && *p != ' ' && *p != '\t' && *p != '\n')
  265.     *arg++ = *p++;
  266.     *arg = 0;
  267.     if (!*p || *p == '\n')
  268.     p --;
  269.     return p;
  270. }
  271.  
  272. /* Tests whether the selected file in the panel is of any of the types
  273.    specified in argument. */
  274. static int test_type (WPanel *panel, char *arg)
  275. {
  276.     int result = 0; /* False by default */
  277.     int st_mode = panel->dir.list [panel->selected].buf.st_mode;
  278.  
  279.     for (;*arg != 0; arg++){
  280.     switch (*arg){
  281.     case 'n':    /* Not a directory */
  282.         result |= !S_ISDIR (st_mode);
  283.         break;
  284.     case 'r':    /* Regular file */
  285.         result |= S_ISREG (st_mode);
  286.         break;
  287.     case 'd':    /* Directory */
  288.         result |= S_ISDIR (st_mode);
  289.         break;
  290.     case 'l':    /* Link */
  291.         result |= S_ISLNK (st_mode);
  292.         break;
  293.     case 'c':    /* Character special */
  294.         result |= S_ISCHR (st_mode);
  295.         break;
  296.     case 'b':    /* Block special */
  297.         result |= S_ISBLK (st_mode);
  298.         break;
  299.     case 'f':    /* Fifo (named pipe) */
  300.         result |= S_ISFIFO (st_mode);
  301.         break;
  302.     case 's':    /* Socket */
  303.         result |= S_ISSOCK (st_mode);
  304.         break;
  305.     case 'x':    /* Executable */
  306.         result |= (st_mode & 0111) ? 1 : 0;
  307.         break;
  308.     case 't':
  309.         result |= panel->marked ? 1 : 0;
  310.         break;
  311.     default:
  312.         debug_error = 1;
  313.         break;
  314.     }
  315.     }
  316.     return result;
  317. }
  318.  
  319. /* Calculates the truth value of the next condition starting from
  320.    p. Returns the point after condition. */
  321. static char *test_condition (char *p, int *condition)
  322. {
  323.     WPanel *panel;
  324.     char arg [256];
  325.  
  326.     /* Handle one condition */
  327.     for (;*p != '\n' && *p != '&' && *p != '|'; p++){
  328.     if (*p == ' ' || *p == '\t')
  329.         continue;
  330.     if (*p >= 'a')
  331.         panel = current_panel;
  332.     else {
  333.         if (get_other_type () == view_listing)
  334.         panel = other_panel;
  335.         else
  336.         panel = NULL;
  337.     }
  338.     *p |= 0x20;
  339.  
  340.     switch (*p++){
  341.     case '!':
  342.         p = test_condition (p, condition);
  343.         *condition = ! *condition;
  344.         p--;
  345.         break;
  346.     case 'f':
  347.         p = extract_arg (p, arg);
  348.         *condition = panel && regexp_match (arg, panel->dir.list [panel->selected].fname, match_file);
  349.         break;
  350.     case 'd':
  351.         p = extract_arg (p, arg);
  352.         *condition = panel && regexp_match (arg, panel->cwd, match_file);
  353.         break;
  354.     case 't':
  355.         p = extract_arg (p, arg);
  356.         *condition = panel && test_type (panel, arg);
  357.         break;
  358.     default:
  359.         debug_error = 1;
  360.         break;
  361.     } /* switch */
  362.  
  363.     } /* while */
  364.     return p;
  365. }
  366.  
  367. /* General purpose condition debug output handler */
  368. void debug_out (char *start, char *end, int cond)
  369. {
  370.     static char msg [256];
  371.     int len;
  372.  
  373.     if (start == NULL && end == NULL){
  374.     if (cond == 0){
  375.         /* Init */
  376.         msg [0] = 0;
  377.     } else {
  378.         /* Show output */
  379.         if (!debug_flag)
  380.         return;
  381.         len = strlen (msg);
  382.         if (len)
  383.         msg [len - 1] = 0;
  384.         message (0, " Debug ", msg);
  385.         debug_flag = 0;
  386.     }
  387.     } else {
  388.     /* Save debug info for later output */
  389.     if (!debug_flag)
  390.         return;
  391.     /* Save the result of the condition */
  392.     if (debug_error){
  393.         strcat (msg, " ERROR: ");
  394.         debug_error = 0;
  395.     }
  396.     else if (cond)
  397.         strcat (msg, " True:  ");
  398.     else
  399.         strcat (msg, " False: ");
  400.     /* Copy condition statement */
  401.     len = strlen (msg);
  402.     if (end == NULL){
  403.         /* Copy one character */
  404.         msg [len] = *start;
  405.         msg [len + 1] = 0;
  406.     } else {
  407.         /* Copy many characters */
  408.         while (start < end){
  409.         msg [len++] = *start++;
  410.         }
  411.         msg [len] = 0;
  412.     }
  413.     strcat (msg, " \n");
  414.     }
  415. }
  416.  
  417. /* Calculates the truth value of one lineful of conditions. Returns
  418.    the point just before the end of line. */
  419. static char *test_line (char *p, int *result)
  420. {
  421.     int condition;
  422.     char operator;
  423.     char *debug_start, *debug_end;
  424.  
  425.     /* Init debugger */
  426.     debug_out (NULL, NULL, 0);
  427.     /* Repeat till end of line */
  428.     while (*p && *p != '\n'){
  429.     while (*p == ' ' || *p == '\t')
  430.         p++;
  431.     if (!*p || *p == '\n')
  432.         break;
  433.     operator = *p++;
  434.     if (*p == '?'){
  435.         debug_flag = 1;
  436.         p++;
  437.     }
  438.     while (*p == ' ' || *p == '\t')
  439.         p++;
  440.     if (!*p || *p == '\n')
  441.         break;
  442.     condition = 1;    /* True by default */
  443.  
  444.     debug_start = p;
  445.     p = test_condition (p, &condition);
  446.     debug_end = p;
  447.     /* Add one debug statement */
  448.     debug_out (debug_start, debug_end, condition);
  449.  
  450.     switch (operator){
  451.     case '+':
  452.     case '=':
  453.         /* Assignment */
  454.         *result = condition;
  455.         break;
  456.     case '&':    /* Logical and */
  457.         *result &= condition;
  458.         break;
  459.     case '|':    /* Logical or */
  460.         *result |= condition;
  461.         break;
  462.     default:
  463.         debug_error = 1;
  464.         break;
  465.     } /* switch */
  466.     /* Add one debug statement */
  467.     debug_out (&operator, NULL, *result);
  468.     
  469.     } /* while (*p != '\n') */
  470.     /* Report debug message */
  471.     debug_out (NULL, NULL, 1);
  472.  
  473.     if (!*p || *p == '\n')
  474.     p --;
  475.     return p;
  476. }
  477.  
  478. /* FIXME: recode this routine on version 3.0, it could be cleaner */
  479. void execute_menu_command (char *s)
  480. {
  481.     char *commands;
  482.     FILE *cmd_file;
  483.     int  expand_prefix_found = 0;
  484.     int parameter_found = 0;
  485.     char prompt [80] = "";
  486.     int  col;
  487.     char *file_name = tmpnam (0);
  488.  
  489. #ifdef _OS_NT
  490.     /* NT requires the command to end in .cmd */
  491.     file_name = copy_strings (file_name, ".cmd", NULL);
  492. #endif
  493.     if ((cmd_file = fopen (file_name, "w+")) == NULL){
  494.     message (1, " Error ", " Can't create temporary command file \n %s ",
  495.          unix_error_string (errno));
  496.     return;
  497.     }
  498.     commands = strchr (s, '\n');
  499.     if (!commands){
  500.     fclose (cmd_file);
  501.     unlink (file_name);
  502.     return;
  503.     }
  504.     commands++;
  505.     
  506.     for (col = 0; *commands; commands++){
  507.     if (col == 0 && (*commands != ' ' && *commands != '\t'))
  508.         break;
  509.         else if (col == 0)
  510.         while (*commands == ' ' || *commands == '\t')
  511.             commands++;
  512.     col++;
  513.     if (*commands == '\n')
  514.         col = 0;
  515.     if (parameter_found){
  516.         if (*commands == '}'){
  517.         char *parameter;
  518.         parameter_found = 0;
  519.         parameter = input_dialog (" Parameter ", prompt, "");
  520.         if (!parameter || !*parameter){
  521.             /* User canceled */
  522.             fclose (cmd_file);
  523.             unlink (file_name);
  524.             return;
  525.         }
  526.         fputs (parameter, cmd_file);
  527.         free (parameter);
  528.         } else {
  529.         int len = strlen (prompt);
  530.  
  531.         if (len+1 < sizeof (prompt)){
  532.             prompt [len] = *commands;
  533.             prompt [len+1] = 0;
  534.         } else
  535.             prompt [sizeof (prompt)] = 0;
  536.         }
  537.     } else if (expand_prefix_found){
  538.         expand_prefix_found = 0;
  539.         if (*commands == '{')
  540.         parameter_found = 1;
  541.         else{
  542.         char *text = expand_format (*commands);
  543.         fputs (text, cmd_file);
  544.         free (text);
  545.         }
  546.     } else {
  547.         if (*commands == '%')
  548.         expand_prefix_found = 1;
  549.         else
  550.         fputc (*commands, cmd_file);
  551.     }
  552.     }
  553.     fclose (cmd_file);
  554.     chmod (file_name, S_IRWXU);
  555.     execute (file_name);
  556.     unlink (file_name);
  557. }
  558.  
  559. void user_menu_cmd (void)
  560. {
  561.     char *menu, *p;
  562.     int  col, i, accept_entry = 1;
  563.     int  selected, old_patterns;
  564.     Listbox *listbox;
  565.     
  566.     if (!vfs_current_is_local ()){
  567.     message (1, " Oops... ",
  568.         " I can't run programs while logged on a non local directory ");
  569.     return;
  570.     }
  571.     
  572.     menu = copy_strings (home_dir, PATH_SEP_STR ".mc.menu", 0);
  573.     if (!exist_file (menu)){
  574.     free (menu);
  575.     menu = copy_strings (LIBDIR, "mc.menu", 0);
  576.     }
  577.  
  578.     if ((data = load_file (menu)) == NULL){
  579.     message (1, " Error ", " Can't open file %s \n %s ",
  580.          menu, unix_error_string (errno));
  581.     free (menu);
  582.     return;
  583.     }
  584.     free (menu);
  585.     
  586.     max_cols = 0;
  587.     for (i = 0; i < MAX_ENTRIES; i++)
  588.     entries [i] = 0;
  589.     selected = 0;
  590.  
  591.     /* Parse the menu file */
  592.     old_patterns = easy_patterns;
  593.     p = check_patterns (data);
  594.     for (menu_lines = col = 0; *p; p++){
  595.     if (col == 0 && !entries [menu_lines]){
  596.         if (*p == '#'){
  597.         /* A commented menu entry */
  598.         accept_entry = 1;
  599.         } else if (*p == '+'){
  600.         if (*(p+1) == '='){
  601.             /* Combined adding and default */
  602.             p = test_line (++p, &accept_entry);
  603.             if (selected == 0 && accept_entry)
  604.             selected = menu_lines;
  605.         } else {
  606.             /* A condition for adding the entry */
  607.             p = test_line (p, &accept_entry);
  608.         }
  609.         } else if (*p == '='){
  610.         if (*(p+1) == '+'){
  611.             /* Combined adding and default */
  612.             p = test_line (++p, &accept_entry);
  613.             if (selected == 0 && accept_entry)
  614.             selected = menu_lines;
  615.         } else {
  616.             /* A condition for making the entry default */
  617.             i = 1;
  618.             p = test_line (p, &i);
  619.             if (selected == 0 && i)
  620.             selected = menu_lines;
  621.         }
  622.         }
  623.         else if (*p > ' ' && *p < 127){
  624.         /* A menu entry title line */
  625.         if (accept_entry)
  626.             entries [menu_lines] = p;
  627.         else
  628.             accept_entry = 1;
  629.         }
  630.     }
  631.     if (menu_lines == MAX_ENTRIES)
  632.         break;
  633.     if (*p == '\t')
  634.         *p = ' ';
  635.     col++;
  636.     if (*p == '\n'){
  637.         if (entries [menu_lines]){
  638.         menu_lines++;
  639.         accept_entry = 1;
  640.         }
  641.         max_cols = max (max_cols, col);
  642.         col = 0;
  643.     }
  644.     }
  645.     max_cols = min (max (max_cols, col), MAX_ENTRY_LEN);
  646.  
  647.     /* Create listbox */
  648.     listbox = create_listbox_window (max_cols+2, menu_lines, " User menu ",
  649.                      "[Menu File Edit]");
  650.     
  651.     /* insert all the items found */
  652.     for (i = 0; i < menu_lines; i++)
  653.     LISTBOX_APPEND_TEXT (listbox, entries [i][0],
  654.                  extract_line (entries [i],
  655.                        entries [i]+MAX_ENTRY_LEN),
  656.                  entries [i]);
  657.  
  658.     /* Select the default entry */
  659.     listbox_select_by_number (listbox->list, selected);
  660.     
  661.     selected = run_listbox (listbox);
  662.     if (selected >= 0)
  663.     execute_menu_command (entries [selected]);
  664.  
  665.     easy_patterns = old_patterns;
  666.     do_refresh ();
  667.     free (data);
  668. }
  669.