LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

Simple C example for beginners and bug fix for libLQ-qt version 3.0.x

Posted 01-13-2013 at 06:57 PM by rainbowsally

Simple C example for beginners and bug fix for libLQ-qt version 3.0.x

[v.3.0 (mc2 and libLQ) will no longer be the current version(s) but it still works and we have a bug fix here that applies to all 3.0.x versions.]

Today's feature:
  • A simple C/C++ example.
  • A converter from relative paths to absolute.
  • A bug fix for the libLQ-qt/mc2 3.0 d/load.

While 'canonicalize_file_name()' is great for paths that don't have macros in them, and for dereferencing symlinks, this expands macros and doesn't dereference the links.

There was at least one bug in the 3.0 lib version, this doubles as a bug fix. Just pop it into the libLQ src folder and 'make install'.

Here's the file. It itself has no dependenices on the lib so this might be a good one for folks just starting out in C or C++.

file: src/filename_absolute.cpp
purpose: source file
Code:
// Simple C -- translate paths to canonical form with or without macros.

#include <sys/types.h> // probably not needed here
#include <stdlib.h>    // getenv()
#include <string.h>    // str*()
#include <stdio.h>     // sprintf()

static char* find_double_dot_path(char* p);
static char* find_dot_path(char* p);

bool filename_absolute(char* buf, size_t buflen, const char* filename)
{
  //int maxlen = strlen(getenv("HOME"));
  //maxlen = MAX(strlen(getenv("PWD"), maxlen);
  //maxlen += 256;

  // bail if null pointer
  if(!filename)
  {
    *buf = 0;
    return false;
  }

  char absname[1024]; // overkill, we hope, though linux allows 2K paths
  
  // init string printer var
  char* p = absname;
  *p = 0;
  
  int changed;
  
  ////////////////////////////////////////////
  // prefixes -- these are OR-ed

  changed = 0;
  
  // expand ~
  if(strstr(filename, "~/") == filename)
  {
    p += sprintf(absname, "%s", getenv("HOME"));
    filename ++;
    changed++;
  }
  
  // expand ../
  if(!changed && (strstr(filename, "..") == filename))
  {
    char* updir = strrchr(getenv("PWD"), '/');
    if(updir)    
    {
      p += sprintf(absname, "%s", getenv("PWD"));
      // delete the updir
      p -= strlen(updir);
      filename +=2;
      changed++;
    }
    // else leave this broken. Can't have a ../ above root
  }
  
  // expand ./
  if(!changed && (strstr(filename, ".") == filename))
  {
    p += sprintf(absname, "%s", getenv("PWD"));
    filename ++;
    changed++;
  }
  
  // no prefix and not pointing to '/', so it must be in the current directory
  if(!changed && (*filename != '/'))
    p += sprintf(absname, "%s/", getenv("PWD"));
  
  
  /////////////////////////////////////////////////////
  // get the rest of the path loaded into absname for
  // cleanup cycle
    
  p += sprintf(p, "%s", filename);
  
  //////////////////////////////////////////////////////
  // now go through absname and remove unnecessary stuff
  
  changed = 1;
  
  while(changed)
  {
    // loop until stays unchanged
    changed = 0; 
    
    // remove all ..
    p = absname;
    for(;;)
    {
      char* e = find_double_dot_path(p);
      if(e)
      { // strip previous part of path and the double dots
        *e = 0;
        p = strrchr(p, '/');
        if(!p)
        {
          *e = '/';
          break; // no p
        }
        strcpy(p, e + 3);
        changed++;
      }
      else // no e
        break;
    } // ..
    
    
    // remove all .
    p = absname;
    for(;;)
    {
      p = find_dot_path(p);
      if(!p)
        break;

      // step to '.' and copy from next char to here.
      // this may create a double '//' if it's not at
      // the end of a path but we'll get all those in 
      // a sec.
      p++;
      strcpy(p, p + 1);
      changed++;
    } // .
    
    // reduce redundant // to to /
    p = absname;
    for(;;)
    {
      p = strstr(p, "//");
      if(!p)
        break;
      strcpy(p, p + 1);
      changed++;
    } // .
  } // while changed
  
  if(buflen <= strlen(absname))
  {
    *buf = 0;
    return false; // buffer too short
  }
  // else
  strcpy(buf, absname);
  return true;  
}

static char* find_double_dot_path(char* p)
{
  if(!p)
    return 0;
  
  // between path parts
  char* found = strstr(p, "/../");
  if(found)
    return found;
  
  // else at end        
  found = strstr(p, "/..");
  if(found && !found[3])
      return found;
  
  else return 0;  
}

static char* find_dot_path(char* p)
{
  if(!p)
    return 0;
  
  // between path parts
  char* found = strstr(p, "/./");
  if(found)
    return found;

  // else at end
  found = strstr(p, "/.");
  if(!p[2])
    return found;
  else
    return 0;
}

Here's a tester for it. See if you can fool it. Note that "..this_file..txt" (note the dots) IS a valid file name for a file in the current directory, but it will be a hidden file, of course.


file: src/main.cpp
purpose: source file
Code:
// Usage: Input the path you want to check

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdbool.h> // if you try this in straight C

void dbg(){}

extern bool filename_absolute(char* buf, size_t buflen, const char* filename);

int usage(int errocode);

int main(int argc, char *argv[])
{
  dbg();
  
  if(argc != 2)
    return usage(1);
  
  char buf[256];
  size_t buflen = 256;
  
  const char* filename = argv[1];
  if(!filename_absolute(buf, buflen, filename))
  {
    printf("Failed to get absolute file name for\n'%s'", filename);
    return 1;
  }
  else
  {
    printf(
      "In  : '%s'\n"
      "Out : '%s'\n\n",
      filename, buf
    );
  }
}


int usage(int errcode)
{
  printf(
    "Usage:\n  No, no, no, no...\n"
    "  All wrong, you got it, young Skywalker\n"
    "  You must use the source\n"
    ":-)\n\n");

return errcode;
}

Here's some makefile definitions, if you want to create a makefile with mc2. Type mc2 -init, then after that just 'make update' if you add or remove files from the build.

file: mc2.def
purpose: makefile definitions
Code:
OUTNAME = main

SRCDIR = src
OBJDIR = o
BINDIR = .

# compile function overrides
COMPILE = g++ -m32 -c -o # COMPILE <output_file> ...
# CFLAGS = -Wall -g3 # debug
CFLAGS = -Wall -O2 # optimized
INCLUDE = -I $(SRCDIR) -I /usr/include 

# link function overrides
LINK = g++ -m32 -o # LINK <output_file> ...
LDFLAGS = 
LIB = -L/usr/lib

clean:
  @rm -f $(MAIN)
  @rm -f $(OBJ)
  @rm -f *~ */*~ */*/*~ */*/*/*~
Here's the last of the 3.0 series mc2 uploads.
http://www.linuxquestions.org/questi...support-34783/

And here's some stuff I checked. You can run this as 'sh testit.txt'. It's assumed that the output file is simply named main.

file: testit.txt
purpose: avoid excessive typing
Code:
echo "Testing some paths with the filename_absolute routine."
echo
main  "."
main  ".."
main  "./filename"
main  "../filename"
main  "../.."
main  "needs/pwd/prefix"
main  "~/needs/HOME/prefix"
main  "./needs/pwd/(dot)/prefix"
main  "../needs/pwd-1/prefix"
main  "/needs/some/../double/.and/./single/dot/../dots/./removed/here/.."
main  "/checking/..valid/...but/.crazy/./or/hidden/../../names"
The Computer Mad Science Team

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

Comments

 

  



All times are GMT -5. The time now is 07:33 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