diff options
| author | Stefan Monnier | 2022-01-03 15:04:12 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2022-01-03 15:04:12 -0500 |
| commit | 460f35e96df1c39ce2ba0f424b36365a2f9e9825 (patch) | |
| tree | 28398a961fccf9538114174b32d96f5dd918043a /lib-src | |
| parent | ab5ee3e29e916d4009b301841e9780aad564a6a0 (diff) | |
| download | emacs-460f35e96df1c39ce2ba0f424b36365a2f9e9825.tar.gz emacs-460f35e96df1c39ce2ba0f424b36365a2f9e9825.zip | |
Revert part of 59732a83c8 to fix bug#52969
While we don't need to put docstrings of .elc files into etc/DOC,
we still need to put those of `loaddefs.el` there since we don't have
a "dynamic docstring" feature for the non-compiled files and keeping
the actual docstrings in the heap would be prohibitive.
* src/Makefile.in ($(etc)/DOC): Scan `lisp/loaddefs.el` still.
* lib-src/make-docfile.c (scan_lisp_file): New function.
(scan_file): Use it.
(skip_white, read_lisp_symbol, search_lisp_doc_at_eol): New functions.
Diffstat (limited to 'lib-src')
| -rw-r--r-- | lib-src/make-docfile.c | 362 |
1 files changed, 359 insertions, 3 deletions
diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index 33ed5ec0c92..199f1dbbcc7 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c | |||
| @@ -19,8 +19,8 @@ You should have received a copy of the GNU General Public License | |||
| 19 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | 19 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | /* The arguments given to this program are all the C files | 22 | /* The arguments given to this program are all the C and some Lisp source files |
| 23 | of GNU Emacs. .c files are allowed. | 23 | of GNU Emacs. .el and .c files are allowed. |
| 24 | A .o file can also be specified; the .c file it was made from is used. | 24 | A .o file can also be specified; the .c file it was made from is used. |
| 25 | This helps the makefile pass the correct list of files. | 25 | This helps the makefile pass the correct list of files. |
| 26 | Option -d DIR means change to DIR before looking for files. | 26 | Option -d DIR means change to DIR before looking for files. |
| @@ -65,6 +65,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 65 | #endif /* not DOS_NT */ | 65 | #endif /* not DOS_NT */ |
| 66 | 66 | ||
| 67 | static void scan_file (char *filename); | 67 | static void scan_file (char *filename); |
| 68 | static void scan_lisp_file (const char *filename, const char *mode); | ||
| 68 | static void scan_c_file (char *filename, const char *mode); | 69 | static void scan_c_file (char *filename, const char *mode); |
| 69 | static void scan_c_stream (FILE *infile); | 70 | static void scan_c_stream (FILE *infile); |
| 70 | static void start_globals (void); | 71 | static void start_globals (void); |
| @@ -234,9 +235,14 @@ put_filename (char *filename) | |||
| 234 | static void | 235 | static void |
| 235 | scan_file (char *filename) | 236 | scan_file (char *filename) |
| 236 | { | 237 | { |
| 238 | ptrdiff_t len = strlen (filename); | ||
| 239 | |||
| 237 | if (!generate_globals) | 240 | if (!generate_globals) |
| 238 | put_filename (filename); | 241 | put_filename (filename); |
| 239 | scan_c_file (filename, "r"); | 242 | if (len > 3 && !strcmp (filename + len - 3, ".el")) |
| 243 | scan_lisp_file (filename, "r"); | ||
| 244 | else | ||
| 245 | scan_c_file (filename, "r"); | ||
| 240 | } | 246 | } |
| 241 | 247 | ||
| 242 | static void | 248 | static void |
| @@ -1214,4 +1220,354 @@ scan_c_stream (FILE *infile) | |||
| 1214 | fatal ("read error"); | 1220 | fatal ("read error"); |
| 1215 | } | 1221 | } |
| 1216 | 1222 | ||
| 1223 | /* Read a file of Lisp source code. | ||
| 1224 | Looks for | ||
| 1225 | (defun NAME ARGS DOCSTRING ...) | ||
| 1226 | (defmacro NAME ARGS DOCSTRING ...) | ||
| 1227 | (defsubst NAME ARGS DOCSTRING ...) | ||
| 1228 | (autoload (quote NAME) FILE DOCSTRING ...) | ||
| 1229 | (defvar NAME VALUE DOCSTRING) | ||
| 1230 | (defconst NAME VALUE DOCSTRING) | ||
| 1231 | (fset (quote NAME) (make-byte-code ... DOCSTRING ...)) | ||
| 1232 | (fset (quote NAME) #[... DOCSTRING ...]) | ||
| 1233 | (defalias (quote NAME) #[... DOCSTRING ...]) | ||
| 1234 | (custom-declare-variable (quote NAME) VALUE DOCSTRING ...) | ||
| 1235 | starting in column zero. | ||
| 1236 | (quote NAME) may appear as 'NAME as well. | ||
| 1237 | |||
| 1238 | We also look for #@LENGTH CONTENTS^_ at the beginning of the line. | ||
| 1239 | When we find that, we save it for the following defining-form, | ||
| 1240 | and we use that instead of reading a doc string within that defining-form. | ||
| 1241 | |||
| 1242 | For defvar, defconst, and fset we skip to the docstring with a kludgy | ||
| 1243 | formatting convention: all docstrings must appear on the same line as the | ||
| 1244 | initial open-paren (the one in column zero) and must contain a backslash | ||
| 1245 | and a newline immediately after the initial double-quote. No newlines | ||
| 1246 | must appear between the beginning of the form and the first double-quote. | ||
| 1247 | For defun, defmacro, and autoload, we know how to skip over the | ||
| 1248 | arglist, but the doc string must still have a backslash and newline | ||
| 1249 | immediately after the double quote. | ||
| 1250 | The only source files that follow this convention are autoload-generated | ||
| 1251 | files like loaddefs.el; | ||
| 1252 | The NAME and DOCSTRING are output. | ||
| 1253 | NAME is preceded by `F' for a function or `V' for a variable. | ||
| 1254 | An entry is output only if DOCSTRING has \ newline just after the opening ". | ||
| 1255 | */ | ||
| 1256 | |||
| 1257 | static void | ||
| 1258 | skip_white (FILE *infile) | ||
| 1259 | { | ||
| 1260 | int c; | ||
| 1261 | do | ||
| 1262 | c = getc (infile); | ||
| 1263 | while (c_isspace (c)); | ||
| 1264 | |||
| 1265 | ungetc (c, infile); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | static void | ||
| 1269 | read_lisp_symbol (FILE *infile, char *buffer) | ||
| 1270 | { | ||
| 1271 | int c; | ||
| 1272 | char *fillp = buffer; | ||
| 1273 | |||
| 1274 | skip_white (infile); | ||
| 1275 | while (true) | ||
| 1276 | { | ||
| 1277 | c = getc (infile); | ||
| 1278 | if (c == '\\') | ||
| 1279 | { | ||
| 1280 | c = getc (infile); | ||
| 1281 | if (c < 0) | ||
| 1282 | return; | ||
| 1283 | *fillp++ = c; | ||
| 1284 | } | ||
| 1285 | else if (c_isspace (c) || c == '(' || c == ')' || c < 0) | ||
| 1286 | { | ||
| 1287 | ungetc (c, infile); | ||
| 1288 | *fillp = 0; | ||
| 1289 | break; | ||
| 1290 | } | ||
| 1291 | else | ||
| 1292 | *fillp++ = c; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | if (! buffer[0]) | ||
| 1296 | fprintf (stderr, "## expected a symbol, got '%c'\n", c); | ||
| 1297 | |||
| 1298 | skip_white (infile); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | static bool | ||
| 1302 | search_lisp_doc_at_eol (FILE *infile) | ||
| 1303 | { | ||
| 1304 | int c = 0, c1 = 0, c2 = 0; | ||
| 1305 | |||
| 1306 | /* Skip until the end of line; remember two previous chars. */ | ||
| 1307 | while (c != '\n' && c != '\r' && c != EOF) | ||
| 1308 | { | ||
| 1309 | c2 = c1; | ||
| 1310 | c1 = c; | ||
| 1311 | c = getc (infile); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | /* If two previous characters were " and \, | ||
| 1315 | this is a doc string. Otherwise, there is none. */ | ||
| 1316 | if (c2 != '"' || c1 != '\\') | ||
| 1317 | { | ||
| 1318 | #ifdef DEBUG | ||
| 1319 | fprintf (stderr, "## non-docstring found\n"); | ||
| 1320 | #endif | ||
| 1321 | ungetc (c, infile); | ||
| 1322 | return false; | ||
| 1323 | } | ||
| 1324 | return true; | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | static void | ||
| 1328 | scan_lisp_file (const char *filename, const char *mode) | ||
| 1329 | { | ||
| 1330 | FILE *infile; | ||
| 1331 | int c; | ||
| 1332 | int i; | ||
| 1333 | int flen = strlen (filename); | ||
| 1334 | |||
| 1335 | if (generate_globals) | ||
| 1336 | fatal ("scanning lisp file when -g specified"); | ||
| 1337 | |||
| 1338 | infile = fopen (filename, mode); | ||
| 1339 | if (infile == NULL) | ||
| 1340 | { | ||
| 1341 | perror (filename); | ||
| 1342 | exit (EXIT_FAILURE); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | c = '\n'; | ||
| 1346 | while (!feof (infile)) | ||
| 1347 | { | ||
| 1348 | char buffer[BUFSIZ]; | ||
| 1349 | char type; | ||
| 1350 | |||
| 1351 | /* If not at end of line, skip till we get to one. */ | ||
| 1352 | if (c != '\n' && c != '\r') | ||
| 1353 | { | ||
| 1354 | c = getc (infile); | ||
| 1355 | continue; | ||
| 1356 | } | ||
| 1357 | /* Skip the line break. */ | ||
| 1358 | while (c == '\n' || c == '\r') | ||
| 1359 | c = getc (infile); | ||
| 1360 | |||
| 1361 | if (c != '(') | ||
| 1362 | continue; | ||
| 1363 | |||
| 1364 | read_lisp_symbol (infile, buffer); | ||
| 1365 | |||
| 1366 | if (! strcmp (buffer, "defun") | ||
| 1367 | || ! strcmp (buffer, "defmacro") | ||
| 1368 | || ! strcmp (buffer, "defsubst")) | ||
| 1369 | { | ||
| 1370 | type = 'F'; | ||
| 1371 | read_lisp_symbol (infile, buffer); | ||
| 1372 | |||
| 1373 | /* Skip the arguments: either "nil" or a list in parens. */ | ||
| 1374 | |||
| 1375 | c = getc (infile); | ||
| 1376 | if (c == 'n') /* nil */ | ||
| 1377 | { | ||
| 1378 | if ((c = getc (infile)) != 'i' | ||
| 1379 | || (c = getc (infile)) != 'l') | ||
| 1380 | { | ||
| 1381 | fprintf (stderr, "## unparsable arglist in %s (%s)\n", | ||
| 1382 | buffer, filename); | ||
| 1383 | continue; | ||
| 1384 | } | ||
| 1385 | } | ||
| 1386 | else if (c != '(') | ||
| 1387 | { | ||
| 1388 | fprintf (stderr, "## unparsable arglist in %s (%s)\n", | ||
| 1389 | buffer, filename); | ||
| 1390 | continue; | ||
| 1391 | } | ||
| 1392 | else | ||
| 1393 | while (! (c == ')' || c < 0)) | ||
| 1394 | c = getc (infile); | ||
| 1395 | skip_white (infile); | ||
| 1396 | |||
| 1397 | /* If the next three characters aren't `dquote bslash newline' | ||
| 1398 | then we're not reading a docstring. | ||
| 1399 | */ | ||
| 1400 | if ((c = getc (infile)) != '"' | ||
| 1401 | || (c = getc (infile)) != '\\' | ||
| 1402 | || ((c = getc (infile)) != '\n' && c != '\r')) | ||
| 1403 | { | ||
| 1404 | #ifdef DEBUG | ||
| 1405 | fprintf (stderr, "## non-docstring in %s (%s)\n", | ||
| 1406 | buffer, filename); | ||
| 1407 | #endif | ||
| 1408 | continue; | ||
| 1409 | } | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | else if (! strcmp (buffer, "defvar") | ||
| 1413 | || ! strcmp (buffer, "defconst") | ||
| 1414 | || ! strcmp (buffer, "defcustom")) | ||
| 1415 | { | ||
| 1416 | type = 'V'; | ||
| 1417 | read_lisp_symbol (infile, buffer); | ||
| 1418 | |||
| 1419 | if (!search_lisp_doc_at_eol (infile)) | ||
| 1420 | continue; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | else if (! strcmp (buffer, "custom-declare-variable") | ||
| 1424 | || ! strcmp (buffer, "defvaralias") | ||
| 1425 | ) | ||
| 1426 | { | ||
| 1427 | type = 'V'; | ||
| 1428 | |||
| 1429 | c = getc (infile); | ||
| 1430 | if (c == '\'') | ||
| 1431 | read_lisp_symbol (infile, buffer); | ||
| 1432 | else | ||
| 1433 | { | ||
| 1434 | if (c != '(') | ||
| 1435 | { | ||
| 1436 | fprintf (stderr, | ||
| 1437 | "## unparsable name in custom-declare-variable in %s\n", | ||
| 1438 | filename); | ||
| 1439 | continue; | ||
| 1440 | } | ||
| 1441 | read_lisp_symbol (infile, buffer); | ||
| 1442 | if (strcmp (buffer, "quote")) | ||
| 1443 | { | ||
| 1444 | fprintf (stderr, | ||
| 1445 | "## unparsable name in custom-declare-variable in %s\n", | ||
| 1446 | filename); | ||
| 1447 | continue; | ||
| 1448 | } | ||
| 1449 | read_lisp_symbol (infile, buffer); | ||
| 1450 | c = getc (infile); | ||
| 1451 | if (c != ')') | ||
| 1452 | { | ||
| 1453 | fprintf (stderr, | ||
| 1454 | "## unparsable quoted name in custom-declare-variable in %s\n", | ||
| 1455 | filename); | ||
| 1456 | continue; | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | if (!search_lisp_doc_at_eol (infile)) | ||
| 1461 | continue; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias")) | ||
| 1465 | { | ||
| 1466 | type = 'F'; | ||
| 1467 | |||
| 1468 | c = getc (infile); | ||
| 1469 | if (c == '\'') | ||
| 1470 | read_lisp_symbol (infile, buffer); | ||
| 1471 | else | ||
| 1472 | { | ||
| 1473 | if (c != '(') | ||
| 1474 | { | ||
| 1475 | fprintf (stderr, "## unparsable name in fset in %s\n", | ||
| 1476 | filename); | ||
| 1477 | continue; | ||
| 1478 | } | ||
| 1479 | read_lisp_symbol (infile, buffer); | ||
| 1480 | if (strcmp (buffer, "quote")) | ||
| 1481 | { | ||
| 1482 | fprintf (stderr, "## unparsable name in fset in %s\n", | ||
| 1483 | filename); | ||
| 1484 | continue; | ||
| 1485 | } | ||
| 1486 | read_lisp_symbol (infile, buffer); | ||
| 1487 | c = getc (infile); | ||
| 1488 | if (c != ')') | ||
| 1489 | { | ||
| 1490 | fprintf (stderr, | ||
| 1491 | "## unparsable quoted name in fset in %s\n", | ||
| 1492 | filename); | ||
| 1493 | continue; | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | if (!search_lisp_doc_at_eol (infile)) | ||
| 1498 | continue; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | else if (! strcmp (buffer, "autoload")) | ||
| 1502 | { | ||
| 1503 | type = 'F'; | ||
| 1504 | c = getc (infile); | ||
| 1505 | if (c == '\'') | ||
| 1506 | read_lisp_symbol (infile, buffer); | ||
| 1507 | else | ||
| 1508 | { | ||
| 1509 | if (c != '(') | ||
| 1510 | { | ||
| 1511 | fprintf (stderr, "## unparsable name in autoload in %s\n", | ||
| 1512 | filename); | ||
| 1513 | continue; | ||
| 1514 | } | ||
| 1515 | read_lisp_symbol (infile, buffer); | ||
| 1516 | if (strcmp (buffer, "quote")) | ||
| 1517 | { | ||
| 1518 | fprintf (stderr, "## unparsable name in autoload in %s\n", | ||
| 1519 | filename); | ||
| 1520 | continue; | ||
| 1521 | } | ||
| 1522 | read_lisp_symbol (infile, buffer); | ||
| 1523 | c = getc (infile); | ||
| 1524 | if (c != ')') | ||
| 1525 | { | ||
| 1526 | fprintf (stderr, | ||
| 1527 | "## unparsable quoted name in autoload in %s\n", | ||
| 1528 | filename); | ||
| 1529 | continue; | ||
| 1530 | } | ||
| 1531 | } | ||
| 1532 | skip_white (infile); | ||
| 1533 | c = getc (infile); | ||
| 1534 | if (c != '\"') | ||
| 1535 | { | ||
| 1536 | fprintf (stderr, "## autoload of %s unparsable (%s)\n", | ||
| 1537 | buffer, filename); | ||
| 1538 | continue; | ||
| 1539 | } | ||
| 1540 | read_c_string_or_comment (infile, 0, false, 0); | ||
| 1541 | |||
| 1542 | if (!search_lisp_doc_at_eol (infile)) | ||
| 1543 | continue; | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | #ifdef DEBUG | ||
| 1547 | else if (! strcmp (buffer, "if") | ||
| 1548 | || ! strcmp (buffer, "byte-code")) | ||
| 1549 | continue; | ||
| 1550 | #endif | ||
| 1551 | |||
| 1552 | else | ||
| 1553 | { | ||
| 1554 | #ifdef DEBUG | ||
| 1555 | fprintf (stderr, "## unrecognized top-level form, %s (%s)\n", | ||
| 1556 | buffer, filename); | ||
| 1557 | #endif | ||
| 1558 | continue; | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | /* At this point, we should gobble a doc string from the input file. | ||
| 1562 | The opening quote (and leading backslash-newline) | ||
| 1563 | have already been read. */ | ||
| 1564 | |||
| 1565 | printf ("\037%c%s\n", type, buffer); | ||
| 1566 | read_c_string_or_comment (infile, 1, false, 0); | ||
| 1567 | } | ||
| 1568 | if (ferror (infile) || fclose (infile) != 0) | ||
| 1569 | fatal ("%s: read error", filename); | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | |||
| 1217 | /* make-docfile.c ends here */ | 1573 | /* make-docfile.c ends here */ |