LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

UsR2usr (libLQ version)

Posted 04-15-2012 at 04:42 PM by rainbowsally

Here's UsR2usr version 1.5 (using libLQ)

file: src/Usr2usr.cpp
purpose: allows dummy installations and renaming of paths and rpaths for creating binary installations without committing to overwriting original files until you're ready.
Code:
// UsR2usr.cpp

#include <stdio.h>        // printf, sprintf, etc.
#include <stdlib.h>       // exit()
#include <unistd.h>       // tons of stuff
#include <malloc.h>       // memory allocation
#include <string.h>       // strings and block comparisons and moves
#include <ctype.h>
#include "version.h"
#include <LQ/slist.h>

void dbg() {
}                // a breakpoint for kdbg.exec

// simplified syntax for comparing and moving str/mem
// case insensitive
#define streq(a, b) (strcasecmp(a, b) == 0)

// case sensitive
#define memeq(buf, str) \
(memcmp(buf, str, strlen(str)) == 0)

// move a string minus terminator
#define memstrcpy(buf, str) \
memcpy(buf, str, strlen(str))

enum {
  FILETYPE_ELF = 101,
  FILETYPE_TEXT,
  // others...
  FILETYPE_BINARY
};

static char errmsg[256];
typedef struct {
  char *buf;
  size_t buflen;
  bool armed_flag;      // = 0;
  bool etc_flag;        // = 0;
  bool elf_only_flag;     // = 0;
  bool pass_filter;
  int filetype;        // elf, text, other binary..
  int scan_number;      // = 0; // true if first file in scan
  const char *filename;
} VARS;

VARS *global_vars;
// #define obj (*global_vars)

VARS *vars_new() {
  VARS *vars = (VARS *) malloc(sizeof(VARS));
  memset(vars, 0, sizeof(VARS));
}

void vars_delete(VARS ** po) {
  // destructor
  if(*po)
    free(*po);
  *po = 0;
}

void obj_setPassFilter(VARS * o);
void obj_setFiletype(VARS * o);

// returns count of replacements proposed
int obj_scanAndReplace(VARS * o);

// returns 0 on success, else fatal
int obj_processFile(VARS * o, const char *filename);


const char *appname;

int usage(int errcode);
int show_version(int errcode);
int do_errmsg(int errcode, const char *msgfmt, const char *errvar);
int reorder_args(int argc, char **argv);

int main(int argc, char **argv) {
  dbg();

  appname = basename(argv[0]);
  int arg_offset = reorder_args(argc, argv);

  if(argc == 1)
    return usage(1);

  if(streq(argv[1], "--help"))
    return usage(0);

  VARS *o = vars_new();

  for(int i = 1; i <= arg_offset; i++) {
    if(streq(argv[i], "--armed"))
      o->armed_flag = 1;
    else if(streq(argv[i], "--etc"))
      o->etc_flag = 1;
    else if(memeq(argv[i], "--elf"))  // --elf | --elf-only
      o->elf_only_flag = 1;
    else if(streq(argv[i], "-v") || streq(argv[i], "--version"))
      return show_version(0);
    else {
      fprintf(stderr, "Unknown switch '%s'\n", argv[i]);
      exit(1);
    }
  }

  const char *dirname = argv[1 + arg_offset];

  // get list of files in dirname
  char tmpbuf[4096];
  char **filelist = slist_new();
  int numfiles;

  sprintf(tmpbuf, "find %s -type f", dirname);
  slist_pipeRead(&filelist, tmpbuf);

  numfiles = slist_count(filelist);

  // tell type of scan
  if(numfiles > 0) {
    printf("\n");
    sprintf(tmpbuf, "Scan type selected = [%s, %s]",
        o->armed_flag ? "ARMED" : "check",
        o->elf_only_flag ? "ELF only" : "all");
    printf("%s\n", tmpbuf);
    for(int i = 0; i < strlen(tmpbuf); i++)
      printf("-");
    printf("\n");
  } else {
    printf("%s: No files!\n", appname);
    return 1;
  }

  for(int i = 0; i < numfiles; i++) {
    const char *filename = filelist[i];
    int fatal = obj_processFile(o, filename);
    if(fatal)
      return 1;

  }
  vars_delete(&o);
  slist_delete(&filelist);
  return 0;
}

int show_version(int errcode) {
  printf("UsR2usr version %s", VERSION);
  return errcode;
}


int do_errmsg(int errcode, const char *msgfmt, const char *errvar) {
  if(errvar == 0)
    errvar = (char *)"";
  sprintf(errmsg, msgfmt, errvar);
  perror(errmsg);
  return errcode;
}


int usage(int errcode) {
#include "usage_str.dat"    // has one %s format for appname

  // test for presence of kdialog for the usage message
  char syscmd[4096];
  sprintf(syscmd, "kdialog -v >/dev/null 2>&1");
  int err = system(syscmd);

  if(err)
    printf(usage_str, appname);
  else {
    char msg[4096];
    sprintf(msg, usage_str, appname);
    sprintf(syscmd, "kdialog -msgbox \"%s\" -title \"Usage     \"",
        msg);
    err = system(syscmd);

    if(err)
      printf(usage_str, appname);
  }
  return errcode;
}

int reorder_args(int argc, char **argv) {
  // make literal copy of argv in temporary list
  char **tmp_args = (char **)malloc(argc * sizeof(char **));
  memcpy(tmp_args, argv, argc * sizeof(char **));

  int cnt = 1;        // current copy back number, app path is at [0] still
  int nswitches = 0;      // the value to return

  // copy switches back first
  for(int i = 1; i < argc; i++)
    if(tmp_args[i][0] == '-')
      argv[cnt++] = tmp_args[i];

  nswitches = cnt - 1;    // number of actual switches

  // copy non-switches back next
  for(int i = 1; i < argc; i++)
    if(tmp_args[i][0] != '-')
      argv[cnt++] = tmp_args[i];

  free(tmp_args);
  return nswitches;
}


void obj_setFiletype(VARS * o) {
  do {
    if(memeq(o->buf + 1, "ELF"))
      printf("");
    if(memeq(o->buf, "\177ELF"))
      printf("");
    if(o->buflen >= 4) {
      if(memeq(o->buf, "\177ELF")) {
        o->filetype = FILETYPE_ELF;
        break;
      }
    }
    int testlen = 32;
    int ascii_flag = 1;
    if(o->buflen < testlen)
      testlen = o->buflen;
    for(int i = 0; i < testlen; i++) {
      if(!isascii(o->buf[i])) {
        ascii_flag = 0;
        break;
      }
    }
    if(ascii_flag && (testlen > 0)) {
      o->filetype = FILETYPE_TEXT;
      break;
    }
    // other checks..
    // default
    o->filetype = FILETYPE_BINARY;
  } while(0);
}

// returns 0 on success, else fatal
int obj_processFile(VARS * o, const char *filename) {
  o->filename = filename;
  FILE *fp = fopen(filename, "rb+");
  if(!fp)
    return do_errmsg(1, "Can't open file '%s'", o->filename);

  fseek(fp, 0, SEEK_END);
  o->buflen = ftell(fp);
  fseek(fp, 0, SEEK_SET);

  o->buf = (char *)malloc(o->buflen);
  if(!o->buf)
    return do_errmsg(1, "Can't allocate memory", 0);

  fread(o->buf, 1, o->buflen, fp);

  // check file type and set accessible flag to indicate 
  // whether this is selected to be modified or not.
  obj_setFiletype(o);
  obj_setPassFilter(o);

  // scan for text /UsR or UsR/ and replace with 
  // /usr or usr/
  // save file only if not just checking and
  // filetype matches user selection
  if(o->pass_filter) {
    int count = obj_scanAndReplace(o);
    if(count < 0)
      return do_errmsg(1, "Can't scan file '%s'", o->filename);

    int nbytes;       // for debugger
    if(o->armed_flag) {
      //fseek(fp, 0, SEEK_SET);
      fseek(fp, 0, SEEK_SET);
      nbytes = fwrite(o->buf, 1, o->buflen, fp);
    }
  }
  fclose(fp);
  free(o->buf);
  return 0;
}

void obj_dumpContext(VARS * o, char *bufptr, bool change) {
  if(!o->scan_number)
    printf("\n");

  o->scan_number += 1;

  // hex dump 16 bytes of context

  unsigned char *cptr;
  int dumplen = 16;

  cptr = (unsigned char *)bufptr - 6;
  if((char *)cptr < o->buf)
    cptr = (unsigned char *)o->buf;

  char *bufend = o->buf + o->buflen;
  if(cptr > (unsigned char *)bufend)
    dumplen = (unsigned char *)bufend - cptr;

  if(!change)
    printf("NOT ");

  printf("To change in '%s' at offset 0x%0X:\n", o->filename,
       bufptr - o->buf);
  for(int i = 0; i < dumplen; i++) {
    printf("%0.2X ", cptr[i]);
  }
  printf(" %s\"...", change ? "CHG: \x1b[1;31m" : "---- \x1b[37m");
  for(int i = 0; i < dumplen; i++) {
    if(isprint(cptr[i]))
      printf("%c", cptr[i]);
    else
      printf(".");
  }
  printf("...\"\x1b[0;30m\n\n");
}


void obj_showStatus(VARS * o, char *bufptr, bool change) {
  if(o->armed_flag) {
    printf("Found 'UsR' in '%s' at 0x%X %s\n", o->filename,
         bufptr - o->buf, change ? "(CONVERTED)" : "(No Change)");
  } else {
    obj_dumpContext(o, bufptr, change);
  }
}


// returns number of changes made.
int obj_scanAndReplace(VARS * o) {
  int count = 0;
  char *ptr = o->buf;
  char *bufend = ptr + o->buflen;

  int found;
  found = 0;
  while(ptr < bufend) {
    found = 0;        // flag found in context
    const char *replstr = 0;  // flag and string to skip
    do {
      // UsR in correct context
      // check for any all at once
      if(strchr("/U\'\"", *ptr)) {
        if(*ptr == 'U') {
          static const char *this_str = "UsR/";
          if(memeq(ptr, this_str)) {
            found++;
            replstr = "usr/";
          } else if(memeq(ptr, "UsR")) {
            replstr = "UsR";
          }
        } else if(*ptr == '/') {
          static const char *this_str = "/UsR";
          if(memeq(ptr, this_str)) {
            found++;
            replstr = "/usr";
          }
        } else if(*ptr == '\'') {
          static const char *this_str = "\'UsR\'";
          if(memeq(ptr, this_str)) {
            found++;
            replstr = "\'usr\'";
          }
        } else if(*ptr == '\"') {
          static const char *this_str = "\"UsR\"";
          if(memeq(ptr, this_str)) {
            found++;
            replstr = "\"usr\"";
          }
        }
      }          // if(strchr

      //  found & show copy count steps
      //        | (replstr & steps | 1step)
      if(found) {
        obj_showStatus(o, ptr, true); // and show 'change'
        memstrcpy(ptr, replstr);
        ptr += strlen(replstr);
        count++;
      } else if(o->etc_flag) {
        // same thing but for EtC to etc
        if(strchr("/E\'\"", *ptr)) {
          if(*ptr == 'E') {
            static const char *this_str = "EtC/";
            if(memeq(ptr, this_str)) {
              found++;
              replstr = "etc/";
            } else if(memeq(ptr, "EtC")) {
              replstr = "etc";
            }
          } else if(*ptr == '/') {
            static const char *this_str = "/EtC";
            if(memeq(ptr, this_str)) {
              found++;
              replstr = "/etc";
            }
          } else if(*ptr == '\'') {
            static const char *this_str = "\'EtC\'";
            if(memeq(ptr, this_str)) {
              found++;
              replstr = "\'etc\'";
            }
          } else if(*ptr == '\"') {
            static const char *this_str = "\"EtC\"";
            if(memeq(ptr, this_str)) {
              found++;
              replstr = "\"etc\"";
            }
          }
        }        // if(strchr
        if(found) {
          obj_showStatus(o, ptr, true); // and show 'change'
          memstrcpy(ptr, replstr);
          ptr += strlen(replstr);
          count++;
        }
      }

      if(!found)      // not found
      {
        if(replstr) {
          obj_showStatus(o, ptr, false);  // show 'no change'
          ptr += strlen(replstr);
        } else
          ptr++;    // step 1
      }

    } while(0);       // do
  }              // while(bufptr...)
  return count;
}

void obj_setPassFilter(VARS * o) {
  // if filetype = elf or --all then true else false
  o->pass_filter = false;
  if(o->filetype == FILETYPE_ELF)
    o->pass_filter = true;
  else if(!o->elf_only_flag)
    o->pass_filter = true;
}
file: src/version.h
Code:
// created 
// #define VERSION "1.0"

// added '--check' option, tweeked help and more options and added version printout
// #define VERSION "1.1"

// minor changes.
// #define VERSION "1.2"

// Feb 2, 2012
// added  gui-based help if supported, made vars an 'object' for better
// debug view, etc.
// #define VERSION "1.3.0"

// Feb 9, 2012
// added /EtC to conversions for stuff like gtk+
// added warn if info/dir is present in share/info directory.
// #define VERSION "1.4.0"

// Apr 15, 2012 
// converted to use libLQ slist funcs
#define VERSION "1.5.0"
file: src/usage_str.dat
Code:
/* usage_str.txt converted with txt2cstr */
const char *usage_str =
  "Usage: %s [switches] dir\n"
  "\n"
  "  For use with 'configure' based installations to install into\n"
  "  prefix=/UsR and then converted to a binary installation into the\n"
  "  real /usr folder.\n"
  "\n"
  "  How: \n"
  "  This application scans and replaces the path UsR found in elf\n"
  "  binaries and other files allowing installation into /UsR and\n"
  "  then changing the path to a real system directory.\n"
  "\n"
  "  Several checks are performed on the text before committing to\n"
  "  a change and it can be first (without the --armed switch( which \n"
  "  you can verify in a hex viewers before creating the binary \n"
  "  installation.  \n"
  "\n"
  "  Is it fool proof?  Pretty close, but some caution is advised.\n"
  "  Keeping a backup of folders and doing a test installation on\n"
  "  your own system to see what changes is just good sense and as\n"
  "  usual, no warantees at this end.  Just best wishes and a bit\n"
  "  of optimism considering the alternatives which you might want\n"
  "  to check out if there are any.\n"
  "\n"
  "  Switches:\n"
  "    [none]          - prints file names and offsets where /UsR is\n"
  "                      found and set to be changed in an armed run.\n"
  "                      Shows a bit of context (hex/ascii dump) and\n"
  "                      can also be used to view in a hex editor.\n"
  "\n"
  "                      Includes text files and other other binaries\n"
  "                      in the scan in addition to elf binaries (the\n"
  "                      default).\n"
  "\n"
  "    --etc             Adds /EtC to /etc conversion for installations \n"
  "                      that need --infodir=/EtC to keep them from \n"
  "                      clobbering installed files.\n"
  "\n"
  "    --armed         - commit to changes reported in dry run (default).\n"
  "\n"
  "    --elf | --elf-only\n"
  "                    - Only change rpaths and other occurrances of \n"
  "                      UsR in a path context in elf binaries (libs\n"
  "                      and executables).\n"
  "\n"
  "  Tips: \n"
  "    * After intalling into prefix=/UsR, tar up the /UsR directory\n"
  "    where it is before moving the files to where you will build\n"
  "    your package to avoid broken links and missing files, etc.\n"
  "\n"
  "    * If after running with no switches and none of the non-elf files\n"
  "      need to be modified, you can add the '--elf' switch for the \n"
  "      armed run.\n"
  "\n"
  "    * For use as a non-root user, any directory tree is assumed to \n"
  "      have come from and is intended to be installed at root.  You \n"
  "      may run this before or after having moved the files.  Don't\n"
  "      run it in the source though. :-)\n"
  "\n"
  "  Note: The /UsR (prefix) folder you install into using the initial\n"
  "  compilation and installation must be located at the root of the\n"
  "  file system.  The number of letters in the path is critical to\n"
  "  this application's ability to accurately modify 'rpath' and other\n"
  "  text after you have installed into the /UsR dummy directory.\n"
  "\n"
  "    Copyright (C) 2012, Rainbow Sally\n"
  "\n" "    Released under the GPL license.\n" "\n";
[If you use mc to create the makefile start with the LQ++ template: 'mc2 -fetch LQ++' and edit the OUTNAME = UsR2usr.]
Posted in Uncategorized
Views 269 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 09:31 PM.

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