Path: utzoo!utgpu!news-server.csri.toronto.edu!mailrus!cs.utexas.edu!sdd.hp.com!mips!pacbell.com!pacbell!sactoh0!jak From: jak@sactoh0.UUCP (Jay A. Konigsberg) Newsgroups: alt.sources Subject: simped (the simple editor) version 2 Keywords: part2 of 2 Message-ID: <3461@sactoh0.UUCP> Date: 7 Jul 90 00:59:43 GMT Organization: Sacramento Public Access, Ca. USA Lines: 1056 simped.shar.02 / part02 of 02 -------------------------------- cut here ------------------------------ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # listtext.c # main.c # mod # modify.c # one # options.c # position.c # savefile.c # simped.doc # simped.h # This archive created: Fri Jul 6 17:53:12 1990 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'listtext.c'" '(1557 characters)' if test -f 'listtext.c' then echo shar: "will not over-write existing file 'listtext.c'" else sed 's/^ X//' << \SHAR_EOF > 'listtext.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * listtext - display the text buffer area starting at 'start' line X * number and pausing every PAUSE lines. X */ X X#include "simped.h" X#define BS "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" X#define SP " " X Xvoid listtext(text, count, start) Xchar **text; /* the text buffer area */ Xint count; /* lines in text area */ Xint start; /* the line to start the listing at */ X{ Xint puts(), X fputs(), X printf(); X Xint loop, /* loop var */ X inpchar; /* input char for pause */ X Xputs(""); Xif (! count) X { X puts("There is no text."); X } Xelse X { X for(loop=start-1; loop < count; ++loop) X { X if ( !((loop+start-1) % PAUSE) && loop && loop != count-1 X && loop != start-1 && count - start > PAUSE) X { X fputs ("[Press q to quit]", stdout); X if ( (inpchar = getchar()) == 'q' || inpchar == 'Q') X { X puts("q"); X return; X } X else X { X fputs(BS, stdout); X fputs(SP, stdout); X fputs(BS, stdout); X } X } X printf("%3d> ", loop+1); X fputs(text[loop], stdout); X } X } X} SHAR_EOF if test 1557 -ne "`wc -c < 'listtext.c'`" then echo shar: "error transmitting 'listtext.c'" '(should have been 1557 characters)' fi fi echo shar: "extracting 'main.c'" '(2336 characters)' if test -f 'main.c' then echo shar: "will not over-write existing file 'main.c'" else sed 's/^ X//' << \SHAR_EOF > 'main.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * main - general setup and signal functions. Its life's work is to make X * the bed for commands(). X */ X X#include "simped.h" X X/* XGlobal for interrupt routine: cleanup() X*/ Xstruct termio ttyset; /* terminal settings */ Xunsigned short c_lflag_hold; /* hold original values for reset */ Xunsigned char VEOF_hold; /* hold original value for reset */ X X/* global to replace \b in input */ Xunsigned char bs_char; X Xvoid main(argc, argv) Xint argc; Xchar **argv; X{ Xint fprintf(), X ioctl(); X Xvoid commands(), X exit(); X Xextern int cleanup(); X Xchar *options(); /* command line options - returns filename */ X Xchar *editfile=NULL; /* The file being edited */ X Xint newfile=FALSE, /* new or existing file */ X postnews=FALSE, /* flag for postnews compatability */ X append=FALSE; /* flag to always come up in append mode */ X XFILE *fopen(), X *fd=stdin; /* file descriptor for command line */ X X/* X * was a filename was entered on the command line? X */ X Xeditfile = options(argc, argv, &postnews, &append, editfile); Xif (editfile) X { X if ((fd = fopen(editfile, "r+")) == NULL) X { X newfile = TRUE; X if ((fd = fopen(editfile, "a")) == NULL) X { X fprintf(stderr,"fopen failed: error=%d\n", errno); X exit(2); X } X } X } Xelse X { X newfile = TRUE; X editfile = NULL; X fd = stdin; X } X X/* Xenter raw mode X*/ Xif ( ioctl(0, TCGETA, &ttyset) == -1 ) X { X fprintf(stderr, "ioctl: error=%d\n", errno); X exit(2); X } Xc_lflag_hold=ttyset.c_lflag; XVEOF_hold = ttyset.c_cc[4]; Xbs_char = ttyset.c_cc[2]; Xttyset.c_cc[4] = (unsigned char)1; Xttyset.c_lflag &= ~(ICANON | ECHO); X Xif ( ioctl(0, TCSETAW, &ttyset) == -1 ) X { X fprintf(stderr, "ioctl: error=%d\n",errno); X } Xsignal(SIGINT, cleanup); X Xcommands(editfile, &newfile, fd, postnews, append); X/*NOTREACHED*/ X} SHAR_EOF if test 2336 -ne "`wc -c < 'main.c'`" then echo shar: "error transmitting 'main.c'" '(should have been 2336 characters)' fi fi echo shar: "extracting 'mod'" '(32 characters)' if test -f 'mod' then echo shar: "will not over-write existing file 'mod'" else sed 's/^ X//' << \SHAR_EOF > 'mod' XThos sentence isstoobbe mortifd SHAR_EOF if test 32 -ne "`wc -c < 'mod'`" then echo shar: "error transmitting 'mod'" '(should have been 32 characters)' fi fi echo shar: "extracting 'modify.c'" '(4096 characters)' if test -f 'modify.c' then echo shar: "will not over-write existing file 'modify.c'" else sed 's/^ X//' << \SHAR_EOF > 'modify.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * modify - a multi-capability line editing function. X */ X X#include "simped.h" X Xchar **modify(text, linenum) Xchar **text; Xint linenum; X{ Xextern char *realloc(); X Xchar *getline(); X Xint printf(), X fprintf(), X fputs(), X puts(), X cleanup(); X Xint modifylen, /* length of the modify directive string */ X modifyinx, /* pointer inside the directive string */ X loopinx, /* for inserts and deletes */ X newlen, /* length of the new text */ X newinx, /* index for newtext */ X adjust=0, /* ins & del change text[], so text ref must adjust X Note: adjust may be positive *or* negitive */ X text_entered=0; X Xchar modifybuf[LINELEN+2], /* the modify directives */ X *newtext; /* a pointer into modifybuf for inserts */ X Xprintf("%3d> ", linenum); Xfputs(text[linenum-1], stdout); Xprintf("edit>"); Xif (getline(modifybuf, &text_entered, stdin, '\0', TRUE)) X { X puts("\nWarning: modify directive overflow, continuing"); X } X X/* remove the trailing '\n' */ Xmodifylen=strlen(modifybuf); Xmodifybuf[--modifylen]='\0'; X X/* now parse modify's buffer for the changes to text[linenum-1] */ Xfor(modifyinx=0; modifyinx < modifylen; ++modifyinx) X { X if (modifybuf[modifyinx] == ' ') X { X /* do nothing */ X } X/* else if ((modifybuf[modifyinx] >= 'a' && modifybuf[modifyinx] <= 'z' || X modifybuf[modifyinx] >= 'A' && modifybuf[modifyinx] <= 'Z')&& X modifyinx+adjust != strlen(text[linenum-1])-1) X { X * replace that char in text[] * X text[linenum-1][modifyinx + adjust]=modifybuf[modifyinx]; X } X*/ X else if (modifybuf[modifyinx] == '&') X { X /* replace char with a sp */ X text[linenum-1][modifyinx+adjust]=' '; X } X else if (modifybuf[modifyinx] == '#') X { X if (modifyinx+adjust != strlen(text[linenum-1])-1) X { X /* delete char - requires roll back. Note: adjust may be negitive */ X for (loopinx=modifyinx+adjust; loopinx <= strlen(text[linenum-1]); X ++loopinx) X { X text[linenum-1][loopinx]=text[linenum-1][loopinx+1]; X } X --adjust; X } X } X else if (modifybuf[modifyinx] == '^' || X modifyinx+adjust == strlen(text[linenum-1])-1) X { X /* insert char(s) - requires a roll out and maybe a realloc */ X if (modifyinx+adjust != strlen(text[linenum-1])-1) X newtext=(char *)&modifybuf[modifyinx+1]; X else X { X newtext=(char *)&modifybuf[modifyinx]; X } X if (newtext[0]=='#') /* insert a # */ X { X newlen=1; X } X else X { X for (newlen=0; newtext[newlen] != '#'; ++newlen) X { X if (newtext[newlen] == '\0') X break; X } X } X /* create the space needed */ X if((text[linenum-1]=realloc(text[linenum-1], X (unsigned int)(strlen(text[linenum-1])+newlen+adjust)))==NULL) X { X fprintf(stderr, "realloc: error=%d\n", errno); X cleanup(2); X } X /* do the roll out */ X for (loopinx=strlen(text[linenum-1])+newlen; X loopinx >= modifyinx-1+newlen; --loopinx) X { X text[linenum-1][loopinx]=text[linenum-1][loopinx-newlen]; X } X /* copy in the new text */ X newinx=0; X for (loopinx=modifyinx+adjust;loopinx ", linenum); Xfputs(text[linenum-1], stdout); Xreturn (text); X} SHAR_EOF if test 4096 -ne "`wc -c < 'modify.c'`" then echo shar: "error transmitting 'modify.c'" '(should have been 4096 characters)' fi fi echo shar: "extracting 'one'" '(83 characters)' if test -f 'one' then echo shar: "will not over-write existing file 'one'" else sed 's/^ X//' << \SHAR_EOF > 'one' XNow is the time Xfor all good Xmen to come to Xthe aid of Xtheir party. XAdding num six SHAR_EOF if test 83 -ne "`wc -c < 'one'`" then echo shar: "error transmitting 'one'" '(should have been 83 characters)' fi fi echo shar: "extracting 'options.c'" '(1547 characters)' if test -f 'options.c' then echo shar: "will not over-write existing file 'options.c'" else sed 's/^ X//' << \SHAR_EOF > 'options.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * get options for simped. -p postnews, -a append mode. X */ X X#include "simped.h" X Xchar *options (argc, argv, postnews, append, editfile) Xint argc; Xchar **argv, X *postnews, /* flag to add a '--' line to file */ X *append, /* always come up in append mode */ X *editfile; X{ X/* system calls and library functions */ Xint getopt(), X fprintf(), X exit(); X X/* variables */ X/* extern char *optarg; UNUSED */ Xextern int optind; X Xint option, /* getopt variable */ X error=FALSE; /* error checking the options */ X Xwhile ((option = getopt(argc, argv, "pa")) != -1) X { X switch (option) X { X case 'p': X *postnews=TRUE; X break; X case 'a': X *append=TRUE; X break; X case '?': X error=TRUE; X break; X } X } Xif ( argc > optind+1 ) X { X fprintf(stderr, "Max one filename on command line.\n"); X error=TRUE; X } Xelse X { X editfile=argv[optind]; X } X Xif (error) X { X fprintf(stderr, X "usage: simped [-p] [-a] filename\n"); X exit(2); X } Xreturn(editfile); X} SHAR_EOF if test 1547 -ne "`wc -c < 'options.c'`" then echo shar: "error transmitting 'options.c'" '(should have been 1547 characters)' fi fi echo shar: "extracting 'position.c'" '(1237 characters)' if test -f 'position.c' then echo shar: "will not over-write existing file 'position.c'" else sed 's/^ X//' << \SHAR_EOF > 'position.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * position - locate the first occurence of substr in string and X * return the subscript X */ X X#include "simped.h" X Xint position(substr, string) Xchar substr[]; Xchar string[]; X{ Xint strinx, /* index for string */ X subinx, /* index for substr */ X sublen, /* length of substr */ X found=FALSE; /* boolean - is a match found? */ X Xsublen=strlen(substr); Xfor(strinx=0; strinx < strlen(string); ++strinx) X { X for (subinx=0; subinx < sublen; ++subinx) X { X if (substr[subinx] == string[strinx+subinx]) X { X found=TRUE; X } X else X { X found=FALSE; X break; X } X } X if (subinx == sublen && found) X break; X } Xif (! found) X strinx = -1; Xreturn(strinx); X} SHAR_EOF if test 1237 -ne "`wc -c < 'position.c'`" then echo shar: "error transmitting 'position.c'" '(should have been 1237 characters)' fi fi echo shar: "extracting 'savefile.c'" '(2238 characters)' if test -f 'savefile.c' then echo shar: "will not over-write existing file 'savefile.c'" else sed 's/^ X//' << \SHAR_EOF > 'savefile.c' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * savefile - save the text buffer to a file. X */ X X#include "simped.h" X Xvoid savefile(editfile, newfile, fd, text, count) Xchar *editfile; Xint *newfile; XFILE *fd; Xchar **text; Xint count; X{ XFILE *fopen(); X Xint fclose(), X fprintf(), X printf(), X fputs(), X puts(), X unlink(), X cleanup(); X Xchar *getline(); X Xint textinx, /* index for the text buffer */ X text_entered=0, /* was something entered */ X ch=0; /* characters read in */ X Xchar buffer[LINELEN+2]; /* buffer for filename entry */ X Xputs(""); Xif (! editfile) X { X for(;;) X { X fputs("\nPlease enter a filename: ", stdout); X if (getline(buffer, &text_entered, stdin, '\0', TRUE)) X { X puts("Too many characters."); X return; X } X if( ! text_entered ) /* a return by itself was entered */ X return; X buffer[strlen(buffer)-1]='\0'; X editfile = buffer; X if ( (fd=fopen(editfile, "r")) == NULL) X { X break; X } X printf("%s: file exists! return to exit.\n", editfile); X } X fclose(fd); X if( (fd=fopen(editfile, "w")) == NULL ) X { X fprintf(stderr,"Can't open %s, probably an illegal filename",editfile); X return; X } X } X Xif ( ! *newfile ) X { X fclose(fd); X if( (fd=fopen(editfile, "w")) == NULL ) X { X fprintf(stderr,"fopen for write failed.\n"); X cleanup(2); X } X } Xelse X { X rewind(fd); X } Xfor (textinx=0; textinx < count; ++textinx) X { X ch += strlen(text[textinx]); X fputs(text[textinx], fd); X } Xif (ch > 0) X { X printf("\n%s: written, %d lines, %d characters\n", editfile, count, ch); X } Xelse X { X if ( unlink(editfile) == -1 ) X { X fprintf(stderr,"unlink failed: error=%d", errno); X } X else X { X puts("Empty file - no action"); X } X } Xcleanup(0); X} SHAR_EOF if test 2238 -ne "`wc -c < 'savefile.c'`" then echo shar: "error transmitting 'savefile.c'" '(should have been 2238 characters)' fi fi echo shar: "extracting 'simped.doc'" '(8932 characters)' if test -f 'simped.doc' then echo shar: "will not over-write existing file 'simped.doc'" else sed 's/^ X//' << \SHAR_EOF > 'simped.doc' X X X X simped() Unix SysV simped() X X X X Name X simped - a simple, bbs style editor. X X X Syntax X simped [-a] [-p] [filename] X (Note: only one file may be specified at a time) X X -a Causes the editor to always come up in append mode. This X fits nicely with the first 15 lines listed on entry. (Note: X the number of lines listed is configurable). X X -p Compatibility for postnews/inews. Because of the way X blank lines are stored by simped, inews deletes the first X line of text. This option will add a line with two dashes X "--" at the end of the header provided by postnews. X X Example for calling simped from a menu: X X EDITOR="/.../simped -p"; export EDITOR X postnews X EDITOR=".../simped"; export EDITOR X X Description X simped is a simple line oriented editor for use by people X who don't want to spend time learning a more flexible X editor like ed, ex or vi. It was designed with the intent X that useful work could be done immediately and without X reading this manual page or any of the online help. X X In keeping it simple much flexibility is lost. In an effort X to recover some of this and allow simped to act as a bridge X to more flexible editors, some commands may be wholly or X partially entered on a single line instead of a separate X line for each portion of a command. X X For example: those commands that require line numbers may X be entered one of two ways: X X Command? d (delete a line) X Line number: 1 X -or- X Command? d1 X X Also an extra editing command (Modify) has been added which X (once learned) makes editing much easier. Though there is X no complement to this command in any Unix editor, it does X regain some editing flexibility. X X Program entry X If no file is named on the command line, or the file is a X new file, an introductory message is printed and the user X is placed directly into input mode with the prompt: X X 1> _ X X simped 64 Page 1 simped X X X X simped() Unix SysV simped() X X X If the file exists, the last half page of text is listed X and the: X X Options: S)ave and quit, A)bort/cancel, L)ist text, E)dit line, X I)nsert line, D)elete line, C)ontinue, M)odify, H)elp X X Command? _ X X prompt is displayed, unless the -a option is specified, then X the user is dropped directly into C)ontinue mode. X X The number of lines listed is set at compile time by the X #define'd value PAUSE. X X Input mode X While in input mode characters typed in are placed in a X buffer. If the text being typed in spills over the end of X the line, it is automatically moved to the next line and X typing continues uninterrupted. X X Entering a on a line by itself will exit input X mode and display the command prompt. X X If you want a blank line, enter a space and . X X The length of the lines is set at compile time by the X #define'd value LINELEN. X X Command options: X X * S)ave and quit X X Saves the file and quits the editor. X No options. X X * A)bort/cancel X X Verifies that no save is to be done and then abandons any X editing that was done. X No options. X X * List text X X Lists the text entered, pausing every half page or so. The X number of lines listed is set at compile time. X X Options: A starting line number may be entered. A return X will start listing at line 1. X X X X X X X X X X X X simped 130 Page 2 simped X X X X simped() Unix SysV simped() X X X * Edit line X X Substitute old text for new text on a line. X X Example 1: X X Command? e X Line number: 1 X X 1> A line of text X X Old text: f t X New text: f modified t X X 1> A line of modified text X X (returns to the command prompt) X X X Example 2: X X Command? e1/f t/f modified t/ X X 1> A line of modified text X X (returns to the command prompt) X X To insert characters at the beginning of a line, enter X nothing for the "Old text:" and the characters to be X inserted for the "New text:". X X To delete characters, enter them in the "Old text:" and X nothing in the new text. X X Entering nothing in both "Old text:" and "New text:" will X result in no change. X X * Insert line X X Insert lines of text BEFORE the line number specified. The X user is dropped into 'insert mode'. X X Option: The line number you want the inserted text to X appear BEFORE. X X * Delete line X X Delete a line. The line will be printed and you must enter X a 'y' for the line to be deleted. X X Option: The line number you want deleted. X X X X X X X X X simped 196 Page 3 simped X X X X simped() Unix SysV simped() X X X X * Continue X X Continue entry. Drops the user into input mode AFTER the X last line in the file. X X No options. X X * Help X X An online source of information about commands. X X * Modify X X This is a multi-use editing function added in a effort X to regain some of the flexibility lost in making this X editor simple. X X Option: The line number you want to modify. X X This command can: X X - replace text X - put blanks in place of characters X - delete text X - insert text X X The line is printed and and the cursor is placed under the X first character on the next line. Changes are made by placing X one or more of the available options below the portion of X the line. X X OPTION EXPLANATION X ------ ----------- X ^text# Inserts the characters between the '^' X and the '#' BEFORE the character X pointed to by the '^'. Inside the '^' X and the '#', both the '&' and the '^' X are treated as regular characters. X X ^# Inserts a # before the ^ (special case) X X # (When not the first character after a X '^') causes the character above it to X be deleted and the space closed. X X & Replaces the character above it with a X blank space. X X (space) No effect. X X ANY OTHER CHARACTER WILL REPLACE THE CHARACTER ABOVE IT! X ======================================================== X X X X X X X simped 262 Page 4 simped X X X X simped() Unix SysV simped() X X X Example: X X Command? m1 X X 1> Thos sentence isstoobbe mortifd X edit> i ^is the #### #& d ^ie# X X 1> This is the sentence to be modified X X Command? _ X X X X Bugs X X If the terminal erase character is set to something other than X ^H (\010) like DEL, hitting a BS will place a ^H in the text. X X X/* X * Copyright (C) 1990 by Jay Konigsberg. X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X(Translation: you may use this software freely, but if you sell it, I get Xsome money. The amount depends on the selling price). X X X X X X X X X X X X X X X X X X X X X X X X X X X simped 328 Page 5 simped SHAR_EOF if test 8932 -ne "`wc -c < 'simped.doc'`" then echo shar: "error transmitting 'simped.doc'" '(should have been 8932 characters)' fi fi echo shar: "extracting 'simped.h'" '(878 characters)' if test -f 'simped.h' then echo shar: "will not over-write existing file 'simped.h'" else sed 's/^ X//' << \SHAR_EOF > 'simped.h' X X/* X * Copyright (C) 1990 by Jay Konigsberg. mail: jak@sactoh0 X * X * Permission to use, copy, modify, and distribute this software and its X * documentation is hereby granted, provided that the above copyright X * notice appear in all copies and that both the copyright notice and X * this permission notice appear in supporting documentation. This software X * is provided "as is" without express or implied warranty. However, the X * author retains all Copyright priviliges and rights to renumeration if X * this software is sold. X */ X X/* X * simped.h - just a header file, nothing special X */ X X#include X#include X#include X#include X#include X#include X#include X X#define PTR_CHUNK 50 X#define MAXNAMESIZ 14 X#define TRUE 1 X#define FALSE 0 X#define FUDGE 3 X Xint _filbuf(), X _flsbuf(); SHAR_EOF if test 878 -ne "`wc -c < 'simped.h'`" then echo shar: "error transmitting 'simped.h'" '(should have been 878 characters)' fi fi exit 0 # End of shell archive -- ------------------------------------------------------------- Jay @ SAC-UNIX, Sacramento, Ca. UUCP=...pacbell!sactoh0!jak If something is worth doing, its worth doing correctly.