Table of Contents

Debian Install

This install was scrapped due to not being able to figure out the additional section and the Ubuntu install started and completed

debian-lenny[text]

  1. F12: boot up menu
  2. Integrated NIC
  3. 32-bit (i386)
  4. Debian/i386 Netboot
  5. Install Lenny/stable [text]
  1. language: English
  2. Country: United States
  3. Keyboard layout: American English
  4. Host Name: Unicorn
  5. Domain Name: offbyone.lan
  6. Debian archive mirror hostname: mirror (for this scroll to the top and enter manually)
  7. Use default settings and continue until you can select a time zome.
  8. Time zone: Eastern
  9. Partitioning Method: Guided - use entire disk
  10. Then select the drive you want. In my case IDE1 master (hda) - 80GB Maxtor 6Y080L0 is the only one available.

Since this is my first time ever doing this, the partitioning scheme I chose was all files in one partition. Once that is selected continue a couple times until it installs the base system stuff.

  1. Root password: password (pretty much the best pw out there, no one would ever think of this!)
  2. verify pw
  3. Full name for the new user: Jesse Short
  4. User account name: ender
  5. User account password: god (Pretty much the second best pw out there, for the same reason stated above)
  6. verify pw
  1. Participate in package usage survey?: No (can change this later on by running: “dpkg-reconfigurepopularity-contest”)
  2. software to install: none in our case since its all server stuff but one (web, print, DNS, file, mail, SQL, and the odd one, laptop)
  1. Install GRUB boot loader to master boot record: Yes
  2. Continue

Now it will boot up a login screen. Enter the created user account name earlier (ender) and then the password (god).

  1. After logging in for our purposes we will check the window size and its color depth. To do this: Applications → Accessories → Terminal:

xwininfo → click on the desktop. The size and color are correct for our case: w:1280, H:1020, Depth:24.

  1. To log into lab46: Applications → Accessories → Terminal: ssh jshort6@lab46

+++Additional+++

  • Dual-head Accelerated graphics with Compiz Desktop effects

System → Administration → Add/Remove Applications → System Tools → scroll down until you find: Compiz Fusion Icon or you can search for it. Click the box so an 'X' appears and hit Apply. After quite a bit of time with Matt, he said to switch to Ubuntu. Therefore, the debian-lenny install is no more.

Ubuntu Install

  1. F12
  2. Integrated NIC
  3. 32-bit (i368)
  4. Ubuntu/i386 Netboot
  5. Install Jaunty “9.04” [text]
  1. language: English
  2. Country: United States
  3. Detect keyboard layout: no
  4. Origin of keyboard: USA
  5. keyboard layout: USA
  6. Hostname: Unicorn
  7. mirror: Go to the top and enter manually: mirror
  8. continue
  9. continue
  10. Time Zone: Eastern
  11. Partitioning Method: Guided - use entire disk
  12. In our case: SCSI1 (0,0,0) (sda) - 40GB ATA WDC WD400BB-75FJ
  13. write changes to disk: Yes
  1. User name: Jesse Short
  2. Username: ender
  3. User password: god
  4. Verificate
  5. In our case: it's a weak pw and it yelled at me, but I continued anyway :)
  6. Encrypt home directory: no
  7. Manage upgrades: no automatic updates
  8. Choose software to install: Ubuntu desktop
  9. Is system clock set to UTC: Yes
  10. Continue
  1. log in: ender
  2. pw: god

Should be a pop-up about drivers - click on it and enter your password. Let it install, close when done, and restart.

  1. Check display settings: System → Preferences → Display → Yes → X Screen 0
  2. Internets works → Firefox

Terminal: Applications → Accessories → Terminal → ssh jshort6@lab46 → yes → password

+++Additional+++

  • Dual-head Accelerated graphics with Compiz Desktop effects

With the pop-up earlier the driver for advanced graphics should be installed. Right click on desktop → Change desktop background → visual effects (tab) → Extra

In case you skipped the install ubuntu desktop selection (or you pressed enter instead of spacebar then enter) do this:
log in → sudo bash (to become root) → passwd → choose password → verificate → tasksel (task selection)

Linux VM Server Install

Bank: 00
Slot: 01
DO NOT FORGET TO UNCOMMENT IN: /etc/xen-tools/xen-tools.conf (towards the end of the install)

  1. f12: boot up menu
  2. Integrated NIC
  3. 32 bit
  4. Debian/i386 Netboot
  5. lenny/stable [text]
  6. language: English
  7. territory: United States
  8. key map: American English
  9. hostname: vmserver01
  10. domain name: student.lab
  11. mirror: scroll up to enter manually: mirror
  12. continue
  13. continue
  14. time zone: eastern
  15. partitioning method: use entire disk
  16. select a hard drive
  17. all files in one partition
  18. finish
  19. yes
  20. root password: bob
  21. verificate
  22. full name of user: RHINO
  23. user name: rhino
  24. user password: bob
  25. verificate
  26. participate in survey: no
  27. software to install: none (uncheck everything)
  28. Install GRUB boot loader: yes
  29. continue
  30. restart computer (automatic)
  31. log in: root pw: bob
  32. command: cd /etc/apt
  33. command: aptitude update
  34. command: aptitude upgrade (not required but a good habit to do after updating)
  35. command: aptitude install openssh-client openssh-server
  36. access remotely

To delete a user (command): userdel -r userName

To access remotely: Terminal → ssh root@vmserver01.student.lab → pw: bob

Installing vim is a good idea because it is more user friendly.
command: aptitude install vim

  1. aptitude install xen-linux-system-2.6.26-2-xen-686 and xen-tools
  2. continue: y
  3. command: vi/etc/modules (to add loop support module)
  • max_loop=255

network-script network-bridge setting in xend-config.sxp

  1. command: cd /etc/xen
  2. command: vi xend-config.sxp (uncomment: (network-script network-bridge) comment: (network-script network-dummy) (# = comment)

ensure vif-script is vif-bridge, same file

  1. command: cd /etc/xen
  2. command: vi xend-config.sxp (search for vif-script and make sure it is uncommented and accompanied with vif-bridge)

configure the grub boot entry accordingly (allow for the host to have 256MB)

  1. command: cd /boot/grub
  2. command: vi menu.lst
  3. Go to the bottom and remove the second and third entries (in our case).
  4. Go to:
  • title Xen 3.2-1-i386 / Debian GNU/Linux, kernel 2.6.26-2-xen-686
  • root (hd0,0)
  • kernel /boot/xen-3.2-1-i386.gz
  • module /boot/vmlinuz-2.6.26-2-xen-686 root=/dev/hda1 ro console=tty0
  • module /boot/initrd.img-2.6.26-2-xen-686

And where it says:

  • kernel /boot/xen-3.2-1-i386.gz

Add at the end of line: dom0_mem=262144 For this to take affect, reboot.

configure Xen-tools (/etc/xen-tools/xen-tools.conf) with some useful defaults:

  1. command: cd /etc/xen-tools/
  2. vi xen-tools.conf

Create the following directories: /xen, /xen/images, /xen/boot, /xen/conf, /xen/save

  1. command: mkdir and the paths specified above

FINAL PROCESS:
command: reboot

Creating Virtual Machine

ssh root@vmserver01.student.lab

  1. network-script network-bridge setting in xend-config.sxp (uncommented in vm server install; make sure there is no space before that line)
  2. xen-create-image –hostname=vm07 - -mac=00:16:3E:2E:C0:07 - -role=udev - -dhcp - -force (after xen-create-image there are actually two hyphens(- - force will overwrite an existing vm as well))
  3. Enter new password: bob
  4. verificate
  • log file created: /var/log/xen-tools/vm07.log
  1. xm create -c /xen/conf/vm07.cfg
  2. login: root
  3. password: bob
  • detach: ^] (ctrl closing square bracket)
  • reattach command: xm console vm07 (hit enter again)
  • command: xm list (lists virtual machines; run from vmserver01)
  • command: xm shutdown vm# (run from vmserver01)

ssh root@vm07.student.lab
can be done right from lab46

Data and Storage

vm09 is the virtual machine used for this

ssh root@vm09.student.lab

  1. from vmserver01 → command: xm shutdown vm09
  2. xm list to verificate
  3. command: cd /xen/domains/vm09
  4. command: dd if=/dev/zero of=disk1.img bs=1M count=1024
  5. repeat previous step 5 times BUT replacing disk with an incremented count up to 5
  6. mkfs.ext3 -v disk1.img (formats disk)
  7. proceed (for previous step)
  8. cd /xen/conf
  9. vi vm09.cfg
  10. copy/paste the line with 'file:/xen/domains/vm09/disk.img,xvda2,w', 5 times and put int the count of numbers after disk as well as after xvda
  11. xm create -c /xen/conf/vm09.cfg (- -force if overwriting an existing vm)
  12. aptitude search mdadm
  13. aptitude install mdadm (if discovered via the search from the previous step, this one can be skipped)
  14. yes (for previous step)

Raid 0 (striped raid)

  1. mdadm - -create - -verbose /dev/md0 - -level=0 - -raid-device=5 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7
  2. cat /proc/mdstat
  3. mkfs.ext3 -v /dev/md0 (formats md0 found from previous step)
  4. command: mount /dev/md0 /mnt
  • for details:
    • command: df or df -h or mdadm - -detail /dev/md0

Raid 1 (mirroring)

  1. mdadm - -create /dev/md0 - -level=1 - -raid-devices=4 - -spare-devices=1 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7
  2. cat /proc/mdstat
  3. mkfs.ext3 -v /dev/md0
  4. mount /dev/md0 /mnt

Raid 5

  1. mdadm - -create - -verbose /dev/md0 - -chunk=64 - -level=5 - -raid-devices=5 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7
  2. mkfs.ext3 -v /dev/md0 (formats md0 found from previous step)
  3. command: mount /dev/md0 /mnt

To unmount - command: umount “path” ie (and in our case): umount /dev/md0
To take raid offline - command: mdadm - -stop /dev/md0
To zero out drives - command: mdadm - -zero-superblock /dev/xvda3 (repeat but replace the # in xvda# by incrementing from 3 to 7
NOTE: If a vm gets taken down - command: xm create -c /xen/conf/vm09.cfg → ^] → ssh root@vm09.student.lab

Linux Terminal Server Project

~We had to install Ubuntu on a new machine, therefore, account info below~
user name: bob
pw: ginger
root
pw: bob

  1. eth1 (the second networking card: dhcp)
  2. sudo apt-get install dhcp3-server

Don't have to do this

  1. sudo vi /etc/dhcp3/dhcpd.conf -sudo (super user do: makes a normal user God)
  2. pw: bob

change:
NOTE: do not forget to uncomment this section
option domain-name “Example.org”; -change “Example.org” to whatever you want

  • pegasus.lan

option domain-name-servers ns1.example.org, ns2.example.org; -change the domains

  • ns1.pegasus.lan
  • ns2.pegasus.lan

scroll down a little and change:
#option domain-name-servers ns1.example.org; -change the domain

  • ns1.pegasus.lan

#option domain-name “internal.example.org”; -change the domain

  • ubuntu.pegasus.lan

Don't have to do this

  1. cd /etc/default/
  2. sudo vi dhcp3-server
  3. change: INTERFACES=“” … TO … INTERFACES=“eth1”
  4. sudo vi /etc/network/default/interfaces
  5. uncomment iface eth0 inet dhcp
  6. ADD:
  • auto eth1
  • iface eth1 inet static
    • address 10.5.5.1 (will be different for you)
    • netmask 255.255.255.0 (will be different for you)
  1. Concerning the 2 previous steps: sudo vi /etc/dhcp3/dhcpd.conf → scroll down until you find: # A slightly different configuration for an internal subnet. → use that subnet and netmask right under the comment
  2. reboot computer
  3. command: ifconfig → check to see if the ip address and mask took to eth1

determine which eth# will be used for the interface within the file: vi dhcp3-server (command to edit: sudo vi dhcp3-server) to determine this - command: ifconfig → and whichever eth# has an ip address thats the one to currently use. We use eth1.

  1. sudo apt-get install openssh-server
  2. sudo apt-get install ltsp-server-standalone
  3. Run LTSP build client - command: sudo ltsp-build-client - -mirror “http://mirror/ubuntu” (had to give it the mirror because it yelled without it)
  4. In case of having to repeat the previous step make sure to - command: rm -rf /opt/ltsp/i386/ -before you rerun the command
  1. Command: sudo vi /etc/ltsp/dhcpd.conf
  • Change subnet, range, option routers, option domain-name, option domain-name-server, option broadcast-address
    • Subnet: 10.5.5.0
    • Range: 10.5.5.10 - 10.5.5.60;
    • Option Domain-Name: ubuntu.pegasus.lan;
    • Option Domain-Name-Server: ns1.pegasus.lan;
    • Option Broadcast-address: 10.5.5.255;
    • Option Routers: 10.5.5.1;
  1. Command: sudo vi /etc/default/tftp-hpa
  • Change: RUN DEAMON=“no” … to … RUN DEAMON=“yes”
  1. Command: sudo /etc/init.d/dhcp3-server restart
  • Get another computer → Connect new computer's ethernet port to ltsp server's second ethernet port → turn on new computer → log on with an existing account within the server

Automated Drawing

  1. Open a Terminal
  • Right click → terminal
  1. basic command: xwit -root -warp # # (# is user's choice of any number within the pixel range of the monitor; col - row)
  2. To make the script:
  • #!/bin/bash (first line of file)
  • then bash goodness
  • chmod +x 'filename' (when you have written your script, saved, and exited, run this command to make it an executable)

command: xpaint &

The following code is very basic and really only works if there is a browser up and the terminal window is small and under the BLANK url of a webpage. The code will move the mouse, ask for the enter key to move on, loop through mouse movements, ask for enter to key move on, and then it clicks off of the terminal in the url box of a webpage, inputs lab46.corning-cc.edu and launches the page.

  • Just playing
   #!/bin/bash
 
echo "Moving mouse to - x: 500 y: 500"
xte 'mousemove 500 500'
 
echo "return to continue"
read cont
 
echo "Loop moving mouse - x: 200 - 220 AND y: 200 - 220"
for (( x=200; x<=220; x++ ))
do
    for (( y=200; y<=220; y++ ))
    do
        xte "mousemove $x $y"
    done
done        
 
echo "return to continue"
read cont
 
echo "Clicking mouse"
xte 'mousemove 250 60'
xte 'mouseclick 1'
 
echo "Inputing lab46 url"
xte 'str lab46.corning-cc.edu'
xte 'mousemove 980 60'
xte 'mouseclick 1'                        
  • The following code will move the mouse horizontally, vertically, and diagonally. -adMousemove.sh
#!/bin/bash
 
for (( x=0; x<=400; x++ ))
do
    xte "mousemove $x 400"
done
 
for (( y=0; y<=400; y++ ))
do
    xte "mousemove 400 $y"
done
 
x=400
y=400
while [ $y -ne 200 ]
do
    xte "mousemove $x $y"
    let x=$x+1
    let y=$y-1
done
 
x=400
y=400
while [ $y -ne 600 ]
do
    xte "mousemove $x $y"
    let x=$x+1
    let y=$y+1
done
 
x=400
y=400
while [ $x -ne 200 ]
do
    xte "mousemove $x $y"
    let x=$x-1
    let y=$y+1
done
 
x=400
y=400
while [ $x -ne 200 ]
do
    xte "mousemove $x $y"
    let x=$x-1
    let y=$y-1
done
 
exit 0              
  • The following code will move the mouse around the screen in a clockwise motion. -adBox1.sh
#!/bin/bash                                                                                                                                  
 
for (( x=0; x<=1280; x=$x+5 ))
do
    xte "mousemove $x 0"
done
 
for (( y=0; y<=1024; y=$y+5 ))
do
    xte "mousemove $x $y"
done
 
for (( x=$x; x>=0; x=$x-5 ))
do
    xte "mousemove $x $y"
done
 
for (( y+$y; y>=0; y=$y-5 ))
do
    xte "mousemove $x $y"
done
 
exit 0
  • The following code will move the mouse in a counter clockwise motion. -adBox2.sh
#!/bin/bash
 
for (( y=0; y<=1024; y=$y+10 ))
do
    xte "mousemove 0 $y"
done
 
for (( x=0; x<=1280; x=$x+10 ))
do
    xte "mousemove $x $y"
done
 
for (( y=$y; y>=0; y=$y-10 ))
do
    xte "mousemove $x $y"
done
 
for (( x=$x; x>=0; x=$x-10 ))
do
    xte "mousemove $x $y"
done
 
exit 0                     
  • The following code will find the Process ID number for xeyes. -adFindingIdNum.sh
#!/bin/bash                                                                                                                                  
 
xwit -print -all | grep xeyes > check
procCheck=`wc -l < check`
 
if [ $procCheck -eq 0 ]; then
    xeyes &
fi
 
winId=`xwit -print -all | grep xeyes | cut -d":" -f1`
echo "winId: $winId"
 
pkill xeyes
rm check
 
exit 0
  • The following code will move and resize xeyes. -adresizing_moving.sh
#!/bin/bash                                                                                                                                  
 
pkill xeyes
 
xwit -print -all | grep xeyes > check
procCheck=`wc -l < check`
 
if [ $procCheck -eq 0 ]; then
    xeyes &
fi
 
sleep 2
 
winId=`xwit -print -all | grep xeyes | cut -d":" -f1`
echo "winId: $winId"
 
xwit -id $winId -move 0 0 -resize 500 650
 
sleep 3
 
rm check
 
exit 0
  • The following code will implement screen size awareness logic so a program will operate on any window size. -adSSAL.sh
#!/bin/bash                                                                                                                                  
 
xwininfo -root | grep geometry > check
cat check | cut -d" " -f4 | cut -d"+" -f1 > check
maxX=`cat check | cut -d"x" -f1`
maxY=`cat check | cut -d"x" -f2`
echo "$maxX $maxY"
rm check
exit 0
  • The following code will draw a circle. -circle.sh
#!/bin/bash                                                                                                                                  
 
#screen awareness
maxX=`xwininfo -root | grep Width | cut -d" " -f4`
maxY=`xwininfo -root | grep Height | cut -d" " -f4`
 
pkill xpaint
sleep 2
 
let x=$maxX-135 #124 is the width of the xpaint tool panel
 
xpaint -size "$x"x"$maxY" &
sleep 3
 
winId=`xwit -print -all | grep -i xpaint | cut -d":" -f1`
toolBoxSize=`xwininfo -id $winId | grep Width | cut -d" " -f4` #This will get the width of the toolbox for xpaint
let canvasSize=$maxX-$toolBoxSize                               #This will find the max width of the canvas 
sleep 3
 
xwit -id $winId -move 0 0
sleep 4
 
#opens a new canvas
xte 'mousemove 40 40'
xte 'mousedown 1'
xte 'mousemove 30 70'
xte 'mousedown 1'
xte 'mouseup 1'
sleep 3
 
winId=`xwit -print -all | grep -i untitled | cut -d":" -f1`
sleep 3
 
xwit -id $winId -move $toolBoxSize 0
sleep 3
 
pi=3.14
rad=$pi/180
radius=200
for (( deg=0; deg<=360; deg++ ))
do
    #convert degree to radians
    theta=`echo "$deg*$rad" | bc -lq | sed 's/^\./0./'`
    #coordinates    
    y=`echo "400+$radius*s($theta)" | bc -lq | sed 's/^\./0./' | sed 's/-//' | cut -c1-3`
    x=`echo "400+$radius*c($theta)" | bc -lq | sed 's/^\./0./' | sed 's/-//' | cut -c1-3`
    xte "mousemove $x $y"
    xte 'mousedown 1'
done
xte 'mouseup 1'
 
 
exit 0
  • The following code will draw a random amount of circles and squares and fill them with random colors. Upon completion of execution it will then randomly color the background.
  • First file: the main file (use this one to create the art and make millions!) -random.sh
#!/bin/bash
 
#screen awareness
maxX=`xwininfo -root | grep Width | cut -d" " -f4`
maxY=`xwininfo -root | grep Height | cut -d" " -f4`
 
pkill xpaint
sleep 2
 
let x=$maxX-135 #124 is the width of the xpaint tool panel
 
xpaint -size "$x"x"$maxY" &
sleep 3
 
winId=`xwit -print -all | grep -i xpaint | cut -d":" -f1`
toolBoxSize=`xwininfo -id $winId | grep Width | cut -d" " -f4` #This will get the width of the toolbox for xpaint
let canvasSize=$maxX-$toolBoxSize                                                        #This will find the max width of the canvas 
sleep 3                                                                                  
 
xwit -id $winId -move 0 0
sleep 4
 
#open new canvas
xte 'mousemove 40 40'
xte 'mousedown 1'
xte 'mousemove 30 70'
xte 'mousedown 1'
xte 'mouseup 1'
sleep 3
 
winId=`xwit -print -all | grep -i untitled | cut -d":" -f1`
sleep 3
xwit -id $winId -move $toolBoxSize 0
sleep 3
 
#SHAPES - Squares & Circles
#Squares - 0
#Circles - 1
 
shape=`echo $(($RANDOM % 2))` #0 - 1
 
finish=0
sqr=0
cir=0
while [ $finish -ne 1 ]
do
        if [ $shape -eq 0 ]; then 
                ./randomSquare.sh
                sqr=1
        else
                ./randomCircle.sh
                cir=1
        fi
 
        if [ $shape -eq 0 ]; then
                shape=1
        else
                shape=0
        fi
 
        if [ $sqr -eq 1 ] && [ $cir -eq 1 ]; then
                finish=1
        fi
done
 
#top left hand corner of canvas
canvasStartX=135
canvasStartY=90
 
./colors.sh
xte "mousemove $canvasStartX $canvasStartY"
xte 'mousedown 1'
xte 'mouseup 1'
./pencil.sh
exit 0
  • The second file: draws squares -randomSquare.sh
#!/bin/bash
 
canvasId=`xwit -print -all | grep -i untitled | cut -d":" -f1`
 
width=`xwininfo -id $canvasId | grep Width | cut -d" " -f4`
height=`xwininfo -id $canvasId | grep Height | cut -d" " -f4`
 
#top left hand corner of canvas
canvasStartX=135
canvasStartY=90
 
count=`echo $(($RANDOM % 15))` #0 - 15
while [ $count -eq 0 ]
do
        count=`echo $(($RANDOM % 15))` #0 - 15
done
 
#box size
#used in xend, yend, w2, and h2
xBox=100
yBox=100
 
let w2=$width-$xBox             #box boundary
let h2=$height-$yBox    #box boundary
 
while [ $count -ne 0 ]
do
                xstart=`echo "($RANDOM % $w2 + $canvasStartX)" | bc -q`
                ystart=`echo "($RANDOM % $h2 + $canvasStartY)" | bc -q`
                let xend=$xstart+$xBox
                let yend=$ystart+$yBox
                xte "mousemove $xstart $ystart"
                xte 'mousedown 1'
 
                xte "mousermove $(($xend-$xstart)) 0"
                xte "mousermove 0 $(($yend-$ystart))"
                xte "mousermove -$(($xend-$xstart)) 0"
                xte "mousermove 0 -$(($yend-$ystart))"
xte 'mouseup 1'
                ./colors.sh
                let inBoxX=$xstart+15
                let inBoxY=$ystart+15
                xte "mousemove $inBoxX $inBoxY"
                xte 'mousedown 1'
                xte 'mouseup 1'
                ./pencil.sh
 
        xte 'mouseup 1'
        let count=$count-1
done
 
exit 0
  • Third file: draws circles -randomCircle.sh
#!/bin/bash
 
canvasId=`xwit -print -all | grep -i untitled | cut -d":" -f1`
 
width=`xwininfo -id $canvasId | grep Width | cut -d" " -f4`
height=`xwininfo -id $canvasId | grep Height | cut -d" " -f4`
 
#top left hand corner of canvas
canvasStartX=135
canvasStartY=90
 
count=`echo $(($RANDOM % 15))` #0 - 15
while [ $count -eq 0 ]
do
        count=`echo $(($RANDOM % 15))` #0 - 15
done
 
#start circle stuff
winId=`xwit -print -all | grep -i untitled | cut -d":" -f1`
w=`xwininfo -id $winId | grep Width | cut -d" " -f4`
h=`xwininfo -id $winId | grep Height | cut -d" " -f4`
 
let w=$w/2
let h=$h/2
let w=$w+100
 
pi=3.14
rad=$pi/180
radius=50
#end circle stuff
 
while [ $count -ne 0 ]
do
        xstart=`echo "($RANDOM % $w + $canvasStartX)" | bc -q`
        ystart=`echo "($RANDOM % $h + $canvasStartY)" | bc -q`
for (( deg=0; deg<=360; deg+=2 ))
        do
                #convert degree to radians
                theta=`echo "$deg*$rad" | bc -lq | sed 's/^\./0./'`
                #coordinates    
                y=`echo "$ystart+$radius*s($theta)" | bc -lq | sed 's/^\./0./' | sed 's/-//' | cut -c1-3`
                x=`echo "$xstart+$radius*c($theta)" | bc -lq | sed 's/^\./0./' | sed 's/-//' | cut -c1-3`
                xte "mousemove $x $y"
                xte 'mousedown 1'
        done
 
        xte 'mouseup 1'
        ./colors.sh
        let inCirX=$xstart-15
        xte "mousemove $inCirX $ystart"
        xte 'mousedown 1'
        xte 'mouseup 1'
        ./pencil.sh
        let count=$count-1
done
 
exit 0
  • Fourth file: randomly chooses a color -colors.sh
#!/bin/bash
#28 colors
#170 60 starting point in 1st row 180 will be used however, so white cannot be chosen
#170 70 starting point in 2nd row
 
row1=60
row2=70
 
colorRow=`echo $(($RANDOM % 2))` #0-1
color=`echo $(($RANDOM % 28))` #0-28
 
if [ $colorRow -eq 0 ]; then
        let y=$row1
        col=180
else
        let y=$row2
        col=170
fi      
 
colCnt=10
for (( z=0; z<=28; z++ ))
do
        if [ $z -eq 0 ]; then
                let colorArr[$z]=$col
        else
                let     colorArr[$z]=$col+$colCnt
                let colCnt=$colCnt+10
        fi
done
 
xte "mousemove ${colorArr[$color]} $y" #color
xte 'mousedown 1'
xte 'mouseup 1'
xte 'mousemove 30 400' #bucket
xte 'mousedown 1'
xte 'mouseup 1'
 
exit 0
  • Fifth (final) file: Chooses the pencil tool -pencil.sh
#!/bin/bash
 
xte 'mousemove 30 80'
xte 'mousedown 1'
xte 'mouseup 1'
 
exit 0

VNC Server

  • Virtual Network Computing
  1. In case if server is not up:
  2. ssh root@vmserver01.student.lab
  3. pw: bob
  4. command: xm list
  5. command: xm create -c /xen/conf/vm07.cfg
  6. login: root
  7. pw: bob
  8. detach: ^]
  9. ssh root@vm07.student.lab
  10. pw
  11. command: aptitude update
  12. command: aptitude install vnc-server
  13. command: aptitude install vnc4server
  14. command: aptitude install xautomation xwit rxvt fluxbox xpaint
  15. command: vnc4server -geometry 1280×1024
  16. pw: bobbob
  17. verificate
  1. open putty
  2. click: load lab46
  3. Category: ssh → tunnels
  4. check: local ports accept connections from other hosts
  5. source port: 5901
  6. Destination: vm07.student.lab:5901
  7. add
  8. Category: session → save
  1. Internet: download tightvnc portable
  2. extract
  3. run
  4. Host name or IP address: localhost:5901
  5. connect
  6. pw: bobbob
  • To copy files from lab46 - vm account (you are on vm): scp $USER@lab46.offbyone.lan:“relative/absolute path to file” “destination”
  • Example: scp jshort6@lab46.offbyone.lan:fileTracker.sh .
  • pw for $USER
  • scp -r … (-r will copy a directory)

Dialog Tutorial

If you are reading this, then it is assumed that you know a shell scripting language such as bash and understand some of the command line arguments.

This tutorial will teach the fundamentals of dialog and the majority of the boxes it offers. At the end there will be a full blown example of a coin tracking script using dialog and a script in dialog showing each unique box. So what is dialog? Dialog is a utility for building console like front ends in a unix environment. Dialog comes with most gnu/linux distributions so you shouldn't have to worry about installing it. However, there is another version called xdialog which in my opinion looks better but you have to install it (more on this later). Dialog boxes are mouse click sensitive and key stroke sensitive. So lets get started.

This tutorial will cover the fundamentals of:

  • Menu
  • Input Box
  • Message Box
  • Yes/No Box
  • Text Box
  • Check List
  • Radio List
  • Info Box
  • Gauge
  • Calendar
  • Time Box
  • File Selection
  • Pause Box
  • Password Box
Menu

Menu's have a hot key and a description. They also store the user's input in a temp file. Many dialog boxes use a temp file and I suggest you create one that they all use. For example: tempfile=`tempfile` (note these are back ticks). And make that statement global. If done this way, you dont have to create one for every box that needs it. The syntax for a menu is:

dialog --title "Menu Example" --backtitle "$backtitle"\
    --menu "Choose one" 30 50 2\
    "1" "Hi"\
    "2" "Bye" 2>$tempfile
    case $? in
        0)  choice=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    case $choice in
        1)  dialog --title "msg box" --backtitle "$backtitle"\
            --msgbox "You selected Hi" 10 30
        ;;
 
        2)  dialog --title "msg box" --backtitle "$backtitle"\
            --msgbox "You selected Bye" 10 30
        ;;
    esac

Disecting this snippet we see that the line begins with dialog. This says hey, I'm using dialog. Then the - -title “…” is what will be displayed on the top of the dialog box (center aligned). The - -backtitle “…” is not needed, however it offers a cool effect that I would suggest using for author identification. Then the \. This is important. Pay attention where they go because this is most likely where mistakes will occur (forgetting or placing in the wrong spot).

The second line says - -menu that signifies what kind of dialog box we are using. The double quotes next to it is the main text of the box that will be seen (left aligned). The following numbers are sizes and list count. The first number is the width, second is the height, and the third is the number of items in the list. The third number doesn't have to match the total amount of items in the list. It will automatically give a scroll bar if there are more items than the number specifies. Finally, the backslash.

The third line says “1” “Hi”\. The first set of double quotes represents the hot key. In other words, the letter/number/string represents the keyboard shortcut. Push what it says and it will teleport your cursor to that selection (if a string is used the first character is the hot key). This is where I suggest you don't go passed 10 because it will use 1 as its hot key. So, if you push 1 on the keyboard it will take you to 1. But if you push it again it will then take you to 10. I suggest switching to the alphabet once you reach 9. Also, you can just use your mouse and click on one that you want.

The fourth line says “2” “Bye” 2>$tempfile. You know what the “2” “Bye” is doing but what about this 2>$tempfile. Like I mentioned earlier. User input is stored in a file for menus. This file will contain whatever the hot key sequence is. So if you want “1” then '1' is stored in that file. From here you see a case structure. 0) is the user input. As in, you chose something from the list. 1) is the cancel button being selected. Note that cancel in this case is another function. Therefore, that function is being called real quick executing then returning back to the callee. 255) is escape being pressed on the keyboard. Note that esc in this case is another function. Therefore, that function is being called real quick executing then returning back to the callee. Both the cancel and esc functions are defined by me. The whole code where these examples were taken from will be posted at the end.

Finally, you see another case structure. This one is dealing with what the user chose from the list on the menu. The msgbox will be explained later.

Input Box

Input boxes are for trapping user input and storing it in a tempfile. With one of these dialogs the user can enter anything they wish. An example of the input box:

dialog --title "Input Box Example" --backtitle "$backtitle"\
    --inputbox "Enter something" 20 40 2>$tempfile
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your entry: $input" 10 50

If you haven't read menu yet I recommend you do so now because that is the only spot where I will explain the first line. With that said lets skip to the new parts of this code. The - - input says were using the input box from dialog. The following double quotes is the heading of the box (left aligned) and the two numbers again represent the width and height. Everything else was covered in the menu section.

Message Box

Message boxes are the simplest kind of box to write in dialog. They represent an echo to the screen. An example of a message box:

dialog --title "Message Box Example" --backtitle "$backtitle"\
    --msgbox "This is a Message Box" 10 30

If you haven't read menu yet I recommend you do so now because that is the only spot where I will explain the first line. With that said lets skip to the new parts of this code. The - - msgbox says were using the message box from dialog. The content of the following double quotes is what will be displayed. The numbers again represent width and height.

Yes/No Box

One of the most important boxes. This does store the user's input in tempfile but you don't actually need to access it with cat like you have been on the previous examples. An example of Yes/No box:

dialog --title "Yes/No Box Example" --backtitle "$backtitle"\
    --yesno "Do you like unicorns?" 10 30 2>$tempfile
    case $? in
        0)  answer="yes"
        ;;
 
        1)  answer="no"
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your answer: $answer" 10 30

If you haven't read menu yet I recommend you do so now because that is the only spot where I will explain the first line. With that said lets skip to the new parts of this code. The - - yesno says were using the yes/no box of dialog. The following content of the double quotes is what will be displayed (left aligned). The following numbers again represent width and height.

Now, in this case structure 0) and 1) is user input. Since you can only have yes or no these are the only two options.

Text Box

The text box is used to view a file. These are really cool because they are somewhat intelligent. Although you can specify the size of the box, the content doesn't necessarily have to fit. It will provide with a scroll bar. An example of a text box:

dialog --title "Text Box Example" --backtitle "$backtitle"\
    --textbox ./dialogExamples.sh 40 150

If you haven't read menu yet I recommend you do so now because that is the only spot where I will explain the first line. With that said lets skip to the new parts of this code. The - -textbox says were using the text box of dialog. The following ./“filename.extension” tells text box what to open. The following numbers are width and height.

Check List

A check list is very similar to the menu in structure (even with the hot keys and description method). If you have yet to read the menu section I suggest you do so now because some parts of this example will not be covered because they were already covered in the menu section. An example of the Check List:

 #toggle on/off with spacebar when running
 
    dialog --title "Check List Example" --backtitle "$backtitle"\
    --checklist "Select your favorite Star Wars character - toggle with the space bar" 20 75 8\
    "Luke Skywalker" "Mark Hamill" off\
    "Han Solo" "Harrison Ford" off\
    "Princess Leia Organa" "Carrie Fisher" off\
    "Ben Obi-Wan Kenobi" "Alec Guinness" off\
    "C-3PO" "Anthony Daniels" off\
    "R2-D2" "Kenny Baker" off\
    "Chewbacca" "Peter Mayhew" off\
    "Darth Vader" "David Prowse" off 2>$tempfile
    case $? in
        0)  fav=`cat $tempfile`
            echo "$fav" > tmp
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    #if sed didn't strip out \ and " it would look like: "Luke\ Skywalker"
    #   Now it looks like: Luke Skywalker
    cat tmp | sed 's/\\//g' | sed 's/"//g' > tmp2
    mv tmp2 tmphhhhhhhhh
 
    dialog --title "txt box" --backtitle "$backtitle"\
    --textbox ./tmp 30 50
 
    rm tmp

Starting on the second line the - -checklist says were using the check list of dialog. The following content of the double quotes is what will be displayed (left aligned). Then the numbers are width, height, and the number of elements in the list (so far, just like the menu). However, we see something new on the third line and the following lines until we get to Darth Vader. This is “off” which is at the end of each line. This is significant because it tells dialog to not check this box automatically. If you did want one or more of them checked all you have to do is replace “off” with “on”. Which ever one(s) you choose, they will be stored in the tempfile. This is my work around so this doesn't mean it's the best or prettiest, it only means I did what I knew I could do. I throw the contents of the tempfile into a variable so I can work some sed goodness on it. The comments in the code snippet show you why I did this.

Radio List

Radio Lists are just like check lists except for one difference. You can only check one at a time. For this reason, I will only show the code snippet and not disect it because you should have read menu and check list before coming to this one. However, I will point out, the only difference from this and check list is the - -radiolist. Example of the Radio List:

dialog --title "Check List Example" --backtitle "$backtitle"\
    --radiolist "Choose your favorite Star Wars Character - toggle with the space bar" 20 50 8\
    "Luke Skywalker" "Mark Hamill" off\
    "Han Solo" "Harrison For" off\
    "Princess Leia Organa" "Carrie Fisher" off\
    "Ben Obi-Wan Kenobi" "Alec Guinness" off\
    "C-3PO" "Anthony Daniels" off\
    "R2-D2" "Kenny Baker" off\
    "Chewbacca" "Peter Mayhew" off\
    "Darth Vader" "David Prowse" off 2>$tempfile
    case $? in
        0)  fav=`cat $tempfile`
            echo "$fav" > tmp
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your favorite Character: $fav" 10 50
 
    rm tmp
Info Box

Info boxes will probably not be used that often. I for one don't really think they're necessary but then again I've never written anything process intensive that would make them worthy. They're used to display things while commands or calculations or whatever goes on in the background. An example of the Info Box:

 dialog --title "Info Box example" --backtitle "$backtitle"\
    --infobox "`vmstat; echo ;free`" 20 80
 
    sleep 5 #w/o it you would see nothing because an info box is used when something is 
            #being worked on

The double quote backtick vmstat; echo; free backtick double quote is just a command that I used as an example for this. The sleep is there because it computes it so quickly you don't even see the dialog box. So I made it sleep for 5 seconds so that you could see what it did.

Gauge

This is pretty cool. Gauges are used to depict something being downloaded, transfered, or anything that is taking time and you want the user to know that something is happening. Essentially it's a progress bar. An example of a Gauge:

count=10
    max=100
    (
        while test $count != $max
        do
            echo $count
            echo "XXX"
            echo "($count% of $max%)"
            echo "XXX"
            count=`expr $count + 10`
            sleep 1
        done
    ) |
    dialog --title "Gauge Example" --backtitle "$backtitle"\
    --guage "Gauge" 20 50 0
    #~NOTE~ the echo "($count% of $max%)" is what will be printed on the gauge box.  
     #It MUST be between the: echo "XXX"
    #increments by 10 every other second

count is the variable that holds the value of which the gauge will increment by. max is the value which the gauge will fill up to and finish when reached. Jump into the loop and test to see if count is not equal to max (if it is then exit). Print count to the screen. Now comes the magic. Notice two echo “XXX”. This is the bar. Take count mod max which gives the remainder. This is the number that will be used to show the percentage of completion. Then increment count by its self, then sleep for 1 second. Sleep is there so it takes more time to fill the gauge. Call dialog like we have been except use - - guage. The numbers in this case are height, width, and the number to start at in the loading box.

Calendar

The calendar does exactly what it sounds like it should. It displays a calendar.

dialog --title "Calendar Example" --backtitle "$backtitle"\                                                                
    --calendar "Use tab/arrow keys to navigate - tab: switch topics - arrows: cycle" 0 0 0 0 0 2>$tempfile
 
    #first two numbers are height and width: 0 0 #defaults the size next three are: month/day/year 
    #   month/day/year can be 0 and it will use current date
 
    case $? in
        0)  date=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your date: $date" 10 40

Using - -calendar dialog will display a calendar of your choice. Notice where the size is specified. There are now a few more numbers to work with. The first two are height and width. The following three are month, day, and year respectively. Zeroes can be used in all places. What this is does is set a default size and sets the calendar to the current month and year. Navigation is controlled by tab and arrow keys. Tab will cycle through the headings. So, month, year, days, and the buttons. The arrow keys can do the same but it can be confusing. I suggest using them only inside of headings. The user can choose a date and this will be stored in the tempfile.

Time Box

The time box allows the user to choose a time down to the second.

dialog --title "Time Box Example" --backtitle "$backtitle"\
    --timebox "Please enter the time:" 0 0 0 0 0 2>$tempfile
    #Again the first two numbers are height and width, the other three are hour/minute/second again, all 0s means to use defaults                                                                                                                             
    case $? in
        0)  userTime=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your time: $userTime" 10 40

Please note the use of - -timebox. Similar to the calendar, the places for hieght and width controls are the first two places. The following three are hours, minutes, and seconds respectively. The same navigation from calendar applies here too. Tab will cycle through hours, minutes, seconds, and the buttons while the arrow keys can be used to alter the headings contents.

File Selection

Have a directory you want to display in a dialog box, well now you can (new dialog box smell not included)! For this example the home directory is used.

dialog --title "File Selection Example" --backtitle "$backtitle"\                                                          
    --fselect $HOME 15 50 2>$tempfile
    case $? in
        0)  file=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    #$HOME is your home directory
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your file: $file" 10 40

Note the use of - -fselect. Following fselect is an environment variable. That is the home directory. Directories are on the left hand side of the box while its contents are on the right. If the user selects a path that path is stored in the temp file. Also the numbers following the environment variable represent the height and width.

Pause Box

Essentially pause is sleep but in graphic form.

dialog --title "Pause Box Example" --backtitle "$backtitle"\
    --pause "Pause" 10 30 5                                                                                                    
 
    #3rd number '5' is time in seconds to be paused
 
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
</bash>
Note the use of - -pause. The following numbers are height, width, and the seconds to be paused. When executed a progress bar will be displayed and it will count backwards until 0 is reached meaning the amount of time spent to be sleeping has completed.
 
  Password Box
I recommend you read the input box section because this function is the same except that the user's input is not displayed when typed.
<file bash>
#password boxes are just like input boxes except the user's entry is not displayed
    dialog --title "Pasword box Example" --backtitle "$backtitle"\
    --passwordbox "Enter something:" 10 30 2>$tempfile
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return                                                                                                         
        ;;
    esac
 
    dialog --title "msb box" --backtitle "$backtitle"\
    --msgbox "Your entry: $input" 10 30

Note the use of - -passwordbox. The following numbers represent the height and width. Although the user's input is not being displayed, what they type can still be stored in the temp file and used.

  • Keep in mind the backslashes and their proper locations!
  • Make the tempfile globally defined.
  • So now you know what it takes to make several dialog boxes. Here is the code where the snippets were taken from. You can run it and see what they look like.
#!/bin/bash                                                                                                                    
 
###
#
#   Written by: Jesse Short 
#
#   Purpose: To explain and give in depth examples of dialog.  Dialog is a command that enables the use of window boxes in a shell script to make them more
#       user friendly and interactive.  Dialog is easy, simple, and quite flexible.  This script will use dialog to show and explain it.
#
#   Executable: dialogExample.sh
#
#       chmod +x dialogExample.sh
#
#   Execute: ./dialogExample.sh 
#
#   References/Sources:
#
#       http://linuxgazette.net/101/sunil.html
#
#       http://unstableme.blogspot.com/2009/12/linux-dialog-utility-short-tutorial.html
#
###
 
###
#
# ~NOTE~ do not put comments within dialog syntax. It will break. Also, Menus be wary when you go past 9.  It will display but '1' will take you to 1, 10, 
#   11, etc. 
###
 
function cancel()
{
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "$cancel" 10 30
 
return
}
 
function esc()
{
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "$esc" 10 30
 
return
}
 
function mainMenu()
{
    option=0
    while [[ $option -ne 10 || "$option" != "10" ]]
    do
        dialog --title "Main Menu" --backtitle "$backtitle"\
        --menu "$menu" 30 50 16\
        "1" "Menu"\
        "2" "Input Box"\
        "3" "Message Box"\
        "4" "Yes/No"\
        "5" "Text Box"\
        "6" "Check List"\
        "7" "Radio List"\
        "8" "Info Box"\
        "9" "Gauge"\
        "A" "Calendar"\
        "B" "Time Box"\
        "C" "File Selection"\
        "D" "Pause Box"\
        "E" "Password Box"\
        "F" "Exit" 2>$tempfile
        case $? in      #$? is the number picked from the menu and stored in tempfile
            0)  option=`cat $tempfile`
            ;;
 
            1)  cancel
                return  #cancel is pressed
            ;;
 
            255)    esc
                    return  #esc key is pressed
            ;;
        esac
 
        #height width menuItemCnt IF 3rd number is less than number of items in list, list will scroll
        case $option in
            1)  menu
            ;;
 
            2)  inputBox
            ;;
 
            3)  msgBox
            ;;
 
            4)  yesNoBox
            ;;
 
            5)  txtBox
            ;;
 
            6)  chkList
            ;;
 
            7)  radList
            ;;
 
            8)  infoBox
            ;;
 
            9)  gauge
            ;;
 
            A)  calendar
            ;;
 
            B)  timeBox
            ;;
 
            C)  fileSelect
            ;;
 
            D)  pause
            ;;
 
            E)  pw
            ;;
 
            F) option=10
                #break      #does not require break but can have it
            ;;
        esac
    done
 
return
}
 
function menu()
{
    dialog --title "Menu Example" --backtitle "$backtitle"\
    --menu "Choose one" 30 50 2\
    "1" "Hi"\
    "2" "Bye" 2>$tempfile
    case $? in
        0)  choice=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    case $choice in
        1)  dialog --title "msg box" --backtitle "$backtitle"\
            --msgbox "You selected Hi" 10 30
        ;;
 
        2)  dialog --title "msg box" --backtitle "$backtitle"\
            --msgbox "You selected Bye" 10 30
        ;;
    esac
 
return
}
 
function inputBox()
{
    dialog --title "Input Box Example" --backtitle "$backtitle"\
    --inputbox "Enter something" 20 40 2>$tempfile
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your entry: $input" 10 50
 
return
}
 
function msgBox()
{
    dialog --title "Message Box Example" --backtitle "$backtitle"\
    --msgbox "This is a Message Box" 10 30
 
return
}
 
function yesNoBox()
{
    dialog --title "Yes/No Box Example" --backtitle "$backtitle"\
    --yesno "Do you like unicorns?" 10 30 2>$tempfile
    case $? in
        0)  answer="yes"
        ;;
 
        1)  answer="no"
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your answer: $answer" 10 30
 
return
}
 
function txtBox()
{
    dialog --title "Text Box Example" --backtitle "$backtitle"\
    --textbox ./dialogExamples.sh 40 150
 
return
}
 
function chkList()
{
    #toggle on/off with spacebar when running
 
    dialog --title "Check List Example" --backtitle $backtitle"\
    --checklist "Select your favorite Star Wars character - toggle with the space bar" 20 75 8\
    "Luke Skywalker" "Mark Hamill" off\
    "Han Solo" "Harrison For" off\
    "Princess Leia Organa" "Carrie Fisher" off\
    "Ben Obi-Wan Kenobi" "Alec Guinness" off\
    "C-3PO" "Anthony Daniels" off\
    "R2-D2" "Kenny Baker" off\
    "Chewbacca" "Peter Mayhew" off\
    "Darth Vader" "David Prowse" off 2>$tempfile
    case $? in
        0)  fav=`cat $tempfile`
            echo "$fav" > tmp
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    #if sed didn't strip out \ and " it would look like: "Luke\ Skywalker"
    #   Not it looks like: Luke Skywalker
    cat tmp | sed 's/\\//g' | sed 's/"//g' > tmp2
    mv tmp2 tmp
 
    dialog --title "txt box" --backtitle "$backtitle"\
    --textbox ./tmp 30 50
 
    rm tmp
 
return
}
 
function radList()
{
    dialog --title "Check List Example" --backtitle "$backtitle"\
    --radiolist "Choose your favorite Star Wars Character - toggle with the space bar" 20 50 8\
    "Luke Skywalker" "Mark Hamill" off\
    "Han Solo" "Harrison For" off\
    "Princess Leia Organa" "Carrie Fisher" off\
    "Ben Obi-Wan Kenobi" "Alec Guinness" off\
    "C-3PO" "Anthony Daniels" off\
    "R2-D2" "Kenny Baker" off\
    "Chewbacca" "Peter Mayhew" off\
    "Darth Vader" "David Prowse" off 2>$tempfile
    case $? in
        0)  fav=`cat $tempfile`
            echo "$fav" > tmp
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your favorite Character: $fav" 10 50
 
    rm tmp
 
return
}
 
function infoBox()
{
    dialog --title "Info Box example" --backtitle "$backtitle"\
    --infobox "`vmstat; echo ;free`" 20 80
 
    sleep 5 #w/o it you would see nothing because an info box is used when something is being worked on
 
return
}
 
function gauge()
{
    count=10
    max=100
    (
        while test $count != $max
        do
            echo $count
            echo "XXX"
            echo "($count% of $max%)"
            echo "XXX"
            count=`expr $count + 10`
            sleep 1
        done
      ) |
    dialog --title "Gauge Example" --backtitle "$backtitle"\
    --guage "Gauge" 20 50 0
    #~NOTE~ the echo "($count% of $max%)" is what will be printed on the gauge box.  It MUST be between the: echo "XXX"
    #increments by 10 every other second
return
}
 
function calendar()
{
    dialog --title "Calendar Example" --backtitle "$backtitle"\
    --calendar "Use tab/arrow keys to navigate - tab: switch topics - arrows: cycle" 0 0 0 0 0 2>$tempfile
 
    #first two numbers are height and width: 0 0 #defaults the size next three are: month/day/year 
    #   month/day/year can be 0 and it will use current date
 
    case $? in
        0)  date=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your date: $date" 10 40
 
return
}
 
function timeBox()
{
    dialog --title "Time Box Example" --backtitle "$backtitle"\
    --timebox "Please enter the time:" 0 0 0 0 0 2>$tempfile
    #Again the first two numbers are height and width, the other three are hour/minute/second again, all 0s means to use defaults
    case $? in
        0)  userTime=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your time: $userTime" 10 40
 
return
}
 
function fileSelect()
{
    dialog --title "File Selection Example" --backtitle "$backtitle"\
    --fselect $HOME 15 50 2>$tempfile
    case $? in
        0)  file=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    #$HOME is your home directory
 
    dialog --title "msg box" --backtitle "$backtitle"\
    --msgbox "Your file: $file" 10 40
 
return
}
 
function pause()
{
    dialog --title "Pause Box Example" --backtitle "$backtitle"\
    --pause "Pause" 10 30 5
 
    #3rd number '5' is time in seconds to be paused
 
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
return
}
 
function pw()
{
    #password boxes are just like input boxes except the user's entry is not displayed
    dialog --title "Pasword box Example" --backtitle "$backtitle"\
    --passwordbox "Enter something:" 10 30 2>$tempfile
    case $? in
        0)  input=`cat $tempfile`
        ;;
 
        1)  cancel
            return
        ;;
 
        255)    esc
                return
        ;;
    esac
 
    dialog --title "msb box" --backtitle "$backtitle"\
    --msgbox "Your entry: $input" 10 30
 
 
return
}
 
tempfile=`tempfile` #most dialog require a file for their input to be directed to
backtitle="Obsidian Shield: Dialog Examples"
menu="Chose the corresponding number for the desired action"
cancel="You pressed cancel"
esc="You pressed the escape key"  
 
mainMenu
 
exit 0                                                                 

Xming

If you are learning or know java then this will be your friend if you're using an ssh client. Java can manipulate dialog boxes that Xming will display. Xming is from the X window system and will work on Windows XP, Windows Server 2003, Vista, and 7.

  1. Download
  2. Install
  3. Open Putty
  4. Load Lab46
  5. (Left hand side of box) Go to: SSH; sub-menu: X11
  6. Click the check box for: Enable X11 Forwarding
  7. Go back to session (The main screen; left hand side of box at the top) and click save

Xming has to be running for graphics to be displayed. You know it's working when there is an 'X' in the tray

That's it :)

Dual Boot

  • Get a disk of win xp
  • Press f12 during bootup when the option is allowed
  • Choose CD/DVD from the boot menu
  • Press any key
  • It will then begin installing drivers and such
  • Enter to continue
  • f8 to agree
  • Now it will ask how you want to partition the hard drive (you can do whatever you want just make sure adequate space is used for both os's)
  • Enter to install
  • Formatting partition: Format the partition using the NTFS file system (don't use the one that has (Quick) at the end of it)
  • Enter to continue install (Formatting will probably take 30 minutes)
  • Next (Assuming you want the defaults)
  • Name/Organization (Just do your name)
  • Enter product key (If it says invalid scroll down to Invalid Product Key)
  • Enter admin password
  • Verificate password
  • Next
  • Choose date/Time settings and Time Zone
  • Network settings: Typical settings
  • No computer is not on a network or is on a network without a domain: Next (unless you wan't something else)
  • Allow windows to change screen resolution
  • Next
  • Click: Help protect my PC by turning on Automatic Updates
  • Next
  • Enter the names of users if any
  • Next
  • Finish

~~If there is a password and one was never set (or you forgot it) boot over a network with some kind of Windows backtracking password recovery method~~

  • Once booted into command: mount -t ntfs-3g /dev/sda1 /mnt -o force
  • command: cd /mnt/WINDOWS/system32/config/
  • command: chntpw -i sam userdiff
  • enter: 1
  • enter: “User Name”
  • enter: 1
  • enter: q
  • enter: y
  • command: cd
  • command: umount /mnt
  • command: reboot
  • What this should do is set the password for “User Name” to nothing

This still may not work, just reinstall again because it probably skipped some steps (like it did to me).

  • Invalid Product Key

When you enter your product key and it says invalid a potential cause of this could be that you have a volume license disk and an Original equipment manufacturer key or vice versa. Volume Licensing Keys (VLK) bypass activation methods which allows for the key to be used on several installations. Original Equipment Manufacturer (OEM) is meant for retail. The key on your computer is OEM and therefore will not work if a volume license install disk is used and vice versa. To amend this you can either legally buy a Windows XP install disk or illegally get a key gen for whichever scenario you are in (VLK gen or an OEM key gen).

Ubuntu: Install over network

  • f12 upon startup when allowed
  • choose lan
  • 32-bit (i386)
  • Ubuntu/i386/ Netboot
  • Install Lucid “10.04”
  • Engrish
  • US
  • No: Detect keyboard layout
  • USA: origin of keyboard
  • Keyboard layout: USA
  • Hostname: JesseShort
  • Ubuntu archive mirror: scroll up to - enter info manually - mirror (Use whatever mirror you should)
  • continue
  • continue
  • If given time zone is correct: yes
  • Partitioning: scroll down to the line that says FREE SPACE
  • Create new partition
  • Take 2 gigs away from it
  • Primary
  • Done
  • Scroll down to the line that says FREE SPACE
  • Create new partition
  • Primary
  • Edit the first line - swap area (Virtual Memory)
  • Finish partitioning
  • Yes
  • Full name: Jesse Short
  • Username for account: jesse
  • pw: what ever
  • verificate: what ever
  • If password is too weak (ie: under 8 characters): yes use weak pw
  • No
  • Install security updates automatically (your choice)
  • Ubuntu desktop
  • Yes: install GRUB boot loader to master boot record
  • Continue

Configuring OS to auto boot os of your choice:

  • Log in to ubuntu
  • Open terminal
  • cd /boot/grub
  • sudo vi grub.cfg
  • cut out the block of text that starts with: menuentry “Microsoft Windows XP Professional… and ends with ### END /etc/grub.d/30_os-prober ### and paste it on the top of the list
  • sudo reboot
  • An option screen will load up and you can choose which os to boot into or let it boot into the first of the list: windows xp
  • ~~~Further Configurations (Time duration to display list of bootables)~~~
  • within ubuntu open a terminal
  • cd /boot/grub
  • sudo vi grub.cfg
  • search for: set timeout
  • change: set timeout=10 to whatever you want ie: set timeout=5

Changing the mirror:

  • sudo vi /etc/apt/sources.list
  • :%s/mirror/us.archive.ubuntu.com/g
  • Above searches for the string mirror and replaces it with us.archive.ubuntu.com and do it globally.

Battleship

So we all remember this childhood game. Often sending brothers into a furry of rage, or is this only my experience? Anyway, I thought it would be a good challenge to write this game. A simple ascii art display, some arrays, and you're good to go! So without further adue, I shall explain what each function does, however, I will be doing them in a custom order just for this documentation. That means, they will not match in the actual program (since I worked bottom to top). Also note, This is a salvo based battleship. That means each player gets as many attacks as they have ships. For example, player 1 has 4 ships, therefore, he has 4 attacks.

  • Firstly Include these header files:
    • <cstdio>
    • <cstdlib>
    • <unistd.h> used for sleep function
    • <time.h> used for random
  • Next: prototypes, With the order my functions lie, I only needed one
//prototypes
int *nextAtkPos(int, int, int);	//so medium can use it
  • After that declare and initialize some constants. These constants are the sizes of the board
const int ROWS = 10;
const int COLS = 10;
  • Then make the struct (This will hold each player's information)
struct Player
{
	char posXY[ROWS][COLS];		//positions of ships
	char attackXY[ROWS][COLS];	//positions bombed on Y axis and X axis
	int carrier;
	int battleship;
	int destroyer;
	int submarine;
	int patrol;
	int turns;			//how many moves the player gets (equal to number of battleships)
};
 
typedef struct Player Player;
  • Once all of that is done, declare the pointers that will be used to point to this struct. I've learned to always have one point to it, but to have another use operations, Therefore, I have 4 pointers, but only 2 are actually required.
Player *head1, *current1;
Player *head2, *current2;
  • The Main function
int main()
{
	int menuOption = -1;
	while ( menuOption != 5 )
	{
		while ( menuOption < 0 || menuOption > 5 )
		{
			printf("\n\nWelcome to the salvo based battleship game v1.17\n");
			printf("Choose your type:\n");
			printf("1. PvP\n2. Easy\n3. Medium\n4. Hard\n5. Exit\n");
			scanf("%d", &menuOption);
		}
		switch (menuOption)
		{
			case 1: pvp();
					cleanUp();
					break;
			case 2: easy();
					cleanUp();
					break;
			case 3: medium();
					cleanUp();
					break;
			case 4: hard();
					cleanUp();
					break;
			case 5: break;
		}
		if ( menuOption != 5 )
			menuOption = -1;
	}
 
return 0;
}

As you can see there are 5 options total. The first is PvP. This pits two real players in head to head lethal naval combat! Then there is Easy. This is a real player versus the computer with an easy level of AI (more on this in the easy section). Next is Medium. This is a real player versus the computer with a medium level of AI (more on this in the medium section). After that is Hard. This is a real player versus the computer with a hard level of AI (more on this in the hard section). Lastly, is Exit. Note the cleanUp function. This is used to free the pointers pointing to the structs once control is back in the main function.

  • Next is the initPlayer function. This function is called by pvp, easy, medium, and hard.
void initPlayers()
{
	int x, y;
 
	head1 = (Player *) malloc (sizeof(Player));
	current1 = head1;
	current1 -> carrier = 5;
	current1 -> battleship = 4;
	current1 -> destroyer = 3;
	current1 -> submarine = 3;
	current1 -> patrol = 2;
	current1 -> turns = 5;
 
	head2 = (Player *) malloc (sizeof(Player));
	current2 = head2;
	current2 -> carrier = 5;
	current2 -> battleship = 4;
	current2 -> destroyer = 3;
	current2 -> submarine = 3;
	current2 -> patrol = 2;
	current2 -> turns = 5;
 
	for ( x = 0; x < ROWS; x++ )
	{
		for ( y = 0; y < COLS; y++ )
		{
			current1 -> posXY[x][y]='o';
			current1 -> attackXY[x][y] = '-';
			current2 -> posXY[x][y]='o';
			current2 -> attackXY[x][y] = '-';
		}
	}
 
return;
}

This function initializes the struct. It allocates memory for two pointers (head1 and head2) which will represent player 1 and player 2. It then initializes the ships sizes and the turn count. After that, it fills the arrays with initial values. This is done for comparing in other functions. The 'o' in posXY represents an open space while the '-' in attackXY represents a space that has yet to be attacked.

  • Next up is the info function. Every function from the main menu (pvp, easy, medium, and hard) will invoke this function at least once.
void info()
{
	printf("There are 5 total ships.\n");
	printf("Ship\t\tSize\tLetter\n");
	printf("====\t\t====\t======\n");
	printf("Carrier\t\t5\tC\n");
	printf("Battleship\t4\tB\n");
	printf("Destroyer\t3\tD\n");
	printf("Submarine\t3\tS\n");
	printf("Patrol Boat\t2\tB\n\n\n");
 
	printf("Each player starts off with 5 turns.\nTurns are based on the amount of ships you have.\nTurns are represented at the highest value and count down to zero\n");
	printf("An X on the attack board indicates one of your strikes.\nAn H on the attack board indicates one your hits.\n");
	printf("A lowever case of a ship letter represents that part of the ship being damaged. As in your opponent hit the ship at that spot.\n");
 
return;
}

The purpose of this function is to give the player the rules and an idea on how the game works. As you can see a list of each ship size and their representative character is shown. Then it goes on to explain turns and what other characters mean. Such as an 'X' on the attack board represents an attack but it missed while an 'H' represents an attack that hit. Also, if a ships character is in lower case, then that means it was hit.

  • The PvP method
void pvp()
{
	int player = 1, winner = 0, turnCnt, x, y, go = 0;
	int ai = 0;
 
	initPlayers();
 
	printf("Welcome to the PvP section.\n");
	printf("Play nicely and no cheating!\n");
	printf("Player 1 please set your battleships.\n\n");
	info();
	setShips(player, ai);
	printf("\n\nPlayer 2 please set your battleships.\n\n");
	player++;
	info();
	setShips(player, ai);
	player--;
	turnCnt = current1 -> turns;
 
	while ( winner == 0 )
	{
		while ( turnCnt != 0 )
		{
			while ( go == 0 )
			{
				printBoard(player);
				printAtkBoard(player);
				printf("Player %d's turn\nTurn: %d\n\n", player, turnCnt);
 
				printf("Enter attack coordinate y: ");
				scanf("%d", &y);
 
				while ( y < 0 || y > 9 )
				{
					printf("Enter attack coordinate y: ");
					scanf("%d", &y);
				}
 
				printf("Enter attack coordinate x: ");
				scanf("%d", &x);
 
				while ( x < 0 || x > 9 )
					{
					printf("Enter attack coordinate x: ");
					scanf("%d", &x);
				}
 
				go = checkAtk(player, x, y);
			}
			winner = checkWin(player);
			if ( winner == 1 )
				break;
			turnCnt--;
			go--;
		}
		if ( player == 1 )
		{
			player++;
			turnCnt = current2 -> turns;
		}
		else
		{
			player--;
			turnCnt = current1 -> turns;
		}
	}
 
return;
}
1 Variables

So now its time for some friendly competition. Lets declare and initialize some variables. The player variable is set to 1 so that when it is passed to other functions they know which player called it. Winner is set to 0 because no one has one yet. It will be set to 1 when someone wins. It is also the external loop control variable. All other loops reside in this one. After all, once there is a winner there is no reason to keep playing. TurnCnt is a variable that will keep track of the players turns. It compares itself to the players turn that it points to at the struct. TurnCnt automatically starts at the player's turn (ie: current1 → turns) and counts down to 0. Thus, when its the players last turn (in this case 1) it will give control to the other player. X and y are coordinate variables. The user defines these. Go is another loop control variable. This variable controls when a successful hit has been made. If go is set to 0 then there was some kind of problem. As in, the attack has already been made, therefore, no reason to make that attack again. Once it is set to 1 another turn may be had. Ai is for other functions purposes. If it is 0 then it is a real player. If it is not 0 then it is the computer.

2 body

The setShips function will be covered in its own section later. All you need to know now is that it will ultimately have placed each users ships wherever they wanted. So lets jump inside these loops. Each player gets to choose an attack coordinate during their turn. Y is selected first, then the x. Both are combined and sent to the checkAtk function. This function will be covered later in its own section. All you need to know right now is that it checks if the attack is a repeat or not. If it is not then control is given back to pvp and checkWin is called. This function will also be covered in its own section. All you need to know is that it checks for a winner. If no winner, then keep repeating until winner is found. So player 1 takes his/her turns then its player 2's turn. This repeats until a winner. To switch players it checks if its player 1 or not and adjusts the player variable accordingly. Remember, 1 represents the first player and 2 represents the second player. Then it resets the turnCnt variable to the appropriate players turn held in the struct.

  • setShips function is the starting block for placing a ship
void setShips(int player, int ai)
{
	int x, y, shipSize; //shipType is the number of spots the ship can have. ie: carrier - 5, patrol - 2
	char shipChar;	//used so other functions know what to fill the board with given the ships starting char
	int go = 0;		//control loops
 
	if ( player == 1 )
	{
		while ( go == 0 ) //set carrier
		{
			printf("Setting Carrier - %d blocks in length\n", current1 -> carrier);
			printBoard(player);
			printf("Choose the starting y location of your carrier: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your carrier: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your carrier: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your carrier: ");
				scanf("%d", &x);
			}
 
			if ( current1 -> posXY[y][x] == 'o' )
			{
				shipChar = 'C';				//placeShip uses this when checkSet() calls it
				current1 -> posXY[y][x] = shipChar;
 
				shipSize = current1 -> carrier;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current1 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 )	//set battleship
		{
			printf("Setting Battleship - %d blocks in length\n", current1 -> battleship);
			printBoard(player);
			printf("Choose the starting y location of your battleship: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your battleship: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your battleship: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting y location of your battleship: ");
				scanf("%d", &x);
			}
 
			if ( current1 -> posXY[y][x] == 'o' )
			{
				shipChar = 'B';
				current1 -> posXY[y][x] = shipChar;
 
				shipSize = current1 -> battleship;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
                                if ( go == 0 )
                                        current1 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 )	//set destroyer
		{
			printf("Setting Destroyer - %d blocks in length\n", current1 -> destroyer);
			printBoard(player);
			printf("Choose the starting y location of your destroyer: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your destroyer: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your destroyer: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting y location of your destroyer: ");
				scanf("%d", &x);
			}
 
			if ( current1 -> posXY[y][x] == 'o' )
			{
				shipChar = 'D';
				current1 -> posXY[y][x] = shipChar;
 
				shipSize = current1 -> destroyer;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
                                if ( go == 0 )
                                        current1 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 ) //set submarine
		{
			printf("Setting Submarine - %d blocks in length\n", current1 -> submarine);
			printBoard(player);
			printf("Choose the starting y location of your submarine: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your submarine: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your submarine: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your submarine: ");
				scanf("%d", &x);
			}
 
			if ( current1 -> posXY[y][x] == 'o' )
			{
				shipChar = 'S';				//placeShip uses this when checkSet() calls it
				current1 -> posXY[y][x] = shipChar;
 
				shipSize = current1 -> submarine;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current1 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 ) //set patrol
		{
			printf("Setting Patrol Boat - %d blocks in length\n", current1 -> patrol);
			printBoard(player);
			printf("Choose the starting y location of your patrol boat: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your patrol boat: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your patrol boat: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your patrol boat: ");
				scanf("%d", &x);
			}
 
			if ( current1 -> posXY[y][x] == 'o' )
			{
				shipChar = 'P';				//placeShip uses this when checkSet() calls it
				current1 -> posXY[y][x] = shipChar;
 
				shipSize = current1 -> patrol;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current1 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
	}
	else if ( player == 2 && ai == 0 )
	{
		go = 0;
 
		while ( go == 0 ) //set carrier
		{
			printf("Setting Carrier - %d blocks in length\n", current2 -> carrier);
			printBoard(player);
			printf("Choose the starting y location of your carrier: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your carrier: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your carrier: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your carrier: ");
				scanf("%d", &x);
			}
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'C';				//placeShip uses this when checkSet() calls it
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> carrier;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current2 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 )	//set battleship
		{
			printf("Setting Battleship - %d blocks in length\n", current2 -> battleship);
			printBoard(player);
			printf("Choose the starting y location of your battleship: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your battleship: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your battleship: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting y location of your battleship: ");
				scanf("%d", &x);
			}
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'B';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> battleship;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
                                if ( go == 0 )
                                        current2 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 )	//set destroyer
		{
			printf("Setting Destroyer - %d blocks in length\n", current2 -> destroyer);
			printBoard(player);
			printf("Choose the starting y location of your destroyer: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your destroyer: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your destroyer: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting y location of your destroyer: ");
				scanf("%d", &x);
			}
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'D';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> destroyer;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
                                if ( go == 0 )
                                        current2 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 ) //set submarine
		{
			printf("Setting Submarine - %d blocks in length\n", current2 -> submarine);
			printBoard(player);
			printf("Choose the starting y location of your submarine: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your submarine: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your submarine: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your submarine: ");
				scanf("%d", &x);
			}
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'S';				//placeShip uses this when checkSet() calls it
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> submarine;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current2 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
 
		go = 0;
 
		while ( go == 0 ) //set patrol
		{
			printf("Setting Patrol Boat - %d blocks in length\n", current2 -> patrol);
			printBoard(player);
			printf("Choose the starting y location of your patrol boat: ");
			scanf("%d", &y);
			while ( y < 0 || y > 9 )
			{
				printf("\nChoose the starting y location of your patrol boat: ");
				scanf("%d", &y);
			}
 
			printf("\nChoose the starting x location of your patrol boat: ");
			scanf("%d", &x);
			while ( x < 0 || x > 9 )
			{
				printf("\nChoose the starting x location of your patrol boat: ");
				scanf("%d", &x);
			}
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'P';				//placeShip uses this when checkSet() calls it
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> patrol;				//checkSet() uses this number for bounds
				go = checkSet(x, y, shipSize, player, shipChar, ai);	//check to see if space is open and within bounds of board
                                if ( go == 0 )
                                        current2 -> posXY[y][x] = 'o';
			}
 
			printBoard(player);
		}
	}
	else
	{
		srand(time(NULL));
		while ( go == 0 )	//set carrier
		{
			y = rand() % 10;
			x = rand() % 10;
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'C';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> carrier;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
			}
		}
		//test
		go = 0;
 
		while ( go == 0 )	//set battleship
		{
			y = rand() % 10;
			x = rand() % 10;
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'B';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> battleship;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
			}
		}
 
		go = 0;
 
		while ( go == 0 )	//set destroyer
		{
			x = rand() % 10;
			y = rand() % 10;
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'D';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> destroyer;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
			}
		}
 
		go = 0;
 
		while ( go == 0 )	//set submarine
		{
			x = rand() % 10;
			y = rand() % 10;
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'S';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> submarine;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
			}
		}
 
		go = 0;
 
		while ( go == 0 )	//set patrol boat
		{
			x = rand() % 10;
			y = rand() % 10;
 
			if ( current2 -> posXY[y][x] == 'o' )
			{
				shipChar = 'P';
				current2 -> posXY[y][x] = shipChar;
 
				shipSize = current2 -> patrol;
				go = checkSet(x, y, shipSize, player, shipChar, ai);
			}
		}
	}
 
return;
}
1 Parameters

Note there are parameters with this function. It receives two integers. The player variable is to allow the function to know which player to use. Without this it wouldn't know what current pointer to use. The ai variable is to allow the function to know if the ai called it. Because if it did, this is a special case. In part 3, the body section, this will be covered further.

2 Variables

The x and y variables are coordinates. These will hold the values at which you want to place the beginning of your ship. The shipSize variable holds the number which represents how many spaces the ship will take. For example: current1 → carrier is equal to 5. Therefore, shipSize = current1 → carrier. This variable (shipSize) is used to send to another function (checkSet). Next is the shipChar of data type char. This variable holds the character of which the ship will represent. In this case, I took the first character of each ship and that was the character for that ship (this worked because there were no repeats). Therfore, carrier is 'c', battleship is 'b', etc. Lastly, the variable go is a loop control variable.

3 Body

If a player called this function, then the if and else if of the selection structure are used. Within each loop (controlled by go) the player will select a y and an x coordinate at which they want their ship to start. Then, if the current position is open (if player 1 called it: current1 → posXY[y][x] == 'o') it will set the shipChar and set that position on the board to this character. Then the shipSize is calculated and checkSet function is called. This function is sent x, y, shipSize, player, shipChar, and ai. For now, all we need to know is that when control comes back to this function, a ship will have been placed. Then the next ship is started and the cycle repeats until the players last ship is set. The same goes for the other player. However, if the ai called this function there are some different methods to setting its ship. The ai portion is in the else part of the selection structure (towards the bottom). Note the srand(time(NULL)); line. This means random will be used. Which is why I had to include <time.h>. For each ship a y and an x is randomly selected. Other than that, this is the only difference.

  • checkSet function checks the bounds of the ship and sets up the placement
int checkSet(int x, int y, int shipSize, int player, char shipChar, int ai)
{
	int right, left, up, down, a, z;
	int rightFlag = 1, leftFlag = 1, upFlag = 1, downFlag = 1; //1 if testable 0 if out of bounds
	int option;				//user input var
	int choice = -1;		//loop control var
	int available[4] = {0}; //used for condition statement (see bottom of if ( player == 1 ) for ex)
	int dirFlag;
 
	//calculating end position of ship
	right = (x + shipSize ) - 1;
	left = (x - shipSize ) + 1;
	up = (y - shipSize ) + 1;
	down = (y + shipSize ) - 1;
 
	if ( player == 1 )
	{
		//Checking for bounds and open spots - right
		if ( right < 0 || right > 9 )
			rightFlag = 0;
		else
		{
			z = x + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current1 -> posXY[y][z] != 'o' )
				{
					rightFlag = 0;
					break;
				}
				z++;
			}
		}
 
		//checking for bounds and open spots - left
		if ( left < 0 || left > 9 )
			leftFlag = 0;
		else
		{
			z = x - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current1 -> posXY[y][z] != 'o' )
				{
					leftFlag = 0;
					break;
				}
				z--;
			}
		}
 
		//checking for bounds and open spots - up
		if ( up < 0 || up > 9 )
			upFlag = 0;
		else
		{
			z = y - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current1 -> posXY[z][x] != 'o' )
				{
					upFlag = 0;
					break;
				}
				z--;
			}
		}
 
		//checking for bounds and open spots - down
		if ( down < 0 || down > 9 )
			downFlag = 0;
		else
		{
			z = y + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current1 -> posXY[z][x] != 'o' )
				{
					downFlag = 0;
					break;
				}
				z++;
			}
		}
 
		if ( upFlag == 0 && rightFlag == 0 && downFlag == 0 && leftFlag == 0 )
			return 0;	//0 is put into go var in setShip() if 0 don't go on and redo loop
	}
	else
	{
		//Checking for bounds and open spots - right
		if ( right < 0 || right > 9 )
			rightFlag = 0;
		else
		{
			z = x + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current2 -> posXY[y][z] != 'o' )
				{
					rightFlag = 0;
					break;
				}
				z++;
			}
		}
 
		//checking for bounds and open spots - left
		if ( left < 0 || left > 9 )
			leftFlag = 0;
		else
		{
			z = x - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current2 -> posXY[y][z] != 'o' )
				{
					leftFlag = 0;
					break;
				}
				z--;
			}
		}
 
		//checking for bounds and open spots - up
		if ( up < 0 || up > 9 )
			upFlag = 0;
		else
		{
			z = y - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current2 -> posXY[z][x] != 'o' )
				{
					upFlag = 0;
					break;
				}
				z--;
			}
		}
 
		//checking for bounds and open spots - down
		if ( down < 0 || down > 9 )
			downFlag = 0;
		else
		{
			z = y + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				if ( current2 -> posXY[z][x] != 'o' )
				{
					downFlag = 0;
					break;
				}
				z++;
			}
		}
 
		if ( upFlag == 0 && rightFlag == 0 && downFlag == 0 && leftFlag == 0 )
			return 0;	//0 is put into go var in setShip() if 0 don't go on and redo loop
	}
 
	if ( ai == 1 )
		option = rand() % 4;
 
	while ( choice != 0 )
	{
		if ( upFlag == 1 )
		{
			if ( ai == 0 )
				printf("0: End at: (%d, %d) - up\n", x, up);
			available[0] = 1;
		}
 
		if ( rightFlag == 1 )
		{
			if ( ai == 0 )
				printf("1: End at: (%d, %d) - right\n", right, y);
			available[1] = 1;
		}
 
		if ( downFlag == 1 )
		{
			if ( ai == 0 )
				printf("2: End at: (%d, %d) - down\n", x, down);
			available[2] = 1;
		}
 
		if ( leftFlag == 1 )
		{
			if ( ai == 0 )
				printf("3: End at: (%d, %d) - left\n", left, y);
			available[3] = 1;
		}
 
		if ( ai == 0 )
		{
			printf("Enter the corresponding number to place end of ship at: ");
			scanf("%d", &option);
 
			while ( option < 0 || option > 4 || available[option] == 0 )
			{
				printf("Enter the corresponding number to place end of ship at: ");
				scanf("%d", &option);
			}
		}
 
		if ( ai == 1 )
		{
			while ( available[option] == 0 )
				option = rand() % 3;
		}
 
		switch ( option )
		{
			case 0: dirFlag = 0;	//up
					placeShip(player, x, y, dirFlag, shipSize, shipChar);
					choice = 0;
					return 1;
 
			case 1: dirFlag = 1;	//right
					placeShip(player, x, y, dirFlag, shipSize, shipChar);
					choice = 0;
					return 1;
 
			case 2: dirFlag = 2;	//down
					placeShip(player, x, y, dirFlag, shipSize, shipChar);
					choice = 0;
					return 1;
 
			case 3: dirFlag = 3;	//left
					placeShip(player, x, y, dirFlag, shipSize, shipChar);
					choice = 0;
					return 1;
		}
	}
}
1 Parameters

The calling function (setShip) passed this function its x, y, shipSize, and shipChar. It also passes variables it was passed: player and ai (these two are unchanged).

2 Variables

The directional integer variables (right, left, up, down) will find the end of the ship. To explain this I'll give a mock y and x. Say I said y was 0 and x was 0 and I was trying to place the carrier (remember, the carrier is 5 blocks in length). the right variable would be 4, left would be -4, up would be -4 and down would be 4. Note the negative numbers. This case is tested for because if there is a negative number then we are out of bounds on the board. That leaves at least right and down. If there is no other ship in the way then these are two valid options. But more on this in the body. The variable a is a loop control variable and z is a coordinate. The directional integer variables that have “Flag” appended to them (ie: leftFlag, upFlag, etc) are variables that will hold 1 if the player can go in that direction and a 0 if they cannot. Option holds the user input (found at bottom). This input is for the direction they want to go. The choice variable is a loop control variable. The array of data type integer of 4 elements in size called available is used for the condition statement at the bottom. Each element represents the position that is available. It is initialized to 0 meaning none are available. However, when the rest of the code executes some or all of the elements will be set to 1. This is mainly used for the ai. The ai randomly chooses a number between 0 and 3 and that is the direction it will go. This direction is held in the dirFlag variable.

3 Body

The first steps are to calculate where the ship will end. That is what the first 4 lines are doing. They take the given x and y and add or subtract the shipSize and then add or subtract 1. The last addition/subtraction is done because the space at which the ship begins is included. If this was left out then every ship would be 1 block larger. After that It checks for bounds and open spots within these bounds. If the end position coordinate is out of the board then the directional flag (ie: rightFlag) is automatically set to 0 representing out of bounds. No further tests are made. However, if the number is within bounds, then it will search every element from the beginning to the end of the ship to make sure that it is open. If it is then the flag variable is set to 1 representing a valid direction. This is repeated for every ship and each player. If all of the flags are 0 then it returns control to the calling function, otherwise, it gives a menu of the available options. This is where you have to careful as a player using this. The options may not always count by 1. Just pay attention to the menu and you'll know what numbers are available to you. If the ai called this function then the option will be selected randomly with a number between 0 and 3. Once the player chooses their direction, the placeShip function is called and is passed player, x, y, dirFlag, shipSize, shipChar. Note, that option 4 will always be displayed and is the option to go back to selecting where you want your ship to start.

  • placeShip function places the ship in the direction the user wanted
void placeShip(int player, int x, int y, int dirFlag, int shipSize, char shipChar)
{
	int a, z;
 
	if ( player == 1 )
	{
		if ( dirFlag == 0 ) //up
		{
			z = y - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current1 -> posXY[z][x] = shipChar;
				z--;
			}
		}
 
		else if ( dirFlag == 1 ) //right
		{
			z = x + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current1 -> posXY[y][z] = shipChar;
				z++;
			}
		}
 
		else if ( dirFlag == 2 ) //down
		{
			z = y + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current1 -> posXY[z][x] = shipChar;
				z++;
			}
		}
 
		else //left
		{
			z = x - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current1 -> posXY[y][z] = shipChar;
				z--;
			}
 
		}
	}
 
	else
	{
		if ( dirFlag == 0 ) //up
		{
			z = y - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current2 -> posXY[z][x] = shipChar;
				z--;
			}
		}
 
		else if ( dirFlag == 1 ) //right
		{
			z = x + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current2 -> posXY[y][z] = shipChar;
				z++;
			}
		}
 
		else if ( dirFlag == 2 ) //down
		{
			z = y + 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current2 -> posXY[z][x] = shipChar;
				z++;
			}
		}
 
		else //left
		{
			z = x - 1;
			for ( a = 0; a < shipSize - 1; a++ )
			{
				current2 -> posXY[y][z] = shipChar;
				z--;
			}
		}
	}
 
return;
}
1 Parameters

This function receives player, x, y, dirFlag, shipSize, and shipChar. Again player, x, and y are unchanged. The player represents who called it and x and y are the player's coordinates. The variable dirFlag represents the direction they chose, more on this in the body section. The shipSize is the number that represents the bounds of the ship (the ship's size). Finally, the shipChar is the character that represents the ship.

2 Variables

This function only has two variables and they are a and z of type integer. The variable a is a loop control variable and z is a place holder for a coordinate.

3 Body

Placing a ship is determined by the direction the player chose. Therefore, a nested selection structure represents each direction possible. If dirFlag is equal to 0, then the player went up, if 1 then right, if 2 then down, or 3 then left. This is repeated for both players. Z is the variable that is incremented or decremented depending on which way you went. For example, if you went up the z variable would go into the first bracket of posXY and be decremented after each iteration in the for loop. Since the board's origin is in the top left, you decrement the y to go up.

  • printBoard prints the board where your ships are
void printBoard(int player)
{
	int x, y;
 
	if ( player == 1 )
	{
		for ( x = 0; x < ROWS; x++)
		{
			if ( x == 0 )
				printf("     0     1     2     3     4     5     6     7     8     9\n");
			printf("--------------------------------------------------------------\n");
			printf("%d |", x);
			for ( y = 0; y < COLS; y++ )
			{
				printf("  %c  |", current1 -> posXY[x][y]);
			}
				printf("\n");
		}
		printf("--------------------------------------------------------------\n");
	}
	else
	{
		for ( x = 0; x < ROWS; x++)
		{
			if ( x == 0 )
				printf("     0     1     2     3     4     5     6     7     8     9\n");
			printf("--------------------------------------------------------------\n");
			printf("%d |", x);
			for ( y = 0; y < COLS; y++ )
			{
				printf("  %c  |", current2 -> posXY[x][y]);
			}
				printf("\n");
		}
		printf("--------------------------------------------------------------\n");
	}
 
return;
}
1 Parameters

This function receives player as its only parameter. If it didn't receive this variable then it wouldn't know which board to print (player 1 or player 2's board).

2 Variables

This function only has two variables and they are x and y of the integer data type. These variables are loop control variables for the nested for loops that traverse the posXY two dimensional array current1 and current2 point to.

3 Body

Not much to say about this except to look at the code. It's pretty self explanatory. Within the outer for loop I test if x is equal to 0 because I want to print the column heading. Then it prints a dashed line and a pipe. Then in the inner for loop it displays a grid with each character and a row heading (the row's number).

  • printAtkBoard prints where the player has attacked
void printAtkBoard(int player)
{
	int x, y;
 
	if ( player == 1 )
	{
		for ( x = 0; x < ROWS; x++)
		{
			if ( x == 0 )
				printf("     0     1     2     3     4     5     6     7     8     9\n");
			printf("--------------------------------------------------------------\n");
			printf("%d |", x);
			for ( y = 0; y < COLS; y++ )
			{
				printf("  %c  |", current1 -> attackXY[x][y]);
			}
				printf("\n");
		}
		printf("--------------------------------------------------------------\n");
	}
	else
	{
		for ( x = 0; x < ROWS; x++)
		{
			if ( x == 0 )
				printf("     0     1     2     3     4     5     6     7     8     9\n");
			printf("--------------------------------------------------------------\n");
			printf("%d |", x);
			for ( y = 0; y < COLS; y++ )
			{
				printf("  %c  |", current2 -> attackXY[x][y]);
			}
				printf("\n");
		}
		printf("--------------------------------------------------------------\n");
	}
 
return;
}
1 Parameters

The only parameters for this function is player. This variable allows for the function to know which board to print.

2 Variables

There are only two integer type variables called x and y. These variables are loop control variables (they control the nested for loops).

3 Body

Like the printBoard function above, reading the code is the best way to understand what it is doing. It is taking the player's attackXY two dimensional array and displaying it in a grid layout.

  • checkATk function checks the bounds of each attack and places it in the players attackXY array
int checkAtk(int player, int x, int y)
{
	int hit = 0;
 
	if ( player == 1 )
	{
		if ( current1 -> attackXY[y][x] == '-' )
		{
			current1 -> attackXY[y][x] = 'X';
			if ( current2 -> posXY[y][x] != 'o' )
			{
				printf("Hit\n");
				hit++;
				sleep(1);
				if ( current2 -> posXY[y][x] == 'C' )
				{
					current2 -> carrier--;
					current2 -> posXY[y][x] = 'c';
					if ( current2 -> carrier == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current2 -> turns--;
					}
				}
				else if ( current2 -> posXY[y][x] == 'B' )
				{
					current2 -> battleship--;
					current2 -> posXY[y][x] = 'b';
					if ( current2 -> battleship == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current2 -> turns--;
					}
				}
				else if ( current2 -> posXY[y][x] == 'D' )
				{
					current2 -> destroyer--;
					current2 -> posXY[y][x] = 'd';
					if ( current2 -> destroyer == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current2 -> turns--;
					}
				}
				else if ( current2 -> posXY[y][x] == 'S' )
				{
					current2 -> submarine--;
					current2 -> posXY[y][x] = 's';
					if ( current2 -> submarine == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current2 -> turns--;
					}
				}
				else if ( current2 -> posXY[y][x] == 'P' )
				{
					current2 -> patrol--;
					current2 -> posXY[y][x] = 'p';
					if ( current2 -> patrol == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current2 -> turns--;
					}
				}
				if ( hit > 0 )
					current1 -> attackXY[y][x] = 'H';
			}
			else
				printf("Miss\n");
 
			sleep(1);
			return 1;
		}
		return 0;
	}
 
	else
	{
		if ( current2 -> attackXY[y][x] == '-' )
		{
			current2 -> attackXY[y][x] = 'X';
			if ( current1 -> posXY[y][x] != 'o' )
			{
				printf("Hit\n");
				hit++;
				sleep(1);
				if ( current1 -> posXY[y][x] == 'C' )
				{
					current1 -> carrier--;
					current1 -> posXY[y][x] = 'c';
					if ( current1 -> carrier == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current1 -> turns--;
					}
				}
				else if ( current1 -> posXY[y][x] == 'B' )
				{
					current1 -> battleship--;
					current1 -> posXY[y][x] = 'b';
					if ( current1 -> battleship == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current1 -> turns--;
					}
				}
				else if ( current1 -> posXY[y][x] == 'D' )
				{
					current1 -> destroyer--;
					current1 -> posXY[y][x] = 'd';
					if ( current1 -> destroyer == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current1 -> turns--;
					}
				}
				else if ( current1 -> posXY[y][x] == 'S' )
				{
					current1 -> submarine--;
					current1 -> posXY[y][x] = 's';
					if ( current1 -> submarine == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current1 -> turns--;
					}
				}
				else if ( current1 -> posXY[y][x] == 'P' )
				{
					current1 -> patrol--;
					current1 -> posXY[y][x] = 'p';
					if ( current1 -> patrol == 0 )
					{
						printf("You sank a ship\n");
						sleep(1);
						current1 -> turns--;
					}
				}
				if ( hit > 0 )
					current2 -> attackXY[y][x] = 'H';
			}
		else
			printf("Miss\n");
 
		sleep(1);
		return 1;
		}
		return 0;
	}
}
1 Parameters

This function receives all integer type variables. They are called, player, x, and y. The player tells the function which current pointer to use (current1 or current2). The variables x and y are coordinates that the player chose to attack.

2 Variables

The only variable is hit which is an integer and initialized to 0. This variable represents a hit on the enemies ship. If it is greater than 0 then there was a hit, if not, a miss.

3 Body

In the initPlayers function the attackXY two dimensional array was initialized to '-'. Therefore, if the coordinates (y, x) are equal to this character then it is a valid attack, otherwise, the spot has already been hit. The coordinates are then immediately set to the character 'X' on the player's attackXY. After that it tests to see if the opponents posXY (The array that holds the ship locations) at the coordinate (y, x) is not equal to 'o'. If it is not equal then it is a ship and further testing is made, but if it is equal, then display “miss” to the screen and return to the calling function. But lets say it was not equal, it will test that coordinate against every ship character until it finds a match. So lets say it was equal to 'C' (carrier of 5 blocks in length). The opponents carrier size is reduced by one (ie: current2 → carrier–;) and the opponents posXY is changed from a 'C' to a 'c' (capital letter to lower case letter). Then a quick test to see if the carrier is at 0. If it is then this represents a sunken ship. The opponents turn will be decremented and the string “You sank a ship” will be displayed on the screen. Finally, control will be given back to the calling function.

  • checkWin is the function that decides a winner
int checkWin(int player)
{
	if ( player == 1 )		//player 1 wins
	{
		if ( current2 -> turns == 0 )
		{
			printf("Congratulations Player %d, You Win!\n", player);
			return 1;
		}
	}
 
	else	//player 2 wins
	{
		if ( current1 -> turns == 0 )
		{
			printf("Congratulations Player %d, You Win!\n", player);
			return 1;
		}
	}
 
return 0;
}
1 Parameters

This function receives one variable of type integer and is called player. This variables represents the player that called it.

2 Body

All this function does is look at the turns of both players. If one is set to 0 then there is a winner. For example, it checks for current2 → turns. This means if player 2's turn count is equal to 0 then player 1 wins and a message is displayed to the screen and control given back to the calling function. That function then gives control back to the main function which called it.

  • easy player vs computer on an easy level of difficulty
void easy()
{
	int player = 1, winner = 0, turnCnt, x, y, go = 0;
	int ai = 0;		//lets other functions know that its player vs computer if ai == 0 its pvp else pve
 
	initPlayers();
 
	printf("Welcome to the PvE section: Easy\n");
	printf("Good luck player, then again, it is only on easy.\nFor a real challenge try me on hard ^_O\n");
	printf("Player 1 please set your battleships.\n\n");
	info();
	setShips(player, ai);
 
	//AI set ships
	player++;
	ai++;
	setShips(player, ai);
 
	player--;
	ai--;
	turnCnt = current1 -> turns;
 
	while ( winner == 0 )
	{
		while ( turnCnt != 0 )
		{
			printf("Player %d's turn\nTurn: %d\n\n", player, turnCnt);
			while ( go == 0 )
			{
				if ( player == 1 )	
				{
					if ( ai == 1 )
						ai = 0;
					printBoard(player);
					printAtkBoard(player);
					printf("Enter attack coordinate y: ");
					scanf("%d", &y);
 
					while ( y < 0 || y > 9 )
					{
						printf("Enter attack coordinate y: ");
						scanf("%d", &y);
					}
 
					printf("Enter attack coordinate x: ");
					scanf("%d", &x);
 
					while ( x < 0 || x > 9 )
					{
						printf("Enter attack coordinate x: ");
						scanf("%d", &x);
					}
				}
				else
				{
					if ( ai == 0 )
						ai = 1;
					y = rand() % 9;
					x = rand() % 9;
				}
				go = checkAtk(player, x, y);
			}
			winner = checkWin(player);
			if ( winner == 1 )
				break;
			turnCnt--;
			go--;
		}
		if ( player == 1 )
		{
			player++;
			turnCnt = current2 -> turns;
		}
		else
		{
			player--;
			turnCnt = current1 -> turns;
		}
	}
 
return;
}
1 Variables

This function has many variables and all of them are of the data type integer. The variable player represents which player is currently running. The variable winner represents the end of the game. It is also a loop control variable. It is initialized to 0 representing no winner, but when it is changed to 1 then there is a winner. The variable turnCnt holds the players turns for that round. It is set to the player's turn that which current points to (ie: current1 → turns). It then decrements until 0. Once 0, it is the other players turn. The variables x and y are user defined and ai defined. They hold the coordinates of which one wishes to unleash a payload on. The variable go is a loop control variable. While it is equal to 0 keep getting attack coordinates (because something was wrong; ie: tried to attack out of bounds or attacked a spot where you have already attacked). Finally, the variable ai is now of importance. When it is equal to 0 it represents a real player but, when it is equal to 1 it represents the computer. This variable allows other functions to know how to deal with it.

2 Body

Like the pvp function this one also calls initPlayers and info functions. Then it calls setShips and passes it player and ai. These sections can be found somewhere above this section. This function ultimately sets the ships for each player. However, when the ai is set to 1 the ships are placed randomly for the computer player. After the ships are set the players take turns attacking (starting with player 1), just like pvp, medium and hard. However, this time the second player is the computer. The computer on the easy level attacks randomly no matter what. Then like pvp, medium, and hard, attacks are checked by the checkAtk function and a winner is check in the checkWin function.

  • medium player vs computer on a medium level difficulty
void medium()
{
	int player = 1, winner = 0, turnCnt, x, y, go = 0;
	int ai = 0;		//lets other functions know that its player vs computer if ai == 0 its pvp else pve
	int *nextAtk = NULL;
	int level = 0; //used for next attack positions. 1 reps hard 0 reps medium.
	int brFlag = 0;
	int overflow = 0; /*used in case medium ai didn't sink every ship and it completed checkerboard atk
					   * Invert checker style - before: even row even column, odd row odd column.
					   * Now: even row odd column, odd row even column
					   */
 
	initPlayers();
 
	printf("Welcome to the PvE section: Medium\n");
	printf("Good luck player... You'll need it.\n");
	printf("Player 1 please set your battleships.\n\n");
	info();
	setShips(player, ai);
 
	//AI set ships
	player++;
	ai++;
	setShips(player, ai);
 
	player--;
	ai--;
	turnCnt = current1 -> turns;
 
	while ( winner == 0 )
	{
		while ( turnCnt != 0 )
		{
			printf("Player %d's turn\nTurn: %d\n\n", player, turnCnt);
			while ( go == 0 )
			{
				if ( player == 1 )	
				{
					if ( ai == 1 )
						ai = 0;
					printBoard(player);
					printAtkBoard(player);
					printf("Enter attack coordinate y: ");
					scanf("%d", &y);
 
					while ( y < 0 || y > 9 )
					{
						printf("Enter attack coordinate y: ");
						scanf("%d", &y);
					}
 
					printf("Enter attack coordinate x: ");
					scanf("%d", &x);
 
					while ( x < 0 || x > 9 )
					{
						printf("Enter attack coordinate x: ");
						scanf("%d", &x);
					}
					go = checkAtk(player, x, y);
				}
				else
				{
					if ( ai == 0 )
						ai = 1;
 
					if ( nextAtk != NULL )
					{
						y = nextAtk[0];
						x = nextAtk[1];
						go = checkAtk(player, x, y);
						nextAtk = nextAtkPos(level, y, x);
					}
					else
					{
						//checkerboard style attack
						for ( y = 0; y < COLS; y++ )
						{
							for ( x = 0; x < ROWS; x++ )
							{
								if ( overflow == 0 )
								{
									if ( y % 2 == 0 )			//even row
									{
										if ( x % 2 == 0 )		//even col
										{
											go = checkAtk(player, x, y);
											if ( go == 1 )
											{
												brFlag = 1;
												break;
											}
										}
									}
									else					//odd row
									{
										if ( x % 2 != 0 )	//odd col
										{
											go = checkAtk(player, x, y);
											if ( go == 1 )
											{
												brFlag = 1;
												break;
											}
										}
									}
									if ( y == COLS - 1 && x == ROWS - 1 )
										overflow = 1;
								}
								else
								{
									if ( y % 2 == 0 )	//even row
									{
										if ( x % 2 != 0 )	//odd col
										{
											go = checkAtk(player, x, y);
											if ( go == 1 )
											{
												brFlag = 1;
												break;
											}
										}
									}
									else				//odd row
									{
										if ( x % 2 == 0 )	//even col
										{
											go = checkAtk(player, x, y);
											if ( go == 1 )
											{
												brFlag = 1;
												break;
											}
										}
									}
								}
							}
							if ( brFlag == 1 )
							{
								brFlag = 0;
								break;
							}
						}
						nextAtk = nextAtkPos(level, y, x);
					}
				}
			}
			winner = checkWin(player);
			if ( winner == 1 )
				break;
			turnCnt--;
			go--;
		}
		if ( player == 1 )
		{
			player++;
			turnCnt = current2 -> turns;
		}
		else
		{
			player--;
			turnCnt = current1 -> turns;
		}
	}
 
	delete [] nextAtk;
 
 
return;
}
1 Variables

This function has many variables and all of them are of the data type integer. The variable player represents which player is currently running. The variable winner represents the end of the game. It is also a loop control variable. It is initialized to 0 representing no winner, but when it is changed to 1 then there is a winner. The variable turnCnt holds the players turns for that round. It is set to the player's turn that which current points to (ie: current1 → turns). It then decrements until 0. Once 0, it is the other players turn. The variables x and y are user defined and ai defined. They hold the coordinates of which one wishes to unleash a payload on. The variable go is a loop control variable. While it is equal to 0 keep getting attack coordinates (because something was wrong; ie: tried to attack out of bounds or attacked a spot where you have already attacked). The variable ai is now of importance. When it is equal to 0 it represents a real player but, when it is equal to 1 it represents the computer. This variable allows other functions to know how to deal with it. The integer pointer named nextAtk is used for the brain of the ai. In easy attacks were made at random. But now attacks are made based on knowledge (or rather, cheating). This pointer will point to an array that is set up in the nextAtk function. For now all you need to know is that this function will give medium level ai a 25% chance of knowing where to strike next. The variable level is used for the nextAtk function. level represents which ai called it. If level is equal to 0 then it is a medium ai, but if it is equal to 1 then it is a hard ai. The variable brFlag is used for the nested for loop at the bottom. This will be covered below in the the body section. The variable overflow is to detect when to change attack style patterns. This variable will be explained below in the body section as well.

2 Body

Like the pvp, easy, and hard functions this one calls initPlayers and info functions. It also calls setShips function. For medium the ships are also placed at random for the computer player. Like pvp, easy, and hard, player 1 takes the first turn. Then it is the computers turn. I will be explaining only the computers turn since the players turn has been explained in pvp and easy. The first two lines is just some defensive programming. I want to force ai to be equal to 1 sine we are dealing with a computer player now. I realize all you have to do is say ai = 1 and omit the if statement. The next test is for nextAtk. On the first move nextAtk is set to NULL, so this is skipped for now. However I will come back to this. Medium employs a basic technique for battleship attack coordinates. If you attack every other spot you will maximize your odds of finding a ship. I started off with even row even column, and odd row odd column method. For example, the first attack would be (0, 0), the next (0, 2), next (0, 4), and so on until you get to 8 then it goes down a row at which the first attack is (1, 1), the next (1, 3), next (1, 5), and so on until you get to 9. Now I will explain briefly how it figures out its next attack (further explanation can be found towards the bottom of this tutorial in the *nextAtk function section). When the ai makes a move it sends its coordinates and level to *nextAtk function. This function determines if there was a hit or not. For now all we need to know is that this function may return the coordinates for the computer's next attack. If nextAtk does not equal NULL then it will use those coordinates and take another chance after the attack to figure out its next attack again. It may return the coordinates, it may not. This leads to a problem with the checkerboard attack style earlier explained. It is possible that it did not sink every ship yet it completed the checkerboard attack style. This is where the overFlow variable comes in. It alters the checkerboard attack style to go back to the beginning and attack all of the remaining empty spaces. It uses an even row odd column and odd row even column method. For example its first attack would be (0, 1), next (0, 3), next (0, 5), and so on until it hit 9. Then it goes down a row and its attack would be (1, 0), next (1, 2), next (1, 4), and so on until it hit 8. Keep in mind that again with every attack there is a chance that it will know where to strike next vi nextAtk function. Then like the others (pvp, easy, and hard) it checks for a winner after each attack. This function also has some maintenance to take care of. To free up memory it deletes the pointer nextAtk before returning control to the calling function.

  • hard player vs computer on a hard level of difficulty
void hard()
{
	int player = 1, winner = 0, turnCnt, x, y, go = 0;
	int ai = 0;		//lets other functions know that its player vs computer if ai == 0 its pvp else pve
	int *nextAtk = NULL;
	int level = 1; //used for next attack positions. 1 reps hard 0 reps medium.
	int brFlag = 0;
 
	initPlayers();
 
	printf("Welcome to the PvE section: Hard\n");
	printf("Good luck player... You'll need it.\n");
	printf("Player 1 please set your battleships.\n\n");
	info();
	setShips(player, ai);
 
	//AI set ships
	player++;
	ai++;
	setShips(player, ai);
 
	player--;
	ai--;
	turnCnt = current1 -> turns;
 
	while ( winner == 0 )
	{
		while ( turnCnt != 0 )
		{
			printf("Player %d's turn\nTurn: %d\n\n", player, turnCnt);
			while ( go == 0 )
			{
				if ( player == 1 )	
				{
					if ( ai == 1 )
						ai = 0;
					printBoard(player);
					printAtkBoard(player);
					printf("Enter attack coordinate y: ");
					scanf("%d", &y);
 
					while ( y < 0 || y > 9 )
					{
						printf("Enter attack coordinate y: ");
						scanf("%d", &y);
					}
 
					printf("Enter attack coordinate x: ");
					scanf("%d", &x);
 
					while ( x < 0 || x > 9 )
					{
						printf("Enter attack coordinate x: ");
						scanf("%d", &x);
					}
					go = checkAtk(player, x, y);
				}
				else
				{
					if ( ai == 0 )
						ai = 1;
 
					if ( nextAtk != NULL )
					{
						y = nextAtk[0];
						x = nextAtk[1];
						go = checkAtk(player, x, y);
						nextAtk = nextAtkPos(level, y, x);
					}
					else
					{
						//checkerboard style attack
						for ( y = 0; y < COLS; y++ )
						{
							for ( x = 0; x < ROWS; x++ )
							{
								if ( y % 2 == 0 )			//even row
								{
									if ( x % 2 == 0 )		//even col
									{
										go = checkAtk(player, x, y);
										if ( go == 1 )
										{
											brFlag = 1;
											break;
										}
									}
								}
								else					//odd row
								{
									if ( x % 2 != 0 )	//odd col
									{
										go = checkAtk(player, x, y);
										if ( go == 1 )
										{
											brFlag = 1;
											break;
										}
									}
								}
							}
							if ( brFlag == 1 )
							{
								brFlag = 0;
								break;
							}
						}
						nextAtk = nextAtkPos(level, y, x);
					}
				}
			}
			winner = checkWin(player);
			if ( winner == 1 )
				break;
			turnCnt--;
			go--;
		}
		if ( player == 1 )
		{
			player++;
			turnCnt = current2 -> turns;
		}
		else
		{
			player--;
			turnCnt = current1 -> turns;
		}
	}
 
	delete [] nextAtk;
 
return;
}
1 Variables

This function has many variables and all of them are of the data type integer. The variable player represents which player is currently running. The variable winner represents the end of the game. It is also a loop control variable. It is initialized to 0 representing no winner, but when it is changed to 1 then there is a winner. The variable turnCnt holds the players turns for that round. It is set to the player's turn that which current points to (ie: current1 → turns). It then decrements until 0. Once 0, it is the other players turn. The variables x and y are user defined and ai defined. They hold the coordinates of which one wishes to unleash a payload on. The variable go is a loop control variable. While it is equal to 0 keep getting attack coordinates (because something was wrong; ie: tried to attack out of bounds or attacked a spot where you have already attacked). The variable ai is now of importance. When it is equal to 0 it represents a real player but, when it is equal to 1 it represents the computer. This variable allows other functions to know how to deal with it. The integer pointer named nextAtk is used for the brain of the ai. In easy attacks were made at random. But now attacks are made based on knowledge (or rather, cheating). This pointer will point to an array that is set up in the nextAtk function. For now all you need to know is that this function will give medium level ai a 25% chance of knowing where to strike next. The variable level is used for the nextAtk function. level represents which ai called it. If level is equal to 0 then it is a medium ai, but if it is equal to 1 then it is a hard ai. The variable brFlag is used for the nested for loop at the bottom. This will be covered below in the the body section. The variable overflow is to detect when to change attack style patterns. This variable will be explained below in the body section as well.

2 Body

Like pvp, easy and medium this function calls initPlayer and info functions. It also calls setShips. The hard level ai like easy and medium also sets its ships randomly. I will now explain how the hard level ai takes its turns (the explanation for the player can be found in pvp or easy. For pvp, easy, medium, and hard it is unchanged). Like the medium level ai, the hard ai figures out its attack coordinates by a checkerboard attack style. I started it off with the even row even column and odd row odd column method. For example its first attack will be (0, 0), next (0, 2), nexst (0, 4), and so on until it hits 8. Then it will go down one row and its attack will be (1, 1), next (1,3), next (1, 5) and so on until it hits 9. Now, with every attack it has a chance to know where it will strike next by the nextAtk function. The pointer nextAtk set up in this function will point the array that the nextAtk function will create. This function will be explained below in its own section. All you need to know for now is that if the attack coordinate it sent to the function was a hit, it will always return its next attack; so no need for going back and having to cover our tracks by changing the checkerboard attack style method like I had to with the medium function. After each turn it checks for a winner and also deletes the nextAtk pointer for better memory management.

  • *nextAtkPos function is the brain for the medium and hard level functions
int *nextAtkPos(int level, int y, int x)
{
	int *shipPtr = new int[2]; //holds the y and x coordinates of next attack
	int a, b;
	int random;		//if level == 0 then medium and give 50% chance of knowing next attack. If random = 1 then next attack is known
	int skill = 5; 	//used for mediums ability to "guess" where its next attack should be. Currently: 25%
 
	if ( level == 0 )
		random = rand() % skill;
 
	if ( current1 -> posXY[y][x] == 'c' )
	{
		for ( a = 0; a < COLS; a++ )
		{
			for ( b = 0; b < ROWS; b++ )
			{
				if ( current1 -> posXY[a][b] == 'C' )
				{
					if ( level == 1 || random == 1 )
					{
						shipPtr[0] = a;
						shipPtr[1] = b;
						return shipPtr;
					}
				}
			}
		}
	}
 
	else if ( current1 -> posXY[y][x] == 'b' )
	{
		for ( a = 0; a < COLS; a++ )
		{
			for ( b = 0; b < ROWS; b++ )
			{
				if ( current1 -> posXY[a][b] == 'B' )
				{
					if ( level == 1 || random == 1 )
					{
						shipPtr[0] = a;
						shipPtr[1] = b;
						return shipPtr;
					}
				}
			}
		}
	}
 
	else if ( current1 -> posXY[y][x] == 'd' )
	{
		for ( a = 0; a < COLS; a++ )
		{
			for ( b = 0; b < ROWS; b++ )
			{
				if ( current1 -> posXY[a][b] == 'D' )
				{
					if ( level == 1 || random == 1 )
					{
						shipPtr[0] = a;
						shipPtr[1] = b;
						return shipPtr;
					}
				}
			}
		}
	}
 
	else if ( current1 -> posXY[y][x] == 's' )
	{
		for ( a = 0; a < COLS; a++ )
		{
			for ( b = 0; b < ROWS; b++ )
			{
				if ( current1 -> posXY[a][b] == 'S' )
				{
					if ( level == 1 || random == 1 )
					{
						shipPtr[0] = a;
						shipPtr[1] = b;
						return shipPtr;
					}
				}
			}
		}
	}
 
	else if ( current1 -> posXY[y][x] == 'p' )
	{
		for ( a = 0; a < COLS; a++ )
		{
			for ( b = 0; b < ROWS; b++ )
			{
				if ( current1 -> posXY[a][b] == 'P' )
				{
					if ( level == 1 || random == 1 )
					{
						shipPtr[0] = a;
						shipPtr[1] = b;
						return shipPtr;
					}
				}
			}
		}
	}
 
return NULL;
}
1 Parameters

This function receives three integer variables called level, y, and x. The variable level determines which function called it. If it is equal to 0 then medium called it, otherwise (it is equal to 1) hard called it. The variables y and x are the attack coordinates.

2 Variables

This function has quite a few integer variables. The pointer shipPtr points to an array of two elements in size. If the next attack is calculated, then this pointer will fill the array. The variables a and b are loop control variables (nested for loops to traverse through posXY of player 1 (current1 → posXY). The variable random is for the medium level ai. It will hold the number it guesses and it will be compared to 1. If it guessed one then it will know its next attack. More on this in the body section. The variable skill is used for the medium level ai. This number represents the percentage at which it can guess to know its next attack. More on this in the body section.

3 Body

The first test is to see if medium called it and if so it will determine if it will know its next attack or not. For now, there is a 25% chance for the medium level ai to know, whereas, the hard level ai has a 100% chance to know. This function basically just tests the coordinates (y, x) against the first players posXY two dimensional array (ie: current1 → posXY[y][x] == 'C'). It will test for every ship character. If there is a match then the shipPtr[0] and shipPtr[1] will be filled with those coordinates (a and b respectively) and this pointer will be returned to the calling function (medium or hard). The pointer can only fill the array if there was a hit. Otherwise it will return NULL instead of the coordinates.

  • cleanUp at the end of the day I made a mess and have to clean it up. This function frees memory
void cleanUp()
{
	free(head1);
	free(head2);
 
return;
}
1 Body

When dealing with malloc it is always a good idea to de-allocate the memory you used. That is exactly what this does. I'm freeing head1 and head2 which current1 and current2 point to respectively. Since I malloc'ed head1 and head2 I should free these.

You have reached the end of the battleship documentationar. Now that your brain has been liquified you may click the download link and compile with: g++ battleship.cpp -o battleship and execute with ./battleship and have some fun :)

Download: C++ source

Pong

Tutorials for SDL can be found here: http://www.lazyfoo.net/SDL_tutorials/
If named pongV9.cpp then compile with:
g++ pongV9.cpp -lSDL -lSDL_ttf -lSDL_image -o pongV9

Controls: Player 1: w/s – Player 2: up/down

Well here it is, a two player pong game using c, c++, and SDL. I hope you enjoy.

  1. Create two bmps (Why bitmaps? Because that is the native image format that SDL will deal with, otherwise you will have to download and include another header file). One a square of 15×15 pixels and one a paddle of 15×100 pixels. Make these pictures have a transparent background and color the objects white.
  2. Download the .ttf (lazy.ttf) font from: http://www.lazyfoo.net/SDL_tutorials/lesson07/index.php
  3. Download the appropriate header files: SDL, SDL_image, SDL_ttf (can be found: http://lazyfoo.net/SDL_tutorials/lesson01/index.php)

Once those first three steps are completed, this is what the code will look like.

  • header files
#include <cstdlib>
#include <cstdio>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>
#include <string>
 
using namespace std;

The first two are for normal c functionality, the next three are for SDL and its extended libraries (which is why the compile argument changes with each one you have) and the last one is for the C++ string functionality. I'm using namespace std so I can avoid the scope resolution operator as much as possible.

  • The global variables
//used to hold the string of each player's score
char *s = (char *) malloc (sizeof(char)*5);
 
//The screen attributes
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;
 
//The frame rate
const int FRAMES_PER_SECOND = 20;
 
//The dimensions of the paddle
const int PADDLE_WIDTH = 20;
const int PADDLE_HEIGHT = 100;
 
//The dimensions of the ball
const int BALL_WIDTH = 15;
const int BALL_HEIGHT = 15;
 
//The surfaces
SDL_Surface *screen = NULL;
SDL_Surface *message = NULL;
SDL_Joystick *stickL = NULL;	//used for usb joystick control
SDL_Joystick *stickR = NULL;
 
//the font that's going to be used
TTF_Font *font = NULL;
 
//color of the font -- white
SDL_Color textColor = {255, 255, 255};
 
//The event structure
SDL_Event event;

The first line is to declare a c string of 5 elements in size so that later (in the main function) an SDL function can use it to display the scores. Next is the screen's size and resolution (BPP stands for Bits Per Pixel). After that is how we put a cap on the frame rate so that now matter how strong the computer, it will always and only display 20 frames per second. Then the next two blocks are for setting the dimensions of the ball and paddle. After that are the SDL surfaces. One will point to the screen, another to the message (each player's score in this case), and two joysticks (this is for extra functionality, in case there are joysticks plugged into the USB). Then there is the pointer for the font so that we can display a message to our screen, and underneath that is the method of setting its color using RGB values (Red, Green, Blue; mine is set to white). Finally, the event structure. This structure captures mouse and keyboard activity.

  • The class
class PongObj
{
	private:
		//player scores
		int p1, p2;
 
	public:
		//the collision box of the ball
		SDL_Rect box;
		SDL_Surface *surface;
 
		//velocity of ball
		int xVel, yVel;
 
		//default constructor
		PongObj(int, int, int, int, int, int);
 
		//moves ball
		void move(int, PongObj, PongObj, PongObj);
 
		//shows ball on screen
		void show();
 
		//keeps track of score
		void score();
 
		//returns player 1 score
		char *getP1();
 
		//returns player 2 score
		char *getP2();
 
		//Reset ball when it goes past a paddle
		void resetBall();
};
1 Variables

These variables are of type integer and are set to private. They will hold the numeric score for each player. After that are all of the methods and some other variables that are set to public. The inline comments should suffice enough information to explain what each does.

  • Check for collisions
bool check_collision(SDL_Rect A, SDL_Rect B )
{
	//The sides
	int leftA, leftB;
	int rightA, rightB;
	int topA, topB;
	int bottomA, bottomB;
 
	//calculate sides of rect A
	leftA = A.x;
	rightA = A.x + A.w;
	topA = A.y;
	bottomA = A.y + A.h;
 
	//calculate sides of rect B
	leftB = B.x;
	rightB = B.x + B.w;
	topB = B.y;
	bottomB = B.y + B.h;
 
	//if any of the sides from A are outside of B (no collision)
	if ( bottomA <= topB )
		return false;
	if ( topA >= bottomB )
		return false;
	if ( rightA <= leftB )
		return false;
	if ( leftA >= rightB )
		return false;
 
	//If none of the sides from A are outside B (collision)
	return true;
}

This function is passed two SDL rectangles (a paddle and the ball in this case). Each side is then set to a variable using its box variable. Then each side's location is compared to its opposite (ie: bottomA to topB). If the location is touching or within another side, then there was a collision and true is returned, otherwise, false.

  • The second class - timer
//The timer
class Timer
{
    private:
    //The clock time when the timer started
    int startTicks;
 
    //The ticks stored when the timer was paused
    int pausedTicks;
 
    //The timer status
    bool paused;
    bool started;
 
    public:
    //Default constructor
    Timer();
 
    //The various clock actions
    void start();
    void stop();
    void pause();
    void unpause();
 
    //Gets the timer's time
    int get_ticks();
 
    //Checks the status of the timer
    bool is_started();
    bool is_paused();
};

This class is used for setting the frame rate and capping it. Most of its functions I do not use because I don't use anything time driven.

  • Timer constructor
Timer::Timer()
{
    //Initialize the variables
    startTicks = 0;
    pausedTicks = 0;
    paused = false;
    started = false;
}

Fairly self explanatory. The two integers are keeping track of how many ticks have been made while the timer is running and paused. The other two are setting the bool variables to false because the timer has not yet started nor is it paused.

  • Start timer
void Timer::start()
{
    //Start the timer
    started = true;
 
    //Unpause the timer
    paused = false;
 
    //Get the current clock time
    startTicks = SDL_GetTicks();
}

This function allows for the clock to begin ticking, unpause it, and start counting the number of ticks during the unpaused phase.

  • Stop timer
void Timer::stop()
{
    //Stop the timer
    started = false;
 
    //Unpause the timer
    paused = false;
}

This function stops the clock but does not count as being paused.

  • Pause timer
void Timer::pause()
{
    //If the timer is running and is not paused
    if( ( started == true ) && ( paused == false ) )
    {
        //Pause the timer
        paused = true;
 
        //Calculate the paused ticks
        pausedTicks = SDL_GetTicks() - startTicks;
    }
}

This function begins with an initial check to make sure the timer is actually on. Then, if it really is, it will pause it and figure the amount of paused ticks that pass.

  • Unpause timer
void Timer::unpause()
{
    //If the timer is paused
    if( paused == true )
    {
        //Unpause the timer
        paused = false;
 
        //Reset the starting ticks
        startTicks = SDL_GetTicks() - pausedTicks;
 
        //Reset the paused ticks
        pausedTicks = 0;
    }
}

This function begins with a check to make sure the timer is paused. If it is then it will unpause it, figure the amount of ticks that were counted while running and reset the amount of ticks that passed while paused.

  • Get the amount of ticks for paused/unpaused
int Timer::get_ticks()
{
    //If the timer is running
    if( started == true )
    {
        //If the timer is paused
        if( paused == true )
        {
            //Return the number of ticks when the timer was paused
            return pausedTicks;
        }
        else
        {
            //Return the current time minus the start time
            return SDL_GetTicks() - startTicks;
        }
    }
 
    //If the timer isn't running
    return 0;
}

This function begins with a check to make sure the timer is running (because if it is not then there would not have been any ticks for it to record). But, if it is running then check its state, paused or unpaused, and return that value.

  • Check if timer is started
bool Timer::is_started()
{
    return started;
}

Pure and simple, a method that returns if the timer is started or not.

  • Check if the timer is paused
bool Timer::is_paused()
{
    return paused;
}

Pure and simple, a method that returns if the timer is paused or not.

  • Load and optimize images
SDL_Surface *load_image( string filename )
{
    //The image that's loaded
    SDL_Surface* loadedImage = NULL;
 
    //The optimized surface that will be used
    SDL_Surface* optimizedImage = NULL;
 
    //Load the image
    loadedImage = IMG_Load( filename.c_str() );
 
    //If the image loaded
    if( loadedImage != NULL )
    {
        //Create an optimized surface
        optimizedImage = SDL_DisplayFormat( loadedImage );
 
        //Free the old surface
        SDL_FreeSurface( loadedImage );
 
        //If the surface was optimized
        if( optimizedImage != NULL )
        {
            //Color key surface
            SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) );
        }
    }
 
    //Return the optimized surface
    return optimizedImage;
}
1 Parameters

This function receives a string which is the name of the image that it will load and optimize.

2 Variables

Two SDL surface pointers are set to NULL. One is for the original image, the other for the optimized.

3 Body

The image is loaded and errors are checked for. If the image was fine, then it is operated on by SDL. This method is setting every picture to be 32 bits per pixel. Then the optimized image is returned, but not before the old one is deleted from memory.

  • Apply to the surface
void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{
    //Holds offsets
    SDL_Rect offset;
 
    //Get offsets
    offset.x = x;
    offset.y = y;
 
    //Blit
    SDL_BlitSurface( source, clip, destination, &offset );
}
1 Parameters

The first two are the coordinates for the image to be located on the screen. The next is the image and the fourth is where it will be going (to the screen). The last one I never use.

2 Variables

There is an SDL rectangle which will be used to set the x and y offsets (coordinates of the image) of that rect.

3 Body

Then it blits the image to the surface.

  • Time to initialize the SDL stuff
bool init()
{
    //Initialize all SDL subsystems
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
        return false;
 
    //Set up the screen
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
 
    //If there was an error in setting up the screen
    if( screen == NULL )
        return false;
 
	//Initialize SDL_ttf
	if ( TTF_Init() == -1 )
		return false;
 
	//Check if there's any joysticks
	if ( SDL_NumJoysticks() > 0 )
	{
		//open the joystick
		stickL = SDL_JoystickOpen( 0 );
 
		//if there's a problem opening joystick
		if ( stickL == NULL )
			return false;
 
		if ( SDL_NumJoysticks() == 2 )
		{
			stickR = SDL_JoystickOpen( 1 );
			//if there's a problem opening joystick
			if ( stickR == NULL )
				return false;
		}
	}
 
    //Set the window caption
    SDL_WM_SetCaption( "Pong", NULL );
 
    //If everything initialized fine
    return true;
}
1 Body

The first block will initialize all of the basic things required for SDL to work. Then we set up the screen, passing it the dimensions and resolution. Then we check to make sure the screen loaded ok. Then we have to initialize the font. Then comes the extra stuff. This pong is compatible with a joystick (known working controllers: Super nintendo and Xbox 360). I check to see if there are actually any plugged in (because if there aren't then why initialize it) and if so, how many. It can only initialize and open two of them. Then checks are made to make sure that each stick was ok and we set the caption for the title of the window. If everything initialized ok, then we return true.

  • Load the font
bool load_font()
{
	//Open the font
	font = TTF_OpenFont( "lazy.ttf", 20 );
 
	//If error loading font
	if ( font == NULL )
		return false;
}

In order to use a font you have to open it. So we set our pointer (font) to open lazy.ttf with a point of 20 (size of 20). A quick check to make sure it opened ok and on we go.

  • Load the files
bool load_files( PongObj *thing, string filename )
{
    //Load the images
    thing -> surface = load_image( filename );
 
    //If there was a problem in loading the paddles and ball
    if( thing -> surface  == NULL )
        return false;
 
    //If everything loaded fine
    return true;
}
1 Parameters

A PongObj and a string is passed to this function. The PongObj is a pointer (thing) to the object that called it.

2 Body

We begin by loading the image by pointing to the surface. A quick to check to make sure it loaded ok and on we go.

  • Time to clean up our mess
void clean_up( PongObj *thing )
{
    //Free the surface
    SDL_FreeSurface( thing -> surface );
}
1 Parameters

This function is sent the address of the object that called it.

2 Body

With every surface, we must free it. So the object that called this function will free itself.

  • Close up one joystick
void closeJoyStickL()
{
	//Close player 1 joystick
	SDL_JoystickClose( stickL );
}

This function will only be called if there are joysticks present. But this will close the first player's joystick.

  • Close up the other joystick
void closeJoyStickR()
{
	//Close player 2 joystick
	SDL_JoystickClose( stickR );
}

This function will only be called if there are two joysticks present. But this will close the second player's joystick.

  • Default constructor for the PongObj class
PongObj::PongObj(int x, int y, int w, int h, int vx, int vy)
{
	//Initialize player score count
	p1 = 0;
	p2 = 0;
 
    //Initialize the offsets
    box.x = x;
    box.y = y;
 
	//set dimensions
	box.w = w;
	box.h = h;
 
    //Initialize the velocity
    xVel = vx;
    yVel = vy;
}
1 Parameters

This function is sent the x and y coordinates, the width and height, and the x and y velocities of each object. Since I call this class three times each object has its own. And like all constructors, this one just initializes the variables.

  • Handling input
void handle_input(PongObj *thing, PongObj *thing2)
{
	if ( SDL_NumJoysticks() == 0 )
	{
		//If a key was pressed
		if( event.type == SDL_KEYDOWN )
		{
			//Adjust the velocity
			switch( event.key.keysym.sym )
			{
				case SDLK_UP: thing->yVel -= PADDLE_HEIGHT / 5;
						break;
				case SDLK_DOWN: thing->yVel += PADDLE_HEIGHT / 5;
						break;
				case SDLK_w: thing2->yVel -= PADDLE_HEIGHT / 5;
						break;
				case SDLK_s: thing2->yVel += PADDLE_HEIGHT / 5;
						break;
		 	}	
		}
	 	//If a key was released
	 	else if( event.type == SDL_KEYUP )
	 	{
			//Adjust the velocity
			switch( event.key.keysym.sym )
			{
				case SDLK_UP: thing->yVel += PADDLE_HEIGHT / 5;
						break;
				case SDLK_DOWN: thing->yVel -= PADDLE_HEIGHT / 5;
						break;
				case SDLK_w: thing2->yVel += PADDLE_HEIGHT / 5;
						break;
				case SDLK_s: thing2->yVel -= PADDLE_HEIGHT / 5;
						break;
			}
		}
	}
	else if ( SDL_NumJoysticks() == 1 ) //player 1 gets controller
	{
		//joystick controls -- x axis: 0 y axis: 1 -- axis range: -32768 to 32767
		//If an axis was changed
		if ( event.type == SDL_JOYAXISMOTION )
		{
			//If joystick 0 has moved
			if ( event.jaxis.which == 0 )
			{
				//If the y axis changed
				if ( event.jaxis.axis == 1 )
				{
					//If the X axis is neutral
					if ( ( event.jaxis.value > -8000 ) && ( event.jaxis.value < 8000 ) )
						thing2->yVel = 0;
					//If not
					else
					{
						//Adjust the velocity
						if ( event.jaxis.value < 0 )
							thing2->yVel = -PADDLE_HEIGHT / 5;
						else
							thing2->yVel = PADDLE_HEIGHT / 5;
					}
				}
			}
		}
 
		switch ( event.type )
		{
			//joyhat events -- player 1
			case SDL_JOYHATMOTION:
				if ( event.jhat.value & SDL_HAT_UP )
					thing2->yVel = -PADDLE_HEIGHT / 5;
				if ( event.jhat.value & SDL_HAT_DOWN )
					thing2->yVel = PADDLE_HEIGHT / 5;
				break;
		}
 
		//If a key was pressed
		if( event.type == SDL_KEYDOWN )
		{
			//Adjust the velocity
			switch( event.key.keysym.sym )
			{
				case SDLK_UP: thing->yVel -= PADDLE_HEIGHT / 5;
						break;
				case SDLK_DOWN: thing->yVel += PADDLE_HEIGHT / 5;
						break;
		 	}	
		}
	 	//If a key was released
	 	else if( event.type == SDL_KEYUP )
	 	{
			//Adjust the velocity
			switch( event.key.keysym.sym )
			{
				case SDLK_UP: thing->yVel += PADDLE_HEIGHT / 5;
						break;
				case SDLK_DOWN: thing->yVel -= PADDLE_HEIGHT / 5;
						break;
			}
		}
	}	
	else	//at least two controllers were detected
	{	
		//player 1 hat events
		//joyhat events
		switch ( event.type )
		{
			case SDL_JOYHATMOTION:
			if ( event.jhat.value & SDL_HAT_UP & event.jhat.which == 0 )
				thing2->yVel = -PADDLE_HEIGHT / 5;
			if ( event.jhat.value & SDL_HAT_DOWN )
				thing2->yVel = PADDLE_HEIGHT / 5;
 
		//player 2 hat events
		//joyhat events
			if ( event.jhat.value & SDL_HAT_UP  & event.jhat.which == 1 )
				thing->yVel = -PADDLE_HEIGHT / 5;
			if ( event.jhat.value & SDL_HAT_DOWN )
				thing->yVel = PADDLE_HEIGHT / 6;
			break;
		}
		//player 1
		//joystick controls -- x axis: 0 y axis: 1 -- axis range: -32768 to 32767
		//If an axis was changed
		if ( event.type == SDL_JOYAXISMOTION )
		{
			//If joystick 0 has moved
			if ( event.jaxis.which == 0 )
			{
				//If the y axis changed
				if ( event.jaxis.axis == 1 )
				{
					//If the X axis is neutral
					if ( ( event.jaxis.value > -8000 ) && ( event.jaxis.value < 8000 ) )
						thing2->yVel = 0;
					//If not
					else
					{
						//Adjust the velocity
						if ( event.jaxis.value < 0 )
							thing2->yVel = -PADDLE_HEIGHT / 5;
						else
							thing2->yVel = PADDLE_HEIGHT / 5;
					}
				}
			}
		}
		//player 2
		//joystick controls -- x axis: 0 y axis: 1 -- axis range: -32768 to 32767
		//If an axis was changed
		if ( event.type == SDL_JOYAXISMOTION )
		{
			//If joystick 0 has moved
			if ( event.jaxis.which == 1 )
			{
				//If the y axis changed
				if ( event.jaxis.axis == 1 )
				{
					//If the X axis is neutral
					if ( ( event.jaxis.value > -8000 ) && ( event.jaxis.value < 8000 ) )
						thing->yVel = 0;
					//If not
					else
					{
						//Adjust the velocity
						if ( event.jaxis.value < 0 )
							thing->yVel = -PADDLE_HEIGHT / 5;
						else
							thing->yVel = PADDLE_HEIGHT / 5;
					}
				}
			}
		}
 
	}
}
1 Parameters

This function receives the address of the paddleL and paddleR objects (created in the main function).

2 Body

This is where the magic happens. This function will react to keyboard/joystick input. Player one keyboard controls are 'w' and 's' and player two controls are up and down arrow keys. First I test to see if there are any joysticks. If not then use only the keyboard. Check the event (key sym) and move up or down at a fifth of the paddle height (smoother motion than half). If there is a joystick though, it is assumed to be player one's and the 'w' and 's' keys are locked. If there are two joysticks then the keyboard is locked entirely and only the joysticks are allowed. To explain the joysticks: a jaxis is a joystick axis like a dpad, whereas, the joystick hat events are things like an analog stick.

  • Move the object
//obj represents what called it (ball: 1 paddle: 0)
void PongObj::move(int obj, PongObj paddleL, PongObj paddleR, PongObj ball)
{
	//up/down
	box.y += yVel;
 
	//left/right
	box.x += xVel;
 
	if ( obj == 0 )
	{
		//too far up or down
		if( ( box.y < 0 ) || ( box.y + PADDLE_HEIGHT > SCREEN_HEIGHT ) )
			box.y -= yVel;
	}
	else
	{
		//too far up or down
		if( ( box.y < 0 ) || ( box.y + BALL_HEIGHT > SCREEN_HEIGHT ) || ( check_collision(box, paddleR.box) ) || ( check_collision(box, paddleL.box) ) )
		{	
			box.y -= yVel;
			yVel = ~yVel;
		}
 
		//too far left or right
		if( ( check_collision(box, paddleR.box) ) || ( check_collision(box, paddleL.box) ) )
		{
			box.x -= xVel;
			xVel = ~xVel;
			yVel = ~yVel;
		}
	}
}
1 Parameters

Probably not the best way to do things, but it was the only way I could come up with something that worked. This function is passed an integer data type called obj. This variable lets the function know what called it. If it is 0 then a paddle called it and if 1 the ball called it.

2 Variables

It starts by moving the x and y coordinates by the x velocity and y velocity.

3 Body

The first check is for the bounds on a paddle. It is checked to make sure that it cannot go higher or lower than the height dimension of the screen. If it did, then it is set back to where it last was before it went out. The next block is for the ball. I check to make sure it did not go too high or too low like the paddle, but I also check for collisions against a paddle. If there was one, then the x and/or the y velocity is inverted so it will move the opposite way. As you may notice there is an inverting of the y velocity in the left and right check. This is to make for realistic bounces rather than have it follow the path it just came from.

  • Show an object
void PongObj::show()
{
    //Show the object
    apply_surface( box.x, box.y, this -> surface, screen );
}

This function will apply the surface to the screen using the objects box's x and y coordinates and surface pointer.

  • Reset the ball
void PongObj::resetBall()
{
	int tmp;
 
	//place ball in middle of screen
	box.x = SCREEN_WIDTH / 2;
	box.y = SCREEN_HEIGHT / 2;
 
	//randomly select direction of ball to travel
	tmp = rand() % 4;
	if ( tmp == 0 )	//right/down
	{
		xVel = 5;
		yVel = 5;
	}
	if ( tmp == 1 ) //right/up
	{
		xVel = 5;
		yVel = -5;
	}
	if ( tmp == 2 ) //left/down
	{
		xVel = -5;
		yVel = 5;
	}
	if ( tmp == 3 ) //left/up
	{
		xVel = -5;
		yVel = -5;
	}
}

When someone scores this function is called. This places the ball in the middle of the screen and will randomly select the direction it goes. Diagonals are chosen so that there is movement on both planes rather than just one. The speed can be changed by setting the 5's to something smaller or larger (slower or faster). Obviously they should be in a variable, but I didn't feel like it.

  • Scoring
void PongObj::score()
{
	//player 1 and player 2 scores
	if ( box.x + BALL_WIDTH >= SCREEN_WIDTH )
	{
		p1++;
		resetBall();
	}
	else if ( box.x + BALL_WIDTH <= 0 )
	{
		p2++;
		resetBall();
	}
}

This function keeps track of each players score. It is only called from main and only increments the score if the ball goes passed a paddle. The variables p1 and p2 are the ones that will be incremented. After the incrementation, resetting the ball is next in line.

  • Get player one's and two's score
char *PongObj::getP1()
{
	sprintf(s,"%d", p1);
	return s;
}
 
char *PongObj::getP2()
{
	sprintf(s,"%d", p2);
	return s;
}

Remember the global malloc of the char *s in the beginning. This is how SDL can get the player's number into a string. Using sprintf, I use the variable s to store the number p1/p2 into it and return s.

  • main
int main( int argc, char* args[] )
{
    //Quit flag
    bool quit = false;
 
    //The frame rate regulator
    Timer fps;
 
    //The paddles that will be used
	//params (int x	, int y, int w, int h, int vx, int vy) - v = velocity
    PongObj paddleR(SCREEN_WIDTH - PADDLE_WIDTH, 0, PADDLE_WIDTH, PADDLE_HEIGHT, 0, 0);
    PongObj paddleL(0, 0, PADDLE_WIDTH, PADDLE_HEIGHT, 0, 0);
	PongObj ball(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, BALL_WIDTH, BALL_HEIGHT, 5, 5);
 
    //Initialize
    if( init() == false )
    {
        return 1;
    }
 
	//load font
	load_font();
 
    //Load the files
    if( load_files( &paddleR, "paddle.bmp" ) == false )
    {
        return 1;
    }
    //Load the files
    if( load_files( &paddleL, "paddle.bmp" ) == false )
    {
        return 1;
    }
    //Load the files
    if( load_files( &ball, "ball.bmp" ) == false )
    {
        return 1;
    }
 
    //While the user hasn't quit
    while( quit == false )
    {
        //Start the frame timer
        fps.start();
 
        //While there's events to handle
        while( SDL_PollEvent( &event ) )
        {
            //Handle events for the paddles
     		handle_input( &paddleR, &paddleL);
 
            //If the user has Xed out the window
            if( event.type == SDL_QUIT )
            {
                //Quit the program
                quit = true;
            }
        }
 
		//check score
		ball.score();
 
        //Move the paddle(s)
        paddleR.move(0, paddleL, paddleR, ball);
		paddleL.move(0, paddleL, paddleR, ball);
		ball.move(1, paddleL, paddleR, ball);
 
 
        //Fill the screen black
        SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0x00, 0x00, 0x00 ) );
 
        //Show the paddle on the screen
        paddleR.show();
		paddleL.show();
		ball.show();
 
		//Render text
		message = TTF_RenderText_Solid ( font, ball.getP1(), textColor );
		//if error in rendering text
		if ( message == NULL )
			return 1;
 
		//Apply image to screen
		apply_surface(200, 20, message, screen);
 
		//Render text
		message = TTF_RenderText_Solid ( font, ball.getP2(), textColor );
		//if error in rendering text
		if ( message == NULL )
			return 1;
 
		//Apply image to screen
		apply_surface(400, 20, message, screen);
 
        //Update the screen
        if( SDL_Flip( screen ) == -1 )
        {
            return 1;
        }
 
        //Cap the frame rate
        if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
        }
    }
 
    //Clean up
    clean_up(&paddleL);
	clean_up(&paddleR);
	clean_up(&ball);
	if ( SDL_NumJoysticks() >= 1 )
		closeJoyStickL();
	if ( SDL_NumJoysticks() == 2 )
		closeJoyStickR();
 
	//close font
	TTF_CloseFont( font );
	//Quit SDL_ttf
	TTF_Quit();
 
    //Quit SDL
    SDL_Quit();
 
	delete s;
 
    return 0;
}

Main begins with setting the bool variable to false. This variable is used to make an endless loop (this loop is used to monitor input and update the screen). Then we create an Timer object called fps and three PongObjects. Note how the ball object has two 5s for the final parameters, this will set the ball in motion automatically. Then we call the initializing and loading functions. Then its time for the endless loop. This loop can only be ended when the player clicks the 'x' button on the screen. The rest of it is pretty self explanatory and with the inline comments made even easier to follow.

1-Wire/OWFS/HydroMon

Author: Jesse Short

Objective:


This project was to install OWFS (One Wire File System) and get it to work with our 1-wire devices. Then we were supposed to come up with a way to monitor that device. So far we've dealt with the temperature sensor.

Prerequisites:


The following steps came from here: http://www.technotes.se/?p=26

Installing the needed software for OWFS:

  • sudo apt-get install libusb-dev
  • sudo apt-get install libfuse-dev
  • sudo apt-get install checkinstall

Next you have to edit /etc/fuse.conf:
# Allow non-root users to specify the 'allow_other' or 'allow_root'
# mount options.
#
user_allow_other

Time to install:

Time to set up the sensor (temperature)

  • sudo mkdir /var/1-wire
  • sudo owfs -u –allow_other /var/1-wire *Does not work in case you were following the link*
  • sudo owfs /dev/ttyUSB0 /var/1-wire *Thanks to our teacher (Matthew Haas). Also note, tab complete on the USB (the number changes)*

So now you have what you need to get at the sensor. cd into /var/1-wire and look around. The first thing you should do is set the temperature settings to Fahrenheit probably. It defaults to Celsius. To change it use this command: cd /var/1-wire/settings/units/temperature_scale and then: echo -n “F” > temperature_scale. To access the temperature use this: cd into your 1-wire device /var/1-wire/uniqueDeviceIDNum and cat temperature.

Background:


1-wire is a device communications bus system. Its name is intuitive in that it really does almost require one wire. In our case we have to use two because of the sensor's design. Using a USB to ethernet cable converter we plug in the sensor. With each sensor there is a unique id for it. That is how you distinguish between them in case you string more together.

Procedure:


Now it's time to make some user friendly scripts to monitor this temperature.
I made two different ways to get information about the temperature. The best way is to use the website. It has graphs and pictures and anyone can use it. The other, is if you don't have an internet connection, you can use the bash scripts with a dialog front-end which helps for a user friendly feel. I will start discussion with the bash scripts first.

To start with:
ylwSensoorId: 26.54538C000000 every sensor will have its own unique number
Temperature Location: /var/1-wire/$ylwSensorId/temperature
Temperature Unit Location: /var/1-wire/settings/units/temperature_scale Default unit is in C but can be converted to F with:
echo -n “F” > /var/1-wire/settings/units/temperature_scale

Now that you know where the temperature file is, the name of the sensor, and how to change from Celsius to Fahrenheit, you need to begin data collection. I decided to create headers and footers for every day. Using the date command and formatting it to show dayName mon dayNum (Tue Apr 12 or Sat Apr 09) and prefixing this with “Beginning of the day: date AND End of the day: date”. I had to save the date into a file which I called .curDay which will hold the most current day but is used to initially check for a new day before it is updated (overwritten). So, once the header/footer if needed is written, it grabs the temperature as is from the temperature file shown above. This number can be anywhere from a whole number to having four decimal places. All of this is stored in the file: temperatures.txt. This file is then tossed into cron to run every 30 minutes (that's 48 temperatures per day). The script is smart enough to know when the month is over. When this happens it will prefix the file name (temperatures.txt) with a ”.Mon“ where the '.' will make it hidden and “Mon” stands for abbreviated month (.Apr.temperatures.txt) and it will create a new temperatures.txt. Here is the code.

Cron Insert:

  • Command: crontab -e
  • */30 * * * * “path to file”
  • */30 * * * * /home/john/owfs/hydromonTempCron.sh
  • Save and exit (also note, you must do this while as the super user because of permissions)
#!/bin/bash
 
	cd /home/john/owfs #so cron knows its at the right spot
 
	ylwSensorId=26.54538C000000
	echo -n "F" > /var/1-wire/settings/units/temperature_scale #just in case its C
	temp=`cat /var/1-wire/$ylwSensorId/temperature | sed -e 's/^[ \t]*//g' | cut -f1-6` #temperature from the sensor
 
	if [[ ! -e temperatures.txt || ! -s temperatures.txt ]];	#if temperatures.txt does not exist create it and add header
	then
		# %a = Abbreviated weekday
		# %b = Abbreviated month
		# %d = Day of month (ex: 01, 21)
		echo "Beginning of the day:" `date +"%a %b %d"` > temperatures.txt 
	fi
 
	echo "$temp" >> temperatures.txt
 
	if [[ -e .curDay && -s .curDay ]];	#-s = file is NOT zero size
	then
		day="`head -1 .curDay | cut -d' ' -f3`"
		curDay=`date +"%d"`
		endDay=`cat .curDay`
		if [ $curDay -gt $day ]; #another day in the month
		then
			echo "End of the day: $endDay" >> temperatures.txt
			date +"%a %b %d" > .curDay
			echo "Beginning of the day: "`date +"%a %b %d"` >> temperatures.txt
		elif [ $curDay -lt $day ];
		then  #its a new month
			echo "End of the day: "`date +"%a %b %d"` >> temperatures.txt
			month=`head -1 .curDay | cut -d" " -f2`
			date +"%a %b %d" > .curDay
			mv temperatures.txt ".$month.Temperatures.txt" 
			`gzip -f ".$month.Temperatures.txt"`	#-f = force it to make it (When a new year rolls around it can overwrite the previous file
			echo "Beginning of the day: " `date +"%a %b %d"` > temperatures.txt
		fi
	else
		date +"%a %b %d" > .curDay
	fi
 
exit 0

Example temperatures.txt file.

Beginning of the day: Fri Apr 01
70.475
70.1937
70.5875
70.7
70.475
70.6437
70.1937
69.9688
70.3063
71.0375
70.8688
70.4188
74.2438
74.4688
74.75
74.6937
74.4125
74.8063
74.8625
74.5812
74.6937
75.0875
74.9188
74.975
74.975
75.0312
75.8187
75.425
75.5375
74.8063
73.9062
74.975
74.6937
75.2
74.6375
74.525
74.975
74.8063
74.75
74.6937
74.3
74.3
74.525
75.1437
71.3187
71.5438
71.0938
70.7562
End of the day: Fri Apr 01
Beginning of the day: Sat Apr 02
70.6437
70.9813
71.2625
70.925
71.2625
70.7

Now that we have data collection and a sort of a round robin database, we can write the script to use this data. I used dialog to give it a gui front-end. There is a main menu which will allow for just viewing the temperature as well as creating two different reports. There is a monthly report which will take every temperature sampled thus far and compute the low, high, mode, and average and display them. The other report will take the temperatures from each day and compute their low, high, mode, and average and then display them. Now the reason is clear why I used headers and footers, to make traversal through the file easier. Just grep for them and you know where you are. Also note, since bash cannot naturally deal with decimal places I cut them off with the cut command and used the '.' period as the delimiter. The inline comments should suffice for all the code I give.

#!/bin/bash
##
# This script will monitor temperature and light of the hydroponics lab
##
 
function cancel()
{
	dialog --title "Cancelled" --backtitle "$backtitle"\
	--msgbox "$cancel" 10 30
return
}
 
function esc()
{
	dialog --title "Escaped" --backtitle "$backtitle"\
	--msgbox "$esc" 10 30
return
}
 
function mainMenu()
{
	option=0
	while [ $option -ne 4 ]
	do
		dialog --title "Main Menu" --backtitle "$backtitle"\
		--menu "Options" 30 50 4\
		"1" "Display Temperature"\
		"2" "Overall Temperature Report"\
		"3" "Detailed Temperature Report"\
		"4" "Exit" 2>$tempfile
		case $? in
			0)	choice=`cat $tempfile`
			;;
 
			1)	cancel
				return
			;;
 
			255)	esc
				return
			;;
		esac
 
		case $choice in
			1)	getTemp
			;;
 
			2)	overallTempReport
			;;
 
			3)	detailTempReport
			;;
 
			4)	option=4
			;;
		esac
	done
return
}
 
function getTemp()
{
	temp=`cat /var/1-wire/$ylwSensorId/temperature`
	dialog --title "Temperature: F" --backtitle "$backtitle"\
	--msgbox "$temp degrees" 10 30
return
}
 
function overallTempReport()
{
	if [ -e temperatures.txt ];
	then
		cat temperatures.txt | grep '^[0-9]' | cut -d"." -f1 > hydromonTemps
 
		sum=0
		count=0
		#calculate avg
		while read line
		do
			sum=$(($sum + $line))
			count=$(($count + 1 ))
		done < hydromonTemps
		#avg=`echo "$sum/$count" | bc -lq`
		avg=$(($sum / $count))
 
		#calculate mode
		#sort temps - faster than bubble sort from load...Graphs.sh in /var/www/hydroMonGraphs
		cat hydromonTemps | sort > hydromonSortedTemps
 
		#find unique numbers only
		cat hydromonSortedTemps | uniq > hydromonUniqTemps
 
		#load unique numbers into array
		uniqNumIndex=0
		while read line
		do
			uniqueNumArr[$uniqNumIndex]=$line
			modeArr[$uniqNumIndex]=0
			uniqNumIndex=$(($uniqNumIndex + 1))
		done < hydromonUniqTemps
 
		#count every occurence of each number
		maxModeNums=`cat hydromonUniqTemps | wc -l`
		while read line
		do
			for (( i=0; i<$maxModeNums; i++ ))
			do
				if [[ $line == ${uniqueNumArr[$i]} ]];
				then
					modeArr[$i]=$((${modeArr[$i]} + 1))
				fi
			done
		done < hydromonSortedTemps
 
		#figure the highest mode
		mode=${uniqueNumArr[0]} #default value
		highestMode=${modeArr[0]}
		for (( x=0; x<$maxModeNums; x++ ))
		do
			if [ $highestMode -lt ${modeArr[$x]} ];
			then
				highestMode=${modeArr[$x]}
				mode=${uniqueNumArr[$x]}
			fi
		done
 
		#figure the low/high
		low=`head -1 hydromonSortedTemps`
		high=`tail -1 hydromonSortedTemps`
 
		dialog --title "Temperature Report" --backtitle "$backtitle"\
		--msgbox "Lowest Temperature: $low\nHighest Temperature: $high\nAverage Temperature: $avg\nCommon Temperature: $mode" 10 30
		#Good use of printf
		#--msgbox "Lowest Temperature: $low\nHighest Temperature: $high\nAverage Temperature: `printf \"%.4f\" $avg`" 10 30
	else
		dialog --title "Error" --backtitle "$backtitle"\
		--msgbox "Not enough information to create a report yet" 10 30
	fi
 
	rm hydromonTemps hydromonSortedTemps hydromonUniqTemps 2>/dev/null
return
}
 
function detailTempReport()
{	
if [ -e temperatures.txt ];
then
	while read line
	do
		if echo "$line" | grep -i -q "beg" #the line is a header -i=any case -q=quiet
		then
			echo -e "$line\n" >> detailedTempReport.txt
			tempsIndex=0
		elif echo "$line" | grep -i -q "end"  #the line is a footer
		then #add the low/high/avg/mode variables to the file
			#sort temps[]: bubble style
			cnt=0
			performanceBoost=1      #this is used to keep the bubble sort from over checking
			while [ $cnt -ne $tempsIndex ]
			do
				for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
				do
					if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
						swap1=${temps[$i]}
						swap2=${temps[$(($i+1))]}
						temps[$i]=$swap2
						temps[$(($i+1))]=$swap1
					fi
				done
				performanceBoost=$(($performanceBoost + 1))
				cnt=$(($cnt + 1))
			done
 
			#calculate avg
			sum=0
			count=0
			for (( i=0; i<$tempsIndex; i++ ))
			do
				sum=$(($sum + ${temps[$i]} ))
				count=$(($count + 1))
			done
			#avg=`echo "$total/$count" | bc -lq`
			avg=$(($sum / $count))
 
			#calculate mode
			#find unique numbers - easier to use uniq on file
			#start with outputting temps[] to file
			echo -n "" > hydromonTemps
			for (( x=0; x<$tempsIndex; x++ ))
			do
				echo "${temps[$x]}" >> hydromonTemps
			done
 
			#find unique numbers only
			cat hydromonTemps | uniq > hydromonUniqTemps
 
			#load unique numbers into array
			y=0
			while read line
			do
				uniqueNumArr[$y]=$line #store uniqe numbers
				modeArr[$y]=0 #store count of each temp occurrence
				y=$(($y + 1)) #increment index
			done < hydromonUniqTemps
 
			#count every occurrence of each number
			maxModeNums=`cat hydromonUniqTemps | wc -l`
			while read line
			do
				for (( i=0; i<$maxModeNums; i++ ))
				do
					if [[ $line == ${uniqueNumArr[$i]} ]];
					then
						modeArr[$i]=$((${modeArr[$i]} + 1))
					fi
				done
			done < hydromonTemps
 
			#figure the highest mode
			mode=${uniqueNumArr[0]} #default value
			highestMode=${modeArr[0]}
			for (( x=0; x<$maxModeNums; x++ ))
			do
				if [ $highestMode -lt ${modeArr[$x]} ];
				then
					highest=${modeArr[$x]}
					mode=${uniqueNumArr[$x]}
				fi
			done
 
			#figure low/high
			low="${temps[0]}"
			high="${temps[$(($tempsIndex - 1))]}"
 
			echo -e "Low: $low\n" >> detailedTempReport.txt
			echo -e "High: $high\n" >> detailedTempReport.txt
			# good use of printf exampleecho -e "Average: `printf \"%.4f\" $avg`\n" >> detailedTempReport.txt
			echo -e "Average: $avg\n" >> detailedTempReport.txt
			echo -e "Mode: $mode\n" >> detailedTempReport.txt
			echo -e "$line\n" >> detailedTempReport.txt	
		else #the line is a temperature
			#strip off decimal places
			temps[$tempsIndex]=`echo "$line" | cut -d"." -f1`
			tempsIndex=$(($tempsIndex + 1))
		fi
	done < temperatures.txt
 
	#last day wont get included in above so lets take care of it now
	#sort temps[]: bubble style
	cnt=0
	performanceBoost=1      #this is used to keep the bubble sort from over checking
	while [ $cnt -ne $tempsIndex ]
	do
		for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
		do
			if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
				swap1=${temps[$i]}
				swap2=${temps[$(($i+1))]}
				temps[$i]=$swap2
				temps[$(($i+1))]=$swap1
			fi
		done
		performanceBoost=$(($performanceBoost + 1))
		cnt=$(($cnt + 1))
	done
 
	#calculate avg
	sum=0
	count=0
	for (( i=0; i<$tempsIndex; i++ ))
	do
		sum=$(($sum + ${temps[$i]} ))
		count=$(($count + 1))
	done
	#avg=`echo "$total/$count" | bc -lq`
	avg=$(($sum / $count))
 
	#calculate mode
	#find unique numbers - easier to use uniq on file
	echo -n "" > hydromonTemps
	for (( x=0; x<$tempsIndex; x++ ))
	do
		echo "${temps[$x]}" >> hydromonTemps
	done
 
	#find unique numbers only
	cat hydromonTemps | uniq > hydromonUniqTemps
 
	#load unique numbers into array
	y=0
	while read line
	do
		uniqueNumArr[$y]=$line #store uniqe numbers
		modeArr[$y]=0 #store count of each temp occurrence
		y=$(($y + 1)) #increment index
	done < hydromonUniqTemps
 
	#count every occurrence of each number
	maxModeNums=`cat hydromonUniqTemps | wc -l`
	while read line
	do
		for (( i=0; i<$maxModeNums; i++ ))
		do
			if [[ $line == ${uniqueNumArr[$i]} ]];
			then
				modeArr[$i]=$((${modeArr[$i]} + 1))
			fi
		done
	done < hydromonTemps
 
	#figure the highest mode
	mode=${uniqueNumArr[0]} #default value
	highestMode=${modeArr[0]}
	for (( x=0; x<$maxModeNums; x++ ))
	do
		if [ $highestMode -lt ${modeArr[$x]} ];
		then
			highest=${modeArr[$x]}
			mode=${uniqueNumArr[$x]}
		fi
	done
 
	#figure low/high
	low="${temps[0]}"
	high="${temps[$(($tempsIndex - 1))]}"
 
	echo -e "Low: $low\n" >> detailedTempReport.txt
	echo -e "High: $high\n" >> detailedTempReport.txt
	# good use of printf exampleecho -e "Average: `printf \"%.4f\" $avg`\n" >> detailedTempReport.txt
	echo -e "Average: $avg\n" >> detailedTempReport.txt
	echo -e "Mode: $mode\n" >> detailedTempReport.txt
	echo -e "$line\n" >> detailedTempReport.txt	
 
	dialog --title "Daily Temperature Report" --backtitle "$backtitle"\
	--textbox ./detailedTempReport.txt 30 80
 
	rm hydromonTemps hydromonUniqTemps detailedTempReport.txt 2>/dev/null
fi
return
}
 
function refresh()
{
	go="false"
	while [ $go == "false" ];
	do
		`ls -1 /dev/ttyUSB* 2> tmp` 2> /dev/null
		checkSensor=`cat tmp`
		rm tmp 2>/dev/null
		if echo "$checkSensor" | grep -i -q "cannot access"
		then
			dialog --title "Error: Sensor Verification" --backtitle "$backtitle"\
			--msgbox "Please plug in the yellow temperature sensor!" 10 30
		else
			go="true"
			sensor=`ls -1 /dev/ttyUSB*`
 
			`umount /var/1-wire/`
			`owfs "$sensor" /var/1-wire/`
 
			#commented out --annoying
			#dialog --title "Temperature Settings" --backtitle "$backtitle"\
			#--msgbox "Changed Temperature Settings To Fahrenheit" 10 30
 
			echo -n "F" > /var/1-wire/settings/units/temperature_scale
			temp=`cat /var/1-wire/$ylwSensorId/temperature`
		fi
	done
return
}
 
#Variables to hold sensor information
ylwSensorId=26.54538C000000
 
#Generic variables to hold header strings
tempfile=`tempfile`
backtitle="Hydromon"
cancel="You chose to cancel - backing out"
esc="You pressed the esc key - backing out"
 
if [[ ! -e hydromonTempCron.sh || ! -s hydromonTempCron.sh ]];
then	#hydromonTempCron.sh does not exist or it IS zero in size therefore this script is pretty much useless
	dialog --title "Error: File Not Found" --backtitle "$backtitle"\
	--msgbox "Could NOT locate hydromonTempCron.sh in the same directory!\n\nExiting!" 10 65
	echo -e "\n"
	exit 0
fi
 
refresh
mainMenu
 
echo -e "\n"
exit 0

Now I'll get into the web-based scripts. You will need a web server installed, such as Apache. That is what I'm using. You will also have to have the necessary php and jpgraph libraries installed. The jpgraph download can be found here: http://jpgraph.net/download/ First I will talk about the home page. It is index.php and I will show that code next. The home page has a link to the camera we set up to watch the hydroponics lab. Since it is a link, it will update every time you refresh the page. There are four links. Three of them are dynamically created using php. I did this in case there was an error with the scripts that create the graphs. The last link will take you to the camera so you can watch real-time. Back to the three links real quick to explain those. Like the reports created above, there is one link for just the current information. It shows a line graph with the temperatures as the plots. Underneath it is the low, high, and mode. The next link will take you to a page with a link for every day recorded so far including the current day. Like before, each link will have a jpgraph line graph, low, high, and mode. The last link is similar to the overall report for the bash with dialog front-end script. It takes a certain amount of temperatures every so often, creates a line jpgraph as well as again, calculates the low, high, and mode for the number of samples taken. Right now, I have mine set at 6. However, this one is smart enough to not create a graph if the three calculations are the same numbers. I'll explain the code as it is displayed. The first will be index.php followed by the css. Then, I'll get into the bash scripts that creates everything.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 
<?php
	//Check to see if php graphs exist, if they do create links to them
	$path = "hydroMonGraphs/";
	$filename = "$path" . "hydroMonCurrentTempGraph.html";
	if ( file_exists($filename))
	{
		$currentGraph = "<a href=\"$filename\">Current Temps</a>";
	}
 
	$filename = "$path" . "hydroMonDayIndex.html";
	if ( file_exists($filename))
	{
		$dayGraph = "<a href=\"$filename\">Daily Temps</a>";
	}
 
	$filename = "$path" . "hydroMonMonthIndex.html";
	if ( file_exists($filename))
	{
		$monthGraph = "<a href=\"$filename\">Monthly Temps</a>";
	}
?>
 
<html>
<head>
	<title>
		CCC - lair - Hydroponics
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
	<div id="header">
		<h1>LAIR - Hydroponics</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><?=$currentGraph ?></li>
			<li><?=$dayGraph ?></li>
			<li><?=$monthGraph ?></li>
			<li><a href="http://laircam/mjpg/1/video.mjpg">Live Video</a></li>
		</ul>
	</div>
 
	<img id="homeImg" src="http://laircam/jpg/1/image.jpg" alt="lair cam - image" width="400" height="300">
	<div id="content">
		<h2 id="welcome">Welcome</h2>
		<p>
			&nbsp;&nbsp;This website is dedicated to the monitoring of the hydroponics lab in the lair. There are graphs created via jpgraph used to monitor temperature, light, and ph. There is also live video feed used to monitor the plants. Finally, the image to the right will update on refresh.
		</p>
		<p>
			&nbsp;&nbsp;Plese note, the links above <em>(except for Home and Live Video)</em> are created dynamically. If their path to a file does not exist there will not be a link. Also note, the graphs are updated every 35 minutes.
		</p>
	</div>
 
	<div id="footer">
		<h5>LAIR Hydroponics Lab | Created: 3/22/2011</h5>
	</div>
</body>
</html>
  • styles.css
a:link
{
	color: #0f0;
}
 
a:visited
{
	color: #f0c;
}
 
a:hover
{
	text-decoration: none;
}
 
#header
{
	background: url("images/titleBar.jpg");
	color: #369;
	font-variant: small-caps;
	height: 70px;
	text-align: center;
	padding-top: 1px;
}
 
#nav
{
	background-color: black;
	width: 580px;
}
 
#nav li
{
	display: inline;
	list-style: none;
	background-color: #000;
	width: 200px;
	padding: 5px;
}
 
h2
{
	font-variant: small-caps;
	color: #f30;
}
 
p
{
	padding-left: 25px;
}
 
body
{
	font-family: sans-serif;
	background: url("images/background.jpg");
	background-attachment: fixed;
	color: #3cf;
}
 
#content
{
	background-color: #000;
	width: 500px;
	padding-left: 10px;
}
 
#homeImg
{
	float: right;
}
 
#footer
{
	background: url("images/footerBar.jpg");
	color: #369;
	height: 50px;
	text-align: center;
	padding-top: 1px;
	clear: both;
	position: absolute;
	bottom: 0px;
	width: 98%;
	left: 10px;
}

The three scripts that create the html and php files can be explained once because all of them use the same method. First I'll explain how they create the html files. Each one creates a master html. This master has links to the rest of them. The next thing they do is create a unique directory to store their php and html files. It creates the html files and php files by simply redirecting. You'll see how in the code shown next. The php graphs are created a little differently. I have a base text file I altered slightly to make it super friendly with sed so it would be easier to search and replace values where I needed it. This base text file contains all of the php code required to make a graph. Then the scripts just output the contents of this text file to a new one within its directory it created and supplies the php graph it just created with its own unique values. It may seem a little confusing now but you'll see after looking through the code.

  • loadCurrentGraph.sh
#!/bin/bash
 
####
###
##
#	Author: Jesse Short
#
#	Purpose:
#		This script will create an html and a php file to display temperature information only for the current day. The html page will import the image from the php script and append low/high/mode variables underneath the image.
#
#	crontab -e
#	2,32 * * * * /home/www/hydroMonGraphs/loadCurrentGraph.sh
#
##
###
####
 
 
#for cron
cd /var/www/hydroMonGraphs
 
#check to make sure it has the resources to work
if [[ ! -e /home/john/owfs/temperatures.txt && ! -s /home/john/owfs/temperatures.txt ]];
then
	echo "Could not locate file: temperatures.txt"
	echo "Could not locate file: temperatures.txt" > loadCurrentGraphERROR1
	exit 0
fi
 
if [[ ! -e baseGraph.txt && ! -s baseGraph.txt ]];
then
	echo "Could not locate file: baseGraph.txt in current directory"
	echo "Could not locate file: baseGraph.txt in current directory" > loadCurrentGraphERROR2
	exit 0
fi
 
#make sure there are at least 2 temperatures in temperatures.txt
#  if not, don't do anything because you cant plot just one point with jpgraph
checkNumOfTemps=`cat /home/john/owfs/temperatures.txt | wc -l`
if [[ $checkNumOfTemps -gt 2 ]];
then
 
#set the dimensions of the graph
width=1500
height=200
 
#set titles of graph
xAxisTitle="Number of Checks"
yAxisTitle="Temperatures: F"
 
#variable to hold the line number for the php array to be injected on (created in this script)
arrayLineNum=7
 
#create title of graph
# %a: abbreviated day
# %b: abbreviated month
# %d: day number
date=`date +"%a %b %d"`
title="Current Temperature Report: $date"
 
#figuring out the current day and begin reading file from there
startReadHere=`cat "/home/john/owfs/temperatures.txt" | grep -i -n "$date" | cut -d":" -f1` #n will make it prefix its output with the line number followed by a colon with no spaces
stopReadHere=`cat "/home/john/owfs/temperatures.txt" | wc -l`
stopReadHere=$(($stopReadHere + 1)) #solves off by one issue
 
#load the array of the current temperatures throughout the day
tempsIndex=0
while [ $startReadHere -ne $stopReadHere ];
do
	line=`sed -n "$startReadHere p" "/home/john/owfs/temperatures.txt"`
	if echo "$line" | grep -i -q "^[0-9]"
	then #its a temperature load it into array
		temps[$tempsIndex]=$line
		tempsIndex=$(($tempsIndex + 1))
	fi
	startReadHere=$(($startReadHere + 1)) #go to next line because initial startReadHere is on a header
done
 
#create the array for the php graph to use
for (( i=0; i<$tempsIndex; i++ ))
do
	if [ $i -eq `echo "$tempsIndex -1" | bc -lq` ]; 
	then	#end of values to add
		data="$data${temps[$i]}"	#no comma
	else
		data="$data${temps[$i]}, "
	fi
done
 
str="\$data = array($data);"	#str holding the array to inject to php
 
#start calculation of low/high/mode
#first cut the decimal places off
for (( i=0; i<$tempsIndex; i++ ))
do
	num=`echo "${temps[$i]}" | cut -d"." -f1`
	temps[$i]=$num
done
 
#sort array: bubble style
cnt=0
performanceBoost=1	#this is used to keep the bubble sort from over checking
while [ $cnt -ne $tempsIndex ]
do
	for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
	do
		if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
			swap1=${temps[$i]}
			swap2=${temps[$(($i+1))]}
			temps[$i]=$swap2
			temps[$(($i+1))]=$swap1
		fi
	done
	performanceBoost=$(($performanceBoost + 1))
	cnt=$(($cnt + 1))
done
 
#first step to find unique numbers - output all temps[] to file(easier to just use uniq command on a file)
echo -n "" > hydromonTemps
for (( i=0; i<$tempsIndex; i++ ))
do
	echo "${temps[$i]}" >> hydromonTemps
done
 
#find unique numbers only
cat hydromonTemps | uniq > hydromonUniqTemps
 
#load unique numbers into array
y=0
while read line
do
	uniqueNumArr[$y]=$line	#store unique numbers
	modeArr[$y]=0		#store count of each temp occurrence
	y=$(($y + 1))	#increment index
done < hydromonUniqTemps
 
#count every occurrence of each number
maxModeNums=`cat hydromonUniqTemps | wc -l`
while read line
do
	for (( i=0; i<$maxModeNums; i++ ))
	do
		if [[ $line == ${uniqueNumArr[$i]} ]];
		then
			modeArr[$i]=$((${modeArr[$i]} + 1))
		fi
	done
done < hydromonTemps
 
#figure the highest mode
mode=${uniqueNumArr[0]}	#default value 
highestMode=${modeArr[0]}
for (( x=0; x<$maxModeNums; x++ ))
do
	if [ $highestMode -lt ${modeArr[$x]} ];
	then
		highestMode=${modeArr[$x]}
		mode=${uniqueNumArr[$x]}
	fi
done
 
#figure the high/low
#  temps array is sorted from lowest to highest (low=0, high=maxIndex)
if [ $tempsIndex == 0 ];
then
	high=${temps[$tempsIndex]}
else
	high=${temps[$(($tempsIndex - 1))]}
fi
low=${temps[0]}
 
#cat the baseGraph.txt - holds key words to be replaced by sed and write the new file to hyrdroMonTempMonthlyGraph.php
cat baseGraph.txt | sed -e "s/insertData/\$data/g" | sed -e "s/insertTitle/$title/g" | sed -e "s/insertWidth/$width/g" | sed -e "s/insertHeight/$height/g" | sed -e "s/insertXaxisTitle/$xAxisTitle/g" | sed -e "s/insertYaxisTitle/$yAxisTitle/g" > hydroMonCurrentTempGraph.php
 
#this is the injection of the str variable created above. That specific line because it's blank and meant to hold this value
sed -i "$arrayLineNum c$str" hydroMonCurrentTempGraph.php #write to line 7 of createGraph.php
 
#create the html that will display current high/low/mode
echo -e "<html>\n<head>\n<title>\n$title\n</title>\n</head>\n<body>\n<img src=\"hydroMonCurrentTempGraph.php\">\n<ul>\n<li>Low: $low</li>\n<li>High: $high</li>\n<li>Mode: $mode</li>\n<li><a href=\"../index.php\">Home</a></li>\n</ul>\n</body>\n</html>" > hydroMonCurrentTempGraph.html
 
#change the permissions for the php/html file for the web
chmod 755 hydroMonCurrentTempGraph.php
chmod 755 hydroMonCurrentTempGraph.html
 
#clean up anything created that is no longer needed
rm hydromonTemps hydromonUniqTemps
 
else #initial check if temperatures.txt has at least 2 temps
echo "Too few temperatures to create graphs" > loadCurrentGraphERROR3
fi
  • loadDailyGraph.sh
#!/bin/bash
 
#####
####
###
##		Author: Jesse Short
#
#	This script will create daily charts for the temperatures. It loops through the temperatures.txt file looking for temperatures and the end of the day. It loads the temperatures into an array. If the line has "end" in it then it will stop adding to the array. It then cuts the line up for the date. After that it hops into a for loop. Here it makes the body of the string it will inject to the php. This is the array for the graph to use. It tests to see if the loop counter is one less than the total amount of elements in the array because this is a special case. There should not be a comma after the last number. Otherwise, do number and comma. After that it makes the string to be injected. Before it is injected though, it cats baseGraph.txt which holds the shell for the php graph. This has the blank space for the string to be injected as well as key words to be replaced later with sed. Anyway back to catting baseGraph.txt. This is where the before mentioned key words get replaced. It searches for insertData which is where the array variable goes (only the variable) and insertTitle which is where the heading of the graph goes (I use the date as the title). This is output to tmp which is then mv'd to createGraph$a.php. The a is counter variable for naming purposes. After that the str that was created in the beginning for the php graph to use to make the graph is enjected on line 7. Note, this has to be blank space or it WILL OVERWRITE. Next it chmod's it so it can be viewed on the web (createGraph$a.php). Finally, it crates an index.html. It will add the beginning html code then create links as needed to the graphs and end with the proper html code. It also chmods this to be viewed on the web. I recommend running destroyGraphs.sh when done because there are going to be a lot of files.
#
#	This is script is made executable with:
#		chmod 700 loadDailyGraph.sh
#	
#	crontab -e
#		3,33 * * * * /var/www/hydroMonGraphs/loadDailyGraph.sh
#
##
###
####
#####
 
#for cron
cd /var/www/hydroMonGraphs
 
#check to make sure it has the resources to work
if [[ ! -e /home/john/owfs/temperatures.txt && ! -s /home/john/owfs/temperatures.txt ]];
then
	echo "Could not locate file: temperatures.txt"
	echo "Could not locate file: temperatures.txt" > loadDailyGraphERROR1
	exit 0
fi
 
if [[ ! -e baseGraph.txt && ! -s baseGraph.txt ]];
then
	echo "Could not locate file: baseGraph.txt in current directory"
	echo "Could not locate file: baseGraph.txt in current directory" > loadDailyGraphERROR2
	exit 0
fi
 
#make sure there are at least 2 temperatures in temperatures.txt
#  if not, don't do anything because you cant plot just one point with jpgraph
checkNumOfTemps=`cat /home/john/owfs/temperatures.txt | wc -l`
if [[ $checkNumOfTemps -gt 2 ]];
then
 
#set dimensions of graph
width=1550
height=200
 
#set titles of graph
xAxisTitle="Number of Checks"
yAxisTitle="Temperatures: F"
 
#create the beginning of the html to navigate between charts
echo -e "<html>\n<head>\n<title>Hydromon's Daily Temperature Reports</title>\n</head>\n<body>\n<a href=\"../index.php\">Home</a><br><br>\n" > hydroMonDayIndex.html
 
#the directory that will hold the php graphs
if [ ! -d hydroMonTempGraphs ];
then
	mkdir hydroMonTempGraphs
	#change the permissions for the web
	chmod 755 hydroMonTempGraphs
fi
 
a=0 #number to track amount of graphs created
tempsIndex=0	#array index var
arrayLineNum=7 #first available line for the array to be injected into the php file (it overwrites)
 
while read line
do
	#looking for temperatures and storing them in an array
	if echo $line | grep -q "^[0-9]"
	then
		temperatureArr[$tempsIndex]=$line
		tempsIndex=$(($tempsIndex + 1))
	fi
 
	#looking for the string "end" and using that to kick out a graph
	if echo $line | grep -i -q "end" #end of the day: make graph for that day
	then
		title=`echo $line | cut -d" " -f5-7` #title the graph with the date
		for (( i=0; i<$tempsIndex; i++ ))
		do
			if [ $i -eq `echo "$tempsIndex -1" | bc -lq` ]; 
			then	#end of values to add
				data="$data${temperatureArr[$i]}"	#no comma
			else
				data="$data${temperatureArr[$i]}, "
			fi
		done
 
		str="\$data = array($data);"	#str holding the array to inject to php
 
		#cut off decimal points from temperatureArr and store in temps[]
		for (( i=0; i<$tempsIndex; i++ ))
		do
			temps[$i]=`echo "${temperatureArr[$i]}" | cut -d"." -f1`
		done
 
		#sort array: bubble style
		cnt=0
		performanceBoost=1      #this is used to keep the bubble sort from over checking
		while [ $cnt -ne $tempsIndex ]
		do
			for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
			do
				if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
					swap1=${temps[$i]}
					swap2=${temps[$(($i+1))]}
					temps[$i]=$swap2
					temps[$(($i+1))]=$swap1
				fi
			done
			performanceBoost=$(($performanceBoost + 1))
			cnt=$(($cnt + 1))
		done
 
		#first step to find unique numbers - output all temps[] to file(easier to just use uniq command on a file)
		echo -n "" > hydromonTemps
		for (( i=0; i<$tempsIndex; i++ ))
		do
		        echo "${temps[$i]}" >> hydromonTemps
		done
 
		#find unique numbers only
		cat hydromonTemps | uniq > hydromonUniqTemps
 
		#load unique numbers into array
		y=0
		while read line
		do
			uniqueNumArr[$y]=$line  #store unique numbers
			modeArr[$y]=0           #store count of each temp occurrence
			y=$(($y + 1))   #increment index
		done < hydromonUniqTemps
 
		#count every occurrence of each number
		maxModeNums=`cat hydromonUniqTemps | wc -l`
		while read line
		do
			for (( i=0; i<$maxModeNums; i++ ))
			do
				if [[ $line == ${uniqueNumArr[$i]} ]];
				then
					modeArr[$i]=$((${modeArr[$i]} + 1))
				fi
			done
		done < hydromonTemps
 
		#figure the highest mode
		mode=${uniqueNumArr[0]} #default value 
		highestMode=${modeArr[0]}
		for (( x=0; x<$maxModeNums; x++ ))
		do
			if [ $highestMode -lt ${modeArr[$x]} ];
			then
				highestMode=${modeArr[$x]}
				mode=${uniqueNumArr[$x]}
			fi
		done
 
		#figure the high/low
		#  temps array is sorted from lowest to highest (low=0, high=maxIndex)
		if [ $tempsIndex == 0 ];
		then
			high=${temps[$tempsIndex]}
		else
			high=${temps[$(($tempsIndex - 1))]}
		fi
		low=${temps[0]}
 
		#search and replace key words and save to php file
		cat baseGraph.txt | sed -e "s/insertData/\$data/g" | sed -e "s/insertTitle/$title/g" | sed -e "s/insertWidth/$width/g" | sed -e "s/insertHeight/$height/g" | sed -e "s/insertXaxisTitle/$xAxisTitle/g" | sed -e "s/insertYaxisTitle/$yAxisTitle/g" > hydroMonTempGraphs/createGraph$a.php
 
		#inject the array to php file
		sed -i "$arrayLineNum c$str" hydroMonTempGraphs/createGraph$a.php #write to line 7 of createGraph.php
 
		#change permissions for web of php file just created
		chmod 755 hydroMonTempGraphs/createGraph$a.php
 
		#create the html file to hold the low/high/mode and php image
		echo -e "<html>\n<head>\n<title>$title</title>\n</head>\n<body>\n<img src=\"createGraph$a.php\">\n<br><br>\n<ul>\n\t<li>Low: $low</li>\n\t<li>High: $high</li>\n\t<li>Mode: $mode</li>\n<li><a href=\"../hydroMonDayIndex.html\">Back</a></li>\n<li><a href=\"../../index.php\">Home</a></li>\n</ul>\n</body>\n</html>" > hydroMonTempGraphs/dailyGraph$a.html
 
		#change the permissions for the web
		chmod 755 hydroMonTempGraphs/dailyGraph$a.html
 
		#Create the link to the html file in the main html file
		echo -e "<a href=\"hydroMonTempGraphs/dailyGraph$a.html\"> Temperature Graph: $title</a>\n <br><br>\n" >> hydroMonDayIndex.html
 
		#increment the graph count variable
		a=$(($a + 1))
 
		#restart the array counting var
		tempsIndex=0
 
		#reset the php array string
		data=""
	fi	
	done < /home/john/owfs/temperatures.txt
 
	#the current day will not be figured because there wont be a footer so lets make the current html/php now
	title=`date +"%a %b %d"` #title the graph with the date
	for (( i=0; i<$tempsIndex; i++ ))
	do
		if [ $i -eq `echo "$tempsIndex -1" | bc -lq` ]; 
		then	#end of values to add
			data="$data${temperatureArr[$i]}"	#no comma
		else
			data="$data${temperatureArr[$i]}, "
		fi
	done
 
	str="\$data = array($data);"	#str holding the array to inject to php
 
	#cut off decimal points from temperatureArr and store in temps[]
	for (( i=0; i<$tempsIndex; i++ ))
	do
		temps[$i]=`echo "${temperatureArr[$i]}" | cut -d"." -f1`
	done
 
	#sort array: bubble style
	cnt=0
	performanceBoost=1      #this is used to keep the bubble sort from over checking
	while [ $cnt -ne $tempsIndex ]
	do
		for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
		do
			if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
				swap1=${temps[$i]}
				swap2=${temps[$(($i+1))]}
				temps[$i]=$swap2
				temps[$(($i+1))]=$swap1
			fi
		done
		performanceBoost=$(($performanceBoost + 1))
		cnt=$(($cnt + 1))
	done
 
	#first step to find unique numbers - output all temps[] to file(easier to just use uniq command on a file)
	echo -n "" > hydromonTemps
	for (( i=0; i<$tempsIndex; i++ ))
	do
		echo "${temps[$i]}" >> hydromonTemps
	done
 
	#find unique numbers only
	cat hydromonTemps | uniq > hydromonUniqTemps
 
	#load unique numbers into array
	y=0
	while read line
	do
		uniqueNumArr[$y]=$line  #store unique numbers
		modeArr[$y]=0           #store count of each temp occurrence
		y=$(($y + 1))   #increment index
	done < hydromonUniqTemps
 
	#count every occurrence of each number
	maxModeNums=`cat hydromonUniqTemps | wc -l`
	while read line
	do
		for (( i=0; i<$maxModeNums; i++ ))
		do
			if [[ $line == ${uniqueNumArr[$i]} ]];
			then
				modeArr[$i]=$((${modeArr[$i]} + 1))
			fi
		done
	done < hydromonTemps
 
	#figure the highest mode
	mode=${uniqueNumArr[0]} #default value 
	highestMode=${modeArr[0]}
	for (( x=0; x<$maxModeNums; x++ ))
	do
		if [ $highestMode -lt ${modeArr[$x]} ];
		then
			highestMode=${modeArr[$x]}
			mode=${uniqueNumArr[$x]}
		fi
	done
 
	#figure the high/low
	#  temps array is sorted from lowest to highest (low=0, high=maxIndex)
	if [ $tempsIndex == 0 ];
	then
		high=${temps[$tempsIndex]}
	else
		high=${temps[$(($tempsIndex - 1))]}
	fi
	low=${temps[0]}
 
	#search and replace key words and save to php file
	cat baseGraph.txt | sed -e "s/insertData/\$data/g" | sed -e "s/insertTitle/$title/g" | sed -e "s/insertWidth/$width/g" | sed -e "s/insertHeight/$height/g" | sed -e "s/insertXaxisTitle/$xAxisTitle/g" | sed -e "s/insertYaxisTitle/$yAxisTitle/g" > hydroMonTempGraphs/createGraph$a.php
 
	#inject the array to php file
	sed -i "$arrayLineNum c$str" hydroMonTempGraphs/createGraph$a.php #write to line 7 of createGraph.php
 
	#change permissions for web of php file just created
	chmod 755 hydroMonTempGraphs/createGraph$a.php
 
	#create the html file to hold the low/high/mode and php image
	echo -e "<html>\n<head>\n<title>$title</title>\n</head>\n<body>\n<img src=\"createGraph$a.php\">\n<br><br>\n<ul>\n\t<li>Low: $low</li>\n\t<li>High: $high</li>\n\t<li>Mode: $mode</li>\n<li><a href=\"../hydroMonDayIndex.html\">Back</a></li>\n<li><a href=\"../../index.php\">Home</a></li>\n</ul>\n</body>\n</html>" > hydroMonTempGraphs/dailyGraph$a.html
 
	#change the permissions for the web
	chmod 755 hydroMonTempGraphs/dailyGraph$a.html
 
	#Create the link to the html file in the main html file
	echo -e "<a href=\"hydroMonTempGraphs/dailyGraph$a.html\"> Temperature Graph: $title</a>\n <br><br>\n" >> hydroMonDayIndex.html
 
	#increment the graph count variable
	a=$(($a + 1))
 
	#restart the array counting var
	tempsIndex=0
 
	#reset the php array string
	data=""
 
	#finish the html for the index file
	echo -e "</body>\n</html>" >> hydroMonDayIndex.html
 
	#change the permissions for the html file for the web
	chmod 755 hydroMonDayIndex.html
 
	#clean up
	rm hydromonTemps hydromonUniqTemps 2>/dev/null
 
else #initial check if temperatures.txt has at least 2 temps
echo "Too few temperatures to create graphs" > loadDailyGraphError3
fi
  • loadMonthlyGraph.sh
#!/bin/bash
 
#####
####
###		Author: Jesse Short
##
#	This script will create a jpgraph with a php file. It uses every temperature from the temperatures.txt file to create a monthly graph displaying low, high, and mode per specified sample count. For example, you want the stats for every 6 temperatures (3 hours). Set SAMPLE to 6.
#
#	This script is made executable by:
#		chmod 700 loadMonthlyGraph.sh
#	
#	crontab -e
#		4,34 * * * * /var/www/hydroMonGraphs/loadMonthlyGraph.sh
#
##
###
####
#####
 
#for cron
cd /var/www/hydroMonGraphs
 
#check to make sure it has the resources to work
if [[ ! -e /home/john/owfs/temperatures.txt && ! -s /home/john/owfs/temperatures.txt ]];
then
	echo "Could not locate file: temperatures.txt"
	echo "Could not locate file: temperatures.txt" > loadMonthlyGraphERROR1
	exit 0
fi
 
if [[ ! -e baseGraph.txt && ! -s baseGraph.txt ]];
then
	echo "Could not locate file: baseGraph.txt in current directory"
	echo "Could not locate file: baseGraph.txt in current directory" > loadMonthlyGraphERROR2
	exit 0
fi
 
#make sure there are at least 2 temperatures in temperatures.txt
#  if not, don't do anything because you cant plot just one point with jpgraph
checkNumOfTemps=`cat /home/john/owfs/temperatures.txt | wc -l`
if [[ $checkNumOfTemps -gt 2 ]];
then
 
#set the dimensions of the graph
width=500
height=200
 
#set titles of graph
xAxisTitle="Low | High | Mode"
yAxisTitle="Temperatures: F"
 
 
#variable to hold the line number for the php array to be injected on (created in this script)
arrayLineNum=7
 
#create the beginning of the html to navigate between charts
echo -e "<html>\n<head>\n<title>Hydromon's Monthly Temperature Reports</title>\n</head>\n<body>\n<a href=\"../index.php\">Home</a><br><br>\n" > hydroMonMonthIndex.html
 
#the directory that will hold the php graphs
if [ ! -d hydroMonMonthTempGraphs ];
then
	mkdir hydroMonMonthTempGraphs
	#change the permissions for the web
	chmod 755 hydroMonMonthTempGraphs
fi
 
#control structure to sample every so many temperatures
SAMPLE=6 #change this variable to set frequency
count=0	#number of php graphs made 0 to n
tempsIndex=0 #variable to hold the count of the array which will hold sampled temperatures
createPhp="yes"	#if low/high/mode are all the same, cannot create graph
 
#read temperatures.txt and load temps[] and create title
#  Also calculates low/high/mode 
while read line
do
	if echo "$line" | grep -q "^[0-9]"
	then
		#make the numbers whole (no decimal values)
		temps[$tempsIndex]=`echo "$line" | cut -d"." -f1`
		tempsIndex=$(($tempsIndex + 1))
	elif echo "$line" | grep -i -q "beg"
	then
		date=`echo "$line" | cut -d" " -f5-7`
	fi
 
	if [ $tempsIndex == $SAMPLE ];
	then #begin data collection and graph creation
		#solves off by one issue
		tempsIndex=$(($tempsIndex - 1))
 
		#the link and php title
		title="$date - $SAMPLE Samples - Graph: $count" #title to be inserted into graph
 
		#sort the numbers: bubble style
		cnt=0
		performanceBoost=1	#this is used to keep the bubble sort from over checking
		while [ $cnt -ne $tempsIndex ]
		do
			for (( i=0; i<$(($tempsIndex-$performanceBoost)); i++ ))
			do
				if [[ ${temps[$i]} -gt ${temps[$(($i+1))]} ]]; then
					swap1=${temps[$i]}
					swap2=${temps[$(($i+1))]}
					temps[$i]=$swap2
					temps[$(($i+1))]=$swap1
				fi
			done
			performanceBoost=$(($performanceBoost + 1))
			cnt=$(($cnt + 1))
		done
 
		#first step to find unique numbers - output all temps[] to file(easier to just use uniq command on a file)
		echo -n "" > hydromonTemps
		for (( i=0; i<$tempsIndex; i++ ))
		do
			echo "${temps[$i]}" >> hydromonTemps
		done
 
		#find unique numbers only
		cat hydromonTemps | uniq > hydromonUniqTemps
 
		#load unique numbers into array
		y=0
		while read line
		do
			uniqueNumArr[$y]=$line	#store unique numbers
			modeArr[$y]=0		#store count of each temp occurrence
			y=$(($y + 1))	#increment index
		done < hydromonUniqTemps
 
		#count every occurence of each number
		maxModeNums=`cat hydromonUniqTemps | wc -l`
		while read line
		do
			for (( i=0; i<$maxModeNums; i++ ))
			do
				if [[ $line == ${uniqueNumArr[$i]} ]];
				then
					modeArr[$i]=$((${modeArr[$i]} + 1))
				fi
			done
		done < hydromonTemps
 
		#figure the highest mode
		mode=${uniqueNumArr[0]}	#default value 
		highestMode=${modeArr[0]}
		for (( x=0; x<$maxModeNums; x++ ))
		do
			if [ $highestMode -lt ${modeArr[$x]} ];
			then
				highestMode=${modeArr[$x]}
				mode=${uniqueNumArr[$x]}
			fi
		done
 
		#figure the high/low
		#  temps array is sorted from lowest to highest (low=0, high=maxIndex)
		high=${temps[$tempsIndex]}
		low=${temps[0]}
 
		if [[ $high == $low && $low == $mode ]]; # $high == $mode ]];
		then	#cannot create a graph with all of the same numbers
			createPhp="no"
			title="$date - $SAMPLE Samples - Graph: $count - low/high/mode = $mode" #title to be inserted into graph
			echo -e "$title<br><br>\n" >> hydroMonMonthIndex.html
		fi
 
		if [ $createPhp == "yes" ];
		then
			#used for creating php graph
			data="$low, $high, $mode"		#numbers for the array
			str="\$data = array($data);"	#str for php array uses above line for data
 
			#cat the baseGraph.txt - holds key words to be replaced by sed and write the new file to hyrdroMonTempMonthlyGraph.php
			cat baseGraph.txt | sed -e "s/insertData/\$data/g" | sed -e "s/insertTitle/$title/g" | sed -e "s/insertWidth/$width/g" | sed -e "s/insertHeight/$height/g" | sed -e "s/insertXaxisTitle/$xAxisTitle/g" | sed -e "s/insertYaxisTitle/$yAxisTitle/g" > hydroMonMonthTempGraphs/hydroMonTempMonthlyGraph$count.php
 
			#this is the injection of the str variable created above. That specific line because it's blank and meant to hold this value
			sed -i "$arrayLineNum c$str" hydroMonMonthTempGraphs/hydroMonTempMonthlyGraph$count.php #write to line 7 of createGraph.php
 
			#change the permissions for the web
			chmod 755 hydroMonMonthTempGraphs/hydroMonTempMonthlyGraph$count.php
 
			#create the link to the php file in the html file
			echo -e "<a href=\"hydroMonMonthTempGraphs/hydroMonTempMonthlyGraph$count.php\"> Monthly Temperature Graph: $title</a>\n<br><br>\n" >> hydroMonMonthIndex.html
		fi
		createPhp="yes" #reset the var
 
		#increment graph count variable
		count=$(($count + 1))
 
		#reset the php array string
		data=""
 
		#reset index for temps[]
		tempsIndex=0
	fi
done < /home/john/owfs/temperatures.txt
 
	#finish the html for the index file
	echo -e "</body>\n</html>" >> hydroMonMonthIndex.html
 
	#change the permissions for the html file for the web
	chmod 755 hydroMonMonthIndex.html
 
	#delete the temp files used for low/high/mode calculation
	rm hydromonTemps hydromonUniqTemps 2>/dev/null
 
else #initial check if temperatures.txt has at least 2 temps
echo "Too few temperatures to create graphs" > loadMonthlyGraphERROR3
fi
  • destroyHydromonGraphs.sh This one is used to delete anything the three previous scripts create
#!/bin/bash
 
#####
####
###		Author: Jesse Short
##
#	This script is purely meant to clean up after loadMonthlyGraph.sh and loadDailyGraph.sh. It just looks to see if certain files/folders exist and it removes them.
#
#	This script is made executable by:
#		chmod 700 destroyHydroMonGraphs.sh
#
#	crontab -e
#	1,31 * * * * /var/www/hydroMonGraphs/destroyHydroMonGraphs.sh
#
##
###
####
#####
 
#for cron
cd /var/www/hydroMonGraphs
 
#the directory for the php graphs
if [ -d hydroMonTempGraphs ];
then
	rm -rf hydroMonTempGraphs
fi
 
#the directory for the monthly php graphs
if [ -d hydroMonMonthTempGraphs ];
then
	rm -rf hydroMonMonthTempGraphs
fi
 
#the html file holding the links to the graphs
if [ -e hydroMonDayIndex.html ];
then
	rm hydroMonDayIndex.html
fi
 
#the php file with the monthly graph links
if [ -e hydroMonMonthIndex.html ];
then
	rm hydroMonMonthIndex.html
fi
 
#the php file with the current graph made by loadCurrentTempGraph.sh
if [ -e hydroMonCurrentTempGraph.php ];
then
	rm hydroMonCurrentTempGraph.php
fi
 
#the html file holding the image of the current days temperature graph
if [ -e hydroMonCurrentTempGraph.html ];
then
	rm hydroMonCurrentTempGraph.html
fi
  • Note the crontab inserts. As long as You grab the temperature first then destroy, it doesn't matter what order the loads go in.
  • Note that the files and folders dealing with the web based version need to have the proper permissions. Something like chmod 705 will work.

References:


http://www.technotes.se/?p=26 Notes from the professor: Matthew Haas

Lair Tech Docu Store

Author: Jesse Short

Objective:


The Lair Tech Docu Store (full name: LAIRTechWeb - DocuStore XL9000j) is a place to store your pdfs converted to text. Using some languages of the web (html, css, and php) as well as using some of the knowledge I've learned from my schooling Created a user friendly web-based front end to an sql database. The purpose of this website was to be able to quickly upload a pdf, convert it to text, and store it in a database. Also, to provide search through and download capabilities.

Prerequisites:


Install Apache and php5 (new versions may be out when you read this, please check)

  • apt-get install apache2 php5 libapache2-mod-php5
  • The Apache config file location: /etc/apache2/apache2.conf
  • The web folder location: /var/www
  • Check it is working properly
  • mkdir /var/www/test
  • cd /var/www/test
  • touch test.php
  • echo -e ”<?php\nphpinfo();\n?>“ > test.php
  • phpinfo() is a function that will output php configurations
  • Open your favorite browser and point it at: http://localhost/test/test.php

Now to install mysql:

  • apt-get install mysql-server mysql-client php5-mysql
  • Mysql config file location: /etc/mysql/my.cnf
  • By default the user is root and there is no password. To change this:
  • mysql -u root
  • use mysql;
  • update user set Password=PASSWORD WHERE user=root; 'PASSWORD' would be your password
  • FLUSH PRIVILEGES; frees old cached memory

Now to install phpMyAdmin, a great web-based management and administration software package

  • apt-get install phpmyadmin
  • phpmyadmin config file location: /etc/phpmyadmin
  • To set up under Apache include the following line in /etc/apache2/apache2.conf:
    • Include /etc/phpmyadmin/apache.conf
  • Restart Apache with:
    • /etc/init.d/apache2 restart
  • To access phpmyadmin point your browser to: http://localhost/phpmyadmin

Finally, install pdftotext:

  • Depending on your os (redhat, centos, fedora): yum install poppler-utils
  • Depending on your os (debian, ubuntu): sudo apt-get install poppler-utils
  • Command: pdftotext “file.pdf” “file.txt”
    • It will automatically create the text file if it does not exist. Also, do not add the quotes
    • EX: pdftotext sample.pdf sample.txt

Background:


A Web server is a computer program that serves content, such as web pages, using the Hypertext Transfer Protocol (HTTP), over the World Wide Web. Their most common use is to host web sites. But they can also be used for things such as data storage. Not to skimp out on an explanation of how a web server works, you will be better served if you go here: http://www.howstuffworks.com/web-server.htm for an explanation.

Procedure:



Now onto the code. I'll first state That I know I could have used functions and made the code smaller by not repeating some of the same steps.

  • Index.php
  • Cook.php
  • Img.php
  • Add.php
  • Del.php
  • Find.php
  • Read.php
  • styles.css

Index.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"400\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
//this cookie will be tested by the cookie page so that page knows what to do
@setcookie("from", "home");
//unset secret cookie
@setcookie("secret", "fail");
 
//set the address of the server --used for redirection
$ipAddress = $_SERVER['SERVER_ADDR'];
 
if ( $_POST['submit'] )
{
	$pw = $_POST['images'];
	if ( $pw == "iCantTellYouThis" )
	{	
		//redirect quickly to page to set a cookie
		//use this header if not in lair
		//@header("Location: http://localhost:8081/docuStore/cook.php");
		//else use this (in lair)
		@header("Location: http://$ipAddress/docuStore/cook.php");
	}
	else
		$wrongPw = "<br>Incorrect Pass Code<br>";
}
?>
<html>
<head>
	<title>
		LAIRTechWeb DocuStore XL 9000j
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIRTechWeb - DocuStore XL9000j</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Welcome</h2>
		<p>
			LAIRTechWeb DocuStore XL 9000j is a place for you to store your pdf files which will be converted to text. Above are links that will allow you to add a pdf, remove a pdf, search for/through pdfs, read a pdf, and download pdfs/text files from your collection.
		</p>
		<form method="post" action="" >
			<fieldset>
				<legend>Password To Upload Images</legend>
				Enter password to upload images for the right column:
				<br>
				<?=$wrongPw?>
				<input type="password" size="20" name="images">
				<input type="submit" value="Submit" name="submit">
			</fieldset>
		</form>
	</div>
 
	<div id="footer">
		<h5>LAIRTechWeb DocuStore XL 9000j | Created: 04/07/11</h5>
	</div>
</body>
</html>

I will explain the first bit of code only once because every page uses it. The directory rtColImgs holds your images to be displayed on the right hand side of the page. It opens up the directory, grabs each file individually, checks to make sure it is an allowed image format, stores the file name in an array, and finally chooses a random index of the array of file names just created and compares it to the index saved in a cookie to make sure it wont display the same image more than once in a row. After that, two more cookies get set. The first will tell the pages that need to know (cook.php) where the user came from. The second cookie is to let the page cook.php know if you had the right password or not entered in the password box found on the index.php page. You haven't filled it in yet so the secret page is locked still. If you knew the password, the page would become available for you. Finally, notice the $ipAddress variable. Using the super global array $_SERVER variable, I grab the server's ip address and store it in a variable which is used for header redirection.

You will see a simple form with a password text box and a submit button. The php to process this form is equally as simple. It merely compares the user's input to the password. If it was incorrect then it will display a message to let the user know it was wrong. If it was correct then it will use a header redirection to cook.php. A session may be a (better) alternative to the cookies.

Cook.php


<?php
$ipAddress = $_SERVER['SERVER_ADDR'];
//check if user came from home
if ( $_COOKIE['from'] == "home" )
{
	setcookie("secret", "win");
	//redirect to img.php
	//header("Location: http://localhost:8081/docuStore/img.php");
	header("Location: http://$ipAddress/docuStore/img.php");
}
else//redirect back to page
{
	//header("Location: http://localhost:8081/docuStore/$from");
	header("Location: http://$ipAddress/docuStore/index.php");
}
?>

This page is very simple. You should only be hitting this page if you entered the correct password on the index.php page. It checks the cookie 'from' to make sure you came from the home page. If not, then you get redirected back to the home page. If you did come from the home page that means you knew the password so it sets the 'secret' cookie and redirects you to img.php. I'm sure you see the backdoor to this already. Simply go to the home page, then enter cook.php in the url and it will redirect you to the img.php page. I know, easy and simple to get around it, but I'm a beginner at all of this, this was my attempt to securing a page.

Img.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//check from and secret cookies
if ( $_COOKIE['from'] !== "home" || $_COOKIE['secret'] !== "win" )
	die("cannot view this page without correct password on <a href=\"index.php\">home</a> page");
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"400\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
$ipAddress = $_SERVER['SERVER_ADDR'];
 
if ( $_POST['upload'])
{
	$_POST['submit'] = 1;
	//check super global array for errors
	if ( $_FILES["file"]["error"] > 0 )
	{
		$fileError = "Errors with file: ";
		switch ($_FILES["file"]["error"])
		{
			case 1:	$fileError .= " File exceeds max upload size<br>";
				break;
			case 2: $fileError .= " File exceeds max file size<br>";
				break;
			case 3: $fileError .= " File was only paritally uploaded<br>";
				break;
			case 4: $fileError .= " File was not uploaded<br>";
				break;
			case 6: $fileError .= " Missing a temporary folder<br>";
				break;
			case 7: $fileError .= " Failed to write file to disk<br>";
				break;
		}	
		//$_FILES["file"]["error"]
	}
	else
	{
		//make sure the file uploaded was an image
		if ( $_FILES["file"]["type"] == "image/gif" ||
			$_FILES["file"]["type"] == "image/jpeg" ||
			$_FILES["file"]["type"] == "image/pjpeg" ||
			$_FILES["file"]["type"] == "image/png" )
		{
			$upFile = $_FILES["file"]["name"];
			//lets make sure the file is safe:
			$upFile = str_replace("#", "No.", $upFile);
			$upFile = str_replace("$", "Dollar", $upFile);
			$upFile = str_replace("%", "Percent", $upFile);
			$upFile = str_replace("^", "Caret", $upFile);
			$upFile = str_replace("&", "Ampersand", $upFile);
			$upFile = str_replace("*", "Asterisk", $upFile);
			$upFile = str_replace("?", "Question", $upFile);
			//check if the file already exists in the $dir (dupe)
			if ( file_exists("/var/www/docuStore/$dir/$upFile") )
			{
				$fileExistError = "<br>File exists. Try renaming your file<br>";
				unset($_POST);
			}
			else
			{
				//prepare server for upload
				if ( !is_dir("/var/www/docuStoreUp/$dir") )
					mkdir("/var/www/docuStore/$dir", 0777);
				//upload file
				if ( ! move_uploaded_file($_FILES["file"]["tmp_name"], "/var/www/docuStore/$dir/$upFile"))
					$fileMoveError = "<br>Error moving file<br>";
				else
					$fileMoveError = "<br>File upload success!<br>";
			}
		}
	}
}
?>
<html>
<head>
	<title>
		LAIRTechWeb DocuStore XL 9000j
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIRTechWeb - DocuStore XL9000j</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Upload Images</h2>
 
		<p>
			Upload images for the right column of this website.
		</p>
 
		<form action="" method="post" enctype="multipart/form-data">
		 <fieldset>
		  <legend>Upload Files Only</legend>
			<?=$fileError?>
			<?=$fileExistError?>
			<?=$fileMoveError?>
			<?=$fileTypeError?>
			<?=$fileUploadSuccess?>
			<br>
			Upload File: <input type="file" name="file">
			<input type="submit" value="Upload" name="upload">
		 </fieldset>
		</form>
	</div>
 
	<div id="footer">
		<h5>LAIRTechWeb DocuStore XL 9000j | Created: 04/07/11</h5>
	</div>
</body>
</html>

This page is only used to upload images to the server in the rtColImgs/ directory. There is a simple upload form. Note the \<form\> tag for the enctype: enctype=“multipart/form-data”. This is very important when uploading something; it is required. So, the part of the code you need to focus on is where it begins with: if ( $_POST['upload']) (about line 42 if using jEdit). The button is named 'upload' and comes from the super global array $_POST. The super global array $_FILES has error handling. The errors can be found at php.net. Just search for the array. Each error has a specific number, so I figured a case structure would be perfect for figuring out which error you got. My method will only show one error at a time. But, it will go in order. So after checking if there are any errors (if any, it will create a string which will be output further down inline of the html) and you don't have any, it will go on to checking the file type. If it does not match the types I provided then it will not be uploaded. I only allow png, jpg, and gif file types (popular image formats). If it is a valid file type I then save the file name from the $_FILES array to a variable so I can access it easier. I then go through the name and look for harmful characters and replace them. This will stop injections. I then check to see if the file exists in the rtColImgs/ directory. If it does, it fails, and creates a message. If it passes then it goes on to checking if the rtColImgs/ dir exists. If it does not exist it will create it. Then with the move file command it will try to upload the image. If there is a problem with the upload it will create a string to be displayed to the user.

Add.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//error_reporting(E_ALL); //debugging
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"350\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
//this cookie will be tested by the cookie page so that page knows what to do
@setcookie("from", "add");
//unset secret cookie
@setcookie("secret", "fail");
 
$ipAddress = $_SERVER['SERVER_ADDR'];
 
function db($tName, $pName, $title, $author, $isbn, $categories)
{
	global $error;
	$error = 0;
	$content = file_get_contents("/var/www/docuStore/$tName.txt");
	$content = addslashes($content);
	//make the database
	$server = 'localhost';
	$user = 'root';
	$database = 'pdfsToTxt';
	$db = mysql_connect($server, $user, $password, $database);
	if (!$db)
		die("Could Not Connect To Database");
	$result = mysql_query("create database if not exists $database", $db);
	if (!$result)
	{
		global $dbCreateError;
		$dbCreateError = "<br>Error Creating Database<br>";
		$error++;
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	$result = mysql_select_db($database, $db);
	if (!$result)
	{
		global $dbSelectError;
		$dbSelectError = "<br>Error Selecting Database<br>";
		$error++;
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	$result = mysql_query("create table if not exists myPdfs (id int primary key auto_increment not null, title varchar(50) not null,  author varchar(50) not null, isbn varchar(13), content mediumtext not null, category text not null )", $db);
	if (!$result)
	{
		global $dbCreateTableError;
		$dbCreateTableError = "<br>Error Creating Table<br>";
		$error++;
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	if ( $error == 0 )
	{
		$result = mysql_query("insert into myPdfs (title, author, isbn, content, category) values ('$title', '$author', '$isbn', '$content', '$categories')");
		if (!$result)
		{
			global $dbInsertError;
			$dbInsertError = "<br>Error Inserting Content<br>";
			$error++;
			//die(mysql_error()); //debugging
		}
	}
	mysql_close($db);
 
	if ( $error == 0 )
	{
		global $tableAdded;
		$tableAdded = "<br><h2>$tName added successfully to $database in sql database</h2><br>";
		//move the pdf and txt files to their respective dir's
		rename("$tName.txt", "pdfToTxt/$tName.txt");
		rename("$pName", "pdfs/$pName");
		global $added;
		$added = "<br><h2>$pName converted to $tName <br>and stored in pdfs/ and pdfToTxt/</h2><br>";
	}
	$txt = "*.txt";
	$pdf = "*.pdf";
	array_map('unlink', glob($txt) );
	array_map('unlink', glob($pdf) );
}
 
if ( $_POST['upload'] )
{
	//check super global array for errors
	if ( $_FILES["file"]["error"] > 0 )
	{
		$fileError = "Errors with file: ";
		switch ($_FILES["file"]["error"])
		{
			case 1:	$fileError .= " File exceeds max upload size<br>";
				break;
			case 2: $fileError .= " File exceeds max file size<br>";
				break;
			case 3: $fileError .= " File was only paritally uploaded<br>";
				break;
			case 4: $fileError .= " File was not uploaded<br>";
				break;
			case 6: $fileError .= " Missing a temporary folder<br>";
				break;
			case 7: $fileError .= " Failed to write file to disk<br>";
				break;
		}	
 
		//$_FILES["file"]["error"]
	}
	else
	{
		//make sure the file uploaded was a pdf
		if ( $_FILES["file"]["type"] == "application/pdf" )
		{
			$upFile = $_FILES["file"]["name"];
			$ext = pathinfo( $upFile, PATHINFO_EXTENSION );
			$ext = strtolower($ext);
			if ( $ext !== "pdf" )
				die("Fatal Error");
			//lets make sure the file is safe:
			$upFile = str_replace("#", "Hash", $upFile);
			$upFile = str_replace("$", "Dollar", $upFile);
			$upFile = str_replace("%", "Percent", $upFile);
			$upFile = str_replace("^", "Caret", $upFile);
			$upFile = str_replace("&", "Ampersand", $upFile);
			$upFile = str_replace("*", "Asterisk", $upFile);
			$upFile = str_replace("?", "Question", $upFile);
 
			if ( !file_exists("/var/www/docuStore/pdfs") )
				mkdir("pdfs", 0755);
 
			if ( !file_exists("/var/www/docuStore/pdfToTxt") )
				mkdir("pdfToTxt", 0755);
 
			//check if the file already exists in the pdfs folder (dupe)
			if ( file_exists("/var/www/docuStore/pdfs/$upFile") )
				$fileExistError = "<br>File exists. Try renaming your file<br>";
			else
			{
				$error = 0;
 
				//upload file
				if ( !move_uploaded_file($_FILES['file']['tmp_name'], "/var/www/docuStore/$upFile") )
				{
					$fileMoveError = "<br>Error moving file<br>";
					$error++;
				}
				else
				{
					$tName = substr($upFile, 0, -4);
					$tName = "$tName.txt";
					system("pdftotext $upFile $tName");
					if ( file_exists($tName) || file_exists($upFile) )
					{
						//figure title, author, and isbn
						// --start isbn
						if ( $_POST['isbn'] )
						{
							$isbn = $_POST['isbn'];
							$isbn = trim($isbn);
							$isbn = htmlentities($isbn);
							$isbn = addslashes($isbn);
							$isbn = str_replace('-','',$isbn);
							if ( strlen($isbn) !== 10 && strlen($isbn) !== 13 )
							{
								$isbnLenError = "<br>Invalid ISBN length. Attempting to find an ISBN in content: ";
								$error++;
							}
 
							for ( $x = 0; $x < strlen($isbn); $x++ )
							{
								if ( !preg_match("/[0-9]/i", $isbn[$x]) )
								{
									$error++;
									$isbnNumberError = "<br>Invalid ISBN. Must contain only numbers<br>";
								}
								if ( ctype_space($isbn[$x]) )
								{
									$error++;
									$isbnCountError = "<br>Invalid ISBN. Only one word<brr>";
								}
							}
						}
 
						if ( empty($_POST['isbn']) || $isbnLenError || $isbnNumberError || $isbnCountError )
						{
							if ( !$isbnLenError )
								$isbnLenError = "<br>Invalid ISBN length. Attempting to find an ISBN in content: ";
							$maxIsbnLen = 17; //counting hyphens
							$isbn = preg_match("/\bisbn\b/i",$content, $match, PREG_OFFSET_CAPTURE); 
							$start = $match[0][1];
							//add the length of the preg search param to $start
							$start = $start + 9; //9 because common structure: isbn-10: or isbn-13: 
							if ( $isbn )
							{
								unset($isbn); //gets rid of the preg_match number: 1
								for ( $q = $start; $q < $start + $maxIsbnLen; $q++ )
								{
									if (  preg_match("/\d/", $content[$q]) )
										$isbn .= $content[$q];
									//if ( $content[$q] == "" || preg_match("/[a-z]/i", $content[$q]) )
									if ( preg_match("/\s|[a-z]/i", $content[$q]) )
										break;
								}
								if ( strlen($isbn) == 10 || strlen($isbn) == 13 )
								{
									$_POST['isbn'] = $isbn;
									$isbnLenError .= "<span style=\"color: yellow\">SUCCESS</span><br>";
									if ( $error > 0 )
										$error--;
								}
								else
								{
									$errorIsbn1 = "<br>Could not figure a proper isbn<br>";
									$isbnLenError .= "<span style=\"color: red\">FAIL</span><br>";
									$error++;
								}
							}
							else
							{
								$isbnLenError .= "<span style=\"color: red\">FAIL</span><br>";
								$errorIsbn2 = "<br>Empty ISBN AND/OR could not find it<br>";
								$error++;
							}
						}
						// --end isbn
						// --start title
						if ( $_POST['title'] )
						{
							$title = $_POST['title'];
							$title = trim($title);
							$title = htmlentities($title);
							$title = addslashes($title);
							$_POST['title'] = $title;
						}
						if ( !$_POST['title'] )
						{
							$_POST['title'] = substr($upFile, 0, -4);
							$title = $_POST['title'];
						}
						if ( empty($_POST['title']) )
						{
							$errorTitle = "<br>Empty title<br>";
							$error++;
						}
						// --end title
						// --start author
						if ( !$_POST['author'] )
						{
							$errorAuthor = "<br> Empty author. Attempting to find an author in content: ";
							$author = preg_match("/\bauthor\b/i",$content, $match, PREG_OFFSET_CAPTURE); 
							$start = $match[0][1];
							$start = $start + 7;  //number of chars in author and space
							$spaces = 0; //count for number of spaces. Most authors give three names, therefore, stop after 3rd space
							if ( $author )
							{
								unset($author);
								for ( $q = $start; $q < $start + 100; $q++ )
								{
									if ( preg_match("/[a-z]/i", $content[$q]) )
										$author .= $content[$q];
									if ( preg_match("/\s/", $content[$q]) )
									{
										$author .= " ";
										$spaces++;
									}
									if ( $spaces == 3 )
										break;
								}
								$_POST['author'] = $author;
								$errorAuthor .= "<span style=\"color: yellow\">SUCCESS</span><br>";
							}
							else
							{
								$errorAuthor .= "<span style=\"color: red\">FAIL</span><br>";
								$error++;
							}
						}
						else
						{
							$author = $_POST['author'];
							$author = trim($author);
							$author = htmlentities($author);
							$author = addslashes($author);
							$_POST['author'] = $author;
						}
						// --end author
 
						//get text box lengths
						// --start text box lengths
						$titleLen = strlen($_POST['title']);
						$isbnLen = strlen($_POST['isbn']);
						$authorLen = strlen($_POST['author']);
						// --end text box lengths
 
						//figure categories 
						// --start categories
						if ( !$_POST['cat'] )
						{
							$catError = "<br>Must choose at least one category<br>";
							$error++;
						}
						else
						{
							$numOfCats = count($_POST['cat'], COUNT_RECURSIVE);
 
							for ( $furBalls = 0; $furBalls < $numOfCats; $furBalls++ )
							{
								if ( $furBalls == 0 )
									$categories .= $_POST['cat'][$furBalls];
								else
									$categories .= ",".$_POST['cat'][$furBalls];
							}
						}
						// --end categories
					}
					else
					{
						$error++;
						$conversionError = "<br>Failed to convert pdf to text<br>";
					}
					$pName = $upFile;
					$tName = substr($tName, 0, -4);
 
					if ( $error == 0 )
					{
						$addInfo = "<br><h2>Title: $title<br>Author: $author<br>ISBN: $isbn<br>Categories: $categories</h2><br>";
						$doubleCheck = "<form action=\"\" method=\"post\">\n<input type=\"submit\" name=\"imSure\" value=\"ADD\">\n<input type=\"hidden\" name=\"title\" value=\"$title\">\n<input type=\"hidden\" name=\"author\" value=\"$author\">\n<input type=\"hidden\" name=\"isbn\" value=\"$isbn\">\n<input type=\"hidden\" name=\"categories\" value=\"$categories\">\n<input type=\"hidden\" name=\"tName\" value=\"$tName\">\n<input type=\"hidden\" name=\"pName\" value=\"$pName\">\n</form>";
					}
					else
					{
						$txt = "*.txt";
						$pdf = "*.pdf";
						array_map('unlink', glob($txt) );
						array_map('unlink', glob($pdf) );
					}
				}
			}
		}
		else
			$fileTypeError = "<br>Incorrect file type".$_FILES["file"]["error"]."<br>";
	}
}
if ( isset($_POST['imSure']) )
{
	$tName = $_POST['tName'];
	$pName = $_POST['pName'];
	$title = $_POST['title'];
	$author = $_POST['author'];
	$isbn = $_POST['isbn'];
	$categories = $_POST['categories'];
 
	db($tName, $pName, $title, $author, $isbn, $categories);
}
 
?>
<html>
<head>
	<title>
		LairTech Docu Store
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIR - Docu Store</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Add</h2>
		<p>
			Here you will be able to upload a pdf to be converted to text and stored in a database. The first field is the file you wish to upload. The second field, title, is what you want it to be labeled as in the database. If left blank it will default to the file name being uploaded. The third field, author, is who wrote the pdf. If left blank it will attempt to find the author within the content of the pdf. The fourth field, isbn, is the isbn of the pdf (if it has one). If this field is also left blank it will attempt to find it within the content of the pdf. The last fields are the categories. Given a checkbox list, check all that apply to the pdf being uploaded. If there are any errors during the upload process they will be displayed within the form field. If no errors, then there will be text above the field listing all the fields you are going to add and a button to add it will appear. After clicking add, there will be a few more lines of text indicating success or failure of the upload to the database.
		</p>
		<?=$addInfo?>
		<?=$doubleCheck?>
		<?=$added?>
		<?=$tableAdded?>
		<?=$conversionError?>
		<form action="" method="post" enctype="multipart/form-data">
		 <fieldset>
		  <legend id="legend">Upload Files Only</legend>
			<?=$fileError?>
			<?=$fileExistError?>
			<?=$fileMoveError?>
			<?=$fileTypeError?>
			<br>
			Upload File: <input type="file" name="file">
			<br><br>
			Title: <input type="text" name="title" size="<?=$titleLen?>" value="<?=$_POST['title']?>">
			<?=$errorTitle?>
			<br><br>
			Author: <input type="text" name="author" size="<?=$authorLen?>" value="<?=$_POST['author']?>">
			<?=$errorAuthor?>
			<br><br>
			ISBN: <input type="text" name="isbn" size="<?=$isbnLen?>" value="<?=$_POST['isbn']?>">
			<?=$isbnNumberError?>
			<?=$isbnLenError?>
			<?=$isbnCountError?>
			<?=$errorIsbn1?>
			<?=$errorIsbn2?>
			<br><br>
			Categories:
			<div id="category" style="width: 400px; height: 100px; overflow: auto">
			<?php
				//  ~~~NO COMMAS IN NAME~~~ --also add item to find.php
				$cats = array( 'programming', 'scripting', 'games', 'system administration', 'theory', 'ai', 'philosophy',
					'science fiction', 'hardware', 'networking', 'software', 'database', 'os', 'proceedings/papers',
					'humor', 'electricity', 'math', 'physics', 'science', 'unix/linux', 'hydroponics', 'web', 'concepts');
 
				$meow = count($cats);
 
				$categories = trim($categories);
				$kittens = explode(',', $categories);
				$kittensCnt = count($kittens);
 
				$matchCnt = 0;
				for ( $x = 0; $x < $kittensCnt; $x++ )
				{
					for ( $y = 0; $y < $meow; $y++ )
					{
						if ( $kittens[$x] == $cats[$y] )
						{
							$matches[$matchCnt] = $y;
							$matchCnt++;
							$kittens[$x] = NULL;
							break;
						}
					}
				}
				unset($kittens);
 
				for ( $dogs = 0; $dogs < $meow; $dogs++ )
				{
					if ( $dogs % 3 == 0 )
						echo "<br><br>";
 
					for ( $catNip = 0; $catNip < count($matches); $catNip++ )
					{
						if ( $dogs == $matches[$catNip] )
						{
							$matches[$catNip] = NULL;
							$punted = "checked";
							break;
						}
					}
					echo "<input type=\"checkbox\" name=\"cat[]\" value=\"$cats[$dogs]\" $punted>$cats[$dogs]\n";
					$punted = NULL;
				}
			?>
			</div>
			<?=$categoryError1?>
			<?=$catError?>
			<br><br>
			<input type="submit" value="Upload" name="upload">
			<a href="/docuStore/add.php" style="border: 2px outset yellow; background-color: green; color: black; float: right">RESET</a>
		 </fieldset>
			<?=$dbCreateError?>
			<?=$dbCreateTableError?>
			<?=$dbSelectError?>
			<?=$dbInsertError?>
			<?=$tableAddedError?>
			<?=$dbShowDeleteError?>
		</form>
	</div>
 
	<div id="footer">
		<h5>LairTech Docu Store | Created: 04/07/11</h5>
	</div>
</body>
</html>

This page is responsible for uploading, converting, and saving of the pdfs. After selecting a file, I use the same method as in img.php. I check the file array for errors using a case structure. If there aren't any errors then I go and check the file type. If it does not match that of a pdf then an error message is created. Otherwise, it goes on to check the extension (a double check to make sure it is what the user claimed it to be). I then save the file's name to a variable so I can access it easier and do a bunch of string replacements which will stop injections. I then check if it exists in the pdfs/ directory. If it does then an error message kicks out. I then move that file to the directory I'm in (in my case /var/www/docuStore/). I then create the text file's name and invoke pdftotext. After that I check the rest of the fields. First up is isbn. I make sanitize it and make sure it is the appropriate lengths and that it only contains numbers. If it is blank then I try to find it within the content of the pdf. I look for the string isbn and if there is a match I grab the numbers until I hit a character or a white space. If an isbn was found, a message is displayed and the isbn box filled in with the discovered isbn. If it was not found a message appears. Next up, title. If a title was provided then it is sanitized. If a title was not supplied, then it takes on the name of the file being uploaded. Next is author. Much like isbn, if left blank it will attempt to find it within the content of the pdf. It searches for author then grabs characters until it hits 3 white spaces (a typical name has 3 parts: first, middle, last). Otherwise, if one is provided, it merely sanitizes it. Then, for pure aesthetics, it figures the lengths of all the fields and makes the text boxes of the form the size of each entry. The categories are handled with string concatenation and adding commas (also used for delimiting). I count how many categories were picked then add each category to a string. Along the way, I was checking for errors and keeping track if any were triggered by having a count. Therefore, if the count is greater than zero, there was an error. However, if it is zero, then I display a new form that only gives the information you provided and a button to add it to the directories and the database. I then check to see if the user clicked this new button. If you did, I grab the text file name, pdf file name, title, author, isbn, and categories then pass all those fields to the function called db (standing for database).

Finally, it is time to add to the database. I'm using mysql, and to connect to a database you need the server, user, password, and database name. I am the server so I set $server to 'localhost'. I used root as the user so user is set to 'root' and root does not have a password (look at the top of this section to learn how to add one) so password is set to ” “, and finally the database name, I set this to pdfsToTxt. After every mysql command I check if it was successful or not. In which case I either die() with an error message or I trigger an error message. It depends on how severe I thought the error to be. Such as connecting to the database. I figured it should die if it cannot connect. I then try to create the database if it does not exist. After that, I select my database (pdfsToTxt). I then create a table. The columns of this table are: id which is an integer, primary key (pk), auto incremented and not null. The second column is title which is a varchar up to 50 and is not null. The third column is isbn which is a varchar up to 13 and is not null. The fourth column is content which is mediumtext and not null. The mediumtext allows 16,777,215 characters. The fifth and last column is category which is text and not null. The text allows 65,535 characters (tinytext may be more preferable, but I wanted room for future additions to categories). I then insert all those fields as well as the content grabbed by file_get_contents php function in the beginning of this function to the table (myPdfs) in the database (pdfToTxt).

Del.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//error_reporting(E_ALL); //debugging
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"350\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
//this cookie will be tested by the cookie page so that page knows what to do
@setcookie("from", "del");
//unset secret cookie
@setcookie("secret", "fail");
 
$ipAddress = $_SERVER['SERVER_ADDR'];
 
if ( !empty($_POST['remove']) )
{
	/* Debugging
	echo "<pre>";
		print_r($_POST['tables']);
	echo "</pre>";
	*/
	//remove from sql database
	$server = 'localhost';
	$user = 'root';
	$database = 'pdfsToTxt';
	$db = mysql_connect($server, $user, $password, $database);
	if (!$db)
		die("Could Not Connect To Database".mysql_error());
	$result = mysql_select_db($database, $db);
	if (!$result)
	{
		$dbSelectError = "<br>Error Selecting Database<br>";
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	$cnt = count($_POST['tables']);
	$tables = $_POST['tables'];
	for ( $x = 0; $x < $cnt; $x++ )
	{
		$titleRes = mysql_query("select title from myPdfs where id = '$tables[$x]'");
		$title = mysql_fetch_assoc($titleRes);
		$result = mysql_query("delete from myPdfs where id = '$tables[$x]'");
		if (!$result)
		{
			$dbShowDeleteError = "<br>Error deleting table(s)<br>";
			//echo mysql_error(); //debugging
		}
		$result = NULL;
		//remove from dirs: pdfs/ && pdfToTxt/
		unlink("/var/www/docuStore/pdfs/".$title['title'].".pdf");
		unlink("/var/www/docuStore/pdfToTxt/".$title['title'].".txt");
	}
	mysql_close($db);
}
?>
<html>
<head>
	<title>
		LairTech Docu Store
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIR - Docu Store</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Remove</h2>
		<p>
			Here you will be able to remove a table from the database (text document from pdf). Simply select the one(s) you would like to remove and click remove. This will drop the tables from the database as well as remove them from the pdfs/ and pdfToTxt/ directories.
		</p>
 
		<form action="" method="post">
		<?php
			$server = 'localhost';
			$user = 'root';
			$database = 'pdfsToTxt';
			$db = mysql_connect($server, $user, $password, $database);
			if (!$db)
				die("Could Not Connect To Database".mysql_error());
			mysql_select_db($database);
			$result = mysql_query("select id, title from myPdfs");
			if (!$result)
			{
				$dbShowTableError = "<br>Error showing titles<br>";
				//echo mysql_error(); //debugging
			}
			$cnt = 0;
			echo "Table(s) To Remove: \n";
			echo "<table style=\"text-align: center\">\n<tr>\n";
			while ( $row = mysql_fetch_assoc($result) )
			{
				if ( $cnt % 3 !== 0 )
					echo "<td><input type=\"checkbox\" name=\"tables[]\" value=\"".$row['id']."\"><br>".$row['title']."</td>\n";
				else
					echo "\n</tr>\n<tr>\n<td><input type=\"checkbox\" name=\"tables[]\" value=\"".$row['id']."\"><br>".$row['title']."</td>\n";
 
				$cnt++;
			}
			echo "</tr>\n</table>\n";
			mysql_free_result($result);
			mysql_close($db);
		?>
			<input type="submit" value="Remove" name="remove">
			<?=$dbShowTableError?>
			<?=$dbSelectError?>
			<?=$dbShowDeleteError?>
		</form>
	</div>
 
	<div id="footer">
		<h5>LairTech Docu Store | Created: 04/07/11</h5>
	</div>
</body>
</html>

This page is a checkbox list of all the pdfs in the database. All you have to do is check the ones you want to remove and click remove.

Skip all the way down to the html and look at how I populate the checkbox list (around line 152 if in jEdit). I connect to the database, and issue a select command in mysql. I then hop into a while loop and fetch each row (titles and ids). Within this loop I am creating a table. Note the $cnt % 3 !== 0. The '%' (percent) sign is called the modulus or mod operator. It looks at the remainder after division. So, if there is a remainder after dividing $cnt by 3, then I am not on a number that is a multiple of 3. What this is going to do is create a table that is 3 elements (td –table data) wide. But, the key thing to note here is the name of each checkbox: name=“tables[]”. This will create an array within the $_POST array so it can hold everything that has been checked. Without the '[]' (square brackets) it would just overwrite the others if more than one was chosen.

Scroll back up to about line 79 (if in jEdit) and see this is where I start processing the form. I hop into mysql, connect to the database, count how many pdfs were chosen, set the pdf id's to a variable array ($tables). This is where all the magic happens. I issue mysql delete from commands with a where clause using the ids from the $tables variable to locate the correct pdf to remove from the database. With each delete I also issue an unlink command to the pdfs/ and pdfToTxt/ directories to take out the pdf/text files from those directories.

Find.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//error_reporting(E_ALL); //debugging
//unset the secret cookie for uploading images
@setcookie("secret", "fail");
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"350\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
//this cookie will be tested by the cookie page so that page knows what to do
@setcookie("from", "find");
//unset secret cookie
@setcookie("secret", "fail");
 
//used for header redirection
$ipAddress = $_SERVER['SERVER_ADDR'];
 
//create table of titles for each pdf in database and load into array
$server = 'localhost';
$user = 'root';
$database = 'pdfsToTxt';
$password = '';
$db = mysql_connect($server, $user, $password, $database);
if (!$db)
	die("Could Not Connect To Database".mysql_error());
mysql_select_db($database);
$result = mysql_query("select id, title from myPdfs");
if (!$result)
{
	$dbShowTableError = "<br>Error showing tables<br>";
	//echo mysql_error(); //debugging
}
 
$cnt = 0;
$rowNum = 1;
$tableNameTable = "<div id=\"findTable\" style=\"width: 610px; height: 200px; overflow: auto\">\n<br><br>Table(s): \n";
$tableNameTable .= "<table border=\"1\" cellpadding=\"3\">\n<tr>\n";
while ( $row = mysql_fetch_assoc($result) )
{
	if ( $cnt % 3 !== 0 )
		$tableNameTable .= "<td>".$row['title']."</td>\n";
	else
	{
		$tableNameTable .= "\n</tr>\n<tr>\n<td>$rowNum</td>\n<td>".$row['title']."</td>\n";
		$rowNum++;
	}
	//load names into array
	$tableNames[$cnt] = $row['title'];
	//load ids into array
	$tableIds[$cnt] = $rowp['id'];
 
	$cnt++;
}
$tableNameTable .= "</tr>\n</table>\n</div>\n";
mysql_free_result($result);
mysql_close($db);
 
function found($tableName, $content, $search)
{
 
	//set if you want highlighting on or off (1/0) respectively
	$highlighter = 1;
	//set how to search (bounded or not bounded)
	$boundSearch = 0;
	if ( isset($_POST['check1']) )
		$boundSearch = $_POST['check1'];
	//set how many matches you want to display
	//$maxMatches = 3;
	$maxMatches = $_POST['displayMatches'];
	//set how many sentences you want
	//$numOfSentences = 2;
	$numOfSentences = $_POST['numOfSentences'];
	//reset $sentence
	$sentence = NULL;
 
	if ( $boundSearch )
	{
		//look for match --\b{}\b is a word bound for exact match
		preg_match_all("/\b{$search}\b/i", $content, $matches, PREG_OFFSET_CAPTURE);
	}
	else
		preg_match_all("/$search/i", $content, $matches, PREG_OFFSET_CAPTURE);
	//below is for debugging
	/*
	echo "<pre>";
	print_r($matches);
	echo "</pre>";
	*/
	//total number including array names
	$numOfMatches = count($matches, COUNT_RECURSIVE);
	$actualNumOfMatches = 0;
	$checkIndex = 0; //make sure we are on the index of offset capture
	foreach ( $matches as $key )
	{
		foreach ( $key as $val )
		{
			foreach ( $val as $stuff )
			{
				$checkIndex++;
				if ( $checkIndex == 2 )
					if ( is_int($stuff) ) 
					{
						$here[$actualNumOfMatches] = $stuff;
						$actualNumOfMatches++;
					}
			}
			$checkIndex = 0;
		}
	}
	//below is for debugging
	/*
	echo "<pre>";
	print_r($here);
	echo "</pre>";
	echo "<br>numOfMatches: $numOfMatches<br>";
	echo "<br>actualNumOfMatches: $actualNumOfMatches<br>";
	*/
	//if too many results use $maxMatches
	if ( $actualNumOfMatches > $maxMatches )
		$matches = $maxMatches;
	else
		$matches = $actualNumOfMatches;
	for ( $z = 0; $z < $matches; $z++ )
	{
	$a = $z; //used for outputting current number of match
	$a++;
	//look for beginning of sentence
	//first find strpos of current location
	$foo = stripos($content, $search, $here[$z]);
 
	//look for period
	for ( $x = $foo; $x > 0; $x-- )
	{
		if ( $content[$x] == '.' || $content[$x] == '!' || $content[$x] == '?' )
		{
			$begin = $x;
			break;
		}
	}
	/*
	works but I like the one above more
	for ( $x = $foo; $x > 0; $x-- )
	{
		$period = substr($content, $x, 1);
		if ( $period == "." || $period == '!' || $period == '?')
		{
			$begin = $x;
			break;
		}
	}
	*/
 
	//dont display the period
	$begin++;
	//move ahead amount of $numOfSentences
	$words = strlen($content);
	$cnt = 0;
	for ( $x = $foo; $x < $words; $x++ )
	{
		if ( $content[$x] == '.' || $content[$x] == '!' || $content[$x] == '?' )
			$cnt++;
		if ( $cnt == $numOfSentences )
		{
			$end = $x;
			break;
		}
	} 
	//add one to $end to include period
	$end++;
	//assign sentence
	$sentence .= "<br><span style=\"color: #f30\"><a href=\"pdfs/$tableName\">$tableName</a> <a href=\"pdfToTxt/$tableName\" style=\"font-size: .7em\">[txt]</a> - $a OF $actualNumOfMatches matches</span><br>";
	if ( $highlighter )
	{
		//size of search string --char count
		$sizeOfSearch = strlen($search);
 
		//find the difference between $foo (beginning of search string) and $begin
		$difference = $foo - $begin;
 
		//store the sentence up to the search string
		$first = substr($content, $begin, $difference);
		$startHighlighter = "<span style=\"color: black; background-color: yellow\">";
		$stopHighlighter = "</span>";
		//find where the search string ends
		$highlight = substr($content, $foo, $sizeOfSearch);
		//exclude the search string (dupes it otherwise)
		$start = $foo + $sizeOfSearch;
		//find the difference between $start and $end
		$difference = $end - $start;
		$last = substr($content, $start, $difference);
		$sentence .= $first.$startHighlighter.$highlight.$stopHighlighter.$last;
	}
	else
	{
		/* below method works but I like the new one more --$difference
		for ( $x = $begin; $x < $end; $x++ )
			$sentence .= $content[$x];
		*/
		//find end of sentence string
		$difference = $end - $begin;
 
		//store the sentence up to the search string
		$sentence .= substr($content, $begin, $difference);
		//below used for debugging
		/*
		echo "<br>$sentence<br>";
		*/
	}
	$sentence .= "<br>";
	}
	return $sentence;
}
 
	if ( isset($_POST['searchTitle']) )
	{
		if ( empty($_POST['searchTitleStr']) )
			$searchTitleError1 = "<br>Search by title is empty<br>";
		else
		{
			$error = 0;
 
			$_POST['searchTitleStr'] = trim($_POST['searchTitleStr']);
			$_POST['searchTitleStr'] = htmlentities($_POST['searchTitleStr']);
			$_POST['searchTitleStr'] = addslashes($_POST['searchTitleStr']);
			$searchTitleStr = $_POST['searchTitleStr'];
 
			//check word count
			$maxTitleCnt = 10;
			$maxTitleLen = 50;
 
			$wordCnt = count(explode(' ', $_POST['searchTitleStr']));
			$len = strlen($_POST['searchTitleStr']);
 
			if ( $wordCnt > $maxTitleCnt )
			{
				$titleWordCntError = "<br>Too many words in title search<br>";
				$error++;
			}
 
			if ( $len > $maxTitleLen )
			{
				$titleLenError = "<br>Too many characters in title search<br>";
				$error++;
			}
 
			if ( $error == 0 )
			{
				$server = 'localhost';
				$user = 'root';
				$database = 'pdfsToTxt';
				$db = mysql_connect($server, $user, $password, $database);
				if (!$db)
					die("Could Not Connect To Database".mysql_error());
				$result = mysql_select_db($database, $db);
				if (!$result)
				{
					 $dbSelectError = "<br>Error Selecting Database<br>";
					 //die(mysql_error()); //debugging
				}
				else
				{
					$exactTitleMatches = 0;
					$possibleTitleMatches = 0;
					$result = mysql_query("select title from myPdfs");
					if ( $result )
					{
						$x = 0;
						while ( $row = mysql_fetch_assoc($result) )
						{
							$titles[$x] = $row['title'];
							$x++;
						}
 
						$y = 0;
						foreach ( $tableNames as $tableName )
						{
							if ( preg_match("/^$searchTitleStr$/i", $titles[$y]) )
							{
								$exactTitleSuccess .= "<a href=\"pdfs/$tableName\">$tableName</a>  <a href=\"pdfToTxt/$tableName\" style=\"font-size: .7em\">[txt]</a><br>";
								$exactTitleMatches++;
							}
							elseif ( preg_match("/[$searchTitleStr]/i", $titles[$y]) )
							{
								$titleSuccess .= "<a href=\"pdfs/$tableName\">$tableName</a>  <a href=\"pdfToTxt/$tableName\" style=\"font-size: .7em\">[txt]</a><br>";
								$possibleTitleMatches++;
							}
							elseif ( preg_match("/^$searchTitleStr[0]/i", $titles[$y]) )
							{
								$titleSuccess .= "<a href=\"pdfs/$tableName\">$tableName</a>  <a href=\"pdfToTxt/$tableName\" style=\"font-size: .7em\">[txt]</a><br>";
								$possibleTitleMatches++;
							}
							$y++;
						}
					}
				}
				mysql_close($db);
 
				if ( $exactTitleMatches > 0 || $possibleTitleMatches > 0 )
				{
					$totTitleMatches = $exactTitleMatches + $possibleTitleMatches;
					$titleSuccess = "Total Title Matches: $totTitleMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">Exact Title Matches: $exactTitleMatches<br>$exactTitleSuccess Possible Title Matches: $possibleTitleMatches<br>$titleSuccess</div>";
				}
				else
					$titleError = "<br>Could not find your title<br>";
			}
		}
 
	}
 
	//start processing search by category
	if ( isset($_POST['catSearch']) )
	{
		if ( empty($_POST['cat']) )
			$categoryError1 = "<br>Please select at least one category<br>";
		else
		{
			$error = 0;
			$numOfCats = count($_POST['cat'], COUNT_RECURSIVE);
			//see if they chose and/or/not
			$logic = $_POST['catParam'];
			//connect to db
			$server = 'localhost';
			$user = 'root';
			$database = 'pdfsToTxt';
			$password = '';
			$db = mysql_connect($server, $user, $password, $database);
			if (!$db)
				die("Could Not Connect To Database");
			$result = mysql_select_db($database, $db);
			if (!$result)
			{
				 $dbSelectError = "<br>Error Selecting Database<br>";
				 //die(mysql_error()); //debugging
			}
			$result = NULL;
 
			$result = mysql_query("select title, category from myPdfs") or die("Failed to select category");
			$totMatches = 0;
			$categories = NULL;
			$t = 0;
			while ( $row = mysql_fetch_assoc($result) )
			{
				$_POST['cat'][$t] = trim(addslashes($_POST['cat'][$t]));
				$row['category'] = trim(addslashes($row['category']));
				$categories = $row['category'];
				$t++;
				$table = $row['title'];
				$match = 0;
				$cat = explode(',', $categories);
				for ( $a = 0; $a < count($cat); $a++ )
				{
					for ( $x = 0; $x < $numOfCats; $x++ )
					{
						if ( $cat[$a] == $_POST['cat'][$x] )
						{
							$match++;
							break;
						}
					}
				}
 
				if ( $logic == "AND" )
				{
					if ( $match == $numOfCats )
					{
						$andSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a><br>";
						$totMatches++;
					}
				}
				elseif ( $logic == "OR" )
				{
					if ( $match > 0 )
					{
						$orSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a><br>";
						$totMatches++;
					}
				}
				else
				{
					if ( $match == 0 )
					{
						$notSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a><br>";
						$totMatches++;
					}
				}
			}
			mysql_close($db);
			if ( $logic == "AND" )
			{
				if ( isset($andSuccess) )
					$andSuccess = "Category Matches (AND): $totMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">".$andSuccess."</div><br>";
				else
					$andError = "<br>Could not find any matching tables with your categories (AND)<br>";
			}
			elseif ( $logic == "OR" )
			{
				if ( isset($orSuccess) )
					$orSuccess = "Category Matches (OR): $totMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">".$orSuccess."</div><br>";
				else
					$orError = "<br>Could not find any matching tables with your categories (OR)<br>";
			}
			elseif ( $logic == "NOT" )
			{
				if ( isset($notSuccess) )
					$notSuccess = "Category Matches (NOT): $totMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">".$notSuccess."</div><br>";
				else
					$notError = "<br>Could not find any matching tables with your categories (NOT)<br>";
			}
		}
	}
 
	if ( $_POST['searchAuthor'] )
	{
		$error = 0;
		$maxAuthorLen = 50;
		$maxAuthorCnt = 3;
 
		$totMatches = 0;
		$exactMatches = 0;
 
		$author = $_POST['searchAuthorStr'];
		$author = trim($author);
		$author = htmlentities($author);
		$author = addslashes($author);
		$author = str_replace('#','',$author);
 
		if ( strlen($author) > $maxAuthorLen )
		{
			$error++;
			$authorLenError = "<br>Author search is too long<br>";
		}
 
		if ( count(explode(' ', $author)) > $maxAuthorCnt )
		{
			$error++;
			$authorCntError = "<br>Author search has too many words<br>";
		}
 
		if ( empty($author) )
		{
			$error++;
			$authorEmptyError = "<br>Author search is empty<br>";
		}
 
		if ( $error == 0 )
		{
			$server = 'localhost';
			$user = 'root';
			$database = 'pdfsToTxt';
			$password = '';
			$db = mysql_connect($server, $user, $password, $database);
			if (!$db)
				die("Could Not Connect To Database");
			$result = mysql_select_db($database, $db);
			if (!$result)
			{
				 $dbSelectError = "<br>Error Selecting Database<br>";
				 //die(mysql_error()); //debugging
			}
			$result = NULL;
				$result = mysql_query("select title, author from myPdfs where author like '%$searchAuthorStr%'");
				if ( $result )
				{
					while ( $row = mysql_fetch_assoc($result) )
					{
						$table = $row['title'];
						$tableAuthor = $row['author'];
						//exact search
						if ( preg_match("/^$author$/i", $tableAuthor) )
						{
							$exactMatches++;
							$authorExactSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a><br>";
						}
						elseif ( preg_match("/[$author]/i", $tableAuthor) )
						{
							$totMatches++;
							$authorSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a> -- $tableAuthor<br>";
						}
						elseif ( preg_match("/^$author[0]/i", $tableAuthor) )
						{
							$totMatches++;
							$authorSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a> -- $tableAuthor<br>";
						}
						$tableAuthor = NULL;
					}
				}
			mysql_close($db);
 
			if ( isset($authorSuccess) || isset($authorExactSuccess) )
			{
				$totMatches = $totMatches + $exactMatches;
				$possibleAuthorMatches = $totMatches - $exactMatches;
				$authorSuccess = "<br>Total Author Matches: $totMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">"."Exact Author Matches: $exactMatches<br>".$authorExactSuccess."Possible Author Matches: $possibleAuthorMatches<br>".$authorSuccess."</div><br>";
			}
			else
				$authorError = "<br>Could not find any matching tables with your author<br>";
		}
	}
 
	if ( isset($_POST['searchIsbn']) )
	{
		$error = 0;
 
		$isbn = $_POST['searchIsbnStr'];
		$isbn = trim($isbn);
		$isbn = htmlentities($isbn);
		$isbn = addslashes($isbn);
		$isbn = str_replace('-','',$isbn);
 
		if ( strlen($isbn) !== 10 && strlen($isbn) !== 13 )
		{
			$isbnLenError = "<br>Invalid ISBN. Must be 10 OR 13 characters long";
			$error++;
		}
		for ( $x = 0; $x < strlen($isbn); $x++ )
		{
			if ( !preg_match("/[0-9]/i", $isbn[$x]) )
			{
				$error++;
				$isbnNumberError = "<br>Invalid ISBN. Must contain only numbers";
			}
			if ( ctype_space($isbn[$x]) )
			{
				$error++;
				$isbnCountError = "<br>Invalid ISBN. Only one word<br>";
			}
		}
 
		if ( $error == 0 )
		{
			$totMatches = 0;
 
			$server = 'localhost';
			$user = 'root';
			$database = 'pdfsToTxt';
			$password = '';
			$db = mysql_connect($server, $user, $password, $database);
			if (!$db)
				die("Could Not Connect To Database");
			$result = mysql_select_db($database, $db);
			if (!$result)
			{
				 $dbSelectError = "<br>Error Selecting Database<br>";
				 //die(mysql_error()); //debugging
			}
			$result = NULL;
			$result = mysql_query("select title, isbn from myPdfs where isbn like '%$isbn%'");
			if ( $result )
			{
				while ( $row = mysql_fetch_assoc($result) )
				{
					$table = $row['title'];
					$ISBN = $row['isbn'];
					if ( preg_match("/^$isbn$/i", $ISBN) )
					{
						$totMatches++;
						$isbnSuccess .= "<a href=\"pdfs/$table\">$table</a>  <a href=\"pdfToTxt/$table\" style=\"font-size: .7em\">[txt]</a><br>";
					}
				}
			}
			mysql_close($db);
 
			if ( $totMatches > 0 )
				$isbnSuccess = "<br>Total ISBN Matches: $totMatches<br><div style=\"width: 610px; height: 100px; overflow: auto\">$isbnSuccess</div><br>";
			else
				$isbnError = "<br>Could not find your ISBN<br>";
		}
	}
 
	//check if they chose to read a pdf text file
	if ( isset($_POST['readTableName']) )
	{
		//create a file to hold the table to be read
		$tableNameMatches = $_POST['tableNameMatch'];
		$server = 'localhost';
		$user = 'root';
		$database = 'pdfsToTxt';
		$password = '';
		$db = mysql_connect($server, $user, $password, $database);
		if (!$db)
			die("Could Not Connect To Database");
		$result = mysql_select_db($database, $db);
		if (!$result)
		{
			 $dbSelectError = "<br>Error Selecting Database<br>";
			 //die(mysql_error()); //debugging
		}
		$result = NULL;
			$result = mysql_query("select id from myPdfs where title = '$tableNameMatches[0]' limit 1") or die("Failed to select id -readFile");
		$id = mysql_fetch_assoc($result);
		mysql_free_result($result);
		mysql_close($result);
		$fh = fopen('readFile', 'w') or die("failed to open: readFile");
		fwrite($fh, $id['id']) or die("failed to write: readFile");
		fclose($fh);
		//use this header if in lair
		@header("Location: http://$ipAddress/docuStore/read.php");
		//else use this header if tunneling --change it to meet your needs
		//@header("Location: http://localhost:8081/docuStore/read.php");
	}
	//check if they chose to download the text of the pdf
	if ( isset($_POST['downloadTableName']) )
	{
		//create a file to hold the table to be downloaded
		$tableNameMatches = $_POST['tableNameMatch'];
		$server = 'localhost';
		$user = 'root';
		$database = 'pdfsToTxt';
		$password = '';
		$db = mysql_connect($server, $user, $password, $database);
		if (!$db)
			die("Could Not Connect To Database");
		$result = mysql_select_db($database, $db);
		if (!$result)
		{
			 $dbSelectError = "<br>Error Selecting Database<br>";
			 //die(mysql_error()); //debugging
		}
		$result = NULL;
			$result = mysql_query("select id from myPdfs where title = '$tableNameMatches[0]' limit 1") or die("Failed to select id -downFile");
		$id = mysql_fetch_assoc($result);
		mysql_free_result($result);
		mysql_close($result);
		$fh = fopen('downFile', 'w') or die("failed to open: downFile");
		fwrite($fh, $id['id']) or die("failed to write: downFile");
		fclose($fh);
		//use this header if in lair
		@header("Location: http://$ipAddress/docuStore/read.php");
		//else use this header if tunneling --change it to meet your needs
		//@header("Location: http://localhost:8081/docuStore/read.php");
	}
?>
<html>
<head>
	<title>
		LairTech Docu Store
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIR - Docu Store</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Search</h2>
		<p>
			Here you can search through content, titles, isbn, authors, and categories to find a specific pdf in your database. Results will have links to pdf/txt.
		</p>
 
		<form action="" method="post">
		<fieldset>
			<legend id="legend">Search By Table Name/Content</legend>
			Settings:
			<br>
			<input type="checkbox" name="check1" value="1" <?php if (isset($_POST['check1'])) echo "checked";?>> Exact Search 
			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Matches
			<select name="displayMatches">
				<option value="1">1</option>
				<option value="2">2</option>
				<option value="3" selected="selected">3</option>
				<option value="4">4</option>
				<option value="5">5</option>
			</select>
			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sentences
			<select name="numOfSentences">
				<option value="1">1</option>
				<option value="2" selected="selected">2</option>
				<option value="3">3</option>
				<option value="4">4</option>
				<option value="5">5</option>
			</select>
 
			<br><br>
			Search Content and Table Name:
			<br><input type="text" size="30" name="searchStr" value="<?=$_POST['searchStr']?>">
			<input type="submit" value="Search" name="searchTableName">
		</fieldset>
			<br><br>
		<fieldset>
			<legend id="legend">Search By Title</legend>
			Search Title:
			<br>
			<input type="text" size="30" name="searchTitleStr" value="<?=$_POST['searchTitleStr']?>">
			<input type="submit" value="Search" name="searchTitle">
			<?=$searchTitleError1?>
			<?=$titleWordCntError?>
			<?=$titleLenError?>
			<?=$titleError?>
		</fieldset>
			<?=$titleSuccess?>
			<br><br>
		<fieldset>
			<legend id="legend">Search By Author</legend>
			Search Author:
			<br>
			<input type="text" size="30" name="searchAuthorStr" value="<?=$_POST['searchAuthorStr']?>">
			<input type="submit" value="Search" name="searchAuthor">
			<?=$authorLenError?>
			<?=$authorCntError?>
			<?=$authorEmptyError?>
			<?=$authorError?>
		</fieldset>
			<?=$authorSuccess?>
			<br><br>
		<fieldset>
			<legend id="legend">Search By ISBN</legend>
			Search ISBN:
			<br>
			<input type="text" size="30" name="searchIsbnStr" value="<?=$_POST['searchIsbnStr']?>">
			<input type="submit" value="Search" name="searchIsbn">
			<?=$isbnLenError?>
			<?=$isbnNumberError?>
			<?=$isbnCountError?>
			<?=$isbnError?>
		</fieldset>
			<?=$isbnSuccess?>
			<br><br>
		<fieldset>
			<legend id="legend">Search By Categories</legend>
			Search Categories: <select name='catParam'>
						<option selected="selected">AND</option>
						<option>OR</option>
						<option>NOT</option>
					</select>
			<div id="category" style="width: 400px; height: 100px; overflow: auto">
			<?php
				//  ~~~NO COMMAS IN NAME~~~  --also add item to add.php
				$cats = array( 'programming', 'scripting', 'games', 'system administration', 'theory', 'ai', 'philosophy',
					'science fiction', 'hardware', 'networking', 'software', 'database', 'os', 'proceedings/papers',
					'humor', 'electricity', 'math', 'physics', 'science', 'unix/linux', 'hydroponics', 'web', 'concepts');
 
				$meow = count($cats);
 
				$categories = NULL;
				if ( isset($_POST['cat']) )
					foreach ( $_POST['cat'] as $kitty )
						$categories .= $kitty.',';
				$categories = trim($categories);
				$kittens = explode(',', $categories);
				$kittensCnt = count($kittens);
 
				$matchCnt = 0;
				for ( $x = 0; $x < $kittensCnt; $x++ )
				{
					for ( $y = 0; $y < $meow; $y++ )
					{
						if ( $kittens[$x] == $cats[$y] )
						{
							$matches[$matchCnt] = $y;
							$matchCnt++;
							$kittens[$x] = NULL;
							break;
						}
					}
				}
				unset($kittens);
 
				for ( $dogs = 0; $dogs < $meow; $dogs++ )
				{
					if ( $dogs % 3 == 0 )
						echo "<br><br>";
 
					for ( $catNip = 0; $catNip < count($matches); $catNip++ )
					{
						if ( $dogs == $matches[$catNip] )
						{
							$matches[$catNip] = NULL;
							$punted = "checked";
							break;
						}
					}
					echo "<input type=\"checkbox\" name=\"cat[]\" value=\"$cats[$dogs]\" $punted>$cats[$dogs]\n";
					$punted = NULL;
				}
			?>
			</div>
			<input type="submit" value="Search" name="catSearch">
			<?=$categoryError1?>
			<?=$andError?>
			<?=$orError?>
			<?=$notError?>
		</fieldset>
			<?=$andSuccess?>
			<?=$orSuccess?>
			<?=$notSuccess?>
			<br><br>
			<a href="/docuStore/find.php" style="border: 2px outset yellow; background-color: green; color: black;">RESET</a>
		<?php
			if ( $_POST['searchTableName'] )
			{
				if ( strlen($_POST['searchStr']) > 0 )
				{
					$search = $_POST['searchStr'];
					$search = addslashes($search);
					$search = htmlentities($search);
					$words = explode(" ", $search);
					$wordCnt = count($words);
					$wordCntMax = 51;
					$tableNameCnt = count($tableNames);
					$tableNameMatchCnt = 0;
					$dupe = 0;
					if ( $wordCnt < $wordCntMax )
					{
						//begin looking for table name matches
						$numTableNames = count($tableNames);
						for ( $x = 0; $x < $wordCnt; $x++ )
						{
							for ( $i = 0; $i < $tableNameCnt; $i++ )
							{
								if ( $words[$x] == $tableNames[$i] )
								{
									//initial add
									if ( $tableNameMatchCnt == 0 )
									{
										$tableNameMatch[$tableNameMatchCnt] = $tableNames[$i];
										$tableNameMatchCnt++;
									}
									//check to see if match is already in array
									for ( $z = 0; $z < $tableNameMatchCnt; $z++ )
									{
										if ( $words[$x] == $tableNameMatch[$z] )
											$dupe++;
									}
									if ( $dupe == 0 )
									{
										$tableNameMatch[$tableNameMatchCnt] = $tableNames[$i];
										$tableNameMatchCnt++;
									}
								}
								$dupe = 0;
							}
						}
 
						//see if there were matches
						if ( $tableNameMatchCnt == 0 )
						{
							//no table name matches
							$noTableNameMatches = "<br>Your search did not match any of the title's<br>";
						}
						else
						{
							$tableNameMatchStr = "<form action=\"\" method=\"post\"><fieldset><legend>Search Table Name Results:</legend>";
							//table name matche(s)
							$tableNameMatchStr .= "<br>Your matching table names:<br>";
							for ( $x = 0; $x < $tableNameMatchCnt; $x++ )
								$tableNameMatchStr .= "<input type=\"radio\" name=\"tableNameMatch[]\" value=\"$tableNameMatch[$x]\">$tableNameMatch[$x]<br>";
							$tableNameMatchStr .= "<br><input type=\"submit\" value=\"Read\" name=\"readTableName\">";
							$tableNameMatchStr .= "<input type=\"submit\" value=\"Download\" name=\"downloadTableName\">";
							$tableNameMatchStr .= "</fieldset></form>";
						}
						//lets search through every table in the database for the search string
						$searchStrFound = 0;
						$dupe = 0;
						$server = 'localhost';
						$user = 'root';
						$database = 'pdfsToTxt';
						$db = mysql_connect($server, $user, $password, $database);
						if (!$db)
							die("Could Not Connect To Database".mysql_error());
					 	$result = mysql_select_db($database, $db);
						if (!$result)
						{
							 $dbSelectError = "<br>Error Selecting Database<br>";
							 //die(mysql_error()); //debugging
						}
						$result = NULL;
 
						foreach ( $tableNames as $tableName )
						{
							$result = mysql_query("select content from myPdfs where content like '%$search%' and title like '$tableName'");
							if ( $result )
							{
								$content = NULL;
								//load table content to var
								while ( $row = mysql_fetch_assoc($result) )
									$content .= $row['content'];
								//preg_match for search string
								$found = preg_match("/$search/i", $content);
								if ( $found )
								{
									//initial load
									if ( $searchStrFound == 0 )
									{
										$searchStrTables[$searchStrFound] = $tableName;
										$searchStrFound++;
										$sentence = found($tableName, $content, $search);
									}
									else
									{
										//check for dupe table name
										for ( $x = 0; $x < $searchStrFound; $x++ )
										{
											if ( $searchStrTables[$x] == $tableName )
												$dupe++;
										}
										//load table name if not a dupe
										if ( $dupe == 0 )
										{
											$searchStrTables[$searchStrFound] = $tableName;
											$searchStrFound++;
											$sentence .= found($tableName, $content, $search);
										}
										$dupe = 0;
									}
								}
							}
							else //nothing found reset $result just in case
								$result = NULL;
						}
						mysql_close($db);
						//check if searchStr was found in any of the tables
						if ( $searchStrFound > 0 )
						{
							$tableContentMatchStr = "<form action=\"\" method=\"post\"><fieldset><legend>Search Content Results:</legend>";
							//table name matche(s)
							$tableContentMatchStr .= "<br>Your matching table names:<br>";
							for ( $x = 0; $x < $searchStrFound; $x++ )
								$tableContentMatchStr .= "<input type=\"radio\" name=\"tableNameMatch[]\" value=\"$searchStrTables[$x]\">$searchStrTables[$x]<br>";
							$tableContentMatchStr .= "<br><input type=\"submit\" value=\"Read\" name=\"readTableName\">";
							$tableContentMatchStr .= "<input type=\"submit\" value=\"Download\" name=\"downloadTableName\">";
							$tableContentMatchStr .= "</fieldset></form>";
							$searchStrFound = 0;
						}
						else
							$noSearchContentFound = "<br>Search string did not match any content within any of the tables<br>";
					}
					else
					{
						$searchWordCntError = "<br>Search string is too long. Max words: $wordCntMax<br";
					}
				}	
				else
					$emptySearchError = "<br>Search string is empty<br>";
			}
		?>
			<?=$tableNameMatchStr?>
			<?=$tableContentMatchStr?>
			<?=$noSearchContentFound?>
			<?=$tableNameTable?>
			<?=$searchContentError?>
			<?=$noTableNameMatches?>
			<?=$emptySearchError?>
			<?=$searchWordCntError?>
			<?=$dbShowTableError?>
			<?=$dbSelectError?>
			<?=$dbSelectTableError?>
			<?=$sentence?>
		</form>
	</div>
 
	<div id="footer">
		<h5>LairTech Docu Store | Created: 04/07/11</h5>
	</div>
</body>
</html>

This page is Nuckin Futs! First you will notice the settings section. Here you can choose to search exactly what you entered (as in it will be bounded by your search. Ie: search string: add. Exact search: Add. Not exact search: Add, additions, etc). Then there is a select box for how many results to display. Finally, there is another select box for how many sentences to display. Underneath the settings is the text field for you to enter your search string. Simply click 'Search' and it's off. Now, most reset buttons only work when you have not submitted the form. However, this button will work even if you have (because I just reload the page). Finally, there is a table which holds all of the pdfs in the database (this table as well as all of the results are held in a div with overflow set to auto. This way, when the content surpasses the alotted height, it will scroll, but only in the box (div). Now, onto the code.

Scroll all the way down to line 834 (if in jEdit). It starts with: if ( $_POST['searchTableName'] ). This is where the first form gets processed. The first thing to check is if the user actually entered something. If not, then create an error message and display it. Otherwise, move on to sanitizing it. The first thing I did was addslashes. This will escape any special character such as quotes. Then, I run htmlentities on it which will convert all applicable characters to html entities. Doing this will protect against injection. The next thing I do is separate the search string into individual words by utilizing explode and using ” “ (blank space) as the delimiter. I then count the words to make sure you didn't enter a string that was passed my max word count ($wordCntMax). This was my attempt from someone trying to dump a load of crap into it and bogging down the system. So, if you are under the word count lets get started. I first loop through all of your words and see if they match any of the pdf's names (titles in the database). I initially add the name into an array at index zero and increment the variable tracking the number of matches ($tableNameMatchCnt). From there I check to make sure that no other match is added into the array which matches one already in it (No duplications added this way). I then check if $tableNameMatchCnt is zero. If it is, then no table name matches were found. Create a message to display that will say this. Otherwise, there were matches, create a form so the user can read and download it. If either of these buttons are clicked it will create a file depending on the one you clicked (readFile and downFile respectively). These files hold the id of the file that you checked. More on this later. So, now it is time to check the content of the tables (content field) within the database. Connect to the database and loop through all the content (using a foreach and adding an additional like clause, I only get the content per title (pdf)). The $tableNames array was figured around line 49 (if in jEdit). This was when I was creating the table to display all of the titles (pdfs) in the database. So, for each element in that array issue a mysql query “select content from $tableName where content like %$search% and title like '$tableName'. The like clause will search through the content field and look for a match of the search string ($search) and the second like clause will make it only take the content from the row with that title. If there is a match I then select the content from that table and loop through it and piece it together row by row in one variable called $content. I then double check the like clause by issuing a preg_match on it. If that is also true then I save that table name in an array (and like before, I check for duplications and when it is finally done it will output a radio list with read and download. More on this later). So, we have a search string match within the content of a pdf. Call the function found and pass it $tableName (title), $content (the content field from the table in the database (the text from the pdf)), and $search (the search string entered by the user).

Scroll all the way up to line 88 (if in jEdit) and this is where the found function is. This is also where the settings come into play. The first setting (which is also always on, but can be turned off if you have access to the code (just set the setting variables to 0)) is $highlighter which is a neat little bit of code I thought would be nice to have. Like the variable says, highlighter, it highlights the search string in the results. The next thing you see is $boundSearch. This variable is used to control if the search should be exact or not. The next variable is $maxMatches which is also set like the one before by the user. This controls how many matches are displayed. By default, 3 are shown no matter what unless the user chooses otherwise. Also note, that this setting is not saved if changed. It will default back to 3. Finally you will see $numOfSentences. Like the two mentioned before, this is set by the user but like the previous one, it has a default that will not be saved if altered. It is defaulted to 2. The next thing you will see is how to search through the $content. Either exact search or not. Notice the flag at the end: PREG_OFFSET_CAPTURE. This will store not only the word(s) it found, but also the beginning location of that string. I then count how many matches were found by recursively counting the $matches variable (from preg_match_all). Again, note the flag, COUNT_RECURSIVE. This is required to count a multidimensional array. However, I only want to count the number of offsets discovered. So, using three nested foreach loops, I can get inside of the $matches array, check to make sure I'm on the second value in the inner most index of the $matches array and keep track of how many times I hit each spot like this. I then use this count to see if there are too many results to display at once by comparing my count variable ($actualNumOfMatches) to $maxMatches. If the actual number of matches is greater than the allowed matches then only display the number of matches up to the allowed amount, otherwise, display all of the actual matches. It is now time to figure out the sentences around the matching string in the $content. Using stripos (note the i for case insensitivity) I start looking backwards until I hit an end of line character such as a period (.), question mark (?), or an exclamation mark (!). And I save this position in $begin which is the beginning of the sentence. I then loop forward through the sentence looking for the same end of line characters and I count how many times I hit one until I need to stop which is governed by $numOfSentences. So, now I know exactly where to begin my sentence and where to stop. If $highlighter is on (1) I will highlight the text. First I have to save the sentence up to the beginning of the search string in a variable. Then I have to change the styles from the beginning of the search string to the end and save that in another variable. Finally, I have to figure out the position from the end of the search string to the end of the sentence(s) before I can apply the styles. So now I have it all broken apart. After all of that, using string concatenation (the period '.') I glue all of the sentence pieces back together to form one piece. Once the sentence is computed and stored in $sentence, $sentence is returned. $sentence keeps getting added onto for every match. Also note, that each sentence starts with two links, the number of results being displayed out of the total number of results. The first link is a link to the pdf. If you have a web pdf viewer it will open it up right there. If not, it will ask to download it. The next link is a link to the text. It will simply just output the text. The next number is the number of the result you are on. The last number is the number of total results found.

Coming back to some of the stuff I said I would talk about later. The forms that are created if there is a match in table name and/or table content. There is a radio button list for both. If you click one and click one of the buttons (read or download) it will redirect you to the page that knows how to handle that. Say you click on read, go to line 501 (if in jEdit) and see that it will create a file called readFile and store the id of the pdf inside of it. It then redirects you to read.php. Say you clicked on download, go to line 532 (if in jEdit). Just like readFile it will create a file but this one is called downFile and it again, stores the id of the pdf in this file and redirects to read.php.

The next method of searching is by title. Scroll to about line 244 (if in jEdit) and this is where it begins to be processed. Every input box is sanitized with trim, htmlentities, and addslashes. I first check the lengths and word count to make sure it does not go over my limit. If it passes both then continue on. Connect to the database and query for every title. Then, compare that title to an exact match, a partial match, or a beginning character match. The exact match has its own count while the other two have the same count and are grouped together. A partial match is where the title has at least one word you entered. The beginning character match is every title that matches the first character you entered. The following methods of search use the same method: author, and isbn (except isbn only matches exact).

The category search is awesome. You have three choices. The first is and. This means, whatever categories you check, the pdf must have all of them (programming AND scripting AND networking). The second choice is or. This means, whatever categories you check, the pdf must have at least one of them (programming OR scripting OR networking). The third and last choice is not. This means, whatever categories you check, the pdf must not have any of them (programming AND NOT scripting AND NOT networking). This setting is not saved, so, consecutive searches on anything other than and, will need to be set to what you want every time (and is default). The checks however, are saved. This explanation pretty much sums up the code without getting too technical. But, to get a little more in depth on the process. Count how many categories were checked. Connect to the database. Select all of the categories per title (pdf), explode the category string by using the ',' (comma) as the delimiter and check for matches. Check which setting the user chose (and, or, not) and see how the matches compare. If and, matches should be equal to the number of categories checked. If or, matches should be greater than one. If not, matches should be equal to zero.

Read.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<?php
//error_reporting(E_ALL); //debugging
//just for fun lets display some pictures in the empty space on the right side of the page
 
//first get total count of images in rtColImgs/
//  and load their names into an array
$dir = "rtColImgs";
$dh = opendir($dir);
$totImgs = 0;
while ( ( $file = readdir($dh) ) !== false )
{
	$ext = pathinfo( $file, PATHINFO_EXTENSION);
	$ext = strtolower($ext);
	if ( $ext == 'jpg' || $ext == 'jpeg' || $ext == 'png' || $ext == 'gif' )
	{
		$imgArr[$totImgs] = "$file";
		$totImgs++;
	}
}
closedir($dh);
 
function random_image($imgArr)
{
	return $x = rand(0, count($imgArr)-1);
}
 
$imgIndex = random_image($imgArr);
@setcookie("imgIndex", $imgIndex);
while ( $_COOKIE['imgIndex'] == $imgIndex )
{
	$imgIndex = random_image($imgArr);
	@setcookie("imgIndex", $imgIndex);
}
 
$img = "<img src=\"rtColImgs/$imgArr[$imgIndex]\" width=\"350\" height=\"350\" id=\"homeImg\" alt=\"$imgArr[$imgIndex]\">";
 
//this cookie will be tested by the cookie page so that page knows what to do
@setcookie("from", "read");
//unset secret cookie
@setcookie("secret", "fail");
 
$ipAddress = $_SERVER['SERVER_ADDR'];
$maxDisplay = 10;	//how many rows to display in table
 
//used if coming from search
if ( file_exists("/var/www/docuStore/downFile") )
{
	$table = file_get_contents("/var/www/docuStore/downFile");
	unlink("/var/www/docuStore/downFile");
	//read file from database
	$server = 'localhost';
	$user = 'root';
	$database = 'pdfsToTxt';
	$db = mysql_connect($server, $user, $password, $database);
	if (!$db)
		die("Could Not Connect To Database".mysql_error());
	$result = mysql_select_db($database, $db);
	if (!$result)
	{
		$dbSelectError = "<br>Error Selecting Database<br>";
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	$result = mysql_query("select title, content from myPdfs where id = '$table'", $db);
	if (!$result)
	{
		$dbSelectTableError = "<br>Error selecting content from table<br>";
		//echo mysql_error(); //debugging
	}
	while ( $row = mysql_fetch_assoc($result))
	{
		$content .= "{$row['content']}";
		$table = $row['title'];
		//echo "{$row['content']}";
	}
	mysql_close($db);
 
	$filename = $table.".txt";
	@header("Content-type: text/plain");
	@header("Content-disposition: attachment; filename=$filename");
	@header("Pragma: no-cache");
	@header("Expires: 0");
	echo $content;
	die();
}
 
//used if coming from search
if ( file_exists("/var/www/docuStore/readFile") )
{
	$table = file_get_contents("/var/www/docuStore/readFile");
	unlink("/var/www/docuStore/readFile");
	//read file from database
	$server = 'localhost';
	$user = 'root';
	$database = 'pdfsToTxt';
	$db = mysql_connect($server, $user, $password, $database);
	if (!$db)
		die("Could Not Connect To Database".mysql_error());
	$result = mysql_select_db($database, $db);
	if (!$result)
	{
		$dbSelectError = "<br>Error Selecting Database<br>";
		//die(mysql_error()); //debugging
	}
	$result = NULL;
	$result = mysql_query("select content from myPdfs where id = '$table'", $db);
	if (!$result)
	{
		$dbSelectTableError = "<br>Error selecting content from table<br>";
		//echo mysql_error(); //debugging
	}
	$content = "<div id=\"pdfTxt\"><p>";
	while ( $row = mysql_fetch_assoc($result))
	{
		$content .= "<br>{$row['content']}<br>";
		//echo "{$row['content']}";
	}
	mysql_close($db);
	$content .= "</p></div>";
}
?>
<html>
<head>
	<title>
		LairTech Docu Store
	</title>
 
	<link rel="stylesheet" type="text/css" href="styles.css">
	<?php
		echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";
	?>
</head>
<body>
	<div id="header">
		<h1>LAIR - Docu Store</h1>	
	</div>
 
	<div id="nav">
		<ul>
			<li><a href="index.php">Home</a></li>
			<li><a href="add.php">Add</a></li>
			<li><a href="del.php">Remove</a></li>
			<li><a href="find.php">Search</a></li>
			<li><a href="read.php">Read/Download</a></li>
		</ul>
	</div>
	<?=$img ?>
	<div id="content">
		<h2 id="welcome">Read/Download</h2>
		<p>
			This page will give a link list of every pdf in the database. You may simply click one and read/download it.
		</p>
 
		<?php echo $content?>
 
		<?php
			$server = 'localhost';
			$user = 'root';
			$database = 'pdfsToTxt';
			$db = mysql_connect($server, $user, $password, $database);
			if (!$db)
				die("Could Not Connect To Database".mysql_error());
			mysql_select_db($database);
			$result = mysql_query("select title from myPdfs");
			if (!$result)
			{
				$dbShowTableError = "<br>Error showing tables<br>";
				//echo mysql_error(); //debugging
			}
			$nameIndex = 0;
			//load all table names into array first
			while ( $row = mysql_fetch_row($result) )
			{
				$names[$nameIndex] = "{$row[0]}";
				$nameIndex++;
			}
			mysql_free_result($result);
			mysql_close($db);
 
			if ( $nameIndex > $maxDisplay )
			{
				$curDisplay = $maxDisplay;
				$next = "<form action=\"\" method=\"post\">\n<input type=\"submit\" value=\"Next\" name=\"next\" style=\"float: right\">\n</form>\n";
			}
			else
				$curDisplay = $nameIndex;
			$c = 0;
			for ( $a = 0; $a < $nameIndex; $a++ )
			{
				$div[$a] = "<br><br>Table(s): $curDisplay Of $nameIndex\n";
				$div[$a] .= "<table cellpadding=\"5\" border=\"1\">\n";
				for ( $b = 0; $b < $maxDisplay; $b++ )
				{
					$div[$a] .= "<tr><td><a href=\"pdfToTxt/$names[$c]\">Text: <span style=\"font-size: .8em\">$names[$c]</span></a><br><a href=\"pdfs/$names[$b]\">PDF: <span style=\"font-size: .8em\">$names[$c]</span></a></td></tr>\n";
					$c++;
					if ( $c == $nameIndex )
					{
						$stop = 1;
						break;
					}
				}
				$div[$a] .= "</table>\n";
				if ( $stop )
					break;
				$curDisplay = $curDisplay + $maxDisplay;
				if ( $curDisplay > $nameIndex )
					$curDisplay = $nameIndex;
			}
 
			if ( !$_POST['next'] && !$_POST['prev'] )
			{
				@setcookie("divIndex", 0);
				$divIndex = 0;
				echo $div[$divIndex];
			}
 
			if ( $_POST['next'] )
			{
				$divIndex = $_COOKIE['divIndex'];
				$divIndex++;
				@setcookie("divIndex", $divIndex);
				if ( $divIndex >= $a )
				{
					$next = NULL;
					$divIndex = $a;
					@setcookie("divIndex", $divIndex);
				}
				$prev = "<form action=\"\" method=\"post\">\n<input type=\"submit\" value=\"Previous\" name=\"prev\" style=\"float: left\">\n</form>\n";
				echo $div[$divIndex];
			}
 
			if ( $_POST['prev'] )
			{
				$prev = "<form action=\"\" method=\"post\">\n<input type=\"submit\" value=\"Previous\" name=\"prev\" style=\"float: left\">\n</form>\n";
				$divIndex = $_COOKIE['divIndex'];
				$divIndex--;
				@setcookie("divIndex", $divIndex);
				if ( $divIndex <= 0 )
				{
					$prev = NULL;
					$divIndex = 0;
					@setcookie("divIndex", $divIndex);
				}
				$next = "<form action=\"\" method=\"post\">\n<input type=\"submit\" value=\"Next\" name=\"next\" style=\"float: right\">\n</form>\n";
				echo $div[$divIndex];
			}
		?>
			<?=$dbShowTableError?>
			<?=$dbSelectError?>
			<?=$dbSelectTableError?>
			<?=$prev?>
			<?=$next?>
	</div>
 
	<div id="footer">
		<h5>LairTech Docu Store | Created: 04/07/11</h5>
	</div>
</body>
</html>

This page contains a table of all the pdfs in the database. There are links to each pdf and text file.

Scroll down to line 157 (if in jEdit). This is where I create the table. First thing I do is connect to the database, query it to show the titles, then save all of the titles in an array and close my connection to the database. I have $maxDisplay set to 6 which is how many will show on one page. If the count from the table name array ($nameIndex) is greater than the $maxDisplay allowed, I set two variables. One is for the number I will currently display and the other is a form which will be used to view the next batch. Then I hop into a for loop where I begin creating the tables in an array. Again, I only want to show 3 elements (td –table data) wide, so using the mod operator '%', this can be achieved. Take note of the difference of text for the $div. I then check to see if I have hit the maximum count of names in the array and kick out, otherwise it would over extend the bounds. I then calculate the $curDisplay which is the number of results being displayed. I add $curdisplay to $maxDisplay. I then compare if that number is greater than $nameIndex (the count of all the tables) and if it is, I then set $curDisplay to $nameIndex because it is less than or equal to the amount I am able to display next. If applicable $next will be printed to the user. That means there are more tables than $maxDisplay, so another page must be computed to show the rest. There is also $prev but I will talk about that later. For now, I check to see if $next and $prev have not been clicked, this means to just display the first batch of table links ($div[0]) and set a cookie. The cookie: divIndex with a value of $divIndex (initially 0).

If $next is applicable (there are more tables than $maxDisplay. Then this button will appear. Scroll to line 220 (if in jEdit) and you will see that it grabs the index from the cookie and increments it by one. This means it will show the next batch of tables. A quick check to see if $divIndex is equal to The total count of elements in $div ($a). If that is the case, then it will set the cookie, divIndex, to the value of $a as well as the divIndex to $a, so this way if you hit refresh, it wont keep going, it will cap at the end bound of the $div array. It also unsets $next. Now, if you clicked next, then you should be able to go back, that is what $prev is for. The next paragraph will cover what $prev does.

If $prev is applicable (user clicked next) then scroll to line 235 (if in jEdit). Here $prev is set again, so if the user clicked next more than once it will continue to show up until you cant go back any further. Like above, it will grab the divIndex from the cookie but this time it decrements so that it can show the previous table in the $div array. A quick check to see if $divIndex is equal to 0 to make sure you don't go passed the lower bound of the $div array. If it is 0, then it will unset the $prev variable so that it wont show up, and it sets the cookie, divIndex, to 0 so that if you refresh it wont try to go passed the lower bounds of the $div array. It also re-instantiates $next.

Scroll up to the top to line 47 (if in jEdit). This is where I test if the user came from search.php and they clicked download for one of the pdfs. A quick check to make sure downFile exists and it's off. First it stores the id from the file with file_get_contents. From there it removes the file it just used and connects to the database. Once connected it gets the content from the content field and stores it line by line in $content and grabs the title which is stored in $table. The previous two fields are found with a where clause using the id. Then using headers it downloads the file. First, tell the browser it's a text file, then tell it that you are going to send something to the user, then tell it not to cache what you're sending (this way you can download it over and over in one session), and tell it to expire immediately. Finally, echoing $content will give the content to the browser that it needs to send. Calling die will stop it from outputting to the browser as well.

Scroll to line 88 (if in jEdit). This is where I test if the user came from search.php and they clicked read for one of the pdfs. A quick check to make sure readFile exists and it's off. First it stores the id from the file by using file_get_contents. From there it removes the file it just used and connects to the database. Once connected it gets the content from the content field and stores it line by line in $content. The previous fields are found by using a where clause with the id. This is where it is different from above. It prefixes $content with html to make it css friendly so that my css file (styles.css) can manipulate it. It also suffixes $content with html to close up what it prefixed it with. $content is then put inline of the html towards the bottom.

Styles.css


a:link
{
	color: #0f0;
}
 
a:visited
{
	color: #f0c;
}
 
a:hover
{
	text-decoration: none;
}
 
#header
{
	background: url("images/titleBar.jpg");
	color: #369;
	font-variant: small-caps;
	height: 70px;
	text-align: center;
	padding-top: 1px;
}
 
#nav
{
	background-color: black;
	width: 580px;
}
 
#nav li
{
	display: inline;
	list-style: none;
	background-color: #000;
	width: 200px;
	padding: 5px;
}
 
h2
{
	font-variant: small-caps;
	color: #f30;
}
 
p
{
	padding-left: 25px;
}
 
body
{
	font-family: sans-serif;
	background: url("images/background.jpg");
	background-attachment: fixed;
	color: #3cf;
}
 
#homeImg
{
	float: right;
}
 
#content
{
	background-color: #000;
	width: 500px;
	padding-left: 10px;
}
 
#pdfTxt
{
	border-top: dashed #f30 3px;
	border-bottom: dashed #f30 3px;
	background-color: #000;
	width: 950px;
	padding-left: 5px;
	padding-right: 5px;
}
 
#footer
{
	background: url("images/footerBar.jpg");
	color: #369;
	height: 50px;
	text-align: center;
	padding-top: 1px;
	clear: both;
	position: relative;
	bottom: 0px;
	width: 100%;
	/*width: 98%*/
	/*left: 10px;*/
}

This is the css (cascading style sheet) which controls the styling for the whole website.

References:


Good Reference Material And Notes:
http://www.howtoforge.com/ubuntu_debian_lamp_server
http://www.cyberciti.biz/faq/converter-pdf-files-to-text-format-command/
Well enough permissions for web: chmod 755
Notice:
<?php

	echo "<link rel=\"shortcut icon\" href=\"http://$ipAddress/docuStore/favicon.ico\">";\\

?>

I've learned (thanks to prof. dj from ccc) that browser will sit there and continually ask for a favicon. This obviously, will create unnecessary server load. So I created a favicon real quick and added this line in between the \<head\> tags.

The last thing you have to do is give apache ownership of the directory it is serving.

  • command: sudo chown -r www-data:www-data /var/www


http://www.howstuffworks.com/web-server.htm

Preparation: Walmart Payroll Monitor: paymon

Author: Jesse Short

Objective:


Automate the monitoring of my payroll at walmart. Using some kind of tool or programming language to make your own, automate the process of logging into walmart's website, traverse your way to the payroll, save the file and parse it for the fields that interest you. Then, email your information to yourself (for email alerts on my phone).

Prerequisites:


You will need a working web server with something like apache or lampp running. The use of curl is a possibility. Setup the service of your choice to allow curl. Create a web-based front end to set up the cron script that will get your information. Knowledge of html, css, php, curl, and possibly java script is required. Firefox is the best browser to do this in. Using Live Headers you can track what information is being sent from client → server and server → client.

Background:


Curl allows you to connect and communicate to many different types of servers with many different types of protocols. libcurl currently supports the http, https, ftp, gopher, telnet, dict, file, and ldap protocols. libcurl also supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading (this can also be done with PHP's ftp extension), HTTP form based upload, proxies, cookies, and user+password authentication. - Found Here

Procedure:


Php's libcurl was difficult to work with. I attempted some things, but it was tricky and clunky. Php.net is a very handy source for everything php related. I was also told about the linux command line curl, which I had much more success with, but nothing finished.

Theoretic Steps:

  • Create a simple php form page to get the users login info (user name and password)
  • Save the form contents in a secure place
  • Get curl to log into the walmart website
  • Get curl to answer the security question
  • Get curl to save the payroll file
  • Get php or bash to parse the payroll file and look for specific fields
  • Get bash to email it to your most used email address
  • Create the cron script to do this automatically every payday

References:


http://www.php.net
http://www.php.net/manual/en/intro.curl.php

Preparation: Lab46 Email Monitor: mailmon

Author: Jesse Short

Objective:


Set up a script in your language of preference (such as bash) for cron to automate the watch of your lab46 email account and forward a message to your commonly used email (for phone alert). Include as much information about the email on your lab46 account in the forwarded email. Things such as From and Subject must at least be included. Extra will include the message body itself in the forwarded email, and even a way to either delete or mark read for any emails on your lab46 account by replying to it from your commonly used email address.

Prerequisites:


Knowledge of the lab46 email client (pine). Knowledge of how email works (smtp, pop, imap, etc). Knowledge of how cron works. An email account outside of lab46.

Background:


Emails are sent from the client computer to a server via protocol. One protocol to accomplish this is pop3 or post office protocol 3. Emails are sent from server to server via a protocol called smtp or simple mail transfer protocol. More Here

Procedure:


Theoretic Steps:

  • Using bash as the language create a script to dump the contents of your lab46 email account to a file
  • Search through that file to look for new mail
  • Create an email with the Sender/Subject information in the body
  • Send that email to an email account outside of lab46
  • Only send that email if there is new mail BUT, do not send an email if nothing new came in from the previous alert(s)

Extra Theoretic Steps:

  • Get the new email message body and put them in the body of the email to be sent out
  • Look for specific subject titles from specific person and do what that subject indicates. Ie: From: jesse_shrt@yahoo.com Subject: Delete: Just saying hi From: Bob Saget

References:


http://www.washington.edu/pine/
http://communication.howstuffworks.com/email.htm
http://www.writersservices.com/www/t_email_goes_wrong.htm
http://communication.howstuffworks.com/email.htm