rda-python-miscs 2.0.0__py3-none-any.whl → 2.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,744 @@
1
+ #
2
+ ###############################################################################
3
+ #
4
+ # Title : pg_docs.py
5
+ # Author : Zaihua Ji, zjiucar.edu
6
+ # Date : 09/14/2020
7
+ # Purpose : python library module to help convert text help documents into
8
+ # html format with help of html templates
9
+ #
10
+ # Work File : $DSSHOME/lib/python/PgDOCS.py
11
+ # Github : https://github.com/NCAR/rda-shared-libraries.git
12
+ #
13
+ ###############################################################################
14
+ #
15
+ import os
16
+ import re
17
+ from os import path as op
18
+ import PgLOG
19
+ import PgUtil
20
+ import PgFile
21
+
22
+
23
+ class PgDOCS:
24
+
25
+ Q0 = "'"
26
+ Q1 = "<i><b>"
27
+ Q2 = "</i></b>"
28
+
29
+ EMLIST = {
30
+ 'dsarch' : 1,
31
+ 'msarch' : 1,
32
+ 'dsupdt' : 1,
33
+ 'dsrqst' : 1,
34
+ 'gatherxml' : 1,
35
+ 'pgconvert' : 1,
36
+ 'publish_filelist' : 1,
37
+ 'rcm' : 1,
38
+ 'dcm' : 1,
39
+ }
40
+
41
+ SEARCH = "(Action|Info|Mode|Multi-Value|Single-Value)"
42
+
43
+ def __init__(self):
44
+ self.OPTS = {}
45
+ self.ALIAS = {}
46
+
47
+ self.SECIDS = { # section ids for category
48
+ 'Action' : None,
49
+ 'Info' : None,
50
+ 'Mode' : None,
51
+ 'Multi-Value' : None,
52
+ 'Single-Value' : None,
53
+ }
54
+
55
+ # Section array with each section pointing to a hash:
56
+ # secid - section ID (1, 1.1, 1.1.1, ...)
57
+ # title - section title
58
+ # level - section level, 0 top level
59
+ # desc - section decription
60
+ # opts - pointer to an array of included option short names
61
+ self.sections = []
62
+
63
+ # Option hash keyed by short option names and each is a hash itself
64
+ # secid - section ID the option belongs
65
+ # name - option long name
66
+ # type - option type, 0 - Mode, 1 - Info, 2 - Action
67
+ # alias - array of alias option names None if none
68
+ # desc - option decription
69
+ # examples - array of example indices included for the option
70
+ self.options = {}
71
+
72
+ # Example array with each example pointing to a hash:
73
+ # opt - option short name the example belongs
74
+ # title - example title
75
+ # desc - example decription
76
+ self.examples = []
77
+
78
+ # global info to be used by the whole application
79
+ self.DOCS = {
80
+ 'ORIGIN' : PgLOG.PGLOG['DSSHOME'] + "/dssdb/prog_usage", # directory to the original document
81
+ 'TMPDIR' : PgLOG.PGLOG['DSSHOME'] + "/lib/templates", # directory to find the templates
82
+ 'DCROOT' : None, # root directory to html documents
83
+ 'DOCDIR' : "", # directory to final html documents
84
+ 'DOCNAM' : "", # document name: dsarch, dsupdt, etc.
85
+ 'DOCTIT' : "", # document name in upper case letters
86
+ 'DOCLNK' : None,
87
+ }
88
+
89
+ self.LINKS = ['dsarch', 'dsupdt', 'dsrqst', 'dscheck']
90
+ self.DOCS['DCROOT'] = PgLOG.get_environment("WEBROOT", PgLOG.PGLOG['DSSWEB']) + "/rdadocs"
91
+
92
+ #
93
+ # Function process_docs(docname: document name, 'dsarch', 'dsupdt'
94
+ # opts: option hash defined for the document
95
+ # alias: alias names for given opts)
96
+ #
97
+ def process_docs(self, docname, opts, alias):
98
+
99
+ self.OPTS = opts
100
+ self.ALIAS = alias
101
+
102
+ self.parse_docs(docname)
103
+ if not self.sections: PgLOG.pglog(docname + ": empty document", PgLOG.LGWNEX)
104
+
105
+ self.DOCS['DOCNAM'] = docname
106
+ if docname in self.LINKS: self.LINKS.remove(docname)
107
+ self.DOCS['DOCLNK'] = r"({})".format('|'.join(self.LINKS))
108
+ self.DOCS['DOCTIT'] = docname.upper()
109
+ self.DOCS['DOCDIR'] = "{}/{}".format(self.DOCS['DCROOT'], docname)
110
+
111
+ PgFile.change_local_directory(self.DOCS['DOCDIR'], PgLOG.LGWNEX)
112
+ PgLOG.pglog("Write html document '{}' under {}".format(docname, self.DOCS['DOCDIR']), PgLOG.LOGWRN)
113
+
114
+ if op.exists("index.html"): # write index file once
115
+ PgLOG.pglog("index.html exists already, delete first if needs to be regenerated", PgLOG.LOGWRN)
116
+ else:
117
+ self.write_index(self.sections[0])
118
+
119
+ self.write_toc()
120
+
121
+ for section in self.sections:
122
+ self.write_section(section)
123
+
124
+ #
125
+ # parse the original document and return a array of sections,
126
+ #
127
+ def parse_docs(self, docname):
128
+
129
+ docfile = "{}/{}.usg".format(self.DOCS['ORIGIN'], docname)
130
+ PgLOG.pglog("Parsing info for Document '{}'".format(docname), PgLOG.LOGWRN)
131
+ section = self.init_section('0', "Preface")
132
+ option = example = None
133
+ fh = open(docfile, 'r')
134
+ line = fh.readline()
135
+ while line:
136
+ if re.match(r'\s*#', line):
137
+ line = fh.readline()
138
+ continue # skip comment lines
139
+ ms = re.match(r'^(.*\S)\s+#', line)
140
+ if ms:
141
+ line = ms.group(1) # remove comments
142
+ else:
143
+ line = line.rstrip() # remove trailing white spaces
144
+
145
+ # check and replace temporal pattern quotes
146
+ while True:
147
+ ms = re.search(r'(<([A-Z/\-\.]+)>)', line)
148
+ if ms:
149
+ line = line.replace(ms.group(1), "&lt{}&gt".format(ms.group(2)))
150
+ else:
151
+ break
152
+ ms = re.match(r'^([\d\.]+)\s+(.+)$', line)
153
+ if ms: # start new section
154
+ section = self.record_section(section, option, example, ms.group(1), ms.group(2))
155
+ option = example = None
156
+ else:
157
+ ms = re.match(r'^ -([A-Z]{2}) or -\w+(.*)$', line)
158
+ if ms: # found new option
159
+ option = self.record_option(section, option, example, ms.group(1), ms.group(2))
160
+ example = None
161
+ elif option:
162
+ ms = re.match(r'^ For( | another )example, (.*)$', line)
163
+ if ms: # found example
164
+ example = self.record_example(option, example, ms.group(2))
165
+ elif example:
166
+ example['desc'] += line + "\n"
167
+ else:
168
+ option['desc'] += line + "\n"
169
+ else:
170
+ section['desc'] += line + "\n"
171
+
172
+ line = fh.readline()
173
+ fh.close()
174
+
175
+ self.record_section(section, option, example)
176
+
177
+ # check completion of options
178
+ for opt in self.OPTS:
179
+ if opt not in self.options:
180
+ PgLOG.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), PgLOG.LOGWRN)
181
+ if self.sections:
182
+ cnt = len(self.sections)
183
+ s = 's' if cnt > 1 else ''
184
+ PgLOG.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), PgLOG.LOGWRN)
185
+
186
+ #
187
+ # cache section information
188
+ #
189
+ def record_section(self, section, option, exmaple, nsecid=None, ntitle=None):
190
+
191
+ if option or section['desc'] != "\n":
192
+ if option: self.record_option(section, option, exmaple)
193
+ self.sections.append(section) # record section globally
194
+
195
+ if nsecid: return self.init_section(nsecid, ntitle)
196
+
197
+ #
198
+ # cache option information
199
+ #
200
+ def record_option(self, section, option, example, nopt=None, ndesc=None):
201
+
202
+ if option:
203
+ if example: self.record_example(option, example)
204
+ self.options[option['opt']] = option # record option globally
205
+ section['opts'].append(option['opt']) # record option short name in section
206
+
207
+ if nopt: return self.init_option(section['secid'], nopt, ndesc)
208
+
209
+ def record_example(self, option, example, ndesc=None):
210
+
211
+ if example:
212
+ ms = re.match(r'^(.*)\.\s*(.*)$', example['desc'])
213
+ if ms:
214
+ example['title'] = ms.group(1)
215
+ example['desc'] = ms.group(2)
216
+ option['exmidxs'].append(len(self.examples)) # record example index in option
217
+ self.examples.append(example) # record example globally
218
+
219
+ if ndesc: return self.init_example(option['opt'], ndesc)
220
+
221
+ #
222
+ # initialize section dict
223
+ #
224
+ def init_section(self, secid, title):
225
+
226
+ section = {
227
+ 'secid' : secid,
228
+ 'title' : title,
229
+ 'desc' : "",
230
+ 'level' : 0,
231
+ 'opts' : []
232
+ }
233
+ level = len(re.split(r'\.', secid))
234
+ section['level'] = level
235
+ if level == 1:
236
+ if re.match(r'^ACTION', section['title']):
237
+ self.SECIDS['Action'] = secid
238
+ elif re.match(r'^MODE', section['title']):
239
+ self.SECIDS['Mode'] = secid
240
+ elif re.match(r'^INFORMATION', section['title']):
241
+ self.SECIDS['Info'] = secid
242
+ elif level == 2:
243
+ if re.match(r'^Single-Value', section['title']):
244
+ self.SECIDS['Single-Value'] = secid
245
+ elif re.match(r'^Multi-Value', section['title']):
246
+ self.SECIDS['Multi-Value'] = secid
247
+
248
+ return section
249
+
250
+ #
251
+ # initialize option dict
252
+ #
253
+ def init_option(self, secid, opt, desc):
254
+
255
+ option = {}
256
+ types = ("Mode", "Info", "Info", "Action")
257
+
258
+ if opt not in self.OPTS:
259
+ PgLOG.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), PgLOG.LGWNEX)
260
+ option['secid'] = secid
261
+ option['opt'] = opt
262
+ ms = re.match(r'^(, | \(Alias: .*\), )(.*)', desc)
263
+ if ms: desc = ms.group(2)
264
+ option['desc'] = desc + "\n"
265
+ option['exmidxs'] = []
266
+ option['name'] = self.OPTS[opt][1]
267
+ if opt in self.ALIAS: option['alias'] = self.ALIAS[opt]
268
+ typidx = self.OPTS[opt][0]
269
+ if typidx > 3: typidx = 3
270
+ option['type'] = types[typidx]
271
+
272
+ return option
273
+
274
+ #
275
+ # initialize example dic
276
+ #
277
+ def init_example(self, opt, desc):
278
+
279
+ example = {'opt' : opt, 'title' : "", 'desc' : desc.title() + "\n"}
280
+
281
+ return example
282
+
283
+ #
284
+ # write the entry file: index.html
285
+ #
286
+ def write_index(self, section):
287
+
288
+ hash = {'TITLE' : self.DOCS['DOCTIT'], 'SECID' : section['secid']}
289
+
290
+ self.template_to_html("index", hash)
291
+
292
+ #
293
+ # write the table of contents: toc.html
294
+ #
295
+ def write_toc(self):
296
+
297
+ hash = {'TITLE' : self.DOCS['DOCTIT'], 'TOC' : self.create_toc()}
298
+
299
+ self.template_to_html("toc", hash)
300
+
301
+ #
302
+ # write a section html file
303
+ #
304
+ def write_section(self, section):
305
+
306
+ hash = {}
307
+ secid = section['secid']
308
+ hash['TITLE'] = section['title']
309
+ hash['SECID'] = secid
310
+ hash['SECTION'] = self.create_section(section)
311
+
312
+ self.template_to_html("section", hash, secid)
313
+
314
+ #
315
+ # convert template to html file
316
+ #
317
+ def template_to_html(self, template, hash, extra=None):
318
+
319
+ tempfile = "{}/{}.temp".format(self.DOCS['TMPDIR'], template)
320
+ if extra is None: extra = ""
321
+ htmlfile = "{}/{}{}.html".format(self.DOCS['DOCDIR'], template, extra)
322
+
323
+ tf = open(tempfile, 'r')
324
+ hf = open(htmlfile, 'w')
325
+ idx = 0
326
+ line = tf.readline()
327
+ while line:
328
+ idx += 1
329
+ if re.match(r'\s*#', line):
330
+ line = tf.readline()
331
+ continue # skip comment lines
332
+ ms = re.match(r'^(.*\S)\s+#', line)
333
+ if ms:
334
+ line = ms.group(1) # remove comments
335
+ else:
336
+ line = line.rstrip() # remove trailing white spaces
337
+
338
+ matches = re.findall(r'__([A-Z]+)__', line)
339
+ if matches:
340
+ for key in matches:
341
+ if key not in hash: PgLOG.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), PgLOG.LGWNEX)
342
+ if not hash[key]: PgLOG.pglog(key + ": empty content", PgLOG.LGWNEX)
343
+ line = line.replace("__{}__".format(key), hash[key])
344
+ hf.write(line + "\n")
345
+ line = tf.readline()
346
+
347
+ tf.close()
348
+ hf.close()
349
+ PgLOG.pglog("{}{}.html created from {}.temp".format(template, extra, template), PgLOG.LOGWRN)
350
+
351
+ #
352
+ # create a html file for table of contents
353
+ #
354
+ def create_toc(self):
355
+
356
+ content = ""
357
+
358
+ # table content for all sections
359
+ lvl = 1
360
+ for section in self.sections:
361
+ secid = section['secid']
362
+ if section['level'] > lvl:
363
+ while lvl < section['level']:
364
+ content += "<tr><td>&nbsp</td><td><table>\n"
365
+ lvl += 1
366
+ elif section['level'] < lvl:
367
+ while lvl > section['level']:
368
+ content += "</table></td></tr>\n"
369
+ lvl -= 1
370
+ lvl = section['level']
371
+ content += (("<tr><td align=right><small>{}.</small></td>\n".format(secid)) +
372
+ ("<td align=left><a href=\"section{}.html\">".format(secid)) +
373
+ ("<small>{}</small></a></td></tr>\n".format(section['title'])))
374
+
375
+ while lvl > 1:
376
+ content += "</table></td></tr>\n"
377
+ lvl -= 1
378
+
379
+ # table content for appendix A of examples
380
+ content += ("<tr><td align=right><small>A.</small></td>\n" +
381
+ "<td align=left><small>List of Examples</small></td></tr>\n" +
382
+ "<tr><td>&nbsp</td><td><table>\n")
383
+
384
+ idx = 1 # used as exmaple index
385
+ for example in self.examples:
386
+ opt = example['opt']
387
+ option = self.options[opt]
388
+ secid = option['secid']
389
+ content += (("<tr><td align=right><small>A.{}.</small></td>\n".format(idx)) +
390
+ ("<td align=left><a href=\"section{}.html#e{}\">\n".format(secid, idx)) +
391
+ ("<small>{} Option -{} (-{})</small></a></td></tr>\n".format(option['type'], opt, option['name'])))
392
+ idx += 1
393
+ content += "</table></td></tr>\n"
394
+
395
+ return content
396
+
397
+ #
398
+ # create a section html content
399
+ #
400
+ def create_section(self, section):
401
+
402
+ secid = section['secid']
403
+ content = self.create_description(section['desc'], secid, 0)
404
+
405
+ for opt in section['opts']:
406
+ content += self.create_option(opt, secid)
407
+
408
+ return content
409
+
410
+ #
411
+ # create a option html content
412
+ #
413
+ def create_option(self, opt, secid):
414
+
415
+ option = self.options[opt]
416
+ content = self.create_option_name(opt, option)
417
+ dtype = 3 if option['type'] == "Action" else 1
418
+ content += self.create_description(option['desc'], secid, dtype)
419
+
420
+ if 'exmidxs' in option:
421
+ for idx in option['exmidxs']:
422
+ content += self.create_example(idx, secid)
423
+
424
+ return content
425
+
426
+ #
427
+ # create html text for option name
428
+ #
429
+ def create_option_name(self, opt, option):
430
+
431
+ qopt = self.Q1 + opt + self.Q2
432
+ nopt = self.Q1 + self.OPTS[opt][1] + self.Q2
433
+ content = "<p><a name={}></a>{} Option -{} (-{})".format(opt, option['type'], qopt, nopt)
434
+ if 'alias' in option:
435
+ alias = option['alias']
436
+ acnt = len(alias)
437
+ s = 'es' if acnt > 1 else ''
438
+ for i in range(acnt):
439
+ content += "<tr>"
440
+ if i == 0:
441
+ content += " (Aliass: "
442
+ else:
443
+ content += ", "
444
+ content += "-{}{}{}".format(self.Q1, alias[i], self.Q2)
445
+ content += ")"
446
+
447
+ content += " :</p>\n"
448
+
449
+ return content
450
+
451
+ #
452
+ # create an example html content
453
+ #
454
+ def create_example(self, exmidx, secid):
455
+
456
+ example = self.examples[exmidx]
457
+ exm = exmidx+1
458
+ content = "<br><a name=\"e{}\"></a>EXAMPLE {}. {}\n".format(exm, exm, example['title'])
459
+ content += self.create_description(example['desc'], secid, 2)
460
+
461
+ return content
462
+
463
+ #
464
+ # add links to other options
465
+ def replace_option_link(self, line, csecid, ptype=None, dtype=None):
466
+
467
+ if ptype is None: ptype = 0
468
+ if dtype is None: dtype = -1
469
+
470
+ ms = re.search(r'<([=!:])>', line)
471
+ if ms:
472
+ if ms.group(1) == ":":
473
+ opts = re.findall(r'(^|>)([a-zA-Z]{2,})(<:)', line)
474
+ else:
475
+ opts = re.findall(r'(^)([a-zA-Z]{2,})(<%s>)/' % ms.group(1), line)
476
+ elif ptype == 2:
477
+ opts = re.findall(r'(-\(*)([a-zA-Z]{2,})(\W|$)', line)
478
+ ms = re.match(r'^\s*%s(\s+[\w\.]+\s+|\s+)([a-zA-Z]{2})(\s)' % self.DOCS['DOCNAM'], line)
479
+ if ms: opts = opts.insert(0, ms.groups())
480
+ else:
481
+ opts = re.findall(r'(^-\(*|\W-\(*)([a-zA-Z]{2,})(\W|$)', line)
482
+
483
+ if opts is None: opts = []
484
+ for optary in opts:
485
+ opt = self.get_short_option(optary[1])
486
+ pre = optary[0]
487
+ after = optary[2]
488
+ secid = self.options[opt]['secid']
489
+ if secid == csecid:
490
+ link = "#" + opt
491
+ elif self.options[opt]['type'] == "Action":
492
+ link = "section{}.html".format(secid)
493
+ elif ptype == 2 and opt == "FN":
494
+ link = "#field"
495
+ else:
496
+ link = "section{}.html#{}".format(secid, opt)
497
+
498
+ ms = re.search(r'-\(({}\|\w+)\)'.format(opt), line)
499
+ if ms:
500
+ if secid == csecid and ptype == 2: continue
501
+ opt = ms.group(1)
502
+ after = ')'
503
+
504
+ replace = pre + opt + after
505
+ if re.search(r'<!>', after): after = after.replace(r'<!>', '<&#33;>')
506
+ link = "{}<a href=\"{}\">{}</a>{}".format(pre, link, opt, after)
507
+ line = line.replace(replace, link)
508
+
509
+ opts = re.findall(r'(^|\W){}( Options*\W|\W|$)'.format(self.SEARCH), line)
510
+ for optary in opts:
511
+ opt = optary[1]
512
+ if not self.SECIDS[opt]: continue
513
+ secid = self.SECIDS[opt]
514
+ if secid == csecid or re.match(r'{}'.format(secid), csecid): continue
515
+ pre = optary[0]
516
+ after = optary[2]
517
+ replace = pre + opt + after
518
+ ms = re.search(r'(\sOptions*)\W', after)
519
+ if ms:
520
+ opt += ms.group(1)
521
+ after = after.replace(ms.group(1), '')
522
+ if ptype == 2 and re.search(r'Mode Options*', opt) and dtype == 3:
523
+ link = "{}<a href=\"#mode\">{}</a>{}".format(pre, opt, after)
524
+ else:
525
+ link = "{}<a href=\"section{}.html\">{}</a>{}".format(pre, secid, opt, after)
526
+ line = line.replace(replace, link)
527
+
528
+
529
+ ms = re.search(r'(https*://\S+)(\.|\,)', line)
530
+ if ms:
531
+ replace = ms.group(1)
532
+ link = "<a href=\"{}\" target=_top>{}</a>".format(replace, replace)
533
+ line = line.replace(replace, link)
534
+
535
+ pattern = r"{q}(\S+){q}".format(q=re.escape(self.Q0))
536
+ opts = re.findall(pattern, line)
537
+ for opt in opts:
538
+ if opt not in self.EMLIST: continue # quote only predefined ones
539
+ replace = self.Q0+opt+self.Q0
540
+ if re.search(self.DOCS['DOCLNK'], opt):
541
+ link = "{}<a href=\"{}/internal/docs/{}\" target=_top>{}</a>{}".format(self.Q1, PgLOG.PGLOG['DSSURL'], opt, opt, self.Q2)
542
+ else:
543
+ link = self.Q1+opt+self.Q2
544
+ line = line.replace(replace, link)
545
+
546
+ return line
547
+
548
+ #
549
+ # description type (dtype): 0 - section, 1 - option, 2 - exmaple, 3 - action
550
+ #
551
+ def create_description(self, desc, secid, dtype):
552
+
553
+ if desc == "\n": return ''
554
+ ptype = 0 # paragraph type: 0 - normal, 1 - table, 2 - synopsys
555
+ content = ''
556
+ cnt = 0
557
+ alllines = re.split(r'\n', desc)
558
+ lines = []
559
+ for line in alllines:
560
+ if re.match(r'^\s*\S', line):
561
+ lines.append(line)
562
+ cnt += 1
563
+ if ptype == 0:
564
+ if re.search(r':\s*$', line):
565
+ content += self.create_paragraph(lines, cnt, secid, dtype)
566
+ lines = []
567
+ ptype = 1
568
+ cnt = 0
569
+ elif cnt == 1 and re.match(r'^\s+%s\s(-|\[|ds\d*|\d+|[A-Z]{2}\s)' % self.DOCS['DOCNAM'], line):
570
+ ptype = 2
571
+ elif cnt > 0:
572
+ content += self.create_desc_content(lines, cnt, secid, dtype, ptype)
573
+ cnt = ptype = 0
574
+ lines = []
575
+
576
+ if cnt > 0:
577
+ content += self.create_desc_content(lines, cnt, secid, dtype, ptype)
578
+
579
+ return content
580
+
581
+ #
582
+ # create description content according to the paragraph type
583
+ #
584
+ def create_desc_content(self, lines, cnt, secid, dtype, ptype):
585
+
586
+ if ptype == 1:
587
+ return self.create_table(lines, cnt, secid)
588
+ elif ptype == 2:
589
+ return self.create_synopsis(lines, cnt, secid, dtype)
590
+ else:
591
+ return self.create_paragraph(lines, cnt, secid, dtype)
592
+
593
+ #
594
+ # description type (dtype): 0 - section, 1 - option, 2 - exmaple, 3 - action
595
+ #
596
+ def create_paragraph(self, lines, cnt, secid, dtype):
597
+
598
+ doreplace = 1
599
+ content = "<p>\n"
600
+ line0 = lines[0]
601
+ normal = 1
602
+ if dtype == 2:
603
+ ms = re.match(r'^<<(Content .*)>>$', line0)
604
+ if ms: # input files for examples
605
+ content += ms.group(1) + ":\n</p><p>\n"
606
+ normal = 0
607
+ for i in range(1, cnt):
608
+ line = lines[i]
609
+ if doreplace and line.find('<:>') > -1 and not re.match(r'^[A-Z]\w+<:>[A-Z]\w+<:>', line):
610
+ doreplace = 0
611
+ if doreplace:
612
+ content += self.replace_option_link(line, secid, 0) + "<br>\n"
613
+ else:
614
+ content += line + "<br>\n"
615
+ if re.match(r'^\[\w+\]$', line): doreplace = 1
616
+ content += "</p>\n"
617
+ if normal: # normal paragraph
618
+ ii = 0
619
+ if dtype == 3:
620
+ if re.match(r'^\s*Mode options* that ', line0):
621
+ content += "<a name=\"mode\"></a>" + self.replace_option_link(line0, secid, 0) + "\n"
622
+ ii = 1
623
+ elif re.match(r'^\s*Use Info option -FN ', line0):
624
+ content += "<a name=\"field\"></a>" + self.replace_option_link(line0, secid, 0) + "\n"
625
+ ii = 1
626
+ for i in range(ii, cnt):
627
+ line = lines[i]
628
+ content += self.replace_option_link(line, secid, 0) + "\n"
629
+ content += "</p>\n"
630
+
631
+ return content
632
+
633
+ #
634
+ # create table html content
635
+ #
636
+ def create_table(self, lines, cnt, secid):
637
+
638
+ line0 = lines[0]
639
+ ms = re.match(r'^\s+-\s+(.*)', line0)
640
+ if ms: # create a list
641
+ content = "<ol>\n<li>" + self.replace_option_link(ms.group(1), secid, 1) + "\n"
642
+ for i in range(1, cnt):
643
+ line = lines[i]
644
+ ms = re.match(r'^\s+-\s+(.*)', line)
645
+ if ms:
646
+ content += "</li><li>" + self.replace_option_link(ms.group(1), secid, 1) + "\n"
647
+ else:
648
+ content += self.replace_option_link(line, secid, 1) + "\n"
649
+ content += "</li></ol>\n"
650
+ elif re.search(r'=>$', line0):
651
+ line = re.sub(r'={1,}', '=', line0)
652
+ content = "&nbsp&nbsp{}<br>\n".format(line)
653
+ for i in range(1, cnt):
654
+ line = lines[i]
655
+ line = re.sub(r'={2,}', '=', line)
656
+ content += "&nbsp&nbsp{}<br>\n".format(line)
657
+ else:
658
+ content = "<p><table border=2 cellspacing=0 cellpadding=2 bgcolor=\"#dfcfb3\">\n"
659
+ if re.search(r'\S\s+-\s+\S', line0):
660
+ vals = ['', '']
661
+ for i in range(cnt):
662
+ line = lines[i]
663
+ line = line.lstrip()
664
+ ms = re.match(r'^(.*\S)\s+-\s+(\S.*)$', line)
665
+ if ms:
666
+ vals[0] = ms.group(1)
667
+ vals[1] = self.replace_option_link(ms.group(2), secid, 1)
668
+ if re.match(r'^-', vals[0]):
669
+ vals[0] = self.replace_option_link(vals[0], secid, 1)
670
+ else:
671
+ vals[0] = self.get_title_link(vals[0])
672
+ if i > 0: content += "<tr><td align=\"right\" nowrap>{}</td><td>{}</td></tr>\n".format(vals[0], vals[1])
673
+ else:
674
+ vals[1] += "\n" + self.replace_option_link(line, secid, 1)
675
+ content += "<tr><td align=\"right\" nowrap>{}</td><td>{}</td></tr>\n".format(vals[0], vals[1])
676
+ else:
677
+ for i in range(cnt):
678
+ line = lines[i]
679
+ vals = re.split(r'\s{2,}', self.replace_option_link(line, secid, 1))
680
+ for val in vals:
681
+ content += "<td>{}</td>".format(val)
682
+ content += "</tr>\n"
683
+ content += "</table></p>\n"
684
+
685
+ return content
686
+
687
+ #
688
+ # description type (dtype): 0 - section, 1 - option, 2 - exmaple, 3 - action
689
+ #
690
+ def create_synopsis(self, lines, cnt, secid, dtype):
691
+
692
+ content = "<p><table cellspacing=10>\n"
693
+
694
+ for i in range(cnt):
695
+ line = self.replace_option_link(lines[i], secid, 2, dtype)
696
+ if re.search(r'\sor\s', line, re.I):
697
+ content += "<tr><td>Or</td><td>&nbsp</td></tr>"
698
+ else:
699
+ ms = re.match(r'^\s*{}\s+(.+)$'.format(self.DOCS['DOCNAM']), line)
700
+ if ms:
701
+ content += "<tr><td>{}{}{}</td><td>{}</td></tr>\n".format(self.Q1, self.DOCS['DOCNAM'], self.Q2, ms.group(1))
702
+ else:
703
+ content += "<tr><td>&nbsp</td><td>{}</td></tr>\n".format(line)
704
+ content += "</table></p>\n"
705
+
706
+ return content
707
+
708
+ #
709
+ # get a short option name by searching hashes OPTS and ALIAS
710
+ #
711
+ def get_short_option(self, p):
712
+
713
+ plen = len(p)
714
+ if plen == 2 and p in self.options: return p
715
+
716
+ for opt in self.OPTS:
717
+ if re.match(r'^{}$'.format(self.OPTS[opt][1]), p, re.I): return opt
718
+
719
+ for opt in self.ALIAS:
720
+ for alias in self.ALIAS[opt]:
721
+ if re.match(r'^{}$'.format(alias), p, re.I): return opt
722
+
723
+ PgLOG.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), PgLOG.LGWNEX)
724
+
725
+ #
726
+ # replace with link for a given section title
727
+ #
728
+ def get_title_link(self, title):
729
+
730
+ for section in self.sections:
731
+ if title == section['title']:
732
+ return "<a href=\"section{}.html\">{}</a>".format(section['secid'], title)
733
+
734
+ return title
735
+
736
+ #
737
+ # get section for given section id
738
+ #
739
+ def get_section(self, secid):
740
+
741
+ for section in self.sections:
742
+ if section['secid'] == secid: return section
743
+
744
+ PgLOG.pglog("Uknown Section ID {}".format(secid), PgLOG.LGWNEX)