1/*
2 * dig_opt.c --
3 *
4 *	Implements the C level procedures handling option processing
5 *	for message digest generators.
6 *
7 *
8 * Copyright (c) 1996 Andreas Kupries (a.kupries@westend.com)
9 * All rights reserved.
10 *
11 * Permission is hereby granted, without written agreement and without
12 * license or royalty fees, to use, copy, modify, and distribute this
13 * software and its documentation for any purpose, provided that the
14 * above copyright notice and the following two paragraphs appear in
15 * all copies of this software.
16 *
17 * IN NO EVENT SHALL I LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
18 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
19 * SOFTWARE AND ITS DOCUMENTATION, EVEN IF I HAVE BEEN ADVISED OF THE
20 * POSSIBILITY OF SUCH DAMAGE.
21 *
22 * I SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
25 * I HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
26 * ENHANCEMENTS, OR MODIFICATIONS.
27 *
28 * CVS: $Id: dig_opt.c,v 1.11 2009/05/07 04:57:27 andreas_kupries Exp $
29 */
30
31#include "transformInt.h"
32
33/*
34 * forward declarations of all internally used procedures.
35 */
36
37static Trf_Options CreateOptions _ANSI_ARGS_ ((ClientData clientData));
38static void        DeleteOptions _ANSI_ARGS_ ((Trf_Options options,
39					       ClientData clientData));
40static int         CheckOptions  _ANSI_ARGS_ ((Trf_Options options,
41					       Tcl_Interp* interp,
42					       CONST Trf_BaseOptions* baseOptions,
43					       ClientData clientData));
44static int         SetOption     _ANSI_ARGS_ ((Trf_Options options,
45					       Tcl_Interp* interp,
46					       CONST char* optname,
47					       CONST Tcl_Obj* optvalue,
48					       ClientData clientData));
49
50static int         QueryOptions  _ANSI_ARGS_ ((Trf_Options options,
51					       ClientData clientData));
52
53static int         TargetType _ANSI_ARGS_ ((Tcl_Interp* interp,
54					    CONST char* typeString,
55					    int* isChannel));
56
57static int         DigestMode _ANSI_ARGS_ ((Tcl_Interp* interp,
58					    CONST char* modeString,
59					    int* mode));
60
61/*
62 *------------------------------------------------------*
63 *
64 *	TrfMDOptions --
65 *
66 *	------------------------------------------------*
67 *	Accessor to the set of vectors realizing option
68 *	processing for message digest generators.
69 *	------------------------------------------------*
70 *
71 *	Sideeffects:
72 *		None.
73 *
74 *	Result:
75 *		See above.
76 *
77 *------------------------------------------------------*
78 */
79
80Trf_OptionVectors*
81TrfMDOptions ()
82{
83  static Trf_OptionVectors optVec = /* THREADING: constant, read-only => safe */
84    {
85      CreateOptions,
86      DeleteOptions,
87      CheckOptions,
88      NULL,      /* no string procedure for 'SetOption' */
89      SetOption,
90      QueryOptions,
91      NULL       /* unseekable, unchanged by options */
92    };
93
94  return &optVec;
95}
96
97/*
98 *------------------------------------------------------*
99 *
100 *	CreateOptions --
101 *
102 *	------------------------------------------------*
103 *	Create option structure for message digest generators.
104 *	------------------------------------------------*
105 *
106 *	Sideeffects:
107 *		Allocates memory and initializes it as
108 *		option structure for message digest generators.
109 *
110 *	Result:
111 *		A reference to the allocated block of
112 *		memory.
113 *
114 *------------------------------------------------------*
115 */
116
117static Trf_Options
118CreateOptions (clientData)
119ClientData clientData;
120{
121  TrfMDOptionBlock* o;
122
123  o			= (TrfMDOptionBlock*) ckalloc (sizeof (TrfMDOptionBlock));
124  o->behaviour		= TRF_IMMEDIATE; /* irrelevant until set by 'CheckOptions' */
125  o->mode		= TRF_UNKNOWN_MODE;
126  o->readDestination	= (char*) NULL;
127  o->writeDestination	= (char*) NULL;
128  o->rdIsChannel        = 0;
129  o->wdIsChannel        = 1;
130  o->matchFlag		= (char*) NULL;
131  o->vInterp		= (Tcl_Interp*) NULL;
132  o->rdChannel		= (Tcl_Channel) NULL;
133  o->wdChannel		= (Tcl_Channel) NULL;
134
135  return (Trf_Options) o;
136}
137
138/*
139 *------------------------------------------------------*
140 *
141 *	DeleteOptions --
142 *
143 *	------------------------------------------------*
144 *	Delete option structure of a message digest generators.
145 *	------------------------------------------------*
146 *
147 *	Sideeffects:
148 *		A memory block allocated by 'CreateOptions'
149 *		is released.
150 *
151 *	Result:
152 *		None.
153 *
154 *------------------------------------------------------*
155 */
156
157static void
158DeleteOptions (options, clientData)
159Trf_Options options;
160ClientData  clientData;
161{
162  TrfMDOptionBlock* o = (TrfMDOptionBlock*) options;
163
164  if (o->readDestination) {
165    ckfree ((char*) o->readDestination);
166  }
167
168  if (o->writeDestination) {
169    ckfree ((char*) o->writeDestination);
170  }
171
172  if (o->matchFlag) {
173    ckfree ((char*) o->matchFlag);
174  }
175
176  ckfree ((char*) o);
177}
178
179/*
180 *------------------------------------------------------*
181 *
182 *	CheckOptions --
183 *
184 *	------------------------------------------------*
185 *	Check the given option structure for errors.
186 *	------------------------------------------------*
187 *
188 *	Sideeffects:
189 *		May modify the given structure to set
190 *		default values into uninitialized parts.
191 *
192 *	Result:
193 *		A standard Tcl error code.
194 *
195 *------------------------------------------------------*
196 */
197
198static int
199CheckOptions (options, interp, baseOptions, clientData)
200Trf_Options            options;
201Tcl_Interp*            interp;
202CONST Trf_BaseOptions* baseOptions;
203ClientData             clientData;
204{
205  TrfMDOptionBlock*                   o = (TrfMDOptionBlock*) options;
206  Trf_MessageDigestDescription* md_desc = (Trf_MessageDigestDescription*) clientData;
207
208  /*
209   * Call digest dependent check of environment first.
210   */
211
212  START (dig_opt:CheckOptions);
213
214  if (md_desc->checkProc != NULL) {
215    if (TCL_OK != (*md_desc->checkProc) (interp)) {
216      DONE (dig_opt:CheckOptions);
217      return TCL_ERROR;
218    }
219  }
220
221  /* TRF_IMMEDIATE: no options allowed
222   * TRF_ATTACH:    -mode required
223   *                TRF_ABSORB_HASH: -matchflag required (only if channel is read)
224   *                TRF_WRITE_HASH:  -write/read-destination required according to
225   *				      access mode of attached channel. If a channel
226   *                                  is used as target, then it has to be writable.
227   *                TRF_TRANSPARENT: see TRF_WRITE_HASH.
228   */
229
230  if (baseOptions->attach == (Tcl_Channel) NULL) {
231    if ((o->mode             != TRF_UNKNOWN_MODE) ||
232	(o->matchFlag        != (char*) NULL)     ||
233	(o->readDestination  != (char*) NULL)     ||
234	(o->writeDestination != (char*) NULL)) {
235      /* IMMEDIATE MODE */
236      Tcl_AppendResult (interp, "immediate: no options allowed", (char*) NULL);
237      DONE (dig_opt:CheckOptions);
238      return TCL_ERROR;
239    }
240  } else {
241    /* ATTACH MODE / FILTER */
242    if (o->mode == TRF_UNKNOWN_MODE) {
243      Tcl_AppendResult (interp, "attach: -mode not defined", (char*) NULL);
244      DONE (dig_opt:CheckOptions);
245      return TCL_ERROR;
246
247    } else if (o->mode == TRF_ABSORB_HASH) {
248      if ((baseOptions->attach_mode & TCL_READABLE) &&
249	  (o->matchFlag == (char*) NULL)) {
250	Tcl_AppendResult (interp, "attach: -matchflag not defined", (char*) NULL);
251	DONE (dig_opt:CheckOptions);
252	return TCL_ERROR;
253      }
254
255    } else if ((o->mode == TRF_WRITE_HASH) || (o->mode == TRF_TRANSPARENT)) {
256      if (o->matchFlag != (char*) NULL) {
257	Tcl_AppendResult (interp, "attach: -matchflag not allowed", (char*) NULL);
258	DONE (dig_opt:CheckOptions);
259	return TCL_ERROR;
260      }
261
262      if (baseOptions->attach_mode & TCL_READABLE) {
263	if (o->readDestination == (char*) NULL) {
264	  Tcl_AppendResult (interp, "attach, external: -read-destination missing",
265			    (char*) NULL);
266	  DONE (dig_opt:CheckOptions);
267	  return TCL_ERROR;
268	} else if (o->rdIsChannel) {
269	  int mode;
270	  o->rdChannel = Tcl_GetChannel (interp, (char*) o->readDestination, &mode);
271
272	  if (o->rdChannel == (Tcl_Channel) NULL) {
273	    DONE (dig_opt:CheckOptions);
274	    return TCL_ERROR;
275	  } else  if (! (mode & TCL_WRITABLE)) {
276	    Tcl_AppendResult (interp,
277			      "read destination channel '", o->readDestination,
278			      "' not opened for writing", (char*) NULL);
279	    DONE (dig_opt:CheckOptions);
280	    return TCL_ERROR;
281	  }
282	}
283      }
284
285      if (baseOptions->attach_mode & TCL_WRITABLE) {
286	if (o->writeDestination == (char*) NULL) {
287	  Tcl_AppendResult (interp, "attach, external: -write-destination missing",
288			    (char*) NULL);
289	  DONE (dig_opt:CheckOptions);
290	  return TCL_ERROR;
291	} else if (o->wdIsChannel) {
292	  int mode;
293
294	  o->wdChannel = Tcl_GetChannel (interp, (char*) o->writeDestination, &mode);
295
296	  if (o->wdChannel == (Tcl_Channel) NULL) {
297	    DONE (dig_opt:CheckOptions);
298	    return TCL_ERROR;
299	  } else  if (! (mode & TCL_WRITABLE)) {
300	    Tcl_AppendResult (interp,
301			      "write destination channel '", o->writeDestination,
302			      "' not opened for writing", (char*) NULL);
303	    DONE (dig_opt:CheckOptions);
304	    return TCL_ERROR;
305	  }
306	}
307      }
308    } else {
309      Tcl_Panic ("unknown mode given to dig_opt.c::CheckOptions");
310    }
311  }
312
313  o->behaviour = (baseOptions->attach == (Tcl_Channel) NULL ?
314		  TRF_IMMEDIATE :
315		  TRF_ATTACH);
316
317  PRINT ("Ok\n"); FL;
318  DONE (dig_opt:CheckOptions);
319  return TCL_OK;
320}
321
322/*
323 *------------------------------------------------------*
324 *
325 *	SetOption --
326 *
327 *	------------------------------------------------*
328 *	Define value of given option.
329 *	------------------------------------------------*
330 *
331 *	Sideeffects:
332 *		Sets the given value into the option
333 *		structure
334 *
335 *	Result:
336 *		A standard Tcl error code.
337 *
338 *------------------------------------------------------*
339 */
340
341static int
342SetOption (options, interp, optname, optvalue, clientData)
343Trf_Options options;
344Tcl_Interp* interp;
345CONST char* optname;
346CONST Tcl_Obj* optvalue;
347ClientData  clientData;
348{
349  /* Possible options:
350   *
351   *	-mode			absorb|write|transparent
352   *	-matchflag		<varname>
353   *	-write-destination	<channel> | <variable>
354   *	-read-destination	<channel> | <variable>
355   */
356
357  TrfMDOptionBlock* o = (TrfMDOptionBlock*) options;
358  CONST char*       value;
359
360  int len = strlen (optname);
361
362  value = Tcl_GetStringFromObj ((Tcl_Obj*) optvalue, NULL);
363
364  switch (optname [1]) {
365  case 'm':
366    if (len < 3)
367      goto unknown_option;
368
369    if (0 == strncmp (optname, "-mode", len)) {
370      return DigestMode (interp, value, &o->mode);
371
372    } else if (0 == strncmp (optname, "-matchflag", len)) {
373      if (o->matchFlag) {
374	ckfree (o->matchFlag);
375      }
376
377      o->vInterp   = interp;
378      o->matchFlag = strcpy (ckalloc (1 + strlen (value)), value);
379
380    } else
381      goto unknown_option;
382    break;
383
384  case 'w':
385    if (len < 8)
386      goto unknown_option;
387
388    if (0 == strncmp (optname, "-write-destination", len)) {
389      if (o->writeDestination) {
390	ckfree (o->writeDestination);
391      }
392
393      o->vInterp          = interp;
394      o->writeDestination = strcpy (ckalloc (1+strlen (value)), value);
395
396    } else if (0 == strncmp (optname, "-write-type", len)) {
397      return TargetType (interp, value, &o->wdIsChannel);
398    } else
399      goto unknown_option;
400    break;
401
402  case 'r':
403    if (len < 7)
404      goto unknown_option;
405
406    if (0 == strncmp (optname, "-read-destination", len)) {
407      if (o->readDestination) {
408	ckfree (o->readDestination);
409      }
410
411      o->vInterp         = interp;
412      o->readDestination = strcpy (ckalloc (1+strlen (value)), value);
413
414    } else if (0 == strncmp (optname, "-read-type", len)) {
415      return TargetType (interp, value, &o->rdIsChannel);
416    } else
417      goto unknown_option;
418    break;
419
420  default:
421    goto unknown_option;
422    break;
423  }
424
425  return TCL_OK;
426
427 unknown_option:
428  Tcl_AppendResult (interp, "unknown option '", optname, "', should be '-mode', '-matchflag', '-write-destination', '-write-type', '-read-destination' or '-read-type'", (char*) NULL);
429
430  return TCL_ERROR;
431}
432
433/*
434 *------------------------------------------------------*
435 *
436 *	QueryOptions --
437 *
438 *	------------------------------------------------*
439 *	Returns a value indicating wether the encoder or
440 *	decoder set of vectors is to be used by immediate
441 *	execution.
442 *	------------------------------------------------*
443 *
444 *	Sideeffects:
445 *		None
446 *
447 *	Result:
448 *		1 - use encoder vectors.
449 *		0 - use decoder vectors.
450 *
451 *------------------------------------------------------*
452 */
453
454static int
455QueryOptions (options, clientData)
456Trf_Options options;
457ClientData  clientData;
458{
459  /* Always use encoder for immediate execution */
460  return 1;
461}
462
463/*
464 *------------------------------------------------------*
465 *
466 *	TargetType --
467 *
468 *	------------------------------------------------*
469 *	Determines from a string what destination was
470 *	given to the message digest.
471 *	------------------------------------------------*
472 *
473 *	Sideeffects:
474 *		May leave an error message in the
475 *		interpreter result area.
476 *
477 *	Result:
478 *		A standard Tcl error code, in case of
479 *		success 'isChannel' is set too.
480 *
481 *------------------------------------------------------*
482 */
483
484static int
485TargetType (interp, typeString, isChannel)
486Tcl_Interp* interp;
487CONST char* typeString;
488int*        isChannel;
489{
490  int len = strlen (typeString);
491
492  switch (typeString [0]) {
493  case 'v':
494    if (0 == strncmp ("variable", typeString, len)) {
495      *isChannel = 0;
496    } else
497      goto unknown_type;
498    break;
499
500  case 'c':
501    if (0 == strncmp ("channel", typeString, len)) {
502      *isChannel = 1;
503    } else
504      goto unknown_type;
505    break;
506
507  default:
508  unknown_type:
509    Tcl_AppendResult (interp, "unknown target-type '",
510		      typeString, "'", (char*) NULL);
511    return TCL_ERROR;
512  }
513
514  return TCL_OK;
515}
516
517/*
518 *------------------------------------------------------*
519 *
520 *	DigestMode --
521 *
522 *	------------------------------------------------*
523 *	Determines the operation mode of the digest.
524 *	------------------------------------------------*
525 *
526 *	Sideeffects:
527 *		May leave an error message in the
528 *		interpreter result area.
529 *
530 *	Result:
531 *		A standard Tcl error code, in case of
532 *		success 'mode' is set too.
533 *
534 *------------------------------------------------------*
535 */
536
537static int
538DigestMode (interp, modeString, mode)
539Tcl_Interp* interp;
540CONST char* modeString;
541int*        mode;
542{
543  int len = strlen (modeString);
544
545  switch (modeString [0]) {
546  case 'a':
547    if (0 == strncmp (modeString, "absorb", len)) {
548      *mode = TRF_ABSORB_HASH;
549    } else
550      goto unknown_mode;
551    break;
552
553  case 'w':
554    if (0 == strncmp (modeString, "write", len)) {
555      *mode = TRF_WRITE_HASH;
556    } else
557      goto unknown_mode;
558    break;
559
560  case 't':
561    if (0 == strncmp (modeString, "transparent", len)) {
562      *mode = TRF_TRANSPARENT;
563    } else
564      goto unknown_mode;
565    break;
566
567  default:
568  unknown_mode:
569    Tcl_AppendResult (interp, "unknown mode '", modeString, "', should be 'absorb', 'write' or 'transparent'", (char*) NULL);
570    return TCL_ERROR;
571  }
572
573  return TCL_OK;
574}
575