1/*
2 * This program is copyright Alec Muffett 1993. The author disclaims all
3 * responsibility or liability with respect to it's usage or its effect
4 * upon hardware or computer systems, and maintains copyright as set out
5 * in the "LICENCE" document which accompanies distributions of Crack v4.0
6 * and upwards.
7 */
8
9#include <string.h>
10#include <stdarg.h>
11
12#ifndef IN_CRACKLIB
13
14#include "crack.h"
15
16#else
17
18#include "packer.h"
19
20static char __unused vers_id[] = "rules.c : v5.0p3 Alec Muffett 20 May 1993";
21
22static void
23Debug(int __unused val, const char *fmt, ...)
24{
25	va_list args;
26	va_start(args, fmt);
27	vfprintf(stderr, fmt, args);
28	va_end(args);
29}
30
31#endif
32
33#define RULE_NOOP	':'
34#define RULE_PREPEND	'^'
35#define RULE_APPEND	'$'
36#define RULE_REVERSE	'r'
37#define RULE_UPPERCASE	'u'
38#define RULE_LOWERCASE	'l'
39#define RULE_PLURALISE	'p'
40#define RULE_CAPITALISE	'c'
41#define RULE_DUPLICATE	'd'
42#define RULE_REFLECT	'f'
43#define RULE_SUBSTITUTE	's'
44#define RULE_MATCH	'/'
45#define RULE_NOT	'!'
46#define RULE_LT		'<'
47#define RULE_GT		'>'
48#define RULE_EXTRACT	'x'
49#define RULE_OVERSTRIKE	'o'
50#define RULE_INSERT	'i'
51#define RULE_EQUALS	'='
52#define RULE_PURGE	'@'
53#define RULE_CLASS	'?'	/* class rule? socialist ethic in cracker? */
54
55#define RULE_DFIRST	'['
56#define RULE_DLAST	']'
57#define RULE_MFIRST	'('
58#define RULE_MLAST	')'
59
60int
61Suffix(myword, suffix)
62    char *myword;
63    char *suffix;
64{
65    register int i;
66    register int j;
67    i = strlen(myword);
68    j = strlen(suffix);
69
70    if (i > j)
71    {
72	return (STRCMP((myword + i - j), suffix));
73    } else
74    {
75	return (-1);
76    }
77}
78
79char *
80Reverse(str)			/* return a pointer to a reversal */
81    register char *str;
82{
83    register int i;
84    register int j;
85    static char area[STRINGSIZE];
86    j = i = strlen(str);
87    while (*str)
88    {
89	area[--i] = *str++;
90    }
91    area[j] = '\0';
92    return (area);
93}
94
95char *
96Uppercase(str)			/* return a pointer to an uppercase */
97    register char *str;
98{
99    register char *ptr;
100    static char area[STRINGSIZE];
101    ptr = area;
102    while (*str)
103    {
104	*(ptr++) = CRACK_TOUPPER(*str);
105	str++;
106    }
107    *ptr = '\0';
108
109    return (area);
110}
111
112char *
113Lowercase(str)			/* return a pointer to an lowercase */
114    register char *str;
115{
116    register char *ptr;
117    static char area[STRINGSIZE];
118    ptr = area;
119    while (*str)
120    {
121	*(ptr++) = CRACK_TOLOWER(*str);
122	str++;
123    }
124    *ptr = '\0';
125
126    return (area);
127}
128
129char *
130Capitalise(str)			/* return a pointer to an capitalised */
131    register char *str;
132{
133    register char *ptr;
134    static char area[STRINGSIZE];
135    ptr = area;
136
137    while (*str)
138    {
139	*(ptr++) = CRACK_TOLOWER(*str);
140	str++;
141    }
142
143    *ptr = '\0';
144    area[0] = CRACK_TOUPPER(area[0]);
145    return (area);
146}
147
148char *
149Pluralise(string)		/* returns a pointer to a plural */
150    register char *string;
151{
152    register int length;
153    static char area[STRINGSIZE];
154    length = strlen(string);
155    strlcpy(area, string, sizeof(area));
156
157    if (!Suffix(string, "ch") ||
158	!Suffix(string, "ex") ||
159	!Suffix(string, "ix") ||
160	!Suffix(string, "sh") ||
161	!Suffix(string, "ss"))
162    {
163	/* bench -> benches */
164	strlcat(area, "es", sizeof(area));
165    } else if (length > 2 && string[length - 1] == 'y')
166    {
167	if (strchr("aeiou", string[length - 2]))
168	{
169	    /* alloy -> alloys */
170	    strlcat(area, "s", sizeof(area));
171	} else
172	{
173	    /* gully -> gullies */
174	    strlcpy(area + length - 1, "ies", sizeof(area)-length+1);
175	}
176    } else if (string[length - 1] == 's')
177    {
178	/* bias -> biases */
179	strlcat(area, "es", sizeof(area));
180    } else
181    {
182	/* catchall */
183	strlcat(area, "s", sizeof(area));
184    }
185
186    return (area);
187}
188
189char *
190Substitute(string, old, new)	/* returns pointer to a swapped about copy */
191    register char *string;
192    register char old;
193    register char new;
194{
195    register char *ptr;
196    static char area[STRINGSIZE];
197    ptr = area;
198    while (*string)
199    {
200	*(ptr++) = (*string == old ? new : *string);
201	string++;
202    }
203    *ptr = '\0';
204    return (area);
205}
206
207char *
208Purge(string, target)		/* returns pointer to a purged copy */
209    register char *string;
210    register char target;
211{
212    register char *ptr;
213    static char area[STRINGSIZE];
214    ptr = area;
215    while (*string)
216    {
217	if (*string != target)
218	{
219	    *(ptr++) = *string;
220	}
221	string++;
222    }
223    *ptr = '\0';
224    return (area);
225}
226/* -------- CHARACTER CLASSES START HERE -------- */
227
228/*
229 * this function takes two inputs, a class identifier and a character, and
230 * returns non-null if the given character is a member of the class, based
231 * upon restrictions set out below
232 */
233
234int
235MatchClass(class, input)
236    register char class;
237    register char input;
238{
239    register char c;
240    register int retval;
241    retval = 0;
242
243    switch (class)
244    {
245	/* ESCAPE */
246
247    case '?':			/* ?? -> ? */
248	if (input == '?')
249	{
250	    retval = 1;
251	}
252	break;
253
254	/* ILLOGICAL GROUPINGS (ie: not in ctype.h) */
255
256    case 'V':
257    case 'v':			/* vowels */
258	c = CRACK_TOLOWER(input);
259	if (strchr("aeiou", c))
260	{
261	    retval = 1;
262	}
263	break;
264
265    case 'C':
266    case 'c':			/* consonants */
267	c = CRACK_TOLOWER(input);
268	if (strchr("bcdfghjklmnpqrstvwxyz", c))
269	{
270	    retval = 1;
271	}
272	break;
273
274    case 'W':
275    case 'w':			/* whitespace */
276	if (strchr("\t ", input))
277	{
278	    retval = 1;
279	}
280	break;
281
282    case 'P':
283    case 'p':			/* punctuation */
284	if (strchr(".`,:;'!?\"", input))
285	{
286	    retval = 1;
287	}
288	break;
289
290    case 'S':
291    case 's':			/* symbols */
292	if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input))
293	{
294	    retval = 1;
295	}
296	break;
297
298	/* LOGICAL GROUPINGS */
299
300    case 'L':
301    case 'l':			/* lowercase */
302	if (islower(input))
303	{
304	    retval = 1;
305	}
306	break;
307
308    case 'U':
309    case 'u':			/* uppercase */
310	if (isupper(input))
311	{
312	    retval = 1;
313	}
314	break;
315
316    case 'A':
317    case 'a':			/* alphabetic */
318	if (isalpha(input))
319	{
320	    retval = 1;
321	}
322	break;
323
324    case 'X':
325    case 'x':			/* alphanumeric */
326	if (isalnum(input))
327	{
328	    retval = 1;
329	}
330	break;
331
332    case 'D':
333    case 'd':			/* digits */
334	if (isdigit(input))
335	{
336	    retval = 1;
337	}
338	break;
339
340    default:
341	Debug(1, "MatchClass: unknown class %c\n", class);
342	return (0);
343	break;
344    }
345
346    if (isupper(class))
347    {
348	return (!retval);
349    }
350    return (retval);
351}
352
353char *
354PolyStrchr(string, class)
355    register char *string;
356    register char class;
357{
358    while (*string)
359    {
360	if (MatchClass(class, *string))
361	{
362	    return (string);
363	}
364	string++;
365    }
366    return ((char *) 0);
367}
368
369char *
370PolySubst(string, class, new)	/* returns pointer to a swapped about copy */
371    register char *string;
372    register char class;
373    register char new;
374{
375    register char *ptr;
376    static char area[STRINGSIZE];
377    ptr = area;
378    while (*string)
379    {
380	*(ptr++) = (MatchClass(class, *string) ? new : *string);
381	string++;
382    }
383    *ptr = '\0';
384    return (area);
385}
386
387char *
388PolyPurge(string, class)	/* returns pointer to a purged copy */
389    register char *string;
390    register char class;
391{
392    register char *ptr;
393    static char area[STRINGSIZE];
394    ptr = area;
395    while (*string)
396    {
397	if (!MatchClass(class, *string))
398	{
399	    *(ptr++) = *string;
400	}
401	string++;
402    }
403    *ptr = '\0';
404    return (area);
405}
406/* -------- BACK TO NORMALITY -------- */
407
408int
409Char2Int(character)
410    char character;
411{
412    if (isdigit(character))
413    {
414	return (character - '0');
415    } else if (islower(character))
416    {
417	return (character - 'a' + 10);
418    } else if (isupper(character))
419    {
420	return (character - 'A' + 10);
421    }
422    return (-1);
423}
424
425char *
426Mangle(input, control)		/* returns a pointer to a controlled Mangle */
427    char *input;
428    char *control;
429{
430    int limit;
431    register char *ptr;
432    static char area[STRINGSIZE];
433    char area2[STRINGSIZE];
434    area[0] = '\0';
435    strlcpy(area, input, sizeof(area));
436
437    for (ptr = control; *ptr; ptr++)
438    {
439	switch (*ptr)
440	{
441	case RULE_NOOP:
442	    break;
443	case RULE_REVERSE:
444	    strlcpy(area, Reverse(area), sizeof(area));
445	    break;
446	case RULE_UPPERCASE:
447	    strlcpy(area, Uppercase(area), sizeof(area));
448	    break;
449	case RULE_LOWERCASE:
450	    strlcpy(area, Lowercase(area), sizeof(area));
451	    break;
452	case RULE_CAPITALISE:
453	    strlcpy(area, Capitalise(area), sizeof(area));
454	    break;
455	case RULE_PLURALISE:
456	    strlcpy(area, Pluralise(area), sizeof(area));
457	    break;
458	case RULE_REFLECT:
459	    strlcat(area, Reverse(area), sizeof(area));
460	    break;
461	case RULE_DUPLICATE:
462	    strlcpy(area2, area, sizeof(area2));
463	    strlcat(area, area2, sizeof(area));
464	    break;
465	case RULE_GT:
466	    if (!ptr[1])
467	    {
468		Debug(1, "Mangle: '>' missing argument in '%s'\n", control);
469		return ((char *) 0);
470	    } else
471	    {
472		limit = Char2Int(*(++ptr));
473		if (limit < 0)
474		{
475		    Debug(1, "Mangle: '>' weird argument in '%s'\n", control);
476		    return ((char *) 0);
477		}
478		if (strlen(area) <= limit)
479		{
480		    return ((char *) 0);
481		}
482	    }
483	    break;
484	case RULE_LT:
485	    if (!ptr[1])
486	    {
487		Debug(1, "Mangle: '<' missing argument in '%s'\n", control);
488		return ((char *) 0);
489	    } else
490	    {
491		limit = Char2Int(*(++ptr));
492		if (limit < 0)
493		{
494		    Debug(1, "Mangle: '<' weird argument in '%s'\n", control);
495		    return ((char *) 0);
496		}
497		if (strlen(area) >= limit)
498		{
499		    return ((char *) 0);
500		}
501	    }
502	    break;
503	case RULE_PREPEND:
504	    if (!ptr[1])
505	    {
506		Debug(1, "Mangle: prepend missing argument in '%s'\n", control);
507		return ((char *) 0);
508	    } else
509	    {
510		area2[0] = *(++ptr);
511		strlcpy(area2 + 1, area, sizeof(area2)-1);
512		strlcpy(area, area2, sizeof(area));
513	    }
514	    break;
515	case RULE_APPEND:
516	    if (!ptr[1])
517	    {
518		Debug(1, "Mangle: append missing argument in '%s'\n", control);
519		return ((char *) 0);
520	    } else
521	    {
522		register char *string;
523		string = area;
524		while (*(string++));
525		string[-1] = *(++ptr);
526		*string = '\0';
527	    }
528	    break;
529	case RULE_EXTRACT:
530	    if (!ptr[1] || !ptr[2])
531	    {
532		Debug(1, "Mangle: extract missing argument in '%s'\n", control);
533		return ((char *) 0);
534	    } else
535	    {
536		register int i;
537		int start;
538		int length;
539		start = Char2Int(*(++ptr));
540		length = Char2Int(*(++ptr));
541		if (start < 0 || length < 0)
542		{
543		    Debug(1, "Mangle: extract: weird argument in '%s'\n", control);
544		    return ((char *) 0);
545		}
546		strlcpy(area2, area, sizeof(area2));
547		for (i = 0; length-- && area2[start + i]; i++)
548		{
549		    area[i] = area2[start + i];
550		}
551		/* cant use strncpy() - no trailing NUL */
552		area[i] = '\0';
553	    }
554	    break;
555	case RULE_OVERSTRIKE:
556	    if (!ptr[1] || !ptr[2])
557	    {
558		Debug(1, "Mangle: overstrike missing argument in '%s'\n", control);
559		return ((char *) 0);
560	    } else
561	    {
562		register int i;
563		i = Char2Int(*(++ptr));
564		if (i < 0)
565		{
566		    Debug(1, "Mangle: overstrike weird argument in '%s'\n",
567			  control);
568		    return ((char *) 0);
569		} else
570		{
571		    ++ptr;
572		    if (area[i])
573		    {
574			area[i] = *ptr;
575		    }
576		}
577	    }
578	    break;
579	case RULE_INSERT:
580	    if (!ptr[1] || !ptr[2])
581	    {
582		Debug(1, "Mangle: insert missing argument in '%s'\n", control);
583		return ((char *) 0);
584	    } else
585	    {
586		register int i;
587		register char *p1;
588		register char *p2;
589		i = Char2Int(*(++ptr));
590		if (i < 0)
591		{
592		    Debug(1, "Mangle: insert weird argument in '%s'\n",
593			  control);
594		    return ((char *) 0);
595		}
596		p1 = area;
597		p2 = area2;
598		while (i && *p1)
599		{
600		    i--;
601		    *(p2++) = *(p1++);
602		}
603		*(p2++) = *(++ptr);
604		strlcpy(p2, p1, STRINGSIZE);
605		strlcpy(area, area2, sizeof(area));
606	    }
607	    break;
608	    /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */
609
610	case RULE_PURGE:	/* @x or @?c */
611	    if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
612	    {
613		Debug(1, "Mangle: delete missing arguments in '%s'\n", control);
614		return ((char *) 0);
615	    } else if (ptr[1] != RULE_CLASS)
616	    {
617		strlcpy(area, Purge(area, *(++ptr)), sizeof(area));
618	    } else
619	    {
620		strlcpy(area, PolyPurge(area, ptr[2]), sizeof(area));
621		ptr += 2;
622	    }
623	    break;
624	case RULE_SUBSTITUTE:	/* sxy || s?cy */
625	    if (!ptr[1] || !ptr[2] || (ptr[1] == RULE_CLASS && !ptr[3]))
626	    {
627		Debug(1, "Mangle: subst missing argument in '%s'\n", control);
628		return ((char *) 0);
629	    } else if (ptr[1] != RULE_CLASS)
630	    {
631		strlcpy(area, Substitute(area, ptr[1], ptr[2]), sizeof(area));
632		ptr += 2;
633	    } else
634	    {
635		strlcpy(area, PolySubst(area, ptr[2], ptr[3]), sizeof(area));
636		ptr += 3;
637	    }
638	    break;
639	case RULE_MATCH:	/* /x || /?c */
640	    if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
641	    {
642		Debug(1, "Mangle: '/' missing argument in '%s'\n", control);
643		return ((char *) 0);
644	    } else if (ptr[1] != RULE_CLASS)
645	    {
646		if (!strchr(area, *(++ptr)))
647		{
648		    return ((char *) 0);
649		}
650	    } else
651	    {
652		if (!PolyStrchr(area, ptr[2]))
653		{
654		    return ((char *) 0);
655		}
656		ptr += 2;
657	    }
658	    break;
659	case RULE_NOT:		/* !x || !?c */
660	    if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
661	    {
662		Debug(1, "Mangle: '!' missing argument in '%s'\n", control);
663		return ((char *) 0);
664	    } else if (ptr[1] != RULE_CLASS)
665	    {
666		if (strchr(area, *(++ptr)))
667		{
668		    return ((char *) 0);
669		}
670	    } else
671	    {
672		if (PolyStrchr(area, ptr[2]))
673		{
674		    return ((char *) 0);
675		}
676		ptr += 2;
677	    }
678	    break;
679	    /*
680	     * alternative use for a boomerang, number 1: a standard throwing
681	     * boomerang is an ideal thing to use to tuck the sheets under
682	     * the mattress when making your bed.  The streamlined shape of
683	     * the boomerang allows it to slip easily 'twixt mattress and
684	     * bedframe, and it's curve makes it very easy to hook sheets
685	     * into the gap.
686	     */
687
688	case RULE_EQUALS:	/* =nx || =n?c */
689	    if (!ptr[1] || !ptr[2] || (ptr[2] == RULE_CLASS && !ptr[3]))
690	    {
691		Debug(1, "Mangle: '=' missing argument in '%s'\n", control);
692		return ((char *) 0);
693	    } else
694	    {
695		register int i;
696		if ((i = Char2Int(ptr[1])) < 0)
697		{
698		    Debug(1, "Mangle: '=' weird argument in '%s'\n", control);
699		    return ((char *) 0);
700		}
701		if (ptr[2] != RULE_CLASS)
702		{
703		    ptr += 2;
704		    if (area[i] != *ptr)
705		    {
706			return ((char *) 0);
707		    }
708		} else
709		{
710		    ptr += 3;
711		    if (!MatchClass(*ptr, area[i]))
712		    {
713			return ((char *) 0);
714		    }
715		}
716	    }
717	    break;
718
719	case RULE_DFIRST:
720	    if (area[0])
721	    {
722		register int i;
723		for (i = 1; area[i]; i++)
724		{
725		    area[i - 1] = area[i];
726		}
727		area[i - 1] = '\0';
728	    }
729	    break;
730
731	case RULE_DLAST:
732	    if (area[0])
733	    {
734		register int i;
735		for (i = 1; area[i]; i++);
736		area[i - 1] = '\0';
737	    }
738	    break;
739
740	case RULE_MFIRST:
741	    if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
742	    {
743		Debug(1, "Mangle: '(' missing argument in '%s'\n", control);
744		return ((char *) 0);
745	    } else
746	    {
747		if (ptr[1] != RULE_CLASS)
748		{
749		    ptr++;
750		    if (area[0] != *ptr)
751		    {
752			return ((char *) 0);
753		    }
754		} else
755		{
756		    ptr += 2;
757		    if (!MatchClass(*ptr, area[0]))
758		    {
759			return ((char *) 0);
760		    }
761		}
762	    }
763	case RULE_MLAST:
764	    if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
765	    {
766		Debug(1, "Mangle: ')' missing argument in '%s'\n", control);
767		return ((char *) 0);
768	    } else
769	    {
770		register int i;
771
772		for (i = 0; area[i]; i++);
773
774		if (i > 0)
775		{
776		    i--;
777		} else
778		{
779		    return ((char *) 0);
780		}
781
782		if (ptr[1] != RULE_CLASS)
783		{
784		    ptr++;
785		    if (area[i] != *ptr)
786		    {
787			return ((char *) 0);
788		    }
789		} else
790		{
791		    ptr += 2;
792		    if (!MatchClass(*ptr, area[i]))
793		    {
794			return ((char *) 0);
795		    }
796		}
797	    }
798
799	default:
800	    Debug(1, "Mangle: unknown command %c in %s\n", *ptr, control);
801	    return ((char *) 0);
802	    break;
803	}
804    }
805    if (!area[0])		/* have we deweted de poor widdle fing away? */
806    {
807	return ((char *) 0);
808    }
809    return (area);
810}
811
812int
813PMatch(control, string)
814register char *control;
815register char *string;
816{
817    while (*string && *control)
818    {
819    	if (!MatchClass(*control, *string))
820    	{
821    	    return(0);
822    	}
823
824    	string++;
825    	control++;
826    }
827
828    if (*string || *control)
829    {
830    	return(0);
831    }
832
833    return(1);
834}
835