LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

Tooling Up: Clickable Script Programs

Posted 12-13-2012 at 02:10 PM by rainbowsally
Updated 12-23-2012 at 04:15 PM by rainbowsally

Note: shell-exec and lq-launch (the lq version) are now included in the mc2 libLQ d/load. They are in the examples/goodies folders and can be copied directly into your ~/bin folder and should be usable immediately with no changes to your paths required.
---------------------------------------

Today's Features
  • Clickable sequential backup utility. You'll need to create a program to copy it to the directory you want to use it in. That's pretty easy, but well.. maybe not. In any case, you're on your own here, and it IS POSSIBLE! Don't give up. :-)
  • FORCING newer linuxes to be nice.
  • Repost of the 'launch' utility. (The Computer Mad Science Team's Top Choice)

Linux needs to re-evaluate it's approach to competing with brain-dead vendor-friendly operating systems.

Much of the difficulty with running clickable scripts has arisen in the last 10 years and is UTTERLY USELESS as security measures in that the only people that our computer ends up protected from is US!

Until now! -- The Computer Mad Science Dept.

So, with that as a preface. Let's take control again.

The sequantial backup utility uses the MsgBox created a couple of entries back.

Here's the file. (Doesn't require a makefile. Should run, even if you didn't build the MsgBox.)

Note: The additional files may not be needed, but if they are, they will help you run ALL of your clickable scripts, not just this one. Either the mouse click method or select and ENTER on the icon should work, if not both. Both work on my linux.


file: bak-tgz.exec
purpose: utility (executable)
Code:
#!/bin/sh

# start where the file was clicked on
CURDIR=`dirname "$0"`
cd "$CURDIR"

# count the number of files in the backup folder
NENTRIES=01

mkdir bak 2>/dev/null

if ! ls bak/* 2>/dev/null 
then
   NENTRIES=00
fi


for i in bak/*
do
    let NENTRIES=NENTRIES+1
done


# make all entries two digits for display in konqueror, dolphin, etc.
if [ $NENTRIES -lt 10 ]
then
echo "doing..."
    NENTRIES=0$NENTRIES
fi

NEWDIR=bak/$NENTRIES

# do a bit of cleanup first
rm *~
rm */*~
rm */*/*~
rm */*/*/*~
rm */*/*/*/*~


mkdir $NEWDIR

# grab the usual culprits and copy them to the backup folder
# and make a cursory list of what got copied (not recursive).
cp -f -t $NEWDIR *
cp -r -t $NEWDIR src/
cp -r -t $NEWDIR include/
cp -r -t $NEWDIR lib/

DIRLIST=`ls $NEWDIR`
cd bak


# create a message and include the file list
MSG="Creating backup '$NEWDIR.tgz'
---------------------------------------
$DIRLIST"

## Testing various message display possibilities
# xmessage -default "okay" -center "$MSG"; exit
# xterm -hold -e sh "echo '$MSG'"; exit
# kdialog -msgbox "$MSG"; exit
# MsgBox -i "$MSG"; exit

# display the message and filelist 
if ! MsgBox -i "$MSG"; then
  if ! kdialog -msgbox "$MSG"; then
      if ! xmessage -default "okay" -center "$MSG"; then
          xterm -e 'echo "$MSG"'
      fi
  fi
fi

#pack-tgz $NENTRIES
if tar -caf $NENTRIES.tgz $NENTRIES; then
  # no errors? remove the dir safely (i.e., in two steps)
  rm -r $NENTRIES/*               # first the contents
  rmdir $NENTRIES                 # THEN the dir itself
fi
This SHOULD run as an application mime type x-executable, but if it doesn't, here's shell-exec, which can be used to run it.

It uses 'launch' (which we built here a while back) but you could also probably use kshell4, or just call the app with an '&' at the end. 'launch' is a bit better, but whatever works for you is fine.


file: shell-exec
purpose: utility (executable)
Code:
#!/bin/sh
msg="
  PWD=  $PWD
  PATH= $PATH
  FILE= $1
  LD_LIBRARY_PATH = $LD_LIBRARY_PATH
 "
# kdialog -msgbox "$msg"

# Add code as needed here, for example,
cd `dirname "$0"` # to go to the directory where the file was clickded on
launch "$1"

# where launch can be omitted or you can call the 'launch' program
# that does roughly, this
# nohup "$1" >/dev/null 2>&1 &; return 0
# which may or may not reparent the app to 'init', but it acts the same
# either way.  (Thanks to kubicle and vinny for the script-based versions
# of this utility.)

##########################################################################
## NOTE:
## If you set your scripts to open with shell-exec (allows pressing ENTER
## on the files to run them), the ~/.local/share/applications/mimeapps.list
## file may need to be edited manually from time to time to remove or to 
## add lines to remove conflicts with your choice.  
## shell-exec should be the first in the list for types:
## application/x-shellscript
## application/x-executable
## (and possibly also)
## application/x-csh (bug in kde4 konqueror?).
## Also, the Exec line in the shell-exec desktop file in that same folder
## should probably be
## MimeType=application/x-executable;application/x-shellscript
See the notes above if you have any trouble. TEST first to see if it will do the job and then set it as the default application to open these files with.

Here's launch again. This is probably a newer version than was posted here at the blog before.

file: src/launch.c
purpose: source file
Code:
// does execl() in a fork returning control to a terminal immediately.

#include <stdio.h> // stderr
#include <stdlib.h> // EXIT_FAILURE
#include <unistd.h> // fork(), execl(), _exit()
#include <sys/types.h> // pid_t
#include "usage.str"
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include "version.h"

struct
{
  char warn;
}flg;

int usage(int err);
int unparse_args(char* cmdline, int limit, int argc, char** argv);
int print_version(int err);
// if error, returns argc of option to list in error display
int parse_options(int argc, char** argv);


int main(int argc, char** argv)
{
  int err = 0;
  
  // help and version switches
  if(argc == 1)
    return usage(0);
  
  if(argc == 2)
  {
    if(strcmp(argv[1], "--help") == 0)
      return usage(0);
    if(strcmp(argv[1], "-v") == 0)
      return print_version(0);
  }

  err = parse_options(argc, argv);
  if(err) 
  {
    printf("Unknown launch option '%s'\n", argv[err]);
  }
  
  // reassemble commandline
  char args[1024];
  err = unparse_args(args, 1000, argc, argv);
  if(err)
  {
    if(err == ENOMEM)
      fprintf(stderr, "Out of memory or args list too long\n");
    else if (err == E2BIG)
      fprintf(stderr, "Argument list too long\n");
    return 1;
  }

  // if -q suppress all feedback from the app
  if(!flg.warn)
  {
    fclose(stdout);
    fclose(stderr);
    strcat(args, " > /dev/null 2>&1");
  }

  // do the fork and execute the commandline
  pid_t pid;

  pid = fork ();
  if (pid == 0)
  {
    // command path, command name, arg1, arg2, ...
    execl ("/bin/sh", "sh", "-c", args, NULL);

    // _exit(code) for threads and forks.
    _exit (EXIT_FAILURE);
  }
  else if (pid < 0)
    return -1;
  else
    return 0;
}


int usage(int err)
{
  if(err)
    fprintf(stderr, "%s", usage_str);
  else
    fprintf(stdout, "%s", usage_str);
  return err;
}

// returns 0 on success, or one of the following
// #define ENOMEM    12  /* Out of memory */
// #define E2BIG    7  /* Argument list too long */
// defined in /usr/include/asm-generic/errno-base.h

int unparse_args(char* cmdline, int limit, int argc, char** argv)
{
  cmdline[0] = 0;
  if(argc < 2)
    return 0;
  
  char* buf = (char*) malloc(limit+256);
  if(!buf)
    return errno = ENOMEM;
  
  char* p = buf;
  char* stopat = buf+limit -1;
  int len;

  // point to args list
  argc --;
  argv ++;

  // skip option switches
  if(flg.warn)
  {
    argc --;
    argv ++;
  }
  
  //////////////////////////////////////
  // recreate command and commandline
  //////////////////////////////////////
  
  for(int i = 0; i < argc; i++)
  {
    p += sprintf(p, "%s ", argv[i]);
    if(p >= stopat)
    {
      free(buf);
      return errno = E2BIG;
    }
  }
  p[-1] = 0; // terminate
  memcpy(cmdline, buf, p-buf);
  free(buf);
  return 0;
}

int print_version(int err)
{
  printf("Launch version %s\n", VERSION);
  return err;
}

int parse_options(int argc, char** argv)
{
  // if no options, return
  if(argv[1][0] != '-')
    return 0;
  
  // init opts
  flg.warn = 0; // default

  // check args (only 1 so far)
  int arg = 1;

  // assume error each iteration
  int err = 1;

  // check and break if match
  if(strcmp(argv[arg], "-a") == 0)    
  { 
    flg.warn = 1;
    err = 0;
  }
  
  // return argN where error occurred or 0
  if(err) 
    return arg;
  else 
    return 0;
}

This is a clickable script. txt2str is in the mc2 examples included in the d/load.

file: src/update-strings.exec
purpose: utility (executable)
Code:
#!/bin/sh
cd `dirname "$0"`
printf "" >.msg
txt2str usage.txt usage_str usage.str 2>>.msg
echo 'done.' >> .msg
kdialog --msgbox "$(<.msg)"
rm -f .msg

file: src/usage.str
purpose: source file
Code:
/* usage.txt converted with txt2cstr */
const char* usage_str =
    "\n"
    "Usage launch [opt] <command> [parameters]\n"
    "\n"
    "Launch just returns control to the terminal immediately after \n"
    "starting an app.  Useful for launching one or more other \n"
    "applications while retaining ability to use the terminal.\n"
    "\n"
    "Options for launch must preceed the command and it's arguments.\n"
    "\n"
    "Recognized optiions are:\n"
    "  --help          this display\n"
    "  -v              version\n"
    "  -a              allow feedback from the launched app.\n"
    "\n"
    ;

file: src/usage.txt
purpose: source file
Code:
Usage launch [opt] <command> [parameters]

Launch just returns control to the terminal immediately after 
starting an app.  Useful for launching one or more other 
applications while retaining ability to use the terminal.

Options for launch must preceed the command and it's arguments.

Recognized optiions are:
  --help          this display
  -v              version
  -a              allow feedback from the launched app.

file: src/version.h
purpose: source file
Code:
// initial version
// #define VERSION "1.0"

// changed switch to -w to allow warnings to display.  Defaults to
// supress warnings and other feedback from launched applications.
// #define VERSION "1.1"

// Added these notes in the version header.
// #define VERSION "1.2"

// Can't just close stdout or kde will spin out.  Trying redirect
// to /dev/null for kde and also closing stdin and stderr to -1
// for normal user.
// #define VERSION "1.3"

// Changing switch to -a for 'allow' error and warnings instead
// of -w, which might be useful for a windows style /w(ait) 
// if anyone uses this for a 'start' clone.
// #define VERSION "1.4"

// Release .1 corrects a misspelling in this file.
#define VERSION "1.4.1"

Put the sources in a 'src' subdir to make sure it works first time and change the output name to launch.

To create the makefile I used my old AutoBake utility. Fun stuff. But it will build with "mc2 -fetch" Grab the defs for a C 32 bit build and it should 'make' straight away.

Rarely fails and when it does, the fix is usually very simple. Something like adding a lib.

The Computer Mad Science Team

:-)
Posted in Uncategorized
Views 408 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 02:52 AM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration