Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/expr.c
1069 views
1
/*
2
* expr.c -- The expression mode parser and the textual mode parser
3
* #included by alias.c -- DO NOT DELETE
4
*
5
* Copyright 1990 Michael Sandrof
6
* Copyright 1997 EPIC Software Labs
7
* See the COPYRIGHT file for more info
8
*
9
* $Id: expr.c 26 2008-04-30 13:57:56Z keaston $
10
*/
11
12
#include "irc.h"
13
#include <math.h>
14
15
#undef PANA_EXP
16
#undef PANA_EXP1
17
18
/* Function decls */
19
static void TruncateAndQuote(char **, const char *, int, const char *, char);
20
static void do_alias_string (char *, char *);
21
22
char *alias_string = NULL;
23
24
/************************** EXPRESSION MODE PARSER ***********************/
25
/* canon_number: canonicalizes number to something relevant */
26
/* If FLOATING_POINT_MATH isnt set, it truncates it to an integer */
27
static char *canon_number (char *input)
28
{
29
int end = strlen(input);
30
31
if (end)
32
end--;
33
else
34
return input; /* nothing to do */
35
36
if (get_int_var(FLOATING_POINT_MATH_VAR))
37
{
38
/* remove any trailing zeros */
39
while (input[end] == '0')
40
end--;
41
42
/* If we removed all the zeros and all that is
43
left is the decimal point, remove it too */
44
if (input[end] == '.')
45
end--;
46
47
input[end+1] = 0;
48
}
49
else
50
{
51
char *dot = strchr(input, '.');
52
if (dot)
53
*dot = 0;
54
}
55
56
return input;
57
}
58
59
60
/* Given a pointer to an operator, find the last operator in the string */
61
static char *lastop (char *ptr)
62
{
63
/* dont ask why i put the space in there. */
64
while (ptr[1] && strchr("!=<>&^|#+/%,-* ", ptr[1]))
65
ptr++;
66
return ptr;
67
}
68
69
70
71
72
#define NU_EXPR 0
73
#define NU_ASSN 1
74
#define NU_TERT 2
75
#define NU_CONJ 3
76
#define NU_BITW 4
77
#define NU_COMP 5
78
#define NU_ADD 6
79
#define NU_MULT 7
80
#define NU_UNIT 8
81
82
/*
83
* Cleaned up/documented by Jeremy Nelson, Feb 1996.
84
*
85
* What types of operators does ircII (EPIC) support?
86
*
87
* The client handles as 'special cases" the () and []
88
* operators which have ircII-specific meanings.
89
*
90
* The general-purpose operators are:
91
*
92
* additive class: +, -, ++, --, +=, -=
93
* multiplicative class: *, /, %, *=, /=, %=
94
* string class: ##, #=
95
* and-class: &, &&, &=
96
* or-class: |, ||, |=
97
* xor-class: ^, ^^, ^=
98
* equality-class: ==, !=
99
* relative-class: <, <=, >, >=
100
* negation-class: ~ (1s comp), ! (2s comp)
101
* selector-class: ?: (tertiary operator)
102
* list-class: , (comma)
103
*
104
* Backslash quotes any character not within [] or (),
105
* which have their own quoting rules (*sigh*)
106
*/
107
char *BX_next_unit (char *str, const char *args, int *arg_flag, int stage)
108
{
109
register char *ptr; /* pointer to the current op */
110
111
char *ptr2, /* used to point matching brackets */
112
*right, /* used to denote end of bracket-set */
113
*lastc, /* used to denote end of token-set */
114
*tmp = NULL,
115
op; /* the op were working on */
116
int got_sloshed = 0, /* If the last char was a slash */
117
display;
118
119
char *result1 = NULL, /* raw lefthand-side of operator */
120
*result2 = NULL, /* raw righthand-side of operator */
121
*varname = NULL; /* Where we store varnames */
122
long value1 = 0, /* integer value of lhs */
123
value2 = 0, /* integer value of rhs */
124
value3 = 0; /* integer value of operation */
125
double dvalue1 = 0.0, /* floating value of lhs */
126
dvalue2 = 0.0, /* floating value of rhs */
127
dvalue3 = 0.0; /* floating value of operation */
128
#ifdef PANA_EXP
129
union
130
{
131
char s[4];
132
unsigned long t;
133
} strlong;
134
#endif
135
136
/*
137
* These macros make my life so much easier, its not funny.
138
*/
139
140
/*
141
* An implied operation is one where the left-hand argument is
142
* "implied" because it is both an lvalue and an rvalue. We use
143
* the rvalue of the left argument when we do the actual operation
144
* then we do an assignment to the lvalue of the left argument.
145
*/
146
#define SETUP_IMPLIED(var1, var2, func) \
147
{ \
148
/* Save the type of op, skip over the X=. */ \
149
op = *ptr; \
150
*ptr++ = '\0'; \
151
ptr++; \
152
\
153
/* Figure out the variable name were working on */ \
154
varname = expand_alias(str, args, arg_flag, NULL); \
155
lastc = varname + strlen(varname) - 1; \
156
while (lastc > varname && *lastc == ' ') \
157
*lastc-- = '\0'; \
158
while (my_isspace(*varname)) \
159
varname++; \
160
\
161
/* Get the value of the implied argument */ \
162
result1 = get_variable(varname); \
163
var1 = (result1 && *result1) ? func (result1) : 0; \
164
new_free(&result1); \
165
\
166
/* Get the value of the explicit argument */ \
167
result2 = next_unit(ptr, args, arg_flag, stage); \
168
var2 = func (result2); \
169
new_free(&result2); \
170
}
171
172
/*
173
* This make sure the calculated value gets back into the lvalue of
174
* the left operator, and turns the display back on.
175
*/
176
#define CLEANUP_IMPLIED() \
177
{ \
178
/* Make sure were really an implied case */ \
179
if (ptr[-1] == '=' && stage == NU_ASSN) \
180
{ \
181
/* Turn off the display */ \
182
display = window_display; \
183
window_display = 0; \
184
\
185
/* Make sure theres an lvalue */ \
186
if (*varname) \
187
add_var_alias(varname, tmp); \
188
else \
189
debugyell("Invalid assignment: No lvalue"); \
190
\
191
/* Turn the display back on */ \
192
window_display = display; \
193
} \
194
new_free(&varname); \
195
}
196
197
/*
198
* This sets up an ordinary explicit binary operation, where both
199
* arguments are used as rvalues. We just recurse and get their
200
* values and then do the operation on them.
201
*/
202
#define SETUP_BINARY(var1, var2, func) \
203
{ \
204
/* Save the op were working on cause we clobber it. */ \
205
op = *ptr; \
206
*ptr++ = '\0'; \
207
\
208
/* Get the two explicit operands */ \
209
result1 = next_unit(str, args, arg_flag, stage); \
210
result2 = next_unit(ptr, args, arg_flag, stage); \
211
\
212
/* Convert them with the specified function */ \
213
var1 = func (result1); \
214
var2 = func (result2); \
215
\
216
/* Clean up the mess we created. */ \
217
new_free(&result1); \
218
new_free(&result2); \
219
}
220
221
222
/*
223
* This sets up a type-independant section of code for doing an
224
* operation when the X and X= forms are both valid.
225
*/
226
#define SETUP(var1, var2, func, STAGE) \
227
{ \
228
/* If its an X= op, do an implied operation */ \
229
if (ptr[1] == '=' && stage == NU_ASSN) \
230
SETUP_IMPLIED(var1, var2, func) \
231
\
232
/* Else if its just an X op, do a binary operation */ \
233
else if (ptr[1] != '=' && stage == STAGE) \
234
SETUP_BINARY(var1, var2, func) \
235
\
236
/* Its not our turn to do this operation, just punt. */ \
237
else \
238
{ \
239
ptr = lastop(ptr); \
240
break; \
241
} \
242
}
243
244
/* This does a setup for a floating-point operation. */
245
#define SETUP_FLOAT_OPERATION(STAGE) \
246
SETUP(dvalue1, dvalue2, atof, STAGE)
247
248
/* This does a setup for an integer operation. */
249
#define SETUP_INTEGER_OPERATION(STAGE) \
250
SETUP(value1, value2, my_atol, STAGE)
251
252
/* remove leading spaces */
253
while (my_isspace(*str))
254
++str;
255
256
/* If there's nothing there, return it */
257
if (!*str)
258
return m_strdup(empty_string);
259
260
261
/* find the end of the rest of the expression */
262
if ((lastc = str+strlen(str)) > str)
263
lastc--;
264
265
/* and remove trailing spaces */
266
while (my_isspace(*lastc))
267
*lastc-- = '\0';
268
269
if (!args)
270
args = empty_string;
271
272
/*
273
* If we're in the last parsing level, and this token is in parens,
274
* strip the parens and parse the insides immediately.
275
*/
276
if (stage == NU_UNIT && *lastc == ')' && *str == '(')
277
{
278
str++, *lastc-- = '\0';
279
return next_unit(str, args, arg_flag, NU_EXPR);
280
}
281
282
283
/*
284
* Ok. now lets get some work done.
285
*
286
* Starting at the beginning of the string, look for something
287
* resembling an operator. This divides the expression into two
288
* parts, a lhs and an rhs. The rhs starts at "str", and the
289
* operator is at "ptr". So if you do ptr = lastop(ptr), youll
290
* end up at the beginning of the rhs, which is the rest of the
291
* expression. You can then parse it at the same level as the
292
* current level and it will recursively trickle back an rvalue
293
* which you can then apply to the lvalue give the operator.
294
*
295
* PROS: This is a very simplistic setup and not (terribly) confusing.
296
* CONS: Every operator is evaluated right-to-left which is *WRONG*.
297
*/
298
299
for (ptr = str; *ptr; ptr++)
300
{
301
if (got_sloshed)
302
{
303
got_sloshed = 0;
304
continue;
305
}
306
307
switch(*ptr)
308
{
309
case '\\':
310
{
311
got_sloshed = 1;
312
continue;
313
}
314
315
/*
316
* Parentheses have two contexts:
317
* 1) (text) is a unary precedence operator. It is nonassoc,
318
* and simply parses the insides immediately.
319
* 2) text(text) is the function operator. It calls the
320
* specified function/alias passing it the given args.
321
*/
322
case '(':
323
{
324
int savec = 0;
325
/*
326
* If we're not in NU_UNIT, then we have a paren-set
327
* that (probably) is still an left-operand for some
328
* binary op. Anyhow, we just immediately parse the
329
* paren-set, as thats the general idea of parens.
330
*/
331
if (stage != NU_UNIT || ptr == str)
332
{
333
/*
334
* If there is no matching ), gobble up the
335
* entire expression.
336
*/
337
if (!(ptr2 = MatchingBracket(ptr+1, '(', ')')))
338
ptr = ptr + strlen(ptr) - 1;
339
else
340
{
341
#ifdef PANA_EXP1
342
if ((ptr+1) == ptr2 && stage > NU_ASSN)
343
stage = NU_UNIT-1;
344
#endif
345
ptr = ptr2;
346
}
347
break;
348
}
349
350
/*
351
* and if that token is a left-paren, then we have a
352
* function call. We gobble up the arguments and
353
* ship it all off to call_function.
354
*/
355
if ((ptr2 = MatchingBracket(ptr + 1, '(', ')')))
356
{
357
ptr2++;
358
savec = *ptr2;
359
*ptr2 = 0;
360
}
361
362
363
result1 = call_function(str, args, arg_flag);
364
365
if (savec)
366
*ptr2 = savec;
367
/*
368
* and what do we do with this value? Why we prepend
369
* it to the next token! This is actually a hack
370
* that if you have a NON-operator as the next token,
371
* it has an interesting side effect:
372
* ie:
373
* /eval echo ${foobar()4 + 3}
374
* where
375
* alias foobar {@ function_return = 2}
376
*
377
* you get '27' as a return value, "as-if" you had done
378
*
379
* /eval echo ${foobar() ## 4 + 3}
380
*
381
* Dont depend on this behavior.
382
*/
383
if (ptr && *ptr)
384
{
385
malloc_strcat(&result1, ptr);
386
result2 = next_unit(result1, args, arg_flag, stage);
387
new_free(&result1);
388
result1 = result2;
389
}
390
391
return result1;
392
}
393
394
/*
395
* Braces are used for anonymous functions:
396
* @ condition : {some code} : {some more code}
397
*
398
* Dont yell at me if you dont think its useful. Im just
399
* supporting it because it makes sense. And it saves you
400
* from having to declare aliases to do the parts.
401
*/
402
case '{':
403
{
404
display = window_display;
405
406
ptr2 = MatchingBracket(ptr + 1, LEFT_BRACE, RIGHT_BRACE);
407
if (!ptr2)
408
ptr2 = ptr + strlen(ptr) - 1;
409
410
if (stage != NU_UNIT)
411
{
412
ptr = ptr2;
413
break;
414
}
415
416
*ptr2++ = 0;
417
*ptr++ = 0;
418
419
/* Taken more or less from call_user_function */
420
make_local_stack(NULL/*"anonymous"*/);
421
window_display = 0;
422
add_local_alias("FUNCTION_RETURN", empty_string);
423
window_display = display;
424
425
will_catch_return_exceptions++;
426
parse_line(NULL, ptr, args, 0, 1, 1);
427
will_catch_return_exceptions--;
428
return_exception = 0;
429
430
result1 = get_variable("FUNCTION_RETURN");
431
destroy_local_stack();
432
if (!result1)
433
result1 = m_strdup(empty_string);
434
435
return result1;
436
}
437
438
/*
439
* Brackets have two contexts:
440
* [text] is the literal-text operator. The contents are
441
* not parsed as an lvalue, but as literal text.
442
* This also covers the case of the array operator,
443
* since it just appends whats in the [] set with what
444
* came before it.
445
*
446
* The literal text operator applies not only to entire
447
* tokens, but also to the insides of array qualifiers.
448
*/
449
case '[':
450
{
451
#ifdef PANA_EXP
452
int got_it = 0;
453
#endif
454
if (stage != NU_UNIT)
455
{
456
if (!(ptr2 = MatchingBracket(ptr+1, LEFT_BRACKET, RIGHT_BRACKET)))
457
ptr = ptr+strlen(ptr)-1;
458
else
459
{
460
#ifdef PANA_EXP1
461
if ((ptr+1) == ptr2 && stage > NU_ASSN)
462
stage = NU_UNIT-1;
463
#endif
464
ptr = ptr2;
465
}
466
break;
467
}
468
#ifdef PANA_EXP
469
strlong.t = 0;
470
{
471
#if 0
472
<hop> if little endian is ABCD and big endian is DCBA
473
<hop> ive heard of middle enddian which is CDAB
474
#endif
475
memcpy(&strlong.t, ptr, 4);
476
if ((strlong.t & 0xfff0ffff) == 0x5d30245b)
477
{
478
unsigned int k;
479
if (!(k = ((strlong.t & 0x000f0000) >> 16) & 0x07))
480
{
481
result1 = extract2(args, k, k);
482
got_it = *arg_flag = 1;
483
/* *str = 0;*/
484
*ptr = 0;
485
ptr = ptr + 4;
486
487
}
488
}
489
}
490
if (!got_it)
491
#endif
492
{
493
/* ptr points right after the [] set */
494
*ptr++ = '\0';
495
right = ptr;
496
497
/*
498
* At this point, we check to see if it really is a
499
* '[', and if it is, we skip over it.
500
*/
501
if ((ptr = MatchingBracket(right, LEFT_BRACKET, RIGHT_BRACKET)))
502
*ptr++ = '\0';
503
504
/*
505
* Here we expand what is inside the [] set, as
506
* literal text.
507
*/
508
#ifndef NO_CHEATING
509
/*
510
* Very simple heuristic... If its $<num> or
511
* $-<num>, handle it here. Otherwise, if its
512
* got no $'s, its a literal, otherwise, do the
513
* normal thing.
514
*/
515
if (*right == '$')
516
{
517
char *end = NULL;
518
long j = strtol(right + 1, &end, 10);
519
if (end && !*end)
520
{
521
if (j < 0)
522
result1 = extract2(args, SOS, -j);
523
else
524
result1 = extract2(args, j, j);
525
*arg_flag = 1;
526
}
527
/*
528
* Gracefully handle $X- here, too.
529
*/
530
else if (*end == '-' && !end[1])
531
{
532
result1 = extract2(args, j, EOS);
533
*arg_flag = 1;
534
}
535
else
536
result1 = expand_alias(right, args, arg_flag, NULL);
537
}
538
else if (!strchr(right, '$') && !strchr(right, '\\'))
539
result1 = m_strdup(right);
540
else
541
#endif
542
result1 = expand_alias(right, args, arg_flag, NULL);
543
544
}
545
/*
546
* You need to look closely at this, as this test
547
* is actually testing to see if (ptr != str) at the
548
* top of this case, which would imply that the []
549
* set was an array qualifier to some other variable.
550
*
551
* Before you ask "how do you know that?" Remember
552
* that if (ptr == str) at the beginning of the case,
553
* then when we *ptr++ = 0, we would then be setting
554
* *str to 0; so testing to see if *str is not zero
555
* tells us if (ptr == str) was true or not...
556
*/
557
if (*str)
558
{
559
/* array qualifier */
560
int size = strlen(str) + (result1 ? strlen(result1) : 0) + (ptr ? strlen(ptr) : 0) + 2;
561
562
result2 = alloca(size);
563
strcpy(result2, str);
564
strcat(result2, ".");
565
strcat(result2, result1);
566
new_free(&result1);
567
568
/*
569
* Notice of unexpected behavior:
570
*
571
* If $foobar.onetwo is "999"
572
* then ${foobar[one]two + 3} is "1002"
573
* Dont depend on this behavior.
574
*/
575
if (ptr && *ptr)
576
{
577
strcat(result2, ptr);
578
result1 = next_unit(result2, args, arg_flag, stage);
579
}
580
else
581
{
582
if (!(result1 = get_variable(result2)))
583
malloc_strcpy(&result1, empty_string);
584
}
585
}
586
587
/*
588
* Notice of unexpected behavior:
589
*
590
* If $onetwo is "testing",
591
* /eval echo ${[one]two} returns "testing".
592
* Dont depend on this behavior.
593
*/
594
else if (ptr && *ptr)
595
{
596
malloc_strcat(&result1, ptr);
597
result2 = next_unit(result1, args, arg_flag, stage);
598
new_free(&result1);
599
result1 = result2;
600
}
601
602
/*
603
* result1 shouldnt ever be pointing at an empty
604
* string here, but if it is, we just malloc_strcpy
605
* a new empty_string into it. This fixes an icky
606
* memory hog bug my making sure that a (long) string
607
* with a leading null gets replaced by a (small)
608
* string of size one. Capish?
609
*/
610
if (!*result1)
611
malloc_strcpy(&result1, empty_string);
612
613
return result1;
614
}
615
616
/*
617
* The addition and subtraction operators have four contexts:
618
* 1) + is a binary additive operator if there is an rvalue
619
* as the token to the left (ignored)
620
* 2) + is a unary magnitudinal operator if there is no
621
* rvalue to the left.
622
* 3) ++text or text++ is a unary pre/post in/decrement
623
* operator.
624
* 4) += is the binary implied additive operator.
625
*/
626
case '-':
627
#if 0
628
if (ptr[1] && (ptr[1] == '>'))
629
{
630
char *ptr3;
631
char savec;
632
varname = str, *ptr = 0;
633
ptr++; ptr++;
634
if (!*ptr)
635
break;
636
if ((ptr3 = MatchingBracket(varname + 1, '(', ')')))
637
{
638
ptr3++;
639
savec = *ptr3;
640
*ptr3 = 0;
641
}
642
643
/* now pointing to the member check if valid */
644
if (!(ptr2 = lookup_member(varname, ptr3, ptr, args)))
645
break;
646
647
}
648
#endif
649
case '+':
650
{
651
if (ptr[1] == ptr[0])
652
{
653
int prefix;
654
long r;
655
656
if (stage != NU_UNIT)
657
{
658
/*
659
* only one 'ptr++' because a 2nd
660
* one is done at the top of the
661
* loop after the 'break'.
662
*/
663
ptr++;
664
break;
665
}
666
667
if (ptr == str) /* prefix */
668
prefix = 1, ptr2 = ptr + 2;
669
else /* postfix */
670
prefix = 0, ptr2 = str, *ptr++ = 0;
671
672
varname = expand_alias(ptr2, args, arg_flag, NULL);
673
upper(varname);
674
675
if (!(result1 = get_variable(varname)))
676
malloc_strcpy(&result1,zero);
677
678
r = my_atol(result1);
679
if (*ptr == '+')
680
r++;
681
else
682
r--;
683
684
display = window_display;
685
window_display = 0;
686
add_var_alias(varname,ltoa(r));
687
window_display = display;
688
689
if (!prefix)
690
r--;
691
692
new_free(&result1);
693
new_free(&varname);
694
return m_strdup(ltoa(r));
695
}
696
697
/* Unary op is ignored */
698
else if (ptr == str)
699
break;
700
#if 0
701
if (get_int_var(FLOATING_POINT_MATH_VAR))
702
#endif
703
{
704
SETUP_FLOAT_OPERATION(NU_ADD)
705
706
if (op == '-')
707
dvalue3 = dvalue1 - dvalue2;
708
else
709
dvalue3 = dvalue1 + dvalue2;
710
711
tmp = m_sprintf("%f", dvalue3);
712
canon_number(tmp);
713
714
}
715
#if 0
716
else
717
{
718
SETUP_INTEGER_OPERATION(NU_ADD)
719
720
if (op == '-')
721
value3 = value1 - value2;
722
else
723
value3 = value1 + value2;
724
725
tmp = m_strdup(ltoa(value3));
726
}
727
#endif
728
CLEANUP_IMPLIED()
729
return tmp;
730
}
731
732
733
/*
734
* The Multiplication operators have two contexts:
735
* 1) * is a binary multiplicative op
736
* 2) *= is the implied binary multiplicative op
737
*/
738
case '/':
739
case '*':
740
case '%':
741
{
742
/* Unary op is ignored */
743
if (ptr == str)
744
break;
745
746
/* default value on error */
747
dvalue3 = 0.0;
748
749
/*
750
* XXXX This is an awful hack to support
751
* the ** (pow) operator. Sorry.
752
*/
753
if (ptr[0] == '*' && ptr[1] == '*' && stage == NU_MULT)
754
{
755
*ptr++ = '\0';
756
SETUP_BINARY(dvalue1, dvalue2, atof)
757
return m_sprintf("%f", pow(dvalue1, dvalue2));
758
}
759
760
761
SETUP_FLOAT_OPERATION(NU_MULT)
762
763
if (op == '*')
764
dvalue3 = dvalue1 * dvalue2;
765
else
766
{
767
if (dvalue2 == 0.0)
768
debugyell("Division by zero!");
769
770
else if (op == '/')
771
dvalue3 = dvalue1 / dvalue2;
772
else
773
dvalue3 = (int)dvalue1 % (int)dvalue2;
774
}
775
776
tmp = m_sprintf("%f", dvalue3);
777
canon_number(tmp);
778
CLEANUP_IMPLIED()
779
return tmp;
780
}
781
782
783
/*
784
* The # operator has three contexts:
785
* 1) ## is a binary string catenation operator
786
* 2) #= is an implied string catenation operator
787
* 3) #~ is an implied string prepend operator
788
*/
789
case '#':
790
{
791
if (ptr[1] == '#' && stage == NU_ADD)
792
{
793
*ptr++ = '\0';
794
ptr++;
795
result1 = next_unit(str, args, arg_flag, stage);
796
result2 = next_unit(ptr, args, arg_flag, stage);
797
malloc_strcat(&result1, result2);
798
new_free(&result2);
799
return result1;
800
}
801
else if (ptr[1] == '~' && stage == NU_ASSN)
802
{
803
char *sval1, *sval2;
804
805
ptr[1] = '=';
806
SETUP_IMPLIED(sval1, sval2, m_strdup);
807
malloc_strcat(&sval2, sval1);
808
new_free(&sval1);
809
tmp = sval2;
810
CLEANUP_IMPLIED()
811
return sval2;
812
}
813
else if (ptr[1] == '=' && stage == NU_ASSN)
814
{
815
char *sval1, *sval2;
816
817
SETUP_IMPLIED(sval1, sval2, m_strdup)
818
malloc_strcat(&sval1, sval2);
819
new_free(&sval2);
820
tmp = sval1;
821
CLEANUP_IMPLIED()
822
return sval1;
823
}
824
825
else
826
{
827
ptr = lastop(ptr);
828
break;
829
}
830
}
831
832
833
/*
834
* Reworked - Jeremy Nelson, Feb 1994
835
* Reworked again, Feb 1996 (jfn)
836
*
837
* X, XX, and X= are all supported, where X is one of "&" (and),
838
* "|" (or) and "^" (xor). The XX forms short-circuit, as they
839
* do in C and perl. X and X= forms are bitwise, XX is logical.
840
*/
841
case '&':
842
{
843
/* && is binary short-circuit logical and */
844
if (ptr[0] == ptr[1] && stage == NU_CONJ)
845
{
846
*ptr++ = '\0';
847
ptr++;
848
849
result1 = next_unit(str, args, arg_flag, stage);
850
if (check_val(result1))
851
{
852
result2 = next_unit(ptr, args, arg_flag, stage);
853
value3 = check_val(result2);
854
}
855
else
856
value3 = 0;
857
858
new_free(&result1);
859
new_free(&result2);
860
return m_strdup(value3 ? one : zero);
861
}
862
863
/* &= is implied binary bitwise and */
864
else if (ptr[1] == '=' && stage == NU_ASSN)
865
{
866
SETUP_IMPLIED(value1, value2, my_atol)
867
value1 &= value2;
868
tmp = m_strdup(ltoa(value1));
869
CLEANUP_IMPLIED();
870
return tmp;
871
}
872
873
/* & is binary bitwise and */
874
else if (ptr[1] != ptr[0] && ptr[1] != '=' && stage == NU_BITW)
875
{
876
SETUP_BINARY(value1, value2, my_atol)
877
return m_strdup(ltoa(value1 & value2));
878
}
879
880
else
881
{
882
ptr = lastop(ptr);
883
break;
884
}
885
}
886
887
888
case '|':
889
{
890
/* || is binary short-circuiting logical or */
891
if (ptr[0] == ptr[1] && stage == NU_CONJ)
892
{
893
*ptr++ = '\0';
894
ptr++;
895
896
result1 = next_unit(str, args, arg_flag, stage);
897
if (!check_val(result1))
898
{
899
result2 = next_unit(ptr, args, arg_flag, stage);
900
value3 = check_val(result2);
901
}
902
else
903
value3 = 1;
904
905
new_free(&result1);
906
new_free(&result2);
907
return m_strdup(value3 ? one : zero);
908
}
909
910
/* |= is implied binary bitwise or */
911
else if (ptr[1] == '=' && stage == NU_ASSN)
912
{
913
SETUP_IMPLIED(value1, value2, my_atol)
914
value1 |= value2;
915
tmp = m_strdup(ltoa(value1));
916
CLEANUP_IMPLIED();
917
return tmp;
918
}
919
920
/* | is binary bitwise or */
921
else if (ptr[1] != ptr[0] && ptr[1] != '=' && stage != NU_BITW)
922
{
923
SETUP_BINARY(value1, value2, my_atol)
924
return m_strdup(ltoa(value1 | value2));
925
}
926
927
else
928
{
929
ptr = lastop(ptr);
930
break;
931
}
932
}
933
934
case '^':
935
{
936
/* ^^ is binary logical xor */
937
if (ptr[0] == ptr[1] && stage == NU_CONJ)
938
{
939
*ptr++ = '\0';
940
ptr++;
941
942
value1 = check_val((result1 = next_unit(str, args, arg_flag, stage)));
943
value2 = check_val((result2 = next_unit(ptr, args, arg_flag, stage)));
944
new_free(&result1);
945
new_free(&result2);
946
947
return m_strdup((value1 ^ value2) ? one : zero);
948
}
949
950
/* ^= is implied binary bitwise xor */
951
else if (ptr[1] == '=' && stage == NU_ASSN) /* ^= op */
952
{
953
954
SETUP_IMPLIED(value1, value2, my_atol)
955
value1 ^= value2;
956
tmp = m_strdup(ltoa(value1));
957
CLEANUP_IMPLIED();
958
return tmp;
959
}
960
961
/* ^ is binary bitwise xor */
962
else if (ptr[1] != ptr[0] && ptr[1] != '=' && stage == NU_BITW)
963
{
964
SETUP_BINARY(value1, value2, my_atol)
965
return m_strdup(ltoa(value1 ^ value2));
966
}
967
968
else
969
{
970
ptr = lastop(ptr);
971
break;
972
}
973
}
974
975
/*
976
* ?: is the tertiary operator. Confusing.
977
*/
978
case '?':
979
{
980
if (stage == NU_TERT)
981
{
982
*ptr++ = '\0';
983
result1 = next_unit(str, args, arg_flag, stage);
984
ptr2 = MatchingBracket(ptr, '?', ':');
985
986
/* Unbalanced :, or possibly missing */
987
if (!ptr2) /* ? but no :, ignore */
988
{
989
ptr = lastop(ptr);
990
break;
991
}
992
*ptr2++ = '\0';
993
if ( check_val(result1) )
994
result2 = next_unit(ptr, args, arg_flag, stage);
995
else
996
result2 = next_unit(ptr2, args, arg_flag, stage);
997
998
/* XXXX - needed? */
999
ptr2[-1] = ':';
1000
new_free(&result1);
1001
return result2;
1002
}
1003
1004
else
1005
{
1006
ptr = lastop(ptr);
1007
break;
1008
}
1009
}
1010
1011
/*
1012
* = is the binary assignment operator
1013
* == is the binary equality operator
1014
* =~ is the binary matching operator
1015
*/
1016
case '=':
1017
{
1018
if (ptr[1] == '~' && stage == NU_COMP)
1019
{
1020
*ptr++ = 0;
1021
ptr++;
1022
result1 = next_unit(str, args, arg_flag, stage);
1023
result2 = next_unit(ptr, args, arg_flag, stage);
1024
if (wild_match(result2, result1))
1025
malloc_strcpy(&result1, one);
1026
else
1027
malloc_strcpy(&result1, zero);
1028
new_free(&result2);
1029
return result1;
1030
}
1031
1032
if (ptr[1] != '=' && ptr[1] != '~' && stage == NU_ASSN)
1033
{
1034
*ptr++ = '\0';
1035
upper(str);
1036
result1 = expand_alias(str, args, arg_flag, NULL);
1037
result2 = next_unit(ptr, args, arg_flag, stage);
1038
1039
lastc = result1 + strlen(result1) - 1;
1040
while (lastc > result1 && *lastc == ' ')
1041
*lastc-- = '\0';
1042
for (varname = result1; my_isspace(*varname);)
1043
varname++;
1044
1045
display = window_display;
1046
window_display = 0;
1047
upper(varname);
1048
1049
if (*varname)
1050
add_var_alias(varname, result2);
1051
else
1052
debugyell("Invalid assignment: no lvalue");
1053
1054
window_display = display;
1055
new_free(&result1);
1056
return result2;
1057
}
1058
1059
else if (ptr[1] == '=' && stage == NU_COMP)
1060
{
1061
*ptr++ = '\0';
1062
ptr++;
1063
result1 = next_unit(str, args, arg_flag, stage);
1064
result2 = next_unit(ptr, args, arg_flag, stage);
1065
if (!my_stricmp(result1, result2))
1066
malloc_strcpy(&result1, one);
1067
else
1068
malloc_strcpy(&result1, zero);
1069
new_free(&result2);
1070
return result1;
1071
}
1072
1073
else
1074
{
1075
ptr = lastop(ptr);
1076
break;
1077
}
1078
}
1079
1080
/*
1081
* < is the binary relative operator
1082
* << is the binary bitwise shift operator (not supported)
1083
*/
1084
case '>':
1085
case '<':
1086
{
1087
if (ptr[1] == ptr[0] && stage == NU_BITW) /* << or >> op */
1088
{
1089
op = *ptr;
1090
*ptr++ = 0;
1091
ptr++;
1092
1093
result1 = next_unit(str, args, arg_flag, stage);
1094
result2 = next_unit(ptr, args, arg_flag, stage);
1095
1096
value1 = my_atol(result1);
1097
value2 = my_atol(result2);
1098
if (op == '>')
1099
value3 = value1 >> value2;
1100
else
1101
value3 = value1 << value2;
1102
new_free(&result1);
1103
new_free(&result2);
1104
return m_strdup(ltoa(value3));
1105
break;
1106
}
1107
1108
else if (ptr[1] != ptr[0] && stage == NU_COMP)
1109
{
1110
op = *ptr;
1111
if (ptr[1] == '=')
1112
value3 = 1, *ptr++ = '\0';
1113
else
1114
value3 = 0;
1115
1116
*ptr++ = '\0';
1117
result1 = next_unit(str, args, arg_flag, stage);
1118
result2 = next_unit(ptr, args, arg_flag, stage);
1119
1120
if ((my_isdigit(result1)) && (my_isdigit(result2)))
1121
{
1122
dvalue1 = atof(result1);
1123
dvalue2 = atof(result2);
1124
value1 = (dvalue1 == dvalue2) ? 0 : ((dvalue1 < dvalue2) ? -1 : 1);
1125
}
1126
else
1127
value1 = my_stricmp(result1, result2);
1128
1129
if (value1)
1130
{
1131
value2 = (value1 > 0) ? 1 : 0;
1132
if (op == '<')
1133
value2 = 1 - value2;
1134
}
1135
else
1136
value2 = value3;
1137
1138
new_free(&result1);
1139
new_free(&result2);
1140
return m_strdup(ltoa(value2));
1141
}
1142
1143
else
1144
{
1145
ptr = lastop(ptr);
1146
break;
1147
}
1148
}
1149
1150
/*
1151
* ~ is the 1s complement (bitwise negation) operator
1152
*/
1153
case '~':
1154
{
1155
if (ptr == str && stage == NU_UNIT)
1156
{
1157
result1 = next_unit(str+1, args, arg_flag, stage);
1158
if (isdigit((unsigned char)*result1))
1159
value1 = ~my_atol(result1);
1160
else
1161
value1 = 0;
1162
1163
return m_strdup(ltoa(value1));
1164
}
1165
1166
else
1167
{
1168
ptr = lastop(ptr);
1169
break;
1170
}
1171
}
1172
1173
/*
1174
* ! is the 2s complement (logical negation) operator
1175
* != is the inequality operator
1176
*/
1177
case '!':
1178
{
1179
if (ptr == str && stage == NU_UNIT)
1180
{
1181
result1 = next_unit(str+1, args, arg_flag, stage);
1182
1183
if (my_isdigit(result1))
1184
{
1185
value1 = my_atol(result1);
1186
value2 = value1 ? 0 : 1;
1187
}
1188
else
1189
value2 = ((*result1)?0:1);
1190
1191
new_free(&result1);
1192
return m_strdup(ltoa(value2));
1193
}
1194
1195
else if (ptr != str && ptr[1] == '~' && stage == NU_COMP)
1196
{
1197
*ptr++ = 0;
1198
ptr++;
1199
1200
result1 = next_unit(str, args, arg_flag, stage);
1201
result2 = next_unit(ptr, args, arg_flag, stage);
1202
1203
if (!wild_match(result2, result1))
1204
malloc_strcpy(&result1, one);
1205
else
1206
malloc_strcpy(&result1, zero);
1207
1208
new_free(&result2);
1209
return result1;
1210
}
1211
1212
else if (ptr != str && ptr[1] == '=' && stage == NU_COMP)
1213
{
1214
*ptr++ = '\0';
1215
ptr++;
1216
1217
result1 = next_unit(str, args, arg_flag, stage);
1218
result2 = next_unit(ptr, args, arg_flag, stage);
1219
1220
if (!my_stricmp(result1, result2))
1221
malloc_strcpy(&result1, zero);
1222
else
1223
malloc_strcpy(&result1, one);
1224
1225
new_free(&result2);
1226
return result1;
1227
}
1228
1229
else
1230
{
1231
ptr = lastop(ptr);
1232
break;
1233
}
1234
}
1235
1236
1237
/*
1238
* , is the binary right-hand operator
1239
*/
1240
case ',':
1241
{
1242
if (stage == NU_EXPR)
1243
{
1244
*ptr++ = '\0';
1245
result1 = next_unit(str, args, arg_flag, stage);
1246
result2 = next_unit(ptr, args, arg_flag, stage);
1247
new_free(&result1);
1248
return result2;
1249
}
1250
1251
else
1252
{
1253
ptr = lastop(ptr);
1254
break;
1255
}
1256
}
1257
1258
} /* end of switch */
1259
}
1260
1261
/*
1262
* If were not done parsing, parse it again.
1263
*/
1264
if (stage != NU_UNIT)
1265
return next_unit(str, args, arg_flag, stage + 1);
1266
1267
/*
1268
* If the result is a number, return it.
1269
*/
1270
if (my_isdigit(str))
1271
return m_strdup(str);
1272
1273
1274
/*
1275
* If the result starts with a #, or a @, its a special op
1276
*/
1277
if (*str == '#' || *str == '@')
1278
op = *str++;
1279
else
1280
op = '\0';
1281
1282
/*
1283
* Its not a number, so its a variable, look it up.
1284
*/
1285
if (!*str)
1286
result1 = m_strdup(args);
1287
else if (!(result1 = get_variable(str)))
1288
return m_strdup(empty_string);
1289
1290
/*
1291
* See if we have to take strlen or word_count on the variable.
1292
*/
1293
if (op)
1294
{
1295
if (op == '#')
1296
value1 = word_count(result1);
1297
else if (op == '@')
1298
value1 = strlen(result1);
1299
new_free(&result1);
1300
return m_strdup(ltoa(value1));
1301
}
1302
1303
/*
1304
* Nope. Just return the variable.
1305
*/
1306
return result1;
1307
}
1308
1309
/*
1310
* parse_inline: This evaluates user-variable expression. I'll talk more
1311
* about this at some future date. The ^ function and some fixes by
1312
* [email protected] (Troy Rollo)
1313
*/
1314
char *BX_parse_inline(char *str, const char *args, int *args_flag)
1315
{
1316
#ifndef WINNT
1317
if (x_debug & DEBUG_NEW_MATH)
1318
return matheval(str, args, args_flag);
1319
else
1320
#endif
1321
return next_unit(str, args, args_flag, NU_EXPR);
1322
}
1323
1324
1325
1326
/**************************** TEXT MODE PARSER *****************************/
1327
/*
1328
* expand_alias: Expands inline variables in the given string and returns the
1329
* expanded string in a new string which is malloced by expand_alias().
1330
*
1331
* Also unescapes anything that was quoted with a backslash
1332
*
1333
* Behaviour is modified by the following:
1334
* Anything between brackets (...) {...} is left unmodified.
1335
* If more_text is supplied, the text is broken up at
1336
* semi-colons and returned one at a time. The unprocessed
1337
* portion is written back into more_text.
1338
* Backslash escapes are unescaped.
1339
*/
1340
char *BX_expand_alias (const char *string, const char *args, int *args_flag, char **more_text)
1341
{
1342
char *buffer = NULL,
1343
*ptr,
1344
*stuff = NULL,
1345
*free_stuff,
1346
*quote_str = NULL;
1347
char quote_temp[2];
1348
char ch;
1349
int is_quote = 0;
1350
int unescape = 1;
1351
1352
if (!string || !*string)
1353
return m_strdup(empty_string);
1354
1355
if (*string == '@' && more_text)
1356
{
1357
unescape = 0;
1358
*args_flag = 1; /* Stop the @ command from auto appending */
1359
}
1360
quote_temp[1] = 0;
1361
1362
ptr = free_stuff = stuff = LOCAL_COPY(string);
1363
1364
if (more_text)
1365
*more_text = NULL;
1366
1367
while (ptr && *ptr)
1368
{
1369
if (is_quote)
1370
{
1371
is_quote = 0;
1372
++ptr;
1373
continue;
1374
}
1375
switch(*ptr)
1376
{
1377
case '$':
1378
{
1379
/*
1380
* The test here ensures that if we are in the
1381
* expression evaluation command, we don't expand $.
1382
* In this case we are only coming here to do command
1383
* separation at ';'s. If more_text is not defined,
1384
* and the first character is '@', we have come here
1385
* from [] in an expression.
1386
*/
1387
if (more_text && *string == '@')
1388
{
1389
ptr++;
1390
break;
1391
}
1392
*ptr++ = 0;
1393
if (!*ptr)
1394
break;
1395
m_strcat_ues(&buffer, stuff, unescape);
1396
1397
for (; *ptr == '^'; ptr++)
1398
{
1399
ptr++;
1400
if (!*ptr)
1401
break;
1402
quote_temp[0] = *ptr;
1403
malloc_strcat(&quote_str, quote_temp);
1404
}
1405
stuff = alias_special_char(&buffer, ptr, args, quote_str, args_flag);
1406
if (quote_str)
1407
new_free(&quote_str);
1408
ptr = stuff;
1409
break;
1410
}
1411
case ';':
1412
{
1413
if (!more_text)
1414
{
1415
ptr++;
1416
break;
1417
}
1418
*more_text = (char *)(string + (ptr - free_stuff) + 1);
1419
*ptr = '\0'; /* To terminate the loop */
1420
break;
1421
}
1422
case LEFT_PAREN:
1423
case LEFT_BRACE:
1424
{
1425
ch = *ptr;
1426
*ptr = '\0';
1427
m_strcat_ues(&buffer, stuff, unescape);
1428
stuff = ptr;
1429
*args_flag = 1;
1430
if (!(ptr = MatchingBracket(stuff + 1, ch,
1431
(ch == LEFT_PAREN) ?
1432
RIGHT_PAREN : RIGHT_BRACE)))
1433
{
1434
debugyell("Unmatched %c", ch);
1435
ptr = stuff + strlen(stuff+1)+1;
1436
}
1437
else
1438
ptr++;
1439
1440
*stuff = ch;
1441
ch = *ptr;
1442
*ptr = '\0';
1443
malloc_strcat(&buffer, stuff);
1444
stuff = ptr;
1445
*ptr = ch;
1446
break;
1447
}
1448
case '\\':
1449
{
1450
is_quote = 1;
1451
ptr++;
1452
break;
1453
}
1454
default:
1455
ptr++;
1456
break;
1457
}
1458
}
1459
if (stuff)
1460
m_strcat_ues(&buffer, stuff, unescape);
1461
1462
if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
1463
debugyell("Expanded [%s] to [%s]", string, buffer);
1464
#if 0
1465
if ((internal_debug & DEBUG_CMDALIAS) && alias_debug)
1466
debugyell("%d %s", debug_count++, string);
1467
#endif
1468
return buffer;
1469
}
1470
1471
1472
extern char *call_structure_internal(char *, const char *, char *, char *);
1473
1474
char *call_structure(char *name, const char *args, int *args_flag, char *rest, char *rest1)
1475
{
1476
char *ret = NULL, *tmp = NULL;
1477
char *lparen, *rparen;
1478
1479
if ((lparen = strchr(name, '(')))
1480
{
1481
if ((rparen = MatchingBracket(lparen + 1, '(', ')')))
1482
*rparen++ = 0;
1483
else
1484
debugyell("Unmatched lparen in function call [%s]", name);
1485
1486
*lparen++ = 0;
1487
}
1488
else
1489
lparen = empty_string;
1490
1491
tmp = expand_alias(lparen, args, args_flag, NULL);
1492
1493
if ((internal_debug & DEBUG_STRUCTURES) && !in_debug_yell)
1494
debugyell("%s->%s %d", name, rest, *args_flag);
1495
ret = call_structure_internal(name, tmp ? tmp : "0", rest, rest1);
1496
new_free(&tmp);
1497
return m_strdup(ret ? ret : empty_string);
1498
}
1499
1500
1501
/*
1502
* alias_special_char: Here we determine what to do with the character after
1503
* the $ in a line of text. The special characters are described more fully
1504
* in the help/ALIAS file. But they are all handled here. Parameters are the
1505
* return char ** pointer to which things are placed,
1506
* a ptr to the string (the first character of which is the special
1507
* character), the args to the alias, and a character indication what
1508
* characters in the string should be quoted with a backslash. It returns a
1509
* pointer to the character right after the converted alias.
1510
*
1511
* The args_flag is set to 1 if any of the $n, $n-, $n-m, $-m, $*, or $()
1512
* is used in the alias. Otherwise it is left unchanged.
1513
*/
1514
char *BX_alias_special_char(char **buffer, char *ptr, const char *args, char *quote_em, int *args_flag)
1515
{
1516
char *tmp,
1517
*tmp2,
1518
pad_char = 0;
1519
register unsigned char c;
1520
1521
int length;
1522
1523
length = 0;
1524
if ((c = *ptr) == LEFT_BRACKET)
1525
{
1526
ptr++;
1527
if ((tmp = MatchingBracket(ptr, '[', ']')))
1528
{
1529
*tmp = 0;
1530
if (*ptr == '$')
1531
{
1532
char *str;
1533
size_t slen;
1534
1535
str = expand_alias(ptr, args, args_flag, NULL);
1536
1537
slen = strlen(str);
1538
if (slen &&
1539
!isdigit((unsigned char)str[slen - 1]))
1540
pad_char = str[slen - 1];
1541
1542
length = my_atol(str);
1543
new_free(&str);
1544
}
1545
else
1546
{
1547
if (!isdigit((unsigned char)*(tmp - 1)))
1548
pad_char = *(tmp - 1);
1549
length = my_atol(ptr);
1550
}
1551
ptr = ++tmp;
1552
c = *ptr;
1553
1554
}
1555
else
1556
{
1557
say("Missing %c", RIGHT_BRACKET);
1558
return (ptr);
1559
}
1560
}
1561
tmp = ptr+1;
1562
switch (c)
1563
{
1564
/*
1565
* $(...) is the "dereference" case. It allows you to
1566
* expand whats inside of the parens and use *that* as a
1567
* variable name. The idea can be used for pointers of
1568
* sorts. Specifically, if $foo == [bar], then $($foo) is
1569
* actually the same as $(bar), which is actually $bar.
1570
* Got it?
1571
*
1572
* epic4pre1.049 -- I changed this somewhat. I dont know if
1573
* itll get me in trouble. It will continue to expand the
1574
* inside of the parens until the first character isnt a $.
1575
* since by all accounts the result of the expansion is
1576
* SUPPOSED to be an rvalue, obviously a leading $ precludes
1577
* this. However, there are definitely some cases where you
1578
* might want two or even three levels of indirection. Im
1579
* not sure i have any immediate ideas why, but Kanan probably
1580
* does since he's the one that needed this. (esl)
1581
*/
1582
case LEFT_PAREN:
1583
{
1584
char *sub_buffer = NULL,
1585
*tmp2 = NULL,
1586
*tmpsav = NULL,
1587
*ph = ptr + 1;
1588
1589
if ((ptr = MatchingBracket(ph, '(', ')')) ||
1590
(ptr = strchr(ph, ')')))
1591
*ptr++ = 0;
1592
else
1593
debugyell("Unmatched ( (continuing anyways)");
1594
1595
/*
1596
* Keep expanding as long as neccesary.
1597
*/
1598
do
1599
{
1600
tmp2 = expand_alias(tmp, args, args_flag, NULL);
1601
if (tmpsav)
1602
new_free(&tmpsav);
1603
tmpsav = tmp = tmp2;
1604
}
1605
while (*tmp == '$');
1606
alias_special_char(&sub_buffer, tmp, args,
1607
quote_em, args_flag);
1608
if (sub_buffer == NULL)
1609
sub_buffer = m_strdup(empty_string);
1610
1611
TruncateAndQuote(buffer, sub_buffer, length, quote_em, pad_char);
1612
new_free(&sub_buffer);
1613
new_free(&tmpsav);
1614
*args_flag = 1;
1615
return (ptr);
1616
1617
}
1618
case '!':
1619
{
1620
if ((ptr = (char *) strchr(tmp, '!')) != NULL)
1621
*(ptr++) = (char) 0;
1622
if ((tmp = do_history(tmp, empty_string)) != NULL)
1623
{
1624
TruncateAndQuote(buffer, tmp, length, quote_em, pad_char);
1625
new_free(&tmp);
1626
}
1627
return (ptr);
1628
}
1629
case LEFT_BRACE:
1630
{
1631
char *ph = ptr + 1;
1632
/*
1633
* BLAH. This didnt allow for nesting before.
1634
* How lame.
1635
*/
1636
if ((ptr = MatchingBracket(ph, '{', '}')) ||
1637
(ptr = strchr(ph, '}')))
1638
*ptr++ = 0;
1639
else
1640
debugyell("Unmatched { (continuing anyways)");
1641
1642
if ((tmp = parse_inline(tmp, args, args_flag)) != NULL)
1643
{
1644
TruncateAndQuote(buffer, tmp, length, quote_em, pad_char);
1645
new_free(&tmp);
1646
}
1647
return (ptr);
1648
}
1649
case DOUBLE_QUOTE:
1650
case '\'':
1651
{
1652
if ((ptr = strchr(tmp, c)))
1653
*ptr++ = 0;
1654
1655
alias_string = NULL;
1656
add_wait_prompt(tmp, do_alias_string, NULL,
1657
(c == DOUBLE_QUOTE) ? WAIT_PROMPT_LINE
1658
: WAIT_PROMPT_KEY, 1);
1659
while (!alias_string)
1660
io("Input Prompt");
1661
1662
TruncateAndQuote(buffer, alias_string, length,quote_em, pad_char);
1663
new_free(&alias_string);
1664
return (ptr);
1665
}
1666
case '*':
1667
{
1668
TruncateAndQuote(buffer, args, length, quote_em, pad_char);
1669
*args_flag = 1;
1670
return (ptr + 1);
1671
}
1672
1673
/* ok, ok. so i did forget something. */
1674
case '#':
1675
case '@':
1676
{
1677
char c2 = 0;
1678
char *sub_buffer = NULL;
1679
char *rest = NULL, *val;
1680
int dummy;
1681
1682
rest = after_expando(ptr + 1, 0, &dummy);
1683
if (rest == ptr + 1)
1684
{
1685
sub_buffer = m_strdup(args);
1686
*args_flag = 1;
1687
}
1688
else
1689
{
1690
c2 = *rest;
1691
*rest = 0;
1692
alias_special_char(&sub_buffer, ptr + 1,
1693
args, quote_em, args_flag);
1694
1695
*rest = c2;
1696
}
1697
1698
if (c == '#')
1699
val = m_strdup(ltoa(word_count(sub_buffer)));
1700
else
1701
val = m_strdup(ltoa(sub_buffer?strlen(sub_buffer):0));
1702
1703
TruncateAndQuote(buffer, val, length, quote_em, pad_char);
1704
new_free(&val);
1705
new_free(&sub_buffer);
1706
if (rest)
1707
*rest = c2;
1708
return rest;
1709
}
1710
1711
case '$':
1712
{
1713
TruncateAndQuote(buffer, "$", length, quote_em, pad_char);
1714
return ptr + 1;
1715
}
1716
default:
1717
{
1718
/*
1719
* Is it a numeric expando? This includes the
1720
* "special" expando $~.
1721
*/
1722
if (isdigit(c) || (c == '-') || c == '~')
1723
{
1724
int first, last;
1725
1726
*args_flag = 1;
1727
1728
/*
1729
* Handle $~. EOS especially handles this
1730
* condition.
1731
*/
1732
if (c == '~')
1733
{
1734
first = last = EOS;
1735
ptr++;
1736
}
1737
1738
/*
1739
* Handle $-X where X is some number. Note that
1740
* any leading spaces in the args list are
1741
* retained in the result, even if X is 0. The
1742
* stock client stripped spaces out on $-0, but
1743
* not for any other case, which was judged to be
1744
* in error. We always retain the spaces.
1745
*
1746
* If there is no number after the -, then the
1747
* hyphen is slurped and expanded to nothing.
1748
*/
1749
else if (c == '-')
1750
{
1751
first = SOS;
1752
ptr++;
1753
last = parse_number(&ptr);
1754
if (last == -1)
1755
return empty_string; /* error */
1756
}
1757
1758
/*
1759
* Handle $N, $N-, and $N-M, where N and M are
1760
* numbers.
1761
*/
1762
else
1763
{
1764
first = parse_number(&ptr);
1765
if (*ptr == '-')
1766
{
1767
ptr++;
1768
last = parse_number(&ptr);
1769
if (last == -1)
1770
last = EOS;
1771
}
1772
else
1773
last = first;
1774
}
1775
1776
/*
1777
* Protect against a crash. There
1778
* are some gross syntactic errors
1779
* that can be made that will result
1780
* in ''args'' being NULL here. That
1781
* will crash the client, so we have
1782
* to protect against that by simply
1783
* chewing the expando.
1784
*/
1785
if (!args)
1786
tmp2 = m_strdup(empty_string);
1787
else
1788
tmp2 = extract2(args, first, last);
1789
1790
TruncateAndQuote(buffer, tmp2, length, quote_em, pad_char);
1791
new_free(&tmp2);
1792
return (ptr ? ptr : empty_string);
1793
}
1794
1795
/*
1796
* Ok. So we know we're doing a normal rvalue
1797
* expando. Slurp it up.
1798
*/
1799
else
1800
{
1801
char *rest, d = 0;
1802
char *rest1 = NULL;
1803
int function_call = 0;
1804
int struct_call = 0;
1805
1806
rest = after_expando(ptr, 0, &function_call);
1807
if (*rest)
1808
{
1809
d = *rest;
1810
*rest = 0;
1811
}
1812
if ((d == '-') && *(rest + 1) == '>')
1813
{
1814
struct_call = 1;
1815
rest = rest + 2;
1816
function_call = 0;
1817
d = 0;
1818
}
1819
1820
if (function_call)
1821
tmp = call_function(ptr, args, args_flag);
1822
else if (struct_call)
1823
{
1824
rest1 = after_expando(rest, 0, &function_call);
1825
if (*rest1)
1826
{
1827
d = *rest1;
1828
if (*rest1)
1829
{
1830
*rest1 = 0;
1831
rest1++;
1832
}
1833
}
1834
tmp = call_structure(ptr, args, args_flag, rest, rest1);
1835
if ((d == '-') && (*rest1 == '>'))
1836
rest1 = strchr(rest1, ' '), d = 0;
1837
}
1838
else
1839
tmp = get_variable_with_args(ptr, args, args_flag);
1840
1841
if (!tmp)
1842
tmp = m_strdup(empty_string);
1843
TruncateAndQuote(buffer, tmp, length, quote_em, pad_char);
1844
new_free(&tmp);
1845
1846
if (struct_call)
1847
{
1848
if (d)
1849
rest = rest1 - 1;
1850
else
1851
rest = rest1;
1852
}
1853
if (d)
1854
*rest = d;
1855
return(rest);
1856
}
1857
}
1858
}
1859
return NULL;
1860
}
1861
1862
/*
1863
* TruncateAndQuote: This handles string width formatting and quoting for irc
1864
* variables when [] or ^x is specified.
1865
*/
1866
static void TruncateAndQuote(char **buff, const char *add, int length, const char *quote_em, char pad_char)
1867
{
1868
/*
1869
* Semantics:
1870
* If length is nonzero, then "add" will be truncated
1871
* to "length" characters
1872
* If length is zero, nothing is done to "add"
1873
* If quote_em is not NULL, then the resulting string
1874
* will be quoted and appended to "buff"
1875
* If quote_em is NULL, then the value of "add" is
1876
* appended to "buff"
1877
*/
1878
if (length)
1879
{
1880
char *buffer = NULL;
1881
buffer = alloca(abs(length)+1);
1882
strformat(buffer, add, length, pad_char ? pad_char:get_int_var(PAD_CHAR_VAR));
1883
add = buffer;
1884
}
1885
if (quote_em && add)
1886
{
1887
char *ptr = alloca(strlen(add) * 2 + 2);
1888
add = double_quote(add, quote_em, ptr);
1889
}
1890
1891
if (buff)
1892
malloc_strcat(buff, add);
1893
1894
return;
1895
}
1896
1897
static void do_alias_string (char *unused, char *input)
1898
{
1899
malloc_strcpy(&alias_string, input);
1900
}
1901
1902
1903