Browsing articles by " Kay Leacock"

External notification script patch for Observium

I’ve written a patch for Observium 0.12.9.3385 that defines a couple of additional configuration options. When set, these allow Observium to call an additional external script for notifications in addition to the default mail notification method. This is useful for SMS notifications, although it could be used for any notification via a script or command. It expects the script to accept the notification text from STDIN, and respects the per-device notifications-disabled setting on the device configuration tab.

 

The following configuration options are defined:

$config['alerts']['script']['enable'] = TRUE|FALSE;
$config['alerts']['script']['file'] = "/path/to/script-file";

 

external_notifications.patch

*** includes/functions.php.dist 2012-11-23 05:52:27.769189651 +0000
--- includes/functions.php      2012-11-25 10:19:48.309370976 +0000
***************
*** 707,712 ****
--- 707,726 ----
        }
      }
    }
+
+   if ($config['alerts']['script']['enable'] && $config['alerts']['script']['file'] && !get_dev_attrib($device,'disable_notify'))
+   {
+     if (!get_dev_attrib($device,'disable_notify'))
+     {
+       $script = popen($config['alerts']['script']['file'], "w");
+       if($script){
+         fwrite($script, $message."\r\n");
+       }else{
+         echo "Notification Script Error!\n";
+       }
+       pclose($script);
+     }
+   }
  }

  function formatCiscoHardware(&$device, $short = false)

Project Morior

Having recently joined Jenny in an endeavour to create an adventure game, I got to indulge an old hobby of mine that I haven’t done so in quite a while; photography. Armed with a Canon PowerShot S3 and a new tripod, we went out to take some Myst-like shots in and around Portsmouth. It was only the first day of picture-taking, but a good start. What we neglected to consider was the weather.

 

Most of the pictures we got came out nicely despite the rain and torrential winds, but we sure got wet. A few pictures did end up suffering from ‘wet-lens’ though, which can be a cool effect but isn’t quite what we’re going for. We did, however, get some nice 360-degree shots of inside some of the structures of Old Portsmouth that should be pretty good for an adventure game!

Now comes the fun decision; flash, HTML 5/JavaScript or something else altogether… Ah well, to be decided.

Check out the project website if you want to follow development.

WebDAV revisited

In my previous endeavour with WebDAV, I ended up using a complicated lighttpd/apache proxy system to get the permissions right. This did work well enough, but it had a few limitations. In involved modifying the registry on the client systems and it required a new lighty instance running for each share with different permissions to the last (which wasn’t terribly efficient). Additionally, lighty’s mod_webdav lacked support for a few vital operations which made working with the shares a bit difficult, not to mention there were some MIME-type conflicts between lighty/apache/the webDAV client that made working with certain types of files near impossible.

I have since revised some of the methods I’ve used to implement the WebDAV server. For a start, I managed to resolve the XP SP2/Vista/7 basic vs DIGEST authentication method hacks to not need registry editing – it turns out, by default, if the share is SSL encrypted, basic authentication should be fine. I also decided on a new approach. Using Apache, PHP5, suPHP and eZ Component’s WebDAV module, I was able to implement a sturdier WebDAV solutions that still solved the problems.

First things first, Apache, PHP5, mod_php, mod_suphp all needed to be installed and configured. Once that was done, ez Component’s WebDAV module needed to be installed – on Fedora, this is php-ezc-Webdav in the Yum repositories. Then I needed to make a couple of scripts to handle authentication and module loading. These are modified right from the ezc-Webdav documentation. These scripts need to go in Apache’s DocumentRoot.

autoload.php

<?php
require_once "/usr/share/pear/ezc/Base/base.php";

/**
 * Autoload ezc classes
 *
 * @param string $className
 */
function __autoload( $className )
{
    ezcBase::autoload( $className );
}
?>

You may need to alter the path to base.php above.

authenticator.php

<?php
class ldapAuthenticator extends ezcWebdavDigestAuthenticatorBase
                   implements ezcWebdavAuthorizer
{

    public function authenticateAnonymous( ezcWebdavAnonymousAuth $data )
    {
        return false;
    }

    public function authenticateBasic( ezcWebdavBasicAuth $data )
    {
        $username = $data->username;
        $password = $data->password;

	if(strstr($username, '\\')){
		$username = strstr($username, '\\');
		$username = preg_replace("^.", '', $username);
	}

	$ldap = ldap_connect("ldap.company.tld", 389);
	$bind = @ldap_bind($ldap);
	$search = ldap_search($ldap, "dc=company,dc=tld", "(&(objectClass=shadowAccount)(uid=".$username."))", array("dn"));
	$data = ldap_get_entries($ldap, $search);
	$dn = $data[0]['dn'];

	if(!$dn){ return false; }

	$bind = @ldap_bind($ldap, $dn, $password);
	$search = ldap_search($ldap, "dc=company,dc=tld", "(&(objectClass=shadowAccount)(uid=".$username."))", array("dn"));
	$data = ldap_get_entries($ldap, $search);

	if(!$bind){ return false; }

	return true;
    }

    public function authenticateDigest( ezcWebdavDigestAuth $data )
    {
        $username = $data->username;

	error_log("User attempted unsupported DIGEST authentication: ".$username);

	return false;
   } 

    public function authorize( $user, $path, $access = ezcWebdavAuthorizer::ACCESS_READ )
    {
        return true;
    }
}
?>

You will need to modify the authenticateBasic function. The one above is to authenticate with an LDAP backend.

sharename.php

<?php
require_once 'autoload.php';
require_once 'authenticator.php';

$server = ezcWebdavServer::getInstance();

$server->options->realm = "CCPG Solutions Ltd WebDAV";

$server->auth = new ldapAuthenticator();

$backend = new ezcWebdavFileBackend(
   '/path/to/share'
);

$server->handle( $backend );
?>

In this one, you will need to modify the share path (in $backend) and the realm.

Then you need to change the owning uid/gid (chown user:group sharename.php) of the sharename.php file to the user/group you want to use for read/write permissions of the files. You can make as many sharename.php files for each share (or combination of share/permissions) you need.

With all that done, you should be able to go ahead and map the share using the URL https://dav.company.tld/sharename.php

The eZ Component’s WebDAV module seems pretty in-depth – the above is really just a very basic usage of it’s capabilities, but it does work pretty well. The only other thing you may have to do, depending how pedantic your Windows boxes are going to be, is disable the DIGEST authentication headers altogether. To do this, edit /usr/share/pear/ezc/Webdav/servers.php (or wherever your Webdav/server.php is located) and comment the following lines in the createUnauthenticatedResponse function:

$wwwAuthHeader['digest'] = 'Digest realm="' .$this->options->realm . '"'
 . ', nonce="' . $this->getNonce() . '"'
 . ', algorithm="MD5"';

Save the file and exit. That’s all it takes. With all that done, your users should be able to map the share in Windows, authenticate via LDAP and read/write files with the permissions of the PHP script owner.

uop2cal: Subscribe to your @PortsmouthUni timetable as an iCal feed (UPyoursUPlink?)

Anyone who goes to the University of Portsmouth will be familiar with UPlink, the University’s portal system, and with the timetable it contains. Most people (especially anyone under what used to be ECE) will be familiar with the frustration of lectures changing times/dates/moving room/no longer existing despite being marked as “confirmed”, and the univeristy-standard oh-so-helpful response of “You should be checking it everyday”, along with the other dozen-or-so university sites we’re supposed to be checking every day. (Can you notice my frustration yet? I was originally planning to call this UPyoursUPlink but have since decided on the less-offensive but less-descriptive uop2cal)

This script is a pretty simple one, it uses cURL to screen-scrape the portal and retrieve your timetable, an obnoxious set of regexes (<3) to parse the data into an array, and then the iCalcreator class to generate an ICS file of your timetable, complete with the details (name, lecturer, room, confirmed/unconfirmed (not that I can find a difference, mind), group, etc).

Update: Previously, I was supplying a hosted copy for anyone to use, but I have been advised by the University that you willingly giving me your username and password somehow contravenes the Computer Misuse Act 1990. Whether or not this is the case is irrelevant, as they have asked I stopped doing this. In any case, the source code is still available below (unless they later tell me that’s a problem somehow, too), so you can run your own copy.

Update update: Apparently the University still weren’t too happy with the provisions I had made the last time they contacted me, and have once again requested I remove this. As such, this source is no longer publicly available. Queries about why can be directed towards the University itself, queries about the source are welcome to me.

irc4sms.pl: I’m in your IRC, SMSing your SMSes

This script is a proof-of-concept that I wanted to try more than anything else. Lightly modelled on bitlbee, it pretends to be an IRC server, and accepts/sends SMSes via a gateway (or a gateway app on your Android phone). It’s very much not a finished product and is known to have memory leaks. It also doesn’t clean up after it’s threads.

This script requires threads, threads::shared and IO::Socket.

Config-wise, it’s fairly straightforward – it’s all done in lines 6-21 of the script source. Set $IRCport and $IRCbind to the IP and TCP port you want the server to listen on. Set $IRCpassword to be the password that the IRC client must submit to connect.

The only part that gets tricky is interacting with the gateway. $SMScommand should be set to a script to pass the SMS off to the gateway for delivery. You can use the variables %TO% and %TEXT% in the script path as parameters – these will be replaced with the recipient and the text of the message respectively before the script is called. This could be a PHP script (“/usr/bin/php send-sms.php %TO% ‘%TEXT%’”), a URL using curl or wget (“/usr/bin/wget http://localhost/cgi-bin/sms.cgi?to=%TO%&text=%TEXT%”), an email address using sendmail or similar (“echo %TEXT% | /usr/sbin/sendmail %TO%@sms.gateway.com”) or anything else you might want to use.

Likewise, receiving SMSes can be a bit tricky. $SMSreceiveKey needs to be set to a key that will be used to ensure that the SMSes are received from the real gateway. SMSes are accepted from the gateway by connecting to the IRC server socket and sending nothing more than:

DELIVER sms-receive-key 1234567890 :SMS text goes here.\r\n

The connection does not have be registered as an IRC client or authenticated to the server. It’s important to note that the IP and port you have set earlier in the configuration must be accessible by whatever is delivering the SMSes as well, or it will not be able to connect. In a lot of cases, SMS gateway services or gateway apps running on your phone will only support email forwarding or HTTP delivery of incoming texts. In either of these cases, I suggest creating a script to deliver the SMS for you. In the former case, a Perl script that gets called by a pipe from the mail server which connects to the server’s socket and deliver’s the SMS would be pretty straightforward. Likewise, a PHP script on a webserver could be configured to accept an SMS by GET/POST parameters and connect to the socket to deliver it.

Use-wise, it’s fairly straightforward. You can type ‘help’ in #sms after connecting for a short summary of this information. When you receive an SMS, the phone number it is received from will join #sms and address the text message to your attention:

* 1234567890 has joined #sms.
<1234567890> your-nickname: Hello there.

You can reply in kind:

<your-nickname> 1234567890: Hiya.

You can also send a text message to a number not presently in the channel the same way – just address the message at the number and SMSbot will ensure it’s delivered for you.

You can also message people by opening a private chat with them – either use ‘/query 123456790′ or double-click their name on the userlist of most IRC clients to open a private chat. Send your message, and replies will automatically be sent back to you in private message. To switch back to receiving their messages in #sms, just message them from the channel again and their replies will go there instead.

Finally, you can use the channel’s userlist as a sort of contact list by adding and removing users from it. When you receive an SMS from someone not already in the channel, they will join. You can also add them to the channel without sending them a message:

<your-nickname> add 1234567890
* 1234567890 has joined #sms.

Likewise, you can remove someone from the channel whether they joined because you added them or because they sent you a message:

<your-nickname> remove 1234567890
* 1234567890 has left #sms.

If you remove someone from the channel, they will join again the next time they send you a message.


Script Source

#!/usr/bin/perl
use IO::Socket;
use threads;
use threads::shared;

##### Config #####

# IRCd settings. At least change the password.
my $IRCport = "21337";
my $IRCbind = "127.0.0.1";
my $IRCpassword = "irc4sms";

# Shell command to send SMSes via gateway.
# Consider using wget or sendmail!
# Variables! %TO% and %TEXT%
my $SMScommand = "/usr/bin/wget http://localhost/cgi-bin/send-sms.cgi?to=%TO%&text=%TEXT%";

# Key to receive inbound SMSes. Change this.
my $SMSreceiveKey = "1rc45m5!";

##### End Config ######

$version = "0.2";
$crlf = "\r\n";

my @msgQueue;
share(@msgQueue);
my %joined;
share(%joined);

$SIG{CHILD} = 'IGNORE';
$SIG{PIPE} = 'IGNORE';

my $listen_socket = IO::Socket::INET->new(
	LocalPort => $IRCport,
	LocalAddr => $IRCbind,
	Listen => 10,
	Proto => 'tcp',
	Reuse => 1
);
$listen_socket->autoflush(1);
die "Can't bind local socket: $!\n" unless $listen_socket;

while(my $connection = $listen_socket->accept){
	my $child = threads->create("read_data", $connection);
}

sub deliver_data {
	my($socket, $nickname) = @_;
	while(1){
		foreach my $message(@msgQueue) {
				my ($sender, $text) = $message =~ /^(.+?)\:(.+)$/;
				if($sender && $text){
						if($joined{$sender} < 1){
								$joined{$sender} = 1;
								print $socket ':'.$sender.' JOIN #sms'.$crlf;
						}
						if($joined{$sender} == 1){ print $socket ':'.$sender.' PRIVMSG #sms :'.$nickname.': '.$text.$crlf; }
						if($joined{$sender} > 1){ print $socket ':'.$sender.' PRIVMSG '.$nickname.' :'.$text.$crlf; }
				}
		}
		@msgQueue = ();
	}
}

sub read_data {
	my($socket) = @_;
	my $registered = 0;

	print $socket ':irc4sms.local NOTICE AUTH :irc4sms ready. Login or deliver an SMS.'.$crlf;
	while(){
		$data = $_;
		print $data;
		$data =~ s/[\r\n]//ig;

		if($data =~ /^PING (.+)$/){
			print $socket ':irc4sms.local PONG irc4sms.local :'.$1.$crlf;
		}

		if($registered < 3){
			if($data =~ /^DELIVER (.+?) (.+?) \:(.+)$/ && $1 eq $SMSreceiveKey){
				$sender = $2;
				$text = $3;
				$sender =~ s/^\+//;
				$msgQueue[++$#msgQueue] = $sender.":".$text;
				print $socket 'MESSAGE OK ('.$sender.'): '.$text.' - Queued for delivery.'.$crlf;
			}elsif($data =~ /^USER (.+?) .+ (?:\:|)(.+)$/){
				$username = $1;
				$realname = $3;
				$registered++;
			}elsif($data =~ /^NICK (.+)$/){
				$nickname = $1;
				print $socket ':irc4sms.local 464 '.$nickname.' :P assword required.'.$crlf;
				$registered++;
			}elsif($data =~ /^PASS (.+)$/){
				if($1 eq $IRCpassword){
					$registered++;
				}else{
					print $socket ':irc4sms.local 464 '.$nickname.' :Invalid password.'.$crlf;
				}
			}

			if($registered == 3){
				print $socket ':irc4sms.local 001 '.$nickname.' :Welcome'.$crlf;
				print $socket ':irc4sms.local 002 '.$nickname.' :Host irc4sms.local is running irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 003 '.$nickname.' :irc4sms (http://code.cmantito.com/IrcSms)'.$crlf;
				print $socket ':irc4sms.local 004 '.$nickname.' irc4sms.local abiswRo ntC'.$crlf;
				print $socket ':irc4sms.local 005 '.$nickname.' PREFIX=(ohv)@%+ CHANTYPES=# CHANMODES=,,,ntC NICKLEN=25 CHANNELLEN=25 NETWORK=irc4sms SAFELIST CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server'.$crlf;
				print $socket ':irc4sms.local 375 '.$nickname.' :- irc4sms.local MOTD:'.$crlf;
				print $socket ':irc4sms.local 372 '.$nickname.' :- Welcome to irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 376 '.$nickname.' :End of MOTD'.$crlf;
				print $socket ':'.$nickname.'!'.$username.'@irc4sms.local MODE cm :+sR'.$crlf;
				print $socket ':'.$nickname.'!'.$username.'@irc4sms.local JOIN :#sms'.$crlf;
				print $socket ':irc4sms.local MODE #sms +Ct'.$crlf;
				print $socket ':irc4sms.local 353 '.$nickname.' = #sms :@'.$nickname.' @SMSbot'.$crlf;
				print $socket ':irc4sms.local 366 '.$nickname.' #sms :End of /NAMES list'.$crlf;
				print $socket ':irc4sms.local 332 '.$nickname.' #sms :irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 333 '.$nickname.' #sms SMSbot!SMSbot@irc4sms.local '.(time - 60).$crlf;
				$joined{$nickname} = 1;
				$joined{'SMSbot'} = 1;
				my $delivery = threads->create("deliver_data", $socket, $nickname);
			}
		}else{
			if($data =~ /^MODE/){
				print $socket ':irc4sms.local 324 '.$nickname.' #sms +Ct'.$crlf;
			}elsif($data =~ /^WHO/){
				print $socket ':irc4sms.local 352 '.$nickname.' #sms '.$username.' irc4sms.local irc4sms.local '.$nickname.' H@ :0 '.$realname.$crlf;
				print $socket ':irc4sms.local 352 '.$nickname.' #sms irc4sms irc4sms.local irc4sms.local SMSbot H@ :0 irc4sms '.$version.$crlf;
			}elsif($data =~ /^PRIVMSG \#sms \:(.+?)(?:\:|\,) (.+)$/){
				if($joined{$1} > 0){
					$joined{$1} = 1;
				}
				$to = $1;
				$text = $2;
				$cmd = $SMScommand;
				$cmd =~ s/\%TO\%/$to/g;
				$cmd =~ s/\%TEXT\%/$text/g;
				system($cmd);
			}elsif($data =~ /^PRIVMSG ([a-zA-Z0-9\+]+?) \:(.+?)$/){
				if($joined{$1} > 0){
					$joined{$1} = 2;
				}
				$to = $1;
				$text = $2;
				$cmd = $SMScommand;
				$cmd =~ s/\%TO\%/$to/g;
				$cmd =~ s/\%TEXT\%/$text/g;
				system($cmd);
			}elsif($data =~ /^PRIVMSG \#sms :( .+?)$/){
				$command = $1;
				if($command =~ /^add (.+)/ && !$joined{$1} ){
					$joined{$1} = 1;
					print $socket ':'.$1.' JOIN #sms'.$crlf;
				}elsif($command =~ /^remove (.+?)$/ && ($joined{$1})){
					$joined{$1} = '';
					print $socket ':'.$1.' QUIT :Leaving...'.$crlf;
				}elsif($command =~ /^help/){
					print $socket ':SMSbot PRIVMSG #sms :Commands: add 442392123456, remove 442392123456'.$crlf;
					print $socket ':SMSbot PRIVMSG #sms :If you /msg a contact, responses will automatically come as a /msg'.$crlf;
					print $socket ':SMSbot PRIVMSG #sms :Otherwise, address the contact in this channel, and their replies will come here'.$crlf;
				}else{
					print $socket ':SMSbot PRIVMSG #sms :I don\'t recognise that as a valid command. Sorry. Try "help".'.$crlf;
				}
			}
		}
	}
}

WebDAV is a … pain

[Titled edited ;P]

As a concept – WebDAV is great. File shares over HTTP – no fussy protocols to deal with, security and encryption built right in.

Problems. You want to use Apache as a WebDAV server with mod_dav. Theroetically, this is fine. In my circumstances, I wanted to use it for a few specific people to reach their /home/username folders, so I thought using Apache I could just export those directories as DAV shares, using LDAP authentication. I hit several problems with this.

First, permissions. Apache obviously runs as ‘nobody’ or ‘apache’ or ‘httpd’ depending on your system. This user doesn’t have permission to access your home directory, much less, write to it. And you don’t REALLY want your front-facing web server having that permission. I solved this by installing mod_proxy and mod_proxy_http, then running a tiny little lighttpd instance as the user(s) who wanted access to their home directories, with it’s own mod_webdav inserted. I had Apache pass connections to the webdav-server.domain/username vhost/folder alias through the proxy to the lighttpd server for that user, which was exporting that user’s home directory as a single share. I also used lighttpd’s own mod_auth to do the LDAP authentication for each user instead of Apache, because I didn’t want to chance someone getting around the front-facing proxy and connecting directly to one of the DAV servers, obviously. It is worth noting that you will want to set the document-root to either /home or /home/username for the lighty instance, as COPY and MOVE will not work if you don’t. Since authentication is required for the site anyway, it’s not a huge concern to set it to the user’s home directory. This setup seems to work pretty well.

Which brings us to the second problem. Windows XP SP2, Windows Vista and Windows 7 refuse to authenticate to a WebDAV server using BASIC as it’s authentication method (instead of DIGEST). If I were using password files, this would be no problem to change to DIGEST authentication using lighty’s mod_auth, but because I’m using LDAP as a backend, it’s not feasible. You can’t use DIGEST with LDAP because, with DIGEST, the password is never actually sent – just a hash to be compared, but the comparison hash will simply never match what’s in the LDAP database, so there’s no way to do the auth. Instead, the solution seems to be dealing with Windows. After lots and lots of Googling, I found a few registry hacks:

HKLM\SYSTEM\CurrentControlSet\services\WebClient\Parameters\UseBasicAuth
HKLM\SYSTEM\CurrentControlSet\services\WebClient\Parameters\BasicAuthLevel

If you set UseBasicAuth (a DWORD value) to 0×00000001 and BasicAuthLevel (also a DWORD value) to 0×00000002, you will be able to authenticate to the DAV server with BASIC authentication. The first parameter enabled BASIC authentication altogether. The latter has three possible values: 0, which disabled BASIC authentication, 1, which enabled it for SSL-secured sites and 2, the value I’m using above, to enable it for all sites. When you’re done, restart the webclient service. (In Vista/7, you can only do this from services.msc or by using an ELEVATED privileges command prompt – not a normal one.)

Now, I wanted to use this with Active Directory to set a user’s N-drive to their home directory. I know it would be easier to use Samba, but if a user is behind a vaguely-filtered internet connection, say Comcast, Virgin Media or Verizon Fios, you can’t actually hit a Samba share over the internet. And VPNs can be even more of a … pain than WebDAV. In theory, you can address a WebDAV share with a UNC path and it works (\\webdav-server.fqdn\sharename). It doesn’t, however, seem to work when you set that UNC path to a drive letter in someone’s AD account. Which sucks.

So I thought, ooh – I know! Map it with a logon script! DERP. Logon scripts have to run from a Samba share. Which we can’t get to. Hence using WebDAV. The solution, if I say so myself, is pretty creative, if not a little over-the-top. I created a folder, say, C:\logon. In there, there is ‘wget.exe’, a Windows binary-only version of the ‘wget’ tool for Linux, which is a simple command-line based HTTP/FTP downloader. Then there’s a file called loader. bat. This file fetches a logon.bat from a web server, and runs it. Then, in their Active Directory profile, I set their logon script to \\localhost\c$\logon\loader.bat – causing them, each time they logon, to download the latest logon.bat and run it. In logon.bat, I use wget again to download a little .exe file written in AutoIT that checks if the registry has already been updated to connect to WebDAV shares using BASIC authentication as above, and if not, requests administrative privileges to update the registry and then restart the webclient service for you. After all this happens, it finally uses ‘net use’ to map the WebDAV shares (the lighty one from earlier as well as an additional SharePoint share) to their relevant drive letters. Scripts below.

The wget.exe I’m using comes from: http://pages.interlog.com/~tcharron/wgetwin.html

loader.bat

@echo off
cd C:\logon
del c:\logon\logon.bat
C:\logon\wget http://some-http-server.fqdn/logon/logon.bat
C:\logon\logon.bat

logon.bat

@echo off
cd C:\logon
del C:\logon\dav-setup.exe
C:\logon\wget http://some-http-server.fqdn/logon/dav-setup.exe
C:\logon\dav-setup.exe

net use N: "http://apache-proxy-to-lighty.fqdn/sharename" /user:username /persistent:yes /savecred
net use T: "http://sharepoint.fqdn/sharename" /user:domain\username /persistent:yes /savecred

dav-setup.au3

#RequireAdmin

$useBasicAuth = RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "UseBasicAuth")
$basicAuthLevel = RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "BasicAuthLevel")

$restartService = 0

if $useBasicAuth <> 1 Then
    RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "UseBasicAuth", "REG_DWORD", "0x00000001")
    $restartService = 1
EndIf

if $basicAuthLevel <> 2 Then
    RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "BasicAuthLevel", "REG_DWORD", "0x00000002")
    $restartService = 1
EndIf

if $restartService = 1 Then
    RunWait(@WindowsDir & "\System32\net stop webclient")
    RunWait(@WindowsDir & "\System32\net start webclient")
EndIf

checkdupes.pl

Script Information

This script checks files specified on the command line for duplicates based on checksums. Version 0.3a will also highlight files in different directories with the same name. Version 0.2a introduced the capability of recursion using the -a flag. See documentation.

Documentation

Usage: ./checkdupes.pl
At least two or more files must be specified. Any folders
specified will be skipped. Wildcards will work, for example:
     ./checkdupes.pl *
will check all files in the current folder. Likewise:
     ./checkdupes.pl folder/* folder-2/*
will check all files in both those folders. It will not
recurse on it's own.

Alternatively (as of 0.2a), you can pass the -a flag:
      ./checkdupes.pl -a
This will check all files, recursively, starting from the
current working directory.

As of version 0.3a, it will also find files with duplicate
names that reside in different directories. No flag is
necessary to enable this functionality.

Script Source

#!/usr/bin/perl

if($ARGV[0] =~ /\-a/i && !$ARGV[1]){
    system("find --version &>/dev/null");
    if($? != 0){
        print STDERR "FATAL: find not in \$PATH (".$ENV{'PATH'}.") - aborting.\n";
        exit(2);
    }

    $flist = `find ./`;
    @ARGV = split(/\n/, $flist);
}

if(@ARGV[0] =~ /\-(?:h|v|\?)/i || !$ARGV[0] || !$ARGV[1] || ($ARGV[0] =~ /\-a/i && $ARGV[1])){
    print STDERR "\nUsage: ".$0." \n";
    print STDERR "\tAt least two or more files must be specified. Any folders\n";
    print STDERR "\tspecified will be skipped. Wildcards will work, for example:\n";
    print STDERR "\t\t".$0." *\n";
    print STDERR "\twill check all files in the current folder. Likewise:\n";
    print STDERR "\t\t".$0." folder/* folder-2/*\n";
    print STDERR "\twill check all files in both those folders. It will not\n";
    print STDERR "\trecurse on it's own.\n\n";
    print STDERR "\tAlternatively (as of 0.2a), you can pass the -a flag:\n";
    print STDERR "\t\t$0 -a\n";
    print STDERR "\tThis will check all files, recursively, starting from the\n";
    print STDERR "\tcurrent working directory.\n\n";
    print STDERR "\tAs of version 0.3a, it will also find files with duplicate\n";
    print STDERR "\tnames that reside in different directories. No flag is\n";
    print STDERR "\tnecessary to enable this functionality.\n\n";
    print STDERR $0." 0.3a by cmantito \n\n";
    exit(1);
}

system("md5sum --version &>/dev/null");
if($? != 0){
    print STDERR "FATAL: md5sum not in \$PATH (".$ENV{'PATH'}.") - aborting.\n";
    exit(2);
}

$progress = 0;
while($file = shift(@ARGV)){
    print "Processing ".$progress."/".++$#ARGV."...\n";
    if(-e $file){
        if( !-d $file){
            $escfile = $file;
            $escfile =~ s/(.)/\\$1/ig;
            $sum = `md5sum $escfile`;
            $sum =~ s/^(.+?)\s+.+$/\1/ig;
            chomp($sum);
            $sums{$sum}++;
            push(@$sum, $file);

            $filename = $file;
            $filename =~ s/(.+)\/(.+)$/\2/;
            if(!$filenames{$filename}){
                $filenames{$filename} = $file;
            }else{
                $filenames{$filename} .= "¬".$file;
            }
            $filenamecount{$filename}++;
        }
    }else{
        print STDERR "FATAL: Couldn't open file: ".$file." - aborting.\n";
        exit(1);
    }
    $progress++;
}

print "\n--- DUPLICATE FILES ---\n";
foreach $sum(keys %sums){
    if($sums{$sum} > 1){
        print $sum.": ";
        foreach $file(@$sum){
            print $file." ";
        }
        print "\n";
    }
}
print "\n--- DUPLICATE NAMES ---\n";
foreach $filename(keys %filenamecount){
    if($filenamecount{$filename} > 1){
        print $filename.": ";
        $filelist = $filenames{$filename};
        $filelist =~ s/¬/ /ig;
        print $filelist."\n";
    }
}

print "\n";
exit(0);

Micro and ProgLog CW1: Line-follower

#include <p18F252.h>

int antiTwist = 0;	// Flag for anti-twist code.
int i = 0;			// Counting var.
int turnedAround = 0;

void allStop(void);
void turnRight(void);
void turnLeft(void);
void goStraight(void);
void adcOff(void);
void triggerPulse(void);
void catchInterrupt(void);
void setupInterrupts(void);
void turnAround(void);

#pragma code int_vector=0x08
void ISR (void) {
	_asm
	goto catchInterrupt
	_endasm
}
#pragma code

#pragma interrupt catchInterrupt
void catchInterrupt(void) {
	int p;
	int turningFlag;

	if(PORTBbits.RB0 == 1){		// If it's rising.
		IPR1bits.TMR2IP = 0;	// Tmr 2, low priority.
		PIR1bits.TMR2IF = 0;	// Tmr 2 flag cleared.
		PIE1bits.TMR2IE = 1;	// Enable overflow interrupt.

		//Timer2 Registers Prescaler= 16 - TMR2 PostScaler = 16 - PR2 = 255 - Freq = 91.91 Hz - Period = 0.010880 seconds
		T2CON |= 120;        // bits 6-3 Post scaler 1:1 thru 1:16
		T2CONbits.TMR2ON = 1;  	 // bit 2 turn timer2 on;
		T2CONbits.T2CKPS1 = 1;	 // bits 1-0  Prescaler Rate Select bits
		T2CONbits.T2CKPS0 = 0;
		PR2 = 15;        	 // PR2 (Timer2 Match value)

		INTCONbits.INT0IF = 0; // Clear interrupt flag.
	}else{
		if(PIR1bits.TMR2IF == 0){
			PORTAbits.RA4 = 0; // LED ON.
			T2CONbits.TMR2ON = 0;	// Timer off.

			allStop();
			INTCONbits.GIEH = 0; // Temporarily disable int.

			while(PORTCbits.RC0 > 0 || PORTCbits.RC1 > 0 || PORTCbits.RC2 > 0){
				if(antiTwist == 0){
					turnRight();
				}else{
					turnLeft();
				}
			}
		}else{
			PORTAbits.RA4 = 1;		// LED OFF.
		}

		PIR1bits.TMR2IF = 0;	// Tmr 2 flag cleared.
		INTCON3bits.INT1IF = 0; // Clear interrupt flag.

		for(p = 0; p <= 5000; p++){
			// Kill 12-ish mS.
		}

		triggerPulse();
	}

}

void main (void) {
	adcOff();

	PORTAbits.RA4 = 1;		// LED OFF.

	setupInterrupts();

	TRISC = 0xff;       // Port C = input
	TRISB = 0xff;		// Port B = input
	TRISA = 0x00;       // Port A = output

	allStop();			// Start with motors off.
	triggerPulse();		// Start the ultrasound.

	while(1){
			// Sensors:     RIGHT           	 MIDDLE     	     	 LEFT	

		while(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 0 && PORTCbits.RC2 == 0){
			turnAround();
		}

		if(PORTCbits.RC0 == 1 && PORTCbits.RC1 == 1 && PORTCbits.RC2 == 1){
			allStop();
		}else if(PORTCbits.RC0 == 1 && PORTCbits.RC1 == 0 && PORTCbits.RC2 == 0){
			turnRight();
		}else if(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 1 && PORTCbits.RC2 == 0){
			goStraight();
		}else if(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 0 && PORTCbits.RC2 == 1){
			turnLeft();
		}else if(PORTCbits.RC0 == 1 && PORTCbits.RC1 == 1 && PORTCbits.RC2 == 0){
			turnRight();
		}else if(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 1 && PORTCbits.RC2 == 1){
		   	turnLeft();
	   	}

		for(i = 0; i <= 2500; i++){
			// Kill some time.
		}

		// Reset the motors.
		allStop();
	}
}

void turnRight(void) {
	PORTAbits.RA0 = 0;  // Right reverse.
	PORTAbits.RA2 = 0;  // Enable right.

	PORTAbits.RA1 = 1;  // Left forward.
	PORTAbits.RA3 = 0;  // Enable left.
}

void turnLeft(void) {
	PORTAbits.RA0 = 1;  // Right forward.
	PORTAbits.RA2 = 0;  // Enable right.

	PORTAbits.RA1 = 0;  // Left reverse.
	PORTAbits.RA3 = 0;  // Enable left.
}

void goStraight(void) {
	PORTAbits.RA0 = 1;  // Right forward.
	PORTAbits.RA2 = 0;  // Enable right.

	PORTAbits.RA1 = 1;  // Left forward.
	PORTAbits.RA3 = 0;  // Enable left.
}

void allStop(void) {
	PORTAbits.RA2 = 1; 	// Disable right.
	PORTAbits.RA3 = 1; 	// Disable left.
}

void adcOff(void) {
	// The next three lines set all of PORTA to be digital,
	// disabling the ADC. According to datasheet, 110 is
	// all pins digital.
	ADCON1bits.PCFG1 = 1;
	ADCON1bits.PCFG2 = 1;
	ADCON1bits.PCFG3 = 0;
}

void triggerPulse(void) {
	int q;

	PORTAbits.RA5 = 1;   // Start pulse.
	for(q = 0; q <= 20; q++){
		// Kill 54.2-ish uS.
	}
	PORTAbits.RA5 = 0;   // Stop pulse.
}

void turnAround(void) {
			if(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 0 && PORTCbits.RC2 == 0){
				while(PORTCbits.RC0 == 0 && PORTCbits.RC1 == 0 && PORTCbits.RC2 == 0){
					turnedAround = 1;
					if(antiTwist == 0){
						turnRight();
					}else{
						turnLeft();
					}
				}
			}

			if(turnedAround == 1){
				antiTwist =~ antiTwist;			//Toggle Twist
				turnedAround = 0;
			}

}

void setupInterrupts() {
	RCONbits.IPEN = 1;			// Priorities on.

	INTCONbits.INT0IF = 0;		// Clear IF.
	INTCONbits.INT0IE = 1;		// INT0 on - it's always high priority.
	INTCON2bits.INTEDG0 = 1;	// Catch INT0 on rising edge.

	INTCON3bits.INT1IF = 0;		// Clear IF.
	INTCON3bits.INT1IE = 1;		// INT1 on.
	INTCON3bits.INT1IP = 1;		// High priority.
	INTCON2bits.INTEDG1 = 0;	// Catch INT1 on falling edge.

	INTCONbits.GIEH = 1;		// Enable all high priority interrupts.
}

Intro To Computing CW2: Text modification

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DELIMITER        "."
#define NEWLINE_DELIM    ".\n"

#define STATE_COMMAND     1
#define STATE_INPUT       2
#define STATE_EXIT        3

#define BUFFER_SIZE     1024
#define CMD_DELIMITER   ";"

#define CMD_PRINT       'p'
#define CMD_QUIT        'q'
#define CMD_APPENDS     'a'
#define CMD_REPLACE     'r'
#define CMD_DELETE      'd'
#define CMD_INSERT      'i'

int main (void) {
	// Setup whatever we need.
	char* buffer = NULL;
	int buffer_size = 0;
	int buffer_length = 0;
	int chars_read = 0;
	int appState = STATE_COMMAND;
	int lineNumber = 0;
	int char_pos = 0;

	char valid_cmds[BUFFER_SIZE + 1] = {'\0'};
	char* all_cmds = NULL;
	char* cmd_line = NULL;

	int location = 0;
	int locationB = 0;
	char commandLetter;
	char notModifier;
	char rangeModifier;
	char* string = "";

	int skipPrint = 0;

	// We're going to loop through here until we're all done. (ie, STATE_EXIT)
	do {
		// Read input into the buffer.
		chars_read = getline(&buffer, &buffer_size, stdin);

		// Make sure it exists.
		if(chars_read < 1){
			break;
		}

		// Delimiters make the state increase.
		if(strcmp(buffer, DELIMITER) == 0 || strcmp(buffer, NEWLINE_DELIM) == 0){
			appState = appState + 1;
			continue;
		}

		// Command state.
		if(appState == STATE_COMMAND){

			// Get some commands from the buffer.
			// Go through each character of the command, and see if it matches a
			// real command, if it does, put it in the valid commands buffer.
			buffer_length = strlen(buffer);
			for(char_pos = 0; char_pos < buffer_length; char_pos++){
					if(islower(buffer[char_pos])){
						if(buffer[char_pos] == CMD_PRINT ||
							buffer[char_pos] == CMD_QUIT ||
							buffer[char_pos] == CMD_APPENDS ||
							buffer[char_pos] == CMD_REPLACE ||
							buffer[char_pos] == CMD_DELETE ||
							buffer[char_pos] == CMD_INSERT ){
								strcat(valid_cmds, buffer);
								strcat(valid_cmds, CMD_DELIMITER);
								break;
						}

					appState  = STATE_EXIT;
					break;
				}
			}
		}

		// Text input state.
		if(appState == STATE_INPUT){
			skipPrint = 0; // This must must MUST get set to 0 on each loop, or everything will break.
			lineNumber = lineNumber + 1; // Increment the line number each time we go through.
			all_cmds = strdup(valid_cmds); // And now all_cmds is the same as valid_cmds.

			// Get a command from the commands buffer.
			for(cmd_line = strtok(all_cmds, CMD_DELIMITER); cmd_line != NULL; cmd_line = strtok(NULL, CMD_DELIMITER)){

				// Match this one first, because it's the most specific - put the various bits of the command
				// into the various variables. Also, make sure the range modifier is one of the valid ones,
				// and that the not modifier is what we expect.
				if(sscanf(cmd_line, "%d%c%d%c%c%s", &location, &rangeModifier, &locationB, &notModifier, &commandLetter, string) == 6
					&& (rangeModifier == ',' || rangeModifier == '~')
					&& notModifier == '!'){

					// Now check the command issued. Appends? Yeah, check the range --
					// For comma, make sure the line number isn't in the range specified. (Remember the '!'.)
					// For tilde, check that A) the line number isn't the one specified, B) the line number minux
					// the starting point divded by the second number has a remainder, C) the line number is less than
					// the starting point. If A and B or A and C are true, we'll match.
					if(commandLetter == CMD_APPENDS && (
						(rangeModifier == ',' && (location > lineNumber || lineNumber > locationB))
						|| (rangeModifier == '~' && location != lineNumber && ((lineNumber - location) % locationB > 0  || lineNumber < location))
					)){
						// Append, stick the string to the end of the existing buffer and attach a newline.
						// Append will use this same method for the rest off the programme.
						strcat(buffer, string);
						strcat(buffer, "\n");
					}

					// Same method for matching replace.
					if(commandLetter == CMD_REPLACE && (
						(rangeModifier == ',' && (location > lineNumber || lineNumber > locationB))
						|| (rangeModifier == '~' && location != lineNumber && ((lineNumber - location) % locationB > 0  || lineNumber < location))
					)){
						// Just replace the buffer with the new string and add a newline to the end.
						// Replacement will be done the same way as this for the rest of the app.
						strcpy(buffer, string);
						strcat(buffer, "\n");
					}

					// And insert.
					if(commandLetter == CMD_INSERT && (
						(rangeModifier == ',' && (location > lineNumber || lineNumber > locationB))
						|| (rangeModifier == '~' && location != lineNumber && ((lineNumber - location) % locationB > 0  || lineNumber < location))
					)){
						// Add a newline to the end of the new string, stick the buffer on after that, and then
						// move it all in to the buffer. We'll reuse this method for insert throughout the app.
						strcat(string, "\n");
						strcat(string, buffer);
						strcpy(buffer, string);
					}

				// We're moving down in order of most specific matching regime to least specific. So this one's next.
				}else if(sscanf(cmd_line, "%d%c%d%c%c", &location, &rangeModifier, &locationB, &notModifier, &commandLetter) == 5
					&& (rangeModifier == ',' || rangeModifier == '~')
					&& notModifier == '!'){

					// We're using the same matching system as before to match the ranges for print, remembering that
					// there's still an !.
					if(commandLetter == CMD_PRINT && (
						(rangeModifier == ',' && (location > lineNumber || lineNumber > locationB))
						|| (rangeModifier == '~' && location != lineNumber && ((lineNumber - location) % locationB > 0  || lineNumber < location))
					)){
						printf("%s", buffer);
					}

					// And delete.
					if(commandLetter == CMD_DELETE && (
						(rangeModifier == ',' && (location > lineNumber || lineNumber > locationB))
						|| (rangeModifier == '~' && location != lineNumber && ((lineNumber - location) % locationB > 0  || lineNumber < location))
					)){
						skipPrint = 1;
					}

				// This is the next one in order, this time no !
				}else if(sscanf(cmd_line, "%d%c%d%c%s", &location, &rangeModifier, &locationB, &commandLetter, string) == 5
					&& (rangeModifier == ',' || rangeModifier == '~')){   

					// For appends, we're checking if the range is a comma or a tilde. If it's comma, is the line number
					// in between the addresses specified? If it's a tilde, check A) is line number equal to the location
					// specified, B) does the line number minus the location, divided by the second number,
					// have NO remainder? and C) is the line number greater than the location specified?
					// If A is true, or B and C are true, we match.
					if(commandLetter == CMD_APPENDS && (
						(rangeModifier == ',' && location <= lineNumber && lineNumber <= locationB)
						|| (rangeModifier == '~' && (location == lineNumber || (((lineNumber - location) % locationB) == 0 && lineNumber > location)))
					)){
						strcat(buffer, string);
						strcat(buffer, "\n");
					}

					if(commandLetter == CMD_REPLACE && (
						(rangeModifier == ',' && location == lineNumber)
						|| (rangeModifier == '~' && (location == lineNumber || (((lineNumber - location) % locationB) == 0 && lineNumber > location)))
					)){
						strcpy(buffer, string);
						strcat(buffer, "\n");
					}

					// Using this to for the ranged replace for a comma-range only, once we've replaced the first line,
					// We make sure that none of the lines after it print until we're out of the range.
					// Ref: http://cnfolio.com/IntroComputingTutorial05#example05
					if(commandLetter == CMD_REPLACE && (
						(rangeModifier == ',' && lineNumber > location && lineNumber <= locationB)
					)){
						skipPrint = 1;
					}

					if(commandLetter == CMD_INSERT && (
						(rangeModifier == ',' && location <= lineNumber && lineNumber <= locationB)
						|| (rangeModifier == '~' && (location == lineNumber || (((lineNumber - location) % locationB) == 0 && lineNumber > location)))
					)){
						strcat(string, "\n");
						strcat(string, buffer);
						strcpy(buffer, string);
					}

				// And then this one goes next.
				}else if(sscanf(cmd_line, "%d%c%d%c", &location, &rangeModifier, &locationB, &commandLetter) == 4
					&& (rangeModifier == ',' || rangeModifier == '~')){

					// Same method as before for dealing with the different ranges.
					if(commandLetter == CMD_PRINT && (
						(rangeModifier == ',' && location <= lineNumber && lineNumber <= locationB)
						|| (rangeModifier == '~' && (location == lineNumber || (((lineNumber - location) % locationB) == 0 && lineNumber > location)))
					)){
						printf("%s", buffer);
					}

					if(commandLetter == CMD_DELETE && (
						(rangeModifier == ',' && location <= lineNumber && lineNumber <= locationB)
						|| (rangeModifier == '~' && (location == lineNumber || (((lineNumber - location) % locationB) == 0 && lineNumber > location)))
					)){
						skipPrint = 1;
					}

				// This one's next but it doesn't have a range, just the not modifier.
				}else if(sscanf(cmd_line, "%d%c%c%s", &location, &notModifier, &commandLetter, string) == 4
					&& notModifier == '!'){

					// So basically, because of the !, we only make sure that the line number DOESN'T match.
					// We don't have to use the range detection here.
					if(commandLetter == CMD_APPENDS && location != lineNumber){
						strcat(buffer, string);
						strcat(buffer, "\n");
					}

					// And here.
					if(commandLetter == CMD_REPLACE && location != lineNumber){
						strcpy(buffer, string);
						strcat(buffer, "\n");
					}

					// And here.
					if(commandLetter == CMD_INSERT && location == lineNumber){
						strcat(string, "\n");
						strcat(string, buffer);
						strcpy(buffer, string);
					}

				// We do have a not modifier here, so we need to not match everything.
				}else if(sscanf(cmd_line, "%d%c%c", &location, &notModifier, &commandLetter) == 3
					&& notModifier == '!'){

					// Same here.
					if(commandLetter == CMD_PRINT && location != lineNumber){
						printf("%s", buffer);
					}

					// And here.
					if(commandLetter == CMD_DELETE && location != lineNumber){
						skipPrint = 1;
					}

				// This time we don't have the not modifier.
				}else if(sscanf(cmd_line, "%d%c%s", &location, &commandLetter, string) == 3){

					// So we check that the line numbers DO match. Again, no worry about ranges.
					if(commandLetter == CMD_APPENDS && location == lineNumber){
						strcat(buffer, string);
						strcat(buffer, "\n");
					}

					// Same here.
					if(commandLetter == CMD_REPLACE && location == lineNumber){
						strcpy(buffer, string);
						strcat(buffer, "\n");
					}

					// And here.
					if(commandLetter == CMD_INSERT && location == lineNumber){
						strcat(string, "\n");
						strcat(string, buffer);
						strcpy(buffer, string);
					}

				// We don't really have to make sure location > 0, but it's better to
				// validate as much of the input as you can, to weed out falsely-matching
				// input.
				}else if(sscanf(cmd_line, "%d%c", &location, &commandLetter) == 2
					&& location > 0){

					// Again, it's a simple case of does the line number match?
					if(commandLetter == CMD_PRINT && location == lineNumber){
						printf("%s", buffer);
					}

					// Here too.
					if(commandLetter == CMD_QUIT && location == lineNumber){
						appState = STATE_EXIT;
					}

					// This one as well.
					if(commandLetter == CMD_DELETE && location == lineNumber){
						skipPrint = 1;
					}

				// We have a not modifier here.
				}else if(sscanf(cmd_line, "%c%c", &notModifier, &commandLetter) == 2
					&& notModifier == '!'){

					// But no line numbers, so it matches every line.
					if(commandLetter == CMD_PRINT){
						// well, if we DON'T (from the !) print every line an additional time,
						// then it just does what it normally does so this doesn't
						// actually need to do anything?
					}

					/// Same here.
					if(commandLetter == CMD_DELETE){
						// likewise, if we don't delete every line, then it acts
						// normally and again, this doesn't do anything.
						// I don't actually understand the point in !p and !d.
					}

				// We're back to a simple command that effects every line, but not ! modifier.
				}else if(sscanf(cmd_line, "%c%s", &commandLetter, string) == 2){

					// So we don't need to worry about line matching.
					if(commandLetter == CMD_APPENDS){
						strcat(buffer, string);
						strcat(buffer, "\n");
					}

					// Or here.
					if(commandLetter == CMD_REPLACE){
						strcpy(buffer, string);
						strcat(buffer, "\n");
					}

					// Or here.
					if(commandLetter == CMD_INSERT){
						strcat(string, "\n");
						strcat(string, buffer);
						strcpy(buffer, string);
					}   

				// Very simple one letter commands.
				}else if(sscanf(cmd_line, "%c", &commandLetter) == 1){

					// Which match every line.
					if(commandLetter == CMD_PRINT){
						printf("%s", buffer);
					}

					// And here.
					if(commandLetter == CMD_QUIT){
						appState = STATE_EXIT;
					}

					// Same here.
					if(commandLetter == CMD_DELETE){
						skipPrint = 1;
					}

				}

				// If nothing's told it not to print since the beginning of the loop,
				// print the buffer. By this point, the buffer may have been modified
				// by a replace, append, or insert. Doesn't matter, print it anyway as
				// long as nothing set skipPrint to 1.
				if(skipPrint == 0){
					printf("%s", buffer);
				}
			}

		}
	} while(appState != STATE_EXIT);

	return 0;
}

Digital Systems CW2: Stopwatch

C – micro

#include <reg66x.h>

/////////////////////////
//// PIN DEFINITIONS ////
/////////////////////////

// Port 0 - in - in from CPLD
#define inDigits P0

// Port 1 - all out - out to CPLD
sbit outClock = P1^0;
sbit outReset = P1^1;

// Port 2 - all out - out to display
#define outDisplay P2

// Port 3 - in/out - buttons & speaker & CPLD control
sbit btnStartStop = P3^3;  // in
sbit outAcknowledge = P3^7;// out

/////////////////////////////
//// DECLARE SUBROUTINES ////
/////////////////////////////

void setupTimer();
void setupInputs();
void runningLoop();
void timerCallback();
void digitsCallback();
void controlCallback();
void resetCPLD();

///////////////////////////
//// DECLARE VARIABLES ////
///////////////////////////

int modeState = 0;
unsigned char hunths, tenths, seconds, tenSecs;

////////////////////////
//// PROGRAMME CODE ////
////////////////////////

void main() {
    setupTimer();
    setupInputs();
    resetCPLD();
   runningLoop();
}

void runningLoop() {
    while(1){
        outDisplay = tenSecs + 64;
        outDisplay = seconds + 128;
        outDisplay = tenths + 192;
        outDisplay = hunths + 0;
    }
}

void resetCPLD(){
    outReset = 1;
    outReset = 0;

    return;
}

void setupTimer() {
    TMOD = 0x01;        // M0 = 1 (Timer mode 1 - 16 bit mode)
    TL0 = 0xFF;         // 400Hz = 2304 delay count, 65535-2304 = 63231
    TH0 = 0xF6;         // TH0 = 0xF6 :: TL0(0xFF) = 0xF6FF = 63231
    ET0 = 1;                // T0 Interrupt enabled.
    EA = 1;             // Interrupts enabled.
    TR0 = 1;                // Begin timer.

    P2 = 0x00000000;

    return;
}

void setupInputs() {
    EA = 1;             // Interrupts enabled.
    IT0 = 1;                // Set on falling edge.
    IT1 = 1;                // Set on falling edge.
    EX0 = 1;            // Enable external interrupt 0.
    EX1 = 1;                // Enable external interrupt 1.

    modeState = 0;
    outAcknowledge = 0;

    return;
}

//////// INTERRUPT CALLBACKS ////////

void timerCallback() interrupt 1 using 2 {
   TR0 = 0;
    TL0 = 0xFF;         // 400Hz = 2304 delay count, 65535-2304 = 63231
    TH0 = 0xF6;         // TH0 = 0xF6 :: TL0(0xFF) = 0xF6FF = 63231
    TF0 = 0;
    TR0 = 1;                // Begin timer.
    outClock =~ outClock;   // Invert the clock output pin.
}

void digitsCallback() interrupt 0 {
    if(modeState == 0){
        seconds = inDigits & 0x0f;
    }else if(modeState == 1){
        tenSecs = inDigits & 0x0f;
    }else if(modeState == 2){
        hunths = inDigits & 0x0f;
    }else if(modeState == 3){
        tenths = inDigits & 0x0f;
    }

    if(modeState < 3){
        modeState++;
    }else{
        modeState = 0;
    }

    outAcknowledge = 1;
    outAcknowledge = 0;
}

void controlCallback() interrupt 2 {
    TR0 =~ TR0;
}

VHDL – CPLD

----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date:    12:25:53 03/18/2009
-- Design Name:
-- Module Name:    clock - Behavioral
-- Project Name:
-- Target Devices:
-- Tool versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity clock is
    Port ( clockSource : in STD_LOGIC;  -- clock pulse from micro
           reset : in  STD_LOGIC;        -- reset pulse from micro
           microAck : in STD_LOGIC;      -- acknowledgement from micro
              digit : out  STD_LOGIC_VECTOR (3 downto 0);
                                                      -- numbers to micro
              cpldRts : out STD_LOGIC);     -- ready to send to micro (ACTIVE LOW)
end clock;

architecture Behavioral of clock is

signal hunthsCount : std_logic_vector(3 downto 0);
signal tenthsCount : std_logic_vector(3 downto 0);
signal secondCount : std_logic_vector(3 downto 0);
signal tenSecCount : std_logic_vector(3 downto 0);
signal controlFlag : std_logic_vector(1 downto 0);

begin                                                         

process(reset, clockSource)
begin
if reset = '1' then
    hunthsCount
Pages:123»