summaryrefslogtreecommitdiffstats
path: root/modules-available/serversetup-bwlp-ipxe/inc/pxelinux.inc.php
blob: 24b099dc5fb1d78c0d4c1ec3933981e1130d1b0f (plain) (blame)
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
<?php


class PxeLinux
{

	/**
	 * Takes a (partial) pxelinux menu and parses it into
	 * a PxeMenu object.
	 *
	 * @param string $input The pxelinux menu to parse
	 * @return ?PxeMenu the parsed menu, or null if input is not a PXELinux menu
	 */
	public static function parsePxeLinux(string $input, bool $isCp437): ?PxeMenu
	{
		if (empty($input))
			return null;
		if ($isCp437) {
			$input = iconv('IBM437', 'UTF8//TRANSLIT//IGNORE', $input);
		}
		$menu = new PxeMenu;
		$sectionPropMap = [
			'menu label' => ['string', 'title'],
			'menu default' => ['true', 'isDefault'],
			'menu hide' => ['true', 'isHidden'],
			'menu disabled' => ['true', 'isDisabled'],
			'menu indent' => ['int', 'indent'],
			'kernel' => ['string', 'kernel'],
			'com32' => ['string', 'kernel'],
			'pxe' => ['string', 'kernel'],
			'initrd' => ['string', 'initrd'],
			'append' => ['string', 'append'],
			'ipappend' => ['int', 'ipAppend'],
			'sysappend' => ['int', 'ipAppend'],
			'localboot' => ['int', 'localBoot'],
		];
		$globalPropMap = [
			'timeout' => ['int', 'timeoutMs', 100],
			'totaltimeout' => ['int', 'totalTimeoutMs', 100],
			'menu title' => ['string', 'title'],
			'menu clear' => ['true', 'menuClear'],
			'menu immediate' => ['true', 'immediateHotkeys'],
			'ontimeout' => ['string', 'timeoutLabel'],
			'default' => ['string', 'default'],
		];
		$lines = preg_split('/[\r\n]+/', $input);
		$section = null;
		$count = count($lines);
		for ($li = 0; $li < $count; ++$li) {
			$line =& $lines[$li];
			if (!preg_match('/^\s*([^m]\S*|menu\s+\S+)(\s+.*?|)\s*$/i', $line, $out))
				continue;
			$val = trim($out[2]);
			$key = trim($out[1]);
			$key = strtolower($key);
			$key = preg_replace('/\s+/', ' ', $key);
			if ($key === 'label') {
				if ($section !== null) {
					$menu->sections[] = $section;
				}
				$section = new PxeSection($val);
			} elseif ($key === 'menu separator') {
				if ($section !== null) {
					$menu->sections[] = $section;
					$section = null;
				}
				$menu->sections[] = new PxeSection(null);
			} elseif (self::handleKeyword($key, $val, $globalPropMap, $menu)) {
				continue;
			} elseif ($section === null) {
				continue;
			} elseif ($key === 'text' && strtolower($val) === 'help') {
				$text = '';
				while (++$li < $count) {
					$line =& $lines[$li];
					if (strtolower(trim($line)) === 'endtext')
						break;
					$text .= $line . "\n";
				}
				$section->helpText = $text;
			} elseif (self::handleKeyword($key, $val, $sectionPropMap, $section)) {
				//continue;
			}
		}
		if ($section !== null) {
			$menu->sections[] = $section;
		}
		if (empty($menu->sections))
			return null; // Probably not a PXE menu but random text?
		foreach ($menu->sections as $section) {
			$section->mangle();
		}
		return $menu;
	}

	/**
	 * Check if keyword is valid and if so, add its interpreted value
	 * to the given object. The map to look up the keyword has to be passed
	 * as well as the object to set the value in. Map and object should
	 * obviously match.
	 *
	 * @param string $key keyword of parsed line
	 * @param string $val raw value of currently parsed line (empty if not present)
	 * @param array $map Map in which $key is looked up as key
	 * @param PxeMenu|PxeSection $object The object to set the parsed and sanitized value in
	 * @return bool true if the value was found in the map (and set in the object), false otherwise
	 */
	private static function handleKeyword(string $key, string $val, array $map, $object): bool
	{
		if (!isset($map[$key]))
			return false;
		$opt = $map[$key];
		// opt[0] is the type the value should be cast to; special case "true" means
		// this is a bool option that will be set as soon as the keyword is present,
		// as it doesn't have any parameters
		if ($opt[0] === 'true') {
			$val = true;
		} else {
			settype($val, $opt[0]);
		}
		// If opt[2] is present it's a multiplier for the value
		if (isset($opt[2])) {
			$val *= $opt[2];
		}
		$object->{$opt[1]} = $val;
		return true;
	}

}