Page 1 of 5 123 ... LastLast
Results 1 to 10 of 46

Thread: Beginner's Programming Challenge #12

  1. #1
    Join Date
    Jun 2009
    Location
    0000:0400
    Beans
    Hidden!

    Beginner's Programming Challenge #12

    Beginner Programming Challenge #12

    Welcome to the 12th Beginner Programming Challenge! As I was selected the winner of Challenge #11, I've been tasked with presenting a new challenge!

    If you need help, feel free to drop by on IRC on irc.freenode.net in #ubuntu-beginners-dev.

    As many of the previous challenges have been math related, I've opted for something a little more practical. This opens up a wider variety of languages and prevents an exercise in number crunching to make some languages less desireable (im looking at you, Ruby). It might immediately disqualify a language or two, but I think we'll be okay.

    Task:
    Implement a slimmed down version of the everyday command 'ls'. This may seem simple, but it's a good opportunity to write well modularized code as it's an extremely extensible task.

    Requirements:
    • Implement, at a minimum, the following flags: -l, -a, -F, -h. If you need a description of these, consult the man page. Most languages have a library similar to getopt that should make the actual flag parsing fairly straightforward. Sorry, Luasers.
    • Some subreqs for -l: You do not need to convert permissions to letter format. Octal is fine. Owner and group can be presented as numerical or converted to names. Columns should be fixed size so that output is neatly arranged.
    • All output should be well formatted. This means -l output should be in fixed columns. Non -l output can be a single column, as it would look in a pipe.
    • Allow for argument(s) to list a specific path rather than the pwd. If a directory is specified, list the contents of that directory. These arguments can be relative or absolute paths. If multiple arguments are specified, any output belonging to a directory that isn't the pwd should have a header giving the relative path. (This behavior can be seen by `ls /bin /sbin`).


    Extra Credit:
    • Add more flags: the more the merrier. Allow for long opts as well as short opts.
    • Toss out the -l subreqs: Convert octal permissions to letter format: e.g. -rwxr-xr-x, and GID/UID to real names.
    • Recursion.


    Caveats:
    Any entry using a call to system() or anything similar will be disqualified. The idea here is to traverse the file system using your own implementation, not someone else's.

    As always:
    Any overly obfuscated code will be immediately disqualified without account for programmers skill. Please remember that these challenges are for beginners and therefore the code should be easily readable and well commented.

    Any non-beginner entries will not be judged. Please use common sense when posting code examples. Please do not give beginners a copy paste solution before they have had a chance to try this for themselves.

    Please provide instructions for how to run your program along with your submission.

    And with that...
    Have fun!

  2. #2
    Join Date
    Dec 2007
    Location
    Behind you!!
    Beans
    978
    Distro
    Ubuntu 10.04 Lucid Lynx

    Re: Beginner's Programming Challenge #12

    Sounds like a great challenge, well pitched to the level of skill for beginners. I will definately be giving this a go when I get home tonight.

    Bodsda
    computer-howto
    Linux is not windows
    Fluxbox & Flux menu how to
    Programming is an art. Learn it, Live it, Love it!


  3. #3
    Join Date
    Jan 2010
    Location
    China
    Beans
    31
    Distro
    Ubuntu 10.04 Lucid Lynx

    Re: Beginner's Programming Challenge #12

    This isn't allowed right?

    PHP Code:
    import sysos
    print os.listdir(os.getcwd()) 
    Because otherwise, I don't really understand how we can find all the files in the working directory.

  4. #4
    Join Date
    May 2008
    Location
    UK
    Beans
    1,451
    Distro
    Ubuntu 8.04 Hardy Heron

    Re: Beginner's Programming Challenge #12

    pokebirdd : even though in Python your are right - that does produce a simple list of all the files - what about the other requirements of the challenge - like the flags - and the ability to pass in a directory to be listed as a parameter. That will take a lot more code - even in python.
    Tony - Happy to try to help.
    Unless otherwise stated - all code posted by me is untested. Remember to Mark the Thread as Solved.
    Ubuntu user number # 24044 Projects : TimeWarp - on the fly Backups

  5. #5
    Join Date
    Feb 2007
    Location
    Tuxland
    Beans
    Hidden!
    Distro
    Ubuntu Development Release

    Re: Beginner's Programming Challenge #12

    You can also kind of see how "ls" is implemented by looking at its source code. It's quite complicated, at least the GNU coreutils "ls" that is bundled with Ubuntu.
    Proud GNU/Linux zealot and lover of penguins
    "Value your freedom or you will lose it, teaches history." --Richard Stallman

  6. #6
    Join Date
    Feb 2006
    Beans
    68
    Distro
    Ubuntu 10.04 Lucid Lynx

    Re: Beginner's Programming Challenge #12

    Here is my 1st shot at it written in python. Had to look a lot of stuff up for this one. http://docs.python.org/library/ was my friend. Probably will add more functionality and format the output better latter on.

    Code:
    #! /usr/bin/python
    import sys, os, grp, pwd, datetime
    
    class Command:
        def __init__(self, apath, aoptions):
            self.path = apath
            self.l = False
            self.a = False
            self.F = False
            self.h = False
            for option in aoptions:
                if option == "-l":
                    self.l = True
                elif option == "-a":
                    self.a = True
                elif option == "-F":
                    self.F = True
                elif option == "-h":
                    self.h = True
    
        def display(self):
            #test to see if directory exists and
            # if you are allowed to read the directory
            if os.access(self.path, os.F_OK) and os.access(self.path, os.R_OK):
                #path name is good, process options
                print self.path + ":"
                for entry in sorted(os.listdir(self.path)):
                    #test whether or not to print the entry
                    if entry[0] != "." or self.a:
                        #check for -l option
                        if self.l:
                            stat_info = os.stat(self.path + "/" + entry)
                            user = pwd.getpwuid(stat_info.st_uid)[0]
                            group = grp.getgrgid(stat_info.st_gid)[0]
                            time = str(datetime.date.fromtimestamp(stat_info.st_mtime))
                            size = str(stat_info.st_size)
                            #check for -h option
                            if self.h:
                                size = str(stat_info.st_size / 1024) + "KB"
                            sys.stdout.write(str(stat_info.st_mode) + " ")
                            sys.stdout.write(str(stat_info.st_nlink) + " ")
                            sys.stdout.write(user + " ")
                            sys.stdout.write(group + " ")
                            sys.stdout.write(size + " ")
                            sys.stdout.write(time + " ")
                        #write entry
                        sys.stdout.write(entry)
                        #check for F option
                        if self.F:
                            if os.path.isdir(self.path + "/" + entry):
                                sys.stdout.write("/")
                            elif os.path.islink(self.path + "/" + entry):
                                sys.stdout.write("@")
                            elif os.access(self.path + "/" + entry, os.X_OK):
                                sys.stdout.write("*")
                        print
            else:
                print "Directory " + self.path + " does not exist,",
                print "or you do not have reading privledges."
    
    def main():
        #all commands are kept as a class in a list
        commands = []
        #set default pathname and options
        path_name = os.getcwd()
        options = []
        length = len(sys.argv)
        if length == 1:
            #no path or options given, add default command
            commands.append(Command(path_name, options))
        else:
            while True:
                #test for path or option
                if sys.argv[1][0] == "/":
                    #argv[1] is a path name, now check for options
                    path_name = sys.argv[1]
                    del sys.argv[1]
                    length -= 1
                    if length == 1:
                        #there are no options, add Command
                        commands.append(Command(path_name, options))
                        break
                    while sys.argv[1][0] == "-":
                        options.append(sys.argv[1])
                        del sys.argv[1]
                        length -= 1
                        if length == 1:
                            break
                    #now all the options are accounted for, add Command
                    commands.append(Command(path_name, options))
                elif sys.argv[1][0] == "-":
                    #an option was given with no path name
                    while sys.argv[1][0] == "-":
                        options.append(sys.argv[1])
                        del sys.argv[1]
                        length -= 1
                        if length == 1:
                            break
                    commands.append(Command(path_name, options))
                else:
                    sys.exit("Invalid entry.")
                if length == 1:
                    break
        #process all of the commands
        for item in commands:
            item.display()         
    
    if __name__ == "__main__":
        main()

  7. #7
    Join Date
    Nov 2009
    Location
    /dev/null
    Beans
    74

    Re: Beginner's Programming Challenge #12

    Perl


    Exclude mine from the contest..


    EDIT: Eliminated chdir and %ENV hash.
    EDIT2: Fixed a bug.

    Code:
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    #use File::stat;
    use Fcntl ':mode';
    use POSIX 'strftime';
     
     
    our($max_nlink, $max_uid, $max_gid, $max_size)=(0,0,0,0);
    our(%type, %exec, %perm, %nlink, %username, %groupname, %modtime, %filesize, %filename);
     
    my(@x, @arg);
    my($l, $F, $a, $h)=(0,0,0,0);
    my($dev, $inode, $mode, $nlink, $uid, $gid, $rdev, $size, $time, $mtime, $ctime, $blksize, $blocks);
     
     
    foreach (@ARGV){
      if(/^-/){
         if(/l/){ $l =1 ;}
         if(/F/){ $F =1 ;}
         if(/a/){ $a =1 ;}
         if(/h/){ $h =1 ;}
      }     
      else {
         push(@arg, $_);
      }
    }
     
    if(scalar(@arg)==0){
         push(@arg, '.');
    }
     
    foreach (@arg){
       my $argment = $_;
       if(-e $argment || die "$!\n"){
         if(-d $argment){
            if($argment ne '/'){
               $argment =~ s/\/$//;
            }
            opendir(D, "$argment") || die "$!\n";
            @x = readdir(D);
         }
         else{
            push (@x, $argment) ;
         }
       }
     
       foreach (@x){
         my $file = $_;
         my $fullpathname;
     
         if(-d $argment){
            $fullpathname = $argment.'/'.$file;     # need full path name to get the info unless traversing the current dir.
         }
         else {
            $fullpathname = $file;
         }
     
         ($dev, $inode, $mode, $nlink, $uid, $gid, $rdev, $size, $time, $mtime, $ctime, $blksize, $blocks) = lstat($fullpathname);
         $type{$file} = (type($fullpathname))[0];           # sub call to get the file type.
         $exec{$file} = (type($fullpathname))[1];           # sub call to get the file type.
         $perm{$file} = perm($mode);                        # sub call to get the permission info.
         $nlink{$file} = $nlink;                            # link count
         $username{$file} = getpwuid($uid);                 # convert uid to user name
         $groupname{$file} = getgrgid($gid);                # convert gid to group name
         $modtime{$file} = strftime("%Y-%m-%d %H:%M", localtime($mtime));   # convert mtime in second to 'Y-M-D H-M' format
         $filesize{$file} = filesize($h, $size);            # sub call to calculate file size
     
         filename($F, $l, $file, $fullpathname);    # sub call to append /, @ or * if needed. 
         fieldlen($nlink{$file}, $username{$file}, $groupname{$file}, $filesize{$file});    # sub call to calculate the field size.
       }
     
       if(-d $argment){
         print "$argment".":\n";
       }
       else {
         print "\n";
       }
     
       disp($l, $F, $a, $h, @x);
     
     
       if(-d $argment){
          closedir(D);
       }
     
       undef(@x);
     
     
    }
     
     
     
    sub perm {              # subroutine to convert the permission to rwx format.
       my $mode = shift(@_);
       my $p = sprintf("%04o", S_IMODE($mode)); 
       my (@tmp, $perm, $t);
     
       @tmp = split(//, $p);
     
       for(0..2) {
          $t = pop(@tmp);
          SWITCH: {
            ($t == 0) && do {unshift(@tmp, '---') ; last SWITCH; };
            ($t == 1) && do {unshift(@tmp, '--x') ; last SWITCH; };
            ($t == 2) && do {unshift(@tmp, '-w-') ; last SWITCH; };
            ($t == 3) && do {unshift(@tmp, '-wx') ; last SWITCH; };
            ($t == 4) && do {unshift(@tmp, 'r--') ; last SWITCH; };
            ($t == 5) && do {unshift(@tmp, 'r-x') ; last SWITCH; };
            ($t == 6) && do {unshift(@tmp, 'rw-') ; last SWITCH; };
            ($t == 7) && do {unshift(@tmp, 'rwx') ; last SWITCH; };
          }
       }
     
       $t = pop(@tmp);
     
       if($t==1 || $t==3 || $t==5 || $t==7){                # sticky 
            (substr($tmp[2],2,1) eq 'x') ? substr($tmp[2],2,1, 't') : substr($tmp[2],2,1, 'T');
       }
       elsif($t==2 || $t==3 || $t==6 || $t==7){             # sgid
            (substr($tmp[1],2,1) eq 'x') ? substr($tmp[1],2,1, 's') : substr($tmp[1],2,1, 'S');
       }
       elsif($t== 4 || $t==5 || $t==6 || $t==7){            # suid
            (substr($tmp[0],2,1) eq 'x') ? substr($tmp[0],2,1, 's') : substr($tmp[0],2,1, 'S');
       }
     
       $perm = join('', @tmp);
     
       return $perm ; 
    }
     
     
    sub type {              # subroutine to find the file type
      my $file = shift(@_);
      my $exec = 0;
      my $type;
     
      (-l $file) && do {$type = 'l'} ||     # symbolic link
      (-b $file) && do {$type = 'b'} ||     # block device
      (-c $file) && do {$type = 'c'} ||     # character device
      (-p $file) && do {$type = 'p'} ||     # pipe
      (-S $file) && do {$type = 's'} ||     # socket
      (-d $file) && do {$type = 'd'} ||     # directory
      do {$type = '-'};                     # others
     
      (-x $file) && !(-d $file) && do {$exec = 1} ;
     
      return ($type, $exec);
    }
     
     
    sub filesize {          # subroutine to convert the file size to in K/M/G.
      my ($h, $size) = @_;
      my $filesize = $size;
      my $len=length($filesize);
     
      if($h && $l){
        if($len > 9){     
            $filesize = substr($size/1000000000, 0, 3);
            $filesize =~ s/\.$//;
            $filesize = $filesize.'G';
        } 
        elsif($len > 6){     
            $filesize = substr($size/1000000, 0, 3);
            $filesize =~ s/\.$//;
            $filesize = $filesize.'M';
        } 
        elsif($len > 3){     
            $filesize = substr($size/1000, 0, 3);
            $filesize =~ s/\.$//;
            $filesize = $filesize.'K';
        } 
      }
     
      return $filesize;
    }
     
     
    sub filename {          # subroutine to append /, @ or * if needed.
      my ($F, $l, $file, $fullpathname) = @_;
      my ($tgt);
     
      $filename{$file} = $file;
     
      if($F){
        if (-l $fullpathname){
          $filename{$file} = $file.'@';
        }
        elsif(-d $fullpathname){
          $filename{$file} = $file.'/';
        }
        elsif (-f $fullpathname && $exec{$file}){
          $filename{$file} = $file.'*';
        }
      }    
     
      if($l && -l $fullpathname){
          $filename{$file} = "$file".' -> '.readlink($fullpathname) ;
      }
     
      return $filename{$file};
    }
     
     
     
    sub fieldlen {                  # subroutine to find the longest field for each items to determie the field size upon the printing.
      my ($nlink, $uid, $gid, $size) = @_;
      my($len);
     
      $len = length($nlink);
      ($len > $max_nlink) && do {$max_nlink = $len};
     
      $len = length($uid);
      ($len > $max_uid) && do {$max_uid = $len};
     
      $len = length($gid);
      ($len > $max_gid) && do {$max_gid = $len};
     
      $len = length($size);
      ($len > $max_size) && do {$max_size = $len};
    }
     
    sub disp {                      # subroutine to display the result.
       my ($l, $F, $a, $h, @x) = @_;
       my $flag = $l.$F.$a.$h ;
     
       SWITCH: {
         ($flag eq '0000') && do { shrt_prn(@x)     ; last SWITCH; };
         ($flag eq '0001') && do { shrt_prn(@x)     ; last SWITCH; };
         ($flag eq '0010') && do { shrt_all_prn(@x) ; last SWITCH; };
         ($flag eq '0011') && do { shrt_all_prn(@x) ; last SWITCH; };
         ($flag eq '0100') && do { shrt_prn(@x)     ; last SWITCH; };
         ($flag eq '0101') && do { shrt_prn(@x)     ; last SWITCH; };
         ($flag eq '0110') && do { shrt_all_prn(@x) ; last SWITCH; };
         ($flag eq '0111') && do { shrt_prn(@x)     ; last SWITCH; };
         ($flag eq '1000') && do { long_prn(@x)     ; last SWITCH; };
         ($flag eq '1001') && do { long_prn(@x)     ; last SWITCH; };
         ($flag eq '1010') && do { long_all_prn(@x) ; last SWITCH; };
         ($flag eq '1011') && do { long_all_prn(@x) ; last SWITCH; };
         ($flag eq '1100') && do { long_prn(@x)     ; last SWITCH; };
         ($flag eq '1101') && do { long_prn(@x)     ; last SWITCH; };
         ($flag eq '1110') && do { long_all_prn(@x) ; last SWITCH; };
         ($flag eq '1111') && do { long_all_prn(@x) ; last SWITCH; };
     
       }
    }
     
     
    sub shrt_prn {
      foreach (@_){
         if(!/^\./){                ## other than . and ..
            print "$filename{$_}\n";
         }
      }
    }
     
    sub shrt_all_prn {
      foreach (@_){                 ## all printed out
          print "$filename{$_}\n";
      }
    }
     
    sub long_prn {
      foreach (@_){
         if(!/^\./){                ## other than . and ..
            printf "%10s %${max_nlink}s %${max_uid}s %${max_gid}s %${max_size}s %15s %-s\n", $type{$_}.$perm{$_},  $nlink{$_}, $username{$_},  $groupname{$_},  $filesize{$_},  $modtime{$_},  $filename{$_};
         }
      }
    }
     
    sub long_all_prn {
      foreach (@_){
         printf "%10s %${max_nlink}s %${max_uid}s %${max_gid}s %${max_size}s %15s %-s\n", $type{$_}.$perm{$_},  $nlink{$_}, $username{$_},  $groupname{$_},  $filesize{$_},  $modtime{$_},  $filename{$_};
      }
    }
    Last edited by lostinxlation; April 26th, 2010 at 08:30 AM.

  8. #8
    Join Date
    Jan 2006
    Beans
    Hidden!
    Distro
    Ubuntu 10.10 Maverick Meerkat

    Re: Beginner's Programming Challenge #12

    I think this one should be restricted to C.

    the python submission uses listdir ... no readdir?
    the perl submission uses ENV and chdir ... chdir is not a system call.

    I believe the intent is to use system calls only, as in, you are writing ls and there is no shell.
    I am infallible, you should know that by now.
    "My favorite language is call STAR. It's extremely concise. It has exactly one verb '*', which does exactly what I want at the moment." --Larry Wall
    (02:15:31 PM) ***TimToady and snake oil go way back...
    42 lines of Perl - SHI - Home Site

  9. #9
    Join Date
    May 2007
    Beans
    251

    Re: Beginner's Programming Challenge #12

    Quote Originally Posted by falconindy View Post
    Any non-beginner entries will not be judged. Please use common sense when posting code examples. Please do not give beginners a copy paste solution before they have had a chance to try this for themselves.
    Sorry, I haven't been following these challenges for a while - been busy with work
    o. What is the criteria for defining 'beginner' in these challenges?
    o. I agree with slavik - at least this one should be restricted to C.

    I was wondering if I should submit the code too - hence the 1st question.
    It's been a while I've written 'ls' and a shell
    The Unforgiven

  10. #10
    Join Date
    Jan 2006
    Beans
    Hidden!
    Distro
    Ubuntu 10.10 Maverick Meerkat

    Re: Beginner's Programming Challenge #12

    found some old code I have written.

    This does ls on /

    Code:
    #include <stdio.h>
    #include <dirent.h>
    #include <sys/types.h>
    
    int main() {
            DIR *dptr;
            struct dirent *dstruct;
    
            dptr = opendir("");
            if (!dptr) { printf("problem\n"); return 1; }
            printf("dptr is not NULL\n");
            while ((dstruct = readdir(dptr)) != NULL) 
                    printf("%s\n",dstruct->d_name);
            closedir(dptr);
            return 0;
    }
    I am infallible, you should know that by now.
    "My favorite language is call STAR. It's extremely concise. It has exactly one verb '*', which does exactly what I want at the moment." --Larry Wall
    (02:15:31 PM) ***TimToady and snake oil go way back...
    42 lines of Perl - SHI - Home Site

Page 1 of 5 123 ... LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •