12

I began to use org-mode for planning out my tasks in GTD-style system. Putting every org files in a directory of a Dropbox folder, I run emacs to edit / manage these files from three different local machines: Cygwin, Mac OS X, and Debian. As I also use MobileOrg to access these org files from my iPad, a checksums.dat file must be kept up-to-date when any changes are made. This can be done by running md5sum *.org > checksums.dat.

The problem is that there are three different commands for md5sum command: md5sum.exe in Cygwin, md5 in Mac OS X, and md5sum in Debian. The most ideal situation is a makefile, stored in a Dropbox foder, detects which command is available in the current machine and runs that command to do the md5 checksum operation.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • [gnu `autotools`](http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Autotools-Introduction.html) – Kevin Jan 13 '12 at 04:19
  • 1
    @Kevin That sounds like an overkill. I don't want to install a program in three distinct systems. In each system, a Dropbox folder is accessible as a local directory. When I run `make checksum` to generate checksum data under a Dropbox folder, I want the makefile uses an appropriate command after detecting which checksum command is available in the current machine. – Federico Magallanez Jan 13 '12 at 04:44
  • If you are going to do a significant amount of scripting, and the project is small, I'd suggest scons. – Faheem Mitha Jan 13 '12 at 08:58

4 Answers4

8

Possible commands to generate a checksum

Unfortunately, there's no standard utility to generate a cryptographic checksum. There is a standard utility to generate a CRC: cksum; this may be sufficient for your purpose of detecting changes in a non-hostile environment.

I would recommend using SHA1 rather than MD5. There aren't many systems that have an MD5 utility but no SHA1, and if you're going to use cryptographic checksums, you might as well use an algorithm with no known method to find collisions (assuming you also check the size).

One tool that's not standard but common and can calculate digests is OpenSSL. It's available for Cygwin, Debian and OSX, but unfortunately not part of the default installation on OSX.

openssl dgst -sha1

On OSX 10.6, there is a shasum utility, which is also present on Debian (it's part of the perl package) and I believe on Cygwin too. This is a Perl script. Most unix systems have Perl installed, so you could bundle that script alongside your makefile if you're worried about this script not being available everywhere.

Selecting the right command for your system

Ok, let's say you really can't find a command that works everywhere.

In the shell

Use the type built-in to see if a command is available.

sum=
for x in sha1sum sha1 shasum 'openssl dgst -sha1'; do
  if type "${x%% *}" >/dev/null 2>/dev/null; then sum=$x; break; fi
done
if [ -z "$sum" ]; then echo 1>&2 "Unable to find a SHA1 utility"; exit 2; fi
$sum *.org

GNU make

You can use the shell function to run a shell snippet when the makefile is loaded and store the output in a variable.

sum := $(shell { command -v sha1sum || command -v sha1 || command -v shasum; } 2>/dev/null)
%.sum: %
        $(sum) $< >$@

Portable (POSIX) make

You can only run shell commands in rule, so each rule that computes a checksum has to contain the lookup code. You can put the snippet in a variable. Remember that separate lines in rules are evaluated independently. Also remember that $ signs that are to be passed to the shell need to be escaped to $$.

determine_sum = \
        sum=; \
        for x in sha1sum sha1 shasum 'openssl dgst -sha1'; do \
          if type "$${x%% *}" >/dev/null 2>/dev/null; then sum=$$x; break; fi; \
        done; \
        if [ -z "$$sum" ]; then echo 1>&2 "Unable to find a SHA1 utility"; exit 2; fi

checksums.dat: FORCE
    $(determine_sum); \
    $$sum *.org
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • I had to use the `-P` option of the `type` command in order to get the "GNU make" method described above to work properly, so I ended up with `sum := $(shell { type -P sha1sum || type -P sha1 || type -P shasum; } 2>/dev/null)`. – Sean Mar 27 '15 at 05:08
  • @Sean Thanks for the bug report. `type -P` only works if your shell is bash, not if it's e.g. ksh or dash (which is the case on Ubuntu among others). You can use `command -v` for portability. – Gilles 'SO- stop being evil' Mar 27 '15 at 08:32
  • It appears you've switched from using `type` to using `command`. Should the other methods be updated as well? – Sean Mar 27 '15 at 16:30
  • @Sean The other methods are fine: `type` is a correct, portable way to test the presence of a command, the problem with it is that it produces output like `sha1sum is /usr/bin/sha1sum` rather than just the program name. This was the only place where I used the output of `type` rather than just its return value. – Gilles 'SO- stop being evil' Mar 27 '15 at 17:12
5

It's something of a hack, but you could test whether each exists and execute them if they do:

[ -x "`which md5 2>/dev/null`" ] && md5 newfile >>sums
[ -x "`which md5sum 2>/dev/null`" ] && md5sum newfile >>sums

I'm quite certain cygwin recognizes commands (including md5sum) without the .exe, but include it if you have to.

Kevin
  • 40,087
  • 16
  • 88
  • 112
  • 4
    `which` exit values are not portable. Use `type` instead (and you probably want to use `if; then; elif` to avoid having problems in there are multiple executables). `which` and `type` only look for executables anyway, so your test with -x is pointless. – Chris Down Jan 13 '12 at 07:00
  • more on using "type" : cmd=$(for cmd in foo md5 md5sum bar; do type $cmd >/dev/null 2>&1 && echo $cmd && break; done); $cmd file1 file2 file3 > md5.txt – michael Jan 14 '12 at 00:00
1

Either GNU autotools (as mentioned in a comment), or also CMake is an option.

GNU autotools are the more traditional approach, but (and perhaps speaking only for myself) I don't think people are really too excited at the prospect of initially setting a project up using them. Well, it's a learning curve, in any case. CMake is the newer kid on the block, and perhaps less common (and still has a learning curve). It has its own syntax, and it generates makefiles for your platform. CMake does has the distinct advantage of being not so un*x-centric; it works reliably on unix, windows and mac as well (e.g., it can generate msvc projects, for instance, instead of makefiles.)

Edit: and of course since 'makefiles' were suggested, this answer goes along with that presumption. But perhaps just a python script or (gasp) a simple Java program would be more appropriate for this task; the scripting/programming language would do this the same across platforms.

michael
  • 822
  • 6
  • 11
1

Since you are running emacs, then you could use emacs itself to compute the checksums: http://www.gnu.org/software/emacs/manual/html_node/elisp/MD5-Checksum.html.

Alien Life Form
  • 621
  • 5
  • 12