Subversion Repositories SvarDOS

Rev

Rev 936 | Rev 946 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 936 Rev 941
1
<?php /*
1
<?php /*
2
 
2
 
3
  SvarDOS repo index builder
3
  SvarDOS repo index builder
4
  Copyright (C) Mateusz Viste 2012-2022
4
  Copyright (C) Mateusz Viste 2012-2022
5
 
5
 
6
  buildidx computes an index json file for the SvarDOS repository.
6
  buildidx computes an index json file for the SvarDOS repository.
7
  it must be executed pointing to a directory that stores packages (*.svp)
7
  it must be executed pointing to a directory that stores packages (*.svp)
8
  files. buildidx will generate the index file and save it into the package
8
  files. buildidx will generate the index file and save it into the package
9
  repository.
9
  repository.
10
 
10
 
11
  requires php-zip
11
  requires php-zip
12
 
12
 
-
 
13
  23 feb 2022: basic validation of source archives (not empty + matches an existing svp file)
13
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
14
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
14
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
15
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
15
  16 feb 2022: added warning about overlong version strings and wild files location
16
  16 feb 2022: added warning about overlong version strings and wild files location
16
  15 feb 2022: index is generated as json, contains all filenames and alt versions
17
  15 feb 2022: index is generated as json, contains all filenames and alt versions
17
  14 feb 2022: packages are expected to have the *.svp extension
18
  14 feb 2022: packages are expected to have the *.svp extension
18
  12 feb 2022: skip source packages from being processed (*.src.zip)
19
  12 feb 2022: skip source packages from being processed (*.src.zip)
19
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
20
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
20
  13 feb 2021: 'title' LSM field is no longer looked after
21
  13 feb 2021: 'title' LSM field is no longer looked after
21
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
22
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
22
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
23
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
23
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
24
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
24
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
25
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
25
  27 aug 2016: accepting full paths to repos (starting with /...)
26
  27 aug 2016: accepting full paths to repos (starting with /...)
26
  07 dec 2013: rewritten buildidx in ANSI C89
27
  07 dec 2013: rewritten buildidx in ANSI C89
27
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
28
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
28
  22 jul 2013: creating a listing.txt file with list of packages
29
  22 jul 2013: creating a listing.txt file with list of packages
29
  18 jul 2013: writing the number of packaged into the first line of the lst file
30
  18 jul 2013: writing the number of packaged into the first line of the lst file
30
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
31
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
31
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
32
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
32
  04 feb 2013: added CRC32 support
33
  04 feb 2013: added CRC32 support
33
  22 sep 2012: forked 1st version from FDUPDATE builder
34
  22 sep 2012: forked 1st version from FDUPDATE builder
34
*/
35
*/
35
 
36
 
36
$PVER = "20220222";
37
$PVER = "20220223";
37
 
38
 
38
 
39
 
39
// computes the BSD sum of a file and returns it
40
// computes the BSD sum of a file and returns it
40
function file2bsum($fname) {
41
function file2bsum($fname) {
41
  $result = 0;
42
  $result = 0;
42
 
43
 
43
  $fd = fopen($fname, 'rb');
44
  $fd = fopen($fname, 'rb');
44
  if ($fd === false) return(0);
45
  if ($fd === false) return(0);
45
 
46
 
46
  while (!feof($fd)) {
47
  while (!feof($fd)) {
47
 
48
 
48
    $buff = fread($fd, 1024 * 1024);
49
    $buff = fread($fd, 1024 * 1024);
49
 
50
 
50
    $slen = strlen($buff);
51
    $slen = strlen($buff);
51
    for ($i = 0; $i < $slen; $i++) {
52
    for ($i = 0; $i < $slen; $i++) {
52
      // rotr
53
      // rotr
53
      $result = ($result >> 1) | ($result << 15);
54
      $result = ($result >> 1) | ($result << 15);
54
      // add and truncate to 16 bits
55
      // add and truncate to 16 bits
55
      $result += ord($buff[$i]);
56
      $result += ord($buff[$i]);
56
      $result &= 0xffff;
57
      $result &= 0xffff;
57
    }
58
    }
58
  }
59
  }
59
 
60
 
60
  fclose($fd);
61
  fclose($fd);
61
  return($result);
62
  return($result);
62
}
63
}
63
 
64
 
64
 
65
 
65
// translates a version string into a array of integer values.
66
// translates a version string into a array of integer values.
66
// Accepted formats follow:
67
// Accepted formats follow:
67
//    300.12.1
68
//    300.12.1
68
//    1
69
//    1
69
//    12.2.34.2-4.5
70
//    12.2.34.2-4.5
70
//    1.2c
71
//    1.2c
71
//    1.01 beta+3
72
//    1.01 beta+3
72
//    2013-12-31
73
//    2013-12-31
73
//    20220222 alpha
74
//    20220222 alpha
74
function vertoarr($verstr) {
75
function vertoarr($verstr) {
75
  $subver = array(0,0,0,0);
76
  $subver = array(0,0,0,0);
76
 
77
 
77
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
78
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
78
  $verstr = strtolower(trim($verstr));
79
  $verstr = strtolower(trim($verstr));
79
 
80
 
80
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
81
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
81
  $verstr = strtr($verstr, '-/', '..');
82
  $verstr = strtr($verstr, '-/', '..');
82
 
83
 
83
  // is there a subversion value? (for example "+4" in "1.05+4")
84
  // is there a subversion value? (for example "+4" in "1.05+4")
84
  $i = strrpos($verstr, '+', 1);
85
  $i = strrpos($verstr, '+', 1);
85
  if ($i !== false) {
86
  if ($i !== false) {
86
    // validate the svar-version is a proper integer
87
    // validate the svar-version is a proper integer
87
    $svarver = substr($verstr, $i + 1);
88
    $svarver = substr($verstr, $i + 1);
88
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
89
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
89
      return(false);
90
      return(false);
90
    }
91
    }
91
    $subver[3] = intval($svarver); // set the +rev as a very minor item
92
    $subver[3] = intval($svarver); // set the +rev as a very minor item
92
    $verstr = substr($verstr, 0, $i);
93
    $verstr = substr($verstr, 0, $i);
93
  }
94
  }
94
 
95
 
95
  // beta reordering: convert "beta 0.95" to "0.95 beta"
96
  // beta reordering: convert "beta 0.95" to "0.95 beta"
96
  if (preg_match('/^beta /', $verstr)) $verstr = substr($verstr, 5) . ' beta';
97
  if (preg_match('/^beta /', $verstr)) $verstr = substr($verstr, 5) . ' beta';
97
 
98
 
98
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
99
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
99
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
100
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
100
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
101
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
101
 
102
 
102
  // same as above, but this time adding a trailing space separator
103
  // same as above, but this time adding a trailing space separator
103
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
104
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
104
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
105
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
105
 
106
 
106
  // is the version ending with ' alpha', 'beta', etc?
107
  // is the version ending with ' alpha', 'beta', etc?
107
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
108
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
108
    // if there is a trailing beta-number, process it first
109
    // if there is a trailing beta-number, process it first
109
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
110
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
110
      $i = strrpos($verstr, ' ');
111
      $i = strrpos($verstr, ' ');
111
      $subver[2] = intval(substr($verstr, $i + 1));
112
      $subver[2] = intval(substr($verstr, $i + 1));
112
      $verstr = trim(substr($verstr, 0, $i));
113
      $verstr = trim(substr($verstr, 0, $i));
113
    }
114
    }
114
    $i = strrpos($verstr, ' ');
115
    $i = strrpos($verstr, ' ');
115
    $greek = substr($verstr, $i + 1);
116
    $greek = substr($verstr, $i + 1);
116
    $verstr = trim(substr($verstr, 0, $i));
117
    $verstr = trim(substr($verstr, 0, $i));
117
    if ($greek == 'alpha') {
118
    if ($greek == 'alpha') {
118
      $subver[1] = 1;
119
      $subver[1] = 1;
119
    } else if ($greek == 'beta') {
120
    } else if ($greek == 'beta') {
120
      $subver[1] = 2;
121
      $subver[1] = 2;
121
    } else if ($greek == 'gamma') {
122
    } else if ($greek == 'gamma') {
122
      $subver[1] = 3;
123
      $subver[1] = 3;
123
    } else if ($greek == 'delta') {
124
    } else if ($greek == 'delta') {
124
      $subver[1] = 4;
125
      $subver[1] = 4;
125
    } else if ($greek == 'pre') {
126
    } else if ($greek == 'pre') {
126
      $subver[1] = 5;
127
      $subver[1] = 5;
127
    } else if ($greek == 'rc') {
128
    } else if ($greek == 'rc') {
128
      $subver[1] = 6;
129
      $subver[1] = 6;
129
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
130
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
130
      $subver[1] = 99;
131
      $subver[1] = 99;
131
    } else {
132
    } else {
132
      return(false);
133
      return(false);
133
    }
134
    }
134
  } else {
135
  } else {
135
    $subver[1] = 98; // one less than the 'patch' level
136
    $subver[1] = 98; // one less than the 'patch' level
136
  }
137
  }
137
 
138
 
138
  // does the version string have a single-letter subversion? (1.0c)
139
  // does the version string have a single-letter subversion? (1.0c)
139
  if (preg_match('/[a-z]$/', $verstr)) {
140
  if (preg_match('/[a-z]$/', $verstr)) {
140
    $subver[0] = ord(substr($verstr, -1));
141
    $subver[0] = ord(substr($verstr, -1));
141
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
142
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
142
  }
143
  }
143
 
144
 
144
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
145
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
145
  // note that dashes have already been replaced by dots
146
  // note that dashes have already been replaced by dots
146
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
147
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
147
    $verstr = str_replace('.', '', $verstr);
148
    $verstr = str_replace('.', '', $verstr);
148
  }
149
  }
149
 
150
 
150
  // convert "2009mar17" versions to "17mar2009"
151
  // convert "2009mar17" versions to "17mar2009"
151
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
152
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
152
    $dy = substr($verstr, 7);
153
    $dy = substr($verstr, 7);
153
    $mo = substr($verstr, 4, 3);
154
    $mo = substr($verstr, 4, 3);
154
    $ye = substr($verstr, 0, 4);
155
    $ye = substr($verstr, 0, 4);
155
    $verstr = "{$dy}{$mo}{$ye}";
156
    $verstr = "{$dy}{$mo}{$ye}";
156
  }
157
  }
157
 
158
 
158
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
159
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
159
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
160
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
160
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
161
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
161
    $dy = substr($verstr, 0, 2);
162
    $dy = substr($verstr, 0, 2);
162
    $mo = $months[substr($verstr, 2, 3)];
163
    $mo = $months[substr($verstr, 2, 3)];
163
    $ye = substr($verstr, 5);
164
    $ye = substr($verstr, 5);
164
    $verstr = "{$ye}.{$mo}.{$dy}";
165
    $verstr = "{$ye}.{$mo}.{$dy}";
165
  }
166
  }
166
 
167
 
167
  // validate the format is supported, should be something no more complex than 1.05.3.33
168
  // validate the format is supported, should be something no more complex than 1.05.3.33
168
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
169
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
169
    return(false);
170
    return(false);
170
  }
171
  }
171
 
172
 
172
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
173
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
173
  //       has a special meaning
174
  //       has a special meaning
174
  $exploded = explode('.', $verstr);
175
  $exploded = explode('.', $verstr);
175
  if (count($exploded) > 16) {
176
  if (count($exploded) > 16) {
176
    return(false);
177
    return(false);
177
  }
178
  }
178
  $exploded[16] = $subver[0]; // a-z (1.0c)
179
  $exploded[16] = $subver[0]; // a-z (1.0c)
179
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
180
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
180
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
181
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
181
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
182
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
182
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
183
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
183
 
184
 
184
  ksort($exploded);
185
  ksort($exploded);
185
 
186
 
186
  return($exploded);
187
  return($exploded);
187
}
188
}
188
 
189
 
189
 
190
 
190
function dos_version_compare($v1, $v2) {
191
function dos_version_compare($v1, $v2) {
191
  $v1arr = vertoarr($v1);
192
  $v1arr = vertoarr($v1);
192
  $v2arr = vertoarr($v2);
193
  $v2arr = vertoarr($v2);
193
  for ($i = 0; $i < count($v1arr); $i++) {
194
  for ($i = 0; $i < count($v1arr); $i++) {
194
    if ($v1arr[$i] > $v2arr[$i]) return(1);
195
    if ($v1arr[$i] > $v2arr[$i]) return(1);
195
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
196
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
196
  }
197
  }
197
  return(0);
198
  return(0);
198
}
199
}
199
 
200
 
200
 
201
 
201
// reads file fil from zip archive z and returns its content, or false on error
202
// reads file fil from zip archive z and returns its content, or false on error
202
function read_file_from_zip($z, $fil) {
203
function read_file_from_zip($z, $fil) {
203
  $zip = new ZipArchive;
204
  $zip = new ZipArchive;
204
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
205
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
205
    echo "ERROR: failed to open zip file '{$z}'\n";
206
    echo "ERROR: failed to open zip file '{$z}'\n";
206
    return(false);
207
    return(false);
207
  }
208
  }
208
 
209
 
209
  // load the appinfo/pkgname.lsm file
210
  // load the appinfo/pkgname.lsm file
210
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
211
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
211
 
212
 
212
  $zip->close();
213
  $zip->close();
213
  return($res);
214
  return($res);
214
}
215
}
215
 
216
 
216
 
217
 
217
function read_list_of_files_in_zip($z) {
218
function read_list_of_files_in_zip($z) {
218
  $zip = new ZipArchive;
219
  $zip = new ZipArchive;
219
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
220
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
220
    echo "ERROR: failed to open zip file '{$z}'\n";
221
    echo "ERROR: failed to open zip file '{$z}'\n";
221
    return(false);
222
    return(false);
222
  }
223
  }
223
 
224
 
224
  $res = array();
225
  $res = array();
225
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
226
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
226
 
227
 
227
  $zip->close();
228
  $zip->close();
228
  return($res);
229
  return($res);
229
}
230
}
230
 
231
 
231
 
232
 
232
// reads a LSM string and returns it in the form of an array
233
// reads a LSM string and returns it in the form of an array
233
function parse_lsm($s) {
234
function parse_lsm($s) {
234
  $res = array();
235
  $res = array();
235
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
236
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
236
    // the line is "token: value", let's find the colon
237
    // the line is "token: value", let's find the colon
237
    $colpos = strpos($l, ':');
238
    $colpos = strpos($l, ':');
238
    if (($colpos === false) || ($colpos === 0)) continue;
239
    if (($colpos === false) || ($colpos === 0)) continue;
239
    $tok = strtolower(trim(substr($l, 0, $colpos)));
240
    $tok = strtolower(trim(substr($l, 0, $colpos)));
240
    $val = trim(substr($l, $colpos + 1));
241
    $val = trim(substr($l, $colpos + 1));
241
    $res[$tok] = $val;
242
    $res[$tok] = $val;
242
  }
243
  }
243
  return($res);
244
  return($res);
244
}
245
}
245
 
246
 
246
 
247
 
247
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
248
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
248
function str_head_is($haystack, $needle) {
249
function str_head_is($haystack, $needle) {
249
  return strpos($haystack, $needle) === 0;
250
  return strpos($haystack, $needle) === 0;
250
}
251
}
251
 
252
 
252
 
253
 
253
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
254
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
254
function load_core_list($repodir) {
255
function load_core_list($repodir) {
255
  $res = array();
256
  $res = array();
256
 
257
 
257
  foreach (scandir($repodir . '/core/') as $f) {
258
  foreach (scandir($repodir . '/core/') as $f) {
258
    if (!preg_match('/\.svp$/', $f)) continue;
259
    if (!preg_match('/\.svp$/', $f)) continue;
259
    $res[] = explode('.', $f)[0];
260
    $res[] = explode('.', $f)[0];
260
  }
261
  }
261
  return($res);
262
  return($res);
262
}
263
}
263
 
264
 
264
 
265
 
265
// ***************** MAIN ROUTINE *********************************************
266
// ***************** MAIN ROUTINE *********************************************
266
 
267
 
267
//echo "SvarDOS repository index generator ver {$PVER}\n";
268
//echo "SvarDOS repository index generator ver {$PVER}\n";
268
 
269
 
269
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
270
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
270
  echo "usage: php buildidx.php repodir\n";
271
  echo "usage: php buildidx.php repodir\n";
271
  exit(1);
272
  exit(1);
272
}
273
}
273
 
274
 
274
$repodir = $_SERVER['argv'][1];
275
$repodir = $_SERVER['argv'][1];
275
 
276
 
276
$pkgfiles = scandir($repodir);
277
$pkgfiles = scandir($repodir);
277
$pkgcount = 0;
278
$pkgcount = 0;
278
 
279
 
279
 
280
 
280
// load the list of CORE and MSDOS_COMPAT packages
281
// load the list of CORE and MSDOS_COMPAT packages
281
 
282
 
282
$core_packages_list = load_core_list($repodir);
283
$core_packages_list = load_core_list($repodir);
283
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
284
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
284
 
285
 
285
// do a list of all svp packages with their available versions and descriptions
286
// do a list of all svp packages with their available versions and descriptions
286
 
287
 
287
$pkgdb = array();
288
$pkgdb = array();
288
foreach ($pkgfiles as $fname) {
289
foreach ($pkgfiles as $fname) {
-
 
290
 
-
 
291
  // zip files (ie. source archives)
-
 
292
  if (preg_match('/\.zip$/', $fname)) {
-
 
293
    // the zip archive should contain at least one file
-
 
294
    if (count(read_list_of_files_in_zip($repodir . '/' . $fname)) < 1) echo "WARNING: source archive {$fname} contains no files (either empty or corrupted)\n";
-
 
295
    // check that the file relates to an existing svp package
-
 
296
    $svpfname = preg_replace('/zip$/', 'svp', $fname);
-
 
297
    if (!file_exists($repodir . '/' . $svpfname)) echo "ERROR: orphaned source archive '{$fname}' (no matching svp file, expecting a package named '{$svpfname}')\n";
-
 
298
    // that is for zip files
-
 
299
    continue;
-
 
300
  }
-
 
301
 
-
 
302
  // skip (and warn about) non-svp
289
  if (!preg_match('/\.svp$/i', $fname)) continue; // skip non-svp files
303
  if (!preg_match('/\.svp$/', $fname)) {
-
 
304
    $okfiles = array('.', '..', '_cats.json', '_index.json', 'core');
-
 
305
    if (array_search($fname, $okfiles) !== false) continue;
-
 
306
    echo "WARNING: wild file '{$fname} (this is either an useless file that should be removed, or a misnamed package or source archive)'\n";
-
 
307
    continue;
-
 
308
  }
290
 
309
 
291
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
310
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
292
    echo "ERROR: {$fname} has a very weird name\n";
311
    echo "ERROR: {$fname} has a very weird name\n";
293
    continue;
312
    continue;
294
  }
313
  }
295
 
314
 
296
  $path_parts = pathinfo($fname);
315
  $path_parts = pathinfo($fname);
297
  $pkgnam = explode('-', $path_parts['filename'])[0];
316
  $pkgnam = explode('-', $path_parts['filename'])[0];
298
  $pkgfullpath = realpath($repodir . '/' . $fname);
317
  $pkgfullpath = realpath($repodir . '/' . $fname);
299
 
318
 
300
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
319
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
301
  if ($lsm == false) {
320
  if ($lsm == false) {
302
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
321
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
303
    continue;
322
    continue;
304
  }
323
  }
305
  $lsmarray = parse_lsm($lsm);
324
  $lsmarray = parse_lsm($lsm);
306
  if (empty($lsmarray['version'])) {
325
  if (empty($lsmarray['version'])) {
307
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
326
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
308
    continue;
327
    continue;
309
  }
328
  }
310
  if (strlen($lsmarray['version']) > 16) {
329
  if (strlen($lsmarray['version']) > 16) {
311
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
330
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
312
    continue;
331
    continue;
313
  }
332
  }
314
  if (empty($lsmarray['description'])) {
333
  if (empty($lsmarray['description'])) {
315
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
334
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
316
    continue;
335
    continue;
317
  }
336
  }
318
 
337
 
319
  // validate the files present in the archive
338
  // validate the files present in the archive
320
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
339
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
321
  $pkgdir = $pkgnam;
340
  $pkgdir = $pkgnam;
322
 
341
 
323
  // special rule for "parent and children" packages
342
  // special rule for "parent and children" packages
324
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
343
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
325
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
344
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
326
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
345
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
327
 
346
 
328
  // array used to detect duplicated entries after lower-case conversion
347
  // array used to detect duplicated entries after lower-case conversion
329
  $duparr = array();
348
  $duparr = array();
330
 
349
 
331
  // will hold the list of categories that this package belongs to
350
  // will hold the list of categories that this package belongs to
332
  $catlist = array();
351
  $catlist = array();
333
 
352
 
334
  foreach ($listoffiles as $f) {
353
  foreach ($listoffiles as $f) {
335
    $f = strtolower($f);
354
    $f = strtolower($f);
336
    $path_array = explode('/', $f);
355
    $path_array = explode('/', $f);
337
    // emit a warning when non-8+3 filenames are spotted and find duplicates
356
    // emit a warning when non-8+3 filenames are spotted and find duplicates
338
    foreach ($path_array as $item) {
357
    foreach ($path_array as $item) {
339
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
358
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
340
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
359
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
341
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
360
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
342
      }
361
      }
343
    }
362
    }
344
    // look for dups
363
    // look for dups
345
    if (array_search($f, $duparr) !== false) {
364
    if (array_search($f, $duparr) !== false) {
346
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
365
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
347
    } else {
366
    } else {
348
      $duparr[] = $f;
367
      $duparr[] = $f;
349
    }
368
    }
350
    // LSM file is ok
369
    // LSM file is ok
351
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
370
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
352
    if ($f === "appinfo/") continue;
371
    if ($f === "appinfo/") continue;
353
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
372
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
354
    $core_or_msdoscompat = 0;
373
    $core_or_msdoscompat = 0;
355
    if (array_search($pkgnam, $core_packages_list) !== false) {
374
    if (array_search($pkgnam, $core_packages_list) !== false) {
356
      $catlist[] = 'core';
375
      $catlist[] = 'core';
357
      $core_or_msdoscompat = 1;
376
      $core_or_msdoscompat = 1;
358
    }
377
    }
359
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
378
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
360
      $catlist[] = 'msdos_compat';
379
      $catlist[] = 'msdos_compat';
361
      $core_or_msdoscompat = 1;
380
      $core_or_msdoscompat = 1;
362
    }
381
    }
363
    if ($core_or_msdoscompat == 1) {
382
    if ($core_or_msdoscompat == 1) {
364
      if (str_head_is($f, 'bin/')) continue;
383
      if (str_head_is($f, 'bin/')) continue;
365
      if (str_head_is($f, 'cpi/')) continue;
384
      if (str_head_is($f, 'cpi/')) continue;
366
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
385
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
367
      if ($f === 'doc/') continue;
386
      if ($f === 'doc/') continue;
368
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
387
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
369
      if ($f === 'nls/') continue;
388
      if ($f === 'nls/') continue;
370
    }
389
    }
371
    // the help package is allowed to put files in... help
390
    // the help package is allowed to put files in... help
372
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
391
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
373
    // must be category-prefixed file, add it to the list of categories for this package
392
    // must be category-prefixed file, add it to the list of categories for this package
374
    $catlist[] = explode('/', $f)[0];
393
    $catlist[] = explode('/', $f)[0];
375
    // well-known "category" dirs are okay
394
    // well-known "category" dirs are okay
376
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
395
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
377
    if ($f === 'progs/') continue;
396
    if ($f === 'progs/') continue;
378
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
397
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
379
    if ($f === 'devel/') continue;
398
    if ($f === 'devel/') continue;
380
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
399
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
381
    if ($f === 'games/') continue;
400
    if ($f === 'games/') continue;
382
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
401
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
383
    if ($f === 'drivers/') continue;
402
    if ($f === 'drivers/') continue;
384
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
403
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
385
  }
404
  }
386
 
405
 
387
  // do I understand the version string?
406
  // do I understand the version string?
388
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
407
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
389
 
408
 
390
  $meta['fname'] = $fname;
409
  $meta['fname'] = $fname;
391
  $meta['desc'] = $lsmarray['description'];
410
  $meta['desc'] = $lsmarray['description'];
392
  $meta['cats'] = array_unique($catlist);
411
  $meta['cats'] = array_unique($catlist);
393
 
412
 
394
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
413
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
395
}
414
}
396
 
415
 
397
 
416
 
398
$db = array();
417
$db = array();
399
$cats = array();
418
$cats = array();
400
 
419
 
401
// ******** compute the version-sorted list of packages with a single *********
420
// ******** compute the version-sorted list of packages with a single *********
402
// ******** description and category list for each package ********************
421
// ******** description and category list for each package ********************
403
 
422
 
404
// iterate over each svp package
423
// iterate over each svp package
405
foreach ($pkgdb as $pkg => $versions) {
424
foreach ($pkgdb as $pkg => $versions) {
406
 
425
 
407
  // sort filenames by version, highest first
426
  // sort filenames by version, highest first
408
  uksort($versions, "dos_version_compare");
427
  uksort($versions, "dos_version_compare");
409
  $versions = array_reverse($versions, true);
428
  $versions = array_reverse($versions, true);
410
 
429
 
411
  foreach ($versions as $ver => $meta) {
430
  foreach ($versions as $ver => $meta) {
412
    $fname = $meta['fname'];
431
    $fname = $meta['fname'];
413
    $desc = $meta['desc'];
432
    $desc = $meta['desc'];
414
 
433
 
415
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
434
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
416
 
435
 
417
    $meta2['ver'] = strval($ver);
436
    $meta2['ver'] = strval($ver);
418
    $meta2['bsum'] = $bsum;
437
    $meta2['bsum'] = $bsum;
419
 
438
 
420
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
439
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
421
    if (empty($db[$pkg]['cats'])) {
440
    if (empty($db[$pkg]['cats'])) {
422
      $db[$pkg]['cats'] = $meta['cats'];
441
      $db[$pkg]['cats'] = $meta['cats'];
423
      $cats = array_unique(array_merge($cats, $meta['cats']));
442
      $cats = array_unique(array_merge($cats, $meta['cats']));
424
    }
443
    }
425
    $db[$pkg]['versions'][$fname] = $meta2;
444
    $db[$pkg]['versions'][$fname] = $meta2;
426
  }
445
  }
427
 
446
 
428
  $pkgcount++;
447
  $pkgcount++;
429
 
448
 
430
}
449
}
431
 
450
 
432
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
451
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
433
 
452
 
434
$json_blob = json_encode($db);
453
$json_blob = json_encode($db);
435
if ($json_blob === false) {
454
if ($json_blob === false) {
436
  echo "ERROR: JSON convertion failed! -> ";
455
  echo "ERROR: JSON convertion failed! -> ";
437
  switch (json_last_error()) {
456
  switch (json_last_error()) {
438
    case JSON_ERROR_DEPTH:
457
    case JSON_ERROR_DEPTH:
439
      echo 'maximum stack depth exceeded';
458
      echo 'maximum stack depth exceeded';
440
      break;
459
      break;
441
    case JSON_ERROR_STATE_MISMATCH:
460
    case JSON_ERROR_STATE_MISMATCH:
442
      echo 'underflow of the modes mismatch';
461
      echo 'underflow of the modes mismatch';
443
      break;
462
      break;
444
    case JSON_ERROR_CTRL_CHAR:
463
    case JSON_ERROR_CTRL_CHAR:
445
      echo 'unexpected control character found';
464
      echo 'unexpected control character found';
446
      break;
465
      break;
447
    case JSON_ERROR_UTF8:
466
    case JSON_ERROR_UTF8:
448
      echo 'malformed utf-8 characters';
467
      echo 'malformed utf-8 characters';
449
      break;
468
      break;
450
    default:
469
    default:
451
      echo "unknown error";
470
      echo "unknown error";
452
      break;
471
      break;
453
  }
472
  }
454
  echo "\n";
473
  echo "\n";
455
}
474
}
456
 
475
 
457
file_put_contents($repodir . '/_index.json', $json_blob);
476
file_put_contents($repodir . '/_index.json', $json_blob);
458
 
477
 
459
$cats_json = json_encode($cats);
478
$cats_json = json_encode($cats);
460
file_put_contents($repodir . '/_cats.json', $cats_json);
479
file_put_contents($repodir . '/_cats.json', $cats_json);
461
 
480
 
462
exit(0);
481
exit(0);
463
 
482
 
464
?>
483
?>
465
 
484