======= 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] - F12: boot up menu - Integrated NIC - 32-bit (i386) - Debian/i386 Netboot - Install Lenny/stable [text] - language: English - Country: United States - Keyboard layout: American English - Host Name: Unicorn - Domain Name: offbyone.lan - Debian archive mirror hostname: mirror (for this scroll to the top and enter manually) - Use default settings and continue until you can select a time zome. - Time zone: Eastern - Partitioning Method: Guided - use entire disk - 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. - Root password: password (pretty much the best pw out there, no one would ever think of this!) - verify pw - Full name for the new user: Jesse Short - User account name: ender - User account password: god (Pretty much the second best pw out there, for the same reason stated above) - verify pw - Participate in package usage survey?: No (can change this later on by running: "dpkg-reconfigurepopularity-contest") - 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) - Install GRUB boot loader to master boot record: Yes - Continue Now it will boot up a login screen. Enter the created user account name earlier (ender) and then the password (god). - 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. - 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 ====== - F12 - Integrated NIC - 32-bit (i368) - Ubuntu/i386 Netboot - Install Jaunty "9.04" [text] - language: English - Country: United States - Detect keyboard layout: no - Origin of keyboard: USA - keyboard layout: USA - Hostname: Unicorn - mirror: Go to the top and enter manually: mirror - continue - continue - Time Zone: Eastern - Partitioning Method: Guided - use entire disk - In our case: SCSI1 (0,0,0) (sda) - 40GB ATA WDC WD400BB-75FJ - write changes to disk: Yes - User name: Jesse Short - Username: ender - User password: god - Verificate - In our case: it's a weak pw and it yelled at me, but I continued anyway :) - Encrypt home directory: no - Manage upgrades: no automatic updates - Choose software to install: Ubuntu desktop - Is system clock set to UTC: Yes - Continue - log in: ender - pw: god Should be a pop-up about drivers - click on it and enter your password. Let it install, close when done, and restart. - Check display settings: System -> Preferences -> Display -> Yes -> X Screen 0 - 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) - f12: boot up menu - Integrated NIC - 32 bit - Debian/i386 Netboot - lenny/stable [text] - language: English - territory: United States - key map: American English - hostname: vmserver01 - domain name: student.lab - mirror: scroll up to enter manually: mirror - continue - continue - time zone: eastern - partitioning method: use entire disk - select a hard drive - all files in one partition - finish - yes - root password: bob - verificate - full name of user: RHINO - user name: rhino - user password: bob - verificate - participate in survey: no - software to install: none (uncheck everything) - Install GRUB boot loader: yes - continue - restart computer (automatic) - log in: root pw: bob - command: cd /etc/apt - command: wget http://10.80.2.6/files/student/sources.list - command: aptitude update - command: aptitude upgrade (not required but a good habit to do after updating) - command: aptitude install openssh-client openssh-server - 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 - aptitude install xen-linux-system-2.6.26-2-xen-686 and xen-tools - continue: y - command: vi/etc/modules (to add loop support module) * max_loop=255 network-script network-bridge setting in xend-config.sxp - command: cd /etc/xen - command: vi xend-config.sxp (uncomment: (network-script network-bridge) comment: (network-script network-dummy) (# = comment) ensure vif-script is vif-bridge, same file - command: cd /etc/xen - 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) - command: cd /boot/grub - command: vi menu.lst - Go to the bottom and remove the second and third entries (in our case). - 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: - command: cd /etc/xen-tools/ - vi xen-tools.conf * Make the adjustments specified at: http://lab46.corning-cc.edu/haas/hpc0/projects/vm_server_install Create the following directories: /xen, /xen/images, /xen/boot, /xen/conf, /xen/save - command: mkdir and the paths specified above FINAL PROCESS:\\ command: reboot =======Creating Virtual Machine======= ssh root@vmserver01.student.lab - network-script network-bridge setting in xend-config.sxp (uncommented in vm server install; make sure there is no space before that line) - 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)) - Enter new password: bob - verificate * log file created: /var/log/xen-tools/vm07.log - xm create -c /xen/conf/vm07.cfg - login: root - 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 - from vmserver01 -> command: xm shutdown vm09 - xm list to verificate - command: cd /xen/domains/vm09 - command: dd if=/dev/zero of=disk1.img bs=1M count=1024 - repeat previous step 5 times BUT replacing disk with an incremented count up to 5 - mkfs.ext3 -v disk1.img (formats disk) - proceed (for previous step) - cd /xen/conf - vi vm09.cfg - 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 - xm create -c /xen/conf/vm09.cfg (- -force if overwriting an existing vm) - aptitude search mdadm - aptitude install mdadm (if discovered via the search from the previous step, this one can be skipped) - yes (for previous step) * RAID (redundant array of inexpensive disks) information: http://en.wikipedia.org/wiki/RAID#RAID_0 * mdadm commands for raiding: http://www.sanitarium.net/golug/Linux_Software_RAID.html Raid 0 (striped raid) - mdadm - -create - -verbose /dev/md0 - -level=0 - -raid-device=5 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7 - cat /proc/mdstat - mkfs.ext3 -v /dev/md0 (formats md0 found from previous step) - command: mount /dev/md0 /mnt * for details: * command: df or df -h or mdadm - -detail /dev/md0 Raid 1 (mirroring) - mdadm - -create /dev/md0 - -level=1 - -raid-devices=4 - -spare-devices=1 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7 - cat /proc/mdstat - mkfs.ext3 -v /dev/md0 - mount /dev/md0 /mnt Raid 5 - mdadm - -create - -verbose /dev/md0 - -chunk=64 - -level=5 - -raid-devices=5 /dev/xvda3 /dev/xvda4 /dev/xvda5 /dev/xvda6 /dev/xvda7 - mkfs.ext3 -v /dev/md0 (formats md0 found from previous step) - 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\\ * Good info here: http://www.sucka.net/2009/08/how-to-install-ltsp-on-ubuntu-9-04/ - eth1 (the second networking card: dhcp) - sudo apt-get install dhcp3-server Don't have to do this - sudo vi /etc/dhcp3/dhcpd.conf -sudo (super user do: makes a normal user God) - 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 - cd /etc/default/ - sudo vi dhcp3-server - change: INTERFACES="" ... TO ... INTERFACES="eth1" - sudo vi /etc/network/default/interfaces - uncomment iface eth0 inet dhcp - 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) - 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 - reboot computer - 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. - sudo apt-get install openssh-server - sudo apt-get install ltsp-server-standalone - Run LTSP build client - command: sudo ltsp-build-client - -mirror "http://mirror/ubuntu" (had to give it the mirror because it yelled without it) - In case of having to repeat the previous step make sure to - command: rm -rf /opt/ltsp/i386/ -before you rerun the command - 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; - Command: sudo vi /etc/default/tftp-hpa * Change: RUN DEAMON="no" ... to ... RUN DEAMON="yes" - 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 ====== - Open a Terminal * Right click -> terminal - basic command: xwit -root -warp # # (# is user's choice of any number within the pixel range of the monitor; col - row) - For more info - command: man xte OR http://titotheman.wordpress.com/2010/01/16/generate-fake-keyboard-and-mouse-input-in-linux/ - 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 - In case if server is not up: - ssh root@vmserver01.student.lab - pw: bob - command: xm list - command: xm create -c /xen/conf/vm07.cfg - login: root - pw: bob - detach: ^] - ssh root@vm07.student.lab - pw - command: aptitude update - command: aptitude install vnc-server - command: aptitude install vnc4server - command: aptitude install xautomation xwit rxvt fluxbox xpaint - command: vnc4server -geometry 1280x1024 - pw: bobbob - verificate - open putty - click: load lab46 - Category: ssh -> tunnels - check: local ports accept connections from other hosts - source port: 5901 - Destination: vm07.student.lab:5901 - add - Category: session -> save - Internet: download tightvnc portable - Home page: http://www.tightvnc.com/ - Portable: http://www.tightvnc.com/download/tightvnc-portable-1.3.10.exe - extract - run - Host name or IP address: localhost:5901 - connect - 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 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. #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.\\ * You can download it here: http://sourceforge.net/projects/xming/ - Download - Install - Open Putty - Load Lab46 - (Left hand side of box) Go to: SSH; sub-menu: X11 - Click the check box for: Enable X11 Forwarding - 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: * * * used for sleep function * 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 . 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: [[http://lab46.corning-cc.edu/~jshort6/battleship/battleship.cpp|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. - 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 15x15 pixels and one a paddle of 15x100 pixels. Make these pictures have a transparent background and color the objects white. - Download the .ttf (lazy.ttf) font from: http://www.lazyfoo.net/SDL_tutorials/lesson07/index.php - 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 #include #include #include #include #include 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: * wget http://downloads.sourceforge.net/project/owfs/owfs/2.8p2/owfs-2.8p2.tar.gz * tar zxvf owfs-2.8p2.tar.gz && cd owfs-2.8p2 * ./configure --prefix=/usr --enable-owfs --enable-usb * make * sudo checkinstall --pgkname=owfs --pkgversion=owfs-2.8p2 --backup=no --deldoc=yes --default 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. Current Temps"; } $filename = "$path" . "hydroMonDayIndex.html"; if ( file_exists($filename)) { $dayGraph = "Daily Temps"; } $filename = "$path" . "hydroMonMonthIndex.html"; if ( file_exists($filename)) { $monthGraph = "Monthly Temps"; } ?> CCC - lair - Hydroponics lair cam - image

Welcome

  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.

  Plese note, the links above (except for Home and Live Video) 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.

* 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 "\n\n\n$title\n\n\n\n\n\n\n" > 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 "\n\nHydromon's Daily Temperature Reports\n\n\nHome

\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 "\n\n$title\n\n\n\n

\n\n\n" > 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 " Temperature Graph: $title\n

\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 "\n\n$title\n\n\n\n

\n\n\n" > 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 " Temperature Graph: $title\n

\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 "\n" >> 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 "\n\nHydromon's Monthly Temperature Reports\n\n\nHome

\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

\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 " Monthly Temperature Graph: $title\n

\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 "\n" >> 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 "" > 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 ---- "; //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 = "
Incorrect Pass Code
"; } ?> LAIRTechWeb DocuStore XL 9000j "; ?>

Welcome

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.

Password To Upload Images Enter password to upload images for the right column:
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 ---- 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 ---- home 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 = "\"$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
"; break; case 2: $fileError .= " File exceeds max file size
"; break; case 3: $fileError .= " File was only paritally uploaded
"; break; case 4: $fileError .= " File was not uploaded
"; break; case 6: $fileError .= " Missing a temporary folder
"; break; case 7: $fileError .= " Failed to write file to disk
"; 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 = "
File exists. Try renaming your file
"; 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 = "
Error moving file
"; else $fileMoveError = "
File upload success!
"; } } } } ?> LAIRTechWeb DocuStore XL 9000j "; ?>

Upload Images

Upload images for the right column of this website.

Upload Files Only
Upload File:
This page is only used to upload images to the server in the rtColImgs/ directory. There is a simple upload form. Note the \ 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 ---- "; //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 = "
Error Creating Database
"; $error++; //die(mysql_error()); //debugging } $result = NULL; $result = mysql_select_db($database, $db); if (!$result) { global $dbSelectError; $dbSelectError = "
Error Selecting Database
"; $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 = "
Error Creating Table
"; $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 = "
Error Inserting Content
"; $error++; //die(mysql_error()); //debugging } } mysql_close($db); if ( $error == 0 ) { global $tableAdded; $tableAdded = "

$tName added successfully to $database in sql database


"; //move the pdf and txt files to their respective dir's rename("$tName.txt", "pdfToTxt/$tName.txt"); rename("$pName", "pdfs/$pName"); global $added; $added = "

$pName converted to $tName
and stored in pdfs/ and pdfToTxt/


"; } $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
"; break; case 2: $fileError .= " File exceeds max file size
"; break; case 3: $fileError .= " File was only paritally uploaded
"; break; case 4: $fileError .= " File was not uploaded
"; break; case 6: $fileError .= " Missing a temporary folder
"; break; case 7: $fileError .= " Failed to write file to disk
"; 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 = "
File exists. Try renaming your file
"; else { $error = 0; //upload file if ( !move_uploaded_file($_FILES['file']['tmp_name'], "/var/www/docuStore/$upFile") ) { $fileMoveError = "
Error moving file
"; $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 = "
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 = "
Invalid ISBN. Must contain only numbers
"; } if ( ctype_space($isbn[$x]) ) { $error++; $isbnCountError = "
Invalid ISBN. Only one word"; } } } if ( empty($_POST['isbn']) || $isbnLenError || $isbnNumberError || $isbnCountError ) { if ( !$isbnLenError ) $isbnLenError = "
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 .= "SUCCESS
"; if ( $error > 0 ) $error--; } else { $errorIsbn1 = "
Could not figure a proper isbn
"; $isbnLenError .= "FAIL
"; $error++; } } else { $isbnLenError .= "FAIL
"; $errorIsbn2 = "
Empty ISBN AND/OR could not find it
"; $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 = "
Empty title
"; $error++; } // --end title // --start author if ( !$_POST['author'] ) { $errorAuthor = "
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 .= "SUCCESS
"; } else { $errorAuthor .= "FAIL
"; $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 = "
Must choose at least one category
"; $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 = "
Failed to convert pdf to text
"; } $pName = $upFile; $tName = substr($tName, 0, -4); if ( $error == 0 ) { $addInfo = "

Title: $title
Author: $author
ISBN: $isbn
Categories: $categories


"; $doubleCheck = "
\n\n\n\n\n\n\n\n
"; } else { $txt = "*.txt"; $pdf = "*.pdf"; array_map('unlink', glob($txt) ); array_map('unlink', glob($pdf) ); } } } } else $fileTypeError = "
Incorrect file type".$_FILES["file"]["error"]."
"; } } 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); } ?> LairTech Docu Store "; ?>

Add

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.

Upload Files Only
Upload File:

Title:

Author:

ISBN:

Categories:

"; for ( $catNip = 0; $catNip < count($matches); $catNip++ ) { if ( $dogs == $matches[$catNip] ) { $matches[$catNip] = NULL; $punted = "checked"; break; } } echo "$cats[$dogs]\n"; $punted = NULL; } ?>


RESET
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 ---- "; //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 "
";
		print_r($_POST['tables']);
	echo "
"; */ //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 = "
Error Selecting Database
"; //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 = "
Error deleting table(s)
"; //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); } ?> LairTech Docu Store "; ?>

Remove

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.

Error showing titles
"; //echo mysql_error(); //debugging } $cnt = 0; echo "Table(s) To Remove: \n"; echo "\n\n"; while ( $row = mysql_fetch_assoc($result) ) { if ( $cnt % 3 !== 0 ) echo "\n"; else echo "\n\n\n\n"; $cnt++; } echo "\n

".$row['title']."

".$row['title']."
\n"; mysql_free_result($result); mysql_close($db); ?>
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 ---- "; //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 = "
Error showing tables
"; //echo mysql_error(); //debugging } $cnt = 0; $rowNum = 1; $tableNameTable = "
\n

Table(s): \n"; $tableNameTable .= "\n\n"; while ( $row = mysql_fetch_assoc($result) ) { if ( $cnt % 3 !== 0 ) $tableNameTable .= "\n"; else { $tableNameTable .= "\n\n\n\n\n"; $rowNum++; } //load names into array $tableNames[$cnt] = $row['title']; //load ids into array $tableIds[$cnt] = $rowp['id']; $cnt++; } $tableNameTable .= "\n
".$row['title']."
$rowNum".$row['title']."
\n
\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 "
";
	print_r($matches);
	echo "
"; */ //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 "
";
	print_r($here);
	echo "
"; echo "
numOfMatches: $numOfMatches
"; echo "
actualNumOfMatches: $actualNumOfMatches
"; */ //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 .= "
$tableName [txt] - $a OF $actualNumOfMatches matches
"; 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 = ""; $stopHighlighter = ""; //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 "
$sentence
"; */ } $sentence .= "
"; } return $sentence; } if ( isset($_POST['searchTitle']) ) { if ( empty($_POST['searchTitleStr']) ) $searchTitleError1 = "
Search by title is empty
"; 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 = "
Too many words in title search
"; $error++; } if ( $len > $maxTitleLen ) { $titleLenError = "
Too many characters in title search
"; $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 = "
Error Selecting Database
"; //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 .= "$tableName [txt]
"; $exactTitleMatches++; } elseif ( preg_match("/[$searchTitleStr]/i", $titles[$y]) ) { $titleSuccess .= "$tableName [txt]
"; $possibleTitleMatches++; } elseif ( preg_match("/^$searchTitleStr[0]/i", $titles[$y]) ) { $titleSuccess .= "$tableName [txt]
"; $possibleTitleMatches++; } $y++; } } } mysql_close($db); if ( $exactTitleMatches > 0 || $possibleTitleMatches > 0 ) { $totTitleMatches = $exactTitleMatches + $possibleTitleMatches; $titleSuccess = "Total Title Matches: $totTitleMatches
Exact Title Matches: $exactTitleMatches
$exactTitleSuccess Possible Title Matches: $possibleTitleMatches
$titleSuccess
"; } else $titleError = "
Could not find your title
"; } } } //start processing search by category if ( isset($_POST['catSearch']) ) { if ( empty($_POST['cat']) ) $categoryError1 = "
Please select at least one category
"; 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 = "
Error Selecting Database
"; //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 .= "$table [txt]
"; $totMatches++; } } elseif ( $logic == "OR" ) { if ( $match > 0 ) { $orSuccess .= "$table [txt]
"; $totMatches++; } } else { if ( $match == 0 ) { $notSuccess .= "$table [txt]
"; $totMatches++; } } } mysql_close($db); if ( $logic == "AND" ) { if ( isset($andSuccess) ) $andSuccess = "Category Matches (AND): $totMatches
".$andSuccess."

"; else $andError = "
Could not find any matching tables with your categories (AND)
"; } elseif ( $logic == "OR" ) { if ( isset($orSuccess) ) $orSuccess = "Category Matches (OR): $totMatches
".$orSuccess."

"; else $orError = "
Could not find any matching tables with your categories (OR)
"; } elseif ( $logic == "NOT" ) { if ( isset($notSuccess) ) $notSuccess = "Category Matches (NOT): $totMatches
".$notSuccess."

"; else $notError = "
Could not find any matching tables with your categories (NOT)
"; } } } 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 = "
Author search is too long
"; } if ( count(explode(' ', $author)) > $maxAuthorCnt ) { $error++; $authorCntError = "
Author search has too many words
"; } if ( empty($author) ) { $error++; $authorEmptyError = "
Author search is empty
"; } 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 = "
Error Selecting Database
"; //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 .= "$table [txt]
"; } elseif ( preg_match("/[$author]/i", $tableAuthor) ) { $totMatches++; $authorSuccess .= "$table [txt] -- $tableAuthor
"; } elseif ( preg_match("/^$author[0]/i", $tableAuthor) ) { $totMatches++; $authorSuccess .= "$table [txt] -- $tableAuthor
"; } $tableAuthor = NULL; } } mysql_close($db); if ( isset($authorSuccess) || isset($authorExactSuccess) ) { $totMatches = $totMatches + $exactMatches; $possibleAuthorMatches = $totMatches - $exactMatches; $authorSuccess = "
Total Author Matches: $totMatches
"."Exact Author Matches: $exactMatches
".$authorExactSuccess."Possible Author Matches: $possibleAuthorMatches
".$authorSuccess."

"; } else $authorError = "
Could not find any matching tables with your author
"; } } 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 = "
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 = "
Invalid ISBN. Must contain only numbers"; } if ( ctype_space($isbn[$x]) ) { $error++; $isbnCountError = "
Invalid ISBN. Only one word
"; } } 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 = "
Error Selecting Database
"; //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 .= "$table [txt]
"; } } } mysql_close($db); if ( $totMatches > 0 ) $isbnSuccess = "
Total ISBN Matches: $totMatches
$isbnSuccess

"; else $isbnError = "
Could not find your ISBN
"; } } //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 = "
Error Selecting Database
"; //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 = "
Error Selecting Database
"; //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"); } ?> LairTech Docu Store "; ?>

Search

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.

Search By Table Name/Content Settings:
> Exact Search      Matches      Sentences

Search Content and Table Name:


Search By Title Search Title:


Search By Author Search Author:


Search By ISBN Search ISBN:


Search By Categories Search Categories:

"; for ( $catNip = 0; $catNip < count($matches); $catNip++ ) { if ( $dogs == $matches[$catNip] ) { $matches[$catNip] = NULL; $punted = "checked"; break; } } echo "$cats[$dogs]\n"; $punted = NULL; } ?>


RESET 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 = "
Your search did not match any of the title's
"; } else { $tableNameMatchStr = "
Search Table Name Results:"; //table name matche(s) $tableNameMatchStr .= "
Your matching table names:
"; for ( $x = 0; $x < $tableNameMatchCnt; $x++ ) $tableNameMatchStr .= "$tableNameMatch[$x]
"; $tableNameMatchStr .= "
"; $tableNameMatchStr .= ""; $tableNameMatchStr .= "
"; } //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 = "
Error Selecting Database
"; //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 = "
Search Content Results:"; //table name matche(s) $tableContentMatchStr .= "
Your matching table names:
"; for ( $x = 0; $x < $searchStrFound; $x++ ) $tableContentMatchStr .= "$searchStrTables[$x]
"; $tableContentMatchStr .= "
"; $tableContentMatchStr .= ""; $tableContentMatchStr .= "
"; $searchStrFound = 0; } else $noSearchContentFound = "
Search string did not match any content within any of the tables
"; } else { $searchWordCntError = "
Search string is too long. Max words: $wordCntMax
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 ---- "; //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 = "
Error Selecting Database
"; //die(mysql_error()); //debugging } $result = NULL; $result = mysql_query("select title, content from myPdfs where id = '$table'", $db); if (!$result) { $dbSelectTableError = "
Error selecting content from table
"; //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 = "
Error Selecting Database
"; //die(mysql_error()); //debugging } $result = NULL; $result = mysql_query("select content from myPdfs where id = '$table'", $db); if (!$result) { $dbSelectTableError = "
Error selecting content from table
"; //echo mysql_error(); //debugging } $content = "

"; while ( $row = mysql_fetch_assoc($result)) { $content .= "
{$row['content']}
"; //echo "{$row['content']}"; } mysql_close($db); $content .= "

"; } ?> LairTech Docu Store "; ?>

Read/Download

This page will give a link list of every pdf in the database. You may simply click one and read/download it.

Error showing tables
"; //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 = "
\n\n
\n"; } else $curDisplay = $nameIndex; $c = 0; for ( $a = 0; $a < $nameIndex; $a++ ) { $div[$a] = "

Table(s): $curDisplay Of $nameIndex\n"; $div[$a] .= "\n"; for ( $b = 0; $b < $maxDisplay; $b++ ) { $div[$a] .= "\n"; $c++; if ( $c == $nameIndex ) { $stop = 1; break; } } $div[$a] .= "
Text: $names[$c]
PDF: $names[$c]
\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 = "
\n\n
\n"; echo $div[$divIndex]; } if ( $_POST['prev'] ) { $prev = "
\n\n
\n"; $divIndex = $_COOKIE['divIndex']; $divIndex--; @setcookie("divIndex", $divIndex); if ( $divIndex <= 0 ) { $prev = NULL; $divIndex = 0; @setcookie("divIndex", $divIndex); } $next = "
\n\n
\n"; echo $div[$divIndex]; } ?>
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: \\ ";\\ ?>\\ \\ 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 \ 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. -[[http://www.php.net/manual/en/intro.curl.php | 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 [[http://communication.howstuffworks.com/email.htm | 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\\