-
Notifications
You must be signed in to change notification settings - Fork 2
/
aa_macro.py
executable file
·6626 lines (6196 loc) · 177 KB
/
aa_macro.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python
import re
import imghdr
import struct
import imp
import time
import datetime
import subprocess
import random
try:
import acroclass
except:
pass
class macro(object):
"""Class to provide an HTML macro language
Author: fyngyrz (Ben)
Contact: fyngyrz@gmail.com (bugs, feature requests, kudos, bitter rejections)
Project: aa_macro.py
Homepage: https://github.com/fyngyrz/aa_macro
License: None. It's free. *Really* free. Defy invalid social and
legal norms.
Disclaimers: 1) Probably completely broken. Do Not Use. You were explicitly
warned. Phbbbbt.
2) My code is blackbox, meaning I wrote it without reference
to other people's code.
3) I can't check other people's contributions effectively,
so if you use any version of aa_macro.py that incorporates
accepted commits from others, you are risking the use of
OPC, which may or may not be protected by copyright,
patent, and the like, because our intellectual property
system is pathological. The risks and responsibilities
and any subsequent consequences are entirely yours. Have
you written your congresscritter about patent and
copyright reform yet?
Incep Date: June 17th, 2015 (for Project)
LastRev: January 28th, 2019 (for Class)
LastDocRev: January 28th, 2019 (for Class)
Version:
"""
def version_set(self):
return('1.0.137 Beta')
"""
Tab spacing: 4 (set your editor to this for sane formatting while reading)
Dev Env: OS X 10.6.8, Python 2.6.1 from inception
OS X 10.12, Python 2.7.10 as of Jan 31st, 2017
Status: BETA
1st-Rel: 1.0.0
Policies: 1) I will make every effort to never remove functionality or
alter existing functionality once past BETA stage. Anything
new will be implemented as something new, thus preserving all
behavior and the API. The only intentional exceptions to this
are if a bug is found that does not match the intended behavior,
or I determine there is some kind of security risk. What I
*will* do is not document older and less capable versions of a
function, unless the new functionality is incapable of doing
something the older version(s) could do. Remember, this only
applies to production code. Until the BETA status is removed,
ANYTHING may change. Sorry this was unclear previously.
Examples: At bottom. Run in shell like so: python aa_macro.py
The best way to use them is open a shell and run them there,
and open a shell with aa_macro.py in an editor or reader,
then scroll through the example results in the one shell as
you peruse the source for them in the other within aa_macro.py
Testing: python test_aa_macro.py
Typical Use: from aa_macro import *
mod = macro()
mod.do(text)
Warning: Do NOT use this to parse general user input without fully sanitizing that
input for subsequent string processing in Python FIRST, as well as for
output via your webserver (because <script>, etc.) Otherwise, you've
just created a huge security hole. Not Good! The default intention is that
class macro() is for authoring by you, the person who ISN'T trying to
hack your website, not access to the public, which may very well contain
someone who wants to do you wrong. Having said that, see the sanitize()
utility function within this class.
History: (for Class)
See changelog.md
Todo:
Anything with a * in the first column needs recoding
to incorporate (sep=X,)
Available built-ins:
====================
Note that () designate optional parameters, a|b designates alternate parameter a or b
Text Styling
------------
[p paraText]
[bq quotetext]
[b bold text]
[i italic text]
[u underlined text]
[color HEX3|HEX6 colored text] # HEX3 example: f09 (which means FF0099) HEX6 example: fe7842
Linking
-------
[url (sep=|,)(nam=Name,)(css=CSS,)(tgt=_target,)URLsepITEM]
Older Linking (will remain, but [url] is better)
--------------------------------------------------
[a (tab,)URL(,linked text)] # The WORD tab, not a tab character. As in a browser tab
[web URL (text)] # If you don't provide text, you get "On the web"
[link URL (text)] # If you don't provide text, you get the URL as the text
[urlencode URL] # converts certain chars to character entities, etc.:
Images
------
[img (title,)URL( linkTarget)] # makes a link if linktarget present
[lipath localImagePath] # sets filesystem path to local images. This is used by [limg] to
find the image and read it to obtain x,y dimensions
[lipath] and [wepath] first, then [limg]
[wepath localImagePath] # sets web path to images. This is used by [limg] to
find set the image's URL properly
[lipath] and [wepath] first, then [limg]
[limg (title=,)(alt=,)(target=,)ImageFileName] # makes a link if target present, also inserts img size
# [limg] can read the size of jpg, png, and gif
# Examples:
[lipath]
[limg mypic.png] - image in same dir as python cmd on host
- and in / of webserver
[lipath /usr/www/mysite.com/htdocs/]
[limg mypic.png] - image in /usr/www/mysite.com/htdocs/ on host
- and in / of webserver
[lipath /usr/www/mysite.com/htdocs/pics/]
[limg pics/mypic.png] - image in /usr/www/mysite.com/htdocs/pics/ on host
- and in /pics/ of webserver
HTML Lists
----------
[ul (lstyle=hstyle,)(istyle=hstyle,)(wrap=style,)(sep=X,)item1(Xitem2Xitem3...)]
[ol (lstyle=hstyle,)(istyle=hstyle,)(wrap=style,)(type=X)(start=N,)(sep=X,)item1(Xitem2Xitem3...)]
[iful (wrap=style,)(sep=X,)item1(Xitem2Xitem3...)] - more than one item makes a list
[ifol (wrap=style,)(sep=X,)item1(Xitem2Xitem3...)] - more than one item makes a list
[t (wrap=style,)(sep=X,)item1(Xitem2Xitem3...)]
Tables
------
[table (options,)CONTENT] HTML table
[row (options,)CONTENT] table ROW
[header (options,)CONTENT] table header cell
[cell (options,)CONTENT] table cell
Variables
---------
[local variableName value] # define a variable in the local environment
[raw variableName value] # define a variable in the local environment
[vs variableName value] # ditto - same as local
[global variableName value] # define a variable in the global environment
[graw variableName value] # define a variable in the global environment
[v variableName] # use a variable (local, if not local, then global)
[gv variableName] # use the global variable and ignore the local
[lv variableName] # use the local variable and ignore the global
[vinc (quiet=1,)(pre=1,)variableName] # increment a local(global) variable
[vdec (quiet=1,)(pre=1,)variableName] # deccrement a local(global) variable
[load variableName fileName] # load filename into local variable
[gload variableName fileName] # load filename into global variable
[save variableName fileName] # save filename from local variable
[gsave variableName fileName] # save filename from global variable
[page] # reset local environment: variables
global variables are unaffected
Data Lists
----------
[append (opt=yes|no,)listname,item] # add an item to the end of a list
[lcopy srcList,destList] # copy a list to a new or existing list
[lpush listname,item] # add an item to the end of a list (synonym for [append])
[slice sliceSpec,listToSlice,resultList] # [lslice 3:6,mylist,mylist] or [lslice 3:6,myList,otherList]
[lsplit (sep=^,)listName,splitKey^contentToSplit] # [lsplit myList, ,A Good Test] = ['A','Good','Test']
[lpop listName(,index)] # [lpop] == [lpop -1] remove list item
[lset listName,index,stuff] # set a list item by index (must exist)
[lcc srcList,addList,tgtList] # concatinate lists
[llen listName] # length of list
[cmap listName] # creates 256-entry list of 1:1 8-bit char mappings
[hmap listName] # creates 256-entry list of 1:1 2-digit hex char mappings
[postparse pythoncode] # pretty-prints python (must replace [] {})
[pythparse pythoncode] # pretty-prints python into local loc_pyth
[getc (var=varName,)(tabsiz=n,)(tabchar=X,)(high=c|cp|oc)filename] # c/cpp/oc file or var to aa_macro format
[lsub (ci=1,)(sep=X,)listName,content] # sequenced replacement by list
[dlist (style=X,)(fs=X,)(ls=X,)(parms=X,)(inter=X)(ntl=X)(posts=X,)listName]
# output list elements, can be wrapped with style X
# and with parms, if any, prefixed to list elements
# and with posts, if any, postfixed to list elements
# and with inter, if any, interspersed between list elements
# and ntl, if any, this goes between next to last and last
# if fs is present, the first display will wrap with fs style
# if ls is present, the last display will wrap with ls style
# if wrap is present, displays will wrap with it, barring fs and ls
[translate (pre=PRE,)(post=POST,)(inter=INTER)listName,text]
# characters are mapped to listName (see examples)
[ljoin listname,joinContent] # join a list into a string with interspersed content
[scase listName,content] # Case words as they are cased in listName
[ltol listName,content] # splits lines into a list
[list (sep=X,)listName,item(Xitem)] # Create list: sep default: ','
[list mylist,a,b,c]
[list sep=|,myblist,nil|one|2|0011|IV|sinco]
[e listName,index] # fetch an item from a list, base of zero:
[e mylist,0] = 'a'
[e myblist,4] = 'IV'
[asort (rev=1,)listName] # sort the list as case-sensitive text
[aisort (rev=1,)listName] # sort the list as case-insensitive text
[lhsort (rev=1,)listName] # sort the list by leading ham radio callsign
[isort (rev-1,)(sep=x,)listName] # sort the list according to a leading numeric value
ie [1,this thing][2,that thing] sep default: ','
Dictionaries
------------
[dict (sep=X,)(keysep=Y,)dictName,keyYvalue(XkeyYvalue)] # create multivalue dictionary
[dcopy srcDict,dstDict] # copy a dictionary to a new or existing list
[dkeys srcDict,dstList] # dictionary keys --> new or existing list
[dset (keysep=Y,)dictName,keyYvalue] # set a single dictionary value (can create dict)
[d (sep=X)dictNameXkey(XnotFound)] # retrieve a single dictionary value
[expand dictName,content] # replace words in content with words in dict
dict to be constructed with lower-case keys
Stack
-----
[push (sep=X,)(nX)CONTENT] # push CONTENT n deep onto stack. 1 is the same as no N.
[pop] # pop stack. If stack empty, does nothing.
[fetch (N)] # get element N from stack but no pop. 0 is top, no N = 0
[flush] # toss out entire stack
Math
----
[add (mode=float,)value addend] # add a number to a number
[sub (mode=float,)value subtrahend] # subtract a number from a number
[mul (mode=float,)value multiplier] # multiply a number by a number
[div (mode=float,)value divisor] # divide a number by a number
[abs value] # return the absolute value of the value
[max v1 v2] # return larger value
[min v1 v2] # return smaller value
[inc value] # add one to a number
[dec value] # subtract one from a number
[int value] # return the integer of the value
[round (digits=decdigits,)value] # return the value, rounded
[stage start end steps step] # produce number in range
[random( )(seed=none,)(icount=N)] # generate a random number
Conditionals
------------
[even (style=styleName,)value conditionalContent] # use cc if value is even
[odd (style=styleName,)value conditionalContent] # use cc if value is odd
[if (sep=X,)(style=styleName,)value match conditionalContent] # use cc if value == match
[else (sep=X,)(style=styleName,)value match conditionalContent] # use cc if value != match
[ne (sep=X,)(style=styleName,)value,conditionalContent] # use cc if value Not Empty
[eq (sep=X,)(style=styleName,)value,conditionalContent] # use cc if value Empty
[ifge (style=styleName,)iValue,iValue,conditionalContent] # use cc if integer1 >= integer2
[ifle (style=styleName,)iValue,iValue,conditionalContent] # use cc if integer1 <= integer2
Parsing and text processing
---------------------------
[alphalead (trail=1,)content] # return leading alpha, discard remainder
[alphanumlead (trail=1,)content] # return leading alphanumerics, discard remainder
[slice sliceSpec,contentToSlice] # [slice 3:6,foobarfoo] = bar ... etc.
[splitcount N] # limit number of splits to N for next split ONLY
[splash (inter=,)(pre=,)(post=,)(ntl=,)(sep=,,)(limit=N,)(style=Style,)data] # Split data, optionally limit times, apply style
[split splitSpec,contentToSplit] # [split |,x|y|z] results in parms 0,1,2
Because a comma is used to separate the
splitSpec from the contentToSplit, you
can't just use a comma directly. But
there is a syntax to support it...
[split [co],contentToSplit]]
...where contentToSplit is separated by
actual commas. Comes in handy sometimes.
Obeys [splitcount]
[parm N] # per above [split, [parm 1] results in y
[upper textString] # convert to uppercase
[lower textString] # convert to lowercase
[rstrip content] # remove trailing whitespace
[len content] # return length of content in characters
[lc content] # return length of content in lines
[wc content] # return length of content in words
[wwrap (eol=X)(nohtml=1,)(wrap=style,)col,content] # wrap content at col - styles usually want newlines
[roman decNumber] # convert decimal to roman (1...4000)
[dtohex decNumber] # convert decimal to hexadecimal
[dtooct decNumber] # convert decimal to octal
[dtobin decNumber] # convert decimal to binary
[htodec hexNumber] # convert hexadecimal to decimal
[otodec octNumber] # convert octal to decimal
[btodec binNumber] # convert binary to decimal
[crush content] # return only alphanumerics
[collapse content] # whitespace collapsed to single spaces
[crop (words=no,)(eol=\n,)(neol=\n,)(col=78),content] # a brutal content wrap w/o collapse
[chr number] # e.g. [chr 65] = "A"
[ord character] # e.g. [ord A] = "65"
[csep integer] # e.g. [csep 1234] = "1,234"
[fcsep integer] # e.g. [fcsep 1234.56] = "1,234.56"
[soundex content] # returns soundex code
[strip content] # strip out HTML tags
[stripe (charset=chars,)content] # strip whitespace or chars in charset from ends of lines
[dup count,content] # e.g. [dup 3,foo] = "foofoofoo"
[eval (style=styleName,)count,content] # e.g. [eval 3,foo] = "foofoofoo"
[find (sep=X,)thisStringXinString] # returns -1 if not found, X default=,
[count (overlaps=yes)(casesens=yes,)(sep=X,)patternXcontent] # count term occurances in content
[replace (sep=X,)thisStrXwithStrXinStr] # e.g. [replace b,d,abc] = "adc" X default=,
[caps content] # Capitalize first letter of first word
[capw content] # Capitalize first letter of every word
[capt content] # Use title case (style: U.S. Government Printing Office Style Manual)
[specialcase listName,content] # Case words as they are cased in listName
[ssort (rev=1,)content] # sort lines cases-INsensitive
[sisort (rev=1,)content] # sort lines cases-sensitive
[issort (rev=1,)content] # sort lines by leading integer,comma,content
[hsort (rev=1,)content] # sort lines by leading ham radio callsign
[hlit (format=1,)content] # turn content into HTML; process NOTHING
[vlit (format=1,)variable-name] # turn variable content into HTML; process NOTHING
[slit (format=1,)(wrap=1,)style-name] # turn style content into HTML; process NOTHING
[inter iStr,L|R,everyN,content] # intersperse iStr every N in content from left or right
* [rjust width,padChar,content] # e.g. [rjust 6,#,foo] = "###foo"
* [ljust width,padChar,content] # e.g. [ljust 6,#,foo] = "foo###"
* [center width,padChar,content] # e.g. [center 7,#,foo] = "##foo"
negative width means pad both sides:
[center -7,=,foo] = "==foo=="
[th integer] # returns st, nd, rd, th...
[nd integer] # returns 1st, 2nd, 3rd, 4th...
[br (parms=stuff,)(content)] # generates an HTML break
Encryption
----------
[encrypt (mode=1,)(again=1,)(breakat=N,)(seed=N,)(icount=N,)salt=string,)content]
[decrypt (mode=1,)(seed=N,)(icount=N,)salt=string,)content]
Misc
----
[date] # date processing took place
[ddelta YYYYMMDD YYYYMMDD] # returns difference as 'year month day'
[time (sfx=,)(asfx=,)(psfx=,)(mode=12|24,)] # time processing took place
[datetime] # atomic YYYYmmDDhhMMss
[month (mode=long,)N] # Jan, January
[ampm N] # AM or PM from hour
[twelve N] # twelve hour number from 24
[term (astyle=CSSSTYLE)CAPS] # expand term if it is known to acroclass (requires acroclass.py)
[fref label] # forward reference
[resolve (hex=1,)label,content] # resolve forward label reference(s) to content
[sys shellCommand] # execute shell command
[repeat count content] # repeat content count times
# count may be any one of:
# an integer
# [v variableName] (local, or global if local does not exist)
# [gv globalVariableName]
# [lv localvariablename]
# [parm parameterNumber] (from [split])
[comment content] # content does not render
[back HEX3|HEX6] # set back color for HTML 4.01s operations
[mode 3.2|4.01s] # set HTML version/mode
[include filename] # grab some previously defined content, styles, etc.
[embrace modulename] # install new built-ins, and/or replace existing ones
see 'embrace-example.py'
Escape Codes:
-------------
[co] # produces HTML ',' as ,
[lt] # produces HTML '<' as <
[gt] # produces HTML '>' as >
[sp] # produces HTML ' ' as  
[lb] # produces HTML '[' as [
[rb] # produces HTML ']' as ]
[ls] # produces HTML '{' as {
[rs] # produces HTML '}' as }
[vb] # produces HTML '|' as |
[lf] # produces HTML newline (0x0a)
[nl] # produces HTML newline (0x0a)
Styles
------
[style (help=helpstring,)(help2=helpstring,)styleName Style] # Defines a local style. Use [b] for body of style
[gstyle (help=helpstring,)(help2=helpstring,)styleName Style] # Defines a global style. Use [b] for body of style
[helps localstylename] # returns helpstring, if any
[helpg globalstylename] # returns helpstring, if any
[helps2 localstylename] # returns second helpstring, if any
[helpg2 globalstylename] # returns second helpstring, if any
[glos styleName contentToStyle] # contentToStyle goes where [b] tag(s) is/are in style...
only uses global styles
[locs styleName contentToStyle] # contentToStyle goes where [b] tag(s) is/are in style...
only uses local styles
[s styleName contentToStyle] # contentToStyle goes where [b] tag(s) is/are in style...
uses local style, if doesn't exist, then uses global style
but use {styleName contentToStyle} as shown next
you can thank me later. :)
{styleName contentToStyle} # ...same as [s styleName], in preferred "squiggly" syntax
==> Only if crtospace is True:
{styleNameNLcontentToStyle} # ...same thing, but simplified "squiggly" syntax
# NL is a *nix newline, 0x0A
[spage] # reset local environment: styles
global styles are unaffected
[for style,X,Y,Z]
[in style,list]
[case (sep=X,)switchName caseXcontent]
[switch (csep=X,)(isep=Y,)(default=styleName,)switchName caseYstyleName(XcaseXstylename)]
[ghost (source=local,) stylename] # allows output of a style in web-compatible format
[listg (source=local,)listName] # names of global or local styles --> listname
More on styles:
---------------
Styles give you ultimate power in creating your own text processing tool.
Styles are pretty easy to understand. You can have as many as you want, and they
can contain other styles, presets and so on. There are two components to styles;
defining them, and using them.
Here's a simple style definition:
+-- DEFINE a style
| +-- name of style
| | +-- beginning of style
| | | +-- where the content fed to the style will go
| | | | +-- rest of pre-defined style
| | | | | +-- ends style definition
| | | | | |
[style strike <strike>[b]</strike>]
So now to use that, you do this:
+-- USE a style
| +-- name of style to use
| | +-- the content that goes where the [b] tag(s) is/are in the style
| | | +-- ends style content
| | | |
[s strike me out]
Or you can do this, which is intended to be more readable and convenient:
+-- USE a style
|+-- name of style to use
|| +-- the body that goes where the [b] tag(s) is/are in the style
|| | +-- ends style content
|| | |
{strike me out}
Either of which will come out of object.do() as:
<strike>me out</strike>
You can also break the content into multiple parameters and use them individually
using a combination of the [split] and [parm] functions. See the examples at the
end of this file for details on that.
You can nest more or less indefinitely:
---------------------------------------
[b bold text [s strike [i bold and italic text]]]
Parameters to object instantiation:
-----------------------------------
dlimit ----- max nesting depth of looping constructs:
for
repeat
dup
eval
xlimit ----- max number of iterations for looping constructs:
for
repeat
dup
eval
locklipath - '' (default) if string is non-empty, lipath cannot be changed from string
lockwepath - '' (default) if string is non-empty, wepath cannot be changed from string
debug ------ False (default) or True, then call getdebug()
mode ------- '3.2' (default) or '4.01s' to set HTML rendering mode
nodinner --- True (default) or False eats sequences of two spaces followed by a newline
noshell ---- False (default) or True disables [sys] [load] and [gload] built-ins
noembrace -- False (default) or True disables [embrace] built-in
noinclude -- False (default) or True disables [include] built-in
back ------- ffffff (default) HEX3 or HEX color for background color in HTML 4.01s mode
ucin ------- False (default) presumes input is 0-127 ASCII; True is Unicode
ucout ------ False (default) output is ASCII; True converts output to Unicode
dothis ----- None (default) you can pass in initial text to be processed here if you like
the object returns the result in its string method:
mod = macro(dothis='[style x foo [b]]'{x bar})
print mod # prints 'foo bar' if the data is ASCII
if it is unicode, you need to do a little more
Unicode
=======
Unicode presents encoding issues for Python 2.7
The following examples show how to deal with
unicode in the context of aa_macro:
Processing unicode input to ASCII / HTML output:
------------------------------------------------
mod = macro(ucin=True)
s = mod.unido(testBlock) # s will be ASCII with unicode HTML entities
Processing Unicode input to Unicode output:
-------------------------------------------
mod = macro(ucin=True,ucout=True)
mod.unido(testBlock)
s = mod.uniget() # s will be unicode
The Rules:
----------
o Unicode requires specific processing as shown above.
o Do not attempt to define one style inside another.
o Style names may contain anything but a space or a newline
o repeat gets a number or a variable parameter. Nothing else. No nesting in the parameter!
(but you can use anything in what you want it to repeat)
o Give me a space or a newline. One space or newline only, Vasily.
Between the tag and any parameters, that is
o observe the format for tags. Some require spaces, some commas, to separate items.
or suffer the consequences, which will be aborted macros. :)
FYI: Generally commas are used for variable numbers of parameters.
o You can use [co] for literal commas, likewise [lb] [rb] [ls] and [rs] for literal braces.
Any operation that uses a comma for parameters or optional parameters is looking
for commas, so use [co] in the content of anything you feed to them. Else format=yuck
Also see [nc contentToConvertCommasIn], which is a handy way to see to it that your
content can have commas, but the macro system won't see them. Of course, you can't
use that on anything that *needs* commas for parameters. Life is so complicated. :)
"""
def __init__(self,dothis=None,mode='3.2',back="ffffff",nodinner=False,noshell=False,noinclude=False,noembrace=False,debug=False,locklipath='',lockwepath='',xlimit=0,dlimit=0,ucin=False,ucout=False,acrofile='acrobase.txt'):
self.locklipath = locklipath
self.lockwepath = lockwepath
self.xlimit = xlimit
self.dlimit = dlimit
self.xdcount = 0
self.ucin = ucin
self.ucout = ucout
self.lipath = locklipath
self.wepath = lockwepath
self.setMode(mode)
self.setBack(back)
self.setNoDinner(nodinner)
self.setDebug(debug)
self.page()
self.setFuncs()
self.resetLocals()
self.resetLists()
self.resetDicts()
self.resetGlobals()
self.placeholder = 'Q|zXaH7RppY#32m' # hopefully you'll never use this string, lol
self.styles = {}
self.gstyles = {}
self.dstyles = {}
self.dgstyles = {}
self.dstyles2 = {}
self.dgstyles2 = {}
self.stack = []
self.parms = []
self.refs = {}
self.switches = {}
self.refcounter = 0
self.padCallLocalToggle = 0
self.padCallLocalRegion = -1
self.noshell = noshell
try:
self.acros = acroclass.core(acrofile=acrofile)
except:
self.haveacros = False
else:
self.haveacros = True
self.noembrace = noembrace
self.noinclude = noinclude
self.sexdigs = '01230120022455012623010202'
self.romans = ['m','cm','d','cd','c','xc','l','xl','x','ix','v','iv','i']
self.integers = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
self.notcase = ['a','an','the','at','by','for',
'in','of','on','to','up','and','as',
'but','or','nor']
self.result = ''
self.splitCount = 0
self.theGlobals['txl_lb'] = '[lb]'
self.theGlobals['txl_rb'] = '[rb]'
self.theGlobals['txl_ls'] = '[ls]'
self.theGlobals['txl_rs'] = '[rs]'
self.theGlobals['txl_lt'] = '<'
self.theGlobals['txl_gt'] = '>'
self.theGlobals['txl_am'] = '&'
self.theGlobals['txl_qu'] = '"'
self.theGlobals['txl_lf'] = '<br>'
self.theGlobals['cpp_fpre'] = 'ff8844'
self.theGlobals['cpp_spre'] = '4488ff'
self.theGlobals['cpp_stpre'] = 'ffffff'
self.theGlobals['cpp_copre'] = '888888'
self.theGlobals['cpp_pppre'] = 'ff0000'
self.theGlobals['cpp_atpre'] = 'ff00ff'
self.theGlobals['aam_version'] = self.version_set()
self.theGlobals['i401s_open'] = '<span style="font-style: italic;">'
self.theGlobals['i401s_clos'] = '</span>'
self.theGlobals['b401s_open'] = '<span style="font-weight: bold;">'
self.theGlobals['b401s_clos'] = '</span>'
self.theGlobals['u401s_open'] = '<span style="text-decoration: underline;">'
self.theGlobals['u401s_clos'] = '</span>'
self.theGlobals['c401s_open'] = '<span style="background-color: #BACKCOLOR; color: #FORECOLOR;">'
self.theGlobals['c401s_clos'] = '</span>'
self.theGlobals['pp_trigger'] = '#'
self.theGlobals['tx_pybrace'] = '<span style="color:#ff8844;">'
self.theGlobals['tx_pysym'] = '<span style="color:#00ffff;">'
self.theGlobals['tx_epybrace'] = '</span>'
self.theGlobals['tx_epysym'] = '</span>'
self.theGlobals['tx_prekey'] = '<span style="color: #ff00ff">'
self.theGlobals['tx_poskey'] = '</span>'
self.theGlobals['tx_prequo'] = '<span style="color: #ffffff">'
self.theGlobals['tx_posquo'] = '</span>'
self.theGlobals['tx_precod'] = '<span style="color: #00ff00">'
self.theGlobals['tx_poscod'] = '</span>'
self.theGlobals['tx_pretxt'] = '<span style="color: #ff0000">'
self.theGlobals['tx_postxt'] = '</span>'
self.theGlobals['tx_precom'] = '<span style="color: #ffff00">'
self.theGlobals['tx_poscom'] = '</span>'
self.theGlobals['loc_splitcount'] = 0
self.theGlobals['loc_splitnum'] = 0
self.theGlobals['loc_splashcount'] = 0
self.keywords = ['and','del','from','not','while',
'as','elif','global','or','with',
'assert','else','if','pass','yeild',
'break','except','import','print',
'class','exec','in','raise',
'continue','finally','is','return',
'def','for','lambda','try']
self.months = ['January','February','March','April','may','June','July','August','September','October','November','December']
self.setup_urle()
if dothis != None:
if self.ucin == True:
self.unido(dothis)
else:
self.do(dothis)
def __str__(self):
return self.result
def uniget(self):
if self.ucout == True:
self.result = self.asciitounicode(self.result)
return self.result
def unido(self,text):
tmp = unicode(text)
text = self.unicodetoascii(tmp) # convert input unicode to ASCII
self.do(text)
return self.result
def asciitounicode(self,text):
state = 0 # nothing detected
accum = u''
o = u''
for c in text:
if state == 0: # nothing as yet?
if c == u'&': # ampersand?
state = 1 # ampersand!
else:
o += c
elif state == 1: # ampersand found?
if c == u'#': # hash?
state = 2 # hash!
accum = u'' # clear accumulator
else: # not a hash, so not an entity encoding
state = 0 # abort
o += u'&'+c # flush char, done
elif state == 2: # expecting digits or terminating semicolon
if c.isdigit(): # digit?
accum += c # add it to accumulator if so
elif c == u';': # terminating
s = u'\\U%08x' % (int(accum))
ss= s.decode('unicode-escape')
o += ss
state = 0
else: # bad encoding?
o += u'&#'
o += accum
state = 0
return o
def unicodetoascii(self,text):
o = ''
n = len(text)
for i in range(0,n):
try:
c = text[i].encode("ascii")
o += c
except:
o += '&#{:d};'.format(ord(text[i]))
return o
def setDebug(self,db):
if db != False:
self.debug = True
else:
self.debug = False
self.debuglevel = 0
self.debstack = []
def setNoDinner(self,nd=False):
if nd != False:
nd = True
self.noDinner = nd
def cReduce(self,txtCall):
l = re.split('([0-9]*)',txtCall.upper(),1)
if len(l) != 3:
return 'z',0,'t'
return l[0],int(l[1]),l[2]
def sCallsignKeyF(self,line):
txtCall = ''
for c in line:
if ((c >= 'a' and c <= 'z') or
(c >= 'A' and c <= 'Z') or
(c >= '0' and c <= '9')):
txtCall += c
else:
break
a,b,c = self.cReduce(txtCall)
return b,a,c
def gis(self,fn):
def rhead(fn, length):
try:
fh = open(fn)
content = fh.read(length)
fh.close()
except:
return ''
if len(content) != length:
return ''
return(content)
x = 0
y = 0
insmell = '<'
moto = '>'
int4 = 'i'
uint16 = 'H'
try:
it = imghdr.what(fn)
except:
return 'unable to handle "%s"' % (fn,)
if it == 'jpeg':
try:
fh = open(fn)
sz = 2; ft = 0
while not 0xC0 <= ft <= 0xCF:
fh.seek(sz,1)
b = fh.read(1)
while ord(b) == 0xFF: b = fh.read(1)
sz = struct.unpack(moto + uint16,fh.read(2))[0]-2
ft = ord(b)
fh.seek(1,1)
y,x = struct.unpack(moto + uint16 + uint16, fh.read(4))
except:
try:
fh.close()
except:
pass
return 'unable to parse "%s"' % (fn,)
fh.close()
elif it == 'gif':
header = rhead(fn, 10)
if header == '': return '"%s" is pathological' % (fn,)
x,y = struct.unpack(insmell + uint16 + uint16, header[6:10])
elif it == 'png':
header = rhead(fn, 24)
if header == '': return '"%s" is pathological' % (fn,)
x,y = struct.unpack(moto + int4 + int4, header[16:24])
else:
return 'unhandled image type: "%s"' % (it,)
return x,y
def sanitize(self,
s, # input string
uwl='', # user whitelist
ubl=[], # user blacklist
wl=' []{}()<>~/!@#$%^&*_-+=;:",.?/|\t\n"'+"'", # default whitelist
bl=['<script','<!--#'], # default blacklist
linelimit=0, # lines truncate at nonzero linelimit
newline='\n', # character that delineates a line
limit=0, # string truncate at nonzero length
pre=True, # limit entire string length prior to line limits
report=True, # provide error reports
dt=True): # defeat HTML tags flag
"""Utility to clean user-provided text of things you don't want in it.
sanitize() provides character whitelisting and string blacklisting.
sanitize() allows letters and numbers through, as well as the
characters in dwl and wl, while stripping out any string in the
ubl or bl lists.
If you don't like the defaults, you can over-ride wl and/or bl with
'' and [] respectively and feed in uwl and/or ubl instead; or, you
can leave wl and/or bl alone, and extend either or both with uwl and/or
ubl.
If something is found that is in one of the blacklists, the returned
string will have all <> characters converted into HTML entities,
which will prevent any tag from working. If you don't want this
behavior, set nff=False.
If you set linelimit to a non-zero value, the string will be treated
as lines delineated by the newline character, and each line that exceeds
a length of linelimit will be truncated to linelimit-1, plus a newline
character, so the resulting lines are guaranteed to be at or under
linelimit in size. Terminating newlines are preserved. You can set
newline to any character you want.
If you set limit to a non-zero value, strings that are longer than
that number will be truncated to that size. Any terminating newline is
not preserved. If pre is True (the default), this limit is processed
prior to any processing of linelimit.
On return, you get the string back, and a list that may contain warnings
about any issues if report=True (the default):
['cs ordinal'] means that non-whitelisted character was stripped
['ss stripcount oldsize'] means that the string of oldsize was stripcount too long
['ls stripcount oldsize'] means that a line of oldsize was stripcount too long
['bs] means that one or more blacklisted strings were stripped
['hc'] means that < and/or > were present after ['bs'] and converted to HTML entities
The contents of the list are safe to include in the output if you like.
"""
o = ''
warnings = []
s = str(s)
if pre == True:
if limit != 0:
ct = len(s)
if ct > limit:
s = s[:limit]
if report == True: warnings += ['ss %d %d' % (ct,ct-limit)]
if linelimit != 0:
ull = linelimit - 1
to = ''
slist = s.split(newline)
lastflag = False
for line in slist:
ct = len(line)
if ct > ull:
line = line[0:linelimit]
if report == True: warnings += ['ls %d %d' % (ct,ct-ull)]
line += newline
to += line
if len(to) > 0:
to = to[0:-1]
s = to
if pre != True:
if limit != 0:
ct = len(s)
if ct > limit:
s = s[:limit]
if report == True: warnings += ['ss %d %d' % (ct,ct-limit)]
for c in s:
if ((c >= '0' and c <= '9') or
(c >= 'A' and c <= 'Z') or
(c >= 'a' and c <= 'z') or
(c in wl) or
(c in uwl)):
o += c
else:
if report == True: warnings += ['cs '+str(ord(c))]
nf = o
for f in bl:
o = o.replace(f,'')
for f in ubl:
o = o.replace(f,'')
if nf != o:
if report == True: warnings += ['bs']
if dt == True:
nf = o
o = o.replace('<','<')
o = o.replace('>','>')
if nf != o:
if report == True: warnings += ['hs']
return o,warnings
def resetGlobals(self):
self.theGlobals = {}
self.gstyles = {}
self.dgstyles = {}
self.dgstyles2 = {}
def resetLocals(self):
self.theLocals = {}
self.styles = {}
self.dstyles = {}
self.dstyles2 = {}
def resetDicts(self):
self.theDicts = {}
def resetLists(self):
self.theLists = {}
def htest(self,c):
if type(c) != str:
return False
c = c.upper()
for n in c:
if n < '0' or n > 'F':
return False
if n > '9' and n < 'A':
return False
return True
def getm(self,vname):
o = self.theLocals.get(vname,self.theGlobals.get(vname,''))
return o
def mcolor(self,cc):
ll = len(cc)
if ll != 3 and ll != 6: return cc
c = cc
d = 'ffffff'
if type(c) != str:
c = d
if len(c) == 3:
c = c[0]+c[0] + c[1]+c[1] + c[2]+c[2]
if len(c) != 6:
c = d
c = c.upper()
if self.htest(c) != True:
c = cc
return c
def setBack(self,back="ffffff"):
self.back = self.mcolor(back)
def popts(self,olist,data,justopts=False):
ropts = []
plist = []
if justopts == True:
if data.find(',') == -1:
plist += [data];
else:
plist = data.split(',')
if len(plist) == 0 or plist[0] == '':
return ropts,data
else:
if data.find(',') != -1:
plist = data.split(',')
else:
return ropts,data
run = True
while run == True:
hit = False
for el in olist:
el += '='
l = len(el)
if len(plist) != 0:
mystr = plist[0]
if mystr[:l] == str(el):
ropts += [[el,plist[0][l:]]]
plist.pop(0)
hit = True
if hit == False:
run = False
result = ''
for el in plist:
if result != '':
result += ','
result += el
return ropts,result
def mkey(self,key):
return 'hY#n_K+fR'+key+'6/tt@8f*d0!!rf'
# [dkeys srcDict,dstList]
def dkeys_fn(self,tag,data):
o = ''
p = data.split(',',1)
if len(p) == 2:
a,b = p
if a != '' and b != '':
try:
self.theLists[b] = self.theDicts.get(a,{}).keys()
except:
try:
self.theLists[b] = []
except:
pass
return o
# [dcopy srcDict,dstDict]
def dcopy_fn(self,tag,data):
o = ''
p = data.split(',',1)
if len(p) == 2:
a,b = p
if a != '' and b != '':
try:
self.theDicts[b] = self.theDicts.get(a,{})
except: